lmnr 0.6.15__py3-none-any.whl → 0.6.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py +193 -0
- lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +8 -0
- lmnr/opentelemetry_lib/tracing/instruments.py +2 -1
- lmnr/sdk/browser/browser_use_otel.py +10 -3
- lmnr/version.py +1 -1
- {lmnr-0.6.15.dist-info → lmnr-0.6.17.dist-info}/METADATA +3 -4
- {lmnr-0.6.15.dist-info → lmnr-0.6.17.dist-info}/RECORD +10 -9
- {lmnr-0.6.15.dist-info → lmnr-0.6.17.dist-info}/LICENSE +0 -0
- {lmnr-0.6.15.dist-info → lmnr-0.6.17.dist-info}/WHEEL +0 -0
- {lmnr-0.6.15.dist-info → lmnr-0.6.17.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
from lmnr.opentelemetry_lib.decorators import json_dumps
|
2
|
+
from lmnr.sdk.browser.utils import with_tracer_wrapper
|
3
|
+
from lmnr.sdk.utils import get_input_from_func_args
|
4
|
+
from lmnr.version import __version__
|
5
|
+
|
6
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
7
|
+
from opentelemetry.instrumentation.utils import unwrap
|
8
|
+
from opentelemetry.trace import get_tracer, Tracer
|
9
|
+
from typing import Collection
|
10
|
+
from wrapt import wrap_function_wrapper
|
11
|
+
import pydantic
|
12
|
+
|
13
|
+
try:
|
14
|
+
from skyvern import Skyvern
|
15
|
+
except ImportError as e:
|
16
|
+
raise ImportError(
|
17
|
+
f"Attempted to import {__file__}, but it is designed "
|
18
|
+
"to patch Skyvern, which is not installed. Use `pip install skyvern` "
|
19
|
+
"to install Skyvern or remove this import."
|
20
|
+
) from e
|
21
|
+
|
22
|
+
_instruments = ("skyvern >= 0.1.0",)
|
23
|
+
|
24
|
+
WRAPPED_METHODS = [
|
25
|
+
{
|
26
|
+
"package": "skyvern.library.skyvern",
|
27
|
+
"object": "Skyvern", # Class name
|
28
|
+
"method": "run_task", # Method name
|
29
|
+
"span_name": "Skyvern.run_task",
|
30
|
+
"span_type": "DEFAULT",
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"package": "skyvern.webeye.scraper.scraper",
|
34
|
+
# No "object" field for module-level functions
|
35
|
+
"method": "get_interactable_element_tree", # Function name
|
36
|
+
"span_name": "get_interactable_element_tree",
|
37
|
+
"span_type": "DEFAULT",
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"package": "skyvern.forge.agent",
|
41
|
+
"object": "ForgeAgent",
|
42
|
+
"method": "execute_step",
|
43
|
+
"span_name": "ForgeAgent.execute_step",
|
44
|
+
"span_type": "DEFAULT",
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"package": "skyvern.services.task_v2_service",
|
48
|
+
"method": "initialize_task_v2",
|
49
|
+
"span_name": "initialize_task_v2",
|
50
|
+
"span_type": "DEFAULT",
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"package": "skyvern.services.task_v2_service",
|
54
|
+
"method": "run_task_v2_helper",
|
55
|
+
"span_name": "run_task_v2_helper",
|
56
|
+
"span_type": "DEFAULT",
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"package": "skyvern.forge.sdk.workflow.models.block",
|
60
|
+
"object": "Block",
|
61
|
+
"method": "_generate_workflow_run_block_description",
|
62
|
+
"span_name": "Block._generate_workflow_run_block_description",
|
63
|
+
"span_type": "DEFAULT",
|
64
|
+
},
|
65
|
+
{
|
66
|
+
"package": "skyvern.webeye.actions.handler",
|
67
|
+
"method": "extract_information_for_navigation_goal",
|
68
|
+
"span_name": "extract_information_for_navigation_goal",
|
69
|
+
"span_type": "DEFAULT",
|
70
|
+
},
|
71
|
+
]
|
72
|
+
|
73
|
+
|
74
|
+
@with_tracer_wrapper
|
75
|
+
async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
76
|
+
span_name = to_wrap.get("span_name")
|
77
|
+
attributes = {
|
78
|
+
"lmnr.span.type": to_wrap.get("span_type"),
|
79
|
+
}
|
80
|
+
|
81
|
+
attributes["lmnr.span.input"] = json_dumps(
|
82
|
+
get_input_from_func_args(wrapped, True, args, kwargs)
|
83
|
+
)
|
84
|
+
|
85
|
+
with tracer.start_as_current_span(span_name, attributes=attributes) as span:
|
86
|
+
try:
|
87
|
+
result = await wrapped(*args, **kwargs)
|
88
|
+
|
89
|
+
to_serialize = result
|
90
|
+
serialized = (
|
91
|
+
to_serialize.model_dump_json()
|
92
|
+
if isinstance(to_serialize, pydantic.BaseModel)
|
93
|
+
else json_dumps(to_serialize)
|
94
|
+
)
|
95
|
+
span.set_attribute("lmnr.span.output", serialized)
|
96
|
+
return result
|
97
|
+
|
98
|
+
except Exception as e:
|
99
|
+
span.record_exception(e)
|
100
|
+
raise
|
101
|
+
|
102
|
+
|
103
|
+
def instrument_llm_handler(tracer: Tracer):
|
104
|
+
from skyvern.forge import app
|
105
|
+
|
106
|
+
# Store the original handler
|
107
|
+
original_handler = app.LLM_API_HANDLER
|
108
|
+
|
109
|
+
async def wrapped_llm_handler(*args, **kwargs):
|
110
|
+
|
111
|
+
prompt_name = kwargs.get("prompt_name", "")
|
112
|
+
|
113
|
+
if prompt_name:
|
114
|
+
span_name = f"{prompt_name}"
|
115
|
+
else:
|
116
|
+
span_name = "app.LLM_API_HANDLER"
|
117
|
+
|
118
|
+
attributes = {
|
119
|
+
"lmnr.span.type": "DEFAULT",
|
120
|
+
}
|
121
|
+
|
122
|
+
with tracer.start_as_current_span(span_name, attributes=attributes) as span:
|
123
|
+
try:
|
124
|
+
result = await original_handler(*args, **kwargs)
|
125
|
+
|
126
|
+
to_serialize = result
|
127
|
+
serialized = (
|
128
|
+
to_serialize.model_dump_json()
|
129
|
+
if isinstance(to_serialize, pydantic.BaseModel)
|
130
|
+
else json_dumps(to_serialize)
|
131
|
+
)
|
132
|
+
span.set_attribute("lmnr.span.output", serialized)
|
133
|
+
return result
|
134
|
+
except Exception as e:
|
135
|
+
span.record_exception(e)
|
136
|
+
raise
|
137
|
+
|
138
|
+
# Replace the global handler
|
139
|
+
app.LLM_API_HANDLER = wrapped_llm_handler
|
140
|
+
|
141
|
+
|
142
|
+
class SkyvernInstrumentor(BaseInstrumentor):
|
143
|
+
def __init__(self):
|
144
|
+
super().__init__()
|
145
|
+
|
146
|
+
def instrumentation_dependencies(self) -> Collection[str]:
|
147
|
+
return _instruments
|
148
|
+
|
149
|
+
def _instrument(self, **kwargs):
|
150
|
+
|
151
|
+
tracer_provider = kwargs.get("tracer_provider")
|
152
|
+
tracer = get_tracer(__name__, __version__, tracer_provider)
|
153
|
+
|
154
|
+
instrument_llm_handler(tracer)
|
155
|
+
|
156
|
+
for wrapped_method in WRAPPED_METHODS:
|
157
|
+
wrap_package = wrapped_method.get("package")
|
158
|
+
wrap_object = wrapped_method.get("object")
|
159
|
+
wrap_method = wrapped_method.get("method")
|
160
|
+
|
161
|
+
# For class methods: "Class.method", for module functions: just "function_name"
|
162
|
+
if wrap_object:
|
163
|
+
target = f"{wrap_object}.{wrap_method}"
|
164
|
+
else:
|
165
|
+
target = wrap_method
|
166
|
+
|
167
|
+
try:
|
168
|
+
wrap_function_wrapper(
|
169
|
+
wrap_package,
|
170
|
+
target,
|
171
|
+
_wrap(
|
172
|
+
tracer,
|
173
|
+
wrapped_method,
|
174
|
+
),
|
175
|
+
)
|
176
|
+
except ModuleNotFoundError:
|
177
|
+
pass # that's ok, we're not instrumenting everything
|
178
|
+
|
179
|
+
def _uninstrument(self, **kwargs):
|
180
|
+
|
181
|
+
for wrapped_method in WRAPPED_METHODS:
|
182
|
+
wrap_package = wrapped_method.get("package")
|
183
|
+
wrap_object = wrapped_method.get("object")
|
184
|
+
wrap_method = wrapped_method.get("method")
|
185
|
+
|
186
|
+
# For class methods: "package.Class", for module functions: just "package"
|
187
|
+
if wrap_object:
|
188
|
+
module_path = f"{wrap_package}.{wrap_object}"
|
189
|
+
else:
|
190
|
+
module_path = wrap_package
|
191
|
+
|
192
|
+
unwrap(module_path, wrap_method)
|
193
|
+
|
@@ -345,6 +345,14 @@ class SageMakerInstrumentorInitializer(InstrumentorInitializer):
|
|
345
345
|
|
346
346
|
return SageMakerInstrumentor()
|
347
347
|
|
348
|
+
class SkyvernInstrumentorInitializer(InstrumentorInitializer):
|
349
|
+
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
350
|
+
if not is_package_installed("skyvern"):
|
351
|
+
return None
|
352
|
+
|
353
|
+
from ..opentelemetry.instrumentation.skyvern import SkyvernInstrumentor
|
354
|
+
|
355
|
+
return SkyvernInstrumentor()
|
348
356
|
|
349
357
|
class TogetherInstrumentorInitializer(InstrumentorInitializer):
|
350
358
|
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
@@ -40,13 +40,13 @@ class Instruments(Enum):
|
|
40
40
|
QDRANT = "qdrant"
|
41
41
|
REPLICATE = "replicate"
|
42
42
|
SAGEMAKER = "sagemaker"
|
43
|
+
SKYVERN = "skyvern"
|
43
44
|
TOGETHER = "together"
|
44
45
|
TRANSFORMERS = "transformers"
|
45
46
|
VERTEXAI = "vertexai"
|
46
47
|
WATSONX = "watsonx"
|
47
48
|
WEAVIATE = "weaviate"
|
48
49
|
|
49
|
-
|
50
50
|
INSTRUMENTATION_INITIALIZERS: dict[
|
51
51
|
Instruments, initializers.InstrumentorInitializer
|
52
52
|
] = {
|
@@ -77,6 +77,7 @@ INSTRUMENTATION_INITIALIZERS: dict[
|
|
77
77
|
Instruments.QDRANT: initializers.QdrantInstrumentorInitializer(),
|
78
78
|
Instruments.REPLICATE: initializers.ReplicateInstrumentorInitializer(),
|
79
79
|
Instruments.SAGEMAKER: initializers.SageMakerInstrumentorInitializer(),
|
80
|
+
Instruments.SKYVERN: initializers.SkyvernInstrumentorInitializer(),
|
80
81
|
Instruments.TOGETHER: initializers.TogetherInstrumentorInitializer(),
|
81
82
|
Instruments.TRANSFORMERS: initializers.TransformersInstrumentorInitializer(),
|
82
83
|
Instruments.VERTEXAI: initializers.VertexAIInstrumentorInitializer(),
|
@@ -76,9 +76,16 @@ async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
76
76
|
)
|
77
77
|
else:
|
78
78
|
if not to_wrap.get("ignore_input"):
|
79
|
-
|
80
|
-
|
81
|
-
)
|
79
|
+
inp_dict = get_input_from_func_args(wrapped, True, args, kwargs)
|
80
|
+
# Add task to the `agent.run` span input
|
81
|
+
if to_wrap.get("method") == "run" and hasattr(instance, "task"):
|
82
|
+
inp_dict["task"] = instance.task
|
83
|
+
attributes["lmnr.span.input"] = json_dumps(inp_dict)
|
84
|
+
if to_wrap.get("method") == "step" and to_wrap.get("object") == "Agent":
|
85
|
+
# Add step number to the `agent.step` span name
|
86
|
+
step_info = kwargs.get("step_info", args[0] if len(args) > 0 else None)
|
87
|
+
if step_info and hasattr(step_info, "step_number"):
|
88
|
+
span_name = f"agent.step.{step_info.step_number}"
|
82
89
|
with tracer.start_as_current_span(span_name, attributes=attributes) as span:
|
83
90
|
span.set_attributes(attributes)
|
84
91
|
result = await wrapped(*args, **kwargs)
|
lmnr/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.6.
|
3
|
+
Version: 0.6.17
|
4
4
|
Summary: Python SDK for Laminar
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -40,7 +40,6 @@ Provides-Extra: transformers
|
|
40
40
|
Provides-Extra: vertexai
|
41
41
|
Provides-Extra: watsonx
|
42
42
|
Provides-Extra: weaviate
|
43
|
-
Requires-Dist: argparse (>=1.0)
|
44
43
|
Requires-Dist: grpcio (>=1)
|
45
44
|
Requires-Dist: httpx (>=0.25.0)
|
46
45
|
Requires-Dist: opentelemetry-api (>=1.33.0)
|
@@ -58,8 +57,8 @@ Requires-Dist: opentelemetry-instrumentation-cohere (>=0.40.12) ; extra == "all"
|
|
58
57
|
Requires-Dist: opentelemetry-instrumentation-cohere (>=0.40.12) ; extra == "cohere"
|
59
58
|
Requires-Dist: opentelemetry-instrumentation-crewai (>=0.40.12) ; extra == "all"
|
60
59
|
Requires-Dist: opentelemetry-instrumentation-crewai (>=0.40.12) ; extra == "crewai"
|
61
|
-
Requires-Dist: opentelemetry-instrumentation-google-generativeai (
|
62
|
-
Requires-Dist: opentelemetry-instrumentation-google-generativeai (
|
60
|
+
Requires-Dist: opentelemetry-instrumentation-google-generativeai (<0.40.10) ; extra == "all"
|
61
|
+
Requires-Dist: opentelemetry-instrumentation-google-generativeai (<0.40.10) ; extra == "google-generativeai"
|
63
62
|
Requires-Dist: opentelemetry-instrumentation-groq (>=0.40.12) ; extra == "all"
|
64
63
|
Requires-Dist: opentelemetry-instrumentation-groq (>=0.40.12) ; extra == "groq"
|
65
64
|
Requires-Dist: opentelemetry-instrumentation-haystack (>=0.40.12) ; extra == "all"
|
@@ -10,12 +10,13 @@ lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py,sha2
|
|
10
10
|
lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py,sha256=8SSBliRoJtiZME5RDEwt90CI2BadKPHQrtV4p6bDy_0,7669
|
11
11
|
lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py,sha256=Jyv9koZdGA4-oTaB7ATB7DaX7aNOY-3YOGL4wX0c7PM,3107
|
12
12
|
lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py,sha256=nf9sJZXnnts4gYZortEiDvwYjYqYJZTAT0zutuP_R6Y,1512
|
13
|
+
lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py,sha256=dk5P6Xn7CNeCFBmjzFw66JpmZLYm75KCWfjxdck56uo,6334
|
13
14
|
lmnr/opentelemetry_lib/tracing/__init__.py,sha256=27QogAe-aHyrVr6b56-DduUm0KEE24K1aV2e1nouTNg,6007
|
14
|
-
lmnr/opentelemetry_lib/tracing/_instrument_initializers.py,sha256=
|
15
|
+
lmnr/opentelemetry_lib/tracing/_instrument_initializers.py,sha256=WVmcVTL8SJPN4hvATqywM6nc9Sv7NzGMR7Ew7rWv2eg,14898
|
15
16
|
lmnr/opentelemetry_lib/tracing/attributes.py,sha256=MvowVluXfCqSIC3Cvx3tWDqB0Cpr9bpSlY91qL4Iy74,1497
|
16
17
|
lmnr/opentelemetry_lib/tracing/context_properties.py,sha256=aWbvMdWB4Q7uqc0GGSsjcRXMTcO18aWOaIZe3QyS_aA,2314
|
17
18
|
lmnr/opentelemetry_lib/tracing/exporter.py,sha256=avjmH9hz6PXbMV2bnx7b9GuGC6flAUDwvcxoZMbTWgM,2082
|
18
|
-
lmnr/opentelemetry_lib/tracing/instruments.py,sha256=
|
19
|
+
lmnr/opentelemetry_lib/tracing/instruments.py,sha256=3gL60IhFj1C2cvEEqZGFpP5rjjAJeYg9QePXLmKifus,5362
|
19
20
|
lmnr/opentelemetry_lib/tracing/processor.py,sha256=YJ_2j02oU_YZD8cuDltXkCU_fN1hcACI61X5qgCZd7s,3441
|
20
21
|
lmnr/opentelemetry_lib/tracing/tracer.py,sha256=oNC6V8eFvuK3i5IWXsKDjEMFL_axeSov3L1fPevwuWM,476
|
21
22
|
lmnr/opentelemetry_lib/utils/__init__.py,sha256=pNhf0G3vTd5ccoc03i1MXDbricSaiqCbi1DLWhSekK8,604
|
@@ -24,7 +25,7 @@ lmnr/opentelemetry_lib/utils/package_check.py,sha256=ST2BvDxt0bax-ih9F9Wf0jlKAEr
|
|
24
25
|
lmnr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
26
|
lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
27
|
lmnr/sdk/browser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
|
-
lmnr/sdk/browser/browser_use_otel.py,sha256=
|
28
|
+
lmnr/sdk/browser/browser_use_otel.py,sha256=L8fm_pOAi6XkuSED4hTIItzA_BUMYj7YvN7BgFsHZDA,5093
|
28
29
|
lmnr/sdk/browser/patchright_otel.py,sha256=O7n1dB_Mw-_L70zi0zqpnFFiKuTPEEpO7ry1ia6IXWQ,5454
|
29
30
|
lmnr/sdk/browser/playwright_otel.py,sha256=LFg1iJXbez-BEgEIY9eaO_2T2uR6SxyfFL46fHcS7Mg,13423
|
30
31
|
lmnr/sdk/browser/pw_utils.py,sha256=PPVdeE6RV-n7de3ApuEVqjTXrbkJxtGOsZ47Ce-Nna0,10307
|
@@ -52,9 +53,9 @@ lmnr/sdk/laminar.py,sha256=oOVco_c9ZstT71HsquGsgbtFumXd2Ejz0rl_qpmMlTU,33996
|
|
52
53
|
lmnr/sdk/log.py,sha256=nt_YMmPw1IRbGy0b7q4rTtP4Yo3pQfNxqJPXK3nDSNQ,2213
|
53
54
|
lmnr/sdk/types.py,sha256=ZQp5SeYJNZsK3KrbSeXPY_xn6mGjW5mSw_i0Rd_Oa4k,12328
|
54
55
|
lmnr/sdk/utils.py,sha256=yrcHIhoADf9lWH9qJWZMmkRWYvd0DuxPSLP3mY6YFw0,4327
|
55
|
-
lmnr/version.py,sha256=
|
56
|
-
lmnr-0.6.
|
57
|
-
lmnr-0.6.
|
58
|
-
lmnr-0.6.
|
59
|
-
lmnr-0.6.
|
60
|
-
lmnr-0.6.
|
56
|
+
lmnr/version.py,sha256=X2cJf0gdlak_gTkSX8K7hD1iv3rMr48NxBaEODOlzko,1322
|
57
|
+
lmnr-0.6.17.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
58
|
+
lmnr-0.6.17.dist-info/METADATA,sha256=dGCAXz4gY1LcOHUTGjR-cHU3hCeROzCQM4M1q0wlFPU,15152
|
59
|
+
lmnr-0.6.17.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
60
|
+
lmnr-0.6.17.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
|
61
|
+
lmnr-0.6.17.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|