Skip to content

Commit 7398657

Browse files
committed
Add Vertex gen AI response span attributes
1 parent d18c5fe commit 7398657

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

instrumentation-genai/opentelemetry-instrumentation-vertexai/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
([#3208](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3208))
1616
- VertexAI emit user, system, and assistant events
1717
([#3203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3203))
18+
- Add Vertex gen AI response span attributes
19+
([#3227](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3227))

instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/patch.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from opentelemetry.instrumentation.vertexai.utils import (
2626
GenerateContentParams,
2727
get_genai_request_attributes,
28+
get_genai_response_attributes,
2829
get_server_attributes,
2930
get_span_name,
3031
request_to_events,
@@ -113,25 +114,27 @@ def traced_method(
113114
name=span_name,
114115
kind=SpanKind.CLIENT,
115116
attributes=span_attributes,
116-
) as _span:
117+
) as span:
117118
for event in request_to_events(
118119
params=params, capture_content=capture_content
119120
):
120121
event_logger.emit(event)
121122

122123
# TODO: set error.type attribute
123124
# https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-spans.md
124-
result = wrapped(*args, **kwargs)
125+
response = wrapped(*args, **kwargs)
125126
# TODO: handle streaming
126127
# if is_streaming(kwargs):
127128
# return StreamWrapper(
128129
# result, span, event_logger, capture_content
129130
# )
130131

132+
if span.is_recording():
133+
span.set_attributes(get_genai_response_attributes(response))
131134
# TODO: add response attributes and events
132135
# _set_response_attributes(
133136
# span, result, event_logger, capture_content
134137
# )
135-
return result
138+
return response
136139

137140
return traced_method

instrumentation-genai/opentelemetry-instrumentation-vertexai/src/opentelemetry/instrumentation/vertexai/utils.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,17 @@
3939
from opentelemetry.util.types import AnyValue, AttributeValue
4040

4141
if TYPE_CHECKING:
42-
from google.cloud.aiplatform_v1.types import content, tool
42+
from google.cloud.aiplatform_v1.types import (
43+
content,
44+
prediction_service,
45+
tool,
46+
)
4347
from google.cloud.aiplatform_v1beta1.types import (
4448
content as content_v1beta1,
4549
)
50+
from google.cloud.aiplatform_v1beta1.types import (
51+
prediction_service as prediction_service_v1beta1,
52+
)
4653
from google.cloud.aiplatform_v1beta1.types import (
4754
tool as tool_v1beta1,
4855
)
@@ -137,6 +144,21 @@ def get_genai_request_attributes(
137144
return attributes
138145

139146

147+
def get_genai_response_attributes(
148+
response: prediction_service.GenerateContentResponse
149+
| prediction_service_v1beta1.GenerateContentResponse,
150+
) -> dict[str, AttributeValue]:
151+
finish_reasons: list[str] = [
152+
candidate.finish_reason.name for candidate in response.candidates
153+
]
154+
return {
155+
GenAIAttributes.GEN_AI_RESPONSE_MODEL: response.model_version,
156+
GenAIAttributes.GEN_AI_RESPONSE_FINISH_REASONS: finish_reasons,
157+
GenAIAttributes.GEN_AI_USAGE_INPUT_TOKENS: response.usage_metadata.prompt_token_count,
158+
GenAIAttributes.GEN_AI_USAGE_OUTPUT_TOKENS: response.usage_metadata.candidates_token_count,
159+
}
160+
161+
140162
_MODEL_STRIP_RE = re.compile(
141163
r"^projects/(.*)/locations/(.*)/publishers/google/models/"
142164
)

instrumentation-genai/opentelemetry-instrumentation-vertexai/tests/test_chat_completions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ def test_generate_content(
3838
assert dict(spans[0].attributes) == {
3939
"gen_ai.operation.name": "chat",
4040
"gen_ai.request.model": "gemini-1.5-flash-002",
41+
"gen_ai.response.finish_reasons": ("STOP",),
42+
"gen_ai.response.model": "gemini-1.5-flash-002",
4143
"gen_ai.system": "vertex_ai",
44+
"gen_ai.usage.input_tokens": 5,
45+
"gen_ai.usage.output_tokens": 19,
4246
"server.address": "us-central1-aiplatform.googleapis.com",
4347
"server.port": 443,
4448
}
@@ -81,7 +85,11 @@ def test_generate_content_without_events(
8185
assert dict(spans[0].attributes) == {
8286
"gen_ai.operation.name": "chat",
8387
"gen_ai.request.model": "gemini-1.5-flash-002",
88+
"gen_ai.response.finish_reasons": ("STOP",),
89+
"gen_ai.response.model": "gemini-1.5-flash-002",
8490
"gen_ai.system": "vertex_ai",
91+
"gen_ai.usage.input_tokens": 5,
92+
"gen_ai.usage.output_tokens": 19,
8593
"server.address": "us-central1-aiplatform.googleapis.com",
8694
"server.port": 443,
8795
}
@@ -255,7 +263,11 @@ def test_generate_content_extra_params(span_exporter, instrument_no_content):
255263
"gen_ai.request.stop_sequences": ("\n\n\n",),
256264
"gen_ai.request.temperature": 0.20000000298023224,
257265
"gen_ai.request.top_p": 0.949999988079071,
266+
"gen_ai.response.finish_reasons": ("MAX_TOKENS",),
267+
"gen_ai.response.model": "gemini-1.5-flash-002",
258268
"gen_ai.system": "vertex_ai",
269+
"gen_ai.usage.input_tokens": 5,
270+
"gen_ai.usage.output_tokens": 5,
259271
"server.address": "us-central1-aiplatform.googleapis.com",
260272
"server.port": 443,
261273
}

0 commit comments

Comments
 (0)