lmnr 0.4.65__py3-none-any.whl → 0.5.0__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.
Files changed (37) hide show
  1. lmnr/__init__.py +30 -0
  2. lmnr/openllmetry_sdk/__init__.py +4 -15
  3. lmnr/openllmetry_sdk/tracing/attributes.py +0 -1
  4. lmnr/openllmetry_sdk/tracing/tracing.py +24 -9
  5. lmnr/sdk/browser/browser_use_otel.py +11 -12
  6. lmnr/sdk/browser/playwright_otel.py +214 -229
  7. lmnr/sdk/browser/pw_utils.py +289 -0
  8. lmnr/sdk/browser/utils.py +18 -53
  9. lmnr/sdk/client/asynchronous/async_client.py +157 -0
  10. lmnr/sdk/client/asynchronous/resources/__init__.py +13 -0
  11. lmnr/sdk/client/asynchronous/resources/agent.py +215 -0
  12. lmnr/sdk/client/asynchronous/resources/base.py +32 -0
  13. lmnr/sdk/client/asynchronous/resources/browser_events.py +40 -0
  14. lmnr/sdk/client/asynchronous/resources/evals.py +64 -0
  15. lmnr/sdk/client/asynchronous/resources/pipeline.py +89 -0
  16. lmnr/sdk/client/asynchronous/resources/semantic_search.py +60 -0
  17. lmnr/sdk/client/synchronous/resources/__init__.py +7 -0
  18. lmnr/sdk/client/synchronous/resources/agent.py +209 -0
  19. lmnr/sdk/client/synchronous/resources/base.py +32 -0
  20. lmnr/sdk/client/synchronous/resources/browser_events.py +40 -0
  21. lmnr/sdk/client/synchronous/resources/evals.py +102 -0
  22. lmnr/sdk/client/synchronous/resources/pipeline.py +89 -0
  23. lmnr/sdk/client/synchronous/resources/semantic_search.py +60 -0
  24. lmnr/sdk/client/synchronous/sync_client.py +170 -0
  25. lmnr/sdk/datasets.py +7 -2
  26. lmnr/sdk/evaluations.py +53 -27
  27. lmnr/sdk/laminar.py +22 -175
  28. lmnr/sdk/types.py +121 -23
  29. lmnr/sdk/utils.py +10 -0
  30. lmnr/version.py +6 -6
  31. {lmnr-0.4.65.dist-info → lmnr-0.5.0.dist-info}/METADATA +88 -38
  32. lmnr-0.5.0.dist-info/RECORD +55 -0
  33. lmnr/sdk/client.py +0 -313
  34. lmnr-0.4.65.dist-info/RECORD +0 -39
  35. {lmnr-0.4.65.dist-info → lmnr-0.5.0.dist-info}/LICENSE +0 -0
  36. {lmnr-0.4.65.dist-info → lmnr-0.5.0.dist-info}/WHEEL +0 -0
  37. {lmnr-0.4.65.dist-info → lmnr-0.5.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lmnr
3
- Version: 0.4.65
3
+ Version: 0.5.0
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: opentelemetry-api (>=1.28.0)
46
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (>=1.28.0)
47
- Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.28.0)
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.50b0)
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.50b0)
90
- Requires-Dist: opentelemetry-instrumentation-threading (>=0.50b0)
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.50b0)
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.28.0)
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
- Note that you need to only initialize Laminar once in your application.
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
- ## Laminar pipelines as prompt chain managers
304
+ ## Client for HTTP operations
285
305
 
286
- You can create Laminar pipelines in the UI and manage chains of LLM calls there.
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
- After you are ready to use your pipeline in your code, deploy it in Laminar by selecting the target version for the pipeline.
309
+ ### Agent
289
310
 
290
- Once your pipeline target is set, you can call it from Python in just a few lines.
291
-
292
- Example use:
311
+ To run Laminar agent, you can invoke `client.agent.run`
293
312
 
