Skip to content

Commit 80c8df1

Browse files
committed
Fix lint issues.
1 parent d3526fa commit 80c8df1

File tree

4 files changed

+195
-152
lines changed

4 files changed

+195
-152
lines changed

instrumentation-genai/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/dict_util.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ def _is_homogenous_primitive_list(v):
4646
return True
4747
if not _is_primitive(v[0]):
4848
return False
49-
t = type(v[0])
49+
first_entry_value_type = type(v[0])
5050
for entry in v[1:]:
51-
if not isinstance(entry, t):
51+
if not isinstance(entry, first_entry_value_type):
5252
return False
5353
return True
5454

@@ -63,25 +63,15 @@ def _get_flatten_func(
6363
return None
6464

6565

66-
def _flatten_value(
66+
def _flatten_compound_value(
6767
key: str,
6868
value: Any,
6969
exclude_keys: Set[str],
7070
rename_keys: Dict[str, str],
7171
flatten_functions: Dict[str, Callable],
72+
key_names: Set[str],
7273
_from_json=False,
7374
) -> FlattenedDict:
74-
if value is None:
75-
return {}
76-
key_names = set([key])
77-
renamed_key = rename_keys.get(key)
78-
if renamed_key is not None:
79-
key_names.add(renamed_key)
80-
key = renamed_key
81-
if key_names & exclude_keys:
82-
return {}
83-
if _is_primitive(value):
84-
return {key: value}
8575
flatten_func = _get_flatten_func(flatten_functions, key_names)
8676
if flatten_func is not None:
8777
func_output = flatten_func(
@@ -112,9 +102,8 @@ def _flatten_value(
112102
flatten_functions=flatten_functions,
113103
)
114104
if hasattr(value, "model_dump"):
115-
d = value.model_dump()
116105
return _flatten_dict(
117-
d,
106+
value.model_dump(),
118107
key_prefix=key,
119108
exclude_keys=exclude_keys,
120109
rename_keys=rename_keys,
@@ -135,6 +124,36 @@ def _flatten_value(
135124
)
136125

137126

127+
def _flatten_value(
128+
key: str,
129+
value: Any,
130+
exclude_keys: Set[str],
131+
rename_keys: Dict[str, str],
132+
flatten_functions: Dict[str, Callable],
133+
_from_json=False,
134+
) -> FlattenedDict:
135+
if value is None:
136+
return {}
137+
key_names = set([key])
138+
renamed_key = rename_keys.get(key)
139+
if renamed_key is not None:
140+
key_names.add(renamed_key)
141+
key = renamed_key
142+
if key_names & exclude_keys:
143+
return {}
144+
if _is_primitive(value):
145+
return {key: value}
146+
return _flatten_compound_value(
147+
key=key,
148+
value=value,
149+
exclude_keys=exclude_keys,
150+
rename_keys=rename_keys,
151+
flatten_functions=flatten_functions,
152+
key_names=key_names,
153+
_from_json=_from_json,
154+
)
155+
156+
138157
def _flatten_dict(
139158
d: Dict[str, Any],
140159
key_prefix: str,

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

Lines changed: 1 addition & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
import os
1717
import unittest
1818

19-
from google.genai.types import GenerateContentConfig
20-
2119
from .base import TestCase
2220

2321

@@ -37,7 +35,7 @@ def generate_content(self, *args, **kwargs):
3735
def expected_function_name(self):
3836
raise NotImplementedError("Must implement 'expected_function_name'.")
3937

40-
def generate_and_get_span(self, config):
38+
def _generate_and_get_span(self, config):
4139
self.generate_content(
4240
model="gemini-2.0-flash",
4341
contents="Some input prompt",
@@ -105,125 +103,6 @@ def test_generated_span_has_vertex_ai_system_when_configured(self):
105103
span.attributes["gen_ai.operation.name"], "generate_content"
106104
)
107105

108-
def test_option_reflected_to_span_attribute_choice_count_config_dict(self):
109-
self.configure_valid_response(text="Some response")
110-
span = self.generate_and_get_span(config={"candidate_count": 2})
111-
self.assertEqual(span.attributes["gen_ai.request.choice.count"], 2)
112-
113-
def test_option_reflected_to_span_attribute_choice_count_config_obj(self):
114-
self.configure_valid_response(text="Some response")
115-
span = self.generate_and_get_span(
116-
config=GenerateContentConfig(candidate_count=2)
117-
)
118-
self.assertEqual(span.attributes["gen_ai.request.choice.count"], 2)
119-
120-
def test_option_reflected_to_span_attribute_seed_config_dict(self):
121-
self.configure_valid_response(text="Some response")
122-
span = self.generate_and_get_span(config={"seed": 12345})
123-
self.assertEqual(span.attributes["gen_ai.request.seed"], 12345)
124-
125-
def test_option_reflected_to_span_attribute_seed_config_obj(self):
126-
self.configure_valid_response(text="Some response")
127-
span = self.generate_and_get_span(
128-
config=GenerateContentConfig(seed=12345)
129-
)
130-
self.assertEqual(span.attributes["gen_ai.request.seed"], 12345)
131-
132-
def test_option_reflected_to_span_attribute_frequency_penalty(self):
133-
self.configure_valid_response(text="Some response")
134-
span = self.generate_and_get_span(config={"frequency_penalty": 1.0})
135-
self.assertEqual(
136-
span.attributes["gen_ai.request.frequency_penalty"], 1.0
137-
)
138-
139-
def test_option_reflected_to_span_attribute_max_tokens(self):
140-
self.configure_valid_response(text="Some response")
141-
span = self.generate_and_get_span(
142-
config=GenerateContentConfig(max_output_tokens=5000)
143-
)
144-
self.assertEqual(span.attributes["gen_ai.request.max_tokens"], 5000)
145-
146-
def test_option_reflected_to_span_attribute_presence_penalty(self):
147-
self.configure_valid_response(text="Some response")
148-
span = self.generate_and_get_span(
149-
config=GenerateContentConfig(presence_penalty=0.5)
150-
)
151-
self.assertEqual(
152-
span.attributes["gen_ai.request.presence_penalty"], 0.5
153-
)
154-
155-
def test_option_reflected_to_span_attribute_stop_sequences(self):
156-
self.configure_valid_response(text="Some response")
157-
span = self.generate_and_get_span(
158-
config={"stop_sequences": ["foo", "bar"]}
159-
)
160-
stop_sequences = span.attributes["gen_ai.request.stop_sequences"]
161-
self.assertEqual(len(stop_sequences), 2)
162-
self.assertEqual(stop_sequences[0], "foo")
163-
self.assertEqual(stop_sequences[1], "bar")
164-
165-
def test_option_reflected_to_span_attribute_top_k(self):
166-
self.configure_valid_response(text="Some response")
167-
span = self.generate_and_get_span(
168-
config=GenerateContentConfig(top_k=20)
169-
)
170-
self.assertEqual(span.attributes["gen_ai.request.top_k"], 20)
171-
172-
def test_option_reflected_to_span_attribute_top_p(self):
173-
self.configure_valid_response(text="Some response")
174-
span = self.generate_and_get_span(config={"top_p": 10})
175-
self.assertEqual(span.attributes["gen_ai.request.top_p"], 10)
176-
177-
def test_option_not_reflected_to_span_attribute_system_instruction(self):
178-
self.configure_valid_response(text="Some response")
179-
span = self.generate_and_get_span(
180-
config={"system_instruction": "Yadda yadda yadda"}
181-
)
182-
self.assertNotIn(
183-
"gen_ai.gcp.request.system_instruction", span.attributes
184-
)
185-
self.assertNotIn("gen_ai.request.system_instruction", span.attributes)
186-
for key in span.attributes:
187-
value = span.attributes[key]
188-
if isinstance(value, str):
189-
self.assertNotIn("Yadda yadda yadda", value)
190-
191-
def test_option_not_reflected_to_span_attribute_http_headers(self):
192-
self.configure_valid_response(text="Some response")
193-
span = self.generate_and_get_span(
194-
config={
195-
"http_options": {
196-
"base_url": "my.backend.override",
197-
"headers": {
198-
"sensitive": 12345,
199-
},
200-
}
201-
}
202-
)
203-
self.assertEqual(
204-
span.attributes["gen_ai.gcp.request.http_options.base_url"],
205-
"my.backend.override",
206-
)
207-
self.assertNotIn(
208-
"gen_ai.gcp.request.http_options.headers.sensitive",
209-
span.attributes,
210-
)
211-
212-
def test_option_reflected_to_span_attribute_automatic_func_calling(self):
213-
self.configure_valid_response(text="Some response")
214-
span = self.generate_and_get_span(
215-
config={
216-
"automatic_function_calling": {
217-
"ignore_call_history": True,
218-
}
219-
}
220-
)
221-
self.assertTrue(
222-
span.attributes[
223-
"gen_ai.gcp.request.automatic_function_calling.ignore_call_history"
224-
]
225-
)
226-
227106
def test_generated_span_counts_tokens(self):
228107
self.configure_valid_response(input_tokens=123, output_tokens=456)
229108
self.generate_content(model="gemini-2.0-flash", contents="Some input")
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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+
from google.genai.types import GenerateContentConfig
16+
17+
from .base import TestCase
18+
19+
20+
class ConfigSpanAttributesTestCase(TestCase):
21+
22+
def setUp(self):
23+
super().setUp()
24+
self.configure_valid_response(text="Some response")
25+
26+
def generate_content(self, *args, **kwargs):
27+
return self.client.models.generate_content(*args, **kwargs)
28+
29+
def generate_and_get_span(self, config):
30+
self.client.models.generate_content(
31+
model="gemini-2.0-flash",
32+
contents="Some input prompt",
33+
config=config,
34+
)
35+
self.otel.assert_has_span_named("generate_content gemini-2.0-flash")
36+
return self.otel.get_span_named("generate_content gemini-2.0-flash")
37+
38+
def test_option_reflected_to_span_attribute_choice_count_config_dict(self):
39+
span = self.generate_and_get_span(config={"candidate_count": 2})
40+
self.assertEqual(span.attributes["gen_ai.request.choice.count"], 2)
41+
42+
def test_option_reflected_to_span_attribute_choice_count_config_obj(self):
43+
span = self.generate_and_get_span(
44+
config=GenerateContentConfig(candidate_count=2)
45+
)
46+
self.assertEqual(span.attributes["gen_ai.request.choice.count"], 2)
47+
48+
def test_option_reflected_to_span_attribute_seed_config_dict(self):
49+
span = self.generate_and_get_span(config={"seed": 12345})
50+
self.assertEqual(span.attributes["gen_ai.request.seed"], 12345)
51+
52+
def test_option_reflected_to_span_attribute_seed_config_obj(self):
53+
span = self.generate_and_get_span(
54+
config=GenerateContentConfig(seed=12345)
55+
)
56+
self.assertEqual(span.attributes["gen_ai.request.seed"], 12345)
57+
58+
def test_option_reflected_to_span_attribute_frequency_penalty(self):
59+
span = self.generate_and_get_span(config={"frequency_penalty": 1.0})
60+
self.assertEqual(
61+
span.attributes["gen_ai.request.frequency_penalty"], 1.0
62+
)
63+
64+
def test_option_reflected_to_span_attribute_max_tokens(self):
65+
span = self.generate_and_get_span(
66+
config=GenerateContentConfig(max_output_tokens=5000)
67+
)
68+
self.assertEqual(span.attributes["gen_ai.request.max_tokens"], 5000)
69+
70+
def test_option_reflected_to_span_attribute_presence_penalty(self):
71+
span = self.generate_and_get_span(
72+
config=GenerateContentConfig(presence_penalty=0.5)
73+
)
74+
self.assertEqual(
75+
span.attributes["gen_ai.request.presence_penalty"], 0.5
76+
)
77+
78+
def test_option_reflected_to_span_attribute_stop_sequences(self):
79+
span = self.generate_and_get_span(
80+
config={"stop_sequences": ["foo", "bar"]}
81+
)
82+
stop_sequences = span.attributes["gen_ai.request.stop_sequences"]
83+
self.assertEqual(len(stop_sequences), 2)
84+
self.assertEqual(stop_sequences[0], "foo")
85+
self.assertEqual(stop_sequences[1], "bar")
86+
87+
def test_option_reflected_to_span_attribute_top_k(self):
88+
span = self.generate_and_get_span(
89+
config=GenerateContentConfig(top_k=20)
90+
)
91+
self.assertEqual(span.attributes["gen_ai.request.top_k"], 20)
92+
93+
def test_option_reflected_to_span_attribute_top_p(self):
94+
span = self.generate_and_get_span(config={"top_p": 10})
95+
self.assertEqual(span.attributes["gen_ai.request.top_p"], 10)
96+
97+
def test_option_not_reflected_to_span_attribute_system_instruction(self):
98+
span = self.generate_and_get_span(
99+
config={"system_instruction": "Yadda yadda yadda"}
100+
)
101+
self.assertNotIn(
102+
"gen_ai.gcp.request.system_instruction", span.attributes
103+
)
104+
self.assertNotIn("gen_ai.request.system_instruction", span.attributes)
105+
for key in span.attributes:
106+
value = span.attributes[key]
107+
if isinstance(value, str):
108+
self.assertNotIn("Yadda yadda yadda", value)
109+
110+
def test_option_not_reflected_to_span_attribute_http_headers(self):
111+
span = self.generate_and_get_span(
112+
config={
113+
"http_options": {
114+
"base_url": "my.backend.override",
115+
"headers": {
116+
"sensitive": 12345,
117+
},
118+
}
119+
}
120+
)
121+
self.assertEqual(
122+
span.attributes["gen_ai.gcp.request.http_options.base_url"],
123+
"my.backend.override",
124+
)
125+
self.assertNotIn(
126+
"gen_ai.gcp.request.http_options.headers.sensitive",
127+
span.attributes,
128+
)
129+
130+
def test_option_reflected_to_span_attribute_automatic_func_calling(self):
131+
span = self.generate_and_get_span(
132+
config={
133+
"automatic_function_calling": {
134+
"ignore_call_history": True,
135+
}
136+
}
137+
)
138+
self.assertTrue(
139+
span.attributes[
140+
"gen_ai.gcp.request.automatic_function_calling.ignore_call_history"
141+
]
142+
)

0 commit comments

Comments
 (0)