From 2c3aa58b59233edc3da39edf2cbee8611f5e8bcb Mon Sep 17 00:00:00 2001 From: "cristiano.hoshikawa" Date: Mon, 7 Sep 2026 20:16:57 -0300 Subject: [PATCH] bugfix reconciliation extractor --- .../__pycache__/__init__.cpython-313.pyc | Bin 412 -> 411 bytes .../__pycache__/extensions.cpython-313.pyc | Bin 2140 -> 2139 bytes .../__pycache__/idempotency.cpython-313.pyc | Bin 8913 -> 8912 bytes .../__pycache__/observer.cpython-313.pyc | Bin 17556 -> 17555 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 548 -> 547 bytes .../composite_publisher.cpython-313.pyc | Bin 2503 -> 2502 bytes .../__pycache__/event_builder.cpython-313.pyc | Bin 1261 -> 1260 bytes .../__pycache__/factory.cpython-313.pyc | Bin 3997 -> 3996 bytes .../__pycache__/publisher.cpython-313.pyc | Bin 1873 -> 1872 bytes .../tim_payload_mapper.cpython-313.pyc | Bin 9287 -> 9286 bytes .../__pycache__/tim_sequence.cpython-313.pyc | Bin 17562 -> 17561 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 515 -> 514 bytes .../__pycache__/kafka.cpython-313.pyc | Bin 1813 -> 1812 bytes .../__pycache__/langfuse.cpython-313.pyc | Bin 23173 -> 23172 bytes .../__pycache__/oci_streaming.cpython-313.pyc | Bin 1822 -> 1821 bytes .../__pycache__/pubsub.cpython-313.pyc | Bin 7441 -> 7440 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 404 -> 403 bytes .../usage_repository.cpython-313.pyc | Bin 15687 -> 15686 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 214 -> 213 bytes .../cache/__pycache__/cache.cpython-313.pyc | Bin 16524 -> 16523 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 217 -> 216 bytes .../__pycache__/adapters.cpython-313.pyc | Bin 4173 -> 4172 bytes .../channels/__pycache__/base.cpython-313.pyc | Bin 1797 -> 1796 bytes .../__pycache__/gateway.cpython-313.pyc | Bin 5772 -> 5771 bytes .../__pycache__/interruption.cpython-313.pyc | Bin 6457 -> 6456 bytes .../__pycache__/transcription.cpython-313.pyc | Bin 1430 -> 1429 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 924 -> 923 bytes .../checkpoint_repository.cpython-313.pyc | Bin 27164 -> 27163 bytes .../langgraph_saver.cpython-313.pyc | Bin 33266 -> 33265 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 215 -> 214 bytes .../agent_registry.cpython-313.pyc | Bin 6948 -> 6947 bytes .../__pycache__/settings.cpython-313.pyc | Bin 13790 -> 13789 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 307 -> 306 bytes .../mcp_gateway_client.cpython-313.pyc | Bin 3333 -> 3332 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1501 -> 1501 bytes .../__pycache__/base.cpython-313.pyc | Bin 1375 -> 1375 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 9160 -> 9160 bytes .../__pycache__/custom_rails.cpython-313.pyc | Bin 4404 -> 4404 bytes .../framework_llm_client.cpython-313.pyc | Bin 19673 -> 19673 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 5923 -> 5923 bytes .../output_supervisor.cpython-313.pyc | Bin 24306 -> 24306 bytes .../parallel_executor.cpython-313.pyc | Bin 21862 -> 21862 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 10889 -> 10889 bytes .../__pycache__/rail_action.cpython-313.pyc | Bin 654 -> 654 bytes .../__pycache__/rail_decision.cpython-313.pyc | Bin 1605 -> 1605 bytes .../__pycache__/rail_result.cpython-313.pyc | Bin 1004 -> 1004 bytes .../__pycache__/rails.cpython-313.pyc | Bin 43324 -> 43324 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 2994 -> 2994 bytes .../__pycache__/_compat.cpython-313.pyc | Bin 2445 -> 2445 bytes .../contestation_validation.cpython-313.pyc | Bin 2177 -> 2177 bytes .../__pycache__/contracts.cpython-313.pyc | Bin 6901 -> 6901 bytes .../__pycache__/input_size.cpython-313.pyc | Bin 3649 -> 3649 bytes .../__pycache__/llm_adapter.cpython-313.pyc | Bin 3799 -> 3799 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 7454 -> 7454 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 8894 -> 8894 bytes .../output_sanitization.cpython-313.pyc | Bin 13910 -> 13910 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 12661 -> 12661 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 473 -> 473 bytes .../__pycache__/_context.cpython-313.pyc | Bin 5871 -> 5871 bytes .../ausencia_oferta_proativa.cpython-313.pyc | Bin 12205 -> 12205 bytes .../__pycache__/coerencia.cpython-313.pyc | Bin 4157 -> 4157 bytes .../__pycache__/dlex_in.cpython-313.pyc | Bin 1304 -> 1304 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 1815 -> 1815 bytes .../dlex_out.cpython-313.pyc.128272546232176 | 0 .../__pycache__/fallback.cpython-313.pyc | Bin 17652 -> 17652 bytes .../__pycache__/fraseologia.cpython-313.pyc | Bin 7633 -> 7633 bytes .../__pycache__/out_of_scope.cpython-313.pyc | Bin 17493 -> 17493 bytes .../prompts/__pycache__/pinj.cpython-313.pyc | Bin 8424 -> 8424 bytes .../__pycache__/ragsec.cpython-313.pyc | Bin 1120 -> 1120 bytes .../__pycache__/revprec.cpython-313.pyc | Bin 3150 -> 3150 bytes .../prompts/__pycache__/tox.cpython-313.pyc | Bin 644 -> 644 bytes .../toxicidade_output.cpython-313.pyc | Bin 928 -> 928 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1663 -> 1663 bytes .../rails/__pycache__/alcada.cpython-313.pyc | Bin 4999 -> 4999 bytes .../rails/__pycache__/anatel.cpython-313.pyc | Bin 9148 -> 9148 bytes .../__pycache__/confirmation.cpython-313.pyc | Bin 11042 -> 11042 bytes .../rails/__pycache__/dlex_in.cpython-313.pyc | Bin 3279 -> 3279 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 3331 -> 3331 bytes .../rails/__pycache__/ragsec.cpython-313.pyc | Bin 5462 -> 5462 bytes .../rails/__pycache__/revprec.cpython-313.pyc | Bin 5470 -> 5470 bytes .../rails/__pycache__/tox.cpython-313.pyc | Bin 8293 -> 8293 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 533 -> 533 bytes .../rules/__pycache__/alcada.cpython-313.pyc | Bin 1881 -> 1881 bytes .../__pycache__/pinj_patterns.cpython-313.pyc | Bin 3730 -> 3730 bytes .../__pycache__/tox_blocklist.cpython-313.pyc | Bin 1506 -> 1506 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 409 -> 408 bytes .../__pycache__/mcp_mapper.cpython-313.pyc | Bin 4693 -> 4692 bytes .../__pycache__/models.cpython-313.pyc | Bin 3145 -> 3144 bytes .../__pycache__/resolver.cpython-313.pyc | Bin 4714 -> 4713 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 619 -> 618 bytes .../judges/__pycache__/judge.cpython-313.pyc | Bin 41126 -> 41125 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 226 -> 225 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 5405 -> 5404 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 234 -> 233 bytes .../prompts/__pycache__/aluc.cpython-313.pyc | Bin 6530 -> 6529 bytes .../prompts/__pycache__/csi.cpython-313.pyc | Bin 1333 -> 1332 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 8036 -> 8035 bytes .../prompts/__pycache__/rqlt.cpython-313.pyc | Bin 1166 -> 1165 bytes .../prompts/__pycache__/vctn.cpython-313.pyc | Bin 627 -> 626 bytes .../llm/__pycache__/__init__.cpython-313.pyc | Bin 477 -> 476 bytes .../llm/__pycache__/base.cpython-313.pyc | Bin 1624 -> 1623 bytes .../profile_resolver.cpython-313.pyc | Bin 9323 -> 9322 bytes .../llm/__pycache__/providers.cpython-313.pyc | Bin 46979 -> 46978 bytes .../structured_output.cpython-313.pyc | Bin 7460 -> 7459 bytes .../llm/__pycache__/types.cpython-313.pyc | Bin 1275 -> 1274 bytes .../mcp/__pycache__/__init__.cpython-313.pyc | Bin 474 -> 473 bytes .../mcp/__pycache__/client.cpython-313.pyc | Bin 18427 -> 18426 bytes .../mcp/__pycache__/models.cpython-313.pyc | Bin 2137 -> 2136 bytes .../mcp/__pycache__/registry.cpython-313.pyc | Bin 4741 -> 4740 bytes .../__pycache__/tool_policy.cpython-313.pyc | Bin 5686 -> 5685 bytes .../__pycache__/tool_router.cpython-313.pyc | Bin 14190 -> 14189 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1136 -> 1135 bytes .../long_term_extractor.cpython-313.pyc | Bin 2514 -> 2513 bytes .../long_term_memory.cpython-313.pyc | Bin 6840 -> 6839 bytes .../long_term_models.cpython-313.pyc | Bin 1749 -> 1748 bytes .../long_term_store.cpython-313.pyc | Bin 29187 -> 29186 bytes .../message_history.cpython-313.pyc | Bin 8360 -> 8359 bytes .../summary_memory.cpython-313.pyc | Bin 12511 -> 12510 bytes .../__pycache__/summary_store.cpython-313.pyc | Bin 9521 -> 9520 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 215 -> 214 bytes .../__pycache__/identity.cpython-313.pyc | Bin 3466 -> 3465 bytes .../__pycache__/session.cpython-313.pyc | Bin 2709 -> 2708 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1395 -> 1394 bytes .../__pycache__/code_mapper.cpython-313.pyc | Bin 21448 -> 21447 bytes .../__pycache__/context.cpython-313.pyc | Bin 7387 -> 7386 bytes .../__pycache__/event_bus.cpython-313.pyc | Bin 3633 -> 3632 bytes .../guardrail_events.cpython-313.pyc | Bin 2051 -> 2050 bytes .../__pycache__/ic_events.cpython-313.pyc | Bin 626 -> 625 bytes .../__pycache__/judge_events.cpython-313.pyc | Bin 1411 -> 1410 bytes .../langgraph_telemetry.cpython-313.pyc | Bin 4964 -> 4963 bytes .../__pycache__/llm_advisors.cpython-313.pyc | Bin 2938 -> 2937 bytes .../__pycache__/noc_contract.cpython-313.pyc | Bin 5075 -> 5074 bytes .../__pycache__/noc_otel.cpython-313.pyc | Bin 6322 -> 6321 bytes .../__pycache__/observer.cpython-313.pyc | Bin 5105 -> 5104 bytes .../__pycache__/otel.cpython-313.pyc | Bin 3412 -> 3411 bytes .../streaming_events.cpython-313.pyc | Bin 1835 -> 1834 bytes .../__pycache__/telemetry.cpython-313.pyc | Bin 52428 -> 52427 bytes .../__pycache__/token_cost.cpython-313.pyc | Bin 8691 -> 8690 bytes .../workflow_events.cpython-313.pyc | Bin 3330 -> 3329 bytes .../oci/__pycache__/__init__.cpython-313.pyc | Bin 212 -> 211 bytes .../oci/__pycache__/auth.cpython-313.pyc | Bin 3244 -> 3243 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 220 -> 219 bytes .../__pycache__/oracle_store.cpython-313.pyc | Bin 45932 -> 45931 bytes .../__pycache__/sqlite_store.cpython-313.pyc | Bin 15019 -> 15018 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 455 -> 454 bytes .../__pycache__/renderers.cpython-313.pyc | Bin 4318 -> 4317 bytes .../rag/__pycache__/__init__.cpython-313.pyc | Bin 710 -> 709 bytes .../embedding_provider.cpython-313.pyc | Bin 6517 -> 6516 bytes .../__pycache__/graph_store.cpython-313.pyc | Bin 5528 -> 5527 bytes .../rag/__pycache__/ingest.cpython-313.pyc | Bin 12581 -> 12580 bytes .../__pycache__/kbdb_service.cpython-313.pyc | Bin 10012 -> 10011 bytes .../__pycache__/rag_service.cpython-313.pyc | Bin 10898 -> 10897 bytes .../__pycache__/vector_store.cpython-313.pyc | Bin 18656 -> 18655 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 221 -> 220 bytes .../session_repository.cpython-313.pyc | Bin 8590 -> 8589 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 426 -> 425 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 2297 -> 2296 bytes .../__pycache__/continuity.cpython-313.pyc | Bin 15934 -> 15933 bytes .../enterprise_router.cpython-313.pyc | Bin 61972 -> 62132 bytes .../__pycache__/models.cpython-313.pyc | Bin 2447 -> 2446 bytes .../routing/enterprise_router.py | 40 +- .../__pycache__/__init__.cpython-313.pyc | Bin 348 -> 348 bytes .../__pycache__/agent_runtime.cpython-313.pyc | Bin 200597 -> 210431 bytes .../transaction_parameters.cpython-313.pyc | Bin 14476 -> 32568 bytes .../agent_framework/runtime/agent_runtime.py | 247 ++++++++++-- .../runtime/transaction_parameters.py | 335 +++++++++++++++- .../sse/__pycache__/__init__.cpython-313.pyc | Bin 212 -> 211 bytes .../sse/__pycache__/events.cpython-313.pyc | Bin 9588 -> 9587 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 219 -> 218 bytes .../__pycache__/supervisor.cpython-313.pyc | Bin 4944 -> 4943 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 879 -> 878 bytes .../__pycache__/graph.cpython-313.pyc | Bin 1416 -> 1415 bytes .../input_contract.cpython-313.pyc | Bin 8791 -> 8790 bytes .../__pycache__/models.cpython-313.pyc | Bin 7670 -> 7669 bytes .../__pycache__/registry.cpython-313.pyc | Bin 2362 -> 2361 bytes .../__pycache__/repository.cpython-313.pyc | Bin 2715 -> 2714 bytes .../__pycache__/runtime.cpython-313.pyc | Bin 36153 -> 36152 bytes .../__pycache__/tool_executor.cpython-313.pyc | Bin 1967 -> 1966 bytes .../conftest.cpython-313-pytest-9.0.2.pyc | Bin 791 -> 806 bytes ...on_parameters.cpython-313-pytest-9.0.2.pyc | Bin 5451 -> 43623 bytes ...saction_shift.cpython-313-pytest-9.0.2.pyc | Bin 25806 -> 25812 bytes ...ral_candidate.cpython-313-pytest-9.0.2.pyc | Bin 0 -> 17575 bytes ...t_and_structural_candidate.cpython-313.pyc | Bin 0 -> 9600 bytes ..._descriptions.cpython-313-pytest-9.0.2.pyc | Bin 10435 -> 10430 bytes ...lm_precedence.cpython-313-pytest-9.0.2.pyc | Bin 47193 -> 57846 bytes ...ession_matrix.cpython-313-pytest-9.0.2.pyc | Bin 52049 -> 52053 bytes ..._interruption.cpython-313-pytest-9.0.2.pyc | Bin 28283 -> 28289 bytes ...nal_tool_flow.cpython-313-pytest-9.0.2.pyc | Bin 127006 -> 133120 bytes ...ntextual_reentry_transaction_parameters.py | 369 +++++++++++++++++- tests/test_reconciliation_state_rules_8_16.py | 191 +++++++++ ...it_abandonment_and_structural_candidate.py | 293 ++++++++++++++ ...st_transaction_parameter_llm_precedence.py | 126 ++++++ tests/test_transactional_tool_flow.py | 156 +++++++- 193 files changed, 1689 insertions(+), 68 deletions(-) create mode 100644 libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc.128272546232176 create mode 100644 tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc create mode 100644 tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313.pyc create mode 100644 tests/test_reconciliation_state_rules_8_16.py create mode 100644 tests/test_transaction_explicit_abandonment_and_structural_candidate.py diff --git a/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc index dd2ef206b629071b9100effdd8bc62016e5b34f0..c8644504dadbf4361aa73d3aad724fdde447bda5 100644 GIT binary patch delta 35 pcmbQkJe!&OGcPX}0}!-Z&!5Qso;y!JH?KrLMc+5rb21y_GytsP3LXFe delta 36 qcmbQuJcpV4GcPX}0}zCroi>sCJx{)VZeEFgvc6++Y4K!M#%Tb-5eq2* diff --git a/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc index 3c4a8cde693c53b8af55ef135d40457278ed706e..0b3673ef0959f13c6f96f42df240e6de9cff65be 100644 GIT binary patch delta 37 rcmca3a9e=;GcPX}0}!-Z&)>+c!pvQupPN^rpQ7&@?77*RS&-H#>`!)pPN^rpQ7&@?72CXxkMfS)tCzZ delta 38 scmccMdeN2pGcPX}0}zCrowkwNjhUxNKR2&LKUv?gxU_h440DM*0PB?u3IG5A diff --git a/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc index 16c60ca26a2f2f4ab837a772c4cd4037d70c96e1..9f2cc95ada629fb719a3a4df8933b18bad6c5e43 100644 GIT binary patch delta 39 tcmbQz$vC-_k^3_*FBbz4v|G>L$eqr{ou{9hSE8Sy?;Gs7xsFZD4glQ{3l#tW delta 40 ucmbQ-$vCBxk^3_*FBbz4gq@wXkvpA@Ctp7|uS7pt-?6x~cyleAm>mG~Jq#cK diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc index f90a6c2e8bd31fe9971df572aefa712d317b8715..5d2df1d91522c863aa7bb6856a5c1d1d62d62992 100644 GIT binary patch delta 36 qcmZ3&vY3VYGcPX}0}!-Z&)>+M#mHTupPN^rpQ7&@>^ZrSaWMd|?FwE1 delta 37 rcmZ3?vV?{EGcPX}0}zCtowkuXi;<^NKR2&LKUv?gxU_h31LI-<#6$~f diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc index d80e9263b47b00c062f15230a548265a771cf263..d0bf3578b90877a06740f796af072f9e8ac41454 100644 GIT binary patch delta 37 rcmX>ud`y`8GcPX}0}!-Z&)>+soQbmd|a6OGcPX}0}zCtowkvCITKHder{fgezLw}acS}9y-b|!0OSA*;s5{u diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc index f26065c252ec97901cd89f82dc680a9da0d927c6..86e20b7e129bb941ec30dc1f5ef20c7ff83af479 100644 GIT binary patch delta 37 rcmaFM`G%AGGcPX}0}!-Z&)>-Xf{D9UKR2&LKSkd+*mLuLrgw}0-5(5T delta 38 scmaFE`IeLWGcPX}0}zCtowkwt1rtx5er{fgezLw}acS}9e@yQf0rR2_bpQYW diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc index f2c6660db0fad5cdccf5d1521d56934d5c90137f..6c1576622286d7c40c2edb1cb959df8e2f0c417f 100644 GIT binary patch delta 37 rcmbO$KS!SXGcPX}0}!-Z&)>-H!o*#!pPN^rpQ7&@?72Cb$%z*LygLd9 delta 38 scmbOuKUbdnGcPX}0}zCtowkwNg^8y^KR2&LKUv?gxU_h46q6G#0MVce6951J diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc index 08395717ab6ea5adfcf6233fbf40c1ce3fd15785..4a48fe975da9e23ea9d635f12509237b9e0ee701 100644 GIT binary patch delta 37 rcmcb}cY%-lGcPX}0}!-Z&)>*>h>5#WKR2&LKSkd+*mLturV17S(h3WB delta 38 scmcb>cae|#GcPX}0}zCtowkwt5ED<8er{fgezLw}acS}98%z}}0O&Ogga7~l diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc index d60075e3db794c79e28f718679d05e224e663d05..e54ad9bc4183565dab6a48e1a3b05cb707c17462 100644 GIT binary patch delta 37 rcmX@^am<7JGcPX}0}!-Z&)>*h#>m~IpPN^rpQ7&@?76v@ahDPR*L$j!*YU8A3ySE8Sy?;Gs7S%Rg>0RY!H3e5lj delta 40 ucmbQ)$vCT%k^3_*FBbz4grA+Zk(-f)r&d2VuS7pt-?6x~c(XW5lLG+lFbmlL diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc index b3d431558f0b01a0ba9d7b3af436386694e5a52d..db8287d1aa0c9816e2bcfc6424fe5060f3ae3b0f 100644 GIT binary patch delta 36 qcmZo>X=362%*)Hg00iyU^EYzaGjccS=jN5@r|A0zdrl5xTnhlC014Uv delta 37 rcmZo-X=dU6%*)Hg00iM@r)}i6XXI(t&&?~*Pu6!VE-juM%D5H)xAY3; diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc index b84fe312abf9458fe51dac50a890335db40ba060..3c07bb4db4a6e5b1c847f8015442e58dc33bbedb 100644 GIT binary patch delta 37 rcmbQrH-(S;GcPX}0}!-Z&)>-Hz{FjzpPN^rpQ7&@?72CdX&(y!wrC1S delta 38 scmbQjHL$UTXfyGcJcuS7pZ-#6HE^GfDBVF2s743Pi; delta 40 ucmZqK%GkP+sk%_xqKR2&LKSkd+*mLsJ0N9BO=l}o! diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc index b3e70975cb6f15c1b0d7dfee8aa95907a776470e..60262b1d655d60d942b94614cdaf021b62fd78df 100644 GIT binary patch delta 37 rcmbPeHNlGeGcPX}0}!-Z&)>-Xj)S{FKR2&LKSkd+*mE;0CyxjK%r6RH delta 38 scmbPWHPMRuGcPX}0}zCtowkwt9S2XNer{fgezLw}acS{p7ET@!0OD&4YybcN diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc index 268522ef4ee2659cb545674ecaf7634a974915dc..437e994fcd3d1672f4de952efe3355eaf8405a4c 100644 GIT binary patch delta 34 ocmbQjJeisMGcPX}0}!-Z&!5Qsn7d3rH?KrLMc+5rbKsCF;BUEZeEFgvc6++Y4OCLvjDa43<>}M diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc index 8fe711565190e828d5e6ae19e9d406ffaf0e5842..c15ebda3c71987b8a259b6217f3debfa4b45d85d 100644 GIT binary patch delta 37 rcmX?Jb*zf}GcPX}0}!-Z&)>+M$Ie}+pPN^rpQ7&@?76v>{g5dD=ClkR delta 38 scmX?Bb-arEGcPX}0}zCrowkuXkDaGpKR2&LKUv?gxU_h43;Q8c0R2D>DF6Tf diff --git a/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc index a0808ef17b677bca4bea04f460f20f9e94d9e58c..7fd87c5b7b16a1325c264ae443c5c2a754bbbe43 100644 GIT binary patch delta 34 ocmcb{c$Ja+GcPX}0}!-Z&!5O`%w3|Nn^&TrqVF5*Ink>J0H)ar4*&oF delta 35 pcmcc0c#Vr%n5R@fH?KrLS>Lg^w0NRt4*h0H@3f8vpr%n5RNNH?KrLS>Lg^w0NRtKLEP33nl;n diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc index e099198789afb696318821a538114d3e68f42069..b2e0598bff2dab1f61a6912cee40c1dbec88e279 100644 GIT binary patch delta 37 rcmX@Ba7KaqGcPX}0}!-Z&)>*>mWjJuKR2&LKSkd+*mLs}rc7P{)}9NZ delta 38 scmX@3a8`l)GcPX}0}zCrowkwtEE7+Ker{fgezLw}acS}9$4r^L0PNKatpET3 diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc index 2dfbd907a2024fca5833da04965fda317c0cd6fc..df2a44d7584ac23d0f8969c08600f27df55edebb 100644 GIT binary patch delta 37 rcmZqWYvJSm%*)Hg00iyU^EYx^F>x2`=jN5@r|A0zdu|S7+QI?=u!;%> delta 38 scmZqSYvtqq%*)Hg00d!Yr)}i6V&W;$&&?~*Pu6!VE-l_1z_f)00L69-5dZ)H diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc index 9b06a47fad9d7b8b4a97f77f1614b38d1781a9c6..d1b67b04f4f4023ebd50721c5380eb2edfa4df80 100644 GIT binary patch delta 37 rcmeCt?bhY~%*)Hg00iyU^EYzOXXh@{&&?~*Pto@c_T0RU{Td$t#P$nS delta 38 scmeCy?a}4_%*)Hg00d!Yr)}h(&(2e>pPN^rpRDg#Tw1(&EBiG*0NR@jU;qFB diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc index f51d600e8163d915c377dcad1592b25e077ba070..14f5aa7b6b4479689715dba09a8eefc999f60ca6 100644 GIT binary patch delta 37 rcmdmKw8Mz|GcPX}0}!-Z&)>*BjfuNjKR2&LKSkd+*mLt*rXDc>(TEFR delta 38 scmdmCw9|+c%gkM)pPN^rpQ7&@?77*6Ifw}Wvor~> delta 38 scmbQrJ&l|DGcPX}0}zCrowku%mzk$lKR2&LKUv?gxU_h)Gjk9V0LVECx&QzG diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc index 38d35de01c0fdff8922033b2e14c6d6feaaf056d..c2fd46843afb6af8005e8c566c4c499b7f41917f 100644 GIT binary patch delta 36 qcmbQkKAWBUGcPX}0}!-Z&)>*>iHW;PKR2&LKSkd+*mLqrrp*AiQVTc$ delta 37 rcmbQuK8KzAGcPX}0}zCrowkwt5))6ger{fgezLw}acS}77fhQ0%km6E diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc index 7466402be22cc5fb5eac1e0b03a90a785d341a42..9e5786b2d556f4bb2ea8e7396e2338a9ec85e5b1 100644 GIT binary patch delta 39 tcmbPpg>m*3M()qNyj%=G&~80{Beya$ce{RWUWtB+zHhMSW-I1LX#nxS3_1V+ delta 40 ucmbPzg>lXmM()qNyj%=G5O#LjMs8(ho(}!oyb}FneaGU`;?0)KkJ11LbPYuS diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc index 2c808c8d433c84d649e120f6f213034fe636363b..33c0b82e72114c829bb766b82c3faae21936474a 100644 GIT binary patch delta 39 tcmey=%=EFDiTg7zFBbz4v|G>L$i0n;yHP(kuS7pZ-#6HE^I0aZ0H-Pn6951J delta 35 pcmcb{c%6~^GcPX}0}zCroi>r%n5RrXH?KrLS>Lg^w0NRtF95m=3myOf diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc index ac1c17ec433b6347038f316db15a65e2a33fbcf3..0b1127f3e01bf119a14037a0db19f7e254c9d83f 100644 GIT binary patch delta 37 rcmZ2tw%CmOGcPX}0}!-Z&)>-H$jn`>pPN^rpQ7&@?72CD`HVOK#j6U} delta 38 scmZ2%w#1D4GcPX}0}zCrowkwNk(sAPKR2&LKUv?gxU_h4IP)2C0NXVS;s5{u diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 1678ae97a96e7b027ba090b5b9d7dbad601a87b2..d4da59b2a6c77de3c3789932b7d6140b4df0284d 100644 GIT binary patch delta 37 rcmcbYeK(u?GcPX}0}!-Z&)>)$rNv#UpPN^rpQ7&@?76v6E0-Gp=)nub delta 38 scmcbceJ`8)GcPX}0}zCrowkuXN{gpVKR2&LKUv?gxU_h4fmSXz0RLkQ&j0`b diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc index 7dcc5682ec8eb6609ca9e90b670352caee6688ee..ef984047a8eb0f3a2032c0f626a76c96b392270f 100644 GIT binary patch delta 34 ocmdnYw26uPGcPX}0}ymr&!5QM#9gkRn^&TrqVF5*IdSGJ0Hf{-djJ3c delta 35 pcmdnQw3&(fGcPX}0}zCtoi>rXiKjw8H?KrLS>Lg^w0Po-Spc@%3x)sy diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc index a661b749a58696e7cefeec919beb367dfeaa480c..a12b9560a59d4c600ea4495194dee479f61aa362 100644 GIT binary patch delta 37 rcmZpbYLVjp%*)Hg00bS@^EYyzVB~Jp&&?~*Pto@c_S}4*QH>h_z+wu( delta 38 scmZpXYL(*t%*)Hg00iM@r)}gu!N}93pPN^rpRDg#Tw1*O9-|sJ0M-Hv%K!iX diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index bc07267eb827d1aee88907ef46a6e8f889f788d2..4c92ecd39ca143dcd0cd5fee50ca166160da247d 100644 GIT binary patch delta 20 acmcc1eV3d2GcPX}0}ymr&)>*>i4_1p^#$kv delta 20 acmcc1eV3d2GcPX}0}w>I%-G0%i4_1ozXiJh diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc index 50fd70a42aaadee7f47df7e732c6dbe7828c045f..613c2b32ab06baba643f4839375f526aeb57a87f 100644 GIT binary patch delta 20 acmcc5b)Sp-GcPX}0}ymr&)>)$!U_OCAq8vz delta 20 acmcc5b)Sp-GcPX}0}w>I%-F~s!U_OA>jggm diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc index 868b18d1c68ef28ae7b7788843ff58f67e7b3512..0d971ac27bb5aaab3ee37cff23be1c6b33568392 100644 GIT binary patch delta 20 acmX@%e!`vmGcPX}0}ymr&)>+sR~Z0BI|e@h delta 20 acmX@%e!`vmGcPX}0}w>I%-G1iR~Z0A1qKoT diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc index 5297e5ad4953625c7f5d11fa71b9483e3c353f83..c7409ffbe1a49d2744fe84a57fa5dc698788ec60 100644 GIT binary patch delta 20 acmdm@v_*;gGcPX}0}ymr&)>+cDF^^N)deR2 delta 20 acmdm@v_*;gGcPX}0}w>I%-G1SDF^^Mp9J~< diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index 6a077d2e3b0467ea95eedd50ac46848b3dc5d928..e794c9bad9b751e89af97a01ea8696697c319abf 100644 GIT binary patch delta 22 ccmcaPlkw(EM()qNyj%=G&|y7)BlkHU09X+RK>z>% delta 22 ccmcaPlkw(EM()qNyj%=G5a}{wBlkHU09M-v6aWAK diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 70803d59f104dd23c8029ee18eda570ab42132de..551e4fdae582748bf8cac4bbe52a2a53068dd109 100644 GIT binary patch delta 20 acmZ3iw^)z+GcPX}0}ymr&)>)`Ar1gK;{_4` delta 20 acmZ3iw^)z+GcPX}0}w>I%-F~+Ar1gJtpw!& diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc index c62f7b427e4f20701c0f23fb7bf4fe6c18a25b1c..8c8dbfde303015698221600d56591aa914584f1b 100644 GIT binary patch delta 22 ccmeygm+{kHM()qNyj%=G&|y7)Blq(-0AbPx<^TWy delta 22 ccmeygm+{kHM()qNyj%=G5a}{wBlq(-0AQR4xc~qF diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index dd9c3d8e84ab119f8abf8d43b3830397bd3431fa..bf2bc30a3c2768c80518bd266b801b2683836830 100644 GIT binary patch delta 22 ccmaF1it*VhM()qNyj%=G&|y7)BX@Kt09)V(6951J delta 22 ccmaF1it*VhM()qNyj%=G5a}{wBX@Kt09vXB<^TWy diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc index 49c70be651ff6bd9e8d04c9676ae6ef38066c70e..1a2aa4dfe7b167bff3be54cf8aee75325ad3c343 100644 GIT binary patch delta 20 acmeAS?F{Aq%*)Hg00bS@^EYzWX#oH|Vg;oD delta 20 acmeAS?F{Aq%*)Hg00faPGd6P9X#oH{ECqM~ diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc index 93052a6e8103fc2547c304dcdfaf3d2198f8ddd3..6408ba934a053d88ab393ab2ea20b9d1c4843458 100644 GIT binary patch delta 20 acmeBU?PKNs%*)Hg00bS@^EYxgGXVfH!UXI9 delta 20 acmeBU?PKNs%*)Hg00faPGd6NJGXVfGj0C>` diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index 863f5c04ab1833ed542542988e3b3ab11ece477a..5e4f560096a7502d13bb47e3fae427cbc57777f9 100644 GIT binary patch delta 20 acmX@gbCiesGcPX}0}ymr&)>*x#RdR7S_LBj delta 20 acmX@gbCiesGcPX}0}w>I%-G0n#RdR6Bn0*V diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc index 67dcd0c3d0071a67e1fdedad6ccec30942b24954..e4861d3fead9bae895cb805dec778fc44b77b5fc 100644 GIT binary patch delta 20 acmaFE{)V0VGcPX}0}ymr&)>-XkQo3$y9NsY delta 20 acmaFE{)V0VGcPX}0}w>I%-G2NkQo3#g$3RK diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc index 0dbd89ea93dcfd053c5674d3e89c87f550cb12b9..f91e7d199721d81d4528277ae03c04e97c21cb1d 100644 GIT binary patch delta 22 ccmdmUiD}OzChpI?yj%=G&|y7)Be&s709#`RI{*Lx delta 22 ccmdmUiD}OzChpI?yj%=G5a}{wBe&s709q{v4gdfE diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc index 715230e57f7d0b450a19bedc8256c74252cba0d3..47e364facaeda8efc259cd639c1fa1bd925f445f 100644 GIT binary patch delta 20 acmdlazDb<>GcPX}0}ymr&)>+soErc-^96+f delta 20 acmdlazDb<>GcPX}0}w>I%-G1ioErc+y#-hR diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 4d7ec5293811e4addbe3a4d1a072f1b14b42769c..cd05940548dfd3d5cb7cf2f816c278fe85135bcb 100644 GIT binary patch delta 20 acmeAb?iJ?#%*)Hg00bS@^EYxgaRLA|nFS00 delta 20 acmeAb?iJ?#%*)Hg00faPGd6NJaRLA{V+7v- diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index ac2b9d0892605011c7935ffe13ab9719ff671ada..01ee952ff99709c731e0a43c3fe80d2332b6c032 100644 GIT binary patch delta 20 acmZn^Y!u}F%*)Hg00bS@^EYyraR2}^a0KH3 delta 20 acmZn^Y!u}F%*)Hg00faPGd6OUaR2}@Is~== diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index c5bcaafc4319105428670a5510c3000483d114ec..1208cdc79710f2b82da4395089c6d52d23361c79 100644 GIT binary patch delta 20 acmexr`qh;CGcPX}0}ymr&)>-XN(ul;vIdy| delta 20 acmexr`qh;CGcPX}0}w>I%-G2NN(ul-dob5Mr+GcPX}0}ymr&)>*x#s>gB0tG(+ delta 20 acmX>ob5Mr+GcPX}0}w>I%-G0n#s>g9%moqv diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index df23f355650fb7952bf91ec532fba9bf41687cf6..a670b4acac0531badf6c5b9c3281d095a11102ea 100644 GIT binary patch delta 20 acmcaEdtH|MGcPX}0}ymr&)>*>h7SNimIea= delta 20 acmcaEdtH|MGcPX}0}w>I%-G0%h7SNhU)`EDHcT(FGv@ delta 20 acmbPdHP4FsGcPX}0}w>I%-F~+EDHcSn*{U# diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index 8b9f7599029680bc86942b628d4438211f0474dd..2687ec92da8002f8047ffbba491283a947cb77b0 100644 GIT binary patch delta 20 acmdnzy3dvSGcPX}0}ymr&)>+sSqT6`Vg?)l delta 20 acmdnzy3dvSGcPX}0}w>I%-G1iSqT6_ECufX diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc index fb48f8ca3aeb0f4fff944978aafa48b6fcc70cee..a5d3af3e62706ed754d84f1293cfb4fb2d59214f 100644 GIT binary patch delta 20 acmcbXb1jGaGcPX}0}ymr&)>-HZ3X~KvIa%~ delta 20 acmcbXb1jGaGcPX}0}w>I%-G27Z3X~Jd+MWe5OI9R`X3 delta 20 acmeyG^fih5GcPX}0}w>I%-G1CWe5OG=LTH> diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index a1c0ae305a27df0d2bc176fe80f54529231552cb..6068ff8e6fb100eeb2c80bd021c59e4bf90db0d0 100644 GIT binary patch delta 20 acmcb~e3O~`GcPX}0}ymr&)>*>ju8MpF$Kr~ delta 20 acmcb~e3O~`GcPX}0}w>I%-G0%ju8Mn`vsc- diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index 85e68340b1d72c1cd90dea97e82cc26e173bac58..7f7d1e4ae1e447e8be8ff9619c440c5fe5795856 100644 GIT binary patch delta 20 acmaE_`(Bs(GcPX}0}ymr&)>-XL<|5&o(6LO delta 20 acmaE_`(Bs(GcPX}0}w>I%-G2NL<|5%Xa+_A diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index 014d27e96a36f95ec1c0fd0c4e9266dd33eab2a8..a301fe15cbc80535aa77ba612b09655d7d91b03a 100644 GIT binary patch delta 20 acmZ1*zc!xxGcPX}0}ymr&)>+sNFM-1Zw4v= delta 20 acmZ1*zc!xxGcPX}0}w>I%-G1iNFM-0IR*Uy diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index 1adbe28eb841de9616eebac4d71ef9f39cefebd4..5c64d5e55caa21eab3e007bc5b2d2d6eb76669f0 100644 GIT binary patch delta 20 acmdn1uvdZmGcPX}0}ymr&)>*xBme+B^aVWt delta 20 acmdn1uvdZmGcPX}0}w>I%-G0nBme+Az6B5f diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc index 4c25391337980b36a9fe703021c28963b20fc7d5..d64f6e6239a513be5945df5624434a5391afa37a 100644 GIT binary patch delta 20 acmbQiHG_-$GcPX}0}ymr&)>++%L)KA2?Un_ delta 20 acmbQiHG_-$GcPX}0}w>I%-G1y%L)K8(*$Y& diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index 4c834efd5965242d74ec0a4ffec468de942f8bec..8113eaf6e4f9d3ae1359b15aa4b45bcbb8fac9c1 100644 GIT binary patch delta 20 acmbQvH=U3BGcPX}0}ymr&)>++!wvv6a|E6M delta 20 acmbQvH=U3BGcPX}0}w>I%-G1y!wvv5Jp^$8 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc.128272546232176 b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc.128272546232176 new file mode 100644 index 00000000..e69de29b diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index ae92b20e17b3fe65ed64b052b5263daebf64da16..8dcd58de12c157bd78a27a1d566126472f8370d3 100644 GIT binary patch delta 22 ccmey;$@ry{k^3_*FBbz4bXd>d$o*>Ocnq{?FKvm delta 20 acmca;ebJixGcPX}0}w>I%-G0%Ocnq`w+0UY diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc index cdb2534faeb3b0ccf92dc5089f9a827a21f2427f..eba298e3684db2e162a0d907e860c85c2926bf1e 100644 GIT binary patch delta 22 ccmccG!FaWUk^3_*FBbz4bXd>d$nE6<08_>WivR!s delta 22 ccmccG!FaWUk^3_*FBbz4M7qq_$nE6<08)?!UH||9 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index d785a3a97bbd4faf9de0b58fee6ae3858a830843..4faf4396915df03c37b989d2cd21f618e04552a1 100644 GIT binary patch delta 20 acmaFi_`;F*>R{;P?UI%-G0%R{;P>Dh6W! diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc index c586b640d91df9e5c49d5e988e69e9320d33275f..9de8beb68fa2b0fce74cfc419e5cba429683cb9e 100644 GIT binary patch delta 20 acmaFB@qmN-GcPX}0}ymr&)>)$$^rmC00n9Q delta 20 acmaFB@qmN-GcPX}0}w>I%-F~s$^rmA$^|_D diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc index 6b2041d107c31b9079ddeab16eabbac66e8c7731..e6ed86321cf1f9249e76cbdb2bd30e6ab5f14d8b 100644 GIT binary patch delta 20 acmX>naZZB!GcPX}0}ymr&)>-H%mV;Ecm-Sl delta 20 acmX>naZZB!GcPX}0}w>I%-G27%mV;DLIp1X diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc index 50fcb15315db1bede1c74cb5e84546b43cf02b93..002f8cc3fe346941655c9d357a99af69386e1be0 100644 GIT binary patch delta 20 acmZo+ZDHm9%*)Hg00bS@^EYx=G64WFG6c>5 delta 20 acmZo+ZDHm9%*)Hg00faPGd6NpG64WD`~;x@ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc index ec8f20bfffdc05d4eb959676e0e5b4481cf8ffad..58400365e1465e68a04f551a633807bb0c56b9d6 100644 GIT binary patch delta 20 acmZ3$zJQ(kGcPX}0}ymr&)>*Bl^Fmv+XW{8 delta 20 acmZ3$zJQ(kGcPX}0}w>I%-G01l^Fmur3Cr_ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc index 8f3fac0e59edec9b26db2429aa7ccabf2a329098..d36213833f1886657bd86d230e868ca6d330be79 100644 GIT binary patch delta 20 acmey*^Ph+NGcPX}0}ymr&)>*h!Uh0DY6Z~% delta 20 acmey*^Ph+NGcPX}0}w>I%-G0X!Uh0CGzFvp diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index e93d71eecabdb08a030bd5d6888654fc87e23c2c..ab7d4782c86891561a885ca8a3766647ad1ad6f7 100644 GIT binary patch delta 20 acmZoyZ&&C3%*)Hg00bS@^EYzW2m=5%g9R)A delta 20 acmZoyZ&&C3%*)Hg00faPGd6P92m=5$O$7e{ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc index 659cca02653235768827f185384e795c29ffaf11..83c2bda1028d432afeff085ff8472d043ff6e8ed 100644 GIT binary patch delta 20 acmdnvzQ>*WGcPX}0}ymr&)>+sQ5gV3TLv2d delta 20 acmdnvzQ>*WGcPX}0}w>I%-G1iQ5gV2B?ayP diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index 6c7e38415c743787c9984e35043d9e144c4d1715..cd0623856f903574e1c8aa3e3c85eb959ec8820d 100644 GIT binary patch delta 20 acmZ1!wkVAIGcPX}0}ymr&)>)`t_=V{Zv}Gz delta 20 acmZ1!wkVAIGcPX}0}w>I%-F~+t_=V`IR!=l diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index f157348833b606390bde56eab02e3b9cc89f11d1..a096035621ab81a69daba30d46728a2818ff1e19 100644 GIT binary patch delta 20 acmX>vd0vwHGcPX}0}ymr&)>*>ga-gW#0B91 delta 20 acmX>vd0vwHGcPX}0}w>I%-G0%ga-gVjs>&; diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index e437bea977eb6981c862977590bbd233fde71cd5..fff88a07faf257f2f6fa58b6a62d254526c52049 100644 GIT binary patch delta 20 acmZpcYL?>u%*)Hg00bS@^EYz;-~j+N2nBxt delta 20 acmZpcYL?>u%*)Hg00faPGd6Pn-~j+L(gjig diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index b62a84fc863e7fdf412599be1a47d8ef8bef78f3..ffbc949c3ffb0c97efb2b92cdefeb2abc0e4c592 100644 GIT binary patch delta 20 acmcbnbxn)=GcPX}0}ymr&)>-HEeZfaNd=(* delta 20 acmcbnbxn)=GcPX}0}w>I%-G27EeZfZ69set diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index aa2be86e684c2786e50c074b35df14c8e86ac486..3f75fb4dc1d0a611f95a57e4f395b3e36a52d5d0 100644 GIT binary patch delta 20 acmcbobx(`?GcPX}0}ymr&)>)$ED8Wai3Pa; delta 20 acmcbobx(`?GcPX}0}w>I%-F~sED8WZQw59w diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc index 15dcb19ffffced5f7b0297c4055f470672870e6a..69e442e873a3b354d7d564d8f86d5b9ddcfd6c6c 100644 GIT binary patch delta 20 acmaFr@YI3(GcPX}0}ymr&)>)$r2qg�CWb delta 20 acmaFr@YI3(GcPX}0}w>I%-F~sr2qg%js@5N diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 313b48ab619251efb0447e993f5cc7bfc3b3e974..b7192ce121d5f182d8a9651e619e1e51b30fc19a 100644 GIT binary patch delta 20 acmbQrGL?n++#RLE{wgi6w delta 20 acmbQrGL?nI%-G1y#RLE`fCN$i diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc index 86c6d7e1a5dfc97a944f3d63945d56ce79d1629c..c26edca129efca6c13bf31285cd184d893aa6f13 100644 GIT binary patch delta 20 acmcb~cax9%GcPX}0}ymr&)>-H#|{8K!v$mj delta 20 acmcb~cax9%GcPX}0}w>I%-G27#|{8JjRiLV diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc index 72d4bc1b534679d1811d220b0cdeab3dd64ef363..8bb4be0b0030032ffc39948af168eeaf04a043f7 100644 GIT binary patch delta 20 acmbOvJ4u%NGcPX}0}ymr&)>-1&IbTC&IL38 delta 20 acmbOvJ4u%NGcPX}0}w>I%-G1?&IbTBm<0y_ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc index 85ecb207a8e7a9a6ebfa42f633054a272febda51..5d61d1fb648da1481f875cf502390a64d5221b84 100644 GIT binary patch delta 20 acmaFF{fL|UGcPX}0}ymr&)>*>ofQB;y#@CG delta 20 acmaFF{fL|UGcPX}0}w>I%-G0%ofQB-hXu+2 diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index 262a3b6125381c8197999499b2c48d479c149bab..bb9ed415cdf234e0122c644248049069cb440165 100644 GIT binary patch delta 34 ocmbQqJcF70GcPX}0}ymr&!5Qsl)GF%H?KrLMc+5rbK;-50HkvZ5&!@I delta 35 pcmbQiJd>IGGcPX}0}zCtoi>sCDNludZeEFgvc6++Y4OD0a{;%J3>^Rf diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc index 78250ff95c18ca69c756d1188e092aae5568c5dd..081033bac2a7141677a3e5cb6a0e2de6a289ac66 100644 GIT binary patch delta 37 rcmcbraz%yvGcPX}0}ymr&)>-H#LQi(pPN^rpQ7&@?72CT`41le&g~05 delta 38 scmcbja#e-paYBOoGcPX}0}ymr&)>*h&dgn^pPN^rpQ7&@?76v*c{c|D%s>k) delta 38 scmX>haZ-Z&GcPX}0}zCtowkv?oSCOgKR2&LKUv?gxU_h4FY|5=0OAP@Hvj+t diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc index ddd7be65763bdbbca544d509002462bfdb1575c3..385d9b924fb80e6c08cb2650e57421f221d3ea58 100644 GIT binary patch delta 36 qcmaE*@=}HSGcPX}0}ymr&)>)$%*+sk&(MpKR2&LKSkd+*mLp;#>D{16bpC& delta 37 rcmaFG@|uPFGcPX}0}zCtowkvCBO_0ler{fgezLw}acS}7|-X;u# diff --git a/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc index 1954bc6f82a3f641ac81cdc798531bb2fc106771..68ef4a2cd4f68ab5df81d0b9cdc2ee6c8a847829 100644 GIT binary patch delta 39 tcmZ2>kZI{bChpI?yj%=G&|y7)Bey*ZcaeT>UWtB+zHhMS<}j9kIRNkd3-ka0 delta 40 ucmZ2_kZIXLChpI?yj%=G5Po*rMs9l+o?`vnyb}FneaGU`;?1Ef0doKbsto@C diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc index 4b91dab978230b8b6552541c02bb67d6a64defc9..521a732ca265c1e1388c60780d1a581c9374bae3 100644 GIT binary patch delta 34 ocmaFF_>htNGcPX}0}ymr&!5O`%-x`$n^&TrqVF5*Iniqd0IIqQKmY&$ delta 35 pcmaFJ_=u7FGcPX}0}zCtoi>r%n5R)cH?KrLS>Lg^w0NTD3;?~o3rhe1 diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc index 2e44150e4a94d120408898e883b8449bddec26a4..5072435897101c92acfaca6b7b74ac59885d72c5 100644 GIT binary patch delta 37 rcmbQMHAjp4GcPX}0}ymr&)>-Xote8yKR2&LKSkd+*mE-<3%?)$%g_pA delta 38 scmbQEHCK!KGcPX}0}zCtowkwtJ2OwSer{fgezLw}acS{pUKV~q0O9frZU6uP diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index 0ab8d48c5dd19b866fe2dfcca9e25608b58575df..eff1366dc350bfb1f249766c485fc859356d6892 100644 GIT binary patch delta 34 ocmaFG_>z(PGcPX}0}ymr&!5O`%-x}%n^&TrqVF5*IniqY0Ifm_U;qFB delta 35 pcmaFK_==JHGcPX}0}zCtoi>r%n5R=eH?KrLS>Lg^w0NTD0sz2W3u^!X diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc index 6c3e14d1ece3f4f805ee4a186657a8eee102bb7e..08fc36b6c425b886cf93b94bdbe020d59edafcb0 100644 GIT binary patch delta 37 rcmZoNZZzip%*)Hg00bS@^EYzGNpQF5=jN5@r|A0zdu}e3;AI2=zOf1> delta 38 scmZoPZZhWn%*)Hg00iM@r)}hpli+F9&&?~*Pu6!VE-l_%BEicD0MsoDF#rGn diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc index 63a7acfad6dcb40a64eb282ac638f43394e4362d..7e429d197c56f31eae1f934ea4e10d7b0a2b4423 100644 GIT binary patch delta 36 qcmdnWwS|lOGcPX}0}ymr&)>+c%EH~OpPN^rpQ7&@>^a$n diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index c3493b7de516228c63fa09d9fdca56c73fb52280..58913fa511fa7a1c5d57a5f249172636e093c34d 100644 GIT binary patch delta 37 rcmaE2_t=j6GcPX}0}ymr&)>-1&d1%MpPN^rpQ7&@?74X!-!TCI;o%Gl delta 38 scmaEC_r#9-GcPX}0}zCtowkv?osXweKR2&LKUv?gxU_ilT)txh0Qg!B6#xJL diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc index 032224bc1762a2d63934b67fa54170b9dee24128..612b94ccce0764fed05f9c5dced5fba51ba191d9 100644 GIT binary patch delta 37 rcmeC?BnGA%*)Hg00iM@r)}geVCHGn&&?~*Pu6!VE-l{N#>~zL0L!Zi=>Px# diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc index 6ba3e52648bd7d484881e5592ec463450fbe7ad3..5e92c07e067a75b411482961cd178ffea4aed35d 100644 GIT binary patch delta 36 qcmey&@`;7}GcPX}0}ymr&)>+M$jIHIpPN^rpQ7&@>^ZrD@izd)?F&x; delta 37 rcmeyw@|lJEGcPX}0}zCtowkuXk&&lWKR2&LKUv?gxU_h3Ipc2t+RhAG diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc index da7e2194d3e1cbc02ae6de36077e707945ae013f..acb7f1fc7182b50dcfeeacdcc2fec8c4b04d4149 100644 GIT binary patch delta 36 qcmcc1e21C)GcPX}0}ymr&)>+c$;e%#pPN^rpQ7&@>^a$iaV`M4p$b6& delta 37 rcmcb^e3zN~GcPX}0}zCrowku%laZ%bKR2&LKUv?gxU_h(J>y&e&GHLN diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc index 972b6626a15dc2c21f74e63ed132814672b234d3..524939efb438f240b49d0ba871008514222112f0 100644 GIT binary patch delta 37 rcmcb?bDf9#GcPX}0}ymr&)>*>hLJl@KR2&LKSkd+*mLt^#!?ml)2R!G delta 38 scmcc4bAyNbGcPX}0}zCrowkwt3?omzer{fgezLw}acS}9M~tN`0O_j?kpKVy diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc index c2b4ecf028e5d374b8270efdc71dc93a716eedac..6727084c88411b615b7d66657103fcf21628da96 100644 GIT binary patch delta 37 rcmaFu@ydhyGcPX}0}ymr&)>*h&CFe;pPN^rpQ7&@?74Xo^GyW+d$gRrCU96v*SE8Sy?;Gs7*@o4969Dk73+4a- delta 40 ucmZpA&(!>$iTg7zFBbz4gq@wXkz19Or$j$DuS7pt-?6x~c(XOD`z8PfKMe2y diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc index f55398b848cf05da32e1c18aa86ee428ead339ae..3f35e1fd066a314cb244a1afb807005c3fdef7b4 100644 GIT binary patch delta 37 rcmZ2twb+XLGcPX}0}ymr&)>+shnc%tKR2&LKSkd+*mLt`=0HgR(DVyk delta 38 scmZ2%wZw}1GcPX}0}zCrowkvC4>M1Ver{fgezLw}acS}9OU!|i0OsioX8-^I diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc index 05014a07f54ea9675ce69b8e48635523902b644e..2ab8fa3c455f3367619fc0bf8286acb0ce4d5072 100644 GIT binary patch delta 37 rcmey(`HPeLGcPX}0}ymr&)>*h!OWenpPN^rpQ7&@?76w0IfoGd)kq5% delta 38 scmeyx`J0pbGcPX}0}zCrowkv?f|;j4KR2&LKUv?gxU_h4A9D^P0P8snA^-pY diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index c872622ec26f1cc1747386c877fb130238d83e38..9d7e74541f64c0bddc9594edd12970973f382d39 100644 GIT binary patch delta 36 qcmcb`e3O~`GcPX}0}ymr&)>+c#mHTxpPN^rpQ7&@>^a$yaV7w`;0iea delta 37 rcmcb~e2ba;GcPX}0}zCrowku%i;<^TKR2&LKUv?gxU_h(1LI5p%`6K; diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc index d8ae516a55b17af386c3d8d5bf75f2dbbeb35fa7..f22ce715a4a3ede51dca25c74927bd75fe1c32b8 100644 GIT binary patch delta 39 tcmey}&-kmKk^3_*FBbz4bXd>d$i0x6yFfoTuS7pZ-#6HE^A2Wb7XSj043hu= delta 40 ucmey>&-lBak^3_*FBbz4gq@wXk$WLCPoaKpUWtCPzGHD|@#gK!&Mp8Mxec8F diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc index a64e27058d4aa21d0d6e7b193da7345ca3bf86f1..423945af892f1b69e4258eb6fdd567737aa18538 100644 GIT binary patch delta 37 rcmca9a6^FmGcPX}0}ymr&)>-H!NgslpPN^rpQ7&@?72Cf=@%;i%54i1 delta 38 scmca1a8rQ$GcPX}0}zCrowkwNgNdh5KR2&LKUv?gxU_h49MdmW0N?lw9RL6T diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc index 16e0b00f5c6e473ece8985ac4f72503b4633fe04..549de3a2ae837ca1ca183f3bf61d03bd6bde7c80 100644 GIT binary patch delta 37 rcmZowZBga^%*)Hg00bS@^EYyfFmV^@=jN5@r|A0zdv4ZdiV^?-vdRgX delta 38 scmZosZB^y|%*)Hg00d!Yr)}gGVd5#)&&?~*Pu6!VE-l`y#S|p~0LP^YqyPW_ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc index ca72d392d3580a0e9ab11b1ed83d63a50c05a53b..d82cf4d9765ece75948a61123f2921fff1ee8631 100644 GIT binary patch delta 37 rcmdm{vsH)tGcPX}0}ymr&)>*h#Kc{ypPN^rpQ7&@?76v536KR2&LKUv?gxU_h42h&<%0O4~CDF6Tf diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc index bc53c02fab16f5c20c1822cc380e537811be8282..0b2a4c5ebcad2d22ef2f2d8547c7e255f5d125a1 100644 GIT binary patch delta 37 rcmaEt_co9FGcPX}0}ymr&)>+spNqRxKR2&LKSkd+*mLtWt{FN2^%V@k delta 38 scmaEx_b!k7GcPX}0}zCrowkvCKNnA#er{fgezLw}acS}9t6Vd501O!o%K!iX diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc index fab016ee1dae31bc404450e4438727b722f89a8e..c3f6be3936d017c0ee04636ded5a36239d1e6d18 100644 GIT binary patch delta 36 qcmeys@t%YGGcPX}0}ymr&)>*Bmzld%KR2&LKSkd+*mLq0<~;z)9SeT| delta 37 rcmaFQ@qvT;GcPX}0}zCtowkvCE;CP=er{fgezLw}acS}7&CGiM-!BY` diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc index ab2e2fc2a13a9b825f39c0551f01397e83b25c8f..3e11289ba63b9b41a41dd3d2f2601518a9032d5d 100644 GIT binary patch delta 37 rcmca4d{LPDGcPX}0}ymr&)>+sm5sYWKR2&LKSkd+*mLt4Ha2De(tHaC delta 38 scmca8d`X!5GcPX}0}zCtowkvCD;rOver{fgezLw}acS}9(`;*x#l&5!pPN^rpQ7&@?72CRDO~~p$-WAV delta 38 scmdmPy2F(FGcPX}0}zCtowkwNiixLAKR2&LKUv?gxU_h408_dI0N-f~mjD0& diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc index 08efccd53fb960b829bbf917c1bf138a91a37892..32da699d734332464c2831a6ba7fb370a0bf6a04 100644 GIT binary patch delta 37 rcmcc0dxe+#GcPX}0}ymr&)>-1#mHT&pPN^rpQ7&@?74X%qX{bj&6o<# delta 38 scmcb@dzF{_GcPX}0}zCtowkv?i;<^JKR2&LKUv?gxU_il0!9;70ONcM*#H0l diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc index d2853bee60a7039b7a915b7a4a0edbefb241319c..58493a3ee181a8dcad498fa7681144a15c6782ec 100644 GIT binary patch delta 39 tcmZpE!r1hLk^3_*FBbz4bXd>d$o-g!yGB1ZuS7pZ-#6HE^DidOd;tCD49oxk delta 40 ucmZpA!r1(Tk^3_*FBbz4grA+Zk^3-XkCD4ZKR2&LKSkd+*mJWe(_}dS)?N!d delta 38 scmZ4PxWbY9GcPX}0}zCtowkwtA0tn#er{fgezLw}acS{p5vIv<0PMpIM*si- diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc index 71ed337859bdc169e1e74c34a0cc4ff8f7e41261..9da6a97c0e2213efce7fec69976552ee03a7e64c 100644 GIT binary patch delta 37 rcmcbgcrTIrGcPX}0}ymr&)>*>l$pC)KR2&LKSkd+*mLt8W@cRg?0pOQ delta 38 scmcbYct4T*GcPX}0}zCtowkwtC^Juuer{fgezLw}acS}9+sw?m00U)`%fwx!pPN^rpQ7&@?77*5>8}z1%kK*d delta 38 scmdnswb6_FGcPX}0}zCtowku%mWiiYKR2&LKUv?gxU_h)G1Ffq0O6Jl761SM diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc index 06466e2a88364d42b722f7ac173bbf73aa6baf77..00f52bbc46483a2549db4192e368f55693f39c44 100644 GIT binary patch delta 34 ocmcc4c#VZ0H;3+6aWAK delta 35 pcmcb{c%6~^GcPX}0}zCroi>r%n5RrXH?KrLS>Lg^w0NRtF95m=3myOf diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc index ffe490336559c264811a10b5f7ee644dbfb47ebc..9d334e0d45a533cab4fe51f461e9ba508080fbe4 100644 GIT binary patch delta 37 rcmeB@?v&>K%*)Hg00bS@^EYxIW#lf^&&?~*Pto@c_S}4jaUC}R!W#=h delta 38 scmeB_?vm#I%*)Hg00d!Yr)}gu%E(iupPN^rpRDg#Tw1*OHsd;O0N0-kOaK4? diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc index b1d210447a1ae412bd57e8b8f483bc0dcb0b2384..491605e0cc248640eacb208e001cd0e5a45e0208 100644 GIT binary patch delta 37 rcmbO#Iz^QGGcPX}0}ymr&)>++%*X delta 38 scmbOtI#rbWGcPX}0}zCrowkvinVF|lKR2&LKUv?gxU_h)By%-80LGpQo&W#< diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc index d0cd341a547b2b927be0bd0aa69f606f3336748a..9f0978d9923c145ef9d904576a85a67c76f06cd0 100644 GIT binary patch delta 37 rcmey&^@)r7GcPX}0}ymr&)>)$$--TupPN^rpQ7&@?76vsg_#im(3lEx delta 38 scmeyw^_h$NGcPX}0}zCtowkuXl7**MKR2&LKUv?gxU_h4J_|D=0Oo@WeE*>nvJ_!KR2&LKSkd+*mLtEwx6N^;|dIs delta 38 scmca*dE1ivGcPX}0}zCtowkwtG#gKier{fgezLw}acS}9hipGZ0r{2-XgPFTlKR2&LKSkd+*mE;KixL+A&4LPe delta 38 scmdlWvr&foGcPX}0}zCtowkwt2QyEder{fgezLw}acS{pJ{Bb|0OPC+g8%>k diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc index eccd28030bab23c5352ad1a704b146b6d672bc59..11581865aec44bd8abfe7ddf65490a68494cbc1d 100644 GIT binary patch delta 37 rcmZn`XcFN5%*)Hg00bS@^EYxUF><%)=jN5@r|A0zdv3O5JjDh8ub&D2 delta 38 scmZn?Xcpl9%*)Hg00a?dr)}g`V&rMn&&?~*Pu6!VE-l_{!FY-d0K}aO3IG5A diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc index 96696e6377a821fdd954bea31ffdb1c0ffb4f73b..c58ea89d7651f84d7c41798f336ab16f24ca916e 100644 GIT binary patch delta 36 qcmeyw@{xu6GcPX}0}ymr&)>*BiIKZjKR2&LKSkd+*mLqq#*+Za5es$z delta 37 rcmey!@`;7}GcPX}0}w=u(tJw)) delta 38 scmZqTZsz9x%*)Hg00a?dr)}gGVdQDl&&?~*Pu6!VE-l`y#TdZ?0KmHnYybcN diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc index 5634f6cac6298ec637f8dcc66dfd8e93468fcd96..20d68af936a437bbda6f013a798e6d7c6d4d17de 100644 GIT binary patch delta 37 rcmaE&_E?SkGcPX}0}ymr&)>*x&&=JfpPN^rpQ7&@?72CNnNI)!(sBxW delta 38 scmaE?_C$^QGcPX}0}zCtowkwNo|&gZKR2&LKUv?gxU_h4C^Me`0O)56h5!Hn diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc index 169138a569dee4b90f31fd8a86e2b31339ce39ea..8faa9094a48f49308fb214d4e4dfc2bee4ee2116 100644 GIT binary patch delta 37 rcmew*_EU`eGcPX}0}ymr&)>+sh>^QNKR2&LKSkd+*mLtv#)F&y-RukQ delta 38 scmew<_DhWWGcPX}0}zCtowkvC5hG8der{fgezLw}acS}99gGJ#0rW=<`2YX_ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc index 7d81579cac7bbf5f3636d0611d4d84a5cf173ce9..a499f1a0945f7f353704db1344aee4c4d8bdd121 100644 GIT binary patch delta 37 rcmcbteo3ACGcPX}0}ymr&)>-H#Khg8pPN^rpQ7&@?72CTsY(z4&WQ@e delta 38 scmcblep#LSGcPX}0}w=+sj+whgKR2&LKSkd+*mLty=C>jM(r*jn delta 38 scmdmJxXF+skeRziKR2&LKSkd+*mLs^W=jD8-qZ_w delta 38 scmeyM{!yL#GcPX}0}w=+sijBKMKR2&LKSkd+*mLtiw#jS&)Rqg2 delta 38 scmcaCbw!H%GcPX}0}zCtowkvC6&p{ber{fgezLw}acS}918kGo0P3;~mH+?% diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc index 69cab22412d45707bd3291d85025df69c5b283c7..89c31f4123ec0c25a6c919f39b501a5e18e3267e 100644 GIT binary patch delta 37 rcmZ3@w~CMZGcPX}0}ymr&)>*xz{uUApPN^rpQ7&@?77*U@d+ycyT}TE delta 38 scmZ3*x0;XpGcPX}0}zCtowkwNfRU$FKR2&LKUv?gxU_h)8{-pJ0MQ2viU0rr diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc index b6eae4372e5793dbc6ed542c8d1633bcb2eda7ea..c4de4f6c6cce9515856e3850a41f387041c6b981 100644 GIT binary patch delta 39 tcmX>zllk;aX710tyj%=G&|y7)Bew!OcddSIUWtB+zHhMSW^?wbM*;5F3^M=# delta 40 vcmX>-llja{X710tyj%=G5OH?eMs5Xmo;v;9yb}FneaGU`;>~94Q;z}w1&R$n diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc index 584ec9fb33f4517cf916a48cb1710de73d7082b0..63bb607642c51e0555958ad50daa9f3fb7458e55 100644 GIT binary patch delta 37 rcmezD{K=X7GcPX}0}ymr&)>*x#>ri$pPN^rpQ7&@?77*ObD0DH-tP-! delta 38 scmez5{MniNGcPX}0}w=Z~y=R diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc index da593ed6dfd22ffe5f10a688a40a305b87cf2a78..90691c5d118c15181751f872074b738f22337984 100644 GIT binary patch delta 37 rcmZpYYLw#s%*)Hg00bS@^EYz)GIBTT=jN5@r|A0zdu~o)oW~6SwT%is delta 38 scmZpaYLepq%*)Hg00a?dr)}i+W#nnm&&?~*Pu6!VE-l`i%s7u50LsS;N&o-= diff --git a/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc index 48f640609babaf8ed513d3c09a40bc3efb4bf708..998eca43615aefba1e22f9f8d9ab53c7130cc7a5 100644 GIT binary patch delta 34 ocmcb@c$ty=GcPX}0}ymr&!5O`%w43Pn^&TrqVF5*Ink>N0H#a|2mk;8 delta 35 pcmcc2c!iPsGcPX}0}zCtoi>r%n5S4jH?KrLS>Lg^w0NRt7XY~>3ljhU diff --git a/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc b/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc index 548cdccb95aee101b6e7ed39716fe7b52c40106d..0ea7cb93df0c1256515000fd08e795616791a285 100644 GIT binary patch delta 37 rcmZ1@xmuF@GcPX}0}ymr&)>*h%gLRmpPN^rpQ7&@?74Xgrvx(q!>bBi delta 38 scmZ22xki%vGcPX}0}zCtowkv?mXjx6KR2&LKUv?gxU_ilWKIcY0NFtcXaE2J diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc index 41a9bdd9ee8fa74468d0c7893feff5a46bab0c56..e25b5b770ebee9453fc23ca3c9b84d996bc17112 100644 GIT binary patch delta 34 ocmcb^c$<;?GcPX}0}ymr&!5O`%w46Qn^&TrqVF5*Iniqp0I1XoC;$Ke delta 35 pcmcc3c!!buGcPX}0}zCroi>r%n5SAlH?KrLS>Lg^w0NTDBmldw3o-xz diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc index 162b64074f8dc2b13d4a7833c0ab968d9d0b97f1..468a07cb23b05f5c9e2cc473117498f4fb6f4be9 100644 GIT binary patch delta 39 tcmaF!jOq0=ChpI?yj%=G&|y7)BR4w}cb$H2UWtB+zHhMSW;rImjQ|R|40Qki delta 40 ucmaF;jOooYChpI?yj%=G5O#LjMs9W{o_hVGcPX}0}ymr&)>+M!pL2xpPN^rpQ7&@?76v`(ZCV_+X@RW delta 38 scmZ2gy1JD6GcPX}0}zCrowkuXg^{OTKR2&LKUv?gxU_h46{CSA0P!pgIRF3v diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc index 220e3febee452ddd94505dc8e2b21d8df6af81ab..1d12793c651ecc367cc6c8d1d6a6f98f141586e8 100644 GIT binary patch delta 36 qcmX@ke2kg++!^mB&pPN^rpQ7&@>^WJPaR~si$_eWL delta 37 rcmX@ce4Ls4GcPX}0}zCtowkvihmog7KR2&LKUv?gxU_h(65|p8#q$dG diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc index 0e257d59d73baa5ed44fc056d609c6958d42bf1e..4e45ca54c9651f79cece041d38598ece36492865 100644 GIT binary patch delta 37 rcmcbocvq48GcPX}0}ymr&)>*>nUT9jKR2&LKSkd+*mLtM#xHyT-JlF& delta 38 scmcbscu$f0GcPX}0}zCtowkwtG9yo|er{fgezLw}acS}9myBQd0Q3e8Z2$lO diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc index eeabd1b8c37cd46363e1b5e52517641e72e0ead7..a7bfaea1b90020991c295ec5c51b803d3ee30c72 100644 GIT binary patch delta 36 qcmX@cdX$y>GcPX}0}ymr&)>++$;4fxpPN^rpQ7&@>^WJ1X&C^qTM6O- delta 37 rcmX@gdW@C(GcPX}0}zCrowkvilZmHTKR2&LKUv?gxU_h(Jkv4&#bXNW diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc index bf4541b273ae5f1b69855c8adb93d486a979b25c..6c0fc9fa1a14c086700b52698680539eb353dc40 100644 GIT binary patch delta 37 rcmexr^u>t#GcPX}0}ymr&)>-H%g9}$pPN^rpQ7&@?72CGQBVQ^+V~31 delta 38 scmexj^wo&_GcPX}0}zCrowkwNmyxGdKR2&LKUv?gxU_h4GNYgb0PznC*8l(j diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc index 4b40d3cb1fbfb38091a49327381938c9a0554b06..3467122c0378b48ec65dbc24cd2f24f8b7c0aaa5 100644 GIT binary patch delta 37 rcmbQCJzbmoGcPX}0}ymr&)>-H&B$G(^b diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc index f43963f6302b1be0c7d39ec1c8d629e40e135c76..1bcaf79f194310ef6c963ca09284ab74311bf2f3 100644 GIT binary patch delta 37 rcmZ3Qv?Pi9GcPX}0}ymr&)>)`$jn`!pPN^rpQ7&@?73Nk`L8Yj&5H{T delta 38 scmZ3Iv^0tPGcPX}0}zCrowku%keR1YKR2&LKUv?gxU_h)I`dy$0OK4C7ytkO diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc index 1c3214e2c8de13d98ad29126f7c14a2a8a8cb9f9..6bee9b9853a677fef62bc5e5386836bdbd51a359 100644 GIT binary patch delta 37 rcmbQ^H`|Z*Bi;=rbKR2&LKSkd+*mLtn#%xsp(BunF delta 38 scmbR3H^-0rGcPX}0}zCrowkvC79&r&er{fgezLw}acS}94UE~U0OrUHSO5S3 diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc index ac74e8a0b70f07405d07190bdfe8411c7ba11ccb..625161e438ea6d9257af9e12f82dae0b2a547db4 100644 GIT binary patch delta 37 rcmbOfIx&>{GcPX}0}ymr&)>-H%EDc$pPN^rpQ7&@?72CH#Y_zV$RP@E delta 38 scmbOjIw_Re%U0Ntz$c>n+a diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc index 2775a7a0c57d8bdcf5815e55417fc199dc905372..760553de84208028fd629eb657beb870b1b0a1e5 100644 GIT binary patch delta 39 ucmaDbk@5aSM()qNyj%=G&|y7)BlmMA?lS${yb}Esecxcu&HtDlxdH$K4h@k2 delta 40 vcmcaVk@3MqM()qNyj%=G5O#LjM(*cKJmvbic_sSE`i{k=#hd>!J#qyA8czr%n5RZRH?KrLS>Lg^w0NTDWB|Ma3pM}% diff --git a/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc index 90f2737dfc18093f01ca45ddbfbd0e9fea83d26f..44d7a8e944a10bd1dfdd67ef1e402ce6a497ef5d 100644 GIT binary patch delta 37 rcmeBk?sew=%*)Hg00bS@^EYzKGI6)+=jN5@r|A0zdu}#iijoHazmy6b delta 38 scmeBm?sMk;%*)Hg00d!Yr)}hxW#Vbm&&?~*Pu6!VE-l_{%oHUL0MywFCIA2c diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc index d18782551cb9d38ea8c1a1981e2526879b966a24..76635cf0d907bc9e9fb673ec51461cd872a22821 100644 GIT binary patch delta 35 pcmZ3*ypoywGcPX}0}ymr&!5QsmAgzoH?KrLMc+5rb21O(JOHt%3ReIC delta 36 qcmZ3sCD^Iz8ZeEFgvc6++Y4K!k#(4n6>kDB3 diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc index 5dc21fb9db74203844d944882e52c5f511c7d23c..843d10e49262b405f708efa7cc21241dfed86df7 100644 GIT binary patch delta 37 rcmew<_(PEUGcPX}0}ymr&)>-1&&XY^pPN^rpQ7&@?74XvV=y}a*<%Yw delta 38 scmew%_*0PkGcPX}0}zCrowkv?pOL3VKR2&LKUv?gxU_ilQpR9*0PleeQ2+n{ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc index 170e4f41c41c2e7e82ba082fd78fe97afb476f96..8fcdd19668785299c969b881eb305bd8b5feb864 100644 GIT binary patch delta 37 rcmdm2v$ux(GcPX}0}ymr&)>*h!NgsmpPN^rpQ7&@?76w0X}vW7(mV7gR-yX|H<@SV6?l?MB^N=_ScRaLJ%D2j!;l;me>~sL zaXb{2`yPHql86V@(LFr7+{b%KcdWBsrXJ77A;ez(z^^WIeYu@KrUd;`Zoe0@_goG= z!WYktqOClZ>x%W)Pss!Fw!A+#=6PK`w*zhGk?OX{U_g?G5{(cqcJq82S!1d~~!7Y*IO^khkO zs|Cd}aFJk(5#1#2l6%I$CY`sHqJf7D8|z>g#zl&CqfD@(TU7U)65;S68_rmiM_=1i zNv6J+O3$P-hmRf1W~S1)>C{X*n;F5jMsPtTrm4&+gHn_x!gp_meLof1<>0%ruu3+`z=tCGSA6(S~2&{NMCtOy}k zv0xNwQGiqDia)tB$~%AkVr2s4`dy>oF2im?0T0ut)nWpY*fwqVG*b+mnn-01Wiu%} zdE~M0qz|TNQqz-}naO)0g!+^+B$EeiEF zJ!%2C0`R0#i^tCIsCY!$;35Q2A}T2hHcdk%zz;YYHB1sXM=gWt6oYI)KKaQyc?8eF zDO|FPK)nh7f}zhTOsi>V1eT0ZPl+Rf1othCS_}wcbdEB&vaA;rA}CRG(_(f_*Qzov zu6N(W28d-t>>dgu;8##>Qz=u?7eX%XDm81F4wirqTp)UZs)P;JGprl1w=Ghb?6_>#_Qqg|eGB{E{@>$X8YrK6~K_c*nkbVVpOtwxe<0 zy;}6KOZ*3`PooL`+3J<(iGU>Clzt@L?v?gVAYOTXyYt0wLnsiEI(wXN{_X=5P}U-c zdHCXcaNzRA9`q>x=f$6rsUdk3eXyfFwXLVL9Pni*AyK{c8ga!am#}ED66n>iqrpqdxQ>C-g_` zL&zEa(~KW=@#ox+QH)Q&PtcgN^1k9nJx*S`WrWo z!JB?K$PU0~fUSyQ9~juUbL6v%Z}Om+Pl{nm4B|t^ekh!t6T`P*m|~~I^sv}@R176p zJ{wk5Edjc*C}U~fvp$Wo{P_9+K>PCg4fKk0^6G1Tw8j(H`}sFMzk&YY^j(W0v<`+9 zbj`VPeb9$)IKi8*26uhS*U)vfb<3>=F}W3zTH9_ldDF1ewf$Dan>I_~-rFtS^oVo$ T%e}tV@5fQ}Uy>NvKFH_4Gamxe delta 2075 zcmY*aYitx%6rQ`&oo=Vw?b0pX@@g-o1=_9K)ANc2Q@}b`g3PcXBZI@rzb@2B$LLI^2({-9|*Cp$kEyr?q1}k3l?GoK*4`&ei z#@+gQBg$RJarK-WprfP3RNqh(XhBjy<~vL2{Lu*QI#x3$PZHz)79 z^e_n0;FbH{ky@%AWqAm6)oWG0&L%p)WOhiZSiYO%2o%>VbgiI&A1y}<=!|=2&6txd zSdYjNU|C6D8M%xqVJ|{ERDtI@*Lb`yAMHfV^yi}usLDIHFndl~^#7Lm7t4ViL8=dz z7BivbFo;#1!$~#qT%cd)kzG6%L@mt9FTP6?qj!!~&AvA)K`uuy$LU2~OQu)ouYEIp z5~iDu$0}=`N1&4RimfDVf-OZOx~(ROrs%eUH77;%%n*I*ROx`D5p0`8w@79vbJ&QB zVmsWhvPZSRGYv>^O2K_3P5Mn@fnl-*3pO$%dQv6WaI%D+C$TI?w~aWKH6p4I&NQ7q zj~na6N-B(v1!Y^hJ%Ceck79ahnTlnk`_&Y|eTM1zJg)5BZI~%BY3My_uVWI}(+%9+ zZ5ocP>b>#3L`!#fLkhJ(j)`MU=af>?r1maNN=d2~R3(BAnM0lsIA!21TiWo#q@iI0 zLjYUZV;dL({6`Jkr|2n`8*M$&Q2AAGD(AfnLtI^tM@>QtQ_vMg1@1jCqFq*u{^ z(aaXkdJY}#GZ>?(cuj}#N!Rt0v|-lih6XIG0nnLjTD9P48>X({bsg=AjqP%a7mStd zWa$1Ic@M(yCDSmHQ-ckYqnMV`>!>Cu2^C^v$1cTI`;C8@qzxNe6HpABr2gy}vU8eh z!|c&ZH!9q@ZX*)>o+o|iR7aKB53ktmTkt)p&Nc6c7t3sBu$I9(2I~Q6+v)A_$rw7l zrhEmso0k|=F=%A4g!wfwXr+IiPSMnxkD`tA+?zMVH*$cSdz!m5*ByE5UbM-V+fZ~p z8XJ!;8I3L(k1jvso8)0>qJH)D>ifp4*N#@N^^8@c)vK=8EE})ce5T`iY|U>4;B+-y zITpTWGR&3MUJb|Gi8G&|oZf5sEp+6ab7(o$-kpn9(r4b?PaddAG@?&yi>0QV3poNq zQGk^T;pSXvbVjKicPxUAv9Y4)^^p7UV`-K&@)QT*>4+&0tDC_m8g!d@Y-plv^0IF<>8V^C{-E zkHKbuR&y^i+u2SBgFbkCQcS0{ID22=!?cy2{k9Dys5nuHT4~+HE%cIG`rSbujngYX zE}&oDyaj*5AN&wP=zCZ=fo{9|ep=*1Kf8msUli)weg0V!h4Uu;%$N*tg+-Hjo+)y( h7Eb1SW{?xh?i6@tx2sJq^A&bSQ0O;~q1goa`~z+cnXUi; diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc index 369446f1188f75443b7363147ff01d1c1564b9d6..d2da5c79432061ae81816ccf1046baaa89f23eeb 100644 GIT binary patch delta 37 rcmeAd?i1$z%*)Hg00f=Z^EYy{yb=jN5@r|A0zdv3O64q^iUvVRGo delta 38 scmeAZ?ic3%%*)Hg00d!Yr)}g`W9BK<&&?~*Pu6!VE-l_{!yLp00LMoPssI20 diff --git a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index e622918f..ff6e5d5e 100644 --- a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -880,12 +880,13 @@ class EnterpriseRouter: text: str, state_decision: RouteDecision, ) -> RouteDecision | None: - """Detecta semanticamente mudança de intenção durante uma transação. + """Detecta abandono explícito da transação seguido (ou não) de nova intenção. - Não existe lista de palavras para desistência ou mudança de assunto. Uma - interrupção nasce de uma intent diferente resolvida por uma keyword - configurada no ``routing.yaml`` ou, na ausência dela, por uma decisão - semântica do LLM com o contexto da transação pendente. + Durante COLLECTING_PARAMETERS/AWAITING_CONFIRMATION, uma classificação + genérica de outra intent nunca basta para interromper a transação. A saída + só é permitida quando o turno expressa semanticamente abandono explícito + do objetivo atual (por exemplo, desistir/deixar para lá) ou handoff tratado + por sua regra própria. Não há blacklist/keyword hardcoded de abandono. """ active_tx = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} started_intent = str(active_tx.get("started_from_intent") or "").strip() @@ -910,15 +911,10 @@ class EnterpriseRouter: # both failure modes: parameter extraction cannot hide a real new goal, # and a broad keyword cannot steal a legitimate parameter turn. if not (self.enable_llm_router and self.llm is not None): - configured_candidate.metadata = { - **(configured_candidate.metadata or {}), - "transaction_interruption": "intent_shift", - "interrupted_state": state_decision.next_state, - "interrupted_agent": state_decision.agent, - "interrupted_intent": started_intent or previous_intent, - "interruption_source": "configured_routing", - } - return configured_candidate + # Sem classificador semântico não há evidência suficiente para + # distinguir uma nova meta explícita de uma expressão pertencente + # à própria transação (ex.: "isso mesmo, pode cancelar"). + return None if not (self.enable_llm_router and self.llm is not None): return None @@ -951,11 +947,11 @@ class EnterpriseRouter: ), } system = ( - "Você decide apenas se o turno atual continua a transação ativa ou muda de intenção. " - "Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " - "A extração dos parâmetros pendentes já foi tentada antes desta etapa e não consumiu o turno. " - "Se ainda assim a mensagem for apenas uma resposta referencial/valor/nome ao dado pendente, retorne CONTINUE. " - "Se o usuário passou claramente a perseguir outro objetivo, retorne SHIFT e a nova intent permitida. " + "Você decide somente se o usuário ABANDONA EXPLICITAMENTE a transação ativa. " + "Uma nova intent semanticamente plausível NÃO basta. Use o significado completo da mensagem e o contexto transacional; nunca use uma palavra isolada como regra. " + "Retorne CONTINUE quando a fala puder ser confirmação, correção, parâmetro, referência ou instrução ligada à operação ativa, inclusive expressões como 'pode cancelar' quando cancelar pode ser a própria ação em andamento. " + "Retorne ABANDON somente quando houver evidência explícita de desistência/abandono da operação atual; se a mesma fala também trouxer um novo objetivo, informe a nova intent/agent permitidos. " + "Exemplo semântico: 'esquece isso, quero ver minha fatura' = ABANDON + nova intent. " "Retorne somente JSON válido com decision, intent, agent, confidence, reason." ) user = { @@ -981,7 +977,7 @@ class EnterpriseRouter: logger.warning("Falha ao avaliar mudança semântica de intent transacional via LLM: %s", exc) return None - if str(data.get("decision") or "").strip().upper() != "SHIFT": + if str(data.get("decision") or "").strip().upper() != "ABANDON": return None confidence = float(data.get("confidence") or 0.0) if confidence < self.intent_shift_threshold: @@ -1002,11 +998,11 @@ class EnterpriseRouter: reason=str(data.get("reason") or "Mudança semântica de intenção durante transação."), method="llm", metadata={ - "transaction_interruption": "intent_shift", + "transaction_interruption": "explicit_abandonment", "interrupted_state": state_decision.next_state, "interrupted_agent": state_decision.agent, "interrupted_intent": started_intent or previous_intent, - "interruption_source": "semantic_classifier", + "interruption_source": "semantic_abandonment_classifier", "configured_routing_hint": ( configured_candidate.intent if configured_candidate is not None else None ), diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc index 5312258964a239b5ffa243eb9639dbd4d27013db..9b34ab592ec42043fc4877c0897703d1d3db0c5b 100644 GIT binary patch delta 20 acmcb^bcc!iGcPX}0}ymt&)>)$$Or&Ej0I2t delta 20 acmcb^bcc!iGcPX}0}w>G%-F~s$Or&DNChDP diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc index 9603442b9cea1a901949c97fc5d7f07aa0c4d8e4..c27cb0da085c70ab3d1ad8053e9a2fe526368905 100644 GIT binary patch delta 41815 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 z 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 delta 34056 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 E0bOHK761SM diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc index b797d4f4500c47dbb651d181a8aca1e61c8e2d14..27ac2787bc828c9f5593291b012f51bcc70448df 100644 GIT binary patch literal 32568 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 Gum1;R$_NYq delta 4090 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 str: + """Group bounded history by reconciliation priority without losing recency. + + The temporal reconciler is a fallback, so current-turn values and validated + transaction state are supplied separately. Within historical conversation, + assistant explanations are intentionally considered before older user turns: + they frequently carry grounded relationships produced from tool/evidence + results (for example item -> amount). They remain interpretive candidates + only and must still pass the configured authoritative pre-validation. + """ + ordered = AgentRuntimeMixin._transaction_context_newest_first(context) + if not ordered: + return "" + assistant: list[str] = [] + user: list[str] = [] + other: list[str] = [] + for line in ordered.splitlines(): + payload = line.split(": ", 1)[1] if ": " in line else line + low = payload.lower().lstrip() + if low.startswith(("assistant:", "agente:")): + assistant.append(line) + elif low.startswith(("user:", "cliente:")): + user.append(line) + else: + other.append(line) + + sections: list[str] = [] + if assistant: + sections.append( + "priority_3_previous_assistant_tool_or_evidence_context:\n" + "\n".join(assistant) + ) + if user: + sections.append("priority_4_previous_user_utterances:\n" + "\n".join(user)) + if other: + sections.append("priority_5_other_conversational_context:\n" + "\n".join(other)) + return "\n".join(sections) + + @staticmethod + def _apply_transaction_reconciliation( + arguments: dict[str, Any], + reconciliation: dict[str, Any] | None, + *, + protected_current_fields: set[str] | None = None, + ) -> tuple[dict[str, Any], list[str]]: + """Apply temporal reconciliation without erasing current-turn candidates. + + Current-turn extraction is the first source of truth for *what the user + just said*. Reconciliation may canonicalize that candidate, and business + pre-validation may reject it, but temporal history must not make it + disappear before the validator sees it. + """ + out = dict(arguments or {}) + rec = dict(reconciliation or {}) + protected = {str(name) for name in (protected_current_fields or set())} + ignored_clears: list[str] = [] + for field in rec.get("clear_fields") or []: + name = str(field) + if name in protected: + ignored_clears.append(name) + continue + out.pop(name, None) + # Canonicalized values are still allowed; this is not a hard field lock. + out.update(dict(rec.get("values") or {})) + return out, ignored_clears + + @staticmethod + def _apply_prevalidated_structural_candidates( + arguments: dict[str, Any], + reconciliation: dict[str, Any] | None, + policy: dict[str, Any] | None, + ) -> tuple[dict[str, Any], dict[str, str]]: + """Apply structural *candidates* only behind authoritative pre-validation. + + Candidates are not resolved parameters. They may be raw local evidence + fragments and exist solely so a configured domain validator can + canonicalize/reject them. Without enabled pre-validation they are ignored. + """ + out = dict(arguments or {}) + cfg = (policy or {}).get("pre_validation") if isinstance(policy, dict) else None + if not isinstance(cfg, dict) or not cfg.get("enabled") or not str(cfg.get("tool") or "").strip(): + return out, {} + rec = dict(reconciliation or {}) + candidates = dict(rec.get("candidates") or {}) + provenance = dict(rec.get("candidate_provenance") or {}) + applied: dict[str, str] = {} + for field, value in candidates.items(): + name = str(field) + if out.get(name) not in _EMPTY_VALUES or value in _EMPTY_VALUES: + continue + out[name] = value + applied[name] = str(provenance.get(name) or "structural_candidate") + return out, applied + async def _reconcile_transaction_parameters( self, state: dict[str, Any], @@ -1543,7 +1649,7 @@ class AgentRuntimeMixin: suffix = "\n\nCONTINUAÇÃO ATUAL DO CLIENTE:\n" if prefix in effective and suffix in effective: conversational_context = effective.split(prefix, 1)[1].split(suffix, 1)[0].strip() - ordered_context = self._transaction_context_newest_first(conversational_context) + ordered_context = self._transaction_context_priority_view(conversational_context) cached = route_meta.get("transaction_parameter_values") effective_known = dict(known_arguments or {}) if isinstance(cached, dict): @@ -1602,7 +1708,7 @@ class AgentRuntimeMixin: route_meta = ((state.get("route_decision") or {}).get("metadata") or {}) if isinstance(state.get("route_decision"), dict) else {} text = route_meta.get("original_input") if route_meta.get("contextual_reentry") else None text = text or state.get("sanitized_input") or state.get("user_text") or "" - return await extract_transaction_parameters( + return await extract_current_transaction_parameters( getattr(self, "llm", None), text=str(text), tool_name=tool_name, @@ -1610,7 +1716,6 @@ class AgentRuntimeMixin: known_arguments=dict(known_arguments or {}), parameter_schema=schema, tool_description=description, - conversational_context="", ) async def _extract_transaction_parameters( @@ -2969,12 +3074,12 @@ class AgentRuntimeMixin: # intent, a transação anterior é encerrada e seus latches são limpos. active_before_interruption = self._active_transaction(state) interruption = str(route_meta.get("transaction_interruption") or "").strip().lower() - if active_before_interruption and interruption == "intent_shift": + if active_before_interruption and interruption == "explicit_abandonment": interrupted_tool = active_before_interruption.get("tool_name") self._finish_active_transaction(state, "CANCELLED") state["transaction_pre_validation"] = None state["tool_policy_result"] = { - "action": "cancelled_by_intent_shift", + "action": "cancelled_by_explicit_abandonment", "tool_name": interrupted_tool, } @@ -3008,21 +3113,59 @@ class AgentRuntimeMixin: policy = self._resolve_tool_execution_policy(tool_name, previous_args) missing_before = self._missing_required_arguments(policy, previous_args) - # Primeira tentativa: coleta tradicional usando SOMENTE a fala - # atual. O histórico não deve participar enquanto a mensagem corrente - # conseguir preencher o contrato normalmente. + # O EnterpriseRouter pode já ter consumido parâmetros pendentes + # neste MESMO turno para garantir precedência da transação sobre + # intent shift. Esses valores não são apenas telemetria: precisam + # ser materializados no estado transacional antes de qualquer nova + # extração. Eles continuam sendo candidatos e passam normalmente + # pela pre-validation autoritativa antes de confirmação/execução. arguments = dict(previous_args) + cached_route_values: dict[str, Any] = {} + cached = route_meta.get("transaction_parameter_values") + missing_before_set = {str(name) for name in missing_before} + if isinstance(cached, dict): + cached_route_values = { + str(key): value + for key, value in cached.items() + if str(key) in missing_before_set and value not in _EMPTY_VALUES + } + arguments.update(cached_route_values) + + # A fala atual pode tanto preencher um campo faltante quanto corrigir + # explicitamente um campo já coletado. Por isso o extrator current-only + # examina TODOS os parâmetros requeridos, nunca apenas missing_parameters. + # Ele continua estritamente local à mensagem atual e só devolve campos + # explicitamente presentes; portanto não compete com a reconciliação + # temporal e não apaga estado quando o usuário não mencionou o campo. + required_fields = [str(name) for name in (policy.get("requires") or [])] + # Legacy/domain overrides historically receive only pending fields. + # Keep that extension contract intact; the base framework extractor + # is the one allowed to inspect every required field so an explicit + # current-turn correction can replace an already collected value. + legacy_override = getattr(type(self), "_extract_transaction_parameters", None) + has_legacy_override = ( + legacy_override is not None + and legacy_override is not AgentRuntimeMixin._extract_transaction_parameters + ) + current_fields = ( + self._missing_required_arguments(policy, arguments) + if has_legacy_override else required_fields + ) extracted_current = await self._extract_transaction_parameters_current_only( state, tool_name=tool_name, - missing_parameters=missing_before, - known_arguments=previous_args, + missing_parameters=current_fields, + known_arguments=arguments, ) arguments.update(dict(extracted_current or {})) state["transaction_parameter_collection"] = { "tool_name": tool_name, "mode": "current_turn", - "resolved_fields": sorted(str(k) for k in (extracted_current or {}).keys()), + "router_resolved_fields": sorted(cached_route_values.keys()), + "current_turn_resolved_fields": sorted(str(k) for k in (extracted_current or {}).keys()), + "resolved_fields": sorted( + set(cached_route_values.keys()) | {str(k) for k in (extracted_current or {}).keys()} + ), } # Segunda opção: somente se a coleta tradicional ainda deixar @@ -3038,15 +3181,23 @@ class AgentRuntimeMixin: parameter_names=required_fields, known_arguments=arguments, ) - for field in reconciliation.get("clear_fields") or []: - arguments.pop(str(field), None) - arguments.update(dict(reconciliation.get("values") or {})) + protected_current_fields = { + str(k) + for k, v in dict(extracted_current or {}).items() + if v not in _EMPTY_VALUES + } + arguments, ignored_clears = self._apply_transaction_reconciliation( + arguments, reconciliation, protected_current_fields=protected_current_fields + ) state["transaction_parameter_reconciliation"] = { "tool_name": tool_name, "trigger": "traditional_collection_unresolved", "decisions": dict(reconciliation.get("decisions") or {}), "provenance": dict(reconciliation.get("provenance") or {}), "clear_fields": list(reconciliation.get("clear_fields") or []), + "values": dict(reconciliation.get("values") or {}), + "protected_current_fields": sorted(protected_current_fields), + "ignored_clear_fields": sorted(ignored_clears), } else: state.pop("transaction_parameter_reconciliation", None) @@ -3071,6 +3222,12 @@ class AgentRuntimeMixin: tool_name, arguments, state, exclude_fields=policy.get("requires") or [] ) policy = self._resolve_tool_execution_policy(tool_name, arguments) + arguments, applied_candidates = self._apply_prevalidated_structural_candidates( + arguments, locals().get("reconciliation"), policy + ) + if applied_candidates: + state.setdefault("transaction_parameter_reconciliation", {})["applied_structural_candidates"] = applied_candidates + policy = self._resolve_tool_execution_policy(tool_name, arguments) missing = self._missing_required_arguments(policy, arguments) if missing: self._set_collecting_parameters( @@ -3321,21 +3478,53 @@ class AgentRuntimeMixin: policy = self._resolve_tool_execution_policy(selected_action, action_args) required = [str(name) for name in (policy.get("requires") or [])] - # Valores já estruturados no contexto podem satisfazer requirements sem - # parsing textual. Para qualquer required field ainda ausente, a fala do - # usuário é interpretada exclusivamente pelo extrator LLM transacional. + # Primeiro turno transacional segue a mesma regra de COLLECTING_PARAMETERS: + # 1) coleta tradicional SOMENTE da fala atual; 2) reconciliação temporal + # apenas como fallback se ainda houver required fields faltando. + # Isso evita que histórico altere o happy path e, ao mesmo tempo, permite + # resolver referências contextuais quando a fala atual é incompleta. missing_initial = self._missing_required_arguments(policy, action_args) - # No primeiro turno, a fala atual pode fornecer/corrigir qualquer required - # field, inclusive um valor que exista no contexto estruturado mas pertença - # a uma transação anterior. O extrator continua restrito ao contrato - # ``requires`` e só sobrescreve quando a LLM realmente extrai um valor. - extracted_initial = await self._extract_transaction_parameters( + extracted_initial = await self._extract_transaction_parameters_current_only( state, tool_name=selected_action, missing_parameters=required, known_arguments={k: v for k, v in action_args.items() if k not in set(required)}, ) action_args.update(extracted_initial) + state["transaction_parameter_collection"] = { + "tool_name": selected_action, + "mode": "current_turn", + "resolved_fields": sorted(str(k) for k in (extracted_initial or {}).keys()), + } + + missing_after_current = self._missing_required_arguments(policy, action_args) + if missing_after_current: + reconciliation = await self._reconcile_transaction_parameters( + state, + tool_name=selected_action, + parameter_names=required, + known_arguments=action_args, + ) + protected_current_fields = { + str(k) + for k, v in dict(extracted_initial or {}).items() + if str(k) in set(missing_initial) and v not in _EMPTY_VALUES + } + action_args, ignored_clears = self._apply_transaction_reconciliation( + action_args, reconciliation, protected_current_fields=protected_current_fields + ) + state["transaction_parameter_reconciliation"] = { + "tool_name": selected_action, + "trigger": "initial_traditional_collection_unresolved", + "decisions": dict(reconciliation.get("decisions") or {}), + "provenance": dict(reconciliation.get("provenance") or {}), + "clear_fields": list(reconciliation.get("clear_fields") or []), + "values": dict(reconciliation.get("values") or {}), + "protected_current_fields": sorted(protected_current_fields), + "ignored_clear_fields": sorted(ignored_clears), + } + else: + state.pop("transaction_parameter_reconciliation", None) # O mapper MCP continua responsável somente por parâmetros auxiliares que # não pertencem ao contrato transacional. @@ -3354,6 +3543,18 @@ class AgentRuntimeMixin: ) state["tool_policy_result"] = {**policy, "tool_name": selected_action} + action_args, applied_candidates = self._apply_prevalidated_structural_candidates( + action_args, locals().get("reconciliation"), policy + ) + if applied_candidates: + state.setdefault("transaction_parameter_reconciliation", {})["applied_structural_candidates"] = applied_candidates + 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" + ) + policy = self._resolve_tool_execution_policy(selected_action, action_args) + missing = self._missing_required_arguments(policy, action_args) if missing: self._set_collecting_parameters( diff --git a/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py b/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py index 88cfbeb6..d6c2687f 100644 --- a/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py +++ b/libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py @@ -69,6 +69,81 @@ def parse_transaction_confirmation(text: str) -> str | None: return None +async def extract_current_transaction_parameters( + llm: Any, + *, + text: str, + tool_name: str, + missing_parameters: list[str], + known_arguments: Mapping[str, Any] | None = None, + parameter_schema: Mapping[str, Any] | None = None, + tool_description: str | None = None, +) -> dict[str, Any]: + """Extract only values explicitly present in the current user utterance. + + This is deliberately narrower than temporal reconciliation: no history, no + cross-turn inference and no routing. Spoken numeric expressions are normalized + according to the declared numeric type, while textual fields must satisfy the + schema description in the current message itself. + """ + names = [str(x) for x in (missing_parameters or []) if str(x).strip()] + message = str(text or "").strip() + if not names or not message or llm is None: + return {} + schema = dict(parameter_schema or {}) + field_spec = { + name: { + "type": schema.get(name, "string") if not isinstance(schema.get(name), dict) else schema.get(name, {}).get("type", "string"), + "description": None if not isinstance(schema.get(name), dict) else schema.get(name, {}).get("description"), + } + for name in names + } + output = {name: None for name in names} + prompt = ( + "Extraia SOMENTE parâmetros explicitamente expressos na mensagem atual do usuário. " + "Não use histórico, não complete referências elípticas com conhecimento anterior e não invente valores. " + "Use exclusivamente type/description do schema para decidir se um trecho preenche um campo. " + "Para campos numéricos, normalize também números falados por extenso para número (por exemplo uma expressão monetária falada deve virar number). " + "Para campos textuais que exigem uma entidade concreta, uma referência apenas por valor/posição não preenche o campo. " + "Retorne SOMENTE JSON válido com exatamente as chaves pedidas; use null quando ausente ou ambíguo.\n" + f"tool: {tool_name}\n" + f"tool_description: {tool_description or ''}\n" + f"known_arguments: {json.dumps(dict(known_arguments or {}), ensure_ascii=False, default=str)}\n" + f"parameter_schema: {json.dumps(field_spec, ensure_ascii=False, default=str)}\n" + f"current_user_message: {message}\n" + f"Formato: {json.dumps(output, ensure_ascii=False)}" + ) + try: + response = await llm.ainvoke( + [{"role": "user", "content": prompt}], + profile_name="transaction_parameter_extraction", + component_name="transaction_parameter_extraction", + generation_name="llm.transaction_parameter_current_only", + temperature=0.0, + ) + except TypeError: + response = await llm.ainvoke([{"role": "user", "content": prompt}]) + except Exception as exc: + logger.warning("transaction.parameter.current_extract_failed tool=%s fields=%s error=%s", tool_name, names, exc) + return {} + raw = _response_text(response).strip() + try: + payload = parse_json_object(raw) + except (TypeError, ValueError): + logger.warning("transaction.parameter.current_extract_invalid tool=%s raw=%r", tool_name, raw[:240]) + return {} + if not isinstance(payload, dict): + return {} + values: dict[str, Any] = {} + for name in names: + value = payload.get(name) + declared = field_spec.get(name, {}).get("type", "string") + coerced = _coerce(value, declared) + if coerced not in _EMPTY_VALUES: + values[name] = coerced + return values + + async def reconcile_transaction_parameters( llm: Any, *, @@ -113,33 +188,217 @@ async def reconcile_transaction_parameters( for name in names } } + + def _anchor_tokens(value: Any) -> list[str]: + """Return generic textual variants for a known scalar anchor.""" + if value in _EMPTY_VALUES: + return [] + raw = str(value).strip().lower() + tokens = {raw} + try: + number = float(str(value).replace(",", ".")) + tokens.add(f"{number:.2f}") + tokens.add(f"{number:.2f}".replace(".", ",")) + if number.is_integer(): + tokens.add(str(int(number))) + except (TypeError, ValueError): + pass + return [token for token in tokens if token] + + def _anchor_relation_candidates(context: str, arguments: Mapping[str, Any]) -> list[str]: + """Surface history fragments related to any already-known parameter. + + This is not a second reconciliation pass and does not resolve anything + deterministically. It only structures the SAME temporal scan so the LLM + can use known/current fields as cross-field anchors while reconciling all + pending fields coherently in one call. + """ + if not context or not arguments: + return [] + # Keep role/history prefixes but split long assistant explanations into + # smaller semantic clauses (bullets/semicolons) so one anchor is not lost + # among many unrelated entity/value pairs. + fragments: list[str] = [] + for raw_line in context.splitlines(): + line = raw_line.strip() + if not line: + continue + parts = [part.strip() for part in re.split(r"\s*\*\s*|\s*;\s*", line) if part.strip()] + fragments.extend(parts or [line]) + + candidates: list[str] = [] + for field_name, field_value in arguments.items(): + variants = _anchor_tokens(field_value) + if not variants: + continue + for fragment in fragments: + low = fragment.lower() + if any(token in low for token in variants): + candidates.append(f"anchor[{field_name}={field_value}]: {fragment}") + return list(dict.fromkeys(candidates)) + + def _structured_evidence_blocks(context: str, arguments: Mapping[str, Any]) -> list[dict[str, Any]]: + """Represent the same bounded history as explicit message evidence blocks. + + The reconciler remains a single LLM call. This helper only prevents role, + recency and anchor relationships from being hidden inside one prose blob. + It is fully domain-neutral: field meaning still comes from parameter_schema. + """ + blocks: list[dict[str, Any]] = [] + current_role = "other" + current_lines: list[str] = [] + rank = 0 + + def flush() -> None: + nonlocal rank, current_lines, current_role + if not current_lines: + return + text_block = " ".join(x.strip() for x in current_lines if x.strip()).strip() + current_lines = [] + if not text_block: + return + + # A single assistant message often contains a list of independent + # entity/attribute relations (for example several invoice items). + # Keeping the whole answer as one evidence block makes an otherwise + # exact anchor ambiguous because every entity in the list competes + # inside the same block. Segment LOCAL relations while preserving + # the original role, priority and message recency. This is still the + # same temporal reconciliation and the same single LLM call. + fragments = [ + frag.strip(" \t-•") + for frag in re.split( + r"\s+(?:\*|•)\s+|\s*;\s+|\s+(?=\d+[.)]\s+)", + text_block, + ) + if frag.strip(" \t-•") + ] + if not fragments: + fragments = [text_block] + + rank += 1 + role_priority = 3 if current_role == "assistant" else 4 if current_role == "user" else 5 + for fragment_index, fragment in enumerate(fragments, start=1): + anchor_matches: list[dict[str, Any]] = [] + low = fragment.lower() + for field_name, field_value in arguments.items(): + variants = _anchor_tokens(field_value) + matched = [token for token in variants if token in low] + if matched: + anchor_matches.append({ + "field": str(field_name), + "value": field_value, + "matched_tokens": matched, + }) + blocks.append({ + "priority": role_priority, + "recency": rank, + "fragment": fragment_index, + "role": current_role, + "text": fragment, + "anchor_matches": anchor_matches, + }) + + for raw_line in str(context or "").splitlines(): + line = raw_line.strip() + if not line: + continue + low = line.lower() + # Priority-view section headers are metadata, not conversational text. + if low.startswith("priority_3_previous_assistant"): + flush(); current_role = "assistant"; continue + if low.startswith("priority_4_previous_user"): + flush(); current_role = "user"; continue + if low.startswith("priority_5_other"): + flush(); current_role = "other"; continue + payload = line.split(": ", 1)[1] if line.startswith("history:") and ": " in line else line + plow = payload.lower().lstrip() + detected = None + if plow.startswith(("assistant:", "agente:")): + detected = "assistant" + elif plow.startswith(("user:", "cliente:")): + detected = "user" + elif plow.startswith("system:"): + detected = "other" + if detected is not None: + flush() + current_role = detected + payload = payload.split(":", 1)[1].strip() if ":" in payload else payload + current_lines.append(payload) + flush() + return blocks + + context_text = str(conversational_context or "").strip() + anchor_relations = _anchor_relation_candidates(context_text, known) + evidence_blocks = _structured_evidence_blocks(context_text, known) + missing_initial = [name for name in names if name not in known] + + # Field-targeted views do not resolve parameters and do not add another LLM + # pass. They expose the SAME local evidence blocks next to the declarative + # semantics of each still-missing field so the single temporal reconciliation + # call can correlate anchors with the correct schema-defined target instead of + # rediscovering that mapping inside a large generic JSON blob. No field names + # or domain concepts are hardcoded here. + anchored_blocks = [block for block in evidence_blocks if block.get("anchor_matches")] + field_evidence_views = { + name: { + "field_spec": field_spec.get(name, {}), + "anchored_evidence_blocks": anchored_blocks, + } + for name in missing_initial + } + + reconciliation_focus = { + "current_user_message": message, + "known_parameters": known, + "missing_parameters": missing_initial, + "missing_field_views": field_evidence_views, + } + prompt = ( + "FOCO PRINCIPAL DA RECONCILIAÇÃO (examine isto antes de qualquer contexto secundário):\n" + f"reconciliation_focus: {json.dumps(reconciliation_focus, ensure_ascii=False, default=str)}\n" + "Se um campo faltante tiver um único evidence_block ancorado semanticamente compatível com seu field_spec, resolva-o; use unresolved somente em ambiguidade real.\n\n" "Você é o conciliador temporal de parâmetros de uma transação ativa. " - "Reconstrua UM CONJUNTO COERENTE de parâmetros; não extraia cada campo de forma isolada. " + "Faça UMA ÚNICA VARREDURA TEMPORAL e reconstrua UM CONJUNTO COERENTE de parâmetros. " + "Todos os campos conhecidos e faltantes devem ser avaliados juntos; não execute uma busca isolada por campo e não faça uma segunda etapa de fallback. " "Não decida roteamento, confirmação nem execução.\n\n" "CONTRATO OBRIGATÓRIO:\n" - "1. O significado de cada campo vem EXCLUSIVAMENTE de parameter_schema e transaction_description, considerando principalmente a descrição semântica quando disponível. O framework não conhece conceitos de domínio por nome de campo.\n" - "2. A ausência de tipo ou descrição NÃO impede a extração quando o restante do contrato e o texto forem semanticamente suficientes.\n" - "3. A busca textual é temporal, em ordem decrescente: user_message é a fonte mais nova; depois conversational_context já vem do texto mais novo para o mais antigo.\n" - "3. Textos do usuário e textos explicativos do assistente podem ser usados para interpretar referências e relações entre campos. Não trate texto do contexto como uma nova afirmação do cliente; eles são contexto, não evidência autoritativa de negócio.\n" - "4. Para cada campo escolha: resolved = um texto determina novo valor; preserve = nenhum texto mais novo invalida o valor conhecido; clear = existe mudança textual mais nova que torna o valor conhecido incompatível/sem vínculo seguro com o novo contexto e ainda não há substituto; unresolved = não há valor conhecido nem candidato textual seguro.\n" - "5. Se uma fonte mais nova muda uma entidade, objeto, escopo ou outro contexto que dava sentido a campos extraídos anteriormente, REAVALIE os demais campos como conjunto. Não combine automaticamente um atributo antigo com um contexto novo só porque ambos existem.\n" - "6. Um valor de texto anterior pode continuar válido após uma mudança somente quando os textos, considerados em conjunto, sustentarem de forma inequívoca que ele ainda pertence ao contexto mais recente. Caso contrário use clear para o campo dependente.\n" - "7. Se a nova menção é inválida no mundo real, NÃO volte silenciosamente ao valor antigo. Ainda assim devolva o candidato textual mais recente como resolved; a pre-validation autoritativa é responsável por rejeitá-lo.\n" - "8. known_arguments é fallback de estado. Use preserve somente quando nenhum texto mais novo o contradiz ou rompe sua associação contextual.\n" - "9. Uma expressão só pode preencher um campo se satisfizer semanticamente type/description desse campo. Não transfira um trecho para outro campo apenas por proximidade lexical.\n" - "10. Se um texto recente corrige explicitamente informação anterior, a correção prevalece para os campos que o schema permite inferir; os demais devem ser reavaliados quanto à coerência com a correção.\n" - "11. Em ambiguidade razoável, prefira clear/unresolved a inventar uma associação. Em caso de dúvida razoável sobre a correspondência ou o valor, prefira null.\n" - "12. Responda SOMENTE JSON válido no formato pedido, sem markdown e sem chaves extras.\n\n" + "1. O significado de cada campo vem EXCLUSIVAMENTE de parameter_schema e transaction_description, principalmente a descrição semântica quando disponível. O framework não conhece conceitos de domínio por nome de campo.\n" + "2. A ausência de tipo ou descrição NÃO impede a extração nem a resolução quando o restante do contrato e o contexto forem semanticamente suficientes.\n" + "3. A resolução deve obedecer ESTA PRIORIDADE, sem inverter níveis: (1) informação explicitamente fornecida pelo usuário na mensagem atual e semanticamente compatível com o schema; (2) parâmetros já conhecidos/validados da transação; (3) respostas anteriores do assistente que apresentem relações locais entre parâmetros/atributos e que possam ter sido apoiadas por tool/evidence; (4) falas anteriores do usuário; (5) demais contexto conversacional.\n" + "4. Dentro de cada nível histórico, preserve a ordem temporal decrescente (mais novo -> mais antigo). Nunca escolha fonte de prioridade menor quando uma fonte de prioridade maior resolve o mesmo campo de forma coerente.\n" + "5. known_arguments contém o estado parcial já coletado/validado. Trate CADA parâmetro conhecido, independentemente de nome ou tipo, como possível ÂNCORA para resolver QUALQUER outro campo faltante no histórico. O significado e a compatibilidade de cada âncora vêm exclusivamente do parameter_schema. Use múltiplas âncoras simultaneamente quando disponíveis.\n" + "6. anchor_relation_candidates são apenas recortes do MESMO histórico que contêm representações de parâmetros conhecidos; eles servem para facilitar a correlação entre campos dentro desta única varredura. Não são uma segunda fonte, não têm prioridade própria e não autorizam inventar relações.\n" + "7. Quando uma âncora conhecida aparecer em um mesmo contexto local com uma ou mais informações semanticamente compatíveis com campos faltantes, use essa relação local para reconciliar TODOS os campos faltantes que puderem ser resolvidos de forma coerente na mesma resposta. Não pressuponha quais tipos de campo devem coexistir; siga apenas o schema e o contexto.\n" + "8. Texto anterior do assistente é CONTEXTO INTERPRETATIVO, não evidência autoritativa: pode fornecer relações locais entre quaisquer parâmetros/atributos definidos pelo schema, mas todo candidato continua sujeito à pre-validation/tool de negócio. Não descarte uma relação apenas por estar na fala do assistente. Não trate texto do contexto como uma nova afirmação do cliente.\n" + "9. Para cada campo escolha: resolved = texto/contexto determina novo valor; preserve = o valor conhecido continua coerente; clear = contexto mais novo rompe sua associação e não há substituto seguro; unresolved = não há candidato seguro.\n" + "10. Se uma fonte mais nova muda o contexto semântico que dava sentido a campos anteriores, REAVALIE os demais campos como conjunto. Não combine automaticamente informação antiga com contexto novo.\n" + "11. Se a nova menção é inválida no mundo real, NÃO volte silenciosamente ao valor antigo. Devolva o candidato textual mais recente como resolved e deixe a pre-validation autoritativa rejeitá-lo.\n" + "12. Uma expressão só pode preencher um campo se satisfizer semanticamente type/description desse campo. Não transfira um trecho para outro campo apenas por proximidade lexical.\n" + "13. Se o usuário corrige explicitamente informação anterior, a correção prevalece nos campos compatíveis e os demais devem ser reavaliados quanto à coerência.\n" + "14. unresolved é ÚLTIMO RECURSO. Antes de devolver unresolved para um campo faltante, você DEVE verificar todos os evidence_blocks na ordem de prioridade e recência e todas as anchor_relation_candidates. Se existir uma única interpretação semanticamente compatível com type/description do campo, resolva-a.\n" + "15. Se um parâmetro conhecido aparecer como anchor_match em um evidence_block e o MESMO bloco contiver uma única informação semanticamente compatível com um campo faltante segundo o schema, use a relação para resolved desse campo; não devolva unresolved apenas porque a informação foi apresentada pelo assistente.\n" + "16. Se não houver âncora útil, mas uma fonte histórica de prioridade 3 ou 4 contiver explicitamente uma informação que satisfaça semanticamente um campo faltante segundo o schema e não houver candidato concorrente de prioridade maior, resolva esse campo. Isso cobre referências posteriores em que o usuário omite um atributo porque ele já estava explícito no diálogo.\n" + "17. field_evidence_views reorganiza os MESMOS evidence_blocks ancorados por cada campo ainda faltante e coloca ao lado a semântica declarativa desse campo. Para cada campo faltante, examine obrigatoriamente essa visão antes de unresolved. Se um bloco ancorado contiver uma única informação compatível com field_spec e não houver concorrente de prioridade/recência maior, retorne resolved. Essa visão não é uma nova fonte nem uma segunda etapa.\n" + "18. Em ambiguidade REAL — dois ou mais candidatos semanticamente plausíveis sem desempate por prioridade, recência ou âncora — prefira clear/unresolved a inventar uma associação. Em caso de dúvida razoável sobre a correspondência ou o valor, prefira null/unresolved conforme o formato de saída.\n" + "19. Responda SOMENTE JSON válido no formato pedido, sem markdown e sem chaves extras.\n\n" f"transaction_tool: {tool_name}\n" f"transaction_description: {tool_description or ''}\n" f"parameter_names: {json.dumps(names, ensure_ascii=False)}\n" + f"known_parameters: {json.dumps(known, ensure_ascii=False, default=str)}\n" + f"missing_parameters: {json.dumps(missing_initial, ensure_ascii=False)}\n" f"pending_parameters: {json.dumps(names, ensure_ascii=False)}\n" f"parameter_schema: {json.dumps(field_spec, ensure_ascii=False, default=str)}\n" - f"known_arguments: {json.dumps(known, ensure_ascii=False, default=str)}\n" + f"anchor_relation_candidates: {json.dumps(anchor_relations, ensure_ascii=False, default=str)}\n" + f"evidence_blocks: {json.dumps(evidence_blocks, ensure_ascii=False, default=str)}\n" + f"field_evidence_views: {json.dumps(field_evidence_views, ensure_ascii=False, default=str)}\n" "conversation_sources_newest_to_oldest:\n" + f"priority_1_current_user_message: {message}\n" f"user_message: {message}\n" - f"conversational_context: {str(conversational_context or '').strip()}\n" + f"prioritized_conversational_context: {context_text}\n" + "RELEMBRE O FOCO PRINCIPAL: examine reconciliation_focus.missing_field_views antes de unresolved. " + "Um bloco ancorado único e semanticamente compatível deve produzir resolved, independentemente do papel assistant/user, respeitada a prioridade declarada.\n" f"Formato obrigatório: {json.dumps(output_shape, ensure_ascii=False)}" ) @@ -212,11 +471,49 @@ async def reconcile_transaction_parameters( else: decisions[name] = "unresolved" + # Structural candidates are intentionally NOT resolved values. They are only + # fallback material for a configured authoritative pre-validator. This lets + # the runtime carry a unique local evidence fragment (or, when no anchor + # exists, a single prior user utterance) to domain validation without letting + # the framework invent/canonicalize business entities. + structural_candidates: dict[str, Any] = {} + candidate_provenance: dict[str, str] = {} + # For a field that was already missing before reconciliation, ``clear`` has + # no distinct state effect: there is no prior value to invalidate. Treat it + # as unresolved *only for structural-candidate fallback* so a unique anchored + # history fragment can still be offered to the configured authoritative + # pre-validator. This does not promote the fragment to a resolved value and + # does not change ``clear_fields`` semantics for fields that were previously + # populated. + unresolved_missing = [ + name for name in missing_initial + if decisions.get(name) in {"unresolved", "clear"} and name not in values + ] + anchored = [b for b in evidence_blocks if b.get("anchor_matches") and str(b.get("text") or "").strip()] + prior_user = [ + b for b in evidence_blocks + if int(b.get("priority") or 99) == 4 and str(b.get("text") or "").strip() + ] + for name in unresolved_missing: + declared = str(field_spec.get(name, {}).get("type") or "string").strip().lower() + if declared not in {"string", "str", "text"}: + continue + if len(anchored) == 1: + structural_candidates[name] = str(anchored[0].get("text") or "").strip() + candidate_provenance[name] = "unique_anchored_evidence_block" + logger.info( - "transaction.parameter.reconciled tool=%s decisions=%s resolved=%s clear=%s provenance=%s", - tool_name, decisions, sorted(values), clear_fields, provenance, + "transaction.parameter.reconciled tool=%s decisions=%s resolved=%s clear=%s provenance=%s candidates=%s", + tool_name, decisions, sorted(values), clear_fields, provenance, sorted(structural_candidates), ) - return {"values": values, "decisions": decisions, "provenance": provenance, "clear_fields": clear_fields} + return { + "values": values, + "decisions": decisions, + "provenance": provenance, + "clear_fields": clear_fields, + "candidates": structural_candidates, + "candidate_provenance": candidate_provenance, + } async def extract_transaction_parameters( diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc index c4a831ab846dcc639c09bc4ef36ce5d6209f0d17..79f8d737b0b317327551fa26ca64e4b3a1856f3d 100644 GIT binary patch delta 34 ocmcb@c$ty=GcPX}0}ymt&!5O`%w43Pn^&TrqVF5*Ink>N0H$FI2><{9 delta 35 pcmcc2c!iPsGcPX}0}zCtoi>r%n5S4jH?KrLS>Lg^w0NRt7XY~>3ljhU diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc index ff084315766d52c634f0e2981dd85af6940d7f20..02876344e99ca41f43c7dbec64b33eeef506f7af 100644 GIT binary patch delta 37 rcmez3_1TO2GcPX}0}ymt&)>-Xote8pKR2&LKSkd+*mE-b%7 diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc index a1defb9b480adccdafaf03344669936056da25ec..1259a44692a3e4ca6eb9b814617dd34400a34d06 100644 GIT binary patch delta 34 ocmcc3c#DzyGcPX}0}ymt&!5O`%w4IUn^&TrqVF5*Iniq(0H~M>B>(^b delta 35 pcmcb`c$<;?GcPX}0}zCtoi>r%n5RlVH?KrLS>Lg^w0NTDL;$;S3oifw diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc index 08a3aab28b88e9f5b5ff1dc616f862ce9c9562b0..3d6ec917f7399b3c485976d1c6f38e48368114b2 100644 GIT binary patch delta 37 rcmcbhc3zG9GcPX}0}ymt&)>-1$IM-=pPN^rpQ7&@?74X<^J;zo)O-tq delta 38 scmX@Fc0rB%GcPX}0}zCtowkv?kC~@NKR2&LKUv?gxU_il66V$X0P1uMi~s-t diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc index 0bd6d734159fbba247704db7f802c0e311295e91..6a0c10f04039a5200219fa3d57daca5ff94e9a1b 100644 GIT binary patch delta 36 qcmaFQ_KuDFGcPX}0}ymt&)>*BpNYFdKR2&LKSkd+*mLqWrkw!Erwe=l delta 37 rcmaFI_MVOVGcPX}0}zCtowkvCJ`+!+er{fgezLw}acS}7txP)s-k=PI diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc index 3a3c26903df9b54eb81dcef9550aca7aebbcf564..9131a481173ac6d1078228c780bd54477e964b52 100644 GIT binary patch delta 37 rcmeC+Zs+Fy%*)Hg00f=Z^EYz4F>{yd=jN5@r|A0zdv1r8&0L9q~qW}N^ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc index 4ca4c0b780d0bc13148d2b6d9808caa20a77c3e7..7fdc6116785a3185586e74c52dc7f211f231f694 100644 GIT binary patch delta 37 rcmccaa?OSNGcPX}0}ymt&)>+sj+whoKR2&LKSkd+*mLty=1Fn@;gt*p delta 38 scmccSa@~ddGcPX}0}zCtowkvC9Wzh8er{fgezLw}acS}9Bg~WJ0QdS04*&oF diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc index 730f0f6463ee2725e0eedc57db453e0d6fe815b0..106dbd5d7a79e9665fd84a22498361b7b42a0449 100644 GIT binary patch delta 37 rcmexn{neWLGcPX}0}ymt&)>-XiHW;RKR2&LKSkd+*mE-{^IItZ>JtnH delta 38 scmexr{mq*DGcPX}0}zCtowkwt6BAFler{fgezLw}acS{p4(7K~008a{5dZ)H diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc index 93500be293bfcaf212b8ca9ceefdb97ab11b1514..becb36666b6414b01bdbf89acc75c815b66c56cf 100644 GIT binary patch delta 37 rcmdlbv{Q)tGcPX}0}ymt&)>*>ijliQKR2&LKSkd+*mLtk#z=Mm%`yu` delta 38 scmdlfv`dKlGcPX}0}zCtowkwt6eCZier{fgezLw}acS}92aJ*I0OJh|PXGV_ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc index eee267b7eced3d6e1a7c6f88275f897a142d80ea..6c1facfa18901d1bd63de977141fa6c2ea94fcc8 100644 GIT binary patch delta 37 rcmbO&I!l!MGcPX}0}ymt&)>*>laaehKR2&LKSkd+*mLs-#?>4E$7&0J delta 38 scmbOwI$MR^er{fgezLw}acS}9_l&DK0Nox8iU0rr diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc index 011d24d9eab9359fb697f31d1e3f707c0dd5a18f..62d17ee12ddacccd8205da39386a741182a75859 100644 GIT binary patch delta 39 tcmdlvi)qI!ChpI?yj%=G&}lt?BX=w_ce#FUUWtB+zHhMS<`U+kodE7j3~m4b delta 40 ucmdlni)rU9ChpI?yj%=G5Po*rM($W_!o+6{OB diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc index b80ecdc29c862ea776cb9670e0d9b3f1adcf0b23..b3aa8244ed2f9886ab6b9a8387a9440b864c4fd6 100644 GIT binary patch delta 37 rcmZ3_zmA{#GcPX}0}ymt&)>*Bn~}R#KR2&LKSkd+*mLtH#=oop#-$5# delta 38 scmZ3-zn-7_GcPX}0}zCtowkvCHX~1+er{fgezLw}acS}9jf{U;0o*zad;kCd diff --git a/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index 1674055131f4d291f7139130c1a0968ea9dd0c60..9f26c74c3b6136e657932816f2b7d50d75858903 100644 GIT binary patch delta 62 zcmbQvwv3JYGcPX}0}uq9owkwNl}Xi0KR2&LKP9mwQNJj)JijPAJ}0pxwYWq-F+DY} QBtAJmuOzW}avIY{09vONUH||9 delta 47 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 OY|zVp+X;--`TqsKM7gy9 delta 610 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

