Skip to content

Commit b09a5bf

Browse files
authored
Create a utils directory (#195)
Moves utils files into a separate directory so we don't end up with a massive utils file --- [//]: # (BEGIN SAPLING FOOTER) * #196 * __->__ #195
2 parents dd651ce + 63aa5d1 commit b09a5bf

24 files changed

+449
-104
lines changed

Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ mypy:
1818
tests:
1919
uv run pytest
2020

21+
.PHONY: snapshots-fix
22+
snapshots-fix:
23+
uv run pytest --inline-snapshot=fix
24+
25+
.PHONY: snapshots-create
26+
snapshots-create:
27+
uv run pytest --inline-snapshot=create
28+
2129
.PHONY: old_version_tests
2230
old_version_tests:
2331
UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m pytest

examples/basic/hello_world_jupyter.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
44

55
# Intended for Jupyter notebooks where there's an existing event loop
6-
result = await Runner.run(agent, "Write a haiku about recursion in programming.") # type: ignore[top-level-await] # noqa: F704
6+
result = await Runner.run(agent, "Write a haiku about recursion in programming.") # type: ignore[top-level-await] # noqa: F704
77
print(result.final_output)
88

99
# Code within code loops,

pyproject.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ dev = [
4747
"mkdocstrings[python]>=0.28.0",
4848
"coverage>=7.6.12",
4949
"playwright==1.50.0",
50+
"inline-snapshot>=0.20.7",
5051
]
5152
[tool.uv.workspace]
5253
members = ["agents"]
@@ -116,4 +117,7 @@ filterwarnings = [
116117
]
117118
markers = [
118119
"allow_call_model_methods: mark test as allowing calls to real model implementations",
119-
]
120+
]
121+
122+
[tool.inline-snapshot]
123+
format-command="ruff format --stdin-filename {filename}"

src/agents/_run_impl.py

+12-12
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from openai.types.responses.response_input_param import ComputerCallOutput
2626
from openai.types.responses.response_reasoning_item import ResponseReasoningItem
2727

28-
from . import _utils
2928
from .agent import Agent
3029
from .agent_output import AgentOutputSchema
3130
from .computer import AsyncComputer, Computer
@@ -59,6 +58,7 @@
5958
handoff_span,
6059
trace,
6160
)
61+
from .util import _coro, _error_tracing
6262

