semantic-router 0.1.0.dev4__tar.gz → 0.1.0.dev6__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.
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/PKG-INFO +3 -3
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/pyproject.toml +2 -2
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/__init__.py +1 -1
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/base.py +189 -30
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/hybrid_local.py +8 -10
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/local.py +21 -12
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/pinecone.py +265 -45
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/postgres.py +22 -11
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/qdrant.py +26 -12
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/routers/base.py +266 -112
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/routers/hybrid.py +136 -16
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/routers/semantic.py +0 -3
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/LICENSE +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/README.md +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/__init__.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/aurelio.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/base.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/bedrock.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/bm25.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/clip.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/cohere.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/fastembed.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/google.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/huggingface.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/mistral.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/openai.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/tfidf.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/vit.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/encoders/zure.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/__init__.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/linear.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/__init__.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/base.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/cohere.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/grammars/json.gbnf +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/llamacpp.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/mistral.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/ollama.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/openai.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/openrouter.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/llms/zure.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/py.typed +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/route.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/routers/__init__.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/schema.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/utils/__init__.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/utils/defaults.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/utils/function_call.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/utils/llm.py +0 -0
- {semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/utils/logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: semantic-router
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev6
|
|
4
4
|
Summary: Super fast semantic router for AI decision making
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Aurelio AI
|
|
@@ -40,7 +40,7 @@ Requires-Dist: numpy (>=1.25.2,<2.0.0)
|
|
|
40
40
|
Requires-Dist: openai (>=1.10.0,<2.0.0)
|
|
41
41
|
Requires-Dist: pillow (>=10.2.0,<11.0.0) ; (python_version < "3.13") and (extra == "vision")
|
|
42
42
|
Requires-Dist: pinecone (>=5.0.0) ; extra == "pinecone"
|
|
43
|
-
Requires-Dist: pinecone-text (>=0.7.1
|
|
43
|
+
Requires-Dist: pinecone-text (>=0.7.1) ; extra == "hybrid"
|
|
44
44
|
Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
|
|
45
45
|
Requires-Dist: pydantic (>=2.10.2,<3.0.0)
|
|
46
46
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "semantic-router"
|
|
3
|
-
version = "0.1.0.
|
|
3
|
+
version = "0.1.0.dev6"
|
|
4
4
|
description = "Super fast semantic router for AI decision making"
|
|
5
5
|
authors = ["Aurelio AI <hello@aurelio.ai>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -17,7 +17,7 @@ numpy = "^1.25.2"
|
|
|
17
17
|
colorlog = "^6.8.0"
|
|
18
18
|
pyyaml = "^6.0.1"
|
|
19
19
|
aurelio-sdk = {version = "^0.0.16"}
|
|
20
|
-
pinecone-text = {version = ">=0.7.1
|
|
20
|
+
pinecone-text = {version = ">=0.7.1", optional = true}
|
|
21
21
|
torch = {version = ">=2.1.0,<2.6.0", optional = true, python = "<3.13" }
|
|
22
22
|
transformers = {version = ">=4.36.2", optional = true, python = "<3.13" }
|
|
23
23
|
tokenizers = {version = ">=0.19", optional = true, python = "<3.13" }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
import time
|
|
3
4
|
from typing import Any, List, Optional, Tuple, Union, Dict
|
|
@@ -11,6 +12,15 @@ from semantic_router.route import Route
|
|
|
11
12
|
from semantic_router.utils.logger import logger
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
RETRY_WAIT_TIME = 2.5
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IndexConfig(BaseModel):
|
|
19
|
+
type: str
|
|
20
|
+
dimensions: int
|
|
21
|
+
vectors: int
|
|
22
|
+
|
|
23
|
+
|
|
14
24
|
class BaseIndex(BaseModel):
|
|
15
25
|
"""
|
|
16
26
|
Base class for indices using Pydantic's BaseModel.
|
|
@@ -34,26 +44,73 @@ class BaseIndex(BaseModel):
|
|
|
34
44
|
utterances: List[Any],
|
|
35
45
|
function_schemas: Optional[List[Dict[str, Any]]] = None,
|
|
36
46
|
metadata_list: List[Dict[str, Any]] = [],
|
|
47
|
+
**kwargs,
|
|
37
48
|
):
|
|
38
|
-
"""
|
|
39
|
-
Add embeddings to the index.
|
|
49
|
+
"""Add embeddings to the index.
|
|
40
50
|
This method should be implemented by subclasses.
|
|
41
51
|
"""
|
|
42
52
|
raise NotImplementedError("This method should be implemented by subclasses.")
|
|
43
53
|
|
|
44
|
-
def
|
|
54
|
+
async def aadd(
|
|
55
|
+
self,
|
|
56
|
+
embeddings: List[List[float]],
|
|
57
|
+
routes: List[str],
|
|
58
|
+
utterances: List[str],
|
|
59
|
+
function_schemas: Optional[Optional[List[Dict[str, Any]]]] = None,
|
|
60
|
+
metadata_list: List[Dict[str, Any]] = [],
|
|
61
|
+
**kwargs,
|
|
62
|
+
):
|
|
63
|
+
"""Add vectors to the index asynchronously.
|
|
64
|
+
This method should be implemented by subclasses.
|
|
65
|
+
"""
|
|
66
|
+
logger.warning("Async method not implemented.")
|
|
67
|
+
return self.add(
|
|
68
|
+
embeddings=embeddings,
|
|
69
|
+
routes=routes,
|
|
70
|
+
utterances=utterances,
|
|
71
|
+
function_schemas=function_schemas,
|
|
72
|
+
metadata_list=metadata_list,
|
|
73
|
+
**kwargs,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def get_utterances(self, include_metadata: bool = False) -> List[Utterance]:
|
|
45
77
|
"""Gets a list of route and utterance objects currently stored in the
|
|
46
78
|
index, including additional metadata.
|
|
47
79
|
|
|
48
|
-
:
|
|
49
|
-
|
|
50
|
-
:
|
|
80
|
+
:param include_metadata: Whether to include function schemas and metadata in
|
|
81
|
+
the returned Utterance objects.
|
|
82
|
+
:type include_metadata: bool
|
|
83
|
+
:return: A list of Utterance objects.
|
|
84
|
+
:rtype: List[Utterance]
|
|
51
85
|
"""
|
|
52
86
|
if self.index is None:
|
|
53
87
|
logger.warning("Index is None, could not retrieve utterances.")
|
|
54
88
|
return []
|
|
55
|
-
_, metadata = self._get_all(include_metadata=True)
|
|
89
|
+
_, metadata = self._get_all(include_metadata=True) # include_metadata required
|
|
56
90
|
route_tuples = parse_route_info(metadata=metadata)
|
|
91
|
+
if not include_metadata:
|
|
92
|
+
# we remove the metadata from the tuples (ie only keep 0, 1 items)
|
|
93
|
+
route_tuples = [x[:2] for x in route_tuples]
|
|
94
|
+
return [Utterance.from_tuple(x) for x in route_tuples]
|
|
95
|
+
|
|
96
|
+
async def aget_utterances(self, include_metadata: bool = False) -> List[Utterance]:
|
|
97
|
+
"""Gets a list of route and utterance objects currently stored in the
|
|
98
|
+
index, including additional metadata.
|
|
99
|
+
|
|
100
|
+
:param include_metadata: Whether to include function schemas and metadata in
|
|
101
|
+
the returned Utterance objects.
|
|
102
|
+
:type include_metadata: bool
|
|
103
|
+
:return: A list of Utterance objects.
|
|
104
|
+
:rtype: List[Utterance]
|
|
105
|
+
"""
|
|
106
|
+
if self.index is None:
|
|
107
|
+
logger.warning("Index is None, could not retrieve utterances.")
|
|
108
|
+
return []
|
|
109
|
+
_, metadata = await self._async_get_all(include_metadata=True)
|
|
110
|
+
route_tuples = parse_route_info(metadata=metadata)
|
|
111
|
+
if not include_metadata:
|
|
112
|
+
# we remove the metadata from the tuples (ie only keep 0, 1 items)
|
|
113
|
+
route_tuples = [x[:2] for x in route_tuples]
|
|
57
114
|
return [Utterance.from_tuple(x) for x in route_tuples]
|
|
58
115
|
|
|
59
116
|
def get_routes(self) -> List[Route]:
|
|
@@ -62,7 +119,7 @@ class BaseIndex(BaseModel):
|
|
|
62
119
|
:return: A list of Route objects.
|
|
63
120
|
:rtype: List[Route]
|
|
64
121
|
"""
|
|
65
|
-
utterances = self.get_utterances()
|
|
122
|
+
utterances = self.get_utterances(include_metadata=True)
|
|
66
123
|
routes_dict: Dict[str, Route] = {}
|
|
67
124
|
# first create a dictionary of route names to Route objects
|
|
68
125
|
for utt in utterances:
|
|
@@ -90,6 +147,14 @@ class BaseIndex(BaseModel):
|
|
|
90
147
|
"""
|
|
91
148
|
raise NotImplementedError("This method should be implemented by subclasses.")
|
|
92
149
|
|
|
150
|
+
async def _async_remove_and_sync(self, routes_to_delete: dict):
|
|
151
|
+
"""
|
|
152
|
+
Remove embeddings in a routes syncing process from the index asynchronously.
|
|
153
|
+
This method should be implemented by subclasses.
|
|
154
|
+
"""
|
|
155
|
+
logger.warning("Async method not implemented.")
|
|
156
|
+
return self._remove_and_sync(routes_to_delete=routes_to_delete)
|
|
157
|
+
|
|
93
158
|
def delete(self, route_name: str):
|
|
94
159
|
"""
|
|
95
160
|
Deletes route by route name.
|
|
@@ -97,10 +162,17 @@ class BaseIndex(BaseModel):
|
|
|
97
162
|
"""
|
|
98
163
|
raise NotImplementedError("This method should be implemented by subclasses.")
|
|
99
164
|
|
|
100
|
-
def describe(self) ->
|
|
165
|
+
def describe(self) -> IndexConfig:
|
|
101
166
|
"""
|
|
102
|
-
Returns
|
|
103
|
-
vector count.
|
|
167
|
+
Returns an IndexConfig object with index details such as type, dimensions, and
|
|
168
|
+
total vector count.
|
|
169
|
+
This method should be implemented by subclasses.
|
|
170
|
+
"""
|
|
171
|
+
raise NotImplementedError("This method should be implemented by subclasses.")
|
|
172
|
+
|
|
173
|
+
def is_ready(self) -> bool:
|
|
174
|
+
"""
|
|
175
|
+
Checks if the index is ready to be used.
|
|
104
176
|
This method should be implemented by subclasses.
|
|
105
177
|
"""
|
|
106
178
|
raise NotImplementedError("This method should be implemented by subclasses.")
|
|
@@ -159,6 +231,10 @@ class BaseIndex(BaseModel):
|
|
|
159
231
|
logger.warning("This method should be implemented by subclasses.")
|
|
160
232
|
self.index = None
|
|
161
233
|
|
|
234
|
+
# ___________________________ CONFIG ___________________________
|
|
235
|
+
# When implementing a new index, the following methods should be implemented
|
|
236
|
+
# to enable synchronization of remote indexes.
|
|
237
|
+
|
|
162
238
|
def _read_config(self, field: str, scope: str | None = None) -> ConfigParameter:
|
|
163
239
|
"""Read a config parameter from the index.
|
|
164
240
|
|
|
@@ -176,13 +252,20 @@ class BaseIndex(BaseModel):
|
|
|
176
252
|
scope=scope,
|
|
177
253
|
)
|
|
178
254
|
|
|
179
|
-
def
|
|
180
|
-
|
|
255
|
+
async def _async_read_config(
|
|
256
|
+
self, field: str, scope: str | None = None
|
|
257
|
+
) -> ConfigParameter:
|
|
258
|
+
"""Read a config parameter from the index asynchronously.
|
|
181
259
|
|
|
260
|
+
:param field: The field to read.
|
|
261
|
+
:type field: str
|
|
262
|
+
:param scope: The scope to read.
|
|
263
|
+
:type scope: str | None
|
|
182
264
|
:return: The config parameter that was read.
|
|
183
265
|
:rtype: ConfigParameter
|
|
184
266
|
"""
|
|
185
|
-
|
|
267
|
+
logger.warning("_async_read_config method not implemented.")
|
|
268
|
+
return self._read_config(field=field, scope=scope)
|
|
186
269
|
|
|
187
270
|
def _write_config(self, config: ConfigParameter) -> ConfigParameter:
|
|
188
271
|
"""Write a config parameter to the index.
|
|
@@ -195,6 +278,67 @@ class BaseIndex(BaseModel):
|
|
|
195
278
|
logger.warning("This method should be implemented by subclasses.")
|
|
196
279
|
return config
|
|
197
280
|
|
|
281
|
+
async def _async_write_config(self, config: ConfigParameter) -> ConfigParameter:
|
|
282
|
+
"""Write a config parameter to the index asynchronously.
|
|
283
|
+
|
|
284
|
+
:param config: The config parameter to write.
|
|
285
|
+
:type config: ConfigParameter
|
|
286
|
+
:return: The config parameter that was written.
|
|
287
|
+
:rtype: ConfigParameter
|
|
288
|
+
"""
|
|
289
|
+
logger.warning("Async method not implemented.")
|
|
290
|
+
return self._write_config(config=config)
|
|
291
|
+
|
|
292
|
+
# _________________________ END CONFIG _________________________
|
|
293
|
+
|
|
294
|
+
def _read_hash(self) -> ConfigParameter:
|
|
295
|
+
"""Read the hash of the previously written index.
|
|
296
|
+
|
|
297
|
+
:return: The config parameter that was read.
|
|
298
|
+
:rtype: ConfigParameter
|
|
299
|
+
"""
|
|
300
|
+
return self._read_config(field="sr_hash")
|
|
301
|
+
|
|
302
|
+
async def _async_read_hash(self) -> ConfigParameter:
|
|
303
|
+
"""Read the hash of the previously written index asynchronously.
|
|
304
|
+
|
|
305
|
+
:return: The config parameter that was read.
|
|
306
|
+
:rtype: ConfigParameter
|
|
307
|
+
"""
|
|
308
|
+
return await self._async_read_config(field="sr_hash")
|
|
309
|
+
|
|
310
|
+
def _is_locked(self, scope: str | None = None) -> bool:
|
|
311
|
+
"""Check if the index is locked for a given scope (if applicable).
|
|
312
|
+
|
|
313
|
+
:param scope: The scope to check.
|
|
314
|
+
:type scope: str | None
|
|
315
|
+
:return: True if the index is locked, False otherwise.
|
|
316
|
+
:rtype: bool
|
|
317
|
+
"""
|
|
318
|
+
lock_config = self._read_config(field="sr_lock", scope=scope)
|
|
319
|
+
if lock_config.value == "True":
|
|
320
|
+
return True
|
|
321
|
+
elif lock_config.value == "False" or not lock_config.value:
|
|
322
|
+
return False
|
|
323
|
+
else:
|
|
324
|
+
raise ValueError(f"Invalid lock value: {lock_config.value}")
|
|
325
|
+
|
|
326
|
+
async def _ais_locked(self, scope: str | None = None) -> bool:
|
|
327
|
+
"""Check if the index is locked for a given scope (if applicable).
|
|
328
|
+
|
|
329
|
+
:param scope: The scope to check.
|
|
330
|
+
:type scope: str | None
|
|
331
|
+
:return: True if the index is locked, False otherwise.
|
|
332
|
+
:rtype: bool
|
|
333
|
+
"""
|
|
334
|
+
lock_config = await self._async_read_config(field="sr_lock", scope=scope)
|
|
335
|
+
if lock_config.value == "True":
|
|
336
|
+
return True
|
|
337
|
+
elif lock_config.value == "False" or not lock_config.value:
|
|
338
|
+
return False
|
|
339
|
+
else:
|
|
340
|
+
raise ValueError(f"Invalid lock value: {lock_config.value}")
|
|
341
|
+
|
|
198
342
|
def lock(
|
|
199
343
|
self, value: bool, wait: int = 0, scope: str | None = None
|
|
200
344
|
) -> ConfigParameter:
|
|
@@ -214,9 +358,14 @@ class BaseIndex(BaseModel):
|
|
|
214
358
|
if self._is_locked(scope=scope) != value:
|
|
215
359
|
# in this case, we can set the lock value
|
|
216
360
|
break
|
|
361
|
+
elif not value:
|
|
362
|
+
# if unlocking, we can break immediately — often with Pinecone the
|
|
363
|
+
# lock/unlocked state takes a few seconds to update, so locking then
|
|
364
|
+
# unlocking quickly will fail without this check
|
|
365
|
+
break
|
|
217
366
|
if (datetime.now() - start_time).total_seconds() < wait:
|
|
218
|
-
# wait for
|
|
219
|
-
time.sleep(
|
|
367
|
+
# wait for a few seconds before checking again
|
|
368
|
+
time.sleep(RETRY_WAIT_TIME)
|
|
220
369
|
else:
|
|
221
370
|
raise ValueError(
|
|
222
371
|
f"Index is already {'locked' if value else 'unlocked'}."
|
|
@@ -229,21 +378,31 @@ class BaseIndex(BaseModel):
|
|
|
229
378
|
self._write_config(lock_param)
|
|
230
379
|
return lock_param
|
|
231
380
|
|
|
232
|
-
def
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
:return: True if the index is locked, False otherwise.
|
|
238
|
-
:rtype: bool
|
|
381
|
+
async def alock(
|
|
382
|
+
self, value: bool, wait: int = 0, scope: str | None = None
|
|
383
|
+
) -> ConfigParameter:
|
|
384
|
+
"""Lock/unlock the index for a given scope (if applicable). If index
|
|
385
|
+
already locked/unlocked, raises ValueError.
|
|
239
386
|
"""
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
387
|
+
start_time = datetime.now()
|
|
388
|
+
while True:
|
|
389
|
+
if await self._ais_locked(scope=scope) != value:
|
|
390
|
+
# in this case, we can set the lock value
|
|
391
|
+
break
|
|
392
|
+
if (datetime.now() - start_time).total_seconds() < wait:
|
|
393
|
+
# wait for a few seconds before checking again
|
|
394
|
+
await asyncio.sleep(RETRY_WAIT_TIME)
|
|
395
|
+
else:
|
|
396
|
+
raise ValueError(
|
|
397
|
+
f"Index is already {'locked' if value else 'unlocked'}."
|
|
398
|
+
)
|
|
399
|
+
lock_param = ConfigParameter(
|
|
400
|
+
field="sr_lock",
|
|
401
|
+
value=str(value),
|
|
402
|
+
scope=scope,
|
|
403
|
+
)
|
|
404
|
+
await self._async_write_config(lock_param)
|
|
405
|
+
return lock_param
|
|
247
406
|
|
|
248
407
|
def _get_all(self, prefix: Optional[str] = None, include_metadata: bool = False):
|
|
249
408
|
"""
|
{semantic_router-0.1.0.dev4 → semantic_router-0.1.0.dev6}/semantic_router/index/hybrid_local.py
RENAMED
|
@@ -25,6 +25,7 @@ class HybridLocalIndex(LocalIndex):
|
|
|
25
25
|
function_schemas: Optional[List[Dict[str, Any]]] = None,
|
|
26
26
|
metadata_list: List[Dict[str, Any]] = [],
|
|
27
27
|
sparse_embeddings: Optional[List[SparseEmbedding]] = None,
|
|
28
|
+
**kwargs,
|
|
28
29
|
):
|
|
29
30
|
if sparse_embeddings is None:
|
|
30
31
|
raise ValueError("Sparse embeddings are required for HybridLocalIndex.")
|
|
@@ -56,23 +57,20 @@ class HybridLocalIndex(LocalIndex):
|
|
|
56
57
|
self.routes = np.concatenate([self.routes, routes_arr])
|
|
57
58
|
self.utterances = np.concatenate([self.utterances, utterances_arr])
|
|
58
59
|
|
|
59
|
-
def get_utterances(self) -> List[Utterance]:
|
|
60
|
+
def get_utterances(self, include_metadata: bool = False) -> List[Utterance]:
|
|
60
61
|
"""Gets a list of route and utterance objects currently stored in the index.
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
:param include_metadata: Whether to include function schemas and metadata in
|
|
64
|
+
the returned Utterance objects - HybridLocalIndex doesn't include metadata so
|
|
65
|
+
this parameter is ignored.
|
|
66
|
+
:type include_metadata: bool
|
|
67
|
+
:return: A list of Utterance objects.
|
|
68
|
+
:rtype: List[Utterance]
|
|
64
69
|
"""
|
|
65
70
|
if self.routes is None or self.utterances is None:
|
|
66
71
|
return []
|
|
67
72
|
return [Utterance.from_tuple(x) for x in zip(self.routes, self.utterances)]
|
|
68
73
|
|
|
69
|
-
def describe(self) -> Dict:
|
|
70
|
-
return {
|
|
71
|
-
"type": self.type,
|
|
72
|
-
"dimensions": self.index.shape[1] if self.index is not None else 0,
|
|
73
|
-
"vectors": self.index.shape[0] if self.index is not None else 0,
|
|
74
|
-
}
|
|
75
|
-
|
|
76
74
|
def _sparse_dot_product(
|
|
77
75
|
self, vec_a: dict[int, float], vec_b: dict[int, float]
|
|
78
76
|
) -> float:
|
|
@@ -3,7 +3,7 @@ from typing import List, Optional, Tuple, Dict
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
5
|
from semantic_router.schema import ConfigParameter, SparseEmbedding, Utterance
|
|
6
|
-
from semantic_router.index.base import BaseIndex
|
|
6
|
+
from semantic_router.index.base import BaseIndex, IndexConfig
|
|
7
7
|
from semantic_router.linear import similarity_matrix, top_scores
|
|
8
8
|
from semantic_router.utils.logger import logger
|
|
9
9
|
from typing import Any
|
|
@@ -26,6 +26,7 @@ class LocalIndex(BaseIndex):
|
|
|
26
26
|
utterances: List[str],
|
|
27
27
|
function_schemas: Optional[List[Dict[str, Any]]] = None,
|
|
28
28
|
metadata_list: List[Dict[str, Any]] = [],
|
|
29
|
+
**kwargs,
|
|
29
30
|
):
|
|
30
31
|
embeds = np.array(embeddings) # type: ignore
|
|
31
32
|
routes_arr = np.array(routes)
|
|
@@ -63,23 +64,31 @@ class LocalIndex(BaseIndex):
|
|
|
63
64
|
# return what was removed
|
|
64
65
|
return route_utterances[~mask]
|
|
65
66
|
|
|
66
|
-
def get_utterances(self) -> List[Utterance]:
|
|
67
|
-
"""
|
|
68
|
-
Gets a list of route and utterance objects currently stored in the index.
|
|
67
|
+
def get_utterances(self, include_metadata: bool = False) -> List[Utterance]:
|
|
68
|
+
"""Gets a list of route and utterance objects currently stored in the index.
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
:param include_metadata: Whether to include function schemas and metadata in
|
|
71
|
+
the returned Utterance objects - LocalIndex doesn't include metadata so this
|
|
72
|
+
parameter is ignored.
|
|
73
|
+
:return: A list of Utterance objects.
|
|
74
|
+
:rtype: List[Utterance]
|
|
72
75
|
"""
|
|
73
76
|
if self.routes is None or self.utterances is None:
|
|
74
77
|
return []
|
|
75
78
|
return [Utterance.from_tuple(x) for x in zip(self.routes, self.utterances)]
|
|
76
79
|
|
|
77
|
-
def describe(self) ->
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
def describe(self) -> IndexConfig:
|
|
81
|
+
return IndexConfig(
|
|
82
|
+
type=self.type,
|
|
83
|
+
dimensions=self.index.shape[1] if self.index is not None else 0,
|
|
84
|
+
vectors=self.index.shape[0] if self.index is not None else 0,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def is_ready(self) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
Checks if the index is ready to be used.
|
|
90
|
+
"""
|
|
91
|
+
return self.index is not None and self.routes is not None
|
|
83
92
|
|
|
84
93
|
def query(
|
|
85
94
|
self,
|