| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- from __future__ import annotations
- import sys
- from pathlib import Path
- REPO_ROOT = Path(__file__).resolve().parents[1]
- if str(REPO_ROOT) not in sys.path:
- sys.path.insert(0, str(REPO_ROOT))
- from tests.conftest import build_postgres_database_url, prepare_known_service_import
- prepare_known_service_import("agent-service")
- from core_domain import ChatCompletionResponseContract
- class FakeModelClient:
- def __init__(self) -> None:
- self.calls = 0
- def create_chat_completion(self, payload: object) -> ChatCompletionResponseContract:
- self.calls += 1
- if self.calls == 1:
- return ChatCompletionResponseContract(
- model="fake",
- content=(
- '{"action":"tool","tool_code":"lookup_order",'
- '"input_json":{"order_id":"123"}}'
- ))
- return ChatCompletionResponseContract(
- model="fake",
- content='{"action":"finish","answer":"Order lookup attempted."}')
- class FakeFunctionCallingModelClient:
- def __init__(self) -> None:
- self.calls = 0
- self.request_tools: list[object] = []
- def create_chat_completion(self, payload: object) -> ChatCompletionResponseContract:
- self.calls += 1
- self.request_tools.append(getattr(payload, "tools_json", []))
- if self.calls == 1:
- return ChatCompletionResponseContract(
- model="fake",
- content="",
- finish_reason="tool_calls",
- tool_calls_json=[
- {
- "id": "call_1",
- "type": "function",
- "function": {
- "name": "lookup_order",
- "arguments": '{"order_id":"123"}',
- },
- }
- ])
- return ChatCompletionResponseContract(
- model="fake",
- content='{"action":"finish","answer":"Function call handled."}')
- def test_react_loop_records_steps_and_tool_invocation(tmp_path: Path) -> None:
- prepare_known_service_import("agent-service")
- from app.application.services import AgentApplicationService
- from app.bootstrap.settings import AgentServiceSettings
- from app.db.session import build_session_factory
- from app.domain.repositories import (
- AgentDefinitionRepository,
- AgentRunRepository,
- AgentToolInvocationRepository,
- AgentConfigRepository,
- )
- from app.schemas.agent import (
- AgentCreateRequest,
- AgentRunCreateRequest,
- AgentRunExecuteRequest,
- AgentConfigCreateRequest,
- )
- from core_db import Base
- session_factory = build_session_factory(
- settings=AgentServiceSettings(
- database_url=build_postgres_database_url(tmp_path, "agent-service-react")))
- engine = session_factory.kw["bind"]
- Base.metadata.create_all(bind=engine)
- with session_factory() as db:
- service = AgentApplicationService(
- agent_repository=AgentDefinitionRepository(db),
- agent_config_repository=AgentConfigRepository(db),
- agent_run_repository=AgentRunRepository(db),
- agent_tool_invocation_repository=AgentToolInvocationRepository(db),
- model_gateway_client=FakeModelClient(),
- memory_client=None,
- tool_client=None,
- skill_client=None,
- event_client=None,
- react_max_steps=3)
- agent = service.create_agent(
- AgentCreateRequest(code="react", name="React")
- )
- service.create_agent_config(
- AgentConfigCreateRequest(
- agent_id=agent.id,
- system_prompt="Use ReAct.",
- model_config={"react_enabled": True, "react_max_steps": 3},
- memory_policy={"enabled": False, "write_enabled": False},
- tool_refs=[
- {"tool_code": "lookup_order", "required": True, "config_json": {}}
- ])
- )
- run = service.create_agent_run(
- AgentRunCreateRequest(
- agent_id=agent.id,
- input_text="check order")
- )
- result = service.execute_agent_run(
- agent_run_id=run.id,
- payload=AgentRunExecuteRequest(worker_key="test"))
- assert result is not None
- assert result.status == "completed"
- assert result.output_text == "Order lookup attempted."
- assert result.output_json is not None
- assert result.output_json["react_enabled"] is True
- assert len(result.output_json["react_steps"]) == 2
- invocations = service.list_agent_tool_invocations(
- agent_run_id=run.id)
- assert len(invocations) == 2
- assert all(item.status == "skipped" for item in invocations)
- engine.dispose()
- def test_react_loop_accepts_openai_tool_calls(tmp_path: Path) -> None:
- prepare_known_service_import("agent-service")
- from app.application.services import AgentApplicationService
- from app.bootstrap.settings import AgentServiceSettings
- from app.db.session import build_session_factory
- from app.domain.repositories import (
- AgentDefinitionRepository,
- AgentRunRepository,
- AgentToolInvocationRepository,
- AgentConfigRepository,
- )
- from app.schemas.agent import (
- AgentCreateRequest,
- AgentRunCreateRequest,
- AgentRunExecuteRequest,
- AgentConfigCreateRequest,
- )
- from core_db import Base
- session_factory = build_session_factory(
- settings=AgentServiceSettings(
- database_url=build_postgres_database_url(tmp_path, "agent-service-react-fn")))
- engine = session_factory.kw["bind"]
- Base.metadata.create_all(bind=engine)
- with session_factory() as db:
- model_client = FakeFunctionCallingModelClient()
- service = AgentApplicationService(
- agent_repository=AgentDefinitionRepository(db),
- agent_config_repository=AgentConfigRepository(db),
- agent_run_repository=AgentRunRepository(db),
- agent_tool_invocation_repository=AgentToolInvocationRepository(db),
- model_gateway_client=model_client,
- memory_client=None,
- tool_client=None,
- skill_client=None,
- event_client=None,
- react_max_steps=3)
- agent = service.create_agent(
- AgentCreateRequest(code="react-fn", name="React Function")
- )
- service.create_agent_config(
- AgentConfigCreateRequest(
- agent_id=agent.id,
- system_prompt="Use ReAct.",
- model_config={
- "react_enabled": True,
- "react_max_steps": 3,
- "function_calling_enabled": True,
- },
- memory_policy={"enabled": False, "write_enabled": False},
- tool_refs=[
- {"tool_code": "lookup_order", "required": True, "config_json": {}}
- ])
- )
- run = service.create_agent_run(
- AgentRunCreateRequest(
- agent_id=agent.id,
- input_text="check order")
- )
- result = service.execute_agent_run(
- agent_run_id=run.id,
- payload=AgentRunExecuteRequest(worker_key="test"))
- assert result is not None
- assert result.status == "completed"
- assert result.output_text == "Function call handled."
- assert result.output_json is not None
- assert result.output_json["react_steps"][0]["action"]["tool_call_protocol"] == "openai"
- assert result.output_json["react_steps"][0]["action"]["input_json"] == {"order_id": "123"}
- assert model_client.request_tools[0]
- engine.dispose()
|