lmnr 0.6.12__py3-none-any.whl → 0.6.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lmnr/opentelemetry_lib/litellm/__init__.py +1 -1
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +40 -32
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +9 -3
- lmnr/version.py +1 -1
- {lmnr-0.6.12.dist-info → lmnr-0.6.13.dist-info}/METADATA +1 -1
- {lmnr-0.6.12.dist-info → lmnr-0.6.13.dist-info}/RECORD +9 -9
- {lmnr-0.6.12.dist-info → lmnr-0.6.13.dist-info}/LICENSE +0 -0
- {lmnr-0.6.12.dist-info → lmnr-0.6.13.dist-info}/WHEEL +0 -0
- {lmnr-0.6.12.dist-info → lmnr-0.6.13.dist-info}/entry_points.txt +0 -0
@@ -347,7 +347,7 @@ try:
|
|
347
347
|
self._process_response_choices(span, response_dict.get("choices"))
|
348
348
|
|
349
349
|
except ImportError as e:
|
350
|
-
logger.
|
350
|
+
logger.debug(f"LiteLLM callback unavailable: {e}")
|
351
351
|
|
352
352
|
# Create a no-op logger when LiteLLM is not available
|
353
353
|
class LaminarLiteLLMCallback:
|
@@ -12,7 +12,6 @@ from .config import (
|
|
12
12
|
Config,
|
13
13
|
)
|
14
14
|
from .utils import (
|
15
|
-
ProcessedContentPart,
|
16
15
|
dont_throw,
|
17
16
|
get_content,
|
18
17
|
role_from_content_union,
|
@@ -130,27 +129,30 @@ def _set_request_attributes(span, args, kwargs):
|
|
130
129
|
)
|
131
130
|
|
132
131
|
tools: list[types.FunctionDeclaration] = []
|
133
|
-
|
134
|
-
|
132
|
+
arg_tools = config_dict.get("tools", kwargs.get("tools"))
|
133
|
+
if arg_tools:
|
134
|
+
for tool in arg_tools:
|
135
135
|
if isinstance(tool, types.Tool):
|
136
136
|
tools += tool.function_declarations or []
|
137
137
|
elif isinstance(tool, Callable):
|
138
138
|
tools.append(types.FunctionDeclaration.from_callable(tool))
|
139
|
+
|
139
140
|
for tool_num, tool in enumerate(tools):
|
141
|
+
tool_dict = to_dict(tool)
|
140
142
|
set_span_attribute(
|
141
143
|
span,
|
142
144
|
f"{SpanAttributes.LLM_REQUEST_FUNCTIONS}.{tool_num}.name",
|
143
|
-
|
145
|
+
tool_dict.get("name"),
|
144
146
|
)
|
145
147
|
set_span_attribute(
|
146
148
|
span,
|
147
149
|
f"{SpanAttributes.LLM_REQUEST_FUNCTIONS}.{tool_num}.description",
|
148
|
-
|
150
|
+
tool_dict.get("description"),
|
149
151
|
)
|
150
152
|
set_span_attribute(
|
151
153
|
span,
|
152
154
|
f"{SpanAttributes.LLM_REQUEST_FUNCTIONS}.{tool_num}.parameters",
|
153
|
-
|
155
|
+
json.dumps(tool_dict.get("parameters")),
|
154
156
|
)
|
155
157
|
|
156
158
|
if should_send_prompts():
|
@@ -162,7 +164,9 @@ def _set_request_attributes(span, args, kwargs):
|
|
162
164
|
set_span_attribute(
|
163
165
|
span,
|
164
166
|
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.content",
|
165
|
-
(get_content(process_content_union(system_instruction)) or {}).get(
|
167
|
+
(get_content(process_content_union(system_instruction)) or {}).get(
|
168
|
+
"text", ""
|
169
|
+
),
|
166
170
|
)
|
167
171
|
set_span_attribute(
|
168
172
|
span, f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.role", "system"
|
@@ -174,6 +178,7 @@ def _set_request_attributes(span, args, kwargs):
|
|
174
178
|
for content in contents:
|
175
179
|
processed_content = process_content_union(content)
|
176
180
|
content_str = get_content(processed_content)
|
181
|
+
|
177
182
|
set_span_attribute(
|
178
183
|
span,
|
179
184
|
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.content",
|
@@ -188,26 +193,35 @@ def _set_request_attributes(span, args, kwargs):
|
|
188
193
|
if isinstance(processed_content, list)
|
189
194
|
else [processed_content]
|
190
195
|
)
|
191
|
-
|
196
|
+
tool_call_index = 0
|
197
|
+
for block in blocks:
|
192
198
|
block_dict = to_dict(block)
|
199
|
+
|
193
200
|
if not block_dict.get("function_call"):
|
194
201
|
continue
|
195
202
|
function_call = to_dict(block_dict.get("function_call", {}))
|
203
|
+
|
196
204
|
set_span_attribute(
|
197
205
|
span,
|
198
|
-
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.tool_calls.{
|
206
|
+
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.tool_calls.{tool_call_index}.name",
|
199
207
|
function_call.get("name"),
|
200
208
|
)
|
201
209
|
set_span_attribute(
|
202
210
|
span,
|
203
|
-
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.tool_calls.{
|
204
|
-
|
211
|
+
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.tool_calls.{tool_call_index}.id",
|
212
|
+
(
|
213
|
+
function_call.get("id")
|
214
|
+
if function_call.get("id") is not None
|
215
|
+
else function_call.get("name")
|
216
|
+
), # google genai doesn't support tool call ids
|
205
217
|
)
|
206
218
|
set_span_attribute(
|
207
219
|
span,
|
208
|
-
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.tool_calls.{
|
220
|
+
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.tool_calls.{tool_call_index}.arguments",
|
209
221
|
json.dumps(function_call.get("arguments")),
|
210
222
|
)
|
223
|
+
tool_call_index += 1
|
224
|
+
|
211
225
|
set_span_attribute(
|
212
226
|
span,
|
213
227
|
f"{gen_ai_attributes.GEN_AI_PROMPT}.{i}.role",
|
@@ -258,21 +272,8 @@ def _set_response_attributes(span, response: types.GenerateContentResponse):
|
|
258
272
|
candidates_list = candidates if isinstance(candidates, list) else [candidates]
|
259
273
|
for i, candidate in enumerate(candidates_list):
|
260
274
|
processed_content = process_content_union(candidate.content)
|
261
|
-
|
262
|
-
|
263
|
-
isinstance(item, dict) and item.get("type") == "text"
|
264
|
-
for item in processed_content
|
265
|
-
):
|
266
|
-
content_str = processed_content[0]["text"]
|
267
|
-
elif all(
|
268
|
-
isinstance(item, ProcessedContentPart) and item.content
|
269
|
-
for item in processed_content
|
270
|
-
):
|
271
|
-
content_str = processed_content[0].content
|
272
|
-
else:
|
273
|
-
content_str = get_content(processed_content)
|
274
|
-
else:
|
275
|
-
content_str = get_content(processed_content)
|
275
|
+
content_str = get_content(processed_content)
|
276
|
+
|
276
277
|
set_span_attribute(
|
277
278
|
span, f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.role", "model"
|
278
279
|
)
|
@@ -290,26 +291,33 @@ def _set_response_attributes(span, response: types.GenerateContentResponse):
|
|
290
291
|
if isinstance(processed_content, list)
|
291
292
|
else [processed_content]
|
292
293
|
)
|
293
|
-
|
294
|
+
|
295
|
+
tool_call_index = 0
|
296
|
+
for block in blocks:
|
294
297
|
block_dict = to_dict(block)
|
295
298
|
if not block_dict.get("function_call"):
|
296
299
|
continue
|
297
300
|
function_call = to_dict(block_dict.get("function_call", {}))
|
298
301
|
set_span_attribute(
|
299
302
|
span,
|
300
|
-
f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.tool_calls.{
|
303
|
+
f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.tool_calls.{tool_call_index}.name",
|
301
304
|
function_call.get("name"),
|
302
305
|
)
|
303
306
|
set_span_attribute(
|
304
307
|
span,
|
305
|
-
f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.tool_calls.{
|
306
|
-
|
308
|
+
f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.tool_calls.{tool_call_index}.id",
|
309
|
+
(
|
310
|
+
function_call.get("id")
|
311
|
+
if function_call.get("id") is not None
|
312
|
+
else function_call.get("name")
|
313
|
+
), # google genai doesn't support tool call ids
|
307
314
|
)
|
308
315
|
set_span_attribute(
|
309
316
|
span,
|
310
|
-
f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.tool_calls.{
|
317
|
+
f"{gen_ai_attributes.GEN_AI_COMPLETION}.{i}.tool_calls.{tool_call_index}.arguments",
|
311
318
|
json.dumps(function_call.get("arguments")),
|
312
319
|
)
|
320
|
+
tool_call_index += 1
|
313
321
|
|
314
322
|
|
315
323
|
@dont_throw
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import base64
|
1
2
|
import logging
|
2
3
|
import traceback
|
3
4
|
|
@@ -74,7 +75,8 @@ def to_dict(obj: BaseModel | pydantic.BaseModel | dict) -> dict[str, Any]:
|
|
74
75
|
return obj
|
75
76
|
else:
|
76
77
|
return dict(obj)
|
77
|
-
except Exception:
|
78
|
+
except Exception as e:
|
79
|
+
logging.error(f"Error converting to dict: {obj}, error: {e}")
|
78
80
|
return dict(obj)
|
79
81
|
|
80
82
|
|
@@ -96,7 +98,7 @@ def get_content(
|
|
96
98
|
else:
|
97
99
|
return None
|
98
100
|
elif isinstance(content, list):
|
99
|
-
return [get_content(item)
|
101
|
+
return [get_content(item) for item in content]
|
100
102
|
elif isinstance(content, str):
|
101
103
|
return {
|
102
104
|
"type": "text",
|
@@ -226,11 +228,15 @@ def _process_image_item(
|
|
226
228
|
content_index: int,
|
227
229
|
) -> ProcessedContentPart | dict | None:
|
228
230
|
# Convert to openai format, so backends can handle it
|
231
|
+
data = blob.get("data")
|
232
|
+
encoded_data = (
|
233
|
+
base64.b64encode(data).decode("utf-8") if isinstance(data, bytes) else data
|
234
|
+
)
|
229
235
|
return (
|
230
236
|
ProcessedContentPart(
|
231
237
|
image_url=ImageUrl(
|
232
238
|
image_url=ImageUrlInner(
|
233
|
-
url=f"data:image/{blob.get('mime_type').split('/')[1]};base64,{
|
239
|
+
url=f"data:image/{blob.get('mime_type').split('/')[1]};base64,{encoded_data}",
|
234
240
|
)
|
235
241
|
)
|
236
242
|
)
|
lmnr/version.py
CHANGED
@@ -3,11 +3,11 @@ lmnr/cli.py,sha256=uHgLUfN_6eINtUlcQdOtODf2tI9AiwmlhojQF4UMB5Y,6047
|
|
3
3
|
lmnr/opentelemetry_lib/.flake8,sha256=bCxuDlGx3YQ55QHKPiGJkncHanh9qGjQJUujcFa3lAU,150
|
4
4
|
lmnr/opentelemetry_lib/__init__.py,sha256=aWKsqRXUhVhu2BS555nO2JhZSsK8bTUylAVwWybquGE,2160
|
5
5
|
lmnr/opentelemetry_lib/decorators/__init__.py,sha256=45HVoYnHC1Y9D_VSkioDbqD3gm4RPC5sKoztomBI5j8,8496
|
6
|
-
lmnr/opentelemetry_lib/litellm/__init__.py,sha256=
|
6
|
+
lmnr/opentelemetry_lib/litellm/__init__.py,sha256=wjo46It5GdhmxPCaiA8kaKyaz3VsuRDRYUCCstvK0-Y,14858
|
7
7
|
lmnr/opentelemetry_lib/litellm/utils.py,sha256=2ozwVT-C3HIDEJ8Rekx7QYXouvNMqtEteCOHVRUgGic,539
|
8
|
-
lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py,sha256=
|
8
|
+
lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py,sha256=mltiTDVCCyMuhQNuoLHvblg9O5X0ncG6xN3f1opSeQU,18613
|
9
9
|
lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py,sha256=25zevJ7g3MtJP_5gju3jBH7-wg7SbDkktysuUO29ksI,245
|
10
|
-
lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py,sha256=
|
10
|
+
lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py,sha256=8SSBliRoJtiZME5RDEwt90CI2BadKPHQrtV4p6bDy_0,7669
|
11
11
|
lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py,sha256=Jyv9koZdGA4-oTaB7ATB7DaX7aNOY-3YOGL4wX0c7PM,3107
|
12
12
|
lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py,sha256=nf9sJZXnnts4gYZortEiDvwYjYqYJZTAT0zutuP_R6Y,1512
|
13
13
|
lmnr/opentelemetry_lib/tracing/__init__.py,sha256=27QogAe-aHyrVr6b56-DduUm0KEE24K1aV2e1nouTNg,6007
|
@@ -52,9 +52,9 @@ lmnr/sdk/laminar.py,sha256=oOVco_c9ZstT71HsquGsgbtFumXd2Ejz0rl_qpmMlTU,33996
|
|
52
52
|
lmnr/sdk/log.py,sha256=nt_YMmPw1IRbGy0b7q4rTtP4Yo3pQfNxqJPXK3nDSNQ,2213
|
53
53
|
lmnr/sdk/types.py,sha256=ZQp5SeYJNZsK3KrbSeXPY_xn6mGjW5mSw_i0Rd_Oa4k,12328
|
54
54
|
lmnr/sdk/utils.py,sha256=yrcHIhoADf9lWH9qJWZMmkRWYvd0DuxPSLP3mY6YFw0,4327
|
55
|
-
lmnr/version.py,sha256=
|
56
|
-
lmnr-0.6.
|
57
|
-
lmnr-0.6.
|
58
|
-
lmnr-0.6.
|
59
|
-
lmnr-0.6.
|
60
|
-
lmnr-0.6.
|
55
|
+
lmnr/version.py,sha256=9gMGnzWiCqbv_PzYX6c-jQFz17zO1xyIONO32wE9xfY,1322
|
56
|
+
lmnr-0.6.13.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
57
|
+
lmnr-0.6.13.dist-info/METADATA,sha256=95yHwJlTi4CNL1ucZDk9ge2flO712qjpxikJ0jCkmiI,15186
|
58
|
+
lmnr-0.6.13.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
59
|
+
lmnr-0.6.13.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
|
60
|
+
lmnr-0.6.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|