Skip to content

Commit 47d5728

Browse files
committed
test: add tests for non streaming case and tool_call_wrapper.
1 parent 8583327 commit 47d5728

File tree

3 files changed

+91
-28
lines changed

3 files changed

+91
-28
lines changed

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/nonstreaming_base.py

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@
1313
# limitations under the License.
1414

1515
import json
16-
import os
1716
import unittest
17+
from unittest.mock import patch
18+
19+
from opentelemetry.instrumentation._semconv import (
20+
_OpenTelemetrySemanticConventionStability,
21+
_OpenTelemetryStabilitySignalType,
22+
_StabilityMode,
23+
)
24+
from opentelemetry.util.genai.types import ContentCapturingMode
1825

1926
from .base import TestCase
2027

@@ -111,10 +118,8 @@ def test_generated_span_counts_tokens(self):
111118
self.assertEqual(span.attributes["gen_ai.usage.input_tokens"], 123)
112119
self.assertEqual(span.attributes["gen_ai.usage.output_tokens"], 456)
113120

121+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"})
114122
def test_records_system_prompt_as_log(self):
115-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
116-
"true"
117-
)
118123
config = {"system_instruction": "foo"}
119124
self.configure_valid_response()
120125
self.generate_content(
@@ -125,10 +130,8 @@ def test_records_system_prompt_as_log(self):
125130
self.assertEqual(event_record.attributes["gen_ai.system"], "gemini")
126131
self.assertEqual(event_record.body["content"], "foo")
127132

133+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "false"})
128134
def test_does_not_record_system_prompt_as_log_if_disabled_by_env(self):
129-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
130-
"false"
131-
)
132135
config = {"system_instruction": "foo"}
133136
self.configure_valid_response()
134137
self.generate_content(
@@ -139,42 +142,34 @@ def test_does_not_record_system_prompt_as_log_if_disabled_by_env(self):
139142
self.assertEqual(event_record.attributes["gen_ai.system"], "gemini")
140143
self.assertEqual(event_record.body["content"], "<elided>")
141144

145+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"})
142146
def test_does_not_record_system_prompt_as_log_if_no_system_prompt_present(
143147
self,
144148
):
145-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
146-
"true"
147-
)
148149
self.configure_valid_response()
149150
self.generate_content(model="gemini-2.0-flash", contents="Some input")
150151
self.otel.assert_does_not_have_event_named("gen_ai.system.message")
151152

153+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"})
152154
def test_records_user_prompt_as_log(self):
153-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
154-
"true"
155-
)
156155
self.configure_valid_response()
157156
self.generate_content(model="gemini-2.0-flash", contents="Some input")
158157
self.otel.assert_has_event_named("gen_ai.user.message")
159158
event_record = self.otel.get_event_named("gen_ai.user.message")
160159
self.assertEqual(event_record.attributes["gen_ai.system"], "gemini")
161160
self.assertEqual(event_record.body["content"], "Some input")
162161

162+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "false"})
163163
def test_does_not_record_user_prompt_as_log_if_disabled_by_env(self):
164-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
165-
"false"
166-
)
167164
self.configure_valid_response()
168165
self.generate_content(model="gemini-2.0-flash", contents="Some input")
169166
self.otel.assert_has_event_named("gen_ai.user.message")
170167
event_record = self.otel.get_event_named("gen_ai.user.message")
171168
self.assertEqual(event_record.attributes["gen_ai.system"], "gemini")
172169
self.assertEqual(event_record.body["content"], "<elided>")
173170

171+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true"})
174172
def test_records_response_as_log(self):
175-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
176-
"true"
177-
)
178173
self.configure_valid_response(text="Some response content")
179174
self.generate_content(model="gemini-2.0-flash", contents="Some input")
180175
self.otel.assert_has_event_named("gen_ai.choice")
@@ -184,17 +179,46 @@ def test_records_response_as_log(self):
184179
"Some response content", json.dumps(event_record.body["content"])
185180
)
186181

