nucliadb 6.2.0.post2675__py3-none-any.whl → 6.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. migrations/0028_extracted_vectors_reference.py +61 -0
  2. migrations/0029_backfill_field_status.py +149 -0
  3. migrations/0030_label_deduplication.py +60 -0
  4. nucliadb/common/cluster/manager.py +41 -331
  5. nucliadb/common/cluster/rebalance.py +2 -2
  6. nucliadb/common/cluster/rollover.py +12 -71
  7. nucliadb/common/cluster/settings.py +3 -0
  8. nucliadb/common/cluster/standalone/utils.py +0 -43
  9. nucliadb/common/cluster/utils.py +0 -16
  10. nucliadb/common/counters.py +1 -0
  11. nucliadb/common/datamanagers/fields.py +48 -7
  12. nucliadb/common/datamanagers/vectorsets.py +11 -2
  13. nucliadb/common/external_index_providers/base.py +2 -1
  14. nucliadb/common/external_index_providers/pinecone.py +3 -5
  15. nucliadb/common/ids.py +18 -4
  16. nucliadb/common/models_utils/from_proto.py +479 -0
  17. nucliadb/common/models_utils/to_proto.py +60 -0
  18. nucliadb/common/nidx.py +76 -37
  19. nucliadb/export_import/models.py +3 -3
  20. nucliadb/health.py +0 -7
  21. nucliadb/ingest/app.py +0 -8
  22. nucliadb/ingest/consumer/auditing.py +1 -1
  23. nucliadb/ingest/consumer/shard_creator.py +1 -1
  24. nucliadb/ingest/fields/base.py +83 -21
  25. nucliadb/ingest/orm/brain.py +55 -56
  26. nucliadb/ingest/orm/broker_message.py +12 -2
  27. nucliadb/ingest/orm/entities.py +6 -17
  28. nucliadb/ingest/orm/knowledgebox.py +44 -22
  29. nucliadb/ingest/orm/processor/data_augmentation.py +7 -29
  30. nucliadb/ingest/orm/processor/processor.py +5 -2
  31. nucliadb/ingest/orm/resource.py +222 -413
  32. nucliadb/ingest/processing.py +8 -2
  33. nucliadb/ingest/serialize.py +77 -46
  34. nucliadb/ingest/service/writer.py +2 -56
  35. nucliadb/ingest/settings.py +1 -4
  36. nucliadb/learning_proxy.py +6 -4
  37. nucliadb/purge/__init__.py +102 -12
  38. nucliadb/purge/orphan_shards.py +6 -4
  39. nucliadb/reader/api/models.py +3 -3
  40. nucliadb/reader/api/v1/__init__.py +1 -0
  41. nucliadb/reader/api/v1/download.py +2 -2
  42. nucliadb/reader/api/v1/knowledgebox.py +3 -3
  43. nucliadb/reader/api/v1/resource.py +23 -12
  44. nucliadb/reader/api/v1/services.py +4 -4
  45. nucliadb/reader/api/v1/vectorsets.py +48 -0
  46. nucliadb/search/api/v1/ask.py +11 -1
  47. nucliadb/search/api/v1/feedback.py +3 -3
  48. nucliadb/search/api/v1/knowledgebox.py +8 -13
  49. nucliadb/search/api/v1/search.py +3 -2
  50. nucliadb/search/api/v1/suggest.py +0 -2
  51. nucliadb/search/predict.py +6 -4
  52. nucliadb/search/requesters/utils.py +1 -2
  53. nucliadb/search/search/chat/ask.py +77 -13
  54. nucliadb/search/search/chat/prompt.py +16 -5
  55. nucliadb/search/search/chat/query.py +74 -34
  56. nucliadb/search/search/exceptions.py +2 -7
  57. nucliadb/search/search/find.py +9 -5
  58. nucliadb/search/search/find_merge.py +10 -4
  59. nucliadb/search/search/graph_strategy.py +884 -0
  60. nucliadb/search/search/hydrator.py +6 -0
  61. nucliadb/search/search/merge.py +79 -24
  62. nucliadb/search/search/query.py +74 -245
  63. nucliadb/search/search/query_parser/exceptions.py +11 -1
  64. nucliadb/search/search/query_parser/fetcher.py +405 -0
  65. nucliadb/search/search/query_parser/models.py +0 -3
  66. nucliadb/search/search/query_parser/parser.py +22 -21
  67. nucliadb/search/search/rerankers.py +1 -42
  68. nucliadb/search/search/shards.py +19 -0
  69. nucliadb/standalone/api_router.py +2 -14
  70. nucliadb/standalone/settings.py +4 -0
  71. nucliadb/train/generators/field_streaming.py +7 -3
  72. nucliadb/train/lifecycle.py +3 -6
  73. nucliadb/train/nodes.py +14 -12
  74. nucliadb/train/resource.py +380 -0
  75. nucliadb/writer/api/constants.py +20 -16
  76. nucliadb/writer/api/v1/__init__.py +1 -0
  77. nucliadb/writer/api/v1/export_import.py +1 -1
  78. nucliadb/writer/api/v1/field.py +13 -7
  79. nucliadb/writer/api/v1/knowledgebox.py +3 -46
  80. nucliadb/writer/api/v1/resource.py +20 -13
  81. nucliadb/writer/api/v1/services.py +10 -1
  82. nucliadb/writer/api/v1/upload.py +61 -34
  83. nucliadb/writer/{vectorsets.py → api/v1/vectorsets.py} +99 -47
  84. nucliadb/writer/back_pressure.py +17 -46
  85. nucliadb/writer/resource/basic.py +9 -7
  86. nucliadb/writer/resource/field.py +42 -9
  87. nucliadb/writer/settings.py +2 -2
  88. nucliadb/writer/tus/gcs.py +11 -10
  89. {nucliadb-6.2.0.post2675.dist-info → nucliadb-6.2.1.dist-info}/METADATA +11 -14
  90. {nucliadb-6.2.0.post2675.dist-info → nucliadb-6.2.1.dist-info}/RECORD +94 -96
  91. {nucliadb-6.2.0.post2675.dist-info → nucliadb-6.2.1.dist-info}/WHEEL +1 -1
  92. nucliadb/common/cluster/discovery/base.py +0 -178
  93. nucliadb/common/cluster/discovery/k8s.py +0 -301
  94. nucliadb/common/cluster/discovery/manual.py +0 -57
  95. nucliadb/common/cluster/discovery/single.py +0 -51
  96. nucliadb/common/cluster/discovery/types.py +0 -32
  97. nucliadb/common/cluster/discovery/utils.py +0 -67
  98. nucliadb/common/cluster/standalone/grpc_node_binding.py +0 -349
  99. nucliadb/common/cluster/standalone/index_node.py +0 -123
  100. nucliadb/common/cluster/standalone/service.py +0 -84
  101. nucliadb/standalone/introspect.py +0 -208
  102. nucliadb-6.2.0.post2675.dist-info/zip-safe +0 -1
  103. /nucliadb/common/{cluster/discovery → models_utils}/__init__.py +0 -0
  104. {nucliadb-6.2.0.post2675.dist-info → nucliadb-6.2.1.dist-info}/entry_points.txt +0 -0
  105. {nucliadb-6.2.0.post2675.dist-info → nucliadb-6.2.1.dist-info}/top_level.txt +0 -0
@@ -30,7 +30,6 @@ from cachetools import TTLCache
30
30
  from fastapi import HTTPException, Request
31
31
 
32
32
  from nucliadb.common import datamanagers
33
- from nucliadb.common.cluster.manager import get_index_nodes
34
33
  from nucliadb.common.context import ApplicationContext
35
34
  from nucliadb.common.context.fastapi import get_app_context
36
35
  from nucliadb.common.http_clients.processing import ProcessingHTTPClient
@@ -168,7 +167,7 @@ class Materializer:
168
167
  self.ingest_check_interval = ingest_check_interval
169
168
 
170
169
  self.ingest_pending: int = 0
171
- self.indexing_pending: dict[str, int] = {}
170
+ self.indexing_pending: int = 0
172
171
 
173
172
  self._tasks: list[asyncio.Task] = []
174
173
  self._running = False
@@ -232,7 +231,7 @@ class Materializer:
232
231
  response = await self.processing_http_client.stats(kbid=kbid, timeout=0.5)
233
232
  return response.incomplete
234
233
 
235
- def get_indexing_pending(self) -> dict[str, int]:
234
+ def get_indexing_pending(self) -> int:
236
235
  return self.indexing_pending
237
236
 
238
237
  def get_ingest_pending(self) -> int:
@@ -241,20 +240,18 @@ class Materializer:
241
240
  async def _get_indexing_pending_task(self):
