From a5f24dac3fc80c01e991f14bbd71e91e5609299a Mon Sep 17 00:00:00 2001 From: "cristiano.hoshikawa" Date: Thu, 10 Sep 2026 16:12:04 -0300 Subject: [PATCH] new feature: Multi-Intent --- README.md | 29 ++- README_en.md | 30 ++- .../app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../docs/TRANSACTION_SEMANTIC_CONFIRMATION.md | 2 +- .../app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../docs/TRANSACTION_SEMANTIC_CONFIRMATION.md | 2 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- docs/features/en/16_multi_intent_planning.md | 27 +++ docs/features/en/README.md | 1 + .../pt-BR/16_multi_intent_planning.md | 46 ++++ docs/features/pt-BR/README.md | 1 + .../src/agent_framework.egg-info/SOURCES.txt | 2 + .../__pycache__/__init__.cpython-313.pyc | Bin 411 -> 413 bytes .../__pycache__/extensions.cpython-313.pyc | Bin 2139 -> 2141 bytes .../__pycache__/idempotency.cpython-313.pyc | Bin 8912 -> 8914 bytes .../__pycache__/observer.cpython-313.pyc | Bin 17555 -> 17557 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 547 -> 549 bytes .../composite_publisher.cpython-313.pyc | Bin 2502 -> 2504 bytes .../__pycache__/event_builder.cpython-313.pyc | Bin 1260 -> 1262 bytes .../__pycache__/factory.cpython-313.pyc | Bin 3996 -> 3998 bytes .../__pycache__/publisher.cpython-313.pyc | Bin 1872 -> 1874 bytes .../tim_payload_mapper.cpython-313.pyc | Bin 9286 -> 9288 bytes .../__pycache__/tim_sequence.cpython-313.pyc | Bin 17561 -> 17563 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 514 -> 516 bytes .../__pycache__/kafka.cpython-313.pyc | Bin 1812 -> 1814 bytes .../__pycache__/langfuse.cpython-313.pyc | Bin 23172 -> 23174 bytes .../__pycache__/oci_streaming.cpython-313.pyc | Bin 1821 -> 1823 bytes .../__pycache__/pubsub.cpython-313.pyc | Bin 7440 -> 7442 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 403 -> 405 bytes .../usage_repository.cpython-313.pyc | Bin 15686 -> 15688 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 213 -> 215 bytes .../cache/__pycache__/cache.cpython-313.pyc | Bin 16523 -> 16525 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 216 -> 218 bytes .../__pycache__/adapters.cpython-313.pyc | Bin 4172 -> 4174 bytes .../channels/__pycache__/base.cpython-313.pyc | Bin 1796 -> 1798 bytes .../__pycache__/gateway.cpython-313.pyc | Bin 5771 -> 5773 bytes .../__pycache__/interruption.cpython-313.pyc | Bin 6456 -> 6458 bytes .../__pycache__/transcription.cpython-313.pyc | Bin 1429 -> 1431 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 923 -> 925 bytes .../checkpoint_repository.cpython-313.pyc | Bin 27163 -> 27165 bytes .../langgraph_saver.cpython-313.pyc | Bin 33265 -> 33267 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 214 -> 216 bytes .../agent_registry.cpython-313.pyc | Bin 6947 -> 6949 bytes .../__pycache__/settings.cpython-313.pyc | Bin 13789 -> 13791 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 306 -> 308 bytes .../mcp_gateway_client.cpython-313.pyc | Bin 3332 -> 3334 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1501 -> 1503 bytes .../__pycache__/base.cpython-313.pyc | Bin 1375 -> 1377 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 9160 -> 9162 bytes .../__pycache__/custom_rails.cpython-313.pyc | Bin 4404 -> 4406 bytes .../framework_llm_client.cpython-313.pyc | Bin 19673 -> 19675 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 5923 -> 5925 bytes .../output_supervisor.cpython-313.pyc | Bin 24306 -> 24308 bytes .../parallel_executor.cpython-313.pyc | Bin 21862 -> 21864 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 10889 -> 10891 bytes .../__pycache__/rail_action.cpython-313.pyc | Bin 654 -> 656 bytes .../__pycache__/rail_decision.cpython-313.pyc | Bin 1605 -> 1607 bytes .../__pycache__/rail_result.cpython-313.pyc | Bin 1004 -> 1006 bytes .../__pycache__/rails.cpython-313.pyc | Bin 43324 -> 43326 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 2994 -> 2996 bytes .../__pycache__/_compat.cpython-313.pyc | Bin 2445 -> 2447 bytes .../contestation_validation.cpython-313.pyc | Bin 2177 -> 2179 bytes .../__pycache__/contracts.cpython-313.pyc | Bin 6901 -> 6903 bytes .../__pycache__/input_size.cpython-313.pyc | Bin 3649 -> 3651 bytes .../__pycache__/llm_adapter.cpython-313.pyc | Bin 3799 -> 3801 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 7454 -> 7456 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 8894 -> 8896 bytes .../output_sanitization.cpython-313.pyc | Bin 13910 -> 13912 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 12661 -> 12663 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 473 -> 475 bytes .../__pycache__/_context.cpython-313.pyc | Bin 5871 -> 5873 bytes .../ausencia_oferta_proativa.cpython-313.pyc | Bin 12205 -> 12207 bytes .../__pycache__/coerencia.cpython-313.pyc | Bin 4157 -> 4159 bytes .../__pycache__/dlex_in.cpython-313.pyc | Bin 1304 -> 1306 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 1815 -> 1817 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 17652 -> 17654 bytes .../__pycache__/fraseologia.cpython-313.pyc | Bin 7633 -> 7635 bytes .../__pycache__/out_of_scope.cpython-313.pyc | Bin 17493 -> 17495 bytes .../prompts/__pycache__/pinj.cpython-313.pyc | Bin 8424 -> 8426 bytes .../__pycache__/ragsec.cpython-313.pyc | Bin 1120 -> 1122 bytes .../__pycache__/revprec.cpython-313.pyc | Bin 3150 -> 3152 bytes .../prompts/__pycache__/tox.cpython-313.pyc | Bin 644 -> 646 bytes .../toxicidade_output.cpython-313.pyc | Bin 928 -> 930 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1663 -> 1665 bytes .../rails/__pycache__/alcada.cpython-313.pyc | Bin 4999 -> 5001 bytes .../rails/__pycache__/anatel.cpython-313.pyc | Bin 9148 -> 9150 bytes .../__pycache__/confirmation.cpython-313.pyc | Bin 11042 -> 11044 bytes .../rails/__pycache__/dlex_in.cpython-313.pyc | Bin 3279 -> 3281 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 3331 -> 3333 bytes .../rails/__pycache__/ragsec.cpython-313.pyc | Bin 5462 -> 5464 bytes .../rails/__pycache__/revprec.cpython-313.pyc | Bin 5470 -> 5472 bytes .../rails/__pycache__/tox.cpython-313.pyc | Bin 8293 -> 8295 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 533 -> 535 bytes .../rules/__pycache__/alcada.cpython-313.pyc | Bin 1881 -> 1883 bytes .../__pycache__/pinj_patterns.cpython-313.pyc | Bin 3730 -> 3732 bytes .../__pycache__/tox_blocklist.cpython-313.pyc | Bin 1506 -> 1508 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 408 -> 410 bytes .../__pycache__/mcp_mapper.cpython-313.pyc | Bin 4692 -> 4694 bytes .../__pycache__/models.cpython-313.pyc | Bin 3144 -> 3146 bytes .../__pycache__/resolver.cpython-313.pyc | Bin 4713 -> 4715 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 618 -> 620 bytes .../judges/__pycache__/judge.cpython-313.pyc | Bin 41125 -> 41127 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 225 -> 227 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 5404 -> 5406 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 233 -> 235 bytes .../prompts/__pycache__/aluc.cpython-313.pyc | Bin 6529 -> 6531 bytes .../prompts/__pycache__/csi.cpython-313.pyc | Bin 1332 -> 1334 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 8035 -> 8037 bytes .../prompts/__pycache__/rqlt.cpython-313.pyc | Bin 1165 -> 1167 bytes .../prompts/__pycache__/vctn.cpython-313.pyc | Bin 626 -> 628 bytes .../llm/__pycache__/__init__.cpython-313.pyc | Bin 476 -> 478 bytes .../llm/__pycache__/base.cpython-313.pyc | Bin 1623 -> 1625 bytes .../profile_resolver.cpython-313.pyc | Bin 9322 -> 9324 bytes .../llm/__pycache__/providers.cpython-313.pyc | Bin 46978 -> 46980 bytes .../structured_output.cpython-313.pyc | Bin 7459 -> 7461 bytes .../llm/__pycache__/types.cpython-313.pyc | Bin 1274 -> 1276 bytes .../mcp/__pycache__/__init__.cpython-313.pyc | Bin 473 -> 475 bytes .../mcp/__pycache__/client.cpython-313.pyc | Bin 18426 -> 18428 bytes .../mcp/__pycache__/models.cpython-313.pyc | Bin 2136 -> 2138 bytes .../mcp/__pycache__/registry.cpython-313.pyc | Bin 4740 -> 4742 bytes .../__pycache__/tool_policy.cpython-313.pyc | Bin 5685 -> 5687 bytes .../__pycache__/tool_router.cpython-313.pyc | Bin 14189 -> 14191 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1135 -> 1137 bytes .../long_term_extractor.cpython-313.pyc | Bin 2513 -> 2515 bytes .../long_term_memory.cpython-313.pyc | Bin 6839 -> 6841 bytes .../long_term_models.cpython-313.pyc | Bin 1748 -> 1750 bytes .../long_term_store.cpython-313.pyc | Bin 29186 -> 29188 bytes .../message_history.cpython-313.pyc | Bin 8359 -> 8361 bytes .../summary_memory.cpython-313.pyc | Bin 12510 -> 12512 bytes .../__pycache__/summary_store.cpython-313.pyc | Bin 9520 -> 9522 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 214 -> 216 bytes .../__pycache__/identity.cpython-313.pyc | Bin 3465 -> 3467 bytes .../__pycache__/session.cpython-313.pyc | Bin 2708 -> 2710 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1394 -> 1396 bytes .../__pycache__/code_mapper.cpython-313.pyc | Bin 21447 -> 21449 bytes .../__pycache__/context.cpython-313.pyc | Bin 7386 -> 7388 bytes .../__pycache__/event_bus.cpython-313.pyc | Bin 3632 -> 3634 bytes .../guardrail_events.cpython-313.pyc | Bin 2050 -> 2052 bytes .../__pycache__/ic_events.cpython-313.pyc | Bin 625 -> 627 bytes .../__pycache__/judge_events.cpython-313.pyc | Bin 1410 -> 1412 bytes .../langgraph_telemetry.cpython-313.pyc | Bin 4963 -> 4965 bytes .../__pycache__/llm_advisors.cpython-313.pyc | Bin 2937 -> 2939 bytes .../__pycache__/noc_contract.cpython-313.pyc | Bin 5074 -> 5076 bytes .../__pycache__/noc_otel.cpython-313.pyc | Bin 6321 -> 6323 bytes .../__pycache__/observer.cpython-313.pyc | Bin 5104 -> 5106 bytes .../__pycache__/otel.cpython-313.pyc | Bin 3411 -> 3413 bytes .../streaming_events.cpython-313.pyc | Bin 1834 -> 1836 bytes .../__pycache__/telemetry.cpython-313.pyc | Bin 52427 -> 52429 bytes .../__pycache__/token_cost.cpython-313.pyc | Bin 8690 -> 8692 bytes .../workflow_events.cpython-313.pyc | Bin 3329 -> 3331 bytes .../oci/__pycache__/__init__.cpython-313.pyc | Bin 211 -> 213 bytes .../oci/__pycache__/auth.cpython-313.pyc | Bin 3243 -> 3245 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 219 -> 221 bytes .../__pycache__/oracle_store.cpython-313.pyc | Bin 45931 -> 45933 bytes .../__pycache__/sqlite_store.cpython-313.pyc | Bin 15018 -> 15020 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 454 -> 456 bytes .../__pycache__/renderers.cpython-313.pyc | Bin 4317 -> 4319 bytes .../rag/__pycache__/__init__.cpython-313.pyc | Bin 709 -> 711 bytes .../embedding_provider.cpython-313.pyc | Bin 6516 -> 6518 bytes .../__pycache__/graph_store.cpython-313.pyc | Bin 5527 -> 5529 bytes .../rag/__pycache__/ingest.cpython-313.pyc | Bin 12580 -> 12582 bytes .../__pycache__/kbdb_service.cpython-313.pyc | Bin 10011 -> 10013 bytes .../__pycache__/rag_service.cpython-313.pyc | Bin 10897 -> 10899 bytes .../__pycache__/vector_store.cpython-313.pyc | Bin 18655 -> 18657 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 220 -> 222 bytes .../session_repository.cpython-313.pyc | Bin 8589 -> 8591 bytes .../src/agent_framework/routing/__init__.py | 7 + .../__pycache__/__init__.cpython-313.pyc | Bin 425 -> 649 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 2296 -> 2691 bytes .../__pycache__/continuity.cpython-313.pyc | Bin 15933 -> 15935 bytes .../enterprise_router.cpython-313.pyc | Bin 61971 -> 64784 bytes .../__pycache__/models.cpython-313.pyc | Bin 2446 -> 2448 bytes .../__pycache__/multi_intent.cpython-313.pyc | Bin 0 -> 13086 bytes .../pending_topics.cpython-313.pyc | Bin 0 -> 5570 bytes .../agent_framework/routing/config_loader.py | 7 + .../routing/enterprise_router.py | 56 ++++- .../agent_framework/routing/multi_intent.py | 203 ++++++++++++++++++ .../agent_framework/routing/pending_topics.py | 101 +++++++++ .../__pycache__/__init__.cpython-313.pyc | Bin 348 -> 350 bytes .../__pycache__/agent_runtime.cpython-313.pyc | Bin 198228 -> 198240 bytes .../transaction_parameters.cpython-313.pyc | Bin 19573 -> 19585 bytes .../sse/__pycache__/__init__.cpython-313.pyc | Bin 211 -> 213 bytes .../sse/__pycache__/events.cpython-313.pyc | Bin 9587 -> 9589 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 218 -> 220 bytes .../__pycache__/supervisor.cpython-313.pyc | Bin 4943 -> 4945 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 878 -> 880 bytes .../__pycache__/graph.cpython-313.pyc | Bin 1415 -> 1417 bytes .../input_contract.cpython-313.pyc | Bin 8790 -> 8792 bytes .../__pycache__/models.cpython-313.pyc | Bin 7669 -> 7671 bytes .../__pycache__/registry.cpython-313.pyc | Bin 2361 -> 2363 bytes .../__pycache__/repository.cpython-313.pyc | Bin 2714 -> 2716 bytes .../__pycache__/runtime.cpython-313.pyc | Bin 36152 -> 36154 bytes .../__pycache__/tool_executor.cpython-313.pyc | Bin 1966 -> 1968 bytes scripts/sync_pending_topics_to_tuning.py | 109 ++++++++++ templates/agent_template_backend/app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../docs/TRANSACTION_SEMANTIC_CONFIRMATION.md | 2 +- .../app/state.py | 2 + .../app/workflows/agent_graph.py | 23 +- .../docs/TRANSACTION_SEMANTIC_CONFIRMATION.md | 2 +- ...test_route_stickiness_transaction_shift.py | 14 ++ ...it_abandonment_and_structural_candidate.py | 5 + tests/unit/test_multi_intent_planner.py | 66 ++++++ tests/unit/test_pending_topics_isolation.py | 53 +++++ tests/unit/test_readme_feature_catalog.py | 39 ++++ tests/unit/test_tuning_pending_topics_sync.py | 38 ++++ 223 files changed, 1121 insertions(+), 21 deletions(-) create mode 100644 docs/features/en/16_multi_intent_planning.md create mode 100644 docs/features/pt-BR/16_multi_intent_planning.md create mode 100644 libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc create mode 100644 libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc create mode 100644 libs/agent_framework/src/agent_framework/routing/multi_intent.py create mode 100644 libs/agent_framework/src/agent_framework/routing/pending_topics.py create mode 100644 scripts/sync_pending_topics_to_tuning.py create mode 100644 tests/unit/test_multi_intent_planner.py create mode 100644 tests/unit/test_pending_topics_isolation.py create mode 100644 tests/unit/test_readme_feature_catalog.py create mode 100644 tests/unit/test_tuning_pending_topics_sync.py diff --git a/README.md b/README.md index 37427cf0..9fe147f4 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ O framework deve concentrar os motores reutilizáveis: - Telemetria Langfuse/OpenTelemetry. - Analytics IC/NOC/GRL. - MCP Tool Router. +- Planejamento multi-intent e fila persistente de `pending_topics`. - Cache. - RAG genérico. @@ -230,6 +231,8 @@ Input Guardrails ↓ Enterprise Router ou Supervisor ↓ +Planejamento multi-intent / pending_topics + ↓ Agente especializado ↓ MCP Tool Router / RAG / Cache / LLM @@ -11486,6 +11489,8 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo | Um exemplo antigo não compila | documentação histórica versus API atual | [Validação README x Código](docs/developer/pt/VALIDATION_README_ALIGNMENT.md) | | Preciso criar um agente novo do zero | fluxo completo | [`README.md`](README.md) | | Preciso saber onde colocar uma nova feature | arquitetura e boundaries | [Arquitetura e Conceitos](docs/developer/pt/01_architecture_and_concepts.md) | +| Duas solicitações na mesma mensagem acionam apenas uma intent | decomposição multi-intent, `pending_topics`, allowlist de tools | [Planejamento Multi-Intent](docs/features/pt-BR/16_multi_intent_planning.md) | +| O agent secundário repete a transação ou confirmação primária | isolamento de `active_transaction`, `mcp_tools` e estado operacional | [Planejamento Multi-Intent](docs/features/pt-BR/16_multi_intent_planning.md) | ### 34. Funcionalidades Avançadas @@ -11561,9 +11566,31 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo **Use quando:** um `COER`/guardrail de entrada gera mensagem genérica, `route=blocked` carrega resultados antigos ou há dúvida sobre a precedência entre input guardrails, routing e tools. +### Guias de features implementadas + +Os manuais `01` a `12` acima organizam os assuntos por área de desenvolvimento. Os guias abaixo documentam cada feature executável individualmente: + +1. [Autenticação](docs/features/pt-BR/01_authentication.md) +2. [Workflow Transacional Determinístico](docs/features/pt-BR/02_deterministic_transactional_workflow.md) +3. [Composição por LLM Solicitada pelo Domínio](docs/features/pt-BR/03_domain_requested_llm_composition.md) +4. [RAG Solicitado pelo Domínio](docs/features/pt-BR/04_domain_requested_rag.md) +5. [Memória de Longo Prazo](docs/features/pt-BR/05_long_term_memory.md) +6. [Regressão Offline de Workflow](docs/features/pt-BR/06_offline_workflow_regression.md) +7. [Pause e Resume de Workflow](docs/features/pt-BR/07_pause_resume_workflow.md) +8. [Route Stickiness](docs/features/pt-BR/08_route_stickiness.md) +9. [Replay em Interrupções de Voz](docs/features/pt-BR/09_voice_interruption_replay.md) +10. [Recuperação de Erro em Workflow](docs/features/pt-BR/10_workflow_error_recovery.md) +11. [Clarificação](docs/features/pt-BR/11_clarification.md) +12. [Idempotência Durável](docs/features/pt-BR/12_durable_idempotency.md) +13. [Estados Transacionais Dinâmicos](docs/features/pt-BR/13_dynamic_transaction_states.md) +14. [Replay Após Finalização](docs/features/pt-BR/14_post_finalization_replay.md) +15. [Guardrails de Retrieval e Tools](docs/features/pt-BR/15_retrieval_tool_guardrails.md) +16. [Planejamento Multi-Intent e Pending Topics](docs/features/pt-BR/16_multi_intent_planning.md) + +O [índice de features em português](docs/features/pt-BR/README.md) deve ser atualizado sempre que um novo guia for adicionado. + ### Tutorial principal [`README.md`](README.md) continua sendo a referência para o passo a passo completo: `arquitetura → configuração → criação do agente → registro → estado → routing → tools → MCP → identidade → execução → testes → gateways → memória → RAG`. - diff --git a/README_en.md b/README_en.md index 38df2bf4..b877ea44 100644 --- a/README_en.md +++ b/README_en.md @@ -126,7 +126,7 @@ Details the evaluation and certification framework, including evaluation archite Defines the platform operational readiness and SRE model, including managed components, health checks, readiness, SLOs, metrics, dashboards, alerts, runbooks, incident management, capacity planning, and production checklists. -### [IMPORTANT: Disclaimer Auth and Security](specs/Disclaimer%20Auth%20and%20Security_EN.md) +### [IMPORTANT: Disclaimer Auth and Security](specs/Disclaimer_Auth_and_Security_EN.md) Oracle Cloud Infrastructure Security Best Practice Recommendations. @@ -186,6 +186,7 @@ The framework should concentrate the reusable engines: - Langfuse/OpenTelemetry Telemetry. - Analytics IC/NOC/GRL. - MCP Tool Router. +- Multi-intent planning and the persistent `pending_topics` queue. - Cache. - Generic RAG. @@ -231,6 +232,8 @@ Input Guardrails ↓ Enterprise Router or Supervisor ↓ +Multi-intent planning / pending_topics +↓ Specialized agent ↓ MCP Tool Router / RAG / Cache / LLM @@ -11388,6 +11391,8 @@ The content of this folder should be treated as an additional framework extensio | Logs/traces do not correlate the same agent | labels, IDs, observability mapping | [Observability](docs/developer/en/11_observability_persistence_and_operational_readiness.md) | | Historical example no longer compiles | stale docs vs current API | [README Alignment Validation](docs/developer/en/VALIDATION_README_ALIGNMENT.md) | | Need to create a new agent from scratch | complete flow | [`README_en.md`](README_en.md) | +| Two requests in one message trigger only one intent | multi-intent decomposition, `pending_topics`, tool allowlist | [Multi-Intent Planning](docs/features/en/16_multi_intent_planning.md) | +| A secondary agent repeats the primary transaction or confirmation | isolation of `active_transaction`, `mcp_tools`, and operational state | [Multi-Intent Planning](docs/features/en/16_multi_intent_planning.md) | ### 34. Advanced Features @@ -11463,6 +11468,29 @@ The content of this folder should be treated as an additional framework extensio **Use it when:** `COER`/input guardrails generate generic messages, `route=blocked` carries stale results, or you need to reason about precedence between input guardrails, routing, and tools. +### Implemented feature guides + +The `01` through `12` manuals above organize documentation by development area. The guides below document each executable feature individually: + +1. [Authentication](docs/features/en/01_authentication.md) +2. [Deterministic Transactional Workflow](docs/features/en/02_deterministic_transactional_workflow.md) +3. [Domain-Requested LLM Composition](docs/features/en/03_domain_requested_llm_composition.md) +4. [Domain-Requested RAG](docs/features/en/04_domain_requested_rag.md) +5. [Long-Term Memory](docs/features/en/05_long_term_memory.md) +6. [Offline Workflow Regression](docs/features/en/06_offline_workflow_regression.md) +7. [Pause and Resume Workflow](docs/features/en/07_pause_resume_workflow.md) +8. [Route Stickiness](docs/features/en/08_route_stickiness.md) +9. [Voice Interruption Replay](docs/features/en/09_voice_interruption_replay.md) +10. [Workflow Error Recovery](docs/features/en/10_workflow_error_recovery.md) +11. [Clarification](docs/features/en/11_clarification.md) +12. [Durable Idempotency](docs/features/en/12_durable_idempotency.md) +13. [Dynamic Transaction States](docs/features/en/13_dynamic_transaction_states.md) +14. [Post-Finalization Replay](docs/features/en/14_post_finalization_replay.md) +15. [Retrieval and Tool Guardrails](docs/features/en/15_retrieval_tool_guardrails.md) +16. [Multi-Intent Planning and Pending Topics](docs/features/en/16_multi_intent_planning.md) + +The [English feature index](docs/features/en/README.md) must be updated whenever a new feature guide is added. + ### Main tutorial [`README_en.md`](README_en.md) remains the complete step-by-step guide. diff --git a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py index 84d9f49e..6ec11159 100644 --- a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py +++ b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py index ed17e04b..bb2d550a 100644 --- a/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py +++ b/Tuning-Performance/Authentication/agent_template_backend_authentication/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py index 84d9f49e..6ec11159 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py index ed17e04b..bb2d550a 100644 --- a/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/External_Guardrails_Judges/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py index 84d9f49e..6ec11159 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py index ed17e04b..bb2d550a 100644 --- a/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/Tuning-Performance/Normal/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py index fc680927..ef4f2c70 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/state.py @@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py index fc292459..5c5f9a28 100644 --- a/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py +++ b/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py index 1d82e35c..de25c1e8 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/state.py @@ -38,6 +38,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py index e22cf447..b950fa4e 100644 --- a/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Transaction_Evidence/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py index 69555085..0b4da3e9 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/state.py @@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py index e22cf447..b950fa4e 100644 --- a/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py +++ b/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/docs/features/en/16_multi_intent_planning.md b/docs/features/en/16_multi_intent_planning.md new file mode 100644 index 00000000..fbc3c122 --- /dev/null +++ b/docs/features/en/16_multi_intent_planning.md @@ -0,0 +1,27 @@ +# Multi-Intent Planning + +`MultiIntentPlanner` recognizes independent requests in one utterance and +builds a plan constrained to the intents, agents, and tools declared in +`config/routing.yaml`. It does not execute operations or let the LLM invent a +sequence: the router selects the primary operation and the domain workflow +continues to own the transaction. + +## Configuration and execution + +No additional configuration is required. The planner uses the intents already +declared in `routing.yaml`, selects the single transactional operation from +`tool_policies.yaml`, and persists the remaining requests as `pending_topics`. + +Known read-only topics execute through their own agent. A second mutation stays +pending for its own confirmation, while an unknown clause becomes off-context. + +The router resolves clauses from registered intent keywords and routes the +transactional operation first. The graph drains read-only pending topics before +output supervision. During an open transaction, +`yes, and send me the invoice` confirms the transaction and records the +secondary request instead of causing an intent shift. + +For multiple side-effecting actions, implement a transactional coordinator +that persists the plan, confirms it, executes idempotently, and records partial +failure or compensation. See `tests/unit/test_multi_intent_planner.py`. No +special Tuning-Performance template is needed because the behavior is native. diff --git a/docs/features/en/README.md b/docs/features/en/README.md index 4cf22497..e74d579c 100644 --- a/docs/features/en/README.md +++ b/docs/features/en/README.md @@ -17,3 +17,4 @@ Documentation for the main `agent_framework_oci` features. - [Dynamic Transaction States](13_dynamic_transaction_states.md) - [Post Finalization Replay](14_post_finalization_replay.md) - [Retrieval / Tool Guardrails](15_retrieval_tool_guardrails.md) +- [Multi-Intent Planning](16_multi_intent_planning.md) diff --git a/docs/features/pt-BR/16_multi_intent_planning.md b/docs/features/pt-BR/16_multi_intent_planning.md new file mode 100644 index 00000000..92700152 --- /dev/null +++ b/docs/features/pt-BR/16_multi_intent_planning.md @@ -0,0 +1,46 @@ +# Planejamento Multi-Intent + +O `MultiIntentPlanner` reconhece solicitações independentes na mesma mensagem e +cria um plano limitado às intents, agents e tools declarados em +`config/routing.yaml`. O planejador não executa operações nem permite que o LLM +invente uma sequência: o roteador escolhe a operação primária e o workflow do +domínio continua responsável pela transação. + +## Configuração + +Não existe configuração adicional. O planejador usa as intents do `routing.yaml` +e lê `tool_policies.yaml` para priorizar automaticamente a única operação +transacional. Pedidos secundários são persistidos em `pending_topics`. + +- intent conhecida e read-only: executada pelo agent correspondente; +- intent conhecida e transacional: mantida pendente para confirmação própria; +- trecho sem capability correspondente: respondido como `off_context`. + +Consultas secundárias com `execute` podem ser executadas pelo agent correspondente +e consolidadas. Uma segunda mutação é marcada como `defer`, pois exige um +workflow transacional coordenador. + +## Fluxo de execução + +1. O `EnterpriseRouter` separa a entrada em cláusulas e resolve cada uma pelas + keywords das intents cadastradas. +2. `tool_policies.yaml` identifica a operação transacional e a torna primária. +3. A primeira operação é roteada normalmente para seu agent/workflow. +4. O `agent_graph` compõe as mensagens públicas antes do supervisor de saída. +5. Durante uma confirmação, uma frase como `sim e me manda a segunda via` + confirma a transação aberta e registra a solicitação secundária, sem causar + troca indevida de intent. + +## Operações mutáveis + +Para executar mais de uma ação com efeito colateral, implemente um workflow +transacional coordenador. Ele deve persistir o plano, pedir confirmação quando +necessário, executar cada passo com idempotência e registrar sucesso parcial, +falha e compensação. O planejador fornece o contrato; a política de negócio e a +orquestração permanecem no domínio. + +## Referência executável + +Veja os testes genéricos em `tests/unit/test_multi_intent_planner.py`. Como a +feature é padrão do runtime, não existe uma cópia especial do +`agent_template_backend` em `Tuning-Performance`. diff --git a/docs/features/pt-BR/README.md b/docs/features/pt-BR/README.md index 3654d7d7..41b7813d 100644 --- a/docs/features/pt-BR/README.md +++ b/docs/features/pt-BR/README.md @@ -17,3 +17,4 @@ Documentação das principais features do `agent_framework_oci`. - [Estados Transacionais Dinâmicos](13_dynamic_transaction_states.md) - [Replay Após Finalização](14_post_finalization_replay.md) - [Guardrails de Retrieval e Tools](15_retrieval_tool_guardrails.md) +- [Planejamento Multi-Intent](16_multi_intent_planning.md) diff --git a/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt b/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt index ddcd57f4..a54b9a88 100644 --- a/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt +++ b/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt @@ -201,6 +201,8 @@ src/agent_framework/routing/config_loader.py src/agent_framework/routing/continuity.py src/agent_framework/routing/enterprise_router.py src/agent_framework/routing/models.py +src/agent_framework/routing/multi_intent.py +src/agent_framework/routing/pending_topics.py src/agent_framework/runtime/__init__.py src/agent_framework/runtime/agent_runtime.py src/agent_framework/runtime/transaction_input.py diff --git a/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc index ce1e9f932ef9681c3ac6d82b0b3abcf39917f394..df6d85885c4db3dc4b4db7900e4e3bef1f668a63 100644 GIT binary patch delta 31 lcmbQuJeQgKGcPX}0}!ZuS~QXSJ!8Sd->OXUMw1m7s{xO935Wmy delta 28 icmbQsJe!&OGcPX}0}$NKoIjEKJ!9U)->Q?97^?wy@d+RR diff --git a/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc index 807caa308a05696a980d0132b0f4a131280a78af..cb5548522a75b83c8b0af853f355fbe06649b711 100644 GIT binary patch delta 34 ocmcaDa94o)GcPX}0}!ZuTC|Z{g_*HvvH`OyQ@qjUP-Zq(0H^H;od5s; delta 31 lcmcaBa9e=;GcPX}0}$NKoWGGR delta 31 lcmccQdcl?ZGcPX}0}$NKoWGIVjhV4thO30(jH diff --git a/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc index 91e70cbde5248387d855fa5a5b8f9450e8b82efd..b1c9b6240599a23f8c411c1d1fdae8b20191d8b2 100644 GIT binary patch delta 36 qcmbQ-$vCx>k^3_*FBbz4sC-(qkvpA@v0!pBn<`Vh(dHR!zik1*uL}ME delta 33 ncmbQ*$vC-_k^3_*FBbz4+|Hc8kvpA@F>i7)o9gCSY`<**sly7E diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc index fb49d21249dc76d215aae01ae8558e99a0dfe131..e898c3f011cbde695b04dafb3a7c7e7b29e6ff32 100644 GIT binary patch delta 33 ncmZ3?vXq7UGcPX}0}!ZwTC|Zni;=Nvav7s4Q@qjS*^C_kmQ)Ex delta 30 kcmZ3=vY3VYGcPX}0}$NKoWGGfi;=Nnav7uQ-T(jq diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc index e981f8015b7461b23cb310b3750aa1817929347d..e712d2f94ce14c3df605ed1231b504cfb4ca64ee 100644 GIT binary patch delta 34 pcmX>md_tJ}GcPX}0}!ZwTC|aSITK^+7 delta 31 mcmX>hd`y`8GcPX}0}$NKoWGHKITK^^!4&kB)2x0&L diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc index c229aea4baf9f9a73d6e22a6ce2670e9f4207c3e..ae1fbc0b0fd370fb6d0ce008b147ff6dc8add9c9 100644 GIT binary patch delta 34 pcmcb>cZrYtGcPX}0}!ZwTC|b-5EEnd}APP|c diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc index 1283c1078a048d6fc08587964b5efe9576b4165c..d410915717efbdbab5af2c73d209618a51b5bc6f 100644 GIT binary patch delta 34 ocmX@+al(W9GcPX}0}!ZwTC|b7jFGWraucH}Q@qjU<&5){0KA6^oB#j- delta 31 lcmX@%am<7JGcPX}0}$NKoWGH~jFGWvaucKK=9P@|l>nq>3Nruz diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc index 696a9cad26ad465dc634876eed1211932254ec94..cd2fd5384b3d0fc6bba921d72a57088a676ef8bd 100644 GIT binary patch delta 36 qcmbQ)$vC@{k^3_*FBbz4sD4_sk(-f)v2HRiiz-vR(Pkr-bO!*oVF^$G delta 33 ncmbQ;$vCrH4iQ)*D delta 30 kcmZo+X=362%*)Hg00g%)=Wpb;XJl-e?9He;xrlKR0CuDZF8}}l diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc index 1de30648167f05abed817b4765865c9ce59c33a3..96ac00f6f13dd55bb718f8516c065ea33b6a5fc4 100644 GIT binary patch delta 34 ocmbQjH;s?`GcPX}0}!ZwTC|bdfr+tkvJaCgQ@qjULZ-zm0GfCS2mk;8 delta 31 lcmbQnH-(S;GcPX}0}$NKoWGIVfr+txvJaE$=3=JBEC7Zk2%i7| diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc index 97d50e4236dee604530cce4ce7fa2211fa43e604..ca72ccc1cd5492474dc7441f0cfe195ad06a9812 100644 GIT binary patch delta 36 rcmZqK%GkD*k^3_*FBbz4sD4_sk$Vy|W6R`u%&JWBMw<^a9}EKk&c_R+ delta 33 ocmZqM%Gk1%k^3_*FBbz4+|Hc8k$Vy|W7Fh$%&MD@G9L^B0JQ82I{*Lx diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc index d79f3db27efb638aca926455b195f2889595e41d..51cf17eb41f00ad7b97aa40fad86b310ed64c5fe 100644 GIT binary patch delta 34 pcmbQsH=mFDGcPX}0}!ZwTC|aSBNJoij8|}31I*L delta 28 icmbQrJeisMGcPX}0}$NKoIjEKF=N@p_o|b{80!Ibp$PZ@ diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc index dcb6ac636e5ded731cd3f54b9857dac3c217e476..7203cc94d9632e32a87df7cbdd3113d7a1359dcf 100644 GIT binary patch delta 34 ocmX?Bb)t&?szyJUM delta 31 lcmZqUYvJSm%*)Hg00g%)=Wpb;Vqz?w?8cn+a delta 31 lcmeCx?bhY~%*)Hg00g%)=Wpbm&(2sjc{RK0=1c6m`2duq3J?GQ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc index bbcd9dec39f20898c3cf9973fd5f7d13cc57b385..120a77d969e24519857b93394c065c1dd02cd309 100644 GIT binary patch delta 34 pcmdmCw9AP5GcPX}0}!ZwTC|aS8WUsft<8 delta 31 lcmbQvJ(ZjLGcPX}0}$NKoWGG2lM~{ diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc index f0d1c0a3d27435f2caf378446bccd912f15a1bc8..156fbc620ead88022047c72125c743736f322e66 100644 GIT binary patch delta 33 ocmbQuK9`;QGcPX}0}!ZwTC|b-5))(1mi`M()qNyj%=Gp!#XiMs8(h#?Hz5%&JWBMw>&JkEa0u&;ttI delta 33 ocmbPxg>m*3M()qNyj%=Ga65DUMs8(h#`ekj%&MEin2)Cc0Jm=nbpQYW diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc index 5b1b72e52b354a656b251737e60f951f5b1f4999..8a9eeb090c2f945892de4fc4a5e9cde5221d82fe 100644 GIT binary patch delta 36 rcmey^%=EdLiTg7zFBbz4sD4_sk$W2xWAo%gOsY)rMw_2Aan%C=;z0}c delta 33 ocmey|%=EFDiTg7zFBbz4+|Hc8k$W2xW8>sQOsbn-GI7-d0LCv1jQ{`u diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc index d425540198f6eade2b8d2ebf7be4c147ccd9cc5e..30c732eca1ac3114de36c7312a8e1af2f9abec3b 100644 GIT binary patch delta 30 kcmcb{c!QDqGcPX}0}!ZuS~QW{n6Z4Ky(&|@(ZrM@0EK1qk2w4CC diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc index 4cb1188fa51191c52b35d170e580f72eebe78810..8bb7929ced31af1ba6d1d9ce2a0b4e3b1223d309 100644 GIT binary patch delta 34 ocmZ2%w$zOKGcPX}0}!ZuTC|bdk(se}vM;kLQ@qjUBIb?a0H}8fm;e9( delta 31 lcmZ2#w%CmOGcPX}0}$NKoWGIVk(sf2vM;mh<`U+O;sBEH2`~Tv diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 2b8e87945d35c337ab7babf068b3f2cf7df812f3..a3a2721b27af4685fcba81a1b81ecb425e399900 100644 GIT binary patch delta 34 ocmcbceLtJ~GcPX}0}!ZuTC|ZnN{g|4a+;PZQ@qjUZml400LtD9#Q*>R delta 31 lcmcbgeK(u?GcPX}0}$NKoWGGfN{g{{a+;Rv=3cEJZUC`Q3S9sI diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc index d3caec6dbfb738b0929b76c5a8609d64cfda0701..39c367612555acede75fa09b14e65901d65350fe 100644 GIT binary patch delta 30 kcmdnQw1tWLGcPX}0}!ZuS~QWniLr8GpDI(l(ZtR50EA%)cK`qY delta 27 hcmdnOw26uPGcPX}0}$NKoIjDfiLrcQpX$V|^#E@&2@e1O diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc index 35c314ee5b3c43c701992554fdf2b30ea8c9c7fa..ea291b6bfe6fa10d5092a217732cb30db7e1dafa 100644 GIT binary patch delta 34 ocmZpXYLnvr%*)Hg00b(Z7H#A{!N}M=`3j>dQ@qjUpN!nx0H}}(1poj5 delta 31 lcmZpZYLVjp%*)Hg00g%)=Wpab!N}M+`3j@z=HHCm+yId~37r4{ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index 9d7ec1f172bbf2e2aad34879f80a2271fd0da34f..25eb6a003bbf0ea27723bedd32dfd60d309e9c0b 100644 GIT binary patch delta 33 ocmcc1eV?2AGcPX}0}!ZwTC|b-BMW2otC834Z_p diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index 37a3be28a91e1f83bedc1b86c176591245db0216..e250fed35e05e50c58006ba0db1c1f779b5187a3 100644 GIT binary patch delta 36 rcmcaPlkxUUM()qNyj%=Gp!#XiM(#dN#`ejxI8~YAjW+M+lyU|D-A)T- delta 33 ocmcaTlkw(EM()qNyj%=Ga65DUM(#dN#@5NRI8`?vo delta 31 mcmZ3gw^)z+GcPX}0}$NKoWGHKGBab<|a{vGU diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index 7e6aac259fe338f6d76810aa3eafceb6211cbe8e..60934f023578ee4c6ef3399a8db4ded2992a7add 100644 GIT binary patch delta 36 rcmaF1it)uNM()qNyj%=Gp!#XiMs7D&#+J!Jtg1}$Mw`o7-}?gq-Kh(k delta 33 ocmaE{it*VhM()qNyj%=Ga65DUMs7D&#-_O8u0K*a$l5`1} delta 30 kcmbQh+Q-WMnU|M~0SInq&fmzL&&XIaxtdXR@*>7}0DamCF8}}l diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index 15d43753ba9b2a84fac78b18ea10f89de07b65fa..9c9966ad3ac704c91f6be2a78ac65db81aae0b3f 100644 GIT binary patch delta 34 ocmX@gbDW3!GcPX}0}!ZwTC|b7oSCs_Y^qH0Mw{E%wk-kx-a-r4 delta 33 ocmdmYiD}OzChpI?yj%=Ga65DUM($8H#GcPX}0}$NKoWGGffRnLhatx>H=4MVqCIFDG2*UsX diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 6d79bde398d68a35c24d2dbb1c7aa9026a1d3f55..a580e3921007d9f25d609a8dffbc4d1566b3543f 100644 GIT binary patch delta 34 ocmeAb?ic3%%*)Hg00gR^7H#C7!^YS;c?Fv)Q@qjUvup>L0Hx{*LjV8( delta 31 lcmeAd?iJ?#%*)Hg00g%)=Wpbm!^YS=c?Fy5=JRX^m;jGh3EBVv diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index 4dfda6f51ec3c74a81caeb6a090689a7a76ade6e..a101e62981864cc6f79678debc4bf381e8f63d3e 100644 GIT binary patch delta 34 ocmZn^Y!>AH%*)Hg00gR^7H#BKV`ZE=*@#t@Dc)#vII9*j0F#jjJpcdz delta 31 lcmZn`Y!u}F%*)Hg00g%)=WpazV`ZE?*@#tjb0n)4GXQ_>2h;!n diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index 3865d2ccb10837dc2e49b8e7d8222ec4cf2e0d39..49a250a36a0c67e94703133a0c9aace9a549a344 100644 GIT binary patch delta 34 pcmexr`rVZKGcPX}0}!ZwTC|b-6dPmvob6AG^GcPX}0}!ZwTC|b-FAHPGWKLF9rg)>xdaS~n0I>QAIRF3v delta 31 lcmX>sb5Mr+GcPX}0}$NKoWGI#FAHPaWKLGq%?7N(oB)}$2+#lk diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index 0366ecf2ff1b033d61a5370c37a5af783c9465e4..bbf2cb75c6361041fe4a06cf15d2936c56c83a3e 100644 GIT binary patch delta 34 ocmcaEdsCMCGcPX}0}!ZwTC|b7fP=Afat((nQ@qjUg&byV0JVk*>i_@% delta 31 lcmca9dtH|MGcPX}0}$NKoWGH~fP=Apat(*-=EWRlYyh3?34#Cs diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc index 8435c79964d1aef42f5e0a215f4da581bfb3f895..54882cd316d3f48566cc1b3a7c222653ae0337ae 100644 GIT binary patch delta 34 ocmbPdwZMw|GcPX}0}!ZwTC|Znii5FZavFy!Q@qjUZjQ+!0IVem+W-In delta 31 lcmZ2rHP4FsGcPX}0}$NKoWGGfii5FjavF!~=3b7;A^?@T3331c diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index 9f11ca3c5ffb34802a11f668a2e4595be112873a..738d967fceea86e6ee16a2e231cda257e75efbf2 100644 GIT binary patch delta 34 ocmdnzdcc+YGcPX}0}!ZwTC|bdkeRW4vMsYJQ@qjUB<2`-0I@O&dH?_b delta 31 lcmX@$y3dvSGcPX}0}$NKoWGIVkeRV{vMsaf<`m`_c>tQ+2@wDQ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc index 401fe78747fa6765ff6d5e4aa7f1adf40cf4abf0..c9af6e39fb44117422a773e189ff237acc0f9cd7 100644 GIT binary patch delta 34 pcmcbXb0dfQGcPX}0}!ZwTC|Zno|AFnx6F86Q0079p3l#tW delta 31 lcmcbSb1jGaGcPX}0}$NKoWGGfo|CbEayF;x=1H8#bO5iJ3akJC diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc index 69494e732a706979de39af6aa3fb89d8c196b8f9..367c823ff27e4bf9340dd13bd6d19c5b15ed948f 100644 GIT binary patch delta 34 ocmeyG^gW6DGcPX}0}!ZwTC|Z{Re-T=vY~(~Q@qjUFadc*0LBvu=>Px# delta 31 lcmeyK^fih5GcPX}0}$NKoWGGt<8 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index 154eeca3942098426f9ca4fe30ef342edc03b541..efbe6b8bb5e5cec23f67b8cecb46c1beee6ee78b 100644 GIT binary patch delta 34 pcmaE_`%#zsGcPX}0}!ZwTC|aS3kzf4}4Da0++; diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index 8030b6c4ea3971ee3e84eb9d42e108adcc0a82a9..84b19f8fc632b12e12e5880d556d473f166a327f 100644 GIT binary patch delta 34 ocmZ1*zdoM(GcPX}0}!ZwTC|bdSC4Vtxb$a@Y0K6LtEdT%j delta 31 lcmZ1>a diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index 77fcc99b89f5e444dd02db55de42f74f1ea2a5a3..d378484121290287d5894995442c142ee54ee1d7 100644 GIT binary patch delta 34 pcmdn1uwQ}uGcPX}0}!ZwTC|b-G#_LCtdMvL1nV$)B delta 30 kcmbQmHG_-$GcPX}0}$NFoWGI#FEeBJWKI^<$p$R10E8?F3jhEB diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index ec16fea2b8a44c072efec444d2b5f4f5834eab22..59f0fb27f7e0b11f21a34daa8e6110c9bf04317d 100644 GIT binary patch delta 33 ncmbQvHxEi%500J^ve8UO$Q delta 31 lcmca?ebJixGcPX}0}$NFoWGGfScb8Ga=eV{<~A8$MgXF|39pF diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index 6414806b2764b7848fa2bfbf5996ca95571402cf..c947a70ed1e9f72e1b02ceb2a1be01b36030aeac 100644 GIT binary patch delta 34 ocmaFi_{x#{GcPX}0}!ZwTC|b7N}jQ6a+|y=Q@qjU)$+EC0LHiqy8r+H delta 31 lcmaFm_`;FnaY2InGcPX}0}!ZwTC|b-HWy>>}xlbHAz0iVDK^8f$< delta 31 lcmZ3)zJQ(kGcPX}0}$NFoWGGfk%@8Y*6^rg)>tmsr;V0H;R^cmMzZ delta 30 lcmZqV{m;YwnU|M~0SNA7&fmzrkd?7(@>*8a$yZp{0sxNx3J?GQ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index 8e6db06e0b92999e4f7823b53b09e1d19f3fdaec..a6892a37332d2dfb9538660fe8e0ed57017978af 100644 GIT binary patch delta 34 ocmZoy?^Ngh%*)Hg00gR^7H#By&BoX@`8%5`Q@qh;8TM=*0Irh?2LJ#7 delta 31 lcmeBFZ&&C3%*)Hg00eh3=WpbG&BoX<`8%8HW;yn39srfr37-G} diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc index c11fe045c7868d0df77f418b26a915d5260541d6..04fcca85114c1126c0f04e11a8114e79cd9c30c0 100644 GIT binary patch delta 34 ocmdnvzR#WeGcPX}0}!ZwTC|b-D>q};WJVrUrg)>xYCO}#0lR7n6#xJL delta 31 lcmdnzzQ>*WGcPX}0}$NFoWGI#D>q}uWJVs<%^Ez@!~vsH39SGC diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index cc0ecfdaaa98bd87996476b9892c47cd699b882a..ef27b0365afec6e912b037368a4d5c6657630508 100644 GIT binary patch delta 34 ocmZ1!wj_-EGcPX}0}!ZwTC|bdSe$XfWP5Q{rg)>xDdPKt0kPEz1poj5 delta 31 lcmZ1ywkVAIGcPX}0}$NFoWGIVSe&tMvc0(K<}~qr!T_5z37r4{ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index c0af6cba1317f5dbf4a35bca10d653980aee474c..031c6db0c976270a2d1c7c0abf11700c2ffa1735 100644 GIT binary patch delta 34 pcmX>vc~O%4GcPX}0}!ZwTC|b-AS+|{H$Wdi`DKMMW; diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index 5ba6e9b7eff2d1c3b99375ed704cb2d3ea26ad9e..05fe8ace342a6159daf82e4e00498f71338845b3 100644 GIT binary patch delta 34 ocmZpcYL(*t%*)Hg00gR^7H#A{!phh)`2wpdQ@qjUudICR0I1Fi3;+NC delta 31 lcmZpbYL?>u%*)Hg00eh3=Wpab!phh+`2wrz=I^Y0>;REr38Vl3 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index 5131d4d0a6674019fae99a2f6c1953abe000ae87..ec9e2e53aed7f0f425bd6959f2649cc41e440abf 100644 GIT binary patch delta 34 ocmcbnbwi8$GcPX}0}!ZwTC|b-8ardxjiBc>$){3G4s> diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index 65cc96c33437694d3dc1ff285cdc87083bf2df0f..c944a36ec41d93741bb9c5763d5990e3a08551c6 100644 GIT binary patch delta 34 ocmcbo^+1dJGcPX}0}!ZwTC|b-B|BsHGcPX}0}!ZwTC|b-w*X`NWOhMSrg)>xI)aw`0K}OJ5&!@I delta 31 lcmaFv@YI3(GcPX}0}$NFoWGI#w*X`7WOhN-&3b~C`~a(b390}9 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 65e10062714d88d6309bee94fdfcb8be8c039e29..a280463231c8753740771be4e95e7adc4bf24d19 100644 GIT binary patch delta 33 ncmbQrGM$C{GcPX}0}!ZwTC|Znf|0Rjatfm=Q@qjSPR1Spk+=y9 delta 30 kcmbQvGL?nNonc|H$-{jiK3;?Vr3YP!? delta 31 mcmbOtJ4u%NGcPX}0}$NFoWGHK1s7xgx{w$Lj0jiq`I{*Lx delta 31 lcmaFD{fL|UGcPX}0}$NFoWGG2-5%n diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index 02cfd33cdafdcfe487e67a09a4586fcf63201aff..8e60226c1d282f93653ef861ac61487f5594b5ae 100644 GIT binary patch delta 31 lcmbQiJd2t8GcPX}0}!ZwS~QXSDP!ftkE%@ZMw7)Dn*feQ33UJf delta 28 icmbQmJcF70GcPX}0}$NFoIjEKDP#G>kE)X;7@Gig?+Fb6 diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc index ebfe626848cb13201d3f47d16724e2a5a3f9f34d..15e00436bcfa4dbb93b6cc1a06d6331467cef250 100644 GIT binary patch delta 34 ocmcbja!rN%GcPX}0}!ZwTC|bdiJ7r_vLCZ5Q@qjUV&;c@0I>=Q{{R30 delta 31 lcmcbnaz%yvGcPX}0}$NFoWGIViJ7r-vLCbR=2GT|d;prp36}r> diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc index c7643ed88d9fcb0c5f955f74a732bc4e3e9146fc..a9e0d412c2d723ecb824ad3d9331487a6823b590 100644 GIT binary patch delta 34 ocmX>haY};wGcPX}0}!ZwTC|b7oSCtFax=3kQ@qjU70e4b0I;D7BLDyZ delta 31 lcmX>laYBOoGcPX}0}$NFoWGH~oSCt7ax=5)=2grKH~^Wu3A+FQ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc index a6cfed472a36d72cc8cedaa012ff71d6150f488c..667d8c2e1341cb33f8bde81f9549ba4d860976d3 100644 GIT binary patch delta 34 ocmaE<@>+%aGcPX}0}!ZwTC|Znn3=J1ay+vtQ@qjU7UrjX0Jw1qUH||9 delta 31 lcmaE@@=}HSGcPX}0}$NFoWGGfn3=JBay+x@<~HW1d;p+aMs9l+#*)e2EUHZLMw<&*Oy>Xq&uI#? delta 33 ocmZ2}kZI{bChpI?yj%=Ga3^#AMs9l+#-hpIEUKG}SWM>t0JgmfOaK4? diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc index 2f95a868653bc463b2becd4da52c383697130dbf..f29e56b8a016920672c23e851f91ee98a640bba8 100644 GIT binary patch delta 30 kcmaFJ_?VIVGcPX}0}!ZuS~QW{n6YW1y(&|@(ZrNm0Ek@(?EnA( delta 27 hcmaFN_>htNGcPX}0}$NFoIjDx8Z2J~0IQk_*#H0l delta 31 lcmbQIHAjp4GcPX}0}$NFoWGI#J2PX`WM&rC%~~v91OSyg3331c diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index cf731d8f5ed168b5936e3199898001ee582f79c7..4d82da4c3025135e6dbe5be50c7be83e1986b712 100644 GIT binary patch delta 30 kcmaFK_?nUXGcPX}0}!ZuS~QW{n6Yc3y(&|@(ZrNi0E&eP4FCWD delta 27 hcmaFO_>z(PGcPX}0}$NFoIjDk6mK-SU*Z!0n+pl| delta 30 kcmZoRZZzip%*)Hg00eh3=WpbWlVEI_oF$<;d7{K80ELtZkN^Mx diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc index 615c00918c4526c7ee9de6095f341ed089b7250d..f1dc7156ba6270a4527bc3246cc49ffd2362f9ea 100644 GIT binary patch delta 33 ncmdnOwT+AWGcPX}0}!ZuTC|Z{m4&f&vLTBqQ@qjSFqV4&m5m7x delta 30 kcmdnSwS|lOGcPX}0}$NFoWGGxJNQ-$006~13mX6c delta 31 mcmaEA_t=j6GcPX}0}$NFoWGH~osY3&@?<{M&Aa$k3jhGD2@13T diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc index 3de4edefcd0aaa883d0a02dad6c1c0c9e7e158ee..bc32f1015af446afa6bea6f5e58b84a437e8e601 100644 GIT binary patch delta 33 ncmeC>?C0eE%*)Hg00b(Z7H#A%U}kKaT*Iu&6mK+nA@dsmlf((E delta 30 kcmeC@?B(SC%*)Hg00eh3=WpaLU}kKYT*It7c`@@F0Dlt+MF0Q* diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc index 3250734d187c42e76dd191b399bf8bc92674dd2d..16fc593ab54c46d725b1d9245f5b54a430157b91 100644 GIT binary patch delta 33 ncmeyw@`Z)_GcPX}0}!ZuTC|Znk&&@&axSAPQ@qjSNsJEwsq6|Z delta 30 kcmeyu@`;7}GcPX}0}$NFoWGGfk&&@waxSCl#0EkQoe*gdg diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc index 1cc721696070506232a4c3bdbc2104aa3c2474c3..467a89078ec5fe8d66bc74dbfa294754fba67ec7 100644 GIT binary patch delta 34 pcmcc4bCZYrGcPX}0}!ZwTC|b-3?pN~b&}zzS#p diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc index a8df724167e12f1a46531c7d27cb8158ca4c1537..37d1d0c09bb8e2ffac4b2d088dbc788fd8392bd5 100644 GIT binary patch delta 34 ocmaFm@y3JuGcPX}0}!ZwTC|b7nwha?ayzpsQ@qjUHO%`I0LFF;4gdfE delta 31 lcmaFk@ydhyGcPX}0}$NKoWGH~nwha`ayzr?=5@^b6acMj3Z(!5 diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc index 55667e21d4a1c237a62147b39d68e3f00203f89b..31f23ae75f731381df0b18cfde43e091c32d9a72 100644 GIT binary patch delta 36 qcmZpA&(!jsiTg7zFBbz4sD4_skz19Ov2?N_t145x(dIB#olOAGAPS8D delta 33 ncmZp9&(!puiTg7zFBbz4+|Hc8kz19Ov3Rl}tLo+mR-H`%ww?+k diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc index 51f486a6969f6a23d417ecc607343c89d4a949fc..97ff627d28cd7c7e6c3480c87f26731f390a31da 100644 GIT binary patch delta 34 pcmZ2%wbY9HGcPX}0}!ZwTC|aS4>M!!M!+!atpI6Q@qjUmCS*R0J!lA5C8xG delta 31 lcmeyv`HPeLGcPX}0}$NKoWGH~f|)UYatpKS=GDxBi~ylC38w%6 diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index d4bfbb4b0e7b682562881676864f1043fbb418dc..72a2ad3ec4e124d2fc443ccdcf20b70f2868f289 100644 GIT binary patch delta 33 ncmcb~e4Cm3GcPX}0}!ZwTC|Z{i;=NpvKgZ)Q@qjSXvR7KoJ0uW delta 30 kcmcc3e3O~`GcPX}0}$NKoWGG&-kaGk^3_*FBbz4sD4_sk$WLCW6|Wb%&JWBMw>4&Yq$Ua diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc index 2c1f9120711765e55ee48eb043d255067cf4df44..c29938b2d1363f7faf16de53025c0d4c4990a031 100644 GIT binary patch delta 34 ocmca1a7%#uGcPX}0}!ZwTC|bdgNd^=4z(5X$avhT@Q@qjU#Y~fg0k8845C8xG delta 31 lcmdn4vsH)tGcPX}0}$NKoWGH~h>5XuavhWE=A}%NgaMfu38w%6 diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc index 78b78233a1eb585942f249c82c2e99522ba014d5..fbc730745e615e9f7b9ab0ed7067034b121d573a 100644 GIT binary patch delta 34 pcmaEx_dbvNGcPX}0}!ZwTC|aSKNn;9Hq+}Q45{` diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc index eb070c7e05299ca1835aeb4e4b30ab498154fae0..aaf6786f45868c3e0327dbfef2c05a58c615d099 100644 GIT binary patch delta 33 ocmaFQ@sWf3GcPX}0}!ZwTC|aSE;D2K^VGcPX}0}!ZwTC|bdiixp)vKx~sQ@qjUY$hKG0ISdmMF0Q* delta 31 lcmdmKy4{rfGcPX}0}$NKoWGIViixpyvKy1?=3FKp2>_Ms2;2Yw diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc index 2193260b049aaeae9d42d8a436cb8a5696a7bc07..8af63eb24dc8d3bc298366374588642cf33db458 100644 GIT binary patch delta 34 ocmcb@dySX-GcPX}0}!ZwTC|b7i;=N@@-#+Org)>xyBQ@}0kUNY)&Kwi delta 31 lcmcb{dxe+#GcPX}0}$NKoWGH~i;=N*@-#-(&3hRoSpk~q32guX diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc index 5aa19260dc11bc3b250ca1c1af8b687daf1d2d88..43b94734c3f92bbb670a23022b4d979e860d0440 100644 GIT binary patch delta 36 rcmZpA!r1bJk^3_*FBbz4sD4_sk^3;L-{k=S;A9Lg delta 33 ocmZp9!r1hLk^3_*FBbz4+|Hc8k^3R diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc index 8d6318d37ea9c56faa41e7709032b734574327ad..ff0d9324af503f91cee26ebf73bdb30fe37d7332 100644 GIT binary patch delta 34 ocmZ4PxYCjPGcPX}0}!ZwTC|b-A0uPkWG*IErg)>x`b-sa0JE+Mxc~qF delta 31 lcmZ4KxZIKZGcPX}0}$NKoWGI#A0uPUWG*Jv&4x@BasZrA2~Yq4 diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc index eb0f8b4106542bee35f4a3e88d9182285bd33b8b..ad97060a12c9cab32379472a2ccff71c9ce99b04 100644 GIT binary patch delta 34 pcmcbY_#lz{GcPX}0}!ZwTC|b-C^KX2qk2w4CC diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc index 0916ec78ad8068ac37f86ce23a6bcdb7998472ab..470d06179324a7185399ee7f5c91dc571b9822ae 100644 GIT binary patch delta 34 ocmeB_?w02M%*)Hg00b(Z7H#A{%E(wg`68n#Q@qjUZ;X?<0jeK%*)Hg00g%)=Wpab%E(wc`68q0<{ylcxdD>y3LF3c diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc index 68e68130b9cb3a2f7b6a7cb165866deb156b3453..b8dec2db152fdd1f734b6f60ff1ccfc2e5639536 100644 GIT binary patch delta 34 ocmbOtI!%=OGcPX}0}!ZuTC|aynVGR{GC#8_Q@qh;6XpbV0F%lH761SM delta 31 lcmbOxIz^QGGcPX}0}$NKoWGHqnVGR}|Q@qjSE|wPns)q_I delta 30 kcmeyu^@)r7GcPX}0}$NKoWGGfl7+Emaw?1J9bGcPX}0}!ZwTC|b-G#g{>+WEK`xrg)>xnk?*G0IO99@c;k- delta 31 lcmdlavq6UYGcPX}0}$NKoWGI#2Qy>sWEK|H&Dt#NTmY1`2#5dx diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc index aa4a5022d33fe949dbc8b87baf13fdd6db4726fc..b1947d21dea0b1b79913ae35dbc1621732ce08d5 100644 GIT binary patch delta 34 ocmZn?Xc6H4%*)Hg00gR^7H#BKVq|QatjDOz6mPUSm~lND0FvXcFN5%*)Hg00g%)=WpazVq|QYtjDOjIh1id8vuW*2sHoz diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc index 10a61af95e5a4a4452abfdde49a3103a1116b0f5..27ca857fb71f23be39ed24ed2b36dc29f0192f03 100644 GIT binary patch delta 33 ocmey!@|lJEGcPX}0}!ZwTC|aS5+h^%PU<0Irq_h5!Hn delta 30 lcmey&@{xu6GcPX}0}$NKoWGHK5+h^n(T=2^|0c diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc index 6ac05fd6c7a57e4a2962b0957b3eed3c7255d5ce..f953f75d4d843669eee55782d730300a91205871 100644 GIT binary patch delta 34 pcmdmJxY>~VGcPX}0}!ZwTC|aS9W!Iy<}8(+V>H diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc index 4961d2f537277d2974c0f3fcff9b14ee92ceb959..54969b6ed70478c9543f1631e8f123185ff6be51 100644 GIT binary patch delta 34 pcmcaCbybS{GcPX}0}!ZwTC|aS6&qvK0G_4^EdT%j delta 31 lcmZ3(w~CMZGcPX}0}$NKoWGIVfRV9fvJIo^=48f`tN@B92*dyY diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc index 6e14600a6939321ee95ea4ecc1ee7b2f5d0f8f70..1403568adbaa51b2d8d3dfb882f1f3932b7eba33 100644 GIT binary patch delta 36 rcmX>-llkmSX710tyj%=Gp!#XiMs5Xm#`?)R?5a%hMw*llk;aX710tyj%=Ga65DUMs5Xm#@fj`?5dlC*{hBM0JRhfZU6uP diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc index 676cc7dedb94dc6df122e5f6509cbfe3a8ba0116..65093788144fde6d31cecf249f51fb767bb8f999 100644 GIT binary patch delta 34 ocmez5{Kc93GcPX}0}!ZwTC|bdjFYipvJ_?63AX?M diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc index 8cfe8a502b587de0b19ae61825a16d21cb1cd2e3..0895aa48d59a818db7febed3b1f36988da16b933 100644 GIT binary patch delta 34 ocmZpaYL?>u%*)Hg00gR^7H#DAWn^rf9LcE46mPV-j<{9 delta 31 lcmZpcYLw#s%*)Hg00g%)=WpcpWn^rg9LcDGcPX}0}$NFoWGGfg^{stasi|2=IM;0mH?$Q38?@8 diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc index c23bb7da8e2bd7b33448f87330d44e367274a8b1..f3d2b878835cde264dc191337dbb2fa05929b243 100644 GIT binary patch delta 33 ncmX@ce1e(#GcPX}0}!ZwTC|ayhmo;%vIL_lQ@qh+TgFZRl(PtR delta 30 kcmX@Xe2kgGcPX}0}$NKoWGHqlZmltvM7`4WE-Y#0D$5M0RR91 diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc index fdf9cd31ec7deb781ec22a5aea1191f4319b017b..a7defeb1aec60ab8897d4f05d69f2814f9faf40f 100644 GIT binary patch delta 34 ocmexj^v#I-GcPX}0}!ZwTC|bdmyxkv9}0KP#AmjD0& delta 31 lcmexn^u>t#GcPX}0}$NKoWGIVmyxk%awMbb<_5-Z;sBc diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc index 46bd04b43ef8158bb634d05edad9fbd369c33f94..853b2c2e7017d7851f931ddef5a1e26fa7c3c550 100644 GIT binary patch delta 34 ocmbOjIysd4GcPX}0}!ZwTC|bdm4&f{GcPX}0}$NKoWGIVm4&f%av+Q9<_Z>RH2{>~2+RNg diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc index 5cc1572641a040630b657dca0af7d38936184766..bcb6f09dca2d29042e2d7222885ff80544fa68d3 100644 GIT binary patch delta 36 rcmcaVk@4X~M()qNyj%=Gpz>+aM(*cKj1`kVGpRDg8*P?gKJE$t<6aB* delta 33 ocmaDjk@5aSM()qNyj%=Ga65DUM(*cKjAfHQGpTNtVm|H)0LFR?j{pDw diff --git a/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc index 2ba82514c00d0326d5771de7ac4ffe21b2f24e48..f8fa52ddc84229b509cc290c330ecc4fb25ae2e2 100644 GIT binary patch delta 30 kcmcb^c#o0$GcPX}0}!ZuS~QW{n6Y-Ey(&|@(ZrMr0EYw!*#H0l delta 27 hcmcb|c!!buGcPX}0}$NKoIjDS6ov2P|6iX%S$%7ew>zfrDHjNbY_u_s%VOugf3x7*b*kd9-t+!@;m4XTt-3j z5ubSvvJ@@9Tgl*0PR{Y*?-Ren+@v(CDoPiy5kcV3b3#Ie9?uWSZQKsMDvv%xFmoVd5nDQoChNqS011{NW}u=X z77)P-BG`b0pCon4(!>(xFVzY%sPd6NIG!6-Wf~Lzz?<3>Z`xG?}Vwof4C?%M*)Ibd&RQ z3ld8*lX6lON>YE*?TaGX9i|spj5i1YiOFl(4cSb9nu|;) zKVesfF?l&;83iY6bL41%3@+jU5kepW>>duA-29Z%oK(9aYoHh?{EMw8@8U4w|H8$@ NsPvVKh0z488UW1@e+>Wt delta 147 zcmZn`{UONrnU|M~0SNA7&d)l)Jdsa=v2LPzgf?$5rxZgLOA${nUoaP##T(2Y%nfGo z1@i{;fLZ+MESdrnw|ro%p1hP%b@LgGcPX}0}!ZwTC|b7f{C$eato6xQ@qjUl}uBt0ml#u{{R30 delta 31 lcmdmAv$ux(GcPX}0}$NFoWGH~f{C$Wato8{=G9D7tO2eI3YP!? diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc index aa65e5cbfd4533e0400df0d9ed67e4e9b3648647..1c415ff5a6f59e8391dda3168f1e2aaf4cc06448 100644 GIT binary patch delta 11979 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%0AtKySFehhIs{Du&BaZ!pw@m$I_X z>t52dym=9BA0ybFtR=ZXPlN(b(gFVGg;`=MSYBMColE*GiiKa?t(vt2Y2}i9#bXk? z5nE+5skj>VBUyu&oP7bjWLr%odMT5S{x;}5T$)9Wgtz29n=f;fcF*Bv(Fv~|ei(bW z_2r*QD{n^O7Jf}1Z;jo8DK7KZs-{Yf!+2kbLyG-k-@*j;ZG!Ctw-Ve&!2S4k*!F6w za(9^v&%c@r2VR{a*2Ye~Y8Ewmv_IY7rNg$>2FQLbSKI@&uQiFc!N_YlLva2vv@@VMmRE65B!@yGr z>AmVs3*G985=PmnYJFD{d?gnf@C;93-LhZ8H;!h)nYU74^-*(Bijdb|=m)0Lu9Ua;e68XP+6ND_HSinkXYi&tY6JXW{~b1KJlsbyZBM>UaazbMxM z-#qF-DJ^u3nV@mZA$cXdXQYpC-yPG#kH)5FlZz_3^nH zVRul*-2`Y*%)kqTFbhVvC{8Fv!Ys-XGmWo-iFMYf%sK9oRagh^(R?k4C^s>x4Z~nQ zF{RvspFr>mqi>53-Fbs7`o8vA2Ak$9vs{4lvp~B}!d^ZO_n&CgI-IXKDvm(O>zhoa zLVC#=oseGmX$3s``d`tp?|dVy$zOyG<8qVO&k}1iZ3gT_Ea&etRU;1*%{RF^>h0&m zQY3}n*7$lb6k!)%{AOJCYwD!7m!vH$UX zir0AYKE+m1-Ss5Any4(I_|~+N4>}zS*cycKFB^QQ`SRcB)XkFIIJGfx2c8CD8Hteml{%$Ks1RFHSbQctu|gKY71Y^{NDa zdA~}W3%O@9A^U>?*Vn4zZtyrZaB=Z9@XHTMRmUVa`oZjY@$%CfzFwUB``3$i!850) zs@|g33&4CPiB~bJkXmPS+{Y6>`unLCH3L{9?6ThEwh`LC0EoD)v@}YQu zsDz|l*+Y3g?hNrt?<9(+@k1J?csR=`Hjx`4O3M@XL*SF*8a^B1dW~0+Xe|DPfamkc z6EFGOPo|9|Nl{s&4y2GIW|6*{Rfj%vFki=Kedqnui$(Nzc+3UbO$4Nc8j-#HF7tZ5aS^orc$8Ao+RMy96$B36@7GEup9QtIV8DKWlG@CJA(QV#37B=spJ z6I+qd~{VX8P@oF_0 zrw7feL{0ANww%)%B2McCTk>fGCyhd8$!QZO&4MxaV+$uYso?7KcX_Kd#*h2OI>Z}A zNt5!iS<)mEASG%_&uXn2tD+RC;>wqeK40$;&hh|ijPajxaFTWX;l4ilX~cEZiH*?s zd1Ve?VCY&<)((Vw0{9KjNb=kp&G5kIWug^6_`F)n?d*@-H^O#V1LTEWG Wv>X$f;KYRj+cgf+`k_F``}j{VR!3I= diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc index dffeae85abe43d0b7ebeb9704a5bdaaf5677b285..3829534f76ef217219a80694f091d7cc2a1d2595 100644 GIT binary patch delta 34 ocmeAZo*>NqnU|M~0SHt-E!xPf#>`kY*@#({Dc)#vII}q$0F}K6MgRZ+ delta 31 lcmbOr+$YTanU|M~0SNA7&fmzb#>`kU*@#(nb0o7l8vuej2i^bx diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f701a67c4b43b45c850b640d3611aa74e51c6e19 GIT binary patch literal 13086 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 literal 0 HcmV?d00001 diff --git a/libs/agent_framework/src/agent_framework/routing/config_loader.py b/libs/agent_framework/src/agent_framework/routing/config_loader.py index 6ab4bd04..95a9740e 100644 --- a/libs/agent_framework/src/agent_framework/routing/config_loader.py +++ b/libs/agent_framework/src/agent_framework/routing/config_loader.py @@ -33,3 +33,10 @@ def load_state_policies(path: str) -> list[RouterStatePolicy]: def load_router_defaults(path: str) -> dict[str, Any]: data = load_routing_config(path) return data.get("router", {}) + + +def load_multi_intent_config(path: str) -> dict[str, Any]: + """Backward-compatible technical limits; no section is required.""" + data = load_routing_config(path) + value = data.get("multi_intent", {}) + return value if isinstance(value, dict) else {} diff --git a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index e622918f..ac060a6e 100644 --- a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -6,10 +6,12 @@ import re import unicodedata from typing import Any -from .config_loader import load_intents, load_router_defaults, load_state_policies +from .config_loader import load_intents, load_multi_intent_config, load_router_defaults, load_state_policies +from .multi_intent import MultiIntentPlanner from .continuity import SemanticRouteContinuity from .models import IntentDefinition, RouteDecision, RouterStatePolicy from agent_framework.llm.structured_output import parse_json_object +from agent_framework.mcp.tool_policy import ToolPolicyRegistry from agent_framework.runtime.transaction_parameters import extract_transaction_parameters, parse_transaction_confirmation from agent_framework.workflows.input_contract import ( expected_input_reprompt, @@ -44,6 +46,16 @@ class EnterpriseRouter: self.intents: list[IntentDefinition] = load_intents(self.config_path) self.state_policies: list[RouterStatePolicy] = load_state_policies(self.config_path) self.defaults = load_router_defaults(self.config_path) + tool_policies = ToolPolicyRegistry(getattr(settings, "TOOL_POLICIES_PATH", None)) + transactional_tools = { + name for name, policy in tool_policies.policies.items() + if policy.operation_type == "transactional" or policy.require_confirmation + } + self.multi_intent_planner = MultiIntentPlanner( + self.intents, + load_multi_intent_config(self.config_path), + transactional_tools=transactional_tools, + ) self.fallback_agent = self.defaults.get("fallback_agent", "billing_agent") self.intent_shift_threshold = float(self.defaults.get("confidence_threshold", 0.7)) self.transaction_confirmation = dict(self.defaults.get("transaction_confirmation") or {}) @@ -727,6 +739,27 @@ class EnterpriseRouter: # intent/agente ativos devem prevalecer sobre a route stickiness. Isso # evita manter um fluxo read-only (por exemplo, tracking) quando o usuário # muda para uma ação transacional (por exemplo, devolução). + planner = getattr(self, "multi_intent_planner", None) + multi_intent_plan = planner.plan(str(text)) if planner is not None else None + if multi_intent_plan is not None: + primary = multi_intent_plan.operations[0] + decision = RouteDecision( + route=primary.agent, + agent=primary.agent, + intent=primary.intent, + confidence=1.0, + reason="Plano multi-intent validado a partir de intents configuradas.", + method="keyword", + metadata={ + "multi_intent": True, + "multi_intent_plan": multi_intent_plan.model_dump(mode="json"), + }, + domain=primary.domain, + mcp_tools=primary.tools, + ) + await self._emit(decision, state) + return decision + keyword_candidate = self._route_by_keyword(text) active_agent = str(state.get("active_agent") or "").strip() previous = state.get("route_decision") or {} @@ -799,7 +832,13 @@ class EnterpriseRouter: """ tx_status = str(state.get("transaction_status") or "").strip().upper() if tx_status == "AWAITING_CONFIRMATION": - confirmation = parse_transaction_confirmation(text) + active = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + previous = state.get("route_decision") if isinstance(state.get("route_decision"), dict) else {} + owner_intent = str(active.get("started_from_intent") or previous.get("intent") or state.get("intent") or "") + compound_plan = self.multi_intent_planner.plan_confirmation_secondary( + text, primary_intent=owner_intent, primary_agent=state_decision.agent + ) + confirmation = "confirm" if compound_plan is not None else parse_transaction_confirmation(text) source = "deterministic" classifier_output = None raw_classifier = None @@ -828,6 +867,12 @@ class EnterpriseRouter: "transaction_confirmation_decision": confirmation, "transaction_confirmation_source": source, } + if compound_plan is not None: + state_decision.metadata.update({ + "multi_intent": True, + "multi_intent_confirmation": True, + "multi_intent_plan": compound_plan.model_dump(mode="json"), + }) if source == "semantic": state_decision.metadata.update({ "transaction_confirmation_classifier_output": classifier_output, @@ -1129,7 +1174,12 @@ class EnterpriseRouter: kw_normalized = kw.casefold() strategy = None # Exato primeiro para preservar o comportamento existente. - if kw_normalized in normalized: + keyword_tokens = self._keyword_tokens(kw) + text_tokens = self._keyword_tokens(text) + if ( + (len(keyword_tokens) == 1 and keyword_tokens[0] in text_tokens) + or (len(keyword_tokens) > 1 and kw_normalized in normalized) + ): strategy = "exact" elif self._ordered_keyword_match(kw, text): strategy = "ordered_tokens" diff --git a/libs/agent_framework/src/agent_framework/routing/multi_intent.py b/libs/agent_framework/src/agent_framework/routing/multi_intent.py new file mode 100644 index 00000000..27354e17 --- /dev/null +++ b/libs/agent_framework/src/agent_framework/routing/multi_intent.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +import re +import unicodedata +import uuid +from typing import Any, Literal + +from pydantic import BaseModel, Field + +from .models import IntentDefinition + + +class PlannedIntent(BaseModel): + operation_id: str + intent: str + agent: str + domain: str | None = None + tools: list[str] = Field(default_factory=list) + source_text: str = "" + disposition: Literal["execute", "inform", "unsupported", "defer"] = "execute" + message: str | None = None + status: Literal["pending", "completed", "failed", "skipped"] = "pending" + + +class MultiIntentPlan(BaseModel): + plan_id: str + operations: list[PlannedIntent] + execution_mode: Literal["primary_with_secondary", "sequential"] = "primary_with_secondary" + status: Literal["planned", "running", "completed", "partially_completed", "failed"] = "planned" + + +class MultiIntentPlanner: + """Deterministic planner constrained by routing.yaml. + + The planner never invents agents or tools. It recognizes independently + configured intents in conjunction-separated clauses. Execution remains + owned by the agent/workflow runtime. + """ + + _SPLIT = re.compile(r"\s*(?:,|;|\be\b|\bmas\b|\btamb[eé]m\b)\s*", re.I) + _CONFIRM_WITH_REMAINDER = re.compile( + r"^\s*(?:sim|s|pode|pode\s+sim|isso\s+mesmo|claro|ok|confirmo)\s*(?:,|;|\be\b)\s*(.+)$", + re.I, + ) + + def __init__( + self, + intents: list[IntentDefinition], + config: dict[str, Any] | None = None, + *, + transactional_tools: set[str] | None = None, + ): + self.intents = [item for item in intents if item.enabled] + self.config = dict(config or {}) + # Multi-intent é capacidade padrão. O bloco opcional só permite limites + # técnicos/overrides; sua ausência nunca desabilita a detecção. + self.enabled = bool(self.config.get("enabled", True)) + self.max_operations = max(2, int(self.config.get("max_operations", 4))) + self.transactional_tools = set(transactional_tools or set()) + + @staticmethod + def _normalize(value: str) -> str: + text = unicodedata.normalize("NFKD", str(value or "")) + return "".join(ch for ch in text if not unicodedata.combining(ch)).casefold() + + def _intent_for_clause(self, clause: str) -> IntentDefinition | None: + normalized = self._normalize(clause) + clause_tokens = set(re.findall(r"[\w]+", normalized, flags=re.UNICODE)) + matches: list[tuple[int, int, IntentDefinition]] = [] + for intent in self.intents: + for keyword in intent.keywords: + token = self._normalize(keyword) + keyword_tokens = re.findall(r"[\w]+", token, flags=re.UNICODE) + matched = bool( + keyword_tokens + and ( + (len(keyword_tokens) == 1 and keyword_tokens[0] in clause_tokens) + or (len(keyword_tokens) > 1 and token in normalized) + ) + ) + if matched: + matches.append((intent.priority, -len(token), intent)) + if not matches: + return None + matches.sort(key=lambda item: (item[0], item[1])) + return matches[0][2] + + def _is_transactional(self, operation: PlannedIntent) -> bool: + return any(tool in self.transactional_tools for tool in operation.tools) + + @classmethod + def _looks_like_request(cls, clause: str) -> bool: + normalized = cls._normalize(clause) + return bool(re.search( + r"\b(quero|preciso|mandar|manda|mande|enviar|envia|envie|gerar|gera|gere|" + r"alterar|altera|altere|mudar|muda|mude|consultar|consulta|consulte|" + r"cancelar|cancela|cancele|explicar|explica|explique|mostrar|mostra|mostre|" + r"falar|comprar)\b", + normalized, + )) + + @staticmethod + def _off_context(clause: str, operation_id: str) -> PlannedIntent: + return PlannedIntent( + operation_id=operation_id, + intent="off_context", + agent="", + source_text=clause, + disposition="unsupported", + message=f"Sobre {clause}, não consigo ajudar nesta jornada.", + status="pending", + ) + + def plan(self, text: str) -> MultiIntentPlan | None: + if not self.enabled: + return None + clauses = [part.strip(" .") for part in self._SPLIT.split(str(text or "")) if part.strip(" .")] + operations: list[PlannedIntent] = [] + unknown_clauses: list[str] = [] + seen: set[str] = set() + for clause in clauses[: self.max_operations * 2]: + intent = self._intent_for_clause(clause) + if intent is None: + if self._looks_like_request(clause): + unknown_clauses.append(clause) + continue + if intent.name in seen: + continue + seen.add(intent.name) + operations.append(PlannedIntent( + operation_id=f"op-{len(operations) + 1}", intent=intent.name, + agent=intent.agent, domain=intent.domain, tools=list(intent.mcp_tools), + source_text=clause, + )) + if not operations or len(operations) + len(unknown_clauses) < 2: + return None + # Regra técnica geral: uma mutação que exige o contrato transacional é a + # operação primária. Não é necessário cadastrar cada par de intents. + transactional = [item for item in operations if self._is_transactional(item)] + if len(transactional) == 1: + primary_operation = transactional[0] + operations.sort(key=lambda item: item is not primary_operation) + for operation in operations[1:]: + if self._is_transactional(operation) and operation.disposition == "execute": + operation.disposition = "defer" + for clause in unknown_clauses[: max(0, self.max_operations - len(operations))]: + operations.append(self._off_context(clause, f"op-{len(operations) + 1}")) + return MultiIntentPlan( + plan_id=f"mip-{uuid.uuid4().hex}", operations=operations, + execution_mode="primary_with_secondary", + ) + + def plan_confirmation_secondary( + self, text: str, *, primary_intent: str, primary_agent: str + ) -> MultiIntentPlan | None: + """Recognize an explicit confirmation followed by another request. + + The confirmation remains authoritative for the open transaction. The + secondary request is recorded and governed by the same combination + policy instead of turning the whole utterance into an intent shift. + """ + if not self.enabled: + return None + match = self._CONFIRM_WITH_REMAINDER.match(str(text or "")) + if match is None: + return None + clause = match.group(1).strip() + secondary = self._intent_for_clause(clause) + primary = next((item for item in self.intents if item.name == primary_intent), None) + if secondary is not None and secondary.name == primary_intent: + return None + if secondary is None and not self._looks_like_request(clause): + return None + operations = [ + PlannedIntent( + operation_id="op-1", intent=primary_intent, agent=primary_agent, + domain=primary.domain if primary else None, + tools=list(primary.mcp_tools) if primary else [], + source_text="confirmation", status="pending", + ), + (PlannedIntent( + operation_id="op-2", intent=secondary.name, agent=secondary.agent, + domain=secondary.domain, tools=list(secondary.mcp_tools), source_text=clause, + ) if secondary is not None else self._off_context(clause, "op-2")), + ] + if self._is_transactional(operations[1]) and operations[1].disposition == "execute": + operations[1].disposition = "defer" + return MultiIntentPlan( + plan_id=f"mip-{uuid.uuid4().hex}", operations=operations, + execution_mode="primary_with_secondary", + ) + + @staticmethod + def public_messages(plan: dict[str, Any] | MultiIntentPlan | None) -> list[str]: + if plan is None: + return [] + data = plan.model_dump() if isinstance(plan, MultiIntentPlan) else plan + messages = [] + for operation in data.get("operations", []) or []: + message = str(operation.get("message") or "").strip() + if message and message not in messages: + messages.append(message) + return messages diff --git a/libs/agent_framework/src/agent_framework/routing/pending_topics.py b/libs/agent_framework/src/agent_framework/routing/pending_topics.py new file mode 100644 index 00000000..5de65c78 --- /dev/null +++ b/libs/agent_framework/src/agent_framework/routing/pending_topics.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Awaitable, Callable + +from .multi_intent import MultiIntentPlanner + +AgentHandler = Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] + + +@dataclass(frozen=True) +class PendingTopicDrain: + answer: str + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] + + +async def drain_pending_topics( + state: dict[str, Any], + candidate: str, + handlers: dict[str, AgentHandler], +) -> PendingTopicDrain: + """Drain read-only topics and retain anything that cannot finish safely.""" + route = state.get("route_decision") if isinstance(state.get("route_decision"), dict) else {} + plan = route.get("metadata", {}).get("multi_intent_plan") + topics = list(state.get("pending_topics") or ( + plan.get("operations", [])[1:] if isinstance(plan, dict) else [] + )) + remaining: list[dict[str, Any]] = [] + handled = list(state.get("handled_topics") or []) + secondary_answers: list[str] = [] + for topic in topics: + disposition = topic.get("disposition") + if disposition == "defer": + remaining.append(topic) + continue + if disposition != "execute": + handled.append({**topic, "status": "completed"}) + continue + handler = handlers.get(str(topic.get("agent") or "")) + if handler is None: + remaining.append(topic) + continue + source_text = str(topic.get("source_text") or "").strip() + child = { + **state, + "user_text": source_text, + "message_text": source_text, + "sanitized_input": source_text, + "route": topic.get("agent"), + "active_agent": topic.get("agent"), + "intent": topic.get("intent"), + "domain": topic.get("domain"), + "mcp_tools": topic.get("tools") or [], + "route_decision": { + "route": topic.get("agent"), "agent": topic.get("agent"), + "intent": topic.get("intent"), "mcp_tools": topic.get("tools") or [], + "metadata": {"multi_intent_secondary": True}, + }, + "active_transaction": None, + "transaction_status": None, + "transaction_pre_validation": None, + "confirmation_required": False, + "confirmation_received": False, + "selected_tool_call": {}, + "pending_tool_call": {}, + "pending_tool_clarification": None, + "pending_domain_workflow": None, + "missing_parameters": [], + "tool_policy_result": None, + "mcp_results": [], + "available_mcp_tools": topic.get("tools") or [], + "next_state": None, + "workflow_id": None, + "route_bypassed": False, + } + try: + result = await handler(child) + except Exception: + remaining.append(topic) + continue + answer = str((result or {}).get("answer") or "").strip() + if not answer: + remaining.append(topic) + continue + secondary_answers.append(answer) + handled.append({**topic, "status": "completed"}) + + secondary_answers.extend(MultiIntentPlanner.public_messages(plan)) + unique = [] + for answer in secondary_answers: + if answer and answer.casefold() not in candidate.casefold() and answer not in unique: + unique.append(answer) + if unique: + secondary = "\n\n".join(unique) + candidate = ( + f"{secondary}\n\n{candidate.lstrip()}".strip() + if state.get("confirmation_required") + else f"{candidate.rstrip()}\n\n{secondary}".strip() + ) + return PendingTopicDrain(candidate, remaining, handled) diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc index a35920c55239c319b7f9a26329efc678d601cd51..a0fbec33b36e42d8ff815f6d7679383ec17f8df4 100644 GIT binary patch delta 30 lcmcb^bdQPqGcPX}0}!ZuS~QV+En~&RovKXnMicMV0RWMV3Q7O~ delta 27 icmcb|bcc!iGcPX}0}$NKoIjC!Eo0flovITb)Byl`VhQ2^ diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc index 23e52040f9fbf575b364daff0b9504c416890c46..5bb68f33354e72ce5b20de38e8c99aa60b82cd77 100644 GIT binary patch delta 76 zcmcc8!}Fkrhx;=xFBbz4sC-(qk$Vbb8Q2DfIBex5)Mw@8dfZx-_a delta 56 zcmZpi$@p~!Bll-sUM>b8FkxP>k=un?rb<6IuS7p3u_RGHIX|x?u{gdsGq)foGc7YU KWpg<*mnQ(}SQ0+~ diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc index abd217765813067c53de17863ce66d03f5a3e817..cd1f4cc42226925bc65a8022cf8c4b1761ab0ee0 100644 GIT binary patch delta 30 kcmcc2c$Ja+GcPX}0}!ZwS~QW{n6YG{y(&|@(ZrN|0EDOrwg3PC delta 27 hcmcc0c$ty=GcPX}0}$NKoIjDx8Z6$50LStQ9smFU delta 31 lcmezB_1TO2GcPX}0}$NKoWGI#J2PX!WM&rC%~~wpiU6+I3AF$K diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc index 0db928e02476996ce1035f9937179d0c20fd7a42..65b6f01aaa43fc0ea3a54204f1d1bdd163b00477 100644 GIT binary patch delta 30 kcmcb`c!!buGcPX}0}!ZwS~QW{n6Y}Iy(&|@(ZrN80EUYQ(f|Me delta 27 hcmcb^c#DzyGcPX}0}$NKoIjDx`t<8 delta 31 lcmeC=Zs+Fy%*)Hg00eh3=Wpb8V`eO!9K@`;xsq9c2>^kf2lfB} diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc index 1eb76230833fc9cc6342beef525db965132754fb..078f3f4807341d8252a432417369e3c9b35b047f 100644 GIT binary patch delta 34 pcmccSa>IrDGcPX}0}!ZuTC|aS9W!IY_2*#0|2yT3X1>$ delta 31 mcmdljv{Q)tGcPX}0}$NFoWGI#6eDBB$~{BMKw{ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc index 5ab0dd87182b933417206b9d5786abf5bf47de3d..f5f2a86af3e7c7e26b0ded66a2222c64e54cde34 100644 GIT binary patch delta 34 pcmbOwI!BcIGcPX}0}!ZuTC|b-CL?3b+aM($W<#>&Z=%&JWBMw|PXS9Jma&hZOL delta 33 ocmdlri)qI!ChpI?yj%=Ga3^#AM($W<#`4LT%&MCwFt6$a0JZ=M;s5{u diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc index 15f1453ad726f48b72edeacf2e752bddecd388ad..c99df20e8eaeabc70c1572c97f238d87a4e45070 100644 GIT binary patch delta 34 pcmZ3-zk#3oGcPX}0}!ZuTC|aSHX~#G1put23W)#! delta 31 mcmdnMzmA{#GcPX}0}$NFoWGHKHX~#0 str: + if old not in text: + raise RuntimeError(f"Marcador não encontrado em {path}: {old[:60]!r}") + return text.replace(old, new, 1) + + +def update_state(path: Path) -> None: + text = path.read_text(encoding="utf-8") + if " pending_topics: list[dict[str, Any]]" in text: + return + text = replace_once( + text, + " supervisor_results: list[dict[str, Any]]\n", + " supervisor_results: list[dict[str, Any]]\n" + " pending_topics: list[dict[str, Any]]\n" + " handled_topics: list[dict[str, Any]]\n", + path, + ) + path.write_text(text, encoding="utf-8") + + +def update_graph(path: Path) -> None: + text = path.read_text(encoding="utf-8") + if "from agent_framework.routing.pending_topics import drain_pending_topics" in text: + return + text = replace_once( + text, + "from agent_framework.routing.enterprise_router import EnterpriseRouter\n", + "from agent_framework.routing.enterprise_router import EnterpriseRouter\n" + "from agent_framework.routing.pending_topics import drain_pending_topics\n", + path, + ) + text = replace_once( + text, + " } if decision.method == \"continuity\" else {},\n }\n\n async def billing_agent", + " } if decision.method == \"continuity\" else {},\n" + " \"pending_topics\": (\n" + " (decision.metadata or {}).get(\"multi_intent_plan\", {}).get(\"operations\", [])[1:]\n" + " if (decision.metadata or {}).get(\"multi_intent_plan\") else []\n" + " ),\n" + " }\n\n async def billing_agent", + path, + ) + marker = " async def output_supervisor(self, state):" + before, output = text.split(marker, 1) + old = """ if not bool(getattr(self.settings, \"ENABLE_OUTPUT_SUPERVISOR\", True)): + return { + \"output_guardrails_already_applied\": False, + \"supervisor_action\": \"disabled\", + \"supervisor_attempt\": int(state.get(\"supervisor_attempt\", 0)), + } + + candidate = state.get(\"answer\") or \"\" +""" + new = """ drained = await drain_pending_topics( + state, + str(state.get(\"answer\") or \"\"), + { + \"billing_agent\": self.billing.run, + \"product_agent\": self.product.run, + \"orders_agent\": self.orders.run, + \"support_agent\": self.support.run, + }, + ) + candidate = drained.answer + if not bool(getattr(self.settings, \"ENABLE_OUTPUT_SUPERVISOR\", True)): + return { + \"answer\": candidate, + \"final_answer\": candidate, + \"pending_topics\": drained.pending_topics, + \"handled_topics\": drained.handled_topics, + \"output_guardrails_already_applied\": False, + \"supervisor_action\": \"disabled\", + \"supervisor_attempt\": int(state.get(\"supervisor_attempt\", 0)), + } + +""" + output = replace_once(output, old, new, path) + output = replace_once( + output, + " \"output_guardrails_already_applied\": True,\n", + " \"output_guardrails_already_applied\": True,\n" + " \"pending_topics\": drained.pending_topics,\n" + " \"handled_topics\": drained.handled_topics,\n", + path, + ) + path.write_text(before + marker + output, encoding="utf-8") + + +def main() -> None: + tuning = ROOT / "Tuning-Performance" + states = sorted(tuning.glob("**/app/state.py")) + graphs = sorted(tuning.glob("**/app/workflows/agent_graph.py")) + for path in states: + update_state(path) + for path in graphs: + update_graph(path) + print(f"pending_topics sincronizado: {len(states)} states, {len(graphs)} graphs") + + +if __name__ == "__main__": + main() diff --git a/templates/agent_template_backend/app/state.py b/templates/agent_template_backend/app/state.py index 69555085..0b4da3e9 100644 --- a/templates/agent_template_backend/app/state.py +++ b/templates/agent_template_backend/app/state.py @@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/templates/agent_template_backend/app/workflows/agent_graph.py b/templates/agent_template_backend/app/workflows/agent_graph.py index 99bf4c5f..7101db41 100644 --- a/templates/agent_template_backend/app/workflows/agent_graph.py +++ b/templates/agent_template_backend/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -409,6 +410,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -547,14 +552,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -623,6 +642,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/templates/agent_template_backend/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/templates/agent_template_backend_day_zero/app/state.py b/templates/agent_template_backend_day_zero/app/state.py index 7e3f280e..fb4a93bd 100644 --- a/templates/agent_template_backend_day_zero/app/state.py +++ b/templates/agent_template_backend_day_zero/app/state.py @@ -36,6 +36,8 @@ class AgentState(TypedDict, total=False): missing_parameters: list[str] supervisor_plan: dict[str, Any] supervisor_results: list[dict[str, Any]] + pending_topics: list[dict[str, Any]] + handled_topics: list[dict[str, Any]] active_agent: str route_bypassed: bool continuity_signal: dict[str, Any] diff --git a/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py b/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py index 2770df01..3df0833f 100644 --- a/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py +++ b/templates/agent_template_backend_day_zero/app/workflows/agent_graph.py @@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction from agent_framework.guardrails.rail_result import RailResult from agent_framework.judges.judge import JudgePipeline from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.routing.pending_topics import drain_pending_topics from agent_framework.supervisor.supervisor import Supervisor from agent_framework.observability.workflow_events import WorkflowTelemetry from agent_framework.observability.guardrail_events import GuardrailTelemetry @@ -407,6 +408,10 @@ class AgentWorkflow: "reason": decision.reason if decision.method == "continuity" else None, "profile": (decision.metadata or {}).get("continuity_profile"), } if decision.method == "continuity" else {}, + "pending_topics": ( + (decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:] + if (decision.metadata or {}).get("multi_intent_plan") else [] + ), } async def billing_agent(self, state): @@ -545,14 +550,28 @@ class AgentWorkflow: agente gerar `answer` e antes dos judges/persistência, produzindo campos supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver. """ + drained = await drain_pending_topics( + state, + str(state.get("answer") or ""), + { + "billing_agent": self.billing.run, + "product_agent": self.product.run, + "orders_agent": self.orders.run, + "support_agent": self.support.run, + }, + ) + candidate = drained.answer if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)): return { + "answer": candidate, + "final_answer": candidate, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "output_guardrails_already_applied": False, "supervisor_action": "disabled", "supervisor_attempt": int(state.get("supervisor_attempt", 0)), } - candidate = state.get("answer") or "" context = { **(state.get("context") or {}), "tenant_id": state.get("tenant_id"), @@ -621,6 +640,8 @@ class AgentWorkflow: for r in decision.results ], "output_guardrails_already_applied": True, + "pending_topics": drained.pending_topics, + "handled_topics": drained.handled_topics, "guardrail_decisions": state.get("guardrail_decisions", []) + [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])], } diff --git a/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md b/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md index a5508710..8cbc0077 100644 --- a/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md +++ b/templates/agent_template_backend_day_zero/docs/TRANSACTION_SEMANTIC_CONFIRMATION.md @@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP. -Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. +Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação. Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`. diff --git a/tests/test_route_stickiness_transaction_shift.py b/tests/test_route_stickiness_transaction_shift.py index 3ce6135e..099b1bc9 100644 --- a/tests/test_route_stickiness_transaction_shift.py +++ b/tests/test_route_stickiness_transaction_shift.py @@ -2,6 +2,20 @@ import pytest from types import SimpleNamespace from agent_framework.routing.enterprise_router import EnterpriseRouter + + +def test_single_word_keyword_requires_a_complete_token(): + from agent_framework.routing.models import IntentDefinition + + router = object.__new__(EnterpriseRouter) + router.intents = [ + IntentDefinition( + name="cancel", agent="orders_agent", priority=10, + keywords=["cancela"], examples=[], enabled=True, + ) + ] + assert router._route_by_keyword("cancela o pedido") is not None + assert router._route_by_keyword("sim, pode cancelar") is None from agent_framework.routing.models import RouteDecision diff --git a/tests/test_transaction_explicit_abandonment_and_structural_candidate.py b/tests/test_transaction_explicit_abandonment_and_structural_candidate.py index 259cfad6..9a2ea3cb 100644 --- a/tests/test_transaction_explicit_abandonment_and_structural_candidate.py +++ b/tests/test_transaction_explicit_abandonment_and_structural_candidate.py @@ -82,6 +82,11 @@ def _active_state(message): "started_from_intent": "contas_contestation", "parameter_schema": {"valor": {"type": "number"}}, }, + "history": [ + {"role": "user", "content": "não reconheço o Tamboro Mensal, quero tirar da fatura"}, + {"role": "assistant", "content": "Qual é o valor da cobrança que você deseja contestar?"}, + {"role": "user", "content": message}, + ], } diff --git a/tests/unit/test_multi_intent_planner.py b/tests/unit/test_multi_intent_planner.py new file mode 100644 index 00000000..0c720620 --- /dev/null +++ b/tests/unit/test_multi_intent_planner.py @@ -0,0 +1,66 @@ +from agent_framework.routing.models import IntentDefinition +from agent_framework.routing.multi_intent import MultiIntentPlanner + + +def _planner(): + return MultiIntentPlanner([ + IntentDefinition(name="cancel", agent="orders", priority=10, keywords=["cancelar pedido"], mcp_tools=["cancelar_pedido"]), + IntentDefinition(name="invoice", agent="billing", priority=20, keywords=["segunda via"], mcp_tools=["consultar_fatura"]), + ], { + "enabled": True, + }, transactional_tools={"cancelar_pedido"}) + + +def test_builds_validated_plan_and_keeps_primary_first(): + plan = _planner().plan("quero cancelar pedido e preciso da segunda via") + assert plan is not None + assert [item.intent for item in plan.operations] == ["cancel", "invoice"] + assert plan.operations[1].disposition == "execute" + assert plan.operations[1].tools == ["consultar_fatura"] + + +def test_confirmation_with_secondary_does_not_lose_confirmation(): + plan = _planner().plan_confirmation_secondary( + "sim e preciso da segunda via", primary_intent="cancel", primary_agent="orders" + ) + assert plan is not None + assert [item.intent for item in plan.operations] == ["cancel", "invoice"] + assert MultiIntentPlanner.public_messages(plan) == [] + + +def test_transactional_intent_is_primary_even_when_mentioned_second(): + plan = _planner().plan("preciso da segunda via e quero cancelar pedido") + assert plan is not None + assert [item.intent for item in plan.operations] == ["cancel", "invoice"] + + +def test_does_not_plan_single_intent_or_invent_unknown_intent(): + assert _planner().plan("quero cancelar pedido") is None + assert _planner().plan("quero cancelar pedido e saber o clima") is None + + +def test_known_primary_with_actionable_unknown_creates_off_context_topic(): + plan = _planner().plan("quero cancelar pedido e alterar meu CNPJ") + assert plan is not None + assert plan.operations[1].intent == "off_context" + assert plan.operations[1].disposition == "unsupported" + + +def test_confirmation_with_unknown_secondary_keeps_confirmation(): + plan = _planner().plan_confirmation_secondary( + "sim e altere meu CNPJ", primary_intent="cancel", primary_agent="orders" + ) + assert plan is not None + assert plan.operations[0].intent == "cancel" + assert plan.operations[1].intent == "off_context" + + +def test_single_word_keyword_does_not_match_a_longer_word_prefix(): + planner = MultiIntentPlanner([ + IntentDefinition(name="cancel", agent="orders", priority=10, keywords=["cancela"]), + IntentDefinition(name="invoice", agent="billing", priority=20, keywords=["segunda via"]), + ]) + plan = planner.plan("sim, pode cancelar e preciso da segunda via") + assert plan is not None + assert all(item.intent != "cancel" for item in plan.operations) + assert [item.intent for item in plan.operations] == ["invoice", "off_context"] diff --git a/tests/unit/test_pending_topics_isolation.py b/tests/unit/test_pending_topics_isolation.py new file mode 100644 index 00000000..e614cd7a --- /dev/null +++ b/tests/unit/test_pending_topics_isolation.py @@ -0,0 +1,53 @@ +import pytest + +from agent_framework.routing.pending_topics import drain_pending_topics + + +@pytest.mark.asyncio +async def test_secondary_topic_does_not_inherit_primary_transaction_state(): + captured = {} + + async def billing_agent(state): + captured.update(state) + return {"answer": "Aqui está a sua segunda via."} + + state = { + "answer": "Você confirma o cancelamento do Tamboro?", + "sanitized_input": "quero cancelar Tamboro e quero minha segunda via", + "confirmation_required": True, + "active_transaction": {"tool_name": "cancelar_vas_avulso"}, + "transaction_status": "AWAITING_CONFIRMATION", + "selected_tool_call": {"tool_name": "cancelar_vas_avulso"}, + "pending_tool_call": {"tool_name": "cancelar_vas_avulso"}, + "pending_domain_workflow": {"workflow_name": "cancelamento_vas_avulso"}, + "missing_parameters": ["subject"], + "next_state": "WAITING_CONTESTACAO_CONFIRMATION", + "mcp_results": [{"tool_name": "consultar_vas"}], + "pending_topics": [{ + "operation_id": "op-2", + "intent": "contas_invoice_query", + "agent": "faturas_agent", + "domain": "telecom_contas", + "tools": ["consultar_faturas"], + "source_text": "quero minha segunda via", + "disposition": "execute", + "status": "pending", + }], + } + + result = await drain_pending_topics( + state, state["answer"], {"faturas_agent": billing_agent} + ) + + assert captured["active_transaction"] is None + assert captured["selected_tool_call"] == {} + assert captured["pending_tool_call"] == {} + assert captured["pending_domain_workflow"] is None + assert captured["next_state"] is None + assert captured["sanitized_input"] == "quero minha segunda via" + assert captured["mcp_results"] == [] + assert captured["mcp_tools"] == ["consultar_faturas"] + assert captured["available_mcp_tools"] == ["consultar_faturas"] + assert captured["domain"] == "telecom_contas" + assert result.answer.startswith("Aqui está a sua segunda via.") + assert result.pending_topics == [] diff --git a/tests/unit/test_readme_feature_catalog.py b/tests/unit/test_readme_feature_catalog.py new file mode 100644 index 00000000..6a56b5d6 --- /dev/null +++ b/tests/unit/test_readme_feature_catalog.py @@ -0,0 +1,39 @@ +from pathlib import Path +import re +from urllib.parse import unquote + + +ROOT = Path(__file__).resolve().parents[2] + + +def _local_links(path: Path) -> list[str]: + text = path.read_text(encoding="utf-8") + return [ + target for target in re.findall(r"\[[^]]*\]\(([^)]+)\)", text) + if not target.startswith(("http://", "https://", "#", "mailto:")) + ] + + +def test_main_readmes_have_no_broken_local_links(): + for name in ("README.md", "README_en.md"): + readme = ROOT / name + for target in _local_links(readme): + relative = unquote(target.split("#", 1)[0]) + assert not relative or (readme.parent / relative).exists(), (name, target) + + +def test_every_feature_guide_is_listed_in_corresponding_main_readme(): + pairs = (("pt-BR", "README.md"), ("en", "README_en.md")) + for language, readme_name in pairs: + readme = (ROOT / readme_name).read_text(encoding="utf-8") + guides = sorted((ROOT / "docs" / "features" / language).glob("[0-9][0-9]_*.md")) + assert guides + for guide in guides: + expected = f"docs/features/{language}/{guide.name}" + assert expected in readme, (readme_name, expected) + + +def test_portuguese_and_english_feature_catalogs_are_symmetric(): + pt = {path.name for path in (ROOT / "docs" / "features" / "pt-BR").glob("*.md")} + en = {path.name for path in (ROOT / "docs" / "features" / "en").glob("*.md")} + assert pt == en diff --git a/tests/unit/test_tuning_pending_topics_sync.py b/tests/unit/test_tuning_pending_topics_sync.py new file mode 100644 index 00000000..23e29d46 --- /dev/null +++ b/tests/unit/test_tuning_pending_topics_sync.py @@ -0,0 +1,38 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_all_tuning_backend_graphs_support_pending_topics(): + graphs = sorted((ROOT / "Tuning-Performance").glob("**/app/workflows/agent_graph.py")) + assert graphs + for path in graphs: + source = path.read_text(encoding="utf-8") + assert "from agent_framework.routing.pending_topics import drain_pending_topics" in source, path + assert '"pending_topics": drained.pending_topics' in source, path + assert '"handled_topics": drained.handled_topics' in source, path + + +def test_all_tuning_backend_states_expose_pending_topics(): + states = sorted((ROOT / "Tuning-Performance").glob("**/app/state.py")) + assert states + for path in states: + source = path.read_text(encoding="utf-8") + assert "pending_topics: list[dict[str, Any]]" in source, path + assert "handled_topics: list[dict[str, Any]]" in source, path + + +def test_canonical_templates_delegate_drain_to_framework_helper(): + for template in ("agent_template_backend", "agent_template_backend_day_zero"): + graph = ROOT / "templates" / template / "app" / "workflows" / "agent_graph.py" + state = ROOT / "templates" / template / "app" / "state.py" + graph_source = graph.read_text(encoding="utf-8") + state_source = state.read_text(encoding="utf-8") + assert "from agent_framework.routing.pending_topics import drain_pending_topics" in graph_source + assert "drained = await drain_pending_topics(" in graph_source + assert '"pending_topics": drained.pending_topics' in graph_source + assert '"handled_topics": drained.handled_topics' in graph_source + assert "MultiIntentPlanner.public_messages" not in graph_source + assert "pending_topics: list[dict[str, Any]]" in state_source + assert "handled_topics: list[dict[str, Any]]" in state_source