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,1488 @@
1
+ """Standard, multimodal content blocks for Large Language Model I/O.
2
+
3
+ This module provides standardized data structures for representing inputs to and outputs
4
+ from LLMs. The core abstraction is the **Content Block**, a `TypedDict`.
5
+
6
+ **Rationale**
7
+
8
+ Different LLM providers use distinct and incompatible API schemas. This module provides
9
+ a unified, provider-agnostic format to facilitate these interactions. A message to or
10
+ from a model is simply a list of content blocks, allowing for the natural interleaving
11
+ of text, images, and other content in a single ordered sequence.
12
+
13
+ An adapter for a specific provider is responsible for translating this standard list of
14
+ blocks into the format required by its API.
15
+
16
+ **Extensibility**
17
+
18
+ Data **not yet mapped** to a standard block may be represented using the
19
+ `NonStandardContentBlock`, which allows for provider-specific data to be included
20
+ without losing the benefits of type checking and validation.
21
+
22
+ Furthermore, provider-specific fields **within** a standard block are fully supported
23
+ by default in the `extras` field of each block. This allows for additional metadata
24
+ to be included without breaking the standard structure. For example, Google's thought
25
+ signature:
26
+
27
+ ```python
28
+ AIMessage(
29
+ content=[
30
+ {
31
+ "type": "text",
32
+ "text": "J'adore la programmation.",
33
+ "extras": {"signature": "EpoWCpc..."}, # Thought signature
34
+ }
35
+ ], ...
36
+ )
37
+ ```
38
+
39
+
40
+ !!! note
41
+
42
+ Following widespread adoption of [PEP 728](https://peps.python.org/pep-0728/), we
43
+ intend to add `extra_items=Any` as a param to Content Blocks. This will signify to
44
+ type checkers that additional provider-specific fields are allowed outside of the
45
+ `extras` field, and that will become the new standard approach to adding
46
+ provider-specific metadata.
47
+
48
+ ??? note
49
+
50
+ **Example with PEP 728 provider-specific fields:**
51
+
52
+ ```python
53
+ # Content block definition
54
+ # NOTE: `extra_items=Any`
55
+ class TextContentBlock(TypedDict, extra_items=Any):
56
+ type: Literal["text"]
57
+ id: NotRequired[str]
58
+ text: str
59
+ annotations: NotRequired[list[Annotation]]
60
+ index: NotRequired[int]
61
+ ```
62
+
63
+ ```python
64
+ from coco_core.messages.content import TextContentBlock
65
+
66
+ # Create a text content block with provider-specific fields
67
+ my_block: TextContentBlock = {
68
+ # Add required fields
69
+ "type": "text",
70
+ "text": "Hello, world!",
71
+ # Additional fields not specified in the TypedDict
72
+ # These are valid with PEP 728 and are typed as Any
73
+ "openai_metadata": {"model": "gpt-5.5", "temperature": 0.7},
74
+ "anthropic_usage": {"input_tokens": 10, "output_tokens": 20},
75
+ "custom_field": "any value",
76
+ }
77
+
78
+ # Mutating an existing block to add provider-specific fields
79
+ openai_data = my_block["openai_metadata"] # Type: Any
80
+ ```
81
+
82
+ **Example Usage**
83
+
84
+ ```python
85
+ # Direct construction
86
+ from coco_core.messages.content import TextContentBlock, ImageContentBlock
87
+
88
+ multimodal_message: AIMessage(
89
+ content_blocks=[
90
+ TextContentBlock(type="text", text="What is shown in this image?"),
91
+ ImageContentBlock(
92
+ type="image",
93
+ url="https://www.langchain.com/images/brand/langchain_logo_text_w_white.png",
94
+ mime_type="image/png",
95
+ ),
96
+ ]
97
+ )
98
+
99
+ # Using factories
100
+ from coco_core.messages.content import create_text_block, create_image_block
101
+
102
+ multimodal_message: AIMessage(
103
+ content=[
104
+ create_text_block("What is shown in this image?"),
105
+ create_image_block(
106
+ url="https://www.langchain.com/images/brand/langchain_logo_text_w_white.png",
107
+ mime_type="image/png",
108
+ ),
109
+ ]
110
+ )
111
+ ```
112
+
113
+ Factory functions offer benefits such as:
114
+
115
+ - Automatic ID generation (when not provided)
116
+ - No need to manually specify the `type` field
117
+ """
118
+
119
+ from typing import Any, Literal, get_args, get_type_hints
120
+
121
+ from typing_extensions import NotRequired, TypedDict
122
+
123
+ from coco_core.utils.utils import ensure_id
124
+
125
+
126
+ class Citation(TypedDict):
127
+ """Annotation for citing data from a document.
128
+
129
+ !!! note
130
+
131
+ `start`/`end` indices refer to the **response text**,
132
+ not the source text. This means that the indices are relative to the model's
133
+ response, not the original document (as specified in the `url`).
134
+
135
+ !!! note "Factory function"
136
+
137
+ `create_citation` may also be used as a factory to create a `Citation`.
138
+ Benefits include:
139
+
140
+ * Automatic ID generation (when not provided)
141
+ * Required arguments strictly validated at creation time
142
+ """
143
+
144
+ type: Literal["citation"]
145
+ """Type of the content block. Used for discrimination."""
146
+
147
+ id: NotRequired[str]
148
+ """Unique identifier for this content block.
149
+
150
+ Either:
151
+
152
+ - Generated by the provider
153
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
154
+ """
155
+
156
+ url: NotRequired[str]
157
+ """URL of the document source."""
158
+
159
+ title: NotRequired[str]
160
+ """Source document title.
161
+
162
+ For example, the page title for a web page or the title of a paper.
163
+ """
164
+
165
+ start_index: NotRequired[int]
166
+ """Start index of the **response text** (`TextContentBlock.text`)."""
167
+
168
+ end_index: NotRequired[int]
169
+ """End index of the **response text** (`TextContentBlock.text`)"""
170
+
171
+ cited_text: NotRequired[str]
172
+ """Excerpt of source text being cited."""
173
+
174
+ # NOTE: not including spans for the raw document text (such as `text_start_index`
175
+ # and `text_end_index`) as this is not currently supported by any provider. The
176
+ # thinking is that the `cited_text` should be sufficient for most use cases, and it
177
+ # is difficult to reliably extract spans from the raw document text across file
178
+ # formats or encoding schemes.
179
+
180
+ extras: NotRequired[dict[str, Any]]
181
+ """Provider-specific metadata."""
182
+
183
+
184
+ class NonStandardAnnotation(TypedDict):
185
+ """Provider-specific annotation format."""
186
+
187
+ type: Literal["non_standard_annotation"]
188
+ """Type of the content block. Used for discrimination."""
189
+
190
+ id: NotRequired[str]
191
+ """Unique identifier for this content block.
192
+
193
+ Either:
194
+
195
+ - Generated by the provider
196
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
197
+ """
198
+
199
+ value: dict[str, Any]
200
+ """Provider-specific annotation data."""
201
+
202
+
203
+ Annotation = Citation | NonStandardAnnotation
204
+ """A union of all defined `Annotation` types."""
205
+
206
+
207
+ class TextContentBlock(TypedDict):
208
+ """Text output from a LLM.
209
+
210
+ This typically represents the main text content of a message, such as the response
211
+ from a language model or the text of a user message.
212
+
213
+ !!! note "Factory function"
214
+
215
+ `create_text_block` may also be used as a factory to create a
216
+ `TextContentBlock`. Benefits include:
217
+
218
+ * Automatic ID generation (when not provided)
219
+ * Required arguments strictly validated at creation time
220
+ """
221
+
222
+ type: Literal["text"]
223
+ """Type of the content block. Used for discrimination."""
224
+
225
+ id: NotRequired[str]
226
+ """Unique identifier for this content block.
227
+
228
+ Either:
229
+
230
+ - Generated by the provider
231
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
232
+ """
233
+
234
+ text: str
235
+ """Block text."""
236
+
237
+ annotations: NotRequired[list[Annotation]]
238
+ """`Citation`s and other annotations."""
239
+
240
+ index: NotRequired[int | str]
241
+ """Index of block in aggregate response. Used during streaming."""
242
+
243
+ extras: NotRequired[dict[str, Any]]
244
+ """Provider-specific metadata."""
245
+
246
+
247
+ class ToolCall(TypedDict):
248
+ """Represents an AI's request to call a tool.
249
+
250
+ Example:
251
+ ```python
252
+ {"name": "foo", "args": {"a": 1}, "id": "123"}
253
+ ```
254
+
255
+ This represents a request to call the tool named "foo" with arguments {"a": 1}
256
+ and an identifier of "123".
257
+
258
+ !!! note "Factory function"
259
+
260
+ `create_tool_call` may also be used as a factory to create a
261
+ `ToolCall`. Benefits include:
262
+
263
+ * Automatic ID generation (when not provided)
264
+ * Required arguments strictly validated at creation time
265
+ """
266
+
267
+ type: Literal["tool_call"]
268
+ """Used for discrimination."""
269
+
270
+ id: str | None
271
+ """An identifier associated with the tool call.
272
+
273
+ An identifier is needed to associate a tool call request with a tool
274
+ call result in events when multiple concurrent tool calls are made.
275
+ """
276
+ # TODO: Consider making this NotRequired[str] in the future.
277
+
278
+ name: str
279
+ """The name of the tool to be called."""
280
+
281
+ args: dict[str, Any]
282
+ """The arguments to the tool call."""
283
+
284
+ index: NotRequired[int | str]
285
+ """Index of block in aggregate response. Used during streaming."""
286
+
287
+ extras: NotRequired[dict[str, Any]]
288
+ """Provider-specific metadata."""
289
+
290
+
291
+ class ToolCallChunk(TypedDict):
292
+ """A chunk of a tool call (yielded when streaming).
293
+
294
+ When merging `ToolCallChunks` (e.g., via `AIMessageChunk.__add__`),
295
+ all string attributes are concatenated. Chunks are only merged if their
296
+ values of `index` are equal and not `None`.
297
+
298
+ Example:
299
+ ```python
300
+ left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)]
301
+ right_chunks = [ToolCallChunk(name=None, args="1}", index=0)]
302
+
303
+ (
304
+ AIMessageChunk(content="", tool_call_chunks=left_chunks)
305
+ + AIMessageChunk(content="", tool_call_chunks=right_chunks)
306
+ ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)]
307
+ ```
308
+ """
309
+
310
+ # TODO: Consider making fields NotRequired[str] in the future.
311
+
312
+ type: Literal["tool_call_chunk"]
313
+ """Used for serialization."""
314
+
315
+ id: str | None
316
+ """An identifier associated with the tool call.
317
+
318
+ An identifier is needed to associate a tool call request with a tool
319
+ call result in events when multiple concurrent tool calls are made.
320
+ """
321
+ # TODO: Consider making this NotRequired[str] in the future.
322
+
323
+ name: str | None
324
+ """The name of the tool to be called."""
325
+
326
+ args: str | None
327
+ """The arguments to the tool call."""
328
+
329
+ index: NotRequired[int | str]
330
+ """The index of the tool call in a sequence."""
331
+
332
+ extras: NotRequired[dict[str, Any]]
333
+ """Provider-specific metadata."""
334
+
335
+
336
+ class InvalidToolCall(TypedDict):
337
+ """Allowance for errors made by LLM.
338
+
339
+ Here we add an `error` key to surface errors made during generation
340
+ (e.g., invalid JSON arguments.)
341
+ """
342
+
343
+ # TODO: Consider making fields NotRequired[str] in the future.
344
+
345
+ type: Literal["invalid_tool_call"]
346
+ """Used for discrimination."""
347
+
348
+ id: str | None
349
+ """An identifier associated with the tool call.
350
+
351
+ An identifier is needed to associate a tool call request with a tool
352
+ call result in events when multiple concurrent tool calls are made.
353
+ """
354
+ # TODO: Consider making this NotRequired[str] in the future.
355
+
356
+ name: str | None
357
+ """The name of the tool to be called."""
358
+
359
+ args: str | None
360
+ """The arguments to the tool call."""
361
+
362
+ error: str | None
363
+ """An error message associated with the tool call."""
364
+
365
+ index: NotRequired[int | str]
366
+ """Index of block in aggregate response. Used during streaming."""
367
+
368
+ extras: NotRequired[dict[str, Any]]
369
+ """Provider-specific metadata."""
370
+
371
+
372
+ class ServerToolCall(TypedDict):
373
+ """Tool call that is executed server-side.
374
+
375
+ For example: code execution, web search, etc.
376
+ """
377
+
378
+ type: Literal["server_tool_call"]
379
+ """Used for discrimination."""
380
+
381
+ id: str
382
+ """An identifier associated with the tool call."""
383
+
384
+ name: str
385
+ """The name of the tool to be called."""
386
+
387
+ args: dict[str, Any]
388
+ """The arguments to the tool call."""
389
+
390
+ index: NotRequired[int | str]
391
+ """Index of block in aggregate response. Used during streaming."""
392
+
393
+ extras: NotRequired[dict[str, Any]]
394
+ """Provider-specific metadata."""
395
+
396
+
397
+ class ServerToolCallChunk(TypedDict):
398
+ """A chunk of a server-side tool call (yielded when streaming)."""
399
+
400
+ type: Literal["server_tool_call_chunk"]
401
+ """Used for discrimination."""
402
+
403
+ name: NotRequired[str]
404
+ """The name of the tool to be called."""
405
+
406
+ args: NotRequired[str]
407
+ """JSON substring of the arguments to the tool call."""
408
+
409
+ id: NotRequired[str]
410
+ """Unique identifier for this server tool call chunk.
411
+
412
+ Either:
413
+
414
+ - Generated by the provider
415
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
416
+ """
417
+
418
+ index: NotRequired[int | str]
419
+ """Index of block in aggregate response. Used during streaming."""
420
+
421
+ extras: NotRequired[dict[str, Any]]
422
+ """Provider-specific metadata."""
423
+
424
+
425
+ class ServerToolResult(TypedDict):
426
+ """Result of a server-side tool call."""
427
+
428
+ type: Literal["server_tool_result"]
429
+ """Used for discrimination."""
430
+
431
+ id: NotRequired[str]
432
+ """Unique identifier for this server tool result.
433
+
434
+ Either:
435
+
436
+ - Generated by the provider
437
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
438
+ """
439
+
440
+ tool_call_id: str
441
+ """ID of the corresponding server tool call."""
442
+
443
+ status: Literal["success", "error"]
444
+ """Execution status of the server-side tool."""
445
+
446
+ output: NotRequired[Any]
447
+ """Output of the executed tool."""
448
+
449
+ index: NotRequired[int | str]
450
+ """Index of block in aggregate response. Used during streaming."""
451
+
452
+ extras: NotRequired[dict[str, Any]]
453
+ """Provider-specific metadata."""
454
+
455
+
456
+ class ReasoningContentBlock(TypedDict):
457
+ """Reasoning output from a LLM.
458
+
459
+ !!! note "Factory function"
460
+
461
+ `create_reasoning_block` may also be used as a factory to create a
462
+ `ReasoningContentBlock`. Benefits include:
463
+
464
+ * Automatic ID generation (when not provided)
465
+ * Required arguments strictly validated at creation time
466
+ """
467
+
468
+ type: Literal["reasoning"]
469
+ """Type of the content block. Used for discrimination."""
470
+
471
+ id: NotRequired[str]
472
+ """Unique identifier for this content block.
473
+
474
+ Either:
475
+
476
+ - Generated by the provider
477
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
478
+ """
479
+
480
+ reasoning: NotRequired[str]
481
+ """Reasoning text.
482
+
483
+ Either the thought summary or the raw reasoning text itself.
484
+
485
+ Often parsed from `<think>` tags in the model's response.
486
+ """
487
+
488
+ index: NotRequired[int | str]
489
+ """Index of block in aggregate response. Used during streaming."""
490
+
491
+ extras: NotRequired[dict[str, Any]]
492
+ """Provider-specific metadata."""
493
+
494
+
495
+ # Note: `title` and `context` are fields that could be used to provide additional
496
+ # information about the file, such as a description or summary of its content.
497
+ # E.g. with Claude, you can provide a context for a file which is passed to the model.
498
+ class ImageContentBlock(TypedDict):
499
+ """Image data.
500
+
501
+ !!! note "Factory function"
502
+
503
+ `create_image_block` may also be used as a factory to create an
504
+ `ImageContentBlock`. Benefits include:
505
+
506
+ * Automatic ID generation (when not provided)
507
+ * Required arguments strictly validated at creation time
508
+ """
509
+
510
+ type: Literal["image"]
511
+ """Type of the content block. Used for discrimination."""
512
+
513
+ id: NotRequired[str]
514
+ """Unique identifier for this content block.
515
+
516
+ Either:
517
+
518
+ - Generated by the provider
519
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
520
+ """
521
+
522
+ file_id: NotRequired[str]
523
+ """Reference to the image in an external file storage system.
524
+
525
+ For example, OpenAI or Anthropic's Files API.
526
+ """
527
+
528
+ mime_type: NotRequired[str]
529
+ """MIME type of the image.
530
+
531
+ Required for base64 data.
532
+
533
+ [Examples from IANA](https://www.iana.org/assignments/media-types/media-types.xhtml#image)
534
+ """
535
+
536
+ index: NotRequired[int | str]
537
+ """Index of block in aggregate response. Used during streaming."""
538
+
539
+ url: NotRequired[str]
540
+ """URL of the image."""
541
+
542
+ base64: NotRequired[str]
543
+ """Data as a base64 string."""
544
+
545
+ extras: NotRequired[dict[str, Any]]
546
+ """Provider-specific metadata. This shouldn't be used for the image data itself."""
547
+
548
+
549
+ class VideoContentBlock(TypedDict):
550
+ """Video data.
551
+
552
+ !!! note "Factory function"
553
+
554
+ `create_video_block` may also be used as a factory to create a
555
+ `VideoContentBlock`. Benefits include:
556
+
557
+ * Automatic ID generation (when not provided)
558
+ * Required arguments strictly validated at creation time
559
+ """
560
+
561
+ type: Literal["video"]
562
+ """Type of the content block. Used for discrimination."""
563
+
564
+ id: NotRequired[str]
565
+ """Unique identifier for this content block.
566
+
567
+ Either:
568
+
569
+ - Generated by the provider
570
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
571
+ """
572
+
573
+ file_id: NotRequired[str]
574
+ """Reference to the video in an external file storage system.
575
+
576
+ For example, OpenAI or Anthropic's Files API.
577
+ """
578
+
579
+ mime_type: NotRequired[str]
580
+ """MIME type of the video.
581
+
582
+ Required for base64 data.
583
+
584
+ [Examples from IANA](https://www.iana.org/assignments/media-types/media-types.xhtml#video)
585
+ """
586
+
587
+ index: NotRequired[int | str]
588
+ """Index of block in aggregate response. Used during streaming."""
589
+
590
+ url: NotRequired[str]
591
+ """URL of the video."""
592
+
593
+ base64: NotRequired[str]
594
+ """Data as a base64 string."""
595
+
596
+ extras: NotRequired[dict[str, Any]]
597
+ """Provider-specific metadata. This shouldn't be used for the video data itself."""
598
+
599
+
600
+ class AudioContentBlock(TypedDict):
601
+ """Audio data.
602
+
603
+ !!! note "Factory function"
604
+
605
+ `create_audio_block` may also be used as a factory to create an
606
+ `AudioContentBlock`. Benefits include:
607
+
608
+ * Automatic ID generation (when not provided)
609
+ * Required arguments strictly validated at creation time
610
+ """
611
+
612
+ type: Literal["audio"]
613
+ """Type of the content block. Used for discrimination."""
614
+
615
+ id: NotRequired[str]
616
+ """Unique identifier for this content block.
617
+
618
+ Either:
619
+
620
+ - Generated by the provider
621
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
622
+ """
623
+
624
+ file_id: NotRequired[str]
625
+ """Reference to the audio file in an external file storage system.
626
+
627
+ For example, OpenAI or Anthropic's Files API.
628
+ """
629
+
630
+ mime_type: NotRequired[str]
631
+ """MIME type of the audio.
632
+
633
+ Required for base64 data.
634
+
635
+ [Examples from IANA](https://www.iana.org/assignments/media-types/media-types.xhtml#audio)
636
+ """
637
+
638
+ index: NotRequired[int | str]
639
+ """Index of block in aggregate response. Used during streaming."""
640
+
641
+ url: NotRequired[str]
642
+ """URL of the audio."""
643
+
644
+ base64: NotRequired[str]
645
+ """Data as a base64 string."""
646
+
647
+ extras: NotRequired[dict[str, Any]]
648
+ """Provider-specific metadata. This shouldn't be used for the audio data itself."""
649
+
650
+
651
+ class PlainTextContentBlock(TypedDict):
652
+ """Plaintext data (e.g., from a `.txt` or `.md` document).
653
+
654
+ !!! note
655
+
656
+ A `PlainTextContentBlock` existed in `trustcoco-core<1.0.0`. Although the
657
+ name has carried over, the structure has changed significantly. The only shared
658
+ keys between the old and new versions are `type` and `text`, though the
659
+ `type` value has changed from `'text'` to `'text-plain'`.
660
+
661
+ !!! note
662
+
663
+ Title and context are optional fields that may be passed to the model. See
664
+ Anthropic [example](https://platform.claude.com/docs/en/build-with-claude/citations#citable-vs-non-citable-content).
665
+
666
+ !!! note "Factory function"
667
+
668
+ `create_plaintext_block` may also be used as a factory to create a
669
+ `PlainTextContentBlock`. Benefits include:
670
+
671
+ * Automatic ID generation (when not provided)
672
+ * Required arguments strictly validated at creation time
673
+ """
674
+
675
+ type: Literal["text-plain"]
676
+ """Type of the content block. Used for discrimination."""
677
+
678
+ id: NotRequired[str]
679
+ """Unique identifier for this content block.
680
+
681
+ Either:
682
+
683
+ - Generated by the provider
684
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
685
+ """
686
+
687
+ file_id: NotRequired[str]
688
+ """Reference to the plaintext file in an external file storage system.
689
+
690
+ For example, OpenAI or Anthropic's Files API.
691
+ """
692
+
693
+ mime_type: Literal["text/plain"]
694
+ """MIME type of the file.
695
+
696
+ Required for base64 data.
697
+ """
698
+
699
+ index: NotRequired[int | str]
700
+ """Index of block in aggregate response. Used during streaming."""
701
+
702
+ url: NotRequired[str]
703
+ """URL of the plaintext."""
704
+
705
+ base64: NotRequired[str]
706
+ """Data as a base64 string."""
707
+
708
+ text: NotRequired[str]
709
+ """Plaintext content. This is optional if the data is provided as base64."""
710
+
711
+ title: NotRequired[str]
712
+ """Title of the text data, e.g., the title of a document."""
713
+
714
+ context: NotRequired[str]
715
+ """Context for the text, e.g., a description or summary of the text's content."""
716
+
717
+ extras: NotRequired[dict[str, Any]]
718
+ """Provider-specific metadata. This shouldn't be used for the data itself."""
719
+
720
+
721
+ class FileContentBlock(TypedDict):
722
+ """File data that doesn't fit into other multimodal block types.
723
+
724
+ This block is intended for files that are not images, audio, or plaintext. For
725
+ example, it can be used for PDFs, Word documents, etc.
726
+
727
+ If the file is an image, audio, or plaintext, you should use the corresponding
728
+ content block type (e.g., `ImageContentBlock`, `AudioContentBlock`,
729
+ `PlainTextContentBlock`).
730
+
731
+ !!! note "Factory function"
732
+
733
+ `create_file_block` may also be used as a factory to create a
734
+ `FileContentBlock`. Benefits include:
735
+
736
+ * Automatic ID generation (when not provided)
737
+ * Required arguments strictly validated at creation time
738
+ """
739
+
740
+ type: Literal["file"]
741
+ """Type of the content block. Used for discrimination."""
742
+
743
+ id: NotRequired[str]
744
+ """Unique identifier for this content block.
745
+
746
+ Used for tracking and referencing specific blocks (e.g., during streaming).
747
+
748
+ Not to be confused with `file_id`, which references an external file in a
749
+ storage system.
750
+
751
+ Either:
752
+
753
+ - Generated by the provider
754
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
755
+ """
756
+
757
+ file_id: NotRequired[str]
758
+ """Reference to the file in an external file storage system.
759
+
760
+ For example, a file ID from OpenAI's Files API or another cloud storage provider.
761
+ This is distinct from `id`, which identifies the content block itself.
762
+ """
763
+
764
+ mime_type: NotRequired[str]
765
+ """MIME type of the file.
766
+
767
+ Required for base64 data.
768
+
769
+ [Examples from IANA](https://www.iana.org/assignments/media-types/media-types.xhtml)
770
+ """
771
+
772
+ index: NotRequired[int | str]
773
+ """Index of block in aggregate response. Used during streaming."""
774
+
775
+ url: NotRequired[str]
776
+ """URL of the file."""
777
+
778
+ base64: NotRequired[str]
779
+ """Data as a base64 string."""
780
+
781
+ extras: NotRequired[dict[str, Any]]
782
+ """Provider-specific metadata. This shouldn't be used for the file data itself."""
783
+
784
+
785
+ # Future modalities to consider:
786
+ # - 3D models
787
+ # - Tabular data
788
+
789
+
790
+ class NonStandardContentBlock(TypedDict):
791
+ """Provider-specific content data.
792
+
793
+ This block contains data for which there is not yet a standard type.
794
+
795
+ The purpose of this block should be to simply hold a provider-specific payload.
796
+ If a provider's non-standard output includes reasoning and tool calls, it should be
797
+ the adapter's job to parse that payload and emit the corresponding standard
798
+ `ReasoningContentBlock` and `ToolCalls`.
799
+
800
+ Has no `extras` field, as provider-specific data should be included in the
801
+ `value` field.
802
+
803
+ !!! note "Factory function"
804
+
805
+ `create_non_standard_block` may also be used as a factory to create a
806
+ `NonStandardContentBlock`. Benefits include:
807
+
808
+ * Automatic ID generation (when not provided)
809
+ * Required arguments strictly validated at creation time
810
+ """
811
+
812
+ type: Literal["non_standard"]
813
+ """Type of the content block. Used for discrimination."""
814
+
815
+ id: NotRequired[str]
816
+ """Unique identifier for this content block.
817
+
818
+ Either:
819
+
820
+ - Generated by the provider
821
+ - Generated by Coco upon creation (`UUID4` prefixed with `'lc_'`))
822
+ """
823
+
824
+ value: dict[str, Any]
825
+ """Provider-specific content data."""
826
+
827
+ index: NotRequired[int | str]
828
+ """Index of block in aggregate response. Used during streaming."""
829
+
830
+
831
+ # --- Aliases ---
832
+ DataContentBlock = (
833
+ ImageContentBlock
834
+ | VideoContentBlock
835
+ | AudioContentBlock
836
+ | PlainTextContentBlock
837
+ | FileContentBlock
838
+ )
839
+ """A union of all defined multimodal data `ContentBlock` types."""
840
+
841
+ ToolContentBlock = (
842
+ ToolCall | ToolCallChunk | ServerToolCall | ServerToolCallChunk | ServerToolResult
843
+ )
844
+
845
+ ContentBlock = (
846
+ TextContentBlock
847
+ | InvalidToolCall
848
+ | ReasoningContentBlock
849
+ | NonStandardContentBlock
850
+ | DataContentBlock
851
+ | ToolContentBlock
852
+ )
853
+ """A union of all defined `ContentBlock` types and aliases."""
854
+
855
+
856
+ KNOWN_BLOCK_TYPES = {
857
+ # Text output
858
+ "text",
859
+ "reasoning",
860
+ # Tools
861
+ "tool_call",
862
+ "invalid_tool_call",
863
+ "tool_call_chunk",
864
+ # Multimodal data
865
+ "image",
866
+ "audio",
867
+ "file",
868
+ "text-plain",
869
+ "video",
870
+ # Server-side tool calls
871
+ "server_tool_call",
872
+ "server_tool_call_chunk",
873
+ "server_tool_result",
874
+ # Catch-all
875
+ "non_standard",
876
+ # citation and non_standard_annotation intentionally omitted
877
+ }
878
+ """These are block types known to `trustcoco-core >= 1.0.0`.
879
+
880
+ If a block has a type not in this set, it is considered to be provider-specific.
881
+ """
882
+
883
+
884
+ def _get_data_content_block_types() -> tuple[str, ...]:
885
+ """Get type literals from DataContentBlock union members dynamically.
886
+
887
+ Example: ("image", "video", "audio", "text-plain", "file")
888
+
889
+ Note that old style multimodal blocks type literals with new style blocks.
890
+ Specifically, "image", "audio", and "file".
891
+
892
+ See the docstring of `_normalize_messages` in `language_models._utils` for details.
893
+ """
894
+ data_block_types = []
895
+
896
+ for block_type in get_args(DataContentBlock):
897
+ hints = get_type_hints(block_type)
898
+ if "type" in hints:
899
+ type_annotation = hints["type"]
900
+ if hasattr(type_annotation, "__args__"):
901
+ # This is a Literal type, get the literal value
902
+ literal_value = type_annotation.__args__[0]
903
+ data_block_types.append(literal_value)
904
+
905
+ return tuple(data_block_types)
906
+
907
+
908
+ def is_data_content_block(block: dict[str, Any]) -> bool:
909
+ """Check if the provided content block is a data content block.
910
+
911
+ Returns True for both v0 (old-style) and v1 (new-style) multimodal data blocks.
912
+
913
+ Args:
914
+ block: The content block to check.
915
+
916
+ Returns:
917
+ `True` if the content block is a data content block, `False` otherwise.
918
+ """
919
+ if block.get("type") not in _get_data_content_block_types():
920
+ return False
921
+
922
+ if any(key in block for key in ("url", "base64", "file_id", "text")):
923
+ # Type is valid and at least one data field is present
924
+ # (Accepts old-style image and audio URLContentBlock)
925
+
926
+ # 'text' is checked to support v0 PlainTextContentBlock types
927
+ # We must guard against new style TextContentBlock which also has 'text' `type`
928
+ # by ensuring the presence of `source_type`
929
+ if block["type"] == "text" and "source_type" not in block: # noqa: SIM103 # This is more readable
930
+ return False
931
+
932
+ return True
933
+
934
+ if "source_type" in block:
935
+ # Old-style content blocks had possible types of 'image', 'audio', and 'file'
936
+ # which is not captured in the prior check
937
+ source_type = block["source_type"]
938
+ if (source_type == "url" and "url" in block) or (
939
+ source_type == "base64" and "data" in block
940
+ ):
941
+ return True
942
+ if (source_type == "id" and "id" in block) or (
943
+ source_type == "text" and "url" in block
944
+ ):
945
+ return True
946
+
947
+ return False
948
+
949
+
950
+ def create_text_block(
951
+ text: str,
952
+ *,
953
+ id: str | None = None,
954
+ annotations: list[Annotation] | None = None,
955
+ index: int | str | None = None,
956
+ **kwargs: Any,
957
+ ) -> TextContentBlock:
958
+ """Create a `TextContentBlock`.
959
+
960
+ Args:
961
+ text: The text content of the block.
962
+ id: Content block identifier.
963
+
964
+ Generated automatically if not provided.
965
+ annotations: `Citation`s and other annotations for the text.
966
+ index: Index of block in aggregate response.
967
+
968
+ Used during streaming.
969
+
970
+ Returns:
971
+ A properly formatted `TextContentBlock`.
972
+
973
+ !!! note
974
+
975
+ The `id` is generated automatically if not provided, using a UUID4 format
976
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
977
+ """
978
+ block = TextContentBlock(
979
+ type="text",
980
+ text=text,
981
+ id=ensure_id(id),
982
+ )
983
+ if annotations is not None:
984
+ block["annotations"] = annotations
985
+ if index is not None:
986
+ block["index"] = index
987
+
988
+ extras = {k: v for k, v in kwargs.items() if v is not None}
989
+ if extras:
990
+ block["extras"] = extras
991
+
992
+ return block
993
+
994
+
995
+ def create_image_block(
996
+ *,
997
+ url: str | None = None,
998
+ base64: str | None = None,
999
+ file_id: str | None = None,
1000
+ mime_type: str | None = None,
1001
+ id: str | None = None,
1002
+ index: int | str | None = None,
1003
+ **kwargs: Any,
1004
+ ) -> ImageContentBlock:
1005
+ """Create an `ImageContentBlock`.
1006
+
1007
+ Args:
1008
+ url: URL of the image.
1009
+ base64: Base64-encoded image data.
1010
+ file_id: ID of the image file from a file storage system.
1011
+ mime_type: MIME type of the image.
1012
+
1013
+ Required for base64 data.
1014
+ id: Content block identifier.
1015
+
1016
+ Generated automatically if not provided.
1017
+ index: Index of block in aggregate response.
1018
+
1019
+ Used during streaming.
1020
+
1021
+ Returns:
1022
+ A properly formatted `ImageContentBlock`.
1023
+
1024
+ Raises:
1025
+ ValueError: If no image source is provided or if `base64` is used without
1026
+ `mime_type`.
1027
+
1028
+ !!! note
1029
+
1030
+ The `id` is generated automatically if not provided, using a UUID4 format
1031
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1032
+ """
1033
+ if not any([url, base64, file_id]):
1034
+ msg = "Must provide one of: url, base64, or file_id"
1035
+ raise ValueError(msg)
1036
+
1037
+ block = ImageContentBlock(type="image", id=ensure_id(id))
1038
+
1039
+ if url is not None:
1040
+ block["url"] = url
1041
+ if base64 is not None:
1042
+ block["base64"] = base64
1043
+ if file_id is not None:
1044
+ block["file_id"] = file_id
1045
+ if mime_type is not None:
1046
+ block["mime_type"] = mime_type
1047
+ if index is not None:
1048
+ block["index"] = index
1049
+
1050
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1051
+ if extras:
1052
+ block["extras"] = extras
1053
+
1054
+ return block
1055
+
1056
+
1057
+ def create_video_block(
1058
+ *,
1059
+ url: str | None = None,
1060
+ base64: str | None = None,
1061
+ file_id: str | None = None,
1062
+ mime_type: str | None = None,
1063
+ id: str | None = None,
1064
+ index: int | str | None = None,
1065
+ **kwargs: Any,
1066
+ ) -> VideoContentBlock:
1067
+ """Create a `VideoContentBlock`.
1068
+
1069
+ Args:
1070
+ url: URL of the video.
1071
+ base64: Base64-encoded video data.
1072
+ file_id: ID of the video file from a file storage system.
1073
+ mime_type: MIME type of the video.
1074
+
1075
+ Required for base64 data.
1076
+ id: Content block identifier.
1077
+
1078
+ Generated automatically if not provided.
1079
+ index: Index of block in aggregate response.
1080
+
1081
+ Used during streaming.
1082
+
1083
+ Returns:
1084
+ A properly formatted `VideoContentBlock`.
1085
+
1086
+ Raises:
1087
+ ValueError: If no video source is provided or if `base64` is used without
1088
+ `mime_type`.
1089
+
1090
+ !!! note
1091
+
1092
+ The `id` is generated automatically if not provided, using a UUID4 format
1093
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1094
+ """
1095
+ if not any([url, base64, file_id]):
1096
+ msg = "Must provide one of: url, base64, or file_id"
1097
+ raise ValueError(msg)
1098
+
1099
+ if base64 and not mime_type:
1100
+ msg = "mime_type is required when using base64 data"
1101
+ raise ValueError(msg)
1102
+
1103
+ block = VideoContentBlock(type="video", id=ensure_id(id))
1104
+
1105
+ if url is not None:
1106
+ block["url"] = url
1107
+ if base64 is not None:
1108
+ block["base64"] = base64
1109
+ if file_id is not None:
1110
+ block["file_id"] = file_id
1111
+ if mime_type is not None:
1112
+ block["mime_type"] = mime_type
1113
+ if index is not None:
1114
+ block["index"] = index
1115
+
1116
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1117
+ if extras:
1118
+ block["extras"] = extras
1119
+
1120
+ return block
1121
+
1122
+
1123
+ def create_audio_block(
1124
+ *,
1125
+ url: str | None = None,
1126
+ base64: str | None = None,
1127
+ file_id: str | None = None,
1128
+ mime_type: str | None = None,
1129
+ id: str | None = None,
1130
+ index: int | str | None = None,
1131
+ **kwargs: Any,
1132
+ ) -> AudioContentBlock:
1133
+ """Create an `AudioContentBlock`.
1134
+
1135
+ Args:
1136
+ url: URL of the audio.
1137
+ base64: Base64-encoded audio data.
1138
+ file_id: ID of the audio file from a file storage system.
1139
+ mime_type: MIME type of the audio.
1140
+
1141
+ Required for base64 data.
1142
+ id: Content block identifier.
1143
+
1144
+ Generated automatically if not provided.
1145
+ index: Index of block in aggregate response.
1146
+
1147
+ Used during streaming.
1148
+
1149
+ Returns:
1150
+ A properly formatted `AudioContentBlock`.
1151
+
1152
+ Raises:
1153
+ ValueError: If no audio source is provided or if `base64` is used without
1154
+ `mime_type`.
1155
+
1156
+ !!! note
1157
+
1158
+ The `id` is generated automatically if not provided, using a UUID4 format
1159
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1160
+ """
1161
+ if not any([url, base64, file_id]):
1162
+ msg = "Must provide one of: url, base64, or file_id"
1163
+ raise ValueError(msg)
1164
+
1165
+ if base64 and not mime_type:
1166
+ msg = "mime_type is required when using base64 data"
1167
+ raise ValueError(msg)
1168
+
1169
+ block = AudioContentBlock(type="audio", id=ensure_id(id))
1170
+
1171
+ if url is not None:
1172
+ block["url"] = url
1173
+ if base64 is not None:
1174
+ block["base64"] = base64
1175
+ if file_id is not None:
1176
+ block["file_id"] = file_id
1177
+ if mime_type is not None:
1178
+ block["mime_type"] = mime_type
1179
+ if index is not None:
1180
+ block["index"] = index
1181
+
1182
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1183
+ if extras:
1184
+ block["extras"] = extras
1185
+
1186
+ return block
1187
+
1188
+
1189
+ def create_file_block(
1190
+ *,
1191
+ url: str | None = None,
1192
+ base64: str | None = None,
1193
+ file_id: str | None = None,
1194
+ mime_type: str | None = None,
1195
+ id: str | None = None,
1196
+ index: int | str | None = None,
1197
+ **kwargs: Any,
1198
+ ) -> FileContentBlock:
1199
+ """Create a `FileContentBlock`.
1200
+
1201
+ Args:
1202
+ url: URL of the file.
1203
+ base64: Base64-encoded file data.
1204
+ file_id: ID of the file from a file storage system.
1205
+ mime_type: MIME type of the file.
1206
+
1207
+ Required for base64 data.
1208
+ id: Content block identifier.
1209
+
1210
+ Generated automatically if not provided.
1211
+ index: Index of block in aggregate response.
1212
+
1213
+ Used during streaming.
1214
+
1215
+ Returns:
1216
+ A properly formatted `FileContentBlock`.
1217
+
1218
+ Raises:
1219
+ ValueError: If no file source is provided or if `base64` is used without
1220
+ `mime_type`.
1221
+
1222
+ !!! note
1223
+
1224
+ The `id` is generated automatically if not provided, using a UUID4 format
1225
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1226
+ """
1227
+ if not any([url, base64, file_id]):
1228
+ msg = "Must provide one of: url, base64, or file_id"
1229
+ raise ValueError(msg)
1230
+
1231
+ if base64 and not mime_type:
1232
+ msg = "mime_type is required when using base64 data"
1233
+ raise ValueError(msg)
1234
+
1235
+ block = FileContentBlock(type="file", id=ensure_id(id))
1236
+
1237
+ if url is not None:
1238
+ block["url"] = url
1239
+ if base64 is not None:
1240
+ block["base64"] = base64
1241
+ if file_id is not None:
1242
+ block["file_id"] = file_id
1243
+ if mime_type is not None:
1244
+ block["mime_type"] = mime_type
1245
+ if index is not None:
1246
+ block["index"] = index
1247
+
1248
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1249
+ if extras:
1250
+ block["extras"] = extras
1251
+
1252
+ return block
1253
+
1254
+
1255
+ def create_plaintext_block(
1256
+ text: str | None = None,
1257
+ url: str | None = None,
1258
+ base64: str | None = None,
1259
+ file_id: str | None = None,
1260
+ title: str | None = None,
1261
+ context: str | None = None,
1262
+ id: str | None = None,
1263
+ index: int | str | None = None,
1264
+ **kwargs: Any,
1265
+ ) -> PlainTextContentBlock:
1266
+ """Create a `PlainTextContentBlock`.
1267
+
1268
+ Args:
1269
+ text: The plaintext content.
1270
+ url: URL of the plaintext file.
1271
+ base64: Base64-encoded plaintext data.
1272
+ file_id: ID of the plaintext file from a file storage system.
1273
+ title: Title of the text data.
1274
+ context: Context or description of the text content.
1275
+ id: Content block identifier.
1276
+
1277
+ Generated automatically if not provided.
1278
+ index: Index of block in aggregate response.
1279
+
1280
+ Used during streaming.
1281
+
1282
+ Returns:
1283
+ A properly formatted `PlainTextContentBlock`.
1284
+
1285
+ !!! note
1286
+
1287
+ The `id` is generated automatically if not provided, using a UUID4 format
1288
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1289
+ """
1290
+ block = PlainTextContentBlock(
1291
+ type="text-plain",
1292
+ mime_type="text/plain",
1293
+ id=ensure_id(id),
1294
+ )
1295
+
1296
+ if text is not None:
1297
+ block["text"] = text
1298
+ if url is not None:
1299
+ block["url"] = url
1300
+ if base64 is not None:
1301
+ block["base64"] = base64
1302
+ if file_id is not None:
1303
+ block["file_id"] = file_id
1304
+ if title is not None:
1305
+ block["title"] = title
1306
+ if context is not None:
1307
+ block["context"] = context
1308
+ if index is not None:
1309
+ block["index"] = index
1310
+
1311
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1312
+ if extras:
1313
+ block["extras"] = extras
1314
+
1315
+ return block
1316
+
1317
+
1318
+ def create_tool_call(
1319
+ name: str,
1320
+ args: dict[str, Any],
1321
+ *,
1322
+ id: str | None = None,
1323
+ index: int | str | None = None,
1324
+ **kwargs: Any,
1325
+ ) -> ToolCall:
1326
+ """Create a `ToolCall`.
1327
+
1328
+ Args:
1329
+ name: The name of the tool to be called.
1330
+ args: The arguments to the tool call.
1331
+ id: An identifier for the tool call.
1332
+
1333
+ Generated automatically if not provided.
1334
+ index: Index of block in aggregate response.
1335
+
1336
+ Used during streaming.
1337
+
1338
+ Returns:
1339
+ A properly formatted `ToolCall`.
1340
+
1341
+ !!! note
1342
+
1343
+ The `id` is generated automatically if not provided, using a UUID4 format
1344
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1345
+ """
1346
+ block = ToolCall(
1347
+ type="tool_call",
1348
+ name=name,
1349
+ args=args,
1350
+ id=ensure_id(id),
1351
+ )
1352
+
1353
+ if index is not None:
1354
+ block["index"] = index
1355
+
1356
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1357
+ if extras:
1358
+ block["extras"] = extras
1359
+
1360
+ return block
1361
+
1362
+
1363
+ def create_reasoning_block(
1364
+ reasoning: str | None = None,
1365
+ id: str | None = None,
1366
+ index: int | str | None = None,
1367
+ **kwargs: Any,
1368
+ ) -> ReasoningContentBlock:
1369
+ """Create a `ReasoningContentBlock`.
1370
+
1371
+ Args:
1372
+ reasoning: The reasoning text or thought summary.
1373
+ id: Content block identifier.
1374
+
1375
+ Generated automatically if not provided.
1376
+ index: Index of block in aggregate response.
1377
+
1378
+ Used during streaming.
1379
+
1380
+ Returns:
1381
+ A properly formatted `ReasoningContentBlock`.
1382
+
1383
+ !!! note
1384
+
1385
+ The `id` is generated automatically if not provided, using a UUID4 format
1386
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1387
+ """
1388
+ block = ReasoningContentBlock(
1389
+ type="reasoning",
1390
+ reasoning=reasoning or "",
1391
+ id=ensure_id(id),
1392
+ )
1393
+
1394
+ if index is not None:
1395
+ block["index"] = index
1396
+
1397
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1398
+ if extras:
1399
+ block["extras"] = extras
1400
+
1401
+ return block
1402
+
1403
+
1404
+ def create_citation(
1405
+ *,
1406
+ url: str | None = None,
1407
+ title: str | None = None,
1408
+ start_index: int | None = None,
1409
+ end_index: int | None = None,
1410
+ cited_text: str | None = None,
1411
+ id: str | None = None,
1412
+ **kwargs: Any,
1413
+ ) -> Citation:
1414
+ """Create a `Citation`.
1415
+
1416
+ Args:
1417
+ url: URL of the document source.
1418
+ title: Source document title.
1419
+ start_index: Start index in the response text where citation applies.
1420
+ end_index: End index in the response text where citation applies.
1421
+ cited_text: Excerpt of source text being cited.
1422
+ id: Content block identifier.
1423
+
1424
+ Generated automatically if not provided.
1425
+
1426
+ Returns:
1427
+ A properly formatted `Citation`.
1428
+
1429
+ !!! note
1430
+
1431
+ The `id` is generated automatically if not provided, using a UUID4 format
1432
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1433
+ """
1434
+ block = Citation(type="citation", id=ensure_id(id))
1435
+
1436
+ if url is not None:
1437
+ block["url"] = url
1438
+ if title is not None:
1439
+ block["title"] = title
1440
+ if start_index is not None:
1441
+ block["start_index"] = start_index
1442
+ if end_index is not None:
1443
+ block["end_index"] = end_index
1444
+ if cited_text is not None:
1445
+ block["cited_text"] = cited_text
1446
+
1447
+ extras = {k: v for k, v in kwargs.items() if v is not None}
1448
+ if extras:
1449
+ block["extras"] = extras
1450
+
1451
+ return block
1452
+
1453
+
1454
+ def create_non_standard_block(
1455
+ value: dict[str, Any],
1456
+ *,
1457
+ id: str | None = None,
1458
+ index: int | str | None = None,
1459
+ ) -> NonStandardContentBlock:
1460
+ """Create a `NonStandardContentBlock`.
1461
+
1462
+ Args:
1463
+ value: Provider-specific content data.
1464
+ id: Content block identifier.
1465
+
1466
+ Generated automatically if not provided.
1467
+ index: Index of block in aggregate response.
1468
+
1469
+ Used during streaming.
1470
+
1471
+ Returns:
1472
+ A properly formatted `NonStandardContentBlock`.
1473
+
1474
+ !!! note
1475
+
1476
+ The `id` is generated automatically if not provided, using a UUID4 format
1477
+ prefixed with `'lc_'` to indicate it is a Coco-generated ID.
1478
+ """
1479
+ block = NonStandardContentBlock(
1480
+ type="non_standard",
1481
+ value=value,
1482
+ id=ensure_id(id),
1483
+ )
1484
+
1485
+ if index is not None:
1486
+ block["index"] = index
1487
+
1488
+ return block