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,546 @@
1
+ """In-memory vector store."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import uuid
7
+ from pathlib import Path
8
+ from typing import (
9
+ TYPE_CHECKING,
10
+ Any,
11
+ )
12
+
13
+ from typing_extensions import override
14
+
15
+ from coco_core.documents import Document
16
+ from coco_core.load import dumpd, load
17
+ from coco_core.vectorstores import VectorStore
18
+ from coco_core.vectorstores.utils import _cosine_similarity as cosine_similarity
19
+ from coco_core.vectorstores.utils import maximal_marginal_relevance
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Callable, Iterator, Sequence
23
+
24
+ from coco_core.embeddings import Embeddings
25
+
26
+ try:
27
+ import numpy as np
28
+
29
+ _HAS_NUMPY = True
30
+ except ImportError:
31
+ _HAS_NUMPY = False
32
+
33
+
34
+ class InMemoryVectorStore(VectorStore):
35
+ """In-memory vector store implementation.
36
+
37
+ Uses a dictionary, and computes cosine similarity for search using numpy.
38
+
39
+ Setup:
40
+ Install `trustcoco-core`.
41
+
42
+ ```bash
43
+ pip install -U trustcoco-core
44
+ ```
45
+
46
+ Key init args — indexing params:
47
+
48
+ * embedding_function: Embeddings
49
+ Embedding function to use.
50
+
51
+ Instantiate:
52
+ ```python
53
+ from coco_core.vectorstores import InMemoryVectorStore
54
+ from coco_openai import OpenAIEmbeddings
55
+
56
+ vector_store = InMemoryVectorStore(OpenAIEmbeddings())
57
+ ```
58
+
59
+ Add Documents:
60
+ ```python
61
+ from coco_core.documents import Document
62
+
63
+ document_1 = Document(id="1", page_content="foo", metadata={"baz": "bar"})
64
+ document_2 = Document(id="2", page_content="thud", metadata={"bar": "baz"})
65
+ document_3 = Document(id="3", page_content="i will be deleted :(")
66
+
67
+ documents = [document_1, document_2, document_3]
68
+ vector_store.add_documents(documents=documents)
69
+ ```
70
+
71
+ Inspect documents:
72
+ ```python
73
+ top_n = 10
74
+ for index, (id, doc) in enumerate(vector_store.store.items()):
75
+ if index < top_n:
76
+ # docs have keys 'id', 'vector', 'text', 'metadata'
77
+ print(f"{id}: {doc['text']}")
78
+ else:
79
+ break
80
+ ```
81
+
82
+ Delete Documents:
83
+ ```python
84
+ vector_store.delete(ids=["3"])
85
+ ```
86
+
87
+ Search:
88
+ ```python
89
+ results = vector_store.similarity_search(query="thud", k=1)
90
+ for doc in results:
91
+ print(f"* {doc.page_content} [{doc.metadata}]")
92
+ ```
93
+
94
+ ```txt
95
+ * thud [{'bar': 'baz'}]
96
+ ```
97
+
98
+ Search with filter:
99
+ ```python
100
+ def _filter_function(doc: Document) -> bool:
101
+ return doc.metadata.get("bar") == "baz"
102
+
103
+
104
+ results = vector_store.similarity_search(
105
+ query="thud", k=1, filter=_filter_function
106
+ )
107
+ for doc in results:
108
+ print(f"* {doc.page_content} [{doc.metadata}]")
109
+ ```
110
+
111
+ ```txt
112
+ * thud [{'bar': 'baz'}]
113
+ ```
114
+
115
+ Search with score:
116
+ ```python
117
+ results = vector_store.similarity_search_with_score(query="qux", k=1)
118
+ for doc, score in results:
119
+ print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]")
120
+ ```
121
+
122
+ ```txt
123
+ * [SIM=0.832268] foo [{'baz': 'bar'}]
124
+ ```
125
+
126
+ Async:
127
+ ```python
128
+ # add documents
129
+ # await vector_store.aadd_documents(documents=documents)
130
+
131
+ # delete documents
132
+ # await vector_store.adelete(ids=["3"])
133
+
134
+ # search
135
+ # results = vector_store.asimilarity_search(query="thud", k=1)
136
+
137
+ # search with score
138
+ results = await vector_store.asimilarity_search_with_score(query="qux", k=1)
139
+ for doc, score in results:
140
+ print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]")
141
+ ```
142
+
143
+ ```txt
144
+ * [SIM=0.832268] foo [{'baz': 'bar'}]
145
+ ```
146
+
147
+ Use as Retriever:
148
+ ```python
149
+ retriever = vector_store.as_retriever(
150
+ search_type="mmr",
151
+ search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5},
152
+ )
153
+ retriever.invoke("thud")
154
+ ```
155
+
156
+ ```txt
157
+ [Document(id='2', metadata={'bar': 'baz'}, page_content='thud')]
158
+ ```
159
+ """
160
+
161
+ def __init__(self, embedding: Embeddings) -> None:
162
+ """Initialize with the given embedding function.
163
+
164
+ Args:
165
+ embedding: embedding function to use.
166
+ """
167
+ # TODO: would be nice to change to
168
+ # dict[str, Document] at some point (will be a breaking change)
169
+ self.store: dict[str, dict[str, Any]] = {}
170
+ self.embedding = embedding
171
+
172
+ @property
173
+ @override
174
+ def embeddings(self) -> Embeddings:
175
+ return self.embedding
176
+
177
+ @override
178
+ def delete(self, ids: Sequence[str] | None = None, **kwargs: Any) -> None:
179
+ if ids:
180
+ for id_ in ids:
181
+ self.store.pop(id_, None)
182
+
183
+ @override
184
+ async def adelete(self, ids: Sequence[str] | None = None, **kwargs: Any) -> None:
185
+ self.delete(ids)
186
+
187
+ @override
188
+ def add_documents(
189
+ self,
190
+ documents: list[Document],
191
+ ids: list[str] | None = None,
192
+ **kwargs: Any,
193
+ ) -> list[str]:
194
+ texts = [doc.page_content for doc in documents]
195
+ vectors = self.embedding.embed_documents(texts)
196
+
197
+ if ids and len(ids) != len(texts):
198
+ msg = (
199
+ f"ids must be the same length as texts. "
200
+ f"Got {len(ids)} ids and {len(texts)} texts."
201
+ )
202
+ raise ValueError(msg)
203
+
204
+ id_iterator: Iterator[str | None] = (
205
+ iter(ids) if ids else iter(doc.id for doc in documents)
206
+ )
207
+
208
+ ids_ = []
209
+
210
+ for doc, vector in zip(documents, vectors, strict=False):
211
+ doc_id = next(id_iterator)
212
+ doc_id_ = doc_id or str(uuid.uuid4())
213
+ ids_.append(doc_id_)
214
+ self.store[doc_id_] = {
215
+ "id": doc_id_,
216
+ "vector": vector,
217
+ "text": doc.page_content,
218
+ "metadata": doc.metadata,
219
+ }
220
+
221
+ return ids_
222
+
223
+ @override
224
+ async def aadd_documents(
225
+ self, documents: list[Document], ids: list[str] | None = None, **kwargs: Any
226
+ ) -> list[str]:
227
+ texts = [doc.page_content for doc in documents]
228
+ vectors = await self.embedding.aembed_documents(texts)
229
+
230
+ if ids and len(ids) != len(texts):
231
+ msg = (
232
+ f"ids must be the same length as texts. "
233
+ f"Got {len(ids)} ids and {len(texts)} texts."
234
+ )
235
+ raise ValueError(msg)
236
+
237
+ id_iterator: Iterator[str | None] = (
238
+ iter(ids) if ids else iter(doc.id for doc in documents)
239
+ )
240
+ ids_: list[str] = []
241
+
242
+ for doc, vector in zip(documents, vectors, strict=False):
243
+ doc_id = next(id_iterator)
244
+ doc_id_ = doc_id or str(uuid.uuid4())
245
+ ids_.append(doc_id_)
246
+ self.store[doc_id_] = {
247
+ "id": doc_id_,
248
+ "vector": vector,
249
+ "text": doc.page_content,
250
+ "metadata": doc.metadata,
251
+ }
252
+
253
+ return ids_
254
+
255
+ @override
256
+ def get_by_ids(self, ids: Sequence[str], /) -> list[Document]:
257
+ """Get documents by their ids.
258
+
259
+ Args:
260
+ ids: The IDs of the documents to get.
261
+
262
+ Returns:
263
+ A list of `Document` objects.
264
+ """
265
+ documents = []
266
+
267
+ for doc_id in ids:
268
+ doc = self.store.get(doc_id)
269
+ if doc:
270
+ documents.append(
271
+ Document(
272
+ id=doc["id"],
273
+ page_content=doc["text"],
274
+ metadata=doc["metadata"],
275
+ )
276
+ )
277
+ return documents
278
+
279
+ @override
280
+ async def aget_by_ids(self, ids: Sequence[str], /) -> list[Document]:
281
+ """Async get documents by their ids.
282
+
283
+ Args:
284
+ ids: The IDs of the documents to get.
285
+
286
+ Returns:
287
+ A list of `Document` objects.
288
+ """
289
+ return self.get_by_ids(ids)
290
+
291
+ def _similarity_search_with_score_by_vector(
292
+ self,
293
+ embedding: list[float],
294
+ k: int = 4,
295
+ filter: Callable[[Document], bool] | None = None, # noqa: A002
296
+ ) -> list[tuple[Document, float, list[float]]]:
297
+ # Get all docs with fixed order in list
298
+ docs = list(self.store.values())
299
+
300
+ if filter is not None:
301
+ docs = [
302
+ doc
303
+ for doc in docs
304
+ if filter(
305
+ Document(
306
+ id=doc["id"], page_content=doc["text"], metadata=doc["metadata"]
307
+ )
308
+ )
309
+ ]
310
+
311
+ if not docs:
312
+ return []
313
+
314
+ similarity = cosine_similarity([embedding], [doc["vector"] for doc in docs])[0]
315
+
316
+ # Get the indices ordered by similarity score
317
+ top_k_idx = similarity.argsort()[::-1][:k]
318
+
319
+ return [
320
+ (
321
+ Document(
322
+ id=doc_dict["id"],
323
+ page_content=doc_dict["text"],
324
+ metadata=doc_dict["metadata"],
325
+ ),
326
+ float(similarity[idx].item()),
327
+ doc_dict["vector"],
328
+ )
329
+ for idx in top_k_idx
330
+ # Assign using walrus operator to avoid multiple lookups
331
+ if (doc_dict := docs[idx])
332
+ ]
333
+
334
+ def similarity_search_with_score_by_vector(
335
+ self,
336
+ embedding: list[float],
337
+ k: int = 4,
338
+ filter: Callable[[Document], bool] | None = None, # noqa: A002
339
+ **_kwargs: Any,
340
+ ) -> list[tuple[Document, float]]:
341
+ """Search for the most similar documents to the given embedding.
342
+
343
+ Args:
344
+ embedding: The embedding to search for.
345
+ k: The number of documents to return.
346
+ filter: A function to filter the documents.
347
+
348
+ Returns:
349
+ A list of tuples of `Document` objects and their similarity scores.
350
+ """
351
+ return [
352
+ (doc, similarity)
353
+ for doc, similarity, _ in self._similarity_search_with_score_by_vector(
354
+ embedding=embedding, k=k, filter=filter
355
+ )
356
+ ]
357
+
358
+ @override
359
+ def similarity_search_with_score(
360
+ self,
361
+ query: str,
362
+ k: int = 4,
363
+ **kwargs: Any,
364
+ ) -> list[tuple[Document, float]]:
365
+ embedding = self.embedding.embed_query(query)
366
+ return self.similarity_search_with_score_by_vector(
367
+ embedding,
368
+ k,
369
+ **kwargs,
370
+ )
371
+
372
+ @override
373
+ async def asimilarity_search_with_score(
374
+ self, query: str, k: int = 4, **kwargs: Any
375
+ ) -> list[tuple[Document, float]]:
376
+ embedding = await self.embedding.aembed_query(query)
377
+ return self.similarity_search_with_score_by_vector(
378
+ embedding,
379
+ k,
380
+ **kwargs,
381
+ )
382
+
383
+ @override
384
+ def similarity_search_by_vector(
385
+ self,
386
+ embedding: list[float],
387
+ k: int = 4,
388
+ **kwargs: Any,
389
+ ) -> list[Document]:
390
+ docs_and_scores = self.similarity_search_with_score_by_vector(
391
+ embedding,
392
+ k,
393
+ **kwargs,
394
+ )
395
+ return [doc for doc, _ in docs_and_scores]
396
+
397
+ @override
398
+ async def asimilarity_search_by_vector(
399
+ self, embedding: list[float], k: int = 4, **kwargs: Any
400
+ ) -> list[Document]:
401
+ return self.similarity_search_by_vector(embedding, k, **kwargs)
402
+
403
+ @override
404
+ def similarity_search(
405
+ self, query: str, k: int = 4, **kwargs: Any
406
+ ) -> list[Document]:
407
+ return [doc for doc, _ in self.similarity_search_with_score(query, k, **kwargs)]
408
+
409
+ @override
410
+ async def asimilarity_search(
411
+ self, query: str, k: int = 4, **kwargs: Any
412
+ ) -> list[Document]:
413
+ return [
414
+ doc
415
+ for doc, _ in await self.asimilarity_search_with_score(query, k, **kwargs)
416
+ ]
417
+
418
+ @override
419
+ def max_marginal_relevance_search_by_vector(
420
+ self,
421
+ embedding: list[float],
422
+ k: int = 4,
423
+ fetch_k: int = 20,
424
+ lambda_mult: float = 0.5,
425
+ *,
426
+ filter: Callable[[Document], bool] | None = None,
427
+ **kwargs: Any,
428
+ ) -> list[Document]:
429
+ prefetch_hits = self._similarity_search_with_score_by_vector(
430
+ embedding=embedding,
431
+ k=fetch_k,
432
+ filter=filter,
433
+ )
434
+
435
+ if not _HAS_NUMPY:
436
+ msg = (
437
+ "numpy must be installed to use max_marginal_relevance_search "
438
+ "pip install numpy"
439
+ )
440
+ raise ImportError(msg)
441
+
442
+ mmr_chosen_indices = maximal_marginal_relevance(
443
+ np.array(embedding, dtype=np.float32),
444
+ [vector for _, _, vector in prefetch_hits],
445
+ k=k,
446
+ lambda_mult=lambda_mult,
447
+ )
448
+ return [prefetch_hits[idx][0] for idx in mmr_chosen_indices]
449
+
450
+ @override
451
+ def max_marginal_relevance_search(
452
+ self,
453
+ query: str,
454
+ k: int = 4,
455
+ fetch_k: int = 20,
456
+ lambda_mult: float = 0.5,
457
+ **kwargs: Any,
458
+ ) -> list[Document]:
459
+ embedding_vector = self.embedding.embed_query(query)
460
+ return self.max_marginal_relevance_search_by_vector(
461
+ embedding_vector,
462
+ k,
463
+ fetch_k,
464
+ lambda_mult=lambda_mult,
465
+ **kwargs,
466
+ )
467
+
468
+ @override
469
+ async def amax_marginal_relevance_search(
470
+ self,
471
+ query: str,
472
+ k: int = 4,
473
+ fetch_k: int = 20,
474
+ lambda_mult: float = 0.5,
475
+ **kwargs: Any,
476
+ ) -> list[Document]:
477
+ embedding_vector = await self.embedding.aembed_query(query)
478
+ return self.max_marginal_relevance_search_by_vector(
479
+ embedding_vector,
480
+ k,
481
+ fetch_k,
482
+ lambda_mult=lambda_mult,
483
+ **kwargs,
484
+ )
485
+
486
+ @classmethod
487
+ @override
488
+ def from_texts(
489
+ cls,
490
+ texts: list[str],
491
+ embedding: Embeddings,
492
+ metadatas: list[dict[str, Any]] | None = None,
493
+ **kwargs: Any,
494
+ ) -> InMemoryVectorStore:
495
+ store = cls(
496
+ embedding=embedding,
497
+ )
498
+ store.add_texts(texts=texts, metadatas=metadatas, **kwargs)
499
+ return store
500
+
501
+ @classmethod
502
+ @override
503
+ async def afrom_texts(
504
+ cls,
505
+ texts: list[str],
506
+ embedding: Embeddings,
507
+ metadatas: list[dict[str, Any]] | None = None,
508
+ **kwargs: Any,
509
+ ) -> InMemoryVectorStore:
510
+ store = cls(
511
+ embedding=embedding,
512
+ )
513
+ await store.aadd_texts(texts=texts, metadatas=metadatas, **kwargs)
514
+ return store
515
+
516
+ @classmethod
517
+ def load(
518
+ cls, path: str, embedding: Embeddings, **kwargs: Any
519
+ ) -> InMemoryVectorStore:
520
+ """Load a vector store from a file.
521
+
522
+ Args:
523
+ path: The path to load the vector store from.
524
+ embedding: The embedding to use.
525
+ **kwargs: Additional arguments to pass to the constructor.
526
+
527
+ Returns:
528
+ A `VectorStore` object.
529
+ """
530
+ path_: Path = Path(path)
531
+ with path_.open("r", encoding="utf-8") as f:
532
+ store = load(json.load(f), allowed_objects=[Document])
533
+ vectorstore = cls(embedding=embedding, **kwargs)
534
+ vectorstore.store = store
535
+ return vectorstore
536
+
537
+ def dump(self, path: str) -> None:
538
+ """Dump the vector store to a file.
539
+
540
+ Args:
541
+ path: The path to dump the vector store to.
542
+ """
543
+ path_: Path = Path(path)
544
+ path_.parent.mkdir(exist_ok=True, parents=True)
545
+ with path_.open("w", encoding="utf-8") as f:
546
+ json.dump(dumpd(self.store), f, indent=2)
@@ -0,0 +1,163 @@
1
+ """Internal utilities for the in memory implementation of `VectorStore`.
2
+
3
+ !!! warning
4
+
5
+ These are part of a private API, and users should not use them directly as they can
6
+ change without notice.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ import warnings
13
+ from typing import TYPE_CHECKING
14
+
15
+ try:
16
+ import numpy as np
17
+
18
+ _HAS_NUMPY = True
19
+ except ImportError:
20
+ _HAS_NUMPY = False
21
+
22
+ try:
23
+ import simsimd as simd # type: ignore[import-not-found]
24
+
25
+ _HAS_SIMSIMD = True
26
+ except ImportError:
27
+ _HAS_SIMSIMD = False
28
+
29
+ if TYPE_CHECKING:
30
+ import numpy.typing as npt
31
+
32
+ Matrix = (
33
+ list[list[float]] | list[npt.NDArray[np.floating]] | npt.NDArray[np.floating]
34
+ )
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ def _cosine_similarity(x: Matrix, y: Matrix) -> npt.NDArray[np.floating]:
40
+ """Row-wise cosine similarity between two equal-width matrices.
41
+
42
+ Args:
43
+ x: A matrix of shape `(n, m)`.
44
+ y: A matrix of shape `(k, m)`.
45
+
46
+ Returns:
47
+ A matrix of shape `(n, k)` where each element `(i, j)` is the cosine similarity
48
+ between the `i`th row of `x` and the `j`th row of `y`.
49
+
50
+ Raises:
51
+ ValueError: If the number of columns in `x` and `y` are not the same.
52
+ ImportError: If numpy is not installed.
53
+ """
54
+ if not _HAS_NUMPY:
55
+ msg = (
56
+ "cosine_similarity requires numpy to be installed. "
57
+ "Please install numpy with `pip install numpy`."
58
+ )
59
+ raise ImportError(msg)
60
+
61
+ if len(x) == 0 or len(y) == 0:
62
+ return np.array([[]])
63
+
64
+ x = np.array(x)
65
+ y = np.array(y)
66
+
67
+ # Check for NaN
68
+ if np.any(np.isnan(x)) or np.any(np.isnan(y)):
69
+ warnings.warn(
70
+ "NaN found in input arrays, unexpected return might follow",
71
+ category=RuntimeWarning,
72
+ stacklevel=2,
73
+ )
74
+
75
+ # Check for Inf
76
+ if np.any(np.isinf(x)) or np.any(np.isinf(y)):
77
+ warnings.warn(
78
+ "Inf found in input arrays, unexpected return might follow",
79
+ category=RuntimeWarning,
80
+ stacklevel=2,
81
+ )
82
+
83
+ if x.shape[1] != y.shape[1]:
84
+ msg = (
85
+ f"Number of columns in X and Y must be the same. X has shape {x.shape} "
86
+ f"and Y has shape {y.shape}."
87
+ )
88
+ raise ValueError(msg)
89
+ if not _HAS_SIMSIMD:
90
+ logger.debug(
91
+ "Unable to import simsimd, defaulting to NumPy implementation. If you want "
92
+ "to use simsimd please install with `pip install simsimd`."
93
+ )
94
+ x_norm = np.linalg.norm(x, axis=1)
95
+ y_norm = np.linalg.norm(y, axis=1)
96
+ # Ignore divide by zero errors run time warnings as those are handled below.
97
+ with np.errstate(divide="ignore", invalid="ignore"):
98
+ similarity: npt.NDArray[np.floating] = np.dot(x, y.T) / np.outer(
99
+ x_norm, y_norm
100
+ )
101
+ if np.isnan(similarity).all():
102
+ msg = "NaN values found, please remove the NaN values and try again"
103
+ raise ValueError(msg) from None
104
+ similarity[np.isnan(similarity) | np.isinf(similarity)] = 0.0
105
+ return similarity
106
+
107
+ x = np.array(x, dtype=np.float32)
108
+ y = np.array(y, dtype=np.float32)
109
+ return 1 - np.array(simd.cdist(x, y, metric="cosine"))
110
+
111
+
112
+ def maximal_marginal_relevance(
113
+ query_embedding: npt.NDArray[np.floating],
114
+ embedding_list: list[list[float]],
115
+ lambda_mult: float = 0.5,
116
+ k: int = 4,
117
+ ) -> list[int]:
118
+ """Calculate maximal marginal relevance.
119
+
120
+ Args:
121
+ query_embedding: The query embedding.
122
+ embedding_list: A list of embeddings.
123
+ lambda_mult: The lambda parameter for MMR.
124
+ k: The number of embeddings to return.
125
+
126
+ Returns:
127
+ A list of indices of the embeddings to return.
128
+
129
+ Raises:
130
+ ImportError: If numpy is not installed.
131
+ """
132
+ if not _HAS_NUMPY:
133
+ msg = (
134
+ "maximal_marginal_relevance requires numpy to be installed. "
135
+ "Please install numpy with `pip install numpy`."
136
+ )
137
+ raise ImportError(msg)
138
+
139
+ if min(k, len(embedding_list)) <= 0:
140
+ return []
141
+ if query_embedding.ndim == 1:
142
+ query_embedding = np.expand_dims(query_embedding, axis=0)
143
+ similarity_to_query = _cosine_similarity(query_embedding, embedding_list)[0]
144
+ most_similar = int(np.argmax(similarity_to_query))
145
+ idxs = [most_similar]
146
+ selected = np.array([embedding_list[most_similar]])
147
+ while len(idxs) < min(k, len(embedding_list)):
148
+ best_score = -np.inf
149
+ idx_to_add = -1
150
+ similarity_to_selected = _cosine_similarity(embedding_list, selected)
151
+ for i, query_score in enumerate(similarity_to_query):
152
+ if i in idxs:
153
+ continue
154
+ redundant_score = max(similarity_to_selected[i])
155
+ equation_score = (
156
+ lambda_mult * query_score - (1 - lambda_mult) * redundant_score
157
+ )
158
+ if equation_score > best_score:
159
+ best_score = equation_score
160
+ idx_to_add = i
161
+ idxs.append(idx_to_add)
162
+ selected = np.append(selected, [embedding_list[idx_to_add]], axis=0)
163
+ return idxs
coco_core/version.py ADDED
@@ -0,0 +1,3 @@
1
+ """Version information for `trustcoco-core`."""
2
+
3
+ VERSION = "1.6.5"