lmnr 0.5.2__tar.gz → 0.5.3__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.5.2 → lmnr-0.5.3}/PKG-INFO +2 -2
- {lmnr-0.5.2 → lmnr-0.5.3}/README.md +1 -1
- {lmnr-0.5.2 → lmnr-0.5.3}/pyproject.toml +4 -1
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/__init__.py +2 -2
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/cli.py +10 -8
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/__init__.py +3 -3
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/decorators/base.py +5 -5
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/instruments.py +1 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/opentelemetry/instrumentation/google_genai/utils.py +1 -1
- lmnr-0.5.3/src/lmnr/opentelemetry_lib/tracing/__init__.py +1 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/tracing/context_manager.py +1 -1
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/tracing/tracing.py +23 -5
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/browser/browser_use_otel.py +20 -3
- lmnr-0.5.3/src/lmnr/sdk/browser/patchright_otel.py +177 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/browser/playwright_otel.py +16 -7
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/browser/pw_utils.py +118 -80
- lmnr-0.5.3/src/lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +98 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/asynchronous/resources/agent.py +19 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/synchronous/resources/agent.py +20 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/decorators.py +3 -3
- lmnr-0.5.3/src/lmnr/sdk/eval_control.py +5 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/evaluations.py +8 -14
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/laminar.py +8 -8
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/types.py +2 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/version.py +1 -1
- lmnr-0.5.2/src/lmnr/openllmetry_sdk/tracing/__init__.py +0 -1
- lmnr-0.5.2/src/lmnr/sdk/browser/rrweb/rrweb.min.js +0 -18
- lmnr-0.5.2/src/lmnr/sdk/eval_control.py +0 -4
- {lmnr-0.5.2 → lmnr-0.5.3}/LICENSE +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/.flake8 +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/config/__init__.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/decorators/__init__.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/opentelemetry/instrumentation/google_genai/__init__.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/opentelemetry/instrumentation/google_genai/config.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/tracing/attributes.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/tracing/content_allow_list.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/utils/__init__.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/utils/in_memory_span_exporter.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/utils/json_encoder.py +0 -0
- {lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/utils/package_check.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/py.typed +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/browser/__init__.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/browser/utils.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/asynchronous/async_client.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/asynchronous/resources/base.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/asynchronous/resources/browser_events.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/asynchronous/resources/evals.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/synchronous/resources/base.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/synchronous/resources/evals.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/client/synchronous/sync_client.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/datasets.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.5.2 → lmnr-0.5.3}/src/lmnr/sdk/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.3
|
4
4
|
Summary: Python SDK for Laminar
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -222,7 +222,7 @@ def handle_user_request(topic: str):
|
|
222
222
|
Laminar allows you to automatically instrument majority of the most popular LLM, Vector DB, database, requests, and other libraries.
|
223
223
|
|
224
224
|
If you want to automatically instrument a default set of libraries, then simply do NOT pass `instruments` argument to `.initialize()`.
|
225
|
-
See the full list of available instrumentations in the [enum](https://github.com/lmnr-ai/lmnr-python/blob/main/src/lmnr/
|
225
|
+
See the full list of available instrumentations in the [enum](https://github.com/lmnr-ai/lmnr-python/blob/main/src/lmnr/opentelemetry_lib/instruments.py).
|
226
226
|
|
227
227
|
If you want to automatically instrument only specific LLM, Vector DB, or other
|
228
228
|
calls with OpenTelemetry-compatible instrumentation, then pass the appropriate instruments to `.initialize()`.
|
@@ -113,7 +113,7 @@ def handle_user_request(topic: str):
|
|
113
113
|
Laminar allows you to automatically instrument majority of the most popular LLM, Vector DB, database, requests, and other libraries.
|
114
114
|
|
115
115
|
If you want to automatically instrument a default set of libraries, then simply do NOT pass `instruments` argument to `.initialize()`.
|
116
|
-
See the full list of available instrumentations in the [enum](https://github.com/lmnr-ai/lmnr-python/blob/main/src/lmnr/
|
116
|
+
See the full list of available instrumentations in the [enum](https://github.com/lmnr-ai/lmnr-python/blob/main/src/lmnr/opentelemetry_lib/instruments.py).
|
117
117
|
|
118
118
|
If you want to automatically instrument only specific LLM, Vector DB, or other
|
119
119
|
calls with OpenTelemetry-compatible instrumentation, then pass the appropriate instruments to `.initialize()`.
|
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "lmnr"
|
9
|
-
version = "0.5.
|
9
|
+
version = "0.5.3"
|
10
10
|
description = "Python SDK for Laminar"
|
11
11
|
authors = [
|
12
12
|
{ name = "lmnr.ai", email = "founders@lmnr.ai" }
|
@@ -120,6 +120,9 @@ dev = [
|
|
120
120
|
[build-system]
|
121
121
|
requires = ["poetry-core"]
|
122
122
|
build-backend = "poetry.core.masonry.api"
|
123
|
+
|
124
|
+
[tool.uv.workspace]
|
125
|
+
members = ["examples/fastapi-app"]
|
123
126
|
# we can move to uv_build, once it's more stable
|
124
127
|
# https://github.com/astral-sh/uv/issues/3957
|
125
128
|
# requires = ["uv_build>=0.6.16,<0.7"]
|
@@ -13,8 +13,8 @@ from .sdk.types import (
|
|
13
13
|
)
|
14
14
|
from .sdk.decorators import observe
|
15
15
|
from .sdk.types import LaminarSpanContext
|
16
|
-
from .
|
17
|
-
from .
|
16
|
+
from .opentelemetry_lib import Instruments
|
17
|
+
from .opentelemetry_lib.tracing.attributes import Attributes
|
18
18
|
from opentelemetry.trace import use_span
|
19
19
|
|
20
20
|
__all__ = [
|
@@ -1,18 +1,17 @@
|
|
1
1
|
from argparse import ArgumentParser
|
2
2
|
import asyncio
|
3
3
|
import importlib.util
|
4
|
-
import logging
|
5
4
|
import os
|
6
5
|
import re
|
7
6
|
import sys
|
7
|
+
from typing import Optional
|
8
|
+
|
9
|
+
from lmnr.sdk.evaluations import Evaluation
|
8
10
|
|
9
11
|
from .sdk.eval_control import PREPARE_ONLY, EVALUATION_INSTANCE
|
10
|
-
from .sdk.log import
|
12
|
+
from .sdk.log import get_default_logger
|
11
13
|
|
12
|
-
LOG =
|
13
|
-
console_log_handler = logging.StreamHandler()
|
14
|
-
console_log_handler.setFormatter(ColorfulFormatter())
|
15
|
-
LOG.addHandler(console_log_handler)
|
14
|
+
LOG = get_default_logger(__name__)
|
16
15
|
|
17
16
|
|
18
17
|
EVAL_DIR = "evals"
|
@@ -28,7 +27,10 @@ async def run_evaluation(args):
|
|
28
27
|
if re.match(r".*_eval\.py$", f) or re.match(r"eval_.*\.py$", f)
|
29
28
|
]
|
30
29
|
if len(files) == 0:
|
31
|
-
LOG.error("No evaluation files found in evals directory")
|
30
|
+
LOG.error("No evaluation files found in `evals` directory")
|
31
|
+
LOG.info(
|
32
|
+
"Eval files must be located in the `evals` directory and must be named *_eval.py or eval_*.py"
|
33
|
+
)
|
32
34
|
return
|
33
35
|
files.sort()
|
34
36
|
LOG.info(f"Located {len(files)} evaluation files in {EVAL_DIR}")
|
@@ -53,7 +55,7 @@ async def run_evaluation(args):
|
|
53
55
|
sys.modules[name] = mod
|
54
56
|
|
55
57
|
spec.loader.exec_module(mod)
|
56
|
-
evaluation = EVALUATION_INSTANCE.get()
|
58
|
+
evaluation: Optional[Evaluation] = EVALUATION_INSTANCE.get()
|
57
59
|
if evaluation is None:
|
58
60
|
LOG.warning("Evaluation instance not found")
|
59
61
|
if args.fail_on_error:
|
@@ -7,12 +7,12 @@ from opentelemetry.sdk.resources import SERVICE_NAME
|
|
7
7
|
from opentelemetry.propagators.textmap import TextMapPropagator
|
8
8
|
from opentelemetry.util.re import parse_env_headers
|
9
9
|
|
10
|
-
from lmnr.
|
11
|
-
from lmnr.
|
10
|
+
from lmnr.opentelemetry_lib.instruments import Instruments
|
11
|
+
from lmnr.opentelemetry_lib.config import (
|
12
12
|
is_content_tracing_enabled,
|
13
13
|
is_tracing_enabled,
|
14
14
|
)
|
15
|
-
from lmnr.
|
15
|
+
from lmnr.opentelemetry_lib.tracing.tracing import TracerWrapper
|
16
16
|
from typing import Dict
|
17
17
|
|
18
18
|
|
{lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/decorators/base.py
RENAMED
@@ -10,11 +10,11 @@ from opentelemetry import context as context_api
|
|
10
10
|
from opentelemetry.trace import Span
|
11
11
|
|
12
12
|
from lmnr.sdk.utils import get_input_from_func_args, is_method
|
13
|
-
from lmnr.
|
14
|
-
from lmnr.
|
15
|
-
from lmnr.
|
16
|
-
from lmnr.
|
17
|
-
from lmnr.
|
13
|
+
from lmnr.opentelemetry_lib.tracing import get_tracer
|
14
|
+
from lmnr.opentelemetry_lib.tracing.attributes import SPAN_INPUT, SPAN_OUTPUT, SPAN_TYPE
|
15
|
+
from lmnr.opentelemetry_lib.tracing.tracing import TracerWrapper
|
16
|
+
from lmnr.opentelemetry_lib.utils.json_encoder import JSONEncoder
|
17
|
+
from lmnr.opentelemetry_lib.config import MAX_MANUAL_SPAN_PAYLOAD_SIZE
|
18
18
|
|
19
19
|
|
20
20
|
class CustomJSONEncoder(JSONEncoder):
|
@@ -0,0 +1 @@
|
|
1
|
+
from lmnr.opentelemetry_lib.tracing.context_manager import get_tracer
|
{lmnr-0.5.2/src/lmnr/openllmetry_sdk → lmnr-0.5.3/src/lmnr/opentelemetry_lib}/tracing/tracing.py
RENAMED
@@ -7,8 +7,8 @@ from contextvars import Context
|
|
7
7
|
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
8
8
|
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
9
9
|
from lmnr.sdk.log import VerboseColorfulFormatter
|
10
|
-
from lmnr.
|
11
|
-
from lmnr.
|
10
|
+
from lmnr.opentelemetry_lib.instruments import Instruments
|
11
|
+
from lmnr.opentelemetry_lib.tracing.attributes import (
|
12
12
|
ASSOCIATION_PROPERTIES,
|
13
13
|
SPAN_IDS_PATH,
|
14
14
|
SPAN_INSTRUMENTATION_SOURCE,
|
@@ -17,9 +17,9 @@ from lmnr.openllmetry_sdk.tracing.attributes import (
|
|
17
17
|
SPAN_PATH,
|
18
18
|
TRACING_LEVEL,
|
19
19
|
)
|
20
|
-
from lmnr.
|
21
|
-
from lmnr.
|
22
|
-
from lmnr.
|
20
|
+
from lmnr.opentelemetry_lib.tracing.content_allow_list import ContentAllowList
|
21
|
+
from lmnr.opentelemetry_lib.utils import is_notebook
|
22
|
+
from lmnr.opentelemetry_lib.utils.package_check import is_package_installed
|
23
23
|
from opentelemetry import trace
|
24
24
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
25
25
|
OTLPSpanExporter as HTTPExporter,
|
@@ -455,6 +455,9 @@ def init_instrumentations(
|
|
455
455
|
elif instrument == Instruments.PLAYWRIGHT:
|
456
456
|
if init_playwright_instrumentor(client, async_client):
|
457
457
|
instrument_set = True
|
458
|
+
elif instrument == Instruments.PATCHRIGHT:
|
459
|
+
if init_patchright_instrumentor(client, async_client):
|
460
|
+
instrument_set = True
|
458
461
|
elif instrument == Instruments.BROWSER_USE:
|
459
462
|
if init_browser_use_instrumentor():
|
460
463
|
instrument_set = True
|
@@ -499,6 +502,21 @@ def init_playwright_instrumentor(
|
|
499
502
|
return False
|
500
503
|
|
501
504
|
|
505
|
+
def init_patchright_instrumentor(
|
506
|
+
client: LaminarClient, async_client: AsyncLaminarClient
|
507
|
+
):
|
508
|
+
try:
|
509
|
+
if is_package_installed("patchright"):
|
510
|
+
from lmnr.sdk.browser.patchright_otel import PatchrightInstrumentor
|
511
|
+
|
512
|
+
instrumentor = PatchrightInstrumentor(client, async_client)
|
513
|
+
instrumentor.instrument()
|
514
|
+
return True
|
515
|
+
except Exception as e:
|
516
|
+
module_logger.error(f"Error initializing patchright instrumentor: {e}")
|
517
|
+
return False
|
518
|
+
|
519
|
+
|
502
520
|
def init_openai_instrumentor(should_enrich_metrics: bool):
|
503
521
|
try:
|
504
522
|
if is_package_installed("openai") and is_package_installed(
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from lmnr.
|
1
|
+
from lmnr.opentelemetry_lib.decorators.base import json_dumps
|
2
2
|
from lmnr.sdk.browser.utils import with_tracer_wrapper
|
3
3
|
from lmnr.sdk.utils import get_input_from_func_args
|
4
4
|
from lmnr.version import __version__
|
@@ -8,6 +8,16 @@ from opentelemetry.instrumentation.utils import unwrap
|
|
8
8
|
from opentelemetry.trace import get_tracer, Tracer
|
9
9
|
from typing import Collection
|
10
10
|
from wrapt import wrap_function_wrapper
|
11
|
+
import pydantic
|
12
|
+
|
13
|
+
try:
|
14
|
+
from browser_use import AgentHistoryList
|
15
|
+
except ImportError as e:
|
16
|
+
raise ImportError(
|
17
|
+
f"Attempted to import {__file__}, but it is designed "
|
18
|
+
"to patch Browser Use, which is not installed. Use `pip install browser-use` "
|
19
|
+
"to install Browser Use or remove this import."
|
20
|
+
) from e
|
11
21
|
|
12
22
|
_instruments = ("browser-use >= 0.1.0",)
|
13
23
|
|
@@ -18,7 +28,7 @@ WRAPPED_METHODS = [
|
|
18
28
|
"method": "run",
|
19
29
|
"span_name": "agent.run",
|
20
30
|
"ignore_input": False,
|
21
|
-
"ignore_output":
|
31
|
+
"ignore_output": False,
|
22
32
|
"span_type": "DEFAULT",
|
23
33
|
},
|
24
34
|
{
|
@@ -73,7 +83,14 @@ async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
73
83
|
span.set_attributes(attributes)
|
74
84
|
result = await wrapped(*args, **kwargs)
|
75
85
|
if not to_wrap.get("ignore_output"):
|
76
|
-
|
86
|
+
if isinstance(result, AgentHistoryList):
|
87
|
+
result = result.final_result()
|
88
|
+
serialized = (
|
89
|
+
result.model_dump_json()
|
90
|
+
if isinstance(result, pydantic.BaseModel)
|
91
|
+
else json_dumps(result)
|
92
|
+
)
|
93
|
+
span.set_attribute("lmnr.span.output", serialized)
|
77
94
|
return result
|
78
95
|
|
79
96
|
|
@@ -0,0 +1,177 @@
|
|
1
|
+
from lmnr.sdk.browser.playwright_otel import (
|
2
|
+
_wrap_new_page,
|
3
|
+
_wrap_new_page_async,
|
4
|
+
_wrap_new_browser_sync,
|
5
|
+
_wrap_new_browser_async,
|
6
|
+
_wrap_new_context_sync,
|
7
|
+
_wrap_new_context_async,
|
8
|
+
_wrap_close_browser_sync,
|
9
|
+
_wrap_close_browser_async,
|
10
|
+
)
|
11
|
+
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
12
|
+
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
13
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
14
|
+
from opentelemetry.instrumentation.utils import unwrap
|
15
|
+
from opentelemetry.trace import get_tracer
|
16
|
+
from lmnr.version import __version__
|
17
|
+
from typing import Collection
|
18
|
+
from wrapt import wrap_function_wrapper
|
19
|
+
|
20
|
+
_instruments = ("patchright >= 1.9.0",)
|
21
|
+
|
22
|
+
WRAPPED_METHODS = [
|
23
|
+
{
|
24
|
+
"package": "patchright.sync_api",
|
25
|
+
"object": "BrowserContext",
|
26
|
+
"method": "new_page",
|
27
|
+
"wrapper": _wrap_new_page,
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"package": "patchright.sync_api",
|
31
|
+
"object": "Browser",
|
32
|
+
"method": "new_page",
|
33
|
+
"wrapper": _wrap_new_page,
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"package": "patchright.sync_api",
|
37
|
+
"object": "BrowserType",
|
38
|
+
"method": "launch",
|
39
|
+
"wrapper": _wrap_new_browser_sync,
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"package": "patchright.sync_api",
|
43
|
+
"object": "BrowserType",
|
44
|
+
"method": "connect",
|
45
|
+
"wrapper": _wrap_new_browser_sync,
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"package": "patchright.sync_api",
|
49
|
+
"object": "BrowserType",
|
50
|
+
"method": "connect_over_cdp",
|
51
|
+
"wrapper": _wrap_new_browser_sync,
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"package": "patchright.sync_api",
|
55
|
+
"object": "Browser",
|
56
|
+
"method": "close",
|
57
|
+
"wrapper": _wrap_close_browser_sync,
|
58
|
+
},
|
59
|
+
{
|
60
|
+
"package": "patchright.sync_api",
|
61
|
+
"object": "Browser",
|
62
|
+
"method": "new_context",
|
63
|
+
"wrapper": _wrap_new_context_sync,
|
64
|
+
},
|
65
|
+
{
|
66
|
+
"package": "patchright.sync_api",
|
67
|
+
"object": "BrowserType",
|
68
|
+
"method": "launch_persistent_context",
|
69
|
+
"wrapper": _wrap_new_context_sync,
|
70
|
+
},
|
71
|
+
]
|
72
|
+
|
73
|
+
WRAPPED_METHODS_ASYNC = [
|
74
|
+
{
|
75
|
+
"package": "patchright.async_api",
|
76
|
+
"object": "BrowserContext",
|
77
|
+
"method": "new_page",
|
78
|
+
"wrapper": _wrap_new_page_async,
|
79
|
+
},
|
80
|
+
{
|
81
|
+
"package": "patchright.async_api",
|
82
|
+
"object": "Browser",
|
83
|
+
"method": "new_page",
|
84
|
+
"wrapper": _wrap_new_page_async,
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"package": "patchright.async_api",
|
88
|
+
"object": "BrowserType",
|
89
|
+
"method": "launch",
|
90
|
+
"wrapper": _wrap_new_browser_async,
|
91
|
+
},
|
92
|
+
{
|
93
|
+
"package": "patchright.async_api",
|
94
|
+
"object": "BrowserType",
|
95
|
+
"method": "connect",
|
96
|
+
"wrapper": _wrap_new_browser_async,
|
97
|
+
},
|
98
|
+
{
|
99
|
+
"package": "patchright.async_api",
|
100
|
+
"object": "BrowserType",
|
101
|
+
"method": "connect_over_cdp",
|
102
|
+
"wrapper": _wrap_new_browser_async,
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"package": "patchright.async_api",
|
106
|
+
"object": "Browser",
|
107
|
+
"method": "close",
|
108
|
+
"wrapper": _wrap_close_browser_async,
|
109
|
+
},
|
110
|
+
{
|
111
|
+
"package": "patchright.async_api",
|
112
|
+
"object": "Browser",
|
113
|
+
"method": "new_context",
|
114
|
+
"wrapper": _wrap_new_context_async,
|
115
|
+
},
|
116
|
+
{
|
117
|
+
"package": "patchright.async_api",
|
118
|
+
"object": "BrowserType",
|
119
|
+
"method": "launch_persistent_context",
|
120
|
+
"wrapper": _wrap_new_context_async,
|
121
|
+
},
|
122
|
+
]
|
123
|
+
|
124
|
+
|
125
|
+
class PatchrightInstrumentor(BaseInstrumentor):
|
126
|
+
def __init__(self, client: LaminarClient, async_client: AsyncLaminarClient):
|
127
|
+
super().__init__()
|
128
|
+
self.client = client
|
129
|
+
self.async_client = async_client
|
130
|
+
|
131
|
+
def instrumentation_dependencies(self) -> Collection[str]:
|
132
|
+
return _instruments
|
133
|
+
|
134
|
+
def _instrument(self, **kwargs):
|
135
|
+
tracer_provider = kwargs.get("tracer_provider")
|
136
|
+
tracer = get_tracer(__name__, __version__, tracer_provider)
|
137
|
+
|
138
|
+
for wrapped_method in WRAPPED_METHODS:
|
139
|
+
wrap_package = wrapped_method.get("package")
|
140
|
+
wrap_object = wrapped_method.get("object")
|
141
|
+
wrap_method = wrapped_method.get("method")
|
142
|
+
try:
|
143
|
+
wrap_function_wrapper(
|
144
|
+
wrap_package,
|
145
|
+
f"{wrap_object}.{wrap_method}",
|
146
|
+
wrapped_method.get("wrapper")(
|
147
|
+
tracer,
|
148
|
+
self.client,
|
149
|
+
wrapped_method,
|
150
|
+
),
|
151
|
+
)
|
152
|
+
except ModuleNotFoundError:
|
153
|
+
pass
|
154
|
+
|
155
|
+
for wrapped_method in WRAPPED_METHODS_ASYNC:
|
156
|
+
wrap_package = wrapped_method.get("package")
|
157
|
+
wrap_object = wrapped_method.get("object")
|
158
|
+
wrap_method = wrapped_method.get("method")
|
159
|
+
try:
|
160
|
+
wrap_function_wrapper(
|
161
|
+
wrap_package,
|
162
|
+
f"{wrap_object}.{wrap_method}",
|
163
|
+
wrapped_method.get("wrapper")(
|
164
|
+
tracer,
|
165
|
+
self.async_client,
|
166
|
+
wrapped_method,
|
167
|
+
),
|
168
|
+
)
|
169
|
+
except ModuleNotFoundError:
|
170
|
+
pass
|
171
|
+
|
172
|
+
def _uninstrument(self, **kwargs):
|
173
|
+
for wrapped_method in WRAPPED_METHODS + WRAPPED_METHODS_ASYNC:
|
174
|
+
wrap_package = wrapped_method.get("package")
|
175
|
+
wrap_object = wrapped_method.get("object")
|
176
|
+
wrap_method = wrapped_method.get("method")
|
177
|
+
unwrap(wrap_package, f"{wrap_object}.{wrap_method}")
|
@@ -22,10 +22,11 @@ from typing import Collection
|
|
22
22
|
from wrapt import wrap_function_wrapper
|
23
23
|
|
24
24
|
try:
|
25
|
-
from playwright.async_api import Browser, BrowserContext
|
25
|
+
from playwright.async_api import Browser, BrowserContext, Page
|
26
26
|
from playwright.sync_api import (
|
27
27
|
Browser as SyncBrowser,
|
28
28
|
BrowserContext as SyncBrowserContext,
|
29
|
+
Page as SyncPage,
|
29
30
|
)
|
30
31
|
except ImportError as e:
|
31
32
|
raise ImportError(
|
@@ -88,10 +89,15 @@ def _wrap_new_browser_sync(
|
|
88
89
|
_context_spans[id(context)] = span
|
89
90
|
span.set_attribute("lmnr.internal.has_browser_session", True)
|
90
91
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
92
|
+
|
93
|
+
def handle_page_navigation(page: SyncPage):
|
94
|
+
return handle_navigation_sync(page, session_id, trace_id, client)
|
95
|
+
|
91
96
|
context.on(
|
92
97
|
"page",
|
93
|
-
|
98
|
+
handle_page_navigation,
|
94
99
|
)
|
100
|
+
|
95
101
|
for page in context.pages:
|
96
102
|
handle_navigation_sync(page, session_id, trace_id, client)
|
97
103
|
return browser
|
@@ -115,7 +121,7 @@ async def _wrap_new_browser_async(
|
|
115
121
|
span.set_attribute("lmnr.internal.has_browser_session", True)
|
116
122
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
117
123
|
|
118
|
-
async def handle_page_navigation(page):
|
124
|
+
async def handle_page_navigation(page: Page):
|
119
125
|
return await handle_navigation_async(page, session_id, trace_id, client)
|
120
126
|
|
121
127
|
context.on("page", handle_page_navigation)
|
@@ -140,9 +146,12 @@ def _wrap_new_context_sync(
|
|
140
146
|
span.set_attribute("lmnr.internal.has_browser_session", True)
|
141
147
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
142
148
|
|
149
|
+
def handle_page_navigation(page: SyncPage):
|
150
|
+
return handle_navigation_sync(page, session_id, trace_id, client)
|
151
|
+
|
143
152
|
context.on(
|
144
153
|
"page",
|
145
|
-
|
154
|
+
handle_page_navigation,
|
146
155
|
)
|
147
156
|
for page in context.pages:
|
148
157
|
handle_navigation_sync(page, session_id, trace_id, client)
|
@@ -153,7 +162,7 @@ def _wrap_new_context_sync(
|
|
153
162
|
async def _wrap_new_context_async(
|
154
163
|
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
155
164
|
):
|
156
|
-
context:
|
165
|
+
context: BrowserContext = await wrapped(*args, **kwargs)
|
157
166
|
session_id = str(uuid.uuid4().hex)
|
158
167
|
span = get_current_span()
|
159
168
|
if span == INVALID_SPAN:
|
@@ -311,10 +320,10 @@ WRAPPED_METHODS_ASYNC = [
|
|
311
320
|
"wrapper": _wrap_new_context_async,
|
312
321
|
},
|
313
322
|
{
|
314
|
-
"package": "playwright.
|
323
|
+
"package": "playwright.async_api",
|
315
324
|
"object": "BrowserType",
|
316
325
|
"method": "launch_persistent_context",
|
317
|
-
"wrapper":
|
326
|
+
"wrapper": _wrap_new_context_async,
|
318
327
|
},
|
319
328
|
]
|
320
329
|
|