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,200 @@
1
+ """Module implements a memory stream for communication between two co-routines.
2
+
3
+ This module provides a way to communicate between two co-routines using a memory
4
+ channel. The writer and reader can be in the same event loop or in different event
5
+ loops. When they're in different event loops, they will also be in different threads.
6
+
7
+ Useful in situations when there's a mix of synchronous and asynchronous used in the
8
+ code.
9
+ """
10
+
11
+ import asyncio
12
+ import contextlib
13
+ import weakref
14
+ from asyncio import AbstractEventLoop, Queue
15
+ from collections.abc import AsyncIterator
16
+ from typing import Any, Generic, TypeVar
17
+
18
+ T = TypeVar("T")
19
+
20
+
21
+ def _close_loop_quietly(loop: AbstractEventLoop) -> None:
22
+ """Close an event loop that was created internally, as a finalizer callback.
23
+
24
+ Errors are suppressed rather than logged. `weakref.finalize` defaults to
25
+ `atexit=True`, so this can run during interpreter shutdown, where `logging`
26
+ may already be torn down. Python routes finalizer exceptions to
27
+ `sys.unraisablehook` rather than propagating them, so suppressing here only
28
+ avoids dumping a traceback to stderr at a nondeterministic moment, which is
29
+ the same kind of noise this cleanup exists to remove. If `close()` does
30
+ fail, the loop's own `__del__` still emits
31
+ `ResourceWarning: unclosed event loop`, so the leak stays detectable.
32
+
33
+ Args:
34
+ loop: The event loop to close. A running loop is left alone.
35
+ """
36
+ if loop.is_running():
37
+ # `close()` would raise; leave the `ResourceWarning` as the signal.
38
+ return
39
+ with contextlib.suppress(Exception):
40
+ loop.close()
41
+
42
+
43
+ def _get_or_create_loop(owner: object) -> AbstractEventLoop:
44
+ """Get the current event loop, creating one if this thread has none.
45
+
46
+ Args:
47
+ owner: Object whose lifetime bounds a loop created here.
48
+
49
+ A loop created by this function is closed once `owner` is
50
+ garbage collected.
51
+
52
+ Returns:
53
+ The event loop to back a `_MemoryStream` with.
54
+ """
55
+ try:
56
+ return asyncio.get_event_loop()
57
+ except RuntimeError:
58
+ # No loop is set on this thread. That is always the case in a sync
59
+ # context on Python 3.14+, where `get_event_loop()` no longer creates
60
+ # one implicitly, and on any version in a non-main thread.
61
+ #
62
+ # This loop is ours: unlike one returned by `get_event_loop()`, no
63
+ # caller owns it, so close it once `owner` dies. Otherwise the loop's
64
+ # `__del__` emits `ResourceWarning: unclosed event loop` at whatever
65
+ # nondeterministic point GC happens to run.
66
+ loop = asyncio.new_event_loop()
67
+ weakref.finalize(owner, _close_loop_quietly, loop)
68
+ return loop
69
+
70
+
71
+ class _SendStream(Generic[T]):
72
+ def __init__(
73
+ self, reader_loop: AbstractEventLoop, queue: Queue[Any], done: object
74
+ ) -> None:
75
+ """Create a writer for the queue and done object.
76
+
77
+ Args:
78
+ reader_loop: The event loop to use for the writer.
79
+
80
+ This loop will be used to schedule the writes to the queue.
81
+ queue: The queue to write to.
82
+
83
+ This is an asyncio queue.
84
+ done: Special sentinel object to indicate that the writer is done.
85
+ """
86
+ self._reader_loop = reader_loop
87
+ self._queue = queue
88
+ self._done = done
89
+
90
+ async def send(self, item: T) -> None:
91
+ """Schedule the item to be written to the queue using the original loop.
92
+
93
+ This is a coroutine that can be awaited.
94
+
95
+ Args:
96
+ item: The item to write to the queue.
97
+ """
98
+ return self.send_nowait(item)
99
+
100
+ def send_nowait(self, item: T) -> None:
101
+ """Schedule the item to be written to the queue using the original loop.
102
+
103
+ This is a non-blocking call.
104
+
105
+ Args:
106
+ item: The item to write to the queue.
107
+
108
+ Raises:
109
+ RuntimeError: If the event loop is already closed when trying to write to
110
+ the queue.
111
+ """
112
+ try:
113
+ self._reader_loop.call_soon_threadsafe(self._queue.put_nowait, item)
114
+ except RuntimeError:
115
+ if not self._reader_loop.is_closed():
116
+ raise # Raise the exception if the loop is not closed
117
+
118
+ async def aclose(self) -> None:
119
+ """Async schedule the done object write the queue using the original loop."""
120
+ return self.close()
121
+
122
+ def close(self) -> None:
123
+ """Schedule the done object write the queue using the original loop.
124
+
125
+ This is a non-blocking call.
126
+
127
+ Raises:
128
+ RuntimeError: If the event loop is already closed when trying to write to
129
+ the queue.
130
+ """
131
+ try:
132
+ self._reader_loop.call_soon_threadsafe(self._queue.put_nowait, self._done)
133
+ except RuntimeError:
134
+ if not self._reader_loop.is_closed():
135
+ raise # Raise the exception if the loop is not closed
136
+
137
+
138
+ class _ReceiveStream(Generic[T]):
139
+ def __init__(self, queue: Queue[Any], done: object) -> None:
140
+ """Create a reader for the queue and done object.
141
+
142
+ This reader should be used in the same loop as the loop that was passed to the
143
+ channel.
144
+ """
145
+ self._queue = queue
146
+ self._done = done
147
+ self._is_closed = False
148
+
149
+ async def __aiter__(self) -> AsyncIterator[T]:
150
+ while True:
151
+ item = await self._queue.get()
152
+ if item is self._done:
153
+ self._is_closed = True
154
+ break
155
+ yield item
156
+
157
+
158
+ class _MemoryStream(Generic[T]):
159
+ """Stream data from a writer to a reader even if they are in different threads.
160
+
161
+ Uses asyncio queues to communicate between two co-routines. This implementation
162
+ should work even if the writer and reader co-routines belong to two different event
163
+ loops (e.g. one running from an event loop in the main thread and the other running
164
+ in an event loop in a background thread).
165
+
166
+ This implementation is meant to be used with a single writer and a single reader.
167
+
168
+ This is an internal implementation to Coco. Do not use it directly.
169
+ """
170
+
171
+ def __init__(self, loop: AbstractEventLoop) -> None:
172
+ """Create a channel for the given loop.
173
+
174
+ Args:
175
+ loop: The event loop to use for the channel.
176
+
177
+ The reader is assumed to be running in the same loop as the one passed
178
+ to this constructor. This will NOT be validated at run time.
179
+ """
180
+ self._loop = loop
181
+ self._queue = asyncio.Queue[Any](maxsize=0)
182
+ self._done = object()
183
+
184
+ def get_send_stream(self) -> _SendStream[T]:
185
+ """Get a writer for the channel.
186
+
187
+ Returns:
188
+ The writer for the channel.
189
+ """
190
+ return _SendStream[T](
191
+ reader_loop=self._loop, queue=self._queue, done=self._done
192
+ )
193
+
194
+ def get_receive_stream(self) -> _ReceiveStream[T]:
195
+ """Get a reader for the channel.
196
+
197
+ Returns:
198
+ The reader for the channel.
199
+ """
200
+ return _ReceiveStream[T](queue=self._queue, done=self._done)
@@ -0,0 +1,130 @@
1
+ """Tracers that call listeners."""
2
+
3
+ from collections.abc import Awaitable, Callable
4
+ from typing import TYPE_CHECKING
5
+
6
+ from coco_core.runnables.config import (
7
+ RunnableConfig,
8
+ acall_func_with_variable_args,
9
+ call_func_with_variable_args,
10
+ )
11
+ from coco_core.tracers.base import AsyncBaseTracer, BaseTracer
12
+ from coco_core.tracers.schemas import Run
13
+
14
+ if TYPE_CHECKING:
15
+ from uuid import UUID
16
+
17
+ Listener = Callable[[Run], None] | Callable[[Run, RunnableConfig], None]
18
+ AsyncListener = (
19
+ Callable[[Run], Awaitable[None]] | Callable[[Run, RunnableConfig], Awaitable[None]]
20
+ )
21
+
22
+
23
+ class RootListenersTracer(BaseTracer):
24
+ """Tracer that calls listeners on run start, end, and error."""
25
+
26
+ log_missing_parent = False
27
+ """Whether to log a warning if the parent is missing."""
28
+
29
+ def __init__(
30
+ self,
31
+ *,
32
+ config: RunnableConfig,
33
+ on_start: Listener | None,
34
+ on_end: Listener | None,
35
+ on_error: Listener | None,
36
+ ) -> None:
37
+ """Initialize the tracer.
38
+
39
+ Args:
40
+ config: The runnable config.
41
+ on_start: The listener to call on run start.
42
+ on_end: The listener to call on run end.
43
+ on_error: The listener to call on run error
44
+ """
45
+ super().__init__(_schema_format="original+chat")
46
+
47
+ self.config = config
48
+ self._arg_on_start = on_start
49
+ self._arg_on_end = on_end
50
+ self._arg_on_error = on_error
51
+ self.root_id: UUID | None = None
52
+
53
+ def _persist_run(self, run: Run) -> None:
54
+ # This is a legacy method only called once for an entire run tree
55
+ # therefore not useful here
56
+ pass
57
+
58
+ def _on_run_create(self, run: Run) -> None:
59
+ if self.root_id is not None:
60
+ return
61
+
62
+ self.root_id = run.id
63
+
64
+ if self._arg_on_start is not None:
65
+ call_func_with_variable_args(self._arg_on_start, run, self.config)
66
+
67
+ def _on_run_update(self, run: Run) -> None:
68
+ if run.id != self.root_id:
69
+ return
70
+
71
+ if run.error is None:
72
+ if self._arg_on_end is not None:
73
+ call_func_with_variable_args(self._arg_on_end, run, self.config)
74
+ elif self._arg_on_error is not None:
75
+ call_func_with_variable_args(self._arg_on_error, run, self.config)
76
+
77
+
78
+ class AsyncRootListenersTracer(AsyncBaseTracer):
79
+ """Async tracer that calls listeners on run start, end, and error."""
80
+
81
+ log_missing_parent = False
82
+ """Whether to log a warning if the parent is missing."""
83
+
84
+ def __init__(
85
+ self,
86
+ *,
87
+ config: RunnableConfig,
88
+ on_start: AsyncListener | None,
89
+ on_end: AsyncListener | None,
90
+ on_error: AsyncListener | None,
91
+ ) -> None:
92
+ """Initialize the tracer.
93
+
94
+ Args:
95
+ config: The runnable config.
96
+ on_start: The listener to call on run start.
97
+ on_end: The listener to call on run end.
98
+ on_error: The listener to call on run error
99
+ """
100
+ super().__init__(_schema_format="original+chat")
101
+
102
+ self.config = config
103
+ self._arg_on_start = on_start
104
+ self._arg_on_end = on_end
105
+ self._arg_on_error = on_error
106
+ self.root_id: UUID | None = None
107
+
108
+ async def _persist_run(self, run: Run) -> None:
109
+ # This is a legacy method only called once for an entire run tree
110
+ # therefore not useful here
111
+ pass
112
+
113
+ async def _on_run_create(self, run: Run) -> None:
114
+ if self.root_id is not None:
115
+ return
116
+
117
+ self.root_id = run.id
118
+
119
+ if self._arg_on_start is not None:
120
+ await acall_func_with_variable_args(self._arg_on_start, run, self.config)
121
+
122
+ async def _on_run_update(self, run: Run) -> None:
123
+ if run.id != self.root_id:
124
+ return
125
+
126
+ if run.error is None:
127
+ if self._arg_on_end is not None:
128
+ await acall_func_with_variable_args(self._arg_on_end, run, self.config)
129
+ elif self._arg_on_error is not None:
130
+ await acall_func_with_variable_args(self._arg_on_error, run, self.config)
@@ -0,0 +1,40 @@
1
+ """A tracer that collects all nested runs in a list."""
2
+
3
+ from typing import Any
4
+ from uuid import UUID
5
+
6
+ from coco_core.tracers._compat import run_copy
7
+ from coco_core.tracers.base import BaseTracer
8
+ from coco_core.tracers.schemas import Run
9
+
10
+
11
+ class RunCollectorCallbackHandler(BaseTracer):
12
+ """Tracer that collects all nested runs in a list.
13
+
14
+ This tracer is useful for inspection and evaluation purposes.
15
+ """
16
+
17
+ name: str = "run-collector_callback_handler"
18
+
19
+ def __init__(self, example_id: UUID | str | None = None, **kwargs: Any) -> None:
20
+ """Initialize the `RunCollectorCallbackHandler`.
21
+
22
+ Args:
23
+ example_id: The ID of the example being traced.
24
+ **kwargs: Additional keyword arguments.
25
+ """
26
+ super().__init__(**kwargs)
27
+ self.example_id = (
28
+ UUID(example_id) if isinstance(example_id, str) else example_id
29
+ )
30
+ self.traced_runs: list[Run] = []
31
+
32
+ def _persist_run(self, run: Run) -> None:
33
+ """Persist a run by adding it to the `traced_runs` list.
34
+
35
+ Args:
36
+ run: The run to be persisted.
37
+ """
38
+ run_ = run_copy(run)
39
+ run_.reference_example_id = self.example_id
40
+ self.traced_runs.append(run_)
@@ -0,0 +1,14 @@
1
+ """Schemas for tracers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from coco_core.tracers._context import RunTree
6
+
7
+ # Begin V2 API Schemas
8
+
9
+
10
+ Run = RunTree # For backwards compatibility
11
+
12
+ __all__ = [
13
+ "Run",
14
+ ]
@@ -0,0 +1,205 @@
1
+ """Tracers that print to the console."""
2
+
3
+ import json
4
+ from collections.abc import Callable
5
+ from typing import Any
6
+
7
+ from coco_core.tracers.base import BaseTracer
8
+ from coco_core.tracers.schemas import Run
9
+ from coco_core.utils.input import get_bolded_text, get_colored_text
10
+
11
+ MILLISECONDS_IN_SECOND = 1000
12
+
13
+
14
+ def try_json_stringify(obj: Any, fallback: str) -> str:
15
+ """Try to stringify an object to JSON.
16
+
17
+ Args:
18
+ obj: Object to stringify.
19
+ fallback: Fallback string to return if the object cannot be stringified.
20
+
21
+ Returns:
22
+ A JSON string if the object can be stringified, otherwise the fallback string.
23
+ """
24
+ try:
25
+ return json.dumps(obj, indent=2, ensure_ascii=False)
26
+ except Exception:
27
+ return fallback
28
+
29
+
30
+ def elapsed(run: Any) -> str:
31
+ """Get the elapsed time of a run.
32
+
33
+ Args:
34
+ run: any object with a `start_time` and `end_time` attribute.
35
+
36
+ Returns:
37
+ A string with the elapsed time in seconds or milliseconds if time is less than a
38
+ second.
39
+
40
+ """
41
+ elapsed_time = run.end_time - run.start_time
42
+ seconds = elapsed_time.total_seconds()
43
+ if seconds < 1:
44
+ return f"{seconds * MILLISECONDS_IN_SECOND:.0f}ms"
45
+ return f"{seconds:.2f}s"
46
+
47
+
48
+ class FunctionCallbackHandler(BaseTracer):
49
+ """Tracer that calls a function with a single str parameter."""
50
+
51
+ name: str = "function_callback_handler"
52
+ """The name of the tracer.
53
+
54
+ This is used to identify the tracer in the logs.
55
+ """
56
+
57
+ def __init__(self, function: Callable[[str], None], **kwargs: Any) -> None:
58
+ """Create a `FunctionCallbackHandler`.
59
+
60
+ Args:
61
+ function: The callback function to call.
62
+ """
63
+ super().__init__(**kwargs)
64
+ self.function_callback = function
65
+
66
+ def _persist_run(self, run: Run) -> None:
67
+ pass
68
+
69
+ def get_parents(self, run: Run) -> list[Run]:
70
+ """Get the parents of a run.
71
+
72
+ Args:
73
+ run: The run to get the parents of.
74
+
75
+ Returns:
76
+ A list of parent runs.
77
+ """
78
+ parents = []
79
+ current_run = run
80
+ while current_run.parent_run_id:
81
+ parent = self.run_map.get(str(current_run.parent_run_id))
82
+ if parent:
83
+ parents.append(parent)
84
+ current_run = parent
85
+ else:
86
+ break
87
+ return parents
88
+
89
+ def get_breadcrumbs(self, run: Run) -> str:
90
+ """Get the breadcrumbs of a run.
91
+
92
+ Args:
93
+ run: The run to get the breadcrumbs of.
94
+
95
+ Returns:
96
+ A string with the breadcrumbs of the run.
97
+ """
98
+ parents = self.get_parents(run)[::-1]
99
+ return " > ".join(
100
+ f"{parent.run_type}:{parent.name}"
101
+ for i, parent in enumerate([*parents, run])
102
+ )
103
+
104
+ # logging methods
105
+ def _on_chain_start(self, run: Run) -> None:
106
+ crumbs = self.get_breadcrumbs(run)
107
+ run_type = run.run_type.capitalize()
108
+ self.function_callback(
109
+ f"{get_colored_text('[chain/start]', color='green')} "
110
+ + get_bolded_text(f"[{crumbs}] Entering {run_type} run with input:\n")
111
+ + f"{try_json_stringify(run.inputs, '[inputs]')}"
112
+ )
113
+
114
+ def _on_chain_end(self, run: Run) -> None:
115
+ crumbs = self.get_breadcrumbs(run)
116
+ run_type = run.run_type.capitalize()
117
+ self.function_callback(
118
+ f"{get_colored_text('[chain/end]', color='blue')} "
119
+ + get_bolded_text(
120
+ f"[{crumbs}] [{elapsed(run)}] Exiting {run_type} run with output:\n"
121
+ )
122
+ + f"{try_json_stringify(run.outputs, '[outputs]')}"
123
+ )
124
+
125
+ def _on_chain_error(self, run: Run) -> None:
126
+ crumbs = self.get_breadcrumbs(run)
127
+ run_type = run.run_type.capitalize()
128
+ self.function_callback(
129
+ f"{get_colored_text('[chain/error]', color='red')} "
130
+ + get_bolded_text(
131
+ f"[{crumbs}] [{elapsed(run)}] {run_type} run errored with error:\n"
132
+ )
133
+ + f"{try_json_stringify(run.error, '[error]')}"
134
+ )
135
+
136
+ def _on_llm_start(self, run: Run) -> None:
137
+ crumbs = self.get_breadcrumbs(run)
138
+ inputs = (
139
+ {"prompts": [p.strip() for p in run.inputs["prompts"]]}
140
+ if "prompts" in run.inputs
141
+ else run.inputs
142
+ )
143
+ self.function_callback(
144
+ f"{get_colored_text('[llm/start]', color='green')} "
145
+ + get_bolded_text(f"[{crumbs}] Entering LLM run with input:\n")
146
+ + f"{try_json_stringify(inputs, '[inputs]')}"
147
+ )
148
+
149
+ def _on_llm_end(self, run: Run) -> None:
150
+ crumbs = self.get_breadcrumbs(run)
151
+ self.function_callback(
152
+ f"{get_colored_text('[llm/end]', color='blue')} "
153
+ + get_bolded_text(
154
+ f"[{crumbs}] [{elapsed(run)}] Exiting LLM run with output:\n"
155
+ )
156
+ + f"{try_json_stringify(run.outputs, '[response]')}"
157
+ )
158
+
159
+ def _on_llm_error(self, run: Run) -> None:
160
+ crumbs = self.get_breadcrumbs(run)
161
+ self.function_callback(
162
+ f"{get_colored_text('[llm/error]', color='red')} "
163
+ + get_bolded_text(
164
+ f"[{crumbs}] [{elapsed(run)}] LLM run errored with error:\n"
165
+ )
166
+ + f"{try_json_stringify(run.error, '[error]')}"
167
+ )
168
+
169
+ def _on_tool_start(self, run: Run) -> None:
170
+ crumbs = self.get_breadcrumbs(run)
171
+ self.function_callback(
172
+ f"{get_colored_text('[tool/start]', color='green')} "
173
+ + get_bolded_text(f"[{crumbs}] Entering Tool run with input:\n")
174
+ + f'"{run.inputs["input"].strip()}"'
175
+ )
176
+
177
+ def _on_tool_end(self, run: Run) -> None:
178
+ crumbs = self.get_breadcrumbs(run)
179
+ if run.outputs:
180
+ self.function_callback(
181
+ f"{get_colored_text('[tool/end]', color='blue')} "
182
+ + get_bolded_text(
183
+ f"[{crumbs}] [{elapsed(run)}] Exiting Tool run with output:\n"
184
+ )
185
+ + f'"{str(run.outputs["output"]).strip()}"'
186
+ )
187
+
188
+ def _on_tool_error(self, run: Run) -> None:
189
+ crumbs = self.get_breadcrumbs(run)
190
+ self.function_callback(
191
+ f"{get_colored_text('[tool/error]', color='red')} "
192
+ + get_bolded_text(f"[{crumbs}] [{elapsed(run)}] ")
193
+ + f"Tool run errored with error:\n"
194
+ f"{run.error}"
195
+ )
196
+
197
+
198
+ class ConsoleCallbackHandler(FunctionCallbackHandler):
199
+ """Tracer that prints to the console."""
200
+
201
+ name: str = "console_callback_handler"
202
+
203
+ def __init__(self, **kwargs: Any) -> None:
204
+ """Create a ConsoleCallbackHandler."""
205
+ super().__init__(function=print, **kwargs)
@@ -0,0 +1,111 @@
1
+ """Utility functions for Coco.
2
+
3
+ These functions do not depend on any other Coco module.
4
+ """
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ from coco_core._import_utils import import_attr
9
+
10
+ if TYPE_CHECKING:
11
+ # for type checking and IDE support, we include the imports here
12
+ # but we don't want to eagerly import them at runtime
13
+ from coco_core.utils import image
14
+ from coco_core.utils.aiter import abatch_iterate
15
+ from coco_core.utils.env import get_from_dict_or_env, get_from_env
16
+ from coco_core.utils.formatting import StrictFormatter, formatter
17
+ from coco_core.utils.input import (
18
+ get_bolded_text,
19
+ get_color_mapping,
20
+ get_colored_text,
21
+ print_text,
22
+ )
23
+ from coco_core.utils.iter import batch_iterate
24
+ from coco_core.utils.pydantic import pre_init
25
+ from coco_core.utils.strings import (
26
+ comma_list,
27
+ sanitize_for_postgres,
28
+ stringify_dict,
29
+ stringify_value,
30
+ )
31
+ from coco_core.utils.utils import (
32
+ build_extra_kwargs,
33
+ check_package_version,
34
+ convert_to_secret_str,
35
+ from_env,
36
+ get_pydantic_field_names,
37
+ guard_import,
38
+ mock_now,
39
+ raise_for_status_with_text,
40
+ secret_from_env,
41
+ xor_args,
42
+ )
43
+
44
+ __all__ = (
45
+ "StrictFormatter",
46
+ "abatch_iterate",
47
+ "batch_iterate",
48
+ "build_extra_kwargs",
49
+ "check_package_version",
50
+ "comma_list",
51
+ "convert_to_secret_str",
52
+ "formatter",
53
+ "from_env",
54
+ "get_bolded_text",
55
+ "get_color_mapping",
56
+ "get_colored_text",
57
+ "get_from_dict_or_env",
58
+ "get_from_env",
59
+ "get_pydantic_field_names",
60
+ "guard_import",
61
+ "image",
62
+ "mock_now",
63
+ "pre_init",
64
+ "print_text",
65
+ "raise_for_status_with_text",
66
+ "sanitize_for_postgres",
67
+ "secret_from_env",
68
+ "stringify_dict",
69
+ "stringify_value",
70
+ "xor_args",
71
+ )
72
+
73
+ _dynamic_imports = {
74
+ "image": "__module__",
75
+ "abatch_iterate": "aiter",
76
+ "get_from_dict_or_env": "env",
77
+ "get_from_env": "env",
78
+ "StrictFormatter": "formatting",
79
+ "formatter": "formatting",
80
+ "get_bolded_text": "input",
81
+ "get_color_mapping": "input",
82
+ "get_colored_text": "input",
83
+ "print_text": "input",
84
+ "batch_iterate": "iter",
85
+ "pre_init": "pydantic",
86
+ "comma_list": "strings",
87
+ "sanitize_for_postgres": "strings",
88
+ "stringify_dict": "strings",
89
+ "stringify_value": "strings",
90
+ "build_extra_kwargs": "utils",
91
+ "check_package_version": "utils",
92
+ "convert_to_secret_str": "utils",
93
+ "from_env": "utils",
94
+ "get_pydantic_field_names": "utils",
95
+ "guard_import": "utils",
96
+ "mock_now": "utils",
97
+ "secret_from_env": "utils",
98
+ "xor_args": "utils",
99
+ "raise_for_status_with_text": "utils",
100
+ }
101
+
102
+
103
+ def __getattr__(attr_name: str) -> object:
104
+ module_name = _dynamic_imports.get(attr_name)
105
+ result = import_attr(attr_name, module_name, __spec__.parent)
106
+ globals()[attr_name] = result
107
+ return result
108
+
109
+
110
+ def __dir__() -> list[str]:
111
+ return list(__all__)