242
241
  try:
243
242
  while True:
244
- for node in get_index_nodes():
245
- try:
246
- with back_pressure_observer({"type": "get_indexing_pending"}):
247
- self.indexing_pending[node.id] = await get_nats_consumer_pending_messages(
248
- self.nats_manager,
249
- stream=const.Streams.INDEX.name,
250
- consumer=const.Streams.INDEX.group.format(node=node.id),
251
- )
252
- except Exception:
253
- logger.exception(
254
- "Error getting pending messages to index",
255
- exc_info=True,
256
- extra={"node_id": node.id},
243
+ try:
244
+ with back_pressure_observer({"type": "get_indexing_pending"}):
245
+ self.indexing_pending = await get_nats_consumer_pending_messages(
246
+ self.nats_manager,
247
+ stream="nidx",
248
+ consumer="nidx",
257
249
  )
250
+ except Exception:
251
+ logger.exception(
252
+ "Error getting pending messages to index",
253
+ exc_info=True,
254
+ )
258
255
  await asyncio.sleep(self.indexing_check_interval)
259
256
  except asyncio.CancelledError:
260
257
  pass
@@ -386,7 +383,7 @@ async def check_indexing_behind(
386
383
  context: ApplicationContext,
387
384
  kbid: str,
388
385
  resource_uuid: Optional[str],
389
- pending_by_node: dict[str, int],
386
+ pending: int,
390
387
  ):
391
388
  """
392
389
  If a resource uuid is provided, it will check the nodes that have the replicas
@@ -398,36 +395,10 @@ async def check_indexing_behind(
398
395
  # Indexing back pressure is disabled
399
396
  return
400
397
 
401
- if len(pending_by_node) == 0:
402
- logger.warning("No nodes found to check for pending messages")
403
- return
404
-
405
- # Get nodes that are involved in the indexing of the request
406
- if resource_uuid is not None:
407
- nodes_to_check = await get_nodes_for_resource_shard(context, kbid, resource_uuid)
408
- else:
409
- nodes_to_check = await get_nodes_for_kb_active_shards(context, kbid)
410
-
411
- if len(nodes_to_check) == 0:
412
- logger.warning(
413
- "No nodes found to check for pending messages",
414
- extra={"kbid": kbid, "resource_uuid": resource_uuid},
415
- )
416
- return
417
-
418
- # Get the highest pending value
419
- highest_pending = 0
420
- for node in nodes_to_check:
421
- if node not in pending_by_node:
422
- logger.warning("Node not found in pending messages", extra={"node": node})
423
- continue
424
- if pending_by_node[node] > highest_pending:
425
- highest_pending = pending_by_node[node]
426
-
427
- if highest_pending > max_pending:
398
+ if pending > max_pending:
428
399
  try_after = estimate_try_after(
429
400
  rate=settings.indexing_rate,
430
- pending=highest_pending,
401
+ pending=pending,
431
402
  max_wait=settings.max_wait_time,
432
403
  )
433
404
  data = BackPressureData(type="indexing", try_after=try_after)
@@ -437,7 +408,7 @@ async def check_indexing_behind(
437
408
  "kbid": kbid,
438
409
  "resource_uuid": resource_uuid,
439
410
  "try_after": try_after,
440
- "pending": highest_pending,
411
+ "pending": pending,
441
412
  },
442
413
  )
443
414
  raise BackPressureException(data)
@@ -22,17 +22,19 @@ from typing import Optional
22
22
 
23
23
  from fastapi import HTTPException
24
24
 
25
+ from nucliadb.common.models_utils import to_proto
26
+ from nucliadb.common.models_utils.from_proto import (
27
+ RelationNodeTypeMap,
28
+ RelationTypeMap,
29
+ )
25
30
  from nucliadb.ingest.orm.utils import set_title
26
31
  from nucliadb.ingest.processing import PushPayload
27
- from nucliadb_models.common import FIELD_TYPES_MAP_REVERSE
28
32
  from nucliadb_models.content_types import GENERIC_MIME_TYPE
29
33
  from nucliadb_models.file import FileField
30
34
  from nucliadb_models.link import LinkField
31
35
  from nucliadb_models.metadata import (
32
36
  ParagraphAnnotation,
33
37
  QuestionAnswerAnnotation,
34
- RelationNodeTypeMap,
35
- RelationTypeMap,
36
38
  )
37
39
  from nucliadb_models.text import TEXT_FORMAT_TO_MIMETYPE, PushTextFormat, Text
38
40
  from nucliadb_models.writer import (
@@ -93,7 +95,8 @@ def parse_basic_modify(bm: BrokerMessage, item: ComingResourcePayload, toprocess
93
95
  if item.metadata.language:
94
96
  bm.basic.metadata.language = item.metadata.language
95
97
  if item.metadata.languages:
96
- bm.basic.metadata.languages.extend(item.metadata.languages)
98
+ unique_languages = list(set(item.metadata.languages))
99
+ bm.basic.metadata.languages.extend(unique_languages)
97
100
 
98
101
  if item.fieldmetadata is not None:
99
102
  for fieldmetadata in item.fieldmetadata:
@@ -144,9 +147,8 @@ def parse_basic_modify(bm: BrokerMessage, item: ComingResourcePayload, toprocess
144
147
  userfieldmetadata.question_answers.append(qa_annotation_pb)
145
148
 
146
149
  userfieldmetadata.field.field = fieldmetadata.field.field
147
- userfieldmetadata.field.field_type = FIELD_TYPES_MAP_REVERSE[
148
- fieldmetadata.field.field_type.value
149
- ]
150
+
151
+ userfieldmetadata.field.field_type = to_proto.field_type(fieldmetadata.field.field_type)
150
152
 
151
153
  bm.basic.fieldmetadata.append(userfieldmetadata)
152
154
 
@@ -23,16 +23,13 @@ from typing import Optional, Union
23
23
  from google.protobuf.json_format import MessageToDict
24
24
 
25
25
  import nucliadb_models as models
26
+ from nucliadb.common.models_utils import from_proto, to_proto
26
27
  from nucliadb.ingest.fields.conversation import Conversation
27
28
  from nucliadb.ingest.orm.resource import Resource as ORMResource
28
29
  from nucliadb.ingest.processing import PushPayload
29
30
  from nucliadb.writer import SERVICE_NAME
30
31
  from nucliadb.writer.utilities import get_processing
31
- from nucliadb_models.common import (
32
- FIELD_TYPES_MAP,
33
- FIELD_TYPES_MAP_REVERSE,
34
- FieldTypeName,
35
- )
32
+ from nucliadb_models.common import FieldTypeName
36
33
  from nucliadb_models.content_types import GENERIC_MIME_TYPE
37
34
  from nucliadb_models.conversation import PushConversation
38
35
  from nucliadb_models.writer import (
@@ -40,7 +37,7 @@ from nucliadb_models.writer import (
40
37
  UpdateResourcePayload,
41
38
  )
42
39
  from nucliadb_protos import resources_pb2
43
- from nucliadb_protos.writer_pb2 import BrokerMessage
40
+ from nucliadb_protos.writer_pb2 import BrokerMessage, FieldIDStatus, FieldStatus
44
41
  from nucliadb_utils.storages.storage import StorageField
45
42
  from nucliadb_utils.utilities import get_storage
46
43
 
@@ -53,6 +50,7 @@ async def extract_file_field_from_pb(field_pb: resources_pb2.FieldFile) -> str:
53
50
  language=field_pb.language,
54
51
  password=field_pb.password,
55
52
  file=models.File(payload=None, uri=field_pb.file.uri),
53
+ extract_strategy=field_pb.extract_strategy,
56
54
  )
57
55
  return processing.convert_external_filefield_to_str(file_field)
58
56
  else:
@@ -83,7 +81,7 @@ async def extract_fields(resource: ORMResource, toprocess: PushPayload):
83
81
  storage = await get_storage(service_name=SERVICE_NAME)
84
82
  await resource.get_fields()
85
83
  for (field_type, field_id), field in resource.fields.items():
86
- field_type_name = FIELD_TYPES_MAP[field_type]
84
+ field_type_name = from_proto.field_type_name(field_type)
87
85
 
88
86
  if field_type_name not in {
89
87
  FieldTypeName.TEXT,
@@ -174,6 +172,8 @@ def parse_text_field(
174
172
  writer: BrokerMessage,
175
173
  toprocess: PushPayload,
176
174
  ) -> None:
175
+ if text_field.extract_strategy is not None:
176
+ writer.texts[key].extract_strategy = text_field.extract_strategy
177
177
  writer.texts[key].body = text_field.body
178
178
  writer.texts[key].format = resources_pb2.FieldText.Format.Value(text_field.format.value)
179
179
  etw = resources_pb2.ExtractedTextWrapper()
@@ -184,6 +184,13 @@ def parse_text_field(
184
184
  toprocess.textfield[key] = models.Text(
185
185
  body=text_field.body,
186
186
  format=getattr(models.PushTextFormat, text_field.format.value),
187
+ extract_strategy=text_field.extract_strategy,
188
+ )
189
+ writer.field_statuses.append(
190
+ FieldIDStatus(
191
+ id=resources_pb2.FieldID(field_type=resources_pb2.FieldType.TEXT, field=key),
192
+ status=FieldStatus.Status.PENDING,
193
+ )
187
194
  )
188
195
 
189
196
 
@@ -203,6 +210,13 @@ async def parse_file_field(
203
210
  key, file_field, writer, toprocess, kbid, uuid, skip_store=skip_store
204
211
  )
205
212
 
213
+ writer.field_statuses.append(
214
+ FieldIDStatus(
215
+ id=resources_pb2.FieldID(field_type=resources_pb2.FieldType.FILE, field=key),
216
+ status=FieldStatus.Status.PENDING,
217
+ )
218
+ )
219
+
206
220
 
207
221
  async def parse_internal_file_field(
208
222
  key: str,
@@ -216,6 +230,8 @@ async def parse_internal_file_field(
216
230
  writer.files[key].added.FromDatetime(datetime.now())
217
231
  if file_field.language:
218
232
  writer.files[key].language = file_field.language
233
+ if file_field.extract_strategy is not None:
234
+ writer.files[key].extract_strategy = file_field.extract_strategy
219
235
 
220
236
  processing = get_processing()
221
237
 
@@ -251,6 +267,8 @@ def parse_external_file_field(
251
267
  writer.files[key].added.FromDatetime(datetime.now())
252
268
  if file_field.language:
253
269
  writer.files[key].language = file_field.language
270
+ if file_field.extract_strategy is not None:
271
+ writer.files[key].extract_strategy = file_field.extract_strategy
254
272
  uri = file_field.file.uri
255
273
  writer.files[key].url = uri # type: ignore
256
274
  writer.files[key].file.uri = uri # type: ignore
@@ -293,6 +311,9 @@ def parse_link_field(
293
311
  if link_field.xpath is not None:
294
312
  writer.links[key].xpath = link_field.xpath
295
313
 
314
+ if link_field.extract_strategy is not None:
315
+ writer.links[key].extract_strategy = link_field.extract_strategy
316
+
296
317
  toprocess.linkfield[key] = models.LinkUpload(
297
318
  link=link_field.uri,
298
319
  headers=link_field.headers or {},
@@ -300,6 +321,13 @@ def parse_link_field(
300
321
  localstorage=link_field.localstorage or {},
301
322
  css_selector=link_field.css_selector,
302
323
  xpath=link_field.xpath,
324
+ extract_strategy=link_field.extract_strategy,
325
+ )
326
+ writer.field_statuses.append(
327
+ FieldIDStatus(
328
+ id=resources_pb2.FieldID(field_type=resources_pb2.FieldType.LINK, field=key),
329
+ status=FieldStatus.Status.PENDING,
330
+ )
303
331
  )
304
332
 
305
333
 
@@ -313,7 +341,6 @@ async def parse_conversation_field(
313
341
  ) -> None:
314
342
  storage = await get_storage(service_name=SERVICE_NAME)
315
343
  processing = get_processing()
316
-
317
344
  field_value = resources_pb2.Conversation()
318
345
  convs = models.PushConversation()
319
346
  for message in conversation_field.messages:
@@ -338,7 +365,7 @@ async def parse_conversation_field(
338
365
  cm.content.attachments_fields.extend(
339
366
  [
340
367
  resources_pb2.FieldRef(
341
- field_type=FIELD_TYPES_MAP_REVERSE[attachment.field_type],
368
+ field_type=to_proto.field_type_name(attachment.field_type),
342
369
  field_id=attachment.field_id,
343
370
  split=attachment.split if attachment.split is not None else "",
344
371
  )
@@ -377,3 +404,9 @@ async def parse_conversation_field(
377
404
 
378
405
  toprocess.conversationfield[key] = convs
379
406
  writer.conversations[key].CopyFrom(field_value)
407
+ writer.field_statuses.append(
408
+ FieldIDStatus(
409
+ id=resources_pb2.FieldID(field_type=resources_pb2.FieldType.CONVERSATION, field=key),
410
+ status=FieldStatus.Status.PENDING,
411
+ )
412
+ )
@@ -36,7 +36,7 @@ class BackPressureSettings(BaseSettings):
36
36
  alias="back_pressure_enabled",
37
37
  )
38
38
  indexing_rate: float = Field(
39
- default=4,
39
+ default=10,
40
40
  description="Estimation of the indexing rate in messages per second. This is used to calculate the try again in time", # noqa
41
41
  )
42
42
  ingest_rate: float = Field(
@@ -48,7 +48,7 @@ class BackPressureSettings(BaseSettings):
48
48
  description="Estimation of the processing rate in messages per second. This is used to calculate the try again in time", # noqa
49
49
  )
50
50
  max_indexing_pending: int = Field(
51
- default=200,
51
+ default=1000,
52
52
  description="Max number of messages pending to index in a node queue before rate limiting writes. Set to 0 to disable indexing back pressure checks", # noqa
53
53
  alias="back_pressure_max_indexing_pending",
54
54
  )
@@ -354,23 +354,24 @@ class GCloudFileStorageManager(FileStorageManager):
354
354
  if dm.size == 0:
355
355
  if self.storage.session is None:
356
356
  raise AttributeError()
357
- # If there is been no size finish the upload
358
- content_range = "bytes {init}-{chunk}/{total}".format(
359
- init=dm.offset, chunk=dm.offset, total=dm.offset
360
- )
357
+ # In case of empty file, we need to send a PUT request with empty body
358
+ # and Content-Range header set to "bytes */0"
359
+ headers = {
360
+ "Content-Length": "0",
361
+ "Content-Range": "bytes */0",
362
+ }
361
363
  resumable_uri = dm.get("resumable_uri")
362
364
  async with self.storage.session.put(
363
365
  resumable_uri,
364
- headers={
365
- "Content-Length": "0",
366
- "Content-Range": content_range,
367
- },
366
+ headers=headers,
368
367
  data="",
369
368
  ) as call:
370
- text = await call.text() # noqa
371
369
  if call.status not in [200, 201, 308]:
370
+ try:
371
+ text = await call.text()
372
+ except Exception:
373
+ text = ""
372
374
  raise GoogleCloudException(f"{call.status}: {text}")
373
- return call
374
375
  path = dm.get("path")
375
376
  await dm.finish()
376
377
  return path
@@ -1,15 +1,13 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: nucliadb
3
- Version: 6.2.0.post2675
4
- Home-page: https://docs.nuclia.dev/docs/management/nucliadb/intro
5
- Author: NucliaDB Community
6
- Author-email: nucliadb@nuclia.com
7
- License: BSD
3
+ Version: 6.2.1
4
+ Summary: NucliaDB
5
+ Author-email: Nuclia <nucliadb@nuclia.com>
6
+ License: AGPL
8
7
  Project-URL: Nuclia, https://nuclia.com
9
8
  Project-URL: Github, https://github.com/nuclia/nucliadb
10
9
  Project-URL: Slack, https://nuclia-community.slack.com
11
10
  Project-URL: API Reference, https://docs.nuclia.dev/docs/api
12
- Keywords: search,semantic,AI
13
11
  Classifier: Development Status :: 4 - Beta
14
12
  Classifier: Intended Audience :: Developers
15
13
  Classifier: Intended Audience :: Information Technology
@@ -20,14 +18,14 @@ Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
20
  Classifier: Programming Language :: Python :: 3 :: Only
23
- Requires-Python: >=3.9, <4
21
+ Requires-Python: <4,>=3.9
24
22
  Description-Content-Type: text/markdown
25
- Requires-Dist: nucliadb-telemetry[all]>=6.2.0.post2675
26
- Requires-Dist: nucliadb-utils[cache,fastapi,storages]>=6.2.0.post2675
27
- Requires-Dist: nucliadb-protos>=6.2.0.post2675
28
- Requires-Dist: nucliadb-models>=6.2.0.post2675
23
+ Requires-Dist: nucliadb-telemetry[all]>=6.2.1.post3260
24
+ Requires-Dist: nucliadb-utils[cache,fastapi,storages]>=6.2.1.post3260
25
+ Requires-Dist: nucliadb-protos>=6.2.1.post3260
26
+ Requires-Dist: nucliadb-models>=6.2.1.post3260
27
+ Requires-Dist: nidx-protos>=6.2.1.post3260
29
28
  Requires-Dist: nucliadb-admin-assets>=1.0.0.post1224
30
- Requires-Dist: nucliadb-node-binding>=2.26.0
31
29
  Requires-Dist: nuclia-models>=0.24.2
32
30
  Requires-Dist: uvicorn
33
31
  Requires-Dist: argdantic
@@ -78,7 +76,6 @@ Requires-Dist: async_lru>=2.0.4
78
76
  Requires-Dist: async-timeout>=4.0.3
79
77
  Requires-Dist: cachetools>=5.3.2
80
78
  Requires-Dist: types-cachetools>=5.3.0.5
81
- Requires-Dist: kubernetes_asyncio<30.0.0
82
79
  Provides-Extra: redis
83
80
  Requires-Dist: redis>=4.3.4; extra == "redis"
84
81