nucliadb 4.0.0.post542__py3-none-any.whl → 6.2.1.post2777__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 (418) hide show
  1. migrations/0003_allfields_key.py +1 -35
  2. migrations/0009_upgrade_relations_and_texts_to_v2.py +4 -2
  3. migrations/0010_fix_corrupt_indexes.py +10 -10
  4. migrations/0011_materialize_labelset_ids.py +1 -16
  5. migrations/0012_rollover_shards.py +5 -10
  6. migrations/0014_rollover_shards.py +4 -5
  7. migrations/0015_targeted_rollover.py +5 -10
  8. migrations/0016_upgrade_to_paragraphs_v2.py +25 -28
  9. migrations/0017_multiple_writable_shards.py +2 -4
  10. migrations/0018_purge_orphan_kbslugs.py +5 -7
  11. migrations/0019_upgrade_to_paragraphs_v3.py +25 -28
  12. migrations/0020_drain_nodes_from_cluster.py +3 -3
  13. nucliadb/standalone/tests/unit/test_run.py → migrations/0021_overwrite_vectorsets_key.py +16 -19
  14. nucliadb/tests/unit/test_openapi.py → migrations/0022_fix_paragraph_deletion_bug.py +16 -11
  15. migrations/0023_backfill_pg_catalog.py +80 -0
  16. migrations/0025_assign_models_to_kbs_v2.py +113 -0
  17. migrations/0026_fix_high_cardinality_content_types.py +61 -0
  18. migrations/0027_rollover_texts3.py +73 -0
  19. nucliadb/ingest/fields/date.py → migrations/pg/0001_bootstrap.py +10 -12
  20. migrations/pg/0002_catalog.py +42 -0
  21. nucliadb/ingest/tests/unit/test_settings.py → migrations/pg/0003_catalog_kbid_index.py +5 -3
  22. nucliadb/common/cluster/base.py +30 -16
  23. nucliadb/common/cluster/discovery/base.py +6 -14
  24. nucliadb/common/cluster/discovery/k8s.py +9 -19
  25. nucliadb/common/cluster/discovery/manual.py +1 -3
  26. nucliadb/common/cluster/discovery/utils.py +1 -3
  27. nucliadb/common/cluster/grpc_node_dummy.py +3 -11
  28. nucliadb/common/cluster/index_node.py +10 -19
  29. nucliadb/common/cluster/manager.py +174 -59
  30. nucliadb/common/cluster/rebalance.py +27 -29
  31. nucliadb/common/cluster/rollover.py +353 -194
  32. nucliadb/common/cluster/settings.py +6 -0
  33. nucliadb/common/cluster/standalone/grpc_node_binding.py +13 -64
  34. nucliadb/common/cluster/standalone/index_node.py +4 -11
  35. nucliadb/common/cluster/standalone/service.py +2 -6
  36. nucliadb/common/cluster/standalone/utils.py +2 -6
  37. nucliadb/common/cluster/utils.py +29 -22
  38. nucliadb/common/constants.py +20 -0
  39. nucliadb/common/context/__init__.py +3 -0
  40. nucliadb/common/context/fastapi.py +8 -5
  41. nucliadb/{tests/knowledgeboxes/__init__.py → common/counters.py} +8 -2
  42. nucliadb/common/datamanagers/__init__.py +7 -1
  43. nucliadb/common/datamanagers/atomic.py +22 -4
  44. nucliadb/common/datamanagers/cluster.py +5 -5
  45. nucliadb/common/datamanagers/entities.py +6 -16
  46. nucliadb/common/datamanagers/fields.py +84 -0
  47. nucliadb/common/datamanagers/kb.py +83 -37
  48. nucliadb/common/datamanagers/labels.py +26 -56
  49. nucliadb/common/datamanagers/processing.py +2 -6
  50. nucliadb/common/datamanagers/resources.py +41 -103
  51. nucliadb/common/datamanagers/rollover.py +76 -15
  52. nucliadb/common/datamanagers/synonyms.py +1 -1
  53. nucliadb/common/datamanagers/utils.py +15 -6
  54. nucliadb/common/datamanagers/vectorsets.py +110 -0
  55. nucliadb/common/external_index_providers/base.py +257 -0
  56. nucliadb/{ingest/tests/unit/orm/test_orm_utils.py → common/external_index_providers/exceptions.py} +9 -8
  57. nucliadb/common/external_index_providers/manager.py +101 -0
  58. nucliadb/common/external_index_providers/pinecone.py +933 -0
  59. nucliadb/common/external_index_providers/settings.py +52 -0
  60. nucliadb/common/http_clients/auth.py +3 -6
  61. nucliadb/common/http_clients/processing.py +6 -11
  62. nucliadb/common/http_clients/utils.py +1 -3
  63. nucliadb/common/ids.py +240 -0
  64. nucliadb/common/locking.py +29 -7
  65. nucliadb/common/maindb/driver.py +11 -35
  66. nucliadb/common/maindb/exceptions.py +3 -0
  67. nucliadb/common/maindb/local.py +22 -9
  68. nucliadb/common/maindb/pg.py +206 -111
  69. nucliadb/common/maindb/utils.py +11 -42
  70. nucliadb/common/models_utils/from_proto.py +479 -0
  71. nucliadb/common/models_utils/to_proto.py +60 -0
  72. nucliadb/common/nidx.py +260 -0
  73. nucliadb/export_import/datamanager.py +25 -19
  74. nucliadb/export_import/exporter.py +5 -11
  75. nucliadb/export_import/importer.py +5 -7
  76. nucliadb/export_import/models.py +3 -3
  77. nucliadb/export_import/tasks.py +4 -4
  78. nucliadb/export_import/utils.py +25 -37
  79. nucliadb/health.py +1 -3
  80. nucliadb/ingest/app.py +15 -11
  81. nucliadb/ingest/consumer/auditing.py +21 -19
  82. nucliadb/ingest/consumer/consumer.py +82 -47
  83. nucliadb/ingest/consumer/materializer.py +5 -12
  84. nucliadb/ingest/consumer/pull.py +12 -27
  85. nucliadb/ingest/consumer/service.py +19 -17
  86. nucliadb/ingest/consumer/shard_creator.py +2 -4
  87. nucliadb/ingest/consumer/utils.py +1 -3
  88. nucliadb/ingest/fields/base.py +137 -105
  89. nucliadb/ingest/fields/conversation.py +18 -5
  90. nucliadb/ingest/fields/exceptions.py +1 -4
  91. nucliadb/ingest/fields/file.py +7 -16
  92. nucliadb/ingest/fields/link.py +5 -10
  93. nucliadb/ingest/fields/text.py +9 -4
  94. nucliadb/ingest/orm/brain.py +200 -213
  95. nucliadb/ingest/orm/broker_message.py +181 -0
  96. nucliadb/ingest/orm/entities.py +36 -51
  97. nucliadb/ingest/orm/exceptions.py +12 -0
  98. nucliadb/ingest/orm/knowledgebox.py +322 -197
  99. nucliadb/ingest/orm/processor/__init__.py +2 -700
  100. nucliadb/ingest/orm/processor/auditing.py +4 -23
  101. nucliadb/ingest/orm/processor/data_augmentation.py +164 -0
  102. nucliadb/ingest/orm/processor/pgcatalog.py +84 -0
  103. nucliadb/ingest/orm/processor/processor.py +752 -0
  104. nucliadb/ingest/orm/processor/sequence_manager.py +1 -1
  105. nucliadb/ingest/orm/resource.py +249 -402
  106. nucliadb/ingest/orm/utils.py +4 -4
  107. nucliadb/ingest/partitions.py +3 -9
  108. nucliadb/ingest/processing.py +64 -73
  109. nucliadb/ingest/py.typed +0 -0
  110. nucliadb/ingest/serialize.py +37 -167
  111. nucliadb/ingest/service/__init__.py +1 -3
  112. nucliadb/ingest/service/writer.py +185 -412
  113. nucliadb/ingest/settings.py +10 -20
  114. nucliadb/ingest/utils.py +3 -6
  115. nucliadb/learning_proxy.py +242 -55
  116. nucliadb/metrics_exporter.py +30 -19
  117. nucliadb/middleware/__init__.py +1 -3
  118. nucliadb/migrator/command.py +1 -3
  119. nucliadb/migrator/datamanager.py +13 -13
  120. nucliadb/migrator/migrator.py +47 -30
  121. nucliadb/migrator/utils.py +18 -10
  122. nucliadb/purge/__init__.py +139 -33
  123. nucliadb/purge/orphan_shards.py +7 -13
  124. nucliadb/reader/__init__.py +1 -3
  125. nucliadb/reader/api/models.py +1 -12
  126. nucliadb/reader/api/v1/__init__.py +0 -1
  127. nucliadb/reader/api/v1/download.py +21 -88
  128. nucliadb/reader/api/v1/export_import.py +1 -1
  129. nucliadb/reader/api/v1/knowledgebox.py +10 -10
  130. nucliadb/reader/api/v1/learning_config.py +2 -6
  131. nucliadb/reader/api/v1/resource.py +62 -88
  132. nucliadb/reader/api/v1/services.py +64 -83
  133. nucliadb/reader/app.py +12 -29
  134. nucliadb/reader/lifecycle.py +18 -4
  135. nucliadb/reader/py.typed +0 -0
  136. nucliadb/reader/reader/notifications.py +10 -28
  137. nucliadb/search/__init__.py +1 -3
  138. nucliadb/search/api/v1/__init__.py +1 -2
  139. nucliadb/search/api/v1/ask.py +17 -10
  140. nucliadb/search/api/v1/catalog.py +184 -0
  141. nucliadb/search/api/v1/feedback.py +16 -24
  142. nucliadb/search/api/v1/find.py +36 -36
  143. nucliadb/search/api/v1/knowledgebox.py +89 -60
  144. nucliadb/search/api/v1/resource/ask.py +2 -8
  145. nucliadb/search/api/v1/resource/search.py +49 -70
  146. nucliadb/search/api/v1/search.py +44 -210
  147. nucliadb/search/api/v1/suggest.py +39 -54
  148. nucliadb/search/app.py +12 -32
  149. nucliadb/search/lifecycle.py +10 -3
  150. nucliadb/search/predict.py +136 -187
  151. nucliadb/search/py.typed +0 -0
  152. nucliadb/search/requesters/utils.py +25 -58
  153. nucliadb/search/search/cache.py +149 -20
  154. nucliadb/search/search/chat/ask.py +571 -123
  155. nucliadb/search/{tests/unit/test_run.py → search/chat/exceptions.py} +14 -14
  156. nucliadb/search/search/chat/images.py +41 -17
  157. nucliadb/search/search/chat/prompt.py +817 -266
  158. nucliadb/search/search/chat/query.py +213 -309
  159. nucliadb/{tests/migrations/__init__.py → search/search/cut.py} +8 -8
  160. nucliadb/search/search/fetch.py +43 -36
  161. nucliadb/search/search/filters.py +9 -15
  162. nucliadb/search/search/find.py +214 -53
  163. nucliadb/search/search/find_merge.py +408 -391
  164. nucliadb/search/search/hydrator.py +191 -0
  165. nucliadb/search/search/merge.py +187 -223
  166. nucliadb/search/search/metrics.py +73 -2
  167. nucliadb/search/search/paragraphs.py +64 -106
  168. nucliadb/search/search/pgcatalog.py +233 -0
  169. nucliadb/search/search/predict_proxy.py +1 -1
  170. nucliadb/search/search/query.py +305 -150
  171. nucliadb/search/search/query_parser/exceptions.py +22 -0
  172. nucliadb/search/search/query_parser/models.py +101 -0
  173. nucliadb/search/search/query_parser/parser.py +183 -0
  174. nucliadb/search/search/rank_fusion.py +204 -0
  175. nucliadb/search/search/rerankers.py +270 -0
  176. nucliadb/search/search/shards.py +3 -32
  177. nucliadb/search/search/summarize.py +7 -18
  178. nucliadb/search/search/utils.py +27 -4
  179. nucliadb/search/settings.py +15 -1
  180. nucliadb/standalone/api_router.py +4 -10
  181. nucliadb/standalone/app.py +8 -14
  182. nucliadb/standalone/auth.py +7 -21
  183. nucliadb/standalone/config.py +7 -10
  184. nucliadb/standalone/lifecycle.py +26 -25
  185. nucliadb/standalone/migrations.py +1 -3
  186. nucliadb/standalone/purge.py +1 -1
  187. nucliadb/standalone/py.typed +0 -0
  188. nucliadb/standalone/run.py +3 -6
  189. nucliadb/standalone/settings.py +9 -16
  190. nucliadb/standalone/versions.py +15 -5
  191. nucliadb/tasks/consumer.py +8 -12
  192. nucliadb/tasks/producer.py +7 -6
  193. nucliadb/tests/config.py +53 -0
  194. nucliadb/train/__init__.py +1 -3
  195. nucliadb/train/api/utils.py +1 -2
  196. nucliadb/train/api/v1/shards.py +1 -1
  197. nucliadb/train/api/v1/trainset.py +2 -4
  198. nucliadb/train/app.py +10 -31
  199. nucliadb/train/generator.py +10 -19
  200. nucliadb/train/generators/field_classifier.py +7 -19
  201. nucliadb/train/generators/field_streaming.py +156 -0
  202. nucliadb/train/generators/image_classifier.py +12 -18
  203. nucliadb/train/generators/paragraph_classifier.py +5 -9
  204. nucliadb/train/generators/paragraph_streaming.py +6 -9
  205. nucliadb/train/generators/question_answer_streaming.py +19 -20
  206. nucliadb/train/generators/sentence_classifier.py +9 -15
  207. nucliadb/train/generators/token_classifier.py +48 -39
  208. nucliadb/train/generators/utils.py +14 -18
  209. nucliadb/train/lifecycle.py +7 -3
  210. nucliadb/train/nodes.py +23 -32
  211. nucliadb/train/py.typed +0 -0
  212. nucliadb/train/servicer.py +13 -21
  213. nucliadb/train/settings.py +2 -6
  214. nucliadb/train/types.py +13 -10
  215. nucliadb/train/upload.py +3 -6
  216. nucliadb/train/uploader.py +19 -23
  217. nucliadb/train/utils.py +1 -1
  218. nucliadb/writer/__init__.py +1 -3
  219. nucliadb/{ingest/fields/keywordset.py → writer/api/utils.py} +13 -10
  220. nucliadb/writer/api/v1/export_import.py +67 -14
  221. nucliadb/writer/api/v1/field.py +16 -269
  222. nucliadb/writer/api/v1/knowledgebox.py +218 -68
  223. nucliadb/writer/api/v1/resource.py +68 -88
  224. nucliadb/writer/api/v1/services.py +51 -70
  225. nucliadb/writer/api/v1/slug.py +61 -0
  226. nucliadb/writer/api/v1/transaction.py +67 -0
  227. nucliadb/writer/api/v1/upload.py +114 -113
  228. nucliadb/writer/app.py +6 -43
  229. nucliadb/writer/back_pressure.py +16 -38
  230. nucliadb/writer/exceptions.py +0 -4
  231. nucliadb/writer/lifecycle.py +21 -15
  232. nucliadb/writer/py.typed +0 -0
  233. nucliadb/writer/resource/audit.py +2 -1
  234. nucliadb/writer/resource/basic.py +48 -46
  235. nucliadb/writer/resource/field.py +25 -127
  236. nucliadb/writer/resource/origin.py +1 -2
  237. nucliadb/writer/settings.py +6 -2
  238. nucliadb/writer/tus/__init__.py +17 -15
  239. nucliadb/writer/tus/azure.py +111 -0
  240. nucliadb/writer/tus/dm.py +17 -5
  241. nucliadb/writer/tus/exceptions.py +1 -3
  242. nucliadb/writer/tus/gcs.py +49 -84
  243. nucliadb/writer/tus/local.py +21 -37
  244. nucliadb/writer/tus/s3.py +28 -68
  245. nucliadb/writer/tus/storage.py +5 -56
  246. nucliadb/writer/vectorsets.py +125 -0
  247. nucliadb-6.2.1.post2777.dist-info/METADATA +148 -0
  248. nucliadb-6.2.1.post2777.dist-info/RECORD +343 -0
  249. {nucliadb-4.0.0.post542.dist-info → nucliadb-6.2.1.post2777.dist-info}/WHEEL +1 -1
  250. nucliadb/common/maindb/redis.py +0 -194
  251. nucliadb/common/maindb/tikv.py +0 -433
  252. nucliadb/ingest/fields/layout.py +0 -58
  253. nucliadb/ingest/tests/conftest.py +0 -30
  254. nucliadb/ingest/tests/fixtures.py +0 -764
  255. nucliadb/ingest/tests/integration/consumer/__init__.py +0 -18
  256. nucliadb/ingest/tests/integration/consumer/test_auditing.py +0 -78
  257. nucliadb/ingest/tests/integration/consumer/test_materializer.py +0 -126
  258. nucliadb/ingest/tests/integration/consumer/test_pull.py +0 -144
  259. nucliadb/ingest/tests/integration/consumer/test_service.py +0 -81
  260. nucliadb/ingest/tests/integration/consumer/test_shard_creator.py +0 -68
  261. nucliadb/ingest/tests/integration/ingest/test_ingest.py +0 -684
  262. nucliadb/ingest/tests/integration/ingest/test_processing_engine.py +0 -95
  263. nucliadb/ingest/tests/integration/ingest/test_relations.py +0 -272
  264. nucliadb/ingest/tests/unit/consumer/__init__.py +0 -18
  265. nucliadb/ingest/tests/unit/consumer/test_auditing.py +0 -139
  266. nucliadb/ingest/tests/unit/consumer/test_consumer.py +0 -69
  267. nucliadb/ingest/tests/unit/consumer/test_pull.py +0 -60
  268. nucliadb/ingest/tests/unit/consumer/test_shard_creator.py +0 -140
  269. nucliadb/ingest/tests/unit/consumer/test_utils.py +0 -67
  270. nucliadb/ingest/tests/unit/orm/__init__.py +0 -19
  271. nucliadb/ingest/tests/unit/orm/test_brain.py +0 -247
  272. nucliadb/ingest/tests/unit/orm/test_brain_vectors.py +0 -74
  273. nucliadb/ingest/tests/unit/orm/test_processor.py +0 -131
  274. nucliadb/ingest/tests/unit/orm/test_resource.py +0 -331
  275. nucliadb/ingest/tests/unit/test_cache.py +0 -31
  276. nucliadb/ingest/tests/unit/test_partitions.py +0 -40
  277. nucliadb/ingest/tests/unit/test_processing.py +0 -171
  278. nucliadb/middleware/transaction.py +0 -117
  279. nucliadb/reader/api/v1/learning_collector.py +0 -63
  280. nucliadb/reader/tests/__init__.py +0 -19
  281. nucliadb/reader/tests/conftest.py +0 -31
  282. nucliadb/reader/tests/fixtures.py +0 -136
  283. nucliadb/reader/tests/test_list_resources.py +0 -75
  284. nucliadb/reader/tests/test_reader_file_download.py +0 -273
  285. nucliadb/reader/tests/test_reader_resource.py +0 -353
  286. nucliadb/reader/tests/test_reader_resource_field.py +0 -219
  287. nucliadb/search/api/v1/chat.py +0 -263
  288. nucliadb/search/api/v1/resource/chat.py +0 -174
  289. nucliadb/search/tests/__init__.py +0 -19
  290. nucliadb/search/tests/conftest.py +0 -33
  291. nucliadb/search/tests/fixtures.py +0 -199
  292. nucliadb/search/tests/node.py +0 -466
  293. nucliadb/search/tests/unit/__init__.py +0 -18
  294. nucliadb/search/tests/unit/api/__init__.py +0 -19
  295. nucliadb/search/tests/unit/api/v1/__init__.py +0 -19
  296. nucliadb/search/tests/unit/api/v1/resource/__init__.py +0 -19
  297. nucliadb/search/tests/unit/api/v1/resource/test_chat.py +0 -98
  298. nucliadb/search/tests/unit/api/v1/test_ask.py +0 -120
  299. nucliadb/search/tests/unit/api/v1/test_chat.py +0 -96
  300. nucliadb/search/tests/unit/api/v1/test_predict_proxy.py +0 -98
  301. nucliadb/search/tests/unit/api/v1/test_summarize.py +0 -99
  302. nucliadb/search/tests/unit/search/__init__.py +0 -18
  303. nucliadb/search/tests/unit/search/requesters/__init__.py +0 -18
  304. nucliadb/search/tests/unit/search/requesters/test_utils.py +0 -211
  305. nucliadb/search/tests/unit/search/search/__init__.py +0 -19
  306. nucliadb/search/tests/unit/search/search/test_shards.py +0 -45
  307. nucliadb/search/tests/unit/search/search/test_utils.py +0 -82
  308. nucliadb/search/tests/unit/search/test_chat_prompt.py +0 -270
  309. nucliadb/search/tests/unit/search/test_fetch.py +0 -108
  310. nucliadb/search/tests/unit/search/test_filters.py +0 -125
  311. nucliadb/search/tests/unit/search/test_paragraphs.py +0 -157
  312. nucliadb/search/tests/unit/search/test_predict_proxy.py +0 -106
  313. nucliadb/search/tests/unit/search/test_query.py +0 -153
  314. nucliadb/search/tests/unit/test_app.py +0 -79
  315. nucliadb/search/tests/unit/test_find_merge.py +0 -112
  316. nucliadb/search/tests/unit/test_merge.py +0 -34
  317. nucliadb/search/tests/unit/test_predict.py +0 -525
  318. nucliadb/standalone/tests/__init__.py +0 -19
  319. nucliadb/standalone/tests/conftest.py +0 -33
  320. nucliadb/standalone/tests/fixtures.py +0 -38
  321. nucliadb/standalone/tests/unit/__init__.py +0 -18
  322. nucliadb/standalone/tests/unit/test_api_router.py +0 -61
  323. nucliadb/standalone/tests/unit/test_auth.py +0 -169
  324. nucliadb/standalone/tests/unit/test_introspect.py +0 -35
  325. nucliadb/standalone/tests/unit/test_migrations.py +0 -63
  326. nucliadb/standalone/tests/unit/test_versions.py +0 -68
  327. nucliadb/tests/benchmarks/__init__.py +0 -19
  328. nucliadb/tests/benchmarks/test_search.py +0 -99
  329. nucliadb/tests/conftest.py +0 -32
  330. nucliadb/tests/fixtures.py +0 -735
  331. nucliadb/tests/knowledgeboxes/philosophy_books.py +0 -202
  332. nucliadb/tests/knowledgeboxes/ten_dummy_resources.py +0 -107
  333. nucliadb/tests/migrations/test_migration_0017.py +0 -76
  334. nucliadb/tests/migrations/test_migration_0018.py +0 -95
  335. nucliadb/tests/tikv.py +0 -240
  336. nucliadb/tests/unit/__init__.py +0 -19
  337. nucliadb/tests/unit/common/__init__.py +0 -19
  338. nucliadb/tests/unit/common/cluster/__init__.py +0 -19
  339. nucliadb/tests/unit/common/cluster/discovery/__init__.py +0 -19
  340. nucliadb/tests/unit/common/cluster/discovery/test_k8s.py +0 -172
  341. nucliadb/tests/unit/common/cluster/standalone/__init__.py +0 -18
  342. nucliadb/tests/unit/common/cluster/standalone/test_service.py +0 -114
  343. nucliadb/tests/unit/common/cluster/standalone/test_utils.py +0 -61
  344. nucliadb/tests/unit/common/cluster/test_cluster.py +0 -408
  345. nucliadb/tests/unit/common/cluster/test_kb_shard_manager.py +0 -173
  346. nucliadb/tests/unit/common/cluster/test_rebalance.py +0 -38
  347. nucliadb/tests/unit/common/cluster/test_rollover.py +0 -282
  348. nucliadb/tests/unit/common/maindb/__init__.py +0 -18
  349. nucliadb/tests/unit/common/maindb/test_driver.py +0 -127
  350. nucliadb/tests/unit/common/maindb/test_tikv.py +0 -53
  351. nucliadb/tests/unit/common/maindb/test_utils.py +0 -92
  352. nucliadb/tests/unit/common/test_context.py +0 -36
  353. nucliadb/tests/unit/export_import/__init__.py +0 -19
  354. nucliadb/tests/unit/export_import/test_datamanager.py +0 -37
  355. nucliadb/tests/unit/export_import/test_utils.py +0 -301
  356. nucliadb/tests/unit/migrator/__init__.py +0 -19
  357. nucliadb/tests/unit/migrator/test_migrator.py +0 -87
  358. nucliadb/tests/unit/tasks/__init__.py +0 -19
  359. nucliadb/tests/unit/tasks/conftest.py +0 -42
  360. nucliadb/tests/unit/tasks/test_consumer.py +0 -92
  361. nucliadb/tests/unit/tasks/test_producer.py +0 -95
  362. nucliadb/tests/unit/tasks/test_tasks.py +0 -58
  363. nucliadb/tests/unit/test_field_ids.py +0 -49
  364. nucliadb/tests/unit/test_health.py +0 -86
  365. nucliadb/tests/unit/test_kb_slugs.py +0 -54
  366. nucliadb/tests/unit/test_learning_proxy.py +0 -252
  367. nucliadb/tests/unit/test_metrics_exporter.py +0 -77
  368. nucliadb/tests/unit/test_purge.py +0 -136
  369. nucliadb/tests/utils/__init__.py +0 -74
  370. nucliadb/tests/utils/aiohttp_session.py +0 -44
  371. nucliadb/tests/utils/broker_messages/__init__.py +0 -171
  372. nucliadb/tests/utils/broker_messages/fields.py +0 -197
  373. nucliadb/tests/utils/broker_messages/helpers.py +0 -33
  374. nucliadb/tests/utils/entities.py +0 -78
  375. nucliadb/train/api/v1/check.py +0 -60
  376. nucliadb/train/tests/__init__.py +0 -19
  377. nucliadb/train/tests/conftest.py +0 -29
  378. nucliadb/train/tests/fixtures.py +0 -342
  379. nucliadb/train/tests/test_field_classification.py +0 -122
  380. nucliadb/train/tests/test_get_entities.py +0 -80
  381. nucliadb/train/tests/test_get_info.py +0 -51
  382. nucliadb/train/tests/test_get_ontology.py +0 -34
  383. nucliadb/train/tests/test_get_ontology_count.py +0 -63
  384. nucliadb/train/tests/test_image_classification.py +0 -221
  385. nucliadb/train/tests/test_list_fields.py +0 -39
  386. nucliadb/train/tests/test_list_paragraphs.py +0 -73
  387. nucliadb/train/tests/test_list_resources.py +0 -39
  388. nucliadb/train/tests/test_list_sentences.py +0 -71
  389. nucliadb/train/tests/test_paragraph_classification.py +0 -123
  390. nucliadb/train/tests/test_paragraph_streaming.py +0 -118
  391. nucliadb/train/tests/test_question_answer_streaming.py +0 -239
  392. nucliadb/train/tests/test_sentence_classification.py +0 -143
  393. nucliadb/train/tests/test_token_classification.py +0 -136
  394. nucliadb/train/tests/utils.py +0 -101
  395. nucliadb/writer/layouts/__init__.py +0 -51
  396. nucliadb/writer/layouts/v1.py +0 -59
  397. nucliadb/writer/tests/__init__.py +0 -19
  398. nucliadb/writer/tests/conftest.py +0 -31
  399. nucliadb/writer/tests/fixtures.py +0 -191
  400. nucliadb/writer/tests/test_fields.py +0 -475
  401. nucliadb/writer/tests/test_files.py +0 -740
  402. nucliadb/writer/tests/test_knowledgebox.py +0 -49
  403. nucliadb/writer/tests/test_reprocess_file_field.py +0 -133
  404. nucliadb/writer/tests/test_resources.py +0 -476
  405. nucliadb/writer/tests/test_service.py +0 -137
  406. nucliadb/writer/tests/test_tus.py +0 -203
  407. nucliadb/writer/tests/utils.py +0 -35
  408. nucliadb/writer/tus/pg.py +0 -125
  409. nucliadb-4.0.0.post542.dist-info/METADATA +0 -135
  410. nucliadb-4.0.0.post542.dist-info/RECORD +0 -462
  411. {nucliadb/ingest/tests → migrations/pg}/__init__.py +0 -0
  412. /nucliadb/{ingest/tests/integration → common/external_index_providers}/__init__.py +0 -0
  413. /nucliadb/{ingest/tests/integration/ingest → common/models_utils}/__init__.py +0 -0
  414. /nucliadb/{ingest/tests/unit → search/search/query_parser}/__init__.py +0 -0
  415. /nucliadb/{ingest/tests → tests}/vectors.py +0 -0
  416. {nucliadb-4.0.0.post542.dist-info → nucliadb-6.2.1.post2777.dist-info}/entry_points.txt +0 -0
  417. {nucliadb-4.0.0.post542.dist-info → nucliadb-6.2.1.post2777.dist-info}/top_level.txt +0 -0
  418. {nucliadb-4.0.0.post542.dist-info → nucliadb-6.2.1.post2777.dist-info}/zip-safe +0 -0
