lmnr 0.4.40__tar.gz → 0.4.43__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {lmnr-0.4.40 → lmnr-0.4.43}/PKG-INFO +14 -1
  2. {lmnr-0.4.40 → lmnr-0.4.43}/README.md +13 -0
  3. {lmnr-0.4.40 → lmnr-0.4.43}/pyproject.toml +4 -3
  4. lmnr-0.4.43/src/lmnr/cli.py +53 -0
  5. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/tracing/tracing.py +1 -5
  6. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/datasets.py +2 -4
  7. lmnr-0.4.43/src/lmnr/sdk/eval_control.py +4 -0
  8. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/evaluations.py +11 -28
  9. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/laminar.py +122 -46
  10. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/types.py +31 -2
  11. lmnr-0.4.40/src/lmnr/cli.py +0 -39
  12. {lmnr-0.4.40 → lmnr-0.4.43}/LICENSE +0 -0
  13. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/__init__.py +0 -0
  14. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/.flake8 +0 -0
  15. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/.python-version +0 -0
  16. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/__init__.py +0 -0
  17. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/config/__init__.py +0 -0
  18. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/decorators/__init__.py +0 -0
  19. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/decorators/base.py +0 -0
  20. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/instruments.py +0 -0
  21. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/tracing/__init__.py +0 -0
  22. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/tracing/attributes.py +0 -0
  23. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/tracing/content_allow_list.py +0 -0
  24. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/tracing/context_manager.py +0 -0
  25. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/utils/__init__.py +0 -0
  26. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/utils/in_memory_span_exporter.py +0 -0
  27. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/utils/json_encoder.py +0 -0
  28. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/utils/package_check.py +0 -0
  29. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/openllmetry_sdk/version.py +0 -0
  30. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/__init__.py +0 -0
  31. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/decorators.py +0 -0
  32. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/log.py +0 -0
  33. {lmnr-0.4.40 → lmnr-0.4.43}/src/lmnr/sdk/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lmnr
3
- Version: 0.4.40
3
+ Version: 0.4.43
4
4
  Summary: Python SDK for Laminar AI
5
5
  License: Apache-2.0
6
6
  Author: lmnr.ai
@@ -38,6 +38,7 @@ Provides-Extra: transformers
38
38
  Provides-Extra: vertexai
39
39
  Provides-Extra: watsonx
40
40
  Provides-Extra: weaviate
41
+ Requires-Dist: aiohttp (>=3.0,<4.0)
41
42
  Requires-Dist: argparse (>=1.0,<2.0)
42
43
  Requires-Dist: backoff (>=2.0,<3.0)
43
44
  Requires-Dist: deprecated (>=1.0,<2.0)
@@ -290,3 +291,15 @@ PipelineRunResponse(
290
291
  )
291
292
  ```
292
293
 
294
+ ## Semantic search
295
+
296
+ You can perform a semantic search on a dataset in Laminar by calling `Laminar.semantic_search`.
297
+
298
+ ```python
299
+ response = Laminar.semantic_search(
300
+ query="Greatest Chinese architectural wonders",
301
+ dataset_id=uuid.UUID("413f8404-724c-4aa4-af16-714d84fd7958"),
302
+ )
303
+ ```
304
+
305
+ [Read more](https://docs.lmnr.ai/datasets/indexing) about indexing and semantic search.
@@ -204,3 +204,16 @@ PipelineRunResponse(
204
204
  run_id='53b012d5-5759-48a6-a9c5-0011610e3669'
205
205
  )
206
206
  ```
