Skip to content

Commit fa8fa60

Browse files
committed
Add tests and comments that provide some additional clarity regarding the flattening logic in response to PR comments.
1 parent 031369b commit fa8fa60

File tree

2 files changed

+96
-2
lines changed
  • instrumentation-genai/opentelemetry-instrumentation-google-genai

2 files changed

+96
-2
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,22 @@ def _flatten_compound_value(
113113
raise ValueError(
114114
f"Cannot flatten value with key {key}; value: {value}"
115115
)
116-
json_value = json.loads(json.dumps(value))
116+
try:
117+
json_string = json.dumps(value)
118+
except TypeError as exc:
119+
raise ValueError(
120+
f"Cannot flatten value with key {key}; value: {value}. Not JSON serializable."
121+
) from exc
122+
json_value = json.loads(json_string)
117123
return _flatten_value(
118124
key,
119125
json_value,
120126
exclude_keys=exclude_keys,
121127
rename_keys=rename_keys,
122128
flatten_functions=flatten_functions,
129+
# Ensure that we don't recurse indefinitely if "json.loads()" somehow returns
130+
# a complex, compound object that does not get handled by the "primitive", "list",
131+
# or "dict" cases. Prevents falling back on the JSON serialization fallback path.
123132
_from_json=True,
124133
)
125134

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

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,33 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
15+
import pytest
16+
from pydantic import BaseModel
1617
from opentelemetry.instrumentation.google_genai import dict_util
1718

1819

20+
class PydanticModel(BaseModel):
21+
"""Used to verify handling of pydantic models in the flattener."""
22+
str_value: str
23+
int_value: int
24+
25+
26+
class ModelDumpableNotPydantic:
27+
"""Used to verify general handling of 'model_dump'."""
28+
29+
def __init__(self, dump_output):
30+
self._dump_output = dump_output
31+
32+
def model_dump(self):
33+
return self._dump_output
34+
35+
36+
class NotJsonSerializable:
37+
38+
def __init__(self):
39+
pass
40+
41+
1942
def test_flatten_empty_dict():
2043
input_dict = {}
2144
output_dict = dict_util.flatten_dict(input_dict)
@@ -137,3 +160,65 @@ def summarize_int_list(key, value, **kwargs):
137160
"some.deeply.nested.key": "9 items (total: 45, average: 5.0)",
138161
"other": [1, 2, 3, 4, 5, 6, 7, 8, 9],
139162
}
163+
164+
165+
def test_flatten_with_pydantic_model_value():
166+
input_dict = {
167+
"foo": PydanticModel(str_value="bar", int_value=123),
168+
}
169+
170+
output = dict_util.flatten_dict(input_dict)
171+
assert output == {
172+
"foo.str_value": "bar",
173+
"foo.int_value": 123,
174+
}
175+
176+
177+
def test_flatten_with_model_dumpable_value():
178+
input_dict = {
179+
"foo": ModelDumpableNotPydantic({
180+
"str_value": "bar",
181+
"int_value": 123,
182+
}),
183+
}
184+
185+
output = dict_util.flatten_dict(input_dict)
186+
assert output == {
187+
"foo.str_value": "bar",
188+
"foo.int_value": 123,
189+
}
190+
191+
192+
def test_flatten_with_mixed_structures():
193+
input_dict = {
194+
"foo": ModelDumpableNotPydantic({
195+
"pydantic": PydanticModel(str_value="bar", int_value=123),
196+
}),
197+
}
198+
199+
output = dict_util.flatten_dict(input_dict)
200+
assert output == {
201+
"foo.pydantic.str_value": "bar",
202+
"foo.pydantic.int_value": 123,
203+
}
204+
205+
206+
def test_flatten_with_complex_object_not_json_serializable():
207+
with pytest.raises(ValueError):
208+
dict_util.flatten_dict({
209+
"cannot_serialize_directly": NotJsonSerializable(),
210+
})
211+
212+
213+
def test_flatten_with_complex_object_not_json_serializable_and_custom_flatten_func():
214+
def flatten_not_json_serializable(key, value, **kwargs):
215+
assert isinstance(value, NotJsonSerializable)
216+
return "blah"
217+
output = dict_util.flatten_dict({
218+
"cannot_serialize_directly": NotJsonSerializable(),
219+
}, flatten_functions={
220+
"cannot_serialize_directly": flatten_not_json_serializable,
221+
})
222+
assert output == {
223+
"cannot_serialize_directly": "blah",
224+
}

0 commit comments

Comments
 (0)