@@ -18,75 +18,142 @@
18
18
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
19
  #
20
20
  import asyncio
21
- import json
21
+ from functools import partial
22
22
 
23
23
  from fastapi import HTTPException, Response
24
24
  from fastapi_versioning import version
25
- from nucliadb_protos.knowledgebox_pb2 import (
26
- DeleteKnowledgeBoxResponse,
27
- KnowledgeBoxID,
28
- KnowledgeBoxNew,
29
- KnowledgeBoxResponseStatus,
30
- KnowledgeBoxUpdate,
31
- NewKnowledgeBoxResponse,
32
- UpdateKnowledgeBoxResponse,
33
- )
34
25
  from starlette.requests import Request
35
26
 
27
+ from nucliadb import learning_proxy
28
+ from nucliadb.common import datamanagers
29
+ from nucliadb.common.external_index_providers.exceptions import (
30
+ ExternalIndexCreationError,
31
+ )
32
+ from nucliadb.common.maindb.utils import get_driver
33
+ from nucliadb.ingest.orm.exceptions import KnowledgeBoxConflict
34
+ from nucliadb.ingest.orm.knowledgebox import KnowledgeBox
35
+ from nucliadb.writer import logger, vectorsets
36
+ from nucliadb.writer.api.utils import only_for_onprem
36
37
  from nucliadb.writer.api.v1.router import KB_PREFIX, KBS_PREFIX, api
