from __future__ import annotations from collections.abc import Callable from typing import Any, Literal, TypeVar, cast from openai.types.responses import ( ResponseFunctionToolCall, ResponseOutputMessage, ResponseOutputText, ) from agents import Agent from agents._tool_identity import FunctionToolLookupKey, get_function_tool_lookup_key from agents.items import ToolApprovalItem from agents.run_context import RunContextWrapper from agents.run_state import RunState from agents.sandbox.session.sandbox_session_state import SandboxSessionState TContext = TypeVar("TContext") _AUTO_LOOKUP_KEY = object() class TestSessionState(SandboxSessionState): """Concrete ``SandboxSessionState`` subclass for tests that don't need a real backend.""" __test__ = False type: Literal["test"] = "test" def make_tool_call( call_id: str = "call_1", *, name: str = "test_tool", namespace: str | None = None, status: Literal["in_progress", "completed", "incomplete"] | None = "completed", arguments: str = "{}", call_type: Literal["function_call"] = "function_call", ) -> ResponseFunctionToolCall: """Build a ResponseFunctionToolCall with common defaults.""" kwargs: dict[str, Any] = { "type": call_type, "name": name, "call_id": call_id, "status": status, "arguments": arguments, } if namespace is not None: kwargs["namespace"] = namespace return ResponseFunctionToolCall(**kwargs) def make_tool_approval_item( agent: Agent[Any], *, call_id: str = "call_1", name: str = "test_tool", namespace: str | None = None, allow_bare_name_alias: bool = False, status: Literal["in_progress", "completed", "incomplete"] | None = "completed", arguments: str = "{}", tool_lookup_key: FunctionToolLookupKey | None | object = _AUTO_LOOKUP_KEY, ) -> ToolApprovalItem: """Create a ToolApprovalItem backed by a function call.""" resolved_tool_lookup_key: FunctionToolLookupKey | None if tool_lookup_key is _AUTO_LOOKUP_KEY: resolved_tool_lookup_key = get_function_tool_lookup_key(name, namespace) else: resolved_tool_lookup_key = cast(FunctionToolLookupKey | None, tool_lookup_key) return ToolApprovalItem( agent=agent, raw_item=make_tool_call( call_id=call_id, name=name, namespace=namespace, status=status, arguments=arguments, ), tool_namespace=namespace, tool_lookup_key=resolved_tool_lookup_key, _allow_bare_name_alias=allow_bare_name_alias, ) def make_message_output( *, message_id: str = "msg_1", text: str = "Hello", role: Literal["assistant"] = "assistant", status: Literal["in_progress", "completed", "incomplete"] = "completed", ) -> ResponseOutputMessage: """Create a minimal ResponseOutputMessage.""" return ResponseOutputMessage( id=message_id, type="message", role=role, status=status, content=[ResponseOutputText(type="output_text", text=text, annotations=[], logprobs=[])], ) def make_run_state( agent: Agent[Any], *, context: RunContextWrapper[TContext] | dict[str, Any] | None = None, original_input: Any = "input", max_turns: int | None = 3, ) -> RunState[TContext, Agent[Any]]: """Create a RunState with sensible defaults for tests.""" wrapper: RunContextWrapper[TContext] if isinstance(context, RunContextWrapper): wrapper = context else: wrapper = RunContextWrapper(context=context or {}) # type: ignore[arg-type] return RunState( context=wrapper, original_input=original_input, starting_agent=agent, max_turns=max_turns, ) async def roundtrip_state( agent: Agent[Any], state: RunState[TContext, Agent[Any]], mutate_json: Callable[[dict[str, Any]], dict[str, Any]] | None = None, ) -> RunState[TContext, Agent[Any]]: """Serialize and restore a RunState, optionally mutating the JSON in between.""" json_data = state.to_json() if mutate_json is not None: json_data = mutate_json(json_data) return await RunState.from_json(agent, json_data)