lmnr 0.7.4__tar.gz → 0.7.5__tar.gz
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-0.7.4 → lmnr-0.7.5}/PKG-INFO +1 -1
- {lmnr-0.7.4 → lmnr-0.7.5}/pyproject.toml +4 -3
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/decorators/__init__.py +15 -4
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +10 -2
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +18 -2
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -21
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/attributes.py +1 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/instruments.py +0 -2
- lmnr-0.7.5/src/lmnr/sdk/browser/browser_use_cdp_otel.py +95 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/browser_use_otel.py +4 -5
- lmnr-0.7.5/src/lmnr/sdk/browser/cdp_utils.py +733 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/pw_utils.py +57 -51
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/decorators.py +6 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/evaluations.py +6 -1
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/types.py +8 -2
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/version.py +1 -1
- {lmnr-0.7.4 → lmnr-0.7.5}/README.md +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/cli.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/.flake8 +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/litellm/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/litellm/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/config.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_emitter.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_models.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/span_utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/streaming.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/version.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/schema_utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/config.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_emitter.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_models.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/span_utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/version.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/config.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_emitter.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_models.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/image_gen_wrappers.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v0/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/version.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/opentelemetry/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/context.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/exporter.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/processor.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/tracer.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/utils/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/utils/json_encoder.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/utils/package_check.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/py.typed +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/patchright_otel.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/playwright_otel.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/recorder/record.umd.min.cjs +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/utils.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/async_client.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/agent.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/base.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/browser_events.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/evals.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/evaluators.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/tags.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/agent.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/base.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/evals.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/evaluators.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/tags.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/sync_client.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/datasets.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/eval_control.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/laminar.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/utils.py +0 -0
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "lmnr"
|
9
|
-
version = "0.7.
|
9
|
+
version = "0.7.5"
|
10
10
|
description = "Python SDK for Laminar"
|
11
11
|
authors = [
|
12
12
|
{ name = "lmnr.ai", email = "founders@lmnr.ai" }
|
@@ -124,14 +124,15 @@ dev = [
|
|
124
124
|
"pytest-asyncio>=0.26.0",
|
125
125
|
"playwright>=1.52.0",
|
126
126
|
"vcrpy>=7.0.0",
|
127
|
-
|
127
|
+
# litellm breaks with openai>=1.100.0 (as of litellm 1.75.8)
|
128
|
+
"openai>=1.99.7,<1.100.0",
|
128
129
|
"pytest-recording>=0.13.4",
|
129
130
|
"patchright>=1.52.3",
|
130
131
|
"google-genai>=1.19.0",
|
131
132
|
"langgraph>=0.4.8",
|
132
133
|
"langchain-core>=0.3.64",
|
133
134
|
"langchain>=0.3.25",
|
134
|
-
"litellm>=1.
|
135
|
+
"litellm>=1.75.8",
|
135
136
|
"groq>=0.30.0",
|
136
137
|
"anthropic>=0.60.0",
|
137
138
|
"langchain-openai>=0.3.28",
|
@@ -65,14 +65,17 @@ def json_dumps(data: dict) -> str:
|
|
65
65
|
|
66
66
|
|
67
67
|
def _setup_span(
|
68
|
-
span_name: str,
|
68
|
+
span_name: str,
|
69
|
+
span_type: str,
|
70
|
+
association_properties: dict[str, Any] | None,
|
71
|
+
preserve_global_context: bool = False,
|
69
72
|
):
|
70
73
|
"""Set up a span with the given name, type, and association properties."""
|
71
74
|
with get_tracer_with_context() as (tracer, isolated_context):
|
72
75
|
# Create span in isolated context
|
73
76
|
span = tracer.start_span(
|
74
77
|
span_name,
|
75
|
-
context=isolated_context,
|
78
|
+
context=isolated_context if not preserve_global_context else None,
|
76
79
|
attributes={SPAN_TYPE: span_type},
|
77
80
|
)
|
78
81
|
|
@@ -167,6 +170,7 @@ def _cleanup_span(span: Span, wrapper: TracerWrapper):
|
|
167
170
|
|
168
171
|
|
169
172
|
def observe_base(
|
173
|
+
*,
|
170
174
|
name: str | None = None,
|
171
175
|
ignore_input: bool = False,
|
172
176
|
ignore_inputs: list[str] | None = None,
|
@@ -175,6 +179,7 @@ def observe_base(
|
|
175
179
|
association_properties: dict[str, Any] | None = None,
|
176
180
|
input_formatter: Callable[..., str] | None = None,
|
177
181
|
output_formatter: Callable[..., str] | None = None,
|
182
|
+
preserve_global_context: bool = False,
|
178
183
|
):
|
179
184
|
def decorate(fn):
|
180
185
|
@wraps(fn)
|
@@ -185,7 +190,9 @@ def observe_base(
|
|
185
190
|
span_name = name or fn.__name__
|
186
191
|
wrapper = TracerWrapper()
|
187
192
|
|
188
|
-
span = _setup_span(
|
193
|
+
span = _setup_span(
|
194
|
+
span_name, span_type, association_properties, preserve_global_context
|
195
|
+
)
|
189
196
|
new_context = wrapper.push_span_context(span)
|
190
197
|
if session_id := association_properties.get("session_id"):
|
191
198
|
new_context = context_api.set_value(
|
@@ -241,6 +248,7 @@ def observe_base(
|
|
241
248
|
|
242
249
|
# Async Decorators
|
243
250
|
def async_observe_base(
|
251
|
+
*,
|
244
252
|
name: str | None = None,
|
245
253
|
ignore_input: bool = False,
|
246
254
|
ignore_inputs: list[str] | None = None,
|
@@ -249,6 +257,7 @@ def async_observe_base(
|
|
249
257
|
association_properties: dict[str, Any] | None = None,
|
250
258
|
input_formatter: Callable[..., str] | None = None,
|
251
259
|
output_formatter: Callable[..., str] | None = None,
|
260
|
+
preserve_global_context: bool = False,
|
252
261
|
):
|
253
262
|
def decorate(fn):
|
254
263
|
@wraps(fn)
|
@@ -259,7 +268,9 @@ def async_observe_base(
|
|
259
268
|
span_name = name or fn.__name__
|
260
269
|
wrapper = TracerWrapper()
|
261
270
|
|
262
|
-
span = _setup_span(
|
271
|
+
span = _setup_span(
|
272
|
+
span_name, span_type, association_properties, preserve_global_context
|
273
|
+
)
|
263
274
|
new_context = wrapper.push_span_context(span)
|
264
275
|
if session_id := association_properties.get("session_id"):
|
265
276
|
new_context = context_api.set_value(
|
@@ -56,7 +56,6 @@ from opentelemetry.trace import SpanKind, Tracer
|
|
56
56
|
from opentelemetry.trace.status import Status, StatusCode
|
57
57
|
from wrapt import ObjectProxy
|
58
58
|
|
59
|
-
from openai.types.chat.chat_completion_message import FunctionCall
|
60
59
|
import pydantic
|
61
60
|
|
62
61
|
SPAN_NAME = "openai.chat"
|
@@ -1014,7 +1013,7 @@ def _parse_tool_calls(
|
|
1014
1013
|
tool_call_data = copy.deepcopy(tool_call)
|
1015
1014
|
elif _is_tool_call_model(tool_call):
|
1016
1015
|
tool_call_data = tool_call.model_dump()
|
1017
|
-
elif
|
1016
|
+
elif _is_function_call(tool_call):
|
1018
1017
|
function_call = tool_call.model_dump()
|
1019
1018
|
tool_call_data = ToolCall(
|
1020
1019
|
id="",
|
@@ -1040,6 +1039,15 @@ def _is_tool_call_model(tool_call):
|
|
1040
1039
|
return False
|
1041
1040
|
|
1042
1041
|
|
1042
|
+
def _is_function_call(model: Union[dict, pydantic.BaseModel]) -> bool:
|
1043
|
+
try:
|
1044
|
+
from openai.types.chat.chat_completion_message import FunctionCall
|
1045
|
+
|
1046
|
+
return isinstance(model, FunctionCall)
|
1047
|
+
except Exception:
|
1048
|
+
return False
|
1049
|
+
|
1050
|
+
|
1043
1051
|
@singledispatch
|
1044
1052
|
def _parse_choice_event(choice) -> ChoiceEvent:
|
1045
1053
|
has_message = choice.message is not None
|
@@ -464,7 +464,9 @@ def responses_get_or_create_wrapper(tracer: Tracer, wrapped, instance, args, kwa
|
|
464
464
|
output_blocks={block.id: block for block in parsed_response.output}
|
465
465
|
| existing_data.get("output_blocks", {}),
|
466
466
|
usage=existing_data.get("usage", parsed_response.usage),
|
467
|
-
output_text=existing_data.get(
|
467
|
+
output_text=existing_data.get(
|
468
|
+
"output_text", _get_output_text(parsed_response)
|
469
|
+
),
|
468
470
|
request_model=existing_data.get("request_model", kwargs.get("model")),
|
469
471
|
response_model=existing_data.get("response_model", parsed_response.model),
|
470
472
|
)
|
@@ -560,7 +562,9 @@ async def async_responses_get_or_create_wrapper(
|
|
560
562
|
output_blocks={block.id: block for block in parsed_response.output}
|
561
563
|
| existing_data.get("output_blocks", {}),
|
562
564
|
usage=existing_data.get("usage", parsed_response.usage),
|
563
|
-
output_text=existing_data.get(
|
565
|
+
output_text=existing_data.get(
|
566
|
+
"output_text", _get_output_text(parsed_response)
|
567
|
+
),
|
564
568
|
request_model=existing_data.get("request_model", kwargs.get("model")),
|
565
569
|
response_model=existing_data.get("response_model", parsed_response.model),
|
566
570
|
)
|
@@ -639,4 +643,16 @@ async def async_responses_cancel_wrapper(
|
|
639
643
|
return response
|
640
644
|
|
641
645
|
|
646
|
+
def _get_output_text(parsed_response: Response) -> Optional[str]:
|
647
|
+
output_text = None
|
648
|
+
if hasattr(parsed_response, "output_text"):
|
649
|
+
output_text = parsed_response.output_text
|
650
|
+
else:
|
651
|
+
try:
|
652
|
+
output_text = parsed_response.output[0].content[0].text
|
653
|
+
except Exception:
|
654
|
+
pass
|
655
|
+
return output_text
|
656
|
+
|
657
|
+
|
642
658
|
# TODO: build streaming responses
|
@@ -51,19 +51,26 @@ class BedrockInstrumentorInitializer(InstrumentorInitializer):
|
|
51
51
|
|
52
52
|
|
53
53
|
class BrowserUseInstrumentorInitializer(InstrumentorInitializer):
|
54
|
-
def init_instrumentor(
|
54
|
+
def init_instrumentor(
|
55
|
+
self, client, async_client, *args, **kwargs
|
56
|
+
) -> BaseInstrumentor | None:
|
55
57
|
if not is_package_installed("browser-use"):
|
56
58
|
return None
|
57
59
|
|
58
60
|
version = get_package_version("browser-use")
|
59
61
|
from packaging.version import parse
|
60
62
|
|
61
|
-
if version and parse(version)
|
62
|
-
|
63
|
+
if version and parse(version) < parse("0.5.0"):
|
64
|
+
from lmnr.sdk.browser.browser_use_otel import BrowserUseLegacyInstrumentor
|
65
|
+
|
66
|
+
return BrowserUseLegacyInstrumentor()
|
63
67
|
|
64
|
-
|
68
|
+
if version and parse(version) >= parse("1.0.0rc1"):
|
69
|
+
from lmnr.sdk.browser.browser_use_cdp_otel import BrowserUseInstrumentor
|
65
70
|
|
66
|
-
|
71
|
+
return BrowserUseInstrumentor(async_client)
|
72
|
+
|
73
|
+
return None
|
67
74
|
|
68
75
|
|
69
76
|
class ChromaInstrumentorInitializer(InstrumentorInitializer):
|
@@ -102,22 +109,6 @@ class CrewAIInstrumentorInitializer(InstrumentorInitializer):
|
|
102
109
|
return CrewAiInstrumentor()
|
103
110
|
|
104
111
|
|
105
|
-
class GoogleGenerativeAIInstrumentorInitializer(InstrumentorInitializer):
|
106
|
-
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
107
|
-
if not is_package_installed("google-generativeai"):
|
108
|
-
return None
|
109
|
-
if not is_package_installed(
|
110
|
-
"opentelemetry-instrumentation-google-generativeai"
|
111
|
-
):
|
112
|
-
return None
|
113
|
-
|
114
|
-
from opentelemetry.instrumentation.google_generativeai import (
|
115
|
-
GoogleGenerativeAiInstrumentor,
|
116
|
-
)
|
117
|
-
|
118
|
-
return GoogleGenerativeAiInstrumentor()
|
119
|
-
|
120
|
-
|
121
112
|
class GoogleGenAIInstrumentorInitializer(InstrumentorInitializer):
|
122
113
|
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
123
114
|
if not is_package_installed("google-genai"):
|
@@ -19,6 +19,7 @@ PARENT_SPAN_IDS_PATH = "lmnr.span.parent_ids_path"
|
|
19
19
|
SPAN_INSTRUMENTATION_SOURCE = "lmnr.span.instrumentation_source"
|
20
20
|
SPAN_SDK_VERSION = "lmnr.span.sdk_version"
|
21
21
|
SPAN_LANGUAGE_VERSION = "lmnr.span.language_version"
|
22
|
+
HUMAN_EVALUATOR_OPTIONS = "lmnr.span.human_evaluator_options"
|
22
23
|
|
23
24
|
ASSOCIATION_PROPERTIES = "lmnr.association.properties"
|
24
25
|
SESSION_ID = "session_id"
|
@@ -20,7 +20,6 @@ class Instruments(Enum):
|
|
20
20
|
CHROMA = "chroma"
|
21
21
|
COHERE = "cohere"
|
22
22
|
CREWAI = "crewai"
|
23
|
-
GOOGLE_GENERATIVEAI = "google_generativeai"
|
24
23
|
GOOGLE_GENAI = "google_genai"
|
25
24
|
GROQ = "groq"
|
26
25
|
HAYSTACK = "haystack"
|
@@ -63,7 +62,6 @@ INSTRUMENTATION_INITIALIZERS: dict[
|
|
63
62
|
Instruments.CHROMA: initializers.ChromaInstrumentorInitializer(),
|
64
63
|
Instruments.COHERE: initializers.CohereInstrumentorInitializer(),
|
65
64
|
Instruments.CREWAI: initializers.CrewAIInstrumentorInitializer(),
|
66
|
-
Instruments.GOOGLE_GENERATIVEAI: initializers.GoogleGenerativeAIInstrumentorInitializer(),
|
67
65
|
Instruments.GOOGLE_GENAI: initializers.GoogleGenAIInstrumentorInitializer(),
|
68
66
|
Instruments.GROQ: initializers.GroqInstrumentorInitializer(),
|
69
67
|
Instruments.HAYSTACK: initializers.HaystackInstrumentorInitializer(),
|
@@ -0,0 +1,95 @@
|
|
1
|
+
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
2
|
+
from lmnr.sdk.browser.utils import with_tracer_and_client_wrapper
|
3
|
+
from lmnr.version import __version__
|
4
|
+
from lmnr.sdk.browser.cdp_utils import (
|
5
|
+
is_recorder_present,
|
6
|
+
start_recording_events,
|
7
|
+
take_full_snapshot,
|
8
|
+
)
|
9
|
+
|
10
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
11
|
+
from opentelemetry.instrumentation.utils import unwrap
|
12
|
+
from opentelemetry.trace import get_tracer, Tracer
|
13
|
+
from typing import Collection
|
14
|
+
from wrapt import wrap_function_wrapper
|
15
|
+
import uuid
|
16
|
+
|
17
|
+
# Stable versions, e.g. 1.0.0, satisfy this condition too
|
18
|
+
_instruments = ("browser-use >= 1.0.0rc1",)
|
19
|
+
|
20
|
+
WRAPPED_METHODS = [
|
21
|
+
{
|
22
|
+
"package": "browser_use.browser.session",
|
23
|
+
"object": "BrowserSession",
|
24
|
+
"method": "get_or_create_cdp_session",
|
25
|
+
"action": "inject_session_recorder",
|
26
|
+
},
|
27
|
+
{
|
28
|
+
"package": "browser_use.browser.session",
|
29
|
+
"object": "BrowserSession",
|
30
|
+
"method": "on_SwitchTabEvent",
|
31
|
+
"action": "take_full_snapshot",
|
32
|
+
},
|
33
|
+
]
|
34
|
+
|
35
|
+
|
36
|
+
@with_tracer_and_client_wrapper
|
37
|
+
async def _wrap(
|
38
|
+
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
39
|
+
):
|
40
|
+
result = await wrapped(*args, **kwargs)
|
41
|
+
|
42
|
+
if to_wrap.get("action") == "inject_session_recorder":
|
43
|
+
is_registered = await is_recorder_present(result)
|
44
|
+
if not is_registered:
|
45
|
+
await start_recording_events(result, str(uuid.uuid4()), client)
|
46
|
+
|
47
|
+
if to_wrap.get("action") == "take_full_snapshot":
|
48
|
+
target_id = result
|
49
|
+
if target_id:
|
50
|
+
cdp_session = await instance.get_or_create_cdp_session(target_id)
|
51
|
+
await take_full_snapshot(cdp_session)
|
52
|
+
|
53
|
+
return result
|
54
|
+
|
55
|
+
|
56
|
+
class BrowserUseInstrumentor(BaseInstrumentor):
|
57
|
+
def __init__(self, async_client: AsyncLaminarClient):
|
58
|
+
super().__init__()
|
59
|
+
self.async_client = async_client
|
60
|
+
|
61
|
+
def instrumentation_dependencies(self) -> Collection[str]:
|
62
|
+
return _instruments
|
63
|
+
|
64
|
+
def _instrument(self, **kwargs):
|
65
|
+
tracer_provider = kwargs.get("tracer_provider")
|
66
|
+
tracer = get_tracer(__name__, __version__, tracer_provider)
|
67
|
+
|
68
|
+
for wrapped_method in WRAPPED_METHODS:
|
69
|
+
wrap_package = wrapped_method.get("package")
|
70
|
+
wrap_object = wrapped_method.get("object")
|
71
|
+
wrap_method = wrapped_method.get("method")
|
72
|
+
|
73
|
+
try:
|
74
|
+
wrap_function_wrapper(
|
75
|
+
wrap_package,
|
76
|
+
f"{wrap_object}.{wrap_method}",
|
77
|
+
_wrap(
|
78
|
+
tracer,
|
79
|
+
self.async_client,
|
80
|
+
wrapped_method,
|
81
|
+
),
|
82
|
+
)
|
83
|
+
except (ModuleNotFoundError, ImportError):
|
84
|
+
pass # that's ok, we're not instrumenting everything
|
85
|
+
|
86
|
+
def _uninstrument(self, **kwargs):
|
87
|
+
for wrapped_method in WRAPPED_METHODS:
|
88
|
+
wrap_package = wrapped_method.get("package")
|
89
|
+
wrap_object = wrapped_method.get("object")
|
90
|
+
wrap_method = wrapped_method.get("method")
|
91
|
+
|
92
|
+
unwrap(
|
93
|
+
f"{wrap_package}.{wrap_object}" if wrap_object else wrap_package,
|
94
|
+
wrap_method,
|
95
|
+
)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from lmnr.opentelemetry_lib.decorators import json_dumps
|
2
|
-
from lmnr
|
2
|
+
from lmnr import Laminar
|
3
3
|
from lmnr.sdk.browser.utils import with_tracer_wrapper
|
4
4
|
from lmnr.sdk.utils import get_input_from_func_args
|
5
5
|
from lmnr.version import __version__
|
@@ -16,11 +16,11 @@ try:
|
|
16
16
|
except ImportError as e:
|
17
17
|
raise ImportError(
|
18
18
|
f"Attempted to import {__file__}, but it is designed "
|
19
|
-
"to patch Browser Use, which is not installed. Use `pip install browser-use` "
|
19
|
+
"to patch Browser Use < 0.5.0, which is not installed. Use `pip install browser-use` "
|
20
20
|
"to install Browser Use or remove this import."
|
21
21
|
) from e
|
22
22
|
|
23
|
-
_instruments = ("browser-use
|
23
|
+
_instruments = ("browser-use < 0.5.0",)
|
24
24
|
|
25
25
|
WRAPPED_METHODS = [
|
26
26
|
{
|
@@ -89,7 +89,6 @@ async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
89
89
|
span_name = f"agent.step.{step_info.step_number}"
|
90
90
|
|
91
91
|
with Laminar.start_as_current_span(span_name) as span:
|
92
|
-
span.set_attributes(attributes)
|
93
92
|
result = await wrapped(*args, **kwargs)
|
94
93
|
if not to_wrap.get("ignore_output"):
|
95
94
|
to_serialize = result
|
@@ -104,7 +103,7 @@ async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
104
103
|
return result
|
105
104
|
|
106
105
|
|
107
|
-
class
|
106
|
+
class BrowserUseLegacyInstrumentor(BaseInstrumentor):
|
108
107
|
def __init__(self):
|
109
108
|
super().__init__()
|
110
109
|
|