opentelemetry-instrumentation-vertexai 0.38.7__py3-none-any.whl → 2.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opentelemetry-instrumentation-vertexai might be problematic. Click here for more details.
- opentelemetry/instrumentation/vertexai/__init__.py +82 -354
- opentelemetry/instrumentation/vertexai/events.py +188 -0
- opentelemetry/instrumentation/vertexai/package.py +16 -0
- opentelemetry/instrumentation/vertexai/patch.py +142 -0
- opentelemetry/instrumentation/vertexai/py.typed +0 -0
- opentelemetry/instrumentation/vertexai/utils.py +337 -22
- opentelemetry/instrumentation/vertexai/version.py +15 -1
- opentelemetry_instrumentation_vertexai-2.0b0.dist-info/METADATA +106 -0
- opentelemetry_instrumentation_vertexai-2.0b0.dist-info/RECORD +12 -0
- {opentelemetry_instrumentation_vertexai-0.38.7.dist-info → opentelemetry_instrumentation_vertexai-2.0b0.dist-info}/WHEEL +1 -1
- opentelemetry_instrumentation_vertexai-2.0b0.dist-info/entry_points.txt +2 -0
- opentelemetry_instrumentation_vertexai-2.0b0.dist-info/licenses/LICENSE +201 -0
- opentelemetry/instrumentation/vertexai/config.py +0 -2
- opentelemetry_instrumentation_vertexai-0.38.7.dist-info/METADATA +0 -57
- opentelemetry_instrumentation_vertexai-0.38.7.dist-info/RECORD +0 -8
- opentelemetry_instrumentation_vertexai-0.38.7.dist-info/entry_points.txt +0 -3
|
@@ -1,368 +1,96 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"package": "vertexai.generative_models",
|
|
39
|
-
"object": "GenerativeModel",
|
|
40
|
-
"method": "generate_content_async",
|
|
41
|
-
"span_name": "vertexai.generate_content_async",
|
|
42
|
-
"is_async": True,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"package": "vertexai.preview.generative_models",
|
|
46
|
-
"object": "GenerativeModel",
|
|
47
|
-
"method": "generate_content",
|
|
48
|
-
"span_name": "vertexai.generate_content",
|
|
49
|
-
"is_async": False,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"package": "vertexai.preview.generative_models",
|
|
53
|
-
"object": "GenerativeModel",
|
|
54
|
-
"method": "generate_content_async",
|
|
55
|
-
"span_name": "vertexai.generate_content_async",
|
|
56
|
-
"is_async": True,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"package": "vertexai.language_models",
|
|
60
|
-
"object": "TextGenerationModel",
|
|
61
|
-
"method": "predict",
|
|
62
|
-
"span_name": "vertexai.predict",
|
|
63
|
-
"is_async": False,
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"package": "vertexai.language_models",
|
|
67
|
-
"object": "TextGenerationModel",
|
|
68
|
-
"method": "predict_async",
|
|
69
|
-
"span_name": "vertexai.predict_async",
|
|
70
|
-
"is_async": True,
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"package": "vertexai.language_models",
|
|
74
|
-
"object": "TextGenerationModel",
|
|
75
|
-
"method": "predict_streaming",
|
|
76
|
-
"span_name": "vertexai.predict_streaming",
|
|
77
|
-
"is_async": False,
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"package": "vertexai.language_models",
|
|
81
|
-
"object": "TextGenerationModel",
|
|
82
|
-
"method": "predict_streaming_async",
|
|
83
|
-
"span_name": "vertexai.predict_streaming_async",
|
|
84
|
-
"is_async": True,
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"package": "vertexai.language_models",
|
|
88
|
-
"object": "ChatSession",
|
|
89
|
-
"method": "send_message",
|
|
90
|
-
"span_name": "vertexai.send_message",
|
|
91
|
-
"is_async": False,
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
"package": "vertexai.language_models",
|
|
95
|
-
"object": "ChatSession",
|
|
96
|
-
"method": "send_message_streaming",
|
|
97
|
-
"span_name": "vertexai.send_message_streaming",
|
|
98
|
-
"is_async": False,
|
|
99
|
-
},
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def should_send_prompts():
|
|
104
|
-
return (
|
|
105
|
-
os.getenv("TRACELOOP_TRACE_CONTENT") or "true"
|
|
106
|
-
).lower() == "true" or context_api.get_value("override_enable_content_tracing")
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def is_streaming_response(response):
|
|
110
|
-
return isinstance(response, types.GeneratorType)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def is_async_streaming_response(response):
|
|
114
|
-
return isinstance(response, types.AsyncGeneratorType)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def _set_span_attribute(span, name, value):
|
|
118
|
-
if value is not None:
|
|
119
|
-
if value != "":
|
|
120
|
-
span.set_attribute(name, value)
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def _set_input_attributes(span, args, kwargs, llm_model):
|
|
125
|
-
if should_send_prompts() and args is not None and len(args) > 0:
|
|
126
|
-
prompt = ""
|
|
127
|
-
for arg in args:
|
|
128
|
-
if isinstance(arg, str):
|
|
129
|
-
prompt = f"{prompt}{arg}\n"
|
|
130
|
-
elif isinstance(arg, list):
|
|
131
|
-
for subarg in arg:
|
|
132
|
-
prompt = f"{prompt}{subarg}\n"
|
|
133
|
-
|
|
134
|
-
_set_span_attribute(
|
|
135
|
-
span,
|
|
136
|
-
f"{SpanAttributes.LLM_PROMPTS}.0.user",
|
|
137
|
-
prompt,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
_set_span_attribute(span, SpanAttributes.LLM_REQUEST_MODEL, llm_model)
|
|
141
|
-
_set_span_attribute(
|
|
142
|
-
span, f"{SpanAttributes.LLM_PROMPTS}.0.user", kwargs.get("prompt")
|
|
143
|
-
)
|
|
144
|
-
_set_span_attribute(
|
|
145
|
-
span, SpanAttributes.LLM_REQUEST_TEMPERATURE, kwargs.get("temperature")
|
|
146
|
-
)
|
|
147
|
-
_set_span_attribute(
|
|
148
|
-
span, SpanAttributes.LLM_REQUEST_MAX_TOKENS, kwargs.get("max_output_tokens")
|
|
149
|
-
)
|
|
150
|
-
_set_span_attribute(span, SpanAttributes.LLM_REQUEST_TOP_P, kwargs.get("top_p"))
|
|
151
|
-
_set_span_attribute(span, SpanAttributes.LLM_TOP_K, kwargs.get("top_k"))
|
|
152
|
-
_set_span_attribute(
|
|
153
|
-
span, SpanAttributes.LLM_PRESENCE_PENALTY, kwargs.get("presence_penalty")
|
|
154
|
-
)
|
|
155
|
-
_set_span_attribute(
|
|
156
|
-
span, SpanAttributes.LLM_FREQUENCY_PENALTY, kwargs.get("frequency_penalty")
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
return
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@dont_throw
|
|
163
|
-
def _set_response_attributes(span, llm_model, generation_text, token_usage):
|
|
164
|
-
_set_span_attribute(span, SpanAttributes.LLM_RESPONSE_MODEL, llm_model)
|
|
165
|
-
|
|
166
|
-
if token_usage:
|
|
167
|
-
_set_span_attribute(
|
|
168
|
-
span,
|
|
169
|
-
SpanAttributes.LLM_USAGE_TOTAL_TOKENS,
|
|
170
|
-
token_usage.total_token_count,
|
|
171
|
-
)
|
|
172
|
-
_set_span_attribute(
|
|
173
|
-
span,
|
|
174
|
-
SpanAttributes.LLM_USAGE_COMPLETION_TOKENS,
|
|
175
|
-
token_usage.candidates_token_count,
|
|
176
|
-
)
|
|
177
|
-
_set_span_attribute(
|
|
178
|
-
span,
|
|
179
|
-
SpanAttributes.LLM_USAGE_PROMPT_TOKENS,
|
|
180
|
-
token_usage.prompt_token_count,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
_set_span_attribute(span, f"{SpanAttributes.LLM_COMPLETIONS}.0.role", "assistant")
|
|
184
|
-
_set_span_attribute(
|
|
185
|
-
span,
|
|
186
|
-
f"{SpanAttributes.LLM_COMPLETIONS}.0.content",
|
|
187
|
-
generation_text,
|
|
1
|
+
# Copyright The OpenTelemetry Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
VertexAI client instrumentation supporting `google-cloud-aiplatform` SDK, it can be enabled by
|
|
17
|
+
using ``VertexAIInstrumentor``.
|
|
18
|
+
|
|
19
|
+
.. _vertexai: https://pypi.org/project/google-cloud-aiplatform/
|
|
20
|
+
|
|
21
|
+
Usage
|
|
22
|
+
-----
|
|
23
|
+
|
|
24
|
+
.. code:: python
|
|
25
|
+
|
|
26
|
+
import vertexai
|
|
27
|
+
from vertexai.generative_models import GenerativeModel
|
|
28
|
+
from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor
|
|
29
|
+
|
|
30
|
+
VertexAIInstrumentor().instrument()
|
|
31
|
+
|
|
32
|
+
vertexai.init()
|
|
33
|
+
model = GenerativeModel("gemini-1.5-flash-002")
|
|
34
|
+
chat_completion = model.generate_content(
|
|
35
|
+
"Write a short poem on OpenTelemetry."
|
|
188
36
|
)
|
|
189
37
|
|
|
38
|
+
API
|
|
39
|
+
---
|
|
40
|
+
"""
|
|
190
41
|
|
|
191
|
-
|
|
192
|
-
complete_response = ""
|
|
193
|
-
token_usage = None
|
|
194
|
-
for item in response:
|
|
195
|
-
item_to_yield = item
|
|
196
|
-
complete_response += str(item.text)
|
|
197
|
-
if item.usage_metadata:
|
|
198
|
-
token_usage = item.usage_metadata
|
|
199
|
-
|
|
200
|
-
yield item_to_yield
|
|
201
|
-
|
|
202
|
-
_set_response_attributes(span, llm_model, complete_response, token_usage)
|
|
203
|
-
|
|
204
|
-
span.set_status(Status(StatusCode.OK))
|
|
205
|
-
span.end()
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
async def _abuild_from_streaming_response(span, response, llm_model):
|
|
209
|
-
complete_response = ""
|
|
210
|
-
token_usage = None
|
|
211
|
-
async for item in response:
|
|
212
|
-
item_to_yield = item
|
|
213
|
-
complete_response += str(item.text)
|
|
214
|
-
if item.usage_metadata:
|
|
215
|
-
token_usage = item.usage_metadata
|
|
42
|
+
from typing import Any, Collection
|
|
216
43
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
span.set_status(Status(StatusCode.OK))
|
|
222
|
-
span.end()
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
@dont_throw
|
|
226
|
-
def _handle_request(span, args, kwargs, llm_model):
|
|
227
|
-
if span.is_recording():
|
|
228
|
-
_set_input_attributes(span, args, kwargs, llm_model)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
@dont_throw
|
|
232
|
-
def _handle_response(span, response, llm_model):
|
|
233
|
-
if span.is_recording():
|
|
234
|
-
_set_response_attributes(
|
|
235
|
-
span, llm_model, response.candidates[0].text, response.usage_metadata
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
span.set_status(Status(StatusCode.OK))
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def _with_tracer_wrapper(func):
|
|
242
|
-
"""Helper for providing tracer for wrapper functions."""
|
|
243
|
-
|
|
244
|
-
def _with_tracer(tracer, to_wrap):
|
|
245
|
-
def wrapper(wrapped, instance, args, kwargs):
|
|
246
|
-
return func(tracer, to_wrap, wrapped, instance, args, kwargs)
|
|
247
|
-
|
|
248
|
-
return wrapper
|
|
249
|
-
|
|
250
|
-
return _with_tracer
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
@_with_tracer_wrapper
|
|
254
|
-
async def _awrap(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
255
|
-
"""Instruments and calls every function defined in TO_WRAP."""
|
|
256
|
-
if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY) or context_api.get_value(
|
|
257
|
-
SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY
|
|
258
|
-
):
|
|
259
|
-
return await wrapped(*args, **kwargs)
|
|
260
|
-
|
|
261
|
-
llm_model = "unknown"
|
|
262
|
-
if hasattr(instance, "_model_id"):
|
|
263
|
-
llm_model = instance._model_id
|
|
264
|
-
if hasattr(instance, "_model_name"):
|
|
265
|
-
llm_model = instance._model_name.replace("publishers/google/models/", "")
|
|
266
|
-
|
|
267
|
-
name = to_wrap.get("span_name")
|
|
268
|
-
span = tracer.start_span(
|
|
269
|
-
name,
|
|
270
|
-
kind=SpanKind.CLIENT,
|
|
271
|
-
attributes={
|
|
272
|
-
SpanAttributes.LLM_SYSTEM: "VertexAI",
|
|
273
|
-
SpanAttributes.LLM_REQUEST_TYPE: LLMRequestTypeValues.COMPLETION.value,
|
|
274
|
-
},
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
_handle_request(span, args, kwargs, llm_model)
|
|
278
|
-
|
|
279
|
-
response = await wrapped(*args, **kwargs)
|
|
280
|
-
|
|
281
|
-
if response:
|
|
282
|
-
if is_streaming_response(response):
|
|
283
|
-
return _build_from_streaming_response(span, response, llm_model)
|
|
284
|
-
elif is_async_streaming_response(response):
|
|
285
|
-
return _abuild_from_streaming_response(span, response, llm_model)
|
|
286
|
-
else:
|
|
287
|
-
_handle_response(span, response, llm_model)
|
|
288
|
-
|
|
289
|
-
span.end()
|
|
290
|
-
return response
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
@_with_tracer_wrapper
|
|
294
|
-
def _wrap(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
295
|
-
"""Instruments and calls every function defined in TO_WRAP."""
|
|
296
|
-
if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY) or context_api.get_value(
|
|
297
|
-
SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY
|
|
298
|
-
):
|
|
299
|
-
return wrapped(*args, **kwargs)
|
|
300
|
-
|
|
301
|
-
llm_model = "unknown"
|
|
302
|
-
if hasattr(instance, "_model_id"):
|
|
303
|
-
llm_model = instance._model_id
|
|
304
|
-
if hasattr(instance, "_model_name"):
|
|
305
|
-
llm_model = instance._model_name.replace("publishers/google/models/", "")
|
|
306
|
-
|
|
307
|
-
name = to_wrap.get("span_name")
|
|
308
|
-
span = tracer.start_span(
|
|
309
|
-
name,
|
|
310
|
-
kind=SpanKind.CLIENT,
|
|
311
|
-
attributes={
|
|
312
|
-
SpanAttributes.LLM_SYSTEM: "VertexAI",
|
|
313
|
-
SpanAttributes.LLM_REQUEST_TYPE: LLMRequestTypeValues.COMPLETION.value,
|
|
314
|
-
},
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
_handle_request(span, args, kwargs, llm_model)
|
|
318
|
-
|
|
319
|
-
response = wrapped(*args, **kwargs)
|
|
320
|
-
|
|
321
|
-
if response:
|
|
322
|
-
if is_streaming_response(response):
|
|
323
|
-
return _build_from_streaming_response(span, response, llm_model)
|
|
324
|
-
elif is_async_streaming_response(response):
|
|
325
|
-
return _abuild_from_streaming_response(span, response, llm_model)
|
|
326
|
-
else:
|
|
327
|
-
_handle_response(span, response, llm_model)
|
|
44
|
+
from wrapt import (
|
|
45
|
+
wrap_function_wrapper, # type: ignore[reportUnknownVariableType]
|
|
46
|
+
)
|
|
328
47
|
|
|
329
|
-
|
|
330
|
-
|
|
48
|
+
from opentelemetry._events import get_event_logger
|
|
49
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
50
|
+
from opentelemetry.instrumentation.vertexai.package import _instruments
|
|
51
|
+
from opentelemetry.instrumentation.vertexai.patch import (
|
|
52
|
+
generate_content_create,
|
|
53
|
+
)
|
|
54
|
+
from opentelemetry.instrumentation.vertexai.utils import is_content_enabled
|
|
55
|
+
from opentelemetry.semconv.schemas import Schemas
|
|
56
|
+
from opentelemetry.trace import get_tracer
|
|
331
57
|
|
|
332
58
|
|
|
333
59
|
class VertexAIInstrumentor(BaseInstrumentor):
|
|
334
|
-
"""An instrumentor for VertextAI's client library."""
|
|
335
|
-
|
|
336
|
-
def __init__(self, exception_logger=None):
|
|
337
|
-
super().__init__()
|
|
338
|
-
Config.exception_logger = exception_logger
|
|
339
|
-
|
|
340
60
|
def instrumentation_dependencies(self) -> Collection[str]:
|
|
341
61
|
return _instruments
|
|
342
62
|
|
|
343
|
-
def _instrument(self, **kwargs):
|
|
63
|
+
def _instrument(self, **kwargs: Any):
|
|
64
|
+
"""Enable VertexAI instrumentation."""
|
|
344
65
|
tracer_provider = kwargs.get("tracer_provider")
|
|
345
|
-
tracer = get_tracer(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
66
|
+
tracer = get_tracer(
|
|
67
|
+
__name__,
|
|
68
|
+
"",
|
|
69
|
+
tracer_provider,
|
|
70
|
+
schema_url=Schemas.V1_28_0.value,
|
|
71
|
+
)
|
|
72
|
+
event_logger_provider = kwargs.get("event_logger_provider")
|
|
73
|
+
event_logger = get_event_logger(
|
|
74
|
+
__name__,
|
|
75
|
+
"",
|
|
76
|
+
schema_url=Schemas.V1_28_0.value,
|
|
77
|
+
event_logger_provider=event_logger_provider,
|
|
78
|
+
)
|
|
350
79
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
80
|
+
wrap_function_wrapper(
|
|
81
|
+
module="google.cloud.aiplatform_v1beta1.services.prediction_service.client",
|
|
82
|
+
name="PredictionServiceClient.generate_content",
|
|
83
|
+
wrapper=generate_content_create(
|
|
84
|
+
tracer, event_logger, is_content_enabled()
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
wrap_function_wrapper(
|
|
88
|
+
module="google.cloud.aiplatform_v1.services.prediction_service.client",
|
|
89
|
+
name="PredictionServiceClient.generate_content",
|
|
90
|
+
wrapper=generate_content_create(
|
|
91
|
+
tracer, event_logger, is_content_enabled()
|
|
92
|
+
),
|
|
93
|
+
)
|
|
360
94
|
|
|
361
|
-
def _uninstrument(self, **kwargs):
|
|
362
|
-
|
|
363
|
-
wrap_package = wrapped_method.get("package")
|
|
364
|
-
wrap_object = wrapped_method.get("object")
|
|
365
|
-
unwrap(
|
|
366
|
-
f"{wrap_package}.{wrap_object}",
|
|
367
|
-
wrapped_method.get("method", ""),
|
|
368
|
-
)
|
|
95
|
+
def _uninstrument(self, **kwargs: Any) -> None:
|
|
96
|
+
"""TODO: implemented in later PR"""
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Copyright The OpenTelemetry Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Factories for event types described in
|
|
17
|
+
https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-events.md#system-event.
|
|
18
|
+
|
|
19
|
+
Hopefully this code can be autogenerated by Weaver once Gen AI semantic conventions are
|
|
20
|
+
schematized in YAML and the Weaver tool supports it.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from dataclasses import asdict, dataclass
|
|
26
|
+
from typing import Any, Iterable, Literal
|
|
27
|
+
|
|
28
|
+
from opentelemetry._events import Event
|
|
29
|
+
from opentelemetry.semconv._incubating.attributes import gen_ai_attributes
|
|
30
|
+
from opentelemetry.util.types import AnyValue
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def user_event(
|
|
34
|
+
*,
|
|
35
|
+
role: str = "user",
|
|
36
|
+
content: AnyValue = None,
|
|
37
|
+
) -> Event:
|
|
38
|
+
"""Creates a User event
|
|
39
|
+
https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/gen-ai/gen-ai-events.md#user-event
|
|
40
|
+
"""
|
|
41
|
+
body: dict[str, AnyValue] = {
|
|
42
|
+
"role": role,
|
|
43
|
+
}
|
|
44
|
+
if content is not None:
|
|
45
|
+
body["content"] = content
|
|
46
|
+
return Event(
|
|
47
|
+
name="gen_ai.user.message",
|
|
48
|
+
attributes={
|
|
49
|
+
gen_ai_attributes.GEN_AI_SYSTEM: gen_ai_attributes.GenAiSystemValues.VERTEX_AI.value,
|
|
50
|
+
},
|
|
51
|
+
body=body,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def assistant_event(
|
|
56
|
+
*,
|
|
57
|
+
role: str = "assistant",
|
|
58
|
+
content: AnyValue = None,
|
|
59
|
+
) -> Event:
|
|
60
|
+
"""Creates an Assistant event
|
|
61
|
+
https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/gen-ai/gen-ai-events.md#assistant-event
|
|
62
|
+
"""
|
|
63
|
+
body: dict[str, AnyValue] = {
|
|
64
|
+
"role": role,
|
|
65
|
+
}
|
|
66
|
+
if content is not None:
|
|
67
|
+
body["content"] = content
|
|
68
|
+
return Event(
|
|
69
|
+
name="gen_ai.assistant.message",
|
|
70
|
+
attributes={
|
|
71
|
+
gen_ai_attributes.GEN_AI_SYSTEM: gen_ai_attributes.GenAiSystemValues.VERTEX_AI.value,
|
|
72
|
+
},
|
|
73
|
+
body=body,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def system_event(
|
|
78
|
+
*,
|
|
79
|
+
role: str = "system",
|
|
80
|
+
content: AnyValue = None,
|
|
81
|
+
) -> Event:
|
|
82
|
+
"""Creates a System event
|
|
83
|
+
https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/gen-ai/gen-ai-events.md#system-event
|
|
84
|
+
"""
|
|
85
|
+
body: dict[str, AnyValue] = {
|
|
86
|
+
"role": role,
|
|
87
|
+
}
|
|
88
|
+
if content is not None:
|
|
89
|
+
body["content"] = content
|
|
90
|
+
return Event(
|
|
91
|
+
name="gen_ai.system.message",
|
|
92
|
+
attributes={
|
|
93
|
+
gen_ai_attributes.GEN_AI_SYSTEM: gen_ai_attributes.GenAiSystemValues.VERTEX_AI.value,
|
|
94
|
+
},
|
|
95
|
+
body=body,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def tool_event(
|
|
100
|
+
*,
|
|
101
|
+
role: str | None,
|
|
102
|
+
id_: str,
|
|
103
|
+
content: AnyValue = None,
|
|
104
|
+
) -> Event:
|
|
105
|
+
"""Creates a Tool message event
|
|
106
|
+
https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/gen-ai/gen-ai-events.md#event-gen_aitoolmessage
|
|
107
|
+
"""
|
|
108
|
+
if not role:
|
|
109
|
+
role = "tool"
|
|
110
|
+
|
|
111
|
+
body: dict[str, AnyValue] = {
|
|
112
|
+
"role": role,
|
|
113
|
+
"id": id_,
|
|
114
|
+
}
|
|
115
|
+
if content is not None:
|
|
116
|
+
body["content"] = content
|
|
117
|
+
return Event(
|
|
118
|
+
name="gen_ai.tool.message",
|
|
119
|
+
attributes={
|
|
120
|
+
gen_ai_attributes.GEN_AI_SYSTEM: gen_ai_attributes.GenAiSystemValues.VERTEX_AI.value,
|
|
121
|
+
},
|
|
122
|
+
body=body,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass
|
|
127
|
+
class ChoiceMessage:
|
|
128
|
+
"""The message field for a gen_ai.choice event"""
|
|
129
|
+
|
|
130
|
+
content: AnyValue = None
|
|
131
|
+
role: str = "assistant"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class ChoiceToolCall:
|
|
136
|
+
"""The tool_calls field for a gen_ai.choice event"""
|
|
137
|
+
|
|
138
|
+
@dataclass
|
|
139
|
+
class Function:
|
|
140
|
+
name: str
|
|
141
|
+
arguments: AnyValue = None
|
|
142
|
+
|
|
143
|
+
function: Function
|
|
144
|
+
id: str
|
|
145
|
+
type: Literal["function"] = "function"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
FinishReason = Literal[
|
|
149
|
+
"content_filter", "error", "length", "stop", "tool_calls"
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def choice_event(
|
|
154
|
+
*,
|
|
155
|
+
finish_reason: FinishReason | str,
|
|
156
|
+
index: int,
|
|
157
|
+
message: ChoiceMessage,
|
|
158
|
+
tool_calls: Iterable[ChoiceToolCall] = (),
|
|
159
|
+
) -> Event:
|
|
160
|
+
"""Creates a choice event, which describes the Gen AI response message.
|
|
161
|
+
https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/gen-ai/gen-ai-events.md#event-gen_aichoice
|
|
162
|
+
"""
|
|
163
|
+
body: dict[str, AnyValue] = {
|
|
164
|
+
"finish_reason": finish_reason,
|
|
165
|
+
"index": index,
|
|
166
|
+
"message": _asdict_filter_nulls(message),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
tool_calls_list = [
|
|
170
|
+
_asdict_filter_nulls(tool_call) for tool_call in tool_calls
|
|
171
|
+
]
|
|
172
|
+
if tool_calls_list:
|
|
173
|
+
body["tool_calls"] = tool_calls_list
|
|
174
|
+
|
|
175
|
+
return Event(
|
|
176
|
+
name="gen_ai.choice",
|
|
177
|
+
attributes={
|
|
178
|
+
gen_ai_attributes.GEN_AI_SYSTEM: gen_ai_attributes.GenAiSystemValues.VERTEX_AI.value,
|
|
179
|
+
},
|
|
180
|
+
body=body,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _asdict_filter_nulls(instance: Any) -> dict[str, AnyValue]:
|
|
185
|
+
return asdict(
|
|
186
|
+
instance,
|
|
187
|
+
dict_factory=lambda kvs: {k: v for (k, v) in kvs if v is not None},
|
|
188
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright The OpenTelemetry Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_instruments = ("google-cloud-aiplatform >= 1.64",)
|