Skip to content

Commit 45ac282

Browse files
committed
attempt to fix openai native tool calls
1 parent c9cc0a9 commit 45ac282

3 files changed

Lines changed: 69 additions & 11 deletions

File tree

packages/ai-providers/server-ai-openai/src/ldai_openai/openai_agent_graph_runner.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
extract_usage_from_request_entry,
1515
get_ai_usage_from_response,
1616
get_tool_calls_from_run_items,
17+
is_agent_tool_instance,
18+
registry_value_to_agent_tool,
1719
)
1820

1921

@@ -46,7 +48,7 @@ def __init__(self, graph: AgentGraphDefinition, tools: ToolRegistry):
4648
Initialize the runner.
4749
4850
:param graph: The AgentGraphDefinition to execute
49-
:param tools: Registry mapping tool names to callables
51+
:param tools: Registry mapping tool names to callables or native ``Tool`` instances
5052
"""
5153
self._graph = graph
5254
self._tools = tools
@@ -128,8 +130,6 @@ def _build_agents(self, path: List[str], state: _RunState) -> Any:
128130
from agents import (
129131
Agent,
130132
Handoff,
131-
Tool,
132-
function_tool,
133133
handoff,
134134
)
135135
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
@@ -174,17 +174,21 @@ def build_node(node: AgentGraphNode, ctx: dict) -> Any:
174174
)
175175

176176
# --- tools ---
177-
agent_tools: List[Tool] = []
177+
agent_tools: List[Any] = []
178178
for tool_def in tool_defs:
179179
tool_name = tool_def.get('name', '')
180180

181181
tool_fn = self._tools.get(tool_name)
182182
if not tool_fn:
183183
continue
184184

185-
# Map fn.__name__ → config key so tracked names match the AI config.
186-
tool_name_map[tool_fn.__name__] = tool_name
187-
agent_tools.append(function_tool(tool_fn))
185+
# Map runtime tool name → LD config key for metrics (function __name__
186+
# for callables; identity for native tool instances — see get_tool_calls_from_run_items).
187+
if is_agent_tool_instance(tool_fn):
188+
tool_name_map[f'{tool_fn.name}_call'] = tool_name
189+
else:
190+
tool_name_map[tool_fn.__name__] = tool_name
191+
agent_tools.append(registry_value_to_agent_tool(tool_fn))
188192

189193
return Agent(
190194
name=sanitized_name,
@@ -286,6 +290,7 @@ def _track_tool_calls(self, result: Any, tracker: Any) -> None:
286290
"""Track all tool calls from the run result, attributed to the node that called them."""
287291
gk = tracker.graph_key if tracker is not None else None
288292
for agent_name, tool_fn_name in get_tool_calls_from_run_items(result.new_items):
293+
log.info(f"Tracking tool call: agent_name={agent_name}, tool_fn_name={tool_fn_name}")
289294
original_key = self._agent_name_map.get(agent_name, agent_name)
290295
tool_name = self._tool_name_map.get(tool_fn_name, '')
291296
node = self._graph.get_node(original_key)

packages/ai-providers/server-ai-openai/src/ldai_openai/openai_agent_runner.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from ldai.providers import AgentResult, AgentRunner, ToolRegistry
77
from ldai.providers.types import LDAIMetrics
88

9-
from ldai_openai.openai_helper import get_ai_usage_from_response
9+
from ldai_openai.openai_helper import get_ai_usage_from_response, registry_value_to_agent_tool
1010

1111

1212
class OpenAIAgentRunner(AgentRunner):
@@ -81,8 +81,6 @@ async def run(self, input: Any) -> AgentResult:
8181

8282
def _build_agent_tools(self) -> List[Any]:
8383
"""Build tool instances from LD tool definitions and registry."""
84-
from agents import function_tool
85-
8684
tools = []
8785
for td in self._tool_definitions:
8886
if not isinstance(td, dict):
@@ -93,7 +91,7 @@ def _build_agent_tools(self) -> List[Any]:
9391

9492
tool_fn = self._tools.get(name)
9593
if tool_fn:
96-
tools.append(function_tool(tool_fn))
94+
tools.append(registry_value_to_agent_tool(tool_fn))
9795
continue
9896

9997
log.warning(

packages/ai-providers/server-ai-openai/src/ldai_openai/openai_helper.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import typing
12
from typing import Any, Dict, Iterable, List, Optional, Tuple, cast
23

34
from ldai import LDMessage
@@ -113,6 +114,60 @@ def normalize_tool_types(tool_definitions: List[Any]) -> List[Dict[str, Any]]:
113114
'web_search': 'web_search_tool',
114115
}
115116

117+
# ``agents.Tool`` is a typing.Union of concrete tool classes, not a runtime class.
118+
# Using ``isinstance(x, Tool)`` raises TypeError (subscripted generics / union checks).
119+
_AGENT_TOOL_TYPES: Optional[Tuple[type, ...]] = None
120+
121+
122+
def _concrete_agent_tool_types() -> Tuple[type, ...]:
123+
"""Resolve concrete classes behind ``agents.Tool`` (a Union alias)."""
124+
try:
125+
from agents import Tool as ToolUnion
126+
except ImportError:
127+
return ()
128+
args = typing.get_args(ToolUnion)
129+
if not args:
130+
return ()
131+
out: List[type] = []
132+
for a in args:
133+
origin = getattr(a, '__origin__', None)
134+
if origin is not None and isinstance(origin, type):
135+
out.append(origin)
136+
elif isinstance(a, type):
137+
out.append(a)
138+
return tuple(out)
139+
140+
141+
def is_agent_tool_instance(value: Any) -> bool:
142+
"""True if ``value`` is already an openai-agents tool object (not a plain callable)."""
143+
global _AGENT_TOOL_TYPES
144+
if _AGENT_TOOL_TYPES is None:
145+
_AGENT_TOOL_TYPES = _concrete_agent_tool_types()
146+
if not _AGENT_TOOL_TYPES:
147+
return False
148+
return isinstance(value, _AGENT_TOOL_TYPES)
149+
150+
151+
def registry_value_to_agent_tool(value: Any) -> Any:
152+
"""
153+
Turn a ToolRegistry value into an object the OpenAI Agents SDK accepts in ``Agent(tools=…)``.
154+
155+
Plain callables are wrapped with ``function_tool``. Values that are already
156+
tool instances (e.g. ``WebSearchTool()``, ``FileSearchTool(...)``) are
157+
returned unchanged so they are not double-wrapped.
158+
"""
159+
try:
160+
from agents import function_tool
161+
except ImportError as exc:
162+
raise ImportError(
163+
"openai-agents is required for agent tools. "
164+
"Install it with: pip install openai-agents"
165+
) from exc
166+
167+
if is_agent_tool_instance(value):
168+
return value
169+
return function_tool(value)
170+
116171

117172
def get_tool_calls_from_run_items(new_items: List[Any]) -> List[Tuple[str, str]]:
118173
"""

0 commit comments

Comments
 (0)