From 1a31478bfead0692a9f340d48861d06cd863789b Mon Sep 17 00:00:00 2001 From: "cristiano.hoshikawa" Date: Mon, 31 Aug 2026 18:02:54 -0300 Subject: [PATCH] Ajustes no agente de contas --- .../__pycache__/faturas_agent.cpython-313.pyc | Bin 6536 -> 10784 bytes app/agents/faturas_agent.py | 81 ++++++++++++++++++ ...NARIO_23_LIVE_INTERNET_BALANCE_20260831.md | 29 +++++++ .../conftest.cpython-313-pytest-9.0.2.pyc | Bin 853 -> 836 bytes ...sarial_parity.cpython-313-pytest-9.0.2.pyc | Bin 53252 -> 53235 bytes ...ntract_parity.cpython-313-pytest-9.0.2.pyc | Bin 37568 -> 37551 bytes ...nd_guardrails.cpython-313-pytest-9.0.2.pyc | Bin 4283 -> 4266 bytes ...rapper_parity.cpython-313-pytest-9.0.2.pyc | Bin 29095 -> 29078 bytes ...site_workflow.cpython-313-pytest-9.0.2.pyc | Bin 60226 -> 60209 bytes ...aining_parity.cpython-313-pytest-9.0.2.pyc | Bin 40387 -> 40370 bytes ...icy_residuals.cpython-313-pytest-9.0.2.pyc | Bin 30003 -> 29985 bytes ...s_domain_mock.cpython-313-pytest-9.0.2.pyc | Bin 64331 -> 64314 bytes ...ation_routing.cpython-313-pytest-9.0.2.pyc | Bin 17237 -> 17220 bytes ...ss_rules_full.cpython-313-pytest-9.0.2.pyc | Bin 23211 -> 23194 bytes ...ilure_mapping.cpython-313-pytest-9.0.2.pyc | Bin 7688 -> 7671 bytes ..._out_of_scope.cpython-313-pytest-9.0.2.pyc | Bin 3531 -> 3514 bytes ...ck_regression.cpython-313-pytest-9.0.2.pyc | Bin 8401 -> 8384 bytes ...ty_resolution.cpython-313-pytest-9.0.2.pyc | Bin 10112 -> 10095 bytes ...y_mcp_service.cpython-313-pytest-9.0.2.pyc | Bin 20564 -> 20547 bytes ...aining_parity.cpython-313-pytest-9.0.2.pyc | Bin 17966 -> 17949 bytes ...ls_judges_spi.cpython-313-pytest-9.0.2.pyc | Bin 10149 -> 10132 bytes ...rity_extended.cpython-313-pytest-9.0.2.pyc | Bin 39492 -> 39475 bytes ...ent_gap_fixes.cpython-313-pytest-9.0.2.pyc | Bin 6840 -> 6823 bytes ...ion_directive.cpython-313-pytest-9.0.2.pyc | Bin 5898 -> 5881 bytes ...ive_structure.cpython-313-pytest-9.0.2.pyc | Bin 18986 -> 18969 bytes ...rag_directive.cpython-313-pytest-9.0.2.pyc | Bin 9474 -> 9457 bytes ...k_zero_legacy.cpython-313-pytest-9.0.2.pyc | Bin 1809 -> 1792 bytes ...binary_parity.cpython-313-pytest-9.0.2.pyc | Bin 12243 -> 12226 bytes ..._dict_history.cpython-313-pytest-9.0.2.pyc | Bin 2001 -> 1984 bytes ...faults_parity.cpython-313-pytest-9.0.2.pyc | Bin 6465 -> 6448 bytes ...drail_context.cpython-313-pytest-9.0.2.pyc | Bin 8369 -> 8352 bytes ...user_feedback.cpython-313-pytest-9.0.2.pyc | Bin 7707 -> 7690 bytes ...mework_native.cpython-313-pytest-9.0.2.pyc | Bin 43136 -> 43119 bytes ...inal_response.cpython-313-pytest-9.0.2.pyc | Bin 4572 -> 4555 bytes ...andoff_policy.cpython-313-pytest-9.0.2.pyc | Bin 10791 -> 10774 bytes ...ed_meaningful.cpython-313-pytest-9.0.2.pyc | Bin 4645 -> 4628 bytes ..._alternatives.cpython-313-pytest-9.0.2.pyc | Bin 23168 -> 23151 bytes ...subject_guard.cpython-313-pytest-9.0.2.pyc | Bin 21850 -> 21833 bytes ...ct_resolution.cpython-313-pytest-9.0.2.pyc | Bin 5074 -> 5057 bytes ...cy_dependency.cpython-313-pytest-9.0.2.pyc | Bin 1919 -> 1902 bytes ..._code_mapping.cpython-313-pytest-9.0.2.pyc | Bin 23722 -> 23705 bytes ...compatibility.cpython-313-pytest-9.0.2.pyc | Bin 20473 -> 20456 bytes ...ider_boundary.cpython-313-pytest-9.0.2.pyc | Bin 5738 -> 5721 bytes ...istry_actions.cpython-313-pytest-9.0.2.pyc | Bin 16968 -> 16951 bytes ...liance_anatel.cpython-313-pytest-9.0.2.pyc | Bin 17464 -> 17447 bytes ...on_validation.cpython-313-pytest-9.0.2.pyc | Bin 51132 -> 51115 bytes ...transcription.cpython-313-pytest-9.0.2.pyc | Bin 11628 -> 11611 bytes ...ation_message.cpython-313-pytest-9.0.2.pyc | Bin 23909 -> 23892 bytes ...vas_variation.cpython-313-pytest-9.0.2.pyc | Bin 142673 -> 142656 bytes ...n_adversarial.cpython-313-pytest-9.0.2.pyc | Bin 43325 -> 43308 bytes ...orkflow_cases.cpython-313-pytest-9.0.2.pyc | Bin 18751 -> 18734 bytes ...act_hardcodes.cpython-313-pytest-9.0.2.pyc | Bin 9995 -> 9978 bytes ...a_full_parity.cpython-313-pytest-9.0.2.pyc | Bin 18884 -> 18867 bytes ...ional_context.cpython-313-pytest-9.0.2.pyc | Bin 5924 -> 5907 bytes ...ration_compat.cpython-313-pytest-9.0.2.pyc | Bin 14166 -> 14149 bytes ...and_contracts.cpython-313-pytest-9.0.2.pyc | Bin 14567 -> 14550 bytes ...ty_pente_fino.cpython-313-pytest-9.0.2.pyc | Bin 30813 -> 30796 bytes ...c_uncertainty.cpython-313-pytest-9.0.2.pyc | Bin 8271 -> 8254 bytes ...ance_guidance.cpython-313-pytest-9.0.2.pyc | Bin 0 -> 5887 bytes ...n_all_domains.cpython-313-pytest-9.0.2.pyc | Bin 31336 -> 31318 bytes ...nto_grounding.cpython-313-pytest-9.0.2.pyc | Bin 4979 -> 4962 bytes ..._continuation.cpython-313-pytest-9.0.2.pyc | Bin 18782 -> 18765 bytes ...ntract_parity.cpython-313-pytest-9.0.2.pyc | Bin 12444 -> 12427 bytes ...d_idempotency.cpython-313-pytest-9.0.2.pyc | Bin 14809 -> 14792 bytes ...tadata_parity.cpython-313-pytest-9.0.2.pyc | Bin 34911 -> 34894 bytes ...andoff_bypass.cpython-313-pytest-9.0.2.pyc | Bin 14404 -> 14387 bytes ...eration_types.cpython-313-pytest-9.0.2.pyc | Bin 6164 -> 6147 bytes ...ispute_parity.cpython-313-pytest-9.0.2.pyc | Bin 13807 -> 13790 bytes ...onse_renderer.cpython-313-pytest-9.0.2.pyc | Bin 4431 -> 4414 bytes ...ecution_latch.cpython-313-pytest-9.0.2.pyc | Bin 7709 -> 7692 bytes ...ast_contracts.cpython-313-pytest-9.0.2.pyc | Bin 17470 -> 17453 bytes ...nario_23_live_internet_balance_guidance.py | 21 +++++ 72 files changed, 131 insertions(+) create mode 100644 docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md create mode 100644 tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc create mode 100644 tests/migration/test_scenario_23_live_internet_balance_guidance.py diff --git a/app/agents/__pycache__/faturas_agent.cpython-313.pyc b/app/agents/__pycache__/faturas_agent.cpython-313.pyc index 80d6da8ddd638fcc6b55969d91b1984916bb500f..4bbc08eead95d6a64329dc5cc8911e99ca975db0 100644 GIT binary patch delta 5157 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 I8;0`#0DGGln*aa+ delta 1480 zcmZ`&O>7%Q6rS<^_%F5-J9ccxu^lJ)rzr_->NF{Fh|`ePs$-%|76cBsj=cm2|D?0) zQuP87Ab|vjbf|;?sc=E!#K9F;$fa#45?om+5Frk5B1-59P?_1-Ekdx;eEZ(K?|t)j z_Pvi+-WsDopeqb{F;bm zrCC)qhaxLVrBT;;xKY)}8@=;zfb8@hUk=l9aD+la&@2_DQdiVsy(DXVxl&R!Ze6CJ zs8|!4o0~FD@LHGJXjFA2`HHIX6;-ZLa#1a6VqI2LxlB&F3oz2U=H{UxB9cF%!PXa^ zs=?Ha-J19W)2aqtu zX8-~((Z6{aUgvT`7YG9hEc?hMv6t+7ykxrHKbaqB7Yrnr4|Qx5?1&}XKU^|MCdrIK zQ$pQBMFyFOh#Zn7$&rhGPj}sl!jjbv#tmqYa?SM1ZIO-$8X6VI58eb>40zx$DF=q& zBjh)iZzS4b9Z@Dr=MI{(R#`iOq;V%D{U5vb>$Tq zlXrt5_Y|W)$k8}lT*oQ$OHi_=S!9mES#myf0w&4TP_XB!Bmd7acs@+t4;}xP3Z7+I z8S;H7Eb96}azB)ZPLdDD;3#=LJPl*ytMClr1}}RwevNi*q6>Ryk|xj|S7m&RyfZj^ zBEWWR?YfU@#tTgP91ECgbad#DDVUw1bWtGgNZdHdyc=9=KB7W{H_#d$z7KmcoM5&b z*n#(n175J%50=TPXynzb1AYK6oZ&z8QmX|or1+a7)Y=ZgEa11p^HbEh<%3y{zvWLy zsB?Q7W<~yXit+EbVb;vw@uUHDc04d^<#+n#0@S%X4zo7??kOX)Q;^~}Ku1nTQAac7 z(12BUY!t~)(bt@-ta#S5;V|x_FZ>iyV=;Jxd=zVPR*Xn*{Cs+p1=jQ^1Bn?~OooEzUXFvp#8Dn& z=MqLzO+2%xr`MRq14+#^U2Bw^Re1(qq5(F|3Y*1!4nnvK9^VCLcEMx6i_aP0#CNXY Jdw>~w`rij-R9*l8 diff --git a/app/agents/faturas_agent.py b/app/agents/faturas_agent.py index 3d16258..7fd5d84 100644 --- a/app/agents/faturas_agent.py +++ b/app/agents/faturas_agent.py @@ -1,6 +1,8 @@ from app.agents.prompting import apply_agent_profile_prompt from app.agents.contas_prompting import load_domain_prompt from app.agents.runtime import AgentRuntimeMixin +import re +import unicodedata class FaturasAgent(AgentRuntimeMixin): @@ -31,6 +33,74 @@ class FaturasAgent(AgentRuntimeMixin): self.guardrail_pipeline = guardrail_pipeline + @staticmethod + def _normalize_text(value): + text = unicodedata.normalize("NFKD", str(value or "")) + return "".join(ch for ch in text if not unicodedata.combining(ch)).lower().strip() + + @classmethod + def _is_live_internet_balance_request(cls, state): + text = cls._normalize_text( + state.get("sanitized_input") or state.get("user_text") or state.get("input") or "" + ) + if not text: + return False + + internet = bool(re.search(r"\b(internet|dados moveis|dados|franquia)\b", text)) + live_balance = bool( + re.search( + r"\b(quanto.*(?:resta|restou|tenho|sobrou|disponivel)|" + r"saldo|restante|restam|ainda tenho|ainda posso usar|consumo atual)\b", + text, + ) + ) + return internet and live_balance + + @classmethod + def _has_live_internet_balance_evidence(cls, tool_context): + # Não bloqueia uma integração futura que passe a devolver o saldo em tempo real. + evidence_keys = { + "saldo_internet", + "internet_balance", + "remaining_data", + "remaining_data_mb", + "remaining_data_gb", + "saldo_dados", + "franquia_disponivel", + "consumo_atual", + "current_usage", + } + + def walk(value): + if isinstance(value, dict): + for key, nested in value.items(): + if cls._normalize_text(key).replace(" ", "_") in evidence_keys and nested not in (None, "", [], {}): + return True + if walk(nested): + return True + elif isinstance(value, list): + return any(walk(item) for item in value) + return False + + for item in tool_context or []: + if not isinstance(item, dict) or item.get("ok") is False: + continue + if walk(item.get("result")): + return True + return False + + @classmethod + def _live_internet_balance_guidance(cls, state, tool_context): + if not cls._is_live_internet_balance_request(state): + return None + if cls._has_live_internet_balance_evidence(tool_context): + return None + return ( + "Não consigo consultar o saldo de internet em tempo real por aqui. " + "Para ver quanto ainda resta neste mês, consulte o app Meu TIM, " + "onde você acompanha o consumo atual da sua franquia." + ) + @staticmethod def _handoff_patch_from_tool_context(tool_context): for item in tool_context or []: @@ -93,6 +163,17 @@ class FaturasAgent(AgentRuntimeMixin): } return result + live_balance_guidance = self._live_internet_balance_guidance(state, tool_context) + if live_balance_guidance: + return { + "answer": f"[FaturasAgent] {live_balance_guidance}", + "next_state": state.get("next_state") or "ACTIVE", + "mcp_results": tool_context, + "rag": {"enabled": False, "skipped": True, "reason": "live_internet_balance_not_available"}, + **self.transaction_state_patch(state), + **handoff_patch, + } + direct_answer = self.build_direct_mcp_answer(state, tool_context, agent_label="FaturasAgent") if direct_answer: return { diff --git a/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md b/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md new file mode 100644 index 0000000..ecbf7ee --- /dev/null +++ b/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md @@ -0,0 +1,29 @@ +# Correção do cenário 23 — saldo de internet em tempo real + +## Problema + +O FaturasAgent respondia que não havia informação nos "dados consultados" sobre o saldo de internet restante mesmo quando nenhuma tool de consumo em tempo real havia sido executada. Isso criava uma alegação de consulta sem evidência funcional. + +## Solução + +A correção foi mantida no agente, sem alteração do framework. Para perguntas explícitas de saldo/franquia restante em tempo real, o FaturasAgent: + +1. verifica se alguma tool bem-sucedida trouxe evidência de saldo/consumo atual; +2. se houver evidência, deixa o fluxo normal responder com os dados reais; +3. se não houver evidência, não afirma que consultou dados e orienta o cliente ao Meu TIM. + +Resposta de fallback: + +> Não consigo consultar o saldo de internet em tempo real por aqui. Para ver quanto ainda resta neste mês, consulte o app Meu TIM, onde você acompanha o consumo atual da sua franquia. + +A regra não intercepta perguntas genéricas sobre internet/plano e não bloqueia uma integração futura que passe a devolver saldo real. + +## Arquivo alterado + +- `app/agents/faturas_agent.py` + +## Testes + +- `tests/migration/test_scenario_23_live_internet_balance_guidance.py` +- 3/3 testes novos PASS +- regressão `tests/migration`: 821 PASS / 2 FAIL preexistentes diff --git a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index 6307e1611fffc6aba8d6b1db09965a46cdde2664..1f8ed39d917b78e1c8939fc03fb016dc38aeddb5 100644 GIT binary patch delta 55 zcmcc0c7%=lGcPX}0}${po4S!ZiAl~b8;9oX%Blmq~IS>8Zyb}GC#F9k)Ge#G=I9 N+~V}jk}R)J0{}P>6%GIZ delta 75 zcmex7pSfiLGxuj+UM>b85WO;KBlmq~{Yd@Xyb}GC#F9k)ra%Ox8W`o Z@lb*M diff --git a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc index f100c2e564c5ec5a587f1334afd71796358f99be..dcbeadda8a67e5d9138363ebe7fc7248375a7a85 100644 GIT binary patch delta 56 zcmdn3xJr@xGcPX}0}${po4S#EH=|sLer{fgeoA6VqJDCIUP)qcd|GBjd_iJSVs376 K`sS~U$M^vFgA>*O delta 73 zcmZ3bxLc9?GcPX}0}zN_nY596H=};4er{fgeoA6VqJDCIUP)qcd|qm~eqwrRUP(Mu WAU`=Xz9cobASbaTb@LO(V|)ONG#cIj diff --git a/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc index 2fe5e19a0f985b27ce9b8323f143c499bb86967e..926bf2af884a56b09267cadf6867ac1aff412ae1 100644 GIT binary patch delta 58 zcmZ4fm~q-;M()qNyj%=Gz`tzjMs8zfxnTX=yb}GC#F9k)Ge#G=I9 M+~V}jxy)91059_s`v3p{ delta 75 zcmbRCm~r`IM()qNyj%=GAbMreMs8zf{S^J&yb}GC#F9k)ra%Ox8W`o Z@lb*MGe#G=I9 M+~V}jO)QQx0WWra%Ox8Zyb}GC#F9k)Ge#G=I9 N+~V}j$viLK0RTx26^;M^ delta 75 zcmdn>jrsI9X710tyj%=GAbMreMs7_W{Yd@Xyb}GC#F9k)ra%Ox8W`o Z@lb*Mty-S+=GcPX}0}${po4S!Zf=e!5KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 Kee)zPOJ)G>VG^kT delta 73 zcmdlbeOj9PGcPX}0}zN_nY58Rf=jhKR2&LKP9mwQ9n69uOzWJJ}Z diff --git a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc index cc9af6917c1b6d94260b1c5d65ad4e3e2e85dfd6..048e47e76463c9a7e5e6c652015160f729ff9ad3 100644 GIT binary patch delta 56 zcmZqhfA7crnU|M~0SNe)P2I@-m{TrBKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeX}fAvMc}*pA%33 delta 73 zcmaFw*Wl0nnU|M~0SH8|Oxno(m{UJjKR2&LKP9mwQ9n69uOzWJJ}Ge#G=I9 M+~V}jrJVQO04f3$X8-^I delta 75 zcmX@Sfbq%#M()qNyj%=GAbMreMs90P{bc>zyb}GC#F9k)ra%OxW*XC(~1B00`9+lmGw# delta 75 zcmbQ+!?>=8k^3_*FBbz4h+dhrk(-}MKV3gJuS7p3u_RGHIX|x?u{b_2wOl_jJvFZ+ Y9x9NZoEcw|np=>QSdzNgg6XL<0G-PkoB#j- diff --git a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc index de91aa049643ecaf6b855c8cfc5795abe64f40ff..0ba4609c8c01feb3c265933dc7d644cc214df079 100644 GIT binary patch delta 56 zcmZ4LKgFN>GcPX}0}${po4S#kg-=VmT@nCsT^VKo diff --git a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc index 581b193da30422b634afb1c1f2ca7e9e22578d41..0a86270dd13400b7db173a9b1179ba697df6d6a5 100644 GIT binary patch delta 58 zcmX@Ig=zB^ChpI?yj%=Gz`tzjM(%PZxgh=Ayb}GC#F9k)Ge#G=I9 N+~V}j8<{3g2LLpL6yN{= delta 75 zcmdnIh3Uu^ChpI?yj%=GAbMreM(%PZ{bc>zyb}GC#F9k)ra%Oxht1tkCI~m6S diff --git a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc index 5f8eb5dffd890771fbd82f2945c51304c150e9f5..8c05746f71ddc93bb271fd61ba5c57911798fc9b 100644 GIT binary patch delta 56 zcmeCu`>D(QnU|M~0SNe)P2I@7id8OBKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 Kee)AmH9-IY6BAni delta 73 zcmeyV+oi|-nU|M~0SH8|Oxno3id8>LKR2&LKP9mwQ9n69uOzWJJ}mK-M()qNyj%=Gz`tzjM(%J{xd8p#yb}GC#F9k)Ge#G=I9 N+~V}j6Io}v000$e6X5^= delta 75 zcmbO^g>lssM()qNyj%=GAbMreM(%J{{Y3rTyb}GC#F9k)ra%OxPx# diff --git a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc index d4fd0b4235c2fd0cea9e216d0a1b72bc842e63bf..d37dc7e76dab1917625da9e79afd76559af600a6 100644 GIT binary patch delta 56 zcmZqj`sm61nU|M~0SNe)P2I@N#3bjdpPN^rpORRTsGpplSCUv9pO#q>UyxXon44Ri KzS)N9m=XZ(2@=u( delta 73 zcmez9+2qCjnU|M~0SH8|OxnoJ#H1gmpPN^rpORRTsGpplSCUv9pO;#$pO~JSR}v2u W$WP9UFGUe>tA}GcPX}0}zN_nY58Rmq|ZPKR2&LKP9mwQ9n69uOzWJJ}UyxXon44Ri KzS)NHz6=1@>Joqe delta 73 zcmeCOnQg=UnU|M~0SH8|OxnoJ#HgR5pPN^rpORRTsGpplSCUv9pO;#$pO~JSR}v2u W$WP9UFGb8;9oX%BXb85WO;KBX7CQiUY8m|i diff --git a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc index 5d75edf9362a2f00d04e7bf775ba819a40d4c51d..00d4cbc2acf15f35767114c023d90c29e6e9fef6 100644 GIT binary patch delta 58 zcmZqJ%J_Z@Bll-sUM>b8;9oX%Bex^FoS%MfUWtB6Vo9QYa(-S(VsU(0W<`8KVo_pl MZgKkNYIfFO04K~7p8x;= delta 75 zcmaFAg|T5PBll-sUM>b85WO;KBex^Fe!PBeUWtB6Vo9QYa(-S(VsU(4YPo)5dTL%t YJX9b*IWxW_HMbxqu_SeK7CUP&0Ldd7rvLx| diff --git a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc index 80e4baecadba0f0fa19403cea51451198da01868..cc24aede8c71784e1e56eb478bad68f8c13ad986 100644 GIT binary patch delta 58 zcmcb$it*$sM()qNyj%=Gz`tzjM(zM+xe)!_yb}GC#F9k)Ge#G=I9 M+~V}jUCb8(0W-4{qW}N^ delta 75 zcmX@Pit*MeM()qNyj%=GAbMreM(zM+{Z#$jyb}GC#F9k)ra%OxHz diff --git a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc index a04404df87d76de7064b07dcbee27ab690e627ba..3c46450fe29bf0453bfb224c742c034468d72725 100644 GIT binary patch delta 56 zcmey*_l}SIGcPX}0}${po4S!(j#bV}KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeRBk>Iuii#?GjM{ delta 73 zcmaFI_n(jZGcPX}0}zN_nY58xj#WQeKR2&LKP9mwQ9n69uOzWJJ}8W`o Z@lb*MM()qNyj%=GAbMreM(%A)`bGM=c_sQOi6x2p$@zIDiN*1Gspa~K>8W`o Y@lb*MW+CMxN=W03)9iMgRZ+ delta 75 zcmdnq!g!*Ek^3_*FBbz4h+dhrk-MBnKUY6DuS7p3u_RGHIX|x?u{b_2wOl_jJvFZ+ Y9x9NZoEcw|np=>QSdzMV0nc<(0K)MbP5=M^ diff --git a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc index ae7a9a36a0af92230076ab425f0484f17b33632b..b07780c8e98f57a159b4991939065abcd97b581f 100644 GIT binary patch delta 58 zcmdnd!MMDGk^3_*FBbz4@GqOXk=ukvEW)s9?vOr01^=s(EtDd delta 75 zcmZ49!MLM?k^3_*FBbz4h+dhrk=ukvKT$t7uS7p3u_RGHIX|x?u{b_2wOl_jJvFZ+ Y9x9NZoEcw|np=>QSdzLqmgkf?0IG}{*#H0l diff --git a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc index f3310dc1fe61888da0bae22c4afe9e06a0feaf74..74c60d54e87b455ad83f1480183fea3528f6c9dd 100644 GIT binary patch delta 58 zcmdnf&%C;ynfo&@FBbz4@GqOXkz0dFE=)f+uS7p3u_RGHIX|x?u{b_0vm(AAu_!S& Mw>W)s5>wPs02Htj;{X5v delta 75 zcmZ48&%CFfnfo&@FBbz4h+dhrkz0dFKV3gJuS7p3u_RGHIX|x?u{b_2wOl_jJvFZ+ Y9x9NZoEcw|np=>QSdzLqkSXdY0Ik&;>i_@% diff --git a/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc index 5bda56ca25681f342e90ce7d8d75b2804ee6acf0..004efbe4c42e88c9ef0a4d670cbdf4136a461027 100644 GIT binary patch delta 56 zcmaD8bvugtGcPX}0}${po4S#EhNxVmer{fgeoA6VqJDCIUP)qcd|GBjd_iJSVs376 K`sPcbv%~=-ZWL|+ delta 73 zcmcZ|^(Kn@GcPX}0}zN_nY596hNym)er{fgeoA6VqJDCIUP)qcd|qm~eqwrRUP(Mu WAU`=Xz9cobASbaTb@M^dS>gc1dK-2C diff --git a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc index 498c10f8890a8b297230a1f2bfcf1e0254fdb978..d3aed9b100b460e7772464f323367de5af426954 100644 GIT binary patch delta 58 zcmaF5i}A`XM()qNyj%=Gz`tzjM(zV_a^d>9c_sQOi6x2p$@zIDiN*10nHBK`iA9OI Nxy9+5f3anU0RU0@6=DDY delta 75 zcmcbzi}C3$M()qNyj%=GAbMreM(zV_`WgDUc_sQOi6x2p$@zIDiN*1Gspa~K>8W`o Y@lb*M~az6UGc_sQOi6x2p$@zIDiN*10nHBK`iA9OI Uxy9+ts$A`=T#VaQxtMg60R`F>*8l(j delta 83 zcmX?bi{s)g4(`vqyj%=GAbMreM()>~`my@Cc_sQOi6x2p$@zIDiN*1Gspa~K>8W`o g@lb*M8W`o Z@lb*MGe#G=I9 M+~V}j`ra%Oxx@OaK4? diff --git a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc index dae0dabcf784703d775a00d7f06267d766e22f37..235e056b376e742c3d4a2e07ec75fd1611f2b022 100644 GIT binary patch delta 56 zcmeD7`{m31nU|M~0SNe)P2I@d%p@19pPN^rpORRTsGpplSCUv9pO#q>UyxXon44Ri KzIiW`p9%m85)+63 delta 73 zcmez6+wI5wnU|M~0SH8|OxnoZ%%q>EpPN^rpORRTsGpplSCUv9pO;#$pO~JSR}v2u W$WP9UFGynQ`-EM()qNyj%=Gz`tzjM(#`|IWPU(yb}GC#F9k)Ge#G=I9 M+~V}jiE%yb}GC#F9k)ra%OxAKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeX~8=Qvm?jrxJ?* delta 73 zcmbQNw?vQoGcPX}0}zN_nY59cl}$fiKR2&LKP9mwQ9n69uOzWJJ}=9ysQ>_9?HQ8* diff --git a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc index 5566fb011e112ab961ef1666a8629cad492a6dfc..b1cd14d3681ac06e2c194a0a17576a84c4c5bce0 100644 GIT binary patch delta 56 zcmcbXcQlXtGcPX}0}${po4S#^mr*W6KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 Kee-d~ep3J()f74a delta 73 zcmX?_cP)?mGcPX}0}zN_nY59+mr*}eKR2&LKP9mwQ9n69uOzWJJ}uRF diff --git a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc index 59b49306f2110b25ab96b25420997c4f4a769a24..a63a2999c64cfd38202f58259f39faf2fb779027 100644 GIT binary patch delta 58 zcmccnf$_`-M()qNyj%=Gz`tzjM(zL(xp4j5yb}GC#F9k)Ge#G=I9 M+~V}jT^yJ50aUdWV*mgE delta 75 zcmX@}f${DKM()qNyj%=G@O$Hgjobko`WgDUc_sQOi6x2p$@zIDiN*1Gspa~K>8W`o Y@lb*M7)R7VhrpnX#vBXC0ytY$-H`tkMW({BI%>^0!%pgN@dm5s`9em8Z)?lbP;G zRW}eLq#QWpzyS_O9FbOg2q*UDBN7)5dzi5uY_*U$um`xH71mz%w6ChWtGmO1Z4?9t zMm^QUGcey_&t@;fe&GZ~y(_>;EiB(l<;=K1wX?odw}T$&^g_szhXdR~8FY z(WOO|6m{04TrDk@Ng3qAm_!Cl71}|w2yLY)8>R2qRLh1O4*eDR0YUU)6T5yRUVz`q z&_V>#-foevCY#En>`FI;&q@-Hut?H`RF`VfT-@VAY~B-fIqWgq27aN|3yFH#;Whn2BB5lOuZ) z8#R;f|1t=@6(HC5bQ|lA z>to_#U&n!t9M?v=kKNZiy+e>5P(*Z0Dc0Mce?re2^8ZQ>)m_gwN=Fqu47hHrGmOuZyr#C#8I?d=Fw0v04(6vu^FjP z`J!wwklCJKLohPg6PqWpBy-)X5XO<=<5tUg16xVPtX0%PZUbA*z_FPSRxWgI`l66@}b8lhehfmc$|zwLyfH#<$gM=OhrmG zem1MjXWb?#s}1z%6TVt&yME0^KB~bJ^AWY^^h}NImDbu$lkfnzZqWvI5pg_gdS;(| zRXexC=Xt(hvdA|{>6?xZN%0}YjhooDEqHsW)y9G4J8h9~qCubzI%R09gD6_b?okp&?4b}&elsc z=y>0Lz!_BV*wvQ1YQ4Ks?`icNHNG~%qAf@2c19Ks%(#;J_wuT6+fwVh+Oe)W{!yW) zjqj-OwW*HZJril$sVKU^_EAA-y__b@Tk80(cCxD)A2)iMv7^So zKW+%Em(zq9oc7?!NV#fr5DdK9{1R$MsBI5aZMt#|>dOh~x*{sh0#uxr>*~vrS5GRT z7iD`PSJ(Yi%~|9%=WJRF9kRlCJhw{Unlr7QlPW5S5{>0X_pQBtJo-R2sHk(z(nGJI zr629%AT_iY1@V{mtvE#uy{~5>iDq9Mr|4b()5MmRsZgn4Mt-NzOCgf)l;x#JoFs|lQ2{Uj?BacC-Mv1T=CL`u) z&X+`s)rC+6UY%S&lA-V^h=ZG_$q_bqfX_kTD3+s~m{_8LIbu1JWtrz9QjExGYAUDA zP{w011L*{Qcv3hg=mL(im^h7;jc*0OMv!aEp%(CW)X~5lAKRn`V~62O{{>A<6qmX~ zmw_m>M|ZS^p0?0c7dHqrU2S2bxTReJ@iRMWw8b4YzP6adC80*beRc7@v%=O(HRyop zL{B7a!$L)21Mfrw?+p@lWS0T?Ga#Nc1|dUq6TUYAcq{LNx9uF3^~c+Co#E}k^(@{F9txj+8@wId7h42gnUzEB2Yx7-x%=`* zT_C66jl<$?#nc{pRDxj+6_pqTETCm}-k1C8zXaQd1hx;GBM%kZv$%dF!ga#l^K0QK z!EHD#Id0QpMX}X{V;29MyZ|Q;#k(1n2&QnxAWwoB;N4}oJD*@V2oDwBXAPQx_cK{^ z4~xF%wP+0TF53LlHQAHU()n4PhFbQ4Yh9fBK}8^}3WmHmT8zlSDQ`|ln*(LMFt#*^Kr zUg!;%I7X3+VaWyIrbnxNi$h+waQ>3$Nof94kY)L6NqSehFHQYWD#=g(a8!}6%6pZ9ye#wa{|7I`Ktuol literal 0 HcmV?d00001 diff --git a/tests/migration/__pycache__/test_subject_entity_resolution_all_domains.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_subject_entity_resolution_all_domains.cpython-313-pytest-9.0.2.pyc index 52bc7629b96b86841ed00dd60de1e87c5726668b..cb146c2d710f4371dedf5e23327e369de3d45e40 100644 GIT binary patch delta 57 zcmaFyh4I=KMy}7iyj%=Gz`txG*IqWcX#L#068)6Kl0^OF{JfIH;`p@8iui)WqQuad<68)6Kl0^OF{JfIH;`p@83M2i*^whkP Yc&JEza%Ox*iSgbfM()qNyj%=GAbMreM((xD`q}!qc_sQOi6x2p$@zIDiN*1Gspa~K>8W`o Y@lb*MUyxXon44Ri KzWD&}9VGz%9uwsN delta 73 zcmeB9oRi4?nU|M~0SH8|OxnoZ#;YHtpPN^rpORRTsGpplSCUv9pO;#$pO~JSR}v2u W$WP9UFGGe#G=I9 M+~V}jZcLJG03Fv8Y5)KL delta 75 zcmX>%f$9DPChpI?yj%=GAbMreMs6M^{RI8oyb}GC#F9k)ra%OxZasU7T diff --git a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc index 1050d471e1049ec47e7c249a9074f0f1474667a5..95a1c252412862c44aa2bca5f7ebb0d21db56335 100644 GIT binary patch delta 55 zcmX?7u(^QyGcPX}0}${po4SyjSUyxXon44Ri KzIm}wAu9mlEfT{3 delta 73 zcmZoRm}0>FnU|M~0SH8|OxnnuDWspIpPN^rpORRTsGpplSCUv9pO;#$pO~JSR}v2u W$WP9UFGlqGcPX}0}${po4S!(j8o2AKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeRBZke|7-oHz$oXZpPN^rpORRTsGpplSCUv9pO#q>UyxXon44Ri KzPXEWvJ3#^OA^lj delta 73 zcmeCNnQO!SnU|M~0SH8|OxnmDz^EUupPN^rpORRTsGpplSCUv9pO;#$pO~JSR}v2u W$WP9UFGW*XC(~C)01W99umAu6 delta 75 zcmZ46!MLx3k^3_*FBbz4h+dhrk(-}MKUP0CuS7p3u_RGHIX|x?u{b_2wOl_jJvFZ+ Y9x9NZoEcw|np=>QSdzNgg6XRx0HVzrxBvhE diff --git a/tests/migration/test_scenario_23_live_internet_balance_guidance.py b/tests/migration/test_scenario_23_live_internet_balance_guidance.py new file mode 100644 index 0000000..d502f13 --- /dev/null +++ b/tests/migration/test_scenario_23_live_internet_balance_guidance.py @@ -0,0 +1,21 @@ +from app.agents.faturas_agent import FaturasAgent + + +def test_s23_live_balance_without_backend_uses_meu_tim_guidance(): + state = {"user_text": "quanto de internet eu ainda tenho pra usar esse mês?"} + msg = FaturasAgent._live_internet_balance_guidance(state, []) + assert msg is not None + assert "Não consigo consultar o saldo de internet em tempo real por aqui" in msg + assert "Meu TIM" in msg + assert "dados consultados" not in msg + + +def test_s23_generic_internet_question_is_not_intercepted(): + state = {"user_text": "como funciona a internet do meu plano?"} + assert FaturasAgent._live_internet_balance_guidance(state, []) is None + + +def test_s23_real_time_backend_evidence_takes_precedence(): + state = {"user_text": "quanto de internet eu ainda tenho pra usar esse mês?"} + tools = [{"ok": True, "result": {"remaining_data_gb": 7.5}}] + assert FaturasAgent._live_internet_balance_guidance(state, tools) is None