Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions src/agents/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,17 +364,25 @@ class ToolCallItem(RunItemBase[Any]):

@property
def tool_name(self) -> str | None:
"""Return the tool name from the raw item, if available."""
"""Return the tool name from the raw item when available."""
if isinstance(self.raw_item, dict):
return self.raw_item.get("name")
return getattr(self.raw_item, "name", None)
candidate = self.raw_item.get("name") or self.raw_item.get("tool_name")
else:
candidate = getattr(self.raw_item, "name", None) or getattr(
self.raw_item, "tool_name", None
)
return str(candidate) if candidate is not None else None

@property
def call_id(self) -> str | None:
"""Return the call identifier from the raw item, if available."""
"""Return the call identifier from the raw item when available."""
if isinstance(self.raw_item, dict):
return self.raw_item.get("call_id") or self.raw_item.get("id")
return getattr(self.raw_item, "call_id", None) or getattr(self.raw_item, "id", None)
candidate = self.raw_item.get("call_id") or self.raw_item.get("id")
else:
candidate = getattr(self.raw_item, "call_id", None) or getattr(
self.raw_item, "id", None
)
return str(candidate) if candidate is not None else None


ToolCallOutputTypes: TypeAlias = (
Expand Down Expand Up @@ -405,11 +413,14 @@ class ToolCallOutputItem(RunItemBase[Any]):

@property
def call_id(self) -> str | None:
"""Return the call identifier from the raw item, if available."""
"""Return the call identifier from the raw item when available."""
if isinstance(self.raw_item, dict):
cid = self.raw_item.get("call_id") or self.raw_item.get("id")
return str(cid) if cid is not None else None
return getattr(self.raw_item, "call_id", None) or getattr(self.raw_item, "id", None)
candidate = self.raw_item.get("call_id") or self.raw_item.get("id")
else:
candidate = getattr(self.raw_item, "call_id", None) or getattr(
self.raw_item, "id", None
)
return str(candidate) if candidate is not None else None

def to_input_item(self) -> TResponseInputItem:
"""Converts the tool output into an input item for the next model turn.
Expand Down
13 changes: 11 additions & 2 deletions src/agents/sandbox/capabilities/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,17 @@ async def load_skill(
src_root = self._src_root(source_grants=source_grants)
if src_root is None:
raise SkillsConfigError(
message="lazy skill source directory is unavailable",
context={"skill_name": skill_name},
message=(
"lazy skill source directory is unavailable; "
"LocalDirLazySkillSource expects a host filesystem path, not a path "
"inside the sandbox"
),
context={
"skill_name": skill_name,
"configured_source_path": None
if self.source.src is None
else str(self.source.src),
},
)

matches = [
Expand Down
12 changes: 11 additions & 1 deletion tests/sandbox/capabilities/test_skills_capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,19 @@ async def test_load_skill_rejects_missing_lazy_source_directory(self, tmp_path:
)
)

with pytest.raises(SkillsConfigError):
with pytest.raises(SkillsConfigError) as exc_info:
await capability.load_skill("missing-skill")

assert (
exc_info.value.message
== "lazy skill source directory is unavailable; "
"LocalDirLazySkillSource expects a host filesystem path, not a path inside the sandbox"
)
assert exc_info.value.context == {
"skill_name": "missing-skill",
"configured_source_path": str(tmp_path / "missing-skills"),
}

@pytest.mark.asyncio
async def test_load_skill_rejects_ambiguous_skill_name(self, tmp_path: Path) -> None:
workspace_root = tmp_path / "workspace"
Expand Down
47 changes: 47 additions & 0 deletions tests/test_run_internal_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from agents.items import (
ReasoningItem,
ToolCallItem,
ToolCallOutputItem,
ToolSearchCallItem,
ToolSearchOutputItem,
TResponseInputItem,
Expand Down Expand Up @@ -550,6 +551,52 @@ def test_run_item_to_input_item_omits_tool_call_metadata() -> None:
assert "title" not in result_dict


def test_tool_call_item_exposes_convenience_properties_for_typed_raw_items() -> None:
agent = Agent(name="A")
item = ToolCallItem(
agent=agent,
raw_item=ResponseFunctionToolCall(
id="fc_123",
call_id="call_123",
name="lookup_account",
arguments="{}",
type="function_call",
status="completed",
),
)

assert item.tool_name == "lookup_account"
assert item.call_id == "call_123"


def test_tool_call_item_exposes_convenience_properties_for_dict_raw_items() -> None:
agent = Agent(name="A")
item = ToolCallItem(
agent=agent,
raw_item={
"id": "fc_456",
"call_id": "call_456",
"name": "lookup_account",
"arguments": "{}",
"type": "function_call",
},
)

assert item.tool_name == "lookup_account"
assert item.call_id == "call_456"


def test_tool_call_output_item_exposes_call_id_for_dict_raw_items() -> None:
agent = Agent(name="A")
item = ToolCallOutputItem(
agent=agent,
raw_item={"type": "function_call_output", "call_id": "call_456", "output": "ok"},
output="ok",
)

assert item.call_id == "call_456"


def test_normalize_input_items_for_api_strips_internal_tool_call_metadata() -> None:
item = cast(
TResponseInputItem,
Expand Down
Loading