lmnr 0.4.66__py3-none-any.whl → 0.5.1__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 +30 -0
- lmnr/openllmetry_sdk/__init__.py +4 -16
- lmnr/openllmetry_sdk/tracing/attributes.py +0 -1
- lmnr/openllmetry_sdk/tracing/tracing.py +30 -10
- lmnr/sdk/browser/browser_use_otel.py +4 -4
- lmnr/sdk/browser/playwright_otel.py +299 -228
- lmnr/sdk/browser/pw_utils.py +289 -0
- lmnr/sdk/browser/utils.py +18 -53
- lmnr/sdk/client/asynchronous/async_client.py +157 -0
- lmnr/sdk/client/asynchronous/resources/__init__.py +13 -0
- lmnr/sdk/client/asynchronous/resources/agent.py +220 -0
- lmnr/sdk/client/asynchronous/resources/base.py +32 -0
- lmnr/sdk/client/asynchronous/resources/browser_events.py +40 -0
- lmnr/sdk/client/asynchronous/resources/evals.py +64 -0
- lmnr/sdk/client/asynchronous/resources/pipeline.py +89 -0
- lmnr/sdk/client/asynchronous/resources/semantic_search.py +60 -0
- lmnr/sdk/client/synchronous/resources/__init__.py +7 -0
- lmnr/sdk/client/synchronous/resources/agent.py +215 -0
- lmnr/sdk/client/synchronous/resources/base.py +32 -0
- lmnr/sdk/client/synchronous/resources/browser_events.py +40 -0
- lmnr/sdk/client/synchronous/resources/evals.py +102 -0
- lmnr/sdk/client/synchronous/resources/pipeline.py +89 -0
- lmnr/sdk/client/synchronous/resources/semantic_search.py +60 -0
- lmnr/sdk/client/synchronous/sync_client.py +170 -0
- lmnr/sdk/datasets.py +7 -2
- lmnr/sdk/evaluations.py +59 -35
- lmnr/sdk/laminar.py +34 -174
- lmnr/sdk/types.py +124 -23
- lmnr/sdk/utils.py +10 -0
- lmnr/version.py +6 -6
- {lmnr-0.4.66.dist-info → lmnr-0.5.1.dist-info}/METADATA +88 -38
- lmnr-0.5.1.dist-info/RECORD +55 -0
- {lmnr-0.4.66.dist-info → lmnr-0.5.1.dist-info}/WHEEL +1 -1
- lmnr/sdk/client.py +0 -313
- lmnr-0.4.66.dist-info/RECORD +0 -39
- {lmnr-0.4.66.dist-info → lmnr-0.5.1.dist-info}/LICENSE +0 -0
- {lmnr-0.4.66.dist-info → lmnr-0.5.1.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.5.1
|
4
4
|
Summary: Python SDK for Laminar
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -39,12 +39,12 @@ Provides-Extra: transformers
|
|
39
39
|
Provides-Extra: vertexai
|
40
40
|
Provides-Extra: watsonx
|
41
41
|
Provides-Extra: weaviate
|
42
|
-
Requires-Dist: aiohttp (>=3.0)
|
43
42
|
Requires-Dist: argparse (>=1.0)
|
44
43
|
Requires-Dist: grpcio (<1.68.0)
|
45
|
-
Requires-Dist:
|
46
|
-
Requires-Dist: opentelemetry-
|
47
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-
|
44
|
+
Requires-Dist: httpx (>=0.28.1)
|
45
|
+
Requires-Dist: opentelemetry-api (>=1.31.1)
|
46
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (>=1.31.1)
|
47
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.31.1)
|
48
48
|
Requires-Dist: opentelemetry-instrumentation-alephalpha (>=0.38.12) ; extra == "alephalpha"
|
49
49
|
Requires-Dist: opentelemetry-instrumentation-alephalpha (>=0.38.12) ; extra == "all"
|
50
50
|
Requires-Dist: opentelemetry-instrumentation-anthropic (>=0.38.12) ; extra == "all"
|
@@ -83,27 +83,26 @@ Requires-Dist: opentelemetry-instrumentation-qdrant (>=0.38.12) ; extra == "all"
|
|
83
83
|
Requires-Dist: opentelemetry-instrumentation-qdrant (>=0.38.12) ; extra == "qdrant"
|
84
84
|
Requires-Dist: opentelemetry-instrumentation-replicate (>=0.38.12) ; extra == "all"
|
85
85
|
Requires-Dist: opentelemetry-instrumentation-replicate (>=0.38.12) ; extra == "replicate"
|
86
|
-
Requires-Dist: opentelemetry-instrumentation-requests (>=0.
|
86
|
+
Requires-Dist: opentelemetry-instrumentation-requests (>=0.52b0)
|
87
87
|
Requires-Dist: opentelemetry-instrumentation-sagemaker (>=0.38.12) ; extra == "all"
|
88
88
|
Requires-Dist: opentelemetry-instrumentation-sagemaker (>=0.38.12) ; extra == "sagemaker"
|
89
|
-
Requires-Dist: opentelemetry-instrumentation-sqlalchemy (>=0.
|
90
|
-
Requires-Dist: opentelemetry-instrumentation-threading (>=0.
|
89
|
+
Requires-Dist: opentelemetry-instrumentation-sqlalchemy (>=0.52b0)
|
90
|
+
Requires-Dist: opentelemetry-instrumentation-threading (>=0.52b0)
|
91
91
|
Requires-Dist: opentelemetry-instrumentation-together (>=0.38.12) ; extra == "all"
|
92
92
|
Requires-Dist: opentelemetry-instrumentation-together (>=0.38.12) ; extra == "together"
|
93
93
|
Requires-Dist: opentelemetry-instrumentation-transformers (>=0.38.12) ; extra == "all"
|
94
94
|
Requires-Dist: opentelemetry-instrumentation-transformers (>=0.38.12) ; extra == "transformers"
|
95
|
-
Requires-Dist: opentelemetry-instrumentation-urllib3 (>=0.
|
95
|
+
Requires-Dist: opentelemetry-instrumentation-urllib3 (>=0.52b0)
|
96
96
|
Requires-Dist: opentelemetry-instrumentation-vertexai (>=0.38.12) ; extra == "all"
|
97
97
|
Requires-Dist: opentelemetry-instrumentation-vertexai (>=0.38.12) ; extra == "vertexai"
|
98
98
|
Requires-Dist: opentelemetry-instrumentation-watsonx (>=0.38.12) ; extra == "all"
|
99
99
|
Requires-Dist: opentelemetry-instrumentation-watsonx (>=0.38.12) ; extra == "watsonx"
|
100
100
|
Requires-Dist: opentelemetry-instrumentation-weaviate (>=0.38.12) ; extra == "all"
|
101
101
|
Requires-Dist: opentelemetry-instrumentation-weaviate (>=0.38.12) ; extra == "weaviate"
|
102
|
-
Requires-Dist: opentelemetry-sdk (>=1.
|
102
|
+
Requires-Dist: opentelemetry-sdk (>=1.31.1)
|
103
103
|
Requires-Dist: opentelemetry-semantic-conventions-ai (>=0.4.2)
|
104
104
|
Requires-Dist: pydantic (>=2.0.3)
|
105
105
|
Requires-Dist: python-dotenv (>=1.0)
|
106
|
-
Requires-Dist: requests (>=2.0)
|
107
106
|
Requires-Dist: tenacity (>=8.0)
|
108
107
|
Requires-Dist: tqdm (>=4.0)
|
109
108
|
Description-Content-Type: text/markdown
|
@@ -145,7 +144,28 @@ from lmnr import Laminar
|
|
145
144
|
Laminar.initialize(project_api_key="<PROJECT_API_KEY>")
|
146
145
|
```
|
147
146
|
|
148
|
-
|
147
|
+
You can also skip passing the `project_api_key`, in which case it will be looked
|
148
|
+
in the environment (or local .env file) by the key `LMNR_PROJECT_API_KEY`.
|
149
|
+
|
150
|
+
Note that you need to only initialize Laminar once in your application. You should
|
151
|
+
try to do that as early as possible in your application, e.g. at server startup.
|
152
|
+
|
153
|
+
## Set-up for self-hosting
|
154
|
+
|
155
|
+
If you self-host a Laminar instance, the default connection settings to it are
|
156
|
+
`http://localhost:8000` for HTTP and `http://localhost:8001` for gRPC. Initialize
|
157
|
+
the SDK accordingly:
|
158
|
+
|
159
|
+
```python
|
160
|
+
from lmnr import Laminar
|
161
|
+
|
162
|
+
Laminar.initialize(
|
163
|
+
project_api_key="<PROJECT_API_KEY>",
|
164
|
+
base_url="http://localhost",
|
165
|
+
http_port=8000,
|
166
|
+
grpc_port=8001,
|
167
|
+
)
|
168
|
+
```
|
149
169
|
|
150
170
|
## Instrumentation
|
151
171
|
|
@@ -281,49 +301,79 @@ You can run evaluations locally by providing executor (part of the logic used in
|
|
281
301
|
|
282
302
|
Read the [docs](https://docs.lmnr.ai/evaluations/introduction) to learn more about evaluations.
|
283
303
|
|
284
|
-
##
|
304
|
+
## Client for HTTP operations
|
285
305
|
|
286
|
-
|
306
|
+
Various interactions with Laminar [API](https://docs.lmnr.ai/api-reference/) are available in `LaminarClient`
|
307
|
+
and its asynchronous version `AsyncLaminarClient`.
|
287
308
|
|
288
|
-
|
309
|
+
### Agent
|
289
310
|
|
290
|
-
|
291
|
-
|
292
|
-
Example use:
|
311
|
+
To run Laminar agent, you can invoke `client.agent.run`
|
293
312
|
|
294
313
|
```python
|
295
|
-
from lmnr import
|
314
|
+
from lmnr import LaminarClient
|
296
315
|
|
297
|
-
|
316
|
+
client = LaminarClient(project_api_key="<YOUR_PROJECT_API_KEY>")
|
298
317
|
|
299
|
-
|
300
|
-
|
301
|
-
inputs = {'input_node_name': 'some_value'},
|
302
|
-
# all environment variables
|
303
|
-
env = {'OPENAI_API_KEY': 'sk-some-key'},
|
318
|
+
response = client.agent.run(
|
319
|
+
prompt="What is the weather in London today?"
|
304
320
|
)
|
321
|
+
|
322
|
+
print(response.result.content)
|
305
323
|
```
|
306
324
|
|
307
|
-
|
325
|
+
#### Streaming
|
326
|
+
|
327
|
+
Agent run supports streaming as well.
|
308
328
|
|
309
329
|
```python
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
330
|
+
from lmnr import LaminarClient
|
331
|
+
|
332
|
+
client = LaminarClient(project_api_key="<YOUR_PROJECT_API_KEY>")
|
333
|
+
|
334
|
+
for chunk in client.agent.run(
|
335
|
+
prompt="What is the weather in London today?",
|
336
|
+
stream=True
|
337
|
+
):
|
338
|
+
if chunk.chunkType == 'step':
|
339
|
+
print(chunk.summary)
|
340
|
+
elif chunk.chunkType == 'finalOutput':
|
341
|
+
print(chunk.content.result.content)
|
316
342
|
```
|
317
343
|
|
318
|
-
|
344
|
+
#### Async mode
|
345
|
+
|
346
|
+
```python
|
347
|
+
from lmnr import AsyncLaminarClient
|
348
|
+
|
349
|
+
client = AsyncLaminarClient(project_api_key="<YOUR_PROJECT_API_KEY>")
|
319
350
|
|
320
|
-
|
351
|
+
response = await client.agent.run(
|
352
|
+
prompt="What is the weather in London today?"
|
353
|
+
)
|
354
|
+
|
355
|
+
print(response.result.content)
|
356
|
+
```
|
357
|
+
|
358
|
+
#### Async mode with streaming
|
321
359
|
|
322
360
|
```python
|
323
|
-
|
324
|
-
|
325
|
-
|
361
|
+
from lmnr import AsyncLaminarClient
|
362
|
+
|
363
|
+
client = AsyncLaminarClient(project_api_key="<YOUR_PROJECT_API_KEY>")
|
364
|
+
|
365
|
+
# Note that you need to await the operation even though we use `async for` below
|
366
|
+
response = await client.agent.run(
|
367
|
+
prompt="What is the weather in London today?",
|
368
|
+
stream=True
|
326
369
|
)
|
370
|
+
async for chunk in client.agent.run(
|
371
|
+
prompt="What is the weather in London today?",
|
372
|
+
stream=True
|
373
|
+
):
|
374
|
+
if chunk.chunkType == 'step':
|
375
|
+
print(chunk.summary)
|
376
|
+
elif chunk.chunkType == 'finalOutput':
|
377
|
+
print(chunk.content.result.content)
|
327
378
|
```
|
328
379
|
|
329
|
-
[Read more](https://docs.lmnr.ai/datasets/indexing) about indexing and semantic search.
|
@@ -0,0 +1,55 @@
|
|
1
|
+
lmnr/__init__.py,sha256=q_LvkFcUKZCxrCap4Mr4cewyNg1vh-t2tX-kaOUdWos,1186
|
2
|
+
lmnr/cli.py,sha256=4J2RZQhHM3jJcjFvBC4PChQTS-ukxykVvI0X6lTkK-o,2918
|
3
|
+
lmnr/openllmetry_sdk/.flake8,sha256=bCxuDlGx3YQ55QHKPiGJkncHanh9qGjQJUujcFa3lAU,150
|
4
|
+
lmnr/openllmetry_sdk/__init__.py,sha256=dCZF_pImihR5lt7hkM0ezKNyxShTuO2yMO2v2dQMAzs,2443
|
5
|
+
lmnr/openllmetry_sdk/config/__init__.py,sha256=5aGdIdo1LffBkNwIBUbqzN6OUCMCrURU4b0rf5LBSI0,300
|
6
|
+
lmnr/openllmetry_sdk/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
lmnr/openllmetry_sdk/decorators/base.py,sha256=RNyvSreGBwwh8EWaa3O9samrnmKoZHK7eBzGOPT3lHc,7132
|
8
|
+
lmnr/openllmetry_sdk/instruments.py,sha256=9KoJ19Qar1dBrmO1wikNEkKxRfus8znQTj-g_maRTTM,1098
|
9
|
+
lmnr/openllmetry_sdk/tracing/__init__.py,sha256=xT73L1t2si2CM6QmMiTZ7zn-dKKYBLNrpBBWq6WfVBw,68
|
10
|
+
lmnr/openllmetry_sdk/tracing/attributes.py,sha256=BEMMGrX_7kPu4PNCV7Bz1uaclY4DNhlaLT0bWwFQnRE,1366
|
11
|
+
lmnr/openllmetry_sdk/tracing/content_allow_list.py,sha256=3feztm6PBWNelc8pAZUcQyEGyeSpNiVKjOaDk65l2ps,846
|
12
|
+
lmnr/openllmetry_sdk/tracing/context_manager.py,sha256=rdSus-p-TaevQ8hIAhfbnZr5dTqRvACDkzXGDpflncY,306
|
13
|
+
lmnr/openllmetry_sdk/tracing/tracing.py,sha256=V0A6bP_SCU6r58sFtb62CcYtkLgwEFnANkLxYQ55I-k,35985
|
14
|
+
lmnr/openllmetry_sdk/utils/__init__.py,sha256=pNhf0G3vTd5ccoc03i1MXDbricSaiqCbi1DLWhSekK8,604
|
15
|
+
lmnr/openllmetry_sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
|
16
|
+
lmnr/openllmetry_sdk/utils/json_encoder.py,sha256=dK6b_axr70IYL7Vv-bu4wntvDDuyntoqsHaddqX7P58,463
|
17
|
+
lmnr/openllmetry_sdk/utils/package_check.py,sha256=_-Fu9Zbp9tOyy27_-Rul7tDc8JaXYR2FmqF8SWOXSCc,244
|
18
|
+
lmnr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
+
lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
+
lmnr/sdk/browser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
+
lmnr/sdk/browser/browser_use_otel.py,sha256=wKFe07XTUUCuPDwFD9gqGrcaaj82BRg-kLLZQM_KshM,3915
|
22
|
+
lmnr/sdk/browser/playwright_otel.py,sha256=kvGk7qXbbWjnawrlhQ8lTD72h95Tp_VpdFFp9dkfADY,12478
|
23
|
+
lmnr/sdk/browser/pw_utils.py,sha256=YVIdIsQnnV2DwO-7oVUdm0hBFSozxaTaW7vOuZ2M_mQ,9335
|
24
|
+
lmnr/sdk/browser/rrweb/rrweb.min.js,sha256=X5pgaoX1j_OjKTqGQgKB-83xUSuydNLQa-Kkh1AAZYM,140485
|
25
|
+
lmnr/sdk/browser/utils.py,sha256=xPpMRP2y9aJIsdIDNg2wN4PSa_4w0LSsra-GIMx9VXc,2366
|
26
|
+
lmnr/sdk/client/asynchronous/async_client.py,sha256=zj5YbDik5Uc30iRMzeU-CeIXirSssPPS6OZg8XqlxMc,4785
|
27
|
+
lmnr/sdk/client/asynchronous/resources/__init__.py,sha256=ul1K_uvLmN5pw1Z_NZojdZ_CDp7om4RYV78pkgx6-2M,507
|
28
|
+
lmnr/sdk/client/asynchronous/resources/agent.py,sha256=95m72mNL1ScCkaoHg204h7evNbgtm1hkoHf9Pe6f0X4,8940
|
29
|
+
lmnr/sdk/client/asynchronous/resources/base.py,sha256=aJ43Q1rltg23IQaI4eeaZKckxVTgDUbCJrChhQCUEoE,986
|
30
|
+
lmnr/sdk/client/asynchronous/resources/browser_events.py,sha256=T-DUbbAfMQ2VqiVfgVplxuTaJZuoNcC1O6RCxdfw7UQ,1163
|
31
|
+
lmnr/sdk/client/asynchronous/resources/evals.py,sha256=bm3ATwqLozUoW2Ed6psmdjmeJ7joBaQHSv6mBeA_cws,2187
|
32
|
+
lmnr/sdk/client/asynchronous/resources/pipeline.py,sha256=Tdb-ChxBcjDvOLuPQA6V-Gksf-3dJpHfbrGToa2qVmk,3002
|
33
|
+
lmnr/sdk/client/asynchronous/resources/semantic_search.py,sha256=YEshW6sIlkAi29yEKkp7b95V9aK0sj5zwaU6sWgeB-M,2074
|
34
|
+
lmnr/sdk/client/synchronous/resources/__init__.py,sha256=T9gI_QhvdBYbIEzfa_Z8QEtzunqgNGOsSJ9KVvR5cL0,429
|
35
|
+
lmnr/sdk/client/synchronous/resources/agent.py,sha256=dRXkMO6AMZFh5yz8q0TdszG4oTHmOUg1CYNzfEq8zss,8829
|
36
|
+
lmnr/sdk/client/synchronous/resources/base.py,sha256=ne1ZZ10UmNkMrECVvClcEJfcFJlSGvaXOC8K6mZTPdY,971
|
37
|
+
lmnr/sdk/client/synchronous/resources/browser_events.py,sha256=9rFYWZesXQomnFgbZ590tGFMTaNj0OAzT9RcFwD8q_Y,1135
|
38
|
+
lmnr/sdk/client/synchronous/resources/evals.py,sha256=sMMAai7_IW842z_J0W9OpthDhGQPCkTVJZamIkKq0wk,3496
|
39
|
+
lmnr/sdk/client/synchronous/resources/pipeline.py,sha256=MJLF7HICjB7kS1-DvtJPAneIO1aMRl8fGG1QZjCsPl4,2974
|
40
|
+
lmnr/sdk/client/synchronous/resources/semantic_search.py,sha256=oFH5r3bWJEvoZrC_a4jAtZko9tdpuUzCX1M0yvj8MMw,2046
|
41
|
+
lmnr/sdk/client/synchronous/sync_client.py,sha256=e6eNQlpPHck6va3-gbQbBWWAtMJuZYFeZnRkFy_BIEk,5212
|
42
|
+
lmnr/sdk/datasets.py,sha256=jl5Wj5nEI9pww4Jwn4XKF8h0gXBU4TOIrhqNjTJsHZQ,1709
|
43
|
+
lmnr/sdk/decorators.py,sha256=g0VBqUEMCPRbgjgGHauVuKK1wHEd9rkiGzlYUYrcml4,2336
|
44
|
+
lmnr/sdk/eval_control.py,sha256=G6Fg3Xx_KWv72iBaWlNMdyRTF2bZFQnwJ68sJNSpIcY,177
|
45
|
+
lmnr/sdk/evaluations.py,sha256=kjzc257BKhq3z_vMeHD96pJ_xBoiWzW8e0lkVaNJ2jY,20555
|
46
|
+
lmnr/sdk/laminar.py,sha256=sRbM_yESeOIGmrOMQyUrD-AZ8aj-Bf3RmtJ1QA67noQ,27981
|
47
|
+
lmnr/sdk/log.py,sha256=nt_YMmPw1IRbGy0b7q4rTtP4Yo3pQfNxqJPXK3nDSNQ,2213
|
48
|
+
lmnr/sdk/types.py,sha256=wtSiq7KqdvQ5k7URdDnGoWwX3ucDNMabNfVnbKBXfhs,13940
|
49
|
+
lmnr/sdk/utils.py,sha256=o9MybEMM0Tp_xxCMFpzEYDsOAn2I7g1t7MrazOQAxAs,3692
|
50
|
+
lmnr/version.py,sha256=N0CXlGAC06GN9Ieaql9ZCVDbbT25ZPOunYhapKO7PLY,1321
|
51
|
+
lmnr-0.5.1.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
52
|
+
lmnr-0.5.1.dist-info/METADATA,sha256=nJDNApw6xj6V6_8Plx1QyhprjHnm2VxPkc_EW24HnVI,14996
|
53
|
+
lmnr-0.5.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
54
|
+
lmnr-0.5.1.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
|
55
|
+
lmnr-0.5.1.dist-info/RECORD,,
|
lmnr/sdk/client.py
DELETED
@@ -1,313 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Laminar HTTP client. Used to send data to/from the Laminar API.
|
3
|
-
Initialized in `Laminar` singleton, but can be imported
|
4
|
-
in other classes.
|
5
|
-
"""
|
6
|
-
|
7
|
-
import asyncio
|
8
|
-
import json
|
9
|
-
import aiohttp
|
10
|
-
import gzip
|
11
|
-
from opentelemetry import trace
|
12
|
-
from pydantic.alias_generators import to_snake
|
13
|
-
import requests
|
14
|
-
from typing import Awaitable, Optional, Union
|
15
|
-
import urllib.parse
|
16
|
-
import uuid
|
17
|
-
|
18
|
-
from lmnr.sdk.types import (
|
19
|
-
EvaluationResultDatapoint,
|
20
|
-
GetDatapointsResponse,
|
21
|
-
InitEvaluationResponse,
|
22
|
-
NodeInput,
|
23
|
-
PipelineRunError,
|
24
|
-
PipelineRunRequest,
|
25
|
-
PipelineRunResponse,
|
26
|
-
SemanticSearchRequest,
|
27
|
-
SemanticSearchResponse,
|
28
|
-
)
|
29
|
-
from lmnr.version import SDK_VERSION
|
30
|
-
|
31
|
-
|
32
|
-
class LaminarClient:
|
33
|
-
__base_url: str
|
34
|
-
__project_api_key: str
|
35
|
-
__session: aiohttp.ClientSession = None
|
36
|
-
__sync_session: requests.Session = None
|
37
|
-
|
38
|
-
@classmethod
|
39
|
-
def initialize(cls, base_url: str, project_api_key: str):
|
40
|
-
cls.__base_url = base_url
|
41
|
-
cls.__project_api_key = project_api_key
|
42
|
-
cls.__sync_session = requests.Session()
|
43
|
-
loop = asyncio.get_event_loop()
|
44
|
-
if loop.is_running():
|
45
|
-
cls.__session = aiohttp.ClientSession()
|
46
|
-
|
47
|
-
@classmethod
|
48
|
-
def shutdown(cls):
|
49
|
-
cls.__sync_session.close()
|
50
|
-
if cls.__session is not None:
|
51
|
-
try:
|
52
|
-
loop = asyncio.get_event_loop()
|
53
|
-
if loop.is_running():
|
54
|
-
cls.__session.close()
|
55
|
-
else:
|
56
|
-
asyncio.run(cls.__session.close())
|
57
|
-
except Exception:
|
58
|
-
asyncio.run(cls.__session.close())
|
59
|
-
|
60
|
-
@classmethod
|
61
|
-
async def shutdown_async(cls):
|
62
|
-
if cls.__session is not None:
|
63
|
-
await cls.__session.close()
|
64
|
-
|
65
|
-
@classmethod
|
66
|
-
def run_pipeline(
|
67
|
-
cls,
|
68
|
-
pipeline: str,
|
69
|
-
inputs: dict[str, NodeInput],
|
70
|
-
env: dict[str, str] = {},
|
71
|
-
metadata: dict[str, str] = {},
|
72
|
-
parent_span_id: Optional[uuid.UUID] = None,
|
73
|
-
trace_id: Optional[uuid.UUID] = None,
|
74
|
-
) -> Union[PipelineRunResponse, Awaitable[PipelineRunResponse]]:
|
75
|
-
if cls.__project_api_key is None:
|
76
|
-
raise ValueError(
|
77
|
-
"Please initialize the Laminar object with your project "
|
78
|
-
"API key or set the LMNR_PROJECT_API_KEY environment variable"
|
79
|
-
)
|
80
|
-
try:
|
81
|
-
current_span = trace.get_current_span()
|
82
|
-
if current_span != trace.INVALID_SPAN:
|
83
|
-
parent_span_id = parent_span_id or uuid.UUID(
|
84
|
-
int=current_span.get_span_context().span_id
|
85
|
-
)
|
86
|
-
trace_id = trace_id or uuid.UUID(
|
87
|
-
int=current_span.get_span_context().trace_id
|
88
|
-
)
|
89
|
-
request = PipelineRunRequest(
|
90
|
-
inputs=inputs,
|
91
|
-
pipeline=pipeline,
|
92
|
-
env=env or {},
|
93
|
-
metadata=metadata,
|
94
|
-
parent_span_id=parent_span_id,
|
95
|
-
trace_id=trace_id,
|
96
|
-
)
|
97
|
-
loop = asyncio.get_event_loop()
|
98
|
-
if loop.is_running():
|
99
|
-
return loop.run_in_executor(None, cls.__run, request)
|
100
|
-
else:
|
101
|
-
return asyncio.run(cls.__run(request))
|
102
|
-
except Exception as e:
|
103
|
-
raise ValueError(f"Invalid request: {e}")
|
104
|
-
|
105
|
-
@classmethod
|
106
|
-
def semantic_search(
|
107
|
-
cls,
|
108
|
-
query: str,
|
109
|
-
dataset_id: uuid.UUID,
|
110
|
-
limit: Optional[int] = None,
|
111
|
-
threshold: Optional[float] = None,
|
112
|
-
) -> SemanticSearchResponse:
|
113
|
-
request = SemanticSearchRequest(
|
114
|
-
query=query,
|
115
|
-
dataset_id=dataset_id,
|
116
|
-
limit=limit,
|
117
|
-
threshold=threshold,
|
118
|
-
)
|
119
|
-
loop = asyncio.get_event_loop()
|
120
|
-
if loop.is_running():
|
121
|
-
return loop.run_in_executor(None, cls.__semantic_search, request)
|
122
|
-
else:
|
123
|
-
return asyncio.run(cls.__semantic_search(request))
|
124
|
-
|
125
|
-
@classmethod
|
126
|
-
async def init_eval(
|
127
|
-
cls, name: Optional[str] = None, group_name: Optional[str] = None
|
128
|
-
) -> InitEvaluationResponse:
|
129
|
-
session = await cls.__get_session()
|
130
|
-
async with session.post(
|
131
|
-
cls.__base_url + "/v1/evals",
|
132
|
-
json={
|
133
|
-
"name": name,
|
134
|
-
"groupName": group_name,
|
135
|
-
},
|
136
|
-
headers=cls._headers(),
|
137
|
-
) as response:
|
138
|
-
resp_json = await response.json()
|
139
|
-
return InitEvaluationResponse.model_validate(resp_json)
|
140
|
-
|
141
|
-
@classmethod
|
142
|
-
async def save_eval_datapoints(
|
143
|
-
cls,
|
144
|
-
eval_id: uuid.UUID,
|
145
|
-
datapoints: list[EvaluationResultDatapoint],
|
146
|
-
groupName: Optional[str] = None,
|
147
|
-
):
|
148
|
-
session = await cls.__get_session()
|
149
|
-
async with session.post(
|
150
|
-
cls.__base_url + f"/v1/evals/{eval_id}/datapoints",
|
151
|
-
json={
|
152
|
-
"points": [datapoint.to_dict() for datapoint in datapoints],
|
153
|
-
"groupName": groupName,
|
154
|
-
},
|
155
|
-
headers=cls._headers(),
|
156
|
-
) as response:
|
157
|
-
if response.status != 200:
|
158
|
-
raise ValueError(
|
159
|
-
f"Error saving evaluation datapoints: {await response.text()}"
|
160
|
-
)
|
161
|
-
|
162
|
-
@classmethod
|
163
|
-
async def send_browser_events(
|
164
|
-
cls,
|
165
|
-
session_id: str,
|
166
|
-
trace_id: str,
|
167
|
-
events: list[dict],
|
168
|
-
source: str,
|
169
|
-
):
|
170
|
-
session = await cls.__get_session()
|
171
|
-
payload = {
|
172
|
-
"sessionId": session_id,
|
173
|
-
"traceId": trace_id,
|
174
|
-
"events": events,
|
175
|
-
"source": source,
|
176
|
-
"sdkVersion": SDK_VERSION,
|
177
|
-
}
|
178
|
-
compressed_payload = gzip.compress(json.dumps(payload).encode("utf-8"))
|
179
|
-
|
180
|
-
async with session.post(
|
181
|
-
cls.__base_url + "/v1/browser-sessions/events",
|
182
|
-
data=compressed_payload,
|
183
|
-
headers={
|
184
|
-
**cls._headers(),
|
185
|
-
"Content-Encoding": "gzip",
|
186
|
-
},
|
187
|
-
) as response:
|
188
|
-
if response.status != 200:
|
189
|
-
raise ValueError(
|
190
|
-
f"Failed to send events: [{response.status}] {await response.text()}"
|
191
|
-
)
|
192
|
-
|
193
|
-
@classmethod
|
194
|
-
def send_browser_events_sync(
|
195
|
-
cls,
|
196
|
-
session_id: str,
|
197
|
-
trace_id: str,
|
198
|
-
events: list[dict],
|
199
|
-
source: str,
|
200
|
-
):
|
201
|
-
url = cls.__base_url + "/v1/browser-sessions/events"
|
202
|
-
payload = {
|
203
|
-
"sessionId": session_id,
|
204
|
-
"traceId": trace_id,
|
205
|
-
"events": events,
|
206
|
-
"source": source,
|
207
|
-
"sdkVersion": SDK_VERSION,
|
208
|
-
}
|
209
|
-
compressed_payload = gzip.compress(json.dumps(payload).encode("utf-8"))
|
210
|
-
response = cls.__sync_session.post(
|
211
|
-
url,
|
212
|
-
data=compressed_payload,
|
213
|
-
headers={
|
214
|
-
**cls._headers(),
|
215
|
-
"Content-Encoding": "gzip",
|
216
|
-
},
|
217
|
-
)
|
218
|
-
if response.status_code != 200:
|
219
|
-
raise ValueError(
|
220
|
-
f"Failed to send events: [{response.status_code}] {response.text}"
|
221
|
-
)
|
222
|
-
|
223
|
-
@classmethod
|
224
|
-
def get_datapoints(
|
225
|
-
cls,
|
226
|
-
dataset_name: str,
|
227
|
-
offset: int,
|
228
|
-
limit: int,
|
229
|
-
) -> GetDatapointsResponse:
|
230
|
-
# TODO: Use aiohttp. Currently, this function is called from within
|
231
|
-
# `LaminarDataset.__len__`, which is sync, but can be called from
|
232
|
-
# both sync and async (primarily async). Python does not make it easy
|
233
|
-
# to mix things this way, so we should probably refactor `LaminarDataset`.
|
234
|
-
params = {"name": dataset_name, "offset": offset, "limit": limit}
|
235
|
-
url = (
|
236
|
-
cls.__base_url + "/v1/datasets/datapoints?" + urllib.parse.urlencode(params)
|
237
|
-
)
|
238
|
-
response = cls.__sync_session.get(url, headers=cls._headers())
|
239
|
-
if response.status_code != 200:
|
240
|
-
try:
|
241
|
-
resp_json = response.json()
|
242
|
-
raise ValueError(
|
243
|
-
f"Error fetching datapoints: [{response.status_code}] {json.dumps(resp_json)}"
|
244
|
-
)
|
245
|
-
except requests.exceptions.RequestException:
|
246
|
-
raise ValueError(
|
247
|
-
f"Error fetching datapoints: [{response.status_code}] {response.text}"
|
248
|
-
)
|
249
|
-
return GetDatapointsResponse.model_validate(response.json())
|
250
|
-
|
251
|
-
@classmethod
|
252
|
-
async def __run(
|
253
|
-
cls,
|
254
|
-
request: PipelineRunRequest,
|
255
|
-
) -> PipelineRunResponse:
|
256
|
-
session = await cls.__get_session()
|
257
|
-
async with session.post(
|
258
|
-
cls.__base_url + "/v1/pipeline/run",
|
259
|
-
data=json.dumps(request.to_dict()),
|
260
|
-
headers=cls._headers(),
|
261
|
-
) as response:
|
262
|
-
if response.status != 200:
|
263
|
-
raise PipelineRunError(response)
|
264
|
-
try:
|
265
|
-
resp_json = await response.json()
|
266
|
-
keys = list(resp_json.keys())
|
267
|
-
for key in keys:
|
268
|
-
value = resp_json[key]
|
269
|
-
del resp_json[key]
|
270
|
-
resp_json[to_snake(key)] = value
|
271
|
-
return PipelineRunResponse(**resp_json)
|
272
|
-
except Exception:
|
273
|
-
raise PipelineRunError(response)
|
274
|
-
|
275
|
-
@classmethod
|
276
|
-
async def __semantic_search(
|
277
|
-
cls,
|
278
|
-
request: SemanticSearchRequest,
|
279
|
-
) -> SemanticSearchResponse:
|
280
|
-
session = await cls.__get_session()
|
281
|
-
async with session.post(
|
282
|
-
cls.__base_url + "/v1/semantic-search",
|
283
|
-
data=json.dumps(request.to_dict()),
|
284
|
-
headers=cls._headers(),
|
285
|
-
) as response:
|
286
|
-
if response.status != 200:
|
287
|
-
raise ValueError(
|
288
|
-
f"Error performing semantic search: [{response.status}] {await response.text()}"
|
289
|
-
)
|
290
|
-
try:
|
291
|
-
resp_json = await response.json()
|
292
|
-
for result in resp_json["results"]:
|
293
|
-
result["dataset_id"] = uuid.UUID(result["datasetId"])
|
294
|
-
return SemanticSearchResponse(**resp_json)
|
295
|
-
except Exception as e:
|
296
|
-
raise ValueError(
|
297
|
-
f"Error parsing semantic search response: status={response.status} error={e}"
|
298
|
-
)
|
299
|
-
|
300
|
-
@classmethod
|
301
|
-
def _headers(cls):
|
302
|
-
assert cls.__project_api_key is not None, "Project API key is not set"
|
303
|
-
return {
|
304
|
-
"Authorization": "Bearer " + cls.__project_api_key,
|
305
|
-
"Content-Type": "application/json",
|
306
|
-
"Accept": "application/json",
|
307
|
-
}
|
308
|
-
|
309
|
-
@classmethod
|
310
|
-
async def __get_session(cls):
|
311
|
-
if cls.__session is None:
|
312
|
-
cls.__session = aiohttp.ClientSession()
|
313
|
-
return cls.__session
|
lmnr-0.4.66.dist-info/RECORD
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
lmnr/__init__.py,sha256=UiFNMZL5IqflaxgAv5FXcbvFmw4xCQbF6RwXlNYmAAU,494
|
2
|
-
lmnr/cli.py,sha256=4J2RZQhHM3jJcjFvBC4PChQTS-ukxykVvI0X6lTkK-o,2918
|
3
|
-
lmnr/openllmetry_sdk/.flake8,sha256=bCxuDlGx3YQ55QHKPiGJkncHanh9qGjQJUujcFa3lAU,150
|
4
|
-
lmnr/openllmetry_sdk/__init__.py,sha256=G_sNpfg0rnA6n4AaKM88g_gwaCNFcg1Ew8dB8el4yT4,2765
|
5
|
-
lmnr/openllmetry_sdk/config/__init__.py,sha256=5aGdIdo1LffBkNwIBUbqzN6OUCMCrURU4b0rf5LBSI0,300
|
6
|
-
lmnr/openllmetry_sdk/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
lmnr/openllmetry_sdk/decorators/base.py,sha256=RNyvSreGBwwh8EWaa3O9samrnmKoZHK7eBzGOPT3lHc,7132
|
8
|
-
lmnr/openllmetry_sdk/instruments.py,sha256=9KoJ19Qar1dBrmO1wikNEkKxRfus8znQTj-g_maRTTM,1098
|
9
|
-
lmnr/openllmetry_sdk/tracing/__init__.py,sha256=xT73L1t2si2CM6QmMiTZ7zn-dKKYBLNrpBBWq6WfVBw,68
|
10
|
-
lmnr/openllmetry_sdk/tracing/attributes.py,sha256=cLBmSp4AMv9E91Ck3yD5Z1Qx1L5ZRV-80VJxFA-sO0Q,1426
|
11
|
-
lmnr/openllmetry_sdk/tracing/content_allow_list.py,sha256=3feztm6PBWNelc8pAZUcQyEGyeSpNiVKjOaDk65l2ps,846
|
12
|
-
lmnr/openllmetry_sdk/tracing/context_manager.py,sha256=rdSus-p-TaevQ8hIAhfbnZr5dTqRvACDkzXGDpflncY,306
|
13
|
-
lmnr/openllmetry_sdk/tracing/tracing.py,sha256=gn-3jIFHwPFzmzhcfxnqs0Y2YkBbIDtoHCTHFpzhCEQ,35194
|
14
|
-
lmnr/openllmetry_sdk/utils/__init__.py,sha256=pNhf0G3vTd5ccoc03i1MXDbricSaiqCbi1DLWhSekK8,604
|
15
|
-
lmnr/openllmetry_sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
|
16
|
-
lmnr/openllmetry_sdk/utils/json_encoder.py,sha256=dK6b_axr70IYL7Vv-bu4wntvDDuyntoqsHaddqX7P58,463
|
17
|
-
lmnr/openllmetry_sdk/utils/package_check.py,sha256=_-Fu9Zbp9tOyy27_-Rul7tDc8JaXYR2FmqF8SWOXSCc,244
|
18
|
-
lmnr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
lmnr/sdk/browser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
lmnr/sdk/browser/browser_use_otel.py,sha256=lsqc8cpCvpoWUSrodrOptdQSKDqvJ1HUWZ-UI3H-dVg,3917
|
22
|
-
lmnr/sdk/browser/playwright_otel.py,sha256=uj9-P8f0e1i17n5fp1V9GE9CVFjfzq-TiqBgw9RYK2A,10027
|
23
|
-
lmnr/sdk/browser/rrweb/rrweb.min.js,sha256=X5pgaoX1j_OjKTqGQgKB-83xUSuydNLQa-Kkh1AAZYM,140485
|
24
|
-
lmnr/sdk/browser/utils.py,sha256=IGiZc9ydRARk_J6PdxtQb4ffkuUYprdZLdsHYtdRrbU,3500
|
25
|
-
lmnr/sdk/client.py,sha256=f5MsMadaBlEfw9DOSgb6r2Y-l8O8gISnZ_gzzPcW-xU,10567
|
26
|
-
lmnr/sdk/datasets.py,sha256=XMbZtcrm56XPOezKhbyJQWdRZX3_wcxU1Eneld_vKVQ,1579
|
27
|
-
lmnr/sdk/decorators.py,sha256=g0VBqUEMCPRbgjgGHauVuKK1wHEd9rkiGzlYUYrcml4,2336
|
28
|
-
lmnr/sdk/eval_control.py,sha256=G6Fg3Xx_KWv72iBaWlNMdyRTF2bZFQnwJ68sJNSpIcY,177
|
29
|
-
lmnr/sdk/evaluations.py,sha256=2YccO4yiAxWUhXhFBW1D3OY44g1vdpbtKig5yVDlOrU,19712
|
30
|
-
lmnr/sdk/laminar.py,sha256=MKlcLdPBSS2KltSX0xhYIOx4D2rpOvuISCMUPfjYbrc,33884
|
31
|
-
lmnr/sdk/log.py,sha256=nt_YMmPw1IRbGy0b7q4rTtP4Yo3pQfNxqJPXK3nDSNQ,2213
|
32
|
-
lmnr/sdk/types.py,sha256=83rXmIGP12kyF0f27Wrw4kzJPBC44uyiaUwK32eusS0,10822
|
33
|
-
lmnr/sdk/utils.py,sha256=sD1YEqhdPaHweY2VGmjMF9MC-X7Ikdc49E01D-HF77E,3377
|
34
|
-
lmnr/version.py,sha256=1e1FKNz0PvRx7Z8F9UKp3GgWQ-IIiy8dRjfAuxHmGMk,1328
|
35
|
-
lmnr-0.4.66.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
36
|
-
lmnr-0.4.66.dist-info/METADATA,sha256=mNr-SMHD1amELqAE4xgMig5xAFpRJem0JhUm_s7R9Z8,13877
|
37
|
-
lmnr-0.4.66.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
38
|
-
lmnr-0.4.66.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
|
39
|
-
lmnr-0.4.66.dist-info/RECORD,,
|
File without changes
|
File without changes
|