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,337 @@
1
+ """The tracing context and run record the framework uses in place of a vendor client.
2
+
3
+ Upstream core imported these from its vendor's tracing client, which posts runs to a
4
+ hosted service. The framework keeps the same names and shapes so upstream code runs
5
+ unchanged, and nothing here leaves the process. Coco's record of what an agent did is
6
+ the receipt the SDK writes, not a trace.
7
+
8
+ `RunTree` keeps the fields, methods and printed form the tracers and their snapshot
9
+ tests use, with the same defaults, so a run built here compares equal to the one
10
+ upstream built. `coco_core.tracers.schemas.Run` is the same class.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import functools
16
+ import os
17
+ import secrets
18
+ import time
19
+ from collections.abc import Callable, Generator, Mapping, Sequence
20
+ from contextlib import contextmanager
21
+ from contextvars import ContextVar
22
+ from datetime import datetime, timezone
23
+ from typing import Any, TypeVar, cast
24
+ from uuid import UUID
25
+
26
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
27
+
28
+ F = TypeVar("F", bound=Callable[..., Any])
29
+
30
+ _PARENT_RUN_TREE: ContextVar[RunTree | None] = ContextVar("_PARENT_RUN_TREE", default=None)
31
+ _PROJECT_NAME: ContextVar[str | None] = ContextVar("_PROJECT_NAME", default=None)
32
+ _TAGS: ContextVar[list[str] | None] = ContextVar("_TAGS", default=None)
33
+ _METADATA: ContextVar[dict[str, Any] | None] = ContextVar("_METADATA", default=None)
34
+ _TRACING_ENABLED: ContextVar[bool | None] = ContextVar("_TRACING_ENABLED", default=None)
35
+ _CLIENT: ContextVar[Any] = ContextVar("_CLIENT", default=None)
36
+ _REPLICAS: ContextVar[Any] = ContextVar("_REPLICAS", default=None)
37
+ _DISTRIBUTED_PARENT_ID: ContextVar[str | None] = ContextVar("_DISTRIBUTED_PARENT_ID", default=None)
38
+
39
+ _CONTEXT_VARS: dict[str, ContextVar[Any]] = {
40
+ "parent": _PARENT_RUN_TREE,
41
+ "project_name": _PROJECT_NAME,
42
+ "tags": _TAGS,
43
+ "metadata": _METADATA,
44
+ "enabled": _TRACING_ENABLED,
45
+ "client": _CLIENT,
46
+ "replicas": _REPLICAS,
47
+ "distributed_parent_id": _DISTRIBUTED_PARENT_ID,
48
+ }
49
+
50
+
51
+ class Client:
52
+ """Stands where the vendor client stood in type hints. Never constructed here."""
53
+
54
+
55
+ def get_tracing_context(context: Any = None) -> dict[str, Any]:
56
+ """The current tracing context: parent run, project, tags, metadata, enabled."""
57
+ if context is None:
58
+ return {key: var.get() for key, var in _CONTEXT_VARS.items()}
59
+ return {key: context.get(var) for key, var in _CONTEXT_VARS.items()}
60
+
61
+
62
+ def _set_tracing_context(context: Mapping[str, Any] | None = None) -> None:
63
+ """Set the tracing context. `None` clears every key."""
64
+ if context is None:
65
+ for var in _CONTEXT_VARS.values():
66
+ var.set(None)
67
+ return
68
+ for key, value in context.items():
69
+ if key in _CONTEXT_VARS:
70
+ _CONTEXT_VARS[key].set(value)
71
+
72
+
73
+ @contextmanager
74
+ def tracing_context(**kwargs: Any) -> Generator[None, None, None]:
75
+ """Set tracing context keys for the duration of the block."""
76
+ previous = get_tracing_context()
77
+ _set_tracing_context({k: v for k, v in kwargs.items() if k in _CONTEXT_VARS})
78
+ try:
79
+ yield
80
+ finally:
81
+ _set_tracing_context(previous)
82
+
83
+
84
+ def traceable(*args: Any, **kwargs: Any) -> Any:
85
+ """Accepts the vendor decorator's arguments and returns the function unchanged."""
86
+ del kwargs
87
+ if len(args) == 1 and callable(args[0]):
88
+ return args[0]
89
+
90
+ def decorator(func: F) -> F:
91
+ return func
92
+
93
+ return decorator
94
+
95
+
96
+ @functools.lru_cache(maxsize=100)
97
+ def get_env_var(
98
+ name: str,
99
+ default: str | None = None,
100
+ *,
101
+ namespaces: tuple[str, ...] = ("COCO",),
102
+ ) -> str | None:
103
+ """The first of `COCO_<name>` (by default) that is set, else `default`."""
104
+ for namespace in namespaces:
105
+ value = os.environ.get(f"{namespace}_{name}")
106
+ if value is not None:
107
+ return value
108
+ return default
109
+
110
+
111
+ def tracing_is_enabled(ctx: Mapping[str, Any] | None = None) -> bool:
112
+ """Whether runs should be kept by a `CocoTracer`. Off unless asked for."""
113
+ context = ctx if ctx is not None else get_tracing_context()
114
+ if context.get("enabled") is not None:
115
+ return bool(context["enabled"])
116
+ return (get_env_var("TRACING_V2") or "").lower() == "true"
117
+
118
+
119
+ def get_tracer_project(return_default_value: bool = True) -> str | None: # noqa: FBT001, FBT002
120
+ """The project a run belongs to: `COCO_PROJECT`, else "default"."""
121
+ return os.environ.get("COCO_PROJECT") or ("default" if return_default_value else None)
122
+
123
+
124
+ def get_coco_env_var_metadata() -> dict[str, str]:
125
+ """Environment variables the vendor client copied into run metadata. None here."""
126
+ return {}
127
+
128
+
129
+ def uuid7() -> UUID:
130
+ """A time-ordered UUID (version 7), so runs sort by start."""
131
+ return _uuid7_from_ms(time.time_ns() // 1_000_000)
132
+
133
+
134
+ def uuid7_from_datetime(dt: datetime) -> UUID:
135
+ """A version 7 UUID whose time part is `dt`."""
136
+ return _uuid7_from_ms(int(dt.timestamp() * 1000))
137
+
138
+
139
+ def _uuid7_from_ms(ms: int) -> UUID:
140
+ rand = int.from_bytes(secrets.token_bytes(10), "big")
141
+ value = (ms & ((1 << 48) - 1)) << 80
142
+ value |= 0x7 << 76
143
+ value |= ((rand >> 62) & 0xFFF) << 64
144
+ value |= 0b10 << 62
145
+ value |= rand & ((1 << 62) - 1)
146
+ return UUID(int=value)
147
+
148
+
149
+ def _dotted_order(start_time: datetime, run_id: UUID) -> str:
150
+ return start_time.strftime("%Y%m%dT%H%M%S%fZ") + str(run_id)
151
+
152
+
153
+ class RunTree(BaseModel):
154
+ """One unit of work in a trace: a chain, model call, tool call or retriever call."""
155
+
156
+ id: UUID = Field(default_factory=uuid7)
157
+ name: str
158
+ start_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
159
+ run_type: str = "chain"
160
+ end_time: datetime | None = None
161
+ extra: dict = Field(default_factory=dict)
162
+ error: str | None = None
163
+ serialized: dict | None = None
164
+ events: list[dict] = Field(default_factory=list)
165
+ inputs: dict = Field(default_factory=dict)
166
+ outputs: dict | None = None
167
+ reference_example_id: UUID | None = None
168
+ parent_run_id: UUID | None = None
169
+ tags: list[str] | None = Field(default_factory=list)
170
+ attachments: dict = Field(default_factory=dict)
171
+ parent_run: RunTree | None = Field(default=None, exclude=True)
172
+ parent_dotted_order: str | None = Field(default=None, exclude=True)
173
+ child_runs: list[RunTree] = Field(default_factory=list, exclude=True)
174
+ session_name: str | None = Field(
175
+ default_factory=lambda: get_tracer_project() or "default", alias="project_name"
176
+ )
177
+ session_id: UUID | None = Field(default=None, alias="project_id")
178
+ ls_client: Any | None = Field(default=None, exclude=True)
179
+ dotted_order: str = ""
180
+ trace_id: UUID = Field(default=cast("UUID", ""))
181
+
182
+ model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True, extra="ignore")
183
+
184
+ @model_validator(mode="before")
185
+ @classmethod
186
+ def infer_defaults(cls, values: Any) -> Any:
187
+ """Fill the name, id, trace id and empty collections as upstream did."""
188
+ if not isinstance(values, dict):
189
+ return values
190
+ serialized = values.get("serialized")
191
+ if values.get("name") is None and serialized is not None:
192
+ if "name" in serialized:
193
+ values["name"] = serialized["name"]
194
+ elif "id" in serialized:
195
+ values["name"] = serialized["id"][-1]
196
+ if values.get("name") is None:
197
+ values["name"] = "Unnamed"
198
+ if "client" in values:
199
+ values["ls_client"] = values.pop("client")
200
+ elif "_client" in values:
201
+ values["ls_client"] = values.pop("_client")
202
+ parent_run = values.pop("parent_run", None)
203
+ if parent_run is not None:
204
+ values["parent_run_id"] = parent_run.id
205
+ values["parent_dotted_order"] = parent_run.dotted_order
206
+ if "id" not in values:
207
+ if values.get("start_time") is not None:
208
+ values["id"] = uuid7_from_datetime(values["start_time"])
209
+ else:
210
+ now = datetime.now(timezone.utc)
211
+ values["start_time"] = now
212
+ values["id"] = uuid7_from_datetime(now)
213
+ if "trace_id" not in values:
214
+ values["trace_id"] = parent_run.trace_id if parent_run is not None else values["id"]
215
+ values.setdefault("extra", {})
216
+ for key in ("events", "tags"):
217
+ if values.get(key) is None:
218
+ values[key] = []
219
+ if values.get("outputs") is None:
220
+ values["outputs"] = {}
221
+ for key in ("inputs", "outputs"):
222
+ value = values.get(key)
223
+ if value is not None and not isinstance(value, dict):
224
+ values[key] = {"output": value}
225
+ if values.get("attachments") is None:
226
+ values["attachments"] = {}
227
+ return values
228
+
229
+ @model_validator(mode="after")
230
+ def ensure_dotted_order(self) -> RunTree:
231
+ """Give the run its place in the tree when nothing set one."""
232
+ if self.dotted_order and self.dotted_order.strip():
233
+ return self
234
+ current = _dotted_order(self.start_time, self.id)
235
+ if self.parent_dotted_order is not None:
236
+ self.dotted_order = self.parent_dotted_order + "." + current
237
+ else:
238
+ self.dotted_order = current
239
+ return self
240
+
241
+ @property
242
+ def client(self) -> Any:
243
+ """Whatever the caller attached. Nothing is created here."""
244
+ return self.ls_client
245
+
246
+ @property
247
+ def metadata(self) -> dict[str, Any]:
248
+ """The run's metadata, stored under `extra["metadata"]`."""
249
+ if self.extra is None:
250
+ self.extra = {}
251
+ return self.extra.setdefault("metadata", {})
252
+
253
+ @property
254
+ def revision_id(self) -> UUID | None:
255
+ """The revision id from metadata, if any."""
256
+ return self.metadata.get("revision_id")
257
+
258
+ @property
259
+ def latency(self) -> float | None:
260
+ """Seconds from start to end, once ended."""
261
+ if self.end_time is None:
262
+ return None
263
+ return (self.end_time - self.start_time).total_seconds()
264
+
265
+ def add_tags(self, tags: Sequence[str] | str) -> None:
266
+ """Add tags to the run."""
267
+ if isinstance(tags, str):
268
+ tags = [tags]
269
+ if self.tags is None:
270
+ self.tags = []
271
+ self.tags.extend(tags)
272
+
273
+ def add_metadata(self, metadata: dict[str, Any]) -> None:
274
+ """Merge metadata into the run."""
275
+ self.metadata.update(metadata)
276
+
277
+ def add_outputs(self, outputs: dict[str, Any]) -> None:
278
+ """Merge outputs into the run."""
279
+ if self.outputs is None:
280
+ self.outputs = {}
281
+ self.outputs.update(outputs)
282
+
283
+ def add_inputs(self, inputs: dict[str, Any]) -> None:
284
+ """Merge inputs into the run."""
285
+ if self.inputs is None:
286
+ self.inputs = {}
287
+ self.inputs.update(inputs)
288
+
289
+ def add_event(self, events: Sequence[dict] | dict | str) -> None:
290
+ """Append one event, several, or a message recorded as an event."""
291
+ if self.events is None:
292
+ self.events = []
293
+ if isinstance(events, dict):
294
+ self.events.append(events)
295
+ elif isinstance(events, str):
296
+ self.events.append(
297
+ {"name": "event", "time": datetime.now(timezone.utc).isoformat(), "message": events}
298
+ )
299
+ else:
300
+ self.events.extend(events)
301
+
302
+ def end(
303
+ self,
304
+ *,
305
+ outputs: dict | None = None,
306
+ error: str | None = None,
307
+ end_time: datetime | None = None,
308
+ events: Sequence[dict] | None = None,
309
+ metadata: dict[str, Any] | None = None,
310
+ ) -> None:
311
+ """Close the run."""
312
+ self.end_time = end_time or datetime.now(timezone.utc)
313
+ if outputs is not None:
314
+ self.add_outputs(outputs)
315
+ if error is not None:
316
+ self.error = error
317
+ if events is not None:
318
+ self.add_event(list(events))
319
+ if metadata is not None:
320
+ self.add_metadata(metadata)
321
+
322
+ def create_child(self, name: str, run_type: str = "chain", **kwargs: Any) -> RunTree:
323
+ """A child run under this one."""
324
+ child = RunTree(name=name, run_type=run_type, parent_run=self, **kwargs)
325
+ self.child_runs.append(child)
326
+ return child
327
+
328
+ def __repr__(self) -> str:
329
+ return (
330
+ f"RunTree(id={self.id}, name='{self.name}', "
331
+ f"run_type='{self.run_type}', dotted_order='{self.dotted_order}')"
332
+ )
333
+
334
+
335
+ RunTree.model_rebuild()
336
+
337
+ Run = RunTree
@@ -0,0 +1,53 @@
1
+ """Internal tracers used for `stream_log` and `astream` events implementations."""
2
+
3
+ import typing
4
+ from collections.abc import AsyncIterator, Iterator
5
+ from uuid import UUID
6
+
7
+ T = typing.TypeVar("T")
8
+
9
+
10
+ # THIS IS USED IN COCO_GRAPH.
11
+ @typing.runtime_checkable
12
+ class _StreamingCallbackHandler(typing.Protocol[T]):
13
+ """Types for streaming callback handlers.
14
+
15
+ This is a common mixin that the callback handlers for both astream events and
16
+ astream log inherit from.
17
+
18
+ The `tap_output_aiter` method is invoked in some contexts to produce callbacks for
19
+ intermediate results.
20
+ """
21
+
22
+ def tap_output_aiter(
23
+ self, run_id: UUID, output: AsyncIterator[T]
24
+ ) -> AsyncIterator[T]:
25
+ """Used for internal astream_log and astream events implementations."""
26
+
27
+ def tap_output_iter(self, run_id: UUID, output: Iterator[T]) -> Iterator[T]:
28
+ """Used for internal astream_log and astream events implementations."""
29
+
30
+
31
+ # THIS IS USED IN COCO_GRAPH.
32
+ class _V2StreamingCallbackHandler:
33
+ """Marker base class for handlers that consume `on_stream_event` (v2).
34
+
35
+ A handler inheriting from this class signals that it wants content-
36
+ block lifecycle events from `stream_events(version="v3")` (and its
37
+ async equivalent) rather than the v1 `on_llm_new_token` chunks.
38
+ `BaseChatModel.invoke` uses
39
+ `isinstance(handler, _V2StreamingCallbackHandler)` to decide whether
40
+ to route an invoke through the v2 event generator.
41
+
42
+ Implemented as a concrete marker class (not a `Protocol`) so opt-in
43
+ is explicit via inheritance. An empty `runtime_checkable` Protocol
44
+ would match every object and misroute every call. The event
45
+ delivery contract itself lives on
46
+ `BaseCallbackHandler.on_stream_event`.
47
+ """
48
+
49
+
50
+ __all__ = [
51
+ "_StreamingCallbackHandler",
52
+ "_V2StreamingCallbackHandler",
53
+ ]