lmnr 0.5.1a0__py3-none-any.whl → 0.5.2__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 -8
- lmnr/openllmetry_sdk/__init__.py +5 -33
- lmnr/openllmetry_sdk/decorators/base.py +24 -17
- lmnr/openllmetry_sdk/instruments.py +1 -0
- lmnr/openllmetry_sdk/opentelemetry/instrumentation/google_genai/__init__.py +454 -0
- lmnr/openllmetry_sdk/opentelemetry/instrumentation/google_genai/config.py +9 -0
- lmnr/openllmetry_sdk/opentelemetry/instrumentation/google_genai/utils.py +216 -0
- lmnr/openllmetry_sdk/tracing/__init__.py +1 -0
- lmnr/openllmetry_sdk/tracing/context_manager.py +13 -0
- lmnr/openllmetry_sdk/tracing/tracing.py +230 -252
- lmnr/sdk/browser/playwright_otel.py +42 -58
- lmnr/sdk/browser/pw_utils.py +8 -40
- lmnr/sdk/client/asynchronous/async_client.py +0 -34
- lmnr/sdk/client/asynchronous/resources/__init__.py +0 -4
- lmnr/sdk/client/asynchronous/resources/agent.py +96 -6
- lmnr/sdk/client/synchronous/resources/__init__.py +1 -3
- lmnr/sdk/client/synchronous/resources/agent.py +94 -8
- lmnr/sdk/client/synchronous/sync_client.py +0 -36
- lmnr/sdk/decorators.py +16 -2
- lmnr/sdk/laminar.py +3 -3
- lmnr/sdk/types.py +84 -170
- lmnr/sdk/utils.py +8 -1
- lmnr/version.py +1 -1
- {lmnr-0.5.1a0.dist-info → lmnr-0.5.2.dist-info}/METADATA +57 -57
- lmnr-0.5.2.dist-info/RECORD +54 -0
- lmnr/sdk/client/asynchronous/resources/pipeline.py +0 -89
- lmnr/sdk/client/asynchronous/resources/semantic_search.py +0 -60
- lmnr/sdk/client/synchronous/resources/pipeline.py +0 -89
- lmnr/sdk/client/synchronous/resources/semantic_search.py +0 -60
- lmnr-0.5.1a0.dist-info/RECORD +0 -54
- {lmnr-0.5.1a0.dist-info → lmnr-0.5.2.dist-info}/LICENSE +0 -0
- {lmnr-0.5.1a0.dist-info → lmnr-0.5.2.dist-info}/WHEEL +0 -0
- {lmnr-0.5.1a0.dist-info → lmnr-0.5.2.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,9 @@ import logging
|
|
2
2
|
import uuid
|
3
3
|
|
4
4
|
from lmnr.sdk.browser.pw_utils import handle_navigation_async, handle_navigation_sync
|
5
|
-
from lmnr.sdk.browser.utils import
|
5
|
+
from lmnr.sdk.browser.utils import with_tracer_and_client_wrapper
|
6
|
+
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
7
|
+
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
6
8
|
from lmnr.version import __version__
|
7
9
|
|
8
10
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
@@ -16,11 +18,11 @@ from opentelemetry.trace import (
|
|
16
18
|
set_span_in_context,
|
17
19
|
)
|
18
20
|
from opentelemetry.context import get_current
|
19
|
-
from typing import Collection
|
21
|
+
from typing import Collection
|
20
22
|
from wrapt import wrap_function_wrapper
|
21
23
|
|
22
24
|
try:
|
23
|
-
from playwright.async_api import Browser
|
25
|
+
from playwright.async_api import Browser, BrowserContext
|
24
26
|
from playwright.sync_api import (
|
25
27
|
Browser as SyncBrowser,
|
26
28
|
BrowserContext as SyncBrowserContext,
|
@@ -37,12 +39,12 @@ _instruments = ("playwright >= 1.9.0",)
|
|
37
39
|
logger = logging.getLogger(__name__)
|
38
40
|
|
39
41
|
_context_spans: dict[str, Span] = {}
|
40
|
-
_project_api_key: Optional[str] = None
|
41
|
-
_base_http_url: Optional[str] = None
|
42
42
|
|
43
43
|
|
44
|
-
@
|
45
|
-
def _wrap_new_page(
|
44
|
+
@with_tracer_and_client_wrapper
|
45
|
+
def _wrap_new_page(
|
46
|
+
tracer: Tracer, client: LaminarClient, to_wrap, wrapped, instance, args, kwargs
|
47
|
+
):
|
46
48
|
with tracer.start_as_current_span(
|
47
49
|
f"{to_wrap.get('object')}.{to_wrap.get('method')}"
|
48
50
|
) as span:
|
@@ -50,15 +52,13 @@ def _wrap_new_page(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
50
52
|
session_id = str(uuid.uuid4().hex)
|
51
53
|
trace_id = format(get_current_span().get_span_context().trace_id, "032x")
|
52
54
|
span.set_attribute("lmnr.internal.has_browser_session", True)
|
53
|
-
handle_navigation_sync(
|
54
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
55
|
-
)
|
55
|
+
handle_navigation_sync(page, session_id, trace_id, client)
|
56
56
|
return page
|
57
57
|
|
58
58
|
|
59
|
-
@
|
59
|
+
@with_tracer_and_client_wrapper
|
60
60
|
async def _wrap_new_page_async(
|
61
|
-
tracer: Tracer, to_wrap, wrapped, instance, args, kwargs
|
61
|
+
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
62
62
|
):
|
63
63
|
with tracer.start_as_current_span(
|
64
64
|
f"{to_wrap.get('object')}.{to_wrap.get('method')}"
|
@@ -67,14 +67,14 @@ async def _wrap_new_page_async(
|
|
67
67
|
session_id = str(uuid.uuid4().hex)
|
68
68
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
69
69
|
span.set_attribute("lmnr.internal.has_browser_session", True)
|
70
|
-
await handle_navigation_async(
|
71
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
72
|
-
)
|
70
|
+
await handle_navigation_async(page, session_id, trace_id, client)
|
73
71
|
return page
|
74
72
|
|
75
73
|
|
76
|
-
@
|
77
|
-
def _wrap_new_browser_sync(
|
74
|
+
@with_tracer_and_client_wrapper
|
75
|
+
def _wrap_new_browser_sync(
|
76
|
+
tracer: Tracer, client: LaminarClient, to_wrap, wrapped, instance, args, kwargs
|
77
|
+
):
|
78
78
|
global _context_spans
|
79
79
|
browser: SyncBrowser = wrapped(*args, **kwargs)
|
80
80
|
session_id = str(uuid.uuid4().hex)
|
@@ -90,20 +90,16 @@ def _wrap_new_browser_sync(tracer: Tracer, to_wrap, wrapped, instance, args, kwa
|
|
90
90
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
91
91
|
context.on(
|
92
92
|
"page",
|
93
|
-
lambda page: handle_navigation_sync(
|
94
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
95
|
-
),
|
93
|
+
lambda page: handle_navigation_sync(page, session_id, trace_id, client),
|
96
94
|
)
|
97
95
|
for page in context.pages:
|
98
|
-
handle_navigation_sync(
|
99
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
100
|
-
)
|
96
|
+
handle_navigation_sync(page, session_id, trace_id, client)
|
101
97
|
return browser
|
102
98
|
|
103
99
|
|
104
|
-
@
|
100
|
+
@with_tracer_and_client_wrapper
|
105
101
|
async def _wrap_new_browser_async(
|
106
|
-
tracer: Tracer, to_wrap, wrapped, instance, args, kwargs
|
102
|
+
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
107
103
|
):
|
108
104
|
global _context_spans
|
109
105
|
browser: Browser = await wrapped(*args, **kwargs)
|
@@ -120,20 +116,18 @@ async def _wrap_new_browser_async(
|
|
120
116
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
121
117
|
|
122
118
|
async def handle_page_navigation(page):
|
123
|
-
return await handle_navigation_async(
|
124
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
125
|
-
)
|
119
|
+
return await handle_navigation_async(page, session_id, trace_id, client)
|
126
120
|
|
127
121
|
context.on("page", handle_page_navigation)
|
128
122
|
for page in context.pages:
|
129
|
-
await handle_navigation_async(
|
130
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
131
|
-
)
|
123
|
+
await handle_navigation_async(page, session_id, trace_id, client)
|
132
124
|
return browser
|
133
125
|
|
134
126
|
|
135
|
-
@
|
136
|
-
def _wrap_new_context_sync(
|
127
|
+
@with_tracer_and_client_wrapper
|
128
|
+
def _wrap_new_context_sync(
|
129
|
+
tracer: Tracer, client: LaminarClient, to_wrap, wrapped, instance, args, kwargs
|
130
|
+
):
|
137
131
|
context: SyncBrowserContext = wrapped(*args, **kwargs)
|
138
132
|
session_id = str(uuid.uuid4().hex)
|
139
133
|
span = get_current_span()
|
@@ -148,20 +142,16 @@ def _wrap_new_context_sync(tracer: Tracer, to_wrap, wrapped, instance, args, kwa
|
|
148
142
|
|
149
143
|
context.on(
|
150
144
|
"page",
|
151
|
-
lambda page: handle_navigation_sync(
|
152
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
153
|
-
),
|
145
|
+
lambda page: handle_navigation_sync(page, session_id, trace_id, client),
|
154
146
|
)
|
155
147
|
for page in context.pages:
|
156
|
-
handle_navigation_sync(
|
157
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
158
|
-
)
|
148
|
+
handle_navigation_sync(page, session_id, trace_id, client)
|
159
149
|
return context
|
160
150
|
|
161
151
|
|
162
|
-
@
|
152
|
+
@with_tracer_and_client_wrapper
|
163
153
|
async def _wrap_new_context_async(
|
164
|
-
tracer: Tracer, to_wrap, wrapped, instance, args, kwargs
|
154
|
+
tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
|
165
155
|
):
|
166
156
|
context: SyncBrowserContext = await wrapped(*args, **kwargs)
|
167
157
|
session_id = str(uuid.uuid4().hex)
|
@@ -176,21 +166,18 @@ async def _wrap_new_context_async(
|
|
176
166
|
trace_id = format(span.get_span_context().trace_id, "032x")
|
177
167
|
|
178
168
|
async def handle_page_navigation(page):
|
179
|
-
return await handle_navigation_async(
|
180
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
181
|
-
)
|
169
|
+
return await handle_navigation_async(page, session_id, trace_id, client)
|
182
170
|
|
183
171
|
context.on("page", handle_page_navigation)
|
184
172
|
for page in context.pages:
|
185
|
-
await handle_navigation_async(
|
186
|
-
page, session_id, trace_id, _project_api_key, _base_http_url
|
187
|
-
)
|
173
|
+
await handle_navigation_async(page, session_id, trace_id, client)
|
188
174
|
return context
|
189
175
|
|
190
176
|
|
191
|
-
@
|
177
|
+
@with_tracer_and_client_wrapper
|
192
178
|
def _wrap_close_browser_sync(
|
193
179
|
tracer: Tracer,
|
180
|
+
client: LaminarClient,
|
194
181
|
to_wrap,
|
195
182
|
wrapped,
|
196
183
|
instance: SyncBrowser,
|
@@ -208,9 +195,10 @@ def _wrap_close_browser_sync(
|
|
208
195
|
return wrapped(*args, **kwargs)
|
209
196
|
|
210
197
|
|
211
|
-
@
|
198
|
+
@with_tracer_and_client_wrapper
|
212
199
|
async def _wrap_close_browser_async(
|
213
200
|
tracer: Tracer,
|
201
|
+
client: AsyncLaminarClient,
|
214
202
|
to_wrap,
|
215
203
|
wrapped,
|
216
204
|
instance: Browser,
|
@@ -332,24 +320,18 @@ WRAPPED_METHODS_ASYNC = [
|
|
332
320
|
|
333
321
|
|
334
322
|
class PlaywrightInstrumentor(BaseInstrumentor):
|
335
|
-
def __init__(self):
|
323
|
+
def __init__(self, client: LaminarClient, async_client: AsyncLaminarClient):
|
336
324
|
super().__init__()
|
325
|
+
self.client = client
|
326
|
+
self.async_client = async_client
|
337
327
|
|
338
328
|
def instrumentation_dependencies(self) -> Collection[str]:
|
339
329
|
return _instruments
|
340
330
|
|
341
331
|
def _instrument(self, **kwargs):
|
342
|
-
global _project_api_key, _base_http_url
|
343
|
-
|
344
332
|
tracer_provider = kwargs.get("tracer_provider")
|
345
333
|
tracer = get_tracer(__name__, __version__, tracer_provider)
|
346
334
|
|
347
|
-
if kwargs.get("project_api_key"):
|
348
|
-
_project_api_key = kwargs.get("project_api_key")
|
349
|
-
|
350
|
-
if kwargs.get("base_http_url"):
|
351
|
-
_base_http_url = kwargs.get("base_http_url")
|
352
|
-
|
353
335
|
for wrapped_method in WRAPPED_METHODS:
|
354
336
|
wrap_package = wrapped_method.get("package")
|
355
337
|
wrap_object = wrapped_method.get("object")
|
@@ -360,6 +342,7 @@ class PlaywrightInstrumentor(BaseInstrumentor):
|
|
360
342
|
f"{wrap_object}.{wrap_method}",
|
361
343
|
wrapped_method.get("wrapper")(
|
362
344
|
tracer,
|
345
|
+
self.client,
|
363
346
|
wrapped_method,
|
364
347
|
),
|
365
348
|
)
|
@@ -377,6 +360,7 @@ class PlaywrightInstrumentor(BaseInstrumentor):
|
|
377
360
|
f"{wrap_object}.{wrap_method}",
|
378
361
|
wrapped_method.get("wrapper")(
|
379
362
|
tracer,
|
363
|
+
self.async_client,
|
380
364
|
wrapped_method,
|
381
365
|
),
|
382
366
|
)
|
lmnr/sdk/browser/pw_utils.py
CHANGED
@@ -3,7 +3,6 @@ import logging
|
|
3
3
|
import os
|
4
4
|
import time
|
5
5
|
import threading
|
6
|
-
from typing import Optional
|
7
6
|
|
8
7
|
from opentelemetry import trace
|
9
8
|
|
@@ -27,8 +26,6 @@ logger = logging.getLogger(__name__)
|
|
27
26
|
# Track pages we've already instrumented to avoid double-instrumentation
|
28
27
|
instrumented_pages = set()
|
29
28
|
async_instrumented_pages = set()
|
30
|
-
client: Optional[LaminarClient] = None
|
31
|
-
async_client: Optional[AsyncLaminarClient] = None
|
32
29
|
|
33
30
|
|
34
31
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
@@ -223,21 +220,8 @@ async def inject_rrweb_async(page: Page):
|
|
223
220
|
|
224
221
|
@observe(name="playwright.page", ignore_input=True, ignore_output=True)
|
225
222
|
def handle_navigation_sync(
|
226
|
-
page: SyncPage,
|
227
|
-
session_id: str,
|
228
|
-
trace_id: str,
|
229
|
-
project_api_key: Optional[str] = None,
|
230
|
-
base_http_url: Optional[str] = None,
|
223
|
+
page: SyncPage, session_id: str, trace_id: str, client: LaminarClient
|
231
224
|
):
|
232
|
-
global client
|
233
|
-
if client is None:
|
234
|
-
client = LaminarClient(base_url=base_http_url, project_api_key=project_api_key)
|
235
|
-
if (base_http_url is not None and base_http_url != client.base_url) or (
|
236
|
-
project_api_key is not None and project_api_key != client.project_api_key
|
237
|
-
):
|
238
|
-
if client is not None:
|
239
|
-
client.close()
|
240
|
-
client = LaminarClient(base_url=base_http_url, project_api_key=project_api_key)
|
241
225
|
trace.get_current_span().set_attribute("lmnr.internal.has_browser_session", True)
|
242
226
|
# Check if we've already instrumented this page
|
243
227
|
page_id = id(page)
|
@@ -255,10 +239,11 @@ def handle_navigation_sync(
|
|
255
239
|
inject_rrweb_sync(page)
|
256
240
|
|
257
241
|
def collection_loop():
|
258
|
-
while not page.is_closed():
|
242
|
+
while not page.is_closed(): # Stop when page closes
|
259
243
|
send_events_sync(page, session_id, trace_id, client)
|
260
244
|
time.sleep(2)
|
261
245
|
|
246
|
+
# Clean up when page closes
|
262
247
|
if page_id in instrumented_pages:
|
263
248
|
instrumented_pages.remove(page_id)
|
264
249
|
|
@@ -268,25 +253,8 @@ def handle_navigation_sync(
|
|
268
253
|
|
269
254
|
@observe(name="playwright.page", ignore_input=True, ignore_output=True)
|
270
255
|
async def handle_navigation_async(
|
271
|
-
page: Page,
|
272
|
-
session_id: str,
|
273
|
-
trace_id: str,
|
274
|
-
project_api_key: Optional[str] = None,
|
275
|
-
base_http_url: Optional[str] = None,
|
256
|
+
page: Page, session_id: str, trace_id: str, client: AsyncLaminarClient
|
276
257
|
):
|
277
|
-
global async_client
|
278
|
-
if async_client is None:
|
279
|
-
async_client = AsyncLaminarClient(
|
280
|
-
base_url=base_http_url, project_api_key=project_api_key
|
281
|
-
)
|
282
|
-
if (base_http_url is not None and base_http_url != async_client.base_url) or (
|
283
|
-
project_api_key is not None and project_api_key != async_client.project_api_key
|
284
|
-
):
|
285
|
-
if async_client is not None:
|
286
|
-
await async_client.close()
|
287
|
-
async_client = AsyncLaminarClient(
|
288
|
-
base_url=base_http_url, project_api_key=project_api_key
|
289
|
-
)
|
290
258
|
trace.get_current_span().set_attribute("lmnr.internal.has_browser_session", True)
|
291
259
|
# Check if we've already instrumented this page
|
292
260
|
page_id = id(page)
|
@@ -303,19 +271,19 @@ async def handle_navigation_async(
|
|
303
271
|
page.on("load", lambda: asyncio.create_task(on_load()))
|
304
272
|
await inject_rrweb_async(page)
|
305
273
|
|
306
|
-
async def collection_loop(
|
274
|
+
async def collection_loop():
|
307
275
|
try:
|
308
|
-
while not page.is_closed():
|
276
|
+
while not page.is_closed(): # Stop when page closes
|
309
277
|
await send_events_async(page, session_id, trace_id, client)
|
310
278
|
await asyncio.sleep(2)
|
311
|
-
|
279
|
+
# Clean up when page closes
|
312
280
|
async_instrumented_pages.remove(page_id)
|
313
281
|
logger.info("Event collection stopped")
|
314
282
|
except Exception as e:
|
315
283
|
logger.error(f"Event collection stopped: {e}")
|
316
284
|
|
317
285
|
# Create and store task
|
318
|
-
task = asyncio.create_task(collection_loop(
|
286
|
+
task = asyncio.create_task(collection_loop())
|
319
287
|
|
320
288
|
# Clean up task when page closes
|
321
289
|
page.on("close", lambda: task.cancel())
|
@@ -11,8 +11,6 @@ from lmnr.sdk.client.asynchronous.resources import (
|
|
11
11
|
AsyncAgent,
|
12
12
|
AsyncBrowserEvents,
|
13
13
|
AsyncEvals,
|
14
|
-
AsyncPipeline,
|
15
|
-
AsyncSemanticSearch,
|
16
14
|
)
|
17
15
|
from lmnr.sdk.utils import from_env
|
18
16
|
|
@@ -66,12 +64,6 @@ class AsyncLaminarClient:
|
|
66
64
|
)
|
67
65
|
|
68
66
|
# Initialize resource objects
|
69
|
-
self.__pipeline = AsyncPipeline(
|
70
|
-
self.__client, self.__base_url, self.__project_api_key
|
71
|
-
)
|
72
|
-
self.__semantic_search = AsyncSemanticSearch(
|
73
|
-
self.__client, self.__base_url, self.__project_api_key
|
74
|
-
)
|
75
67
|
self.__agent = AsyncAgent(
|
76
68
|
self.__client, self.__base_url, self.__project_api_key
|
77
69
|
)
|
@@ -82,24 +74,6 @@ class AsyncLaminarClient:
|
|
82
74
|
self.__client, self.__base_url, self.__project_api_key
|
83
75
|
)
|
84
76
|
|
85
|
-
@property
|
86
|
-
def pipeline(self) -> AsyncPipeline:
|
87
|
-
"""Get the Pipeline resource.
|
88
|
-
|
89
|
-
Returns:
|
90
|
-
Pipeline: The Pipeline resource instance.
|
91
|
-
"""
|
92
|
-
return self.__pipeline
|
93
|
-
|
94
|
-
@property
|
95
|
-
def semantic_search(self) -> AsyncSemanticSearch:
|
96
|
-
"""Get the SemanticSearch resource.
|
97
|
-
|
98
|
-
Returns:
|
99
|
-
SemanticSearch: The SemanticSearch resource instance.
|
100
|
-
"""
|
101
|
-
return self.__semantic_search
|
102
|
-
|
103
77
|
@property
|
104
78
|
def agent(self) -> AsyncAgent:
|
105
79
|
"""Get the Agent resource.
|
@@ -137,14 +111,6 @@ class AsyncLaminarClient:
|
|
137
111
|
"""
|
138
112
|
await self.__client.aclose()
|
139
113
|
|
140
|
-
@property
|
141
|
-
def base_url(self) -> str:
|
142
|
-
return self.__base_url
|
143
|
-
|
144
|
-
@property
|
145
|
-
def project_api_key(self) -> str:
|
146
|
-
return self.__project_api_key
|
147
|
-
|
148
114
|
async def __aenter__(self: _T) -> _T:
|
149
115
|
return self
|
150
116
|
|
@@ -1,12 +1,8 @@
|
|
1
1
|
from lmnr.sdk.client.asynchronous.resources.agent import AsyncAgent
|
2
2
|
from lmnr.sdk.client.asynchronous.resources.browser_events import AsyncBrowserEvents
|
3
3
|
from lmnr.sdk.client.asynchronous.resources.evals import AsyncEvals
|
4
|
-
from lmnr.sdk.client.asynchronous.resources.pipeline import AsyncPipeline
|
5
|
-
from lmnr.sdk.client.asynchronous.resources.semantic_search import AsyncSemanticSearch
|
6
4
|
|
7
5
|
__all__ = [
|
8
|
-
"AsyncPipeline",
|
9
|
-
"AsyncSemanticSearch",
|
10
6
|
"AsyncAgent",
|
11
7
|
"AsyncEvals",
|
12
8
|
"AsyncBrowserEvents",
|
@@ -35,7 +35,16 @@ class AsyncAgent(BaseAsyncResource):
|
|
35
35
|
model_provider: Optional[ModelProvider] = None,
|
36
36
|
model: Optional[str] = None,
|
37
37
|
enable_thinking: bool = True,
|
38
|
+
agent_state: Optional[str] = None,
|
39
|
+
storage_state: Optional[str] = None,
|
38
40
|
return_screenshots: bool = False,
|
41
|
+
return_agent_state: bool = False,
|
42
|
+
return_storage_state: bool = False,
|
43
|
+
timeout: Optional[int] = None,
|
44
|
+
cdp_url: Optional[str] = None,
|
45
|
+
max_steps: Optional[int] = None,
|
46
|
+
thinking_token_budget: Optional[int] = None,
|
47
|
+
start_url: Optional[str] = None,
|
39
48
|
) -> AsyncIterator[RunAgentResponseChunk]:
|
40
49
|
"""Run Laminar index agent in streaming mode.
|
41
50
|
|
@@ -46,7 +55,17 @@ class AsyncAgent(BaseAsyncResource):
|
|
46
55
|
model_provider (Optional[ModelProvider], optional): LLM model provider
|
47
56
|
model (Optional[str], optional): LLM model name
|
48
57
|
enable_thinking (bool, optional): whether to enable thinking on the underlying LLM. Default to True.
|
58
|
+
agent_state (Optional[str], optional): the agent's state as returned by the previous agent run. Default to None.
|
59
|
+
storage_state (Optional[str], optional): the browser's storage state as returned by the previous agent run. Default to None.
|
49
60
|
return_screenshots (bool, optional): whether to return screenshots of the agent's states at every step. Default to False.
|
61
|
+
return_agent_state (bool, optional): whether to return the agent's state in the final chunk. Default to False.
|
62
|
+
return_storage_state (bool, optional): whether to return the storage state in the final chunk. Default to False.
|
63
|
+
timeout (Optional[int], optional): timeout seconds for the agent's response. Default to None.
|
64
|
+
cdp_url (Optional[str], optional): Chrome DevTools Protocol URL of an existing browser session. Default to None.
|
65
|
+
max_steps (Optional[int], optional): maximum number of steps the agent can take. If not set, the backend will use a default value (currently 100). Default to None.
|
66
|
+
thinking_token_budget (Optional[int], optional): maximum number of tokens the underlying LLM can spend on thinking in each step, if supported by the model. Default to None.
|
67
|
+
start_url (Optional[str], optional): the URL to start the agent on. Must be a valid URL - refer to https://playwright.dev/docs/api/class-page#page-goto. If not specified, the agent infers this from the prompt. Default to None.
|
68
|
+
|
50
69
|
Returns:
|
51
70
|
AsyncIterator[RunAgentResponseChunk]: a generator of response chunks
|
52
71
|
"""
|
@@ -60,7 +79,16 @@ class AsyncAgent(BaseAsyncResource):
|
|
60
79
|
model_provider: Optional[ModelProvider] = None,
|
61
80
|
model: Optional[str] = None,
|
62
81
|
enable_thinking: bool = True,
|
82
|
+
agent_state: Optional[str] = None,
|
83
|
+
storage_state: Optional[str] = None,
|
63
84
|
return_screenshots: bool = False,
|
85
|
+
return_agent_state: bool = False,
|
86
|
+
return_storage_state: bool = False,
|
87
|
+
timeout: Optional[int] = None,
|
88
|
+
cdp_url: Optional[str] = None,
|
89
|
+
max_steps: Optional[int] = None,
|
90
|
+
thinking_token_budget: Optional[int] = None,
|
91
|
+
start_url: Optional[str] = None,
|
64
92
|
) -> AgentOutput:
|
65
93
|
"""Run Laminar index agent.
|
66
94
|
|
@@ -70,7 +98,17 @@ class AsyncAgent(BaseAsyncResource):
|
|
70
98
|
model_provider (Optional[ModelProvider], optional): LLM model provider
|
71
99
|
model (Optional[str], optional): LLM model name
|
72
100
|
enable_thinking (bool, optional): whether to enable thinking on the underlying LLM. Default to True.
|
101
|
+
agent_state (Optional[str], optional): the agent's state as returned by the previous agent run. Default to None.
|
102
|
+
storage_state (Optional[str], optional): the browser's storage state as returned by the previous agent run. Default to None.
|
73
103
|
return_screenshots (bool, optional): whether to return screenshots of the agent's states at every step. Default to False.
|
104
|
+
return_agent_state (bool, optional): whether to return the agent's state. Default to False.
|
105
|
+
return_storage_state (bool, optional): whether to return the storage state. Default to False.
|
106
|
+
timeout (Optional[int], optional): timeout seconds for the agent's response. Default to None.
|
107
|
+
cdp_url (Optional[str], optional): Chrome DevTools Protocol URL of an existing browser session. Default to None.
|
108
|
+
max_steps (Optional[int], optional): maximum number of steps the agent can take. If not set, the backend will use a default value (currently 100). Default to None.
|
109
|
+
thinking_token_budget (Optional[int], optional): maximum number of tokens the underlying LLM can spend on thinking in each step, if supported by the model. Default to None.
|
110
|
+
start_url (Optional[str], optional): the URL to start the agent on. Must be a valid URL - refer to https://playwright.dev/docs/api/class-page#page-goto. If not specified, the agent infers this from the prompt. Default to None.
|
111
|
+
|
74
112
|
Returns:
|
75
113
|
AgentOutput: agent output
|
76
114
|
"""
|
@@ -85,7 +123,15 @@ class AsyncAgent(BaseAsyncResource):
|
|
85
123
|
model: Optional[str] = None,
|
86
124
|
stream: Literal[False] = False,
|
87
125
|
enable_thinking: bool = True,
|
126
|
+
agent_state: Optional[str] = None,
|
127
|
+
storage_state: Optional[str] = None,
|
88
128
|
return_screenshots: bool = False,
|
129
|
+
return_agent_state: bool = False,
|
130
|
+
return_storage_state: bool = False,
|
131
|
+
timeout: Optional[int] = None,
|
132
|
+
max_steps: Optional[int] = None,
|
133
|
+
thinking_token_budget: Optional[int] = None,
|
134
|
+
start_url: Optional[str] = None,
|
89
135
|
) -> AgentOutput:
|
90
136
|
"""Run Laminar index agent.
|
91
137
|
|
@@ -96,7 +142,16 @@ class AsyncAgent(BaseAsyncResource):
|
|
96
142
|
model (Optional[str], optional): LLM model name
|
97
143
|
stream (Literal[False], optional): whether to stream the agent's response
|
98
144
|
enable_thinking (bool, optional): whether to enable thinking on the underlying LLM. Default to True.
|
145
|
+
agent_state (Optional[str], optional): the agent's state as returned by the previous agent run. Default to None.
|
146
|
+
storage_state (Optional[str], optional): the browser's storage state as returned by the previous agent run. Default to None.
|
99
147
|
return_screenshots (bool, optional): whether to return screenshots of the agent's states at every step. Default to False.
|
148
|
+
return_agent_state (bool, optional): whether to return the agent's state. Default to False.
|
149
|
+
return_storage_state (bool, optional): whether to return the storage state. Default to False.
|
150
|
+
timeout (Optional[int], optional): timeout seconds for the agent's response. Default to None.
|
151
|
+
cdp_url (Optional[str], optional): Chrome DevTools Protocol URL of an existing browser session. Default to None.
|
152
|
+
max_steps (Optional[int], optional): maximum number of steps the agent can take. If not set, the backend will use a default value (currently 100). Default to None.
|
153
|
+
thinking_token_budget (Optional[int], optional): maximum number of tokens the underlying LLM can spend on thinking in each step, if supported by the model. Default to None.
|
154
|
+
start_url (Optional[str], optional): the URL to start the agent on. Must be a valid URL - refer to https://playwright.dev/docs/api/class-page#page-goto. If not specified, the agent infers this from the prompt. Default to None.
|
100
155
|
Returns:
|
101
156
|
AgentOutput: agent output
|
102
157
|
"""
|
@@ -110,7 +165,16 @@ class AsyncAgent(BaseAsyncResource):
|
|
110
165
|
model: Optional[str] = None,
|
111
166
|
stream: bool = False,
|
112
167
|
enable_thinking: bool = True,
|
168
|
+
agent_state: Optional[str] = None,
|
169
|
+
storage_state: Optional[str] = None,
|
113
170
|
return_screenshots: bool = False,
|
171
|
+
return_agent_state: bool = False,
|
172
|
+
return_storage_state: bool = False,
|
173
|
+
timeout: Optional[int] = None,
|
174
|
+
cdp_url: Optional[str] = None,
|
175
|
+
max_steps: Optional[int] = None,
|
176
|
+
thinking_token_budget: Optional[int] = None,
|
177
|
+
start_url: Optional[str] = None,
|
114
178
|
) -> Union[AgentOutput, Awaitable[AsyncIterator[RunAgentResponseChunk]]]:
|
115
179
|
"""Run Laminar index agent.
|
116
180
|
|
@@ -121,7 +185,17 @@ class AsyncAgent(BaseAsyncResource):
|
|
121
185
|
model (Optional[str], optional): LLM model name
|
122
186
|
stream (bool, optional): whether to stream the agent's response
|
123
187
|
enable_thinking (bool, optional): whether to enable thinking on the underlying LLM. Default to True.
|
188
|
+
agent_state (Optional[str], optional): the agent's state as returned by the previous agent run. Default to None.
|
189
|
+
storage_state (Optional[str], optional): the browser's storage state as returned by the previous agent run. Default to None.
|
124
190
|
return_screenshots (bool, optional): whether to return screenshots of the agent's states at every step. Default to False.
|
191
|
+
return_agent_state (bool, optional): whether to return the agent's state. Default to False.
|
192
|
+
return_storage_state (bool, optional): whether to return the storage state. Default to False.
|
193
|
+
timeout (Optional[int], optional): timeout seconds for the agent's response. Default to None.
|
194
|
+
cdp_url (Optional[str], optional): Chrome DevTools Protocol URL of an existing browser session. Default to None.
|
195
|
+
max_steps (Optional[int], optional): maximum number of steps the agent can take. If not set, the backend will use a default value (currently 100). Default to None.
|
196
|
+
thinking_token_budget (Optional[int], optional): maximum number of tokens the underlying LLM can spend on thinking in each step, if supported by the model. Default to None.
|
197
|
+
start_url (Optional[str], optional): the URL to start the agent on. Must be a valid URL - refer to https://playwright.dev/docs/api/class-page#page-goto. If not specified, the agent infers this from the prompt. Default to None.
|
198
|
+
|
125
199
|
Returns:
|
126
200
|
Union[AgentOutput, AsyncIterator[RunAgentResponseChunk]]: agent output or a generator of response chunks
|
127
201
|
"""
|
@@ -142,15 +216,23 @@ class AsyncAgent(BaseAsyncResource):
|
|
142
216
|
parent_span_context=parent_span_context,
|
143
217
|
model_provider=model_provider,
|
144
218
|
model=model,
|
145
|
-
|
146
|
-
|
219
|
+
agent_state=agent_state,
|
220
|
+
storage_state=storage_state,
|
221
|
+
# We always connect to stream, because our network configuration
|
222
|
+
# has a hard fixed idle timeout of 350 seconds.
|
147
223
|
# This means that if we don't stream, the connection will be closed.
|
148
224
|
# For now, we just return the content of the final chunk if `stream` is
|
149
225
|
# `False`.
|
150
|
-
# https://aws.amazon.com/blogs/networking-and-content-delivery/introducing-nlb-tcp-configurable-idle-timeout/
|
151
226
|
stream=True,
|
152
227
|
enable_thinking=enable_thinking,
|
153
228
|
return_screenshots=return_screenshots,
|
229
|
+
return_agent_state=return_agent_state,
|
230
|
+
return_storage_state=return_storage_state,
|
231
|
+
timeout=timeout,
|
232
|
+
cdp_url=cdp_url,
|
233
|
+
max_steps=max_steps,
|
234
|
+
thinking_token_budget=thinking_token_budget,
|
235
|
+
start_url=start_url,
|
154
236
|
)
|
155
237
|
|
156
238
|
# For streaming case, use a generator function
|
@@ -174,7 +256,7 @@ class AsyncAgent(BaseAsyncResource):
|
|
174
256
|
async with self._client.stream(
|
175
257
|
"POST",
|
176
258
|
self._base_url + "/v1/agent/run",
|
177
|
-
json=request.
|
259
|
+
json=request.model_dump(by_alias=True),
|
178
260
|
headers=self._headers(),
|
179
261
|
) as response:
|
180
262
|
async for line in response.aiter_lines():
|
@@ -187,6 +269,8 @@ class AsyncAgent(BaseAsyncResource):
|
|
187
269
|
if line:
|
188
270
|
chunk = RunAgentResponseChunk.model_validate_json(line)
|
189
271
|
yield chunk.root
|
272
|
+
if chunk.root.chunk_type in ["finalOutput", "error"]:
|
273
|
+
break
|
190
274
|
|
191
275
|
async def __run_non_streaming(self, request: RunAgentRequest) -> AgentOutput:
|
192
276
|
"""Run agent in non-streaming mode.
|
@@ -202,9 +286,11 @@ class AsyncAgent(BaseAsyncResource):
|
|
202
286
|
async with self._client.stream(
|
203
287
|
"POST",
|
204
288
|
self._base_url + "/v1/agent/run",
|
205
|
-
json=request.
|
289
|
+
json=request.model_dump(by_alias=True),
|
206
290
|
headers=self._headers(),
|
207
291
|
) as response:
|
292
|
+
if response.status_code != 200:
|
293
|
+
raise RuntimeError(await response.read())
|
208
294
|
async for line in response.aiter_lines():
|
209
295
|
line = str(line)
|
210
296
|
if line.startswith("[DONE]"):
|
@@ -214,7 +300,11 @@ class AsyncAgent(BaseAsyncResource):
|
|
214
300
|
line = line[6:]
|
215
301
|
if line:
|
216
302
|
chunk = RunAgentResponseChunk.model_validate_json(line)
|
217
|
-
if chunk.root.
|
303
|
+
if chunk.root.chunk_type == "finalOutput":
|
218
304
|
final_chunk = chunk.root
|
305
|
+
elif chunk.root.chunk_type == "error":
|
306
|
+
raise RuntimeError(chunk.root.error)
|
307
|
+
elif chunk.root.chunk_type == "timeout":
|
308
|
+
raise TimeoutError("Agent timed out")
|
219
309
|
|
220
310
|
return final_chunk.content if final_chunk is not None else AgentOutput()
|
@@ -1,7 +1,5 @@
|
|
1
1
|
from lmnr.sdk.client.synchronous.resources.agent import Agent
|
2
2
|
from lmnr.sdk.client.synchronous.resources.browser_events import BrowserEvents
|
3
3
|
from lmnr.sdk.client.synchronous.resources.evals import Evals
|
4
|
-
from lmnr.sdk.client.synchronous.resources.pipeline import Pipeline
|
5
|
-
from lmnr.sdk.client.synchronous.resources.semantic_search import SemanticSearch
|
6
4
|
|
7
|
-
__all__ = ["
|
5
|
+
__all__ = ["Agent", "Evals", "BrowserEvents"]
|