diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..b449673 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + { + "lastFilter": { + "state": "OPEN", + "assignee": "hoshikawa2" + } +} + { + "selectedUrlAndAccountId": { + "url": "https://github.com/hoshikawa2/agent_platform_oci.git", + "accountId": "bd799ac0-7624-42b2-999a-b96e6e11f98a" + } +} + {} + { + "isMigrated": true +} + + + + { + "customColor": "", + "associatedIndex": 2 +} + + + + { + "keyToString": { + "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true", + "ModuleVcsDetector.initialDetectionPerformed": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", + "RunOnceActivity.git.unshallow": "true", + "RunOnceActivity.typescript.service.memoryLimit.init": "true", + "SHARE_PROJECT_CONFIGURATION_FILES": "true", + "Shell Script.agent_contas.executor": "Run", + "Shell Script.agent_frontend.executor": "Run", + "Shell Script.agent_template_backend.executor": "Run", + "Shell Script.mcp gateway.executor": "Run", + "Shell Script.mcp servers.executor": "Run", + "git-widget-placeholder": "main", + "kotlin-language-version-configured": "true", + "last_opened_file_path": "D:/MSI_Projects/agent_framework_oci", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + + + + + + + 1781996088468 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.pytest_cache/v/cache/lastfailed b/.pytest_cache/v/cache/lastfailed new file mode 100644 index 0000000..d7b0ed7 --- /dev/null +++ b/.pytest_cache/v/cache/lastfailed @@ -0,0 +1,7 @@ +{ + "agent_framework_oci/tests/unit/test_langgraph_checkpoint_saver.py::test_repository_checkpoint_saver_put_get": true, + "agent_framework_oci/tests/unit/test_resilient_checkpointer.py::test_integrity_envelope_and_recovery_skips_corrupt_latest": true, + "agent_framework_oci/tests/unit/test_resilient_checkpointer.py::test_compaction_keeps_last_n_checkpoints": true, + "agent_framework_oci/tests/unit/test_resilient_checkpointer.py::test_recovery_raises_when_only_corrupt_checkpoints_exist": true, + "agent_framework_oci/tests/unit/test_langgraph_checkpoint_saver.py::test_put_writes_does_not_restore_older_checkpoint": true +} \ No newline at end of file diff --git a/.pytest_cache/v/cache/nodeids b/.pytest_cache/v/cache/nodeids new file mode 100644 index 0000000..4bd5d7b --- /dev/null +++ b/.pytest_cache/v/cache/nodeids @@ -0,0 +1,291 @@ +[ + "agent_framework_oci/tests/test_generic_tool_response_presentation.py::test_generic_lines_mode_preserves_order_shape", + "agent_framework_oci/tests/test_generic_tool_response_presentation.py::test_generic_list_mode_supports_contas_multiple_plans_without_none", + "agent_framework_oci/tests/test_generic_tool_response_presentation.py::test_generic_template_mode_preserves_template_backend_plan_contract", + "agent_framework_oci/tests/test_generic_tool_response_presentation.py::test_missing_renderer_falls_back_without_breaking_runtime", + "agent_framework_oci/tests/test_generic_tool_response_presentation.py::test_no_declared_response_keeps_legacy_fallback", + "agent_framework_oci/tests/test_generic_tool_response_presentation.py::test_renderer_mode_uses_application_registered_renderer", + "agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py::test_collection_turn_reuses_declarative_parameter_extraction", + "agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py::test_explicit_order_id_has_precedence_over_contract_key", + "agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py::test_new_transaction_current_message_overrides_stale_subject_from_context", + "agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py::test_runtime_extracts_order_id_from_current_message", + "agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py::test_transaction_extracts_required_fields_before_first_missing_check", + "agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py::test_unique_transactional_tool_is_selected_after_routing_constrains_allowlist", + "agent_framework_oci/tests/test_route_stickiness_transaction_shift.py::test_explicit_keyword_shift_preempts_stickiness", + "agent_framework_oci/tests/test_route_stickiness_transaction_shift.py::test_same_agent_explicit_intent_shift_must_preempt_stickiness", + "agent_framework_oci/tests/test_route_stickiness_transaction_shift.py::test_same_agent_transaction_keyword_preempts_continuity", + "agent_framework_oci/tests/test_route_stickiness_transaction_shift.py::test_short_generic_keyword_does_not_preempt", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_closed_transaction_is_not_operational_context_for_next_turn", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_collecting_parameters_does_not_replace_collected_subject_with_stale_context", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_new_contestation_does_not_inherit_subject_or_value_from_previous_transaction", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_runtime_source_contains_persisted_confirmation_contract", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_terminal_confirmation_closes_active_transaction_and_clears_latches", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_tool_policy_registry_reads_transactional_confirmation", + "agent_framework_oci/tests/test_transactional_tool_flow.py::test_transaction_waits_then_executes_after_confirmation", + "agent_framework_oci/tests/unit/test_langgraph_checkpoint_saver.py::test_put_writes_does_not_restore_older_checkpoint", + "agent_framework_oci/tests/unit/test_langgraph_checkpoint_saver.py::test_repository_checkpoint_saver_put_get", + "agent_framework_oci/tests/unit/test_resilient_checkpointer.py::test_compaction_keeps_last_n_checkpoints", + "agent_framework_oci/tests/unit/test_resilient_checkpointer.py::test_integrity_envelope_and_recovery_skips_corrupt_latest", + "agent_framework_oci/tests/unit/test_resilient_checkpointer.py::test_recovery_raises_when_only_corrupt_checkpoints_exist", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_continue_bypasses_router_without_regex_rules", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_continue_without_active_agent_falls_back_to_router", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_end_session_is_returned_as_global_route", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_global_session_actions_work_without_active_agent", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_human_handoff_is_returned_as_global_route", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_invalid_output_falls_back_safely", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_low_confidence_continue_falls_back_safely", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_no_active_agent_still_classifies_global_session_actions", + "agent_framework_oci/tests/unit/test_semantic_route_stickiness.py::test_route_result_falls_back_to_enterprise_router", + "tests/migration/test_backend_wrapper_contract_parity.py::test_cancelamento_aceita_alias_itens_para_contestacao", + "tests/migration/test_backend_wrapper_contract_parity.py::test_cancelamento_aceita_cpf_como_alias_social_sec_no", + "tests/migration/test_backend_wrapper_contract_parity.py::test_cancelamento_falha_operacional_com_candidato_dispara_contestacao", + "tests/migration/test_backend_wrapper_contract_parity.py::test_cancelamento_falha_workflow_preserva_output_parcial_nao_cancelados", + "tests/migration/test_backend_wrapper_contract_parity.py::test_cancelamento_sem_candidatos_nao_dispara_contestacao", + "tests/migration/test_backend_wrapper_contract_parity.py::test_cancelamento_sms_falho_preserva_sucesso_e_flag", + "tests/migration/test_backend_wrapper_contract_parity.py::test_contestacao_wrapper_mapeia_falha_tecnica_como_sistemica", + "tests/migration/test_backend_wrapper_contract_parity.py::test_contestacao_wrapper_preserva_erro_de_negocio_e_protocolo", + "tests/migration/test_backend_wrapper_contract_parity.py::test_contestacao_wrapper_preserva_tipo_atendimento_e_contexto_do_turno", + "tests/migration/test_backend_wrapper_contract_parity.py::test_invoice_explanation_branch_devolver_orquestrador_nao_auto_finaliza", + "tests/migration/test_backend_wrapper_contract_parity.py::test_invoice_explanation_branch_nao_sem_vas_recomenda_nao_resolvido", + "tests/migration/test_backend_wrapper_contract_parity.py::test_invoice_explanation_service_failure_preserva_fraseologia_sem_auto_finalize", + "tests/migration/test_backend_wrapper_contract_parity.py::test_pro_rata_aceite_e_nao_controle_recomendam_resolvido", + "tests/migration/test_backend_wrapper_contract_parity.py::test_pro_rata_branch_devolver_orquestrador_nao_auto_finaliza", + "tests/migration/test_backend_wrapper_contract_parity.py::test_vas_estrategico_rejeitado_declara_rag_para_cada_servico_estrategico", + "tests/migration/test_backend_wrapper_contract_parity.py::test_vas_estrategico_workflow_payload_preserva_tres_itens_e_action_agrupa_duas_linhas", + "tests/migration/test_business_events_and_guardrails.py::test_business_events_are_published_recursively_once", + "tests/migration/test_business_events_and_guardrails.py::test_cached_tool_result_does_not_republish_business_events", + "tests/migration/test_contas_domain_mock.py::test_cancel_vas_is_domain_operation_not_conversation_engine", + "tests/migration/test_contas_domain_mock.py::test_cancelamento_batch_emite_vaa004_quando_falha_operacional", + "tests/migration/test_contas_domain_mock.py::test_cancelamento_batch_emite_vaa_caminho_feliz_uma_vez", + "tests/migration/test_contas_domain_mock.py::test_cancelamento_nao_recancela_servico_inativo_do_historico", + "tests/migration/test_contas_domain_mock.py::test_cancelamento_usa_historico_como_fallback_quando_ativo_nao_encontra", + "tests/migration/test_contas_domain_mock.py::test_contestation_mock", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_com_protocolo_existente_nao_cria_informacional", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_cria_protocolo_informacional_quando_invoice_explanation_executou", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_force_rt15_cria_novo_protocolo_mesmo_com_veb_fechado", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_cria_protocolo_fechado_sem_duplicar_status", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types0-Explica\\xe7\\xe3o de VAS Bundle]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types1-Explica\\xe7\\xe3o de VAS Estrat\\xe9gico]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types2-Explica\\xe7\\xe3o de VAS Avulso]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types3-Explica\\xe7\\xe3o de VAS Bundle, VAS Estrat\\xe9gico]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types4-Explica\\xe7\\xe3o de VAS Bundle, VAS Avulso]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types5-Explica\\xe7\\xe3o de VAS Estrat\\xe9gico, VAS Avulso]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_informacional_notas_canonicas[types6-Explica\\xe7\\xe3o de VAS Bundle, VAS Estrat\\xe9gico, VAS Avulso]", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_nao_cria_protocolo_apenas_por_prefetch_invoice_explanation", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_nao_duplica_protocolo_quando_contestacao_ja_existe", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_normaliza_status_invalido_e_summary", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_preserva_status_valido_e_prefixa_summary", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_reusa_protocolo_veb_ja_fechado_sem_abrir_outro", + "tests/migration/test_contas_domain_mock.py::test_finalizacao_veb_deferido_suprime_evento_cvn_de_protocolo", + "tests/migration/test_contas_domain_mock.py::test_invoice_explanation_converte_retry_transport_em_rct_por_tentativa", + "tests/migration/test_contas_domain_mock.py::test_invoice_explanation_falha_preserva_rct_das_tres_tentativas", + "tests/migration/test_contas_domain_mock.py::test_read_only_integrations_with_fixtures", + "tests/migration/test_contestation_business_rules_full.py::test_abrir_contestacao_deriva_refund_manual_e_normaliza_barcode_zero", + "tests/migration/test_contestation_business_rules_full.py::test_check_invoice_status_enriquece_prefetch_sem_nova_api", + "tests/migration/test_contestation_business_rules_full.py::test_check_invoice_status_falha_sem_prefetch", + "tests/migration/test_contestation_business_rules_full.py::test_cutdate_da_fatura_define_manual", + "tests/migration/test_contestation_business_rules_full.py::test_cutdate_regra_um_mes", + "tests/migration/test_contestation_business_rules_full.py::test_cutoff_referencia_corrige_bug_mes_seguinte", + "tests/migration/test_contestation_business_rules_full.py::test_dependente_forca_conta_certa_manual", + "tests/migration/test_contestation_business_rules_full.py::test_due_day_iso_e_br", + "tests/migration/test_contestation_business_rules_full.py::test_refund_boleto_para_fatura_em_aberto_com_fatura_normal", + "tests/migration/test_contestation_business_rules_full.py::test_refund_credito_para_dacc_cartao_ou_parcelamento", + "tests/migration/test_contestation_business_rules_full.py::test_refund_credito_para_fatura_paga_ou_ja_contestada", + "tests/migration/test_contestation_failure_mapping.py::test_contestacao_failed_preserva_provider_message_e_protocolo", + "tests/migration/test_contestation_failure_mapping.py::test_contestacao_failed_sem_mensagem_provider_vira_falha_sistemica", + "tests/migration/test_framework_native_structure.py::test_agent_workflow_init_realmente_inicializa_router_agentes_e_graph", + "tests/migration/test_framework_native_structure.py::test_all_active_workflow_actions_registered", + "tests/migration/test_framework_native_structure.py::test_contestacao_workflow_encerra_imediatamente_quando_cval_bloqueia", + "tests/migration/test_framework_native_structure.py::test_domain_does_not_import_langgraph_directly", + "tests/migration/test_framework_native_structure.py::test_guardrail_config_declares_all_execution_stages", + "tests/migration/test_framework_native_structure.py::test_official_templates_use_framework_state_graph", + "tests/migration/test_framework_native_structure.py::test_workflow_runtime_preserva_snapshot_parcial_quando_action_posterior_falha", + "tests/migration/test_mcp_contestation_subject_guard.py::test_correct_subject_is_preserved", + "tests/migration/test_mcp_contestation_subject_guard.py::test_preflight_does_not_replace_explicit_plan_with_vas", + "tests/migration/test_mcp_contestation_subject_guard.py::test_recovers_explicit_contestation_subject_from_original_text", + "tests/migration/test_original_contestation_validation.py::test_contestation_action_calculates_totals_only_for_items_actually_contested", + "tests/migration/test_original_contestation_validation.py::test_contestation_action_classifies_already_contested_and_missing_requested_item", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_aprova_quando_total_ajustes_dentro_do_limite", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_bloqueia_classe_estrategico_canonica", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_bloqueia_item_fora_da_secao_vas", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_bloqueia_item_inexistente", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_bloqueia_quando_total_ajustes_excede_fatura", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_bloqueia_valor_maior_que_o_item", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_bloqueia_vas_estrategico", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_emite_span_quando_aprova", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_emite_spans_quando_bloqueia", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_prefere_evidencia_vas_quando_item_aparece_em_multiplas_visoes", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_reconhece_vas_por_type_do_billing_analysis", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_span_blocked_inclui_contagens", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_usa_classe_avulso_canonica", + "tests/migration/test_original_contestation_validation.py::test_validate_contestation_items_valida_todos_antes_de_retornar", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A01-fluid-light-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A02-fluid-premium-x-light]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A03-aya-books-x-audiobooks]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A04-aya-ensinah-x-books]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A05-exa-seguranca-x-cloud]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A06-bancah-jornais-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A07-itgame-light-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A08-aluguel-filme-1-x-2]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A09-tim-games-mensal-x-semanal]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A10-kids-plus-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A11-exa-premium-x-protecao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A12-tim-games-prefixo]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A13-fit-app-x-fit-me-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A14-tim-music-plus-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[A15-kids-prefixo]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B01-fitme-16.99-x-17.00]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B02-fitme-16.99-x-16.98]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B03-fluid-14.99-x-15.00]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B04-exa-9.99-x-9.98]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B05-fitme-prefixo-valor-errado]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B06-aya-24.90-x-24.91]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B07-itgame-12.90-x-12.09]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[B08-fitme-16.99-x-169.90]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C01-hbo-secao-terceiros]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C02-netflix-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C03-youtube-mis-bucket]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C04-deezer-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C05-apple-music-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C06-bundle-incluso]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[C07-fitme-classificado-estrategico]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D01-fitme-dois-candidatos]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D02-fitme-tres-candidatos]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D03-aya-tres-marcas]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D04-mesmo-nome-duas-linhas]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D05-duplicata-mesma-linha]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D06-fluid-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D07-exa-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[D08-bancah-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[E01-prefixo-invertido]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[E02-substring-no-meio]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[E03-sufixo-games-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[E04-token-parcial-tube]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[E05-token-solto-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[E06-fitme-fail-closed-por-padrao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[F01-msisdn-ausente]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[F02-item-na-linha-secundaria]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[F03-secao-sem-classe]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[F04-desc-vazia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[F05-valor-nao-parseavel]", + "tests/migration/test_original_vas_variation_adversarial.py::test_explicacao_sem_data_nao_altera_nenhum_dos_50[F06-fatura-sem-o-item]", + "tests/migration/test_original_vas_variation_adversarial.py::test_flag_liga_a_segunda_passada_e_so_isso", + "tests/migration/test_original_vas_variation_adversarial.py::test_flag_ligada_nao_reabre_msisdn_nem_ambiguidade", + "tests/migration/test_original_vas_variation_adversarial.py::test_guards_de_prefixo_valem_com_a_flag_ligada[E01-prefixo-invertido]", + "tests/migration/test_original_vas_variation_adversarial.py::test_guards_de_prefixo_valem_com_a_flag_ligada[E02-substring-no-meio]", + "tests/migration/test_original_vas_variation_adversarial.py::test_guards_de_prefixo_valem_com_a_flag_ligada[E03-sufixo-games-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_guards_de_prefixo_valem_com_a_flag_ligada[E04-token-parcial-tube]", + "tests/migration/test_original_vas_variation_adversarial.py::test_guards_de_prefixo_valem_com_a_flag_ligada[E05-token-solto-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_guards_de_prefixo_valem_com_a_flag_ligada[E06-fitme-fail-closed-por-padrao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A01-fluid-light-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A02-fluid-premium-x-light]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A03-aya-books-x-audiobooks]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A04-aya-ensinah-x-books]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A05-exa-seguranca-x-cloud]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A06-bancah-jornais-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A07-itgame-light-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A08-aluguel-filme-1-x-2]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A09-tim-games-mensal-x-semanal]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A10-kids-plus-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A11-exa-premium-x-protecao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A12-tim-games-prefixo]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A13-fit-app-x-fit-me-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A14-tim-music-plus-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[A15-kids-prefixo]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B01-fitme-16.99-x-17.00]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B02-fitme-16.99-x-16.98]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B03-fluid-14.99-x-15.00]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B04-exa-9.99-x-9.98]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B05-fitme-prefixo-valor-errado]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B06-aya-24.90-x-24.91]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B07-itgame-12.90-x-12.09]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[B08-fitme-16.99-x-169.90]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C01-hbo-secao-terceiros]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C02-netflix-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C03-youtube-mis-bucket]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C04-deezer-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C05-apple-music-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C06-bundle-incluso]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[C07-fitme-classificado-estrategico]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D01-fitme-dois-candidatos]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D02-fitme-tres-candidatos]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D03-aya-tres-marcas]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D04-mesmo-nome-duas-linhas]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D05-duplicata-mesma-linha]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D06-fluid-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D07-exa-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[D08-bancah-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[E01-prefixo-invertido]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[E02-substring-no-meio]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[E03-sufixo-games-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[E04-token-parcial-tube]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[E05-token-solto-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[E06-fitme-fail-closed-por-padrao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[F01-msisdn-ausente]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[F02-item-na-linha-secundaria]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[F03-secao-sem-classe]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[F04-desc-vazia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[F05-valor-nao-parseavel]", + "tests/migration/test_original_vas_variation_adversarial.py::test_matcher_determinístico_nao_produz_falso_positivo[F06-fatura-sem-o-item]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A01-fluid-light-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A02-fluid-premium-x-light]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A03-aya-books-x-audiobooks]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A04-aya-ensinah-x-books]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A05-exa-seguranca-x-cloud]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A06-bancah-jornais-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A07-itgame-light-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A08-aluguel-filme-1-x-2]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A09-tim-games-mensal-x-semanal]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A10-kids-plus-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A11-exa-premium-x-protecao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A12-tim-games-prefixo]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A13-fit-app-x-fit-me-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A14-tim-music-plus-x-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[A15-kids-prefixo]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B01-fitme-16.99-x-17.00]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B02-fitme-16.99-x-16.98]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B03-fluid-14.99-x-15.00]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B04-exa-9.99-x-9.98]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B05-fitme-prefixo-valor-errado]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B06-aya-24.90-x-24.91]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B07-itgame-12.90-x-12.09]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[B08-fitme-16.99-x-169.90]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C01-hbo-secao-terceiros]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C02-netflix-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C03-youtube-mis-bucket]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C04-deezer-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C05-apple-music-em-secao-avulsa]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C06-bundle-incluso]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[C07-fitme-classificado-estrategico]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D01-fitme-dois-candidatos]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D02-fitme-tres-candidatos]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D03-aya-tres-marcas]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D04-mesmo-nome-duas-linhas]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D05-duplicata-mesma-linha]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D06-fluid-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D07-exa-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[D08-bancah-familia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[E01-prefixo-invertido]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[E02-substring-no-meio]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[E03-sufixo-games-premium]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[E04-token-parcial-tube]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[E05-token-solto-app]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[E06-fitme-fail-closed-por-padrao]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[F01-msisdn-ausente]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[F02-item-na-linha-secundaria]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[F03-secao-sem-classe]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[F04-desc-vazia]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[F05-valor-nao-parseavel]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_item_devolvido_e_estrategico_ou_bundle[F06-fatura-sem-o-item]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_threshold_de_similaridade_zera_falso_positivo[0.85]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_threshold_de_similaridade_zera_falso_positivo[0.95]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_threshold_de_similaridade_zera_falso_positivo[0.97]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_threshold_de_similaridade_zera_falso_positivo[0.99]", + "tests/migration/test_original_vas_variation_adversarial.py::test_nenhum_threshold_de_similaridade_zera_falso_positivo[0.9]", + "tests/migration/test_original_vas_variation_adversarial.py::test_shadow_similaridade_tem_overlap_irremovivel", + "tests/migration/test_original_vas_variation_adversarial.py::test_suite_tem_exatamente_50_casos_com_a_distribuicao_exigida", + "tests/migration/test_original_vas_variation_adversarial.py::test_valores_sao_decimal_nunca_float", + "tests/migration/test_tool_policy_operation_types.py::test_contas_tool_policies_accept_all_operation_types", + "tests/migration/test_vas_response_renderer.py::test_vas_renderer_does_not_invent_missing_value", + "tests/migration/test_vas_response_renderer.py::test_vas_renderer_includes_available_values" +] \ No newline at end of file diff --git a/README.md b/README.md index 178239a..c2cd3fe 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,7 @@ grep -R "agente_contas_tim" app mcp config ``` Deve retornar zero ocorrências. + + +## V10 - VAS response renderer +`consultar_vas` agora usa `response.mode: renderer` com `renderer: contas.vas`. O renderer inclui o valor de cada serviço quando a tool o fornece (por exemplo `details.valor`) e nunca inventa preço quando ausente. diff --git a/agent_framework_oci/.env b/agent_framework_oci/.env new file mode 100644 index 0000000..4556734 --- /dev/null +++ b/agent_framework_oci/.env @@ -0,0 +1,207 @@ +############################################################################### +# AI AGENT PLATFORM - CONFIGURAÇÃO ÚNICA +# Este arquivo é lido por Pydantic Settings no framework e no backend template. +############################################################################### + +APP_NAME=ai-agent-template +APP_ENV=local +LOG_LEVEL=INFO +API_HOST=0.0.0.0 +API_PORT=8000 +CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 + +############################################################################### +# LLM - OCI Generative AI como provider principal +############################################################################### +# Opções: mock, oci_openai, oci_sdk, openai_compatible +LLM_PROVIDER=oci_sdk +LLM_TEMPERATURE=0.2 +LLM_MAX_TOKENS=2048 +LLM_TIMEOUT_SECONDS=120 + +# OCI OpenAI-compatible endpoint +OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com +OCI_GENAI_MODEL=openai.gpt-4.1 +OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS +OCI_GENAI_PROJECT_OCID= + +#OCI_GENAI_BASE_URL=https://pegruagntaiatenddev.pe.inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com +#OCI_GENAI_MODEL=openai.gpt-4.1 +#OCI_GENAI_API_KEY= +#OCI_GENAI_PROJECT_OCID= + + +# OCI_AUTH_MODE=config_file|instance_principal|resource_principal +OCI_AUTH_MODE=config_file +# OCI SDK / signer / profiles +OCI_CONFIG_FILE=~/.oci/config +OCI_PROFILE=LATINOAMERICA-Chicago +OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q +OCI_REGION=us-chicago-1 + +############################################################################### +# Persistência +############################################################################### +# Opções: memory, autonomous, mongodb +SESSION_REPOSITORY_PROVIDER=autonomous +MEMORY_REPOSITORY_PROVIDER=autonomous +CHECKPOINT_REPOSITORY_PROVIDER=autonomous + +# Autonomous Database +ADB_USER=admin +ADB_PASSWORD=Moniquinha19721972 +ADB_DSN=oradb23ai_high +ADB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/LatinoAmerica/Wallet_ORADB23ai +ADB_WALLET_PASSWORD=Moniquinha1972 +ADB_TABLE_PREFIX=AGENTFW + +# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente +MONGODB_URI=mongodb://mongo:mongopassword@localhost:27017 +MONGODB_DATABASE=agent_platform + +# Redis +REDIS_URL=redis://localhost:6379/0 +ENABLE_REDIS_CACHE=false + +############################################################################### +# RAG / Vector / Graph +############################################################################### +VECTOR_STORE_PROVIDER=autonomous +GRAPH_STORE_PROVIDER=autonomous +RAG_TOP_K=5 +EMBEDDING_PROVIDER=oci +OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0 +RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json + +############################################################################### +# Observabilidade +############################################################################### +ENABLE_LANGFUSE=true + # Opcional: verbose, compact +LANGFUSE_TRACE_MODE=compact +# Nome customizado do trace pai, ex.: backoffice.checklist.workflow ou backoffice.emulador.workflow +LANGFUSE_COMPACT_VISIBLE_EVENT_PREFIXES=AGA.,NOC., IC. +LANGFUSE_COMPACT_SUPPRESSED_PREFIXES=llm.chat_completion +LANGFUSE_IGNORE_HEALTHCHECKS=true +LANGFUSE_IGNORED_PATHS=/health,/ready,/metrics +LANGFUSE_PUBLIC_KEY=pk-lf-4a1e3921-5158-4fd3-a16d-7a77549fb312 +LANGFUSE_SECRET_KEY=sk-lf-efc6fd59-c5ec-4858-b6ec-4aa129734915 +LANGFUSE_HOST=http://localhost:3005 +ENABLE_OTEL=false +OTEL_EXPORTER_OTLP_ENDPOINT= +OTEL_SERVICE_NAME=ai-agent-template +ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true +ENABLE_LANGFUSE_ANALYTICS_PUBLISHER=false + +############################################################################### +# Analytics / Observer corporativo +############################################################################### +# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo. +ENABLE_ANALYTICS=false +# Providers aceitos: oci_streaming,pubsub,noop +ANALYTICS_PROVIDERS=oci_streaming +# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente. +AGENT_PUBSUB_TOPIC= +GCP_PUBSUB_TOPIC_PATH= +GCP_PROJECT_ID= +GCP_PUBSUB_TOPIC= +GCP_PUBSUB_TIMEOUT_SECONDS=30 +# Credencial GCP segue padrão Google: +# GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-service-account.json + +############################################################################### +# OCI Streaming +############################################################################### +ENABLE_OCI_STREAMING=false +OCI_STREAM_ENDPOINT= +OCI_STREAM_OCID= +OCI_STREAM_PARTITION_KEY=agent-events + +############################################################################### +# Guardrails, Judges, Supervisor +############################################################################### +ENABLE_INPUT_GUARDRAILS=true +ENABLE_OUTPUT_GUARDRAILS=true +ENABLE_JUDGES=true +ENABLE_SUPERVISOR=true +ENABLE_OUTPUT_SUPERVISOR=true +ENABLE_PARALLEL_GUARDRAILS=true +GUARDRAILS_FAIL_FAST=true +OUTPUT_SUPERVISOR_MAX_RETRIES=3 +GUARDRAILS_CONFIG_PATH=./config/guardrails.yaml +JUDGES_CONFIG_PATH=./config/judges.yaml +PROMPT_POLICY_PATH=./config/prompt_policy.yaml + +############################################################################### +# Gateway de canais +############################################################################### +DEFAULT_CHANNEL=web +# embedded = backend may parse simple/native channel payloads. +# external = backend only accepts GatewayRequest normalized by an external Channel Gateway. +FRAMEWORK_CHANNEL_INPUT_MODE=embedded +ENABLE_VOICE_ADAPTER=true +ENABLE_WHATSAPP_ADAPTER=true +ENABLE_TEXT_ADAPTER=true + +################################################# +# ENTERPRISE ROUTING +################################################# +# Arquivo YAML com intents, keywords, políticas de estado e fallback. +ROUTING_CONFIG_PATH=./config/routing.yaml +# true = usa LLM para classificar quando keywords/estado não resolverem. +# Em produção, costuma ser útil; em desenvolvimento, false evita custo e latência. +ENABLE_LLM_ROUTER=true + +# Semantic route stickiness (optional). +# Uses a lightweight LLM profile to decide only CONTINUE vs ROUTE. +# There are no regexes or deterministic language rules. +ENABLE_ROUTE_STICKINESS=true +ROUTE_STICKINESS_LLM_PROFILE=route_continuity +ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90 +ROUTE_STICKINESS_HISTORY_TURNS=2 +ROUTE_STICKINESS_MAX_TOKENS=80 +HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa. +END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato. + +############################################################################### +# MCP / Tools +############################################################################### +ENABLE_MCP_TOOLS=true +MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml +TOOLS_CONFIG_PATH=./config/tools.yaml +MCP_TOOL_TIMEOUT_SECONDS=30 + +# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes +ROUTING_MODE=router + +# Usage/cost accounting +USAGE_REPOSITORY_PROVIDER=autonomous +IDENTITY_CONFIG_PATH=./config/identity.yaml +MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml + +# ----------------------------------------------------------------------------- +# ConversationSummaryMemory / compressão de contexto conversacional +# ----------------------------------------------------------------------------- +ENABLE_CONVERSATION_SUMMARY_MEMORY=true +MEMORY_CONTEXT_STRATEGY=summary +MEMORY_HISTORY_LIMIT=80 +MEMORY_RECENT_MESSAGES_LIMIT=8 +MEMORY_SUMMARY_TRIGGER_MESSAGES=20 +MEMORY_MAX_SUMMARY_CHARS=6000 +MEMORY_SUMMARY_USE_LLM=true +MEMORY_INJECT_RECENT_MESSAGES=true +MEMORY_INJECT_SUMMARY=true + +############################################################################### +# LONG-TERM MEMORY +############################################################################### +ENABLE_LONG_TERM_MEMORY=true +LONG_TERM_MEMORY_PROVIDER=sqlite +LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db +LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory +# For Autonomous/Oracle, defaults to ${ADB_TABLE_PREFIX}_LONG_TERM_MEMORY +# LONG_TERM_MEMORY_ORACLE_TABLE=AGENTFW_LONG_TERM_MEMORY +LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20 +LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70 +LONG_TERM_MEMORY_AUTO_EXTRACT=true +LONG_TERM_MEMORY_INJECT_CONTEXT=true diff --git a/agent_framework_oci/.idea/agent_platform_oci.iml b/agent_framework_oci/.idea/agent_platform_oci.iml new file mode 100644 index 0000000..853e1f0 --- /dev/null +++ b/agent_framework_oci/.idea/agent_platform_oci.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/agent_framework_oci/.idea/modules.xml b/agent_framework_oci/.idea/modules.xml new file mode 100644 index 0000000..3fb2fd0 --- /dev/null +++ b/agent_framework_oci/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/agent_framework_oci/.idea/vcs.xml b/agent_framework_oci/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/agent_framework_oci/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/agent_framework_oci/.idea/workspace.xml b/agent_framework_oci/.idea/workspace.xml new file mode 100644 index 0000000..03a129f --- /dev/null +++ b/agent_framework_oci/.idea/workspace.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + { + "lastFilter": { + "state": "OPEN", + "assignee": "hoshikawa2" + } +} + { + "selectedUrlAndAccountId": { + "url": "https://github.com/hoshikawa2/agent_platform_oci.git", + "accountId": "bd799ac0-7624-42b2-999a-b96e6e11f98a" + } +} + {} + { + "isMigrated": true +} + + + + { + "customColor": "", + "associatedIndex": 2 +} + + + + + + + + + + + + + + 1781996088468 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agent_framework_oci/Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md b/agent_framework_oci/Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md new file mode 100644 index 0000000..79d8405 --- /dev/null +++ b/agent_framework_oci/Documentacao/IMPLEMENTACAO_WORKFLOWS_TRANSACIONAIS.md @@ -0,0 +1,64 @@ +# Implementação — workflows transacionais determinísticos + +## Entrega + +Foi adicionada ao `agent_framework_oci` uma capacidade opcional para executar transações multi-etapas como workflows determinísticos compilados em LangGraph. + +### Módulo novo + +`libs/agent_framework/src/agent_framework/workflows/` + +- `models.py`: contratos Pydantic e validação estrutural; +- `repository.py`: resolução de versão ativa e leitura de YAML imutável; +- `registry.py`: registro desacoplado de actions sync/async; +- `runtime.py`: compilação, cache e execução do StateGraph; +- `tool_executor.py`: integração com a política da tool; +- `__init__.py`: API pública. + +### Política expandida + +`ToolPolicy` agora aceita: + +```yaml +execution: + mode: direct_tool | workflow | agent + workflow: nome_do_workflow + version: active | 1 +``` + +O default permanece `direct_tool`, preservando compatibilidade. + +### Configuração + +Foram adicionados: + +- `ENABLE_TRANSACTIONAL_WORKFLOWS=false`; +- `WORKFLOWS_PATH=./workflows`. + +### Template + +Inclui um exemplo completo de devolução de pedido com: + +- confirmação e campos obrigatórios pela política; +- workflow YAML versionado; +- actions de domínio no backend; +- bifurcação determinística baseada no resultado da validação. + +## Validação realizada + +- `tests/unit/test_tool_policies.py`: 4 testes aprovados; +- compilação Python de framework, template e novos testes: aprovada; +- o teste funcional novo do LangGraph foi criado, mas não pôde ser executado neste container porque `langgraph` não está instalado no ambiente. A dependência já está declarada no `pyproject.toml` do framework. + +## Escopo e segurança + +Esta entrega cria o motor e a integração de política. Para operações críticas em produção ainda é necessário conectar: + +- execution store persistente; +- idempotência de negócio nas actions/APIs; +- autorização por escopo; +- telemetria IC/NOC específica de workflow; +- compensação/Saga quando aplicável; +- estratégia corporativa de timeout e retry. + +Esses itens foram explicitamente documentados para evitar a falsa impressão de que retry por si só garante segurança transacional. diff --git a/agent_framework_oci/Documentacao/Implementando_Basic_Auth.md b/agent_framework_oci/Documentacao/Implementando_Basic_Auth.md new file mode 100644 index 0000000..d6ff282 --- /dev/null +++ b/agent_framework_oci/Documentacao/Implementando_Basic_Auth.md @@ -0,0 +1,987 @@ +# Implementando Basic Auth + +Para validar **todo o circuito com Basic Auth**, você precisa configurar três relações distintas: + +```text +Cliente de teste + └─ Basic Auth A ─► Agent Gateway :8010 + └─ Basic Auth B ─► Agent Backend :8000 + └─ Basic Auth C ─► MCP Gateway :8300 +``` + +Há um detalhe importante: no pacote atual, a autenticação Basic já funciona para chamadas **de entrada**, mas os clientes internos ainda não enviam Basic Auth: + +* `Agent Gateway → Agent Backend` não envia credencial; +* `Agent Backend → MCP Gateway` envia apenas Bearer Token. + +Portanto, para testar o circuito inteiro com Basic Auth, faça os dois pequenos ajustes de código descritos abaixo. + +--- + +# 1. Preparar o ambiente + +Considere que o ZIP foi extraído em: + +```bash +cd agent_framework_oci_authentication_v2_1 +``` + +Crie um único ambiente virtual para facilitar o teste: + +```bash +python -m venv .venv +source .venv/bin/activate +``` + +No Windows PowerShell: + +```powershell +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +``` + +Instale o framework e as dependências dos três componentes: + +```bash +pip install -U pip + +pip install -e ./libs/agent_framework + +pip install \ + -r ./Tuning-Performance/Authentication/agent_template_backend_authentication/requirements.txt \ + -r ./apps/agent_gateway/requirements.txt \ + -r ./apps/mcp_gateway/requirements.txt +``` + +Confirme a importação: + +```bash +python -c "from agent_framework.security import install_authentication; print('framework ok')" +``` + +--- + +# 2. Criar três pares de Client ID e Secret + +Use credenciais diferentes para cada trecho. Para teste local: + +| Fluxo | Client ID | Secret de teste | +| ----------------------- | -------------------- | --------------------------- | +| Cliente → Agent Gateway | `tia-test` | `TiaGateway-Test-2026!` | +| Agent Gateway → Backend | `agent-gateway-test` | `GatewayBackend-Test-2026!` | +| Backend → MCP Gateway | `agent-backend-test` | `BackendMcp-Test-2026!` | + +Esses valores são apenas para ambiente local. Não os reutilize em produção. + +## Gerar os hashes + +O script está em: + +```text +Tuning-Performance/Authentication/ + agent_template_backend_authentication/ + scripts/generate_secret_hash.py +``` + +Execute: + +```bash +python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \ + --secret 'TiaGateway-Test-2026!' +``` + +Depois: + +```bash +python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \ + --secret 'GatewayBackend-Test-2026!' +``` + +E: + +```bash +python Tuning-Performance/Authentication/agent_template_backend_authentication/scripts/generate_secret_hash.py \ + --secret 'BackendMcp-Test-2026!' +``` + +Você receberá três valores semelhantes a: + +```text +pbkdf2_sha256:310000:: +``` + +Guarde-os temporariamente: + +```bash +HASH_CLIENT_GATEWAY='pbkdf2_sha256:310000:...' +HASH_GATEWAY_BACKEND='pbkdf2_sha256:310000:...' +HASH_BACKEND_MCP='pbkdf2_sha256:310000:...' +``` + +O hash muda a cada execução porque o salt é aleatório. Isso é esperado. + +--- + +# 3. Configurar o Agent Gateway + +Entre no diretório: + +```bash +cd apps/agent_gateway +``` + +Copie o exemplo: + +```bash +cp .env.example .env +``` + +Adicione ao final do `.env`: + +```env +# Entrada: cliente/TIA -> Agent Gateway +AGENT_GATEWAY_AUTH_ENABLED=true +AGENT_GATEWAY_AUTH_MODE=basic +AGENT_GATEWAY_AUTH_BASIC_CLIENT_ID=tia-test +AGENT_GATEWAY_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_CLIENT_GATEWAY +AGENT_GATEWAY_AUTH_BASIC_REALM=agent-gateway + +AGENT_GATEWAY_AUTH_PUBLIC_PATHS=/health,/docs,/openapi.json,/redoc +AGENT_GATEWAY_AUTH_PUBLIC_PREFIXES= + +# Saída: Agent Gateway -> Agent Backend +BACKEND_AUTH_MODE=basic +BACKEND_AUTH_CLIENT_ID=agent-gateway-test +BACKEND_AUTH_SECRET=GatewayBackend-Test-2026! +``` + +Não coloque aspas no `.env`: + +```env +BACKEND_AUTH_SECRET=GatewayBackend-Test-2026! +``` + +O arquivo de backends já aponta o backend Contas para: + +```yaml +contas: + url: http://localhost:8000 +``` + +Arquivo: + +```text +apps/agent_gateway/config/backends.yaml +``` + +Para este teste, mantenha apenas o backend `contas` ou force o backend no payload. Caso contrário, pedidos sobre ofertas e suporte podem ser roteados para portas em que nenhum backend está rodando. + +--- + +# 4. Fazer o Agent Gateway enviar Basic Auth ao backend + +Abra: + +```text +libs/agent_framework/src/agent_framework/global_supervisor/client.py +``` + +Substitua a classe `BackendClient` por uma versão que aceite autenticação Basic. + +No início do arquivo, adicione: + +```python +import os +``` + +Altere o construtor: + +```python +class BackendClient: + def __init__( + self, + timeout_seconds: float = 120.0, + basic_client_id: str | None = None, + basic_secret: str | None = None, + ): + self.timeout_seconds = timeout_seconds + self.basic_client_id = basic_client_id + self.basic_secret = basic_secret + + def _auth(self) -> httpx.BasicAuth | None: + if self.basic_client_id and self.basic_secret: + return httpx.BasicAuth( + username=self.basic_client_id, + password=self.basic_secret, + ) + return None +``` + +No método `call_message`, troque: + +```python +resp = await client.post(url, json=payload) +``` + +por: + +```python +resp = await client.post( + url, + json=payload, + auth=self._auth(), +) +``` + +No método `health`, você pode manter `/health` público. Caso queira enviar autenticação também, use: + +```python +resp = await client.get(url, auth=self._auth()) +``` + +Agora abra: + +```text +apps/agent_gateway/app/main.py +``` + +Adicione: + +```python +import os +``` + +Troque: + +```python +backend_client = BackendClient( + timeout_seconds=settings.BACKEND_TIMEOUT_SECONDS +) +``` + +por: + +```python +backend_client = BackendClient( + timeout_seconds=settings.BACKEND_TIMEOUT_SECONDS, + basic_client_id=os.getenv("BACKEND_AUTH_CLIENT_ID"), + basic_secret=os.getenv("BACKEND_AUTH_SECRET"), +) +``` + +Isso implementa: + +```text +Agent Gateway → Agent Backend +Authorization: Basic base64(agent-gateway-test:GatewayBackend-Test-2026!) +``` + +--- + +# 5. Configurar o Agent Backend autenticado + +Entre no diretório: + +```bash +cd Tuning-Performance/Authentication/agent_template_backend_authentication +``` + +Copie o exemplo: + +```bash +cp .env.example .env +``` + +Ajuste a seção de autenticação: + +```env +# Entrada: Agent Gateway -> Agent Backend +AGENT_AUTH_ENABLED=true +AGENT_AUTH_MODE=basic +AGENT_AUTH_BASIC_CLIENT_ID=agent-gateway-test +AGENT_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_GATEWAY_BACKEND +AGENT_AUTH_BASIC_REALM=agent-contas + +AGENT_AUTH_PUBLIC_PATHS=/health,/docs,/openapi.json,/redoc +AGENT_AUTH_PUBLIC_PREFIXES= +``` + +Para usar o MCP Gateway: + +```env +MCP_GATEWAY_ENABLED=true +MCP_GATEWAY_URL=http://localhost:8300 +MCP_GATEWAY_TIMEOUT_SECONDS=60 + +# Saída: Agent Backend -> MCP Gateway +MCP_GATEWAY_AUTH_MODE=basic +MCP_GATEWAY_BASIC_CLIENT_ID=agent-backend-test +MCP_GATEWAY_BASIC_SECRET=BackendMcp-Test-2026! +``` + +Para evitar dependências externas durante o primeiro teste, configure também: + +```env +LLM_PROVIDER=mock +ENABLE_LANGFUSE=false +ENABLE_ANALYTICS=false + +SESSION_REPOSITORY_PROVIDER=memory +MEMORY_REPOSITORY_PROVIDER=memory +CHECKPOINT_REPOSITORY_PROVIDER=memory +CACHE_PROVIDER=memory +USAGE_REPOSITORY_PROVIDER=memory +``` + +Os nomes exatos de alguns providers podem depender do arquivo de configuração atual do framework. Caso o `.env.example` já contenha valores locais ou mock, preserve-os. + +--- + +# 6. Fazer o Backend enviar Basic Auth ao MCP Gateway + +Abra: + +```text +libs/agent_framework/src/agent_framework/gateways/mcp_gateway_client.py +``` + +Substitua a implementação por: + +```python +from __future__ import annotations + +import base64 +from typing import Any + +import httpx + + +class MCPGatewayClient: + def __init__( + self, + base_url: str, + token: str | None = None, + timeout_seconds: int = 60, + auth_mode: str | None = None, + basic_client_id: str | None = None, + basic_secret: str | None = None, + ): + self.base_url = base_url.rstrip("/") + self.token = token + self.timeout_seconds = timeout_seconds + self.auth_mode = (auth_mode or "").strip().lower() + self.basic_client_id = basic_client_id + self.basic_secret = basic_secret + + def _headers(self) -> dict[str, str]: + if ( + self.auth_mode == "basic" + and self.basic_client_id + and self.basic_secret + ): + raw = f"{self.basic_client_id}:{self.basic_secret}".encode("utf-8") + encoded = base64.b64encode(raw).decode("ascii") + return {"Authorization": f"Basic {encoded}"} + + if self.token: + return {"Authorization": f"Bearer {self.token}"} + + return {} + + async def list_tools(self) -> dict[str, Any]: + async with httpx.AsyncClient( + timeout=self.timeout_seconds + ) as client: + response = await client.get( + f"{self.base_url}/v1/tools", + headers=self._headers(), + ) + response.raise_for_status() + return response.json() + + async def invoke_tool( + self, + *, + tenant_id: str, + agent_id: str, + channel: str | None, + tool_name: str, + arguments: dict[str, Any] | None = None, + business_context: dict[str, Any] | None = None, + metadata: dict[str, Any] | None = None, + ) -> dict[str, Any]: + payload = { + "tenant_id": tenant_id, + "agent_id": agent_id, + "channel": channel, + "tool_name": tool_name, + "arguments": arguments or {}, + "business_context": business_context or {}, + "metadata": metadata or {}, + } + + async with httpx.AsyncClient( + timeout=self.timeout_seconds + ) as client: + response = await client.post( + f"{self.base_url}/v1/tools/{tool_name}/invoke", + json=payload, + headers=self._headers(), + ) + response.raise_for_status() + return response.json() +``` + +Agora abra: + +```text +libs/agent_framework/src/agent_framework/mcp/tool_router.py +``` + +Localize: + +```python +MCPGatewayClient( + base_url=getattr( + settings, + "MCP_GATEWAY_URL", + "http://localhost:8300", + ), + token=getattr(settings, "MCP_GATEWAY_TOKEN", None), + timeout_seconds=getattr( + settings, + "MCP_GATEWAY_TIMEOUT_SECONDS", + settings.MCP_TOOL_TIMEOUT_SECONDS, + ), +) +``` + +Altere para: + +```python +MCPGatewayClient( + base_url=getattr( + settings, + "MCP_GATEWAY_URL", + "http://localhost:8300", + ), + token=getattr(settings, "MCP_GATEWAY_TOKEN", None), + timeout_seconds=getattr( + settings, + "MCP_GATEWAY_TIMEOUT_SECONDS", + settings.MCP_TOOL_TIMEOUT_SECONDS, + ), + auth_mode=getattr( + settings, + "MCP_GATEWAY_AUTH_MODE", + None, + ), + basic_client_id=getattr( + settings, + "MCP_GATEWAY_BASIC_CLIENT_ID", + None, + ), + basic_secret=getattr( + settings, + "MCP_GATEWAY_BASIC_SECRET", + None, + ), +) +``` + +Adicione estes campos em: + +```text +libs/agent_framework/src/agent_framework/config/settings.py +``` + +Próximo das configurações existentes de MCP Gateway: + +```python +MCP_GATEWAY_AUTH_MODE: str | None = None +MCP_GATEWAY_BASIC_CLIENT_ID: str | None = None +MCP_GATEWAY_BASIC_SECRET: str | None = None +``` + +Há também uma factory local em: + +```text +Tuning-Performance/Authentication/ + agent_template_backend_authentication/ + app/mcp_gateway_client_factory.py +``` + +Ajuste para: + +```python +from __future__ import annotations + +import os + +from agent_framework.gateways import MCPGatewayClient + + +def build_mcp_gateway_client() -> MCPGatewayClient | None: + if os.getenv("MCP_GATEWAY_ENABLED", "true").lower() != "true": + return None + + return MCPGatewayClient( + base_url=os.getenv( + "MCP_GATEWAY_URL", + "http://localhost:8300", + ), + token=os.getenv("MCP_GATEWAY_TOKEN") or None, + timeout_seconds=int( + os.getenv("MCP_GATEWAY_TIMEOUT_SECONDS", "60") + ), + auth_mode=os.getenv("MCP_GATEWAY_AUTH_MODE"), + basic_client_id=os.getenv( + "MCP_GATEWAY_BASIC_CLIENT_ID" + ), + basic_secret=os.getenv( + "MCP_GATEWAY_BASIC_SECRET" + ), + ) +``` + +--- + +# 7. Configurar o MCP Gateway + +Entre no diretório: + +```bash +cd apps/mcp_gateway +``` + +Crie `.env`: + +```bash +cp .env.example .env +``` + +Adicione: + +```env +# Entrada: Agent Backend -> MCP Gateway +MCP_GATEWAY_AUTH_ENABLED=true +MCP_GATEWAY_AUTH_MODE=basic +MCP_GATEWAY_AUTH_BASIC_CLIENT_ID=agent-backend-test +MCP_GATEWAY_AUTH_BASIC_SECRET_HASH=COLE_AQUI_HASH_BACKEND_MCP +MCP_GATEWAY_AUTH_BASIC_REALM=mcp-gateway + +MCP_GATEWAY_AUTH_PUBLIC_PATHS=/health,/ready,/docs,/openapi.json,/redoc +MCP_GATEWAY_AUTH_PUBLIC_PREFIXES= + +MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml +``` + +## Desabilitar o mecanismo Bearer legado + +O MCP Gateway ainda possui um segundo mecanismo antigo, configurado dentro de: + +```text +apps/mcp_gateway/config/mcp_gateway.yaml +``` + +Localize a seção: + +```yaml +auth: + enabled: true +``` + +Altere para: + +```yaml +auth: + enabled: false +``` + +Isso é necessário porque o novo middleware já faz a autenticação Basic. Caso o `auth_check()` legado continue habilitado, a requisição passará pelo Basic e depois será rejeitada por não possuir Bearer Token. + +--- + +# 8. Subir os componentes + +Use quatro terminais. + +## Terminal 1 — MCP Servers + +O MCP Gateway precisa ter pelo menos um servidor MCP disponível para demonstrar uma chamada real. + +Na raiz do projeto: + +```bash +source .venv/bin/activate +``` + +Suba o servidor telecom: + +```bash +uvicorn mcp.servers.telecom_mcp_server.main:app \ + --host 0.0.0.0 \ + --port 8100 \ + --reload +``` + +Em outro terminal, caso queira também o retail: + +```bash +uvicorn mcp.servers.retail_mcp_server.main:app \ + --host 0.0.0.0 \ + --port 8200 \ + --reload +``` + +Confira as URLs configuradas em: + +```text +apps/mcp_gateway/config/mcp_gateway.yaml +``` + +Para execução local, devem apontar para: + +```yaml +url: http://localhost:8100 +``` + +e: + +```yaml +url: http://localhost:8200 +``` + +--- + +## Terminal 2 — MCP Gateway + +```bash +cd apps/mcp_gateway +source ../../.venv/bin/activate +``` + +Suba usando `--env-file`. Isso é importante porque o middleware lê variáveis com `os.getenv()`: + +```bash +uvicorn app.main:app \ + --host 0.0.0.0 \ + --port 8300 \ + --reload \ + --env-file .env +``` + +Teste a saúde pública: + +```bash +curl http://localhost:8300/health +``` + +Teste um endpoint protegido sem credencial: + +```bash +curl -i http://localhost:8300/v1/tools +``` + +Esperado: + +```text +HTTP/1.1 401 Unauthorized +``` + +Teste com Basic Auth: + +```bash +curl -i \ + -u 'agent-backend-test:BackendMcp-Test-2026!' \ + http://localhost:8300/v1/tools +``` + +Esperado: + +```text +HTTP/1.1 200 OK +``` + +--- + +## Terminal 3 — Agent Backend + +```bash +cd Tuning-Performance/Authentication/agent_template_backend_authentication +source ../../../.venv/bin/activate +``` + +Suba: + +```bash +uvicorn app.main:app \ + --host 0.0.0.0 \ + --port 8000 \ + --reload \ + --env-file .env +``` + +Teste saúde: + +```bash +curl http://localhost:8000/health +``` + +Teste endpoint protegido sem credencial: + +```bash +curl -i http://localhost:8000/agents +``` + +Esperado: + +```text +HTTP/1.1 401 Unauthorized +``` + +Teste com a credencial usada pelo Agent Gateway: + +```bash +curl -i \ + -u 'agent-gateway-test:GatewayBackend-Test-2026!' \ + http://localhost:8000/agents +``` + +Esperado: + +```text +HTTP/1.1 200 OK +``` + +Teste mensagem diretamente: + +```bash +curl -X POST http://localhost:8000/gateway/message \ + -u 'agent-gateway-test:GatewayBackend-Test-2026!' \ + -H 'Content-Type: application/json' \ + -d '{ + "channel": "web", + "agent_id": "telecom_contas", + "tenant_id": "default", + "payload": { + "text": "Quero consultar minha fatura", + "session_id": "teste-backend-001", + "user_id": "user-001", + "customer_id": "12345", + "message_id": "msg-001" + } + }' +``` + +--- + +## Terminal 4 — Agent Gateway + +```bash +cd apps/agent_gateway +source ../../.venv/bin/activate +``` + +Suba: + +```bash +uvicorn app.main:app \ + --host 0.0.0.0 \ + --port 8010 \ + --reload \ + --env-file .env +``` + +Teste saúde: + +```bash +curl http://localhost:8010/health +``` + +Teste endpoint protegido sem credencial: + +```bash +curl -i http://localhost:8010/backends +``` + +Esperado: + +```text +HTTP/1.1 401 Unauthorized +``` + +Teste com a credencial externa: + +```bash +curl -i \ + -u 'tia-test:TiaGateway-Test-2026!' \ + http://localhost:8010/backends +``` + +Esperado: + +```text +HTTP/1.1 200 OK +``` + +--- + +# 9. Validar o circuito completo + +Force o backend `contas` para evitar que o roteador selecione um backend não iniciado: + +```bash +curl -X POST http://localhost:8010/gateway/message \ + -u 'tia-test:TiaGateway-Test-2026!' \ + -H 'Content-Type: application/json' \ + -d '{ + "channel": "web", + "backend_id": "contas", + "tenant_id": "default", + "agent_id": "telecom_contas", + "session_id": "circuito-basic-001", + "payload": { + "text": "Quero consultar minha fatura", + "session_id": "circuito-basic-001", + "user_id": "user-001", + "customer_id": "12345", + "message_id": "msg-circuito-001" + } + }' +``` + +O circuito esperado é: + +```text +curl + │ Basic tia-test + ▼ +Agent Gateway :8010 + │ Basic agent-gateway-test + ▼ +Agent Backend :8000 + │ Basic agent-backend-test + ▼ +MCP Gateway :8300 + ▼ +MCP Server :8100 ou :8200 +``` + +--- + +# 10. Como comprovar cada autenticação + +Faça testes negativos em cada trecho. + +## Secret externo incorreto + +```bash +curl -i \ + -u 'tia-test:senha-errada' \ + http://localhost:8010/backends +``` + +Resultado esperado: + +```text +401 Unauthorized +``` + +## Secret do gateway para backend incorreto + +Altere temporariamente no `apps/agent_gateway/.env`: + +```env +BACKEND_AUTH_SECRET=senha-errada +``` + +Reinicie o Agent Gateway e envie uma mensagem. + +O gateway deverá retornar erro de backend, normalmente: + +```text +502 Bad Gateway +``` + +O erro interno será originado por um: + +```text +401 Unauthorized +``` + +do Agent Backend. + +## Secret do backend para MCP incorreto + +Altere temporariamente: + +```env +MCP_GATEWAY_BASIC_SECRET=senha-errada +``` + +Reinicie o backend e execute uma frase que acione uma ferramenta MCP. + +O backend deverá registrar falha na chamada ao MCP Gateway com: + +```text +401 Unauthorized +``` + +--- + +# 11. Verificação rápida de portas + +No Linux ou WSL: + +```bash +ss -lntp | grep -E ':8000|:8010|:8100|:8200|:8300' +``` + +No Windows PowerShell: + +```powershell +Get-NetTCPConnection -State Listen | + Where-Object LocalPort -in 8000,8010,8100,8200,8300 | + Sort-Object LocalPort +``` + +Você deverá ver: + +```text +8000 Agent Backend +8010 Agent Gateway +8100 Telecom MCP Server +8200 Retail MCP Server +8300 MCP Gateway +``` + +## Observação importante + +O segredo original precisa existir no componente cliente: + +```text +TIA ou curl: + TiaGateway-Test-2026! + +Agent Gateway: + GatewayBackend-Test-2026! + +Agent Backend: + BackendMcp-Test-2026! +``` + +Os componentes servidores armazenam apenas os hashes: + +```text +Agent Gateway: + hash de TiaGateway-Test-2026! + +Agent Backend: + hash de GatewayBackend-Test-2026! + +MCP Gateway: + hash de BackendMcp-Test-2026! +``` + +Em produção, os segredos originais e hashes devem vir de Vault ou Kubernetes Secret, não de arquivos `.env`. diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py index e6429c4..7a1a9be 100644 --- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/agents/runtime.py @@ -3,5 +3,8 @@ from __future__ import annotations # Compatibilidade local do template/backend. # A implementação oficial agora fica no framework para evitar duplicação entre agentes. from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext +from app.presentation import register_tool_renderers + +register_tool_renderers() __all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"] diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..0c79403 Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/tool_renderers.py new file mode 100644 index 0000000..f77c47a --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/app/presentation/tool_renderers.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + return f"[{agent_label}] Fatura consultada: {result}." + + +def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + plano = result.get("plano") + if plano is None: + return None + parts = [f"[{agent_label}] Seu plano é {plano}"] + internet_gb = result.get("internet_gb") + status = result.get("status") + if internet_gb is not None: + parts.append(f"com {internet_gb} GB") + if status is not None: + parts.append(f"status {status}") + return ", ".join(parts) + "." + + +def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + status = result.get("status") + if order_id is None or status is None: + return None + lines = [f"[{agent_label}] Pedido {order_id}: status {status}."] + total = result.get("valor_total") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + value = item.get("descricao") or item.get("nome") or item.get("sku") + else: + value = item + if value not in (None, ""): + rendered_items.append(str(value)) + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + return " ".join(lines) + + +def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + transportadora = result.get("transportadora") + codigo = result.get("codigo_rastreio") + previsao = result.get("previsao_entrega") + if any(v is None for v in (order_id, transportadora, codigo, previsao)): + return None + return ( + f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, " + f"rastreio {codigo}, previsão {previsao}." + ) + + +def register_tool_renderers() -> None: + register_tool_response_renderer("telecom.invoice", render_telecom_invoice) + register_tool_response_renderer("telecom.plan", render_telecom_plan) + register_tool_response_renderer("retail.order", render_retail_order) + register_tool_response_renderer("retail.delivery", render_retail_delivery) diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml index 3c4fd05..9b1ab9e 100644 --- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml index d85fae1..f8a53ca 100644 --- a/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Authentication/agent_template_backend_authentication/config/tools.yaml @@ -10,6 +10,9 @@ tools: - fatura - conta - boleto + response: + mode: renderer + renderer: telecom.invoice consultar_pagamentos: description: Consulta histórico de pagamentos do cliente. mcp_server: telecom @@ -28,6 +31,9 @@ tools: asset_id: string selection_keywords: - plano + response: + mode: renderer + renderer: telecom.plan listar_servicos: description: Lista serviços ativos e adicionais VAS. mcp_server: telecom @@ -49,6 +55,9 @@ tools: - consultar pedido - status do pedido - pedido + response: + mode: renderer + renderer: retail.order consultar_entrega: description: Consulta entrega e rastreamento do pedido. mcp_server: retail @@ -61,6 +70,26 @@ tools: - rastreamento - transportadora - previsão + response: + mode: renderer + renderer: retail.delivery + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..009b7c1 Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py new file mode 100644 index 0000000..f77c47a --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/app/presentation/tool_renderers.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + return f"[{agent_label}] Fatura consultada: {result}." + + +def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + plano = result.get("plano") + if plano is None: + return None + parts = [f"[{agent_label}] Seu plano é {plano}"] + internet_gb = result.get("internet_gb") + status = result.get("status") + if internet_gb is not None: + parts.append(f"com {internet_gb} GB") + if status is not None: + parts.append(f"status {status}") + return ", ".join(parts) + "." + + +def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + status = result.get("status") + if order_id is None or status is None: + return None + lines = [f"[{agent_label}] Pedido {order_id}: status {status}."] + total = result.get("valor_total") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + value = item.get("descricao") or item.get("nome") or item.get("sku") + else: + value = item + if value not in (None, ""): + rendered_items.append(str(value)) + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + return " ".join(lines) + + +def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + transportadora = result.get("transportadora") + codigo = result.get("codigo_rastreio") + previsao = result.get("previsao_entrega") + if any(v is None for v in (order_id, transportadora, codigo, previsao)): + return None + return ( + f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, " + f"rastreio {codigo}, previsão {previsao}." + ) + + +def register_tool_renderers() -> None: + register_tool_response_renderer("telecom.invoice", render_telecom_invoice) + register_tool_response_renderer("telecom.plan", render_telecom_plan) + register_tool_response_renderer("retail.order", render_retail_order) + register_tool_response_renderer("retail.delivery", render_retail_delivery) diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml index 3c4fd05..9b1ab9e 100644 --- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml index d85fae1..f8a53ca 100644 --- a/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Deterministic_Transactional_Workflow/agent_template_backend/config/tools.yaml @@ -10,6 +10,9 @@ tools: - fatura - conta - boleto + response: + mode: renderer + renderer: telecom.invoice consultar_pagamentos: description: Consulta histórico de pagamentos do cliente. mcp_server: telecom @@ -28,6 +31,9 @@ tools: asset_id: string selection_keywords: - plano + response: + mode: renderer + renderer: telecom.plan listar_servicos: description: Lista serviços ativos e adicionais VAS. mcp_server: telecom @@ -49,6 +55,9 @@ tools: - consultar pedido - status do pedido - pedido + response: + mode: renderer + renderer: retail.order consultar_entrega: description: Consulta entrega e rastreamento do pedido. mcp_server: retail @@ -61,6 +70,26 @@ tools: - rastreamento - transportadora - previsão + response: + mode: renderer + renderer: retail.delivery + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py index e6429c4..7a1a9be 100644 --- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/agents/runtime.py @@ -3,5 +3,8 @@ from __future__ import annotations # Compatibilidade local do template/backend. # A implementação oficial agora fica no framework para evitar duplicação entre agentes. from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext +from app.presentation import register_tool_renderers + +register_tool_renderers() __all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"] diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..32cbdf8 Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py new file mode 100644 index 0000000..f77c47a --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + return f"[{agent_label}] Fatura consultada: {result}." + + +def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + plano = result.get("plano") + if plano is None: + return None + parts = [f"[{agent_label}] Seu plano é {plano}"] + internet_gb = result.get("internet_gb") + status = result.get("status") + if internet_gb is not None: + parts.append(f"com {internet_gb} GB") + if status is not None: + parts.append(f"status {status}") + return ", ".join(parts) + "." + + +def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + status = result.get("status") + if order_id is None or status is None: + return None + lines = [f"[{agent_label}] Pedido {order_id}: status {status}."] + total = result.get("valor_total") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + value = item.get("descricao") or item.get("nome") or item.get("sku") + else: + value = item + if value not in (None, ""): + rendered_items.append(str(value)) + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + return " ".join(lines) + + +def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + transportadora = result.get("transportadora") + codigo = result.get("codigo_rastreio") + previsao = result.get("previsao_entrega") + if any(v is None for v in (order_id, transportadora, codigo, previsao)): + return None + return ( + f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, " + f"rastreio {codigo}, previsão {previsao}." + ) + + +def register_tool_renderers() -> None: + register_tool_response_renderer("telecom.invoice", render_telecom_invoice) + register_tool_response_renderer("telecom.plan", render_telecom_plan) + register_tool_response_renderer("retail.order", render_retail_order) + register_tool_response_renderer("retail.delivery", render_retail_delivery) diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml index 66c9854..48a83d5 100644 --- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml index d85fae1..f8a53ca 100644 --- a/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Long_Term_Memory/templates/agent_template_backend/config/tools.yaml @@ -10,6 +10,9 @@ tools: - fatura - conta - boleto + response: + mode: renderer + renderer: telecom.invoice consultar_pagamentos: description: Consulta histórico de pagamentos do cliente. mcp_server: telecom @@ -28,6 +31,9 @@ tools: asset_id: string selection_keywords: - plano + response: + mode: renderer + renderer: telecom.plan listar_servicos: description: Lista serviços ativos e adicionais VAS. mcp_server: telecom @@ -49,6 +55,9 @@ tools: - consultar pedido - status do pedido - pedido + response: + mode: renderer + renderer: retail.order consultar_entrega: description: Consulta entrega e rastreamento do pedido. mcp_server: retail @@ -61,6 +70,26 @@ tools: - rastreamento - transportadora - previsão + response: + mode: renderer + renderer: retail.delivery + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py index e6429c4..7a1a9be 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/agents/runtime.py @@ -3,5 +3,8 @@ from __future__ import annotations # Compatibilidade local do template/backend. # A implementação oficial agora fica no framework para evitar duplicação entre agentes. from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext +from app.presentation import register_tool_renderers + +register_tool_renderers() __all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"] diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..23b52e1 Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py new file mode 100644 index 0000000..f77c47a --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + return f"[{agent_label}] Fatura consultada: {result}." + + +def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + plano = result.get("plano") + if plano is None: + return None + parts = [f"[{agent_label}] Seu plano é {plano}"] + internet_gb = result.get("internet_gb") + status = result.get("status") + if internet_gb is not None: + parts.append(f"com {internet_gb} GB") + if status is not None: + parts.append(f"status {status}") + return ", ".join(parts) + "." + + +def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + status = result.get("status") + if order_id is None or status is None: + return None + lines = [f"[{agent_label}] Pedido {order_id}: status {status}."] + total = result.get("valor_total") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + value = item.get("descricao") or item.get("nome") or item.get("sku") + else: + value = item + if value not in (None, ""): + rendered_items.append(str(value)) + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + return " ".join(lines) + + +def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + transportadora = result.get("transportadora") + codigo = result.get("codigo_rastreio") + previsao = result.get("previsao_entrega") + if any(v is None for v in (order_id, transportadora, codigo, previsao)): + return None + return ( + f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, " + f"rastreio {codigo}, previsão {previsao}." + ) + + +def register_tool_renderers() -> None: + register_tool_response_renderer("telecom.invoice", render_telecom_invoice) + register_tool_response_renderer("telecom.plan", render_telecom_plan) + register_tool_response_renderer("retail.order", render_retail_order) + register_tool_response_renderer("retail.delivery", render_retail_delivery) diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml index 66c9854..48a83d5 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml index d85fae1..f8a53ca 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend/config/tools.yaml @@ -10,6 +10,9 @@ tools: - fatura - conta - boleto + response: + mode: renderer + renderer: telecom.invoice consultar_pagamentos: description: Consulta histórico de pagamentos do cliente. mcp_server: telecom @@ -28,6 +31,9 @@ tools: asset_id: string selection_keywords: - plano + response: + mode: renderer + renderer: telecom.plan listar_servicos: description: Lista serviços ativos e adicionais VAS. mcp_server: telecom @@ -49,6 +55,9 @@ tools: - consultar pedido - status do pedido - pedido + response: + mode: renderer + renderer: retail.order consultar_entrega: description: Consulta entrega e rastreamento do pedido. mcp_server: retail @@ -61,6 +70,26 @@ tools: - rastreamento - transportadora - previsão + response: + mode: renderer + renderer: retail.delivery + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml index 66c9854..48a83d5 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml index d85fae1..2329dc7 100644 --- a/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Normal/templates/agent_template_backend_day_zero/config/tools.yaml @@ -61,6 +61,23 @@ tools: - rastreamento - transportadora - previsão + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md b/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md index bbd3250..f3bc196 100644 --- a/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md +++ b/agent_framework_oci/Tuning-Performance/Offline_Workflow_Regression/README.md @@ -2,7 +2,7 @@ O backend de produção de `WorkflowRuntime` continua sendo **LangGraph**. A ausência do pacote `langgraph` em produção é erro de configuração. -Para builders restritos/offline, o runtime aceita `allow_deterministic_fallback=True`. Esse modo é deliberadamente opt-in e existe somente para exercitar a DSL do framework (actions, edges, condições, pause/resume e trace) de forma reproduzível em testes offline/regressão. Quando `allow_deterministic_fallback=True`, o backend determinístico é selecionado explicitamente mesmo que LangGraph esteja instalado. Ele nunca é selecionado automaticamente em produção. +Para builders restritos/offline, o runtime aceita `allow_deterministic_fallback=True`. Esse modo é deliberadamente opt-in e existe somente para exercitar a DSL do framework (actions, edges, condições, pause/resume e trace) quando a dependência externa não pode ser instalada. Ele não é selecionado automaticamente. Exemplo de teste: diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py index e6429c4..7a1a9be 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/agents/runtime.py @@ -3,5 +3,8 @@ from __future__ import annotations # Compatibilidade local do template/backend. # A implementação oficial agora fica no framework para evitar duplicação entre agentes. from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext +from app.presentation import register_tool_renderers + +register_tool_renderers() __all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"] diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..567eb4d Binary files /dev/null and b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py new file mode 100644 index 0000000..f77c47a --- /dev/null +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + return f"[{agent_label}] Fatura consultada: {result}." + + +def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + plano = result.get("plano") + if plano is None: + return None + parts = [f"[{agent_label}] Seu plano é {plano}"] + internet_gb = result.get("internet_gb") + status = result.get("status") + if internet_gb is not None: + parts.append(f"com {internet_gb} GB") + if status is not None: + parts.append(f"status {status}") + return ", ".join(parts) + "." + + +def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + status = result.get("status") + if order_id is None or status is None: + return None + lines = [f"[{agent_label}] Pedido {order_id}: status {status}."] + total = result.get("valor_total") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + value = item.get("descricao") or item.get("nome") or item.get("sku") + else: + value = item + if value not in (None, ""): + rendered_items.append(str(value)) + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + return " ".join(lines) + + +def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + transportadora = result.get("transportadora") + codigo = result.get("codigo_rastreio") + previsao = result.get("previsao_entrega") + if any(v is None for v in (order_id, transportadora, codigo, previsao)): + return None + return ( + f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, " + f"rastreio {codigo}, previsão {previsao}." + ) + + +def register_tool_renderers() -> None: + register_tool_response_renderer("telecom.invoice", render_telecom_invoice) + register_tool_response_renderer("telecom.plan", render_telecom_plan) + register_tool_response_renderer("retail.order", render_retail_order) + register_tool_response_renderer("retail.delivery", render_retail_delivery) diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml index 66c9854..48a83d5 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml index d85fae1..f8a53ca 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend/config/tools.yaml @@ -10,6 +10,9 @@ tools: - fatura - conta - boleto + response: + mode: renderer + renderer: telecom.invoice consultar_pagamentos: description: Consulta histórico de pagamentos do cliente. mcp_server: telecom @@ -28,6 +31,9 @@ tools: asset_id: string selection_keywords: - plano + response: + mode: renderer + renderer: telecom.plan listar_servicos: description: Lista serviços ativos e adicionais VAS. mcp_server: telecom @@ -49,6 +55,9 @@ tools: - consultar pedido - status do pedido - pedido + response: + mode: renderer + renderer: retail.order consultar_entrega: description: Consulta entrega e rastreamento do pedido. mcp_server: retail @@ -61,6 +70,26 @@ tools: - rastreamento - transportadora - previsão + response: + mode: renderer + renderer: retail.delivery + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml index 66c9854..48a83d5 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml index d85fae1..2329dc7 100644 --- a/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml +++ b/agent_framework_oci/Tuning-Performance/Route_Stickness/templates/agent_template_backend_day_zero/config/tools.yaml @@ -61,6 +61,23 @@ tools: - rastreamento - transportadora - previsão + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/apps/agent_gateway/app/__pycache__/governance_middleware.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/__pycache__/governance_middleware.cpython-313.pyc new file mode 100644 index 0000000..9ef1c4f Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/__pycache__/governance_middleware.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..fffdb5d Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/__pycache__/main.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/__pycache__/settings.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/__pycache__/settings.cpython-313.pyc new file mode 100644 index 0000000..0331ed7 Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/__pycache__/settings.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/config/__pycache__/governance_loader.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/config/__pycache__/governance_loader.cpython-313.pyc new file mode 100644 index 0000000..49664df Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/config/__pycache__/governance_loader.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..9facf87 Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/audit.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/audit.cpython-313.pyc new file mode 100644 index 0000000..1da29c6 Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/audit.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/evaluation_hooks.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/evaluation_hooks.cpython-313.pyc new file mode 100644 index 0000000..473f2bc Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/evaluation_hooks.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/model_policies.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/model_policies.cpython-313.pyc new file mode 100644 index 0000000..1141210 Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/model_policies.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/rate_limit.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/rate_limit.cpython-313.pyc new file mode 100644 index 0000000..507ee7d Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/rate_limit.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/usage.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/usage.cpython-313.pyc new file mode 100644 index 0000000..b102d11 Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/governance/__pycache__/usage.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/agent_gateway/app/routes/__pycache__/governed_proxy_example.cpython-313.pyc b/agent_framework_oci/apps/agent_gateway/app/routes/__pycache__/governed_proxy_example.cpython-313.pyc new file mode 100644 index 0000000..e4d8952 Binary files /dev/null and b/agent_framework_oci/apps/agent_gateway/app/routes/__pycache__/governed_proxy_example.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/mcp_gateway/app/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..0e96f67 Binary files /dev/null and b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/apps/mcp_gateway/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..83107d4 Binary files /dev/null and b/agent_framework_oci/apps/mcp_gateway/app/__pycache__/main.cpython-313.pyc differ diff --git a/agent_framework_oci/docs/features/01_authentication.md b/agent_framework_oci/docs/features/01_authentication.md new file mode 100644 index 0000000..1460cbb --- /dev/null +++ b/agent_framework_oci/docs/features/01_authentication.md @@ -0,0 +1,165 @@ +# Autenticação / Authentication + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `security/authentication.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Verifica quem pode acessar APIs, gateways e serviços protegidos antes que a requisição chegue ao agente. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Cliente/Sistema + ↓ +Authentication Provider + ↓ +credencial válida? + ├─ não → 401/nega acesso + └─ sim → principal autenticado → agente +``` + +### 4. Como funciona internamente + +O framework contém uma abstração `AuthenticationProvider` e implementações para cenários diferentes. Entre as implementações atuais estão `NoAuthenticationProvider`, `DenyAuthenticationProvider`, `BasicAuthenticationProvider`, `ApiKeyAuthenticationProvider`, `StaticBearerAuthenticationProvider`, `JwtAuthenticationProvider`, `OAuth2IntrospectionAuthenticationProvider` e `TrustedProxyAuthenticationProvider`. + +A autenticação produz um `AuthenticatedPrincipal` com `subject`, `scheme` e, quando aplicável, `claims`. A regra de negócio do agente não deve validar senha/token diretamente. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```python +from agent_framework.security.authentication import BasicAuthenticationProvider + +provider = BasicAuthenticationProvider( + client_id="client-a", + secret_hash="pbkdf2_sha256:...", +) +result = await provider.authenticate(request) +if not result.authenticated: + # negar acesso + ... +``` + +Segredos podem ser verificados em formato simples, SHA-256 ou PBKDF2; em produção, prefira hashes fortes e secret stores. + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Basic auth retornando 401: validar `Authorization: Basic ...` e o secret configurado. +- Confundir autenticação do usuário com `OCI_AUTH_MODE`: são problemas diferentes. +- Usar `NoAuthenticationProvider` em produção sem decisão explícita de arquitetura. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/security/authentication.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Checks who may access protected APIs, gateways, and services before the request reaches the agent. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Client/System + ↓ +Authentication Provider + ↓ +valid credential? + ├─ no → 401/deny + └─ yes → authenticated principal → agent +``` + +### 4. How it works internally + +The framework exposes an `AuthenticationProvider` abstraction with multiple implementations. Current providers include `NoAuthenticationProvider`, `DenyAuthenticationProvider`, `BasicAuthenticationProvider`, `ApiKeyAuthenticationProvider`, `StaticBearerAuthenticationProvider`, `JwtAuthenticationProvider`, `OAuth2IntrospectionAuthenticationProvider`, and `TrustedProxyAuthenticationProvider`. + +Authentication produces an `AuthenticatedPrincipal` containing `subject`, `scheme`, and optional `claims`. Domain code should not validate credentials directly. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```python +from agent_framework.security.authentication import BasicAuthenticationProvider + +provider = BasicAuthenticationProvider( + client_id="client-a", + secret_hash="pbkdf2_sha256:...", +) +result = await provider.authenticate(request) +if not result.authenticated: + # deny access + ... +``` + +Secrets may be verified as plain, SHA-256, or PBKDF2 values; for production, prefer strong hashes and managed secret stores. + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Basic auth returns 401: validate the `Authorization: Basic ...` header and configured secret. +- Do not confuse API authentication with `OCI_AUTH_MODE`; they solve different problems. +- Avoid `NoAuthenticationProvider` in production unless explicitly accepted by architecture. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/security/authentication.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/02_deterministic_transactional_workflow.md b/agent_framework_oci/docs/features/02_deterministic_transactional_workflow.md new file mode 100644 index 0000000..5485b94 --- /dev/null +++ b/agent_framework_oci/docs/features/02_deterministic_transactional_workflow.md @@ -0,0 +1,183 @@ +# Workflow Transacional Determinístico / Deterministic Transactional Workflow + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `workflows/runtime.py + mcp/tool_policy.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Garante que operações que alteram estado sigam passos previsíveis, com confirmação e controle de execução, em vez de depender da criatividade do LLM. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Mensagem do cliente + ↓ +LLM entende intenção + ↓ +Tool policy = transactional + ↓ +Workflow determinístico + ↓ +confirmação + ↓ +execução controlada + ↓ +resultado +``` + +### 4. Como funciona internamente + +O LLM pode ajudar a interpretar a intenção e extrair parâmetros, mas não deve decidir a sequência crítica de uma transação. O `ToolPolicyRegistry` classifica tools, e `operation_type: transactional` ativa a política transacional. O `WorkflowRuntime` executa o workflow, mantém estado e integra pause/resume e recuperação de erro. + +A configuração `ENABLE_TRANSACTIONAL_WORKFLOWS` controla a capability global, e `WORKFLOWS_PATH` aponta para os YAMLs. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```yaml +tools: + cancelar_servico: + operation_type: transactional + requires_confirmation: true +``` + +```text +1. localizar serviço +2. validar elegibilidade +3. pedir confirmação +4. PAUSE +5. receber confirmação +6. RESUME +7. executar side effect +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Marcar uma tool de escrita como `read_only` elimina proteções transacionais. +- Reexecutar steps anteriores ao pause pode duplicar side effects; use o runtime oficial. +- Não use prompt como única garantia de confirmação. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Ensures state-changing operations follow predictable steps with confirmation and execution control instead of depending on LLM creativity. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Customer message + ↓ +LLM understands intent + ↓ +Tool policy = transactional + ↓ +Deterministic workflow + ↓ +confirmation + ↓ +controlled execution + ↓ +result +``` + +### 4. How it works internally + +The LLM may help interpret intent and extract parameters, but it should not decide the critical sequence of a transaction. `ToolPolicyRegistry` classifies tools, and `operation_type: transactional` activates transactional behavior. `WorkflowRuntime` executes the workflow, preserves state, and integrates pause/resume and error recovery. + +`ENABLE_TRANSACTIONAL_WORKFLOWS` controls the capability globally, while `WORKFLOWS_PATH` points to workflow YAML files. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```yaml +tools: + cancel_service: + operation_type: transactional + requires_confirmation: true +``` + +```text +1. locate service +2. validate eligibility +3. ask for confirmation +4. PAUSE +5. receive confirmation +6. RESUME +7. execute side effect +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Marking a write tool as `read_only` bypasses transactional protections. +- Re-running steps before a pause can duplicate side effects; use the official runtime. +- Do not use prompts as the only confirmation guarantee. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/03_domain_requested_llm_composition.md b/agent_framework_oci/docs/features/03_domain_requested_llm_composition.md new file mode 100644 index 0000000..4d70018 --- /dev/null +++ b/agent_framework_oci/docs/features/03_domain_requested_llm_composition.md @@ -0,0 +1,157 @@ +# Composição por LLM Solicitada pelo Domínio / Domain Requested LLM Composition + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `runtime/agent_runtime.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Permite que a regra de negócio calcule o resultado e peça ao LLM apenas para redigir a resposta final. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Regra de negócio calcula + ↓ +requires_llm_composition=true + ↓ +framework impede resposta MCP direta + ↓ +LLMProvider oficial + ↓ +redação natural +``` + +### 4. Como funciona internamente + +O domínio retorna dados confiáveis e uma instrução de composição. O `AgentRuntimeMixin` detecta `requires_llm_composition` de forma recursiva no resultado da tool/workflow e não encerra a resposta pelo caminho direto de MCP. A composição segue pelo LLM oficial do agente, preservando profiles, tracing, usage e políticas do framework. + +O LLM deve redigir; ele não deve recalcular valores nem decidir regras de negócio já resolvidas. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```json +{ + "success": true, + "refund_amount": "38,00", + "requires_llm_composition": true, + "response_instruction": "Explique a devolução usando somente os valores calculados." +} +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Instrução muito aberta pode fazer o LLM adicionar conteúdo não autorizado. +- Não envie ao LLM a responsabilidade de recalcular valores determinísticos. +- Se não houver necessidade de redação livre, prefira resposta determinística direta. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Lets domain logic compute the authoritative result and ask the LLM only to compose the final user-facing response. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Domain logic computes + ↓ +requires_llm_composition=true + ↓ +framework prevents direct MCP answer + ↓ +official LLMProvider + ↓ +natural-language response +``` + +### 4. How it works internally + +The domain returns authoritative data plus a composition instruction. `AgentRuntimeMixin` recursively detects `requires_llm_composition` in tool/workflow results and avoids terminating through the direct MCP-answer path. Composition then uses the agent's official LLM provider, preserving profiles, tracing, usage accounting, and framework policies. + +The LLM should compose language, not recalculate values or override already-resolved business rules. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```json +{ + "success": true, + "refund_amount": "38.00", + "requires_llm_composition": true, + "response_instruction": "Explain the refund using only the computed values." +} +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- An overly broad instruction may let the LLM add unauthorized content. +- Do not delegate deterministic calculations back to the LLM. +- If free-form wording is unnecessary, prefer a deterministic direct response. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/04_domain_requested_rag.md b/agent_framework_oci/docs/features/04_domain_requested_rag.md new file mode 100644 index 0000000..745397f --- /dev/null +++ b/agent_framework_oci/docs/features/04_domain_requested_rag.md @@ -0,0 +1,167 @@ +# RAG Solicitado pelo Domínio / Domain Requested RAG + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `runtime/agent_runtime.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Permite que uma tool ou workflow declare que a resposta precisa consultar conhecimento externo, mesmo quando já existe resultado MCP. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Tool/Workflow + ↓ +requires_rag=true + ↓ +rag_query / rag_queries + ↓ +RagService do framework + ↓ +Retrieval Guardrails + ↓ +LLM/resposta +``` + +### 4. Como funciona internamente + +Normalmente o framework pode pular RAG quando MCP já trouxe informação suficiente (`SKIP_RAG_WHEN_MCP_SUFFICIENT`). Esta feature permite que o domínio substitua essa decisão para um caso específico. O resultado pode declarar `requires_rag`, `rag_query` ou `rag_queries`; o runtime usa essas queries como override e executa o `RagService`. + +O domínio informa **o que precisa saber**. Ele não implementa cliente de vetor, retriever ou prompt RAG paralelo. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```json +{ + "requires_rag": true, + "rag_queries": [ + "Como cancelar YouTube Premium?", + "Como cancelar Aya Books?" + ] +} +``` + +Configurações relacionadas incluem `RAG_TOP_K` e `SKIP_RAG_WHEN_MCP_SUFFICIENT`. + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Declarar RAG para fatos transacionais já resolvidos pela API pode aumentar custo e latência. +- Query genérica demais reduz relevância. +- Nunca confie no retrieval sem `Retrieval Guardrails` quando o dado influencia resposta crítica. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Allows a tool or workflow to declare that external knowledge retrieval is required even when an MCP result already exists. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Tool/Workflow + ↓ +requires_rag=true + ↓ +rag_query / rag_queries + ↓ +framework RagService + ↓ +Retrieval Guardrails + ↓ +LLM/response +``` + +### 4. How it works internally + +Normally the framework may skip RAG when MCP already provides sufficient data (`SKIP_RAG_WHEN_MCP_SUFFICIENT`). This feature lets the domain override that decision for a specific case. A result may declare `requires_rag`, `rag_query`, or `rag_queries`; the runtime uses those queries as overrides and invokes `RagService`. + +The domain declares **what knowledge is needed**. It does not implement its own vector client, retriever, or parallel RAG prompt stack. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```json +{ + "requires_rag": true, + "rag_queries": [ + "How to cancel YouTube Premium?", + "How to cancel Aya Books?" + ] +} +``` + +Related settings include `RAG_TOP_K` and `SKIP_RAG_WHEN_MCP_SUFFICIENT`. + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Requesting RAG for transactional facts already resolved by an API adds unnecessary cost and latency. +- Queries that are too broad reduce relevance. +- Do not trust retrieved content for critical responses without Retrieval Guardrails. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/05_long_term_memory.md b/agent_framework_oci/docs/features/05_long_term_memory.md new file mode 100644 index 0000000..e9e43e3 --- /dev/null +++ b/agent_framework_oci/docs/features/05_long_term_memory.md @@ -0,0 +1,167 @@ +# Memória de Longo Prazo / Long Term Memory + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `memory/long_term_memory.py + memory/long_term_store.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Permite lembrar informações úteis entre sessões diferentes, sem depender do histórico completo de uma conversa. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Sessão A + ↓ +extração de memória relevante + ↓ +Long Term Memory Store + ↓ +... dias depois ... + ↓ +Sessão B + ↓ +recupera contexto relevante + ↓ +agente +``` + +### 4. Como funciona internamente + +A memória de longo prazo é diferente de histórico de mensagens e de checkpoint. Ela persiste fatos/preferências úteis e os recupera como contexto de uma nova sessão. O framework oferece providers `memory`, `sqlite`, `autonomous` e `oracle`. + +Configurações importantes: `ENABLE_LONG_TERM_MEMORY`, `LONG_TERM_MEMORY_PROVIDER`, `LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS`, `LONG_TERM_MEMORY_MIN_CONFIDENCE`, `LONG_TERM_MEMORY_AUTO_EXTRACT` e `LONG_TERM_MEMORY_INJECT_CONTEXT`. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```env +ENABLE_LONG_TERM_MEMORY=true +LONG_TERM_MEMORY_PROVIDER=oracle +LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20 +LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70 +LONG_TERM_MEMORY_AUTO_EXTRACT=true +LONG_TERM_MEMORY_INJECT_CONTEXT=true +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Não confundir LTM com replay de toda conversa. +- Memória irrelevante ou de baixa confiança não deveria ser injetada. +- Em múltiplas réplicas, prefira storage durável compartilhado em vez de memória local. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/memory/long_term_memory.py` +- `libs/agent_framework/src/agent_framework/memory/long_term_store.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Allows useful information to persist across different sessions without depending on the full transcript of a previous conversation. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Session A + ↓ +extract relevant memory + ↓ +Long Term Memory Store + ↓ +... days later ... + ↓ +Session B + ↓ +retrieve relevant context + ↓ +agent +``` + +### 4. How it works internally + +Long-term memory is different from message history and checkpoints. It persists useful facts/preferences and retrieves them as context for a future session. The framework supports `memory`, `sqlite`, `autonomous`, and `oracle` providers. + +Important settings include `ENABLE_LONG_TERM_MEMORY`, `LONG_TERM_MEMORY_PROVIDER`, `LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS`, `LONG_TERM_MEMORY_MIN_CONFIDENCE`, `LONG_TERM_MEMORY_AUTO_EXTRACT`, and `LONG_TERM_MEMORY_INJECT_CONTEXT`. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```env +ENABLE_LONG_TERM_MEMORY=true +LONG_TERM_MEMORY_PROVIDER=oracle +LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20 +LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70 +LONG_TERM_MEMORY_AUTO_EXTRACT=true +LONG_TERM_MEMORY_INJECT_CONTEXT=true +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Do not confuse LTM with replaying the entire transcript. +- Irrelevant or low-confidence memories should not be injected. +- For multiple replicas, prefer shared durable storage over local memory. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/memory/long_term_memory.py` +- `libs/agent_framework/src/agent_framework/memory/long_term_store.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/06_offline_workflow_regression.md b/agent_framework_oci/docs/features/06_offline_workflow_regression.md new file mode 100644 index 0000000..5569f63 --- /dev/null +++ b/agent_framework_oci/docs/features/06_offline_workflow_regression.md @@ -0,0 +1,163 @@ +# Regressão Offline de Workflow / Offline Workflow Regression + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `workflows/runtime.py + Tuning-Performance/Offline_Workflow_Regression` + +--- + +## Português (PT-BR) + +### 1. O que é + +Permite testar a lógica de workflows sem exigir toda a infraestrutura de produção. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Teste + ↓ +backend determinístico explicitamente habilitado + ↓ +run → PAUSED + ↓ +resume → COMPLETED + ↓ +asserts de estado/side effects +``` + +### 4. Como funciona internamente + +O `WorkflowRuntime` possui um caminho determinístico/offline **explicitamente opt-in para testes**. Ele permite validar DSL, condições, pause/resume e proteção contra reexecução sem exigir LangGraph, banco, OCI ou APIs externas. + +O comportamento de produção continua usando LangGraph. O modo offline não deve virar fallback silencioso quando LangGraph falha ou está ausente em produção. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```text +run(workflow) + action_a = 1 execução + status = PAUSED + +resume(workflow) + action_a continua com 1 execução + action_b = 1 execução + status = COMPLETED +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Usar o backend offline em produção mascara problemas reais. +- Mockar tanto que o teste deixa de validar a DSL real. +- Não verificar side effects anteriores ao pause pode esconder duplicações. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `libs/agent_framework/src/agent_framework/Tuning-Performance/Offline_Workflow_Regression` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Allows workflow logic to be regression-tested without requiring the full production infrastructure. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Test + ↓ +explicit deterministic test backend + ↓ +run → PAUSED + ↓ +resume → COMPLETED + ↓ +state/side-effect assertions +``` + +### 4. How it works internally + +`WorkflowRuntime` includes an **explicitly opt-in deterministic/offline test backend**. It can validate DSL rules, conditions, pause/resume behavior, and duplicate-execution protection without requiring LangGraph, a database, OCI, or external APIs. + +Production behavior still uses LangGraph. Offline mode must never become a silent fallback when LangGraph fails or is unavailable in production. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```text +run(workflow) + action_a = executed once + status = PAUSED + +resume(workflow) + action_a remains executed once + action_b = executed once + status = COMPLETED +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Using the offline backend in production hides real issues. +- Over-mocking can stop the test from validating real DSL behavior. +- Failing to assert pre-pause side effects may hide duplicate execution. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `libs/agent_framework/src/agent_framework/Tuning-Performance/Offline_Workflow_Regression` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/07_pause_resume_workflow.md b/agent_framework_oci/docs/features/07_pause_resume_workflow.md new file mode 100644 index 0000000..b600937 --- /dev/null +++ b/agent_framework_oci/docs/features/07_pause_resume_workflow.md @@ -0,0 +1,165 @@ +# Pause / Resume de Workflow / Pause / Resume Workflow + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `workflows/runtime.py + workflows/graph.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Permite interromper um workflow em um ponto seguro, persistir o estado e continuar depois com a resposta do usuário ou outro evento. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Workflow + ↓ +ações prévias + ↓ +PAUSE + ↓ +checkpoint/estado + ↓ +nova mensagem + ↓ +RESUME + ↓ +ações seguintes +``` + +### 4. Como funciona internamente + +`WorkflowRuntime` expõe `arun(...)` e `aresume(...)`. O nó de pause é separado da action anterior para evitar reexecutar side effects quando o workflow retoma. O mesmo `execution_id/thread_id` identifica a execução pausada e retomada. + +O runtime suporta condições declarativas como `all`, `any`, `not`, `eq`, `neq` e `exists`, permitindo definir quando pausar ou continuar sem colocar lógica conversacional no prompt. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```text +status = await runtime.arun(...) +# status == PAUSED + +status = await runtime.aresume(execution_id, input={"confirmed": true}) +# status == COMPLETED +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Perder o `execution_id` impede retomar a execução correta. +- Reexecutar o workflow do zero após confirmação pode repetir side effects. +- Pause sem storage/checkpoint compartilhado é frágil em múltiplas réplicas. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `libs/agent_framework/src/agent_framework/workflows/graph.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Allows a workflow to stop at a safe point, persist state, and continue later using user input or another event. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +Workflow + ↓ +pre-pause actions + ↓ +PAUSE + ↓ +checkpoint/state + ↓ +new message + ↓ +RESUME + ↓ +remaining actions +``` + +### 4. How it works internally + +`WorkflowRuntime` exposes `arun(...)` and `aresume(...)`. The pause node is separated from the preceding action so previous side effects are not executed again on resume. The same `execution_id/thread_id` identifies the paused and resumed execution. + +The runtime supports declarative conditions such as `all`, `any`, `not`, `eq`, `neq`, and `exists`, so pause/continue decisions do not need to live in the prompt. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```text +status = await runtime.arun(...) +# status == PAUSED + +status = await runtime.aresume(execution_id, input={"confirmed": true}) +# status == COMPLETED +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Losing the `execution_id` prevents resuming the right execution. +- Restarting the workflow from scratch after confirmation may duplicate side effects. +- Pause without shared checkpoint/state storage is fragile across multiple replicas. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `libs/agent_framework/src/agent_framework/workflows/graph.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/08_route_stickiness.md b/agent_framework_oci/docs/features/08_route_stickiness.md new file mode 100644 index 0000000..264a17e --- /dev/null +++ b/agent_framework_oci/docs/features/08_route_stickiness.md @@ -0,0 +1,151 @@ +# Aderência de Rota / Route Stickiness + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `routing/enterprise_router.py + runtime/agent_runtime.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Evita que pequenas mensagens de continuação façam a conversa trocar de agente sem necessidade. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +mensagem atual + + histórico curto + + rota anterior + ↓ +continuidade semântica + ↓ +manter rota ou handoff +``` + +### 4. Como funciona internamente + +Route Stickiness avalia se a nova mensagem continua semanticamente ligada ao assunto/agente atual. Isso reduz ping-pong de agentes em mensagens como “e esse valor?”, “sim”, “o segundo” ou “e no mês passado?”. + +Configurações existentes incluem `ENABLE_ROUTE_STICKINESS`, `ROUTE_STICKINESS_LLM_PROFILE`, `ROUTE_STICKINESS_CONFIDENCE_THRESHOLD`, `ROUTE_STICKINESS_HISTORY_TURNS` e `ROUTE_STICKINESS_MAX_TOKENS`. A decisão pode permitir handoff quando há evidência suficiente de mudança de assunto. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```env +ENABLE_ROUTE_STICKINESS=true +ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90 +ROUTE_STICKINESS_HISTORY_TURNS=2 +ROUTE_STICKINESS_MAX_TOKENS=80 +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Threshold muito baixo pode prender o cliente no agente errado. +- Threshold alto demais perde continuidade em mensagens curtas. +- Stickiness não deve bloquear handoff explícito quando a intenção realmente mudou. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/routing/enterprise_router.py` +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Prevents short follow-up messages from unnecessarily switching the conversation to another agent. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +current message + + short history + + previous route + ↓ +semantic continuity + ↓ +keep route or handoff +``` + +### 4. How it works internally + +Route Stickiness evaluates whether a new message is semantically continuous with the current subject/agent. It reduces agent ping-pong for messages such as “what about that amount?”, “yes”, “the second one”, or “and last month?”. + +Existing settings include `ENABLE_ROUTE_STICKINESS`, `ROUTE_STICKINESS_LLM_PROFILE`, `ROUTE_STICKINESS_CONFIDENCE_THRESHOLD`, `ROUTE_STICKINESS_HISTORY_TURNS`, and `ROUTE_STICKINESS_MAX_TOKENS`. The decision may still allow handoff when there is enough evidence of a topic change. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```env +ENABLE_ROUTE_STICKINESS=true +ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90 +ROUTE_STICKINESS_HISTORY_TURNS=2 +ROUTE_STICKINESS_MAX_TOKENS=80 +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- A threshold that is too low may trap the user on the wrong agent. +- A threshold that is too high may lose continuity on short follow-ups. +- Stickiness should not block explicit handoff when the user clearly changes intent. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/routing/enterprise_router.py` +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/09_voice_interruption_replay.md b/agent_framework_oci/docs/features/09_voice_interruption_replay.md new file mode 100644 index 0000000..85bd363 --- /dev/null +++ b/agent_framework_oci/docs/features/09_voice_interruption_replay.md @@ -0,0 +1,153 @@ +# Replay em Interrupções de Voz / Voice Interruption Replay + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `channels/interruption.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Decide se um áudio recebido durante a fala do agente representa uma nova intenção, um ruído/backchannel ou algo que deve apenas repetir/continuar a última fala. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +áudio durante fala + ↓ +InterruptionPolicy + ├─ process → nova mensagem + ├─ classify → classificador leve + └─ replay → última fala +``` + +### 4. Como funciona internamente + +A política fica no framework, não no domínio. Ela diferencia sessão terminal, `idle_nudge`, fala não interrompível e fala potencialmente interrompível. Quando necessário, pode usar um classificador leve baseado no `LLMProvider`; quando a classificação falha, a política é conservadora e pode optar por replay. + +O objetivo é evitar que “aham”, ruído, eco ou fragmentos residuais sejam tratados como uma nova intenção completa. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```text +Agente: "Sua fatura possui..." +Cliente: "aham" +→ replay/continua + +Agente: "Sua fatura possui..." +Cliente: "espera, quero falar de outra coisa" +→ processa nova intenção +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Classificar todo ruído com LLM aumenta latência e custo. +- Permitir interrupção em fala transacional não interrompível pode corromper UX/estado. +- Replay deve usar uma fala real anterior, não um envelope técnico. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/channels/interruption.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Decides whether audio received while the agent is speaking represents a new intent, a backchannel/noise event, or something that should simply replay/continue the previous speech. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +audio during speech + ↓ +InterruptionPolicy + ├─ process → new message + ├─ classify → lightweight classifier + └─ replay → previous speech +``` + +### 4. How it works internally + +The policy lives in the framework rather than domain code. It distinguishes terminal sessions, `idle_nudge`, non-interruptible speech, and potentially interruptible speech. When needed, it may use a lightweight classifier backed by `LLMProvider`; on classification failure, it can fail safely to replay. + +The goal is to prevent “uh-huh”, noise, echo, or residual audio fragments from being interpreted as a full new intent. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```text +Agent: "Your invoice contains..." +User: "uh-huh" +→ replay/continue + +Agent: "Your invoice contains..." +User: "wait, I want to ask something else" +→ process new intent +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Sending every noise fragment to an LLM increases latency and cost. +- Allowing interruption during non-interruptible transactional speech may corrupt UX/state. +- Replay should use a real previous utterance, not a technical envelope. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/channels/interruption.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/10_workflow_error_recovery.md b/agent_framework_oci/docs/features/10_workflow_error_recovery.md new file mode 100644 index 0000000..e5ef8d6 --- /dev/null +++ b/agent_framework_oci/docs/features/10_workflow_error_recovery.md @@ -0,0 +1,167 @@ +# Recuperação de Erro em Workflow / Workflow Error Recovery + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `workflows/runtime.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Preserva o estado parcial de uma execução quando um passo posterior falha, permitindo entender o que já aconteceu e evitar repetir side effects. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +passo A ✅ +passo B ✅ +passo C ❌ + ↓ +FAILED + snapshot parcial + ↓ +recovery decide o que pode continuar/repetir +``` + +### 4. Como funciona internamente + +O runtime preserva o snapshot parcial do LangGraph quando uma etapa posterior falha e produz `error_details` genérico. Quando a exceção externa possui informações estruturadas, podem ser preservados status HTTP, body, número de tentativas, code e metadata. + +A feature não significa “tentar tudo de novo”. Recuperação segura depende de conhecer o estado já executado, a idempotência e a natureza do erro. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```json +{ + "status": "FAILED", + "error_details": { + "status": 503, + "attempts": 3, + "code": "UPSTREAM_UNAVAILABLE" + }, + "state": { + "protocol_created": true, + "operation_completed": true, + "sms_sent": false + } +} +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Retry indiscriminado pode repetir transações. +- Se a exceção externa perde metadata, a recuperação fica menos precisa. +- Combine sempre com Durable Idempotency em side effects críticos. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Preserves partial execution state when a later step fails, making it possible to know what already happened and avoid repeating side effects. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +step A ✅ +step B ✅ +step C ❌ + ↓ +FAILED + partial snapshot + ↓ +recovery decides what may continue/retry +``` + +### 4. How it works internally + +The runtime preserves the partial LangGraph snapshot when a later step fails and produces generic `error_details`. When an external exception provides structured information, HTTP status, body, attempt count, code, and metadata may be preserved. + +This feature does not mean “retry everything”. Safe recovery depends on knowing what already executed, idempotency guarantees, and the nature of the failure. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```json +{ + "status": "FAILED", + "error_details": { + "status": 503, + "attempts": 3, + "code": "UPSTREAM_UNAVAILABLE" + }, + "state": { + "protocol_created": true, + "operation_completed": true, + "sms_sent": false + } +} +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Blind retries may repeat transactions. +- If external exceptions discard metadata, recovery becomes less precise. +- Always combine with Durable Idempotency for critical side effects. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/workflows/runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/11_clarification.md b/agent_framework_oci/docs/features/11_clarification.md new file mode 100644 index 0000000..6ef6a15 --- /dev/null +++ b/agent_framework_oci/docs/features/11_clarification.md @@ -0,0 +1,169 @@ +# Clarificação / Clarification + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `runtime/agent_runtime.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Quando faltam dados ou uma tool encontra múltiplas opções, o framework pergunta ao usuário em vez de adivinhar. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +pedido ambíguo + ↓ +NEEDS_CLARIFICATION + ↓ +pergunta + opções + ↓ +usuário responde + ↓ +framework resolve + ↓ +retoma mesma tool/workflow +``` + +### 4. Como funciona internamente + +O runtime suporta clarificação tanto de parâmetros faltantes quanto de resultados de tools. Para tool-result clarification, um resultado com `status: NEEDS_CLARIFICATION` pode trazer opções; o runtime persiste `pending_tool_clarification`, entra em `TOOL_RESULT_CLARIFICATION` e consegue resolver respostas por ordinal ou nome. + +Depois da escolha, o framework reutiliza a mesma tool e injeta os argumentos resolvidos, evitando que o roteador trate a resposta curta como uma intenção nova. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```json +{ + "status": "NEEDS_CLARIFICATION", + "question": "Qual serviço?", + "options": [ + {"id": "tim_music", "label": "TIM Music"}, + {"id": "hbo_max", "label": "HBO Max"} + ] +} +``` + +Usuário: `o segundo` → `hbo_max`. + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Não descarte `pending_tool_clarification` entre turns. +- Uma resposta curta deve ser resolvida contra as opções antes do roteamento normal. +- Opções sem identificador/label consistente pioram a resolução. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +When required information is missing or a tool finds multiple options, the framework asks the user instead of guessing. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +ambiguous request + ↓ +NEEDS_CLARIFICATION + ↓ +question + options + ↓ +user answers + ↓ +framework resolves + ↓ +resume same tool/workflow +``` + +### 4. How it works internally + +The runtime supports clarification for both missing parameters and ambiguous tool results. For tool-result clarification, a result with `status: NEEDS_CLARIFICATION` may include options; the runtime persists `pending_tool_clarification`, moves to `TOOL_RESULT_CLARIFICATION`, and can resolve responses by ordinal or name. + +After selection, the framework reuses the same tool and injects resolved arguments, preventing the router from treating a short reply as a brand-new intent. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```json +{ + "status": "NEEDS_CLARIFICATION", + "question": "Which service?", + "options": [ + {"id": "tim_music", "label": "TIM Music"}, + {"id": "hbo_max", "label": "HBO Max"} + ] +} +``` + +User: `the second one` → `hbo_max`. + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Do not discard `pending_tool_clarification` between turns. +- A short answer should be resolved against pending options before normal routing. +- Options without stable identifiers/labels reduce resolution quality. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/12_durable_idempotency.md b/agent_framework_oci/docs/features/12_durable_idempotency.md new file mode 100644 index 0000000..98969e3 --- /dev/null +++ b/agent_framework_oci/docs/features/12_durable_idempotency.md @@ -0,0 +1,163 @@ +# Idempotência Durável / Durable Idempotency + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `idempotency.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Impede que a mesma operação crítica seja executada duas vezes, inclusive quando outra réplica/pod recebe a repetição. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +requisição + ↓ +idempotency key + ↓ +store durável + ├─ existe → retorna resultado anterior + └─ não existe → executa → persiste resultado +``` + +### 4. Como funciona internamente + +`create_idempotency_store(settings, ...)` escolhe o backend conforme configuração/plataforma. O framework possui `IdempotencyStore` e `InMemoryIdempotencyStore`, mas produção distribuída deve preferir storage compartilhado. As configurações incluem `IDEMPOTENCY_PROVIDER`, `IDEMPOTENCY_REQUIRE_DURABLE` e `IDEMPOTENCY_TTL_SECONDS`. + +Idempotência é diferente de retry: retry repete a tentativa; idempotência garante que a repetição não produza um novo side effect. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```text +Pod A recebe cancelamento +→ key=cliente:servico:operacao +→ executa +→ grava resultado + +Pod A cai + +Pod B recebe retry +→ mesma key +→ encontra resultado +→ NÃO cancela de novo +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Usar store em memória com múltiplos pods não é idempotência durável. +- Chave ampla demais pode bloquear operações legítimas; estreita demais permite duplicidade. +- TTL deve ser compatível com a janela real de retry/replay. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/idempotency.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Prevents the same critical operation from executing twice, including when a retry lands on another replica/pod. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +request + ↓ +idempotency key + ↓ +durable store + ├─ exists → return previous result + └─ missing → execute → persist result +``` + +### 4. How it works internally + +`create_idempotency_store(settings, ...)` chooses a backend according to configuration/platform. The framework provides `IdempotencyStore` and `InMemoryIdempotencyStore`, but distributed production should prefer shared storage. Settings include `IDEMPOTENCY_PROVIDER`, `IDEMPOTENCY_REQUIRE_DURABLE`, and `IDEMPOTENCY_TTL_SECONDS`. + +Idempotency is different from retry: retry repeats an attempt; idempotency guarantees that repetition does not create another side effect. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```text +Pod A receives cancellation +→ key=customer:service:operation +→ executes +→ stores result + +Pod A crashes + +Pod B receives retry +→ same key +→ finds stored result +→ DOES NOT cancel again +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- An in-memory store across multiple pods is not durable idempotency. +- A key that is too broad may block legitimate operations; too narrow may allow duplicates. +- TTL should match the real retry/replay window. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/idempotency.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/13_dynamic_transaction_states.md b/agent_framework_oci/docs/features/13_dynamic_transaction_states.md new file mode 100644 index 0000000..6cd06ce --- /dev/null +++ b/agent_framework_oci/docs/features/13_dynamic_transaction_states.md @@ -0,0 +1,157 @@ +# Estados Transacionais Dinâmicos / Dynamic Transaction States + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `runtime/agent_runtime.py + mcp/tool_policy.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Permite criar estados de confirmação baseados no agente/domínio atual sem hardcode de todos os domínios dentro do framework. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +tool transactional + ↓ +agente/domínio atual + ↓ +WAITING__CONFIRMATION + ↓ +confirmação/rejeição + ↓ +estado seguinte +``` + +### 4. Como funciona internamente + +Em vez de manter estados fixos como `WAITING_BILLING_CONFIRMATION`, `WAITING_PRODUCT_CONFIRMATION` etc. para cada domínio conhecido, o runtime deriva o prefixo do agente atual e gera o estado dinamicamente. A função interna de estado transacional mantém o framework genérico. + +A classificação `operation_type` aceita `read_only`, `transactional`, `conversational` e `internal`; somente `transactional` entra no caminho de confirmação transacional. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```text +VasAgent + cancelar_vas +→ WAITING_VAS_CONFIRMATION + +AddressAgent + alterar_endereco +→ WAITING_ADDRESS_CONFIRMATION +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Hardcode de estados no domínio reduz reutilização. +- Classificar uma tool como `conversational` não deve ativar confirmação transacional. +- Mudanças no identificador do agente podem mudar o prefixo; mantenha IDs estáveis. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Allows confirmation states to be derived from the current agent/domain instead of hardcoding every business domain into the framework. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +transactional tool + ↓ +current agent/domain + ↓ +WAITING__CONFIRMATION + ↓ +confirm/reject + ↓ +next state +``` + +### 4. How it works internally + +Instead of maintaining fixed states such as `WAITING_BILLING_CONFIRMATION`, `WAITING_PRODUCT_CONFIRMATION`, and so on for every known domain, the runtime derives a prefix from the current agent and builds the confirmation state dynamically. This keeps the framework generic. + +`operation_type` accepts `read_only`, `transactional`, `conversational`, and `internal`; only `transactional` enters the transactional confirmation path. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```text +VasAgent + cancel_vas +→ WAITING_VAS_CONFIRMATION + +AddressAgent + change_address +→ WAITING_ADDRESS_CONFIRMATION +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Hardcoding states in domain code reduces reuse. +- Classifying a tool as `conversational` should not trigger transactional confirmation. +- Changing agent identifiers may change state prefixes; keep IDs stable. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` +- `libs/agent_framework/src/agent_framework/mcp/tool_policy.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/14_post_finalization_replay.md b/agent_framework_oci/docs/features/14_post_finalization_replay.md new file mode 100644 index 0000000..8175336 --- /dev/null +++ b/agent_framework_oci/docs/features/14_post_finalization_replay.md @@ -0,0 +1,159 @@ +# Replay Após Finalização / Post Finalization Replay + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `channels/interruption.py + config/settings.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Evita que áudio residual ou mensagens tardias reabram uma sessão já finalizada. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +sessão terminal + ↓ +entrada residual + ↓ +policy detecta finalização + ↓ +replay última fala/fallback + ↓ +NÃO reabre LangGraph +``` + +### 4. Como funciona internamente + +A política de interrupção verifica metadata de sessão terminal antes de tratar uma entrada como nova intenção. Quando há texto terminal disponível, usa `last_assistant_text`/`terminal_replay_text`; caso contrário, pode usar a mensagem configurada em `POST_FINALIZE_REPLAY_MESSAGE`. + +O objetivo é proteger o fechamento lógico da sessão, especialmente em canais de voz onde pacotes de áudio podem chegar depois do evento de finalização. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```text +Agente: "Atendimento concluído." +→ sessão finalizada + +chega fragmento: "ã..." +→ replay "Atendimento concluído." +→ nenhum routing / tool / LLM novo +``` + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Se o estado terminal não for persistido, outra réplica pode reabrir a jornada. +- Não use replay técnico/JSON como fala do cliente. +- Essa feature não substitui política de nova sessão intencional. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/channels/interruption.py` +- `libs/agent_framework/src/agent_framework/config/settings.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Prevents residual audio or late messages from reopening a session that has already been finalized. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +terminal session + ↓ +residual input + ↓ +policy detects finalization + ↓ +replay last utterance/fallback + ↓ +DO NOT reopen LangGraph +``` + +### 4. How it works internally + +The interruption policy checks terminal-session metadata before treating an input as a new intent. When terminal speech is available, it uses `last_assistant_text`/`terminal_replay_text`; otherwise it may use `POST_FINALIZE_REPLAY_MESSAGE`. + +The purpose is to protect the logical end of a session, especially on voice channels where audio packets may arrive after the finalization event. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```text +Agent: "The interaction is complete." +→ session finalized + +late fragment arrives: "uh..." +→ replay "The interaction is complete." +→ no new routing / tool / LLM call +``` + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- If terminal state is not persisted, another replica may reopen the journey. +- Do not replay technical/JSON envelopes as user-facing speech. +- This feature does not replace an intentional new-session policy. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/channels/interruption.py` +- `libs/agent_framework/src/agent_framework/config/settings.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/15_retrieval_tool_guardrails.md b/agent_framework_oci/docs/features/15_retrieval_tool_guardrails.md new file mode 100644 index 0000000..0a565a0 --- /dev/null +++ b/agent_framework_oci/docs/features/15_retrieval_tool_guardrails.md @@ -0,0 +1,171 @@ +# Guardrails de Retrieval e Tools / Retrieval / Tool Guardrails + +> Feature do `agent_framework_oci` — guia bilíngue PT-BR / EN. + +**Implementação principal / Main implementation:** `guardrails/pipeline.py + guardrails/rails.py` + +--- + +## Português (PT-BR) + +### 1. O que é + +Aplica proteção não apenas na mensagem do usuário e na resposta final, mas também no conhecimento recuperado por RAG e nos argumentos/resultados de ferramentas. + +### 2. Problema que resolve + +Em agentes de produção, não é suficiente pedir ao LLM que “faça a coisa certa”. Esta feature move uma responsabilidade específica para uma camada controlada do framework, reduzindo comportamento imprevisível e código duplicado nos agentes de domínio. + +### 3. Fluxo simplificado + +```text +Usuário + ↓ +Input Guardrails + ↓ +RAG → Retrieval Guardrails + ↓ +LLM/Tool call → Tool Guardrails + ↓ +API + ↓ +Output Guardrails +``` + +### 4. Como funciona internamente + +O framework possui stages distintos de guardrails. Para retrieval, rails como `RAGSEC` e `RET_REL` podem validar segurança e relevância do conteúdo recuperado. Para tools, `TOOL_VAL` valida o uso/argumentos antes ou ao redor da execução. + +As configurações globais incluem `ENABLE_INPUT_GUARDRAILS`, `ENABLE_OUTPUT_GUARDRAILS`, `ENABLE_PARALLEL_GUARDRAILS`, `GUARDRAILS_FAIL_FAST` e `GUARDRAILS_CONFIG_PATH`. O YAML é a fonte de verdade dos rails ativados por agente. + +### 5. Como ativar/configurar + +A ativação exata depende do template/agente. Verifique o arquivo de settings, YAMLs de configuração e o template usado pelo serviço. Nem toda feature precisa de uma flag global: algumas são ativadas pelo contrato retornado por uma tool/workflow. + +### 6. Exemplo + +```yaml +retrieval: + rails: + - RAGSEC + - RET_REL + +tool: + rails: + - TOOL_VAL +``` + +Exemplo: a pergunta é sobre cancelamento de um serviço, mas o RAG retorna documentação de modem. `RET_REL` pode rejeitar o contexto antes que ele seja usado na resposta. + +### 7. Telemetria e observabilidade + +Quando a feature participa de uma execução de agente, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id` e demais chaves de correlação no estado/eventos. Isso permite acompanhar a decisão no Langfuse/Observer sem colocar lógica de observabilidade dentro do domínio. + +### 8. Como testar + +1. Crie um teste unitário do comportamento principal. +2. Crie um teste de integração do runtime quando houver estado entre turns. +3. Verifique o caso feliz e pelo menos um caso de falha/negação. +4. Confirme que não há side effects duplicados em retry/replay quando a feature toca transações. +5. Em produção, valide também telemetria e correlação de IDs. + +### 9. Erros comuns + +- Ter a implementação do rail não significa que ele está ativo: confira `guardrails.yaml`. +- Fail-fast deve ser escolhido conscientemente para cada stage. +- Tool guardrail não substitui validação de negócio dentro da própria API/action. + +### 10. Relação com outras features + +Esta feature deve ser usada junto das demais capacidades horizontais do framework, em vez de criar uma implementação paralela no agente de domínio. Em fluxos transacionais, considere especialmente **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery** e **Guardrails**. + +### 11. Referências no repositório + +- `libs/agent_framework/src/agent_framework/guardrails/pipeline.py` +- `libs/agent_framework/src/agent_framework/guardrails/rails.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` + +--- + +## English (EN) + +### 1. What it is + +Applies safety and validation not only to user input and final output, but also to RAG-retrieved knowledge and tool arguments/results. + +### 2. Problem it solves + +Production agents should not rely on prompts alone to “do the right thing”. This feature moves a specific responsibility into a controlled framework layer, reducing unpredictable behavior and duplicate domain-agent code. + +### 3. Simplified flow + +```text +User + ↓ +Input Guardrails + ↓ +RAG → Retrieval Guardrails + ↓ +LLM/Tool call → Tool Guardrails + ↓ +API + ↓ +Output Guardrails +``` + +### 4. How it works internally + +The framework has distinct guardrail stages. For retrieval, rails such as `RAGSEC` and `RET_REL` can validate retrieved-content safety and relevance. For tools, `TOOL_VAL` validates usage/arguments before or around execution. + +Global settings include `ENABLE_INPUT_GUARDRAILS`, `ENABLE_OUTPUT_GUARDRAILS`, `ENABLE_PARALLEL_GUARDRAILS`, `GUARDRAILS_FAIL_FAST`, and `GUARDRAILS_CONFIG_PATH`. The agent YAML is the source of truth for enabled rails. + +### 5. How to enable/configure + +Exact activation depends on the template/agent. Check framework settings, YAML configuration, and the service template. Not every feature requires a global flag: some are activated by the contract returned from a tool/workflow. + +### 6. Example + +```yaml +retrieval: + rails: + - RAGSEC + - RET_REL + +tool: + rails: + - TOOL_VAL +``` + +Example: the question concerns canceling a service, but RAG retrieves modem documentation. `RET_REL` can reject that context before it is used in the answer. + +### 7. Telemetry and observability + +When the feature participates in an agent execution, preserve `request_id`, `trace_id`, `session_id`, `agent_id`, `message_id`, and other correlation keys in state/events. This makes the decision observable through Langfuse/Observer without embedding observability logic in the domain. + +### 8. How to test + +1. Add a unit test for the core behavior. +2. Add a runtime integration test when state spans multiple turns. +3. Test the happy path and at least one failure/rejection path. +4. Confirm retries/replays do not duplicate side effects for transactional features. +5. In production, also validate telemetry and ID correlation. + +### 9. Common mistakes + +- Having a rail implementation does not mean it is enabled: check `guardrails.yaml`. +- Fail-fast behavior should be chosen intentionally for each stage. +- Tool guardrails do not replace business validation inside the API/action itself. + +### 10. Relationship with other features + +Use this feature together with the framework's horizontal capabilities rather than creating a parallel implementation in domain-agent code. For transactional journeys, pay special attention to **Clarification**, **Pause/Resume**, **Durable Idempotency**, **Workflow Error Recovery**, and **Guardrails**. + +### 11. Repository references + +- `libs/agent_framework/src/agent_framework/guardrails/pipeline.py` +- `libs/agent_framework/src/agent_framework/guardrails/rails.py` +- `Tuning-Performance/` +- `Documentacao/` +- `libs/agent_framework/docs/` diff --git a/agent_framework_oci/docs/features/README.md b/agent_framework_oci/docs/features/README.md index d09ad96..f925f85 100644 --- a/agent_framework_oci/docs/features/README.md +++ b/agent_framework_oci/docs/features/README.md @@ -1,10 +1,62 @@ -# Feature Guides / Guias de Features +# Feature Guide — agent_framework_oci -Escolha o idioma / Choose a language: +Guia consolidado e bilíngue das principais capabilities horizontais do framework. Cada página contém **Português (PT-BR)** e **English (EN)** no mesmo arquivo. -- [Português (PT-BR)](pt-BR/README.md) -- [English (EN)](en/README.md) +Consolidated bilingual guide for the framework's main horizontal capabilities. Each page contains **Portuguese (PT-BR)** and **English (EN)** in the same file. -Os 15 arquivos bilíngues originais continuam neste diretório para compatibilidade, mas as árvores `pt-BR/` e `en/` são as versões recomendadas para leitura e distribuição. +| # | Feature | +|---:|---| +| 01 | [Autenticação / Authentication](01_authentication.md) | +| 02 | [Workflow Transacional Determinístico / Deterministic Transactional Workflow](02_deterministic_transactional_workflow.md) | +| 03 | [Composição por LLM Solicitada pelo Domínio / Domain Requested LLM Composition](03_domain_requested_llm_composition.md) | +| 04 | [RAG Solicitado pelo Domínio / Domain Requested RAG](04_domain_requested_rag.md) | +| 05 | [Memória de Longo Prazo / Long Term Memory](05_long_term_memory.md) | +| 06 | [Regressão Offline de Workflow / Offline Workflow Regression](06_offline_workflow_regression.md) | +| 07 | [Pause / Resume de Workflow / Pause / Resume Workflow](07_pause_resume_workflow.md) | +| 08 | [Aderência de Rota / Route Stickiness](08_route_stickiness.md) | +| 09 | [Replay em Interrupções de Voz / Voice Interruption Replay](09_voice_interruption_replay.md) | +| 10 | [Recuperação de Erro em Workflow / Workflow Error Recovery](10_workflow_error_recovery.md) | +| 11 | [Clarificação / Clarification](11_clarification.md) | +| 12 | [Idempotência Durável / Durable Idempotency](12_durable_idempotency.md) | +| 13 | [Estados Transacionais Dinâmicos / Dynamic Transaction States](13_dynamic_transaction_states.md) | +| 14 | [Replay Após Finalização / Post Finalization Replay](14_post_finalization_replay.md) | +| 15 | [Guardrails de Retrieval e Tools / Retrieval / Tool Guardrails](15_retrieval_tool_guardrails.md) | -The original 15 bilingual files remain in this directory for backward compatibility, but the `pt-BR/` and `en/` trees are the recommended versions for reading and distribution. +## Mapa conceitual / Conceptual map + +```text +LLM +│ +├── Conversation +│ ├── Clarification +│ ├── Route Stickiness +│ └── Long Term Memory +│ +├── Knowledge +│ ├── Domain Requested RAG +│ └── Retrieval Guardrails +│ +├── Transactions +│ ├── Deterministic Transactional Workflow +│ ├── Pause / Resume +│ ├── Dynamic Transaction States +│ ├── Durable Idempotency +│ └── Workflow Error Recovery +│ +├── Response +│ └── Domain Requested LLM Composition +│ +├── Voice +│ ├── Voice Interruption Replay +│ └── Post Finalization Replay +│ +└── Platform + ├── Authentication + └── Offline Workflow Regression +``` + +## Princípio de arquitetura / Architecture principle + +**PT-BR:** o LLM entende e redige; o framework controla estado, segurança, memória, roteamento e transações; o domínio contém regras específicas de negócio. + +**EN:** the LLM understands and writes; the framework controls state, security, memory, routing, and transactions; the domain contains business-specific rules. diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc index 91d91f9..12a77f9 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/gateway_policy_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/gateway_policy_context.cpython-313.pyc new file mode 100644 index 0000000..83fa4b8 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/gateway_policy_context.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc index 61332e2..dafbb1f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc index e98a68a..7d09d87 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/runtime_mcp_gateway_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/runtime_mcp_gateway_adapter.cpython-313.pyc new file mode 100644 index 0000000..562f4db Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/runtime_mcp_gateway_adapter.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc index 049a94a..da880a8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc index 20d5e8d..a4bf51c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc index c168703..8550871 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc index 208b638..7aa94cb 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc index 44f98cf..9d2f0df 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc index f5d7804..02c8aae 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc index d644694..65c2810 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc index d6bfed6..144ae86 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc index a235c8b..a0b1b2e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc index d76f4f5..4126c11 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc index 29fb75c..8456799 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc index 397f7c9..b7d99e4 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc index 91cee2e..f4a8983 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc index 865873c..67c4c0c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc index a47cdc1..2cc5683 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc index c70523c..1778a22 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc index 4134a81..4e948c1 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc index e045b4c..df8eb6f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc index 073a05f..337545b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc index 58373c1..3871ce3 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc index 266a8cf..3e20c90 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc index 5396652..ebad7ae 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc index 209342c..c0e39fd 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc index 52e8bdb..9111f5b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc index 42367be..3612f13 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py index c82da75..f060ccd 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/langgraph_saver.py @@ -26,104 +26,11 @@ from .checkpoint_repository import create_checkpoint_repository def _jsonable(value: Any) -> Any: - """Convert a value to a JSON-safe tree without collapsing containers to strings. - - The previous implementation used ``json.dumps(..., default=str)`` only as a - probe and then returned the *original* object. Because ``default=str`` makes - virtually every object serializable, the fallback branch was never reached. - JSON repositories could therefore stringify arbitrary nested LangGraph objects - later, and restored checkpoints would contain strings where mappings were - required (for example ``metadata``, ``versions_seen`` or ``checkpoint_map``). - - Keep dict/list structure durable and stringify only unsupported leaf objects. - """ - if value is None or isinstance(value, (str, int, float, bool)): + try: + json.dumps(value, default=str) return value - if isinstance(value, dict): - return {str(k): _jsonable(v) for k, v in value.items()} - if isinstance(value, (list, tuple, set, frozenset)): - return [_jsonable(v) for v in value] - - model_dump = getattr(value, "model_dump", None) - if callable(model_dump): - try: - return _jsonable(model_dump(mode="python")) - except TypeError: - return _jsonable(model_dump()) - except Exception: - pass - - # Unsupported runtime objects are not durable. At this point the object is a - # leaf; converting only the leaf to text cannot destroy an enclosing mapping. - return str(value) - - -def _mapping(value: Any, *, default: dict[str, Any] | None = None) -> dict[str, Any]: - """Recover a mapping from native or legacy JSON-string values. - - Legacy rows written with permissive ``default=str`` serializers may contain a - JSON object encoded as text. Parse that representation when possible; if the - value is an opaque string, return a safe empty/default mapping instead of - letting LangGraph fail with ``'str' object has no attribute 'items'``. - """ - if isinstance(value, dict): - return value - if isinstance(value, str): - try: - decoded = json.loads(value) - except Exception: - decoded = None - if isinstance(decoded, dict): - return decoded - return dict(default or {}) - - -def _normalize_checkpoint(checkpoint: Any) -> dict[str, Any]: - """Normalize LangGraph checkpoint mapping fields after JSON persistence.""" - cp = _mapping(checkpoint) - if not cp: - return {} - cp = dict(cp) - cp["channel_values"] = _mapping(cp.get("channel_values")) - cp["channel_versions"] = _mapping(cp.get("channel_versions")) - - versions_seen = _mapping(cp.get("versions_seen")) - cp["versions_seen"] = { - str(node): _mapping(versions) for node, versions in versions_seen.items() - } - - pending_sends = cp.get("pending_sends") - if pending_sends is None: - cp["pending_sends"] = [] - elif not isinstance(pending_sends, list): - cp["pending_sends"] = [pending_sends] - return cp - - -def _durable_config(config: dict[str, Any] | None) -> dict[str, Any] | None: - """Return the persistable subset of a LangGraph RunnableConfig. - - LangGraph injects private execution objects under ``configurable`` (notably - ``__pregel_runtime``). Those objects are valid only for the current graph - invocation and must never be persisted by a checkpointer. JSON-backed - repositories stringify them; on restore LangGraph then sees that string as - its runtime object and calls ``.override(...)``, producing errors such as - ``AttributeError: 'str' object has no attribute 'override'`` before the first - workflow node runs. - - Durable checkpoint identity lives in the normal configurable keys - (thread_id/checkpoint_ns/checkpoint_id). User-defined configurable values are - preserved; only LangGraph's private ``__pregel_*`` execution keys are removed. - """ - if config is None: - return None - cleaned = dict(_mapping(config)) - configurable = dict(_mapping(cleaned.get("configurable"))) - for key in list(configurable): - if str(key).startswith("__pregel_"): - configurable.pop(key, None) - cleaned["configurable"] = configurable - return cleaned + except TypeError: + return json.loads(json.dumps(value, default=str)) def _thread_id(config: dict[str, Any] | None) -> str: @@ -178,13 +85,6 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver): """Checkpoint saver nativo para LangGraph usando os repositories do framework.""" def __init__(self, settings, repository=None): - # BaseCheckpointSaver initializes LangGraph serializer state in current - # checkpoint releases. Keep compatibility with the lightweight fallback - # class used by framework-only unit tests. - try: - super().__init__() - except TypeError: - pass self.settings = settings self.repository = repository or create_checkpoint_repository(settings) self._loop: asyncio.AbstractEventLoop | None = None @@ -203,10 +103,10 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver): def _make_tuple(self, payload: dict[str, Any] | None): if not payload: return None - config = _durable_config(payload.get("config")) or {"configurable": {"thread_id": payload.get("thread_id")}} - checkpoint = _normalize_checkpoint(payload.get("checkpoint")) - metadata = _mapping(payload.get("metadata")) - parent_config = _durable_config(payload.get("parent_config")) + config = payload.get("config") or {"configurable": {"thread_id": payload.get("thread_id")}} + checkpoint = payload.get("checkpoint") or {} + metadata = payload.get("metadata") or {} + parent_config = payload.get("parent_config") pending_writes = _normalize_pending_writes(payload.get("pending_writes") or []) try: from langgraph.checkpoint.base import CheckpointTuple @@ -229,13 +129,11 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver): async def aput(self, config: dict[str, Any], checkpoint: dict[str, Any], metadata: dict[str, Any] | None = None, new_versions: dict[str, Any] | None = None): thread_id = _thread_id(config) checkpoint_id = _checkpoint_id(checkpoint) - base_config = _durable_config(config) or {} next_config = { - **base_config, + **(config or {}), "configurable": { - **_mapping(base_config.get("configurable")), + **((config or {}).get("configurable") or {}), "thread_id": thread_id, - "checkpoint_ns": str(_mapping(base_config.get("configurable")).get("checkpoint_ns") or ""), "checkpoint_id": checkpoint_id, }, } diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc index 62b2e6f..762487f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc index 1971422..600ca2f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 9a279ad..d14423b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..25db31f Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/oci_streaming.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/oci_streaming.cpython-313.pyc new file mode 100644 index 0000000..6f0b4bf Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/events/__pycache__/oci_streaming.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc index 59be44e..f4d573b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc index 4c5a88a..ee7cc15 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..6bfab5a Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/client.cpython-313.pyc new file mode 100644 index 0000000..7c4d856 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/config.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..16c4b37 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/config.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000..527a4a1 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/router.cpython-313.pyc new file mode 100644 index 0000000..ef4b2a9 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/router.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/session_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/session_store.cpython-313.pyc new file mode 100644 index 0000000..43f4453 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/global_supervisor/__pycache__/session_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index 011d42e..f275cd0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc index 61d7d96..8a466f4 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc index a8427d2..26b9c5f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc index c59a715..1edd310 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/executor.cpython-313.pyc new file mode 100644 index 0000000..d2a59de Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/executor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index ef2258f..3ac5185 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/langgraph_adapters.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/langgraph_adapters.cpython-313.pyc new file mode 100644 index 0000000..359c587 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/langgraph_adapters.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 1e613f4..f78d6e3 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc index b1ab5e7..03df7c1 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index 197f9a0..1105f13 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc index 581e246..2e0eef0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc index 2f1e12b..083aa86 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index a0ceb7b..66859b2 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc index 95396b8..d026f5c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc index 35dd677..25d63db 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc index b90e249..9a1b904 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 9e5f483..ddeb740 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/config.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..1d50f43 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/config.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index 213ca47..9834ffd 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index 0076f8e..2e8ea43 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc index f8e3ae6..d149e1a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index fc8a59a..bfc0458 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc index 001cf91..d75d2ed 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index 1be5ca8..1336193 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc index 29e50e6..cff1ad3 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc index 765f023..6aef87c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py index dc3d302..58bdc74 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/contestation_validation.py @@ -129,6 +129,8 @@ def _is_vas_section_name(section_name: str) -> bool: or "servicos de valor adicionado" in normalized or "servicos valor adicionado" in normalized or "sva detalhe total" in normalized + or "servicos contratados de parceiros" in normalized + or "servico contratado de parceiro" in normalized ) @@ -185,13 +187,23 @@ def _extract_contestation_invoice_items( "validatedAmount", ) if candidate_name and candidate_amount is not None and candidate_amount > 0: + payload_type = str(payload.get("type") or payload.get("tipo") or "").strip() + payload_desc = str(payload.get("desc") or "").strip() + classe = str(payload.get("classe", "")).strip().lower() + is_vas = ( + _is_vas_section_name(section_name) + or _is_vas_section_name(payload_type) + or classe in {"avulso", "estrategico"} + ) found.append( { "name": candidate_name, "amount": _money(candidate_amount), - "is_vas": _is_vas_section_name(section_name), + "is_vas": is_vas, "section": section_name, - "classe": str(payload.get("classe", "")).strip().lower(), + "source_type": payload_type, + "source_desc": payload_desc, + "classe": classe, "estrategico": bool(payload.get("estrategico")), "verb": str(payload.get("verb", "")).strip().lower(), } @@ -397,14 +409,25 @@ def validate_contestation_items( "vas_estrategico": False, "status": "em_validacao", } - matched_candidate = next( - ( - candidate - for candidate in candidates - if _is_same_plan_name(candidate.get("name", ""), item_name) - ), - None, + matching_candidates = [ + candidate + for candidate in candidates + if _is_same_plan_name(candidate.get("name", ""), item_name) + ] + # A mesma cobrança pode aparecer em múltiplas visões da fatura. + # Prefira a evidência que traz classificação explícita de VAS em vez + # de aceitar a primeira ocorrência genérica e concluir incorretamente + # que o item está fora da seção VAS. + matching_candidates.sort( + key=lambda candidate: ( + 0 if ( + str(candidate.get("classe", "")).strip().lower() in {"avulso", "estrategico"} + or bool(candidate.get("is_vas")) + ) else 1, + 0 if _normalize_match_text(candidate.get("name", "")) == _normalize_match_text(item_name) else 1, + ) ) + matched_candidate = matching_candidates[0] if matching_candidates else None if matched_candidate is None: _record_failure( item_log, @@ -414,6 +437,9 @@ def validate_contestation_items( continue item_log["item_na_fatura"] = True item_log["item_confirmado"] = True + item_log["item_fatura_resolvido"] = str(matched_candidate.get("name", "") or "") + item_log["secao_fatura"] = str(matched_candidate.get("section", "") or "") + item_log["tipo_fatura"] = str(matched_candidate.get("source_type", "") or "") classe = str(matched_candidate.get("classe", "")).strip().lower() is_strategic = ( diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index 37db4aa..68e8eef 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index 905bf3d..3f7f8da 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index 592eec5..3c8735d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index 3481bcf..bacf727 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc index dbea066..d3762cc 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index a749b18..2a3ee05 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index ce13289..7938ad5 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc index 62bebad..384d9fa 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc index 4274e7e..27f4ead 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index 7c189e0..c73d159 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc index fa42f8b..791484f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc index 902222b..cfbda6f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/safe_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/safe_out.cpython-313.pyc new file mode 100644 index 0000000..0c08d83 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/safe_out.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc index eb9a0ae..0bc035c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc index 48bd092..22fd64c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..eb1d9a3 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/supervision_template.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/supervision_template.cpython-313.pyc new file mode 100644 index 0000000..4d96758 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/supervision_template.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/tts_rules.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/tts_rules.cpython-313.pyc new file mode 100644 index 0000000..bbf441d Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/shared/__pycache__/tts_rules.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc index 2f1934b..401d555 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index 5e3a917..d2b6615 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc index 1f3bf04..6a18f89 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index 3402eae..c26f95a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index 3cf3cf8..8039453 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index a8f3259..1694530 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index 4810ab0..76358fb 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index ac602c5..32396bf 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc index 5eb9f1c..023b52c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..6d92edd Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/correspondencia_item.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/correspondencia_item.cpython-313.pyc new file mode 100644 index 0000000..693e8a8 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/correspondencia_item.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/groundedness.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/groundedness.cpython-313.pyc new file mode 100644 index 0000000..ab54fd4 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/groundedness.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/intencao_cancelar.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/intencao_cancelar.cpython-313.pyc new file mode 100644 index 0000000..4372f5e Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/intencao_cancelar.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/quantidade_coerente.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/quantidade_coerente.cpython-313.pyc new file mode 100644 index 0000000..8a5d4ee Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/quantidade_coerente.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/servico_correto.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/servico_correto.cpython-313.pyc new file mode 100644 index 0000000..8bdf382 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/servico_correto.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/verbalizacao_prematura.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/verbalizacao_prematura.cpython-313.pyc new file mode 100644 index 0000000..d2b7d24 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/supervision/__pycache__/verbalizacao_prematura.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 4a547bd..3f3f905 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc index 8f3fd51..63d722a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/oos_blocklist.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/oos_blocklist.cpython-313.pyc new file mode 100644 index 0000000..ef9f3f3 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/oos_blocklist.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc index 35ef8dc..6836cd2 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc index 940bc02..4e1446a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py index 810e694..87f4d73 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py @@ -328,47 +328,6 @@ class ProactiveOfferRail(Guardrail): ) -def _sanitize_low_risk_phraseology(text: str, reason: str) -> str | None: - """Remove apenas fechamentos/redirecionamentos de baixo risco. - - FRASEOLOGIA continua fail-closed para conteúdo material. Para B4 e ofertas - genéricas de continuação, porém, bloquear toda uma resposta grounded piora a - UX; nesses casos removemos somente a sentença ofensora. - """ - normalized_reason = (reason or "").casefold() - low_risk = any(token in normalized_reason for token in ( - "viola b4", "outro canal", "atendimento especializado", - "realizar alguma ação", "oferta de ação", "orienta o cliente", - )) - if not low_risk: - return None - - forbidden = ( - "entre em contato", "fale com um atendente", "procure uma loja", - "acesse o app", "acesse o site", "atendimento especializado", - "área de planos", "area de planos", "é só me avisar", - "e so me avisar", "realizar alguma ação", "realizar alguma acao", - "gerenciar esses serviços", "gerenciar esses servicos", - ) - sentences = re.split(r"(?<=[.!?])\s+", (text or "").strip()) - kept: list[str] = [] - removed = False - for sentence in sentences: - normalized = sentence.casefold() - proactive = ( - ("se quiser" in normalized or "caso queira" in normalized or "se desejar" in normalized) - and any(token in normalized for token in ("realizar", "gerenciar", "cancelar", "contratar", "alterar", "ação", "acao")) - ) - if proactive or any(token in normalized for token in forbidden): - removed = True - continue - kept.append(sentence.strip()) - sanitized = " ".join(x for x in kept if x).strip() - if removed and sanitized: - return sanitized - return None - - class PhraseologyRail(Guardrail): """FRASEOLOGIA calibrado: bloqueia fraseados proibidos do agente.""" code = "FRASEOLOGIA" @@ -380,26 +339,9 @@ class PhraseologyRail(Guardrail): _llm(ctx), "FRASEOLOGIA", {"text": text or "", "context": ctx}, profile_name="grl", component_name="guardrail.fraseologia", generation_name="guardrail.fraseologia", ) - allowed = bool(out.get("allowed", True)) - reason = str(out.get("reason") or out.get("label") or "FRASEOLOGIA avaliado") - if not allowed: - sanitized = _sanitize_low_risk_phraseology(text or "", reason) - if sanitized: - return RailDecision( - code=self.code, - allowed=True, - reason=f"FRASEOLOGIA sanitizada: {reason}", - sanitized_text=sanitized, - metadata={ - "mechanism": "llm_rail+deterministic_sanitize", - "data": out, - "calibrated": True, - "original_allowed": False, - }, - ) return RailDecision( - code=self.code, allowed=allowed, - reason=reason, + code=self.code, allowed=bool(out.get("allowed", True)), + reason=str(out.get("reason") or out.get("label") or "FRASEOLOGIA avaliado"), sanitized_text=text, metadata={"mechanism": "llm_rail", "data": out, "calibrated": True}, ) diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index aedbf7c..d5d9eca 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc index cd27ee8..e7dc3ac 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc index 2d103d3..5e9d169 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc index 40efe42..61fc64a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc index 968dc08..93fa52b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc index 4e3cad4..1bb78aa 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc index adef518..23d17df 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/_compat.cpython-313.pyc new file mode 100644 index 0000000..24ca7be Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/_compat.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc index 0c7485d..b569f03 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000..ca31105 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index e9f6114..ceebfe0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc index acfb6af..a4145e0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc index 1255fb1..2558fb8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index b9acc23..9d4070d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc index 9847625..4bd6ec7 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc index 6de755a..86e84c3 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc index 680bdc2..0e67c29 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc index 1e5bb24..56dde3e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc index 336a5bb..2c9431a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc index c50befd..cbab8e6 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index aad2b4b..7abd6a9 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc index 0231a98..7588451 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc index f0a67bc..f028303 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc index fe91e27..2f4014b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc index 2a42e4b..35e7890 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc index e17ae95..8fe790a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py index 98f1529..97046c3 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/models.py @@ -28,6 +28,11 @@ class MCPToolConfig(BaseModel): execution_policy: dict[str, Any] = Field(default_factory=dict) selection_keywords: list[str] = Field(default_factory=list) + # Política declarativa opcional de apresentação da resposta da tool. + # Para novos projetos prefira mode=renderer + renderer=. + # O framework resolve o nome no registry; a regra de negócio fica na aplicação. + response: dict[str, Any] = Field(default_factory=dict) + # Política declarativa de cache da tool, lida diretamente de config/tools.yaml. # Exemplo: # cache: diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py index 6ac4308..b37efc8 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/registry.py @@ -70,6 +70,7 @@ class MCPRegistry: "confirmation_required": tool.confirmation_required, "execution_policy": tool.execution_policy, "selection_keywords": tool.selection_keywords, + "response": tool.response, "cache": tool.cache, }) return out diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc index 2e2302f..fba6fec 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc index 481b774..e87aa7e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc index 579d73a..8e27b87 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc index e18196f..334f559 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc index d699e33..d7f8b96 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc index 15c7fca..739b11c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc index 0e22a97..6f30a8a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc index 49ed10a..5580736 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc index 468a8c2..b6e6beb 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc index a2de671..1b50229 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc index 19e39b4..134e279 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc index 75e29dc..58b2177 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc index 3c92f8d..bd4ee00 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/control_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/control_events.cpython-313.pyc new file mode 100644 index 0000000..3e8308d Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/control_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/decorators.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/decorators.cpython-313.pyc new file mode 100644 index 0000000..3e59d70 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/decorators.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc index bb7f263..e1b3cb8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/grl_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/grl_events.cpython-313.pyc new file mode 100644 index 0000000..a7c7b45 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/grl_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc index 36a0644..a550677 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc index 619b3cb..9f56a00 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/informational_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/informational_events.cpython-313.pyc new file mode 100644 index 0000000..bcdbacf Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/informational_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc index 4690c0c..bec5fe8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langfuse_enterprise.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langfuse_enterprise.cpython-313.pyc new file mode 100644 index 0000000..1a3b13e Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langfuse_enterprise.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc index 9e1f8ea..70ae7b1 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc index 061ef4b..a52834f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc index 85236a7..a65a790 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_events.cpython-313.pyc new file mode 100644 index 0000000..8de88ae Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc index 91103c9..a9ee1ac 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc index 2b6482e..66abde1 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc index b10b2ab..f9bc52a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc index 5e0102f..f096622 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_exporter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_exporter.cpython-313.pyc new file mode 100644 index 0000000..5fcb1d7 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_exporter.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc index 68cf9aa..5795c3f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/tim_backoffice_contract.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/tim_backoffice_contract.cpython-313.pyc new file mode 100644 index 0000000..34add0f Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/tim_backoffice_contract.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc index a202423..da822db 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc index 2b0fffa..2d5dbd6 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc index 70847d7..a7536a0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc index 461c790..120f556 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc index dd7fec2..2bfe499 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/mongodb_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/mongodb_store.cpython-313.pyc new file mode 100644 index 0000000..1f4357f Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/mongodb_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc index 9bbbada..d299268 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc index 5376044..f770641 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__init__.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__init__.py new file mode 100644 index 0000000..a96f24f --- /dev/null +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__init__.py @@ -0,0 +1,15 @@ +from .renderers import ( + ToolResponseRenderer, + ToolResponseRendererRegistry, + register_tool_response_renderer, + render_tool_response, + tool_response_renderers, +) + +__all__ = [ + "ToolResponseRenderer", + "ToolResponseRendererRegistry", + "register_tool_response_renderer", + "render_tool_response", + "tool_response_renderers", +] diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..c0db803 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc new file mode 100644 index 0000000..ea3f4c9 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/renderers.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/renderers.py new file mode 100644 index 0000000..528359a --- /dev/null +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/renderers.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +from collections.abc import Callable +from threading import RLock +from typing import Any, Protocol + + +class ToolResponseRenderer(Protocol): + def __call__( + self, + *, + tool_name: str, + result: dict[str, Any], + state: dict[str, Any], + agent_label: str, + ) -> str | None: ... + + +class ToolResponseRendererRegistry: + """Thread-safe registry for application/domain response renderers. + + The framework stores only symbolic renderer names. Business-specific + formatting lives in the application that registers the renderer. + """ + + def __init__(self) -> None: + self._renderers: dict[str, ToolResponseRenderer] = {} + self._lock = RLock() + + def register( + self, + name: str, + renderer: ToolResponseRenderer, + *, + replace: bool = True, + ) -> None: + key = str(name or "").strip() + if not key: + raise ValueError("renderer name must not be empty") + if not callable(renderer): + raise TypeError("renderer must be callable") + with self._lock: + if not replace and key in self._renderers: + raise KeyError(f"renderer already registered: {key}") + self._renderers[key] = renderer + + def get(self, name: str | None) -> ToolResponseRenderer | None: + key = str(name or "").strip() + if not key: + return None + with self._lock: + return self._renderers.get(key) + + def render( + self, + name: str | None, + *, + tool_name: str, + result: dict[str, Any], + state: dict[str, Any], + agent_label: str, + ) -> str | None: + renderer = self.get(name) + if renderer is None: + return None + value = renderer( + tool_name=tool_name, + result=result, + state=state, + agent_label=agent_label, + ) + if value is None: + return None + text = str(value).strip() + return text or None + + +tool_response_renderers = ToolResponseRendererRegistry() + + +def register_tool_response_renderer( + name: str, + renderer: ToolResponseRenderer, + *, + replace: bool = True, +) -> None: + tool_response_renderers.register(name, renderer, replace=replace) + + +def render_tool_response( + name: str | None, + *, + tool_name: str, + result: dict[str, Any], + state: dict[str, Any], + agent_label: str, +) -> str | None: + return tool_response_renderers.render( + name, + tool_name=tool_name, + result=result, + state=state, + agent_label=agent_label, + ) diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc index 94023bb..7c9bf3f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc index fc5aed8..47239be 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc index d2e385b..25ef9ea 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc index 536008b..c11503f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc index bd9a1f3..66466a4 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc index 0d21834..bb0e699 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc index 9c09002..5fae24b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc index 7804b80..4d042e3 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc index 25cb799..6a14a96 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc index 66b593c..883a239 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc index 51dfa9f..d8940a4 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc index 91a52a9..b755554 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc index 9e388b3..4068e3d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index 534269d..f4d228a 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -73,7 +73,6 @@ class EnterpriseRouter: if ( active_agent and keyword_candidate is not None - and keyword_candidate.agent != active_agent and keyword_candidate.intent != previous_intent and self._is_explicit_intent_shift(keyword_candidate) ): diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc index e288f43..3cd7a85 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc index 9320eaa..cfe0d87 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py index 2580f43..af6c569 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py @@ -4,6 +4,7 @@ import hashlib import json import logging import re +import uuid from dataclasses import dataclass, field from typing import Any, Iterable, Mapping @@ -15,6 +16,21 @@ logger = logging.getLogger(__name__) _EMPTY_VALUES = (None, "", {}, []) +_ACTIVE_TRANSACTION_STATUSES = { + "COLLECTING_PARAMETERS", + "AWAITING_CONFIRMATION", + "WORKFLOW_PAUSED", + "TOOL_RESULT_CLARIFICATION", + "EXECUTING", +} +_TERMINAL_TRANSACTION_STATUSES = { + "COMPLETED", + "FAILED", + "CANCELLED", + "BLOCKED", + "OUT_OF_SCOPE", +} + @dataclass(slots=True) class RuntimeContext: @@ -519,11 +535,42 @@ class AgentRuntimeMixin: return str(response.get("content") or response.get("text") or response.get("answer") or "") return str(getattr(response, "content", None) or getattr(response, "text", None) or response) + def _drop_stale_message_extracted_arguments( + self, + tool_name: str, + arguments: dict[str, Any], + *, + explicit_fields: Iterable[str] = (), + ) -> dict[str, Any]: + """Remove valores herdados para campos cujo contrato diz ``from: message``. + + Em uma NOVA transação, ``context.tool_arguments`` pode ainda carregar + parâmetros de uma operação anterior. Campos declarados pelo mapper como + extraídos da mensagem corrente não podem nascer desse contexto antigo. + Valores explicitamente extraídos deterministicamente do turno atual são + preservados. Durante coleta incremental este helper não é usado. + """ + router = getattr(self, "tool_router", None) + if not router or not hasattr(router, "parameter_extract_rules"): + return dict(arguments or {}) + rules = router.parameter_extract_rules(tool_name) or {} + explicit = {str(name) for name in explicit_fields} + cleaned = dict(arguments or {}) + for field_name, rule in rules.items(): + if ( + str(rule.get("from") or "message").lower() == "message" + and str(field_name) not in explicit + ): + cleaned.pop(str(field_name), None) + return cleaned + async def _extract_mcp_parameters( self, tool_name: str, arguments: dict[str, Any], state: dict[str, Any], + *, + overwrite_from_message: bool = False, ) -> dict[str, Any]: """Executa regras ``extract`` declaradas para a tool escolhida. @@ -544,9 +591,13 @@ class AgentRuntimeMixin: llm = getattr(self, "llm", None) for field_name, rule in rules.items(): - if resolved.get(field_name) not in _EMPTY_VALUES: + from_message = str(rule.get("from") or "message").lower() == "message" + if not from_message: continue - if str(rule.get("from") or "message").lower() != "message": + # Em uma nova transação, a mensagem atual prevalece para campos + # declarados como ``from: message``. Durante COLLECTING_PARAMETERS + # o default permanece False para congelar valores já coletados. + if resolved.get(field_name) not in _EMPTY_VALUES and not overwrite_from_message: continue strategy = str(rule.get("strategy") or "llm").lower() value: Any = None @@ -1167,19 +1218,13 @@ class AgentRuntimeMixin: return None def _select_transactional_tool(self, tools: list[str], text: str) -> str | None: - """Seleciona a ação transacional da intent atual. - - O match por ``selection_keywords`` continua tendo precedência. Porém, depois - que o EnterpriseRouter já restringiu ``tools`` às capabilities da intent, - uma única tool transacional é uma escolha determinística e segura. Isso - evita perder frases naturais como ``quero cancelar TIM Fashion Mensal`` ou - ``não contratei esse serviço`` só porque elas não repetem literalmente uma - keyword de ``tools.yaml``. - """ matched = self._transactional_action_match(text, tools) if matched: return matched + # Generic fallback: once routing has constrained the allowlist, a single + # transactional capability is unambiguous even when the user's wording + # does not contain one of the tool-specific selection keywords. transactional = [ tool for tool in tools @@ -1348,12 +1393,112 @@ class AgentRuntimeMixin: state["transaction_status"] = "COMPLETED" if result.get("ok") else "FAILED" return result + @staticmethod + def _transaction_is_active(state: dict[str, Any]) -> bool: + return str(state.get("transaction_status") or "") in _ACTIVE_TRANSACTION_STATUSES + + @staticmethod + def _transaction_is_terminal(state: dict[str, Any]) -> bool: + return str(state.get("transaction_status") or "") in _TERMINAL_TRANSACTION_STATUSES + + def _active_transaction(self, state: dict[str, Any]) -> dict[str, Any] | None: + """Return only the operationally active transaction. + + Closed transactions are history and must never provide tool/arguments for + a later turn. For backward compatibility, an old checkpoint that has the + legacy selected/pending fields but an ACTIVE status is lazily hydrated into + ``active_transaction``. + """ + if not self._transaction_is_active(state): + return None + current = state.get("active_transaction") + if isinstance(current, dict) and current.get("tool_name"): + return current + legacy = state.get("pending_tool_call") or state.get("selected_tool_call") or {} + if not isinstance(legacy, dict) or not legacy.get("tool_name"): + return None + current = { + "transaction_id": str(uuid.uuid4()), + "tool_name": legacy.get("tool_name"), + "arguments": dict(legacy.get("arguments") or {}), + "status": state.get("transaction_status"), + "started_from_intent": state.get("intent"), + } + state["active_transaction"] = current + return current + + def _set_active_transaction( + self, + state: dict[str, Any], + *, + tool_name: str, + arguments: dict[str, Any], + status: str, + transaction_id: str | None = None, + ) -> dict[str, Any]: + current = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + txid = transaction_id or current.get("transaction_id") or str(uuid.uuid4()) + tx = { + "transaction_id": txid, + "tool_name": tool_name, + "arguments": dict(arguments or {}), + "status": status, + "started_from_intent": current.get("started_from_intent") or state.get("intent"), + } + state["active_transaction"] = tx + return tx + + def _finish_active_transaction(self, state: dict[str, Any], status: str) -> None: + """Close the active transaction without leaving operational state behind.""" + active = self._active_transaction(state) + if isinstance(active, dict): + state["last_transaction"] = { + **active, + "status": status, + } + state["active_transaction"] = None + state["selected_tool_call"] = {} + state["pending_tool_call"] = {} + state["missing_parameters"] = [] + state["confirmation_required"] = False + state["confirmation_received"] = status == "COMPLETED" + state["next_state"] = None + state["transaction_status"] = status + + def _normalize_transaction_lifecycle(self, state: dict[str, Any]) -> None: + """Ensure closed transactions cannot leak into a later user turn.""" + if self._transaction_is_terminal(state): + # Preserve a compact audit snapshot, but remove every operational latch. + active = state.get("active_transaction") + if not isinstance(active, dict): + legacy = state.get("pending_tool_call") or state.get("selected_tool_call") + if isinstance(legacy, dict) and legacy.get("tool_name"): + active = { + "transaction_id": str(uuid.uuid4()), + "tool_name": legacy.get("tool_name"), + "arguments": dict(legacy.get("arguments") or {}), + "status": state.get("transaction_status"), + "started_from_intent": state.get("intent"), + } + if isinstance(active, dict): + state["last_transaction"] = {**active, "status": state.get("transaction_status")} + state["active_transaction"] = None + state["selected_tool_call"] = {} + state["pending_tool_call"] = {} + state["missing_parameters"] = [] + state["confirmation_required"] = False + state["confirmation_received"] = False + state["next_state"] = None + return + if self._transaction_is_active(state): + self._active_transaction(state) + def transaction_state_patch(self, state: dict[str, Any]) -> dict[str, Any]: keys = ( "available_mcp_tools", "selected_tool_call", "pending_tool_call", "transaction_status", "confirmation_required", "confirmation_received", "tool_policy_result", "missing_parameters", "next_state", "pending_domain_workflow", "pending_tool_clarification", - "business_workflows_executed", + "business_workflows_executed", "active_transaction", "last_transaction", ) return {key: state.get(key) for key in keys if key in state} @@ -1411,6 +1556,9 @@ class AgentRuntimeMixin: "next_state": collecting_state, "tool_policy_result": {**policy, "tool_name": tool_name, "action": "collecting_parameters"}, }) + self._set_active_transaction( + state, tool_name=tool_name, arguments=arguments, status="COLLECTING_PARAMETERS" + ) def transaction_confirmation_message(self, state: dict[str, Any]) -> str | None: if state.get("transaction_status") != "AWAITING_CONFIRMATION": @@ -1446,6 +1594,185 @@ class AgentRuntimeMixin: matches.append(name) return matches or available_tools[:1] + @staticmethod + def _response_path_get(data: Any, path: str | None) -> Any: + """Resolve caminho simples ``a.b.c`` em dicts sem conhecer o domínio.""" + if not path: + return data + current = data + for part in str(path).split("."): + if isinstance(current, Mapping): + current = current.get(part) + else: + return None + return current + + @staticmethod + def _response_format_value(value: Any, formatter: str | None) -> Any: + """Formatadores genéricos permitidos pela política declarativa de resposta.""" + if formatter in (None, "", "raw"): + return value + if formatter == "decimal_2_comma": + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return value + if formatter == "decimal_2": + try: + return f"{float(value):.2f}" + except (TypeError, ValueError): + return value + if formatter == "str": + return "" if value is None else str(value) + return value + + @classmethod + def _response_template(cls, template: str, data: Mapping[str, Any], formats: Mapping[str, Any] | None = None) -> str | None: + """Renderiza template somente se todos os placeholders existirem. + + Isso evita respostas como ``None`` quando o contrato da tool não corresponde + à configuração. Nesse caso o runtime cai no fallback legado/LLM. + """ + formats = formats or {} + names = set(re.findall(r"\{([A-Za-z_][A-Za-z0-9_]*)\}", str(template))) + values: dict[str, Any] = {} + for name in names: + if name not in data or data.get(name) is None: + return None + values[name] = cls._response_format_value(data.get(name), formats.get(name)) + try: + return str(template).format(**values) + except Exception: + return None + + def _render_declared_tool_response(self, tool_name: str | None, data: dict[str, Any], *, agent_label: str, state: dict[str, Any] | None = None) -> str | None: + """Renderiza resposta MCP por configuração, sem regras de negócio no core. + + A configuração vive em ``tools.yaml`` e suporta primitives genéricas: + ``template``, ``list`` e ``lines``. Se não houver política, retorna ``None`` + para preservar integralmente o comportamento legado. + """ + router = getattr(self, "tool_router", None) + registry = getattr(router, "registry", None) + cfg = registry.get_tool(str(tool_name)) if registry and tool_name else None + policy = dict(getattr(cfg, "response", None) or {}) if cfg else {} + if not policy: + return None + + mode = str(policy.get("mode") or "").strip().lower() + + # Extensão preferencial: o core conhece apenas um nome simbólico. + # O código do renderer é registrado pela aplicação/domínio. + if mode == "renderer": + renderer_name = str(policy.get("renderer") or "").strip() + if not renderer_name: + return None + try: + from agent_framework.presentation import render_tool_response + + return render_tool_response( + renderer_name, + tool_name=str(tool_name or ""), + result=data, + state=state or {}, + agent_label=agent_label, + ) + except Exception: + # Compatibilidade/fail-open: renderer ausente ou com erro não quebra + # agentes legados; o fluxo continua para o fallback existente. + return None + + # Modos declarativos da versão anterior são preservados apenas por + # compatibilidade. Novos projetos devem usar mode=renderer. + base: dict[str, Any] = {**data, "agent_label": agent_label, "result": data} + + if mode == "template": + template = policy.get("template") + if not template: + return None + # ``result`` pode ser usado para debug/compatibilidade; demais campos + # precisam existir para impedir None em texto de cliente. + if "{result}" in str(template): + try: + return str(template).replace("{result}", str(data)).replace("{agent_label}", agent_label) + except Exception: + return None + return self._response_template(str(template), base, policy.get("formats")) + + if mode == "list": + items = self._response_path_get(data, policy.get("source")) + if not isinstance(items, list) or not items: + return str(policy.get("empty_message") or "").strip() or None + rendered_items: list[str] = [] + item_template = str(policy.get("item_template") or "{item}") + item_formats = policy.get("item_formats") or {} + for raw in items: + if isinstance(raw, Mapping): + item_data = dict(raw) + else: + item_data = {"item": raw} + item_data["agent_label"] = agent_label + line = self._response_template(item_template, item_data, item_formats) + if line: + rendered_items.append(line) + if not rendered_items: + return None + count = len(rendered_items) + heading_template = policy.get("heading_singular") if count == 1 else policy.get("heading_plural") + heading = None + if heading_template: + heading = self._response_template( + str(heading_template), + {"agent_label": agent_label, "count": count}, + ) + sep = str(policy.get("separator") or "\n") + body = sep.join(rendered_items) + return f"{heading}\n{body}" if heading else body + + if mode == "lines": + lines: list[str] = [] + for spec in policy.get("lines") or []: + if not isinstance(spec, Mapping): + continue + kind = str(spec.get("kind") or "template") + if kind == "template": + when = spec.get("when_present") + if when and self._response_path_get(data, str(when)) is None: + continue + line = self._response_template(str(spec.get("template") or ""), base, spec.get("formats")) + if line: + lines.append(line) + elif kind == "list": + values = self._response_path_get(data, spec.get("source")) + if not isinstance(values, list) or not values: + continue + fields = list(spec.get("item_fields") or ["item"]) + rendered: list[str] = [] + for value in values: + if isinstance(value, Mapping): + chosen = next((value.get(f) for f in fields if value.get(f) not in _EMPTY_VALUES), None) + else: + chosen = value + if chosen not in _EMPTY_VALUES: + rendered.append(str(chosen)) + if rendered: + lines.append( + str(spec.get("prefix") or "") + + str(spec.get("separator") or "; ").join(rendered) + + str(spec.get("suffix") or "") + ) + if not lines: + return None + return str(policy.get("joiner") or " ").join(lines) + + if mode == "field": + value = self._response_path_get(data, policy.get("field")) + return str(value).strip() if value not in _EMPTY_VALUES else None + + if mode in {"llm", "none"}: + return None + return None + def build_direct_mcp_answer(self, state: dict[str, Any], mcp_results: list[dict[str, Any]], *, agent_label: str) -> str | None: """Resposta determinística para consultas estruturadas simples.""" requires_rag, _ = self._mcp_rag_directive(mcp_results) @@ -1475,6 +1802,14 @@ class AgentRuntimeMixin: return None tool = ok[0].get("tool_name") data = ok[0]["result"] + + # Primeiro tenta o contrato genérico e declarativo de apresentação. + # Se a aplicação não o configurou, preserva exatamente o fallback legado + # abaixo para não quebrar projetos existentes. + declared = self._render_declared_tool_response(tool, data, agent_label=agent_label, state=state) + if declared is not None: + return declared + if tool == "consultar_pedido": oid=data.get("order_id"); status=data.get("status"); total=data.get("valor_total") lines=[f"[{agent_label}] Pedido {oid}: status {status}."] @@ -1509,6 +1844,7 @@ class AgentRuntimeMixin: available_tools = list(tools if tools is not None else (state.get("mcp_tools") or [])) state["available_mcp_tools"] = available_tools text = state.get("sanitized_input") or state.get("user_text") or "" + self._normalize_transaction_lifecycle(state) # Clarificação de resultado de tool tem precedência: reutiliza a mesma tool # e argumentos, alterando apenas o parâmetro escolhido pelo usuário. @@ -1524,7 +1860,7 @@ class AgentRuntimeMixin: # Antes de confirmar, complete os parâmetros obrigatórios da ação. if state.get("transaction_status") == "COLLECTING_PARAMETERS": - selected = dict(state.get("selected_tool_call") or {}) + selected = dict(self._active_transaction(state) or state.get("selected_tool_call") or {}) tool_name = selected.get("tool_name") if tool_name: previous_args = dict(selected.get("arguments") or {}) @@ -1535,14 +1871,20 @@ class AgentRuntimeMixin: aliases=aliases, extra_args=self._extract_action_arguments(text), ) - arguments = { - **previous_args, - **{k: v for k, v in new_args.items() if v not in (None, "", [], {})}, - } - # Execute parameter extraction before deciding whether the workflow - # must enter COLLECTING_PARAMETERS. Otherwise parameters declared - # with strategy=llm in mcp_parameter_mapping.yaml are invisible to - # the deterministic transaction state machine. + # Durante coleta incremental, valores de contexto podem ainda conter + # parâmetros de uma operação anterior. O que já foi coletado para a + # transação pendente prevalece; o turno atual só preenche lacunas. + non_empty_new = {k: v for k, v in new_args.items() if v not in (None, "", [], {})} + arguments = {**non_empty_new, **previous_args} + + # Campos de envelope pertencem ao turno corrente e devem permanecer + # atualizados, mesmo quando os parâmetros de negócio ficam congelados. + for per_turn_key in ("query", "operator_instructions", "interaction_key"): + if non_empty_new.get(per_turn_key) not in (None, "", [], {}): + arguments[per_turn_key] = non_empty_new[per_turn_key] + + # Reutiliza o contrato declarativo para preencher somente os campos + # ainda faltantes; campos previamente coletados não são sobrescritos. arguments = await self._extract_mcp_parameters(tool_name, arguments, state) policy = self._resolve_tool_execution_policy(tool_name, arguments) missing = self._missing_required_arguments(policy, arguments) @@ -1562,6 +1904,9 @@ class AgentRuntimeMixin: selected = {"tool_name": tool_name, "arguments": arguments} state["selected_tool_call"] = selected + self._set_active_transaction( + state, tool_name=tool_name, arguments=arguments, status="COLLECTING_PARAMETERS" + ) state["missing_parameters"] = [] if policy.get("require_confirmation"): waiting_state = self._waiting_state_name(state) @@ -1573,6 +1918,9 @@ class AgentRuntimeMixin: "next_state": waiting_state, "tool_policy_result": {**policy, "tool_name": tool_name}, }) + self._set_active_transaction( + state, tool_name=tool_name, arguments=arguments, status="AWAITING_CONFIRMATION" + ) return [{ "ok": True, "executed": False, @@ -1586,27 +1934,29 @@ class AgentRuntimeMixin: result = await self._call_mcp_tool(tool_name, arguments, state) self._capture_pending_domain_workflow(state, result) self._capture_pending_tool_clarification(state, result, tool_name=tool_name, arguments=arguments) - state.update({ - "transaction_status": ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))), - "confirmation_required": False, - "confirmation_received": True, - "pending_tool_call": {}, - "missing_parameters": [], - }) + final_status = ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))) + if final_status in _TERMINAL_TRANSACTION_STATUSES: + self._finish_active_transaction(state, final_status) + else: + state.update({ + "transaction_status": final_status, + "confirmation_required": False, + "confirmation_received": True, + "pending_tool_call": {}, + "missing_parameters": [], + }) + self._set_active_transaction( + state, tool_name=tool_name, arguments=arguments, status=final_status + ) return [result] - pending = state.get("pending_tool_call") or {} + active_tx = self._active_transaction(state) + pending = (active_tx if isinstance(active_tx, dict) and active_tx.get("status") == "AWAITING_CONFIRMATION" else state.get("pending_tool_call")) or {} if pending: decision = self._confirmation_decision(text) if decision == "reject": - state.update({ - "transaction_status": "CANCELLED", - "confirmation_received": False, - "confirmation_required": False, - "selected_tool_call": pending, - "pending_tool_call": {}, - "tool_policy_result": {"action": "cancelled", "tool_name": pending.get("tool_name")}, - }) + state["tool_policy_result"] = {"action": "cancelled", "tool_name": pending.get("tool_name")} + self._finish_active_transaction(state, "CANCELLED") return [{"ok": True, "tool_name": pending.get("tool_name"), "transaction_status": "CANCELLED", "cancelled": True}] if decision == "confirm": tool_name = pending.get("tool_name") @@ -1616,17 +1966,26 @@ class AgentRuntimeMixin: result = await self._call_mcp_tool(tool_name, arguments, state) self._capture_pending_domain_workflow(state, result) self._capture_pending_tool_clarification(state, result, tool_name=tool_name, arguments=arguments) - state.update({ - "transaction_status": ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))), - "confirmation_required": False, - "selected_tool_call": pending, - "pending_tool_call": {}, - "tool_policy_result": {"action": "executed_after_confirmation", "tool_name": tool_name}, - }) + final_status = ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))) + state["tool_policy_result"] = {"action": "executed_after_confirmation", "tool_name": tool_name} + if final_status in _TERMINAL_TRANSACTION_STATUSES: + self._finish_active_transaction(state, final_status) + else: + state.update({ + "transaction_status": final_status, + "confirmation_required": False, + "pending_tool_call": {}, + }) + self._set_active_transaction( + state, tool_name=tool_name, arguments=arguments, status=final_status + ) results.append(result) return results state["transaction_status"] = "AWAITING_CONFIRMATION" state["confirmation_required"] = True + self._set_active_transaction( + state, tool_name=str(pending.get("tool_name") or ""), arguments=dict(pending.get("arguments") or {}), status="AWAITING_CONFIRMATION" + ) return [{"ok": False, "tool_name": pending.get("tool_name"), "awaiting_confirmation": True, "transaction_status": "AWAITING_CONFIRMATION"}] read_only_tools = [ @@ -1669,19 +2028,30 @@ class AgentRuntimeMixin: if not selected_action: return results + explicit_action_args = self._extract_action_arguments(text) action_args = self.build_tool_arguments( state, tool_name=selected_action, intent=state.get("intent"), aliases=aliases, - extra_args=self._extract_action_arguments(text), + extra_args=explicit_action_args, + ) + # Nova transação: parâmetros declarados ``from: message`` não podem ser + # herdados de context.tool_arguments de uma operação anterior. + action_args = self._drop_stale_message_extracted_arguments( + selected_action, action_args, explicit_fields=explicit_action_args.keys() + ) + # A mensagem atual é a fonte de verdade para esses campos no primeiro + # turno transacional. + action_args = await self._extract_mcp_parameters( + selected_action, action_args, state, overwrite_from_message=True ) - # Extract parameters (including LLM-declared extraction rules) before - # validating required fields and before persisting the pending call. - action_args = await self._extract_mcp_parameters(selected_action, action_args, state) policy = self._resolve_tool_execution_policy(selected_action, action_args) selected = {"tool_name": selected_action, "arguments": action_args} state["selected_tool_call"] = selected + self._set_active_transaction( + state, tool_name=selected_action, arguments=action_args, status="COLLECTING_PARAMETERS" + ) state["tool_policy_result"] = {**policy, "tool_name": selected_action} missing = self._missing_required_arguments(policy, action_args) @@ -1718,6 +2088,9 @@ class AgentRuntimeMixin: "confirmation_required": True, "confirmation_received": False, }) + self._set_active_transaction( + state, tool_name=selected_action, arguments=action_args, status="AWAITING_CONFIRMATION" + ) state["next_state"] = self._waiting_state_name(state) if emit_events: await self._emit_ic("IC.TRANSACTION_CONFIRMATION_REQUIRED", state, {"tool_name": selected_action, **policy}, component="agent_runtime.tool_policy") @@ -1727,12 +2100,19 @@ class AgentRuntimeMixin: action_args["confirmed"] = True result = await self._call_mcp_tool(selected_action, action_args, state) self._capture_pending_domain_workflow(state, result) - state.update({ - "transaction_status": ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))), - "confirmation_required": False, - "confirmation_received": True, - "pending_tool_call": {}, - }) + final_status = ("WORKFLOW_PAUSED" if state.get("pending_domain_workflow") else ("TOOL_RESULT_CLARIFICATION" if state.get("pending_tool_clarification") else ("COMPLETED" if result.get("ok") else "FAILED"))) + if final_status in _TERMINAL_TRANSACTION_STATUSES: + self._finish_active_transaction(state, final_status) + else: + state.update({ + "transaction_status": final_status, + "confirmation_required": False, + "confirmation_received": True, + "pending_tool_call": {}, + }) + self._set_active_transaction( + state, tool_name=selected_action, arguments=action_args, status=final_status + ) results.append(result) return results diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..d5ac65c Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/authentication.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/authentication.cpython-313.pyc new file mode 100644 index 0000000..ab536da Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/authentication.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/factory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/factory.cpython-313.pyc new file mode 100644 index 0000000..1c721f3 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/factory.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/installer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/installer.cpython-313.pyc new file mode 100644 index 0000000..2e80012 Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/installer.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/middleware.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/middleware.cpython-313.pyc new file mode 100644 index 0000000..57c4c9c Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/security/__pycache__/middleware.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc index fe52525..a9019c8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc index 0820be3..3064cfd 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc index e16baff..c26f2af 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/router_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/router_supervisor.cpython-313.pyc new file mode 100644 index 0000000..45add6e Binary files /dev/null and b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/router_supervisor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc index b8d541d..054da5c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc index d4aef40..8a90d5e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc index 9d19af5..90cd525 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc index 0d5f670..bef74f6 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc index 22a2878..32218f8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc index ee44ad8..f1bf091 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc index fec117d..45e15b4 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc index 168c535..d574549 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py index bc8a1b6..f818233 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py @@ -434,15 +434,11 @@ class WorkflowRuntime: "current_node": None, } config = {"configurable": {"thread_id": eid}} - # Explicit offline-regression mode. When enabled, always use the - # deterministic backend, regardless of whether LangGraph happens to be - # installed in the current environment. This keeps regression results - # reproducible across developer machines and CI while production - # (the default) continues to require/use LangGraph. if self.allow_deterministic_fallback: - return await self._run_fallback( - definition, initial, start_node=definition.start, execution_id=eid - ) + try: + import langgraph # noqa: F401 + except ModuleNotFoundError: + return await self._run_fallback(definition, initial, start_node=definition.start, execution_id=eid) try: graph = self._compile(definition) state = await graph.ainvoke(initial, config=config) @@ -503,9 +499,10 @@ class WorkflowRuntime: definition = self.repository.get_version(name, version) if version else self.repository.get_active(name) config = {"configurable": {"thread_id": execution_id}} if self.allow_deterministic_fallback: - return await self._resume_fallback( - name, execution_id, resume_value, version=version - ) + try: + import langgraph # noqa: F401 + except ModuleNotFoundError: + return await self._resume_fallback(name, execution_id, resume_value, version=version) try: from langgraph.types import Command except ModuleNotFoundError as exc: diff --git a/agent_framework_oci/mcp/servers/retail_mcp_server/main.py b/agent_framework_oci/mcp/servers/retail_mcp_server/main.py index 6f4e3e9..5d8d57e 100644 --- a/agent_framework_oci/mcp/servers/retail_mcp_server/main.py +++ b/agent_framework_oci/mcp/servers/retail_mcp_server/main.py @@ -17,6 +17,10 @@ TOOLS = { "description": "Consulta entrega e rastreamento do pedido.", "input_schema": {"order_id": "string"}, }, + "cancelar_pedido": { + "description": "Simula o cancelamento de um pedido de varejo.", + "input_schema": {"order_id": "string"}, + }, "solicitar_troca": { "description": "Simula abertura de solicitação de troca.", "input_schema": {"order_id": "string", "reason": "string"}, @@ -77,6 +81,13 @@ async def call_tool(call: ToolCall): {"data": "2026-05-29", "descricao": "Em trânsito para o centro de distribuição"}, ], } + elif name == "cancelar_pedido": + result = { + "protocolo": "CANCEL-2026-001", + "order_id": args.get("order_id") or "PED-1001", + "status": "CANCELAMENTO_SOLICITADO", + "orientacao": "Cancelamento registrado para processamento.", + } elif name == "solicitar_troca": result = { "protocolo": "TROCA-2026-001", diff --git a/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py b/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py index 5abf44d..7972f95 100644 --- a/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py +++ b/agent_framework_oci/mcp/servers/retail_mcp_server/main_fastmcp.py @@ -30,6 +30,16 @@ def consultar_entrega(order_id: str | None = None) -> dict[str, Any]: } +@mcp.tool() +def cancelar_pedido(order_id: str | None = None) -> dict[str, Any]: + """Solicita o cancelamento de um pedido de varejo.""" + return { + "order_id": order_id or "ORD-001", + "protocolo": "CANCEL-123456", + "status": "CANCELAMENTO_SOLICITADO", + } + + @mcp.tool() def solicitar_troca(order_id: str | None = None, reason: str | None = None) -> dict[str, Any]: """Abre solicitação de troca para um pedido.""" diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f6a2b79 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..df9fb43 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..8d7093d Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..7828b8e Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/__pycache__/state.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..4e27b20 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..803273e Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..104fb20 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..eda48f5 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..df7bf15 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..7fbc59f Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py b/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py index e6429c4..7a1a9be 100644 --- a/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py +++ b/agent_framework_oci/templates/agent_template_backend/app/agents/runtime.py @@ -3,5 +3,8 @@ from __future__ import annotations # Compatibilidade local do template/backend. # A implementação oficial agora fica no framework para evitar duplicação entre agentes. from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext +from app.presentation import register_tool_renderers + +register_tool_renderers() __all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"] diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..a6540ec Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc new file mode 100644 index 0000000..8fc575e Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/grl_examples.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc new file mode 100644 index 0000000..8253000 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/ic_examples.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc new file mode 100644 index 0000000..41a7208 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/mcp_examples.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc new file mode 100644 index 0000000..a8f3f88 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/noc_examples.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc new file mode 100644 index 0000000..3b7d6a8 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/examples/__pycache__/observer_examples.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..9144c5b Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..9495cf2 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/presentation/__init__.py b/agent_framework_oci/templates/agent_template_backend/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/agent_framework_oci/templates/agent_template_backend/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/agent_framework_oci/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..32e74b9 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/presentation/tool_renderers.py b/agent_framework_oci/templates/agent_template_backend/app/presentation/tool_renderers.py new file mode 100644 index 0000000..f77c47a --- /dev/null +++ b/agent_framework_oci/templates/agent_template_backend/app/presentation/tool_renderers.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_telecom_invoice(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + return f"[{agent_label}] Fatura consultada: {result}." + + +def render_telecom_plan(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + plano = result.get("plano") + if plano is None: + return None + parts = [f"[{agent_label}] Seu plano é {plano}"] + internet_gb = result.get("internet_gb") + status = result.get("status") + if internet_gb is not None: + parts.append(f"com {internet_gb} GB") + if status is not None: + parts.append(f"status {status}") + return ", ".join(parts) + "." + + +def render_retail_order(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + status = result.get("status") + if order_id is None or status is None: + return None + lines = [f"[{agent_label}] Pedido {order_id}: status {status}."] + total = result.get("valor_total") + if total is not None: + lines.append(f"Valor total: R$ {_money_brl(total)}.") + items = result.get("itens") or [] + rendered_items: list[str] = [] + if isinstance(items, list): + for item in items: + if isinstance(item, dict): + value = item.get("descricao") or item.get("nome") or item.get("sku") + else: + value = item + if value not in (None, ""): + rendered_items.append(str(value)) + if rendered_items: + lines.append("Itens: " + "; ".join(rendered_items) + ".") + return " ".join(lines) + + +def render_retail_delivery(*, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str) -> str | None: + order_id = result.get("order_id") + transportadora = result.get("transportadora") + codigo = result.get("codigo_rastreio") + previsao = result.get("previsao_entrega") + if any(v is None for v in (order_id, transportadora, codigo, previsao)): + return None + return ( + f"[{agent_label}] Entrega do pedido {order_id}: transportadora {transportadora}, " + f"rastreio {codigo}, previsão {previsao}." + ) + + +def register_tool_renderers() -> None: + register_tool_response_renderer("telecom.invoice", render_telecom_invoice) + register_tool_response_renderer("telecom.plan", render_telecom_plan) + register_tool_response_renderer("retail.order", render_retail_order) + register_tool_response_renderer("retail.delivery", render_retail_delivery) diff --git a/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..d217376 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc new file mode 100644 index 0000000..9ec9aa1 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/workflow_actions/__pycache__/devolucao.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..c5a3581 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml b/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/templates/agent_template_backend/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/templates/agent_template_backend/config/routing.yaml b/agent_framework_oci/templates/agent_template_backend/config/routing.yaml index 2dbe95e..03aeaa9 100644 --- a/agent_framework_oci/templates/agent_template_backend/config/routing.yaml +++ b/agent_framework_oci/templates/agent_template_backend/config/routing.yaml @@ -79,6 +79,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml b/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml index 3c4fd05..9b1ab9e 100644 --- a/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml +++ b/agent_framework_oci/templates/agent_template_backend/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/templates/agent_template_backend/config/tools.yaml b/agent_framework_oci/templates/agent_template_backend/config/tools.yaml index d85fae1..f8a53ca 100644 --- a/agent_framework_oci/templates/agent_template_backend/config/tools.yaml +++ b/agent_framework_oci/templates/agent_template_backend/config/tools.yaml @@ -10,6 +10,9 @@ tools: - fatura - conta - boleto + response: + mode: renderer + renderer: telecom.invoice consultar_pagamentos: description: Consulta histórico de pagamentos do cliente. mcp_server: telecom @@ -28,6 +31,9 @@ tools: asset_id: string selection_keywords: - plano + response: + mode: renderer + renderer: telecom.plan listar_servicos: description: Lista serviços ativos e adicionais VAS. mcp_server: telecom @@ -49,6 +55,9 @@ tools: - consultar pedido - status do pedido - pedido + response: + mode: renderer + renderer: retail.order consultar_entrega: description: Consulta entrega e rastreamento do pedido. mcp_server: retail @@ -61,6 +70,26 @@ tools: - rastreamento - transportadora - previsão + response: + mode: renderer + renderer: retail.delivery + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..eafedee Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..c13b42c Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/main.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc new file mode 100644 index 0000000..a8f0280 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/mcp_gateway_client_factory.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..f856d0c Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/__pycache__/state.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc new file mode 100644 index 0000000..406206d Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/billing_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc new file mode 100644 index 0000000..cbe2d5d Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/orders_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc new file mode 100644 index 0000000..d394a5b Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/product_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..d96c6e4 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..0d39586 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc new file mode 100644 index 0000000..9dee705 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/agents/__pycache__/support_agent.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..359dee6 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..76a03bb Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc new file mode 100644 index 0000000..a39b585 Binary files /dev/null and b/agent_framework_oci/templates/agent_template_backend_day_zero/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml index 5b29ccf..91d57af 100644 --- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml +++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/mcp_parameter_mapping.yaml @@ -60,6 +60,18 @@ mcp_parameter_mapping: mensagem. pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b group: 1 + cancelar_pedido: + map: + session_key: session_id + extract: + order_id: + from: message + type: string + strategy: hybrid + description: Extraia somente o identificador do pedido informado explicitamente pelo usuário. Retorne null quando não houver identificador de pedido na mensagem. + pattern: (?i)\\b(?:pedido|order)\\s*[:#-]?\\s*([A-Z0-9-]+)\\b + group: 1 + solicitar_troca: map: session_key: session_id diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml index 34c9781..187a070 100644 --- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml +++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/routing.yaml @@ -84,6 +84,25 @@ intents: - Quero saber sobre meu pacote de internet. - Tenho roaming internacional? + + - name: retail_order_cancel + domain: retail + agent: orders_agent + description: Cancelamento explícito de pedido ou compra. + priority: 20 + mcp_tools: + - consultar_pedido + - cancelar_pedido + keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar a compra + - cancelar compra + examples: + - Quero cancelar meu pedido. + - Cancele o pedido. + - Quero cancelar a compra. + - name: retail_order_tracking domain: retail agent: orders_agent diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml index 66c9854..48a83d5 100644 --- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml +++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tool_policies.yaml @@ -7,6 +7,12 @@ defaults: require_confirmation: false tool_policies: + cancelar_pedido: + operation_type: transactional + require_confirmation: true + requires: [order_id] + + solicitar_troca: operation_type: transactional require_confirmation: true diff --git a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml index d85fae1..2329dc7 100644 --- a/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml +++ b/agent_framework_oci/templates/agent_template_backend_day_zero/config/tools.yaml @@ -61,6 +61,23 @@ tools: - rastreamento - transportadora - previsão + cancelar_pedido: + description: Simula o cancelamento de um pedido de varejo. + mcp_server: retail + enabled: true + tool_type: action + requires: + - order_id + confirmation_required: true + args_schema: + order_id: string + selection_keywords: + - cancelar pedido + - cancelamento do pedido + - cancelar compra + - cancelar a compra + + solicitar_troca: description: Simula abertura de solicitação de troca. mcp_server: retail diff --git a/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index db53f3f..dff3d68 100644 Binary files a/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..fccf00c Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_generic_tool_response_presentation.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/__pycache__/test_mcp_parameter_extraction_runtime.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_mcp_parameter_extraction_runtime.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..2c07d30 Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_mcp_parameter_extraction_runtime.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..e3e12c1 Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..5589e90 Binary files /dev/null and b/agent_framework_oci/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/test_generic_tool_response_presentation.py b/agent_framework_oci/tests/test_generic_tool_response_presentation.py new file mode 100644 index 0000000..f57feaa --- /dev/null +++ b/agent_framework_oci/tests/test_generic_tool_response_presentation.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from types import SimpleNamespace + +from agent_framework.presentation import register_tool_response_renderer +from agent_framework.runtime.agent_runtime import AgentRuntimeMixin + + +class _Registry: + def __init__(self, responses): + self.responses = responses + + def get_tool(self, name): + response = self.responses.get(name) + if response is None: + return None + return SimpleNamespace(response=response) + + +class _Runtime(AgentRuntimeMixin): + def __init__(self, responses): + self.tool_router = SimpleNamespace(registry=_Registry(responses)) + + def _mcp_rag_directive(self, results): + return False, None + + def _mcp_llm_composition_directive(self, results): + return False, None + + def _transactional_action_match(self, text): + return None + + def _workflow_payload_from_tool_result(self, item): + return None + + +def _result(tool, data): + return [{"tool_name": tool, "ok": True, "result": data}] + + +def test_renderer_mode_uses_application_registered_renderer(): + def renderer(*, tool_name, result, state, agent_label): + return f"[{agent_label}] {result['name']} / {state['intent']}" + + register_tool_response_renderer("test.entity", renderer) + rt = _Runtime({"consultar_algo": {"mode": "renderer", "renderer": "test.entity"}}) + answer = rt.build_direct_mcp_answer( + {"user_text": "consulta", "intent": "test_intent"}, + _result("consultar_algo", {"name": "OK"}), + agent_label="TestAgent", + ) + assert answer == "[TestAgent] OK / test_intent" + + +def test_missing_renderer_falls_back_without_breaking_runtime(): + rt = _Runtime({"consultar_plano": {"mode": "renderer", "renderer": "missing.renderer"}}) + answer = rt.build_direct_mcp_answer( + {"user_text": "qual meu plano"}, + _result("consultar_plano", {"plano": "Controle", "internet_gb": 50, "status": "ATIVO"}), + agent_label="ProductAgent", + ) + assert answer == "[ProductAgent] Seu plano é Controle, com 50 GB e status ATIVO." + + +def test_no_declared_response_keeps_legacy_fallback(): + rt = _Runtime({}) + answer = rt.build_direct_mcp_answer( + {"user_text": "qual meu plano"}, + _result("consultar_plano", {"plano": "Controle", "internet_gb": 50, "status": "ATIVO"}), + agent_label="ProductAgent", + ) + assert answer == "[ProductAgent] Seu plano é Controle, com 50 GB e status ATIVO." diff --git a/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py b/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py index a70c57e..86b8618 100644 --- a/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py +++ b/agent_framework_oci/tests/test_mcp_parameter_extraction_runtime.py @@ -59,3 +59,54 @@ async def test_runtime_extracts_order_id_from_current_message(): ) assert result["order_id"] == "123" assert result["contract_key"] == "3000131180" + +class _ContestExtractLLM: + async def ainvoke(self, messages, **kwargs): + prompt = messages[0]["content"] + if "Campo: subject" in prompt: + return {"content": '{"subject": "TIM CTRL Redes Sociais 8.0"}'} + if "Campo: valor" in prompt: + return {"content": '{"valor": null}'} + return {"content": '{}'} + + +class _ContestExtractRouter: + def parameter_extract_rules(self, tool_name): + return { + "subject": { + "from": "message", + "strategy": "llm", + "type": "string", + "description": "Extraia o item contestado.", + }, + "valor": { + "from": "message", + "strategy": "llm", + "type": "number", + "description": "Extraia o valor explicitamente informado.", + }, + } + + +class _ContestExtractRuntime(AgentRuntimeMixin): + def __init__(self): + self.tool_router = _ContestExtractRouter() + self.llm = _ContestExtractLLM() + + +@pytest.mark.asyncio +async def test_new_transaction_current_message_overrides_stale_subject_from_context(): + runtime = _ContestExtractRuntime() + result = await runtime._extract_mcp_parameters( + "contestar_cobranca", + {"subject": "TIM Fashion Mensal", "valor": 10.0}, + { + "user_text": "nao contratei TIM CTRL Redes Sociais 8.0", + "sanitized_input": "nao contratei TIM CTRL Redes Sociais 8.0", + }, + overwrite_from_message=True, + ) + assert result["subject"] == "TIM CTRL Redes Sociais 8.0" + # null extraction does not destroy a pre-existing value; transaction start + # sanitization is about explicit current-message evidence, not blind clearing. + assert result["valor"] == 10.0 diff --git a/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py b/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py index 7a7b93e..fee3400 100644 --- a/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py +++ b/agent_framework_oci/tests/test_route_stickiness_transaction_shift.py @@ -1,3 +1,4 @@ +import pytest from types import SimpleNamespace from agent_framework.routing.enterprise_router import EnterpriseRouter @@ -12,3 +13,84 @@ def test_explicit_keyword_shift_preempts_stickiness(): def test_short_generic_keyword_does_not_preempt(): d = RouteDecision(route="x", agent="x", intent="x", method="keyword", metadata={"matched_keyword": "id"}) assert EnterpriseRouter._is_explicit_intent_shift(d) is False + + +def test_same_agent_explicit_intent_shift_must_preempt_stickiness(): + router = object.__new__(EnterpriseRouter) + router.intents = [] + # This test documents the key regression: changing from a read-only intent + # to a transactional intent handled by the SAME agent must still be treated + # as an explicit intent shift. The route() integration tests exercise the + # full async path; here we lock the explicitness predicate itself. + d = RouteDecision( + route="orders_agent", + agent="orders_agent", + intent="retail_order_cancel", + method="keyword", + metadata={"matched_keyword": "cancelar pedido"}, + ) + assert EnterpriseRouter._is_explicit_intent_shift(d) is True + +@pytest.mark.asyncio +async def test_same_agent_transaction_keyword_preempts_continuity(monkeypatch): + """Regression: tracking -> cancelar pedido must not reuse tracking intent.""" + from agent_framework.routing.models import IntentDefinition + + class _Continuity: + async def evaluate(self, state, *, intents): + raise AssertionError("continuity must be preempted by explicit intent shift") + + router = object.__new__(EnterpriseRouter) + router.state_policies = [] + router.intents = [ + IntentDefinition( + name="retail_order_cancel", + domain="retail", + agent="orders_agent", + description="cancelamento", + priority=20, + mcp_tools=["consultar_pedido", "cancelar_pedido"], + keywords=["cancelar pedido"], + examples=[], + enabled=True, + ), + IntentDefinition( + name="retail_order_tracking", + domain="retail", + agent="orders_agent", + description="tracking", + priority=30, + mcp_tools=["consultar_pedido", "consultar_entrega"], + keywords=["pedido"], + examples=[], + enabled=True, + ), + ] + router.continuity = _Continuity() + router.enable_llm_router = False + router.llm = None + router.telemetry = None + router.fallback_agent = "billing_agent" + + decision = await router.route({ + "user_text": "quero cancelar pedido", + "sanitized_input": "quero cancelar pedido", + "active_agent": "orders_agent", + "intent": "retail_order_tracking", + "route_decision": { + "route": "orders_agent", + "agent": "orders_agent", + "intent": "retail_order_tracking", + "domain": "retail", + "mcp_tools": ["consultar_pedido", "consultar_entrega"], + }, + "context": {"session": {}}, + }) + + assert decision.agent == "orders_agent" + assert decision.intent == "retail_order_cancel" + assert decision.method == "keyword" + assert decision.mcp_tools == ["consultar_pedido", "cancelar_pedido"] + assert decision.metadata["route_stickiness_preempted"] is True + assert decision.metadata["previous_agent"] == "orders_agent" + assert decision.metadata["previous_intent"] == "retail_order_tracking" diff --git a/agent_framework_oci/tests/test_transactional_tool_flow.py b/agent_framework_oci/tests/test_transactional_tool_flow.py index f292bec..e2597ec 100644 --- a/agent_framework_oci/tests/test_transactional_tool_flow.py +++ b/agent_framework_oci/tests/test_transactional_tool_flow.py @@ -88,3 +88,202 @@ async def test_transaction_waits_then_executes_after_confirmation(): assert runtime.calls[-1][0] == "solicitar_devolucao" assert runtime.calls[-1][1]["confirmed"] is True assert second[-1]["ok"] is True + +class _ContestPolicyRouter: + def __init__(self): + from types import SimpleNamespace + self.registry = SimpleNamespace( + tools={"contestar_cobranca": object()}, + get_tool=lambda name: SimpleNamespace(selection_keywords=["contestar", "não contratei", "nao contratei"]), + ) + + def resolve_execution_policy(self, tool_name, arguments=None): + return { + "operation_type": "transactional", + "require_confirmation": True, + "requires": ["subject", "valor"], + "policy_source": "test", + } + + def validate_execution_policy(self, tool_name, arguments=None): + policy = self.resolve_execution_policy(tool_name, arguments) + return True, None, policy + + +class _ContestRuntime(AgentRuntimeMixin): + def __init__(self): + self.tool_router = _ContestPolicyRouter() + self.calls = [] + + async def _call_mcp_tool(self, tool_name, arguments, state): + self.calls.append((tool_name, dict(arguments))) + return {"ok": True, "tool_name": tool_name, "result": {"status": "OPENED"}} + + +@pytest.mark.asyncio +async def test_collecting_parameters_does_not_replace_collected_subject_with_stale_context(): + """A later value-only turn must not replace an already collected subject. + + Regression reproduced from Contas: subject was collected as TIM CTRL, while + context/tool_arguments still exposed TIM Fashion from another transaction. + When the user supplied only R$ 71,99, the stale context used to overwrite + the collected subject before confirmation. + """ + runtime = _ContestRuntime() + state = { + "user_text": "R$ 71,99", + "sanitized_input": "R$ 71,99", + "route": "contestacao_agent", + "intent": "state:COLLECTING_CONTESTACAO_PARAMETERS", + "transaction_status": "COLLECTING_PARAMETERS", + "selected_tool_call": { + "tool_name": "contestar_cobranca", + "arguments": { + "subject": "TIM CTRL Redes Sociais 8.0", + "motivo": "não contratei", + }, + }, + # Simulates stale contextual arguments left by another action/session turn. + "context": { + "tool_arguments": { + "subject": "TIM Fashion Mensal", + "valor": 71.99, + } + }, + } + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + assert result[-1]["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["pending_tool_call"]["arguments"]["subject"] == "TIM CTRL Redes Sociais 8.0" + assert state["pending_tool_call"]["arguments"]["valor"] == 71.99 + assert state["pending_tool_call"]["arguments"]["motivo"] == "não contratei" + assert state["pending_tool_call"]["arguments"]["query"] == "R$ 71,99" + assert runtime.calls == [] + +class _InitialContestLLM: + async def ainvoke(self, messages, **kwargs): + prompt = messages[0]["content"] + if "Campo: subject" in prompt: + return {"content": '{"subject": "TIM CTRL Redes Sociais 8.0"}'} + if "Campo: valor" in prompt: + return {"content": '{"valor": null}'} + if "Campo: motivo" in prompt: + return {"content": '{"motivo": "não contratei"}'} + return {"content": '{}'} + + +class _InitialContestRouter(_ContestPolicyRouter): + def resolve_execution_policy(self, tool_name, arguments=None): + return { + "operation_type": "transactional", + "require_confirmation": True, + "requires": ["subject"], + "policy_source": "test", + } + + def parameter_extract_rules(self, tool_name): + return { + "subject": {"from": "message", "strategy": "llm", "type": "string", "description": "item"}, + "valor": {"from": "message", "strategy": "llm", "type": "number", "description": "valor"}, + "motivo": {"from": "message", "strategy": "llm", "type": "string", "description": "motivo"}, + } + + +class _InitialContestRuntime(AgentRuntimeMixin): + def __init__(self): + self.tool_router = _InitialContestRouter() + self.llm = _InitialContestLLM() + self.calls = [] + + async def _call_mcp_tool(self, tool_name, arguments, state): + self.calls.append((tool_name, dict(arguments))) + return {"ok": True, "tool_name": tool_name, "result": {"status": "OPENED"}} + + +@pytest.mark.asyncio +async def test_new_contestation_does_not_inherit_subject_or_value_from_previous_transaction(): + runtime = _InitialContestRuntime() + state = { + "user_text": "nao contratei TIM CTRL Redes Sociais 8.0", + "sanitized_input": "nao contratei TIM CTRL Redes Sociais 8.0", + "mcp_tools": ["contestar_cobranca"], + "route": "contestacao_agent", + "intent": "contas_contestation", + "context": { + "tool_arguments": { + "subject": "TIM Fashion Mensal", + "valor": 10.0, + "motivo": "contestação antiga", + } + }, + } + + result = await runtime.execute_tools_for_intent(state) + pending = state["pending_tool_call"]["arguments"] + assert result[-1]["transaction_status"] == "AWAITING_CONFIRMATION" + assert pending["subject"] == "TIM CTRL Redes Sociais 8.0" + assert pending["motivo"] == "não contratei" + assert "valor" not in pending + assert runtime.calls == [] + +@pytest.mark.asyncio +async def test_closed_transaction_is_not_operational_context_for_next_turn(): + runtime = _InitialContestRuntime() + state = { + "user_text": "nao contratei TIM CTRL Redes Sociais 8.0", + "sanitized_input": "nao contratei TIM CTRL Redes Sociais 8.0", + "mcp_tools": ["contestar_cobranca"], + "route": "contestacao_agent", + "intent": "contas_contestation", + # Historical/closed transaction must never feed the new one. + "transaction_status": "COMPLETED", + "selected_tool_call": { + "tool_name": "cancelar_vas_avulso", + "arguments": {"subject": "TIM Fashion Mensal", "valor": 10.0}, + }, + "pending_tool_call": {}, + "context": { + "tool_arguments": { + "subject": "TIM Fashion Mensal", + "valor": 10.0, + } + }, + } + + result = await runtime.execute_tools_for_intent(state) + + assert result[-1]["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["active_transaction"]["tool_name"] == "contestar_cobranca" + assert state["active_transaction"]["arguments"]["subject"] == "TIM CTRL Redes Sociais 8.0" + assert state["pending_tool_call"]["tool_name"] == "contestar_cobranca" + assert state["pending_tool_call"]["arguments"]["subject"] == "TIM CTRL Redes Sociais 8.0" + assert state["last_transaction"]["tool_name"] == "cancelar_vas_avulso" + assert state["last_transaction"]["arguments"]["subject"] == "TIM Fashion Mensal" + + +@pytest.mark.asyncio +async def test_terminal_confirmation_closes_active_transaction_and_clears_latches(): + runtime = _Runtime() + state = { + "user_text": "Quero devolver o pedido 123 porque me arrependi", + "sanitized_input": "Quero devolver o pedido 123 porque me arrependi", + "mcp_tools": ["consultar_pedido", "solicitar_devolucao"], + "route": "support_agent", + "intent": "retail_support_exchange_return", + } + await runtime.execute_tools_for_intent(state) + assert state["active_transaction"]["status"] == "AWAITING_CONFIRMATION" + + state["user_text"] = "sim" + state["sanitized_input"] = "sim" + await runtime.execute_tools_for_intent(state) + + assert state["transaction_status"] == "COMPLETED" + assert state["active_transaction"] is None + assert state["selected_tool_call"] == {} + assert state["pending_tool_call"] == {} + assert state["missing_parameters"] == [] + assert state["next_state"] is None + assert state["last_transaction"]["tool_name"] == "solicitar_devolucao" + assert state["last_transaction"]["status"] == "COMPLETED" diff --git a/agent_framework_oci/tests/unit/__pycache__/test_langgraph_checkpoint_saver.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/unit/__pycache__/test_langgraph_checkpoint_saver.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..3fdd435 Binary files /dev/null and b/agent_framework_oci/tests/unit/__pycache__/test_langgraph_checkpoint_saver.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/unit/__pycache__/test_resilient_checkpointer.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/unit/__pycache__/test_resilient_checkpointer.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..8453472 Binary files /dev/null and b/agent_framework_oci/tests/unit/__pycache__/test_resilient_checkpointer.cpython-313-pytest-9.0.2.pyc differ diff --git a/agent_framework_oci/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..454695e Binary files /dev/null and b/agent_framework_oci/tests/unit/__pycache__/test_semantic_route_stickiness.cpython-313-pytest-9.0.2.pyc differ diff --git a/app/__pycache__/state.cpython-313.pyc b/app/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000..e4c9a18 Binary files /dev/null and b/app/__pycache__/state.cpython-313.pyc differ diff --git a/app/agents/__pycache__/contas_prompting.cpython-313.pyc b/app/agents/__pycache__/contas_prompting.cpython-313.pyc new file mode 100644 index 0000000..61d4422 Binary files /dev/null and b/app/agents/__pycache__/contas_prompting.cpython-313.pyc differ diff --git a/app/agents/__pycache__/contestacao_agent.cpython-313.pyc b/app/agents/__pycache__/contestacao_agent.cpython-313.pyc new file mode 100644 index 0000000..0cea819 Binary files /dev/null and b/app/agents/__pycache__/contestacao_agent.cpython-313.pyc differ diff --git a/app/agents/__pycache__/faturas_agent.cpython-313.pyc b/app/agents/__pycache__/faturas_agent.cpython-313.pyc new file mode 100644 index 0000000..aaaddde Binary files /dev/null and b/app/agents/__pycache__/faturas_agent.cpython-313.pyc differ diff --git a/app/agents/__pycache__/prompting.cpython-313.pyc b/app/agents/__pycache__/prompting.cpython-313.pyc new file mode 100644 index 0000000..3e5a5bc Binary files /dev/null and b/app/agents/__pycache__/prompting.cpython-313.pyc differ diff --git a/app/agents/__pycache__/runtime.cpython-313.pyc b/app/agents/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000..8def84e Binary files /dev/null and b/app/agents/__pycache__/runtime.cpython-313.pyc differ diff --git a/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc b/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc new file mode 100644 index 0000000..7667f05 Binary files /dev/null and b/app/agents/__pycache__/suporte_contas_agent.cpython-313.pyc differ diff --git a/app/agents/__pycache__/vas_agent.cpython-313.pyc b/app/agents/__pycache__/vas_agent.cpython-313.pyc new file mode 100644 index 0000000..5c848fd Binary files /dev/null and b/app/agents/__pycache__/vas_agent.cpython-313.pyc differ diff --git a/app/agents/runtime.py b/app/agents/runtime.py index e6429c4..7a1a9be 100644 --- a/app/agents/runtime.py +++ b/app/agents/runtime.py @@ -3,5 +3,8 @@ from __future__ import annotations # Compatibilidade local do template/backend. # A implementação oficial agora fica no framework para evitar duplicação entre agentes. from agent_framework.runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext +from app.presentation import register_tool_renderers + +register_tool_renderers() __all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"] diff --git a/app/domain/__pycache__/__init__.cpython-313.pyc b/app/domain/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..2ad9b61 Binary files /dev/null and b/app/domain/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/__init__.cpython-313.pyc b/app/domain/contas/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..381f890 Binary files /dev/null and b/app/domain/contas/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/client.cpython-313.pyc b/app/domain/contas/__pycache__/client.cpython-313.pyc new file mode 100644 index 0000000..c4e531a Binary files /dev/null and b/app/domain/contas/__pycache__/client.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc b/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc new file mode 100644 index 0000000..79a4578 Binary files /dev/null and b/app/domain/contas/__pycache__/contestation_rules.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc b/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc new file mode 100644 index 0000000..84aa2e9 Binary files /dev/null and b/app/domain/contas/__pycache__/ic_tags.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/informational_context.cpython-313.pyc b/app/domain/contas/__pycache__/informational_context.cpython-313.pyc new file mode 100644 index 0000000..0338323 Binary files /dev/null and b/app/domain/contas/__pycache__/informational_context.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc b/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc new file mode 100644 index 0000000..0b31ba0 Binary files /dev/null and b/app/domain/contas/__pycache__/invoice_context.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc b/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc new file mode 100644 index 0000000..c3eb1c8 Binary files /dev/null and b/app/domain/contas/__pycache__/invoice_models.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc b/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc new file mode 100644 index 0000000..8ae4984 Binary files /dev/null and b/app/domain/contas/__pycache__/invoice_resolver.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc b/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc new file mode 100644 index 0000000..5cddda6 Binary files /dev/null and b/app/domain/contas/__pycache__/item_matcher.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc b/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc new file mode 100644 index 0000000..6c0de27 Binary files /dev/null and b/app/domain/contas/__pycache__/pro_rata_rules.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc b/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc new file mode 100644 index 0000000..aef22d7 Binary files /dev/null and b/app/domain/contas/__pycache__/protocol_triplets.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc b/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc new file mode 100644 index 0000000..574bf5e Binary files /dev/null and b/app/domain/contas/__pycache__/rct_policy.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/service.cpython-313.pyc b/app/domain/contas/__pycache__/service.cpython-313.pyc new file mode 100644 index 0000000..cbc6ad9 Binary files /dev/null and b/app/domain/contas/__pycache__/service.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc b/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc new file mode 100644 index 0000000..93334ae Binary files /dev/null and b/app/domain/contas/__pycache__/string_metrics.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc b/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc new file mode 100644 index 0000000..498ed2b Binary files /dev/null and b/app/domain/contas/__pycache__/vas_cancellation_message.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc b/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc new file mode 100644 index 0000000..cfd99cc Binary files /dev/null and b/app/domain/contas/__pycache__/vas_variation.cpython-313.pyc differ diff --git a/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc b/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc new file mode 100644 index 0000000..2b73a38 Binary files /dev/null and b/app/domain/contas/__pycache__/workflow_actions.cpython-313.pyc differ diff --git a/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc b/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc new file mode 100644 index 0000000..5aaabe1 Binary files /dev/null and b/app/domain/contas/integrations/__pycache__/secure_pdf_crypto.cpython-313.pyc differ diff --git a/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc b/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..84bb16b Binary files /dev/null and b/app/domain/contas/parsers/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc b/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc new file mode 100644 index 0000000..3bf1bd8 Binary files /dev/null and b/app/domain/contas/parsers/__pycache__/bill_parser.cpython-313.pyc differ diff --git a/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc b/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc new file mode 100644 index 0000000..4aa36a9 Binary files /dev/null and b/app/domain/contas/parsers/__pycache__/bill_processor.cpython-313.pyc differ diff --git a/app/domain/contas/service.py b/app/domain/contas/service.py index ff78679..84663fc 100644 --- a/app/domain/contas/service.py +++ b/app/domain/contas/service.py @@ -37,6 +37,47 @@ class ContasDomainService: def consultar_faturas(self, *, msisdn: str, **_: Any) -> Any: return self.client.consultar_faturas(msisdn) + def consultar_plano(self, *, msisdn: str, **args: Any) -> dict[str, Any]: + """Retorna somente os planos presentes na evidência de billing analysis. + + A consulta é deliberadamente determinística: não usa VAS, RAG nem o + workflow de invoice_explanation para responder qual é o plano do cliente. + """ + billing = args.get("billing_analysis") + if not isinstance(billing, dict): + billing = self.client.billing_analysis( + msisdn, + invoice_id=args.get("invoice_id"), + customer_id=args.get("customer_id"), + ) + + plans: list[dict[str, Any]] = [] + for section in billing.get("currentInvoice") or [] if isinstance(billing, dict) else []: + if not isinstance(section, dict): + continue + section_type = str(section.get("type") or "").strip().casefold() + section_desc = str(section.get("desc") or "").strip().casefold() + if section_type not in {"plano", "planos"} and section_desc not in {"plano", "planos"}: + continue + for item in section.get("items") or []: + if not isinstance(item, dict): + continue + name = str(item.get("desc") or item.get("name") or "").strip() + if not name: + continue + plans.append({ + "name": name, + "value": item.get("value"), + "type": str(item.get("type") or section.get("type") or "plano"), + "contestable": item.get("contestable"), + }) + + return { + "plans": plans, + "count": len(plans), + "source": "billing_analysis.currentInvoice", + } + def invoice_explanation(self, *, msisdn: str, **args: Any) -> dict[str, Any]: # Reuse framework-prefetched evidence when available; the domain never owns # a second cache/session implementation. Explanation itself is produced by diff --git a/app/observability/__pycache__/__init__.cpython-313.pyc b/app/observability/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..7f5ed4c Binary files /dev/null and b/app/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/observability/__pycache__/telemetry_observer.cpython-313.pyc b/app/observability/__pycache__/telemetry_observer.cpython-313.pyc new file mode 100644 index 0000000..51057b8 Binary files /dev/null and b/app/observability/__pycache__/telemetry_observer.cpython-313.pyc differ diff --git a/app/presentation/__init__.py b/app/presentation/__init__.py new file mode 100644 index 0000000..c0eba0d --- /dev/null +++ b/app/presentation/__init__.py @@ -0,0 +1,3 @@ +from .tool_renderers import register_tool_renderers + +__all__ = ["register_tool_renderers"] diff --git a/app/presentation/__pycache__/__init__.cpython-313.pyc b/app/presentation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..14330eb Binary files /dev/null and b/app/presentation/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/presentation/__pycache__/tool_renderers.cpython-313.pyc b/app/presentation/__pycache__/tool_renderers.cpython-313.pyc new file mode 100644 index 0000000..aadb4a7 Binary files /dev/null and b/app/presentation/__pycache__/tool_renderers.cpython-313.pyc differ diff --git a/app/presentation/tool_renderers.py b/app/presentation/tool_renderers.py new file mode 100644 index 0000000..510b60a --- /dev/null +++ b/app/presentation/tool_renderers.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +from typing import Any + +from agent_framework.presentation import register_tool_response_renderer + + +def _money_brl(value: Any) -> str: + try: + return f"{float(value):.2f}".replace(".", ",") + except (TypeError, ValueError): + return str(value) + + +def render_contas_plan( + *, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str +) -> str | None: + plans = result.get("plans") + if not isinstance(plans, list) or not plans: + return None + + lines: list[str] = [] + for plan in plans: + if not isinstance(plan, dict): + continue + name = plan.get("name") + if not name: + continue + value = plan.get("value") + line = f"- {name}" + if value is not None: + line += f": R$ {_money_brl(value)}" + lines.append(line) + + if not lines: + return None + if len(lines) == 1: + return f"[{agent_label}] Seu plano é:\n{lines[0]}" + return f"[{agent_label}] Existem {len(lines)} planos associados à sua conta:\n" + "\n".join(lines) + + + +def _service_value(service: dict[str, Any]) -> Any: + details = service.get("details") if isinstance(service.get("details"), dict) else {} + for candidate in ( + details.get("valor"), + details.get("value"), + service.get("valor"), + service.get("value"), + service.get("price"), + ): + if candidate not in (None, ""): + return candidate + return None + + +def _money_brl_preserve(value: Any) -> str: + if value is None: + return "" + text = str(value).strip() + if not text: + return "" + # TIM APIs may already return BRL decimal strings such as "10,00". + # Normalize only when needed and never invent a value. + if "," in text and "." not in text: + try: + return f"{float(text.replace(',', '.')):.2f}".replace(".", ",") + except ValueError: + return text + return _money_brl(value) + + +def render_contas_vas( + *, tool_name: str, result: dict[str, Any], state: dict[str, Any], agent_label: str +) -> str | None: + services = result.get("products") or result.get("services") + if not isinstance(services, list) or not services: + return None + + lines: list[str] = [] + for service in services: + if not isinstance(service, dict): + continue + name = service.get("name") or service.get("description") or service.get("billDescription") + if not name: + continue + + details = service.get("details") if isinstance(service.get("details"), dict) else {} + can = service.get("can") if isinstance(service.get("can"), dict) else {} + details_can = details.get("can") if isinstance(details.get("can"), dict) else {} + cancelable = bool(can.get("cancel", details_can.get("cancel", False))) + category = service.get("classe") or details.get("classe") or ("avulso" if cancelable else None) + value = _service_value(service) + + line = f"- {name}" + if category: + line += f" ({category})" + if value not in (None, ""): + line += f": R$ {_money_brl_preserve(value)}" + lines.append(line) + + if not lines: + return None + return f"[{agent_label}] Você possui os seguintes serviços ativos:\n" + "\n".join(lines) + +def register_tool_renderers() -> None: + register_tool_response_renderer("contas.plan", render_contas_plan) + register_tool_response_renderer("contas.vas", render_contas_vas) diff --git a/app/workflows/__pycache__/agent_graph.cpython-313.pyc b/app/workflows/__pycache__/agent_graph.cpython-313.pyc index f04dab5..40457f0 100644 Binary files a/app/workflows/__pycache__/agent_graph.cpython-313.pyc and b/app/workflows/__pycache__/agent_graph.cpython-313.pyc differ diff --git a/config/mcp_parameter_mapping.yaml b/config/mcp_parameter_mapping.yaml index e3dff43..6f9322a 100644 --- a/config/mcp_parameter_mapping.yaml +++ b/config/mcp_parameter_mapping.yaml @@ -6,6 +6,10 @@ mcp_parameter_mapping: map: customer_key: msisdn session_key: session_id + consultar_plano: + map: + customer_key: msisdn + session_key: session_id invoice_explanation: map: customer_key: msisdn diff --git a/config/prompts/billing.yaml b/config/prompts/billing.yaml index 3a8914a..1f249ae 100644 --- a/config/prompts/billing.yaml +++ b/config/prompts/billing.yaml @@ -6,3 +6,15 @@ system: | Se houver resultado de invoice_explanation, preserve nomes, valores e conclusões vindas da tool. Não prometa nem estime valores de faturas futuras. Seja objetivo e mantenha o comportamento de negócio do Contas. + + Quando o cliente perguntar pelo plano contratado, use somente itens + classificados como plano na fatura/tool. Não liste VAS, streamings ou benefícios + como se fossem o plano principal. + + Se houver mais de um plano, diga que há mais de um plano associado à conta + e liste cada um com seu respectivo valor quando disponível. + + Não use informações de uma cobrança antiga para completar a pergunta atual. + + Quando houver resultado de tool, não extrapole procedimentos ou políticas + que não estejam nesse resultado. \ No newline at end of file diff --git a/config/prompts/contestation.yaml b/config/prompts/contestation.yaml index 3929aff..3d20fdb 100644 --- a/config/prompts/contestation.yaml +++ b/config/prompts/contestation.yaml @@ -1,7 +1,43 @@ system: | Você é o agente de Contestação e Ações do Contas TIM. - Trabalhe apenas com itens explicitamente identificados pelos dados da fatura/tool. - Cancelamento de VAS avulso, contestação e demais mutações são transações determinísticas: nunca execute ação em texto livre. - O agent_framework_oci coleta parâmetros, mantém stickiness e exige confirmação antes da tool transacional. - Não conceda ressarcimento em dobro quando a regra de negócio não permitir. - Comunique o resultado real da tool, sem inventar protocolo ou sucesso. + + Trabalhe somente com itens, valores, categorias e resultados explicitamente + presentes na mensagem atual, nas tools ou no contexto de fatura autorizado. + + A referência explícita mais recente do cliente ao item da cobrança prevalece + sobre itens mencionados anteriormente. Nunca reutilize um serviço antigo para + completar uma nova contestação quando o cliente identificou outro item. + + Diferencie: + - VAS avulso: serviço adicional classificado como avulso. + - VAS estratégico: serviço estratégico; não o transforme em avulso por inferência. + - Incluso no plano: benefício do plano; não o trate como VAS cancelável. + - Plano: item classificado como plano na fatura; não o trate como VAS. + - Juros, multas e demais categorias: preserve a classificação retornada pela fatura/tool. + + "Não conheço", "não sabia que tinha" ou dúvida sobre o que é um serviço não + significam automaticamente que o cliente deseja contestar ou cancelar. + "Não contratei", "não reconheço a contratação" ou pedido explícito de contestação + caracterizam intenção de contestação quando houver item identificável. + + Se a referência do cliente for ambígua e houver mais de um item possível, + não escolha um deles por inferência. Peça clarificação. + + Cancelamento, contestação e demais mutações são transações determinísticas. + Nunca execute nem simule uma ação em texto livre. + Confirmação, stickiness, coleta de parâmetros e execução pertencem ao + agent_framework_oci. + + Ao receber resultado de tool: + - preserve exatamente o item e o valor retornados; + - preserve a categoria retornada; + - não substitua o item por outro encontrado na fatura; + - não declare sucesso quando a tool falhou ou bloqueou; + - não invente protocolo. + + Se a tool retornar OUT_OF_SCOPE, bloqueio ou categoria não tratável, + explique somente o motivo efetivamente retornado. + Não invente canal alternativo, central de atendimento, gravações, histórico, + prazo, estorno, recurso ou procedimento que não esteja explícito em tool ou RAG. + + Seja objetivo e responda apenas sobre a solicitação atual. \ No newline at end of file diff --git a/config/prompts/vas.yaml b/config/prompts/vas.yaml index 88c0d71..9f04a04 100644 --- a/config/prompts/vas.yaml +++ b/config/prompts/vas.yaml @@ -12,3 +12,12 @@ system: | Quando a intent for consulta do plano principal, responda somente com os dados de consultar_plano; não substitua o plano por uma lista de VAS. Em consultas informativas, não termine com convite para outra ação ou outro canal. + Preserve a classificação original do serviço retornada pela tool/fatura: + avulso, estratégico ou incluso. + + Nunca trate plano como VAS. + Nunca trate benefício incluso como serviço avulso. + Nunca trate estratégico como avulso por similaridade de nome. + + "Não conheço este serviço" é inicialmente uma dúvida sobre o serviço; + não converta automaticamente em pedido de cancelamento ou contestação. \ No newline at end of file diff --git a/config/routing.yaml b/config/routing.yaml index 66c4a2f..6e1ba69 100644 --- a/config/routing.yaml +++ b/config/routing.yaml @@ -21,6 +21,30 @@ state_policies: - state: COLLECTING_SUPORTE_CONTAS_PARAMETERS agent: suporte_contas_agent intents: +- name: contas_plan_information + domain: telecom_contas + agent: faturas_agent + priority: 142 + description: Consulta do nome, identificação ou valor do plano principal ou dos planos contratados da linha. Não se aplica a VAS, serviços adicionais, aplicativos, assinaturas, parceiros ou benefícios incluídos. + mcp_tools: + - consultar_plano + keywords: + - qual é meu plano + - qual e meu plano + - qual é o meu plano + - qual e o meu plano + - meu plano contratado + - plano contratado + - nome do meu plano + - que plano eu tenho + - quais são meus planos + - quais sao meus planos + examples: + - Qual é meu plano? + - Qual é o meu plano contratado? + - Que plano eu tenho? + - Qual o nome do meu plano? + - Quais são os planos da minha linha? - name: contas_invoice_query domain: telecom_contas agent: faturas_agent @@ -110,6 +134,12 @@ intents: - ajuste na fatura - estorno - valor indevido + - não contratei + - nao contratei + - não reconheço a contratação + - nao reconheco a contratacao + - não contratei esse plano + - nao contratei esse plano examples: - Quero contestar essa cobrança - Esse valor é indevido @@ -118,7 +148,7 @@ intents: domain: telecom_contas agent: contestacao_agent priority: 145 - description: Cancelamento ou retirada de serviço/VAS e sua cobrança recorrente. + description: Cancelamento ou retirada explicitamente solicitada de serviço/VAS. Não use para simples alegação de contratação não reconhecida; nesses casos use contestação. mcp_tools: - consultar_vas - cancelar_vas_avulso @@ -130,12 +160,9 @@ intents: - retirar servico - desativar serviço - desativar servico - - não contratei esse serviço - - nao contratei esse servico - tirar esse vas examples: - Quero cancelar esse serviço - - Não contratei esse VAS - Quero retirar essa assinatura - name: contas_vas_strategic domain: telecom_contas @@ -169,7 +196,8 @@ intents: agent: vas_agent priority: 110 description: Consulta ou explicação de VAS, serviço adicional, assinatura, aplicativo, - parceiro, bundle ou benefício do plano. + parceiro, bundle ou benefício adicional. Não use esta intent para perguntas sobre + nome, identificação ou valor do plano principal/plano contratado da linha. mcp_tools: - consultar_vas - buscar_informacao diff --git a/config/tools.yaml b/config/tools.yaml index eda07b2..48c3e4b 100644 --- a/config/tools.yaml +++ b/config/tools.yaml @@ -1,7 +1,7 @@ tools: consultar_faturas: - description: Consulta faturas atuais/anteriores, status, vencimento, valor e existência - de fatura aberta sem iniciar o workflow de explicação. + description: Consulta faturas atuais/anteriores, status, vencimento, valor e existência de fatura aberta sem iniciar o + workflow de explicação. mcp_server: contas enabled: true args_schema: @@ -15,9 +15,26 @@ tools: - fatura atual - fatura em aberto - conta em aberto + consultar_plano: + description: Consulta exclusivamente o nome e valor do plano ou planos contratados a partir da evidência de billing analysis; + não retorna VAS como plano. + mcp_server: contas + enabled: true + args_schema: + msisdn: string + selection_keywords: + - qual é meu plano + - qual e meu plano + - plano contratado + - nome do meu plano + - que plano eu tenho + - quais são meus planos + response: + mode: renderer + renderer: contas.plan invoice_explanation: - description: Explica composição, diferença ou variação de valores de uma fatura; - não deve ser usada apenas para consultar/obter a fatura. + description: Explica composição, diferença ou variação de valores de uma fatura; não deve ser usada apenas para consultar/obter + a fatura. mcp_server: contas enabled: true args_schema: @@ -33,8 +50,7 @@ tools: - cobrança diferente - por que esse valor buscar_informacao: - description: Conhecimento do Contas é atendido pelo RagService do agent_framework_oci, - não pelo MCP. + description: Conhecimento do Contas é atendido pelo RagService do agent_framework_oci, não pelo MCP. mcp_server: contas enabled: false args_schema: @@ -52,6 +68,9 @@ tools: enabled: true args_schema: msisdn: string + response: + mode: renderer + renderer: contas.vas selection_keywords: - vas - serviço adicional @@ -74,8 +93,7 @@ tools: - serviços anteriores - servicos anteriores cancelar_vas_avulso: - description: Executa cancelamento de VAS avulso no novo domínio Contas; confirmação - e estado são do framework. + description: Executa cancelamento de VAS avulso no novo domínio Contas; confirmação e estado são do framework. mcp_server: contas enabled: true tool_type: action @@ -93,8 +111,7 @@ tools: - desativar serviço - desativar servico tratar_vas_estrategico: - description: Trata VAS estratégico/bundle no novo domínio Contas; diálogo e estado - são do framework. + description: Trata VAS estratégico/bundle no novo domínio Contas; diálogo e estado são do framework. mcp_server: contas enabled: true tool_type: conversational @@ -115,8 +132,7 @@ tools: - globoplay - amazon prime contestar_cobranca: - description: Executa contestação no novo domínio Contas após confirmação transacional - do framework. + description: Executa contestação no novo domínio Contas após confirmação transacional do framework. mcp_server: contas enabled: true tool_type: action @@ -138,8 +154,7 @@ tools: - ajuste na fatura - estorno finalizar_atendimento: - description: Executa apenas efeitos de domínio da finalização; encerramento, memória - e telemetria ficam no framework. + description: Executa apenas efeitos de domínio da finalização; encerramento, memória e telemetria ficam no framework. mcp_server: contas enabled: true tool_type: action @@ -160,8 +175,7 @@ tools: msisdn: string protocol: string enviar_sms: - description: Envia SMS via integração TIM; use apenas quando um fluxo de domínio - exigir. + description: Envia SMS via integração TIM; use apenas quando um fluxo de domínio exigir. mcp_server: contas enabled: true args_schema: diff --git a/contas_mcp/__pycache__/__init__.cpython-313.pyc b/contas_mcp/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..3a7efe4 Binary files /dev/null and b/contas_mcp/__pycache__/__init__.cpython-313.pyc differ diff --git a/contas_mcp/servers/__pycache__/__init__.cpython-313.pyc b/contas_mcp/servers/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..ea8e9ac Binary files /dev/null and b/contas_mcp/servers/__pycache__/__init__.cpython-313.pyc differ diff --git a/contas_mcp/servers/contas_mcp_server/__pycache__/__init__.cpython-313.pyc b/contas_mcp/servers/contas_mcp_server/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f5092eb Binary files /dev/null and b/contas_mcp/servers/contas_mcp_server/__pycache__/__init__.cpython-313.pyc differ diff --git a/contas_mcp/servers/contas_mcp_server/__pycache__/main.cpython-313.pyc b/contas_mcp/servers/contas_mcp_server/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..e7914c6 Binary files /dev/null and b/contas_mcp/servers/contas_mcp_server/__pycache__/main.cpython-313.pyc differ diff --git a/contas_mcp/servers/contas_mcp_server/main.py b/contas_mcp/servers/contas_mcp_server/main.py index bc51e36..25504e2 100644 --- a/contas_mcp/servers/contas_mcp_server/main.py +++ b/contas_mcp/servers/contas_mcp_server/main.py @@ -1,6 +1,8 @@ from __future__ import annotations import sys +import re +import unicodedata from pathlib import Path from typing import Any, Callable @@ -70,6 +72,7 @@ class ToolCall(BaseModel): TOOLS: dict[str, dict[str, Any]] = { "consultar_faturas": {"description": "Consulta faturas do cliente.", "input_schema": {"msisdn": "string"}}, + "consultar_plano": {"description": "Consulta exclusivamente o plano ou planos contratados presentes na fatura.", "input_schema": {"msisdn": "string"}}, "invoice_explanation": {"description": "Executa o workflow de explicação de fatura com pause/resume pelo WorkflowRuntime do framework.", "input_schema": {"msisdn": "string"}}, "consultar_vas": {"description": "Consulta VAS ativos.", "input_schema": {"msisdn": "string"}}, "consultar_historico_vas": {"description": "Consulta histórico de VAS.", "input_schema": {"msisdn": "string"}}, @@ -111,6 +114,97 @@ def _clarification_option(item: Any) -> dict[str, Any]: return {"label": label, "value": name, "msisdn": msisdn, "charge_date": charge_date} + +def _norm_invoice_name(value: Any) -> str: + """Normalização conservadora para comparação exata de nomes de itens.""" + text = str(value or "").strip().casefold().replace("+", " ") + text = unicodedata.normalize("NFKD", text) + text = "".join(ch for ch in text if not unicodedata.combining(ch)) + text = re.sub(r"[^a-z0-9]+", " ", text) + return re.sub(r"\s+", " ", text).strip() + + +def _invoice_subject_catalog(args: dict[str, Any]) -> list[dict[str, Any]]: + """Catálogo mínimo de itens reais da fatura, sem fuzzy matching. + + É usado como barreira de consistência antes do resolver especializado em VAS. + """ + found: dict[str, dict[str, Any]] = {} + + def add(name: Any, value: Any = None, category: Any = None, contestable: Any = None) -> None: + canonical = str(name or "").strip() + key = _norm_invoice_name(canonical) + if not key: + return + item = found.setdefault(key, {"name": canonical, "value": value, "category": str(category or "").strip(), "contestable": contestable}) + if item.get("value") in (None, "") and value not in (None, ""): + item["value"] = value + if not item.get("category") and category: + item["category"] = str(category).strip() + if item.get("contestable") is None and contestable is not None: + item["contestable"] = contestable + + analysis = args.get("billing_analysis") + if isinstance(analysis, dict): + for section_name in ("currentInvoice", "invoiceVariation"): + sections = analysis.get(section_name) + if not isinstance(sections, list): + continue + for section in sections: + if not isinstance(section, dict): + continue + category = section.get("type") or section.get("desc") + items = section.get("items") + if not isinstance(items, list): + continue + for item in items: + if isinstance(item, dict): + add(item.get("desc") or item.get("name"), item.get("value"), item.get("type") or category, item.get("contestable")) + + detail = args.get("invoice_detail") + if isinstance(detail, dict): + parsed = detail.get("parsed_content") if isinstance(detail.get("parsed_content"), dict) else detail + if isinstance(parsed, dict): + # parsed_content normalmente é agrupado por MSISDN. + roots = list(parsed.values()) if parsed and all(isinstance(v, dict) for v in parsed.values()) else [parsed] + for root in roots: + if not isinstance(root, dict): + continue + plans = root.get("Planos") or root.get("Plano") + if isinstance(plans, dict): + for plan_name, plan_data in plans.items(): + value = plan_data.get("valor_final") if isinstance(plan_data, dict) else None + add(plan_name, value, "plano", False) + for section_name, section_value in root.items(): + if section_name in {"Planos", "Plano"} or not isinstance(section_value, list): + continue + for item in section_value: + if isinstance(item, dict): + add(item.get("desc") or item.get("name"), item.get("value") or item.get("valor_final"), item.get("classe") or section_name, item.get("contestable")) + + return list(found.values()) + + +def _recover_explicit_contestation_subject(args: dict[str, Any]) -> dict[str, Any] | None: + """Recupera item explicitamente citado no texto original da contestação. + + Na confirmação a mensagem corrente pode ser apenas ``sim``. O ``motivo``/ + ``descricao`` preserva a fala que abriu a transação e é uma fonte mais forte + que um ``subject`` residual. Só corrige quando há exatamente um nome canônico + da fatura explicitamente presente; não faz fuzzy matching. + """ + original = " ".join(str(args.get(k) or "") for k in ("motivo", "descricao")).strip() + if not original: + return None + norm_original = f" {_norm_invoice_name(original)} " + matches = [] + for item in _invoice_subject_catalog(args): + name_norm = _norm_invoice_name(item.get("name")) + if name_norm and f" {name_norm} " in norm_original: + matches.append(item) + return matches[0] if len(matches) == 1 else None + + def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None: """Resolve/desambigua o item contra a evidência da fatura antes do workflow. @@ -125,6 +219,42 @@ def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None subject = str(args.get("subject") or "").strip() if not msisdn or not subject: return None + # Contestação pode referenciar qualquer categoria da fatura. Antes do + # resolver especializado em serviços/VAS, preserve um item explicitamente + # citado no texto que abriu a transação. Isso impede que um plano explícito + # seja reinterpretado como outro VAS durante a confirmação. + if name == "contestar_cobranca": + explicit = _recover_explicit_contestation_subject(args) + if explicit is not None: + previous_subject = subject + canonical = str(explicit.get("name") or subject) + args["subject"] = canonical + subject = canonical + if explicit.get("value") not in (None, ""): + args["resolved_value"] = str(explicit.get("value")) + category = str(explicit.get("category") or "").strip() + if category: + args["resolved_category"] = category + if _norm_invoice_name(previous_subject) != _norm_invoice_name(canonical): + args["_subject_corrected_from"] = previous_subject + + # O workflow/CVAL atual de contestar_cobranca trata VAS. Um plano + # existente deve ser rejeitado com o próprio nome/valor, nunca + # convertido em outro serviço. + if _norm_invoice_name(category) in {"plano", "planos"}: + return { + "status": "OUT_OF_SCOPE", + "success": False, + "subject": canonical, + "resolved_value": args.get("resolved_value"), + "category": category or "plano", + "error": f"O item '{canonical}' existe na fatura e é um plano, mas não pertence a uma categoria tratável por esta operação.", + "metadata": { + "subject_corrected_from": args.get("_subject_corrected_from"), + "subject_source": "explicit_contestation_text", + }, + } + try: invoice_detail = args.get("billing_analysis") if not isinstance(invoice_detail, dict): @@ -176,7 +306,7 @@ def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None return None async def _enrich_invoice_context(name: str, args: dict[str, Any]) -> None: - if name not in {"invoice_explanation", "cancelar_vas_avulso", "tratar_vas_estrategico", "contestar_cobranca"}: + if name not in {"consultar_plano", "invoice_explanation", "cancelar_vas_avulso", "tratar_vas_estrategico", "contestar_cobranca"}: return msisdn = str(args.get("msisdn") or "").strip() if not msisdn: @@ -690,6 +820,7 @@ async def _run_cancelamento_com_contestacao(args: dict[str, Any]) -> dict[str, A async def _invoke(name: str, args: dict[str, Any]) -> Any: requirements = { "consultar_faturas": ("msisdn",), + "consultar_plano": ("msisdn",), "invoice_explanation": ("msisdn",), "consultar_vas": ("msisdn",), "consultar_historico_vas": ("msisdn",), diff --git a/data/agent_framework.db b/data/agent_framework.db new file mode 100644 index 0000000..0d835a6 Binary files /dev/null and b/data/agent_framework.db differ diff --git a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..857919f Binary files /dev/null and b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..80a0749 Binary files /dev/null and b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..a058388 Binary files /dev/null and b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..7c35b8e Binary files /dev/null and b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..75d18ef Binary files /dev/null and b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..2046841 Binary files /dev/null and b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..d11c3b1 Binary files /dev/null and b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..b0f5f4c Binary files /dev/null and b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..9b41048 Binary files /dev/null and b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..0240ce5 Binary files /dev/null and b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..e08253f Binary files /dev/null and b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..0fc052a Binary files /dev/null and b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/test_mcp_contestation_subject_guard.py b/tests/migration/test_mcp_contestation_subject_guard.py new file mode 100644 index 0000000..0cf4fc4 --- /dev/null +++ b/tests/migration/test_mcp_contestation_subject_guard.py @@ -0,0 +1,72 @@ +from contas_mcp.servers.contas_mcp_server.main import ( + _preflight_subject, + _recover_explicit_contestation_subject, +) + + +def _args(subject="TIM Fashion Mensal"): + return { + "msisdn": "11999999999", + "subject": subject, + "motivo": "não contratei TIM CTRL Redes Sociais 8.0", + "valor": 71.99, + "billing_analysis": { + "currentInvoice": [ + { + "type": "plano", + "desc": "Plano", + "items": [ + {"type": "plano", "desc": "TIM Black A 8.0", "value": "104.99", "contestable": False}, + {"type": "plano", "desc": "TIM CTRL Redes Sociais 8.0", "value": "71.99", "contestable": False}, + ], + }, + { + "type": "servicos_contratados_de_parceiros", + "desc": "Serviços de valor adicionado", + "items": [ + {"type": "servicos_contratados_de_parceiros", "desc": "TIM Fashion Mensal", "value": "10.0", "contestable": True}, + ], + }, + ] + }, + "invoice_detail": { + "parsed_content": { + "1199999999": { + "Planos": { + "TIM CTRL Redes Sociais 8.0": {"valor_final": 71.99} + }, + "SVA Detalhe Total": [ + {"desc": "Tim Fashion", "value": 10, "classe": "avulso"} + ], + } + } + }, + } + + +def test_recovers_explicit_contestation_subject_from_original_text(): + item = _recover_explicit_contestation_subject(_args()) + assert item is not None + assert item["name"] == "TIM CTRL Redes Sociais 8.0" + assert str(item["value"]) == "71.99" + assert item["category"] == "plano" + + +def test_preflight_does_not_replace_explicit_plan_with_vas(): + args = _args() + result = _preflight_subject("contestar_cobranca", args) + assert result is not None + assert result["status"] == "OUT_OF_SCOPE" + assert result["subject"] == "TIM CTRL Redes Sociais 8.0" + assert result["resolved_value"] == "71.99" + assert result["metadata"]["subject_corrected_from"] == "TIM Fashion Mensal" + assert args["subject"] == "TIM CTRL Redes Sociais 8.0" + assert args["resolved_value"] == "71.99" + + +def test_correct_subject_is_preserved(): + args = _args(subject="TIM CTRL Redes Sociais 8.0") + result = _preflight_subject("contestar_cobranca", args) + assert result is not None + assert result["subject"] == "TIM CTRL Redes Sociais 8.0" + assert args.get("_subject_corrected_from") is None diff --git a/tests/migration/test_original_contestation_validation.py b/tests/migration/test_original_contestation_validation.py index 2bc422e..2260a65 100644 --- a/tests/migration/test_original_contestation_validation.py +++ b/tests/migration/test_original_contestation_validation.py @@ -446,3 +446,79 @@ def test_contestation_action_calculates_totals_only_for_items_actually_contested assert [x["itemName"] for x in result["not_contested_items"]] == ["Outro VAS"] assert result["contested_invoice_amount_open"] == "14.99" assert result["contested_invoice_amount"] == "14.99" + + +def test_validate_contestation_items_reconhece_vas_por_type_do_billing_analysis() -> None: + items = [ + { + "item_name": "TIM Fashion Mensal", + "claimed_amount": "10", + "validated_amount": "10", + } + ] + invoice = { + "currentInvoice": [ + { + "type": "servicos_contratados_de_parceiros", + "desc": "Serviços de valor adicionado", + "value": "76.98", + "items": [ + { + "type": "servicos_contratados_de_parceiros", + "desc": "TIM Fashion Mensal", + "value": "10.0", + "contestable": True, + } + ], + } + ] + } + + validated, validation_log, error = validate_contestation_items(items, invoice) + + assert error is None + assert validation_log[-1]["status"] == "aprovado" + assert validation_log[-1]["secao_vas"] is True + assert validation_log[-1]["item_fatura_resolvido"] == "TIM Fashion Mensal" + assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros" + assert validated[0]["validated_amount"] == "10" + + +def test_validate_contestation_items_prefere_evidencia_vas_quando_item_aparece_em_multiplas_visoes() -> None: + items = [ + { + "item_name": "TIM Fashion Mensal", + "claimed_amount": "10", + "validated_amount": "10", + } + ] + invoice = { + "invoiceVariation": [ + { + "desc": "TIM Fashion Mensal", + "value": "10.0", + } + ], + "currentInvoice": [ + { + "type": "servicos_contratados_de_parceiros", + "desc": "Serviços de valor adicionado", + "value": "76.98", + "items": [ + { + "type": "servicos_contratados_de_parceiros", + "desc": "TIM Fashion Mensal", + "value": "10.0", + "contestable": True, + } + ], + } + ], + } + + _, validation_log, error = validate_contestation_items(items, invoice) + + assert error is None + assert validation_log[-1]["status"] == "aprovado" + assert validation_log[-1]["secao_vas"] is True + assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros" diff --git a/tests/migration/test_vas_response_renderer.py b/tests/migration/test_vas_response_renderer.py new file mode 100644 index 0000000..a03ff62 --- /dev/null +++ b/tests/migration/test_vas_response_renderer.py @@ -0,0 +1,24 @@ +from app.presentation.tool_renderers import render_contas_vas + + +def test_vas_renderer_includes_available_values(): + result = { + "products": [ + {"name": "TIM Fashion Mensal", "can": {"cancel": True}, "details": {"valor": "10,00"}}, + {"name": "Aya Audiobooks Premium", "can": {"cancel": True}, "details": {"valor": "9,99"}}, + {"name": "Neymar Jr", "can": {"cancel": True}, "details": {"valor": "12,00"}}, + {"name": "Tamboro Mensal", "can": {"cancel": True}, "details": {"valor": "14,99"}}, + ] + } + text = render_contas_vas(tool_name="consultar_vas", result=result, state={}, agent_label="VasAgent") + assert "TIM Fashion Mensal (avulso): R$ 10,00" in text + assert "Aya Audiobooks Premium (avulso): R$ 9,99" in text + assert "Neymar Jr (avulso): R$ 12,00" in text + assert "Tamboro Mensal (avulso): R$ 14,99" in text + + +def test_vas_renderer_does_not_invent_missing_value(): + result = {"products": [{"name": "Sem Preço", "can": {"cancel": True}, "details": {}}]} + text = render_contas_vas(tool_name="consultar_vas", result=result, state={}, agent_label="VasAgent") + assert "Sem Preço (avulso)" in text + assert "R$" not in text