Skip to content

Commit 5df41d3

Browse files
feat: #2886 add convenience properties (tool_name, call_id) to tool items (#3027)
Co-authored-by: Daniel Cufiño <dancufino@gmail.com>
1 parent 2eb8713 commit 5df41d3

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

src/agents/items.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,20 @@ class ToolCallItem(RunItemBase[Any]):
362362
tool_origin: ToolOrigin | None = None
363363
"""Optional metadata describing the source of a function-tool-backed item."""
364364

365+
@property
366+
def tool_name(self) -> str | None:
367+
"""Return the tool name from the raw item, if available."""
368+
if isinstance(self.raw_item, dict):
369+
return self.raw_item.get("name")
370+
return getattr(self.raw_item, "name", None)
371+
372+
@property
373+
def call_id(self) -> str | None:
374+
"""Return the call identifier from the raw item, if available."""
375+
if isinstance(self.raw_item, dict):
376+
return self.raw_item.get("call_id") or self.raw_item.get("id")
377+
return getattr(self.raw_item, "call_id", None) or getattr(self.raw_item, "id", None)
378+
365379

366380
ToolCallOutputTypes: TypeAlias = (
367381
FunctionCallOutput
@@ -389,6 +403,14 @@ class ToolCallOutputItem(RunItemBase[Any]):
389403
tool_origin: ToolOrigin | None = None
390404
"""Optional metadata describing the source of a function-tool-backed item."""
391405

406+
@property
407+
def call_id(self) -> str | None:
408+
"""Return the call identifier from the raw item, if available."""
409+
if isinstance(self.raw_item, dict):
410+
cid = self.raw_item.get("call_id") or self.raw_item.get("id")
411+
return str(cid) if cid is not None else None
412+
return getattr(self.raw_item, "call_id", None) or getattr(self.raw_item, "id", None)
413+
392414
def to_input_item(self) -> TResponseInputItem:
393415
"""Converts the tool output into an input item for the next model turn.
394416

tests/test_items_helpers.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,3 +615,106 @@ def test_tool_call_item_to_input_item_keeps_payload_api_safe() -> None:
615615
assert result_dict["type"] == "function_call"
616616
assert "title" not in result_dict
617617
assert "description" not in result_dict
618+
619+
620+
def test_tool_call_item_tool_name_from_function_call() -> None:
621+
"""ToolCallItem.tool_name should return the name attribute from a typed raw item."""
622+
agent = Agent(name="test")
623+
raw = ResponseFunctionToolCall(
624+
id="fc1",
625+
call_id="call_1",
626+
name="my_tool",
627+
arguments="{}",
628+
type="function_call",
629+
)
630+
item = ToolCallItem(agent=agent, raw_item=raw)
631+
assert item.tool_name == "my_tool"
632+
633+
634+
def test_tool_call_item_tool_name_from_dict() -> None:
635+
"""ToolCallItem.tool_name should return the 'name' key from a dict raw item."""
636+
agent = Agent(name="test")
637+
raw: dict[str, Any] = {
638+
"type": "function_call",
639+
"name": "dict_tool",
640+
"call_id": "call_1",
641+
"arguments": "{}",
642+
}
643+
item = ToolCallItem(agent=agent, raw_item=raw)
644+
assert item.tool_name == "dict_tool"
645+
646+
647+
def test_tool_call_item_tool_name_returns_none_when_missing() -> None:
648+
"""ToolCallItem.tool_name should be None when the raw item has no name attribute."""
649+
agent = Agent(name="test")
650+
raw = ResponseFileSearchToolCall(
651+
id="fs1",
652+
queries=["q"],
653+
status="completed",
654+
type="file_search_call",
655+
)
656+
item = ToolCallItem(agent=agent, raw_item=raw)
657+
assert item.tool_name is None
658+
659+
660+
def test_tool_call_item_call_id_from_function_call() -> None:
661+
"""ToolCallItem.call_id should return the call_id attribute from a typed raw item."""
662+
agent = Agent(name="test")
663+
raw = ResponseFunctionToolCall(
664+
id="fc1",
665+
call_id="call_abc",
666+
name="t",
667+
arguments="{}",
668+
type="function_call",
669+
)
670+
item = ToolCallItem(agent=agent, raw_item=raw)
671+
assert item.call_id == "call_abc"
672+
673+
674+
def test_tool_call_item_call_id_falls_back_to_id() -> None:
675+
"""ToolCallItem.call_id should fall back to id when call_id is absent."""
676+
agent = Agent(name="test")
677+
raw = ResponseFileSearchToolCall(
678+
id="fs_xyz",
679+
queries=["q"],
680+
status="completed",
681+
type="file_search_call",
682+
)
683+
item = ToolCallItem(agent=agent, raw_item=raw)
684+
assert item.call_id == "fs_xyz"
685+
686+
687+
def test_tool_call_item_call_id_from_dict() -> None:
688+
"""ToolCallItem.call_id should return the 'call_id' key from a dict raw item."""
689+
agent = Agent(name="test")
690+
raw: dict[str, Any] = {
691+
"type": "function_call",
692+
"name": "t",
693+
"call_id": "call_dict_id",
694+
"arguments": "{}",
695+
}
696+
item = ToolCallItem(agent=agent, raw_item=raw)
697+
assert item.call_id == "call_dict_id"
698+
699+
700+
def test_tool_call_output_item_call_id_from_function_call_output() -> None:
701+
"""ToolCallOutputItem.call_id should return call_id from the FunctionCallOutput dict."""
702+
agent = Agent(name="test")
703+
raw = {
704+
"type": "function_call_output",
705+
"call_id": "call_out_1",
706+
"output": "ok",
707+
}
708+
item = ToolCallOutputItem(agent=agent, raw_item=raw, output="ok")
709+
assert item.call_id == "call_out_1"
710+
711+
712+
def test_tool_call_output_item_call_id_returns_none_when_missing() -> None:
713+
"""ToolCallOutputItem.call_id should be None when neither call_id nor id are present."""
714+
agent = Agent(name="test")
715+
raw = {
716+
"type": "function_call_output",
717+
"output": "ok",
718+
}
719+
item = ToolCallOutputItem(agent=agent, raw_item=raw, output="ok")
720+
assert item.call_id is None

0 commit comments

Comments
 (0)