1313# limitations under the License.
1414
1515import json
16- import os
1716import 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
1926from .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" )
0 commit comments