lmnr 0.6.17__py3-none-any.whl → 0.6.19__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/__init__.py +0 -0
- lmnr/cli.py +0 -0
- lmnr/opentelemetry_lib/.flake8 +0 -0
- lmnr/opentelemetry_lib/__init__.py +0 -0
- lmnr/opentelemetry_lib/decorators/__init__.py +0 -0
- lmnr/opentelemetry_lib/litellm/__init__.py +0 -0
- lmnr/opentelemetry_lib/litellm/utils.py +0 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +55 -20
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +0 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/schema_utils.py +23 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +0 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +0 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +0 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/__init__.py +61 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +442 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +1024 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +297 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/config.py +16 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +308 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_emitter.py +100 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_models.py +41 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/image_gen_wrappers.py +68 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +185 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v0/__init__.py +176 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/__init__.py +358 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +319 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +132 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +626 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/version.py +1 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/opentelemetry/__init__.py +69 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py +0 -0
- lmnr/opentelemetry_lib/tracing/__init__.py +0 -0
- lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -3
- lmnr/opentelemetry_lib/tracing/attributes.py +0 -0
- lmnr/opentelemetry_lib/tracing/context_properties.py +0 -0
- lmnr/opentelemetry_lib/tracing/exporter.py +0 -0
- lmnr/opentelemetry_lib/tracing/instruments.py +7 -0
- lmnr/opentelemetry_lib/tracing/processor.py +0 -0
- lmnr/opentelemetry_lib/tracing/tracer.py +0 -0
- lmnr/opentelemetry_lib/utils/__init__.py +0 -0
- lmnr/opentelemetry_lib/utils/json_encoder.py +0 -0
- lmnr/opentelemetry_lib/utils/package_check.py +0 -0
- lmnr/py.typed +0 -0
- lmnr/sdk/__init__.py +0 -0
- lmnr/sdk/browser/__init__.py +0 -0
- lmnr/sdk/browser/browser_use_otel.py +1 -1
- lmnr/sdk/browser/patchright_otel.py +0 -14
- lmnr/sdk/browser/playwright_otel.py +16 -130
- lmnr/sdk/browser/pw_utils.py +45 -31
- lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +0 -0
- lmnr/sdk/browser/utils.py +0 -0
- lmnr/sdk/client/asynchronous/async_client.py +0 -0
- lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
- lmnr/sdk/client/asynchronous/resources/agent.py +0 -0
- lmnr/sdk/client/asynchronous/resources/base.py +0 -0
- lmnr/sdk/client/asynchronous/resources/browser_events.py +0 -0
- lmnr/sdk/client/asynchronous/resources/evals.py +0 -0
- lmnr/sdk/client/asynchronous/resources/tags.py +0 -0
- lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
- lmnr/sdk/client/synchronous/resources/agent.py +0 -0
- lmnr/sdk/client/synchronous/resources/base.py +0 -0
- lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
- lmnr/sdk/client/synchronous/resources/evals.py +0 -0
- lmnr/sdk/client/synchronous/resources/tags.py +0 -0
- lmnr/sdk/client/synchronous/sync_client.py +0 -0
- lmnr/sdk/datasets.py +0 -0
- lmnr/sdk/decorators.py +0 -0
- lmnr/sdk/eval_control.py +0 -0
- lmnr/sdk/evaluations.py +0 -0
- lmnr/sdk/laminar.py +0 -0
- lmnr/sdk/log.py +0 -0
- lmnr/sdk/types.py +0 -0
- lmnr/sdk/utils.py +0 -0
- lmnr/version.py +1 -1
- {lmnr-0.6.17.dist-info → lmnr-0.6.19.dist-info}/METADATA +70 -80
- lmnr-0.6.19.dist-info/RECORD +78 -0
- lmnr-0.6.19.dist-info/WHEEL +4 -0
- lmnr-0.6.19.dist-info/entry_points.txt +3 -0
- lmnr-0.6.17.dist-info/LICENSE +0 -75
- lmnr-0.6.17.dist-info/RECORD +0 -61
- lmnr-0.6.17.dist-info/WHEEL +0 -4
- lmnr-0.6.17.dist-info/entry_points.txt +0 -3
File without changes
|
@@ -261,10 +261,8 @@ class OpenAIInstrumentorInitializer(InstrumentorInitializer):
|
|
261
261
|
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
262
262
|
if not is_package_installed("openai"):
|
263
263
|
return None
|
264
|
-
if not is_package_installed("opentelemetry-instrumentation-openai"):
|
265
|
-
return None
|
266
264
|
|
267
|
-
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
|
265
|
+
from ..opentelemetry.instrumentation.openai import OpenAIInstrumentor
|
268
266
|
|
269
267
|
return OpenAIInstrumentor(
|
270
268
|
# Default in the package provided is an empty function, which
|
@@ -274,6 +272,15 @@ class OpenAIInstrumentorInitializer(InstrumentorInitializer):
|
|
274
272
|
)
|
275
273
|
|
276
274
|
|
275
|
+
class OpenTelemetryInstrumentorInitializer(InstrumentorInitializer):
|
276
|
+
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
277
|
+
from ..opentelemetry.instrumentation.opentelemetry import (
|
278
|
+
OpentelemetryInstrumentor,
|
279
|
+
)
|
280
|
+
|
281
|
+
return OpentelemetryInstrumentor()
|
282
|
+
|
283
|
+
|
277
284
|
class PatchrightInstrumentorInitializer(InstrumentorInitializer):
|
278
285
|
def init_instrumentor(
|
279
286
|
self, client, async_client, *args, **kwargs
|
@@ -345,6 +352,7 @@ class SageMakerInstrumentorInitializer(InstrumentorInitializer):
|
|
345
352
|
|
346
353
|
return SageMakerInstrumentor()
|
347
354
|
|
355
|
+
|
348
356
|
class SkyvernInstrumentorInitializer(InstrumentorInitializer):
|
349
357
|
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
350
358
|
if not is_package_installed("skyvern"):
|
@@ -354,6 +362,7 @@ class SkyvernInstrumentorInitializer(InstrumentorInitializer):
|
|
354
362
|
|
355
363
|
return SkyvernInstrumentor()
|
356
364
|
|
365
|
+
|
357
366
|
class TogetherInstrumentorInitializer(InstrumentorInitializer):
|
358
367
|
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
359
368
|
if not is_package_installed("together"):
|
File without changes
|
File without changes
|
File without changes
|
@@ -34,6 +34,11 @@ class Instruments(Enum):
|
|
34
34
|
MISTRAL = "mistral"
|
35
35
|
OLLAMA = "ollama"
|
36
36
|
OPENAI = "openai"
|
37
|
+
# Patch OpenTelemetry to fix DataDog's broken Span context
|
38
|
+
# See lmnr.opentelemetry_lib.opentelemetry.instrumentation.opentelemetry
|
39
|
+
# for more details.
|
40
|
+
OPENTELEMETRY = "opentelemetry"
|
41
|
+
###
|
37
42
|
PATCHRIGHT = "patchright"
|
38
43
|
PINECONE = "pinecone"
|
39
44
|
PLAYWRIGHT = "playwright"
|
@@ -47,6 +52,7 @@ class Instruments(Enum):
|
|
47
52
|
WATSONX = "watsonx"
|
48
53
|
WEAVIATE = "weaviate"
|
49
54
|
|
55
|
+
|
50
56
|
INSTRUMENTATION_INITIALIZERS: dict[
|
51
57
|
Instruments, initializers.InstrumentorInitializer
|
52
58
|
] = {
|
@@ -71,6 +77,7 @@ INSTRUMENTATION_INITIALIZERS: dict[
|
|
71
77
|
Instruments.MISTRAL: initializers.MistralInstrumentorInitializer(),
|
72
78
|
Instruments.OLLAMA: initializers.OllamaInstrumentorInitializer(),
|
73
79
|
Instruments.OPENAI: initializers.OpenAIInstrumentorInitializer(),
|
80
|
+
Instruments.OPENTELEMETRY: initializers.OpenTelemetryInstrumentorInitializer(),
|
74
81
|
Instruments.PATCHRIGHT: initializers.PatchrightInstrumentorInitializer(),
|
75
82
|
Instruments.PINECONE: initializers.PineconeInstrumentorInitializer(),
|
76
83
|
Instruments.PLAYWRIGHT: initializers.PlaywrightInstrumentorInitializer(),
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
lmnr/py.typed
CHANGED
File without changes
|
lmnr/sdk/__init__.py
CHANGED
File without changes
|
lmnr/sdk/browser/__init__.py
CHANGED
File without changes
|
@@ -5,8 +5,6 @@ from lmnr.sdk.browser.playwright_otel import (
|
|
5
5
|
_wrap_new_browser_async,
|
6
6
|
_wrap_new_context_sync,
|
7
7
|
_wrap_new_context_async,
|
8
|
-
_wrap_close_browser_sync,
|
9
|
-
_wrap_close_browser_async,
|
10
8
|
)
|
11
9
|
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
12
10
|
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
@@ -50,12 +48,6 @@ WRAPPED_METHODS = [
|
|
50
48
|
"method": "connect_over_cdp",
|
51
49
|
"wrapper": _wrap_new_browser_sync,
|
52
50
|
},
|
53
|
-
{
|
54
|
-
"package": "patchright.sync_api",
|
55
|
-
"object": "Browser",
|
56
|
-
"method": "close",
|
57
|
-
"wrapper": _wrap_close_browser_sync,
|
58
|
-
},
|
59
51
|
{
|
60
52
|
"package": "patchright.sync_api",
|
61
53
|
"object": "Browser",
|
@@ -101,12 +93,6 @@ WRAPPED_METHODS_ASYNC = [
|
|
101
93
|
"method": "connect_over_cdp",
|
102
94
|
"wrapper": _wrap_new_browser_async,
|
103
95
|
},
|
104
|
-
{
|
105
|
-
"package": "patchright.async_api",
|
106
|
-
"object": "Browser",
|
107
|
-
"method": "close",
|
108
|
-
"wrapper": _wrap_close_browser_async,
|
109
|
-
},
|
110
96
|
{
|
111
97
|
"package": "patchright.async_api",
|
112
98
|
"object": "Browser",
|
@@ -13,12 +13,7 @@ from opentelemetry.instrumentation.utils import unwrap
|
|
13
13
|
from opentelemetry.trace import (
|
14
14
|
get_tracer,
|
15
15
|
Tracer,
|
16
|
-
get_current_span,
|
17
|
-
Span,
|
18
|
-
INVALID_SPAN,
|
19
|
-
set_span_in_context,
|
20
16
|
)
|
21
|
-
from opentelemetry.context import get_current
|
22
17
|
from typing import Collection
|
23
18
|
from wrapt import wrap_function_wrapper
|
24
19
|
|
@@ -54,59 +49,37 @@ except ImportError as e:
|
|
54
49
|
_instruments = ("playwright >= 1.9.0",)
|
55
50
|
logger = logging.getLogger(__name__)
|
56
51
|
|
57
|
-
_context_spans: dict[str, Span] = {}
|
58
|
-
|
59
52
|
|
60
53
|
@with_tracer_and_client_wrapper
|
61
54
|
def _wrap_new_page(
|
62
55
|
tracer: Tracer, client: LaminarClient, to_wrap, wrapped, instance, args, kwargs
|
63
56
|
):
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
session_id = str(uuid.uuid4().hex)
|
69
|
-
trace_id = format(get_current_span().get_span_context().trace_id, "032x")
|
70
|
-
span.set_attribute("lmnr.internal.has_browser_session", True)
|
71
|
-
handle_navigation_sync(page, session_id, trace_id, client)
|
72
|
-
return page
|
57
|
+
page = wrapped(*args, **kwargs)
|
58
|
+
session_id = str(uuid.uuid4().hex)
|
59
|
+
handle_navigation_sync(page, session_id, client)
|
60
|
+
return page
|
73
61
|
|
74
62
|
|
75
63
|
@with_tracer_and_client_wrapper
|
76
64
|
async def _wrap_new_page_async(
|
77
65
|
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
78
66
|
):
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
session_id = str(uuid.uuid4().hex)
|
84
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
85
|
-
span.set_attribute("lmnr.internal.has_browser_session", True)
|
86
|
-
await handle_navigation_async(page, session_id, trace_id, client)
|
87
|
-
return page
|
67
|
+
page = await wrapped(*args, **kwargs)
|
68
|
+
session_id = str(uuid.uuid4().hex)
|
69
|
+
await handle_navigation_async(page, session_id, client)
|
70
|
+
return page
|
88
71
|
|
89
72
|
|
90
73
|
@with_tracer_and_client_wrapper
|
91
74
|
def _wrap_new_browser_sync(
|
92
75
|
tracer: Tracer, client: LaminarClient, to_wrap, wrapped, instance, args, kwargs
|
93
76
|
):
|
94
|
-
global _context_spans
|
95
77
|
browser: SyncBrowser = wrapped(*args, **kwargs)
|
96
78
|
session_id = str(uuid.uuid4().hex)
|
97
79
|
for context in browser.contexts:
|
98
|
-
span = get_current_span()
|
99
|
-
if span == INVALID_SPAN:
|
100
|
-
span = tracer.start_span(
|
101
|
-
name=f"{to_wrap.get('object')}.{to_wrap.get('method')}"
|
102
|
-
)
|
103
|
-
set_span_in_context(span, get_current())
|
104
|
-
_context_spans[id(context)] = span
|
105
|
-
span.set_attribute("lmnr.internal.has_browser_session", True)
|
106
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
107
80
|
|
108
81
|
def handle_page_navigation(page: SyncPage):
|
109
|
-
return handle_navigation_sync(page, session_id,
|
82
|
+
return handle_navigation_sync(page, session_id, client)
|
110
83
|
|
111
84
|
context.on(
|
112
85
|
"page",
|
@@ -114,7 +87,7 @@ def _wrap_new_browser_sync(
|
|
114
87
|
)
|
115
88
|
|
116
89
|
for page in context.pages:
|
117
|
-
handle_navigation_sync(page, session_id,
|
90
|
+
handle_navigation_sync(page, session_id, client)
|
118
91
|
return browser
|
119
92
|
|
120
93
|
|
@@ -122,26 +95,16 @@ def _wrap_new_browser_sync(
|
|
122
95
|
async def _wrap_new_browser_async(
|
123
96
|
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
124
97
|
):
|
125
|
-
global _context_spans
|
126
98
|
browser: Browser = await wrapped(*args, **kwargs)
|
127
99
|
session_id = str(uuid.uuid4().hex)
|
128
100
|
for context in browser.contexts:
|
129
|
-
span = get_current_span()
|
130
|
-
if span == INVALID_SPAN:
|
131
|
-
span = tracer.start_span(
|
132
|
-
name=f"{to_wrap.get('object')}.{to_wrap.get('method')}"
|
133
|
-
)
|
134
|
-
set_span_in_context(span, get_current())
|
135
|
-
_context_spans[id(context)] = span
|
136
|
-
span.set_attribute("lmnr.internal.has_browser_session", True)
|
137
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
138
101
|
|
139
102
|
async def handle_page_navigation(page: Page):
|
140
|
-
return await handle_navigation_async(page, session_id,
|
103
|
+
return await handle_navigation_async(page, session_id, client)
|
141
104
|
|
142
105
|
context.on("page", handle_page_navigation)
|
143
106
|
for page in context.pages:
|
144
|
-
await handle_navigation_async(page, session_id,
|
107
|
+
await handle_navigation_async(page, session_id, client)
|
145
108
|
return browser
|
146
109
|
|
147
110
|
|
@@ -151,25 +114,16 @@ def _wrap_new_context_sync(
|
|
151
114
|
):
|
152
115
|
context: SyncBrowserContext = wrapped(*args, **kwargs)
|
153
116
|
session_id = str(uuid.uuid4().hex)
|
154
|
-
span = get_current_span()
|
155
|
-
if span == INVALID_SPAN:
|
156
|
-
span = tracer.start_span(
|
157
|
-
name=f"{to_wrap.get('object')}.{to_wrap.get('method')}"
|
158
|
-
)
|
159
|
-
set_span_in_context(span, get_current())
|
160
|
-
_context_spans[id(context)] = span
|
161
|
-
span.set_attribute("lmnr.internal.has_browser_session", True)
|
162
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
163
117
|
|
164
118
|
def handle_page_navigation(page: SyncPage):
|
165
|
-
return handle_navigation_sync(page, session_id,
|
119
|
+
return handle_navigation_sync(page, session_id, client)
|
166
120
|
|
167
121
|
context.on(
|
168
122
|
"page",
|
169
123
|
handle_page_navigation,
|
170
124
|
)
|
171
125
|
for page in context.pages:
|
172
|
-
handle_navigation_sync(page, session_id,
|
126
|
+
handle_navigation_sync(page, session_id, client)
|
173
127
|
return context
|
174
128
|
|
175
129
|
|
@@ -179,67 +133,16 @@ async def _wrap_new_context_async(
|
|
179
133
|
):
|
180
134
|
context: BrowserContext = await wrapped(*args, **kwargs)
|
181
135
|
session_id = str(uuid.uuid4().hex)
|
182
|
-
span = get_current_span()
|
183
|
-
if span == INVALID_SPAN:
|
184
|
-
span = tracer.start_span(
|
185
|
-
name=f"{to_wrap.get('object')}.{to_wrap.get('method')}"
|
186
|
-
)
|
187
|
-
set_span_in_context(span, get_current())
|
188
|
-
_context_spans[id(context)] = span
|
189
|
-
span.set_attribute("lmnr.internal.has_browser_session", True)
|
190
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
191
136
|
|
192
137
|
async def handle_page_navigation(page):
|
193
|
-
return await handle_navigation_async(page, session_id,
|
138
|
+
return await handle_navigation_async(page, session_id, client)
|
194
139
|
|
195
140
|
context.on("page", handle_page_navigation)
|
196
141
|
for page in context.pages:
|
197
|
-
await handle_navigation_async(page, session_id,
|
142
|
+
await handle_navigation_async(page, session_id, client)
|
198
143
|
return context
|
199
144
|
|
200
145
|
|
201
|
-
@with_tracer_and_client_wrapper
|
202
|
-
def _wrap_close_browser_sync(
|
203
|
-
tracer: Tracer,
|
204
|
-
client: LaminarClient,
|
205
|
-
to_wrap,
|
206
|
-
wrapped,
|
207
|
-
instance: SyncBrowser,
|
208
|
-
args,
|
209
|
-
kwargs,
|
210
|
-
):
|
211
|
-
global _context_spans
|
212
|
-
for context in instance.contexts:
|
213
|
-
key = id(context)
|
214
|
-
span = _context_spans.get(key)
|
215
|
-
if span:
|
216
|
-
if span.is_recording():
|
217
|
-
span.end()
|
218
|
-
_context_spans.pop(key)
|
219
|
-
return wrapped(*args, **kwargs)
|
220
|
-
|
221
|
-
|
222
|
-
@with_tracer_and_client_wrapper
|
223
|
-
async def _wrap_close_browser_async(
|
224
|
-
tracer: Tracer,
|
225
|
-
client: AsyncLaminarClient,
|
226
|
-
to_wrap,
|
227
|
-
wrapped,
|
228
|
-
instance: Browser,
|
229
|
-
args,
|
230
|
-
kwargs,
|
231
|
-
):
|
232
|
-
global _context_spans
|
233
|
-
for context in instance.contexts:
|
234
|
-
key = id(context)
|
235
|
-
span = _context_spans.get(key)
|
236
|
-
if span:
|
237
|
-
if span.is_recording():
|
238
|
-
span.end()
|
239
|
-
_context_spans.pop(key)
|
240
|
-
return await wrapped(*args, **kwargs)
|
241
|
-
|
242
|
-
|
243
146
|
WRAPPED_METHODS = [
|
244
147
|
{
|
245
148
|
"package": "playwright.sync_api",
|
@@ -271,12 +174,6 @@ WRAPPED_METHODS = [
|
|
271
174
|
"method": "connect_over_cdp",
|
272
175
|
"wrapper": _wrap_new_browser_sync,
|
273
176
|
},
|
274
|
-
{
|
275
|
-
"package": "playwright.sync_api",
|
276
|
-
"object": "Browser",
|
277
|
-
"method": "close",
|
278
|
-
"wrapper": _wrap_close_browser_sync,
|
279
|
-
},
|
280
177
|
{
|
281
178
|
"package": "playwright.sync_api",
|
282
179
|
"object": "Browser",
|
@@ -322,12 +219,6 @@ WRAPPED_METHODS_ASYNC = [
|
|
322
219
|
"method": "connect_over_cdp",
|
323
220
|
"wrapper": _wrap_new_browser_async,
|
324
221
|
},
|
325
|
-
{
|
326
|
-
"package": "playwright.async_api",
|
327
|
-
"object": "Browser",
|
328
|
-
"method": "close",
|
329
|
-
"wrapper": _wrap_close_browser_async,
|
330
|
-
},
|
331
222
|
{
|
332
223
|
"package": "playwright.async_api",
|
333
224
|
"object": "Browser",
|
@@ -393,13 +284,8 @@ class PlaywrightInstrumentor(BaseInstrumentor):
|
|
393
284
|
|
394
285
|
def _uninstrument(self, **kwargs):
|
395
286
|
# Unwrap methods
|
396
|
-
global _context_spans
|
397
287
|
for wrapped_method in WRAPPED_METHODS + WRAPPED_METHODS_ASYNC:
|
398
288
|
wrap_package = wrapped_method.get("package")
|
399
289
|
wrap_object = wrapped_method.get("object")
|
400
290
|
wrap_method = wrapped_method.get("method")
|
401
291
|
unwrap(wrap_package, f"{wrap_object}.{wrap_method}")
|
402
|
-
for span in _context_spans.values():
|
403
|
-
if span.is_recording():
|
404
|
-
span.end()
|
405
|
-
_context_spans = {}
|
lmnr/sdk/browser/pw_utils.py
CHANGED
@@ -12,7 +12,6 @@ from lmnr.sdk.browser.utils import retry_sync, retry_async
|
|
12
12
|
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
13
13
|
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
14
14
|
|
15
|
-
|
16
15
|
try:
|
17
16
|
if is_package_installed("playwright"):
|
18
17
|
from playwright.async_api import Page
|
@@ -110,11 +109,15 @@ async def send_events_async(
|
|
110
109
|
await client._browser_events.send(session_id, trace_id, events)
|
111
110
|
except Exception as e:
|
112
111
|
if str(e).startswith("Page.evaluate: Execution context was destroyed"):
|
113
|
-
|
114
|
-
await inject_rrweb_async(page)
|
112
|
+
await inject_session_recorder_async(page)
|
115
113
|
await send_events_async(page, session_id, trace_id, client)
|
116
114
|
else:
|
117
|
-
|
115
|
+
# silence the error if the page has been closed, not an issue
|
116
|
+
if (
|
117
|
+
"Page.evaluate: Target page, context or browser has been closed"
|
118
|
+
not in str(e)
|
119
|
+
):
|
120
|
+
logger.warning(f"Could not send events: {e}")
|
118
121
|
|
119
122
|
|
120
123
|
def send_events_sync(
|
@@ -139,14 +142,18 @@ def send_events_sync(
|
|
139
142
|
|
140
143
|
except Exception as e:
|
141
144
|
if str(e).startswith("Page.evaluate: Execution context was destroyed"):
|
142
|
-
|
143
|
-
inject_rrweb_sync(page)
|
145
|
+
inject_session_recorder_sync(page)
|
144
146
|
send_events_sync(page, session_id, trace_id, client)
|
145
147
|
else:
|
146
|
-
|
148
|
+
# silence the error if the page has been closed, not an issue
|
149
|
+
if (
|
150
|
+
"Page.evaluate: Target page, context or browser has been closed"
|
151
|
+
not in str(e)
|
152
|
+
):
|
153
|
+
logger.warning(f"Could not send events: {e}")
|
147
154
|
|
148
155
|
|
149
|
-
def
|
156
|
+
def inject_session_recorder_sync(page: SyncPage):
|
150
157
|
try:
|
151
158
|
page.wait_for_load_state("domcontentloaded")
|
152
159
|
|
@@ -156,34 +163,36 @@ def inject_rrweb_sync(page: SyncPage):
|
|
156
163
|
"""() => typeof window.lmnrRrweb !== 'undefined'"""
|
157
164
|
)
|
158
165
|
except Exception as e:
|
159
|
-
logger.debug(f"Failed to check if
|
166
|
+
logger.debug(f"Failed to check if session recorder is loaded: {e}")
|
160
167
|
is_loaded = False
|
161
168
|
|
162
169
|
if not is_loaded:
|
163
170
|
|
164
|
-
def
|
171
|
+
def load_session_recorder():
|
165
172
|
try:
|
166
173
|
page.evaluate(RRWEB_CONTENT)
|
167
174
|
return True
|
168
175
|
except Exception as e:
|
169
|
-
logger.debug(f"Failed to load
|
176
|
+
logger.debug(f"Failed to load session recorder: {e}")
|
170
177
|
return False
|
171
178
|
|
172
179
|
if not retry_sync(
|
173
|
-
|
180
|
+
load_session_recorder,
|
181
|
+
delay=1,
|
182
|
+
error_message="Failed to load session recorder",
|
174
183
|
):
|
175
184
|
return
|
176
185
|
|
177
186
|
try:
|
178
187
|
page.evaluate(INJECT_PLACEHOLDER)
|
179
188
|
except Exception as e:
|
180
|
-
logger.debug(f"Failed to inject
|
189
|
+
logger.debug(f"Failed to inject session recorder: {e}")
|
181
190
|
|
182
191
|
except Exception as e:
|
183
|
-
logger.error(f"Error during
|
192
|
+
logger.error(f"Error during session recorder injection: {e}")
|
184
193
|
|
185
194
|
|
186
|
-
async def
|
195
|
+
async def inject_session_recorder_async(page: Page):
|
187
196
|
try:
|
188
197
|
await page.wait_for_load_state("domcontentloaded")
|
189
198
|
|
@@ -193,38 +202,40 @@ async def inject_rrweb_async(page: Page):
|
|
193
202
|
"""() => typeof window.lmnrRrweb !== 'undefined'"""
|
194
203
|
)
|
195
204
|
except Exception as e:
|
196
|
-
logger.debug(f"Failed to check if
|
205
|
+
logger.debug(f"Failed to check if session recorder is loaded: {e}")
|
197
206
|
is_loaded = False
|
198
207
|
|
199
208
|
if not is_loaded:
|
200
209
|
|
201
|
-
async def
|
210
|
+
async def load_session_recorder():
|
202
211
|
try:
|
203
212
|
await page.evaluate(RRWEB_CONTENT)
|
204
213
|
return True
|
205
214
|
except Exception as e:
|
206
|
-
logger.debug(f"Failed to load
|
215
|
+
logger.debug(f"Failed to load session recorder: {e}")
|
207
216
|
return False
|
208
217
|
|
209
218
|
if not await retry_async(
|
210
|
-
|
219
|
+
load_session_recorder,
|
220
|
+
delay=1,
|
221
|
+
error_message="Failed to load session recorder",
|
211
222
|
):
|
212
223
|
return
|
213
224
|
|
214
225
|
try:
|
215
226
|
await page.evaluate(INJECT_PLACEHOLDER)
|
216
227
|
except Exception as e:
|
217
|
-
logger.debug(f"Failed to inject
|
228
|
+
logger.debug(f"Failed to inject session recorder placeholder: {e}")
|
218
229
|
|
219
230
|
except Exception as e:
|
220
|
-
logger.error(f"Error during
|
231
|
+
logger.error(f"Error during session recorder injection: {e}")
|
221
232
|
|
222
233
|
|
223
234
|
@observe(name="playwright.page", ignore_input=True, ignore_output=True)
|
224
|
-
def handle_navigation_sync(
|
225
|
-
|
226
|
-
)
|
227
|
-
|
235
|
+
def handle_navigation_sync(page: SyncPage, session_id: str, client: LaminarClient):
|
236
|
+
span = trace.get_current_span()
|
237
|
+
trace_id = format(span.get_span_context().trace_id, "032x")
|
238
|
+
span.set_attribute("lmnr.internal.has_browser_session", True)
|
228
239
|
original_bring_to_front = page.bring_to_front
|
229
240
|
|
230
241
|
def bring_to_front():
|
@@ -245,7 +256,7 @@ def handle_navigation_sync(
|
|
245
256
|
|
246
257
|
def on_load():
|
247
258
|
try:
|
248
|
-
|
259
|
+
inject_session_recorder_sync(page)
|
249
260
|
except Exception as e:
|
250
261
|
logger.error(f"Error in on_load handler: {e}")
|
251
262
|
|
@@ -266,14 +277,17 @@ def handle_navigation_sync(
|
|
266
277
|
|
267
278
|
page.on("load", on_load)
|
268
279
|
page.on("close", on_close)
|
269
|
-
|
280
|
+
inject_session_recorder_sync(page)
|
270
281
|
|
271
282
|
|
272
283
|
@observe(name="playwright.page", ignore_input=True, ignore_output=True)
|
273
284
|
async def handle_navigation_async(
|
274
|
-
page: Page, session_id: str,
|
285
|
+
page: Page, session_id: str, client: AsyncLaminarClient
|
275
286
|
):
|
276
|
-
|
287
|
+
|
288
|
+
span = trace.get_current_span()
|
289
|
+
trace_id = format(span.get_span_context().trace_id, "032x")
|
290
|
+
span.set_attribute("lmnr.internal.has_browser_session", True)
|
277
291
|
|
278
292
|
async def collection_loop():
|
279
293
|
try:
|
@@ -289,7 +303,7 @@ async def handle_navigation_async(
|
|
289
303
|
|
290
304
|
async def on_load():
|
291
305
|
try:
|
292
|
-
await
|
306
|
+
await inject_session_recorder_async(page)
|
293
307
|
except Exception as e:
|
294
308
|
logger.error(f"Error in on_load handler: {e}")
|
295
309
|
|
@@ -321,4 +335,4 @@ async def handle_navigation_async(
|
|
321
335
|
)
|
322
336
|
|
323
337
|
page.bring_to_front = bring_to_front
|
324
|
-
await
|
338
|
+
await inject_session_recorder_async(page)
|
File without changes
|
lmnr/sdk/browser/utils.py
CHANGED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
lmnr/sdk/datasets.py
CHANGED
File without changes
|
lmnr/sdk/decorators.py
CHANGED
File without changes
|
lmnr/sdk/eval_control.py
CHANGED
File without changes
|
lmnr/sdk/evaluations.py
CHANGED
File without changes
|
lmnr/sdk/laminar.py
CHANGED
File without changes
|
lmnr/sdk/log.py
CHANGED
File without changes
|
lmnr/sdk/types.py
CHANGED
File without changes
|
lmnr/sdk/utils.py
CHANGED
File without changes
|