37
38
  from nucliadb.writer.utilities import get_processing
39
+ from nucliadb_models.external_index_providers import (
40
+ ExternalIndexProviderType,
41
+ PineconeServerlessCloud,
42
+ )
38
43
  from nucliadb_models.resource import (
39
44
  KnowledgeBoxConfig,
40
45
  KnowledgeBoxObj,
41
46
  KnowledgeBoxObjID,
42
47
  NucliaDBRoles,
43
48
  )
49
+ from nucliadb_protos import knowledgebox_pb2
44
50
  from nucliadb_utils.authentication import requires
45
- from nucliadb_utils.utilities import get_ingest
46
51
 
47
52
 
53
+ @only_for_onprem
48
54
  @api.post(
49
55
  f"/{KBS_PREFIX}",
50
56
  status_code=201,
51
57
  summary="Create Knowledge Box",
52
- response_model=KnowledgeBoxObj,
53
58
  tags=["Knowledge Boxes"],
54
59
  openapi_extra={"x-hidden-operation": True},
55
60
  )
56
61
  @requires(NucliaDBRoles.MANAGER)
57
62
  @version(1)
58
- async def create_kb(request: Request, item: KnowledgeBoxConfig):
59
- ingest = get_ingest()
60
- requestpb = KnowledgeBoxNew()
61
- requestpb = parse_create_kb_request(item)
62
- kbobj: NewKnowledgeBoxResponse = await ingest.NewKnowledgeBox(requestpb) # type: ignore
63
- if item.slug != "":
64
- slug = item.slug
65
- else:
66
- slug = kbobj.uuid # type: ignore
67
- if kbobj.status == KnowledgeBoxResponseStatus.OK:
68
- return KnowledgeBoxObj(uuid=kbobj.uuid, slug=slug)
69
- elif kbobj.status == KnowledgeBoxResponseStatus.CONFLICT:
63
+ async def create_kb_endpoint(request: Request, item: KnowledgeBoxConfig) -> KnowledgeBoxObj:
64
+ try:
65
+ kbid, slug = await create_kb(item)
66
+ except KnowledgeBoxConflict:
70
67
  raise HTTPException(status_code=419, detail="Knowledge box already exists")
71
- elif kbobj.status == KnowledgeBoxResponseStatus.ERROR:
72
- raise HTTPException(status_code=500, detail="Error on creating knowledge box")
73
-
74
-
75
- def parse_create_kb_request(item: KnowledgeBoxConfig) -> KnowledgeBoxNew:
76
- requestpb = KnowledgeBoxNew()
77
- if item.slug:
78
- requestpb.slug = item.slug
79
- if item.title:
80
- requestpb.config.title = item.title
81
- if item.description:
82
- requestpb.config.description = item.description
83
- if item.release_channel:
84
- requestpb.release_channel = item.release_channel.to_pb()
68
+ except ExternalIndexCreationError as exc:
69
+ raise HTTPException(status_code=502, detail=str(exc))
70
+ except Exception:
71
+ logger.exception("Could not create KB")
72
+ raise HTTPException(status_code=500, detail="Error creating knowledge box")
73
+ else:
74
+ return KnowledgeBoxObj(uuid=kbid, slug=slug)
75
+
76
+
77
+ async def create_kb(item: KnowledgeBoxConfig) -> tuple[str, str]:
78
+ driver = get_driver()
79
+ rollback_learning_config = None
80
+
81
+ kbid = KnowledgeBox.new_unique_kbid()
82
+
83
+ # Onprem KB creation doesn't have an existing learning configuration yet, so
84
+ # we need to call learning proxy to create it
85
85
  if item.learning_configuration:
86
- requestpb.learning_config = json.dumps(item.learning_configuration)
87
- return requestpb
86
+ user_learning_config = item.learning_configuration
87
+ else:
88
+ logger.warning(
89
+ "No learning configuration provided. Default will be used.",
90
+ extra={"kbid": kbid},
91
+ )
92
+ # learning will choose the default values
93
+ user_learning_config = {}
94
+
95
+ # We need to be backward compatible with the old "semantic_model" field where
96
+ # only one semantic model was allowed.
97
+ if "semantic_model" in user_learning_config:
98
+ user_learning_config["semantic_models"] = [user_learning_config.pop("semantic_model")]
88
99
 
100
+ # we rely on learning to return the updated configuration with defaults and
101
+ # any other needed values (e.g. matryoshka settings if available)
102
+ learning_config = await learning_proxy.set_configuration(kbid, config=user_learning_config)
89
103
 
104
+ # if KB creation fails, we'll have to delete its learning config
105
+ async def _rollback_learning_config(kbid: str):
106
+ try:
107
+ await learning_proxy.delete_configuration(kbid)
108
+ except Exception:
109
+ logger.warning(
110
+ "Could not rollback learning configuration",
111
+ exc_info=True,
112
+ extra={"kbid": kbid},
113
+ )
114
+
115
+ rollback_learning_config = partial(_rollback_learning_config, kbid)
116
+ semantic_models = learning_config.into_semantic_models_metadata()
117
+
118
+ external_index_provider = knowledgebox_pb2.CreateExternalIndexProviderMetadata(
119
+ type=knowledgebox_pb2.ExternalIndexProviderType.UNSET,
120
+ )
121
+ if (
122
+ item.external_index_provider
123
+ and item.external_index_provider.type == ExternalIndexProviderType.PINECONE
124
+ ):
125
+ pinecone_api_key = item.external_index_provider.api_key
126
+ serverless_pb = to_pinecone_serverless_cloud_pb(item.external_index_provider.serverless_cloud)
127
+ external_index_provider = knowledgebox_pb2.CreateExternalIndexProviderMetadata(
128
+ type=knowledgebox_pb2.ExternalIndexProviderType.PINECONE,
129
+ pinecone_config=knowledgebox_pb2.CreatePineconeConfig(
130
+ api_key=pinecone_api_key,
131
+ serverless_cloud=serverless_pb,
132
+ ),
133
+ )
134
+
135
+ try:
136
+ (kbid, slug) = await KnowledgeBox.create(
137
+ driver,
138
+ kbid=kbid,
139
+ slug=item.slug or kbid,
140
+ title=item.title or "",
141
+ description=item.description or "",
142
+ semantic_models=semantic_models,
143
+ external_index_provider=external_index_provider,
144
+ hidden_resources_enabled=item.hidden_resources_enabled,
145
+ hidden_resources_hide_on_creation=item.hidden_resources_hide_on_creation,
146
+ )
147
+
148
+ except Exception as exc:
149
+ logger.error("Unexpected error creating KB", exc_info=exc, extra={"slug": item.slug})
150
+ await rollback_learning_config()
151
+ raise
152
+
153
+ return (kbid, slug)
154
+
155
+
156
+ @only_for_onprem
90
157
  @api.patch(
91
158
  f"/{KB_PREFIX}/{{kbid}}",
92
159
  status_code=200,
@@ -97,48 +164,131 @@ def parse_create_kb_request(item: KnowledgeBoxConfig) -> KnowledgeBoxNew:
97
164
  )
98
165
  @requires(NucliaDBRoles.MANAGER)
99
166
  @version(1)
100
- async def update_kb(request: Request, kbid: str, item: KnowledgeBoxConfig):
101
- ingest = get_ingest()
102
- pbrequest = KnowledgeBoxUpdate(uuid=kbid)
103
- if item.slug is not None:
104
- pbrequest.slug = item.slug
105
- if item.title:
106
- pbrequest.config.title = item.title
107
- if item.description:
108
- pbrequest.config.description = item.description
109
- kbobj: UpdateKnowledgeBoxResponse = await ingest.UpdateKnowledgeBox(pbrequest) # type: ignore
110
- if kbobj.status == KnowledgeBoxResponseStatus.OK:
111
- return KnowledgeBoxObjID(uuid=kbobj.uuid)
112
- elif kbobj.status == KnowledgeBoxResponseStatus.NOTFOUND:
167
+ async def update_kb(request: Request, kbid: str, item: KnowledgeBoxConfig) -> KnowledgeBoxObjID:
168
+ driver = get_driver()
169
+ config = None
170
+ if (
171
+ item.slug
172
+ or item.title
173
+ or item.description
174
+ or item.hidden_resources_enabled
175
+ or item.hidden_resources_hide_on_creation
176
+ ):
177
+ config = knowledgebox_pb2.KnowledgeBoxConfig(
178
+ slug=item.slug or "",
179
+ title=item.title or "",
180
+ description=item.description or "",
181
+ hidden_resources_enabled=item.hidden_resources_enabled,
182
+ hidden_resources_hide_on_creation=item.hidden_resources_hide_on_creation,
183
+ )
184
+ try:
185
+ async with driver.transaction() as txn:
186
+ await KnowledgeBox.update(
187
+ txn,
188
+ uuid=kbid,
189
+ slug=item.slug,
190
+ config=config,
191
+ )
192
+ await txn.commit()
193
+ except datamanagers.exceptions.KnowledgeBoxNotFound:
113
194
  raise HTTPException(status_code=404, detail="Knowledge box does not exist")
114
- elif kbobj.status == KnowledgeBoxResponseStatus.ERROR:
115
- raise HTTPException(status_code=500, detail="Error on creating knowledge box")
195
+ except Exception as exc:
196
+ logger.exception("Could not update KB", exc_info=exc, extra={"kbid": kbid})
197
+ raise HTTPException(status_code=500, detail="Error updating knowledge box")
198
+ else:
199
+ return KnowledgeBoxObjID(uuid=kbid)
116
200
 
117
201
 
202
+ @only_for_onprem
118
203
  @api.delete(
119
204
  f"/{KB_PREFIX}/{{kbid}}",
120
205
  status_code=200,
121
206
  summary="Delete Knowledge Box",
122
- response_model=KnowledgeBoxObj,
123
207
  tags=["Knowledge Boxes"],
124
208
  openapi_extra={"x-hidden-operation": True},
125
209
  )
126
210
  @requires(NucliaDBRoles.MANAGER)
127
211
  @version(1)
128
- async def delete_kb(request: Request, kbid: str):
129
- ingest = get_ingest()
130
-
131
- kbobj: DeleteKnowledgeBoxResponse = await ingest.DeleteKnowledgeBox( # type: ignore
132
- KnowledgeBoxID(uuid=kbid)
133
- )
134
- if kbobj.status == KnowledgeBoxResponseStatus.OK:
135
- return KnowledgeBoxObj(uuid=kbid)
136
- elif kbobj.status == KnowledgeBoxResponseStatus.NOTFOUND:
212
+ async def delete_kb(request: Request, kbid: str) -> KnowledgeBoxObj:
213
+ driver = get_driver()
214
+ try:
215
+ await KnowledgeBox.delete(driver, kbid=kbid)
216
+ except datamanagers.exceptions.KnowledgeBoxNotFound:
137
217
  raise HTTPException(status_code=404, detail="Knowledge Box does not exists")
138
- elif kbobj.status == KnowledgeBoxResponseStatus.ERROR:
139
- raise HTTPException(status_code=500, detail="Error on deleting knowledge box")
218
+ except Exception as exc:
219
+ logger.exception("Could not delete KB", exc_info=exc, extra={"kbid": kbid})
220
+ raise HTTPException(status_code=500, detail="Error deleting knowledge box")
221
+
222
+ # onprem nucliadb must delete its learning configuration
223
+ try:
224
+ await learning_proxy.delete_configuration(kbid)
225
+ logger.info("Learning configuration deleted", extra={"kbid": kbid})
226
+ except Exception as exc:
227
+ logger.exception(
228
+ "Unexpected error deleting learning configuration",
229
+ exc_info=exc,
230
+ extra={"kbid": kbid},
231
+ )
140
232
 
233
+ # be nice and notify processing this KB is being deleted so we waste
234
+ # resources
141
235
  processing = get_processing()
142
236
  asyncio.create_task(processing.delete_from_processing(kbid=kbid))
143
237
 
144
- return Response(status_code=204)
238
+ return KnowledgeBoxObj(uuid=kbid)
239
+
240
+
241
+ def to_pinecone_serverless_cloud_pb(
242
+ serverless: PineconeServerlessCloud,
243
+ ) -> knowledgebox_pb2.PineconeServerlessCloud.ValueType:
244
+ return {
245
+ PineconeServerlessCloud.AWS_EU_WEST_1: knowledgebox_pb2.PineconeServerlessCloud.AWS_EU_WEST_1,
246
+ PineconeServerlessCloud.AWS_US_EAST_1: knowledgebox_pb2.PineconeServerlessCloud.AWS_US_EAST_1,
247
+ PineconeServerlessCloud.AWS_US_WEST_2: knowledgebox_pb2.PineconeServerlessCloud.AWS_US_WEST_2,
248
+ PineconeServerlessCloud.AZURE_EASTUS2: knowledgebox_pb2.PineconeServerlessCloud.AZURE_EASTUS2,
249
+ PineconeServerlessCloud.GCP_US_CENTRAL1: knowledgebox_pb2.PineconeServerlessCloud.GCP_US_CENTRAL1,
250
+ }[serverless]
251
+
252
+
253
+ @api.post(
254
+ f"/{KB_PREFIX}/{{kbid}}/vectorsets/{{vectorset_id}}",
255
+ status_code=200,
256
+ summary="Add a vectorset to Knowledge Box",
257
+ tags=["Knowledge Boxes"],
258
+ # TODO: remove when the feature is mature
259
+ include_in_schema=False,
260
+ )
261
+ @requires(NucliaDBRoles.MANAGER)
262
+ @version(1)
263
+ async def add_vectorset(request: Request, kbid: str, vectorset_id: str) -> Response:
264
+ try:
265
+ await vectorsets.add(kbid, vectorset_id)
266
+ except learning_proxy.ProxiedLearningConfigError as err:
267
+ return Response(
268
+ status_code=err.status_code,
269
+ content=err.content,
270
+ media_type=err.content_type,
271
+ )
272
+ return Response(status_code=200)
273
+
274
+
275
+ @api.delete(
276
+ f"/{KB_PREFIX}/{{kbid}}/vectorsets/{{vectorset_id}}",
277
+ status_code=200,
278
+ summary="Delete vectorset from Knowledge Box",
279
+ tags=["Knowledge Boxes"],
280
+ # TODO: remove when the feature is mature
281
+ include_in_schema=False,
282
+ )
283
+ @requires(NucliaDBRoles.MANAGER)
284
+ @version(1)
285
+ async def delete_vectorset(request: Request, kbid: str, vectorset_id: str) -> Response:
286
+ try:
287
+ await vectorsets.delete(kbid, vectorset_id)
288
+ except learning_proxy.ProxiedLearningConfigError as err:
289
+ return Response(
290
+ status_code=err.status_code,
291
+ content=err.content,
292
+ media_type=err.content_type,
293
+ )
294
+ return Response(status_code=200)
@@ -25,8 +25,6 @@ from uuid import uuid4
25
25
 
26
26
  from fastapi import HTTPException, Query, Response
27
27
  from fastapi_versioning import version
28
- from nucliadb_protos.resources_pb2 import Metadata
29
- from nucliadb_protos.writer_pb2 import BrokerMessage, IndexResource
30
28
  from starlette.requests import Request
31
29
 
32
30
  from nucliadb.common import datamanagers
@@ -38,6 +36,7 @@ from nucliadb.ingest.orm.knowledgebox import KnowledgeBox
38
36
  from nucliadb.ingest.processing import ProcessingInfo, PushPayload, Source
39
37
  from nucliadb.writer import SERVICE_NAME, logger
40
38
  from nucliadb.writer.api.constants import SKIP_STORE_DEFAULT, X_NUCLIADB_USER
39
+ from nucliadb.writer.api.v1 import transaction
41
40
  from nucliadb.writer.api.v1.router import (
42
41
  KB_PREFIX,
43
42
  RESOURCE_PREFIX,
@@ -45,10 +44,11 @@ from nucliadb.writer.api.v1.router import (
45
44
  RSLUG_PREFIX,
46
45
  api,
47
46
  )
47
+ from nucliadb.writer.api.v1.slug import ensure_slug_uniqueness, noop_context_manager
48
48
  from nucliadb.writer.back_pressure import maybe_back_pressure
49
49
  from nucliadb.writer.resource.audit import parse_audit
50
50
  from nucliadb.writer.resource.basic import (
51
- parse_basic,
51
+ parse_basic_creation,
52
52
  parse_basic_modify,
53
53
  set_status,
54
54
  set_status_modify,
@@ -63,15 +63,15 @@ from nucliadb_models.writer import (
63
63
  ResourceUpdated,
64
64
  UpdateResourcePayload,
65
65
  )
66
+ from nucliadb_protos.resources_pb2 import Metadata
67
+ from nucliadb_protos.writer_pb2 import BrokerMessage, IndexResource
66
68
  from nucliadb_telemetry.errors import capture_exception
67
69
  from nucliadb_utils.authentication import requires
68
70
  from nucliadb_utils.exceptions import LimitsExceededError, SendToProcessError
69
- from nucliadb_utils.transaction import TransactionCommitTimeoutError
70
71
  from nucliadb_utils.utilities import (
71
72
  get_ingest,
72
73
  get_partitioning,
73
74
  get_storage,
74
- get_transaction_utility,
75
75
  )
76
76
 
77
77
 
@@ -91,10 +91,17 @@ async def create_resource(
91
91
  item: CreateResourcePayload,
92
92
  kbid: str,
93
93
  x_skip_store: bool = SKIP_STORE_DEFAULT,
94
+ x_nucliadb_user: str = X_NUCLIADB_USER,
94
95
  ):
96
+ kb_config = await datamanagers.atomic.kb.get_config(kbid=kbid)
97
+ if item.hidden and not (kb_config and kb_config.hidden_resources_enabled):
98
+ raise HTTPException(
99
+ status_code=422,
100
+ detail="Cannot hide a resource: the KB does not have hidden resources enabled",
101
+ )
102
+
95
103
  await maybe_back_pressure(request, kbid)
96
104
 
97
- transaction = get_transaction_utility()
98
105
  partitioning = get_partitioning()
99
106
 
100
107
  # Create resource message
@@ -106,7 +113,7 @@ async def create_resource(
106
113
  uuid=uuid,
107
114
  kbid=kbid,
108
115
  partition=partition,
109
- userid=request.headers.get("X-NUCLIADB-USER", ""),
116
+ userid=x_nucliadb_user,
110
117
  processing_options=item.processing_options,
111
118
  )
112
119
 
@@ -117,49 +124,46 @@ async def create_resource(
117
124
  toprocess.source = Source.HTTP
118
125
  toprocess.title = item.title
119
126
 
127
+ unique_slug_context_manager = noop_context_manager()
120
128
  if item.slug:
121
- from nucliadb.common import datamanagers
122
-
123
- if await datamanagers.atomic.resources.slug_exists(kbid=kbid, slug=item.slug):
124
- raise HTTPException(
125
- status_code=409, detail=f"Resource slug {item.slug} already exists"
126
- )
129
+ unique_slug_context_manager = ensure_slug_uniqueness(kbid, item.slug)
127
130
  writer.slug = item.slug
128
131
  toprocess.slug = item.slug
129
132
 
130
- parse_audit(writer.audit, request)
131
- parse_basic(writer, item, toprocess)
133
+ async with unique_slug_context_manager:
134
+ parse_audit(writer.audit, request)
135
+ parse_basic_creation(writer, item, toprocess, kb_config)
136
+
137
+ if item.origin is not None:
138
+ parse_origin(writer.origin, item.origin)
139
+ if item.extra is not None:
140
+ parse_extra(writer.extra, item.extra)
141
+
142
+ await parse_fields(
143
+ writer=writer,
144
+ item=item,
145
+ toprocess=toprocess,
146
+ kbid=kbid,
147
+ uuid=uuid,
148
+ x_skip_store=x_skip_store,
149
+ )
132
150
 
133
- if item.origin is not None:
134
- parse_origin(writer.origin, item.origin)
135
- if item.extra is not None:
136
- parse_extra(writer.extra, item.extra)
151
+ set_status(writer.basic, item)
137
152
 
138
- await parse_fields(
139
- writer=writer,
140
- item=item,
141
- toprocess=toprocess,
142
- kbid=kbid,
143
- uuid=uuid,
144
- x_skip_store=x_skip_store,
145
- )
153
+ writer.source = BrokerMessage.MessageSource.WRITER
146
154
 
147
- set_status(writer.basic, item)
155
+ if item.wait_for_commit:
156
+ t0 = time()
157
+ await transaction.commit(writer, partition, wait=item.wait_for_commit)
148
158
 
149
- writer.source = BrokerMessage.MessageSource.WRITER
150
- try:
151
- t0 = time()
152
- await transaction.commit(writer, partition, wait=True)
153
- txn_time = time() - t0
154
- except TransactionCommitTimeoutError:
155
- raise HTTPException(
156
- status_code=501,
157
- detail="Inconsistent write. This resource will not be processed and may not be stored.",
158
- )
159
+ if item.wait_for_commit:
160
+ txn_time = time() - t0
161
+ else:
162
+ txn_time = None
159
163
 
160
- seqid = await maybe_send_to_process(toprocess, partition)
164
+ seqid = await maybe_send_to_process(toprocess, partition)
161
165
 
162
- return ResourceCreated(seqid=seqid, uuid=uuid, elapsed=txn_time)
166
+ return ResourceCreated(seqid=seqid, uuid=uuid, elapsed=txn_time)
163
167
 
164
168
 
165
169
  @api.patch(
@@ -259,7 +263,13 @@ async def modify_resource(
259
263
  *,
260
264
  rid: str,
261
265
  ):
262
- transaction = get_transaction_utility()
266
+ kb_config = await datamanagers.atomic.kb.get_config(kbid=kbid)
267
+ if item.hidden and not (kb_config and kb_config.hidden_resources_enabled):
268
+ raise HTTPException(
269
+ status_code=422,
270
+ detail="Cannot hide a resource: the KB does not have hidden resources enabled",
271
+ )
272
+
263
273
  partitioning = get_partitioning()
264
274
 
265
275
  partition = partitioning.generate_partition(kbid, rid)
@@ -302,14 +312,7 @@ async def modify_resource(
302
312
 
303
313
  maybe_mark_reindex(writer, item)
304
314
 
305
- try:
306
- await transaction.commit(writer, partition, wait=True)
307
- except TransactionCommitTimeoutError:
308
- raise HTTPException(
309
- status_code=501,
310
- detail="Inconsistent write. This resource will not be processed and may not be stored.",
311
- )
312
-
315
+ await transaction.commit(writer, partition)
313
316
  seqid = await maybe_send_to_process(toprocess, partition)
314
317
 
315
318
  return ResourceUpdated(seqid=seqid)
@@ -350,9 +353,7 @@ async def update_resource_slug(
350
353
  new_slug: str,
351
354
  ):
352
355
  async with driver.transaction() as txn:
353
- old_slug = await datamanagers.resources.modify_slug(
354
- txn, kbid=kbid, rid=rid, new_slug=new_slug
355
- )
356
+ old_slug = await datamanagers.resources.modify_slug(txn, kbid=kbid, rid=rid, new_slug=new_slug)
356
357
  await txn.commit()
357
358
  return old_slug
358
359
 
@@ -373,9 +374,7 @@ async def reprocess_resource_rslug_prefix(
373
374
  x_nucliadb_user: str = X_NUCLIADB_USER,
374
375
  ):
375
376
  rid = await get_rid_from_slug_or_raise_error(kbid, rslug)
376
- return await _reprocess_resource(
377
- request, kbid, rid, x_nucliadb_user=x_nucliadb_user
378
- )
377
+ return await _reprocess_resource(request, kbid, rid, x_nucliadb_user=x_nucliadb_user)
379
378
 
380
379
 
381
380
  @api.post(
@@ -393,9 +392,7 @@ async def reprocess_resource_rid_prefix(
393
392
  rid: str,
394
393
  x_nucliadb_user: str = X_NUCLIADB_USER,
395
394
  ):
396
- return await _reprocess_resource(
397
- request, kbid, rid, x_nucliadb_user=x_nucliadb_user
398
- )
395
+ return await _reprocess_resource(request, kbid, rid, x_nucliadb_user=x_nucliadb_user)
399
396
 
400
397
 
401
398
  async def _reprocess_resource(
@@ -407,7 +404,6 @@ async def _reprocess_resource(
407
404
  await validate_rid_exists_or_raise_error(kbid, rid)
408
405
  await maybe_back_pressure(request, kbid, resource_uuid=rid)
409
406
 
410
- transaction = get_transaction_utility()
411
407
  partitioning = get_partitioning()
412
408
 
413
409
  partition = partitioning.generate_partition(kbid, rid)
@@ -441,14 +437,7 @@ async def _reprocess_resource(
441
437
  writer.source = BrokerMessage.MessageSource.WRITER
442
438
  writer.basic.metadata.useful = True
443
439
  writer.basic.metadata.status = Metadata.Status.PENDING
444
- try:
445
- await transaction.commit(writer, partition, wait=False)
446
- except TransactionCommitTimeoutError:
447
- raise HTTPException(
448
- status_code=501,
449
- detail="Inconsistent write. This resource will not be processed and may not be stored.",
450
- )
451
-
440
+ await transaction.commit(writer, partition, wait=False)
452
441
  processing_info = await send_to_process(toprocess, partition)
453
442
 
454
443
  return ResourceUpdated(seqid=processing_info.seqid)
@@ -494,7 +483,6 @@ async def _delete_resource(
494
483
  ):
495
484
  await validate_rid_exists_or_raise_error(kbid, rid)
496
485
 
497
- transaction = get_transaction_utility()
498
486
  partitioning = get_partitioning()
499
487
 
500
488
  partition = partitioning.generate_partition(kbid, rid)
@@ -505,16 +493,7 @@ async def _delete_resource(
505
493
  writer.type = BrokerMessage.MessageType.DELETE
506
494
 
507
495
  parse_audit(writer.audit, request)
508
-
509
- # Create processing message
510
- try:
511
- await transaction.commit(writer, partition, wait=True)
512
- except TransactionCommitTimeoutError:
513
- raise HTTPException(
514
- status_code=501,
515
- detail="Inconsistent write. This resource will not be processed and may not be stored.",
516
- )
517
-
496
+ await transaction.commit(writer, partition)
518
497
  processing = get_processing()
519
498
  asyncio.create_task(processing.delete_from_processing(kbid=kbid, resource_id=rid))
520
499
 
@@ -576,9 +555,7 @@ async def _reindex_resource(
576
555
 
577
556
 
578
557
  async def get_rid_from_slug_or_raise_error(kbid: str, rslug: str) -> str:
579
- rid = await datamanagers.atomic.resources.get_resource_uuid_from_slug(
580
- kbid=kbid, slug=rslug
581
- )
558
+ rid = await datamanagers.atomic.resources.get_resource_uuid_from_slug(kbid=kbid, slug=rslug)
582
559
  if not rid:
583
560
  raise HTTPException(status_code=404, detail="Resource does not exist")
584
561
  return rid
@@ -599,12 +576,16 @@ def needs_resource_reindex(item: UpdateResourcePayload) -> bool:
599
576
  # a resource and that means this message should force reindexing everything.
600
577
  # XXX This is not ideal. Long term, we should handle it differently
601
578
  # so this is not required
602
- return item.usermetadata is not None or (
603
- item.origin is not None
604
- and (
605
- item.origin.created is not None
606
- or item.origin.modified is not None
607
- or item.origin.metadata is not None
579
+ return (
580
+ item.usermetadata is not None
581
+ or item.hidden is not None
582
+ or (
583
+ item.origin is not None
584
+ and (
585
+ item.origin.created is not None
586
+ or item.origin.modified is not None
587
+ or item.origin.metadata is not None
588
+ )
608
589
  )
609
590
  )
610
591
 
@@ -642,7 +623,6 @@ def needs_reprocess(processing_payload: PushPayload) -> bool:
642
623
  "filefield",
643
624
  "linkfield",
644
625
  "textfield",
645
- "layoutfield",
646
626
  "conversationfield",
647
627
  ):
648
628
  if len(getattr(processing_payload, field)) > 0: