Skip to content

Commit 1f55c42

Browse files
authored
fix: show the GA computer alias in tracing (#2641)
1 parent eab77a9 commit 1f55c42

File tree

8 files changed

+90
-16
lines changed

8 files changed

+90
-16
lines changed

examples/tools/computer_use.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
Button,
1616
ComputerProvider,
1717
ComputerTool,
18-
Environment,
1918
RunContextWrapper,
2019
Runner,
2120
trace,
@@ -110,10 +109,6 @@ def page(self) -> Page:
110109
assert self._page is not None
111110
return self._page
112111

113-
@property
114-
def environment(self) -> Environment:
115-
return "browser"
116-
117112
@property
118113
def dimensions(self) -> tuple[int, int]:
119114
return (1024, 768)

src/agents/_tool_identity.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ def get_tool_call_trace_name(tool_call: Any) -> str | None:
161161
)
162162

163163

164+
def get_tool_trace_name_for_tool(tool: Any) -> str | None:
165+
"""Return the trace display name for a tool definition."""
166+
trace_name = getattr(tool, "trace_name", None)
167+
if isinstance(trace_name, str) and trace_name:
168+
return trace_name
169+
170+
tool_name = getattr(tool, "name", None)
171+
return tool_name if isinstance(tool_name, str) and tool_name else None
172+
173+
164174
def _remove_tool_call_namespace(tool_call: Any) -> Any:
165175
"""Return a shallow copy of the tool call without its namespace field."""
166176
if isinstance(tool_call, dict):

src/agents/run.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typing_extensions import Unpack
99

1010
from . import _debug
11+
from ._tool_identity import get_tool_trace_name_for_tool
1112
from .agent import Agent
1213
from .agent_tool_state import set_agent_tool_state_scope
1314
from .exceptions import (
@@ -858,7 +859,11 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult:
858859
output_type=output_type_name,
859860
)
860861
current_span.start(mark_as_current=True)
861-
current_span.span_data.tools = [t.name for t in all_tools]
862+
current_span.span_data.tools = [
863+
tool_name
864+
for tool in all_tools
865+
if (tool_name := get_tool_trace_name_for_tool(tool)) is not None
866+
]
862867

863868
current_turn += 1
864869
if current_turn > max_turns:

src/agents/run_internal/run_loop.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
NamedToolLookupKey,
2222
build_function_tool_lookup_map,
2323
get_function_tool_lookup_key_for_call,
24+
get_tool_trace_name_for_tool,
2425
)
2526
from ..agent import Agent
2627
from ..agent_output import AgentOutputSchemaBase
@@ -708,7 +709,11 @@ async def _save_stream_items_without_count(
708709
output_type=output_type_name,
709710
)
710711
current_span.start(mark_as_current=True)
711-
tool_names = [t.name for t in all_tools]
712+
tool_names = [
713+
tool_name
714+
for tool in all_tools
715+
if (tool_name := get_tool_trace_name_for_tool(tool)) is not None
716+
]
712717
current_span.span_data.tools = tool_names
713718

714719
current_turn += 1

src/agents/run_internal/tool_actions.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
)
1818
from openai.types.responses.response_input_param import ComputerCallOutput
1919

20-
from .._tool_identity import get_mapping_or_attr
20+
from .._tool_identity import get_mapping_or_attr, get_tool_trace_name_for_tool
2121
from ..agent import Agent
2222
from ..exceptions import ModelBehaviorError
2323
from ..items import RunItem, ToolCallOutputItem
@@ -89,8 +89,8 @@ def _serialize_trace_payload(payload: Any) -> str:
8989
class ComputerAction:
9090
"""Execute computer tool actions and emit screenshot outputs with hooks fired."""
9191

92-
TRACE_TOOL_NAME = "computer_use_preview"
93-
"""Keep tracing aligned with the released computer tool name used by hooks and RunState."""
92+
TRACE_TOOL_NAME = "computer"
93+
"""Tracing should expose the GA computer tool alias."""
9494

9595
@classmethod
9696
async def execute(
@@ -104,6 +104,7 @@ async def execute(
104104
acknowledged_safety_checks: list[ComputerCallOutputAcknowledgedSafetyCheck] | None = None,
105105
) -> RunItem:
106106
"""Run a computer action, capturing a screenshot and notifying hooks."""
107+
trace_tool_name = get_tool_trace_name_for_tool(action.computer_tool) or cls.TRACE_TOOL_NAME
107108

108109
async def _run_action(span: Any | None) -> RunItem:
109110
if span and config.trace_include_sensitive_data:
@@ -137,7 +138,7 @@ async def _run_action(span: Any | None) -> RunItem:
137138
SpanError(
138139
message="Error running tool",
139140
data={
140-
"tool_name": cls.TRACE_TOOL_NAME,
141+
"tool_name": trace_tool_name,
141142
"error": trace_error,
142143
},
143144
)
@@ -174,7 +175,7 @@ async def _run_action(span: Any | None) -> RunItem:
174175

175176
return await with_tool_function_span(
176177
config=config,
177-
tool_name=cls.TRACE_TOOL_NAME,
178+
tool_name=trace_tool_name,
178179
fn=_run_action,
179180
)
180181

src/agents/tool.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,11 +519,16 @@ def __post_init__(self) -> None:
519519

520520
@property
521521
def name(self):
522-
# Keep the released preview-era runtime name for hooks, tracing, and
523-
# persisted RunState compatibility. The Responses serializer selects
524-
# the actual wire tool type separately.
522+
# Keep the released preview-era runtime name for hooks and persisted
523+
# RunState compatibility. The Responses serializer selects the actual
524+
# wire tool type separately.
525525
return "computer_use_preview"
526526

527+
@property
528+
def trace_name(self):
529+
# Tracing should display the GA tool alias even while runtime names preserve compatibility.
530+
return "computer"
531+
527532

528533
@dataclass
529534
class _ResolvedComputer:

tests/test_computer_action.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
RunConfig,
3838
RunContextWrapper,
3939
RunHooks,
40+
Runner,
4041
set_tracing_disabled,
4142
trace,
4243
)
@@ -45,6 +46,8 @@
4546
from agents.run_internal.run_loop import ComputerAction, ToolRunComputerAction
4647
from agents.tool import ComputerToolSafetyCheckData
4748

49+
from .fake_model import FakeModel
50+
from .test_responses import get_text_message
4851
from .testing_processor import SPAN_PROCESSOR_TESTING
4952

5053

@@ -61,6 +64,19 @@ def _get_function_span(tool_name: str) -> dict[str, Any]:
6164
raise AssertionError(f"Function span for tool '{tool_name}' not found")
6265

6366

67+
def _get_agent_span(agent_name: str) -> dict[str, Any]:
68+
for span in SPAN_PROCESSOR_TESTING.get_ordered_spans(including_empty=True):
69+
exported = span.export()
70+
if not exported:
71+
continue
72+
span_data = exported.get("span_data")
73+
if not isinstance(span_data, dict):
74+
continue
75+
if span_data.get("type") == "agent" and span_data.get("name") == agent_name:
76+
return exported
77+
raise AssertionError(f"Agent span for '{agent_name}' not found")
78+
79+
6480
class LoggingComputer(Computer):
6581
"""A `Computer` implementation that logs calls to its methods for verification in tests."""
6682

@@ -403,13 +419,49 @@ async def test_execute_emits_function_span() -> None:
403419
)
404420

405421
assert isinstance(result, ToolCallOutputItem)
406-
assert ComputerAction.TRACE_TOOL_NAME == "computer_use_preview"
422+
assert ComputerAction.TRACE_TOOL_NAME == "computer"
407423
function_span = _get_function_span(ComputerAction.TRACE_TOOL_NAME)
408424
span_data = cast(dict[str, Any], function_span["span_data"])
409425
assert span_data.get("input") is not None
410426
assert cast(str, span_data.get("output", "")).startswith("data:image/png;base64,")
411427

412428

429+
@pytest.mark.asyncio
430+
async def test_runner_trace_lists_ga_computer_tool_name() -> None:
431+
SPAN_PROCESSOR_TESTING.clear()
432+
433+
computer = LoggingComputer(screenshot_return="trace_img")
434+
tool_call = ResponseComputerToolCall(
435+
id="tool_trace_agent_tools",
436+
type="computer_call",
437+
action=ActionScreenshot(type="screenshot"),
438+
call_id="tool_trace_agent_tools",
439+
pending_safety_checks=[],
440+
status="completed",
441+
)
442+
model = FakeModel(tracing_enabled=True)
443+
model.add_multiple_turn_outputs(
444+
[
445+
[tool_call],
446+
[get_text_message("done")],
447+
]
448+
)
449+
agent = Agent(
450+
name="test_agent_trace_tools",
451+
model=model,
452+
tools=[ComputerTool(computer=computer)],
453+
)
454+
455+
set_tracing_disabled(False)
456+
with trace("computer-agent-span-test"):
457+
result = await Runner.run(agent, input="take a screenshot")
458+
459+
assert result.final_output == "done"
460+
agent_span = _get_agent_span(agent.name)
461+
span_data = cast(dict[str, Any], agent_span["span_data"])
462+
assert span_data["tools"] == ["computer"]
463+
464+
413465
@pytest.mark.asyncio
414466
async def test_execute_emits_batched_actions_in_function_span() -> None:
415467
computer = LoggingComputer(screenshot_return="trace_img")

tests/test_tool_metadata.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def test_tool_name_properties() -> None:
4242
assert FileSearchTool(vector_store_ids=[]).name == "file_search"
4343
assert WebSearchTool().name == "web_search"
4444
assert ComputerTool(computer=dummy_computer).name == "computer_use_preview"
45+
assert ComputerTool(computer=dummy_computer).trace_name == "computer"
4546
assert HostedMCPTool(tool_config=dummy_mcp).name == "hosted_mcp"
4647
assert CodeInterpreterTool(tool_config=dummy_code).name == "code_interpreter"
4748
assert ImageGenerationTool(tool_config=dummy_image).name == "image_generation"

0 commit comments

Comments
 (0)