@@ -70,10 +70,13 @@ def trace_tool_call(
70
70
function_response_event: The event with the function response details.
71
71
"""
72
72
span = trace .get_current_span ()
73
- span .set_attribute ('gen_ai.system' , 'gcp.vertex.agent' )
73
+
74
+ # Standard OpenTelemetry GenAI attributes as of SemConv 1.36.0 for Agents and Frameworks
75
+ span .set_attribute ('gen_ai.system' , 'gcp.vertex_ai' )
74
76
span .set_attribute ('gen_ai.operation.name' , 'execute_tool' )
75
77
span .set_attribute ('gen_ai.tool.name' , tool .name )
76
78
span .set_attribute ('gen_ai.tool.description' , tool .description )
79
+
77
80
tool_call_id = '<not specified>'
78
81
tool_response = '<not specified>'
79
82
if function_response_event .content .parts :
@@ -86,6 +89,7 @@ def trace_tool_call(
86
89
87
90
span .set_attribute ('gen_ai.tool.call.id' , tool_call_id )
88
91
92
+ # Vendor-specific attributes (moved from gen_ai.* to gcp.vertex.agent.*)
89
93
if not isinstance (tool_response , dict ):
90
94
tool_response = {'result' : tool_response }
91
95
span .set_attribute (
@@ -121,12 +125,15 @@ def trace_merged_tool_calls(
121
125
"""
122
126
123
127
span = trace .get_current_span ()
124
- span .set_attribute ('gen_ai.system' , 'gcp.vertex.agent' )
128
+
129
+ # Standard OpenTelemetry GenAI attributes
130
+ span .set_attribute ('gen_ai.system' , 'gcp.vertex_ai' )
125
131
span .set_attribute ('gen_ai.operation.name' , 'execute_tool' )
126
132
span .set_attribute ('gen_ai.tool.name' , '(merged tools)' )
127
133
span .set_attribute ('gen_ai.tool.description' , '(merged tools)' )
128
134
span .set_attribute ('gen_ai.tool.call.id' , response_event_id )
129
135
136
+ # Vendor-specific attributes
130
137
span .set_attribute ('gcp.vertex.agent.tool_call_args' , 'N/A' )
131
138
span .set_attribute ('gcp.vertex.agent.event_id' , response_event_id )
132
139
try :
@@ -167,23 +174,38 @@ def trace_call_llm(
167
174
llm_response: The LLM response object.
168
175
"""
169
176
span = trace .get_current_span ()
170
- # Special standard Open Telemetry GenaI attributes that indicate
171
- # that this is a span related to a Generative AI system.
172
- span .set_attribute ('gen_ai.system' , 'gcp.vertex.agent' )
177
+
178
+ # Standard OpenTelemetry GenAI attributes
179
+ span .set_attribute ('gen_ai.system' , 'gcp.vertex_ai' )
180
+ span .set_attribute ('gen_ai.operation.name' , 'generate_content' )
173
181
span .set_attribute ('gen_ai.request.model' , llm_request .model )
182
+
183
+ if hasattr (llm_response , 'id' ) and llm_response .id :
184
+ span .set_attribute ('gen_ai.response.id' , llm_response .id )
185
+
186
+ # Set response model if different from request model
187
+ if (
188
+ hasattr (llm_response , 'model' )
189
+ and llm_response .model
190
+ and llm_response .model != llm_request .model
191
+ ):
192
+ span .set_attribute ('gen_ai.response.model' , llm_response .model )
193
+
174
194
span .set_attribute (
175
195
'gcp.vertex.agent.invocation_id' , invocation_context .invocation_id
176
196
)
177
197
span .set_attribute (
178
198
'gcp.vertex.agent.session_id' , invocation_context .session .id
179
199
)
180
200
span .set_attribute ('gcp.vertex.agent.event_id' , event_id )
201
+
181
202
# Consider removing once GenAI SDK provides a way to record this info.
182
203
span .set_attribute (
183
204
'gcp.vertex.agent.llm_request' ,
184
205
_safe_json_serialize (_build_llm_request_for_trace (llm_request )),
185
206
)
186
- # Consider removing once GenAI SDK provides a way to record this info.
207
+
208
+ # Standard GenAI request attributes
187
209
if llm_request .config :
188
210
if llm_request .config .top_p :
189
211
span .set_attribute (
@@ -195,6 +217,14 @@ def trace_call_llm(
195
217
'gen_ai.request.max_tokens' ,
196
218
llm_request .config .max_output_tokens ,
197
219
)
220
+ if (
221
+ hasattr (llm_request .config , 'temperature' )
222
+ and llm_request .config .temperature is not None
223
+ ):
224
+ span .set_attribute (
225
+ 'gen_ai.request.temperature' ,
226
+ llm_request .config .temperature ,
227
+ )
198
228
199
229
try :
200
230
llm_response_json = llm_response .model_dump_json (exclude_none = True )
@@ -206,6 +236,7 @@ def trace_call_llm(
206
236
llm_response_json ,
207
237
)
208
238
239
+ # Standard GenAI usage and response attributes
209
240
if llm_response .usage_metadata is not None :
210
241
span .set_attribute (
211
242
'gen_ai.usage.input_tokens' ,
@@ -239,6 +270,8 @@ def trace_send_data(
239
270
data: A list of content objects.
240
271
"""
241
272
span = trace .get_current_span ()
273
+
274
+ # Vendor-specific attributes (moved from gen_ai.* to gcp.vertex.agent.*)
242
275
span .set_attribute (
243
276
'gcp.vertex.agent.invocation_id' , invocation_context .invocation_id
244
277
)
@@ -286,3 +319,41 @@ def _build_llm_request_for_trace(llm_request: LlmRequest) -> dict[str, Any]:
286
319
)
287
320
)
288
321
return result
322
+
323
+
324
+ def _create_span_name (operation_name : str , model_name : str ) -> str :
325
+ """Creates a span name following OpenTelemetry GenAI conventions.
326
+
327
+ Args:
328
+ operation_name: The GenAI operation name (e.g., 'generate_content', 'execute_tool').
329
+ model_name: The model name being used.
330
+
331
+ Returns:
332
+ A span name in the format '{operation_name} {model_name}'.
333
+ """
334
+ return f'{ operation_name } { model_name } '
335
+
336
+
337
+ def add_genai_prompt_event (span : trace .Span , prompt_content : str ):
338
+ """Adds a GenAI prompt event to the span following OpenTelemetry conventions.
339
+
340
+ Args:
341
+ span: The OpenTelemetry span to add the event to.
342
+ prompt_content: The prompt content as a JSON string.
343
+ """
344
+ span .add_event (
345
+ name = 'gen_ai.content.prompt' , attributes = {'gen_ai.prompt' : prompt_content }
346
+ )
347
+
348
+
349
+ def add_genai_completion_event (span : trace .Span , completion_content : str ):
350
+ """Adds a GenAI completion event to the span following OpenTelemetry conventions.
351
+
352
+ Args:
353
+ span: The OpenTelemetry span to add the event to.
354
+ completion_content: The completion content as a JSON string.
355
+ """
356
+ span .add_event (
357
+ name = 'gen_ai.content.completion' ,
358
+ attributes = {'gen_ai.completion' : completion_content },
359
+ )
0 commit comments