182+
@patch.dict("os.environ", {"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "false"})
187183
def test_does_not_record_response_as_log_if_disabled_by_env(self):
188-
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = (
189-
"false"
190-
)
191184
self.configure_valid_response(text="Some response content")
192185
self.generate_content(model="gemini-2.0-flash", contents="Some input")
193186
self.otel.assert_has_event_named("gen_ai.choice")
194187
event_record = self.otel.get_event_named("gen_ai.choice")
195188
self.assertEqual(event_record.attributes["gen_ai.system"], "gemini")
196189
self.assertEqual(event_record.body["content"], "<elided>")
197190

191+
def test_new_semconv_record_response_as_log(self):
192+
for mode in ContentCapturingMode:
193+
patched_environ = patch.dict(
194+
"os.environ",
195+
{
196+
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": mode.name,
197+
"OTEL_SEMCONV_STABILITY_OPT_IN": "gen_ai_latest_experimental",
198+
},
199+
)
200+
patched_otel_mapping = patch.dict(
201+
_OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING,
202+
{
203+
_OpenTelemetryStabilitySignalType.GEN_AI: _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL
204+
},
205+
)
206+
with self.subTest(f'mode: {mode}', patched_environ=patched_environ):
207+
self.setUp()
208+
with patched_environ, patched_otel_mapping:
209+
self.configure_valid_response(text="Some response content")
210+
self.generate_content(model="gemini-2.0-flash", contents="Some input")
211+
212+
if mode in [
213+
ContentCapturingMode.NO_CONTENT,
214+
ContentCapturingMode.SPAN_ONLY,
215+
]:
216+
self.otel.assert_does_not_have_event_named("gen_ai.client.inference.operation.details")
217+
else:
218+
self.otel.assert_has_event_named("gen_ai.client.inference.operation.details")
219+
220+
self.tearDown()
221+
198222
def test_records_metrics_data(self):
199223
self.configure_valid_response()
200224
self.generate_content(model="gemini-2.0-flash", contents="Some input")

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/requirements.latest.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ pytest-asyncio==0.21.0
4141
pytest-vcr==1.0.2
4242

4343
google-auth==2.38.0
44-
google-genai==1.0.0
44+
google-genai==1.32.0
4545

4646
# Install locally from the folder. This path is relative to the
4747
# root directory, given invocation from "tox" at root level.
4848
-e opentelemetry-instrumentation
49-
-e instrumentation-genai/opentelemetry-instrumentation-google-genai
49+
-e instrumentation-genai/opentelemetry-instrumentation-google-genai
50+
-e util/opentelemetry-util-genai

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/utils/test_tool_call_wrapper.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@
1717
from unittest.mock import patch
1818

1919
from google.genai import types as genai_types
20-
2120
from opentelemetry._events import get_event_logger_provider
22-
from opentelemetry.instrumentation.google_genai import (
23-
otel_wrapper,
24-
tool_call_wrapper,
21+
from opentelemetry.instrumentation._semconv import (
22+
_OpenTelemetrySemanticConventionStability,
23+
_OpenTelemetryStabilitySignalType,
24+
_StabilityMode,
2525
)
26+
from opentelemetry.instrumentation.google_genai import otel_wrapper, tool_call_wrapper
2627
from opentelemetry.metrics import get_meter_provider
2728
from opentelemetry.trace import get_tracer_provider
29+
from opentelemetry.util.genai.types import ContentCapturingMode
2830

2931
from ..common import otel_mocker
3032

@@ -278,3 +280,39 @@ def somefunction(arg=None):
278280
span.attributes["code.function.parameters.arg.value"],
279281
'[123, "abc"]',
280282
)
283+
284+
def test_handle_with_new_sem_conv(self):
285+
def somefunction(arg=None):
286+
pass
287+
288+
for mode in ContentCapturingMode:
289+
patched_environ = patch.dict(
290+
"os.environ",
291+
{
292+
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": mode.name,
293+
"OTEL_SEMCONV_STABILITY_OPT_IN": "gen_ai_latest_experimental",
294+
},
295+
)
296+
patched_otel_mapping = patch.dict(
297+
_OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING,
298+
{
299+
_OpenTelemetryStabilitySignalType.GEN_AI: _StabilityMode.GEN_AI_LATEST_EXPERIMENTAL
300+
},
301+
)
302+
with self.subTest(f'mode: {mode}', patched_environ=patched_environ):
303+
self.setUp()
304+
with patched_environ, patched_otel_mapping:
305+
wrapped_somefunction = self.wrap(somefunction)
306+
wrapped_somefunction(12345)
307+
308+
span = self.otel.get_span_named("execute_tool somefunction")
309+
310+
print(mode)
311+
if mode in [
312+
ContentCapturingMode.NO_CONTENT,
313+
ContentCapturingMode.EVENT_ONLY,
314+
]:
315+
self.assertNotIn("code.function.parameters.arg.value", span.attributes)
316+
else:
317+
self.assertIn("code.function.parameters.arg.value", span.attributes)
318+
self.tearDown()

0 commit comments

Comments
 (0)