Skip to content

Commit 7a23cb0

Browse files
committed
chore: updated semantic conventions on Generative AI spans
1 parent 824c21e commit 7a23cb0

File tree

3 files changed

+127
-54
lines changed

3 files changed

+127
-54
lines changed

src/strands/event_loop/event_loop.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def event_loop_cycle(
104104
# Create tracer span for this event loop cycle
105105
tracer = get_tracer()
106106
cycle_span = tracer.start_event_loop_cycle_span(
107-
event_loop_kwargs=kwargs, parent_span=event_loop_parent_span, messages=messages
107+
event_loop_kwargs=kwargs, messages=messages, parent_span=event_loop_parent_span
108108
)
109109
kwargs["event_loop_cycle_span"] = cycle_span
110110

@@ -126,8 +126,8 @@ def event_loop_cycle(
126126
for attempt in range(MAX_ATTEMPTS):
127127
model_id = model.config.get("model_id") if hasattr(model, "config") else None
128128
model_invoke_span = tracer.start_model_invoke_span(
129-
parent_span=cycle_span,
130129
messages=messages,
130+
parent_span=cycle_span,
131131
model_id=model_id,
132132
)
133133

src/strands/telemetry/tracer.py

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -196,20 +196,31 @@ def end_span_with_error(self, span: Span, error_message: str, exception: Optiona
196196
error = exception or Exception(error_message)
197197
self._end_span(span, error=error)
198198

199+
def _add_event(self, span: Optional[Span], event_name: str, event_attributes: Dict[str, AttributeValue]) -> None:
200+
"""Add an event with attributes to a span.
201+
202+
Args:
203+
span: The span to add the event to
204+
event_name: Name of the event
205+
event_attributes: Dictionary of attributes to set on the event
206+
"""
207+
if not span:
208+
return
209+
210+
span.add_event(event_name, attributes=event_attributes)
211+
199212
def start_model_invoke_span(
200213
self,
214+
messages: Messages,
201215
parent_span: Optional[Span] = None,
202-
agent_name: str = "Strands Agent",
203-
messages: Optional[Messages] = None,
204216
model_id: Optional[str] = None,
205217
**kwargs: Any,
206218
) -> Optional[Span]:
207219
"""Start a new span for a model invocation.
208220
209221
Args:
222+
messages: Messages being sent to the model.
210223
parent_span: Optional parent span to link this span to.
211-
agent_name: Name of the agent making the model call.
212-
messages: Optional messages being sent to the model.
213224
model_id: Optional identifier for the model being invoked.
214225
**kwargs: Additional attributes to add to the span.
215226
@@ -219,8 +230,6 @@ def start_model_invoke_span(
219230
attributes: Dict[str, AttributeValue] = {
220231
"gen_ai.system": "strands-agents",
221232
"gen_ai.operation.name": "chat",
222-
"gen_ai.agent.name": agent_name,
223-
"gen_ai.prompt": serialize(messages),
224233
}
225234

226235
if model_id:
@@ -229,7 +238,14 @@ def start_model_invoke_span(
229238
# Add additional kwargs as attributes
230239
attributes.update({k: v for k, v in kwargs.items() if isinstance(v, (str, int, float, bool))})
231240

232-
return self._start_span("Model invoke", parent_span, attributes, span_kind=trace_api.SpanKind.CLIENT)
241+
span = self._start_span("Model invoke", parent_span, attributes, span_kind=trace_api.SpanKind.CLIENT)
242+
for message in messages:
243+
self._add_event(
244+
span,
245+
f"gen_ai.{message['role']}.message",
246+
{"content": serialize(message["content"])},
247+
)
248+
return span
233249

234250
def end_model_invoke_span(
235251
self, span: Span, message: Message, usage: Usage, error: Optional[Exception] = None
@@ -243,14 +259,19 @@ def end_model_invoke_span(
243259
error: Optional exception if the model call failed.
244260
"""
245261
attributes: Dict[str, AttributeValue] = {
246-
"gen_ai.completion": serialize(message["content"]),
247262
"gen_ai.usage.prompt_tokens": usage["inputTokens"],
248263
"gen_ai.usage.input_tokens": usage["inputTokens"],
249264
"gen_ai.usage.completion_tokens": usage["outputTokens"],
250265
"gen_ai.usage.output_tokens": usage["outputTokens"],
251266
"gen_ai.usage.total_tokens": usage["totalTokens"],
252267
}
253268

269+
self._add_event(
270+
span,
271+
"gen_ai.choice",
272+
event_attributes={"finish_reason": "stop", "message": serialize(message["content"])},
273+
)
274+
254275
self._end_span(span, attributes, error)
255276

256277
def start_tool_call_span(self, tool: ToolUse, parent_span: Optional[Span] = None, **kwargs: Any) -> Optional[Span]:
@@ -265,18 +286,29 @@ def start_tool_call_span(self, tool: ToolUse, parent_span: Optional[Span] = None
265286
The created span, or None if tracing is not enabled.
266287
"""
267288
attributes: Dict[str, AttributeValue] = {
268-
"gen_ai.prompt": serialize(tool),
289+
"gen_ai.operation.name": "execute_tool",
269290
"gen_ai.system": "strands-agents",
270-
"tool.name": tool["name"],
271-
"tool.id": tool["toolUseId"],
272-
"tool.parameters": serialize(tool["input"]),
291+
"gen_ai.tool.name": tool["name"],
292+
"gen_ai.tool.call.id": tool["toolUseId"],
273293
}
274294

275295
# Add additional kwargs as attributes
276296
attributes.update(kwargs)
277297

278298
span_name = f"Tool: {tool['name']}"
279-
return self._start_span(span_name, parent_span, attributes, span_kind=trace_api.SpanKind.INTERNAL)
299+
span = self._start_span(span_name, parent_span, attributes, span_kind=trace_api.SpanKind.INTERNAL)
300+
301+
self._add_event(
302+
span,
303+
"gen_ai.tool.message",
304+
event_attributes={
305+
"role": "tool",
306+
"content": serialize(tool["input"]),
307+
"id": tool["toolUseId"],
308+
},
309+
)
310+
311+
return span
280312

281313
def end_tool_call_span(
282314
self, span: Span, tool_result: Optional[ToolResult], error: Optional[Exception] = None
@@ -293,30 +325,36 @@ def end_tool_call_span(
293325
status = tool_result.get("status")
294326
status_str = str(status) if status is not None else ""
295327

296-
tool_result_content_json = serialize(tool_result.get("content"))
297328
attributes.update(
298329
{
299-
"tool.result": tool_result_content_json,
300-
"gen_ai.completion": tool_result_content_json,
301330
"tool.status": status_str,
302331
}
303332
)
304333

334+
self._add_event(
335+
span,
336+
"gen_ai.choice",
337+
event_attributes={
338+
"message": serialize(tool_result.get("content")),
339+
"id": tool_result.get("toolUseId", ""),
340+
},
341+
)
342+
305343
self._end_span(span, attributes, error)
306344

307345
def start_event_loop_cycle_span(
308346
self,
309347
event_loop_kwargs: Any,
348+
messages: Messages,
310349
parent_span: Optional[Span] = None,
311-
messages: Optional[Messages] = None,
312350
**kwargs: Any,
313351
) -> Optional[Span]:
314352
"""Start a new span for an event loop cycle.
315353
316354
Args:
317355
event_loop_kwargs: Arguments for the event loop cycle.
318356
parent_span: Optional parent span to link this span to.
319-
messages: Optional messages being processed in this cycle.
357+
messages: Messages being processed in this cycle.
320358
**kwargs: Additional attributes to add to the span.
321359
322360
Returns:
@@ -326,7 +364,6 @@ def start_event_loop_cycle_span(
326364
parent_span = parent_span if parent_span else event_loop_kwargs.get("event_loop_parent_span")
327365

328366
attributes: Dict[str, AttributeValue] = {
329-
"gen_ai.prompt": serialize(messages),
330367
"event_loop.cycle_id": event_loop_cycle_id,
331368
}
332369

@@ -337,7 +374,15 @@ def start_event_loop_cycle_span(
337374
attributes.update({k: v for k, v in kwargs.items() if isinstance(v, (str, int, float, bool))})
338375

339376
span_name = f"Cycle {event_loop_cycle_id}"
340-
return self._start_span(span_name, parent_span, attributes, span_kind=trace_api.SpanKind.INTERNAL)
377+
span = self._start_span(span_name, parent_span, attributes)
378+
for message in messages or []:
379+
self._add_event(
380+
span,
381+
f"gen_ai.{message['role']}.message",
382+
{"content": serialize(message["content"])},
383+
)
384+
385+
return span
341386

342387
def end_event_loop_cycle_span(
343388
self,
@@ -354,14 +399,12 @@ def end_event_loop_cycle_span(
354399
tool_result_message: Optional tool result message if a tool was called.
355400
error: Optional exception if the cycle failed.
356401
"""
357-
attributes: Dict[str, AttributeValue] = {
358-
"gen_ai.completion": serialize(message["content"]),
359-
}
402+
event_attributes: Dict[str, AttributeValue] = {"message": serialize(message["content"])}
360403

361404
if tool_result_message:
362-
attributes["tool.result"] = serialize(tool_result_message["content"])
363-
364-
self._end_span(span, attributes, error)
405+
event_attributes["tool.result"] = serialize(tool_result_message["content"])
406+
self._add_event(span, "gen_ai.choice", event_attributes=event_attributes)
407+
self._end_span(span, {}, error)
365408

366409
def start_agent_span(
367410
self,
@@ -387,17 +430,15 @@ def start_agent_span(
387430
"""
388431
attributes: Dict[str, AttributeValue] = {
389432
"gen_ai.system": "strands-agents",
390-
"agent.name": agent_name,
391433
"gen_ai.agent.name": agent_name,
392-
"gen_ai.prompt": prompt,
434+
"gen_ai.operation.name": "invoke_agent",
393435
}
394436

395437
if model_id:
396438
attributes["gen_ai.request.model"] = model_id
397439

398440
if tools:
399441
tools_json = serialize(tools)
400-
attributes["agent.tools"] = tools_json
401442
attributes["gen_ai.agent.tools"] = tools_json
402443

403444
# Add custom trace attributes if provided
@@ -407,7 +448,18 @@ def start_agent_span(
407448
# Add additional kwargs as attributes
408449
attributes.update({k: v for k, v in kwargs.items() if isinstance(v, (str, int, float, bool))})
409450

410-
return self._start_span(agent_name, attributes=attributes, span_kind=trace_api.SpanKind.CLIENT)
451+
span = self._start_span(
452+
f"invoke_agent {agent_name}", attributes=attributes, span_kind=trace_api.SpanKind.CLIENT
453+
)
454+
self._add_event(
455+
span,
456+
"gen_ai.user.message",
457+
event_attributes={
458+
"content": prompt,
459+
},
460+
)
461+
462+
return span
411463

412464
def end_agent_span(
413465
self,
@@ -426,10 +478,10 @@ def end_agent_span(
426478
attributes: Dict[str, AttributeValue] = {}
427479

428480
if response:
429-
attributes.update(
430-
{
431-
"gen_ai.completion": str(response),
432-
}
481+
self._add_event(
482+
span,
483+
"gen_ai.choice",
484+
event_attributes={"message": str(response), "finish_reason": str(response.stop_reason)},
433485
)
434486

435487
if hasattr(response, "metrics") and hasattr(response.metrics, "accumulated_usage"):

0 commit comments

Comments
 (0)