6363
if TYPE_CHECKING:
6464
from .run import RunConfig
@@ -293,7 +293,7 @@ def process_model_response(
293293
elif isinstance(output, ResponseComputerToolCall):
294294
items.append(ToolCallItem(raw_item=output, agent=agent))
295295
if not computer_tool:
296-
_utils.attach_error_to_current_span(
296+
_error_tracing.attach_error_to_current_span(
297297
SpanError(
298298
message="Computer tool not found",
299299
data={},
@@ -324,7 +324,7 @@ def process_model_response(
324324
# Regular function tool call
325325
else:
326326
if output.name not in function_map:
327-
_utils.attach_error_to_current_span(
327+
_error_tracing.attach_error_to_current_span(
328328
SpanError(
329329
message="Tool not found",
330330
data={"tool_name": output.name},
@@ -368,7 +368,7 @@ async def run_single_tool(
368368
(
369369
agent.hooks.on_tool_start(context_wrapper, agent, func_tool)
370370
if agent.hooks
371-
else _utils.noop_coroutine()
371+
else _coro.noop_coroutine()
372372
),
373373
func_tool.on_invoke_tool(context_wrapper, tool_call.arguments),
374374
)
@@ -378,11 +378,11 @@ async def run_single_tool(
378378
(
379379
agent.hooks.on_tool_end(context_wrapper, agent, func_tool, result)
380380
if agent.hooks
381-
else _utils.noop_coroutine()
381+
else _coro.noop_coroutine()
382382
),
383383
)
384384
except Exception as e:
385-
_utils.attach_error_to_current_span(
385+
_error_tracing.attach_error_to_current_span(
386386
SpanError(
387387
message="Error running tool",
388388
data={"tool_name": func_tool.name, "error": str(e)},
@@ -502,7 +502,7 @@ async def execute_handoffs(
502502
source=agent,
503503
)
504504
if agent.hooks
505-
else _utils.noop_coroutine()
505+
else _coro.noop_coroutine()
506506
),
507507
)
508508

@@ -520,7 +520,7 @@ async def execute_handoffs(
520520
new_items=tuple(new_step_items),
521521
)
522522
if not callable(input_filter):
523-
_utils.attach_error_to_span(
523+
_error_tracing.attach_error_to_span(
524524
span_handoff,
525525
SpanError(
526526
message="Invalid input filter",
@@ -530,7 +530,7 @@ async def execute_handoffs(
530530
raise UserError(f"Invalid input filter: {input_filter}")
531531
filtered = input_filter(handoff_input_data)
532532
if not isinstance(filtered, HandoffInputData):
533-
_utils.attach_error_to_span(
533+
_error_tracing.attach_error_to_span(
534534
span_handoff,
535535
SpanError(
536536
message="Invalid input filter result",
@@ -591,7 +591,7 @@ async def run_final_output_hooks(
591591
hooks.on_agent_end(context_wrapper, agent, final_output),
592592
agent.hooks.on_end(context_wrapper, agent, final_output)
593593
if agent.hooks
594-
else _utils.noop_coroutine(),
594+
else _coro.noop_coroutine(),
595595
)
596596

597597
@classmethod
@@ -706,7 +706,7 @@ async def execute(
706706
(
707707
agent.hooks.on_tool_start(context_wrapper, agent, action.computer_tool)
708708
if agent.hooks
709-
else _utils.noop_coroutine()
709+
else _coro.noop_coroutine()
710710
),
711711
output_func,
712712
)
@@ -716,7 +716,7 @@ async def execute(
716716
(
717717
agent.hooks.on_tool_end(context_wrapper, agent, action.computer_tool, output)
718718
if agent.hooks
719-
else _utils.noop_coroutine()
719+
else _coro.noop_coroutine()
720720
),
721721
)
722722

src/agents/_utils.py

-61
This file was deleted.

src/agents/agent.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
from dataclasses import dataclass, field
77
from typing import TYPE_CHECKING, Any, Callable, Generic, cast
88

9-
from . import _utils
10-
from ._utils import MaybeAwaitable
119
from .guardrail import InputGuardrail, OutputGuardrail
1210
from .handoffs import Handoff
1311
from .items import ItemHelpers
@@ -16,6 +14,8 @@
1614
from .models.interface import Model
1715
from .run_context import RunContextWrapper, TContext
1816
from .tool import Tool, function_tool
17+
from .util import _transforms
18+
from .util._types import MaybeAwaitable
1919

2020
if TYPE_CHECKING:
2121
from .lifecycle import AgentHooks
@@ -126,7 +126,7 @@ def as_tool(
126126
"""
127127

128128
@function_tool(
129-
name_override=tool_name or _utils.transform_string_function_style(self.name),
129+
name_override=tool_name or _transforms.transform_string_function_style(self.name),
130130
description_override=tool_description or "",
131131
)
132132
async def run_agent(context: RunContextWrapper, input: str) -> str:

src/agents/agent_output.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
from pydantic import BaseModel, TypeAdapter
55
from typing_extensions import TypedDict, get_args, get_origin
66

7-
from . import _utils
87
from .exceptions import ModelBehaviorError, UserError
98
from .strict_schema import ensure_strict_json_schema
109
from .tracing import SpanError
10+
from .util import _error_tracing, _json
1111

1212
_WRAPPER_DICT_KEY = "response"
1313

@@ -87,10 +87,10 @@ def validate_json(self, json_str: str, partial: bool = False) -> Any:
8787
"""Validate a JSON string against the output type. Returns the validated object, or raises
8888
a `ModelBehaviorError` if the JSON is invalid.
8989
"""
90-
validated = _utils.validate_json(json_str, self._type_adapter, partial)
90+
validated = _json.validate_json(json_str, self._type_adapter, partial)
9191
if self._is_wrapped:
9292
if not isinstance(validated, dict):
93-
_utils.attach_error_to_current_span(
93+
_error_tracing.attach_error_to_current_span(
9494
SpanError(
9595
message="Invalid JSON",
9696
data={"details": f"Expected a dict, got {type(validated)}"},
@@ -101,7 +101,7 @@ def validate_json(self, json_str: str, partial: bool = False) -> Any:
101101
)
102102

103103
if _WRAPPER_DICT_KEY not in validated:
104-
_utils.attach_error_to_current_span(
104+
_error_tracing.attach_error_to_current_span(
105105
SpanError(
106106
message="Invalid JSON",
107107
data={"details": f"Could not find key {_WRAPPER_DICT_KEY} in JSON"},

src/agents/guardrail.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
from typing_extensions import TypeVar
99

10-
from ._utils import MaybeAwaitable
1110
from .exceptions import UserError
1211
from .items import TResponseInputItem
1312
from .run_context import RunContextWrapper, TContext
13+
from .util._types import MaybeAwaitable
1414

1515
if TYPE_CHECKING:
1616
from .agent import Agent

src/agents/handoffs.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
from pydantic import TypeAdapter
99
from typing_extensions import TypeAlias, TypeVar
1010

11-
from . import _utils
1211
from .exceptions import ModelBehaviorError, UserError
1312
from .items import RunItem, TResponseInputItem
1413
from .run_context import RunContextWrapper, TContext
1514
from .strict_schema import ensure_strict_json_schema
1615
from .tracing.spans import SpanError
16+
from .util import _error_tracing, _json, _transforms
1717

1818
if TYPE_CHECKING:
1919
from .agent import Agent
@@ -104,7 +104,7 @@ def get_transfer_message(self, agent: Agent[Any]) -> str:
104104

105105
@classmethod
106106
def default_tool_name(cls, agent: Agent[Any]) -> str:
107-
return _utils.transform_string_function_style(f"transfer_to_{agent.name}")
107+
return _transforms.transform_string_function_style(f"transfer_to_{agent.name}")
108108

109109
@classmethod
110110
def default_tool_description(cls, agent: Agent[Any]) -> str:
@@ -192,15 +192,15 @@ async def _invoke_handoff(
192192
) -> Agent[Any]:
193193
if input_type is not None and type_adapter is not None:
194194
if input_json is None:
195-
_utils.attach_error_to_current_span(
195+
_error_tracing.attach_error_to_current_span(
196196
SpanError(
197197
message="Handoff function expected non-null input, but got None",
198198
data={"details": "input_json is None"},
199199
)
200200
)
201201
raise ModelBehaviorError("Handoff function expected non-null input, but got None")
202202

203-
validated_input = _utils.validate_json(
203+
validated_input = _json.validate_json(
204204
json_str=input_json,
205205
type_adapter=type_adapter,
206206
partial=False,

src/agents/result.py

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .logger import logger
1818
from .stream_events import StreamEvent
1919
from .tracing import Trace
20+
from .util._pretty_print import pretty_print_result, pretty_print_run_result_streaming
2021

2122
if TYPE_CHECKING:
2223
from ._run_impl import QueueCompleteSentinel
@@ -89,6 +90,9 @@ def last_agent(self) -> Agent[Any]:
8990
"""The last agent that was run."""
9091
return self._last_agent
9192

93+
def __str__(self) -> str:
94+
return pretty_print_result(self)
95+
9296

9397
@dataclass
9498
class RunResultStreaming(RunResultBase):
@@ -216,3 +220,6 @@ def _cleanup_tasks(self):
216220

217221
if self._output_guardrails_task and not self._output_guardrails_task.done():
218222
self._output_guardrails_task.cancel()
223+
224+
def __str__(self) -> str:
225+
return pretty_print_run_result_streaming(self)

0 commit comments

Comments
 (0)