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,497 @@
1
+ """Convert functions and runnables to tools."""
2
+
3
+ import inspect
4
+ from collections.abc import Callable
5
+ from typing import Any, Literal, cast, get_type_hints, overload
6
+
7
+ from pydantic import BaseModel, Field, RootModel, create_model
8
+
9
+ from coco_core.callbacks import Callbacks
10
+ from coco_core.runnables import Runnable
11
+ from coco_core.tools.base import ArgsSchema, BaseTool
12
+ from coco_core.tools.simple import Tool
13
+ from coco_core.tools.structured import StructuredTool
14
+ from coco_core.utils.pydantic import TypeBaseModel
15
+
16
+
17
+ @overload
18
+ def tool(
19
+ *,
20
+ description: str | None = None,
21
+ return_direct: bool = False,
22
+ args_schema: ArgsSchema | None = None,
23
+ infer_schema: bool = True,
24
+ response_format: Literal["content", "content_and_artifact"] = "content",
25
+ parse_docstring: bool = False,
26
+ error_on_invalid_docstring: bool = True,
27
+ extras: dict[str, Any] | None = None,
28
+ ) -> Callable[[Callable[..., Any] | Runnable[Any, Any]], BaseTool]: ...
29
+
30
+
31
+ @overload
32
+ def tool(
33
+ name_or_callable: str,
34
+ runnable: Runnable[Any, Any],
35
+ *,
36
+ description: str | None = None,
37
+ return_direct: bool = False,
38
+ args_schema: ArgsSchema | None = None,
39
+ infer_schema: bool = True,
40
+ response_format: Literal["content", "content_and_artifact"] = "content",
41
+ parse_docstring: bool = False,
42
+ error_on_invalid_docstring: bool = True,
43
+ extras: dict[str, Any] | None = None,
44
+ ) -> BaseTool: ...
45
+
46
+
47
+ @overload
48
+ def tool(
49
+ name_or_callable: Callable[..., Any],
50
+ *,
51
+ description: str | None = None,
52
+ return_direct: bool = False,
53
+ args_schema: ArgsSchema | None = None,
54
+ infer_schema: bool = True,
55
+ response_format: Literal["content", "content_and_artifact"] = "content",
56
+ parse_docstring: bool = False,
57
+ error_on_invalid_docstring: bool = True,
58
+ extras: dict[str, Any] | None = None,
59
+ ) -> BaseTool: ...
60
+
61
+
62
+ @overload
63
+ def tool(
64
+ name_or_callable: str,
65
+ *,
66
+ description: str | None = None,
67
+ return_direct: bool = False,
68
+ args_schema: ArgsSchema | None = None,
69
+ infer_schema: bool = True,
70
+ response_format: Literal["content", "content_and_artifact"] = "content",
71
+ parse_docstring: bool = False,
72
+ error_on_invalid_docstring: bool = True,
73
+ extras: dict[str, Any] | None = None,
74
+ ) -> Callable[[Callable[..., Any] | Runnable[Any, Any]], BaseTool]: ...
75
+
76
+
77
+ def tool(
78
+ name_or_callable: str | Callable[..., Any] | None = None,
79
+ runnable: Runnable[Any, Any] | None = None,
80
+ *args: Any,
81
+ description: str | None = None,
82
+ return_direct: bool = False,
83
+ args_schema: ArgsSchema | None = None,
84
+ infer_schema: bool = True,
85
+ response_format: Literal["content", "content_and_artifact"] = "content",
86
+ parse_docstring: bool = False,
87
+ error_on_invalid_docstring: bool = True,
88
+ extras: dict[str, Any] | None = None,
89
+ ) -> BaseTool | Callable[[Callable[..., Any] | Runnable[Any, Any]], BaseTool]:
90
+ """Convert Python functions and `Runnables` to Coco tools.
91
+
92
+ Can be used as a decorator with or without arguments to create tools from functions.
93
+
94
+ Functions can have any signature - the tool will automatically infer input schemas
95
+ unless disabled.
96
+
97
+ !!! note "Requirements"
98
+
99
+ - Functions should have type hints for proper schema inference.
100
+ - Functions may accept multiple arguments and return types are flexible;
101
+ outputs will be serialized if needed.
102
+ - When using with `Runnable`, a string name must be provided.
103
+
104
+ !!! warning "Reserved argument names"
105
+
106
+ Avoid naming tool arguments `config`, `run_manager`, or `callbacks`. These
107
+ collide with values Coco injects into tools at call time. A `config`
108
+ argument is shadowed by the injected `RunnableConfig`, so the tool fails
109
+ at call time with `TypeError: ... missing 1 required positional argument:
110
+ 'config'`, while `run_manager` and `callbacks` are silently dropped from
111
+ the generated schema. To access runtime information (state, context, store,
112
+ config), add a `ToolRuntime` parameter instead.
113
+
114
+ Args:
115
+ name_or_callable: Optional name of the tool or the `Callable` to be
116
+ converted to a tool.
117
+
118
+ Overrides the function's name.
119
+
120
+ Must be provided as a positional argument.
121
+ runnable: Optional `Runnable` to convert to a tool.
122
+
123
+ Must be provided as a positional argument.
124
+ description: Optional description for the tool.
125
+
126
+ Precedence for the tool description value is as follows:
127
+
128
+ - This `description` argument (used even if docstring and/or `args_schema`
129
+ are provided)
130
+ - Tool function docstring (used even if `args_schema` is provided)
131
+ - `args_schema` description (used only if `description` and docstring are
132
+ not provided)
133
+ *args: Extra positional arguments.
134
+
135
+ Must be empty.
136
+ return_direct: Whether to return directly from the tool rather than continuing
137
+ the agent loop.
138
+ args_schema: Optional argument schema for user to specify.
139
+ infer_schema: Whether to infer the schema of the arguments from the function's
140
+ signature.
141
+
142
+ This also makes the resultant tool accept a dictionary input to its `run()`
143
+ function.
144
+ response_format: The tool response format.
145
+
146
+ If `'content'`, then the output of the tool is interpreted as the contents
147
+ of a `ToolMessage`.
148
+
149
+ If `'content_and_artifact'`, then the output is expected to be a two-tuple
150
+ corresponding to the `(content, artifact)` of a `ToolMessage`.
151
+ parse_docstring: If `infer_schema` and `parse_docstring`, will attempt to
152
+ parse parameter descriptions from Google Style function docstrings.
153
+ error_on_invalid_docstring: If `parse_docstring` is provided, configure
154
+ whether to raise `ValueError` on invalid Google Style docstrings.
155
+ extras: Optional provider-specific extra fields for the tool.
156
+
157
+ Used to pass configuration that doesn't fit into standard tool fields.
158
+ Chat models should process known extras when constructing model payloads.
159
+
160
+ !!! example
161
+
162
+ For example, Anthropic-specific fields like `cache_control`,
163
+ `defer_loading`, or `input_examples`.
164
+
165
+ Raises:
166
+ ValueError: If too many positional arguments are provided (e.g. violating the
167
+ `*args` constraint).
168
+ ValueError: If a `Runnable` is provided without a string name. When using `tool`
169
+ with a `Runnable`, a `str` name must be provided as the `name_or_callable`.
170
+ ValueError: If the first argument is not a string or callable with
171
+ a `__name__` attribute.
172
+ ValueError: If the function does not have a docstring and description
173
+ is not provided and `infer_schema` is `False`.
174
+ ValueError: If `parse_docstring` is `True` and the function has an invalid
175
+ Google-style docstring and `error_on_invalid_docstring` is True.
176
+ ValueError: If a `Runnable` is provided that does not have an object schema.
177
+
178
+ Returns:
179
+ The tool.
180
+
181
+ Examples:
182
+ ```python
183
+ @tool
184
+ def search_api(query: str) -> str:
185
+ # Searches the API for the query.
186
+ return
187
+
188
+
189
+ @tool("search", return_direct=True)
190
+ def search_api(query: str) -> str:
191
+ # Searches the API for the query.
192
+ return
193
+
194
+
195
+ @tool(response_format="content_and_artifact")
196
+ def search_api(query: str) -> tuple[str, dict]:
197
+ return "partial json of results", {"full": "object of results"}
198
+ ```
199
+
200
+ Parse Google-style docstrings:
201
+
202
+ ```python
203
+ @tool(parse_docstring=True)
204
+ def foo(bar: str, baz: int) -> str:
205
+ \"\"\"The foo.
206
+
207
+ Args:
208
+ bar: The bar.
209
+ baz: The baz.
210
+ \"\"\"
211
+ return bar
212
+
213
+ foo.args_schema.model_json_schema()
214
+ ```
215
+
216
+ ```python
217
+ {
218
+ "title": "foo",
219
+ "description": "The foo.",
220
+ "type": "object",
221
+ "properties": {
222
+ "bar": {
223
+ "title": "Bar",
224
+ "description": "The bar.",
225
+ "type": "string",
226
+ },
227
+ "baz": {
228
+ "title": "Baz",
229
+ "description": "The baz.",
230
+ "type": "integer",
231
+ },
232
+ },
233
+ "required": ["bar", "baz"],
234
+ }
235
+ ```
236
+
237
+ Note that parsing by default will raise `ValueError` if the docstring is
238
+ considered invalid. A docstring is considered invalid if it contains arguments
239
+ not in the function signature, or is unable to be parsed into a summary and
240
+ `'Args:'` blocks. Examples below:
241
+
242
+ ```python
243
+ # No args section
244
+ def invalid_docstring_1(bar: str, baz: int) -> str:
245
+ \"\"\"The foo.\"\"\"
246
+ return bar
247
+
248
+ # Improper whitespace between summary and args section
249
+ def invalid_docstring_2(bar: str, baz: int) -> str:
250
+ \"\"\"The foo.
251
+ Args:
252
+ bar: The bar.
253
+ baz: The baz.
254
+ \"\"\"
255
+ return bar
256
+
257
+ # Documented args absent from function signature
258
+ def invalid_docstring_3(bar: str, baz: int) -> str:
259
+ \"\"\"The foo.
260
+
261
+ Args:
262
+ banana: The bar.
263
+ monkey: The baz.
264
+ \"\"\"
265
+ return bar
266
+
267
+ ```
268
+ """ # noqa: D214, D410, D411 # We're intentionally showing bad formatting in examples
269
+
270
+ def _create_tool_factory(
271
+ tool_name: str,
272
+ ) -> Callable[[Callable[..., Any] | Runnable[Any, Any]], BaseTool]:
273
+ """Create a decorator that takes a callable and returns a tool.
274
+
275
+ Args:
276
+ tool_name: The name that will be assigned to the tool.
277
+
278
+ Returns:
279
+ A function that takes a callable or `Runnable` and returns a tool.
280
+ """
281
+
282
+ def _tool_factory(
283
+ dec_func: Callable[..., Any] | Runnable[Any, Any],
284
+ ) -> BaseTool:
285
+ tool_description = description
286
+ if isinstance(dec_func, Runnable):
287
+ runnable = dec_func
288
+
289
+ if runnable.get_input_jsonschema().get("type") != "object":
290
+ msg = "Runnable must have an object schema."
291
+ raise ValueError(msg)
292
+
293
+ async def ainvoke_wrapper(
294
+ callbacks: Callbacks | None = None, **kwargs: Any
295
+ ) -> Any:
296
+ return await runnable.ainvoke(kwargs, {"callbacks": callbacks})
297
+
298
+ def invoke_wrapper(
299
+ callbacks: Callbacks | None = None, **kwargs: Any
300
+ ) -> Any:
301
+ return runnable.invoke(kwargs, {"callbacks": callbacks})
302
+
303
+ coroutine = ainvoke_wrapper
304
+ func = invoke_wrapper
305
+ schema: ArgsSchema | None = runnable.input_schema
306
+ tool_description = description or repr(runnable)
307
+ elif inspect.iscoroutinefunction(dec_func):
308
+ coroutine = dec_func
309
+ func = None
310
+ schema = args_schema
311
+ else:
312
+ coroutine = None
313
+ func = dec_func
314
+ schema = args_schema
315
+
316
+ if infer_schema or args_schema is not None:
317
+ return StructuredTool.from_function(
318
+ func,
319
+ coroutine,
320
+ name=tool_name,
321
+ description=tool_description,
322
+ return_direct=return_direct,
323
+ args_schema=schema,
324
+ infer_schema=infer_schema,
325
+ response_format=response_format,
326
+ parse_docstring=parse_docstring,
327
+ error_on_invalid_docstring=error_on_invalid_docstring,
328
+ extras=extras,
329
+ )
330
+ # If someone doesn't want a schema applied, we must treat it as
331
+ # a simple string->string function
332
+ tool_description = tool_description or dec_func.__doc__
333
+ if tool_description is None:
334
+ msg = (
335
+ "Function must have either a docstring or description "
336
+ "when infer_schema is False."
337
+ )
338
+ raise ValueError(msg)
339
+ return Tool(
340
+ name=tool_name,
341
+ func=func,
342
+ description=tool_description,
343
+ return_direct=return_direct,
344
+ coroutine=coroutine,
345
+ response_format=response_format,
346
+ extras=extras,
347
+ )
348
+
349
+ return _tool_factory
350
+
351
+ if len(args) != 0:
352
+ # Triggered if a user attempts to use positional arguments that
353
+ # do not exist in the function signature
354
+ # e.g., @tool("name", runnable, "extra_arg")
355
+ # Here, "extra_arg" is not a valid argument
356
+ msg = "Too many arguments for tool decorator. A decorator "
357
+ raise ValueError(msg)
358
+
359
+ if runnable is not None:
360
+ # tool is used as a function
361
+ # for instance tool_from_runnable = tool("name", runnable)
362
+ if not name_or_callable:
363
+ msg = "Runnable without name for tool constructor"
364
+ raise ValueError(msg)
365
+ if not isinstance(name_or_callable, str):
366
+ msg = "Name must be a string for tool constructor"
367
+ raise ValueError(msg)
368
+ return _create_tool_factory(name_or_callable)(runnable)
369
+ if name_or_callable is not None:
370
+ if callable(name_or_callable) and hasattr(name_or_callable, "__name__"):
371
+ # Used as a decorator without parameters
372
+ # @tool
373
+ # def my_tool():
374
+ # pass
375
+ return _create_tool_factory(name_or_callable.__name__)(name_or_callable)
376
+ if isinstance(name_or_callable, str):
377
+ # Used with a new name for the tool
378
+ # @tool("search")
379
+ # def my_tool():
380
+ # pass
381
+ #
382
+ # or
383
+ #
384
+ # @tool("search", parse_docstring=True)
385
+ # def my_tool():
386
+ # pass
387
+ return _create_tool_factory(name_or_callable)
388
+ msg = (
389
+ f"The first argument must be a string or a callable with a __name__ "
390
+ f"for tool decorator. Got {type(name_or_callable)}"
391
+ )
392
+ raise ValueError(msg)
393
+
394
+ # Tool is used as a decorator with parameters specified
395
+ # @tool(parse_docstring=True)
396
+ # def my_tool():
397
+ # pass
398
+ def _partial(func: Callable[..., Any] | Runnable[Any, Any]) -> BaseTool:
399
+ """Partial function that takes a `Callable` and returns a tool."""
400
+ name_ = func.get_name() if isinstance(func, Runnable) else func.__name__
401
+ tool_factory = _create_tool_factory(name_)
402
+ return tool_factory(func)
403
+
404
+ return _partial
405
+
406
+
407
+ def _get_description_from_runnable(runnable: Runnable[Any, Any]) -> str:
408
+ """Generate a placeholder description of a `Runnable`."""
409
+ input_schema = runnable.get_input_jsonschema()
410
+ return f"Takes {input_schema}."
411
+
412
+
413
+ def _get_schema_from_runnable_and_arg_types(
414
+ runnable: Runnable[Any, Any],
415
+ name: str,
416
+ arg_types: dict[str, type] | None = None,
417
+ ) -> type[BaseModel]:
418
+ """Infer `args_schema` for tool."""
419
+ if arg_types is None:
420
+ try:
421
+ arg_types = get_type_hints(runnable.InputType)
422
+ except TypeError as e:
423
+ msg = (
424
+ "Tool input must be str or dict. If dict, dict arguments must be "
425
+ "typed. Either annotate types (e.g., with TypedDict) or pass "
426
+ f"arg_types into `.as_tool` to specify. {e}"
427
+ )
428
+ raise TypeError(msg) from e
429
+ fields = {key: (key_type, Field(...)) for key, key_type in arg_types.items()}
430
+ return cast("type[BaseModel]", create_model(name, **fields)) # type: ignore[call-overload]
431
+
432
+
433
+ def convert_runnable_to_tool(
434
+ runnable: Runnable[Any, Any],
435
+ args_schema: TypeBaseModel | None = None,
436
+ *,
437
+ name: str | None = None,
438
+ description: str | None = None,
439
+ arg_types: dict[str, type] | None = None,
440
+ ) -> BaseTool:
441
+ """Convert a `Runnable` into a `BaseTool`.
442
+
443
+ Args:
444
+ runnable: The `Runnable` to convert.
445
+ args_schema: The schema for the tool's input arguments.
446
+ name: The name of the tool.
447
+ description: The description of the tool.
448
+ arg_types: The types of the arguments.
449
+
450
+ Returns:
451
+ The tool.
452
+ """
453
+ if args_schema:
454
+ runnable = runnable.with_types(input_type=args_schema)
455
+ description = description or _get_description_from_runnable(runnable)
456
+ name = name or runnable.get_name()
457
+
458
+ schema = runnable.get_input_jsonschema()
459
+ if schema.get("type") == "string":
460
+ return Tool(
461
+ name=name,
462
+ func=runnable.invoke,
463
+ coroutine=runnable.ainvoke,
464
+ description=description,
465
+ )
466
+
467
+ async def ainvoke_wrapper(callbacks: Callbacks | None = None, **kwargs: Any) -> Any:
468
+ return await runnable.ainvoke(kwargs, config={"callbacks": callbacks})
469
+
470
+ def invoke_wrapper(callbacks: Callbacks | None = None, **kwargs: Any) -> Any:
471
+ return runnable.invoke(kwargs, config={"callbacks": callbacks})
472
+
473
+ input_schema_cls = runnable.input_schema
474
+ # Detect `RootModel` input schemas so we don't use them directly as the tool
475
+ # args schema.
476
+ is_root_model = isinstance(input_schema_cls, type) and issubclass(
477
+ input_schema_cls, RootModel
478
+ )
479
+ if (
480
+ arg_types is None
481
+ and schema.get("type") == "object"
482
+ and schema.get("properties")
483
+ and not is_root_model
484
+ ):
485
+ args_schema = input_schema_cls
486
+ else:
487
+ args_schema = _get_schema_from_runnable_and_arg_types(
488
+ runnable, name, arg_types=arg_types
489
+ )
490
+
491
+ return StructuredTool.from_function(
492
+ name=name,
493
+ func=invoke_wrapper,
494
+ coroutine=ainvoke_wrapper,
495
+ description=description,
496
+ args_schema=args_schema,
497
+ )
@@ -0,0 +1,67 @@
1
+ """Utilities to render tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from inspect import signature
7
+
8
+ from coco_core.tools.base import BaseTool
9
+
10
+ ToolsRenderer = Callable[[list[BaseTool]], str]
11
+
12
+
13
+ def render_text_description(tools: list[BaseTool]) -> str:
14
+ """Render the tool name and description in plain text.
15
+
16
+ Args:
17
+ tools: The tools to render.
18
+
19
+ Returns:
20
+ The rendered text.
21
+
22
+ Output will be in the format of:
23
+
24
+ ```txt
25
+ search: This tool is used for search
26
+ calculator: This tool is used for math
27
+ ```
28
+ """
29
+ descriptions = []
30
+ for tool in tools:
31
+ if hasattr(tool, "func") and tool.func:
32
+ sig = signature(tool.func)
33
+ description = f"{tool.name}{sig} - {tool.description}"
34
+ else:
35
+ description = f"{tool.name} - {tool.description}"
36
+
37
+ descriptions.append(description)
38
+ return "\n".join(descriptions)
39
+
40
+
41
+ def render_text_description_and_args(tools: list[BaseTool]) -> str:
42
+ """Render the tool name, description, and args in plain text.
43
+
44
+ Args:
45
+ tools: The tools to render.
46
+
47
+ Returns:
48
+ The rendered text.
49
+
50
+ Output will be in the format of:
51
+
52
+ ```txt
53
+ search: This tool is used for search, args: {"query": {"type": "string"}}
54
+ calculator: This tool is used for math, \
55
+ args: {"expression": {"type": "string"}}
56
+ ```
57
+ """
58
+ tool_strings = []
59
+ for tool in tools:
60
+ args_schema = str(tool.args)
61
+ if hasattr(tool, "func") and tool.func:
62
+ sig = signature(tool.func)
63
+ description = f"{tool.name}{sig} - {tool.description}"
64
+ else:
65
+ description = f"{tool.name} - {tool.description}"
66
+ tool_strings.append(f"{description}, args: {args_schema}")
67
+ return "\n".join(tool_strings)
@@ -0,0 +1,94 @@
1
+ """Retriever tool."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Literal
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+ # Cannot move Callbacks and Document to TYPE_CHECKING as StructuredTool's
10
+ # func/coroutine parameter annotations are evaluated at runtime.
11
+ from coco_core.callbacks import Callbacks # noqa: TC001
12
+ from coco_core.documents import Document # noqa: TC001
13
+ from coco_core.prompts import (
14
+ BasePromptTemplate,
15
+ PromptTemplate,
16
+ aformat_document,
17
+ format_document,
18
+ )
19
+ from coco_core.tools.structured import StructuredTool
20
+
21
+ if TYPE_CHECKING:
22
+ from coco_core.retrievers import BaseRetriever
23
+
24
+
25
+ class RetrieverInput(BaseModel):
26
+ """Input to the retriever."""
27
+
28
+ query: str = Field(description="query to look up in retriever")
29
+
30
+
31
+ def create_retriever_tool(
32
+ retriever: BaseRetriever,
33
+ name: str,
34
+ description: str,
35
+ *,
36
+ document_prompt: BasePromptTemplate[str] | None = None,
37
+ document_separator: str = "\n\n",
38
+ response_format: Literal["content", "content_and_artifact"] = "content",
39
+ ) -> StructuredTool:
40
+ r"""Create a tool to do retrieval of documents.
41
+
42
+ Args:
43
+ retriever: The retriever to use for the retrieval
44
+ name: The name for the tool.
45
+
46
+ This will be passed to the language model, so should be unique and somewhat
47
+ descriptive.
48
+ description: The description for the tool.
49
+
50
+ This will be passed to the language model, so should be descriptive.
51
+ document_prompt: The prompt to use for the document.
52
+ document_separator: The separator to use between documents.
53
+ response_format: The tool response format.
54
+
55
+ If `'content'` then the output of the tool is interpreted as the contents of
56
+ a `ToolMessage`. If `'content_and_artifact'` then the output is expected to
57
+ be a two-tuple corresponding to the `(content, artifact)` of a `ToolMessage`
58
+ (artifact being a list of documents in this case).
59
+
60
+ Returns:
61
+ Tool class to pass to an agent.
62
+ """
63
+ document_prompt_ = document_prompt or PromptTemplate.from_template("{page_content}")
64
+
65
+ def func(
66
+ query: str, callbacks: Callbacks = None
67
+ ) -> str | tuple[str, list[Document]]:
68
+ docs = retriever.invoke(query, config={"callbacks": callbacks})
69
+ content = document_separator.join(
70
+ format_document(doc, document_prompt_) for doc in docs
71
+ )
72
+ if response_format == "content_and_artifact":
73
+ return (content, docs)
74
+ return content
75
+
76
+ async def afunc(
77
+ query: str, callbacks: Callbacks = None
78
+ ) -> str | tuple[str, list[Document]]:
79
+ docs = await retriever.ainvoke(query, config={"callbacks": callbacks})
80
+ content = document_separator.join(
81
+ [await aformat_document(doc, document_prompt_) for doc in docs]
82
+ )
83
+ if response_format == "content_and_artifact":
84
+ return (content, docs)
85
+ return content
86
+
87
+ return StructuredTool(
88
+ name=name,
89
+ description=description,
90
+ func=func,
91
+ coroutine=afunc,
92
+ args_schema=RetrieverInput,
93
+ response_format=response_format,
94
+ )