Connect the Agent Graph to the Backend - #163
Merged
Merged
Conversation
WesleyGoncalves
force-pushed
the
feature/connecting-graph-backend
branch
2 times, most recently
from
September 22, 2026 14:43
6666771 to
41ea05b
Compare
…e regra-submetida Closes #150
WesleyGoncalves
force-pushed
the
feature/connecting-graph-backend
branch
from
September 23, 2026 18:04
8525405 to
c2275ad
Compare
- libpq5 é exigida pelo psycopg do checkpointer em runtime - env_file opcional leva GOOGLE_API_KEY ao container sem sobrescrever os endereços da rede do compose
…xecução - o nó greeting e o nó compartilhado tools ficam registrados no grafo, mas sem aresta de entrada - code_generation não vincula tools e envia só o próprio prompt - registra o modelo greeting no registry com orçamento de saída menor
- exige um único bloco python com aplicar_regra(bases, apuracao_base, competencias) no nível do módulo - valida só com ast.parse: o código gerado nunca é compilado, importado nem executado no codegen
- ids em UUID v5 e INSERT ... ON CONFLICT DO NOTHING tornam a reentrega idempotente só com INSERT - a resposta entra na chave do prompt para que outra chamada com o mesmo prompt não colida
…utar-codigo e pausam - persist_response grava antes da extração, para que uma resposta inextraível continue auditável - dispatch_execution fica separado de await_execution, pois a retomada reexecuta o nó pausado inteiro - o comando leva só codigo_gerado_id e o interrupt só referências (claim-check, ADR-001)
- entrada não nula recomeçava do START e repetia a chamada ao modelo e as gravações - run passa a expor cada nó concluído e a pausa; run_to_completion só o consome
- a reentrega continua sobre a mesma resposta gravada, então a falha é permanente
WesleyGoncalves
marked this pull request as ready for review
September 24, 2026 21:31
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved contract and routing failures, retry-loop configuration errors, and deployment gaps block approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 3
Open (5)
Voice events are routed to a loader requiring a missing rule ID · New API producer omits the required budget field · New Contract example adds budget before the API DTO supports it · New Completion logs omit model, duration, and token usage · New Missing budget causes messages to requeue indefinitely · New
What changed in this PR
Connects the codegen service to LangGraph, RabbitMQ, PostgreSQL, LLM generation, artifact persistence, and worker dispatch.
Changes:
- Adds graph routing, generation, extraction, persistence, and execution nodes.
- Integrates production startup, database/checkpointer wiring, and messaging.
- Updates contracts, deployment, documentation, and tests.
| File | Reviewed scope |
|---|---|
worker/docker-compose.yml |
Removes obsolete worker compose configuration. |
deploy/docker-compose.yml |
Configures codegen environment variables. |
contracts/examples/events/regra-submetida.json |
Updates the event example with budget data. |
contracts/events/regra-submetida.schema.json |
Adds the optional budget field. |
codegen/tests/app/test_roteamento.py |
Tests graph routing. |
codegen/tests/app/test_rabbitmq_integracao.py |
Tests execution command delivery. |
codegen/tests/app/test_mensageria.py |
Tests consumers and lifecycle behavior. |
codegen/tests/app/test_db.py |
Tests database setup. |
codegen/tests/app/test_codigo_gerado.py |
Tests generated-code extraction. |
codegen/tests/app/repositorio/test_regras.py |
Tests rule repository behavior. |
codegen/tests/app/repositorio/test_artefatos.py |
Tests artifact persistence. |
codegen/tests/app/repositorio/__init__.py |
Maintains repository test package structure. |
codegen/tests/app/prompts/test_geracao_codigo.py |
Tests generation prompt formatting. |
codegen/tests/app/graph/tools/test_greeting.py |
Tests the greeting tool. |
codegen/tests/app/graph/tools/test_arithmetic.py |
Removes obsolete arithmetic tests. |
codegen/tests/app/graph/test_entrypoint.py |
Tests graph execution and resumption. |
codegen/tests/app/graph/prompts/test_calculator.py |
Removes obsolete calculator prompt tests. |
codegen/tests/app/graph/nodes/test_load_rule.py |
Tests rule loading. |
codegen/tests/app/graph/nodes/test_greeting.py |
Tests the greeting node. |
codegen/tests/app/graph/nodes/test_code_generation.py |
Tests LLM generation behavior. |
codegen/tests/app/graph/nodes/test_calculator.py |
Removes obsolete calculator node tests. |
codegen/tests/app/graph/nodes/test_artifact_nodes.py |
Tests artifact persistence, extraction, and dispatch. |
codegen/tests/app/graph/core/test_tool_dispatch.py |
Tests tool allowlisting. |
codegen/tests/app/graph/core/test_engine.py |
Tests graph topology and execution. |
codegen/tests/app/graph/core/llm/test_registry.py |
Tests model registry behavior. |
codegen/tests/app/graph/conftest.py |
Updates graph test fixtures. |
codegen/tests/app/banco_falso.py |
Provides an in-memory persistence test helper. |
codegen/run.py |
Uses the production application factory. |
codegen/pyproject.toml |
Registers the LLM test marker. |
codegen/Makefile |
Updates development commands. |
codegen/entrypoint.sh |
Uses the production application factory. |
codegen/docs/retomada-apos-execucao.md |
Documents graph resumption. |
codegen/docs/mensageria.md |
Documents messaging and lifecycle behavior. |
codegen/docs/graph.md |
Updates the graph documentation. |
codegen/Dockerfile |
Adds the PostgreSQL runtime dependency. |
codegen/docker-compose.yml |
Removes obsolete codegen compose configuration. |
codegen/contracts/events/regra-submetida.schema.json |
Updates the bundled event schema. |
codegen/app/repositorio/regras.py |
Loads and validates rules. |
codegen/app/repositorio/artefatos.py |
Persists generated artifacts. |
codegen/app/repositorio/__init__.py |
Adds the repository package. |
codegen/app/prompts/geracao_codigo.py |
Defines the generated response format. |
codegen/app/mensageria/roteamento.py |
Implements graph routing. |
codegen/app/mensageria/consumers.py |
Handles permanent graph failures. |
codegen/app/mensageria/broker.py |
Starts the submission consumer. |
codegen/app/main.py |
Wires application lifecycle resources. |
codegen/app/graph/tools/greeting.py |
Adds the greeting tool. |
codegen/app/graph/tools/arithmetic.py |
Removes obsolete arithmetic tools. |
codegen/app/graph/prompts/greeting.py |
Adds the greeting prompt. |
codegen/app/graph/prompts/calculator.py |
Removes the obsolete calculator prompt. |
codegen/app/graph/nodes/persist_response.py |
Persists model responses. |
codegen/app/graph/nodes/load_rule.py |
Loads the submitted rule. |
codegen/app/graph/nodes/greeting.py |
Adds the greeting node. |
codegen/app/graph/nodes/extract_code.py |
Extracts and stores generated code. |
codegen/app/graph/nodes/dispatch_execution.py |
Publishes worker execution commands. |
codegen/app/graph/nodes/code_generation.py |
Calls the generation model. |
codegen/app/graph/nodes/calculator.py |
Removes the obsolete calculator node. |
codegen/app/graph/nodes/await_execution.py |
Pauses for worker results. |
codegen/app/graph/entrypoint.py |
Runs and resumes graph executions. |
codegen/app/graph/core/tool_dispatch.py |
Updates the tool dispatch allowlist. |
codegen/app/graph/core/state.py |
Expands shared graph state. |
codegen/app/graph/core/llm/registry.py |
Registers generation models. |
codegen/app/graph/core/engine.py |
Defines the graph pipeline. |
codegen/app/db.py |
Adds asynchronous database setup. |
codegen/app/contratos/mensagens.py |
Adds the budget DTO field. |
codegen/app/codigo_gerado.py |
Validates generated Python code. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| is reached once that resumed run has nothing left to do. | ||
| """ | ||
| # graph.add_edge(START, "greeting") | ||
| graph.add_edge(START, "load_rule") |
Comment on lines
+51
to
+55
| "orcamento": { | ||
| "allOf": [ | ||
| { | ||
| "$ref": "../domain/comum.schema.json#/$defs/brl" | ||
| } |
| "submissao_id": "b81e0f4c-52a9-4f0b-8a3d-7c2e5d10ab93", | ||
| "regra_id": "9c7d3e21-4a6b-4c8d-9e0f-1a2b3c4d5e6f" | ||
| "regra_id": "9c7d3e21-4a6b-4c8d-9e0f-1a2b3c4d5e6f", | ||
| "orcamento": 485000.0 |
| if usage is not None: | ||
| update["consumo_tokens"] = usage | ||
|
|
||
| logger.info("code_generation model call finished", extra={"finish_reason": finish_reason}) |
Comment on lines
+25
to
+27
| orcamento = state.get("orcamento") | ||
| if orcamento is None: | ||
| raise OrcamentoAusenteError("executar-codigo requires the job's orcamento") |
Reaplica a T-098, revertida em 2632c87 só para destravar develop, e estende ao ReprocessarJobService, produtor que nasceu depois do revert. Sem o campo, o codegen não tem como montar executar-codigo: o orçamento só existe em jobs.orcamento, e ele não tem permissão nessa tabela. BigDecimal de ponta a ponta, para não perder precisão de um valor monetário. O campo é omitido quando nulo, e a ausência nunca é lida como zero.
… fim Antes, só duas exceções rejeitavam sem requeue; o resto caía em nack com requeue, sem backoff nem DLQ. Uma resposta truncada por MAX_TOKENS voltava para sempre, e cada reentrega retomava do checkpoint e pagava outra chamada ao modelo. Em nenhum caso a api era avisada, então o job ficava em gerando_regra para sempre. - FalhaDoJobError (app/falhas.py) é a base das falhas que uma reentrega não corrige, e cada uma declara a etapa do grafo em que aconteceu - o Consumer passa a ter uma decisão só para todas elas, sem importar exceção de repositorio nem de codigo_gerado - o GraphRouter publica etapa-alterada com erro antes de rejeitar, e a api move o job para erro com motivo erro_<etapa> - dispatch_execution anuncia delegacao_worker/iniciada antes do comando, levando o job a simulando; o evento idempotente vai na frente do comando que não é - code_generation aceita só STOP como parada limpa: o vocabulário de falha do provedor é aberto, e a lista de bloqueio deixava passar resposta bloqueada com texto parcial - o nó greeting e a infra de tools saem do grafo; nada os alcançava desde START Os arquivos se cruzam (code_generation.py carrega a exceção e o finish_reason; o conftest carrega o fake do modelo e a remoção do greeting), então separar em commits menores deixaria estados intermediários quebrados.
…a-submetida Os testes vinham da T-049, quando os três consumers subiam. A b489349 estreitou iniciar_consumers para só regra-submetida e deixou três testes falhando; ninguém rodou `make test-rabbitmq` desde então, porque o marker fica pulado por padrão. - a entrega ponta a ponta passa a cobrir regra-submetida, a única com consumer - um teste novo confere o que a T-094 promete: parametros-confirmados e simulacao-concluida ficam retidas na fila, não descartadas - o teste do fanout confere as duas cópias sem consumir nenhuma, que é o que ele sempre quis provar
A api preenchia as competências ausentes com seis meses, mas o dataset publicado tem cinco: Jul/2025 foi descartado no tratamento dos dados (EXCLUDE_2025_07), não está nas bases embutidas no sandbox e não tem baseline congelado. Como a tela de formulário não tem campo de período e nunca envia competencias, todo job criado por ela nascia pedindo ao worker um mês sem o que comparar, e terminava em erro depois de a geração já ter sido paga. A lista servia também de validação, então estreitá-la recusa na entrada o que antes só falhava lá adiante. Ela vive duplicada nas três requisições que aceitam competências; consolidá-la é outra tarefa. O teste que fixava o comportamento antigo passa a afirmar o novo, e um teste novo cobre a recusa de um mês fora do dataset.
A api cria a linha de `simulacoes` a partir desse evento, e é ela que liga o job ao resultado que o worker grava. Sem ele o resultado não tinha a que se amarrar: o cliente nunca recebia o evento SSE `resultado`, e as telas de relatório e de histórico ficavam sem o desfecho mesmo com os números apurados no banco. Publicado em `extract_code`, que é onde a etapa `geracao_codigo` conclui - prompt, resposta e código gravados -, e estruturalmente antes de `executar-codigo`, que é o que garante a ordem: `SimulacaoConcluidaService` só amarra o resultado se a simulação já existir. O `evento_id` é determinístico (UUID v5 de job, etapa e codigo_gerado_id), derivado junto dos outros ids pelo mesmo motivo: reexecutar o nó republica o mesmo evento, e o índice único de `trilhas_auditoria.evento_id` reconhece a reentrega. `elementos_implementados` fica de fora. O schema diz que é contra essa lista que a cobertura é conferida, e isso é a T-056; declarar uma aqui afirmaria uma verificação que este nó não faz. As demais etapas do grafo seguem sem publicar `no-concluido`.
Nada exercitava o sistema inteiro, e foi por isso que o caminho da tela ficou quebrado sem nenhuma suíte ficar vermelha. Estes testes sobem api, codegen e worker contra um Postgres e um RabbitMQ reais e submetem a regra exatamente como o formulário a submete - sem `competencias`, que a tela não tem como enviar. Quatro cenários, em três classes: o caminho feliz até o resultado apurado; a falha permanente do codegen, que leva o job de `gerando_regra` a `erro`; a falha de infraestrutura do worker, que o leva de `simulando` a `erro`; e a asserção violada, o único desfecho em que houve números e é justamente o número que se recusa. Cada evento do stream é validado contra `EventoProgresso` do openapi antes de qualquer comparação de texto: comparar texto prova que o campo está lá, não que a forma é a publicada. `ContratoDoStreamTests` guarda esse validador, porque um que aceitasse tudo deixaria os quatro cenários passando no vácuo. Três dos quatro cenários rodam sem chave do provedor, e por isso cabem em CI. O bootstrap dos containers está duplicado entre as classes; extraí-lo para um apoio no pacote fica para quando alguma delas precisar mudar.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


T-094 #150, T-096 #154
UPDATE REQUIRED
This PR adds
libpq5to thecodegenDocker container.If you have the
synapse-codegenimage in your Docker environment. Please, refresh it runningdocker compose -f deploy/docker-compose.yml up -d --buildReport