from __future__ import annotations import asyncio from types import SimpleNamespace from app.agents.faturas_agent import FaturasAgent from app.domain.contas.workflow_actions import build_contas_workflow_actions from contas_mcp.servers.contas_mcp_server import main as mcp_main def test_invoice_explanation_handoff_action_is_structural_and_uses_configured_message(): class Service: pass action = build_contas_workflow_actions(Service()).get("preparar_handoff_invoice_explanation") result = action( { "mensagem": "Para continuar com a sua solicitação, aguarde um instante.", "reason": "invoice_explanation_not_resolved", }, {"input": {}}, ) assert result["mensagem"] == "Para continuar com a sua solicitação, aguarde um instante." assert result["session_control"] == "HUMAN_HANDOFF" assert result["human_handoff_requested"] is True assert result["handoff"] is True assert result["session_ended"] is True assert result["terminal_status"] == "human_handoff" def test_result_payload_promotes_invoice_explanation_handoff_control(): result = { "execution_id": "exec-1", "status": "COMPLETED", "output": { "handoff_pos_explicacao_nao": { "success": True, "mensagem": "Para continuar com a sua solicitação, aguarde um instante.", "session_control": "HUMAN_HANDOFF", "handoff_reason": "invoice_explanation_not_resolved", } }, "state": {"current_node": "handoff_pos_explicacao_nao"}, "trace": [{"node": "handoff_pos_explicacao_nao", "status": "COMPLETED"}], } payload = mcp_main._result_payload(result, workflow_name="invoice_explanation") assert payload["mensagem"] == "Para continuar com a sua solicitação, aguarde um instante." assert payload["session_control"] == "HUMAN_HANDOFF" assert payload["human_handoff_requested"] is True assert payload["handoff"] is True assert payload["session_ended"] is True def test_faturas_agent_promotes_handoff_from_tool_context_to_graph_state(): context = [{ "tool_name": "retomar_workflow", "ok": True, "result": { "workflow_name": "invoice_explanation", "status": "COMPLETED", "mensagem": "Para continuar com a sua solicitação, aguarde um instante.", "session_control": "HUMAN_HANDOFF", "human_handoff_requested": True, "session_ended": True, "terminal_status": "human_handoff", "handoff_reason": "invoice_explanation_not_resolved", }, }] patch = FaturasAgent._handoff_patch_from_tool_context(context) assert patch == { "session_control": "HUMAN_HANDOFF", "human_handoff_requested": True, "session_ended": True, "terminal_status": "human_handoff", "handoff_reason": "invoice_explanation_not_resolved", } def test_completed_terminal_workflow_short_circuits_llm_composition_directive(): agent = FaturasAgent.__new__(FaturasAgent) context = [{ "tool_name": "retomar_workflow", "ok": True, "result": { "status": "COMPLETED", "workflow_name": "generic_workflow", # Earlier node still carries a composition directive. It must not win # over the terminal last node. "requires_llm_composition": True, "response_instruction": "Componha uma nova resposta.", "output": { "formatar": { "requires_llm_composition": True, "mensagem": "mensagem anterior", }, "final": { "mensagem": "Encaminhando para atendimento humano.", "session_control": "HUMAN_HANDOFF", "handoff": True, "session_ended": True, "terminal_status": "human_handoff", }, }, "state": {"current_node": "final"}, }, }] answer = agent.build_direct_mcp_answer({}, context, agent_label="AnyAgent") assert answer == "Encaminhando para atendimento humano." def test_result_payload_promotes_generic_terminal_last_node_contract(): result = { "execution_id": "exec-generic", "status": "COMPLETED", "output": { "final": { "mensagem": "Sessão encerrada.", "session_control": "END_SESSION", "session_ended": True, "terminal_status": "done", } }, "state": {"current_node": "final"}, "trace": [{"node": "final", "status": "COMPLETED"}], } payload = mcp_main._result_payload(result, workflow_name="generic_example") assert payload["terminal"] is True assert payload["terminal_action"] == "end_session" assert payload["mensagem"] == "Sessão encerrada." assert payload["session_ended"] is True