Skip to content

Connect the Agent Graph to the Backend - #163

Merged
pedro-fs-garcia merged 32 commits into
developfrom
feature/connecting-graph-backend
Sep 25, 2026
Merged

pedro-fs-garcia merged 32 commits into
developfrom
feature/connecting-graph-backend

Conversation

@WesleyGoncalves

@WesleyGoncalves WesleyGoncalves commented Sep 22, 2026 •

Copy link
Copy Markdown

T-094 #150, T-096 #154

UPDATE REQUIRED

This PR adds libpq5 to the codegen Docker container.
If you have the synapse-codegen image in your Docker environment. Please, refresh it running docker compose -f deploy/docker-compose.yml up -d --build


Report

  • Codegen worked. It saved the prompt, the response (22,581 tokens in, 970 out) and the generated code (2,752 characters), published executar-codigo, and paused the graph as planned.
  • The worker couldn't start the sandbox. It launches each run from the synapse-sandbox:local image. That raises SandboxInfraError, which the worker records as erro_infra. It retried 3 times within a second, then:
    • saved a result with status: erro_infra and published simulacao-concluida, and the api then marked the job erro.

@WesleyGoncalves WesleyGoncalves self-assigned this Sep 22, 2026
@WesleyGoncalves
WesleyGoncalves force-pushed the feature/connecting-graph-backend branch 2 times, most recently from 6666771 to 41ea05b Compare September 22, 2026 14:43
@WesleyGoncalves
WesleyGoncalves force-pushed the feature/connecting-graph-backend branch from 8525405 to c2275ad Compare September 23, 2026 18:04
- 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
WesleyGoncalves marked this pull request as ready for review September 24, 2026 21:31
Copilot AI lite review requested due to automatic review settings September 24, 2026 21:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 High severity · 2 Medium severity

Open (5)
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.
@pedro-fs-garcia
pedro-fs-garcia merged commit 98d8d90 into develop Sep 25, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants