diff --git a/src/strands_evals/mappers/constants.py b/src/strands_evals/mappers/constants.py index 4bee5893..1ef04df3 100644 --- a/src/strands_evals/mappers/constants.py +++ b/src/strands_evals/mappers/constants.py @@ -9,6 +9,7 @@ SCOPE_OPENINFERENCE = "openinference.instrumentation.langchain" SCOPE_OPENINFERENCE_SMOLAGENTS = "openinference.instrumentation.smolagents" SCOPE_OPENINFERENCE_CLAUDE_AGENT_SDK = "openinference.instrumentation.claude_agent_sdk" +SCOPE_OPENINFERENCE_OPENAI_AGENTS = "openinference.instrumentation.openai_agents" SCOPE_ADK = "gcp.vertex.agent" SCOPE_STRANDS = "strands.telemetry.tracer" @@ -18,6 +19,7 @@ SCOPE_OPENINFERENCE, SCOPE_OPENINFERENCE_SMOLAGENTS, SCOPE_OPENINFERENCE_CLAUDE_AGENT_SDK, + SCOPE_OPENINFERENCE_OPENAI_AGENTS, ] ) diff --git a/src/strands_evals/mappers/openinference_session_mapper.py b/src/strands_evals/mappers/openinference_session_mapper.py index 6352357d..ccd2e850 100644 --- a/src/strands_evals/mappers/openinference_session_mapper.py +++ b/src/strands_evals/mappers/openinference_session_mapper.py @@ -38,11 +38,12 @@ from .constants import ( SCOPE_OPENINFERENCE, SCOPE_OPENINFERENCE_CLAUDE_AGENT_SDK, + SCOPE_OPENINFERENCE_OPENAI_AGENTS, SCOPE_OPENINFERENCE_SMOLAGENTS, SCOPES_OPENINFERENCE_FAMILY, ) from .session_mapper import SessionMapper -from .utils import safe_json_parse +from .utils import bridge_parent_gaps, safe_json_parse logger = logging.getLogger(__name__) @@ -124,8 +125,14 @@ def map_to_session(self, data: Any, session_id: str) -> Session: # Per-producer normalization: canonicalize encoding differences so the # shared conversion logic receives a uniform representation. for span in openinference_spans: - if self._get_scope_name(span) == SCOPE_OPENINFERENCE_SMOLAGENTS: - self._normalize_smolagents_span(span) + scope = self._get_scope_name(span) + try: + if scope == SCOPE_OPENINFERENCE_SMOLAGENTS: + self._normalize_smolagents_span(span) + elif scope == SCOPE_OPENINFERENCE_OPENAI_AGENTS: + self._normalize_openai_agents_span(span) + except Exception as e: + logger.warning(f"Failed to normalize {scope} span {span.get('span_id', 'unknown')}: {e}") # Group spans by trace_id grouped = defaultdict(list) @@ -133,6 +140,14 @@ def map_to_session(self, data: Any, session_id: str) -> Session: trace_id = span.get("trace_id", "") grouped[trace_id].append(span) + # OpenAI Agents SDK normalization requires the full trace group, so it runs after grouping. + for trace_spans in grouped.values(): + if any(self._get_scope_name(s) == SCOPE_OPENINFERENCE_OPENAI_AGENTS for s in trace_spans): + try: + self._normalize_openai_agents_trace(trace_spans) + except Exception as e: + logger.warning(f"Failed to normalize openai agents trace: {e}") + # Build traces result_traces: list[Trace] = [] for trace_id, trace_spans in grouped.items(): @@ -258,6 +273,164 @@ def _normalize_smolagents_tool_attrs(self, attrs: dict) -> None: # Fallback: no tool.parameters available to map positional args attrs["input.value"] = json.dumps({"args": args}) + @classmethod + def _normalize_openai_agents_span(cls, span: dict) -> None: + """Normalize OpenAI agent spans in-place. + + This normalizes by: + 1. Unwrapping tool schemas in the format {"type": "function", "function": {...}}. + 2. Aliasing "parameters" to "input_schema" on tool schemas. + """ + attrs = span.get("attributes") or {} + for idx in cls._get_message_indices(attrs, "llm.tools."): + key = f"llm.tools.{idx}.tool.json_schema" + raw = attrs.get(key) + if isinstance(raw, str): + try: + schema = json.loads(raw) + except json.JSONDecodeError: + continue + else: + schema = raw + if not isinstance(schema, dict): + continue + if "name" not in schema and isinstance(schema.get("function"), dict): + schema = schema["function"] + if "input_schema" not in schema and "parameters" in schema: + schema["input_schema"] = schema["parameters"] + attrs[key] = json.dumps(schema) + + def _normalize_openai_agents_trace(self, spans: list[dict]) -> None: + """Normalize OpenAI Agents SDK AGENT spans in-place by injecting input/output. + + OpenAI Agents SDK AGENT spans carry no input.value or output.value. + The user prompt and final response live on descendant LLM spans + (reachable through intermediate CHAIN "turn" spans). + """ + # Collect all spans keyed by parent id for span traversal + spans_by_parent_id: dict[str, list[dict]] = defaultdict(list) + for s in spans: + parent = s.get("parent_span_id") + if parent: + spans_by_parent_id[parent].append(s) + + for span in spans: + attrs = span.get("attributes") or {} + span_id = span.get("span_id", "") + if attrs.get("openinference.span.kind") != "AGENT" or attrs.get("input.value") or not span_id: + continue + + llm_spans = self._get_descendant_llm_spans(span_id, spans_by_parent_id) + if not llm_spans: + continue + + first_attrs = llm_spans[0].get("attributes", {}) + user_prompt = self._get_last_message_text(first_attrs, "llm.input_messages.", role="user") + if user_prompt: + attrs["input.value"] = user_prompt + + # Copy LLM tool schemas onto the AGENT span so it carries its own tools. + for tool_idx in self._get_message_indices(first_attrs, "llm.tools."): + key = f"llm.tools.{tool_idx}.tool.json_schema" + schema = first_attrs.get(key) + if schema is not None: + attrs[key] = schema + + last_attrs = llm_spans[-1].get("attributes", {}) + agent_response = self._get_last_message_text(last_attrs, "llm.output_messages.") + + # Fallback the response to the last message's tool calls if no LLM output was found + if not agent_response: + tool_calls = self._get_last_tool_calls(last_attrs) + if tool_calls: + agent_response = f"[delegated] {tool_calls}" + if agent_response: + attrs["output.value"] = agent_response + + def _get_descendant_llm_spans(self, span_id: str, spans_by_parent_id: dict[str, list[dict]]) -> list[dict]: + """Return the LLM spans descending from `span_id`, earliest first.""" + llm_spans: list[dict] = [] + stack = [span_id] + seen: set[str] = set() + while stack: + current = stack.pop() + if current in seen: + continue + seen.add(current) + for child in spans_by_parent_id.get(current, []): + kind = (child.get("attributes") or {}).get("openinference.span.kind", "") + if kind == "LLM": + llm_spans.append(child) + elif kind == "CHAIN": + cid = child.get("span_id", "") + if cid: + stack.append(cid) + llm_spans.sort(key=lambda s: self.parse_timestamp(s.get("start_time"))) + return llm_spans + + @staticmethod + def _get_message_indices(attrs: dict, prefix: str) -> list[int]: + """Return the message indices present under `prefix`, highest first.""" + indices: set[int] = set() + for key in attrs: + if key.startswith(prefix): + seg = key.removeprefix(prefix).split(".", 1)[0] + if seg.isascii() and seg.isdigit(): + indices.add(int(seg)) + return sorted(indices, reverse=True) + + @classmethod + def _get_last_message_text(cls, attrs: dict, prefix: str, role: str | None = None) -> str | None: + """Return the text of the last matching message, or None.""" + for idx in cls._get_message_indices(attrs, prefix): + base = f"{prefix}{idx}.message" + role_match = role is None or attrs.get(f"{base}.role") == role + is_reasoning = attrs.get(f"{base}.contents.0.message_content.type") == "reasoning" + text = attrs.get(f"{base}.content") or cls._join_content_text(attrs, base) + if role_match and not is_reasoning and text: + return text + return None + + @staticmethod + def _join_content_text(attrs: dict, base: str) -> str | None: + """Concatenate all `text` parts under `{base}.contents.N`, or None. + + Multimodal messages emit ordered parts (e.g. [image, text]), so scan every + part rather than only index 0. + """ + parts: list[str] = [] + i = 0 + while True: + part_type = attrs.get(f"{base}.contents.{i}.message_content.type") + if part_type is None: + break + if part_type == "text": + text = attrs.get(f"{base}.contents.{i}.message_content.text") + if text: + parts.append(text) + i += 1 + return "".join(parts) or None + + @classmethod + def _get_last_tool_calls(cls, attrs: dict) -> str | None: + """Return a text rendering of the last assistant message's tool calls, or None.""" + prefix = "llm.output_messages." + for idx in cls._get_message_indices(attrs, prefix): + base = f"{prefix}{idx}.message" + calls: list[str] = [] + # Convert the tool calls to a string representation + i = 0 + while True: + name = attrs.get(f"{base}.tool_calls.{i}.tool_call.function.name") + if not name: + break + args = attrs.get(f"{base}.tool_calls.{i}.tool_call.function.arguments", "") + calls.append(f"{name}({args})" if args else f"{name}()") + i += 1 + if calls: + return "; ".join(calls) + return None + def _build_trace(self, trace_id: str, spans: list[dict], session_id: str) -> Trace: """Build a Trace from spans with the same trace_id.""" converted_spans: list[InferenceSpan | ToolExecutionSpan | AgentInvocationSpan] = [] @@ -279,6 +452,10 @@ def _build_trace(self, trace_id: str, spans: list[dict], session_id: str) -> Tra except Exception as e: logger.warning(f"Failed to convert span {span.get('span_id', 'unknown')}: {e}") + # Fix parent_span_id on converted spans that point to skipped intermediaries + raw_parent_map = {s.get("span_id", ""): s.get("parent_span_id") for s in spans} + bridge_parent_gaps(converted_spans, raw_parent_map) + # In multi-agent LangGraph systems, each nested sub-graph produces its own # LangGraph CHAIN span. Keep only the last one (root graph finishes last). agent_spans = [s for s in converted_spans if isinstance(s, AgentInvocationSpan)] @@ -374,7 +551,11 @@ def _is_agent_invocation_span(self, span: dict) -> bool: # routing nodes that aren't true agent invocations — reject those by default. if span_kind == "AGENT": scope_name = self._get_scope_name(span) - if scope_name in (SCOPE_OPENINFERENCE_SMOLAGENTS, SCOPE_OPENINFERENCE_CLAUDE_AGENT_SDK): + if scope_name in ( + SCOPE_OPENINFERENCE_SMOLAGENTS, + SCOPE_OPENINFERENCE_CLAUDE_AGENT_SDK, + SCOPE_OPENINFERENCE_OPENAI_AGENTS, + ): input_val = attrs.get("input.value") if input_val: output_val = attrs.get("output.value") @@ -625,7 +806,7 @@ def _convert_agent_invocation_span(self, span: dict, session_id: str) -> AgentIn logger.warning(f"No agent_response for agent span {span.get('span_id')}") return None - available_tools = sorted( + available_tools = self._extract_tools_from_attributes(attrs) or sorted( self._trace_tools_map.get(trace_id, {}).values(), key=lambda t: t.name, ) @@ -816,7 +997,7 @@ def _extract_tools_from_attributes(self, attrs: dict) -> list[ToolConfig]: parameters=tool_info.get("input_schema"), ) ) - except (json.JSONDecodeError, AttributeError): + except (json.JSONDecodeError, AttributeError, ValueError): pass return sorted(tools, key=lambda t: t.name or "") diff --git a/tests/strands_evals/mappers/fixtures/openai_agents_openinference_adot_spans.json b/tests/strands_evals/mappers/fixtures/openai_agents_openinference_adot_spans.json new file mode 100644 index 00000000..60f9f53d --- /dev/null +++ b/tests/strands_evals/mappers/fixtures/openai_agents_openinference_adot_spans.json @@ -0,0 +1,551 @@ +[ + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "816141f373160060", + "parent_span_id": "0d5ab39f79fe5e6a", + "name": "POST", + "start_time": 1786396451003697401, + "end_time": 1786396452796912644, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "telemetry.extended": "true", + "http.url": "https://api.openai.com/v1/responses", + "aws.remote.service": "api.openai.com", + "aws.local.environment": "bedrock-agentcore:default", + "aws.remote.operation": "POST /v1", + "http.status_code": 200, + "aws.local.operation": "UnmappedOperation", + "aws.span.kind": "CLIENT", + "PlatformType": "AWS::BedrockAgentCore", + "http.method": "POST", + "http.response.status_code": 200, + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8" + }, + "scope": { + "name": "opentelemetry.instrumentation.httpx", + "version": "0.65b0" + }, + "status": { + "code": "UNSET" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "0d5ab39f79fe5e6a", + "parent_span_id": "2a26ac2503900fc8", + "name": "response", + "start_time": 1786396450066328064, + "end_time": 1786396452989012992, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.1.message.role": "user", + "llm.token_count.prompt": 148, + "llm.model_name": "gpt-4o-2024-08-06", + "llm.invocation_parameters": "{\"id\": \"resp_0a31d412faab0244006a7a3f23b0b0819ca4b720ae546d47d4\", \"created_at\": 1786396451.0, \"instructions\": \"You are a coordinator agent. You delegate tasks to specialist agents:\\n- Hand off to math_specialist for any mathematical calculations\\n- Hand off to research_specialist for weather or stock price lookups\\nFor tasks that require both, hand off to each specialist as needed and synthesize their results into a final answer.\", \"metadata\": {}, \"model\": \"gpt-4o-2024-08-06\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786396452.0, \"prompt_cache_key\": \"agents-sdk:run:6129c95be34d46e58677d0e7f9ee7425\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "llm.input_messages.1.message.content": "What is 42 * 7, and what's the weather in Seattle?", + "aws.local.environment": "bedrock-agentcore:default", + "gen_ai.provider.name": "openai", + "llm.output_messages.0.message.tool_calls.1.tool_call.id": "call_HxtaQFaymn4MK1H6XUUGChBm", + "llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_BcvqKc5BjhX0NeiqTG2eaeFA", + "llm.input_messages.0.message.content": "You are a coordinator agent. You delegate tasks to specialist agents:\n- Hand off to math_specialist for any mathematical calculations\n- Hand off to research_specialist for weather or stock price lookups\nFor tasks that require both, hand off to each specialist as needed and synthesize their results into a final answer.", + "output.mime_type": "application/json", + "gen_ai.request.model": "gpt-4o-2024-08-06", + "openinference.span.kind": "LLM", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"transfer_to_math_specialist\", \"description\": \"Handoff to the math_specialist agent to handle the request. \", \"parameters\": {\"additionalProperties\": false, \"type\": \"object\", \"properties\": {}, \"required\": []}, \"strict\": true}}", + "llm.system": "openai", + "gen_ai.usage.output_tokens": 47, + "output.value": "{\"id\":\"resp_0a31d412faab0244006a7a3f23b0b0819ca4b720ae546d47d4\",\"created_at\":1786396451.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a coordinator agent. You delegate tasks to specialist agents:\\n- Hand off to math_specialist for any mathematical calculations\\n- Hand off to research_specialist for weather or stock price lookups\\nFor tasks that require both, hand off to each specialist as needed and synthesize their results into a final answer.\",\"metadata\":{},\"model\":\"gpt-4o-2024-08-06\",\"object\":\"response\",\"output\":[{\"arguments\":\"{}\",\"call_id\":\"call_BcvqKc5BjhX0NeiqTG2eaeFA\",\"name\":\"transfer_to_math_specialist\",\"type\":\"function_call\",\"id\":\"fc_0a31d412faab0244006a7a3f24abf8819cb86d11018bb0eefb\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"},{\"arguments\":\"{}\",\"call_id\":\"call_HxtaQFaymn4MK1H6XUUGChBm\",\"name\":\"transfer_to_research_specialist\",\"type\":\"function_call\",\"id\":\"fc_0a31d412faab0244006a7a3f24ac08819c931277cf05c9096b\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"transfer_to_math_specialist\",\"parameters\":{\"additionalProperties\":false,\"type\":\"object\",\"properties\":{},\"required\":[]},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Handoff to the math_specialist agent to handle the request. \",\"output_schema\":null},{\"name\":\"transfer_to_research_specialist\",\"parameters\":{\"additionalProperties\":false,\"type\":\"object\",\"properties\":{},\"required\":[]},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Handoff to the research_specialist agent to handle the request. \",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786396452.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:6129c95be34d46e58677d0e7f9ee7425\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":148,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":47,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":195},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.output_messages.0.message.tool_calls.1.tool_call.function.name": "transfer_to_research_specialist", + "aws.genai.span_kind": "LLM", + "llm.token_count.completion": 47, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.role": "assistant", + "llm.token_count.total": 195, + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"transfer_to_research_specialist\", \"description\": \"Handoff to the research_specialist agent to handle the request. \", \"parameters\": {\"additionalProperties\": false, \"type\": \"object\", \"properties\": {}, \"required\": []}, \"strict\": true}}", + "input.mime_type": "application/json", + "llm.token_count.prompt_details.cache_read": 0, + "gen_ai.usage.input_tokens": 148, + "aws.genai.token_count_total": 195, + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "transfer_to_math_specialist", + "input.value": "[{\"content\": \"What is 42 * 7, and what's the weather in Seattle?\", \"role\": \"user\"}]", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "d0fb2bfaa2e75b2a", + "parent_span_id": "2a26ac2503900fc8", + "name": "handoff to math_specialist", + "start_time": 1786396452990319872, + "end_time": 1786396452990628096, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "output.value": "math_specialist", + "input.value": "coordinator", + "aws.genai.span_kind": "TOOL", + "openinference.span.kind": "TOOL", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "ERROR" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "8c2c69973d95146c", + "parent_span_id": "f34af1c02ac46294", + "name": "coordinator", + "start_time": 1786396450058855936, + "end_time": 1786396452990868992, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "graph.node.id": "coordinator", + "gen_ai.agent.name": "coordinator", + "agent.name": "coordinator", + "aws.genai.span_kind": "AGENT", + "openinference.span.kind": "AGENT", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "2a26ac2503900fc8", + "parent_span_id": "8c2c69973d95146c", + "name": "turn", + "start_time": 1786396450059068928, + "end_time": 1786396452990804992, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "aws.genai.span_kind": "CHAIN", + "openinference.span.kind": "CHAIN", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "c27ea715481191d3", + "parent_span_id": "9dff5545acb5b39e", + "name": "POST", + "start_time": 1786396452999135652, + "end_time": 1786396453879628710, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "telemetry.extended": "true", + "http.url": "https://api.openai.com/v1/responses", + "aws.remote.service": "api.openai.com", + "aws.local.environment": "bedrock-agentcore:default", + "aws.remote.operation": "POST /v1", + "http.status_code": 200, + "aws.local.operation": "UnmappedOperation", + "aws.span.kind": "CLIENT", + "PlatformType": "AWS::BedrockAgentCore", + "http.method": "POST", + "http.response.status_code": 200, + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8" + }, + "scope": { + "name": "opentelemetry.instrumentation.httpx", + "version": "0.65b0" + }, + "status": { + "code": "UNSET" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "9dff5545acb5b39e", + "parent_span_id": "60be61a29768e79f", + "name": "response", + "start_time": 1786396452992902912, + "end_time": 1786396453884219904, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.3.message.tool_calls.0.tool_call.id": "call_HxtaQFaymn4MK1H6XUUGChBm", + "llm.token_count.prompt": 257, + "llm.model_name": "gpt-4o-2024-08-06", + "llm.input_messages.4.message.tool_call_id": "call_HxtaQFaymn4MK1H6XUUGChBm", + "llm.invocation_parameters": "{\"id\": \"resp_0a31d412faab0244006a7a3f251624819cb6da61e3a5643d1a\", \"created_at\": 1786396453.0, \"instructions\": \"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\", \"metadata\": {}, \"model\": \"gpt-4o-2024-08-06\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786396453.0, \"prompt_cache_key\": \"agents-sdk:run:6129c95be34d46e58677d0e7f9ee7425\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "llm.input_messages.1.message.content": "What is 42 * 7, and what's the weather in Seattle?", + "llm.input_messages.4.message.content": "Multiple handoffs detected, ignoring this one.", + "aws.local.environment": "bedrock-agentcore:default", + "gen_ai.provider.name": "openai", + "llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_6OK7LCjPfCaxaJID3WPkrmds", + "llm.input_messages.0.message.content": "You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.", + "output.mime_type": "application/json", + "gen_ai.request.model": "gpt-4o-2024-08-06", + "llm.input_messages.3.message.tool_calls.0.tool_call.function.name": "transfer_to_research_specialist", + "openinference.span.kind": "LLM", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"add_numbers\", \"description\": \"Return the sum of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"add_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.input_messages.5.message.content": "{\"assistant\": \"math_specialist\"}", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{\"a\":42,\"b\":7}", + "llm.system": "openai", + "gen_ai.usage.output_tokens": 19, + "output.value": "{\"id\":\"resp_0a31d412faab0244006a7a3f251624819cb6da61e3a5643d1a\",\"created_at\":1786396453.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\",\"metadata\":{},\"model\":\"gpt-4o-2024-08-06\",\"object\":\"response\",\"output\":[{\"arguments\":\"{\\\"a\\\":42,\\\"b\\\":7}\",\"call_id\":\"call_6OK7LCjPfCaxaJID3WPkrmds\",\"name\":\"multiply_numbers\",\"type\":\"function_call\",\"id\":\"fc_0a31d412faab0244006a7a3f25b06c819caa9a855b3cd6994e\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"add_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"add_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the sum of two numbers.\",\"output_schema\":null},{\"name\":\"multiply_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"multiply_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the product of two numbers.\",\"output_schema\":null},{\"name\":\"divide_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"Dividend.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Divisor.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"divide_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return a divided by b. Returns error message if b is zero.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786396453.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:6129c95be34d46e58677d0e7f9ee7425\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":257,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":19,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":276},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_BcvqKc5BjhX0NeiqTG2eaeFA", + "aws.genai.span_kind": "LLM", + "llm.token_count.completion": 19, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.role": "assistant", + "llm.token_count.total": 276, + "llm.input_messages.5.message.tool_call_id": "call_BcvqKc5BjhX0NeiqTG2eaeFA", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"multiply_numbers\", \"description\": \"Return the product of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"multiply_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "input.mime_type": "application/json", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "transfer_to_math_specialist", + "llm.token_count.prompt_details.cache_read": 0, + "llm.input_messages.5.message.role": "tool", + "gen_ai.usage.input_tokens": 257, + "aws.genai.token_count_total": 276, + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "multiply_numbers", + "llm.input_messages.3.message.role": "assistant", + "input.value": "[{\"content\": \"What is 42 * 7, and what's the weather in Seattle?\", \"role\": \"user\"}, {\"arguments\": \"{}\", \"call_id\": \"call_BcvqKc5BjhX0NeiqTG2eaeFA\", \"name\": \"transfer_to_math_specialist\", \"type\": \"function_call\", \"id\": \"fc_0a31d412faab0244006a7a3f24abf8819cb86d11018bb0eefb\", \"status\": \"completed\"}, {\"arguments\": \"{}\", \"call_id\": \"call_HxtaQFaymn4MK1H6XUUGChBm\", \"name\": \"transfer_to_research_specialist\", \"type\": \"function_call\", \"id\": \"fc_0a31d412faab0244006a7a3f24ac08819c931277cf05c9096b\", \"status\": \"completed\"}, {\"call_id\": \"call_HxtaQFaymn4MK1H6XUUGChBm\", \"output\": \"Multiple handoffs detected, ignoring this one.\", \"type\": \"function_call_output\"}, {\"call_id\": \"call_BcvqKc5BjhX0NeiqTG2eaeFA\", \"output\": \"{\\\"assistant\\\": \\\"math_specialist\\\"}\", \"type\": \"function_call_output\"}]", + "llm.input_messages.4.message.role": "tool", + "llm.input_messages.2.message.role": "assistant", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "llm.tools.2.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"divide_numbers\", \"description\": \"Return a divided by b. Returns error message if b is zero.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"Dividend.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Divisor.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"divide_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "60be61a29768e79f", + "parent_span_id": "2cbdd8e10afa264f", + "name": "turn", + "start_time": 1786396452991239936, + "end_time": 1786396453886953216, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "aws.genai.span_kind": "CHAIN", + "openinference.span.kind": "CHAIN", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "b66d0b3d5a01d316", + "parent_span_id": "60be61a29768e79f", + "name": "multiply_numbers", + "start_time": 1786396453885754112, + "end_time": 1786396453886648832, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "input.mime_type": "application/json", + "output.value": "294.0", + "tool.description": "Return the product of two numbers.", + "tool.parameters": "{\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"multiply_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}", + "input.value": "{\"a\":42,\"b\":7}", + "aws.genai.span_kind": "TOOL", + "openinference.span.kind": "TOOL", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "tool.name": "multiply_numbers", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "5368b1a556ebddf7", + "parent_span_id": "a3638478517b5bf0", + "name": "POST", + "start_time": 1786396453895167565, + "end_time": 1786396455139429318, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "telemetry.extended": "true", + "http.url": "https://api.openai.com/v1/responses", + "aws.remote.service": "api.openai.com", + "aws.local.environment": "bedrock-agentcore:default", + "aws.remote.operation": "POST /v1", + "http.status_code": 200, + "aws.local.operation": "UnmappedOperation", + "aws.span.kind": "CLIENT", + "PlatformType": "AWS::BedrockAgentCore", + "http.method": "POST", + "http.response.status_code": 200, + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8" + }, + "scope": { + "name": "opentelemetry.instrumentation.httpx", + "version": "0.65b0" + }, + "status": { + "code": "UNSET" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "a3638478517b5bf0", + "parent_span_id": "af3164b9d9f76356", + "name": "response", + "start_time": 1786396453887800832, + "end_time": 1786396455173500928, + "attributes": { + "llm.input_messages.0.message.role": "system", + "llm.input_messages.3.message.tool_calls.0.tool_call.id": "call_HxtaQFaymn4MK1H6XUUGChBm", + "llm.model_name": "gpt-4o-2024-08-06", + "llm.input_messages.4.message.tool_call_id": "call_HxtaQFaymn4MK1H6XUUGChBm", + "llm.input_messages.1.message.content": "What is 42 * 7, and what's the weather in Seattle?", + "llm.input_messages.4.message.content": "Multiple handoffs detected, ignoring this one.", + "llm.input_messages.7.message.content": "294.0", + "aws.local.environment": "bedrock-agentcore:default", + "gen_ai.provider.name": "openai", + "llm.output_messages.0.message.contents.0.message_content.type": "text", + "output.mime_type": "application/json", + "gen_ai.request.model": "gpt-4o-2024-08-06", + "llm.output_messages.0.message.contents.0.message_content.text": "The product of \\(42 \\times 7\\) is \\(294\\). As for the weather, I can\u2019t access current weather information. You might check a reliable weather service for that.", + "llm.output_messages.0.message.content": "The product of \\(42 \\times 7\\) is \\(294\\). As for the weather, I can\u2019t access current weather information. You might check a reliable weather service for that.", + "llm.input_messages.6.message.tool_calls.0.tool_call.function.arguments": "{\"a\":42,\"b\":7}", + "gen_ai.usage.output_tokens": 41, + "llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_BcvqKc5BjhX0NeiqTG2eaeFA", + "llm.output_messages.0.message.role": "assistant", + "input.mime_type": "application/json", + "llm.input_messages.7.message.role": "tool", + "llm.input_messages.6.message.role": "assistant", + "llm.input_messages.5.message.role": "tool", + "llm.input_messages.3.message.role": "assistant", + "llm.input_messages.4.message.role": "tool", + "llm.input_messages.2.message.role": "assistant", + "llm.tools.2.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"divide_numbers\", \"description\": \"Return a divided by b. Returns error message if b is zero.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"Dividend.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Divisor.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"divide_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "llm.input_messages.7.message.tool_call_id": "call_6OK7LCjPfCaxaJID3WPkrmds", + "llm.input_messages.1.message.role": "user", + "llm.token_count.prompt": 287, + "llm.input_messages.6.message.tool_calls.0.tool_call.id": "call_6OK7LCjPfCaxaJID3WPkrmds", + "llm.invocation_parameters": "{\"id\": \"resp_0a31d412faab0244006a7a3f25ffcc819cb0e69b4478c7f955\", \"created_at\": 1786396454.0, \"instructions\": \"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\", \"metadata\": {}, \"model\": \"gpt-4o-2024-08-06\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786396454.0, \"prompt_cache_key\": \"agents-sdk:run:6129c95be34d46e58677d0e7f9ee7425\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "llm.input_messages.0.message.content": "You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.", + "llm.input_messages.3.message.tool_calls.0.tool_call.function.name": "transfer_to_research_specialist", + "openinference.span.kind": "LLM", + "llm.input_messages.6.message.tool_calls.0.tool_call.function.name": "multiply_numbers", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"add_numbers\", \"description\": \"Return the sum of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"add_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.input_messages.5.message.content": "{\"assistant\": \"math_specialist\"}", + "llm.system": "openai", + "output.value": "{\"id\":\"resp_0a31d412faab0244006a7a3f25ffcc819cb0e69b4478c7f955\",\"created_at\":1786396454.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\",\"metadata\":{},\"model\":\"gpt-4o-2024-08-06\",\"object\":\"response\",\"output\":[{\"id\":\"msg_0a31d412faab0244006a7a3f269180819c8e3de40ee99911e3\",\"content\":[{\"annotations\":[],\"text\":\"The product of \\\\(42 \\\\times 7\\\\) is \\\\(294\\\\). As for the weather, I can\u2019t access current weather information. You might check a reliable weather service for that.\",\"type\":\"output_text\",\"logprobs\":[]}],\"role\":\"assistant\",\"status\":\"completed\",\"type\":\"message\",\"phase\":null}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"add_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"add_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the sum of two numbers.\",\"output_schema\":null},{\"name\":\"multiply_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"multiply_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the product of two numbers.\",\"output_schema\":null},{\"name\":\"divide_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"Dividend.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Divisor.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"divide_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return a divided by b. Returns error message if b is zero.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786396454.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:6129c95be34d46e58677d0e7f9ee7425\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":287,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":41,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":328},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "aws.genai.span_kind": "LLM", + "llm.token_count.completion": 41, + "llm.token_count.completion_details.reasoning": 0, + "llm.token_count.total": 328, + "llm.input_messages.5.message.tool_call_id": "call_BcvqKc5BjhX0NeiqTG2eaeFA", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"multiply_numbers\", \"description\": \"Return the product of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"multiply_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "transfer_to_math_specialist", + "llm.token_count.prompt_details.cache_read": 0, + "gen_ai.usage.input_tokens": 287, + "aws.genai.token_count_total": 328, + "input.value": "[{\"content\": \"What is 42 * 7, and what's the weather in Seattle?\", \"role\": \"user\"}, {\"arguments\": \"{}\", \"call_id\": \"call_BcvqKc5BjhX0NeiqTG2eaeFA\", \"name\": \"transfer_to_math_specialist\", \"type\": \"function_call\", \"id\": \"fc_0a31d412faab0244006a7a3f24abf8819cb86d11018bb0eefb\", \"status\": \"completed\"}, {\"arguments\": \"{}\", \"call_id\": \"call_HxtaQFaymn4MK1H6XUUGChBm\", \"name\": \"transfer_to_research_specialist\", \"type\": \"function_call\", \"id\": \"fc_0a31d412faab0244006a7a3f24ac08819c931277cf05c9096b\", \"status\": \"completed\"}, {\"call_id\": \"call_HxtaQFaymn4MK1H6XUUGChBm\", \"output\": \"Multiple handoffs detected, ignoring this one.\", \"type\": \"function_call_output\"}, {\"call_id\": \"call_BcvqKc5BjhX0NeiqTG2eaeFA\", \"output\": \"{\\\"assistant\\\": \\\"math_specialist\\\"}\", \"type\": \"function_call_output\"}, {\"arguments\": \"{\\\"a\\\":42,\\\"b\\\":7}\", \"call_id\": \"call_6OK7LCjPfCaxaJID3WPkrmds\", \"name\": \"multiply_numbers\", \"type\": \"function_call\", \"id\": \"fc_0a31d412faab0244006a7a3f25b06c819caa9a855b3cd6994e\", \"status\": \"completed\"}, {\"call_id\": \"call_6OK7LCjPfCaxaJID3WPkrmds\", \"output\": \"294.0\", \"type\": \"function_call_output\"}]", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "aeec121eb055a683", + "parent_span_id": "c2faf1f60c3f0a09", + "name": "Agent workflow", + "start_time": 1786396450056901278, + "end_time": 1786396455175953806, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "aws.genai.span_kind": "AGENT", + "openinference.span.kind": "AGENT", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "f34af1c02ac46294", + "parent_span_id": "aeec121eb055a683", + "name": "Agent workflow", + "start_time": 1786396450057691136, + "end_time": 1786396455175892224, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "aws.genai.span_kind": "CHAIN", + "openinference.span.kind": "CHAIN", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "af3164b9d9f76356", + "parent_span_id": "2cbdd8e10afa264f", + "name": "turn", + "start_time": 1786396453887171072, + "end_time": 1786396455175024896, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "aws.genai.span_kind": "CHAIN", + "openinference.span.kind": "CHAIN", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "2cbdd8e10afa264f", + "parent_span_id": "f34af1c02ac46294", + "name": "math_specialist", + "start_time": 1786396452991072768, + "end_time": 1786396455175770880, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "graph.node.id": "math_specialist", + "gen_ai.agent.name": "math_specialist", + "agent.name": "math_specialist", + "aws.genai.span_kind": "AGENT", + "openinference.span.kind": "AGENT", + "graph.node.parent_id": "coordinator", + "PlatformType": "AWS::BedrockAgentCore", + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "aws.local.environment": "bedrock-agentcore:default" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "2.0.0" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "6a7a3f1922f7ce5131daefbb30a8a41a", + "span_id": "c2faf1f60c3f0a09", + "parent_span_id": "96b00f8c6c642b55", + "name": "POST /invocations", + "start_time": 1786396450054724666, + "end_time": 1786396455176924115, + "attributes": { + "aws.local.service": "CWExperiment_OpenaiOpenInferenceMulti.DEFAULT", + "net.peer.port": 58482, + "telemetry.extended": "true", + "http.target": "/invocations", + "http.flavor": "1.1", + "http.url": "http://cell01.us-east-1.prod.arp.kepler-analytics.aws.dev/invocations", + "net.peer.ip": "127.0.0.1", + "http.host": "127.0.0.1:8080", + "aws.local.environment": "bedrock-agentcore:default", + "http.status_code": 200, + "aws.local.operation": "POST /invocations", + "aws.span.kind": "SERVER", + "http.server_name": "cell01.us-east-1.prod.arp.kepler-analytics.aws.dev", + "net.host.port": 8080, + "http.route": "/invocations", + "PlatformType": "AWS::BedrockAgentCore", + "http.method": "POST", + "http.response.status_code": 200, + "session.id": "29fb7129-8624-4bd2-b37b-dea182f63ce8", + "http.scheme": "http" + }, + "scope": { + "name": "opentelemetry.instrumentation.starlette", + "version": "0.65b0" + }, + "status": { + "code": "UNSET" + }, + "span_events": [] + } +] diff --git a/tests/strands_evals/mappers/fixtures/openai_agents_openinference_live_spans.json b/tests/strands_evals/mappers/fixtures/openai_agents_openinference_live_spans.json new file mode 100644 index 00000000..7bb1b57f --- /dev/null +++ b/tests/strands_evals/mappers/fixtures/openai_agents_openinference_live_spans.json @@ -0,0 +1,550 @@ +[ + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "01647d903e5ce6db", + "parent_span_id": "0f61758b52ae63f8", + "name": "response", + "start_time": 1786569488291881984, + "end_time": 1786569489779081216, + "attributes": { + "llm.system": "openai", + "output.mime_type": "application/json", + "output.value": "{\"id\":\"resp_0e455d84153284dd006a7ce31109c4819e919d71ea0196f845\",\"created_at\":1786569489.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a coordinator agent. You delegate tasks to specialist agents:\\n- Use ask_math_specialist for any mathematical calculations\\n- Use ask_research_specialist for weather or stock price lookups\\nFor tasks that require both, call each specialist as needed and synthesize their results into a final answer.\",\"metadata\":{},\"model\":\"gpt-4o-mini-2024-07-18\",\"object\":\"response\",\"output\":[{\"arguments\":\"{\\\"query\\\":\\\"42 multiplied by 17, then divided by 3\\\"}\",\"call_id\":\"call_Xw9gngSGFTuCw1vZ30iZHqhk\",\"name\":\"ask_math_specialist\",\"type\":\"function_call\",\"id\":\"fc_0e455d84153284dd006a7ce3118e50819eb35c4d67bde3bea1\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"ask_math_specialist\",\"parameters\":{\"properties\":{\"query\":{\"description\":\"The math problem to solve.\",\"title\":\"Query\",\"type\":\"string\"}},\"required\":[\"query\"],\"title\":\"ask_math_specialist_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Delegate a math problem to the math specialist agent. Use this for any\\ncalculations involving addition, multiplication, or division.\",\"output_schema\":null},{\"name\":\"ask_research_specialist\",\"parameters\":{\"properties\":{\"query\":{\"description\":\"The research question to answer.\",\"title\":\"Query\",\"type\":\"string\"}},\"required\":[\"query\"],\"title\":\"ask_research_specialist_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Delegate a research question to the research specialist agent. Use this\\nfor weather lookups or stock price queries.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786569489.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:2e6da150ddff48ccba7a48f9163b9253\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":212,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":239},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"ask_math_specialist\", \"description\": \"Delegate a math problem to the math specialist agent. Use this for any\\ncalculations involving addition, multiplication, or division.\", \"parameters\": {\"properties\": {\"query\": {\"description\": \"The math problem to solve.\", \"title\": \"Query\", \"type\": \"string\"}}, \"required\": [\"query\"], \"title\": \"ask_math_specialist_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"ask_research_specialist\", \"description\": \"Delegate a research question to the research specialist agent. Use this\\nfor weather lookups or stock price queries.\", \"parameters\": {\"properties\": {\"query\": {\"description\": \"The research question to answer.\", \"title\": \"Query\", \"type\": \"string\"}}, \"required\": [\"query\"], \"title\": \"ask_research_specialist_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.token_count.completion": 27, + "llm.token_count.prompt": 212, + "llm.token_count.total": 239, + "llm.token_count.prompt_details.cache_read": 0, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_Xw9gngSGFTuCw1vZ30iZHqhk", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "ask_math_specialist", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{\"query\":\"42 multiplied by 17, then divided by 3\"}", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a coordinator agent. You delegate tasks to specialist agents:\n- Use ask_math_specialist for any mathematical calculations\n- Use ask_research_specialist for weather or stock price lookups\nFor tasks that require both, call each specialist as needed and synthesize their results into a final answer.", + "llm.model_name": "gpt-4o-mini-2024-07-18", + "llm.invocation_parameters": "{\"id\": \"resp_0e455d84153284dd006a7ce31109c4819e919d71ea0196f845\", \"created_at\": 1786569489.0, \"instructions\": \"You are a coordinator agent. You delegate tasks to specialist agents:\\n- Use ask_math_specialist for any mathematical calculations\\n- Use ask_research_specialist for weather or stock price lookups\\nFor tasks that require both, call each specialist as needed and synthesize their results into a final answer.\", \"metadata\": {}, \"model\": \"gpt-4o-mini-2024-07-18\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786569489.0, \"prompt_cache_key\": \"agents-sdk:run:2e6da150ddff48ccba7a48f9163b9253\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "input.mime_type": "application/json", + "input.value": "[{\"content\": \"What is 42 multiplied by 17, then divided by 3?\", \"role\": \"user\"}]", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "What is 42 multiplied by 17, then divided by 3?", + "openinference.span.kind": "LLM" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "e2d2f86c9d8425c6", + "parent_span_id": "71cd419364298995", + "name": "response", + "start_time": 1786569489781728000, + "end_time": 1786569490768940032, + "attributes": { + "llm.system": "openai", + "output.mime_type": "application/json", + "output.value": "{\"id\":\"resp_04f40cc6ac224de5006a7ce311ff48819d86b76b84d438b529\",\"created_at\":1786569490.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\",\"metadata\":{},\"model\":\"gpt-4o-mini-2024-07-18\",\"object\":\"response\",\"output\":[{\"arguments\":\"{\\\"a\\\":42,\\\"b\\\":17}\",\"call_id\":\"call_lVUfG25SyOW6kvKsUFm0FAN9\",\"name\":\"multiply_numbers\",\"type\":\"function_call\",\"id\":\"fc_04f40cc6ac224de5006a7ce312c4d8819d8eb3fb3057d016d8\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"},{\"arguments\":\"{\\\"a\\\":0,\\\"b\\\":3}\",\"call_id\":\"call_bxjY7m8PUxIHvFcq0iAIAnvT\",\"name\":\"divide_numbers\",\"type\":\"function_call\",\"id\":\"fc_04f40cc6ac224de5006a7ce312c4e4819daf074123c743a067\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"add_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"title\":\"add_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the sum of two numbers.\",\"output_schema\":null},{\"name\":\"multiply_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"title\":\"multiply_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the product of two numbers.\",\"output_schema\":null},{\"name\":\"divide_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"Dividend.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Divisor.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"divide_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return a divided by b. Returns error message if b is zero.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786569490.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:edd36592ccd84859a7ab73e91845d96a\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":190,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":53,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":243},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"add_numbers\", \"description\": \"Return the sum of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"integer\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"integer\"}}, \"required\": [\"a\", \"b\"], \"title\": \"add_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"multiply_numbers\", \"description\": \"Return the product of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"integer\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"integer\"}}, \"required\": [\"a\", \"b\"], \"title\": \"multiply_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.2.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"divide_numbers\", \"description\": \"Return a divided by b. Returns error message if b is zero.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"Dividend.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Divisor.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"divide_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.token_count.completion": 53, + "llm.token_count.prompt": 190, + "llm.token_count.total": 243, + "llm.token_count.prompt_details.cache_read": 0, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_lVUfG25SyOW6kvKsUFm0FAN9", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "multiply_numbers", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{\"a\":42,\"b\":17}", + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.tool_calls.1.tool_call.id": "call_bxjY7m8PUxIHvFcq0iAIAnvT", + "llm.output_messages.0.message.tool_calls.1.tool_call.function.name": "divide_numbers", + "llm.output_messages.0.message.tool_calls.1.tool_call.function.arguments": "{\"a\":0,\"b\":3}", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.", + "llm.model_name": "gpt-4o-mini-2024-07-18", + "llm.invocation_parameters": "{\"id\": \"resp_04f40cc6ac224de5006a7ce311ff48819d86b76b84d438b529\", \"created_at\": 1786569490.0, \"instructions\": \"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\", \"metadata\": {}, \"model\": \"gpt-4o-mini-2024-07-18\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786569490.0, \"prompt_cache_key\": \"agents-sdk:run:edd36592ccd84859a7ab73e91845d96a\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "input.mime_type": "application/json", + "input.value": "[{\"content\": \"42 multiplied by 17, then divided by 3\", \"role\": \"user\"}]", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "42 multiplied by 17, then divided by 3", + "openinference.span.kind": "LLM" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "520244f0a9db5c09", + "parent_span_id": "71cd419364298995", + "name": "multiply_numbers", + "start_time": 1786569490769624832, + "end_time": 1786569490770462976, + "attributes": { + "llm.system": "openai", + "tool.name": "multiply_numbers", + "input.value": "{\"a\":42,\"b\":17}", + "input.mime_type": "application/json", + "output.value": "714", + "openinference.span.kind": "TOOL" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "cc231a27bfc88df3", + "parent_span_id": "71cd419364298995", + "name": "divide_numbers", + "start_time": 1786569490769763840, + "end_time": 1786569490770509056, + "attributes": { + "llm.system": "openai", + "tool.name": "divide_numbers", + "input.value": "{\"a\":0,\"b\":3}", + "input.mime_type": "application/json", + "output.value": "0.0", + "openinference.span.kind": "TOOL" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "71cd419364298995", + "parent_span_id": "e87bf69ac679af43", + "name": "turn", + "start_time": 1786569489781244160, + "end_time": 1786569490770701824, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "8ae5f846e0fd053e", + "parent_span_id": "4a6d9f835df332e7", + "name": "response", + "start_time": 1786569490771218944, + "end_time": 1786569492389092096, + "attributes": { + "llm.system": "openai", + "output.mime_type": "application/json", + "output.value": "{\"id\":\"resp_04f40cc6ac224de5006a7ce313cf90819da1b56e1cc36108b2\",\"created_at\":1786569491.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\",\"metadata\":{},\"model\":\"gpt-4o-mini-2024-07-18\",\"object\":\"response\",\"output\":[{\"arguments\":\"{\\\"a\\\":714,\\\"b\\\":3}\",\"call_id\":\"call_Wmh20luZw7HNW3k1CfkWCZY6\",\"name\":\"divide_numbers\",\"type\":\"function_call\",\"id\":\"fc_04f40cc6ac224de5006a7ce3145388819d8884b595c51e0008\",\"caller\":null,\"namespace\":null,\"status\":\"completed\"}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"add_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"title\":\"add_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the sum of two numbers.\",\"output_schema\":null},{\"name\":\"multiply_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"title\":\"multiply_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the product of two numbers.\",\"output_schema\":null},{\"name\":\"divide_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"Dividend.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Divisor.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"divide_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return a divided by b. Returns error message if b is zero.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786569492.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:edd36592ccd84859a7ab73e91845d96a\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":242,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":19,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":261},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"add_numbers\", \"description\": \"Return the sum of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"integer\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"integer\"}}, \"required\": [\"a\", \"b\"], \"title\": \"add_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"multiply_numbers\", \"description\": \"Return the product of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"integer\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"integer\"}}, \"required\": [\"a\", \"b\"], \"title\": \"multiply_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.2.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"divide_numbers\", \"description\": \"Return a divided by b. Returns error message if b is zero.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"Dividend.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Divisor.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"divide_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.token_count.completion": 19, + "llm.token_count.prompt": 242, + "llm.token_count.total": 261, + "llm.token_count.prompt_details.cache_read": 0, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_Wmh20luZw7HNW3k1CfkWCZY6", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "divide_numbers", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": "{\"a\":714,\"b\":3}", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.", + "llm.model_name": "gpt-4o-mini-2024-07-18", + "llm.invocation_parameters": "{\"id\": \"resp_04f40cc6ac224de5006a7ce313cf90819da1b56e1cc36108b2\", \"created_at\": 1786569491.0, \"instructions\": \"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\", \"metadata\": {}, \"model\": \"gpt-4o-mini-2024-07-18\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786569492.0, \"prompt_cache_key\": \"agents-sdk:run:edd36592ccd84859a7ab73e91845d96a\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "input.mime_type": "application/json", + "input.value": "[{\"content\": \"42 multiplied by 17, then divided by 3\", \"role\": \"user\"}, {\"arguments\": \"{\\\"a\\\":42,\\\"b\\\":17}\", \"call_id\": \"call_lVUfG25SyOW6kvKsUFm0FAN9\", \"name\": \"multiply_numbers\", \"type\": \"function_call\", \"id\": \"fc_04f40cc6ac224de5006a7ce312c4d8819d8eb3fb3057d016d8\", \"status\": \"completed\"}, {\"arguments\": \"{\\\"a\\\":0,\\\"b\\\":3}\", \"call_id\": \"call_bxjY7m8PUxIHvFcq0iAIAnvT\", \"name\": \"divide_numbers\", \"type\": \"function_call\", \"id\": \"fc_04f40cc6ac224de5006a7ce312c4e4819daf074123c743a067\", \"status\": \"completed\"}, {\"call_id\": \"call_lVUfG25SyOW6kvKsUFm0FAN9\", \"output\": \"714\", \"type\": \"function_call_output\"}, {\"call_id\": \"call_bxjY7m8PUxIHvFcq0iAIAnvT\", \"output\": \"0.0\", \"type\": \"function_call_output\"}]", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "42 multiplied by 17, then divided by 3", + "llm.input_messages.2.message.role": "assistant", + "llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_lVUfG25SyOW6kvKsUFm0FAN9", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "multiply_numbers", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.arguments": "{\"a\":42,\"b\":17}", + "llm.input_messages.3.message.role": "assistant", + "llm.input_messages.3.message.tool_calls.0.tool_call.id": "call_bxjY7m8PUxIHvFcq0iAIAnvT", + "llm.input_messages.3.message.tool_calls.0.tool_call.function.name": "divide_numbers", + "llm.input_messages.3.message.tool_calls.0.tool_call.function.arguments": "{\"a\":0,\"b\":3}", + "llm.input_messages.4.message.role": "tool", + "llm.input_messages.4.message.tool_call_id": "call_lVUfG25SyOW6kvKsUFm0FAN9", + "llm.input_messages.4.message.content": "714", + "llm.input_messages.5.message.role": "tool", + "llm.input_messages.5.message.tool_call_id": "call_bxjY7m8PUxIHvFcq0iAIAnvT", + "llm.input_messages.5.message.content": "0.0", + "openinference.span.kind": "LLM" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "b4bb79f7c6380585", + "parent_span_id": "4a6d9f835df332e7", + "name": "divide_numbers", + "start_time": 1786569492390699008, + "end_time": 1786569492391855104, + "attributes": { + "llm.system": "openai", + "tool.name": "divide_numbers", + "input.value": "{\"a\":714,\"b\":3}", + "input.mime_type": "application/json", + "output.value": "238.0", + "openinference.span.kind": "TOOL" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "4a6d9f835df332e7", + "parent_span_id": "e87bf69ac679af43", + "name": "turn", + "start_time": 1786569490770854912, + "end_time": 1786569492392192000, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "f8d371cbd386ebbe", + "parent_span_id": "9c050be220ea6434", + "name": "response", + "start_time": 1786569492393261824, + "end_time": 1786569493647748864, + "attributes": { + "llm.system": "openai", + "output.mime_type": "application/json", + "output.value": "{\"id\":\"resp_04f40cc6ac224de5006a7ce314a340819da039b86f46e0b8a6\",\"created_at\":1786569492.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\",\"metadata\":{},\"model\":\"gpt-4o-mini-2024-07-18\",\"object\":\"response\",\"output\":[{\"id\":\"msg_04f40cc6ac224de5006a7ce3150a04819d9f71accc5ff79a1b\",\"content\":[{\"annotations\":[],\"text\":\"First, I calculated \\\\( 42 \\\\times 17 \\\\):\\n\\n\\\\[\\n42 \\\\times 17 = 714\\n\\\\]\\n\\nNext, I divided that result by \\\\( 3 \\\\):\\n\\n\\\\[\\n\\\\frac{714}{3} = 238\\n\\\\]\\n\\nSo, the final answer is **238**.\",\"type\":\"output_text\",\"logprobs\":[]}],\"role\":\"assistant\",\"status\":\"completed\",\"type\":\"message\",\"phase\":null}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"add_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"title\":\"add_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the sum of two numbers.\",\"output_schema\":null},{\"name\":\"multiply_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"First number.\",\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"description\":\"Second number.\",\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"title\":\"multiply_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return the product of two numbers.\",\"output_schema\":null},{\"name\":\"divide_numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"Dividend.\",\"title\":\"A\",\"type\":\"number\"},\"b\":{\"description\":\"Divisor.\",\"title\":\"B\",\"type\":\"number\"}},\"required\":[\"a\",\"b\"],\"title\":\"divide_numbers_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Return a divided by b. Returns error message if b is zero.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786569493.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:edd36592ccd84859a7ab73e91845d96a\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":272,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":65,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":337},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"add_numbers\", \"description\": \"Return the sum of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"integer\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"integer\"}}, \"required\": [\"a\", \"b\"], \"title\": \"add_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"multiply_numbers\", \"description\": \"Return the product of two numbers.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"First number.\", \"title\": \"A\", \"type\": \"integer\"}, \"b\": {\"description\": \"Second number.\", \"title\": \"B\", \"type\": \"integer\"}}, \"required\": [\"a\", \"b\"], \"title\": \"multiply_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.2.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"divide_numbers\", \"description\": \"Return a divided by b. Returns error message if b is zero.\", \"parameters\": {\"properties\": {\"a\": {\"description\": \"Dividend.\", \"title\": \"A\", \"type\": \"number\"}, \"b\": {\"description\": \"Divisor.\", \"title\": \"B\", \"type\": \"number\"}}, \"required\": [\"a\", \"b\"], \"title\": \"divide_numbers_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.token_count.completion": 65, + "llm.token_count.prompt": 272, + "llm.token_count.total": 337, + "llm.token_count.prompt_details.cache_read": 0, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.contents.0.message_content.type": "text", + "llm.output_messages.0.message.contents.0.message_content.text": "First, I calculated \\( 42 \\times 17 \\):\n\n\\[\n42 \\times 17 = 714\n\\]\n\nNext, I divided that result by \\( 3 \\):\n\n\\[\n\\frac{714}{3} = 238\n\\]\n\nSo, the final answer is **238**.", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.", + "llm.model_name": "gpt-4o-mini-2024-07-18", + "llm.invocation_parameters": "{\"id\": \"resp_04f40cc6ac224de5006a7ce314a340819da039b86f46e0b8a6\", \"created_at\": 1786569492.0, \"instructions\": \"You are a math specialist agent. You solve mathematical problems using your available tools. Be precise and show your work.\", \"metadata\": {}, \"model\": \"gpt-4o-mini-2024-07-18\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786569493.0, \"prompt_cache_key\": \"agents-sdk:run:edd36592ccd84859a7ab73e91845d96a\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "input.mime_type": "application/json", + "input.value": "[{\"content\": \"42 multiplied by 17, then divided by 3\", \"role\": \"user\"}, {\"arguments\": \"{\\\"a\\\":42,\\\"b\\\":17}\", \"call_id\": \"call_lVUfG25SyOW6kvKsUFm0FAN9\", \"name\": \"multiply_numbers\", \"type\": \"function_call\", \"id\": \"fc_04f40cc6ac224de5006a7ce312c4d8819d8eb3fb3057d016d8\", \"status\": \"completed\"}, {\"arguments\": \"{\\\"a\\\":0,\\\"b\\\":3}\", \"call_id\": \"call_bxjY7m8PUxIHvFcq0iAIAnvT\", \"name\": \"divide_numbers\", \"type\": \"function_call\", \"id\": \"fc_04f40cc6ac224de5006a7ce312c4e4819daf074123c743a067\", \"status\": \"completed\"}, {\"call_id\": \"call_lVUfG25SyOW6kvKsUFm0FAN9\", \"output\": \"714\", \"type\": \"function_call_output\"}, {\"call_id\": \"call_bxjY7m8PUxIHvFcq0iAIAnvT\", \"output\": \"0.0\", \"type\": \"function_call_output\"}, {\"arguments\": \"{\\\"a\\\":714,\\\"b\\\":3}\", \"call_id\": \"call_Wmh20luZw7HNW3k1CfkWCZY6\", \"name\": \"divide_numbers\", \"type\": \"function_call\", \"id\": \"fc_04f40cc6ac224de5006a7ce3145388819d8884b595c51e0008\", \"status\": \"completed\"}, {\"call_id\": \"call_Wmh20luZw7HNW3k1CfkWCZY6\", \"output\": \"238.0\", \"type\": \"function_call_output\"}]", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "42 multiplied by 17, then divided by 3", + "llm.input_messages.2.message.role": "assistant", + "llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_lVUfG25SyOW6kvKsUFm0FAN9", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "multiply_numbers", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.arguments": "{\"a\":42,\"b\":17}", + "llm.input_messages.3.message.role": "assistant", + "llm.input_messages.3.message.tool_calls.0.tool_call.id": "call_bxjY7m8PUxIHvFcq0iAIAnvT", + "llm.input_messages.3.message.tool_calls.0.tool_call.function.name": "divide_numbers", + "llm.input_messages.3.message.tool_calls.0.tool_call.function.arguments": "{\"a\":0,\"b\":3}", + "llm.input_messages.4.message.role": "tool", + "llm.input_messages.4.message.tool_call_id": "call_lVUfG25SyOW6kvKsUFm0FAN9", + "llm.input_messages.4.message.content": "714", + "llm.input_messages.5.message.role": "tool", + "llm.input_messages.5.message.tool_call_id": "call_bxjY7m8PUxIHvFcq0iAIAnvT", + "llm.input_messages.5.message.content": "0.0", + "llm.input_messages.6.message.role": "assistant", + "llm.input_messages.6.message.tool_calls.0.tool_call.id": "call_Wmh20luZw7HNW3k1CfkWCZY6", + "llm.input_messages.6.message.tool_calls.0.tool_call.function.name": "divide_numbers", + "llm.input_messages.6.message.tool_calls.0.tool_call.function.arguments": "{\"a\":714,\"b\":3}", + "llm.input_messages.7.message.role": "tool", + "llm.input_messages.7.message.tool_call_id": "call_Wmh20luZw7HNW3k1CfkWCZY6", + "llm.input_messages.7.message.content": "238.0", + "openinference.span.kind": "LLM" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "9c050be220ea6434", + "parent_span_id": "e87bf69ac679af43", + "name": "turn", + "start_time": 1786569492392488960, + "end_time": 1786569493650386944, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "e87bf69ac679af43", + "parent_span_id": "83cf452974b7203d", + "name": "math_specialist", + "start_time": 1786569489781133056, + "end_time": 1786569493651197952, + "attributes": { + "llm.system": "openai", + "graph.node.id": "math_specialist", + "openinference.span.kind": "AGENT" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "83cf452974b7203d", + "parent_span_id": "177d8b4441bfc60f", + "name": "Agent workflow", + "start_time": 1786569489780500992, + "end_time": 1786569493651372800, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "177d8b4441bfc60f", + "parent_span_id": "0f61758b52ae63f8", + "name": "ask_math_specialist", + "start_time": 1786569489780019968, + "end_time": 1786569493651823872, + "attributes": { + "llm.system": "openai", + "tool.name": "ask_math_specialist", + "input.value": "{\"query\":\"42 multiplied by 17, then divided by 3\"}", + "input.mime_type": "application/json", + "output.value": "First, I calculated \\( 42 \\times 17 \\):\n\n\\[\n42 \\times 17 = 714\n\\]\n\nNext, I divided that result by \\( 3 \\):\n\n\\[\n\\frac{714}{3} = 238\n\\]\n\nSo, the final answer is **238**.", + "openinference.span.kind": "TOOL" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "0f61758b52ae63f8", + "parent_span_id": "f2bef18525d85d4e", + "name": "turn", + "start_time": 1786569488291183872, + "end_time": 1786569493652321024, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "cc54710d82d329f1", + "parent_span_id": "801484534ca18436", + "name": "response", + "start_time": 1786569493653649920, + "end_time": 1786569494657857792, + "attributes": { + "llm.system": "openai", + "output.mime_type": "application/json", + "output.value": "{\"id\":\"resp_0e455d84153284dd006a7ce315e00c819e86a76962b57147a4\",\"created_at\":1786569493.0,\"error\":null,\"incomplete_details\":null,\"instructions\":\"You are a coordinator agent. You delegate tasks to specialist agents:\\n- Use ask_math_specialist for any mathematical calculations\\n- Use ask_research_specialist for weather or stock price lookups\\nFor tasks that require both, call each specialist as needed and synthesize their results into a final answer.\",\"metadata\":{},\"model\":\"gpt-4o-mini-2024-07-18\",\"object\":\"response\",\"output\":[{\"id\":\"msg_0e455d84153284dd006a7ce3164d30819ea336611ecfcf77f9\",\"content\":[{\"annotations\":[],\"text\":\"The result of \\\\( 42 \\\\) multiplied by \\\\( 17 \\\\), then divided by \\\\( 3 \\\\), is **238**.\",\"type\":\"output_text\",\"logprobs\":[]}],\"role\":\"assistant\",\"status\":\"completed\",\"type\":\"message\",\"phase\":null}],\"parallel_tool_calls\":true,\"temperature\":1.0,\"tool_choice\":\"auto\",\"tools\":[{\"name\":\"ask_math_specialist\",\"parameters\":{\"properties\":{\"query\":{\"description\":\"The math problem to solve.\",\"title\":\"Query\",\"type\":\"string\"}},\"required\":[\"query\"],\"title\":\"ask_math_specialist_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Delegate a math problem to the math specialist agent. Use this for any\\ncalculations involving addition, multiplication, or division.\",\"output_schema\":null},{\"name\":\"ask_research_specialist\",\"parameters\":{\"properties\":{\"query\":{\"description\":\"The research question to answer.\",\"title\":\"Query\",\"type\":\"string\"}},\"required\":[\"query\"],\"title\":\"ask_research_specialist_args\",\"type\":\"object\",\"additionalProperties\":false},\"strict\":true,\"type\":\"function\",\"allowed_callers\":null,\"defer_loading\":null,\"description\":\"Delegate a research question to the research specialist agent. Use this\\nfor weather lookups or stock price queries.\",\"output_schema\":null}],\"top_p\":1.0,\"background\":false,\"completed_at\":1786569494.0,\"conversation\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"moderation\":null,\"previous_response_id\":null,\"prompt\":null,\"prompt_cache_key\":\"agents-sdk:run:2e6da150ddff48ccba7a48f9163b9253\",\"prompt_cache_options\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":null,\"effort\":null,\"generate_summary\":null,\"mode\":null,\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"status\":\"completed\",\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"top_logprobs\":0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":312,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":30,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":342},\"user\":null,\"billing\":{\"payer\":\"developer\"},\"frequency_penalty\":0.0,\"presence_penalty\":0.0,\"store\":true,\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}}}", + "llm.tools.0.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"ask_math_specialist\", \"description\": \"Delegate a math problem to the math specialist agent. Use this for any\\ncalculations involving addition, multiplication, or division.\", \"parameters\": {\"properties\": {\"query\": {\"description\": \"The math problem to solve.\", \"title\": \"Query\", \"type\": \"string\"}}, \"required\": [\"query\"], \"title\": \"ask_math_specialist_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.tools.1.tool.json_schema": "{\"type\": \"function\", \"function\": {\"name\": \"ask_research_specialist\", \"description\": \"Delegate a research question to the research specialist agent. Use this\\nfor weather lookups or stock price queries.\", \"parameters\": {\"properties\": {\"query\": {\"description\": \"The research question to answer.\", \"title\": \"Query\", \"type\": \"string\"}}, \"required\": [\"query\"], \"title\": \"ask_research_specialist_args\", \"type\": \"object\", \"additionalProperties\": false}, \"strict\": true}}", + "llm.token_count.completion": 30, + "llm.token_count.prompt": 312, + "llm.token_count.total": 342, + "llm.token_count.prompt_details.cache_read": 0, + "llm.token_count.completion_details.reasoning": 0, + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.contents.0.message_content.type": "text", + "llm.output_messages.0.message.contents.0.message_content.text": "The result of \\( 42 \\) multiplied by \\( 17 \\), then divided by \\( 3 \\), is **238**.", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a coordinator agent. You delegate tasks to specialist agents:\n- Use ask_math_specialist for any mathematical calculations\n- Use ask_research_specialist for weather or stock price lookups\nFor tasks that require both, call each specialist as needed and synthesize their results into a final answer.", + "llm.model_name": "gpt-4o-mini-2024-07-18", + "llm.invocation_parameters": "{\"id\": \"resp_0e455d84153284dd006a7ce315e00c819e86a76962b57147a4\", \"created_at\": 1786569493.0, \"instructions\": \"You are a coordinator agent. You delegate tasks to specialist agents:\\n- Use ask_math_specialist for any mathematical calculations\\n- Use ask_research_specialist for weather or stock price lookups\\nFor tasks that require both, call each specialist as needed and synthesize their results into a final answer.\", \"metadata\": {}, \"model\": \"gpt-4o-mini-2024-07-18\", \"parallel_tool_calls\": true, \"temperature\": 1.0, \"tool_choice\": \"auto\", \"top_p\": 1.0, \"background\": false, \"completed_at\": 1786569494.0, \"prompt_cache_key\": \"agents-sdk:run:2e6da150ddff48ccba7a48f9163b9253\", \"prompt_cache_retention\": \"in_memory\", \"reasoning\": {}, \"service_tier\": \"default\", \"text\": {\"format\": {\"type\": \"text\"}, \"verbosity\": \"medium\"}, \"top_logprobs\": 0, \"truncation\": \"disabled\", \"billing\": {\"payer\": \"developer\"}, \"frequency_penalty\": 0.0, \"presence_penalty\": 0.0, \"store\": true, \"tool_usage\": {\"image_gen\": {\"input_tokens\": 0, \"input_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"output_tokens\": 0, \"output_tokens_details\": {\"image_tokens\": 0, \"text_tokens\": 0}, \"total_tokens\": 0}, \"web_search\": {\"num_requests\": 0}}}", + "input.mime_type": "application/json", + "input.value": "[{\"content\": \"What is 42 multiplied by 17, then divided by 3?\", \"role\": \"user\"}, {\"arguments\": \"{\\\"query\\\":\\\"42 multiplied by 17, then divided by 3\\\"}\", \"call_id\": \"call_Xw9gngSGFTuCw1vZ30iZHqhk\", \"name\": \"ask_math_specialist\", \"type\": \"function_call\", \"id\": \"fc_0e455d84153284dd006a7ce3118e50819eb35c4d67bde3bea1\", \"status\": \"completed\"}, {\"call_id\": \"call_Xw9gngSGFTuCw1vZ30iZHqhk\", \"output\": \"First, I calculated \\\\( 42 \\\\times 17 \\\\):\\n\\n\\\\[\\n42 \\\\times 17 = 714\\n\\\\]\\n\\nNext, I divided that result by \\\\( 3 \\\\):\\n\\n\\\\[\\n\\\\frac{714}{3} = 238\\n\\\\]\\n\\nSo, the final answer is **238**.\", \"type\": \"function_call_output\"}]", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "What is 42 multiplied by 17, then divided by 3?", + "llm.input_messages.2.message.role": "assistant", + "llm.input_messages.2.message.tool_calls.0.tool_call.id": "call_Xw9gngSGFTuCw1vZ30iZHqhk", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.name": "ask_math_specialist", + "llm.input_messages.2.message.tool_calls.0.tool_call.function.arguments": "{\"query\":\"42 multiplied by 17, then divided by 3\"}", + "llm.input_messages.3.message.role": "tool", + "llm.input_messages.3.message.tool_call_id": "call_Xw9gngSGFTuCw1vZ30iZHqhk", + "llm.input_messages.3.message.content": "First, I calculated \\( 42 \\times 17 \\):\n\n\\[\n42 \\times 17 = 714\n\\]\n\nNext, I divided that result by \\( 3 \\):\n\n\\[\n\\frac{714}{3} = 238\n\\]\n\nSo, the final answer is **238**.", + "openinference.span.kind": "LLM" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "801484534ca18436", + "parent_span_id": "f2bef18525d85d4e", + "name": "turn", + "start_time": 1786569493652689920, + "end_time": 1786569494659105024, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "f2bef18525d85d4e", + "parent_span_id": "c164aa04b7a11544", + "name": "coordinator", + "start_time": 1786569488291055872, + "end_time": 1786569494659516928, + "attributes": { + "llm.system": "openai", + "graph.node.id": "coordinator", + "openinference.span.kind": "AGENT" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "c164aa04b7a11544", + "parent_span_id": "25060f453384d2ff", + "name": "Agent workflow", + "start_time": 1786569488290504960, + "end_time": 1786569494659607808, + "attributes": { + "llm.system": "openai", + "openinference.span.kind": "CHAIN" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + }, + { + "trace_id": "c60958e12a31f7bd1b19cc94b0a96dbe", + "span_id": "25060f453384d2ff", + "parent_span_id": "40398742a36905e5", + "name": "Agent workflow", + "start_time": 1786569488290365000, + "end_time": 1786569494659669000, + "attributes": { + "openinference.span.kind": "AGENT" + }, + "scope": { + "name": "openinference.instrumentation.openai_agents", + "version": "1.6.1" + }, + "status": { + "code": "OK" + }, + "span_events": [] + } +] \ No newline at end of file diff --git a/tests/strands_evals/mappers/test_openinference_session_mapper.py b/tests/strands_evals/mappers/test_openinference_session_mapper.py index d008bf39..923fe5a3 100644 --- a/tests/strands_evals/mappers/test_openinference_session_mapper.py +++ b/tests/strands_evals/mappers/test_openinference_session_mapper.py @@ -20,10 +20,13 @@ _SMOLAGENTS_SPANS_FILE = _FIXTURES_DIR / "smolagents_live_spans.json" _CLAUDE_SPANS_FILE = _FIXTURES_DIR / "claude_live_spans.json" _CLAUDE_ADOT_FILE = _FIXTURES_DIR / "claude_adot_spans.json" +_OPENAI_AGENTS_LIVE_FILE = _FIXTURES_DIR / "openai_agents_openinference_live_spans.json" +_OPENAI_AGENTS_ADOT_FILE = _FIXTURES_DIR / "openai_agents_openinference_adot_spans.json" SCOPE_NAME = "openinference.instrumentation.langchain" SMOLAGENTS_SCOPE_NAME = "openinference.instrumentation.smolagents" CLAUDE_SDK_SCOPE_NAME = "openinference.instrumentation.claude_agent_sdk" +OPENAI_AGENTS_SCOPE_NAME = "openinference.instrumentation.openai_agents" def make_span( @@ -203,6 +206,18 @@ def _load_claude_adot_spans(): return data["session_id"], data["spans"] +def _load_openai_agents_live_spans(): + """Load real OpenAI Agents SDK (openinference-instrumentation-openai-agents) live spans.""" + with open(_OPENAI_AGENTS_LIVE_FILE, encoding="utf-8") as f: + return json.load(f) + + +def _load_openai_agents_adot_spans(): + """Load OpenAI Agents SDK spans from AgentCore (multi-agent with handoffs).""" + with open(_OPENAI_AGENTS_ADOT_FILE, encoding="utf-8") as f: + return json.load(f) + + class TestSpanTypeDetection: def setup_method(self): self.mapper = OpenInferenceSessionMapper() @@ -2115,3 +2130,576 @@ def test_all_spans_have_session_id_attribute(self, claude_adot_session): for span in spans: attrs = span.get("attributes", {}) assert attrs.get("session.id") == session_id + + +# ============================================================================= +# OpenAI Agents SDK Scope Support +# (openinference.instrumentation.openai_agents) +# +# OpenAI Agents SDK instrumentation emits AGENT spans (for the agent itself), +# CHAIN spans (for turns), LLM spans (for response calls), and TOOL spans. +# AGENT spans lack input/output; the normalization step walks descendants to +# inject user_prompt and agent_response from LLM spans. +# ============================================================================= + + +class TestOpenAIAgentsScopeSupport: + """OpenAI Agents SDK-scoped spans: acceptance and conversion.""" + + def setup_method(self): + self.mapper = OpenInferenceSessionMapper() + + def test_openai_agent_span_detected(self): + """AGENT span from openai_agents scope with input+output is agent invocation.""" + span = make_span( + name="math_agent", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "AGENT", + "input.value": "What is 15 multiplied by 37?", + "output.value": "15 multiplied by 37 is 555.", + }, + ) + assert self.mapper._is_agent_invocation_span(span) is True + + def test_openai_agent_span_without_input_rejected(self): + """AGENT span from openai_agents scope without input is NOT agent invocation.""" + span = make_span( + name="Agent workflow", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "AGENT", + }, + ) + assert self.mapper._is_agent_invocation_span(span) is False + + def test_openai_llm_span_detected_as_inference(self): + """LLM span from openai_agents scope detected as inference.""" + span = make_span( + name="response", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "LLM", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a math assistant.", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "What is 15 * 37?", + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "calculator", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": '{"expression":"15 * 37"}', + "llm.output_messages.0.message.tool_calls.0.tool_call.id": "call_abc123", + }, + ) + assert self.mapper._is_inference_span(span) is True + + def test_openai_tool_span_detected(self): + """TOOL span from openai_agents scope detected as tool execution.""" + span = make_span( + name="calculator", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "TOOL", + "tool.name": "calculator", + "input.value": '{"expression":"15 * 37"}', + "input.mime_type": "application/json", + "output.value": "555", + }, + ) + assert self.mapper._is_tool_execution_span(span) is True + + def test_openai_chain_span_not_agent_invocation(self): + """CHAIN span from openai_agents scope is NOT detected as agent invocation.""" + span = make_span( + name="turn", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "CHAIN", + }, + ) + assert self.mapper._is_agent_invocation_span(span) is False + + def test_openai_agent_span_conversion(self): + """AGENT span with input+output produces AgentInvocationSpan.""" + span = make_span( + name="math_agent", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "AGENT", + "input.value": "What is 15 multiplied by 37?", + "output.value": "15 multiplied by 37 is 555.", + "graph.node.id": "math_agent", + "llm.tools.0.tool.json_schema": json.dumps( + { + "type": "function", + "function": { + "name": "calculator", + "description": "Evaluate a mathematical expression.", + "parameters": {"type": "object", "properties": {"expression": {"type": "string"}}}, + }, + } + ), + }, + ) + session = self.mapper.map_to_session([span], "sess-1") + + all_spans = [s for t in session.traces for s in t.spans] + agent_spans = [s for s in all_spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 1 + assert agent_spans[0].user_prompt == "What is 15 multiplied by 37?" + assert agent_spans[0].agent_response == "15 multiplied by 37 is 555." + assert len(agent_spans[0].available_tools) == 1 + tool = agent_spans[0].available_tools[0] + assert tool.name == "calculator" + assert tool.parameters == {"type": "object", "properties": {"expression": {"type": "string"}}} + + def test_openai_tool_span_bare_string_output(self): + """TOOL span with bare string output.value (not JSON) produces ToolExecutionSpan.""" + span = make_span( + name="calculator", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "TOOL", + "tool.name": "calculator", + "input.value": '{"expression":"15 * 37"}', + "input.mime_type": "application/json", + "output.value": "555", + }, + ) + session = self.mapper.map_to_session([span], "sess-1") + + tool_spans = [s for t in session.traces for s in t.spans if isinstance(s, ToolExecutionSpan)] + assert len(tool_spans) == 1 + assert tool_spans[0].tool_call.name == "calculator" + assert tool_spans[0].tool_call.arguments == {"expression": "15 * 37"} + assert tool_spans[0].tool_result.content == "555" + + def test_openai_normalization_injects_input_output_on_agent_span(self): + """Normalization walks LLM descendants to inject input/output on AGENT span.""" + # Simulate the real span structure: AGENT → CHAIN(turn) → LLM(response) + agent_span = make_span( + trace_id="t1", + span_id="agent-1", + name="math_agent", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "AGENT", + "graph.node.id": "math_agent", + }, + ) + turn_span = make_span( + trace_id="t1", + span_id="turn-1", + parent_span_id="agent-1", + name="turn", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "CHAIN", + }, + ) + llm_span = make_span( + trace_id="t1", + span_id="llm-1", + parent_span_id="turn-1", + name="response", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "LLM", + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are a math assistant.", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "What is 5 + 3?", + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.contents.0.message_content.type": "text", + "llm.output_messages.0.message.contents.0.message_content.text": "5 + 3 is 8.", + "llm.output_messages.0.message.content": "5 + 3 is 8.", + "llm.tools.0.tool.json_schema": json.dumps( + { + "type": "function", + "function": { + "name": "calculator", + "description": "Evaluate math.", + "parameters": {"type": "object"}, + }, + } + ), + }, + ) + + session = self.mapper.map_to_session([agent_span, turn_span, llm_span], "sess-1") + + agent_spans = [s for t in session.traces for s in t.spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 1 + assert agent_spans[0].user_prompt == "What is 5 + 3?" + assert agent_spans[0].agent_response == "5 + 3 is 8." + assert len(agent_spans[0].available_tools) == 1 + assert agent_spans[0].available_tools[0].name == "calculator" + + def test_openai_errored_agent_span_detected(self): + """AGENT span with input.value + ERROR status is detected as agent invocation.""" + span = make_span( + name="math_agent", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "AGENT", + "input.value": "Calculate something complex", + }, + ) + span["status"] = {"code": "ERROR", "description": "Rate limit exceeded"} + + assert self.mapper._is_agent_invocation_span(span) is True + + session = self.mapper.map_to_session([span], "sess-1") + agent_spans = [s for t in session.traces for s in t.spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 1 + assert agent_spans[0].user_prompt == "Calculate something complex" + assert agent_spans[0].agent_response == "Rate limit exceeded" + + def test_openai_handoff_tool_span_with_error_status(self): + """Handoff TOOL span with ERROR status produces ToolExecutionSpan with error.""" + span = make_span( + name="handoff to math_specialist", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "TOOL", + "input.value": "coordinator", + "output.value": "math_specialist", + }, + ) + span["status"] = {"code": "ERROR"} + + session = self.mapper.map_to_session([span], "sess-1") + # Handoff spans with plain string input/output should still parse + tool_spans = [s for t in session.traces for s in t.spans if isinstance(s, ToolExecutionSpan)] + assert len(tool_spans) == 1 + + def test_normalization_survives_heterogeneous_start_times(self): + """Descendant LLM sort tolerates mixed start_time types (int / ISO str / None). + + parse_timestamp normalizes each to a comparable datetime, so the sort no + longer raises TypeError on a group with heterogeneous timestamps. + """ + agent = make_span( + trace_id="t1", + span_id="agent-1", + name="math_agent", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={"openinference.span.kind": "AGENT"}, + ) + # Two turns, each with an LLM descendant, carrying incompatible start_time types. + llm_common = { + "openinference.span.kind": "LLM", + "llm.input_messages.0.message.role": "user", + "llm.input_messages.0.message.content": "What is 5 + 3?", + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.content": "5 + 3 is 8.", + } + turn_a = make_span( + trace_id="t1", + span_id="turn-a", + parent_span_id="agent-1", + name="turn", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={"openinference.span.kind": "CHAIN"}, + ) + llm_a = make_span( + trace_id="t1", + span_id="llm-a", + parent_span_id="turn-a", + name="response", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes=dict(llm_common), + start_time=1700000005000000000, + ) # int ns + turn_b = make_span( + trace_id="t1", + span_id="turn-b", + parent_span_id="agent-1", + name="turn", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={"openinference.span.kind": "CHAIN"}, + ) + llm_b = make_span( + trace_id="t1", + span_id="llm-b", + parent_span_id="turn-b", + name="response", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes=dict(llm_common), + start_time="2024-01-01T00:00:00Z", + ) # ISO string + + # Should not raise, and the agent span is still produced. + session = self.mapper.map_to_session([agent, turn_a, llm_a, turn_b, llm_b], "sess-1") + agent_spans = [s for t in session.traces for s in t.spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 1 + assert agent_spans[0].user_prompt == "What is 5 + 3?" + assert agent_spans[0].agent_response == "5 + 3 is 8." + + def test_normalization_survives_none_attributes_span(self): + """A span with attributes=None in the group is skipped, not fatal to the session.""" + agent = make_span( + trace_id="t1", + span_id="agent-1", + name="math_agent", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={"openinference.span.kind": "AGENT"}, + ) + turn = make_span( + trace_id="t1", + span_id="turn-1", + parent_span_id="agent-1", + name="turn", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={"openinference.span.kind": "CHAIN"}, + ) + llm = make_span( + trace_id="t1", + span_id="llm-1", + parent_span_id="turn-1", + name="response", + scope_name=OPENAI_AGENTS_SCOPE_NAME, + attributes={ + "openinference.span.kind": "LLM", + "llm.input_messages.0.message.role": "user", + "llm.input_messages.0.message.content": "What is 5 + 3?", + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.content": "5 + 3 is 8.", + }, + ) + # Malformed sibling: attributes explicitly None (not absent). + bad = make_span(trace_id="t1", span_id="bad-1", parent_span_id="turn-1", scope_name=OPENAI_AGENTS_SCOPE_NAME) + bad["attributes"] = None + + session = self.mapper.map_to_session([agent, turn, llm, bad], "sess-1") + agent_spans = [s for t in session.traces for s in t.spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 1 + assert agent_spans[0].user_prompt == "What is 5 + 3?" + + def test_get_last_message_text_uses_current_turn_user_message(self): + """Multi-turn input: the highest-indexed user message wins, not the oldest.""" + attrs = { + "llm.input_messages.0.message.role": "system", + "llm.input_messages.0.message.content": "You are helpful.", + "llm.input_messages.1.message.role": "user", + "llm.input_messages.1.message.content": "What is 15 * 37?", + "llm.input_messages.2.message.role": "assistant", + "llm.input_messages.2.message.content": "555", + "llm.input_messages.3.message.role": "user", + "llm.input_messages.3.message.content": "Now divide it by 5", + } + result = self.mapper._get_last_message_text(attrs, "llm.input_messages.", role="user") + assert result == "Now divide it by 5" + + def test_get_last_message_text_skips_reasoning_item(self): + """A reasoning item at the highest output index is skipped for the real answer.""" + attrs = { + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.content": "The answer is 8.", + "llm.output_messages.1.message.role": "assistant", + "llm.output_messages.1.message.contents.0.message_content.type": "reasoning", + "llm.output_messages.1.message.contents.0.message_content.text": "Let me think step by step...", + } + result = self.mapper._get_last_message_text(attrs, "llm.output_messages.") + assert result == "The answer is 8." + + def test_get_last_tool_calls_renders_names_and_args(self): + """Tool calls render as name(args); empty-arg calls render as name(); joined by '; '.""" + attrs = { + "llm.output_messages.0.message.role": "assistant", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.name": "ask_math_specialist", + "llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments": '{"query":"42 * 17"}', + "llm.output_messages.0.message.tool_calls.1.tool_call.function.name": "ask_research_specialist", + } + result = self.mapper._get_last_tool_calls(attrs) + assert result == 'ask_math_specialist({"query":"42 * 17"}); ask_research_specialist()' + + +# ============================================================================= +# Integration Tests: Real OpenAI Agents SDK fixture (Live) +# (openinference.instrumentation.openai_agents) +# +# These tests use ACTUAL spans produced by the instrumentor, validating that the +# mapper handles real-world OpenAI Agents SDK trace structure: +# - LLM "response" spans with tool_calls and plural contents.N message parts +# - TOOL spans with bare-string output.value (the delegated sub-agent answer) +# - AGENT spans with NO input/output (normalization injects from LLM descendants) +# - CHAIN "turn" spans that are intermediaries (not mapped directly) +# ============================================================================= + + +@pytest.fixture(scope="module") +def openai_agents_live_session(): + """Map real OpenAI Agents SDK live spans to a Session.""" + spans = _load_openai_agents_live_spans() + mapper = OpenInferenceSessionMapper() + return mapper.map_to_session(spans, "openai-agents-live-sess") + + +class TestOpenAIAgentsLiveFixtureIntegration: + """Integration tests using a real OpenAI Agents SDK live trace. + + Multi-agent, agents-as-tools pattern: a coordinator delegates to a + math_specialist via an `ask_math_specialist` tool; the specialist uses + multiply_numbers / divide_numbers to answer "42 multiplied by 17, then + divided by 3". + """ + + def test_session_has_traces(self, openai_agents_live_session): + """Live fixture produces at least one trace.""" + assert len(openai_agents_live_session.traces) >= 1 + + def test_tool_span_is_multiply_numbers(self, openai_agents_live_session): + """The multiply_numbers tool is correctly identified with arguments and result.""" + all_spans = [s for t in openai_agents_live_session.traces for s in t.spans] + tool_spans = [s for s in all_spans if isinstance(s, ToolExecutionSpan)] + assert len(tool_spans) >= 1 + + multiply = next((s for s in tool_spans if s.tool_call.name == "multiply_numbers"), None) + assert multiply is not None + assert multiply.tool_call.arguments == {"a": 42, "b": 17} + assert multiply.tool_result.content == "714" + + def test_agent_span_has_prompt_and_response(self, openai_agents_live_session): + """Every agent span has a non-empty user_prompt and agent_response.""" + all_spans = [s for t in openai_agents_live_session.traces for s in t.spans] + agent_spans = [s for s in all_spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) >= 1 + + for agent in agent_spans: + assert agent.user_prompt + assert agent.agent_response + assert any("42 multiplied by 17" in a.user_prompt for a in agent_spans) + + def test_agent_span_has_tools(self, openai_agents_live_session): + """The specialist's math tools are attached to an agent span.""" + all_spans = [s for t in openai_agents_live_session.traces for s in t.spans] + agent_spans = [s for s in all_spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) >= 1 + + tool_names = {t.name for a in agent_spans for t in a.available_tools} + assert "multiply_numbers" in tool_names + + def test_chain_and_wrapper_agent_spans_filtered(self, openai_agents_live_session): + """CHAIN turns and wrapper AGENT spans (no input/output) are not mapped as AgentInvocation.""" + all_spans = [s for t in openai_agents_live_session.traces for s in t.spans] + agent_spans = [s for s in all_spans if isinstance(s, AgentInvocationSpan)] + # Only the named agent (math_agent) should produce an AgentInvocationSpan, + # not the wrapper "Agent workflow" spans + for agent in agent_spans: + assert agent.user_prompt != "" + assert agent.agent_response != "" + + def test_agent_span_count_per_trace(self, openai_agents_live_session): + """Both agents (coordinator + math_specialist) map; the wrapper is excluded.""" + for trace in openai_agents_live_session.traces: + agent_spans = [s for s in trace.spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 2 + + +# ============================================================================= +# Integration Tests: Real OpenAI Agents SDK fixture (ADOT) +# (openinference.instrumentation.openai_agents) +# +# These tests use ACTUAL spans produced by the instrumentor, validating that the +# mapper handles real-world OpenAI Agents SDK trace structure: +# - coordinator AGENT span whose LLM turn is tool-call-only (handoff, no text) +# - "handoff to math_specialist" TOOL span; multiply_numbers TOOL span +# - AGENT spans with NO input/output (normalization injects from LLM descendants) +# - httpx / starlette spans (non-openinference) that must be filtered out +# ============================================================================= + + +@pytest.fixture(scope="module") +def openai_agents_adot_session(): + """Map real OpenAI Agents SDK ADOT spans (multi-agent) to a Session.""" + spans = _load_openai_agents_adot_spans() + mapper = OpenInferenceSessionMapper() + return mapper.map_to_session(spans, "openai-agents-adot-sess") + + +class TestOpenAIAgentsAdotFixtureIntegration: + """Integration tests using real OpenAI Agents SDK multi-agent trace from AgentCore.""" + + def test_session_has_one_trace(self, openai_agents_adot_session): + """All spans share one trace_id → one trace.""" + assert len(openai_agents_adot_session.traces) == 1 + + def test_non_openinference_spans_filtered(self, openai_agents_adot_session): + """httpx and starlette spans are filtered out (wrong scope).""" + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + # The fixture has 16 raw spans but only scoped spans should be processed + assert len(all_spans) < 16 + + def test_multiply_numbers_tool_span(self, openai_agents_adot_session): + """multiply_numbers tool call is correctly extracted with arguments and result.""" + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + tool_spans = [s for s in all_spans if isinstance(s, ToolExecutionSpan)] + + multiply = next((s for s in tool_spans if s.tool_call.name == "multiply_numbers"), None) + assert multiply is not None + assert multiply.tool_call.arguments == {"a": 42, "b": 7} + assert multiply.tool_result.content == "294.0" + + def test_math_specialist_agent_span(self, openai_agents_adot_session): + """math_specialist agent (the one that answered) has the computed result.""" + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + agent_spans = [s for s in all_spans if isinstance(s, AgentInvocationSpan)] + + # Select the specialist by its answer, not by the prompt: both the + # coordinator and math_specialist carry the top-level question, so the + # response is what distinguishes the agent that actually computed 294. + math_agent = next((a for a in agent_spans if "294" in (a.agent_response or "")), None) + assert math_agent is not None + assert "294" in math_agent.agent_response + # The specialist carries its own math tools (not the coordinator's transfer tools). + assert "multiply_numbers" in {t.name for t in math_agent.available_tools} + + def test_coordinator_survives_with_delegation_output(self, openai_agents_adot_session): + """A tool-call-only orchestrator turn is captured, with the handoff as its output. + + The coordinator's only LLM emits transfer tool calls and no text; the + delegation act is surfaced as its output so the invocation isn't dropped. + """ + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + agent_spans = [s for s in all_spans if isinstance(s, AgentInvocationSpan)] + + coordinator = next((a for a in agent_spans if "[delegated]" in (a.agent_response or "")), None) + assert coordinator is not None + assert "transfer_to_math_specialist" in coordinator.agent_response + # Its own transfer tools are attached, not the specialist's math tools. + assert "transfer_to_math_specialist" in {t.name for t in coordinator.available_tools} + + def test_both_agents_survive(self, openai_agents_adot_session): + """Coordinator and math_specialist each map to their own agent span.""" + for trace in openai_agents_adot_session.traces: + agent_spans = [s for s in trace.spans if isinstance(s, AgentInvocationSpan)] + assert len(agent_spans) == 2 + + def test_handoff_tool_owned_by_coordinator(self, openai_agents_adot_session): + """The handoff tool is attributed to the coordinator that issued it, not the receiver.""" + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + coordinator = next( + s for s in all_spans if isinstance(s, AgentInvocationSpan) and "[delegated]" in (s.agent_response or "") + ) + handoff = next( + s for s in all_spans if isinstance(s, ToolExecutionSpan) and s.tool_call.name.startswith("handoff") + ) + assert handoff.agent_span_id == coordinator.span_info.span_id + + def test_multiply_numbers_owned_by_math_specialist(self, openai_agents_adot_session): + """The multiply_numbers tool is attributed to the specialist that ran it.""" + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + specialist = next( + s for s in all_spans if isinstance(s, AgentInvocationSpan) and "[delegated]" not in (s.agent_response or "") + ) + multiply = next( + s for s in all_spans if isinstance(s, ToolExecutionSpan) and s.tool_call.name == "multiply_numbers" + ) + assert multiply.agent_span_id == specialist.span_info.span_id + assert multiply.span_info.parent_span_id == specialist.span_info.span_id + + def test_all_spans_have_session_id(self, openai_agents_adot_session): + """All converted spans carry the session_id.""" + all_spans = [s for t in openai_agents_adot_session.traces for s in t.spans] + for span in all_spans: + assert span.span_info.session_id == "openai-agents-adot-sess"