~~NccJni= z2f4Gv^hEI&AI4MNO(qzP{Tk9$9DoxJz2l+b+boNyhQ$Cj@BZbZH(d98ZErR0DK+)n zaQ&>W+_DJ_z#t(Bdqk+?!~F%^tK0BS=1CV~C|G}OEAKyLpED>S=KR2%3QraV z#weeJ8biE>UkIil3bXE#?RrN?A7b%~gywZiS%$&7of5{Efm+ zw0tIaLJvhpM~}-D_caK|pHbhu8NcmJ3wzj~Q1NF8Y$YHO=mOB)GZ|IMVH*;2tFTW- zkr({EG!jO+Mq#khI>RhDmzd{*i!N=j4Y}da!uSQ$*KPxF8sw|_THvL?lCQn!Yri@4 z?c}RN-%<-52k!X}A`YX_)>{hpzSY`(GyIL-ulK&{FLmr)>Ns?Jy3{&!W2_w7dh1{* zwC8*I+t2*orPnUKey}uns?>M7aBiy9H+3Ho7+rhrgEMdIxxb~q@MN^`%%_Wc76J{8%dNtu&9An9f3WcE+%o8kc*3zF2uH^V){i?-AuuE)+cae(N#*}@6Rh(0s;>0{aTz7%zZ<=z$fYEkg zf0Y5FQio{Nx(g;Gn|nwJFgxoXj0nCwyaS@!{!;EnPgaO?xzt z#Iy~h@=nv;8vF><$#&L0Or5fBP^e#mYk3!+&E4j%3)7A}EvO%jUhm=b`tcuidd&^a z&S-*5c0JK|Nj$ooRLl_a4V`=GotIEM*4gD`yXm# z(NiTLl8njiG3+!_x@1e6WU+x=UgO3Yiuffn-U~U{0>`Ne8D|!Q>MbMZM7Ds( z8jcIZL?_spqSfPSHiI|=#InPBQ|Soa6E%zQ{S1N(M)NL)*bzL93 z-*TwD`N?wYo1W&Yho zd2pwZy;qekaeN!iPN2L@ZFU!fJ8oSm2KU|`Dh7`fTu&E9o;F}H`1F#?{JV?t;1MHx zuPR;Q_%@oIK>51X;p{=U!tvkFb5IN(Uvim$cU{K|6v05*dsXQYVcS@C0>!*(?mZg1 zPz*k^EI8brvabQt1=aD>A_6i|0D$C*;HdGuNBLC(|KxfgyyHfq5PXK0;<-_}TwTf= zAY5K*w8YUXz^?cnRpjN%9-;NIU%d45mu?LdTXx)1iY>dZN6SH|#>HTF!F6gW*jEhp zAyMc*Nf7D&lk}~F>(raUQ%gWVA(eL<=@Q4Or29`+(iXl^0{8>hDSSoo^0t?T3$4Ay zV5HzWa2uTx2p(8+nSXa(2MWQ6k-b-yE)k-QWha30c8gFq3a?rdPRZv-{LumNji4Bf zxZbEavmKd@)!u)Fbn?;FUasgm`VA8@DZTs)D5$mg28tp8x`~utU^O7u_y~o! zi7UL#YvN^Xg;#FHFiGw8^?f`A_hv-xtxU5|vo1bk7cWF+&eQb{9;JsDv|oXZcR zy=|bl*JupuVj1pI`y~Qz68HfD9U%HaU&bcXj2Y0NhiqSg;aB`X zoyAaz{)*auivTHCh7aNoD8(I`e~Z*4`+t=GdjR;Nxf%K&sNgbzG=a6Uc-Y7OkxIE2 z;D?Ic8re-Ox!RDaKgnfkb2;J&%gU0s9%@hSaw;2NBWojMSiy%r{QEiE%Wr%VgmueZ ztNy%{{eSfI7r3?a(38lS@Qz0aNgr0gesx!&b1%u*?VCSPygpxSzp$}*{e4#KjD0w{ zdS5ZPzc6^B7(^`V!Lu^>;$;KL{uOS~&Ker%4R12mo*Qbcvp~ z$HjW+R67NS@5`HiYV(rVTojvE98PgUTy+YLmSvtq9KrhMa^<-!zE{&C237=s_xfwZ ut`z~`oz7Nq+)-!?zavm$dGER=c8Leg9*|nb=gn(-=r$j(IFRC=xPJ%uhRS*X literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313.pyc b/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4636b04092749a915fa40ea03daa40d7cb7638ab GIT binary patch literal 9600 zcmd5iZEPIJb-Qu64Yn zXZIwNNead;61z@nJE##QX=@>NVas&`1qjdp{i8tr(f-@Z8Q@~804ma=(65rF4I}>3 zzBhY&M^d!i{Ob_+c6R2?o7tK7J>00M2r*FZfBv^Gc6Kn#AMnL3zCvZ=1j{h*Fd`$e z6AZzpXM#QJAs)eL^Aaz#c_+BDKH{To+=TyZfCL1liP_DFz7|IGAM!ZVNKg#4Fr-Wj z0t{XCCK}x7O3y@7sqWUqvKHo0;e}NE3FAVW##Pew@UWip;(EfgtpeIAi)~f3Eevf{ zR|AK<&a|ZZPnflO)2uafY^3H95{cho?XajWET)yXyrAfda#D$~cGXD6P{<-tbtR!? zp$cu)u{kAUB(fPpT~N-cFRPhk;p^!GPK1_?7I?hFTxAF=GQ=aYa26ieSx)p4-y9e5 z*&)fHIXQXm5uSCja1eBO5a1i|+n5D{dl&&oU2#b>)2zrM4_qmZa5t!%4rUk`#=BUe z2XW6=Tyfc^*mI~rG2@-~Bm#w@n;xRq#SponXBj5tXMky@y~hAYZie}Bm9!)%v$}82 z6BrEt{85ifrHc^*)80hIRy=%j8HiGD`#(?)ie+L*mjtPp?nKTzyIQiEg97Z zc2UWs)XZF|S0B3Lu>*Q`=0zoG*uEQbS|fHq(O=3cNyQGtPQ~J<&&Tc788w~8fokT4 zrY032x|o(TGEQYzxScXG-rO%o|9!dcm;Ze-i)Ss*$eEOu(F7cD{~wa7p$RFuu+sY< z+<#9KWFsr5f!%^BC8_F2(62h5=c5Ge5Pa^eno=^bB0o`N9Z-DY*!Y<#yBc>w)}*}nUl%3gNiTCdicoh~jL%kuxViq+bID=&p3D_Z6L`FA| zn$4wD*cPvD5ZgPa7&iAJojjFYSk!G_TDz%`JDknwN_y4~f>`P>v2OdX-;~K5JmGj2 z77d+)bqG-GmHvf{(VvnHxt}OEHF8}_%Z8#G{dB#O^D%{&Sv)_Aqb1dT#L_90HlH%? zstTJb74~@n4nTq(mte)&q>&|Z8uulo!kkKUamfzIc!JjzyQU=E(Lx&!%hdk~z$0db zdC+_K?#uT)zxvKUPp$Tz&GnvrtLfJ6x7%*1zpL*4-X@4}IA6o4Q>4iG1xzv;3sR*WL2JTmIAX zpI7GjeFY4wZLigG#A@!g+IL$mr`LIZN96|NuM4j+{)+Htl}uILo6CQ(Y&CUQ4H2te zuv*%!hEYIk4FOse4`^llA9c_G%qyq*PaRD0#qZtECpL=S2JbB9PNfz}Yv>-m3g$)D1JIpkg#y;5i3_HyxHse7r7SSVmMQ#ub=9Pjm z0o_y7lUIs5ahi2?jOY{nVqnmFrOcsF(p_S3(9^|4%HqrA1dIiUb96~wNX!2U^Mup0|a^i>k4#)l`y<0GO71ik6>0=fk^VIvjEK)ZsR zNKm~I0Rb|1#A64Ig+(wi#=IRUFq)2zfFMwju>+FBY~%zs!cU(9u)7yS7bfFm4!YF zF%^w(Os-c1%Kd9hz~`qjxJCRbVJ0em2cd%EhXM$Ho_-bu%@LjfYzczsfe9aA;f;^_ z#PVnmgr_2I*YC*2q#6i*+3Ilmju?)yLY6H`l3i2eJ>cgVFyBd;R3V|tDS2T=Bbsnd z0h6A#YY}l9*V~-2v3LMCozo}5<}W=m9O%$kN1uhG?W)!tyfnzG90zS^7kZI^S4L^MpzzXxQ zzRhagZ8h{+?T4(6eYaGrrr&CM;$d^2)w0Ljd&q1)jGcRqTAke=9=?0j>^W`x8yD z9&#MJFmBtT5RRfEm%uYqz%i5bd3Dy1QW{v>jAoQ3a(Q6WD##TebqUGM06@3pKkD0V zJ)5uJw_4wutMC2zntAM*eBY&f{j;m#XII87zV^+Nub(vAkLCGeCU?x_k6Best5u!3 zs?OD_{kIdV;oe-h*X$cz4Ugr*V%!q4UPCHQxFKt7%H)pCCFubeGb4D4meCXU?(!@zDj9DM%CeHkj>}+JP^X4 zj_cU@{N;S#Oujz38cyzf{(S8!-=;q|MXrT;ktIISNBnbM2r??93+N#&C8*o=NP&@$@ewMP zjiZ!iDYO>Zk|XE_rC^~Tau>aWo@plSu7;UoB3zsdZ>NkdV=Yp4EZ}C!U<0YU8TC** z$iXuJKL};}Js8Xma$o_=<91omYN2rx9%!Y+tf zG1_&#Ih)78OL9z^9VE(2Sryz9BAgQopf?f#D&{=UhqIk@f#HdLa{gQsPP67q=jHP`-m4 z2kM z(t%EF`2xNoK=q)P1Nc7ksi)i*`l8M6b7r93N$4pSWD_jkoZoAdrZa`FO(hhRT}UNi zvhTYvd`q$qL&-OgrUk&UYFm?cG(qRi3^;BnxgL(akwF<06Lfnqye*{&6P!D$^n?^0 z1hbH&104jM=OomyRuT0K6kV$U77};gOo@TC8jN!tF=v!miBD?z!f}G$6Tyupm;)pj zEo3_ZoH;*yUbv7Lj}MPuh)oKoV?tsC0;%Eg$?@3zAKZWSyzQS=m2^tCL)qe<+6>4v zIy*G4mIY5;*Rlk%NaItSF|Ml%U$d-NC_buD!c($kmmp$M9E)2wJ$!-LD5Wr?J z#YB?B9#24%-SHKN=q7dk49EQ?0JM%)b+ePi$yImnl(n(K2k*}2!^f?r&i9}CVCWY^ zR$ZGlaQvQOUb>vy^PCkudhd`qbtTt%6_i^?!6^PX=2^69+q-_oJ3)b+Xdjyu?+!o zWHGtI`#K#V!JeIYUx1z+EwL9e+&+;5E9{H-?P_V*O^y}v<&S=dG&gfH+Edgd8*xgr zWG`Lo3pTfiUvm66_2Ot+`XSm(bMD(5LtcS8_(?Co!{%x5R)~!0hYw-14-8LQCsh03 zG>$r>io!a4M1!6&riO_eG+VXP}`HQ?R|~=VbH2?1OwoxkYsU1FeV7U zSK+s@8zw7AI+K8g?PaI1XPPa2|8!B#o+{P^n%Kv%Mt%m;SZHf#f#iNMS>?8&Zg3@a zdze$wDj81Xra79F2iq{@G?=#MK<9)|zd+pYg}C2K zuLW4KLaYQU777v*qw7!6T3wHk`+nN@VOhTA;A+d!yEFNQ!PiEuaL4T<`Ebw2*}Iqj@!GGh-8+&$G?DM0G%rl& z`==j5Qn<6{AsFY~y${<4%oB0*^7FZa(wf(Epk+Pa?`T*LgqmvBm{4`iS_9MA`eF0G z95SyitbvMQ&v@1u=Hw{Csxc2Vm5q7agmT5HFbjK&n@CVD$zK8ZTC4sG#M;TKzl|LT zP}S&YXV9v9eNCT-s(fP}N3~)97S&b_4OB316$SHgRg~GB%%bV60S4F@Oel5jfw`SG zG3Yq-z}z0n*7yv=uS%?r`hm$E%o=E^jq;G2W_A|$enzYt^gNc)@PH`QBgcphTXPs< zBW-P>FhF7R*I)!GM%gxu7TVhC&=A`O*e(6cPHu#_i(7;P?B;)qbEXsGNI@lAZ?!x^$f-FG(1nw9NjVvpIB4o51kd2cIH5!(Y zQ?MK|f`CTF-@;lI0z7Q0K8S*~Sp;(kFmfU)0`wHJ$Yb*mBxq!5=)e%hQt%>Yj3Hr6 z%ie%od5STQ%)5Vb>hh+EZ!E1Dhl0v$T8^DqsMD2jQw6VBA(LV2v5%8 zx1%Mt%UtE7J1GV2^-H+p`%JaxHLw zmz>uKpeZ+;TTf{Q;?L^@7?F8|K0vk!z*ikC^9MwE_lM@9& z79WF9X;vdN&z3P9Gl7>l#L#c5#ykXO0pk2GfnyH-2KT745PZQn47j&CruGEQ=4}5O z`Vo>!FKV3&NlDA3m*{mRIK(B2knzzna6_Ahyt7Vz4fFjS{B#vQou+syJMxvCE29tV zk6NuKtcD)5{t2tP3o^b|&3>zO?`>kX^w3mr_pR$R3EX)ba=;PH0T)8Hhh4qq)6e8u zFIl^L%wvB{dVNJlXswOui?QZ~OJG0D^HQ$;h4ll?75uT+jx!Nc1j@+v;Yo9c0Pp?*-UODrSuQIFm=JA zyMsCYX)|^)&tEjTiza_@TQ_8&j{PQ^=TDp5X_G%qdudi`@Yx)H*$P&}bYRM!XZ;(D z2NTyG9|EZPnTB0Ix%T$8+Xr&>dvD9R`tFsu#e)gW@m;HYe~#}r2hOhY6FIn=F+0d8cqj$T0#~(Ji!#V!&dOcG)0!go0*z#9mWy2x%w>&!>;+ss=!J~M;u0RX9dT~4ix<~XltW;_C@8+dKdHDkKd3_Oy^rBLd=_=uU9R&3 zu5-ke zBLHh&mSz8q;eXF`{(-6ZPv*?;nc*+|wJg8R0Qj`Gll6Z(##XWZwS9~y_=DE(wXU*t zIks-y<7LO$4KL%VU!zrsfVTIGrGAn9qP(6Btup|=7$|2u*BJnxwKuS19V_udY4>& literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_transaction_parameter_descriptions.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_parameter_descriptions.cpython-313-pytest-9.0.2.pyc index 95126f52d6f7cf6a564dc8bcc8fb3185af7b21cc..44c4613f2ec6008454a29600f0077fc4a43dd370 100644 GIT binary patch delta 52 zcmX>cxG#|VGcPX}0}uq9owkv?h)F6=KR2&LKP9mwQNJiPIX^EsGbc4ZEi*4MXY(8; Gbu|FU;}OyT delta 57 zcmdlNcsP*zGcPX}0}u#bnY59+h)F(KKR2&LKP9mwQ9m&~HLoN-IX|x?u{b_0v!Xn| LD0}mCCUrFcBUlsP diff --git a/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc index 0d7b692ed8f25b2a5d2aabe7ab2cd95ea3872861..d728688f7a303d95282720daf298ec673db9b0fa 100644 GIT binary patch delta 7921 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 delta 239 zcmex%nEB=hCce+Syj%=GAbe#~7FWVVJ_)8-OdB<3F|njGX^L&mV;19(Pu0)OE74C$ zEJ@T)Oi#@#iBHbYD@iPlPs^+*&o9c}ypnrX7o+B8muVjv8O=AF&DhG%WX3po-ZaI{ zd@KAJ8ErSmt<>U@abRR%C}(0|DCPj#*2eIaiIFprvB(?9DRQ2?WOEXu=oYM5_P* diff --git a/tests/__pycache__/test_transaction_state_regression_matrix.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_state_regression_matrix.cpython-313-pytest-9.0.2.pyc index d3d04a813ea4b3166b7a2e7b3483533d6375c731..61bf31ba1285688b761b09bbfd24c63693493f1b 100644 GIT binary patch delta 54 zcmcaOjrr;{X710tyj%=G5O8+dMs7DAsd)X|yb}GC#F9k)qSWO4yyVQB)cCZ_yu_T% J={&FY0RZig6P5q~ delta 50 zcmcaQjrrm diff --git a/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc index 66fcb9d3044eecd640d06327c80b75d83c71b6b9..5be251241c59e0ec9c84962dbd9583842db43d78 100644 GIT binary patch delta 65 zcmex;hq3W4Bll-sUM>b82sk@!Bex5)YL0$xUWtB6Vo9QYQEGX9QFeSzVo7RoiGE^w TYFb85WX^LBex5)e42i4UWtB6Vo9QYVtQ&`NqlmCUP)qcd|GBj Nd45s$<`QP!Yyex?6w&|y diff --git a/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transactional_tool_flow.cpython-313-pytest-9.0.2.pyc index 92ba4080fdda1cfd9460905817c1c9c12a9a751e..155772f3c2d067659972f729f1b7c99397026b9f 100644 GIT binary patch delta 6972 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 diff --git a/tests/test_contextual_reentry_transaction_parameters.py b/tests/test_contextual_reentry_transaction_parameters.py index 46a7b3bd..bd2877f6 100644 --- a/tests/test_contextual_reentry_transaction_parameters.py +++ b/tests/test_contextual_reentry_transaction_parameters.py @@ -3,7 +3,7 @@ from __future__ import annotations import json import pytest -from agent_framework.runtime.transaction_parameters import extract_transaction_parameters +from agent_framework.runtime.transaction_parameters import extract_transaction_parameters, reconcile_transaction_parameters class _ContextAwareLLM: @@ -42,3 +42,370 @@ async def test_contextual_reentry_separates_current_claim_from_prior_context_for assert "Cobrança Tamboro Mensal" in llm.prompt assert "user_message: é a de quatorze e noventa e nove" in llm.prompt assert "Não trate texto do contexto como uma nova afirmação do cliente" in llm.prompt + +class _TwoPassContextLLM: + def __init__(self): + self.calls = [] + + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] + self.calls.append(prompt) + # First pass: current utterance provides only the amount. + if "user_message: é a de vinte e cinco e cinquenta" in prompt: + return json.dumps({"subject": None, "valor": 25.50}, ensure_ascii=False) + # Second bounded pass: previous USER utterance provides the missing entity. + if "user_message: não reconheço esse Tamboro Mensal na minha fatura" in prompt: + return json.dumps({"subject": "Tamboro Mensal"}, ensure_ascii=False) + return "{}" + + +@pytest.mark.asyncio +async def test_contextual_reentry_second_pass_recovers_only_missing_candidate_from_prior_user_turn(): + from agent_framework.runtime.agent_runtime import AgentRuntimeMixin + + class Runtime(AgentRuntimeMixin): + pass + + runtime = Runtime() + runtime.llm = _TwoPassContextLLM() + state = { + "route_decision": { + "metadata": { + "contextual_reentry": True, + "original_input": "é a de vinte e cinco e cinquenta", + "relevant_conversation_context": ( + "user: não reconheço esse Tamboro Mensal na minha fatura\n" + "assistant: identifiquei duas cobranças de Tamboro Mensal" + ), + } + }, + "sanitized_input": "é a de vinte e cinco e cinquenta", + } + out = await runtime._extract_transaction_parameters( + state=state, + tool_name="contestar_cobranca", + missing_parameters=["subject", "valor"], + known_arguments={}, + ) + assert float(out["valor"]) == 25.5 + assert out["subject"] == "Tamboro Mensal" + assert len(runtime.llm.calls) >= 2 + +class _CoherentTemporalLLM: + """Simulates the structured decisions expected from temporal reconciliation.""" + def __init__(self): + self.prompts = [] + + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] + self.prompts.append(prompt) + low = prompt.lower() + # Product A + value X established. + if "user_message: na verdade é tim fashion mensal" in low: + return json.dumps({"fields": { + "subject": {"decision": "resolved", "value": "TIM Fashion Mensal", "source": "current"}, + # Previous 14.99 was tied to the former product and must not be transplanted. + "valor": {"decision": "clear", "value": None, "source": "history:1"}, + }}, ensure_ascii=False) + if "user_message: na verdade é produto inexistente" in low: + return json.dumps({"fields": { + # Keep the newest candidate so authoritative validation can reject it. + "subject": {"decision": "resolved", "value": "Produto Inexistente", "source": "current"}, + "valor": {"decision": "clear", "value": None, "source": "history:1"}, + }}, ensure_ascii=False) + if "user_message: desculpa, era tamboro mensal mesmo" in low: + return json.dumps({"fields": { + "subject": {"decision": "resolved", "value": "Tamboro Mensal", "source": "current"}, + # Full temporal text makes the old amount coherent again with the restored product. + "valor": {"decision": "resolved", "value": 14.99, "source": "history:2"}, + }}, ensure_ascii=False) + return json.dumps({"fields": { + "subject": {"decision": "preserve", "value": None, "source": "state"}, + "valor": {"decision": "preserve", "value": None, "source": "state"}, + }}, ensure_ascii=False) + + +@pytest.mark.asyncio +async def test_temporal_reconciliation_product_change_rechecks_old_value_as_part_of_coherent_set(): + from agent_framework.runtime.transaction_parameters import reconcile_transaction_parameters + + llm = _CoherentTemporalLLM() + out = await reconcile_transaction_parameters( + llm, + text="na verdade é TIM Fashion Mensal", + conversational_context=( + "history:1: user: é R$ 14,99\n" + "history:2: user: quero contestar Tamboro Mensal" + ), + tool_name="contestar_cobranca", + parameter_names=["subject", "valor"], + known_arguments={"subject": "Tamboro Mensal", "valor": 14.99}, + parameter_schema={ + "subject": {"type": "string", "description": "Nome do serviço, produto, item ou cobrança objeto da contestação."}, + "valor": {"type": "number", "description": "Valor monetário associado ao item objeto da contestação."}, + }, + tool_description="Contesta uma cobrança após validação.", + ) + assert out["values"] == {"subject": "TIM Fashion Mensal"} + assert out["clear_fields"] == ["valor"] + assert out["provenance"]["subject"] == "current" + + +@pytest.mark.asyncio +async def test_temporal_reconciliation_invalid_new_product_does_not_erase_history_or_fall_back_silently(): + from agent_framework.runtime.transaction_parameters import reconcile_transaction_parameters + + llm = _CoherentTemporalLLM() + history = ( + "history:1: user: é R$ 14,99\n" + "history:2: user: quero contestar Tamboro Mensal" + ) + out = await reconcile_transaction_parameters( + llm, + text="na verdade é Produto Inexistente", + conversational_context=history, + tool_name="contestar_cobranca", + parameter_names=["subject", "valor"], + known_arguments={"subject": "Tamboro Mensal", "valor": 14.99}, + parameter_schema={ + "subject": {"type": "string", "description": "Nome do serviço, produto, item ou cobrança objeto da contestação."}, + "valor": {"type": "number", "description": "Valor monetário associado ao item objeto da contestação."}, + }, + tool_description="Contesta uma cobrança após validação.", + ) + # The newest candidate stays visible for pre-validation; the old product is not silently restored. + assert out["values"] == {"subject": "Produto Inexistente"} + assert "valor" in out["clear_fields"] + assert "Tamboro Mensal" in llm.prompts[-1] # history remains available to future reconciliation. + + +@pytest.mark.asyncio +async def test_temporal_reconciliation_can_restore_coherent_old_value_when_product_is_explicitly_restored(): + from agent_framework.runtime.transaction_parameters import reconcile_transaction_parameters + + llm = _CoherentTemporalLLM() + out = await reconcile_transaction_parameters( + llm, + text="desculpa, era Tamboro Mensal mesmo", + conversational_context=( + "history:1: user: na verdade é Produto Inexistente\n" + "history:2: user: é R$ 14,99\n" + "history:3: user: quero contestar Tamboro Mensal" + ), + tool_name="contestar_cobranca", + parameter_names=["subject", "valor"], + known_arguments={}, + parameter_schema={ + "subject": {"type": "string", "description": "Nome do serviço, produto, item ou cobrança objeto da contestação."}, + "valor": {"type": "number", "description": "Valor monetário associado ao item objeto da contestação."}, + }, + tool_description="Contesta uma cobrança após validação.", + ) + assert out["values"] == {"subject": "Tamboro Mensal", "valor": 14.99} + assert out["provenance"] == {"subject": "current", "valor": "history:2"} + + +def test_temporal_context_priority_places_assistant_before_older_user_history(): + from agent_framework.runtime.agent_runtime import AgentRuntimeMixin + + context = ( + "user: tem uma cobrança aqui que eu não reconheço\n" + "assistant: Cobrança Tamboro Mensal no valor de R$ 14,99; TIM Fashion Mensal no valor de R$ 10,00." + ) + prioritized = AgentRuntimeMixin._transaction_context_priority_view(context) + + assert "priority_3_previous_assistant_tool_or_evidence_context:" in prioritized + assert "priority_4_previous_user_utterances:" in prioritized + assert prioritized.index("assistant: Cobrança Tamboro Mensal") < prioritized.index("user: tem uma cobrança") + + +class _AssistantRelationshipReconcilerLLM: + def __init__(self): + self.prompt = "" + + async def ainvoke(self, messages, **kwargs): + self.prompt = messages[-1]["content"] + # Simula o comportamento desejado: a fala atual dá o valor, enquanto a + # relação item->valor está numa resposta anterior do assistant. + assert "user_message: é a de quatorze e noventa e nove" in self.prompt + assert "priority_3_previous_assistant_tool_or_evidence_context:" in self.prompt + assert "assistant: Cobrança Tamboro Mensal no valor de R$ 14,99" in self.prompt + assert "anchor_relation_candidates:" in self.prompt + assert "anchor[valor=14.99]" in self.prompt + return json.dumps({"fields": { + "subject": {"decision": "resolved", "value": "Tamboro Mensal", "source": "history:1"}, + "valor": {"decision": "resolved", "value": 14.99, "source": "current"}, + }}, ensure_ascii=False) + + +@pytest.mark.asyncio +async def test_temporal_reconciler_can_use_grounded_assistant_relationship_after_current_only_extraction_is_incomplete(): + from agent_framework.runtime.agent_runtime import AgentRuntimeMixin + + class Runtime(AgentRuntimeMixin): + pass + + runtime = Runtime() + runtime.llm = _AssistantRelationshipReconcilerLLM() + runtime.tool_router = type("TR", (), { + "registry": type("REG", (), { + "get_tool": staticmethod(lambda name: type("CFG", (), { + "requires": ["subject", "valor"], + "args_schema": { + "subject": {"type": "string", "description": "Nome do item ou cobrança a contestar."}, + "valor": {"type": "number", "description": "Valor monetário do item a contestar."}, + }, + "description": "Contesta uma cobrança após validação.", + })()) + })(), + "resolve_execution_policy": staticmethod(lambda name, arguments=None: { + "operation_type": "transactional", + "requires": ["subject", "valor"], + }), + })() + + state = { + "sanitized_input": "é a de quatorze e noventa e nove", + "user_text": "é a de quatorze e noventa e nove", + "route_decision": {"metadata": { + "contextual_reentry": True, + "original_input": "é a de quatorze e noventa e nove", + "relevant_conversation_context": ( + "user: tem uma cobrança aqui que eu não reconheço\n" + "assistant: Cobrança Tamboro Mensal no valor de R$ 14,99; TIM Fashion Mensal no valor de R$ 10,00." + ), + }}, + } + + # O happy path corrente só consegue o valor; o subject fica faltando. + class _CurrentOnlyLLM: + async def ainvoke(self, messages, **kwargs): + return json.dumps({"valor": 14.99}, ensure_ascii=False) + + original_llm = runtime.llm + runtime.llm = _CurrentOnlyLLM() + current = await runtime._extract_transaction_parameters_current_only( + state, + tool_name="contestar_cobranca", + missing_parameters=["subject", "valor"], + known_arguments={}, + ) + assert current == {"valor": 14.99} + + runtime.llm = original_llm + reconciled = await runtime._reconcile_transaction_parameters( + state, + tool_name="contestar_cobranca", + parameter_names=["subject", "valor"], + known_arguments=current, + ) + assert reconciled["values"]["subject"] == "Tamboro Mensal" + assert float(reconciled["values"]["valor"]) == 14.99 + assert "(3) respostas anteriores do assistente" in runtime.llm.prompt + +class _UnifiedAnchorScanLLM: + def __init__(self): + self.calls = [] + + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] + self.calls.append(prompt) + assert "UMA ÚNICA VARREDURA TEMPORAL" in prompt + assert "known_parameters: {\"valor\": 14.99}" in prompt + assert "missing_parameters: [\"subject\", \"data\"]" in prompt + assert "anchor_relation_candidates:" in prompt + assert "anchor[valor=14.99]" in prompt + assert "Tamboro Mensal no valor de R$ 14,99" in prompt + return json.dumps({"fields": { + "subject": {"decision": "resolved", "value": "Tamboro Mensal", "source": "history:1"}, + "valor": {"decision": "preserve", "value": None, "source": "state"}, + "data": {"decision": "resolved", "value": "01/11/2025", "source": "history:1"}, + }}, ensure_ascii=False) + + +@pytest.mark.asyncio +async def test_temporal_reconciler_single_scan_uses_all_known_fields_as_anchors_and_resolves_all_missing_fields(): + from agent_framework.runtime.transaction_parameters import reconcile_transaction_parameters + + llm = _UnifiedAnchorScanLLM() + result = await reconcile_transaction_parameters( + llm, + text="é a de quatorze e noventa e nove", + tool_name="contestar_cobranca", + parameter_names=["subject", "valor", "data"], + known_arguments={"valor": 14.99}, + parameter_schema={ + "subject": {"type": "string", "description": "Nome do item ou cobrança a contestar."}, + "valor": {"type": "number", "description": "Valor monetário do item a contestar."}, + "data": {"type": "string", "description": "Data da cobrança selecionada."}, + }, + tool_description="Contesta uma cobrança após validação.", + conversational_context=( + "priority_3_previous_assistant_tool_or_evidence_context:\n" + "history:1: assistant: Cobrança Tamboro Mensal no valor de R$ 14,99 no dia 01/11/2025 * " + "TIM Fashion Mensal no valor de R$ 10,00 no dia 01/11/2025.\n" + "priority_4_previous_user_utterances:\n" + "history:2: user: tem uma cobrança aqui que eu não reconheço" + ), + ) + + assert len(llm.calls) == 1 + assert result["values"]["subject"] == "Tamboro Mensal" + assert float(result["values"]["valor"]) == 14.99 + assert result["values"]["data"] == "01/11/2025" + assert result["provenance"]["subject"] == "history:1" + + +class _AssistantEvidenceSegmentationLLM: + def __init__(self): + self.calls = 0 + + async def ainvoke(self, messages, **kwargs): + import json + self.calls += 1 + prompt = messages[-1]["content"] + evidence = json.loads(prompt.split("evidence_blocks: ", 1)[1].split("\n", 1)[0]) + anchored = [ + block for block in evidence + if block.get("role") == "assistant" + and any(match.get("field") == "valor" and float(match.get("value")) == 14.99 for match in block.get("anchor_matches", [])) + ] + assert len(anchored) == 1 + assert "Tamboro Mensal" in anchored[0]["text"] + assert "TIM Fashion" not in anchored[0]["text"] + assert "Neymar" not in anchored[0]["text"] + return { + "content": json.dumps({ + "fields": { + "subject": {"decision": "resolved", "value": "Tamboro Mensal", "source": "history:1"}, + "valor": {"decision": "preserve", "value": 14.99, "source": "state"}, + } + }, ensure_ascii=False) + } + + +@pytest.mark.asyncio +async def test_reconciler_segments_assistant_list_so_known_anchor_resolves_missing_field(): + llm = _AssistantEvidenceSegmentationLLM() + result = await reconcile_transaction_parameters( + llm, + text="é a de quatorze e noventa e nove", + tool_name="contestar_cobranca", + parameter_names=["subject", "valor"], + known_arguments={"valor": 14.99}, + parameter_schema={ + "subject": {"type": "string", "description": "Nome do item ou cobrança a contestar."}, + "valor": {"type": "number", "description": "Valor monetário do item a contestar."}, + }, + conversational_context=( + "priority_3_previous_assistant_tool_or_evidence_context:\n" + "history:1: assistant: Houve as cobranças: * Cobrança Tamboro Mensal no valor de R$ 14,99 no dia 01/11/25. " + "* Cobrança TIM Fashion Mensal no valor de R$ 10,00 no dia 01/11/25. " + "* Cobrança Neymar Jr no valor de R$ 12,00 no dia 01/11/25.\n" + "priority_4_previous_user_utterances:\n" + "history:2: user: tem uma cobrança aqui que eu não reconheço" + ), + ) + assert llm.calls == 1 + assert result["values"]["subject"] == "Tamboro Mensal" + assert float(result["values"]["valor"]) == 14.99 diff --git a/tests/test_reconciliation_state_rules_8_16.py b/tests/test_reconciliation_state_rules_8_16.py new file mode 100644 index 00000000..a1832f2d --- /dev/null +++ b/tests/test_reconciliation_state_rules_8_16.py @@ -0,0 +1,191 @@ +import json +import pytest +from types import SimpleNamespace + +from agent_framework.runtime.agent_runtime import AgentRuntimeMixin + + +class _Router: + def __init__(self): + self.registry = SimpleNamespace( + tools={"contestar_cobranca": object(), "validar_contestacao": object()}, + get_tool=lambda name: SimpleNamespace( + description="Contesta uma cobrança após validação", + args_schema={ + "subject": { + "type": "string", + "description": "Referência a um item concreto e identificável da fatura", + "user_prompt": "Qual cobrança ou item você deseja contestar?", + }, + "valor": { + "type": "number", + "description": "Valor monetário explicitamente associado pelo cliente ao item", + "user_prompt": "Qual é o valor da cobrança que você deseja contestar?", + }, + }, + requires=["subject", "valor"], + ) if name == "contestar_cobranca" else None, + ) + + def resolve_execution_policy(self, tool_name, arguments=None): + if tool_name == "contestar_cobranca": + return { + "operation_type": "transactional", + "require_confirmation": True, + "requires": ["subject", "valor"], + "policy_source": "test", + "pre_validation": {"enabled": True, "tool": "validar_contestacao", "fail_open": False}, + } + return {"operation_type": "read_only", "require_confirmation": False, "policy_source": "test"} + + +class _LLM: + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] + generation = kwargs.get("generation_name") + if generation == "llm.transaction_parameter_current_only": + low = prompt.lower() + fields = json.loads(prompt.split("Formato: ", 1)[1]) + out = dict(fields) + if "quatorze e noventa e nove" in low: + if "valor" in out: + out["valor"] = 14.99 + elif "vinte e cinco e cinquenta" in low: + if "valor" in out: + out["valor"] = 25.50 + elif "dezenove e noventa e nove" in low: + if "valor" in out: + out["valor"] = 19.99 + return {"content": json.dumps(out, ensure_ascii=False)} + if generation == "llm.transaction_parameter_extraction": + # Deliberately leave subject unresolved so scenario 8 exercises the + # unique anchored structural candidate + authoritative validator path. + return {"content": json.dumps({"fields": { + "subject": {"decision": "unresolved", "value": None, "source": ""}, + "valor": {"decision": "preserve", "value": None, "source": "state"}, + }}, ensure_ascii=False)} + return {"content": "{}"} + + +class _Runtime(AgentRuntimeMixin): + def __init__(self): + self.tool_router = _Router() + self.llm = _LLM() + self.calls = [] + + async def _call_mcp_tool(self, tool_name, arguments, state): + self.calls.append((tool_name, dict(arguments))) + if tool_name != "validar_contestacao": + return {"ok": True, "tool_name": tool_name, "result": {"status": "OK"}} + + subject = str(arguments.get("subject") or "") + valor = arguments.get("valor") + # Canonicalize the raw anchored fragment from scenario 8. + if "Tamboro Mensal" in subject: + canonical = "Tamboro Mensal" + else: + return {"ok": True, "eligible": False, "status": "NEEDS_PARAMETER", "parameter": "subject"} + + if float(valor) not in {14.99, 19.99}: + return { + "ok": True, + "eligible": False, + "status": "NEEDS_PARAMETER", + "parameter": "valor", + "clear_fields": ["resolved_value"], + } + return { + "ok": True, + "eligible": True, + "transaction_decision": { + "resolved_arguments": {"subject": canonical, "valor": float(valor)} + }, + } + + +def _collecting_state(arguments, text, *, metadata=None, context=None): + return { + "user_text": text, + "sanitized_input": text, + "route": "contestacao_agent", + "intent": "state:COLLECTING_CONTESTACAO_PARAMETERS", + "transaction_status": "COLLECTING_PARAMETERS", + "active_transaction": { + "transaction_id": "tx", + "tool_name": "contestar_cobranca", + "arguments": dict(arguments), + "status": "COLLECTING_PARAMETERS", + "parameter_schema": { + "subject": {"type": "string", "description": "Referência a um item concreto e identificável da fatura"}, + "valor": {"type": "number", "description": "Valor monetário explicitamente associado pelo cliente ao item"}, + }, + "parameter_conversational_context": context or "", + }, + "selected_tool_call": {"tool_name": "contestar_cobranca", "arguments": dict(arguments)}, + "missing_parameters": [x for x in ["subject", "valor"] if arguments.get(x) in (None, "")], + "route_decision": {"metadata": metadata or {}}, + } + + +@pytest.mark.asyncio +async def test_scenario8_value_anchor_reconciles_subject_then_awaits_confirmation(): + runtime = _Runtime() + context = ( + "user: tem uma cobrança aqui que eu não reconheço\n" + "assistant: Analisando a fatura. * Cobrança Tamboro Mensal no valor de R$ 14.99 no dia 01/11/25. " + "* Cobrança TIM Fashion Mensal no valor de R$ 10.00 no dia 01/11/25." + ) + state = _collecting_state( + {}, + "é a de quatorze e noventa e nove", + metadata={ + "contextual_reentry": True, + "original_input": "é a de quatorze e noventa e nove", + "relevant_conversation_context": context, + }, + context=context, + ) + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + assert state["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["missing_parameters"] == [] + assert state["pending_tool_call"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["pending_tool_call"]["arguments"]["valor"] == 14.99 + assert state["transaction_pre_validation"]["eligible"] is True + assert result[-1]["awaiting_confirmation"] is True + + +@pytest.mark.asyncio +async def test_scenario16_invalid_value_clears_only_value_and_preserves_subject(): + runtime = _Runtime() + state = _collecting_state({"subject": "Tamboro Mensal"}, "é a de vinte e cinco e cinquenta") + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + assert state["transaction_status"] == "COLLECTING_PARAMETERS" + assert state["active_transaction"]["arguments"]["subject"] == "Tamboro Mensal" + assert "valor" not in state["active_transaction"]["arguments"] + assert state["missing_parameters"] == ["valor"] + assert result[-1]["collecting_parameters"] is True + + +@pytest.mark.asyncio +async def test_scenario16_current_turn_correction_updates_existing_value_then_awaits_confirmation(): + runtime = _Runtime() + state = _collecting_state( + {"subject": "Tamboro Mensal", "valor": 25.50}, + "desculpa é a de dezenove e noventa e nove", + ) + # The old value exists, so missing_parameters alone must NOT restrict which + # current-turn fields may be corrected. + state["missing_parameters"] = ["subject"] + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + assert state["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["missing_parameters"] == [] + assert state["pending_tool_call"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["pending_tool_call"]["arguments"]["valor"] == 19.99 + assert state["transaction_pre_validation"]["eligible"] is True + assert result[-1]["awaiting_confirmation"] is True diff --git a/tests/test_transaction_explicit_abandonment_and_structural_candidate.py b/tests/test_transaction_explicit_abandonment_and_structural_candidate.py new file mode 100644 index 00000000..259cfad6 --- /dev/null +++ b/tests/test_transaction_explicit_abandonment_and_structural_candidate.py @@ -0,0 +1,293 @@ +import json +from types import SimpleNamespace + +import pytest + +from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.runtime.agent_runtime import AgentRuntimeMixin +from agent_framework.runtime.transaction_parameters import reconcile_transaction_parameters + + +class _RouterLLM: + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] if isinstance(messages[-1], dict) else str(messages[-1]) + if kwargs.get("profile_name") == "transaction_parameter_extraction" or "pending_parameters:" in prompt: + return json.dumps({"subject": None, "valor": None}) + if "esquece" in prompt.lower(): + return json.dumps({ + "decision": "ABANDON", + "intent": "billing_invoice_explanation", + "agent": "billing_agent", + "confidence": 0.99, + "reason": "abandono explícito da transação atual", + }) + return json.dumps({ + "decision": "SHIFT", + "intent": "contas_vas_cancel", + "agent": "vas_agent", + "confidence": 0.99, + "reason": "classificação genérica motivada por cancelar", + }) + + +def _router(tmp_path): + routing = tmp_path / "routing.yaml" + routing.write_text( + """ +router: + fallback_agent: contestacao_agent + confidence_threshold: 0.70 +state_policies: + - state: COLLECTING_PARAMETERS + agent: contestacao_agent +intents: + - name: contas_contestation + agent: contestacao_agent + priority: 10 + keywords: [contestar] + - name: contas_vas_cancel + agent: vas_agent + priority: 20 + keywords: [cancelar] + - name: billing_invoice_explanation + agent: billing_agent + priority: 30 + keywords: [fatura] +""", + encoding="utf-8", + ) + return EnterpriseRouter( + SimpleNamespace( + ROUTING_CONFIG_PATH=str(routing), + ENABLE_LLM_ROUTER=True, + ENABLE_ROUTE_STICKINESS=False, + ), + llm=_RouterLLM(), + ) + + +def _active_state(message): + return { + "user_text": message, + "sanitized_input": message, + "next_state": "COLLECTING_PARAMETERS", + "transaction_status": "COLLECTING_PARAMETERS", + "missing_parameters": ["valor"], + "intent": "state:COLLECTING_PARAMETERS", + "active_agent": "contestacao_agent", + "active_transaction": { + "tool_name": "contestar_cobranca", + "arguments": {"subject": "Tamboro Mensal"}, + "status": "COLLECTING_PARAMETERS", + "started_from_intent": "contas_contestation", + "parameter_schema": {"valor": {"type": "number"}}, + }, + } + + +@pytest.mark.asyncio +async def test_generic_shift_does_not_interrupt_active_transaction(tmp_path): + decision = await _router(tmp_path).route(_active_state("isso mesmo, pode cancelar")) + assert decision.intent == "state:COLLECTING_PARAMETERS" + assert not (decision.metadata or {}).get("transaction_interruption") + + +@pytest.mark.asyncio +async def test_explicit_abandonment_can_leave_active_transaction(tmp_path): + decision = await _router(tmp_path).route(_active_state("esquece isso, quero ver minha fatura")) + assert decision.intent == "billing_invoice_explanation" + assert decision.metadata["transaction_interruption"] == "explicit_abandonment" + + +class _PolicyRouter: + def __init__(self): + self.registry = SimpleNamespace( + tools={"contestar_cobranca": object()}, + get_tool=lambda name: SimpleNamespace( + selection_keywords=["contestar"], + args_schema={ + "subject": {"type": "string", "description": "item concreto da fatura"}, + "valor": {"type": "number", "description": "valor associado"}, + }, + requires=["subject", "valor"], + description="Contesta cobrança validada", + ), + ) + + def resolve_execution_policy(self, tool_name, arguments=None): + return { + "operation_type": "transactional", + "require_confirmation": True, + "requires": ["subject", "valor"], + "pre_validation": {"enabled": True, "tool": "validar_contestacao", "fail_open": False}, + } + + +class _UnresolvedReconcileLLM: + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] + if kwargs.get("profile_name") == "transaction_parameter_extraction": + if "FOCO PRINCIPAL DA RECONCILIAÇÃO" in prompt: + return {"content": json.dumps({ + "fields": { + "subject": {"decision": "unresolved", "value": None, "source": ""}, + "valor": {"decision": "preserve", "value": 14.99, "source": "state"}, + } + })} + return {"content": json.dumps({"subject": None, "valor": 14.99})} + return {"content": "{}"} + + +class _CandidateRuntime(AgentRuntimeMixin): + def __init__(self): + self.tool_router = _PolicyRouter() + self.llm = _UnresolvedReconcileLLM() + self.calls = [] + + async def _call_mcp_tool(self, tool_name, arguments, state): + self.calls.append((tool_name, dict(arguments))) + if tool_name == "validar_contestacao": + subject = str(arguments.get("subject") or "") + if "Tamboro Mensal" in subject and float(arguments.get("valor")) == 14.99: + return { + "ok": True, + "tool_name": tool_name, + "result": { + "eligible": True, + "status": "ELIGIBLE", + "transaction_decision": { + "resolved_arguments": {"subject": "Tamboro Mensal", "valor": 14.99} + }, + }, + } + return {"ok": True, "tool_name": tool_name, "result": {"eligible": False, "status": "NEEDS_PARAMETER", "parameter": "subject"}} + + +@pytest.mark.asyncio +async def test_unique_anchored_block_is_candidate_only_and_prevalidator_canonicalizes(): + runtime = _CandidateRuntime() + state = { + "user_text": "é a de quatorze e noventa e nove", + "sanitized_input": "é a de quatorze e noventa e nove", + "mcp_tools": ["contestar_cobranca"], + "route": "contestacao_agent", + "active_agent": "contestacao_agent", + "intent": "contas_contestation", + "route_decision": {"metadata": {}}, + "transaction_status": "COLLECTING_PARAMETERS", + "missing_parameters": ["subject"], + "active_transaction": { + "tool_name": "contestar_cobranca", + "arguments": {"valor": 14.99}, + "status": "COLLECTING_PARAMETERS", + "requires": ["subject", "valor"], + "parameter_schema": { + "subject": {"type": "string", "description": "item concreto da fatura"}, + "valor": {"type": "number", "description": "valor associado"}, + }, + "parameter_conversational_context": ( + "history:1: assistant: Houve: * Cobrança Tamboro Mensal no valor de R$ 14,99 no dia 01/11/25. " + "* Cobrança TIM Fashion Mensal no valor de R$ 10,00 no dia 01/11/25." + ), + }, + "selected_tool_call": {"tool_name": "contestar_cobranca", "arguments": {"valor": 14.99}}, + } + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + assert state["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["pending_tool_call"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["pending_tool_call"]["arguments"]["valor"] == 14.99 + assert state["transaction_parameter_reconciliation"]["applied_structural_candidates"] == { + "subject": "unique_anchored_evidence_block" + } + validator_args = runtime.calls[0][1] + assert validator_args["subject"].startswith("Cobrança Tamboro Mensal") + assert result[-1]["awaiting_confirmation"] is True + +@pytest.mark.asyncio +async def test_current_turn_value_then_unique_anchor_reaches_confirmation_from_empty_transaction(): + """Regression for Contas scenario 08: current amount first, temporal subject second.""" + runtime = _CandidateRuntime() + state = { + "user_text": "é a de quatorze e noventa e nove", + "sanitized_input": "é a de quatorze e noventa e nove", + "mcp_tools": ["contestar_cobranca"], + "route": "contestacao_agent", + "active_agent": "contestacao_agent", + "intent": "contas_contestation", + "route_decision": {"metadata": {}}, + "transaction_status": "COLLECTING_PARAMETERS", + "missing_parameters": ["subject", "valor"], + "active_transaction": { + "tool_name": "contestar_cobranca", + "arguments": {}, + "status": "COLLECTING_PARAMETERS", + "requires": ["subject", "valor"], + "parameter_schema": { + "subject": {"type": "string", "description": "item concreto da fatura"}, + "valor": {"type": "number", "description": "valor monetário associado"}, + }, + "parameter_conversational_context": ( + "history:1: assistant: Houve: * Cobrança Tamboro Mensal no valor de R$ 14,99 no dia 01/11/25. " + "* Cobrança TIM Fashion Mensal no valor de R$ 10,00 no dia 01/11/25." + ), + }, + "selected_tool_call": {"tool_name": "contestar_cobranca", "arguments": {}}, + } + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + assert state["transaction_parameter_collection"]["resolved_fields"] == ["valor"] + assert state["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["pending_tool_call"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["pending_tool_call"]["arguments"]["valor"] == 14.99 + assert result[-1]["awaiting_confirmation"] is True + + +class _ClearMissingSubjectReconcileLLM(_UnresolvedReconcileLLM): + async def ainvoke(self, messages, **kwargs): + prompt = messages[-1]["content"] + if kwargs.get("profile_name") == "transaction_parameter_extraction": + if "FOCO PRINCIPAL DA RECONCILIAÇÃO" in prompt: + return {"content": json.dumps({ + "fields": { + "subject": {"decision": "clear", "value": None, "source": "history:1"}, + "valor": {"decision": "preserve", "value": 14.99, "source": "state"}, + } + })} + return {"content": json.dumps({"subject": None, "valor": 14.99})} + return {"content": "{}"} + + +@pytest.mark.asyncio +async def test_initially_missing_subject_clear_still_allows_unique_anchored_candidate(): + """Scenario 08: clear on an already-missing field must not suppress candidate fallback.""" + rec = await reconcile_transaction_parameters( + _ClearMissingSubjectReconcileLLM(), + text="é a de quatorze e noventa e nove", + tool_name="contestar_cobranca", + parameter_names=["subject", "valor"], + known_arguments={"valor": 14.99}, + parameter_schema={ + "subject": {"type": "string", "description": "item concreto da fatura"}, + "valor": {"type": "number", "description": "valor monetário associado"}, + }, + tool_description="Contesta uma cobrança identificada por item e valor", + conversational_context=( + "priority_3_previous_assistant_tool_or_evidence_context:\n" + "history:1: assistant: Analisando a sua fatura atual. * Cobrança Tamboro Mensal no valor de R$ 14,99 no dia 01/11/25. " + "* Cobrança TIM Fashion Mensal no valor de R$ 10,00 no dia 01/11/25.\n" + "priority_4_previous_user_utterances:\n" + "history:2: user: tem uma cobrança aqui que eu não reconheço" + ), + ) + + assert rec["decisions"]["subject"] == "clear" + assert rec["clear_fields"] == ["subject"] + assert rec["candidates"] == { + "subject": "Cobrança Tamboro Mensal no valor de R$ 14,99 no dia 01/11/25." + } + assert rec["candidate_provenance"] == { + "subject": "unique_anchored_evidence_block" + } diff --git a/tests/test_transaction_parameter_llm_precedence.py b/tests/test_transaction_parameter_llm_precedence.py index fe2ed05b..ef57780f 100644 --- a/tests/test_transaction_parameter_llm_precedence.py +++ b/tests/test_transaction_parameter_llm_precedence.py @@ -643,3 +643,129 @@ intents: [] decision = await router.route(state) assert decision.metadata["transaction_confirmation_decision"] == "confirm" assert decision.metadata["transaction_confirmation_source"] == "deterministic" + +@pytest.mark.asyncio +async def test_collecting_preserves_resolved_required_fields_and_only_extracts_currently_missing(): + class Runtime(_Runtime): + def __init__(self): + super().__init__() + self.pending_seen = None + + async def _extract_transaction_parameters( + self, state, *, tool_name, missing_parameters, known_arguments=None + ): + self.pending_seen = list(missing_parameters) + # Simulate the value extracted for the only missing field. A resolved + # required field must not be offered back to the semantic extractor. + return {"reason": "desisti da compra"} + + runtime = Runtime() + state = { + "user_text": "desisti da compra", + "sanitized_input": "desisti da compra", + "transaction_status": "COLLECTING_PARAMETERS", + "missing_parameters": ["reason"], + "mcp_tools": ["solicitar_devolucao"], + "active_transaction": { + "tool_name": "solicitar_devolucao", + "arguments": {"order_id": "PED-1001"}, + "status": "COLLECTING_PARAMETERS", + "parameter_schema": {"order_id": "string", "reason": "string"}, + "tool_description": "Abre uma solicitação de devolução de pedido.", + }, + "selected_tool_call": { + "tool_name": "solicitar_devolucao", + "arguments": {"order_id": "PED-1001"}, + }, + "route_decision": {"metadata": {}}, + "route": "support_agent", + "intent": "state:COLLECTING_SUPPORT_PARAMETERS", + } + + result = await runtime.execute_tools_for_intent(state) + assert runtime.pending_seen == ["reason"] + assert state["pending_tool_call"]["arguments"]["order_id"] == "PED-1001" + assert state["pending_tool_call"]["arguments"]["reason"] == "desisti da compra" + assert result[-1]["awaiting_confirmation"] is True + +@pytest.mark.asyncio +async def test_correction_of_missing_numeric_value_does_not_reopen_resolved_text_entity(): + class Runtime(_Runtime): + def __init__(self): + super().__init__() + self.pending_seen = None + + async def _extract_transaction_parameters( + self, state, *, tool_name, missing_parameters, known_arguments=None + ): + self.pending_seen = list(missing_parameters) + # If subject were incorrectly offered again, a semantic extractor + # could reinterpret the monetary phrase as an entity. The runtime + # contract must expose only the missing numeric field here. + out = {"valor": 19.99} + if "subject" in missing_parameters: + out["subject"] = "cobrança de R$ 19,99" + return out + + runtime = Runtime() + state = { + "user_text": "desculpa é a de dezenove e noventa e nove", + "sanitized_input": "desculpa é a de dezenove e noventa e nove", + "transaction_status": "COLLECTING_PARAMETERS", + "missing_parameters": ["valor"], + "mcp_tools": ["solicitar_devolucao"], + "active_transaction": { + "tool_name": "solicitar_devolucao", + "arguments": {"order_id": "PED-1001"}, + "status": "COLLECTING_PARAMETERS", + "parameter_schema": {"order_id": "string", "reason": "string", "valor": "number"}, + }, + "selected_tool_call": { + "tool_name": "solicitar_devolucao", + "arguments": {"order_id": "PED-1001"}, + }, + "route_decision": {"metadata": {}}, + "route": "support_agent", + "intent": "state:COLLECTING_SUPPORT_PARAMETERS", + } + # This synthetic tool doesn't require valor, so exercise the extraction + # contract directly with the same resolved-text/missing-number shape. + out = await runtime._extract_transaction_parameters( + state, + tool_name="solicitar_devolucao", + missing_parameters=["valor"], + known_arguments={"subject": "Tamboro Mensal"}, + ) + assert runtime.pending_seen == ["valor"] + assert out == {"valor": 19.99} + +@pytest.mark.asyncio +async def test_reconciler_exposes_schema_driven_field_evidence_views_for_missing_fields(): + class _LLM: + def __init__(self): + self.prompt = "" + async def ainvoke(self, messages, **kwargs): + self.prompt = messages[0]["content"] + return {"content": '{"fields":{"subject":{"decision":"resolved","value":"Produto A","source":"history:1"},"valor":{"decision":"preserve","value":14.99,"source":"state"}}}'} + + from agent_framework.runtime.transaction_parameters import reconcile_transaction_parameters + llm = _LLM() + result = await reconcile_transaction_parameters( + llm, + text="é a de quatorze e noventa e nove", + tool_name="acao_generica", + parameter_names=["subject", "valor"], + known_arguments={"valor": 14.99}, + parameter_schema={ + "subject": {"type": "string", "description": "referência concreta identificável"}, + "valor": {"type": "number", "description": "quantia associada"}, + }, + conversational_context=( + "priority_3_previous_assistant_tool_or_evidence_context:\n" + "history:1: assistant: * Produto A no atributo 14,99. * Produto B no atributo 10,00." + ), + ) + assert result["values"]["subject"] == "Produto A" + assert "field_evidence_views:" in llm.prompt + assert '"subject"' in llm.prompt + assert '"anchor_matches"' in llm.prompt diff --git a/tests/test_transactional_tool_flow.py b/tests/test_transactional_tool_flow.py index 48f6b0f7..49b57403 100644 --- a/tests/test_transactional_tool_flow.py +++ b/tests/test_transactional_tool_flow.py @@ -1058,11 +1058,12 @@ class _CorrectionDuringCollectingLLM: prompt = messages[-1]["content"] self.prompts.append(prompt) if kwargs.get("profile_name") == "transaction_parameter_extraction": + # Current-only extraction now exposes the requested keys through the + # declarative schema/format rather than the legacy pending_parameters label. + if "current_user_message: desculpa, é a de quatorze e noventa e nove" in prompt: + return {"content": json.dumps({"subject": None, "valor": 14.99}, ensure_ascii=False)} pending = json.loads(prompt.split("pending_parameters: ", 1)[1].split("\n", 1)[0]) out = {name: None for name in pending} - # O router já resolveu subject a partir do contexto. O runtime ainda - # precisa permitir que a mensagem atual corrija um valor previamente - # coletado, mesmo que valor não esteja em missing_parameters. if "valor" in out: out["valor"] = 14.99 return {"content": json.dumps(out, ensure_ascii=False)} @@ -1283,3 +1284,152 @@ async def test_prevalidation_parameter_message_is_used_once_without_changing_tra # One-shot: subsequent clarification falls back to the normal prompt. assert runtime.transaction_clarification_message(state) != expected assert "transaction_parameter_message_override" not in state + +class _TemporalFallbackRuntime(AgentRuntimeMixin): + def __init__(self): + from types import SimpleNamespace + self.calls = [] + self.llm = None + self.tool_router = SimpleNamespace( + registry=SimpleNamespace( + get_tool=lambda name: SimpleNamespace( + requires=["subject"], + args_schema={"subject": {"type": "string", "description": "Nome do serviço ou VAS alvo do cancelamento."}}, + description="Cancela o serviço VAS selecionado pelo cliente.", + confirmation_required=True, + tool_type="transactional", + ) + ), + resolve_execution_policy=lambda name, arguments=None: { + "operation_type": "transactional", + "require_confirmation": True, + "requires": ["subject"], + "pre_validation": {"enabled": True, "tool": "validar_vas_subject", "fail_open": False}, + }, + ) + + async def _extract_transaction_parameters_current_only(self, state, *, tool_name, missing_parameters, known_arguments=None): + # Simula o collector tradicional interpretando literalmente a fala atual. + return {"subject": "dezenove e noventa e nove"} + + async def _reconcile_transaction_parameters(self, state, *, tool_name, parameter_names, known_arguments=None): + # Simula o reconciliador temporal usando tools.yaml + contexto newest->oldest. + return { + "values": {"subject": "Tamboro Mensal"}, + "decisions": {"subject": "resolved"}, + "provenance": {"subject": "history:2"}, + "clear_fields": [], + } + + async def _call_mcp_tool(self, tool_name, arguments, state): + self.calls.append((tool_name, dict(arguments))) + if tool_name == "validar_vas_subject": + subject = arguments.get("subject") + if subject == "Tamboro Mensal": + return {"ok": True, "tool_name": tool_name, "result": {"eligible": True, "status": "ELIGIBLE"}} + return {"ok": True, "tool_name": tool_name, "result": { + "eligible": False, + "status": "NEEDS_PARAMETER", + "parameter": "subject", + "reason": "subject_not_resolved", + }} + return {"ok": True, "tool_name": tool_name, "result": {"status": "OK"}} + + +@pytest.mark.asyncio +async def test_temporal_reconciler_is_fallback_after_traditional_candidate_fails_prevalidation(): + runtime = _TemporalFallbackRuntime() + state = { + "user_text": "é a de dezenove e noventa e nove", + "sanitized_input": "é a de dezenove e noventa e nove", + "route": "contestacao_agent", + "intent": "state:COLLECTING_CONTESTACAO_PARAMETERS", + "transaction_status": "COLLECTING_PARAMETERS", + "selected_tool_call": {"tool_name": "cancelar_vas_avulso", "arguments": {}}, + "active_transaction": { + "transaction_id": "tx-1", + "tool_name": "cancelar_vas_avulso", + "arguments": {}, + "status": "COLLECTING_PARAMETERS", + "started_from_intent": "contas_vas_cancel", + "requires": ["subject"], + "parameter_schema": {"subject": {"type": "string", "description": "Nome do serviço ou VAS alvo do cancelamento."}}, + "tool_description": "Cancela o serviço VAS selecionado pelo cliente.", + "parameter_conversational_context": ( + "user: não reconheço esse Tamboro Mensal na minha fatura\n" + "assistant: identifiquei Tamboro Mensal por R$ 14,99 e R$ 19,99" + ), + }, + "context": {}, + } + + result = await runtime.execute_tools_for_intent(state, tools=[]) + + validations = [(name, args.get("subject")) for name, args in runtime.calls if name == "validar_vas_subject"] + assert validations == [ + ("validar_vas_subject", "dezenove e noventa e nove"), + ("validar_vas_subject", "Tamboro Mensal"), + ] + assert state["transaction_parameter_collection"]["mode"] == "current_turn" + assert state["transaction_parameter_reconciliation"]["trigger"] == "prevalidation_needs_parameter" + assert state["pending_tool_call"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["transaction_status"] == "AWAITING_CONFIRMATION" + assert result[-1]["awaiting_confirmation"] is True + +@pytest.mark.asyncio +async def test_collecting_parameters_materializes_router_cache_before_current_only_t6_regression(): + """T6 regression: router-resolved subject must enter active arguments. + + The router already consumed the pending subject in the same turn. A subsequent + current-only extraction over the literal message "sim" must not be required to + rediscover it, and temporal reconciliation must not leave it only as metadata. + """ + runtime = _ContestRuntime() + state = { + "user_text": "sim", + "sanitized_input": "sim", + "route": "contestacao_agent", + "active_agent": "contestacao_agent", + "intent": "state:COLLECTING_CONTESTACAO_PARAMETERS", + "route_decision": { + "route": "contestacao_agent", + "agent": "contestacao_agent", + "intent": "state:COLLECTING_CONTESTACAO_PARAMETERS", + "metadata": { + "transaction_turn_consumed": True, + "transaction_parameter_values": {"subject": "Tamboro Mensal"}, + "transaction_parameter_source": "llm", + "transaction_parameter_missing_before": ["subject"], + }, + }, + "transaction_status": "COLLECTING_PARAMETERS", + "missing_parameters": ["subject"], + "active_transaction": { + "transaction_id": "tx-t6-router-cache", + "tool_name": "contestar_cobranca", + "arguments": {"valor": 19.99}, + "status": "COLLECTING_PARAMETERS", + "started_from_intent": "contas_contestation", + "requires": ["subject", "valor"], + "parameter_schema": { + "subject": {"type": "string", "description": "item concreto da fatura"}, + "valor": {"type": "number", "description": "valor da cobrança"}, + }, + "tool_description": "Contesta cobrança validada", + }, + "selected_tool_call": { + "tool_name": "contestar_cobranca", + "arguments": {"valor": 19.99}, + }, + } + + results = await runtime.execute_tools_for_intent(state, tools=[]) + + assert results[-1]["transaction_status"] == "AWAITING_CONFIRMATION" + assert state["pending_tool_call"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["pending_tool_call"]["arguments"]["valor"] == 19.99 + assert state["active_transaction"]["arguments"]["subject"] == "Tamboro Mensal" + assert state["missing_parameters"] == [] + collection = state["transaction_parameter_collection"] + assert collection["router_resolved_fields"] == ["subject"] + assert runtime.calls == []