nucliadb 6.3.4.post3817__py3-none-any.whl → 6.3.4.post3823__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.
@@ -36,7 +36,7 @@ from nucliadb.common.datamanagers.entities import (
36
36
  from nucliadb.common.maindb.driver import Transaction
37
37
  from nucliadb.ingest.orm.knowledgebox import KnowledgeBox
38
38
  from nucliadb.ingest.settings import settings
39
- from nucliadb.search.search.shards import query_shard
39
+ from nucliadb.search.search.shards import graph_search_shard, query_shard
40
40
  from nucliadb_protos.knowledgebox_pb2 import (
41
41
  DeletedEntitiesGroups,
42
42
  EntitiesGroup,
@@ -45,9 +45,8 @@ from nucliadb_protos.knowledgebox_pb2 import (
45
45
  )
46
46
  from nucliadb_protos.nodereader_pb2 import (
47
47
  Faceted,
48
- RelationNodeFilter,
49
- RelationPrefixSearchRequest,
50
- RelationSearchResponse,
48
+ GraphSearchRequest,
49
+ GraphSearchResponse,
51
50
  SearchRequest,
52
51
  SearchResponse,
53
52
  )
@@ -203,18 +202,16 @@ class EntitiesManager:
203
202
  async def get_indexed_entities_group(self, group: str) -> Optional[EntitiesGroup]:
204
203
  shard_manager = get_shard_manager()
205
204
 
206
- async def do_entities_search(node: AbstractIndexNode, shard_id: str) -> RelationSearchResponse:
207
- request = SearchRequest(
208
- shard=shard_id,
209
- relation_prefix=RelationPrefixSearchRequest(
210
- prefix="",
211
- node_filters=[
212
- RelationNodeFilter(node_type=RelationNode.NodeType.ENTITY, node_subtype=group)
213
- ],
214
- ),
215
- )
216
- response = await query_shard(node, shard_id, request)
217
- return response.relation
205
+ async def do_entities_search(node: AbstractIndexNode, shard_id: str) -> GraphSearchResponse:
206
+ request = GraphSearchRequest()
207
+ # XXX: this is a wild guess. Are those enough or too many?
208
+ request.top_k = 500
209
+ request.kind = GraphSearchRequest.QueryKind.NODES
210
+ request.query.path.path.source.node_type = RelationNode.NodeType.ENTITY
211
+ request.query.path.path.source.node_subtype = group
212
+ request.query.path.path.undirected = True
213
+ response = await graph_search_shard(node, shard_id, request)
214
+ return response
218
215
 
219
216
  results = await shard_manager.apply_for_all_shards(
220
217
  self.kbid,
@@ -224,7 +221,7 @@ class EntitiesManager:
224
221
 
225
222
  entities = {}
226
223
  for result in results:
227
- entities.update({node.value: Entity(value=node.value) for node in result.prefix.nodes})
224
+ entities.update({node.value: Entity(value=node.value) for node in result.nodes})
228
225
 
229
226
  if not entities:
230
227
  return None
@@ -44,7 +44,6 @@ from nucliadb.search.search.find_merge import (
44
44
  hydrate_and_rerank,
45
45
  )
46
46
  from nucliadb.search.search.hydrator import ResourceHydrationOptions, TextBlockHydrationOptions
47
- from nucliadb.search.search.merge import merge_relation_prefix_results
48
47
  from nucliadb.search.search.metrics import RAGMetrics
49
48
  from nucliadb.search.search.rerankers import Reranker, RerankingOptions
50
49
  from nucliadb.search.utilities import get_predict
@@ -65,6 +64,7 @@ from nucliadb_models.search import (
65
64
  NucliaDBClientType,
66
65
  QueryEntityDetection,
67
66
  RelatedEntities,
67
+ RelatedEntity,
68
68
  RelationDirection,
69
69
  RelationRanking,
70
70
  Relations,
@@ -443,26 +443,35 @@ async def fuzzy_search_entities(
443
443
  ) -> Optional[RelatedEntities]:
444
444
  """Fuzzy find entities in KB given a query using the same methodology as /suggest, but split by words."""
445
445
 
446
- request = nodereader_pb2.SearchRequest()
447
- request.relation_prefix.query = query
446
+ # Build an OR for each word in the query matching with fuzzy any word in any
447
+ # node in any position. I.e., for the query "Rose Hamiltn", it'll match
448
+ # "Rosa Parks" and "Margaret Hamilton"
449
+ request = nodereader_pb2.GraphSearchRequest()
450
+ # XXX Are those enough results? Too many?
451
+ request.top_k = 50
452
+ request.kind = nodereader_pb2.GraphSearchRequest.QueryKind.NODES
453
+ for word in query.split():
454
+ subquery = nodereader_pb2.GraphQuery.PathQuery()
455
+ subquery.path.source.value = word
456
+ subquery.path.source.fuzzy.kind = nodereader_pb2.GraphQuery.Node.MatchLocation.WORDS
457
+ subquery.path.source.fuzzy.distance = 1
458
+ subquery.path.undirected = True
459
+ request.query.path.bool_or.operands.append(subquery)
448
460
 
449
- results: list[nodereader_pb2.SearchResponse]
450
461
  try:
451
- (
452
- results,
453
- _,
454
- _,
455
- ) = await node_query(
456
- kbid,
457
- Method.SEARCH,
458
- request,
459
- )
460
- return merge_relation_prefix_results(results)
461
- except Exception as e:
462
- capture_exception(e)
462
+ results, _, _ = await node_query(kbid, Method.GRAPH, request)
463
+ except Exception as exc:
464
+ capture_exception(exc)
463
465
  logger.exception("Error in finding entities in query for graph strategy")
464
466
  return None
465
467
 
468
+ # merge shard results while deduplicating repeated entities across shards
469
+ unique_entities: set[RelatedEntity] = set()
470
+ for response in results:
471
+ unique_entities.update((RelatedEntity(family=e.subtype, value=e.value) for e in response.nodes))
472
+
473
+ return RelatedEntities(entities=list(unique_entities), total=len(unique_entities))
474
+
466
475
 
467
476
  async def rank_relations_reranker(
468
477
  relations: Relations,
@@ -632,19 +632,6 @@ async def merge_suggest_entities_results(
632
632
  return RelatedEntities(entities=list(unique_entities), total=len(unique_entities))
633
633
 
634
634
 
635
- def merge_relation_prefix_results(
636
- responses: list[SearchResponse],
637
- ) -> RelatedEntities:
638
- unique_entities: Set[RelatedEntity] = set()
639
- for response in responses:
640
- response_entities = (
641
- RelatedEntity(family=e.subtype, value=e.value) for e in response.relation.prefix.nodes
642
- )
643
- unique_entities.update(response_entities)
644
-
645
- return RelatedEntities(entities=list(unique_entities), total=len(unique_entities))
646
-
647
-
648
635
  async def merge_suggest_results(
649
636
  suggest_responses: list[SuggestResponse],
650
637
  kbid: str,
@@ -17,6 +17,7 @@
17
17
  # You should have received a copy of the GNU Affero General Public License
18
18
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
19
  #
20
+ import asyncio
20
21
  from typing import Optional, TypeVar, Union
21
22
 
22
23
  from async_lru import alru_cache
@@ -53,12 +54,9 @@ def is_cached(field: Union[T, NotCached]) -> TypeIs[T]:
53
54
 
54
55
  class FetcherCache:
55
56
  predict_query_info: Union[Optional[QueryInfo], NotCached] = not_cached
56
- predict_detected_entities: Union[list[utils_pb2.RelationNode], NotCached] = not_cached
57
57
 
58
58
  # semantic search
59
- query_vector: Union[Optional[list[float]], NotCached] = not_cached
60
59
  vectorset: Union[str, NotCached] = not_cached
61
- matryoshka_dimension: Union[Optional[int], NotCached] = not_cached
62
60
 
63
61
  labels: Union[knowledgebox_pb2.Labels, NotCached] = not_cached
64
62
 
@@ -97,103 +95,80 @@ class Fetcher:
97
95
  self.query = query
98
96
  self.user_vector = user_vector
99
97
  self.user_vectorset = vectorset
98
+ self.user_vectorset_validated = False
100
99
  self.rephrase = rephrase
101
100
  self.rephrase_prompt = rephrase_prompt
102
101
  self.generative_model = generative_model
103
102
 
104
103
  self.cache = FetcherCache()
105
- self._validated = False
106
-
107
- # Validation
108
-
109
- async def initial_validate(self):
110
- """Runs a validation on the input parameters. It can raise errors if
111
- there's some wrong parameter.
112
-
113
- This function should be always called if validated input for fetching is
114
- desired
115
- """
116
- if self._validated:
117
- return
118
-
119
- self._validated = True
120
-
121
- async def _validate_vectorset(self):
122
- if self.user_vectorset is not None:
123
- await validate_vectorset(self.kbid, self.user_vectorset)
104
+ self.locks: dict[str, asyncio.Lock] = {}
124
105
 
125
106
  # Semantic search
126
107
 
127
108
  async def get_matryoshka_dimension(self) -> Optional[int]:
128
- if is_cached(self.cache.matryoshka_dimension):
129
- return self.cache.matryoshka_dimension
130
-
131
109
  vectorset = await self.get_vectorset()
132
- matryoshka_dimension = await get_matryoshka_dimension_cached(self.kbid, vectorset)
133
- self.cache.matryoshka_dimension = matryoshka_dimension
134
- return matryoshka_dimension
110
+ return await get_matryoshka_dimension_cached(self.kbid, vectorset)
135
111
 
136
112
  async def _get_user_vectorset(self) -> Optional[str]:
137
113
  """Returns the user's requested vectorset and validates if it does exist
138
114
  in the KB.
139
115
 
140
116
  """
141
- vectorset = self.user_vectorset
142
- if not self._validated:
143
- await self._validate_vectorset()
144
- return vectorset
117
+ async with self.locks.setdefault("user_vectorset", asyncio.Lock()):
118
+ if not self.user_vectorset_validated:
119
+ if self.user_vectorset is not None:
120
+ await validate_vectorset(self.kbid, self.user_vectorset)
121
+ self.user_vectorset_validated = True
122
+ return self.user_vectorset
145
123
 
146
124
  async def get_vectorset(self) -> str:
147
125
  """Get the vectorset to be used in the search. If not specified, by the
148
126
  user, Predict API or the own uses KB will provide a default.
149
127
 
150
128
  """
129
+ async with self.locks.setdefault("vectorset", asyncio.Lock()):
130
+ if is_cached(self.cache.vectorset):
131
+ return self.cache.vectorset
151
132
 
152
- if is_cached(self.cache.vectorset):
153
- return self.cache.vectorset
154
-
155
- if self.user_vectorset:
156
- # user explicitly asked for a vectorset
157
- self.cache.vectorset = self.user_vectorset
158
- return self.user_vectorset
133
+ user_vectorset = await self._get_user_vectorset()
134
+ if user_vectorset:
135
+ # user explicitly asked for a vectorset
136
+ self.cache.vectorset = user_vectorset
137
+ return user_vectorset
159
138
 
160
- # when it's not provided, we get the default from Predict API
161
- query_info = await self._predict_query_endpoint()
162
- if query_info is None:
163
- vectorset = None
164
- else:
165
- if query_info.sentence is None:
166
- logger.error(
167
- "Asking for a vectorset but /query didn't return one", extra={"kbid": self.kbid}
168
- )
139
+ # when it's not provided, we get the default from Predict API
140
+ query_info = await self._predict_query_endpoint()
141
+ if query_info is None:
169
142
  vectorset = None
170
143
  else:
171
- # vectors field is enforced by the data model to have at least one key
172
- for vectorset in query_info.sentence.vectors.keys():
173
- vectorset = vectorset
174
- break
175
-
176
- if vectorset is None:
177
- # in case predict don't answer which vectorset to use, fallback to
178
- # the first vectorset of the KB
179
- async with datamanagers.with_ro_transaction() as txn:
180
- async for vectorset, _ in datamanagers.vectorsets.iter(txn, kbid=self.kbid):
181
- break
182
- assert vectorset is not None, "All KBs must have at least one vectorset in maindb"
183
-
184
- self.cache.vectorset = vectorset
185
- return vectorset
144
+ if query_info.sentence is None:
145
+ logger.error(
146
+ "Asking for a vectorset but /query didn't return one", extra={"kbid": self.kbid}
147
+ )
148
+ vectorset = None
149
+ else:
150
+ # vectors field is enforced by the data model to have at least one key
151
+ for vectorset in query_info.sentence.vectors.keys():
152
+ vectorset = vectorset
153
+ break
154
+
155
+ if vectorset is None:
156
+ # in case predict don't answer which vectorset to use, fallback to
157
+ # the first vectorset of the KB
158
+ async with datamanagers.with_ro_transaction() as txn:
159
+ async for vectorset, _ in datamanagers.vectorsets.iter(txn, kbid=self.kbid):
160
+ break
161
+ assert vectorset is not None, "All KBs must have at least one vectorset in maindb"
162
+
163
+ self.cache.vectorset = vectorset
164
+ return vectorset
186
165
 
187
166
  async def get_query_vector(self) -> Optional[list[float]]:
188
- if is_cached(self.cache.query_vector):
189
- return self.cache.query_vector
190
-
191
167
  if self.user_vector is not None:
192
168
  query_vector = self.user_vector
193
169
  else:
194
170
  query_info = await self._predict_query_endpoint()
195
171
  if query_info is None or query_info.sentence is None:
196
- self.cache.query_vector = None
197
172
  return None
198
173
 
199
174
  vectorset = await self.get_vectorset()
@@ -206,7 +181,6 @@ class Fetcher:
206
181
  "predict_vectorsets": ",".join(query_info.sentence.vectors.keys()),
207
182
  },
208
183
  )
209
- self.cache.query_vector = None
210
184
  return None
211
185
 
212
186
  query_vector = query_info.sentence.vectors[vectorset]
@@ -223,7 +197,6 @@ class Fetcher:
223
197
  # accordingly
224
198
  query_vector = query_vector[:matryoshka_dimension]
225
199
 
226
- self.cache.query_vector = query_vector
227
200
  return query_vector
228
201
 
229
202
  async def get_rephrased_query(self) -> Optional[str]:
@@ -235,100 +208,98 @@ class Fetcher:
235
208
  # Labels
236
209
 
237
210
  async def get_classification_labels(self) -> knowledgebox_pb2.Labels:
238
- if is_cached(self.cache.labels):
239
- return self.cache.labels
211
+ async with self.locks.setdefault("classification_labels", asyncio.Lock()):
212
+ if is_cached(self.cache.labels):
213
+ return self.cache.labels
240
214
 
241
- labels = await get_classification_labels(self.kbid)
242
- self.cache.labels = labels
243
- return labels
215
+ labels = await get_classification_labels(self.kbid)
216
+ self.cache.labels = labels
217
+ return labels
244
218
 
245
219
  # Entities
246
220
 
247
221
  async def get_entities_meta_cache(self) -> datamanagers.entities.EntitiesMetaCache:
248
- if is_cached(self.cache.entities_meta_cache):
249
- return self.cache.entities_meta_cache
222
+ async with self.locks.setdefault("entities_meta_cache", asyncio.Lock()):
223
+ if is_cached(self.cache.entities_meta_cache):
224
+ return self.cache.entities_meta_cache
250
225
 
251
- entities_meta_cache = await get_entities_meta_cache(self.kbid)
252
- self.cache.entities_meta_cache = entities_meta_cache
253
- return entities_meta_cache
226
+ entities_meta_cache = await get_entities_meta_cache(self.kbid)
227
+ self.cache.entities_meta_cache = entities_meta_cache
228
+ return entities_meta_cache
254
229
 
255
230
  async def get_deleted_entity_groups(self) -> list[str]:
256
- if is_cached(self.cache.deleted_entity_groups):
257
- return self.cache.deleted_entity_groups
231
+ async with self.locks.setdefault("deleted_entity_groups", asyncio.Lock()):
232
+ if is_cached(self.cache.deleted_entity_groups):
233
+ return self.cache.deleted_entity_groups
258
234
 
259
- deleted_entity_groups = await get_deleted_entity_groups(self.kbid)
260
- self.cache.deleted_entity_groups = deleted_entity_groups
261
- return deleted_entity_groups
235
+ deleted_entity_groups = await get_deleted_entity_groups(self.kbid)
236
+ self.cache.deleted_entity_groups = deleted_entity_groups
237
+ return deleted_entity_groups
262
238
 
263
239
  async def get_detected_entities(self) -> list[utils_pb2.RelationNode]:
264
- if is_cached(self.cache.detected_entities):
265
- return self.cache.detected_entities
266
-
267
- # Optimization to avoid calling predict twice
268
- if is_cached(self.cache.predict_query_info):
269
- # /query supersets detect entities, so we already have them
270
- query_info = self.cache.predict_query_info
271
- if query_info is not None and query_info.entities is not None:
272
- detected_entities = convert_relations(query_info.entities.model_dump())
240
+ async with self.locks.setdefault("detected_entities", asyncio.Lock()):
241
+ if is_cached(self.cache.detected_entities):
242
+ return self.cache.detected_entities
243
+
244
+ # Optimization to avoid calling predict twice
245
+ if is_cached(self.cache.predict_query_info):
246
+ # /query supersets detect entities, so we already have them
247
+ query_info = self.cache.predict_query_info
248
+ if query_info is not None and query_info.entities is not None:
249
+ detected_entities = convert_relations(query_info.entities.model_dump())
250
+ else:
251
+ detected_entities = []
273
252
  else:
274
- detected_entities = []
275
- else:
276
- # No call to /query has been done, we'll use detect entities
277
- # endpoint instead (as it's faster)
278
- detected_entities = await self._predict_detect_entities()
253
+ # No call to /query has been done, we'll use detect entities
254
+ # endpoint instead (as it's faster)
255
+ detected_entities = await self._predict_detect_entities()
279
256
 
280
- self.cache.detected_entities = detected_entities
281
- return detected_entities
257
+ self.cache.detected_entities = detected_entities
258
+ return detected_entities
282
259
 
283
260
  # Synonyms
284
261
 
285
262
  async def get_synonyms(self) -> Optional[knowledgebox_pb2.Synonyms]:
286
- if is_cached(self.cache.synonyms):
287
- return self.cache.synonyms
263
+ async with self.locks.setdefault("synonyms", asyncio.Lock()):
264
+ if is_cached(self.cache.synonyms):
265
+ return self.cache.synonyms
288
266
 
289
- synonyms = await get_kb_synonyms(self.kbid)
290
- self.cache.synonyms = synonyms
291
- return synonyms
267
+ synonyms = await get_kb_synonyms(self.kbid)
268
+ self.cache.synonyms = synonyms
269
+ return synonyms
292
270
 
293
271
  # Predict API
294
272
 
295
273
  async def _predict_query_endpoint(self) -> Optional[QueryInfo]:
296
- if is_cached(self.cache.predict_query_info):
297
- return self.cache.predict_query_info
298
-
299
- # calling twice should be avoided as query endpoint is a superset of detect entities
300
- if is_cached(self.cache.predict_detected_entities):
301
- logger.warning("Fetcher is not being efficient enough and has called predict twice!")
302
-
303
- # we can't call get_vectorset, as it would do a recirsive loop between
304
- # functions, so we'll manually parse it
305
- vectorset = await self._get_user_vectorset()
306
- try:
307
- query_info = await query_information(
308
- self.kbid,
309
- self.query,
310
- vectorset,
311
- self.generative_model,
312
- self.rephrase,
313
- self.rephrase_prompt,
314
- )
315
- except (SendToPredictError, TimeoutError):
316
- query_info = None
274
+ async with self.locks.setdefault("predict_query_endpoint", asyncio.Lock()):
275
+ if is_cached(self.cache.predict_query_info):
276
+ return self.cache.predict_query_info
277
+
278
+ # we can't call get_vectorset, as it would do a recirsive loop between
279
+ # functions, so we'll manually parse it
280
+ vectorset = await self._get_user_vectorset()
281
+ try:
282
+ query_info = await query_information(
283
+ self.kbid,
284
+ self.query,
285
+ vectorset,
286
+ self.generative_model,
287
+ self.rephrase,
288
+ self.rephrase_prompt,
289
+ )
290
+ except (SendToPredictError, TimeoutError):
291
+ query_info = None
317
292
 
318
- self.cache.predict_query_info = query_info
319
- return query_info
293
+ self.cache.predict_query_info = query_info
294
+ return query_info
320
295
 
321
296
  async def _predict_detect_entities(self) -> list[utils_pb2.RelationNode]:
322
- if is_cached(self.cache.predict_detected_entities):
323
- return self.cache.predict_detected_entities
324
-
325
297
  try:
326
298
  detected_entities = await detect_entities(self.kbid, self.query)
327
299
  except (SendToPredictError, TimeoutError) as ex:
328
300
  logger.warning(f"Errors on Predict API detecting entities: {ex}", extra={"kbid": self.kbid})
329
301
  detected_entities = []
330
302
 
331
- self.cache.predict_detected_entities = detected_entities
332
303
  return detected_entities
333
304
 
334
305
 
@@ -360,7 +331,7 @@ async def detect_entities(kbid: str, query: str) -> list[utils_pb2.RelationNode]
360
331
 
361
332
 
362
333
  @alru_cache(maxsize=None)
363
- async def get_matryoshka_dimension_cached(kbid: str, vectorset: Optional[str]) -> Optional[int]:
334
+ async def get_matryoshka_dimension_cached(kbid: str, vectorset: str) -> Optional[int]:
364
335
  # This can be safely cached as the matryoshka dimension is not expected to change
365
336
  return await get_matryoshka_dimension(kbid, vectorset)
366
337
 
@@ -221,11 +221,12 @@ def _parse_relation_query(
221
221
  def _set_node_to_pb(node: graph_requests.GraphNode, pb: nodereader_pb2.GraphQuery.Node):
222
222
  if node.value is not None:
223
223
  pb.value = node.value
224
- if node.match == graph_requests.NodeMatchKind.EXACT:
225
- pb.match_kind = nodereader_pb2.GraphQuery.Node.MatchKind.DEPRECATED_EXACT
224
+ if node.match == graph_requests.NodeMatchKindName.EXACT:
225
+ pb.exact.kind = nodereader_pb2.GraphQuery.Node.MatchLocation.FULL
226
226
 
227
- elif node.match == graph_requests.NodeMatchKind.FUZZY:
228
- pb.match_kind = nodereader_pb2.GraphQuery.Node.MatchKind.DEPRECATED_FUZZY
227
+ elif node.match == graph_requests.NodeMatchKindName.FUZZY:
228
+ pb.fuzzy.kind = nodereader_pb2.GraphQuery.Node.MatchLocation.PREFIX
229
+ pb.fuzzy.distance = 1
229
230
 
230
231
  else: # pragma: nocover
231
232
  # This is a trick so mypy generates an error if this branch can be reached,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nucliadb
3
- Version: 6.3.4.post3817
3
+ Version: 6.3.4.post3823
4
4
  Summary: NucliaDB
5
5
  Author-email: Nuclia <nucliadb@nuclia.com>
6
6
  License: AGPL
@@ -20,11 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3 :: Only
21
21
  Requires-Python: <4,>=3.9
22
22
  Description-Content-Type: text/markdown
23
- Requires-Dist: nucliadb-telemetry[all]>=6.3.4.post3817
24
- Requires-Dist: nucliadb-utils[cache,fastapi,storages]>=6.3.4.post3817
25
- Requires-Dist: nucliadb-protos>=6.3.4.post3817
26
- Requires-Dist: nucliadb-models>=6.3.4.post3817
27
- Requires-Dist: nidx-protos>=6.3.4.post3817
23
+ Requires-Dist: nucliadb-telemetry[all]>=6.3.4.post3823
24
+ Requires-Dist: nucliadb-utils[cache,fastapi,storages]>=6.3.4.post3823
25
+ Requires-Dist: nucliadb-protos>=6.3.4.post3823
26
+ Requires-Dist: nucliadb-models>=6.3.4.post3823
27
+ Requires-Dist: nidx-protos>=6.3.4.post3823
28
28
  Requires-Dist: nucliadb-admin-assets>=1.0.0.post1224
29
29
  Requires-Dist: nuclia-models>=0.24.2
30
30
  Requires-Dist: uvicorn
@@ -141,7 +141,7 @@ nucliadb/ingest/fields/text.py,sha256=tFvSQJAe0W7ePpp2_WDfLiE2yglR1OTU0Zht9acvOF
141
141
  nucliadb/ingest/orm/__init__.py,sha256=cp15ZcFnHvpcu_5-aK2A4uUyvuZVV_MJn4bIXMa20ks,835
142
142
  nucliadb/ingest/orm/brain.py,sha256=A8H1J7Bo95sNzDgYr0_UNoemQhWOFEFz9UlYfs6ug-8,29407
143
143
  nucliadb/ingest/orm/broker_message.py,sha256=XWaiZgDOz94NPOPT-hqbRr5ZkpVimUw6PjUJNftfoVw,7514
144
- nucliadb/ingest/orm/entities.py,sha256=3_n6lKhBy2GsdmNmkh0_mvxP8md20OZsbtTNEmfJ8Hg,14888
144
+ nucliadb/ingest/orm/entities.py,sha256=a-aYuKBUQhxDKFtXOzTAkLlY_t2JiTfaptw2vt3AQDQ,14915
145
145
  nucliadb/ingest/orm/exceptions.py,sha256=k4Esv4NtL4TrGTcsQpwrSfDhPQpiYcRbB1SpYmBX5MY,1432
146
146
  nucliadb/ingest/orm/knowledgebox.py,sha256=Bfb4-MIQWlaJrQAUDbgs_iIsXCYjS7s5YiiGl_Jb4jo,23887
147
147
  nucliadb/ingest/orm/metrics.py,sha256=OkwMSPKLZcKba0ZTwtTiIxwBgaLMX5ydhGieKvi2y7E,1096
@@ -229,10 +229,10 @@ nucliadb/search/search/filters.py,sha256=1MkHlJjAQqoRCj7e5cEzK2HvBxGLE17I_omsjik
229
229
  nucliadb/search/search/find.py,sha256=pjc-i55j_p_F1H0La4qc9slZSl_gICoFzl3fy6FLYTM,10314
230
230
  nucliadb/search/search/find_merge.py,sha256=3FnzKFEnVemg6FO_6zveulbAU7klvsiPEBvLrpBBMg8,17450
231
231
  nucliadb/search/search/graph_merge.py,sha256=OiUNiXOWwrUVKqStuRcoUJwvDbDYamqIgiAy_FwPdMI,3405
232
- nucliadb/search/search/graph_strategy.py,sha256=gisL2GpbSIa_SucyOwEt7TWdqURyAQqxvD_-PkXQct8,32339
232
+ nucliadb/search/search/graph_strategy.py,sha256=SPJdDHQcTFsNb1IEWdWzklC5j1Vv9igibo0dYQAgcy0,33113
233
233
  nucliadb/search/search/hydrator.py,sha256=-R37gCrGxkyaiHQalnTWHNG_FCx11Zucd7qA1vQCxuw,6985
234
234
  nucliadb/search/search/ingestion_agents.py,sha256=NeJr4EEX-bvFFMGvXOOwLv8uU7NuQ-ntJnnrhnKfMzY,3174
235
- nucliadb/search/search/merge.py,sha256=bPO51Kc3Ec69UuCbF5ulokd02gWZhp7zlJSuMjGDo9Y,23141
235
+ nucliadb/search/search/merge.py,sha256=fh5WnA_xnXp-Iiq5Cud9hIGole7_0OW2b3Oymk32D6Y,22689
236
236
  nucliadb/search/search/metrics.py,sha256=GGGtXHLhK79_ESV277xkBVjcaMURXHCxYG0EdGamUd8,2886
237
237
  nucliadb/search/search/paragraphs.py,sha256=pNAEiYqJGGUVcEf7xf-PFMVqz0PX4Qb-WNG-_zPGN2o,7799
238
238
  nucliadb/search/search/pgcatalog.py,sha256=s_J98fsX_RuFXwpejpkGqG-tD9ELuzz4YQ6U3ew5h2g,9313
@@ -251,14 +251,14 @@ nucliadb/search/search/chat/prompt.py,sha256=Jnja-Ss7skgnnDY8BymVfdeYsFPnIQFL8tE
251
251
  nucliadb/search/search/chat/query.py,sha256=0IoeW-JNaRBe2d9C3bXNfkYpzmsN_IIg3U4Vqb8eOEk,16485
252
252
  nucliadb/search/search/query_parser/__init__.py,sha256=cp15ZcFnHvpcu_5-aK2A4uUyvuZVV_MJn4bIXMa20ks,835
253
253
  nucliadb/search/search/query_parser/exceptions.py,sha256=szAOXUZ27oNY-OSa9t2hQ5HHkQQC0EX1FZz_LluJHJE,1224
254
- nucliadb/search/search/query_parser/fetcher.py,sha256=jhr__J0KmAzjdsTTadWQmD9qf6lZvqlKAfZdYjZH_UY,15742
254
+ nucliadb/search/search/query_parser/fetcher.py,sha256=vrPgPnebWlrbx9fcelw7eHayEDptxnm9BP1F9LAEMws,15307
255
255
  nucliadb/search/search/query_parser/filter_expression.py,sha256=rws5vsKTofX2iMUK4yvjmLZFxtcbWbyhIcwen4j0rQg,6578
256
256
  nucliadb/search/search/query_parser/models.py,sha256=7czH-jHskl9axEnZV5XnQY8cyN_fs14Cpivzd5aaSxc,2686
257
257
  nucliadb/search/search/query_parser/old_filters.py,sha256=-zbfN-RsXoj_DRjh3Lfp-wShwFXgkISawzVptVzja-A,9071
258
258
  nucliadb/search/search/query_parser/parsers/__init__.py,sha256=ySCNSdbesLXGZyR88919njulA6UE10_3PhqMG_Yj1o4,1034
259
259
  nucliadb/search/search/query_parser/parsers/catalog.py,sha256=XdBiTweGTQkj8m_V_i2xbwp7P5pPO8K1Tud692XKhMw,7149
260
260
  nucliadb/search/search/query_parser/parsers/find.py,sha256=q3wH_i0DGceeKckYEH3c5MqM5EvRiMCL7r-6nCAId9Q,4666
261
- nucliadb/search/search/query_parser/parsers/graph.py,sha256=43S8iSg4j9I_XD8zpW1VggDspQD-NMyS26B5Mw6Dktw,9358
261
+ nucliadb/search/search/query_parser/parsers/graph.py,sha256=QJs-pybNXPsMSEkIHctb0Q0xQG-aArks8BtUxbJL5rU,9386
262
262
  nucliadb/standalone/__init__.py,sha256=cp15ZcFnHvpcu_5-aK2A4uUyvuZVV_MJn4bIXMa20ks,835
263
263
  nucliadb/standalone/api_router.py,sha256=hgq9FXpihzgjHkwcVGfGCSwyXy67fqXTfLFHuINzIi0,5567
264
264
  nucliadb/standalone/app.py,sha256=mAApNK_iVsQgJyd-mtwCeZq5csSimwnXmlQGH9a70pE,5586
@@ -354,8 +354,8 @@ nucliadb/writer/tus/local.py,sha256=7jYa_w9b-N90jWgN2sQKkNcomqn6JMVBOVeDOVYJHto,
354
354
  nucliadb/writer/tus/s3.py,sha256=vF0NkFTXiXhXq3bCVXXVV-ED38ECVoUeeYViP8uMqcU,8357
355
355
  nucliadb/writer/tus/storage.py,sha256=ToqwjoYnjI4oIcwzkhha_MPxi-k4Jk3Lt55zRwaC1SM,2903
356
356
  nucliadb/writer/tus/utils.py,sha256=MSdVbRsRSZVdkaum69_0wku7X3p5wlZf4nr6E0GMKbw,2556
357
- nucliadb-6.3.4.post3817.dist-info/METADATA,sha256=YJJR7no2TypIzteRiSkHcCRCO0O63H7eqF_ezQhgXSQ,4291
358
- nucliadb-6.3.4.post3817.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
359
- nucliadb-6.3.4.post3817.dist-info/entry_points.txt,sha256=XqGfgFDuY3zXQc8ewXM2TRVjTModIq851zOsgrmaXx4,1268
360
- nucliadb-6.3.4.post3817.dist-info/top_level.txt,sha256=hwYhTVnX7jkQ9gJCkVrbqEG1M4lT2F_iPQND1fCzF80,20
361
- nucliadb-6.3.4.post3817.dist-info/RECORD,,
357
+ nucliadb-6.3.4.post3823.dist-info/METADATA,sha256=id3yZ3Ww5cXYXQ1Y9VWVEhl_7cyP3m9RDTwGYxNH5Zc,4291
358
+ nucliadb-6.3.4.post3823.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
359
+ nucliadb-6.3.4.post3823.dist-info/entry_points.txt,sha256=XqGfgFDuY3zXQc8ewXM2TRVjTModIq851zOsgrmaXx4,1268
360
+ nucliadb-6.3.4.post3823.dist-info/top_level.txt,sha256=hwYhTVnX7jkQ9gJCkVrbqEG1M4lT2F_iPQND1fCzF80,20
361
+ nucliadb-6.3.4.post3823.dist-info/RECORD,,