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,62 @@
1
+ """Function Message."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ from typing_extensions import override
6
+
7
+ from coco_core.messages.base import (
8
+ BaseMessage,
9
+ BaseMessageChunk,
10
+ merge_content,
11
+ )
12
+ from coco_core.utils._merge import merge_dicts
13
+
14
+
15
+ class FunctionMessage(BaseMessage):
16
+ """Message for passing the result of executing a tool back to a model.
17
+
18
+ `FunctionMessage` are an older version of the `ToolMessage` schema, and
19
+ do not contain the `tool_call_id` field.
20
+
21
+ The `tool_call_id` field is used to associate the tool call request with the
22
+ tool call response. Useful in situations where a chat model is able
23
+ to request multiple tool calls in parallel.
24
+
25
+ """
26
+
27
+ name: str
28
+ """The name of the function that was executed."""
29
+
30
+ type: Literal["function"] = "function"
31
+ """The type of the message (used for serialization)."""
32
+
33
+
34
+ class FunctionMessageChunk(FunctionMessage, BaseMessageChunk):
35
+ """Function Message chunk."""
36
+
37
+ # Ignoring mypy re-assignment here since we're overriding the value
38
+ # to make sure that the chunk variant can be discriminated from the
39
+ # non-chunk variant.
40
+ type: Literal["FunctionMessageChunk"] = "FunctionMessageChunk" # type: ignore[assignment]
41
+ """The type of the message (used for serialization)."""
42
+
43
+ @override
44
+ def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
45
+ if isinstance(other, FunctionMessageChunk):
46
+ if self.name != other.name:
47
+ msg = "Cannot concatenate FunctionMessageChunks with different names."
48
+ raise ValueError(msg)
49
+
50
+ return self.__class__(
51
+ name=self.name,
52
+ content=merge_content(self.content, other.content),
53
+ additional_kwargs=merge_dicts(
54
+ self.additional_kwargs, other.additional_kwargs
55
+ ),
56
+ response_metadata=merge_dicts(
57
+ self.response_metadata, other.response_metadata
58
+ ),
59
+ id=self.id,
60
+ )
61
+
62
+ return super().__add__(other)
@@ -0,0 +1,70 @@
1
+ """Human message."""
2
+
3
+ from typing import Any, Literal, cast, overload
4
+
5
+ from coco_core.messages import content as types
6
+ from coco_core.messages.base import BaseMessage, BaseMessageChunk
7
+
8
+
9
+ class HumanMessage(BaseMessage):
10
+ """Message from the user.
11
+
12
+ A `HumanMessage` is a message that is passed in from a user to the model.
13
+
14
+ Example:
15
+ ```python
16
+ from coco_core.messages import HumanMessage, SystemMessage
17
+
18
+ messages = [
19
+ SystemMessage(content="You are a helpful assistant! Your name is Bob."),
20
+ HumanMessage(content="What is your name?"),
21
+ ]
22
+
23
+ # Instantiate a chat model and invoke it with the messages
24
+ model = ...
25
+ print(model.invoke(messages))
26
+ ```
27
+ """
28
+
29
+ type: Literal["human"] = "human"
30
+ """The type of the message (used for serialization)."""
31
+
32
+ @overload
33
+ def __init__(
34
+ self,
35
+ content: str | list[str | dict[Any, Any]],
36
+ **kwargs: Any,
37
+ ) -> None: ...
38
+
39
+ @overload
40
+ def __init__(
41
+ self,
42
+ content: str | list[str | dict[Any, Any]] | None = None,
43
+ content_blocks: list[types.ContentBlock] | None = None,
44
+ **kwargs: Any,
45
+ ) -> None: ...
46
+
47
+ def __init__(
48
+ self,
49
+ content: str | list[str | dict[Any, Any]] | None = None,
50
+ content_blocks: list[types.ContentBlock] | None = None,
51
+ **kwargs: Any,
52
+ ) -> None:
53
+ """Specify `content` as positional arg or `content_blocks` for typing."""
54
+ if content_blocks is not None:
55
+ super().__init__(
56
+ content=cast("list[str | dict[Any, Any]]", content_blocks),
57
+ **kwargs,
58
+ )
59
+ else:
60
+ super().__init__(content=content, **kwargs)
61
+
62
+
63
+ class HumanMessageChunk(HumanMessage, BaseMessageChunk):
64
+ """Human Message chunk."""
65
+
66
+ # Ignoring mypy re-assignment here since we're overriding the value
67
+ # to make sure that the chunk variant can be discriminated from the
68
+ # non-chunk variant.
69
+ type: Literal["HumanMessageChunk"] = "HumanMessageChunk" # type: ignore[assignment]
70
+ """The type of the message (used for serialization)."""
@@ -0,0 +1,33 @@
1
+ """Message responsible for deleting other messages."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ from coco_core.messages.base import BaseMessage
6
+
7
+
8
+ class RemoveMessage(BaseMessage):
9
+ """Message responsible for deleting other messages."""
10
+
11
+ type: Literal["remove"] = "remove"
12
+ """The type of the message (used for serialization)."""
13
+
14
+ def __init__(
15
+ self,
16
+ id: str,
17
+ **kwargs: Any,
18
+ ) -> None:
19
+ """Create a RemoveMessage.
20
+
21
+ Args:
22
+ id: The ID of the message to remove.
23
+ **kwargs: Additional fields to pass to the message.
24
+
25
+ Raises:
26
+ ValueError: If the 'content' field is passed in kwargs.
27
+
28
+ """
29
+ if kwargs.pop("content", None):
30
+ msg = "RemoveMessage does not support 'content' field."
31
+ raise ValueError(msg)
32
+
33
+ super().__init__("", id=id, **kwargs)
@@ -0,0 +1,70 @@
1
+ """System message."""
2
+
3
+ from typing import Any, Literal, cast, overload
4
+
5
+ from coco_core.messages import content as types
6
+ from coco_core.messages.base import BaseMessage, BaseMessageChunk
7
+
8
+
9
+ class SystemMessage(BaseMessage):
10
+ """Message for priming AI behavior.
11
+
12
+ The system message is usually passed in as the first of a sequence
13
+ of input messages.
14
+
15
+ Example:
16
+ ```python
17
+ from coco_core.messages import HumanMessage, SystemMessage
18
+
19
+ messages = [
20
+ SystemMessage(content="You are a helpful assistant! Your name is Bob."),
21
+ HumanMessage(content="What is your name?"),
22
+ ]
23
+
24
+ # Define a chat model and invoke it with the messages
25
+ print(model.invoke(messages))
26
+ ```
27
+ """
28
+
29
+ type: Literal["system"] = "system"
30
+ """The type of the message (used for serialization)."""
31
+
32
+ @overload
33
+ def __init__(
34
+ self,
35
+ content: str | list[str | dict[Any, Any]],
36
+ **kwargs: Any,
37
+ ) -> None: ...
38
+
39
+ @overload
40
+ def __init__(
41
+ self,
42
+ content: str | list[str | dict[Any, Any]] | None = None,
43
+ content_blocks: list[types.ContentBlock] | None = None,
44
+ **kwargs: Any,
45
+ ) -> None: ...
46
+
47
+ def __init__(
48
+ self,
49
+ content: str | list[str | dict[Any, Any]] | None = None,
50
+ content_blocks: list[types.ContentBlock] | None = None,
51
+ **kwargs: Any,
52
+ ) -> None:
53
+ """Specify `content` as positional arg or `content_blocks` for typing."""
54
+ if content_blocks is not None:
55
+ super().__init__(
56
+ content=cast("list[str | dict[Any, Any]]", content_blocks),
57
+ **kwargs,
58
+ )
59
+ else:
60
+ super().__init__(content=content, **kwargs)
61
+
62
+
63
+ class SystemMessageChunk(SystemMessage, BaseMessageChunk):
64
+ """System Message chunk."""
65
+
66
+ # Ignoring mypy re-assignment here since we're overriding the value
67
+ # to make sure that the chunk variant can be discriminated from the
68
+ # non-chunk variant.
69
+ type: Literal["SystemMessageChunk"] = "SystemMessageChunk" # type: ignore[assignment]
70
+ """The type of the message (used for serialization)."""
@@ -0,0 +1,418 @@
1
+ """Messages for tools."""
2
+
3
+ import json
4
+ from typing import Any, Literal, cast, overload
5
+ from uuid import UUID
6
+
7
+ from pydantic import Field, model_validator
8
+ from typing_extensions import NotRequired, TypedDict, override
9
+
10
+ from coco_core.messages import content as types
11
+ from coco_core.messages.base import BaseMessage, BaseMessageChunk, merge_content
12
+ from coco_core.messages.content import InvalidToolCall
13
+ from coco_core.utils._merge import merge_dicts, merge_obj
14
+
15
+
16
+ class ToolOutputMixin:
17
+ """Mixin for objects that tools can return directly.
18
+
19
+ If a custom BaseTool is invoked with a `ToolCall` and the output of custom code is
20
+ not an instance of `ToolOutputMixin`, the output will automatically be coerced to
21
+ a string and wrapped in a `ToolMessage`.
22
+
23
+ """
24
+
25
+
26
+ class ToolMessage(BaseMessage, ToolOutputMixin):
27
+ """Message for passing the result of executing a tool back to a model.
28
+
29
+ `ToolMessage` objects contain the result of a tool invocation. Typically, the result
30
+ is encoded inside the `content` field.
31
+
32
+ `tool_call_id` is used to associate the tool call request with the tool call
33
+ response. Useful in situations where a chat model is able to request multiple tool
34
+ calls in parallel.
35
+
36
+ Example:
37
+ A `ToolMessage` representing a result of `42` from a tool call with id
38
+
39
+ ```python
40
+ from coco_core.messages import ToolMessage
41
+
42
+ ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL")
43
+ ```
44
+
45
+ Example:
46
+ A `ToolMessage` where only part of the tool output is sent to the model
47
+ and the full output is passed in to artifact.
48
+
49
+ ```python
50
+ from coco_core.messages import ToolMessage
51
+
52
+ tool_output = {
53
+ "stdout": "From the graph we can see that the correlation between "
54
+ "x and y is ...",
55
+ "stderr": None,
56
+ "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."},
57
+ }
58
+
59
+ ToolMessage(
60
+ content=tool_output["stdout"],
61
+ artifact=tool_output,
62
+ tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL",
63
+ )
64
+ ```
65
+ """
66
+
67
+ tool_call_id: str
68
+ """Tool call that this message is responding to."""
69
+
70
+ type: Literal["tool"] = "tool"
71
+ """The type of the message (used for serialization)."""
72
+
73
+ artifact: Any = None
74
+ """Artifact of the Tool execution which is not meant to be sent to the model.
75
+
76
+ Should only be specified if it is different from the message content, e.g. if only
77
+ a subset of the full tool output is being passed as message content but the full
78
+ output is needed in other parts of the code.
79
+ """
80
+
81
+ status: Literal["success", "error"] = "success"
82
+ """Status of the tool invocation."""
83
+
84
+ additional_kwargs: dict[Any, Any] = Field(default_factory=dict, repr=False)
85
+ """Currently inherited from `BaseMessage`, but not used."""
86
+
87
+ response_metadata: dict[Any, Any] = Field(default_factory=dict, repr=False)
88
+ """Currently inherited from `BaseMessage`, but not used."""
89
+
90
+ @model_validator(mode="before")
91
+ @classmethod
92
+ def coerce_args(cls, values: dict[str, Any]) -> dict[str, Any]:
93
+ """Coerce the model arguments to the correct types.
94
+
95
+ Args:
96
+ values: The model arguments.
97
+
98
+ """
99
+ content = values["content"]
100
+ if isinstance(content, tuple):
101
+ content = list(content)
102
+
103
+ if not isinstance(content, (str, list)):
104
+ try:
105
+ values["content"] = str(content)
106
+ except ValueError as e:
107
+ msg = (
108
+ "ToolMessage content should be a string or a list of string/dicts. "
109
+ f"Received:\n\n{content=}\n\n which could not be coerced into a "
110
+ "string."
111
+ )
112
+ raise ValueError(msg) from e
113
+ elif isinstance(content, list):
114
+ values["content"] = []
115
+ for i, x in enumerate(content):
116
+ if not isinstance(x, (str, dict)):
117
+ try:
118
+ values["content"].append(str(x))
119
+ except ValueError as e:
120
+ msg = (
121
+ "ToolMessage content should be a string or a list of "
122
+ "string/dicts. Received a list but "
123
+ f"element ToolMessage.content[{i}] is not a dict and could "
124
+ f"not be coerced to a string.:\n\n{x}"
125
+ )
126
+ raise ValueError(msg) from e
127
+ else:
128
+ values["content"].append(x)
129
+
130
+ tool_call_id = values["tool_call_id"]
131
+ if isinstance(tool_call_id, (UUID, int, float)):
132
+ values["tool_call_id"] = str(tool_call_id)
133
+ return values
134
+
135
+ @overload
136
+ def __init__(
137
+ self,
138
+ content: str | list[str | dict[Any, Any]],
139
+ **kwargs: Any,
140
+ ) -> None: ...
141
+
142
+ @overload
143
+ def __init__(
144
+ self,
145
+ content: str | list[str | dict[Any, Any]] | None = None,
146
+ content_blocks: list[types.ContentBlock] | None = None,
147
+ **kwargs: Any,
148
+ ) -> None: ...
149
+
150
+ def __init__(
151
+ self,
152
+ content: str | list[str | dict[Any, Any]] | None = None,
153
+ content_blocks: list[types.ContentBlock] | None = None,
154
+ **kwargs: Any,
155
+ ) -> None:
156
+ """Initialize a `ToolMessage`.
157
+
158
+ Specify `content` as positional arg or `content_blocks` for typing.
159
+
160
+ Args:
161
+ content: The contents of the message.
162
+ content_blocks: Typed standard content.
163
+ **kwargs: Additional fields.
164
+ """
165
+ if content_blocks is not None:
166
+ super().__init__(
167
+ content=cast("list[str | dict[Any, Any]]", content_blocks),
168
+ **kwargs,
169
+ )
170
+ else:
171
+ super().__init__(content=content, **kwargs)
172
+
173
+
174
+ class ToolMessageChunk(ToolMessage, BaseMessageChunk):
175
+ """Tool Message chunk."""
176
+
177
+ # Ignoring mypy re-assignment here since we're overriding the value
178
+ # to make sure that the chunk variant can be discriminated from the
179
+ # non-chunk variant.
180
+ type: Literal["ToolMessageChunk"] = "ToolMessageChunk" # type: ignore[assignment]
181
+
182
+ @override
183
+ def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
184
+ if isinstance(other, ToolMessageChunk):
185
+ if self.tool_call_id != other.tool_call_id:
186
+ msg = "Cannot concatenate ToolMessageChunks with different names."
187
+ raise ValueError(msg)
188
+
189
+ return self.__class__(
190
+ tool_call_id=self.tool_call_id,
191
+ content=merge_content(self.content, other.content),
192
+ artifact=merge_obj(self.artifact, other.artifact),
193
+ additional_kwargs=merge_dicts(
194
+ self.additional_kwargs, other.additional_kwargs
195
+ ),
196
+ response_metadata=merge_dicts(
197
+ self.response_metadata, other.response_metadata
198
+ ),
199
+ id=self.id,
200
+ status=_merge_status(self.status, other.status),
201
+ )
202
+
203
+ return super().__add__(other)
204
+
205
+
206
+ class ToolCall(TypedDict):
207
+ """Represents an AI's request to call a tool.
208
+
209
+ Example:
210
+ ```python
211
+ {"name": "foo", "args": {"a": 1}, "id": "123"}
212
+ ```
213
+
214
+ This represents a request to call the tool named `'foo'` with arguments
215
+ `{"a": 1}` and an identifier of `'123'`.
216
+
217
+ !!! note "Factory function"
218
+
219
+ `tool_call` may also be used as a factory to create a `ToolCall`. Benefits
220
+ include:
221
+
222
+ * Required arguments strictly validated at creation time
223
+ """
224
+
225
+ name: str
226
+ """The name of the tool to be called."""
227
+
228
+ args: dict[str, Any]
229
+ """The arguments to the tool call as a dictionary."""
230
+
231
+ id: str | None
232
+ """An identifier associated with the tool call.
233
+
234
+ An identifier is needed to associate a tool call request with a tool
235
+ call result in events when multiple concurrent tool calls are made.
236
+ """
237
+
238
+ type: NotRequired[Literal["tool_call"]]
239
+ """Used for discrimination."""
240
+
241
+
242
+ def tool_call(
243
+ *,
244
+ name: str,
245
+ args: dict[str, Any],
246
+ id: str | None,
247
+ ) -> ToolCall:
248
+ """Create a tool call.
249
+
250
+ Args:
251
+ name: The name of the tool to be called.
252
+ args: The arguments to the tool call as a dictionary.
253
+ id: An identifier associated with the tool call.
254
+
255
+ Returns:
256
+ The created tool call.
257
+ """
258
+ return ToolCall(name=name, args=args, id=id, type="tool_call")
259
+
260
+
261
+ class ToolCallChunk(TypedDict):
262
+ """A chunk of a tool call (yielded when streaming).
263
+
264
+ When merging `ToolCallChunk` objects (e.g., via `AIMessageChunk.__add__`), all
265
+ string attributes are concatenated. Chunks are only merged if their values of
266
+ `index` are equal and not `None`.
267
+
268
+ Example:
269
+ ```python
270
+ left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)]
271
+ right_chunks = [ToolCallChunk(name=None, args="1}", index=0)]
272
+
273
+ (
274
+ AIMessageChunk(content="", tool_call_chunks=left_chunks)
275
+ + AIMessageChunk(content="", tool_call_chunks=right_chunks)
276
+ ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)]
277
+ ```
278
+ """
279
+
280
+ name: str | None
281
+ """The name of the tool to be called."""
282
+
283
+ args: str | None
284
+ """The arguments to the tool call as a JSON-parseable string."""
285
+
286
+ id: str | None
287
+ """An identifier associated with the tool call.
288
+
289
+ An identifier is needed to associate a tool call request with a tool
290
+ call result in events when multiple concurrent tool calls are made.
291
+ """
292
+
293
+ index: int | None
294
+ """The index of the tool call in a sequence.
295
+
296
+ Used for merging chunks.
297
+ """
298
+
299
+ type: NotRequired[Literal["tool_call_chunk"]]
300
+ """Used for discrimination."""
301
+
302
+
303
+ def tool_call_chunk(
304
+ *,
305
+ name: str | None = None,
306
+ args: str | None = None,
307
+ id: str | None = None,
308
+ index: int | None = None,
309
+ ) -> ToolCallChunk:
310
+ """Create a tool call chunk.
311
+
312
+ Args:
313
+ name: The name of the tool to be called.
314
+ args: The arguments to the tool call as a JSON string.
315
+ id: An identifier associated with the tool call.
316
+ index: The index of the tool call in a sequence.
317
+
318
+ Returns:
319
+ The created tool call chunk.
320
+ """
321
+ return ToolCallChunk(
322
+ name=name, args=args, id=id, index=index, type="tool_call_chunk"
323
+ )
324
+
325
+
326
+ def invalid_tool_call(
327
+ *,
328
+ name: str | None = None,
329
+ args: str | None = None,
330
+ id: str | None = None,
331
+ error: str | None = None,
332
+ ) -> InvalidToolCall:
333
+ """Create an invalid tool call.
334
+
335
+ Args:
336
+ name: The name of the tool to be called.
337
+ args: The arguments to the tool call as a JSON string.
338
+ id: An identifier associated with the tool call.
339
+ error: An error message associated with the tool call.
340
+
341
+ Returns:
342
+ The created invalid tool call.
343
+ """
344
+ return InvalidToolCall(
345
+ name=name, args=args, id=id, error=error, type="invalid_tool_call"
346
+ )
347
+
348
+
349
+ def default_tool_parser(
350
+ raw_tool_calls: list[dict[str, Any]],
351
+ ) -> tuple[list[ToolCall], list[InvalidToolCall]]:
352
+ """Best-effort parsing of tools.
353
+
354
+ Args:
355
+ raw_tool_calls: List of raw tool call dicts to parse.
356
+
357
+ Returns:
358
+ A list of tool calls and invalid tool calls.
359
+ """
360
+ tool_calls = []
361
+ invalid_tool_calls = []
362
+ for raw_tool_call in raw_tool_calls:
363
+ if "function" not in raw_tool_call:
364
+ continue
365
+ function_name = raw_tool_call["function"]["name"]
366
+ try:
367
+ function_args = json.loads(raw_tool_call["function"]["arguments"])
368
+ parsed = tool_call(
369
+ name=function_name or "",
370
+ args=function_args or {},
371
+ id=raw_tool_call.get("id"),
372
+ )
373
+ tool_calls.append(parsed)
374
+ except json.JSONDecodeError:
375
+ invalid_tool_calls.append(
376
+ invalid_tool_call(
377
+ name=function_name,
378
+ args=raw_tool_call["function"]["arguments"],
379
+ id=raw_tool_call.get("id"),
380
+ error=None,
381
+ )
382
+ )
383
+ return tool_calls, invalid_tool_calls
384
+
385
+
386
+ def default_tool_chunk_parser(
387
+ raw_tool_calls: list[dict[str, Any]],
388
+ ) -> list[ToolCallChunk]:
389
+ """Best-effort parsing of tool chunks.
390
+
391
+ Args:
392
+ raw_tool_calls: List of raw tool call dicts to parse.
393
+
394
+ Returns:
395
+ List of parsed ToolCallChunk objects.
396
+ """
397
+ tool_call_chunks = []
398
+ for tool_call in raw_tool_calls:
399
+ if "function" not in tool_call:
400
+ function_args = None
401
+ function_name = None
402
+ else:
403
+ function_args = tool_call["function"]["arguments"]
404
+ function_name = tool_call["function"]["name"]
405
+ parsed = tool_call_chunk(
406
+ name=function_name,
407
+ args=function_args,
408
+ id=tool_call.get("id"),
409
+ index=tool_call.get("index"),
410
+ )
411
+ tool_call_chunks.append(parsed)
412
+ return tool_call_chunks
413
+
414
+
415
+ def _merge_status(
416
+ left: Literal["success", "error"], right: Literal["success", "error"]
417
+ ) -> Literal["success", "error"]:
418
+ return "error" if "error" in {left, right} else "success"