coco-framework 0.1.0__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 (400) hide show
  1. coco/__init__.py +3 -0
  2. coco/agents/__init__.py +9 -0
  3. coco/agents/_subagent_transformer.py +307 -0
  4. coco/agents/factory.py +2164 -0
  5. coco/agents/middleware/__init__.py +100 -0
  6. coco/agents/middleware/_execution.py +385 -0
  7. coco/agents/middleware/_redaction.py +454 -0
  8. coco/agents/middleware/_retry.py +134 -0
  9. coco/agents/middleware/_trace_policy.py +61 -0
  10. coco/agents/middleware/context_editing.py +306 -0
  11. coco/agents/middleware/file_search.py +433 -0
  12. coco/agents/middleware/human_in_the_loop.py +680 -0
  13. coco/agents/middleware/internal_call_transformer.py +108 -0
  14. coco/agents/middleware/model_call_limit.py +267 -0
  15. coco/agents/middleware/model_fallback.py +417 -0
  16. coco/agents/middleware/model_retry.py +327 -0
  17. coco/agents/middleware/pii.py +878 -0
  18. coco/agents/middleware/provider_tool_search.py +307 -0
  19. coco/agents/middleware/shell_tool.py +949 -0
  20. coco/agents/middleware/summarization.py +910 -0
  21. coco/agents/middleware/todo.py +357 -0
  22. coco/agents/middleware/tool_call_limit.py +495 -0
  23. coco/agents/middleware/tool_emulator.py +251 -0
  24. coco/agents/middleware/tool_error.py +209 -0
  25. coco/agents/middleware/tool_retry.py +421 -0
  26. coco/agents/middleware/tool_selection.py +515 -0
  27. coco/agents/middleware/types.py +2201 -0
  28. coco/agents/structured_output.py +472 -0
  29. coco/chat_models/__init__.py +7 -0
  30. coco/chat_models/base.py +1082 -0
  31. coco/embeddings/__init__.py +18 -0
  32. coco/embeddings/base.py +279 -0
  33. coco/mcp/__init__.py +36 -0
  34. coco/mcp/adapter.py +245 -0
  35. coco/mcp/elicitation.py +394 -0
  36. coco/mcp/tools.py +307 -0
  37. coco/messages/__init__.py +73 -0
  38. coco/py.typed +0 -0
  39. coco/rate_limiters/__init__.py +13 -0
  40. coco/tools/__init__.py +22 -0
  41. coco/tools/tool_node.py +20 -0
  42. coco_anthropic/__init__.py +15 -0
  43. coco_anthropic/_client_utils.py +81 -0
  44. coco_anthropic/_compat.py +228 -0
  45. coco_anthropic/_sdk_compat.py +124 -0
  46. coco_anthropic/_version.py +3 -0
  47. coco_anthropic/chat_models.py +3349 -0
  48. coco_anthropic/data/__init__.py +1 -0
  49. coco_anthropic/data/_profiles.py +491 -0
  50. coco_anthropic/data/profile_augmentations.toml +69 -0
  51. coco_anthropic/experimental.py +140 -0
  52. coco_anthropic/llms.py +428 -0
  53. coco_anthropic/middleware/__init__.py +25 -0
  54. coco_anthropic/middleware/anthropic_tools.py +1197 -0
  55. coco_anthropic/middleware/bash.py +84 -0
  56. coco_anthropic/middleware/file_search.py +360 -0
  57. coco_anthropic/middleware/prompt_caching.py +276 -0
  58. coco_anthropic/output_parsers.py +93 -0
  59. coco_anthropic/py.typed +0 -0
  60. coco_core/__init__.py +20 -0
  61. coco_core/_api/__init__.py +87 -0
  62. coco_core/_api/beta_decorator.py +266 -0
  63. coco_core/_api/deprecation.py +637 -0
  64. coco_core/_api/internal.py +23 -0
  65. coco_core/_api/path.py +50 -0
  66. coco_core/_import_utils.py +41 -0
  67. coco_core/_protocol.py +625 -0
  68. coco_core/_security/__init__.py +36 -0
  69. coco_core/_security/_exceptions.py +9 -0
  70. coco_core/_security/_policy.py +313 -0
  71. coco_core/_security/_ssrf_protection.py +155 -0
  72. coco_core/_security/_transport.py +254 -0
  73. coco_core/agents.py +258 -0
  74. coco_core/caches.py +272 -0
  75. coco_core/callbacks/__init__.py +132 -0
  76. coco_core/callbacks/base.py +1229 -0
  77. coco_core/callbacks/file.py +266 -0
  78. coco_core/callbacks/manager.py +2826 -0
  79. coco_core/callbacks/stdout.py +123 -0
  80. coco_core/callbacks/streaming_stdout.py +154 -0
  81. coco_core/callbacks/usage.py +124 -0
  82. coco_core/chat_history.py +267 -0
  83. coco_core/chat_loaders.py +26 -0
  84. coco_core/chat_sessions.py +19 -0
  85. coco_core/cross_encoders.py +18 -0
  86. coco_core/document_loaders/__init__.py +36 -0
  87. coco_core/document_loaders/base.py +155 -0
  88. coco_core/document_loaders/blob_loaders.py +38 -0
  89. coco_core/documents/__init__.py +55 -0
  90. coco_core/documents/base.py +347 -0
  91. coco_core/documents/compressor.py +74 -0
  92. coco_core/documents/transformers.py +79 -0
  93. coco_core/embeddings/__init__.py +31 -0
  94. coco_core/embeddings/embeddings.py +78 -0
  95. coco_core/embeddings/fake.py +129 -0
  96. coco_core/env.py +22 -0
  97. coco_core/example_selectors/__init__.py +47 -0
  98. coco_core/example_selectors/base.py +60 -0
  99. coco_core/example_selectors/length_based.py +131 -0
  100. coco_core/example_selectors/semantic_similarity.py +366 -0
  101. coco_core/exceptions.py +166 -0
  102. coco_core/globals.py +72 -0
  103. coco_core/indexing/__init__.py +53 -0
  104. coco_core/indexing/api.py +954 -0
  105. coco_core/indexing/base.py +661 -0
  106. coco_core/indexing/in_memory.py +104 -0
  107. coco_core/language_models/__init__.py +116 -0
  108. coco_core/language_models/_compat_bridge.py +844 -0
  109. coco_core/language_models/_utils.py +343 -0
  110. coco_core/language_models/base.py +500 -0
  111. coco_core/language_models/chat_model_stream.py +1525 -0
  112. coco_core/language_models/chat_models.py +2748 -0
  113. coco_core/language_models/fake.py +137 -0
  114. coco_core/language_models/fake_chat_models.py +396 -0
  115. coco_core/language_models/llms.py +1569 -0
  116. coco_core/language_models/model_profile.py +183 -0
  117. coco_core/load/__init__.py +44 -0
  118. coco_core/load/_validation.py +191 -0
  119. coco_core/load/dump.py +120 -0
  120. coco_core/load/load.py +828 -0
  121. coco_core/load/mapping.py +1097 -0
  122. coco_core/load/serializable.py +406 -0
  123. coco_core/messages/__init__.py +198 -0
  124. coco_core/messages/ai.py +854 -0
  125. coco_core/messages/base.py +518 -0
  126. coco_core/messages/block_translators/__init__.py +112 -0
  127. coco_core/messages/block_translators/anthropic.py +534 -0
  128. coco_core/messages/block_translators/bedrock.py +108 -0
  129. coco_core/messages/block_translators/bedrock_converse.py +329 -0
  130. coco_core/messages/block_translators/coco_v0.py +303 -0
  131. coco_core/messages/block_translators/google_genai.py +610 -0
  132. coco_core/messages/block_translators/google_vertexai.py +21 -0
  133. coco_core/messages/block_translators/groq.py +157 -0
  134. coco_core/messages/block_translators/openai.py +1089 -0
  135. coco_core/messages/chat.py +64 -0
  136. coco_core/messages/content.py +1488 -0
  137. coco_core/messages/function.py +62 -0
  138. coco_core/messages/human.py +70 -0
  139. coco_core/messages/modifier.py +33 -0
  140. coco_core/messages/system.py +70 -0
  141. coco_core/messages/tool.py +418 -0
  142. coco_core/messages/utils.py +2442 -0
  143. coco_core/output_parsers/__init__.py +101 -0
  144. coco_core/output_parsers/base.py +361 -0
  145. coco_core/output_parsers/format_instructions.py +16 -0
  146. coco_core/output_parsers/json.py +139 -0
  147. coco_core/output_parsers/list.py +249 -0
  148. coco_core/output_parsers/openai_functions.py +324 -0
  149. coco_core/output_parsers/openai_tools.py +383 -0
  150. coco_core/output_parsers/pydantic.py +141 -0
  151. coco_core/output_parsers/string.py +63 -0
  152. coco_core/output_parsers/transform.py +175 -0
  153. coco_core/output_parsers/xml.py +301 -0
  154. coco_core/outputs/__init__.py +66 -0
  155. coco_core/outputs/chat_generation.py +147 -0
  156. coco_core/outputs/chat_result.py +39 -0
  157. coco_core/outputs/generation.py +80 -0
  158. coco_core/outputs/llm_result.py +112 -0
  159. coco_core/outputs/run_info.py +22 -0
  160. coco_core/prompt_values.py +161 -0
  161. coco_core/prompts/__init__.py +101 -0
  162. coco_core/prompts/base.py +508 -0
  163. coco_core/prompts/chat.py +1495 -0
  164. coco_core/prompts/dict.py +177 -0
  165. coco_core/prompts/few_shot.py +483 -0
  166. coco_core/prompts/few_shot_with_templates.py +239 -0
  167. coco_core/prompts/image.py +177 -0
  168. coco_core/prompts/loading.py +296 -0
  169. coco_core/prompts/message.py +97 -0
  170. coco_core/prompts/prompt.py +312 -0
  171. coco_core/prompts/string.py +399 -0
  172. coco_core/prompts/structured.py +214 -0
  173. coco_core/py.typed +0 -0
  174. coco_core/rate_limiters.py +254 -0
  175. coco_core/retrievers.py +328 -0
  176. coco_core/runnables/__init__.py +136 -0
  177. coco_core/runnables/base.py +6725 -0
  178. coco_core/runnables/branch.py +457 -0
  179. coco_core/runnables/config.py +712 -0
  180. coco_core/runnables/configurable.py +710 -0
  181. coco_core/runnables/fallbacks.py +663 -0
  182. coco_core/runnables/graph.py +741 -0
  183. coco_core/runnables/graph_ascii.py +366 -0
  184. coco_core/runnables/graph_mermaid.py +503 -0
  185. coco_core/runnables/graph_png.py +215 -0
  186. coco_core/runnables/history.py +631 -0
  187. coco_core/runnables/passthrough.py +849 -0
  188. coco_core/runnables/retry.py +379 -0
  189. coco_core/runnables/router.py +239 -0
  190. coco_core/runnables/schema.py +188 -0
  191. coco_core/runnables/utils.py +798 -0
  192. coco_core/stores.py +291 -0
  193. coco_core/structured_query.py +203 -0
  194. coco_core/sys_info.py +154 -0
  195. coco_core/tools/__init__.py +95 -0
  196. coco_core/tools/base.py +1948 -0
  197. coco_core/tools/convert.py +497 -0
  198. coco_core/tools/render.py +67 -0
  199. coco_core/tools/retriever.py +94 -0
  200. coco_core/tools/simple.py +208 -0
  201. coco_core/tools/structured.py +322 -0
  202. coco_core/tracers/__init__.py +47 -0
  203. coco_core/tracers/_compat.py +95 -0
  204. coco_core/tracers/_context.py +337 -0
  205. coco_core/tracers/_streaming.py +53 -0
  206. coco_core/tracers/base.py +955 -0
  207. coco_core/tracers/coco.py +279 -0
  208. coco_core/tracers/context.py +205 -0
  209. coco_core/tracers/core.py +766 -0
  210. coco_core/tracers/event_stream.py +1101 -0
  211. coco_core/tracers/log_stream.py +765 -0
  212. coco_core/tracers/memory_stream.py +200 -0
  213. coco_core/tracers/root_listeners.py +130 -0
  214. coco_core/tracers/run_collector.py +40 -0
  215. coco_core/tracers/schemas.py +14 -0
  216. coco_core/tracers/stdout.py +205 -0
  217. coco_core/utils/__init__.py +111 -0
  218. coco_core/utils/_gateway.py +274 -0
  219. coco_core/utils/_merge.py +218 -0
  220. coco_core/utils/aiter.py +357 -0
  221. coco_core/utils/env.py +86 -0
  222. coco_core/utils/formatting.py +80 -0
  223. coco_core/utils/function_calling.py +869 -0
  224. coco_core/utils/html.py +132 -0
  225. coco_core/utils/image.py +15 -0
  226. coco_core/utils/input.py +82 -0
  227. coco_core/utils/interactive_env.py +12 -0
  228. coco_core/utils/iter.py +229 -0
  229. coco_core/utils/json.py +230 -0
  230. coco_core/utils/json_schema.py +273 -0
  231. coco_core/utils/mustache.py +706 -0
  232. coco_core/utils/pydantic.py +631 -0
  233. coco_core/utils/strings.py +69 -0
  234. coco_core/utils/usage.py +61 -0
  235. coco_core/utils/utils.py +528 -0
  236. coco_core/utils/uuid.py +57 -0
  237. coco_core/vectorstores/__init__.py +53 -0
  238. coco_core/vectorstores/base.py +1111 -0
  239. coco_core/vectorstores/in_memory.py +546 -0
  240. coco_core/vectorstores/utils.py +163 -0
  241. coco_core/version.py +3 -0
  242. coco_framework-0.1.0.dist-info/METADATA +184 -0
  243. coco_framework-0.1.0.dist-info/RECORD +400 -0
  244. coco_framework-0.1.0.dist-info/WHEEL +4 -0
  245. coco_framework-0.1.0.dist-info/licenses/LICENSE +24 -0
  246. coco_framework-0.1.0.dist-info/licenses/NOTICE +15 -0
  247. coco_graph/_internal/__init__.py +4 -0
  248. coco_graph/_internal/_cache.py +31 -0
  249. coco_graph/_internal/_config.py +474 -0
  250. coco_graph/_internal/_constants.py +140 -0
  251. coco_graph/_internal/_fields.py +213 -0
  252. coco_graph/_internal/_future.py +220 -0
  253. coco_graph/_internal/_pydantic.py +275 -0
  254. coco_graph/_internal/_queue.py +124 -0
  255. coco_graph/_internal/_replay.py +90 -0
  256. coco_graph/_internal/_retry.py +29 -0
  257. coco_graph/_internal/_runnable.py +969 -0
  258. coco_graph/_internal/_scratchpad.py +19 -0
  259. coco_graph/_internal/_serde.py +253 -0
  260. coco_graph/_internal/_timeout.py +25 -0
  261. coco_graph/_internal/_typing.py +54 -0
  262. coco_graph/cache/base/__init__.py +48 -0
  263. coco_graph/cache/base/py.typed +0 -0
  264. coco_graph/cache/memory/__init__.py +73 -0
  265. coco_graph/cache/redis/__init__.py +144 -0
  266. coco_graph/cache/sqlite/__init__.py +120 -0
  267. coco_graph/callbacks.py +394 -0
  268. coco_graph/channels/__init__.py +29 -0
  269. coco_graph/channels/any_value.py +72 -0
  270. coco_graph/channels/base.py +121 -0
  271. coco_graph/channels/binop.py +155 -0
  272. coco_graph/channels/delta.py +202 -0
  273. coco_graph/channels/ephemeral_value.py +79 -0
  274. coco_graph/channels/last_value.py +151 -0
  275. coco_graph/channels/named_barrier_value.py +167 -0
  276. coco_graph/channels/topic.py +94 -0
  277. coco_graph/channels/untracked_value.py +73 -0
  278. coco_graph/checkpoint/base/__init__.py +861 -0
  279. coco_graph/checkpoint/base/id.py +109 -0
  280. coco_graph/checkpoint/base/py.typed +0 -0
  281. coco_graph/checkpoint/memory/__init__.py +698 -0
  282. coco_graph/checkpoint/memory/py.typed +0 -0
  283. coco_graph/checkpoint/postgres/__init__.py +602 -0
  284. coco_graph/checkpoint/postgres/_ainternal.py +23 -0
  285. coco_graph/checkpoint/postgres/_internal.py +21 -0
  286. coco_graph/checkpoint/postgres/aio.py +691 -0
  287. coco_graph/checkpoint/postgres/base.py +666 -0
  288. coco_graph/checkpoint/postgres/py.typed +0 -0
  289. coco_graph/checkpoint/postgres/shallow.py +967 -0
  290. coco_graph/checkpoint/serde/__init__.py +0 -0
  291. coco_graph/checkpoint/serde/_msgpack.py +97 -0
  292. coco_graph/checkpoint/serde/base.py +64 -0
  293. coco_graph/checkpoint/serde/encrypted.py +80 -0
  294. coco_graph/checkpoint/serde/event_hooks.py +52 -0
  295. coco_graph/checkpoint/serde/jsonplus.py +883 -0
  296. coco_graph/checkpoint/serde/py.typed +0 -0
  297. coco_graph/checkpoint/serde/types.py +68 -0
  298. coco_graph/checkpoint/sqlite/__init__.py +645 -0
  299. coco_graph/checkpoint/sqlite/_delta.py +172 -0
  300. coco_graph/checkpoint/sqlite/aio.py +738 -0
  301. coco_graph/checkpoint/sqlite/py.typed +0 -0
  302. coco_graph/checkpoint/sqlite/utils.py +116 -0
  303. coco_graph/config.py +196 -0
  304. coco_graph/constants.py +64 -0
  305. coco_graph/errors.py +241 -0
  306. coco_graph/func/__init__.py +620 -0
  307. coco_graph/graph/__init__.py +12 -0
  308. coco_graph/graph/_branch.py +225 -0
  309. coco_graph/graph/_node.py +103 -0
  310. coco_graph/graph/message.py +437 -0
  311. coco_graph/graph/state.py +1978 -0
  312. coco_graph/graph/ui.py +227 -0
  313. coco_graph/managed/__init__.py +3 -0
  314. coco_graph/managed/base.py +31 -0
  315. coco_graph/managed/is_last_step.py +24 -0
  316. coco_graph/prebuilt/__init__.py +23 -0
  317. coco_graph/prebuilt/_tool_call_stream.py +117 -0
  318. coco_graph/prebuilt/_tool_call_transformer.py +165 -0
  319. coco_graph/prebuilt/chat_agent_executor.py +1015 -0
  320. coco_graph/prebuilt/interrupt.py +105 -0
  321. coco_graph/prebuilt/py.typed +0 -0
  322. coco_graph/prebuilt/tool_node.py +2030 -0
  323. coco_graph/prebuilt/tool_validator.py +221 -0
  324. coco_graph/pregel/__init__.py +3 -0
  325. coco_graph/pregel/_algo.py +1460 -0
  326. coco_graph/pregel/_call.py +298 -0
  327. coco_graph/pregel/_checkpoint.py +331 -0
  328. coco_graph/pregel/_config.py +0 -0
  329. coco_graph/pregel/_draw.py +294 -0
  330. coco_graph/pregel/_executor.py +223 -0
  331. coco_graph/pregel/_io.py +174 -0
  332. coco_graph/pregel/_log.py +3 -0
  333. coco_graph/pregel/_loop.py +1988 -0
  334. coco_graph/pregel/_messages.py +461 -0
  335. coco_graph/pregel/_read.py +312 -0
  336. coco_graph/pregel/_retry.py +854 -0
  337. coco_graph/pregel/_runner.py +941 -0
  338. coco_graph/pregel/_tools.py +268 -0
  339. coco_graph/pregel/_utils.py +221 -0
  340. coco_graph/pregel/_validate.py +120 -0
  341. coco_graph/pregel/_write.py +192 -0
  342. coco_graph/pregel/debug.py +302 -0
  343. coco_graph/pregel/main.py +4364 -0
  344. coco_graph/pregel/protocol.py +288 -0
  345. coco_graph/pregel/types.py +38 -0
  346. coco_graph/py.typed +0 -0
  347. coco_graph/runtime.py +350 -0
  348. coco_graph/store/base/__init__.py +1322 -0
  349. coco_graph/store/base/batch.py +371 -0
  350. coco_graph/store/base/embed.py +436 -0
  351. coco_graph/store/base/py.typed +0 -0
  352. coco_graph/store/memory/__init__.py +592 -0
  353. coco_graph/store/memory/py.typed +0 -0
  354. coco_graph/store/postgres/__init__.py +4 -0
  355. coco_graph/store/postgres/aio.py +592 -0
  356. coco_graph/store/postgres/base.py +1480 -0
  357. coco_graph/store/postgres/py.typed +0 -0
  358. coco_graph/store/sqlite/__init__.py +4 -0
  359. coco_graph/store/sqlite/aio.py +632 -0
  360. coco_graph/store/sqlite/base.py +1514 -0
  361. coco_graph/stream/__init__.py +45 -0
  362. coco_graph/stream/_convert.py +32 -0
  363. coco_graph/stream/_mux.py +523 -0
  364. coco_graph/stream/_types.py +330 -0
  365. coco_graph/stream/run_stream.py +735 -0
  366. coco_graph/stream/stream_channel.py +341 -0
  367. coco_graph/stream/transformers.py +1039 -0
  368. coco_graph/types.py +1079 -0
  369. coco_graph/typing.py +48 -0
  370. coco_graph/utils/__init__.py +1 -0
  371. coco_graph/utils/config.py +4 -0
  372. coco_graph/utils/runnable.py +3 -0
  373. coco_graph/version.py +13 -0
  374. coco_graph/warnings.py +69 -0
  375. coco_openai/__init__.py +20 -0
  376. coco_openai/_compat.py +45 -0
  377. coco_openai/_version.py +3 -0
  378. coco_openai/chat_models/__init__.py +6 -0
  379. coco_openai/chat_models/_client_utils.py +684 -0
  380. coco_openai/chat_models/_compat.py +525 -0
  381. coco_openai/chat_models/azure.py +1227 -0
  382. coco_openai/chat_models/base.py +5654 -0
  383. coco_openai/chat_models/codex.py +557 -0
  384. coco_openai/chatgpt_oauth.py +1063 -0
  385. coco_openai/data/__init__.py +1 -0
  386. coco_openai/data/_profiles.py +1655 -0
  387. coco_openai/data/profile_augmentations.toml +127 -0
  388. coco_openai/embeddings/__init__.py +6 -0
  389. coco_openai/embeddings/azure.py +240 -0
  390. coco_openai/embeddings/base.py +820 -0
  391. coco_openai/llms/__init__.py +6 -0
  392. coco_openai/llms/azure.py +249 -0
  393. coco_openai/llms/base.py +919 -0
  394. coco_openai/middleware/__init__.py +11 -0
  395. coco_openai/middleware/openai_moderation.py +484 -0
  396. coco_openai/output_parsers/__init__.py +9 -0
  397. coco_openai/output_parsers/tools.py +9 -0
  398. coco_openai/py.typed +0 -0
  399. coco_openai/tools/__init__.py +5 -0
  400. coco_openai/tools/custom_tool.py +111 -0
@@ -0,0 +1,661 @@
1
+ """Base classes for indexing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import abc
6
+ import time
7
+ from abc import ABC, abstractmethod
8
+ from typing import TYPE_CHECKING, Any, TypedDict
9
+
10
+ from typing_extensions import override
11
+
12
+ from coco_core._api import beta
13
+ from coco_core.retrievers import BaseRetriever
14
+ from coco_core.runnables import run_in_executor
15
+
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Sequence
18
+
19
+ from coco_core.documents import Document
20
+
21
+
22
+ class RecordManager(ABC):
23
+ """Abstract base class representing the interface for a record manager.
24
+
25
+ The record manager abstraction is used by the coco indexing API.
26
+
27
+ The record manager keeps track of which documents have been
28
+ written into a `VectorStore` and when they were written.
29
+
30
+ The indexing API computes hashes for each document and stores the hash
31
+ together with the write time and the source id in the record manager.
32
+
33
+ On subsequent indexing runs, the indexing API can check the record manager
34
+ to determine which documents have already been indexed and which have not.
35
+
36
+ This allows the indexing API to avoid re-indexing documents that have
37
+ already been indexed, and to only index new documents.
38
+
39
+ The main benefit of this abstraction is that it works across many vectorstores.
40
+ To be supported, a `VectorStore` needs to only support the ability to add and
41
+ delete documents by ID. Using the record manager, the indexing API will
42
+ be able to delete outdated documents and avoid redundant indexing of documents
43
+ that have already been indexed.
44
+
45
+ The main constraints of this abstraction are:
46
+
47
+ 1. It relies on the time-stamps to determine which documents have been
48
+ indexed and which have not. This means that the time-stamps must be
49
+ monotonically increasing. The timestamp should be the timestamp
50
+ as measured by the server to minimize issues.
51
+ 2. The record manager is currently implemented separately from the
52
+ vectorstore, which means that the overall system becomes distributed
53
+ and may create issues with consistency. For example, writing to
54
+ record manager succeeds, but corresponding writing to `VectorStore` fails.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ namespace: str,
60
+ ) -> None:
61
+ """Initialize the record manager.
62
+
63
+ Args:
64
+ namespace: The namespace for the record manager.
65
+ """
66
+ self.namespace = namespace
67
+
68
+ @abstractmethod
69
+ def create_schema(self) -> None:
70
+ """Create the database schema for the record manager."""
71
+
72
+ @abstractmethod
73
+ async def acreate_schema(self) -> None:
74
+ """Asynchronously create the database schema for the record manager."""
75
+
76
+ @abstractmethod
77
+ def get_time(self) -> float:
78
+ """Get the current server time as a high resolution timestamp!
79
+
80
+ It's important to get this from the server to ensure a monotonic clock,
81
+ otherwise there may be data loss when cleaning up old documents!
82
+
83
+ Returns:
84
+ The current server time as a float timestamp.
85
+ """
86
+
87
+ @abstractmethod
88
+ async def aget_time(self) -> float:
89
+ """Asynchronously get the current server time as a high resolution timestamp.
90
+
91
+ It's important to get this from the server to ensure a monotonic clock,
92
+ otherwise there may be data loss when cleaning up old documents!
93
+
94
+ Returns:
95
+ The current server time as a float timestamp.
96
+ """
97
+
98
+ @abstractmethod
99
+ def update(
100
+ self,
101
+ keys: Sequence[str],
102
+ *,
103
+ group_ids: Sequence[str | None] | None = None,
104
+ time_at_least: float | None = None,
105
+ ) -> None:
106
+ """Upsert records into the database.
107
+
108
+ Args:
109
+ keys: A list of record keys to upsert.
110
+ group_ids: A list of group IDs corresponding to the keys.
111
+ time_at_least: Optional timestamp. Implementation can use this
112
+ to optionally verify that the timestamp IS at least this time
113
+ in the system that stores the data.
114
+
115
+ e.g., use to validate that the time in the postgres database
116
+ is equal to or larger than the given timestamp, if not
117
+ raise an error.
118
+
119
+ This is meant to help prevent time-drift issues since
120
+ time may not be monotonically increasing!
121
+
122
+ Raises:
123
+ ValueError: If the length of keys doesn't match the length of group_ids.
124
+ """
125
+
126
+ @abstractmethod
127
+ async def aupdate(
128
+ self,
129
+ keys: Sequence[str],
130
+ *,
131
+ group_ids: Sequence[str | None] | None = None,
132
+ time_at_least: float | None = None,
133
+ ) -> None:
134
+ """Asynchronously upsert records into the database.
135
+
136
+ Args:
137
+ keys: A list of record keys to upsert.
138
+ group_ids: A list of group IDs corresponding to the keys.
139
+ time_at_least: Optional timestamp. Implementation can use this
140
+ to optionally verify that the timestamp IS at least this time
141
+ in the system that stores the data.
142
+
143
+ e.g., use to validate that the time in the postgres database
144
+ is equal to or larger than the given timestamp, if not
145
+ raise an error.
146
+
147
+ This is meant to help prevent time-drift issues since
148
+ time may not be monotonically increasing!
149
+
150
+ Raises:
151
+ ValueError: If the length of keys doesn't match the length of group_ids.
152
+ """
153
+
154
+ @abstractmethod
155
+ def exists(self, keys: Sequence[str]) -> list[bool]:
156
+ """Check if the provided keys exist in the database.
157
+
158
+ Args:
159
+ keys: A list of keys to check.
160
+
161
+ Returns:
162
+ A list of boolean values indicating the existence of each key.
163
+ """
164
+
165
+ @abstractmethod
166
+ async def aexists(self, keys: Sequence[str]) -> list[bool]:
167
+ """Asynchronously check if the provided keys exist in the database.
168
+
169
+ Args:
170
+ keys: A list of keys to check.
171
+
172
+ Returns:
173
+ A list of boolean values indicating the existence of each key.
174
+ """
175
+
176
+ @abstractmethod
177
+ def list_keys(
178
+ self,
179
+ *,
180
+ before: float | None = None,
181
+ after: float | None = None,
182
+ group_ids: Sequence[str] | None = None,
183
+ limit: int | None = None,
184
+ ) -> list[str]:
185
+ """List records in the database based on the provided filters.
186
+
187
+ Args:
188
+ before: Filter to list records updated before this time.
189
+ after: Filter to list records updated after this time.
190
+ group_ids: Filter to list records with specific group IDs.
191
+ limit: optional limit on the number of records to return.
192
+
193
+ Returns:
194
+ A list of keys for the matching records.
195
+ """
196
+
197
+ @abstractmethod
198
+ async def alist_keys(
199
+ self,
200
+ *,
201
+ before: float | None = None,
202
+ after: float | None = None,
203
+ group_ids: Sequence[str] | None = None,
204
+ limit: int | None = None,
205
+ ) -> list[str]:
206
+ """Asynchronously list records in the database based on the provided filters.
207
+
208
+ Args:
209
+ before: Filter to list records updated before this time.
210
+ after: Filter to list records updated after this time.
211
+ group_ids: Filter to list records with specific group IDs.
212
+ limit: optional limit on the number of records to return.
213
+
214
+ Returns:
215
+ A list of keys for the matching records.
216
+ """
217
+
218
+ @abstractmethod
219
+ def delete_keys(self, keys: Sequence[str]) -> None:
220
+ """Delete specified records from the database.
221
+
222
+ Args:
223
+ keys: A list of keys to delete.
224
+ """
225
+
226
+ @abstractmethod
227
+ async def adelete_keys(self, keys: Sequence[str]) -> None:
228
+ """Asynchronously delete specified records from the database.
229
+
230
+ Args:
231
+ keys: A list of keys to delete.
232
+ """
233
+
234
+
235
+ class _Record(TypedDict):
236
+ group_id: str | None
237
+ updated_at: float
238
+
239
+
240
+ class InMemoryRecordManager(RecordManager):
241
+ """An in-memory record manager for testing purposes."""
242
+
243
+ def __init__(self, namespace: str) -> None:
244
+ """Initialize the in-memory record manager.
245
+
246
+ Args:
247
+ namespace: The namespace for the record manager.
248
+ """
249
+ super().__init__(namespace)
250
+ # Each key points to a dictionary
251
+ # of {'group_id': group_id, 'updated_at': timestamp}
252
+ self.records: dict[str, _Record] = {}
253
+ self.namespace = namespace
254
+
255
+ def create_schema(self) -> None:
256
+ """In-memory schema creation is simply ensuring the structure is initialized."""
257
+
258
+ async def acreate_schema(self) -> None:
259
+ """In-memory schema creation is simply ensuring the structure is initialized."""
260
+
261
+ @override
262
+ def get_time(self) -> float:
263
+ return time.time()
264
+
265
+ @override
266
+ async def aget_time(self) -> float:
267
+ return self.get_time()
268
+
269
+ def update(
270
+ self,
271
+ keys: Sequence[str],
272
+ *,
273
+ group_ids: Sequence[str | None] | None = None,
274
+ time_at_least: float | None = None,
275
+ ) -> None:
276
+ """Upsert records into the database.
277
+
278
+ Args:
279
+ keys: A list of record keys to upsert.
280
+ group_ids: A list of group IDs corresponding to the keys.
281
+
282
+ time_at_least: Optional timestamp. Implementation can use this
283
+ to optionally verify that the timestamp IS at least this time
284
+ in the system that stores.
285
+ E.g., use to validate that the time in the postgres database
286
+ is equal to or larger than the given timestamp, if not
287
+ raise an error.
288
+ This is meant to help prevent time-drift issues since
289
+ time may not be monotonically increasing!
290
+
291
+ Raises:
292
+ ValueError: If the length of keys doesn't match the length of group
293
+ ids.
294
+ ValueError: If time_at_least is in the future.
295
+ """
296
+ if group_ids and len(keys) != len(group_ids):
297
+ msg = "Length of keys must match length of group_ids"
298
+ raise ValueError(msg)
299
+ for index, key in enumerate(keys):
300
+ group_id = group_ids[index] if group_ids else None
301
+ if time_at_least and time_at_least > self.get_time():
302
+ msg = "time_at_least must be in the past"
303
+ raise ValueError(msg)
304
+ self.records[key] = {"group_id": group_id, "updated_at": self.get_time()}
305
+
306
+ async def aupdate(
307
+ self,
308
+ keys: Sequence[str],
309
+ *,
310
+ group_ids: Sequence[str | None] | None = None,
311
+ time_at_least: float | None = None,
312
+ ) -> None:
313
+ """Async upsert records into the database.
314
+
315
+ Args:
316
+ keys: A list of record keys to upsert.
317
+ group_ids: A list of group IDs corresponding to the keys.
318
+
319
+ time_at_least: Optional timestamp. Implementation can use this
320
+ to optionally verify that the timestamp IS at least this time
321
+ in the system that stores.
322
+ E.g., use to validate that the time in the postgres database
323
+ is equal to or larger than the given timestamp, if not
324
+ raise an error.
325
+ This is meant to help prevent time-drift issues since
326
+ time may not be monotonically increasing!
327
+ """
328
+ self.update(keys, group_ids=group_ids, time_at_least=time_at_least)
329
+
330
+ def exists(self, keys: Sequence[str]) -> list[bool]:
331
+ """Check if the provided keys exist in the database.
332
+
333
+ Args:
334
+ keys: A list of keys to check.
335
+
336
+ Returns:
337
+ A list of boolean values indicating the existence of each key.
338
+ """
339
+ return [key in self.records for key in keys]
340
+
341
+ async def aexists(self, keys: Sequence[str]) -> list[bool]:
342
+ """Async check if the provided keys exist in the database.
343
+
344
+ Args:
345
+ keys: A list of keys to check.
346
+
347
+ Returns:
348
+ A list of boolean values indicating the existence of each key.
349
+ """
350
+ return self.exists(keys)
351
+
352
+ def list_keys(
353
+ self,
354
+ *,
355
+ before: float | None = None,
356
+ after: float | None = None,
357
+ group_ids: Sequence[str] | None = None,
358
+ limit: int | None = None,
359
+ ) -> list[str]:
360
+ """List records in the database based on the provided filters.
361
+
362
+ Args:
363
+ before: Filter to list records updated before this time.
364
+
365
+ after: Filter to list records updated after this time.
366
+
367
+ group_ids: Filter to list records with specific group IDs.
368
+
369
+ limit: optional limit on the number of records to return.
370
+
371
+
372
+ Returns:
373
+ A list of keys for the matching records.
374
+ """
375
+ result = []
376
+ for key, data in self.records.items():
377
+ if before and data["updated_at"] >= before:
378
+ continue
379
+ if after and data["updated_at"] <= after:
380
+ continue
381
+ if group_ids and data["group_id"] not in group_ids:
382
+ continue
383
+ result.append(key)
384
+ if limit:
385
+ return result[:limit]
386
+ return result
387
+
388
+ async def alist_keys(
389
+ self,
390
+ *,
391
+ before: float | None = None,
392
+ after: float | None = None,
393
+ group_ids: Sequence[str] | None = None,
394
+ limit: int | None = None,
395
+ ) -> list[str]:
396
+ """Async list records in the database based on the provided filters.
397
+
398
+ Args:
399
+ before: Filter to list records updated before this time.
400
+
401
+ after: Filter to list records updated after this time.
402
+
403
+ group_ids: Filter to list records with specific group IDs.
404
+
405
+ limit: optional limit on the number of records to return.
406
+
407
+
408
+ Returns:
409
+ A list of keys for the matching records.
410
+ """
411
+ return self.list_keys(
412
+ before=before, after=after, group_ids=group_ids, limit=limit
413
+ )
414
+
415
+ def delete_keys(self, keys: Sequence[str]) -> None:
416
+ """Delete specified records from the database.
417
+
418
+ Args:
419
+ keys: A list of keys to delete.
420
+ """
421
+ for key in keys:
422
+ if key in self.records:
423
+ del self.records[key]
424
+
425
+ async def adelete_keys(self, keys: Sequence[str]) -> None:
426
+ """Async delete specified records from the database.
427
+
428
+ Args:
429
+ keys: A list of keys to delete.
430
+ """
431
+ self.delete_keys(keys)
432
+
433
+
434
+ class UpsertResponse(TypedDict):
435
+ """A generic response for upsert operations.
436
+
437
+ The upsert response will be used by abstractions that implement an upsert
438
+ operation for content that can be upserted by ID.
439
+
440
+ Upsert APIs that accept inputs with IDs and generate IDs internally
441
+ will return a response that includes the IDs that succeeded and the IDs
442
+ that failed.
443
+
444
+ If there are no failures, the failed list will be empty, and the order
445
+ of the IDs in the succeeded list will match the order of the input documents.
446
+
447
+ If there are failures, the response becomes ill defined, and a user of the API
448
+ cannot determine which generated ID corresponds to which input document.
449
+
450
+ It is recommended for users explicitly attach the IDs to the items being
451
+ indexed to avoid this issue.
452
+ """
453
+
454
+ succeeded: list[str]
455
+ """The IDs that were successfully indexed."""
456
+ failed: list[str]
457
+ """The IDs that failed to index."""
458
+
459
+
460
+ class DeleteResponse(TypedDict, total=False):
461
+ """A generic response for delete operation.
462
+
463
+ The fields in this response are optional and whether the `VectorStore`
464
+ returns them or not is up to the implementation.
465
+ """
466
+
467
+ num_deleted: int
468
+ """The number of items that were successfully deleted.
469
+
470
+ If returned, this should only include *actual* deletions.
471
+
472
+ If the ID did not exist to begin with,
473
+ it should not be included in this count.
474
+ """
475
+
476
+ succeeded: Sequence[str]
477
+ """The IDs that were successfully deleted.
478
+
479
+ If returned, this should only include *actual* deletions.
480
+
481
+ If the ID did not exist to begin with,
482
+ it should not be included in this list.
483
+ """
484
+
485
+ failed: Sequence[str]
486
+ """The IDs that failed to be deleted.
487
+
488
+ !!! warning
489
+ Deleting an ID that does not exist is **NOT** considered a failure.
490
+ """
491
+
492
+ num_failed: int
493
+ """The number of items that failed to be deleted."""
494
+
495
+
496
+ @beta(message="Added in 0.2.29. The abstraction is subject to change.")
497
+ class DocumentIndex(BaseRetriever):
498
+ """A document retriever that supports indexing operations.
499
+
500
+ This indexing interface is designed to be a generic abstraction for storing and
501
+ querying documents that has an ID and metadata associated with it.
502
+
503
+ The interface is designed to be agnostic to the underlying implementation of the
504
+ indexing system.
505
+
506
+ The interface is designed to support the following operations:
507
+
508
+ 1. Storing document in the index.
509
+ 2. Fetching document by ID.
510
+ 3. Searching for document using a query.
511
+ """
512
+
513
+ @abc.abstractmethod
514
+ def upsert(self, items: Sequence[Document], /, **kwargs: Any) -> UpsertResponse:
515
+ """Upsert documents into the index.
516
+
517
+ The upsert functionality should utilize the ID field of the content object
518
+ if it is provided. If the ID is not provided, the upsert method is free
519
+ to generate an ID for the content.
520
+
521
+ When an ID is specified and the content already exists in the `VectorStore`,
522
+ the upsert method should update the content with the new data. If the content
523
+ does not exist, the upsert method should add the item to the `VectorStore`.
524
+
525
+ Args:
526
+ items: Sequence of documents to add to the `VectorStore`.
527
+ **kwargs: Additional keyword arguments.
528
+
529
+ Returns:
530
+ A response object that contains the list of IDs that were
531
+ successfully added or updated in the `VectorStore` and the list of IDs that
532
+ failed to be added or updated.
533
+ """
534
+
535
+ async def aupsert(
536
+ self, items: Sequence[Document], /, **kwargs: Any
537
+ ) -> UpsertResponse:
538
+ """Add or update documents in the `VectorStore`. Async version of `upsert`.
539
+
540
+ The upsert functionality should utilize the ID field of the item
541
+ if it is provided. If the ID is not provided, the upsert method is free
542
+ to generate an ID for the item.
543
+
544
+ When an ID is specified and the item already exists in the `VectorStore`,
545
+ the upsert method should update the item with the new data. If the item
546
+ does not exist, the upsert method should add the item to the `VectorStore`.
547
+
548
+ Args:
549
+ items: Sequence of documents to add to the `VectorStore`.
550
+ **kwargs: Additional keyword arguments.
551
+
552
+ Returns:
553
+ A response object that contains the list of IDs that were
554
+ successfully added or updated in the `VectorStore` and the list of IDs that
555
+ failed to be added or updated.
556
+ """
557
+ return await run_in_executor(
558
+ None,
559
+ self.upsert,
560
+ items,
561
+ **kwargs,
562
+ )
563
+
564
+ @abc.abstractmethod
565
+ def delete(self, ids: list[str] | None = None, **kwargs: Any) -> DeleteResponse:
566
+ """Delete by IDs or other criteria.
567
+
568
+ Calling delete without any input parameters should raise a ValueError!
569
+
570
+ Args:
571
+ ids: List of IDs to delete.
572
+ **kwargs: Additional keyword arguments. This is up to the implementation.
573
+ For example, can include an option to delete the entire index,
574
+ or else issue a non-blocking delete etc.
575
+
576
+ Returns:
577
+ A response object that contains the list of IDs that were
578
+ successfully deleted and the list of IDs that failed to be deleted.
579
+ """
580
+
581
+ async def adelete(
582
+ self, ids: list[str] | None = None, **kwargs: Any
583
+ ) -> DeleteResponse:
584
+ """Delete by IDs or other criteria. Async variant.
585
+
586
+ Calling adelete without any input parameters should raise a ValueError!
587
+
588
+ Args:
589
+ ids: List of IDs to delete.
590
+ **kwargs: Additional keyword arguments. This is up to the implementation.
591
+ For example, can include an option to delete the entire index.
592
+
593
+ Returns:
594
+ A response object that contains the list of IDs that were
595
+ successfully deleted and the list of IDs that failed to be deleted.
596
+ """
597
+ return await run_in_executor(
598
+ None,
599
+ self.delete,
600
+ ids,
601
+ **kwargs,
602
+ )
603
+
604
+ @abc.abstractmethod
605
+ def get(
606
+ self,
607
+ ids: Sequence[str],
608
+ /,
609
+ **kwargs: Any,
610
+ ) -> list[Document]:
611
+ """Get documents by id.
612
+
613
+ Fewer documents may be returned than requested if some IDs are not found or
614
+ if there are duplicated IDs.
615
+
616
+ Users should not assume that the order of the returned documents matches
617
+ the order of the input IDs. Instead, users should rely on the ID field of the
618
+ returned documents.
619
+
620
+ This method should **NOT** raise exceptions if no documents are found for
621
+ some IDs.
622
+
623
+ Args:
624
+ ids: List of IDs to get.
625
+ **kwargs: Additional keyword arguments. These are up to the implementation.
626
+
627
+ Returns:
628
+ List of documents that were found.
629
+ """
630
+
631
+ async def aget(
632
+ self,
633
+ ids: Sequence[str],
634
+ /,
635
+ **kwargs: Any,
636
+ ) -> list[Document]:
637
+ """Get documents by id.
638
+
639
+ Fewer documents may be returned than requested if some IDs are not found or
640
+ if there are duplicated IDs.
641
+
642
+ Users should not assume that the order of the returned documents matches
643
+ the order of the input IDs. Instead, users should rely on the ID field of the
644
+ returned documents.
645
+
646
+ This method should **NOT** raise exceptions if no documents are found for
647
+ some IDs.
648
+
649
+ Args:
650
+ ids: List of IDs to get.
651
+ **kwargs: Additional keyword arguments. These are up to the implementation.
652
+
653
+ Returns:
654
+ List of documents that were found.
655
+ """
656
+ return await run_in_executor(
657
+ None,
658
+ self.get,
659
+ ids,
660
+ **kwargs,
661
+ )