1717import unittest
1818
1919from .base import TestCase
20+ from google .genai .types import GenerateContentConfig
2021
2122
2223class NonStreamingTestCase (TestCase ):
@@ -35,6 +36,14 @@ def generate_content(self, *args, **kwargs):
3536 def expected_function_name (self ):
3637 raise NotImplementedError ("Must implement 'expected_function_name'." )
3738
39+ def generate_and_get_span (self , config ):
40+ self .generate_content (
41+ model = "gemini-2.0-flash" ,
42+ contents = "Some input prompt" ,
43+ config = config )
44+ self .otel .assert_has_span_named ("generate_content gemini-2.0-flash" )
45+ return self .otel .get_span_named ("generate_content gemini-2.0-flash" )
46+
3847 def test_instrumentation_does_not_break_core_functionality (self ):
3948 self .configure_valid_response (text = "Yep, it works!" )
4049 response = self .generate_content (
@@ -94,6 +103,92 @@ def test_generated_span_has_vertex_ai_system_when_configured(self):
94103 span .attributes ["gen_ai.operation.name" ], "generate_content"
95104 )
96105
106+ def test_option_reflected_to_span_attribute_choice_count_config_dict (self ):
107+ self .configure_valid_response (text = 'Some response' )
108+ span = self .generate_and_get_span (config = {"candidate_count" : 2 })
109+ self .assertEqual (span .attributes ["gen_ai.request.choice.count" ], 2 )
110+
111+ def test_option_reflected_to_span_attribute_choice_count_config_obj (self ):
112+ self .configure_valid_response (text = 'Some response' )
113+ span = self .generate_and_get_span (config = GenerateContentConfig (candidate_count = 2 ))
114+ self .assertEqual (span .attributes ["gen_ai.request.choice.count" ], 2 )
115+
116+ def test_option_reflected_to_span_attribute_seed_config_dict (self ):
117+ self .configure_valid_response (text = 'Some response' )
118+ span = self .generate_and_get_span (config = {"seed" : 12345 })
119+ self .assertEqual (span .attributes ["gen_ai.request.seed" ], 12345 )
120+
121+ def test_option_reflected_to_span_attribute_seed_config_obj (self ):
122+ self .configure_valid_response (text = 'Some response' )
123+ span = self .generate_and_get_span (config = GenerateContentConfig (seed = 12345 ))
124+ self .assertEqual (span .attributes ["gen_ai.request.seed" ], 12345 )
125+
126+ def test_option_reflected_to_span_attribute_frequency_penalty (self ):
127+ self .configure_valid_response (text = 'Some response' )
128+ span = self .generate_and_get_span (config = {"frequency_penalty" : 1.0 })
129+ self .assertEqual (span .attributes ["gen_ai.request.frequency_penalty" ], 1.0 )
130+
131+ def test_option_reflected_to_span_attribute_max_tokens (self ):
132+ self .configure_valid_response (text = 'Some response' )
133+ span = self .generate_and_get_span (config = GenerateContentConfig (max_output_tokens = 5000 ))
134+ self .assertEqual (span .attributes ["gen_ai.request.max_tokens" ], 5000 )
135+
136+ def test_option_reflected_to_span_attribute_presence_penalty (self ):
137+ self .configure_valid_response (text = 'Some response' )
138+ span = self .generate_and_get_span (config = GenerateContentConfig (presence_penalty = 0.5 ))
139+ self .assertEqual (span .attributes ["gen_ai.request.presence_penalty" ], 0.5 )
140+
141+ def test_option_reflected_to_span_attribute_stop_sequences (self ):
142+ self .configure_valid_response (text = 'Some response' )
143+ span = self .generate_and_get_span (config = {"stop_sequences" : ["foo" , "bar" ]})
144+ stop_sequences = span .attributes ["gen_ai.request.stop_sequences" ]
145+ self .assertEqual (len (stop_sequences ), 2 )
146+ self .assertEqual (stop_sequences [0 ], "foo" )
147+ self .assertEqual (stop_sequences [1 ], "bar" )
148+
149+ def test_option_reflected_to_span_attribute_top_k (self ):
150+ self .configure_valid_response (text = 'Some response' )
151+ span = self .generate_and_get_span (config = GenerateContentConfig (top_k = 20 ))
152+ self .assertEqual (span .attributes ["gen_ai.request.top_k" ], 20 )
153+
154+ def test_option_reflected_to_span_attribute_top_p (self ):
155+ self .configure_valid_response (text = 'Some response' )
156+ span = self .generate_and_get_span (config = {"top_p" : 10 })
157+ self .assertEqual (span .attributes ["gen_ai.request.top_p" ], 10 )
158+
159+ def test_option_not_reflected_to_span_attribute_system_instruction (self ):
160+ self .configure_valid_response (text = 'Some response' )
161+ span = self .generate_and_get_span (config = {"system_instruction" : "Yadda yadda yadda" })
162+ self .assertNotIn ("gen_ai.gcp.request.system_instruction" , span .attributes )
163+ self .assertNotIn ("gen_ai.request.system_instruction" , span .attributes )
164+ for key in span .attributes :
165+ value = span .attributes [key ]
166+ if isinstance (value , str ):
167+ self .assertNotIn ("Yadda yadda yadda" , value )
168+
169+ def test_option_not_reflected_to_span_attribute_http_headers (self ):
170+ self .configure_valid_response (text = 'Some response' )
171+ span = self .generate_and_get_span (config = {"http_options" : {
172+ "base_url" : "my.backend.override" ,
173+ "headers" : {
174+ "sensitive" : 12345 ,
175+ }
176+ }})
177+ self .assertEqual (
178+ span .attributes ["gen_ai.gcp.request.http_options.base_url" ], "my.backend.override" )
179+ self .assertNotIn (
180+ "gen_ai.gcp.request.http_options.headers.sensitive" ,
181+ span .attributes
182+ )
183+
184+ def test_option_reflected_to_span_attribute_automatic_func_calling (self ):
185+ self .configure_valid_response (text = 'Some response' )
186+ span = self .generate_and_get_span (config = {"automatic_function_calling" : {
187+ "ignore_call_history" : True ,
188+ }})
189+ self .assertTrue (
190+ span .attributes ["gen_ai.gcp.request.automatic_function_calling.ignore_call_history" ])
191+
97192 def test_generated_span_counts_tokens (self ):
98193 self .configure_valid_response (input_tokens = 123 , output_tokens = 456 )
99194 self .generate_content (model = "gemini-2.0-flash" , contents = "Some input" )
0 commit comments