207
+
208
+ ## Semantic search
209
+
210
+ You can perform a semantic search on a dataset in Laminar by calling `Laminar.semantic_search`.
211
+
212
+ ```python
213
+ response = Laminar.semantic_search(
214
+ query="Greatest Chinese architectural wonders",
215
+ dataset_id=uuid.UUID("413f8404-724c-4aa4-af16-714d84fd7958"),
216
+ )
217
+ ```
218
+
219
+ [Read more](https://docs.lmnr.ai/datasets/indexing) about indexing and semantic search.
@@ -6,7 +6,7 @@
6
6
 
7
7
  [project]
8
8
  name = "lmnr"
9
- version = "0.4.40"
9
+ version = "0.4.43"
10
10
  description = "Python SDK for Laminar AI"
11
11
  authors = [
12
12
  { name = "lmnr.ai", email = "founders@lmnr.ai" }
@@ -17,7 +17,7 @@ license = "Apache-2.0"
17
17
 
18
18
  [tool.poetry]
19
19
  name = "lmnr"
20
- version = "0.4.40"
20
+ version = "0.4.43"
21
21
  description = "Python SDK for Laminar AI"
22
22
  authors = ["lmnr.ai"]
23
23
  readme = "README.md"
@@ -43,6 +43,7 @@ jinja2 = "~=3.0"
43
43
  deprecated = "~=1.0"
44
44
  tqdm = "~=4.0"
45
45
  argparse = "~=1.0"
46
+ aiohttp = "~=3.0"
46
47
  opentelemetry-instrumentation-alephalpha = {version = ">=0.33.12", optional = true}
47
48
  opentelemetry-instrumentation-anthropic = {version = ">=0.33.12", optional = true}
48
49
  opentelemetry-instrumentation-bedrock = {version = ">=0.33.12", optional = true}
@@ -69,6 +70,7 @@ opentelemetry-instrumentation-vertexai = {version = ">=0.33.12", optional = true
69
70
  opentelemetry-instrumentation-watsonx = {version = ">=0.33.12", optional = true}
70
71
  opentelemetry-instrumentation-weaviate = {version = ">=0.33.12", optional = true}
71
72
 
73
+ [tool.poetry.extras]
72
74
  # List of all possible extras. You can specify one or more of these extras
73
75
  # when installing the package, using any of the following:
74
76
  # `pip install 'lmnr[anthropic,openai]'`
@@ -78,7 +80,6 @@ opentelemetry-instrumentation-weaviate = {version = ">=0.33.12", optional = true
78
80
 
79
81
  # `all` is the group added for convenience, if you want to install all
80
82
  # the instrumentations.
81
- [tool.poetry.extras]
82
83
  all = [
83
84
  "opentelemetry-instrumentation-alephalpha",
84
85
  "opentelemetry-instrumentation-anthropic",
@@ -0,0 +1,53 @@
1
+ from argparse import ArgumentParser
2
+ import asyncio
3
+ import importlib.util
4
+ import os
5
+ import sys
6
+
7
+ from .sdk.eval_control import PREPARE_ONLY, EVALUATION_INSTANCE
8
+
9
+
10
+ async def run_evaluation(args):
11
+ sys.path.append(os.getcwd())
12
+
13
+ prep_token = PREPARE_ONLY.set(True)
14
+ try:
15
+ file = os.path.abspath(args.file)
16
+ name = "user_module"
17
+
18
+ spec = importlib.util.spec_from_file_location(name, file)
19
+ if spec is None or spec.loader is None:
20
+ raise ImportError(f"Could not load module specification from {file}")
21
+ mod = importlib.util.module_from_spec(spec)
22
+ sys.modules[name] = mod
23
+
24
+ spec.loader.exec_module(mod)
25
+ evaluation = EVALUATION_INSTANCE.get()
26
+ if evaluation is None:
27
+ raise RuntimeError("Evaluation instance not found")
28
+
29
+ await evaluation.run()
30
+ finally:
31
+ PREPARE_ONLY.reset(prep_token)
32
+
33
+
34
+ def cli():
35
+ parser = ArgumentParser(
36
+ prog="lmnr",
37
+ description="CLI for Laminar",
38
+ )
39
+
40
+ subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")
41
+
42
+ parser_eval = subparsers.add_parser(
43
+ "eval",
44
+ description="Run an evaluation",
45
+ help="Run an evaluation",
46
+ )
47
+ parser_eval.add_argument("file", help="A file containing the evaluation to run")
48
+
49
+ parsed = parser.parse_args()
50
+ if parsed.subcommand == "eval":
51
+ asyncio.run(run_evaluation(parsed))
52
+ else:
53
+ parser.print_help()
@@ -183,11 +183,7 @@ class TracerWrapper(object):
183
183
 
184
184
  @classmethod
185
185
  def verify_initialized(cls) -> bool:
186
- if hasattr(cls, "instance"):
187
- return True
188
-
189
- cls.__logger.warning("Laminar not initialized, make sure to initialize")
190
- return False
186
+ return hasattr(cls, "instance")
191
187
 
192
188
  def flush(self):
193
189
  self.__spans_processor.force_flush()
@@ -1,11 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
- import logging
2
+ import asyncio
3
3
 
4
4
  from .log import get_default_logger
5
5
  from .laminar import Laminar as L
6
- from .types import (
7
- Datapoint,
8
- )
6
+ from .types import Datapoint
9
7
 
10
8
  DEFAULT_FETCH_SIZE = 25
11
9
 
@@ -0,0 +1,4 @@
1
+ from contextvars import ContextVar
2
+
3
+ PREPARE_ONLY = ContextVar("__lmnr_prepare_only", default=False)
4
+ EVALUATION_INSTANCE = ContextVar("__lmnr_evaluation_instance", default=None)
@@ -3,7 +3,6 @@ import re
3
3
  import sys
4
4
  import uuid
5
5
 
6
- from contextlib import contextmanager
7
6
  from tqdm import tqdm
8
7
  from typing import Any, Awaitable, Optional, Set, Union
9
8
 
@@ -11,6 +10,7 @@ from ..openllmetry_sdk.instruments import Instruments
11
10
  from ..openllmetry_sdk.tracing.attributes import SPAN_TYPE
12
11
 
13
12
  from .datasets import EvaluationDataset
13
+ from .eval_control import EVALUATION_INSTANCE, PREPARE_ONLY
14
14
  from .laminar import Laminar as L
15
15
  from .log import get_default_logger
16
16
  from .types import (
@@ -28,21 +28,6 @@ from .utils import is_async
28
28
 
29
29
  DEFAULT_BATCH_SIZE = 5
30
30
 
31
- _evaluation = None
32
- _set_global_evaluation = False
33
-
34
-
35
- @contextmanager
36
- def set_global_evaluation(set_global_evaluation: bool):
37
- global _set_global_evaluation
38
- original = _set_global_evaluation
39
- try:
40
- _set_global_evaluation = set_global_evaluation
41
- yield
42
- finally:
43
- _set_global_evaluation = original
44
- pass
45
-
46
31
 
47
32
  def get_evaluation_url(project_id: str, evaluation_id: str):
48
33
  return f"https://www.lmnr.ai/project/{project_id}/evaluations/{evaluation_id}"
@@ -198,15 +183,10 @@ class Evaluation:
198
183
  instruments=instruments,
199
184
  )
200
185
 
201
- def run(self) -> Union[None, Awaitable[None]]:
186
+ async def run(self) -> Awaitable[None]:
202
187
  if self.is_finished:
203
188
  raise Exception("Evaluation is already finished")
204
-
205
- loop = asyncio.get_event_loop()
206
- if loop.is_running():
207
- return loop.create_task(self._run())
208
- else:
209
- return loop.run_until_complete(self._run())
189
+ return await self._run()
210
190
 
211
191
  async def _run(self) -> None:
212
192
  self.reporter.start(len(self.data))
@@ -224,7 +204,7 @@ class Evaluation:
224
204
  for result_datapoint in result_datapoints:
225
205
  result_datapoint.human_evaluators = self.human_evaluators or {}
226
206
 
227
- evaluation = L.create_evaluation(
207
+ evaluation = await L.create_evaluation(
228
208
  data=result_datapoints, group_id=self.group_id, name=self.name
229
209
  )
230
210
  average_scores = get_average_scores(result_datapoints)
@@ -389,8 +369,11 @@ def evaluate(
389
369
  instruments=instruments,
390
370
  )
391
371
 
392
- global _evaluation
393
- if _set_global_evaluation:
394
- _evaluation = evaluation
372
+ if PREPARE_ONLY.get():
373
+ EVALUATION_INSTANCE.set(evaluation)
395
374
  else:
396
- return evaluation.run()
375
+ loop = asyncio.get_event_loop()
376
+ if loop.is_running():
377
+ return loop.run_until_complete(evaluation.run())
378
+ else:
379
+ return asyncio.run(evaluation.run())
@@ -16,8 +16,10 @@ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExport
16
16
  from opentelemetry.util.types import AttributeValue
17
17
 
18
18
  from pydantic.alias_generators import to_snake
19
- from typing import Any, Literal, Optional, Set, Union
19
+ from typing import Any, Awaitable, Literal, Optional, Set, Union
20
20
 
21
+ import aiohttp
22
+ import asyncio
21
23
  import copy
22
24
  import datetime
23
25
  import dotenv
@@ -25,8 +27,8 @@ import json
25
27
  import logging
26
28
  import os
27
29
  import random
28
- import re
29
30
  import requests
31
+ import re
30
32
  import urllib.parse
31
33
  import uuid
32
34
 
@@ -54,6 +56,8 @@ from .types import (
54
56
  PipelineRunResponse,
55
57
  NodeInput,
56
58
  PipelineRunRequest,
59
+ SemanticSearchRequest,
60
+ SemanticSearchResponse,
57
61
  TraceType,
58
62
  )
59
63
 
@@ -64,7 +68,6 @@ class Laminar:
64
68
  __project_api_key: Optional[str] = None
65
69
  __env: dict[str, str] = {}
66
70
  __initialized: bool = False
67
- __http_session: Optional[requests.Session] = None
68
71
 
69
72
  @classmethod
70
73
  def initialize(
@@ -129,7 +132,6 @@ class Laminar:
129
132
  cls.__env = env
130
133
  cls.__initialized = True
131
134
  cls._initialize_logger()
132
- cls.__http_session = requests.Session()
133
135
  Traceloop.init(
134
136
  exporter=OTLPSpanExporter(
135
137
  endpoint=cls.__base_grpc_url,
@@ -164,8 +166,9 @@ class Laminar:
164
166
  metadata: dict[str, str] = {},
165
167
  parent_span_id: Optional[uuid.UUID] = None,
166
168
  trace_id: Optional[uuid.UUID] = None,
167
- ) -> PipelineRunResponse:
168
- """Runs the pipeline with the given inputs
169
+ ) -> Union[PipelineRunResponse, Awaitable[PipelineRunResponse]]:
170
+ """Runs the pipeline with the given inputs. If called from an async
171
+ function, must be awaited.
169
172
 
170
173
  Args:
171
174
  pipeline (str): name of the Laminar pipeline.\
@@ -215,34 +218,47 @@ class Laminar:
215
218
  parent_span_id=parent_span_id,
216
219
  trace_id=trace_id,
217
220
  )
221
+ loop = asyncio.get_event_loop()
222
+ if loop.is_running():
223
+ return cls.__run(request)
224
+ else:
225
+ return asyncio.run(cls.__run(request))
218
226
  except Exception as e:
219
227
  raise ValueError(f"Invalid request: {e}")
220
228
 
221
- response = (
222
- cls.__http_session.post(
223
- cls.__base_http_url + "/v1/pipeline/run",
224
- data=json.dumps(request.to_dict()),
225
- headers=cls._headers(),
226
- )
227
- if cls.__http_session
228
- else requests.post(
229
- cls.__base_http_url + "/v1/pipeline/run",
230
- data=json.dumps(request.to_dict()),
231
- headers=cls._headers(),
232
- )
229
+ @classmethod
230
+ def semantic_search(
231
+ cls,
232
+ query: str,
233
+ dataset_id: uuid.UUID,
234
+ limit: Optional[int] = None,
235
+ threshold: Optional[float] = None,
236
+ ) -> SemanticSearchResponse:
237
+ """Perform a semantic search on a dataset. If called from an async
238
+ function, must be awaited.
239
+
240
+ Args:
241
+ query (str): query string to search by
242
+ dataset_id (uuid.UUID): id of the dataset to search in
243
+ limit (Optional[int], optional): maximum number of results to\
244
+ return. Defaults to None.
245
+ threshold (Optional[float], optional): minimum score for a result\
246
+ to be returned. Defaults to None.
247
+
248
+ Returns:
249
+ SemanticSearchResponse: response object containing the search results sorted by score in descending order
250
+ """
251
+ request = SemanticSearchRequest(
252
+ query=query,
253
+ dataset_id=dataset_id,
254
+ limit=limit,
255
+ threshold=threshold,
233
256
  )
234
- if response.status_code != 200:
235
- raise PipelineRunError(response)
236
- try:
237
- resp_json = response.json()
238
- keys = list(resp_json.keys())
239
- for key in keys:
240
- value = resp_json[key]
241
- del resp_json[key]
242
- resp_json[to_snake(key)] = value
243
- return PipelineRunResponse(**resp_json)
244
- except Exception:
245
- raise PipelineRunError(response)
257
+ loop = asyncio.get_event_loop()
258
+ if loop.is_running():
259
+ return cls.__semantic_search(request)
260
+ else:
261
+ return asyncio.run(cls.__semantic_search(request))
246
262
 
247
263
  @classmethod
248
264
  def event(
@@ -329,6 +345,10 @@ class Laminar:
329
345
  span. Defaults to None.
330
346
  """
331
347
 
348
+ if not cls.is_initialized():
349
+ yield
350
+ return
351
+
332
352
  with get_tracer() as tracer:
333
353
  span_path = get_span_path(name)
334
354
  ctx = set_value("span_path", span_path, context)
@@ -646,30 +666,33 @@ class Laminar:
646
666
  set_association_properties(props)
647
667
 
648
668
  @classmethod
649
- def create_evaluation(
669
+ async def create_evaluation(
650
670
  cls,
651
671
  data: list[EvaluationResultDatapoint],
652
672
  group_id: Optional[str] = None,
653
673
  name: Optional[str] = None,
654
674
  ) -> CreateEvaluationResponse:
655
- response = requests.post(
656
- cls.__base_http_url + "/v1/evaluations",
657
- data=json.dumps(
658
- {
675
+ async with aiohttp.ClientSession() as session:
676
+ async with session.post(
677
+ cls.__base_http_url + "/v1/evaluations",
678
+ json={
659
679
  "groupId": group_id,
660
680
  "name": name,
661
681
  "points": [datapoint.to_dict() for datapoint in data],
662
- }
663
- ),
664
- headers=cls._headers(),
665
- )
666
- if response.status_code != 200:
667
- try:
668
- resp_json = response.json()
669
- raise ValueError(f"Error creating evaluation {json.dumps(resp_json)}")
670
- except requests.exceptions.RequestException:
671
- raise ValueError(f"Error creating evaluation {response.text}")
672
- return CreateEvaluationResponse.model_validate(response.json())
682
+ },
683
+ headers=cls._headers(),
684
+ ) as response:
685
+ if response.status != 200:
686
+ try:
687
+ resp_json = await response.json()
688
+ raise ValueError(
689
+ f"Error creating evaluation {json.dumps(resp_json)}"
690
+ )
691
+ except aiohttp.ClientError:
692
+ text = await response.text()
693
+ raise ValueError(f"Error creating evaluation {text}")
694
+ resp_json = await response.json()
695
+ return CreateEvaluationResponse.model_validate(resp_json)
673
696
 
674
697
  @classmethod
675
698
  def get_datapoints(
@@ -678,6 +701,10 @@ class Laminar:
678
701
  offset: int,
679
702
  limit: int,
680
703
  ) -> GetDatapointsResponse:
704
+ # TODO: Use aiohttp. Currently, this function is called from within
705
+ # `LaminarDataset.__len__`, which is sync, but can be called from
706
+ # both sync and async. Python does not make it easy to mix things this
707
+ # way, so we should probably refactor `LaminarDataset`.
681
708
  params = {"name": dataset_name, "offset": offset, "limit": limit}
682
709
  url = (
683
710
  cls.__base_http_url
@@ -704,3 +731,52 @@ class Laminar:
704
731
  "Authorization": "Bearer " + cls.__project_api_key,
705
732
  "Content-Type": "application/json",
706
733
  }
734
+
735
+ @classmethod
736
+ async def __run(
737
+ cls,
738
+ request: PipelineRunRequest,
739
+ ) -> PipelineRunResponse:
740
+ async with aiohttp.ClientSession() as session:
741
+ async with session.post(
742
+ cls.__base_http_url + "/v1/pipeline/run",
743
+ data=json.dumps(request.to_dict()),
744
+ headers=cls._headers(),
745
+ ) as response:
746
+ if response.status != 200:
747
+ raise PipelineRunError(response)
748
+ try:
749
+ resp_json = await response.json()
750
+ keys = list(resp_json.keys())
751
+ for key in keys:
752
+ value = resp_json[key]
753
+ del resp_json[key]
754
+ resp_json[to_snake(key)] = value
755
+ return PipelineRunResponse(**resp_json)
756
+ except Exception:
757
+ raise PipelineRunError(response)
758
+
759
+ @classmethod
760
+ async def __semantic_search(
761
+ cls,
762
+ request: SemanticSearchRequest,
763
+ ) -> SemanticSearchResponse:
764
+ async with aiohttp.ClientSession() as session:
765
+ async with session.post(
766
+ cls.__base_http_url + "/v1/semantic-search",
767
+ data=json.dumps(request.to_dict()),
768
+ headers=cls._headers(),
769
+ ) as response:
770
+ if response.status != 200:
771
+ raise ValueError(
772
+ f"Error performing semantic search: [{response.status}] {response.text}"
773
+ )
774
+ try:
775
+ resp_json = await response.json()
776
+ for result in resp_json["results"]:
777
+ result["dataset_id"] = uuid.UUID(result["datasetId"])
778
+ return SemanticSearchResponse(**resp_json)
779
+ except Exception as e:
780
+ raise ValueError(
781
+ f"Error parsing semantic search response: status={response.status} error={e}"
782
+ )
@@ -1,7 +1,7 @@
1
+ import aiohttp
1
2
  import datetime
2
3
  from enum import Enum
3
4
  import pydantic
4
- import requests
5
5
  from typing import Any, Awaitable, Callable, Optional, Union
6
6
  import uuid
7
7
 
@@ -55,11 +55,40 @@ class PipelineRunResponse(pydantic.BaseModel):
55
55
  run_id: str
56
56
 
57
57
 
58
+ class SemanticSearchRequest(pydantic.BaseModel):
59
+ query: str
60
+ dataset_id: uuid.UUID
61
+ limit: Optional[int] = pydantic.Field(default=None)
62
+ threshold: Optional[float] = pydantic.Field(default=None, ge=0.0, le=1.0)
63
+
64
+ def to_dict(self):
65
+ res = {
66
+ "query": self.query,
67
+ "datasetId": str(self.dataset_id),
68
+ }
69
+ if self.limit is not None:
70
+ res["limit"] = self.limit
71
+ if self.threshold is not None:
72
+ res["threshold"] = self.threshold
73
+ return res
74
+
75
+
76
+ class SemanticSearchResult(pydantic.BaseModel):
77
+ dataset_id: uuid.UUID
78
+ score: float
79
+ data: dict[str, Any]
80
+ content: str
81
+
82
+
83
+ class SemanticSearchResponse(pydantic.BaseModel):
84
+ results: list[SemanticSearchResult]
85
+
86
+
58
87
  class PipelineRunError(Exception):
59
88
  error_code: str
60
89
  error_message: str
61
90
 
62
- def __init__(self, response: requests.Response):
91
+ def __init__(self, response: aiohttp.ClientResponse):
63
92
  try:
64
93
  resp_json = response.json()
65
94
  self.error_code = resp_json["error_code"]
@@ -1,39 +0,0 @@
1
- from argparse import ArgumentParser
2
- import asyncio
3
- import importlib
4
- import os
5
- import sys
6
-
7
- from lmnr.sdk.evaluations import set_global_evaluation
8
-
9
-
10
- # TODO: Refactor this code
11
- async def run_evaluation(args):
12
- sys.path.insert(0, os.getcwd())
13
-
14
- with set_global_evaluation(True):
15
- file = os.path.abspath(args.file)
16
-
17
- spec = importlib.util.spec_from_file_location("run_eval", file)
18
- mod = importlib.util.module_from_spec(spec)
19
- spec.loader.exec_module(mod)
20
-
21
- from lmnr.sdk.evaluations import _evaluation
22
- evaluation = _evaluation
23
- await evaluation.run()
24
-
25
-
26
- def cli():
27
- parser = ArgumentParser(
28
- prog="lmnr",
29
- description="CLI for Laminar",
30
- )
31
-
32
- subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")
33
-
34
- parser_eval = subparsers.add_parser("eval", description="Run an evaluation")
35
- parser_eval.add_argument("file", help="A file containing the evaluation to run")
36
- parser_eval.set_defaults(func=run_evaluation)
37
-
38
- parsed = parser.parse_args()
39
- asyncio.run(parsed.func(parsed))
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes