from __future__ import annotations import pytest from app.extensions.tim_guardrails import TimPrematureActionRail class FakeLLM: def __init__(self, content: str): self.content = content async def ainvoke(self, messages, **kwargs): return self.content @pytest.mark.asyncio async def test_revprec_allows_structured_insufficient_evidence_without_calling_llm(): text = "Identifiquei dados de desconto, mas os dados disponíveis não informam o motivo da retirada ou do término do desconto." class MustNotRunLLM: async def ainvoke(self, *args, **kwargs): raise AssertionError("LLM não deveria ser chamada no bypass estrutural") ctx = { "guardrail_llm": MustNotRunLLM(), "mcp_results": [ { "tool_name": "termino_desconto", "ok": True, "result": { "output": { "formatar": { "mensagem": text, "epistemic_status": "insufficient_evidence", "discount_reason_grounded": False, } } }, } ], } out = await TimPrematureActionRail().evaluate(text, ctx) assert out.allowed is True assert out.reason == "insufficient_evidence_non_assertive" assert out.metadata["mechanism"] == "deterministic_epistemic_bypass" @pytest.mark.asyncio async def test_revprec_does_not_bypass_if_message_does_not_match_structured_result(): structured = "Os dados disponíveis não informam o motivo." text = "Cancelei o serviço com sucesso." ctx = { "guardrail_llm": FakeLLM("1"), "mcp_results": [{"result": {"output": {"mensagem": structured, "epistemic_status": "insufficient_evidence"}}}], } out = await TimPrematureActionRail().evaluate(text, ctx) assert out.allowed is False @pytest.mark.asyncio async def test_revprec_still_blocks_real_premature_action_without_structured_marker(): out = await TimPrematureActionRail().evaluate( "Cancelei o serviço com sucesso.", {"guardrail_llm": FakeLLM("1"), "mcp_results": []}, ) assert out.allowed is False def test_termino_desconto_declares_epistemic_status(): from app.domain.contas.service import ContasDomainService from app.domain.contas.workflow_actions import build_contas_workflow_actions action = build_contas_workflow_actions(ContasDomainService()).get("formatar_capability_resposta") no_reason = action({"tipo": "termino_desconto"}, {"input": {}}) with_reason = action( {"tipo": "termino_desconto", "discount_evidence": {"discount_reason": "fim da campanha"}}, {"input": {}}, ) assert no_reason["epistemic_status"] == "insufficient_evidence" assert with_reason["epistemic_status"] == "grounded_fact"