lmnr 0.4.12b3__py3-none-any.whl → 0.4.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/sdk/decorators.py +3 -2
- lmnr/sdk/evaluations.py +94 -63
- lmnr/sdk/laminar.py +46 -43
- lmnr/sdk/types.py +44 -7
- lmnr/sdk/utils.py +4 -5
- lmnr/traceloop_sdk/__init__.py +3 -29
- lmnr/traceloop_sdk/config/__init__.py +0 -4
- lmnr/traceloop_sdk/decorators/base.py +29 -12
- lmnr/traceloop_sdk/tracing/attributes.py +9 -0
- lmnr/traceloop_sdk/tracing/tracing.py +45 -142
- {lmnr-0.4.12b3.dist-info → lmnr-0.4.13.dist-info}/METADATA +73 -101
- {lmnr-0.4.12b3.dist-info → lmnr-0.4.13.dist-info}/RECORD +15 -17
- lmnr/traceloop_sdk/metrics/__init__.py +0 -0
- lmnr/traceloop_sdk/metrics/metrics.py +0 -176
- lmnr/traceloop_sdk/tracing/manual.py +0 -57
- {lmnr-0.4.12b3.dist-info → lmnr-0.4.13.dist-info}/LICENSE +0 -0
- {lmnr-0.4.12b3.dist-info → lmnr-0.4.13.dist-info}/WHEEL +0 -0
- {lmnr-0.4.12b3.dist-info → lmnr-0.4.13.dist-info}/entry_points.txt +0 -0
@@ -7,10 +7,11 @@ import warnings
|
|
7
7
|
|
8
8
|
from opentelemetry import trace
|
9
9
|
from opentelemetry import context as context_api
|
10
|
-
from opentelemetry.semconv_ai import SpanAttributes
|
11
10
|
|
11
|
+
from lmnr.sdk.utils import get_input_from_func_args, is_method
|
12
12
|
from lmnr.traceloop_sdk.tracing import get_tracer
|
13
|
-
from lmnr.traceloop_sdk.tracing.
|
13
|
+
from lmnr.traceloop_sdk.tracing.attributes import SPAN_INPUT, SPAN_OUTPUT, SPAN_PATH
|
14
|
+
from lmnr.traceloop_sdk.tracing.tracing import TracerWrapper, get_span_path
|
14
15
|
from lmnr.traceloop_sdk.utils.json_encoder import JSONEncoder
|
15
16
|
|
16
17
|
|
@@ -46,14 +47,23 @@ def entity_method(
|
|
46
47
|
|
47
48
|
with get_tracer() as tracer:
|
48
49
|
span = tracer.start_span(span_name)
|
49
|
-
|
50
|
+
|
51
|
+
span_path = get_span_path(span_name)
|
52
|
+
span.set_attribute(SPAN_PATH, span_path)
|
53
|
+
ctx = context_api.set_value("span_path", span_path)
|
54
|
+
|
55
|
+
ctx = trace.set_span_in_context(span, ctx)
|
50
56
|
ctx_token = context_api.attach(ctx)
|
51
57
|
|
52
58
|
try:
|
53
59
|
if _should_send_prompts():
|
54
60
|
span.set_attribute(
|
55
|
-
|
56
|
-
_json_dumps(
|
61
|
+
SPAN_INPUT,
|
62
|
+
_json_dumps(
|
63
|
+
get_input_from_func_args(
|
64
|
+
fn, is_method(fn), args, kwargs
|
65
|
+
)
|
66
|
+
),
|
57
67
|
)
|
58
68
|
except TypeError:
|
59
69
|
pass
|
@@ -67,7 +77,7 @@ def entity_method(
|
|
67
77
|
try:
|
68
78
|
if _should_send_prompts():
|
69
79
|
span.set_attribute(
|
70
|
-
|
80
|
+
SPAN_OUTPUT,
|
71
81
|
_json_dumps(res),
|
72
82
|
)
|
73
83
|
except TypeError:
|
@@ -99,14 +109,23 @@ def aentity_method(
|
|
99
109
|
|
100
110
|
with get_tracer() as tracer:
|
101
111
|
span = tracer.start_span(span_name)
|
102
|
-
|
112
|
+
|
113
|
+
span_path = get_span_path(span_name)
|
114
|
+
span.set_attribute(SPAN_PATH, span_path)
|
115
|
+
ctx = context_api.set_value("span_path", span_path)
|
116
|
+
|
117
|
+
ctx = trace.set_span_in_context(span, ctx)
|
103
118
|
ctx_token = context_api.attach(ctx)
|
104
119
|
|
105
120
|
try:
|
106
121
|
if _should_send_prompts():
|
107
122
|
span.set_attribute(
|
108
|
-
|
109
|
-
_json_dumps(
|
123
|
+
SPAN_INPUT,
|
124
|
+
_json_dumps(
|
125
|
+
get_input_from_func_args(
|
126
|
+
fn, is_method(fn), args, kwargs
|
127
|
+
)
|
128
|
+
),
|
110
129
|
)
|
111
130
|
except TypeError:
|
112
131
|
pass
|
@@ -119,9 +138,7 @@ def aentity_method(
|
|
119
138
|
|
120
139
|
try:
|
121
140
|
if _should_send_prompts():
|
122
|
-
span.set_attribute(
|
123
|
-
SpanAttributes.TRACELOOP_ENTITY_OUTPUT, json.dumps(res)
|
124
|
-
)
|
141
|
+
span.set_attribute(SPAN_OUTPUT, json.dumps(res))
|
125
142
|
except TypeError:
|
126
143
|
pass
|
127
144
|
|
@@ -0,0 +1,9 @@
|
|
1
|
+
SPAN_INPUT = "lmnr.span.input"
|
2
|
+
SPAN_OUTPUT = "lmnr.span.output"
|
3
|
+
SPAN_TYPE = "lmnr.span.type"
|
4
|
+
SPAN_PATH = "lmnr.span.path"
|
5
|
+
|
6
|
+
ASSOCIATION_PROPERTIES = "lmnr.association.properties"
|
7
|
+
SESSION_ID = "session_id"
|
8
|
+
USER_ID = "user_id"
|
9
|
+
TRACE_TYPE = "trace_type"
|
@@ -3,7 +3,6 @@ import logging
|
|
3
3
|
import os
|
4
4
|
|
5
5
|
|
6
|
-
from colorama import Fore
|
7
6
|
from opentelemetry import trace
|
8
7
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
9
8
|
OTLPSpanExporter as HTTPExporter,
|
@@ -24,17 +23,16 @@ from opentelemetry.trace import get_tracer_provider, ProxyTracerProvider
|
|
24
23
|
from opentelemetry.context import get_value, attach, set_value
|
25
24
|
from opentelemetry.instrumentation.threading import ThreadingInstrumentor
|
26
25
|
|
27
|
-
from opentelemetry.semconv_ai import SpanAttributes
|
28
|
-
|
29
26
|
# from lmnr.traceloop_sdk import Telemetry
|
30
27
|
from lmnr.traceloop_sdk.instruments import Instruments
|
28
|
+
from lmnr.traceloop_sdk.tracing.attributes import ASSOCIATION_PROPERTIES, SPAN_PATH
|
31
29
|
from lmnr.traceloop_sdk.tracing.content_allow_list import ContentAllowList
|
32
30
|
from lmnr.traceloop_sdk.utils import is_notebook
|
33
31
|
from lmnr.traceloop_sdk.utils.package_check import is_package_installed
|
34
32
|
from typing import Dict, Optional, Set
|
35
33
|
|
36
34
|
|
37
|
-
TRACER_NAME = "
|
35
|
+
TRACER_NAME = "lmnr.tracer"
|
38
36
|
EXCLUDED_URLS = """
|
39
37
|
iam.cloud.ibm.com,
|
40
38
|
dataplatform.cloud.ibm.com,
|
@@ -44,7 +42,7 @@ EXCLUDED_URLS = """
|
|
44
42
|
api.anthropic.com,
|
45
43
|
api.cohere.ai,
|
46
44
|
pinecone.io,
|
47
|
-
|
45
|
+
api.lmnr.ai,
|
48
46
|
posthog.com,
|
49
47
|
sentry.io,
|
50
48
|
bedrock-runtime,
|
@@ -130,138 +128,103 @@ class TracerWrapper(object):
|
|
130
128
|
for instrument in instruments:
|
131
129
|
if instrument == Instruments.OPENAI:
|
132
130
|
if not init_openai_instrumentor(should_enrich_metrics):
|
133
|
-
print(
|
134
|
-
print(Fore.RESET)
|
131
|
+
print("Warning: OpenAI library does not exist.")
|
135
132
|
elif instrument == Instruments.ANTHROPIC:
|
136
133
|
if not init_anthropic_instrumentor(should_enrich_metrics):
|
137
134
|
print(
|
138
|
-
|
135
|
+
"Warning: Anthropic library does not exist."
|
139
136
|
)
|
140
|
-
print(Fore.RESET)
|
141
137
|
elif instrument == Instruments.COHERE:
|
142
138
|
if not init_cohere_instrumentor():
|
143
|
-
print(
|
144
|
-
print(Fore.RESET)
|
139
|
+
print("Warning: Cohere library does not exist.")
|
145
140
|
elif instrument == Instruments.PINECONE:
|
146
141
|
if not init_pinecone_instrumentor():
|
147
142
|
print(
|
148
|
-
|
143
|
+
"Warning: Pinecone library does not exist."
|
149
144
|
)
|
150
|
-
print(Fore.RESET)
|
151
145
|
elif instrument == Instruments.CHROMA:
|
152
146
|
if not init_chroma_instrumentor():
|
153
|
-
print(
|
154
|
-
print(Fore.RESET)
|
147
|
+
print("Warning: Chroma library does not exist.")
|
155
148
|
elif instrument == Instruments.GOOGLE_GENERATIVEAI:
|
156
149
|
if not init_google_generativeai_instrumentor():
|
157
|
-
print(
|
158
|
-
Fore.RED
|
159
|
-
+ "Warning: Google Generative AI library does not exist."
|
160
|
-
)
|
161
|
-
print(Fore.RESET)
|
150
|
+
print("Warning: Google Generative AI library does not exist.")
|
162
151
|
elif instrument == Instruments.LANGCHAIN:
|
163
152
|
if not init_langchain_instrumentor():
|
164
153
|
print(
|
165
|
-
|
154
|
+
"Warning: LangChain library does not exist."
|
166
155
|
)
|
167
|
-
print(Fore.RESET)
|
168
156
|
elif instrument == Instruments.MISTRAL:
|
169
157
|
if not init_mistralai_instrumentor():
|
170
158
|
print(
|
171
|
-
|
159
|
+
"Warning: MistralAI library does not exist."
|
172
160
|
)
|
173
|
-
print(Fore.RESET)
|
174
161
|
elif instrument == Instruments.OLLAMA:
|
175
162
|
if not init_ollama_instrumentor():
|
176
|
-
print(
|
177
|
-
print(Fore.RESET)
|
163
|
+
print("Warning: Ollama library does not exist.")
|
178
164
|
elif instrument == Instruments.LLAMA_INDEX:
|
179
165
|
if not init_llama_index_instrumentor():
|
180
166
|
print(
|
181
|
-
|
167
|
+
"Warning: LlamaIndex library does not exist."
|
182
168
|
)
|
183
|
-
print(Fore.RESET)
|
184
169
|
elif instrument == Instruments.MILVUS:
|
185
170
|
if not init_milvus_instrumentor():
|
186
|
-
print(
|
187
|
-
print(Fore.RESET)
|
171
|
+
print("Warning: Milvus library does not exist.")
|
188
172
|
elif instrument == Instruments.TRANSFORMERS:
|
189
173
|
if not init_transformers_instrumentor():
|
190
|
-
print(
|
191
|
-
Fore.RED
|
192
|
-
+ "Warning: Transformers library does not exist."
|
193
|
-
)
|
194
|
-
print(Fore.RESET)
|
174
|
+
print("Warning: Transformers library does not exist.")
|
195
175
|
elif instrument == Instruments.TOGETHER:
|
196
176
|
if not init_together_instrumentor():
|
197
177
|
print(
|
198
|
-
|
178
|
+
"Warning: TogetherAI library does not exist."
|
199
179
|
)
|
200
|
-
print(Fore.RESET)
|
201
180
|
elif instrument == Instruments.REQUESTS:
|
202
181
|
if not init_requests_instrumentor():
|
203
182
|
print(
|
204
|
-
|
183
|
+
"Warning: Requests library does not exist."
|
205
184
|
)
|
206
|
-
print(Fore.RESET)
|
207
185
|
elif instrument == Instruments.URLLIB3:
|
208
186
|
if not init_urllib3_instrumentor():
|
209
|
-
print(
|
210
|
-
print(Fore.RESET)
|
187
|
+
print("Warning: urllib3 library does not exist.")
|
211
188
|
elif instrument == Instruments.PYMYSQL:
|
212
189
|
if not init_pymysql_instrumentor():
|
213
|
-
print(
|
214
|
-
print(Fore.RESET)
|
190
|
+
print("Warning: PyMySQL library does not exist.")
|
215
191
|
elif instrument == Instruments.BEDROCK:
|
216
192
|
if not init_bedrock_instrumentor(should_enrich_metrics):
|
217
|
-
print(
|
218
|
-
print(Fore.RESET)
|
193
|
+
print("Warning: Bedrock library does not exist.")
|
219
194
|
elif instrument == Instruments.REPLICATE:
|
220
195
|
if not init_replicate_instrumentor():
|
221
196
|
print(
|
222
|
-
|
197
|
+
"Warning: Replicate library does not exist."
|
223
198
|
)
|
224
|
-
print(Fore.RESET)
|
225
199
|
elif instrument == Instruments.VERTEXAI:
|
226
200
|
if not init_vertexai_instrumentor():
|
227
201
|
print(
|
228
|
-
|
202
|
+
"Warning: Vertex AI library does not exist."
|
229
203
|
)
|
230
|
-
print(Fore.RESET)
|
231
204
|
elif instrument == Instruments.WATSONX:
|
232
205
|
if not init_watsonx_instrumentor():
|
233
|
-
print(
|
234
|
-
print(Fore.RESET)
|
206
|
+
print("Warning: Watsonx library does not exist.")
|
235
207
|
elif instrument == Instruments.WEAVIATE:
|
236
208
|
if not init_weaviate_instrumentor():
|
237
209
|
print(
|
238
|
-
|
210
|
+
"Warning: Weaviate library does not exist."
|
239
211
|
)
|
240
|
-
print(Fore.RESET)
|
241
212
|
elif instrument == Instruments.ALEPHALPHA:
|
242
213
|
if not init_alephalpha_instrumentor():
|
243
|
-
print(
|
244
|
-
Fore.RED
|
245
|
-
+ "Warning: Aleph Alpha library does not exist."
|
246
|
-
)
|
247
|
-
print(Fore.RESET)
|
214
|
+
print("Warning: Aleph Alpha library does not exist.")
|
248
215
|
elif instrument == Instruments.MARQO:
|
249
216
|
if not init_marqo_instrumentor():
|
250
|
-
print(
|
251
|
-
print(Fore.RESET)
|
217
|
+
print("Warning: marqo library does not exist.")
|
252
218
|
elif instrument == Instruments.LANCEDB:
|
253
219
|
if not init_lancedb_instrumentor():
|
254
|
-
print(
|
255
|
-
print(Fore.RESET)
|
220
|
+
print("Warning: LanceDB library does not exist.")
|
256
221
|
elif instrument == Instruments.REDIS:
|
257
222
|
if not init_redis_instrumentor():
|
258
|
-
print(
|
259
|
-
print(Fore.RESET)
|
223
|
+
print("Warning: redis library does not exist.")
|
260
224
|
|
261
225
|
else:
|
262
226
|
print(
|
263
|
-
|
264
|
-
+ "Warning: "
|
227
|
+
"Warning: "
|
265
228
|
+ instrument
|
266
229
|
+ " instrumentation does not exist."
|
267
230
|
)
|
@@ -270,7 +233,6 @@ class TracerWrapper(object):
|
|
270
233
|
+ "from lmnr.traceloop_sdk.instruments import Instruments\n"
|
271
234
|
+ 'Traceloop.init(app_name="...", instruments=set([Instruments.OPENAI]))'
|
272
235
|
)
|
273
|
-
print(Fore.RESET)
|
274
236
|
|
275
237
|
obj.__content_allow_list = ContentAllowList()
|
276
238
|
|
@@ -283,6 +245,14 @@ class TracerWrapper(object):
|
|
283
245
|
self.flush()
|
284
246
|
|
285
247
|
def _span_processor_on_start(self, span, parent_context):
|
248
|
+
span_path = get_value("span_path")
|
249
|
+
if span_path is not None:
|
250
|
+
# This is done redundantly here for most decorated functions
|
251
|
+
# However, need to do this for auto-instrumented libraries.
|
252
|
+
# Then, for auto-instrumented ones, they'll attach
|
253
|
+
# the final part of the name to the span on the backend.
|
254
|
+
span.set_attribute(SPAN_PATH, span_path)
|
255
|
+
|
286
256
|
association_properties = get_value("association_properties")
|
287
257
|
if association_properties is not None:
|
288
258
|
_set_association_properties_attributes(span, association_properties)
|
@@ -293,49 +263,6 @@ class TracerWrapper(object):
|
|
293
263
|
else:
|
294
264
|
attach(set_value("override_enable_content_tracing", False))
|
295
265
|
|
296
|
-
if is_llm_span(span):
|
297
|
-
managed_prompt = get_value("managed_prompt")
|
298
|
-
if managed_prompt is not None:
|
299
|
-
span.set_attribute(
|
300
|
-
SpanAttributes.TRACELOOP_PROMPT_MANAGED, managed_prompt
|
301
|
-
)
|
302
|
-
|
303
|
-
prompt_key = get_value("prompt_key")
|
304
|
-
if prompt_key is not None:
|
305
|
-
span.set_attribute(SpanAttributes.TRACELOOP_PROMPT_KEY, prompt_key)
|
306
|
-
|
307
|
-
prompt_version = get_value("prompt_version")
|
308
|
-
if prompt_version is not None:
|
309
|
-
span.set_attribute(
|
310
|
-
SpanAttributes.TRACELOOP_PROMPT_VERSION, prompt_version
|
311
|
-
)
|
312
|
-
|
313
|
-
prompt_version_name = get_value("prompt_version_name")
|
314
|
-
if prompt_version_name is not None:
|
315
|
-
span.set_attribute(
|
316
|
-
SpanAttributes.TRACELOOP_PROMPT_VERSION_NAME, prompt_version_name
|
317
|
-
)
|
318
|
-
|
319
|
-
prompt_version_hash = get_value("prompt_version_hash")
|
320
|
-
if prompt_version_hash is not None:
|
321
|
-
span.set_attribute(
|
322
|
-
SpanAttributes.TRACELOOP_PROMPT_VERSION_HASH, prompt_version_hash
|
323
|
-
)
|
324
|
-
|
325
|
-
prompt_template = get_value("prompt_template")
|
326
|
-
if prompt_template is not None:
|
327
|
-
span.set_attribute(
|
328
|
-
SpanAttributes.TRACELOOP_PROMPT_TEMPLATE, prompt_template
|
329
|
-
)
|
330
|
-
|
331
|
-
prompt_template_variables = get_value("prompt_template_variables")
|
332
|
-
if prompt_template_variables is not None:
|
333
|
-
for key, value in prompt_template_variables.items():
|
334
|
-
span.set_attribute(
|
335
|
-
f"{SpanAttributes.TRACELOOP_PROMPT_TEMPLATE_VARIABLES}.{key}",
|
336
|
-
value,
|
337
|
-
)
|
338
|
-
|
339
266
|
# Call original on_start method if it exists in custom processor
|
340
267
|
if self.__spans_processor_original_on_start:
|
341
268
|
self.__spans_processor_original_on_start(span, parent_context)
|
@@ -360,11 +287,7 @@ class TracerWrapper(object):
|
|
360
287
|
if (os.getenv("TRACELOOP_SUPPRESS_WARNINGS") or "false").lower() == "true":
|
361
288
|
return False
|
362
289
|
|
363
|
-
print(
|
364
|
-
Fore.RED
|
365
|
-
+ "Warning: Traceloop not initialized, make sure you call Traceloop.init()"
|
366
|
-
)
|
367
|
-
print(Fore.RESET)
|
290
|
+
print("Warning: Laminar not initialized, make sure to initialize")
|
368
291
|
return False
|
369
292
|
|
370
293
|
def flush(self):
|
@@ -399,10 +322,16 @@ def update_association_properties(properties: dict) -> None:
|
|
399
322
|
def _set_association_properties_attributes(span, properties: dict) -> None:
|
400
323
|
for key, value in properties.items():
|
401
324
|
span.set_attribute(
|
402
|
-
f"{
|
325
|
+
f"{ASSOCIATION_PROPERTIES}.{key}", value
|
403
326
|
)
|
404
327
|
|
405
328
|
|
329
|
+
def get_span_path(span_name: str) -> str:
|
330
|
+
current_span_path = get_value("span_path")
|
331
|
+
span_path = f"{current_span_path}.{span_name}" if current_span_path else span_name
|
332
|
+
return span_path
|
333
|
+
|
334
|
+
|
406
335
|
def set_managed_prompt_tracing_context(
|
407
336
|
key: str,
|
408
337
|
version: int,
|
@@ -427,10 +356,6 @@ def set_external_prompt_tracing_context(
|
|
427
356
|
attach(set_value("prompt_template_variables", variables))
|
428
357
|
|
429
358
|
|
430
|
-
def is_llm_span(span) -> bool:
|
431
|
-
return span.attributes.get(SpanAttributes.LLM_REQUEST_TYPE) is not None
|
432
|
-
|
433
|
-
|
434
359
|
def init_spans_exporter(api_endpoint: str, headers: Dict[str, str]) -> SpanExporter:
|
435
360
|
if "http" in api_endpoint.lower() or "https" in api_endpoint.lower():
|
436
361
|
return HTTPExporter(endpoint=f"{api_endpoint}/v1/traces", headers=headers)
|
@@ -499,7 +424,6 @@ def init_openai_instrumentor(should_enrich_metrics: bool):
|
|
499
424
|
# exception_logger=lambda e: Telemetry().log_exception(e),
|
500
425
|
enrich_assistant=should_enrich_metrics,
|
501
426
|
enrich_token_usage=should_enrich_metrics,
|
502
|
-
get_common_metrics_attributes=metrics_common_attributes,
|
503
427
|
)
|
504
428
|
if not instrumentor.is_instrumented_by_opentelemetry:
|
505
429
|
instrumentor.instrument()
|
@@ -520,7 +444,6 @@ def init_anthropic_instrumentor(should_enrich_metrics: bool):
|
|
520
444
|
instrumentor = AnthropicInstrumentor(
|
521
445
|
# exception_logger=lambda e: Telemetry().log_exception(e),
|
522
446
|
enrich_token_usage=should_enrich_metrics,
|
523
|
-
get_common_metrics_attributes=metrics_common_attributes,
|
524
447
|
)
|
525
448
|
if not instrumentor.is_instrumented_by_opentelemetry:
|
526
449
|
instrumentor.instrument()
|
@@ -988,23 +911,3 @@ def init_groq_instrumentor():
|
|
988
911
|
logging.error(f"Error initializing Groq instrumentor: {e}")
|
989
912
|
# Telemetry().log_exception(e)
|
990
913
|
return False
|
991
|
-
|
992
|
-
|
993
|
-
def metrics_common_attributes():
|
994
|
-
common_attributes = {}
|
995
|
-
workflow_name = get_value("workflow_name")
|
996
|
-
if workflow_name is not None:
|
997
|
-
common_attributes[SpanAttributes.TRACELOOP_WORKFLOW_NAME] = workflow_name
|
998
|
-
|
999
|
-
entity_name = get_value("entity_name")
|
1000
|
-
if entity_name is not None:
|
1001
|
-
common_attributes[SpanAttributes.TRACELOOP_ENTITY_NAME] = entity_name
|
1002
|
-
|
1003
|
-
association_properties = get_value("association_properties")
|
1004
|
-
if association_properties is not None:
|
1005
|
-
for key, value in association_properties.items():
|
1006
|
-
common_attributes[
|
1007
|
-
f"{SpanAttributes.TRACELOOP_ASSOCIATION_PROPERTIES}.{key}"
|
1008
|
-
] = value
|
1009
|
-
|
1010
|
-
return common_attributes
|