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
coco_core/stores.py ADDED
@@ -0,0 +1,291 @@
1
+ """**Store** implements the key-value stores and storage helpers.
2
+
3
+ Module provides implementations of various key-value stores that conform
4
+ to a simple key-value interface.
5
+
6
+ The primary goal of these storages is to support implementation of caching.
7
+ """
8
+
9
+ from abc import ABC, abstractmethod
10
+ from collections.abc import AsyncIterator, Iterator, Sequence
11
+ from typing import (
12
+ Any,
13
+ Generic,
14
+ TypeVar,
15
+ )
16
+
17
+ from typing_extensions import override
18
+
19
+ from coco_core.exceptions import CocoException
20
+ from coco_core.runnables import run_in_executor
21
+
22
+ K = TypeVar("K")
23
+ V = TypeVar("V")
24
+
25
+
26
+ class BaseStore(ABC, Generic[K, V]):
27
+ """Abstract interface for a key-value store.
28
+
29
+ This is an interface that's meant to abstract away the details of different
30
+ key-value stores. It provides a simple interface for getting, setting, and deleting
31
+ key-value pairs.
32
+
33
+ The basic methods are `mget`, `mset`, and `mdelete` for getting, setting, and
34
+ deleting multiple key-value pairs at once. The `yield_keys` method is used to
35
+ iterate over keys that match a given prefix.
36
+
37
+ The async versions of these methods are also provided, which are meant to be used in
38
+ async contexts. The async methods are named with an `a` prefix, e.g., `amget`,
39
+ `amset`, `amdelete`, and `ayield_keys`.
40
+
41
+ By default, the `amget`, `amset`, `amdelete`, and `ayield_keys` methods are
42
+ implemented using the synchronous methods. If the store can natively support async
43
+ operations, it should override these methods.
44
+
45
+ By design the methods only accept batches of keys and values, and not single keys or
46
+ values. This is done to force user code to work with batches which will usually be
47
+ more efficient by saving on round trips to the store.
48
+
49
+ Examples:
50
+ ```python
51
+ from coco.storage import BaseStore
52
+
53
+
54
+ class MyInMemoryStore(BaseStore[str, int]):
55
+ def __init__(self) -> None:
56
+ self.store: dict[str, int] = {}
57
+
58
+ def mget(self, keys: Sequence[str]) -> list[int | None]:
59
+ return [self.store.get(key) for key in keys]
60
+
61
+ def mset(self, key_value_pairs: Sequence[tuple[str, int]]) -> None:
62
+ for key, value in key_value_pairs:
63
+ self.store[key] = value
64
+
65
+ def mdelete(self, keys: Sequence[str]) -> None:
66
+ for key in keys:
67
+ if key in self.store:
68
+ del self.store[key]
69
+
70
+ def yield_keys(self, prefix: str | None = None) -> Iterator[str]:
71
+ if prefix is None:
72
+ yield from self.store.keys()
73
+ else:
74
+ for key in self.store.keys():
75
+ if key.startswith(prefix):
76
+ yield key
77
+ ```
78
+ """
79
+
80
+ @abstractmethod
81
+ def mget(self, keys: Sequence[K]) -> list[V | None]:
82
+ """Get the values associated with the given keys.
83
+
84
+ Args:
85
+ keys: A sequence of keys.
86
+
87
+ Returns:
88
+ A sequence of optional values associated with the keys.
89
+ If a key is not found, the corresponding value will be `None`.
90
+ """
91
+
92
+ async def amget(self, keys: Sequence[K]) -> list[V | None]:
93
+ """Async get the values associated with the given keys.
94
+
95
+ Args:
96
+ keys: A sequence of keys.
97
+
98
+ Returns:
99
+ A sequence of optional values associated with the keys.
100
+ If a key is not found, the corresponding value will be `None`.
101
+ """
102
+ return await run_in_executor(None, self.mget, keys)
103
+
104
+ @abstractmethod
105
+ def mset(self, key_value_pairs: Sequence[tuple[K, V]]) -> None:
106
+ """Set the values for the given keys.
107
+
108
+ Args:
109
+ key_value_pairs: A sequence of key-value pairs.
110
+ """
111
+
112
+ async def amset(self, key_value_pairs: Sequence[tuple[K, V]]) -> None:
113
+ """Async set the values for the given keys.
114
+
115
+ Args:
116
+ key_value_pairs: A sequence of key-value pairs.
117
+ """
118
+ return await run_in_executor(None, self.mset, key_value_pairs)
119
+
120
+ @abstractmethod
121
+ def mdelete(self, keys: Sequence[K]) -> None:
122
+ """Delete the given keys and their associated values.
123
+
124
+ Args:
125
+ keys: A sequence of keys to delete.
126
+ """
127
+
128
+ async def amdelete(self, keys: Sequence[K]) -> None:
129
+ """Async delete the given keys and their associated values.
130
+
131
+ Args:
132
+ keys: A sequence of keys to delete.
133
+ """
134
+ return await run_in_executor(None, self.mdelete, keys)
135
+
136
+ @abstractmethod
137
+ def yield_keys(self, *, prefix: str | None = None) -> Iterator[K] | Iterator[str]:
138
+ """Get an iterator over keys that match the given prefix.
139
+
140
+ Args:
141
+ prefix: The prefix to match.
142
+
143
+ Yields:
144
+ An iterator over keys that match the given prefix.
145
+
146
+ This method is allowed to return an iterator over either K or str
147
+ depending on what makes more sense for the given store.
148
+ """
149
+
150
+ async def ayield_keys(
151
+ self, *, prefix: str | None = None
152
+ ) -> AsyncIterator[K] | AsyncIterator[str]:
153
+ """Async get an iterator over keys that match the given prefix.
154
+
155
+ Args:
156
+ prefix: The prefix to match.
157
+
158
+ Yields:
159
+ The keys that match the given prefix.
160
+
161
+ This method is allowed to return an iterator over either K or str
162
+ depending on what makes more sense for the given store.
163
+ """
164
+ iterator = await run_in_executor(None, self.yield_keys, prefix=prefix)
165
+ done = object()
166
+ while True:
167
+ item = await run_in_executor(None, lambda it: next(it, done), iterator)
168
+ if item is done:
169
+ break
170
+ yield item # type: ignore[misc]
171
+
172
+
173
+ ByteStore = BaseStore[str, bytes]
174
+
175
+
176
+ class InMemoryBaseStore(BaseStore[str, V], Generic[V]):
177
+ """In-memory implementation of the `BaseStore` using a dictionary."""
178
+
179
+ def __init__(self) -> None:
180
+ """Initialize an empty store."""
181
+ self.store: dict[str, V] = {}
182
+
183
+ @override
184
+ def mget(self, keys: Sequence[str]) -> list[V | None]:
185
+ return [self.store.get(key) for key in keys]
186
+
187
+ @override
188
+ async def amget(self, keys: Sequence[str]) -> list[V | None]:
189
+ return self.mget(keys)
190
+
191
+ @override
192
+ def mset(self, key_value_pairs: Sequence[tuple[str, V]]) -> None:
193
+ for key, value in key_value_pairs:
194
+ self.store[key] = value
195
+
196
+ @override
197
+ async def amset(self, key_value_pairs: Sequence[tuple[str, V]]) -> None:
198
+ return self.mset(key_value_pairs)
199
+
200
+ @override
201
+ def mdelete(self, keys: Sequence[str]) -> None:
202
+ for key in keys:
203
+ if key in self.store:
204
+ del self.store[key]
205
+
206
+ @override
207
+ async def amdelete(self, keys: Sequence[str]) -> None:
208
+ self.mdelete(keys)
209
+
210
+ def yield_keys(self, *, prefix: str | None = None) -> Iterator[str]:
211
+ """Get an iterator over keys that match the given prefix.
212
+
213
+ Args:
214
+ prefix: The prefix to match.
215
+
216
+ Yields:
217
+ The keys that match the given prefix.
218
+ """
219
+ if prefix is None:
220
+ yield from self.store.keys()
221
+ else:
222
+ for key in self.store:
223
+ if key.startswith(prefix):
224
+ yield key
225
+
226
+ async def ayield_keys(self, *, prefix: str | None = None) -> AsyncIterator[str]:
227
+ """Async get an async iterator over keys that match the given prefix.
228
+
229
+ Args:
230
+ prefix: The prefix to match.
231
+
232
+ Yields:
233
+ The keys that match the given prefix.
234
+ """
235
+ if prefix is None:
236
+ for key in self.store:
237
+ yield key
238
+ else:
239
+ for key in self.store:
240
+ if key.startswith(prefix):
241
+ yield key
242
+
243
+
244
+ class InMemoryStore(InMemoryBaseStore[Any]):
245
+ """In-memory store for any type of data.
246
+
247
+ Attributes:
248
+ store: The underlying dictionary that stores the key-value pairs.
249
+
250
+ Examples:
251
+ ```python
252
+ from coco.storage import InMemoryStore
253
+
254
+ store = InMemoryStore()
255
+ store.mset([("key1", "value1"), ("key2", "value2")])
256
+ store.mget(["key1", "key2"])
257
+ # ['value1', 'value2']
258
+ store.mdelete(["key1"])
259
+ list(store.yield_keys())
260
+ # ['key2']
261
+ list(store.yield_keys(prefix="k"))
262
+ # ['key2']
263
+ ```
264
+ """
265
+
266
+
267
+ class InMemoryByteStore(InMemoryBaseStore[bytes]):
268
+ """In-memory store for bytes.
269
+
270
+ Attributes:
271
+ store: The underlying dictionary that stores the key-value pairs.
272
+
273
+ Examples:
274
+ ```python
275
+ from coco.storage import InMemoryByteStore
276
+
277
+ store = InMemoryByteStore()
278
+ store.mset([("key1", b"value1"), ("key2", b"value2")])
279
+ store.mget(["key1", "key2"])
280
+ # [b'value1', b'value2']
281
+ store.mdelete(["key1"])
282
+ list(store.yield_keys())
283
+ # ['key2']
284
+ list(store.yield_keys(prefix="k"))
285
+ # ['key2']
286
+ ```
287
+ """
288
+
289
+
290
+ class InvalidKeyException(CocoException):
291
+ """Raised when a key is invalid; e.g., uses incorrect characters."""
@@ -0,0 +1,203 @@
1
+ """Internal representation of a structured query language."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from enum import Enum
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from pydantic import BaseModel
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Sequence
13
+
14
+
15
+ class Visitor(ABC):
16
+ """Defines interface for IR translation using a visitor pattern."""
17
+
18
+ allowed_comparators: Sequence[Comparator] | None = None
19
+ """Allowed comparators for the visitor."""
20
+
21
+ allowed_operators: Sequence[Operator] | None = None
22
+ """Allowed operators for the visitor."""
23
+
24
+ def _validate_func(self, func: Operator | Comparator) -> None:
25
+ if (
26
+ isinstance(func, Operator)
27
+ and self.allowed_operators is not None
28
+ and func not in self.allowed_operators
29
+ ):
30
+ msg = (
31
+ f"Received disallowed operator {func}. Allowed "
32
+ f"comparators are {self.allowed_operators}"
33
+ )
34
+ raise ValueError(msg)
35
+ if (
36
+ isinstance(func, Comparator)
37
+ and self.allowed_comparators is not None
38
+ and func not in self.allowed_comparators
39
+ ):
40
+ msg = (
41
+ f"Received disallowed comparator {func}. Allowed "
42
+ f"comparators are {self.allowed_comparators}"
43
+ )
44
+ raise ValueError(msg)
45
+
46
+ @abstractmethod
47
+ def visit_operation(self, operation: Operation) -> Any:
48
+ """Translate an Operation.
49
+
50
+ Args:
51
+ operation: Operation to translate.
52
+ """
53
+
54
+ @abstractmethod
55
+ def visit_comparison(self, comparison: Comparison) -> Any:
56
+ """Translate a Comparison.
57
+
58
+ Args:
59
+ comparison: Comparison to translate.
60
+ """
61
+
62
+ @abstractmethod
63
+ def visit_structured_query(self, structured_query: StructuredQuery) -> Any:
64
+ """Translate a StructuredQuery.
65
+
66
+ Args:
67
+ structured_query: StructuredQuery to translate.
68
+ """
69
+
70
+
71
+ def _to_snake_case(name: str) -> str:
72
+ """Convert a name into snake_case."""
73
+ snake_case = ""
74
+ for i, char in enumerate(name):
75
+ if char.isupper() and i != 0:
76
+ snake_case += "_" + char.lower()
77
+ else:
78
+ snake_case += char.lower()
79
+ return snake_case
80
+
81
+
82
+ class Expr(BaseModel):
83
+ """Base class for all expressions."""
84
+
85
+ def accept(self, visitor: Visitor) -> Any:
86
+ """Accept a visitor.
87
+
88
+ Args:
89
+ visitor: visitor to accept.
90
+
91
+ Returns:
92
+ result of visiting.
93
+ """
94
+ return getattr(visitor, f"visit_{_to_snake_case(self.__class__.__name__)}")(
95
+ self
96
+ )
97
+
98
+
99
+ class Operator(str, Enum):
100
+ """Enumerator of the operations."""
101
+
102
+ AND = "and"
103
+ OR = "or"
104
+ NOT = "not"
105
+
106
+
107
+ class Comparator(str, Enum):
108
+ """Enumerator of the comparison operators."""
109
+
110
+ EQ = "eq"
111
+ NE = "ne"
112
+ GT = "gt"
113
+ GTE = "gte"
114
+ LT = "lt"
115
+ LTE = "lte"
116
+ CONTAIN = "contain"
117
+ LIKE = "like"
118
+ IN = "in"
119
+ NIN = "nin"
120
+
121
+
122
+ class FilterDirective(Expr, ABC):
123
+ """Filtering expression."""
124
+
125
+
126
+ class Comparison(FilterDirective):
127
+ """Comparison to a value."""
128
+
129
+ comparator: Comparator
130
+ """The comparator to use."""
131
+
132
+ attribute: str
133
+ """The attribute to compare."""
134
+
135
+ value: Any
136
+ """The value to compare to."""
137
+
138
+ def __init__(
139
+ self, comparator: Comparator, attribute: str, value: Any, **kwargs: Any
140
+ ) -> None:
141
+ """Create a Comparison.
142
+
143
+ Args:
144
+ comparator: The comparator to use.
145
+ attribute: The attribute to compare.
146
+ value: The value to compare to.
147
+ """
148
+ # super exists from BaseModel
149
+ super().__init__(
150
+ comparator=comparator, attribute=attribute, value=value, **kwargs
151
+ )
152
+
153
+
154
+ class Operation(FilterDirective):
155
+ """Logical operation over other directives."""
156
+
157
+ operator: Operator
158
+ """The operator to use."""
159
+
160
+ arguments: list[FilterDirective]
161
+ """The arguments to the operator."""
162
+
163
+ def __init__(
164
+ self, operator: Operator, arguments: list[FilterDirective], **kwargs: Any
165
+ ) -> None:
166
+ """Create an Operation.
167
+
168
+ Args:
169
+ operator: The operator to use.
170
+ arguments: The arguments to the operator.
171
+ """
172
+ # super exists from BaseModel
173
+ super().__init__(operator=operator, arguments=arguments, **kwargs)
174
+
175
+
176
+ class StructuredQuery(Expr):
177
+ """Structured query."""
178
+
179
+ query: str
180
+ """Query string."""
181
+
182
+ filter: FilterDirective | None
183
+ """Filtering expression."""
184
+
185
+ limit: int | None
186
+ """Limit on the number of results."""
187
+
188
+ def __init__(
189
+ self,
190
+ query: str,
191
+ filter: FilterDirective | None, # noqa: A002
192
+ limit: int | None = None,
193
+ **kwargs: Any,
194
+ ) -> None:
195
+ """Create a StructuredQuery.
196
+
197
+ Args:
198
+ query: The query string.
199
+ filter: The filtering expression.
200
+ limit: The limit on the number of results.
201
+ """
202
+ # super exists from BaseModel
203
+ super().__init__(query=query, filter=filter, limit=limit, **kwargs)
coco_core/sys_info.py ADDED
@@ -0,0 +1,154 @@
1
+ """Print information about the system and coco packages for debugging purposes."""
2
+
3
+ import pkgutil
4
+ import platform
5
+ import re
6
+ import sys
7
+ from collections.abc import Sequence
8
+ from importlib import metadata, util
9
+
10
+
11
+ def _dist_name(pkg: str) -> str:
12
+ """Map a Coco module name (`coco`, `coco_core`) to its `trustcoco` distribution."""
13
+ dist = pkg.replace("_", "-")
14
+ if pkg == "coco" or pkg.startswith("coco_"):
15
+ return f"trust{dist}"
16
+ return dist
17
+
18
+
19
+ def _get_sub_deps(packages: Sequence[str]) -> list[str]:
20
+ """Get any specified sub-dependencies."""
21
+ sub_deps = set()
22
+ underscored_packages = {pkg.replace("-", "_") for pkg in packages} | {
23
+ _dist_name(pkg).replace("-", "_") for pkg in packages
24
+ }
25
+
26
+ for pkg in packages:
27
+ try:
28
+ required = metadata.requires(_dist_name(pkg))
29
+ except metadata.PackageNotFoundError:
30
+ try:
31
+ required = metadata.requires(pkg)
32
+ except metadata.PackageNotFoundError:
33
+ continue
34
+
35
+ if not required:
36
+ continue
37
+
38
+ for req in required:
39
+ # Extract package name (e.g., "httpx<1,>=0.23.0" -> "httpx")
40
+ match = re.match(r"^([a-zA-Z0-9_.-]+)", req)
41
+ if match:
42
+ pkg_name = match.group(1)
43
+ if pkg_name.replace("-", "_") not in underscored_packages:
44
+ sub_deps.add(pkg_name)
45
+
46
+ return sorted(sub_deps, key=lambda x: x.lower())
47
+
48
+
49
+ def print_sys_info(*, additional_pkgs: Sequence[str] = ()) -> None:
50
+ """Print information about the environment for debugging purposes.
51
+
52
+ Args:
53
+ additional_pkgs: Additional packages to include in the output.
54
+ """
55
+ # Packages that do not start with "coco" prefix or are namespace packages.
56
+ other_coco_packages = [
57
+ "coco_graph",
58
+ "langsmith",
59
+ "deepagents",
60
+ "deepagents-cli",
61
+ ]
62
+
63
+ coco_pkgs = [
64
+ name for _, name, _ in pkgutil.iter_modules() if name.startswith("coco")
65
+ ]
66
+
67
+ coco_graph_pkgs = [
68
+ name for _, name, _ in pkgutil.iter_modules() if name.startswith("coco_graph")
69
+ ]
70
+
71
+ all_packages = sorted(
72
+ set(
73
+ coco_pkgs
74
+ + coco_graph_pkgs
75
+ + other_coco_packages
76
+ + list(additional_pkgs)
77
+ )
78
+ )
79
+
80
+ # Always surface these packages to the top
81
+ order_by = ["coco_core", "coco", "coco_community", "langsmith"]
82
+
83
+ for pkg in reversed(order_by):
84
+ if pkg in all_packages:
85
+ all_packages.remove(pkg)
86
+ all_packages = [pkg, *list(all_packages)]
87
+
88
+ system_info = {
89
+ "OS": platform.system(),
90
+ "OS Version": platform.version(),
91
+ "Python Version": sys.version,
92
+ }
93
+ print()
94
+ print("System Information")
95
+ print("------------------")
96
+ print("> OS: ", system_info["OS"])
97
+ print("> OS Version: ", system_info["OS Version"])
98
+ print("> Python Version: ", system_info["Python Version"])
99
+
100
+ # Print out only coco packages
101
+ print()
102
+ print("Package Information")
103
+ print("-------------------")
104
+
105
+ not_installed = []
106
+
107
+ for pkg in all_packages:
108
+ try:
109
+ found_package = util.find_spec(pkg)
110
+ except Exception:
111
+ found_package = None
112
+ if found_package is None:
113
+ not_installed.append(pkg)
114
+ continue
115
+
116
+ # Package version
117
+ try:
118
+ package_version = metadata.version(_dist_name(pkg))
119
+ except Exception:
120
+ try:
121
+ package_version = metadata.version(pkg)
122
+ except Exception:
123
+ package_version = None
124
+
125
+ # Print package with version
126
+ if package_version is not None:
127
+ print(f"> {pkg}: {package_version}")
128
+
129
+ if not_installed:
130
+ print()
131
+ print("Optional packages not installed")
132
+ print("-------------------------------")
133
+ for pkg in not_installed:
134
+ print(f"> {pkg}")
135
+
136
+ sub_dependencies = _get_sub_deps(all_packages)
137
+
138
+ if sub_dependencies:
139
+ print()
140
+ print("Other Dependencies")
141
+ print("------------------")
142
+
143
+ for dep in sub_dependencies:
144
+ try:
145
+ dep_version = metadata.version(dep)
146
+ except Exception:
147
+ dep_version = None
148
+
149
+ if dep_version is not None:
150
+ print(f"> {dep}: {dep_version}")
151
+
152
+
153
+ if __name__ == "__main__":
154
+ print_sys_info()