Skip to content

Commit 485a7ff

Browse files
committed
remove context propagation and fix issue with llm attributes
1 parent a5c5cbe commit 485a7ff

4 files changed

Lines changed: 121 additions & 332 deletions

File tree

agentops/instrumentation/openai/attributes/response.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@
8383
SpanAttributes.LLM_RESPONSE_ID: "id",
8484
SpanAttributes.LLM_REQUEST_MODEL: "model",
8585
SpanAttributes.LLM_RESPONSE_MODEL: "model",
86-
SpanAttributes.LLM_PROMPTS: "instructions",
8786
SpanAttributes.LLM_REQUEST_MAX_TOKENS: "max_output_tokens",
8887
SpanAttributes.LLM_REQUEST_TEMPERATURE: "temperature",
8988
SpanAttributes.LLM_REQUEST_TOP_P: "top_p",

agentops/instrumentation/openai_agents/attributes/common.py

Lines changed: 113 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@
2424
from agentops.instrumentation.openai.attributes.response import get_response_response_attributes
2525
from agentops.instrumentation.openai_agents import LIBRARY_NAME, LIBRARY_VERSION
2626

27-
from agentops.instrumentation.openai_agents.context import (
28-
full_prompt_contextvar,
29-
agent_name_contextvar,
30-
agent_handoffs_contextvar,
31-
)
3227
from agentops.instrumentation.openai_agents.attributes.model import (
3328
get_model_attributes,
3429
get_model_config_attributes,
@@ -46,10 +41,13 @@
4641
}
4742

4843

44+
# Attribute mapping for FunctionSpanData
4945
FUNCTION_TOOL_ATTRIBUTES: AttributeMap = {
5046
ToolAttributes.TOOL_NAME: "name",
5147
ToolAttributes.TOOL_PARAMETERS: "input",
5248
ToolAttributes.TOOL_RESULT: "output",
49+
# AgentAttributes.AGENT_NAME: "name",
50+
AgentAttributes.FROM_AGENT: "from_agent",
5351
}
5452

5553

@@ -68,7 +66,9 @@
6866

6967
# Attribute mapping for ResponseSpanData
7068
RESPONSE_SPAN_ATTRIBUTES: AttributeMap = {
71-
WorkflowAttributes.WORKFLOW_INPUT: "input",
69+
# Don't map input here as it causes double serialization
70+
# We handle prompts manually in get_response_span_attributes
71+
SpanAttributes.LLM_RESPONSE_MODEL: "model",
7272
}
7373

7474

@@ -193,17 +193,12 @@ def get_agent_span_attributes(span_data: Any) -> AttributeMap:
193193

194194
attributes[SpanAttributes.AGENTOPS_SPAN_KIND] = AgentOpsSpanKindValues.AGENT.value
195195

196-
# Get agent name from contextvar (set by instrumentor wrapper)
197-
ctx_agent_name = agent_name_contextvar.get()
198-
if ctx_agent_name:
199-
attributes[AgentAttributes.AGENT_NAME] = ctx_agent_name
200-
elif hasattr(span_data, "name") and span_data.name:
196+
# Get agent name directly from span_data
197+
if hasattr(span_data, "name") and span_data.name:
201198
attributes[AgentAttributes.AGENT_NAME] = str(span_data.name)
202199

203-
ctx_handoffs = agent_handoffs_contextvar.get()
204-
if ctx_handoffs:
205-
attributes[AgentAttributes.HANDOFFS] = safe_serialize(ctx_handoffs)
206-
elif hasattr(span_data, "handoffs") and span_data.handoffs:
200+
# Get handoffs directly from span_data
201+
if hasattr(span_data, "handoffs") and span_data.handoffs:
207202
attributes[AgentAttributes.HANDOFFS] = safe_serialize(span_data.handoffs)
208203

