Skip to content

Commit b44ca58

Browse files
authored
[openai-agents] Populate instructions and tool definitions from Response obj (#4196)
1 parent 196d088 commit b44ca58

3 files changed

Lines changed: 74 additions & 0 deletions

File tree

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
([#4229](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4229))
1212
- Document official package metadata and README for the OpenAI Agents instrumentation.
1313
([#3859](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3859))
14+
- Populate instructions and tool definitions from Response obj.
15+
([#4196](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4196))
1416

1517
## Version 0.1.0 (2025-10-15)
1618

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,10 +1056,20 @@ def _build_content_payload(self, span: Span[Any]) -> ContentPayload:
10561056

10571057
elif _is_instance_of(span_data, ResponseSpanData):
10581058
span_input = getattr(span_data, "input", None)
1059+
response_obj = getattr(span_data, "response", None)
10591060
if capture_messages and span_input:
10601061
payload.input_messages = (
10611062
self._normalize_messages_to_role_parts(span_input)
10621063
)
1064+
1065+
if (
1066+
capture_system
1067+
and response_obj
1068+
and hasattr(response_obj, "instructions")
1069+
):
1070+
payload.system_instructions = self._normalize_to_text_parts(
1071+
response_obj.instructions
1072+
)
10631073
if capture_system and span_input:
10641074
sys_instr = self._collect_system_instructions(span_input)
10651075
if sys_instr:
@@ -2016,6 +2026,22 @@ def _get_attributes_from_response_span_data(
20162026
if output_tokens is not None:
20172027
yield GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens
20182028

2029+
# Tool definitions from response
2030+
if self._capture_tool_definitions and hasattr(
2031+
span_data.response, "tools"
2032+
):
2033+
yield (
2034+
GEN_AI_TOOL_DEFINITIONS,
2035+
safe_json_dumps(
2036+
list(
2037+
map(
2038+
lambda tool: tool.to_dict(),
2039+
span_data.response.tools,
2040+
)
2041+
)
2042+
),
2043+
)
2044+
20192045
# Input/output messages
20202046
if (
20212047
self.include_sensitive_data

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_tracer.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
set_trace_processors,
2626
trace,
2727
)
28+
from openai.types.responses import FunctionTool # noqa: E402
2829

2930
from opentelemetry.instrumentation.openai_agents import ( # noqa: E402
3031
OpenAIAgentsInstrumentor,
@@ -62,6 +63,9 @@
6263
GEN_AI_OUTPUT_MESSAGES = getattr(
6364
GenAI, "GEN_AI_OUTPUT_MESSAGES", "gen_ai.output.messages"
6465
)
66+
GEN_AI_TOOL_DEFINITIONS = getattr(
67+
GenAI, "GEN_AI_TOOL_DEFINITIONS", "gen_ai.tool.definitions"
68+
)
6569

6670

6771
def _instrument_with_provider(**instrument_kwargs):
@@ -478,8 +482,26 @@ def __init__(self, input_tokens: int, output_tokens: int) -> None:
478482
class _Response:
479483
def __init__(self) -> None:
480484
self.id = "resp-123"
485+
self.instructions = "You are a helpful assistant."
481486
self.model = "gpt-4o-mini"
482487
self.usage = _Usage(42, 9)
488+
self.tools = [
489+
FunctionTool(
490+
name="get_current_weather",
491+
type="function",
492+
description="Get the current weather in a given location",
493+
parameters={
494+
"type": "object",
495+
"properties": {
496+
"location": {
497+
"title": "Location",
498+
"type": "string",
499+
},
500+
},
501+
"required": ["location"],
502+
},
503+
)
504+
]
483505
self.output = [{"finish_reason": "stop"}]
484506

485507
try:
@@ -507,6 +529,30 @@ def __init__(self) -> None:
507529
assert response.attributes[GenAI.GEN_AI_RESPONSE_FINISH_REASONS] == (
508530
"stop",
509531
)
532+
533+
system_instructions = json.loads(
534+
response.attributes[GenAI.GEN_AI_SYSTEM_INSTRUCTIONS]
535+
)
536+
assert system_instructions == [
537+
{"type": "text", "content": "You are a helpful assistant."}
538+
]
539+
tool_definitions = json.loads(
540+
response.attributes[GEN_AI_TOOL_DEFINITIONS]
541+
)
542+
assert tool_definitions == [
543+
{
544+
"type": "function",
545+
"name": "get_current_weather",
546+
"description": "Get the current weather in a given location",
547+
"parameters": {
548+
"type": "object",
549+
"properties": {
550+
"location": {"title": "Location", "type": "string"},
551+
},
552+
"required": ["location"],
553+
},
554+
}
555+
]
510556
finally:
511557
instrumentor.uninstrument()
512558
exporter.clear()

0 commit comments

Comments
 (0)