Files
agent_platform_oci/tests/unit/test_pending_topics_isolation.py

323 lines
15 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pytest
from agent_framework.routing.pending_topics import drain_pending_topics
@pytest.mark.asyncio
async def test_secondary_topic_does_not_inherit_primary_transaction_state():
captured = {}
async def billing_agent(state):
captured.update(state)
return {
"answer": "Aqui está a sua segunda via.",
"mcp_results": [{"tool_name": "consultar_faturas", "ok": True}],
"rag": {"provider": "kbdb", "attempted": True, "status": "executed", "document_count": 1},
"rag_context": "Documento recuperado da base de conhecimento.",
}
state = {
"answer": "Você confirma o cancelamento do Tamboro?",
"sanitized_input": "quero cancelar Tamboro e quero minha segunda via",
"confirmation_required": True,
"active_transaction": {"tool_name": "cancelar_vas_avulso"},
"transaction_status": "AWAITING_CONFIRMATION",
"selected_tool_call": {"tool_name": "cancelar_vas_avulso"},
"pending_tool_call": {"tool_name": "cancelar_vas_avulso"},
"pending_domain_workflow": {"workflow_name": "cancelamento_vas_avulso"},
"missing_parameters": ["subject"],
"next_state": "WAITING_CONTESTACAO_CONFIRMATION",
"mcp_results": [{"tool_name": "consultar_vas"}],
"pending_topics": [{
"operation_id": "op-2",
"intent": "contas_invoice_query",
"agent": "faturas_agent",
"domain": "telecom_contas",
"tools": ["consultar_faturas"],
"source_text": "quero minha segunda via",
"disposition": "execute",
"status": "pending",
}],
}
result = await drain_pending_topics(
state, state["answer"], {"faturas_agent": billing_agent}
)
assert captured["active_transaction"] is None
assert captured["selected_tool_call"] == {}
assert captured["pending_tool_call"] == {}
assert captured["pending_domain_workflow"] is None
assert captured["next_state"] is None
assert captured["sanitized_input"] == "quero minha segunda via"
assert captured["mcp_results"] == []
assert captured["mcp_tools"] == ["consultar_faturas"]
assert captured["available_mcp_tools"] == ["consultar_faturas"]
assert captured["domain"] == "telecom_contas"
assert result.answer.startswith("Você confirma o cancelamento")
assert result.answer.endswith("Aqui está a sua segunda via.")
assert [item["agent"] for item in result.agent_responses] == ["agent", "faturas_agent"]
assert result.agent_responses[0]["primary"] is True
assert result.agent_responses[1]["primary"] is False
assert result.pending_topics == []
assert result.mcp_results == [
{"tool_name": "consultar_vas"},
{"tool_name": "consultar_faturas", "ok": True},
]
assert result.rag_results[0]["metadata"]["provider"] == "kbdb"
assert result.rag_results[0]["context"] == "Documento recuperado da base de conhecimento."
@pytest.mark.asyncio
async def test_deferred_transaction_is_promoted_after_primary_completes():
async def cancellation_agent(state):
assert state["user_text"] == "quero cancelar meu pedido"
return {
"answer": "Você confirma o cancelamento do pedido PED-1001?",
"transaction_status": "AWAITING_CONFIRMATION",
"confirmation_required": True,
"pending_tool_call": {
"tool_name": "cancelar_pedido",
"arguments": {"order_id": "PED-1001"},
},
"active_transaction": {
"tool_name": "cancelar_pedido",
"status": "AWAITING_CONFIRMATION",
},
}
state = {
"answer": "A devolução foi registrada com sucesso.",
"route": "support_agent",
"active_agent": "support_agent",
"intent": "retail_support_exchange_return",
"transaction_status": "COMPLETED",
"pending_topics": [{
"operation_id": "op-3",
"intent": "retail_order_cancel",
"agent": "orders_agent",
"domain": "retail",
"tools": ["consultar_pedido", "cancelar_pedido"],
"source_text": "quero cancelar meu pedido",
"disposition": "defer",
"status": "pending",
}],
}
result = await drain_pending_topics(
state, state["answer"], {"orders_agent": cancellation_agent}
)
assert result.pending_topics == []
assert result.state_patch["route"] == "orders_agent"
assert result.state_patch["intent"] == "retail_order_cancel"
assert result.state_patch["transaction_status"] == "AWAITING_CONFIRMATION"
assert result.state_patch["pending_tool_call"]["tool_name"] == "cancelar_pedido"
assert result.agent_responses[-1]["primary"] is True
assert result.agent_responses[-1]["agent"] == "orders_agent"
@pytest.mark.asyncio
async def test_stale_agent_responses_do_not_leak_into_new_turn_projection():
state = {
"answer": "Lista atual de serviços.",
"route": "vas_agent",
"active_agent": "vas_agent",
"intent": "contas_vas_information",
"agent_responses": [
{"agent": "contestacao_agent", "answer": "Você confirma Tamboro e Paramount+?", "primary": True, "status": "completed"},
{"operation_id": "op-2", "agent": "faturas_agent", "answer": "Resposta antiga de fatura.", "primary": False, "status": "completed"},
],
"pending_topics": [],
}
result = await drain_pending_topics(state, state["answer"], {})
assert [r["answer"] for r in result.agent_responses] == ["Lista atual de serviços."]
assert result.agent_responses[0]["agent"] == "vas_agent"
assert result.agent_responses[0]["primary"] is True
@pytest.mark.asyncio
async def test_completed_secondary_result_is_kept_as_durable_operation_result():
async def billing_agent(state):
return {
"answer": "Fatura consultada.",
"mcp_results": [{"tool_name": "consultar_faturas", "ok": True, "invoice_id": "INV-1"}],
}
state = {
"answer": "Cancelamento concluído.",
"route": "contestacao_agent",
"active_agent": "contestacao_agent",
"intent": "contas_vas_cancel",
"operation_results": {
"op-1": {"operation_id": "op-1", "status": "completed", "answer": "Cancelamento concluído."}
},
"pending_topics": [{
"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent",
"domain": "telecom_contas", "tools": ["consultar_faturas"],
"source_text": "consulte minha fatura", "disposition": "execute", "status": "pending",
}],
}
result = await drain_pending_topics(state, state["answer"], {"faturas_agent": billing_agent})
assert result.operation_results["op-1"]["answer"] == "Cancelamento concluído."
assert result.operation_results["op-2"]["status"] == "completed"
assert result.operation_results["op-2"]["mcp_results"][0]["invoice_id"] == "INV-1"
assert [r["answer"] for r in result.agent_responses] == ["Cancelamento concluído.", "Fatura consultada."]
@pytest.mark.asyncio
async def test_primary_operation_is_not_promoted_while_waiting_confirmation():
state = {
"answer": "Você confirma o cancelamento?",
"route": "contestacao_agent",
"active_agent": "contestacao_agent",
"intent": "contas_vas_cancel",
"transaction_status": "AWAITING_CONFIRMATION",
"confirmation_required": True,
"pending_tool_call": {"tool_name": "cancelar_vas_avulso", "arguments": {"subject": "Tamboro"}},
"multi_intent_plan": {
"plan_id": "mip-1",
"operations": [
{"operation_id": "op-1", "intent": "contas_vas_cancel", "agent": "contestacao_agent", "domain": "telecom_contas", "tools": ["cancelar_vas_avulso"], "source_text": "cancele tamboro"},
],
},
"operation_results": {},
"pending_topics": [],
}
result = await drain_pending_topics(state, state["answer"], {})
assert "op-1" not in result.operation_results
@pytest.mark.asyncio
async def test_terminal_primary_operation_is_promoted_and_keeps_previous_results():
state = {
"answer": "Tamboro cancelado; Paramount+ não encontrado.",
"route": "contestacao_agent",
"active_agent": "contestacao_agent",
"intent": "state:WAITING_CONTESTACAO_CONFIRMATION",
"transaction_status": "COMPLETED",
"multi_intent_plan": {
"plan_id": "mip-1",
"operations": [
{"operation_id": "op-1", "intent": "contas_vas_cancel", "agent": "contestacao_agent", "domain": "telecom_contas", "tools": ["cancelar_vas_avulso"], "source_text": "cancele tamboro e paramount"},
{"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent", "domain": "telecom_contas", "tools": ["consultar_faturas"], "source_text": "consulte minha fatura"},
],
},
"operation_results": {
"op-2": {"operation_id": "op-2", "status": "completed", "answer": "Fatura consultada."}
},
"mcp_results": [
{
"tool_name": "cancelar_vas_avulso",
"ok": True,
"result": {
"status": "COMPLETED",
"output": {
"cancelar_vas_avulso": {
"results": [
{"subject": "Tamboro Mensal", "success": True, "protocol": "123"},
{"subject": "Paramount+", "success": False, "reason": "service_not_found"},
]
}
},
},
}
],
"transaction_evidence": [{"transaction_id": "tx-1", "status": "COMPLETED"}],
"pending_topics": [],
}
result = await drain_pending_topics(state, state["answer"], {})
assert result.operation_results["op-2"]["answer"] == "Fatura consultada."
primary = result.operation_results["op-1"]
assert primary["plan_id"] == "mip-1"
assert primary["status"] == "completed"
assert primary["mcp_results"][0]["result"]["output"]["cancelar_vas_avulso"]["results"][1]["reason"] == "service_not_found"
assert primary["transaction_evidence"][0]["transaction_id"] == "tx-1"
assert [item["answer"] for item in result.agent_responses] == ["Tamboro cancelado; Paramount+ não encontrado."]
@pytest.mark.asyncio
async def test_persisted_plan_does_not_rehydrate_completed_secondary_topics_on_next_turn():
calls = {"faturas": 0, "suporte": 0}
async def billing_agent(state):
calls["faturas"] += 1
return {"answer": "Fatura executada novamente - ERRO."}
async def support_agent(state):
calls["suporte"] += 1
return {"answer": "RAG executado novamente - ERRO."}
plan = {
"plan_id": "mip-continue-1",
"operations": [
{"operation_id": "op-1", "intent": "contas_vas_cancel", "agent": "contestacao_agent", "domain": "telecom_contas", "tools": ["cancelar_vas_avulso"], "source_text": "cancele tamboro"},
{"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent", "domain": "telecom_contas", "tools": ["consultar_faturas"], "source_text": "consulte minha fatura", "disposition": "execute", "status": "pending"},
{"operation_id": "op-3", "intent": "contas_knowledge", "agent": "suporte_contas_agent", "domain": "telecom_contas", "tools": [], "source_text": "explique infinity pos", "disposition": "execute", "status": "pending"},
],
}
state = {
"answer": "Cancelamento concluído.",
"route": "contestacao_agent",
"active_agent": "contestacao_agent",
"intent": "state:WAITING_CONTESTACAO_CONFIRMATION",
"transaction_status": "COMPLETED",
"route_decision": {"route": "contestacao_agent", "intent": "state:WAITING_CONTESTACAO_CONFIRMATION", "metadata": {}},
"multi_intent_plan": plan,
# Empty is authoritative: op-2/op-3 were already drained on turn 1.
"pending_topics": [],
"operation_results": {
"op-2": {"operation_id": "op-2", "plan_id": "mip-continue-1", "agent": "faturas_agent", "intent": "contas_invoice_query", "domain": "telecom_contas", "source_text": "consulte minha fatura", "status": "completed", "answer": "Fatura consultada no turno anterior."},
"op-3": {"operation_id": "op-3", "plan_id": "mip-continue-1", "agent": "suporte_contas_agent", "intent": "contas_knowledge", "domain": "telecom_contas", "source_text": "explique infinity pos", "status": "completed", "answer": "Infinity explicado no turno anterior."},
},
"mcp_results": [{"tool_name": "cancelar_vas_avulso", "ok": True, "result": {"status": "COMPLETED"}}],
}
result = await drain_pending_topics(
state,
state["answer"],
{"faturas_agent": billing_agent, "suporte_contas_agent": support_agent},
)
assert calls == {"faturas": 0, "suporte": 0}
assert [item["answer"] for item in result.agent_responses] == ["Cancelamento concluído."]
assert result.operation_results["op-2"]["answer"] == "Fatura consultada no turno anterior."
assert result.operation_results["op-3"]["answer"] == "Infinity explicado no turno anterior."
assert result.operation_results["op-1"]["status"] == "completed"
@pytest.mark.asyncio
async def test_completed_queued_topic_is_idempotently_skipped_but_result_remains_available_to_processing():
calls = 0
async def billing_agent(state):
nonlocal calls
calls += 1
return {"answer": "não deveria executar"}
plan = {
"plan_id": "mip-idempotent-1",
"operations": [
{"operation_id": "op-1", "intent": "primary", "agent": "primary_agent", "domain": "test", "tools": [], "source_text": "principal"},
{"operation_id": "op-2", "intent": "contas_invoice_query", "agent": "faturas_agent", "domain": "telecom_contas", "tools": ["consultar_faturas"], "source_text": "consulte minha fatura", "disposition": "execute", "status": "pending"},
],
}
existing = {
"operation_id": "op-2", "plan_id": "mip-idempotent-1", "agent": "faturas_agent",
"intent": "contas_invoice_query", "domain": "telecom_contas", "source_text": "consulte minha fatura",
"status": "completed", "answer": "Fatura já consultada.",
"mcp_results": [{"result": {"invoice_id": "INV-1"}}],
}
state = {
"answer": "Resposta atual.",
"route": "primary_agent", "active_agent": "primary_agent", "intent": "primary",
"multi_intent_plan": plan,
"pending_topics": [dict(plan["operations"][1])],
"operation_results": {"op-2": existing},
}
result = await drain_pending_topics(state, state["answer"], {"faturas_agent": billing_agent})
assert calls == 0
assert result.operation_results["op-2"]["mcp_results"][0]["result"]["invoice_id"] == "INV-1"
assert [item["answer"] for item in result.agent_responses] == ["Resposta atual."]
assert result.pending_topics == []