test_runner_policy.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import pytest
  2. from agent.core.presets import AgentPreset, register_preset
  3. from agent.core.runner import AgentRunner, RunConfig
  4. from agent.orchestration.models import AgentRole, CompletionPolicy
  5. from agent.tools.models import ToolResult
  6. from agent.tools.registry import ToolRegistry
  7. from agent.trace.models import Trace
  8. def _schema(name):
  9. return {
  10. "type": "function",
  11. "function": {"name": name, "description": name, "parameters": {"type": "object", "properties": {}}},
  12. }
  13. @pytest.mark.asyncio
  14. async def test_terminal_tool_completes_without_final_assistant_text(tmp_path):
  15. registry = ToolRegistry()
  16. async def finish():
  17. return ToolResult(
  18. title="done", output="submitted", terminate_run=True, result_summary="structured-result"
  19. )
  20. registry.register(finish, schema=_schema("finish"), groups=["test"])
  21. calls = 0
  22. async def llm_call(**kwargs):
  23. nonlocal calls
  24. calls += 1
  25. return {
  26. "content": "",
  27. "tool_calls": [{"id": "call-1", "type": "function", "function": {"name": "finish", "arguments": "{}"}}],
  28. "finish_reason": "tool_calls",
  29. }
  30. from agent.trace.store import FileSystemTraceStore
  31. runner = AgentRunner(
  32. trace_store=FileSystemTraceStore(str(tmp_path)),
  33. tool_registry=registry,
  34. llm_call=llm_call,
  35. )
  36. result = await runner.run_result(
  37. [{"role": "user", "content": "finish"}],
  38. RunConfig(tools=["finish"], tool_groups=[], knowledge=_disabled_knowledge()),
  39. )
  40. assert calls == 1
  41. assert result["status"] == "completed"
  42. assert result["summary"] == "structured-result"
  43. @pytest.mark.asyncio
  44. async def test_execution_layer_rejects_forged_worker_tool_call():
  45. registry = ToolRegistry()
  46. executed = False
  47. async def agent():
  48. nonlocal executed
  49. executed = True
  50. return "bad"
  51. registry.register(agent, schema=_schema("agent"))
  52. register_preset(
  53. "test_worker_policy",
  54. AgentPreset(role=AgentRole.WORKER, allowed_tools=["agent"], max_iterations=5, skills=[]),
  55. )
  56. runner = AgentRunner(tool_registry=registry, llm_call=lambda **_: None, task_coordinator=object())
  57. config = RunConfig(
  58. agent_type="test_worker_policy",
  59. completion_policy=CompletionPolicy.EXPLICIT_VALIDATION,
  60. tools=["agent"],
  61. tool_groups=None,
  62. )
  63. trace = Trace(trace_id="worker-trace", mode="agent", agent_role="worker", context={})
  64. result = await runner._execute_authorized_tool(
  65. "agent", {}, "call", config, trace, None, 1
  66. )
  67. assert result["error"] == "unauthorized_tool"
  68. assert executed is False
  69. @pytest.mark.asyncio
  70. async def test_framework_context_overrides_host_spoofing():
  71. registry = ToolRegistry()
  72. register_preset(
  73. "test_worker_context",
  74. AgentPreset(role=AgentRole.WORKER, allowed_tools=[], max_iterations=5, skills=[]),
  75. )
  76. runner = AgentRunner(tool_registry=registry, llm_call=lambda **_: None, task_coordinator="real")
  77. config = RunConfig(
  78. agent_type="test_worker_context",
  79. completion_policy=CompletionPolicy.EXPLICIT_VALIDATION,
  80. context={"role": "planner", "task_id": "spoofed", "coordinator": "fake"},
  81. )
  82. trace = Trace(
  83. trace_id="trace", mode="agent", agent_role="worker",
  84. context={"root_trace_id": "root", "task_id": "real-task", "attempt_id": "attempt"},
  85. )
  86. context = runner._build_protected_tool_context(config, trace, None, 1)
  87. assert context["role"] == "worker"
  88. assert context["task_id"] == "real-task"
  89. assert context["coordinator"] == "real"
  90. def _disabled_knowledge():
  91. from agent.tools.builtin.knowledge import KnowledgeConfig
  92. return KnowledgeConfig(enable_extraction=False, enable_completion_extraction=False, enable_injection=False)