Backend para geração automática de memoriais descritivos de engenharia a partir de contexto estruturado ou arquivos técnicos. O foco operacional atual é a geração determinística de documentos DOCX a partir de contratos de template e schema, com suporte a revisão manual por sessão e persistência de memoriais gerados.
Este README é voltado principalmente para quem vai integrar a API, mas também inclui o necessário para rodar, configurar, testar e manter o projeto localmente.
A API recebe dados estruturados ou arquivos de projeto, executa extração e mapeamento para o contrato do memorial, valida o resultado contra o schema correspondente e gera um DOCX final.
Fluxos principais já suportados:
- geração direta por JSON
- ingestão de arquivos
- geração direta a partir de arquivos
- revisão manual por sessão
- persistência de memoriais gerados com metadata e download assinado
Princípios que governam o sistema:
- o template DOCX é a fonte de verdade da estrutura final do documento
- o schema JSON é o contrato de dados para renderização
- a geração final precisa permanecer determinística
- saída de LLM pode ajudar na extração, mas não pode ser usada como conteúdo final do memorial
- todo contexto deve ser validado antes da renderização
O projeto já está além de um protótipo. Hoje ele possui uma base funcional de backend com trilhas ativas para múltiplos tipos de memorial.
Tipos de memorial atualmente suportados pela API:
eletricotelecomgas-naturalglp
Observações sobre o estado do produto:
memorial eletrico v1é a trilha mais madura, incluindo fluxo completo de revisão por sessão.memorial telecom v1possui geração por JSON e por arquivos.memorial gas natural v1possui template, schema e fluxo de geração.memorial glppossui trilhasv1ev2no repositório; a API pública principal usaglp, enquanto o histórico persistido também aceita filtro porglp_v2.
Use esse fluxo quando o contexto do memorial já estiver estruturado fora da etapa de extração.
Fluxo esperado:
- receber payload JSON
- validar contra o schema do memorial
- renderizar o DOCX
- retornar o arquivo gerado
Use esse fluxo quando o frontend ou outro cliente precisar apenas enviar arquivos e deixar o backend prepará-los para extração posterior.
Use esse fluxo quando os PDFs do projeto forem a entrada principal.
Fluxo esperado:
- receber upload dos arquivos
- ingerir temporariamente
- extrair dados relevantes
- mapear para o contexto do memorial
- validar o contexto
- renderizar o DOCX
Esse é o fluxo mais importante para casos em que a extração automática precisa de confirmação humana antes da geração final.
Fluxo esperado:
- criar uma sessão
- disparar a extração em background
- persistir contexto parcial e relatório de extração
- permitir correções manuais
- mesclar correções com o contexto
- gerar o memorial final a partir do contexto revisado
Hoje esse fluxo está exposto principalmente para eletrico.
Além dos endpoints binários que retornam o DOCX diretamente, a API também permite gerar o memorial, salvar o artefato em storage e persistir metadata para listagem, consulta e download posterior.
Todas as rotas públicas ficam sob:
/api/v1
A API retorna três categorias principais:
- JSON de sucesso
- JSON de erro
- arquivo DOCX
Quando a geração é bem-sucedida via endpoints binários, a resposta usa:
application/vnd.openxmlformats-officedocument.wordprocessingml.document
O cliente deve tratar essa resposta como download binário.
Todas as respostas incluem:
X-Request-ID
Se o cliente enviar esse header, a API o preserva. Caso contrário, um ID é gerado no backend.
Isso é útil para:
- correlação de logs
- troubleshooting entre frontend e backend
- rastreio de erros em produção
Na prática, a integração fica mais simples se você pensar em cinco grupos:
- healthcheck
- geração por JSON
- ingestão e geração por arquivos
- revisão por sessão
- memoriais persistidos
Rotas disponíveis:
GET /healthGET /health/liveGET /health/ready
Comportamento esperado:
/healthretorna um status simples/health/liveconfirma que a aplicação está viva/health/readyvalida prontidão operacional e pode retornar503se algum check crítico falhar
Esses endpoints recebem um payload JSON já estruturado e retornam o DOCX diretamente.
Rotas:
POST /api/v1/memoriais/eletricoPOST /api/v1/memoriais/telecomPOST /api/v1/memoriais/gas-naturalPOST /api/v1/memoriais/glp
Uso recomendado:
- sistemas que já possuem os dados do memorial em formato estruturado
- integrações em que a etapa de extração acontece fora desta API
Resultado de sucesso:
- status
200 - corpo binário DOCX
Esses endpoints recebem multipart/form-data com arquivos e devolvem o DOCX na própria resposta.
Rotas:
POST /api/v1/memoriais/eletrico/from-filesPOST /api/v1/memoriais/telecom/from-filesPOST /api/v1/memoriais/gas-natural/from-filesPOST /api/v1/memoriais/glp/from-files
Payload esperado:
- campo repetido
files - arquivos de projeto, tipicamente PDFs
Resultado de sucesso:
- status
200 - corpo binário DOCX
Esses endpoints geram o memorial, enviam o DOCX para storage e retornam metadata em JSON.
Rotas:
POST /api/v1/memoriais/eletrico/from-files/persistPOST /api/v1/memoriais/telecom/from-files/persistPOST /api/v1/memoriais/gas-natural/from-files/persistPOST /api/v1/memoriais/glp/from-files/persist
Payload esperado:
multipart/form-data- campo repetido
files - campo opcional
observations
Exemplo com JavaScript:
const form = new FormData();
files.forEach((file) => form.append("files", file));
form.append("observations", "Observações opcionais do usuário");
const response = await fetch("/api/v1/memoriais/telecom/from-files/persist", {
method: "POST",
body: form,
});
const body = await response.json();Exemplo de resposta:
{
"id": "uuid",
"type": "telecom",
"project_name": "Memorial Telecom",
"status": "ready",
"observations": "Observações opcionais do usuário",
"pdf_filenames": ["projeto.pdf"],
"created_at": "2026-04-17T12:00:00Z",
"updated_at": "2026-04-17T12:00:00Z",
"download_url": "https://signed-url"
}Esses endpoints são úteis para dashboard, histórico e download posterior.
GET /api/v1/memoriaisGET /api/v1/memoriais?type=telecom
Filtros suportados atualmente:
eletricotelecomgas-naturalglpglp_v2
Resposta:
{
"memorials": []
}Use:
GET /api/v1/memoriais/{memorial_id}
Parâmetro opcional:
include_context=truepara retornar também o contexto persistido, quando aplicável
Use:
GET /api/v1/memoriais/{memorial_id}/download
Esse endpoint retorna uma URL assinada. Se a URL anterior expirar, chame o endpoint novamente.
Use:
DELETE /api/v1/memoriais/{memorial_id}
Comportamento:
- remove o objeto do storage
- remove a metadata persistida
- retorna
204em caso de sucesso
O backend possui fluxo de revisão manual com persistência de sessão. Esse fluxo é crítico para eletrico e deve ser tratado como parte sensível do contrato do sistema.
Fluxo esperado para integradores:
- criar sessão
- acompanhar o processamento
- ler contexto parcial e relatório de extração
- enviar correções manuais
- solicitar geração final após revisão
Como o contrato dessa trilha é mais específico e pode evoluir com mais cuidado do que os endpoints binários, consulte também:
A API usa envelopes de erro previsíveis para validação, erros HTTP e falhas internas.
Formato base:
{
"detail": "Mensagem legada/compatível",
"error": {
"code": "internal_server_error",
"message": "Erro interno ao processar a requisição.",
"request_id": "..."
}
}Quando o payload ou o contexto extraído não atende ao schema, a API normalmente retorna 400 com lista de problemas.
Exemplo:
{
"detail": "Payload invalido para o memorial eletrico v1.",
"errors": [
{
"path": "$.obra",
"message": "'tipo_edificacao' is a required property",
"validator": "required"
}
]
}Em alguns fluxos, também pode existir extraction_report.
Quando a extração encontra valores quantitativos divergentes e a API bloqueia a geração para evitar memorial incorreto, o retorno é 409.
Exemplo:
{
"detail": "Encontramos valores diferentes nos quantitativos do projeto. A geração foi bloqueada para evitar um memorial incorreto.",
"errors": [
{
"path": "$.pontos_utilizacao.conflitos",
"message": "Conflitos criticos GLP v2 sem resolucao.",
"validator": "glp_v2_conflict"
}
],
"conflicts": [
{
"tipo": "glp_v2_points_total_mismatch",
"status": "unresolved"
}
],
"error": {
"code": "quantitative_conflict_unresolved",
"message": "Encontramos valores diferentes nos quantitativos do projeto. A geração foi bloqueada para evitar um memorial incorreto."
}
}Payloads inválidos na própria requisição HTTP retornam 422.
Falhas inesperadas retornam 500 com mensagem segura, sem stack trace e sem vazamento de exceções brutas.
- sempre leia
detail - se existir
error, useerror.codepara tratamento programático - se existir
errors, apresente a lista em UI de validação - se existir
extraction_report, trate como apoio para depuração ou revisão humana - use
X-Request-IDao reportar falhas
O backend possui limites configuráveis para evitar falhas opacas em parsing, OCR, extração e renderização.
Variáveis atualmente suportadas:
MAX_FILE_COUNTMAX_FILE_SIZE_MBMAX_TOTAL_UPLOAD_MBMAX_PDF_PAGES
Defaults:
10arquivos50 MBpor arquivo200 MBno total100páginas por PDF
Violações desses limites retornam erro de cliente, em geral 413 para excesso de tamanho/quantidade.
- Python 3.11+ recomendado
- ambiente virtual Python
- dependências de
requirements.txt
Crie e ative um ambiente virtual:
python -m venv .venv
source .venv/bin/activateInstale as dependências:
pip install -r requirements.txtCom o ambiente ativo:
uvicorn app.main:app --reloadPor padrão, a aplicação ficará acessível em:
http://127.0.0.1:8000
Configuração mínima comum para ambiente local:
APP_ENV=local
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173Variáveis suportadas pela aplicação:
APP_ENVCORS_ALLOWED_ORIGINSCORS_ORIGINSSUPABASE_URLSUPABASE_SECRET_KEYouSUPABASE_SERVICE_ROLE_KEYSUPABASE_KEY(legado)GENERATED_MEMORIALS_BUCKETGENERATED_MEMORIALS_SIGNED_URL_TTLMAX_FILE_COUNTMAX_FILE_SIZE_MBMAX_TOTAL_UPLOAD_MBMAX_PDF_PAGESOPENAI_API_KEY
Comportamento por ambiente:
- em
localetest, a API aceita origins locais padrão se CORS não for informado - em
production,CORS_ALLOWED_ORIGINSé obrigatório - em
production, a persistência de memoriais exigeGENERATED_MEMORIALS_BUCKET,SUPABASE_URLeSUPABASE_SECRET_KEYouSUPABASE_SERVICE_ROLE_KEY
O projeto suporta persistência de sessões e de memoriais gerados com Supabase.
Os endpoints from-files/persist usam dois componentes:
- metadata em banco
- arquivo DOCX em Supabase Storage
Configuração recomendada:
SUPABASE_URL=...
SUPABASE_SECRET_KEY=...
GENERATED_MEMORIALS_BUCKET=generated-memorials
GENERATED_MEMORIALS_SIGNED_URL_TTL=3600Migrations relacionadas:
migrations/002_generated_memorials.sqlmigrations/003_create_storage_bucket.sqlmigrations/004_generated_memorials_context.sqlmigrations/005_auth_profiles_and_ownership.sql
O dashboard usa Supabase Auth com email/senha. Cadastro público deve ficar desabilitado no Supabase; usuários novos são criados apenas pela API em /api/v1/admin/users, acessível por perfis owner.
Setup inicial:
- Crie o primeiro usuário em Supabase Authentication.
- Execute
migrations/005_auth_profiles_and_ownership.sql. - Confirme que o usuário
adc8635c-193a-4568-9896-2bc523bba923foi inserido empublic.user_profilescomoownerdurante o desenvolvimento. - Antes da produção, crie/promova o usuário do engenheiro chefe para
ownere demova ou desative o usuário temporário.
Comportamento do ciclo de estados:
- cria metadata com
status=processing - faz upload do DOCX final
- muda metadata para
status=ready - em falha, tenta marcar como
status=failed
Regras importantes:
- download só é permitido para memoriais com
status=ready - ausência de metadata retorna
404 - artefato ausente no storage retorna erro seguro
- indisponibilidade de storage retorna
503
O backend também possui store em filesystem e store opcional em Supabase para sessões de revisão. Mudanças nessa área precisam preservar alinhamento entre os dois backends.
Migration relacionada:
migrations/001_review_sessions.sql
Mapa dos diretórios mais importantes:
app/
api/ rotas HTTP e tratamento de erros
schemas/ contratos de request/response e modelos internos
services/ ingestão, extração, mapeamento, sessão, validação e renderização
templates/
eletrico/v1/
telecom/v1/
gas_natural/v1/
glp/v1/
glp/v2/
tests/ cobertura unitária e de integração
migrations/ SQL de suporte a Supabase/Postgres/Storage
docs/ referência de integração, planos e notas operacionais
projects/ exemplos de arquivos técnicos usados em desenvolvimento
Arquivos especialmente úteis:
Cada memorial depende da coerência entre:
template.docxschema.jsonnotes.md
Exemplos:
templates/eletrico/v1/template.docx
templates/eletrico/v1/schema.json
templates/eletrico/v1/notes.md
Restrições importantes:
- não alterar comportamento do template sem checar impacto no schema
- não alterar schema sem checar impacto no template e nos testes
- o DOCX final não pode conter placeholders Jinja não resolvidos
- a renderização deve continuar determinística e orientada por contrato
O projeto usa unittest como fluxo principal de validação automatizada.
Comandos úteis:
python -m unittest discover -s tests
python -m unittest tests.test_api
python -m unittest tests.test_session_store
python -m unittest tests.test_supabase_session_store
python -m unittest tests.test_generated_memorial_api
python -m unittest tests.test_pipeline
python -m unittest tests.test_pipeline_from_files
python -m unittest tests.test_memorial_renderer
python -m unittest tests.test_memorial_validator
python -m unittest tests.test_extraction_mapperRecomendação por tipo de mudança:
- mudanças de API:
tests.test_api - persistência de sessão:
tests.test_session_storeetests.test_supabase_session_store - memoriais persistidos:
tests.test_generated_memorial_apietests.test_generated_memorial_store - extração e mapeamento: testes de pipeline, mapper e extractor
- renderização e validação:
tests.test_memorial_rendereretests.test_memorial_validator - mudanças transversais: suíte completa
- preservar comportamento da API, salvo quando a mudança exigir alteração explícita
- validar dados antes de renderizar
- manter geração final determinística
- não usar LLM para gerar o conteúdo final do memorial
- preservar coerência entre filesystem e Supabase nos fluxos de sessão
- preferir mudanças pequenas e localizadas
Se você só precisa consumir a API:
- escolha entre geração por JSON, por arquivos ou por persistência
- trate sucesso como DOCX binário ou JSON, dependendo do endpoint
- trate falhas pelo envelope
errore pelo campodetail - preserve
X-Request-IDnos logs do cliente - use os endpoints
/persistpara histórico e download assinado