294
313
  ```python
295
- from lmnr import Laminar
314
+ from lmnr import LaminarClient
296
315
 
297
- Laminar.initialize('<YOUR_PROJECT_API_KEY>', instruments=set())
316
+ client = LaminarClient(project_api_key="<YOUR_PROJECT_API_KEY>")
298
317
 
299
- result = Laminar.run(
300
- pipeline = 'my_pipeline_name',
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
- Resulting in:
325
+ #### Streaming
326
+
327
+ Agent run supports streaming as well.
308
328
 
309
329
  ```python
310
- >>> result
311
- PipelineRunResponse(
312
- outputs={'output': {'value': [ChatMessage(role='user', content='hello')]}},
313
- # useful to locate your trace
314
- run_id='53b012d5-5759-48a6-a9c5-0011610e3669'
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
- ## Semantic search
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
- You can perform a semantic search on a dataset in Laminar by calling `Laminar.semantic_search`.
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
- response = Laminar.semantic_search(
324
- query="Greatest Chinese architectural wonders",
325
- dataset_id=uuid.UUID("413f8404-724c-4aa4-af16-714d84fd7958"),
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
+ else if 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=yy9rg5c973REVrVChuZivAN900vfCHAeVKrzaQI8iyw,2493
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=8hLYbi02cejlexT0M9uw7ziDcNVdjrXqJxYdWYRg7io,35824
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=uVc3dT0WtagrRZazP1l70jTjLGHbNVFNFYxzPha0nes,9575
23
+ lmnr/sdk/browser/pw_utils.py,sha256=EY9k0Pey71bGtxfPJk7JH0F0xxf_3KB1rs9UkF4FWsM,9292
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=JNFO9PkJOFN9fUsMNlZzmTkMVSZvnKbWaaE5wbz_5hk,8189
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=14CjcMPMbWuX9H5zQdBNP1juvpGpSinV3qCE70NKaac,8271
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=Rni12O8hr605L_kFr7arFSVa151kwQ8pYe2WHX2N8Iw,20595
46
+ lmnr/sdk/laminar.py,sha256=O4Qamyenb9OaUpLO6BUFeKoP4wt9gTGfBPPoqA0H1JQ,27620
47
+ lmnr/sdk/log.py,sha256=nt_YMmPw1IRbGy0b7q4rTtP4Yo3pQfNxqJPXK3nDSNQ,2213
48
+ lmnr/sdk/types.py,sha256=a0gFqAqEH1c_Bq8fh0dnU-j4jbxTWHBHgfe4maIBuTI,13592
49
+ lmnr/sdk/utils.py,sha256=o9MybEMM0Tp_xxCMFpzEYDsOAn2I7g1t7MrazOQAxAs,3692
50
+ lmnr/version.py,sha256=3D30L9ooaomFRi_CVWb48ZOmdeOy9tenONrZYTUmEGY,1321
51
+ lmnr-0.5.0.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
52
+ lmnr-0.5.0.dist-info/METADATA,sha256=ALTUNPf6zVwA3vVP48vKN2HWSusIamP0ISEimOw9yvs,14999
53
+ lmnr-0.5.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
54
+ lmnr-0.5.0.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
55
+ lmnr-0.5.0.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
@@ -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=OhUS_uIPRcA25EXQpcagY_hGsAVtJxkh69og9vRvrhc,3966
22
- lmnr/sdk/browser/playwright_otel.py,sha256=gqFOQCiNvQbuGuSmpUQNPfE1a68QZ6yUsfvA92ra_uc,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=Hjj2AvIZyO2U2-U6lqZbPOgOne3kXYH1lhbu3RtRcq8,1328
35
- lmnr-0.4.65.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
36
- lmnr-0.4.65.dist-info/METADATA,sha256=SLHcFjOlTbQkm58HNU2rLmGava74_kmxyG_65k0Sfu8,13877
37
- lmnr-0.4.65.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
38
- lmnr-0.4.65.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
39
- lmnr-0.4.65.dist-info/RECORD,,
File without changes
File without changes