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,225 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Awaitable, Callable, Hashable, Sequence
4
+ from inspect import (
5
+ isfunction,
6
+ ismethod,
7
+ signature,
8
+ )
9
+ from itertools import zip_longest
10
+ from types import FunctionType
11
+ from typing import (
12
+ Any,
13
+ Literal,
14
+ NamedTuple,
15
+ cast,
16
+ get_args,
17
+ get_origin,
18
+ get_type_hints,
19
+ )
20
+
21
+ from coco_core.runnables import (
22
+ Runnable,
23
+ RunnableConfig,
24
+ RunnableLambda,
25
+ )
26
+
27
+ from coco_graph._internal._runnable import (
28
+ RunnableCallable,
29
+ )
30
+ from coco_graph.constants import END, START
31
+ from coco_graph.errors import InvalidUpdateError
32
+ from coco_graph.pregel._write import PASSTHROUGH, ChannelWrite, ChannelWriteEntry
33
+ from coco_graph.types import Send
34
+
35
+ _Writer = Callable[
36
+ [Sequence[str | Send], bool],
37
+ Sequence[ChannelWriteEntry | Send],
38
+ ]
39
+
40
+
41
+ def _get_branch_path_input_schema(
42
+ path: Callable[..., Hashable | Sequence[Hashable]]
43
+ | Callable[..., Awaitable[Hashable | Sequence[Hashable]]]
44
+ | Runnable[Any, Hashable | Sequence[Hashable]],
45
+ ) -> type[Any] | None:
46
+ input = None
47
+ # detect input schema annotation in the branch callable
48
+ try:
49
+ callable_: (
50
+ Callable[..., Hashable | Sequence[Hashable]]
51
+ | Callable[..., Awaitable[Hashable | Sequence[Hashable]]]
52
+ | None
53
+ ) = None
54
+ if isinstance(path, (RunnableCallable, RunnableLambda)):
55
+ if isfunction(path.func) or ismethod(path.func):
56
+ callable_ = path.func
57
+ elif (callable_method := getattr(path.func, "__call__", None)) and ismethod(
58
+ callable_method
59
+ ):
60
+ callable_ = callable_method
61
+ elif isfunction(path.afunc) or ismethod(path.afunc):
62
+ callable_ = path.afunc
63
+ elif (
64
+ callable_method := getattr(path.afunc, "__call__", None)
65
+ ) and ismethod(callable_method):
66
+ callable_ = callable_method
67
+ elif callable(path):
68
+ callable_ = path
69
+
70
+ if callable_ is not None and (hints := get_type_hints(callable_)):
71
+ first_parameter_name = next(
72
+ iter(signature(cast(FunctionType, callable_)).parameters.keys())
73
+ )
74
+ if input_hint := hints.get(first_parameter_name):
75
+ if isinstance(input_hint, type) and get_type_hints(input_hint):
76
+ input = input_hint
77
+ except (TypeError, StopIteration):
78
+ pass
79
+
80
+ return input
81
+
82
+
83
+ class BranchSpec(NamedTuple):
84
+ path: Runnable[Any, Hashable | list[Hashable]]
85
+ ends: dict[Hashable, str] | None
86
+ input_schema: type[Any] | None = None
87
+
88
+ @classmethod
89
+ def from_path(
90
+ cls,
91
+ path: Runnable[Any, Hashable | list[Hashable]],
92
+ path_map: dict[Hashable, str] | list[str] | None,
93
+ infer_schema: bool = False,
94
+ ) -> BranchSpec:
95
+ # coerce path_map to a dictionary
96
+ path_map_: dict[Hashable, str] | None = None
97
+ try:
98
+ if isinstance(path_map, dict):
99
+ path_map_ = path_map.copy()
100
+ elif isinstance(path_map, list):
101
+ path_map_ = {name: name for name in path_map}
102
+ else:
103
+ # find func
104
+ func: Callable | None = None
105
+ if isinstance(path, (RunnableCallable, RunnableLambda)):
106
+ func = path.func or path.afunc
107
+ if func is not None:
108
+ # find callable method
109
+ if (cal := getattr(path, "__call__", None)) and ismethod(cal):
110
+ func = cal
111
+ # get the return type
112
+ if rtn_type := get_type_hints(func).get("return"):
113
+ if get_origin(rtn_type) is Literal:
114
+ path_map_ = {name: name for name in get_args(rtn_type)}
115
+ except Exception:
116
+ pass
117
+ # infer input schema
118
+ input_schema = _get_branch_path_input_schema(path) if infer_schema else None
119
+ # create branch
120
+ return cls(path=path, ends=path_map_, input_schema=input_schema)
121
+
122
+ def run(
123
+ self,
124
+ writer: _Writer,
125
+ reader: Callable[[RunnableConfig], Any] | None = None,
126
+ ) -> RunnableCallable:
127
+ return ChannelWrite.register_writer(
128
+ RunnableCallable(
129
+ func=self._route,
130
+ afunc=self._aroute,
131
+ writer=writer,
132
+ reader=reader,
133
+ name=None,
134
+ trace=False,
135
+ ),
136
+ list(
137
+ zip_longest(
138
+ writer([e for e in self.ends.values()], True),
139
+ [str(la) for la, e in self.ends.items()],
140
+ )
141
+ )
142
+ if self.ends
143
+ else None,
144
+ )
145
+
146
+ def _route(
147
+ self,
148
+ input: Any,
149
+ config: RunnableConfig,
150
+ *,
151
+ reader: Callable[[RunnableConfig], Any] | None,
152
+ writer: _Writer,
153
+ ) -> Runnable:
154
+ if reader:
155
+ value = reader(config)
156
+ # passthrough additional keys from node to branch
157
+ # only doable when using dict states
158
+ if (
159
+ isinstance(value, dict)
160
+ and isinstance(input, dict)
161
+ and self.input_schema is None
162
+ ):
163
+ value = {**input, **value}
164
+ else:
165
+ value = input
166
+ result = self.path.invoke(value, config)
167
+ return self._finish(writer, input, result, config)
168
+
169
+ async def _aroute(
170
+ self,
171
+ input: Any,
172
+ config: RunnableConfig,
173
+ *,
174
+ reader: Callable[[RunnableConfig], Any] | None,
175
+ writer: _Writer,
176
+ ) -> Runnable:
177
+ if reader:
178
+ value = reader(config)
179
+ # passthrough additional keys from node to branch
180
+ # only doable when using dict states
181
+ if (
182
+ isinstance(value, dict)
183
+ and isinstance(input, dict)
184
+ and self.input_schema is None
185
+ ):
186
+ value = {**input, **value}
187
+ else:
188
+ value = input
189
+ result = await self.path.ainvoke(value, config)
190
+ return self._finish(writer, input, result, config)
191
+
192
+ def _finish(
193
+ self,
194
+ writer: _Writer,
195
+ input: Any,
196
+ result: Any,
197
+ config: RunnableConfig,
198
+ ) -> Runnable | Any:
199
+ if not isinstance(result, (list, tuple)):
200
+ result = [result]
201
+ if self.ends:
202
+ destinations: Sequence[Send | str] = [
203
+ r if isinstance(r, Send) else self.ends[r] for r in result
204
+ ]
205
+ else:
206
+ destinations = cast(Sequence[Send | str], result)
207
+ if any(dest is None or dest == START for dest in destinations):
208
+ raise ValueError("Branch did not return a valid destination")
209
+ if any(p.node == END for p in destinations if isinstance(p, Send)):
210
+ raise InvalidUpdateError("Cannot send a packet to the END node")
211
+ entries = writer(destinations, False)
212
+ if not entries:
213
+ return input
214
+ else:
215
+ need_passthrough = False
216
+ for e in entries:
217
+ if isinstance(e, ChannelWriteEntry):
218
+ if e.value is PASSTHROUGH:
219
+ need_passthrough = True
220
+ break
221
+ if need_passthrough:
222
+ return ChannelWrite(entries)
223
+ else:
224
+ ChannelWrite.do_write(config, entries)
225
+ return input
@@ -0,0 +1,103 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from dataclasses import dataclass
5
+ from typing import Any, Generic, Protocol, TypeAlias
6
+
7
+ from coco_core.runnables import Runnable, RunnableConfig
8
+ from coco_graph.store.base import BaseStore
9
+
10
+ from coco_graph._internal._typing import EMPTY_SEQ
11
+ from coco_graph.runtime import Runtime
12
+ from coco_graph.types import (
13
+ CachePolicy,
14
+ RetryPolicy,
15
+ StreamWriter,
16
+ TimeoutPolicy,
17
+ TracePolicy,
18
+ )
19
+ from coco_graph.typing import ContextT, NodeInputT, NodeInputT_contra
20
+
21
+
22
+ class _Node(Protocol[NodeInputT_contra]):
23
+ def __call__(self, state: NodeInputT_contra) -> Any: ...
24
+
25
+
26
+ class _NodeWithConfig(Protocol[NodeInputT_contra]):
27
+ def __call__(self, state: NodeInputT_contra, config: RunnableConfig) -> Any: ...
28
+
29
+
30
+ class _NodeWithWriter(Protocol[NodeInputT_contra]):
31
+ def __call__(self, state: NodeInputT_contra, *, writer: StreamWriter) -> Any: ...
32
+
33
+
34
+ class _NodeWithStore(Protocol[NodeInputT_contra]):
35
+ def __call__(self, state: NodeInputT_contra, *, store: BaseStore) -> Any: ...
36
+
37
+
38
+ class _NodeWithWriterStore(Protocol[NodeInputT_contra]):
39
+ def __call__(
40
+ self, state: NodeInputT_contra, *, writer: StreamWriter, store: BaseStore
41
+ ) -> Any: ...
42
+
43
+
44
+ class _NodeWithConfigWriter(Protocol[NodeInputT_contra]):
45
+ def __call__(
46
+ self, state: NodeInputT_contra, *, config: RunnableConfig, writer: StreamWriter
47
+ ) -> Any: ...
48
+
49
+
50
+ class _NodeWithConfigStore(Protocol[NodeInputT_contra]):
51
+ def __call__(
52
+ self, state: NodeInputT_contra, *, config: RunnableConfig, store: BaseStore
53
+ ) -> Any: ...
54
+
55
+
56
+ class _NodeWithConfigWriterStore(Protocol[NodeInputT_contra]):
57
+ def __call__(
58
+ self,
59
+ state: NodeInputT_contra,
60
+ *,
61
+ config: RunnableConfig,
62
+ writer: StreamWriter,
63
+ store: BaseStore,
64
+ ) -> Any: ...
65
+
66
+
67
+ class _NodeWithRuntime(Protocol[NodeInputT_contra, ContextT]):
68
+ def __call__(
69
+ self, state: NodeInputT_contra, *, runtime: Runtime[ContextT]
70
+ ) -> Any: ...
71
+
72
+
73
+ # TODO: we probably don't want to explicitly support the config / store signatures once
74
+ # we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec
75
+ # this is purely for typing purposes though, so can easily change in the coming weeks.
76
+ StateNode: TypeAlias = (
77
+ _Node[NodeInputT]
78
+ | _NodeWithConfig[NodeInputT]
79
+ | _NodeWithWriter[NodeInputT]
80
+ | _NodeWithStore[NodeInputT]
81
+ | _NodeWithWriterStore[NodeInputT]
82
+ | _NodeWithConfigWriter[NodeInputT]
83
+ | _NodeWithConfigStore[NodeInputT]
84
+ | _NodeWithConfigWriterStore[NodeInputT]
85
+ | _NodeWithRuntime[NodeInputT, ContextT]
86
+ | Runnable[NodeInputT, Any]
87
+ )
88
+
89
+
90
+ @dataclass(slots=True)
91
+ class StateNodeSpec(Generic[NodeInputT, ContextT]):
92
+ runnable: StateNode[NodeInputT, ContextT]
93
+ metadata: dict[str, Any] | None
94
+ input_schema: type[NodeInputT]
95
+ retry_policy: RetryPolicy | Sequence[RetryPolicy] | None
96
+ cache_policy: CachePolicy | None
97
+ is_error_handler: bool = False
98
+ error_handler_node: str | None = None
99
+ ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ
100
+ defer: bool = False
101
+ timeout: TimeoutPolicy | None = None
102
+ trace_policy: TracePolicy | None = None
103
+ """Optional policy controlling what this node records on its trace run."""