209204
if hasattr(span_data, "tools") and span_data.tools:
@@ -278,37 +273,111 @@ def get_response_span_attributes(span_data: Any) -> AttributeMap:
278273
Returns:
279274
Dictionary of attributes for response span
280275
"""
276+
# Debug logging
277+
import json
278+
print(f"\n[DEBUG] get_response_span_attributes called")
279+
print(f"[DEBUG] span_data type: {type(span_data)}")
280+
print(f"[DEBUG] span_data attributes: {[attr for attr in dir(span_data) if not attr.startswith('_')]}")
281+
282+
# Check what's in span_data.input
283+
if hasattr(span_data, "input"):
284+
print(f"[DEBUG] span_data.input type: {type(span_data.input)}")
285+
try:
286+
print(f"[DEBUG] span_data.input content: {json.dumps(span_data.input, indent=2) if span_data.input else 'None'}")
287+
except:
288+
print(f"[DEBUG] span_data.input content (repr): {repr(span_data.input)}")
289+
290+
# Check for response and instructions
291+
if hasattr(span_data, "response") and span_data.response:
292+
print(f"[DEBUG] span_data.response type: {type(span_data.response)}")
293+
if hasattr(span_data.response, "instructions"):
294+
print(f"[DEBUG] span_data.response.instructions: {span_data.response.instructions}")
295+
281296
# Get basic attributes from mapping
282297
attributes = _extract_attributes_from_mapping(span_data, RESPONSE_SPAN_ATTRIBUTES)
283298
attributes.update(get_common_attributes())
284299

285-
prompt_attributes_set = False
286-
287-
# Read full prompt from contextvar (set by instrumentor's wrapper)
288-
full_prompt_from_context = full_prompt_contextvar.get()
289-
if full_prompt_from_context:
290-
attributes.update(_get_llm_messages_attributes(full_prompt_from_context, "gen_ai.prompt"))
291-
prompt_attributes_set = True
292-
else:
293-
if (
294-
span_data.response
295-
and hasattr(span_data.response, "request_messages")
296-
and span_data.response.request_messages
297-
):
298-
prompt_messages_from_sdk = span_data.response.request_messages
299-
attributes.update(_get_llm_messages_attributes(prompt_messages_from_sdk, "gen_ai.prompt"))
300-
prompt_attributes_set = True
300+
# Build complete prompt list from system instructions and conversation history
301+
prompt_messages = []
302+
303+
# Add system instruction as first message if available
304+
if span_data.response and hasattr(span_data.response, "instructions") and span_data.response.instructions:
305+
prompt_messages.append({
306+
"role": "system",
307+
"content": span_data.response.instructions
308+
})
309+
print(f"[DEBUG] Added system message from instructions")
310+
311+
# Add conversation history from span_data.input
312+
if hasattr(span_data, "input") and span_data.input:
313+
if isinstance(span_data.input, list):
314+
for i, msg in enumerate(span_data.input):
315+
print(f"[DEBUG] Processing message {i}: type={type(msg)}")
316+
if isinstance(msg, dict):
317+
role = msg.get("role")
318+
content = msg.get("content")
319+
print(f"[DEBUG] Message {i}: role={role}, content type={type(content)}")
320+
321+
# Handle different content formats
322+
if role and content is not None:
323+
# If content is a string, use it directly
324+
if isinstance(content, str):
325+
prompt_messages.append({
326+
"role": role,
327+
"content": content
328+
})
329+
# If content is a list (complex assistant message), extract text
330+
elif isinstance(content, list):
331+
text_parts = []
332+
for item in content:
333+
if isinstance(item, dict):
334+
# Handle output_text type
335+
if item.get("type") == "output_text":
336+
text_parts.append(item.get("text", ""))
337+
# Handle other text content
338+
elif "text" in item:
339+
text_parts.append(item.get("text", ""))
340+
# Handle annotations with text
341+
elif "annotations" in item and "text" in item:
342+
text_parts.append(item.get("text", ""))
343+
344+
if text_parts:
345+
prompt_messages.append({
346+
"role": role,
347+
"content": " ".join(text_parts)
348+
})
349+
# If content is a dict, try to extract text
350+
elif isinstance(content, dict):
351+
if "text" in content:
352+
prompt_messages.append({
353+
"role": role,
354+
"content": content["text"]
355+
})
356+
elif isinstance(span_data.input, str):
357+
# Single string input - assume it's a user message
358+
prompt_messages.append({
359+
"role": "user",
360+
"content": span_data.input
361+
})
362+
print(f"[DEBUG] Added user message from string input")
363+
364+
print(f"[DEBUG] Total prompt_messages: {len(prompt_messages)}")
365+
for i, msg in enumerate(prompt_messages):
366+
print(f"[DEBUG] prompt_messages[{i}]: role={msg.get('role')}, content_len={len(str(msg.get('content', '')))}")
367+
368+
# Format prompts using existing function
369+
if prompt_messages:
370+
attributes.update(_get_llm_messages_attributes(prompt_messages, "gen_ai.prompt"))
301371

302372
# Process response attributes
303373
if span_data.response:
304374
openai_style_response_attrs = get_response_response_attributes(span_data.response)
305375

306-
# Remove prompt attributes if already set from context
307-
if prompt_attributes_set:
308-
keys_to_remove = [k for k in openai_style_response_attrs if k.startswith("gen_ai.prompt")]
309-
for key in keys_to_remove:
310-
if key in openai_style_response_attrs:
311-
del openai_style_response_attrs[key]
376+
# Remove any prompt attributes from response processing since we handle them above
377+
keys_to_remove = [k for k in openai_style_response_attrs if k.startswith("gen_ai.prompt")]
378+
for key in keys_to_remove:
379+
if key in openai_style_response_attrs:
380+
del openai_style_response_attrs[key]
312381

313382
# Remove tool definitions from response attributes
314383
if "gen_ai.request.tools" in openai_style_response_attrs:
@@ -317,6 +386,11 @@ def get_response_span_attributes(span_data: Any) -> AttributeMap:
317386
attributes.update(openai_style_response_attrs)
318387

319388
attributes[SpanAttributes.AGENTOPS_SPAN_KIND] = AgentOpsSpanKindValues.LLM.value
389+
390+
print(f"[DEBUG] Final attributes keys: {list(attributes.keys())}")
391+
prompt_keys = [k for k in attributes.keys() if k.startswith("gen_ai.prompt")]
392+
print(f"[DEBUG] Prompt attribute keys: {prompt_keys}")
393+
320394
return attributes
321395

322396

@@ -336,20 +410,8 @@ def get_generation_span_attributes(span_data: Any) -> AttributeMap:
336410
) # This might set gen_ai.prompt from span_data.input
337411
attributes.update(get_common_attributes())
338412

339-
# Read full prompt from contextvar (set by instrumentor's wrapper)
340-
full_prompt_from_context = full_prompt_contextvar.get()
341-
342-
if full_prompt_from_context:
343-
# Clear any prompt set by _extract_attributes_from_mapping from span_data.input
344-
prompt_keys_to_clear = [k for k in attributes if k.startswith("gen_ai.prompt")]
345-
if SpanAttributes.LLM_PROMPTS in attributes:
346-
prompt_keys_to_clear.append(SpanAttributes.LLM_PROMPTS)
347-
for key in set(prompt_keys_to_clear):
348-
if key in attributes:
349-
del attributes[key]
350-
351-
attributes.update(_get_llm_messages_attributes(full_prompt_from_context, "gen_ai.prompt"))
352-
elif SpanAttributes.LLM_PROMPTS in attributes: # Fallback to span_data.input if contextvar is empty
413+
# Process prompt from span_data.input
414+
if SpanAttributes.LLM_PROMPTS in attributes:
353415
raw_prompt_input = attributes.pop(SpanAttributes.LLM_PROMPTS)
354416
formatted_prompt_for_llm = []
355417
if isinstance(raw_prompt_input, str):

agentops/instrumentation/openai_agents/context.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)