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,276 @@
1
+ """Anthropic prompt caching middleware.
2
+
3
+ Requires:
4
+ - `coco`: For agent middleware framework
5
+ - `trustcoco-anthropic`: For `ChatAnthropic` model (already a dependency)
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Awaitable, Callable
11
+ from typing import Any, Literal
12
+ from warnings import warn
13
+
14
+ from coco_core.messages import SystemMessage
15
+ from coco_core.tools import BaseTool
16
+
17
+ from coco_anthropic.chat_models import ChatAnthropic
18
+
19
+ try:
20
+ from coco.agents.middleware.types import (
21
+ AgentMiddleware,
22
+ ModelCallResult,
23
+ ModelRequest,
24
+ ModelResponse,
25
+ )
26
+ except ImportError as e:
27
+ msg = (
28
+ "AnthropicPromptCachingMiddleware requires 'coco' to be installed. "
29
+ "This middleware is designed for use with Coco agents. "
30
+ "Install it with: pip install coco"
31
+ )
32
+ raise ImportError(msg) from e
33
+
34
+
35
+ def _get_trace_policy() -> Any:
36
+ """Use tracing optimizations when supported by the installed Coco."""
37
+ try:
38
+ from coco.agents.middleware.types import TracePolicy, omit_payload
39
+ except ImportError:
40
+ return None
41
+ return TracePolicy(process_inputs=omit_payload)
42
+
43
+
44
+ _TRACE_POLICY = _get_trace_policy()
45
+
46
+
47
+ class AnthropicPromptCachingMiddleware(AgentMiddleware):
48
+ """Prompt Caching Middleware.
49
+
50
+ Optimizes API usage by caching conversation prefixes for Anthropic models.
51
+
52
+ Requires both `coco` and `trustcoco-anthropic` packages to be installed.
53
+
54
+ The middleware tags stable agent content and passes `cache_control` through
55
+ `model_settings`:
56
+
57
+ - **System message**: Tags the last content block of the system message
58
+ with `cache_control` so static system prompt content is cached.
59
+ - **Tools**: Tags the last tool definition with `cache_control`. Because
60
+ tool definitions are sent as one contiguous block, a single trailing
61
+ breakpoint caches the entire tool set across turns.
62
+ - **`model_settings`**: Passes `cache_control` to the chat model. The chat
63
+ model/provider then applies the correct message-tail and
64
+ provider-specific behavior at request time.
65
+
66
+ Learn more about Anthropic prompt caching
67
+ [here](https://platform.claude.com/docs/en/build-with-claude/prompt-caching).
68
+ """
69
+
70
+ trace_policy = _TRACE_POLICY
71
+
72
+ def __init__(
73
+ self,
74
+ type: Literal["ephemeral"] = "ephemeral", # noqa: A002
75
+ ttl: Literal["5m", "1h"] = "5m",
76
+ min_messages_to_cache: int = 0,
77
+ unsupported_model_behavior: Literal["ignore", "warn", "raise"] = "warn",
78
+ ) -> None:
79
+ """Initialize the middleware with cache control settings.
80
+
81
+ Args:
82
+ type: The type of cache to use, only `'ephemeral'` is supported.
83
+ ttl: The time to live for the cache, only `'5m'` and `'1h'` are
84
+ supported.
85
+ min_messages_to_cache: The minimum number of messages until the
86
+ cache is used.
87
+ unsupported_model_behavior: The behavior to take when an
88
+ unsupported model is used.
89
+
90
+ `'ignore'` will ignore the unsupported model and continue without
91
+ caching.
92
+
93
+ `'warn'` will warn the user and continue without caching.
94
+
95
+ `'raise'` will raise an error and stop the agent.
96
+ """
97
+ self.type = type
98
+ self.ttl = ttl
99
+ self.min_messages_to_cache = min_messages_to_cache
100
+ self.unsupported_model_behavior = unsupported_model_behavior
101
+
102
+ @property
103
+ def _cache_control(self) -> dict[str, str]:
104
+ return {"type": self.type, "ttl": self.ttl}
105
+
106
+ def _should_apply_caching(self, request: ModelRequest) -> bool:
107
+ """Check if caching should be applied to the request.
108
+
109
+ Args:
110
+ request: The model request to check.
111
+
112
+ Returns:
113
+ `True` if caching should be applied, `False` otherwise.
114
+
115
+ Raises:
116
+ ValueError: If model is unsupported and behavior is set to `'raise'`.
117
+ """
118
+ if not isinstance(request.model, ChatAnthropic):
119
+ msg = (
120
+ "AnthropicPromptCachingMiddleware caching middleware only supports "
121
+ f"Anthropic models, not instances of {type(request.model)}"
122
+ )
123
+ if self.unsupported_model_behavior == "raise":
124
+ raise ValueError(msg)
125
+ if self.unsupported_model_behavior == "warn":
126
+ warn(msg, stacklevel=3)
127
+ return False
128
+
129
+ messages_count = (
130
+ len(request.messages) + 1
131
+ if request.system_message
132
+ else len(request.messages)
133
+ )
134
+ return messages_count >= self.min_messages_to_cache
135
+
136
+ def _apply_caching(self, request: ModelRequest) -> ModelRequest:
137
+ """Apply cache control to system message, tools, and model settings.
138
+
139
+ Args:
140
+ request: The model request to modify.
141
+
142
+ Returns:
143
+ New request with cache control applied.
144
+ """
145
+ overrides: dict[str, Any] = {}
146
+ cache_control = self._cache_control
147
+
148
+ # Always set top-level `cache_control` on model settings. The Anthropic
149
+ # chat model translates the kwarg to the correct wire format for the
150
+ # active transport: direct API receives it as-is, while Bedrock has it
151
+ # expanded into a block-level breakpoint by `_get_request_payload`.
152
+ overrides["model_settings"] = {
153
+ **request.model_settings,
154
+ "cache_control": cache_control,
155
+ }
156
+
157
+ system_message = _tag_system_message(request.system_message, cache_control)
158
+ if system_message is not request.system_message:
159
+ overrides["system_message"] = system_message
160
+
161
+ tools = _tag_tools(request.tools, cache_control)
162
+ if tools is not request.tools:
163
+ overrides["tools"] = tools
164
+
165
+ return request.override(**overrides)
166
+
167
+ def wrap_model_call(
168
+ self,
169
+ request: ModelRequest,
170
+ handler: Callable[[ModelRequest], ModelResponse],
171
+ ) -> ModelCallResult:
172
+ """Modify the model request to add cache control blocks.
173
+
174
+ Args:
175
+ request: The model request to potentially modify.
176
+ handler: The handler to execute the model request.
177
+
178
+ Returns:
179
+ The model response from the handler.
180
+ """
181
+ if not self._should_apply_caching(request):
182
+ return handler(request)
183
+
184
+ return handler(self._apply_caching(request))
185
+
186
+ async def awrap_model_call(
187
+ self,
188
+ request: ModelRequest,
189
+ handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
190
+ ) -> ModelCallResult:
191
+ """Modify the model request to add cache control blocks (async version).
192
+
193
+ Args:
194
+ request: The model request to potentially modify.
195
+ handler: The async handler to execute the model request.
196
+
197
+ Returns:
198
+ The model response from the handler.
199
+ """
200
+ if not self._should_apply_caching(request):
201
+ return await handler(request)
202
+
203
+ return await handler(self._apply_caching(request))
204
+
205
+
206
+ def _tag_system_message(
207
+ system_message: Any,
208
+ cache_control: dict[str, str],
209
+ ) -> Any:
210
+ """Tag the last content block of a system message with cache_control.
211
+
212
+ Returns the original system_message unchanged if there are no blocks
213
+ to tag.
214
+
215
+ Args:
216
+ system_message: The system message to tag.
217
+ cache_control: The cache control dict to apply.
218
+
219
+ Returns:
220
+ A new SystemMessage with cache_control on the last block, or the
221
+ original if no modification was needed.
222
+ """
223
+ if system_message is None:
224
+ return system_message
225
+
226
+ content = system_message.content
227
+ if isinstance(content, str):
228
+ if not content:
229
+ return system_message
230
+ new_content: list[str | dict[str, Any]] = [
231
+ {"type": "text", "text": content, "cache_control": cache_control}
232
+ ]
233
+ elif isinstance(content, list):
234
+ if not content:
235
+ return system_message
236
+ new_content = list(content)
237
+ last = new_content[-1]
238
+ base = last if isinstance(last, dict) else {}
239
+ new_content[-1] = {**base, "cache_control": cache_control}
240
+ else:
241
+ return system_message
242
+
243
+ return SystemMessage(content=new_content)
244
+
245
+
246
+ def _tag_tools(
247
+ tools: list[Any] | None,
248
+ cache_control: dict[str, str],
249
+ ) -> list[Any] | None:
250
+ """Tag the last tool with cache_control via its extras dict.
251
+
252
+ Only the last tool is tagged to minimize the number of explicit cache
253
+ breakpoints (Anthropic limits these to 4 per request). Since tool
254
+ definitions are sent as a contiguous block, a single breakpoint on the
255
+ last tool caches the entire set.
256
+
257
+ Creates a copy of the last tool with cache_control added to extras,
258
+ without mutating the original.
259
+
260
+ Args:
261
+ tools: The list of tools to tag.
262
+ cache_control: The cache control dict to apply.
263
+
264
+ Returns:
265
+ A new list with cache_control on the last tool's extras, or the
266
+ original if no tools are present.
267
+ """
268
+ if not tools:
269
+ return tools
270
+
271
+ last = tools[-1]
272
+ if not isinstance(last, BaseTool):
273
+ return tools
274
+
275
+ new_extras = {**(last.extras or {}), "cache_control": cache_control}
276
+ return [*tools[:-1], last.model_copy(update={"extras": new_extras})]
@@ -0,0 +1,93 @@
1
+ """Output parsers for Anthropic tool calls."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, cast
6
+
7
+ from coco_core.messages import AIMessage, ToolCall
8
+ from coco_core.messages.tool import tool_call
9
+ from coco_core.output_parsers import BaseGenerationOutputParser
10
+ from coco_core.outputs import ChatGeneration, Generation
11
+ from pydantic import BaseModel, ConfigDict
12
+
13
+
14
+ class ToolsOutputParser(BaseGenerationOutputParser):
15
+ """Output parser for tool calls."""
16
+
17
+ first_tool_only: bool = False
18
+ """Whether to return only the first tool call."""
19
+ args_only: bool = False
20
+ """Whether to return only the arguments of the tool calls."""
21
+ pydantic_schemas: list[type[BaseModel]] | None = None
22
+ """Pydantic schemas to parse tool calls into."""
23
+
24
+ model_config = ConfigDict(
25
+ extra="forbid",
26
+ )
27
+
28
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
29
+ """Parse a list of candidate model Generations into a specific format.
30
+
31
+ Args:
32
+ result: A list of `Generation` to be parsed. The Generations are assumed
33
+ to be different candidate outputs for a single model input.
34
+ partial: (Not used) Whether the result is a partial result. If `True`, the
35
+ parser may return a partial result, which may not be complete or valid.
36
+
37
+ Returns:
38
+ Structured output.
39
+
40
+ """
41
+ if not result or not isinstance(result[0], ChatGeneration):
42
+ return None if self.first_tool_only else []
43
+ message = cast("AIMessage", result[0].message)
44
+ tool_calls: list = [
45
+ dict(tc) for tc in _extract_tool_calls_from_message(message)
46
+ ]
47
+ if isinstance(message.content, list):
48
+ # Map tool call id to index
49
+ id_to_index = {
50
+ block["id"]: i
51
+ for i, block in enumerate(message.content)
52
+ if isinstance(block, dict) and block["type"] == "tool_use"
53
+ }
54
+ tool_calls = [{**tc, "index": id_to_index[tc["id"]]} for tc in tool_calls]
55
+ if self.pydantic_schemas:
56
+ tool_calls = [self._pydantic_parse(tc) for tc in tool_calls]
57
+ elif self.args_only:
58
+ tool_calls = [tc["args"] for tc in tool_calls]
59
+ else:
60
+ pass
61
+
62
+ if self.first_tool_only:
63
+ return tool_calls[0] if tool_calls else None
64
+ return list(tool_calls)
65
+
66
+ def _pydantic_parse(self, tool_call: dict) -> BaseModel:
67
+ cls_ = {schema.__name__: schema for schema in self.pydantic_schemas or []}[
68
+ tool_call["name"]
69
+ ]
70
+ return cls_(**tool_call["args"])
71
+
72
+
73
+ def _extract_tool_calls_from_message(message: AIMessage) -> list[ToolCall]:
74
+ """Extract tool calls from a list of content blocks."""
75
+ if message.tool_calls:
76
+ return message.tool_calls
77
+ return extract_tool_calls(message.content)
78
+
79
+
80
+ def extract_tool_calls(content: str | list[str | dict[str, Any]]) -> list[ToolCall]:
81
+ """Extract tool calls from a list of content blocks."""
82
+ if isinstance(content, list):
83
+ tool_calls = []
84
+ for block in content:
85
+ if isinstance(block, str):
86
+ continue
87
+ if block["type"] != "tool_use":
88
+ continue
89
+ tool_calls.append(
90
+ tool_call(name=block["name"], args=block["input"], id=block["id"]),
91
+ )
92
+ return tool_calls
93
+ return []
File without changes
coco_core/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ """`trustcoco-core` defines the base abstractions for the Coco ecosystem.
2
+
3
+ The interfaces for core components like chat models, LLMs, vector stores, retrievers,
4
+ and more are defined here. The universal invocation protocol (Runnables) along with
5
+ a syntax for combining components are also defined here.
6
+
7
+ **No third-party integrations are defined here.** The dependencies are kept purposefully
8
+ very lightweight.
9
+ """
10
+
11
+ from coco_core._api import (
12
+ surface_coco_beta_warnings,
13
+ surface_coco_deprecation_warnings,
14
+ )
15
+ from coco_core.version import VERSION
16
+
17
+ __version__ = VERSION
18
+
19
+ surface_coco_deprecation_warnings()
20
+ surface_coco_beta_warnings()
@@ -0,0 +1,87 @@
1
+ """Helper functions for managing the Coco API.
2
+
3
+ This module is only relevant for Coco developers, not for users.
4
+
5
+ !!! warning
6
+
7
+ This module and its submodules are for internal use only. Do not use them in your
8
+ own code. We may change the API at any time with no warning.
9
+ """
10
+
11
+ from typing import TYPE_CHECKING
12
+
13
+ from coco_core._import_utils import import_attr
14
+
15
+ if TYPE_CHECKING:
16
+ from coco_core._api.beta_decorator import (
17
+ CocoBetaWarning,
18
+ beta,
19
+ suppress_coco_beta_warning,
20
+ surface_coco_beta_warnings,
21
+ )
22
+ from coco_core._api.deprecation import (
23
+ CocoDeprecationWarning,
24
+ deprecated,
25
+ suppress_coco_deprecation_warning,
26
+ surface_coco_deprecation_warnings,
27
+ warn_deprecated,
28
+ )
29
+ from coco_core._api.path import as_import_path, get_relative_path
30
+
31
+ __all__ = (
32
+ "CocoBetaWarning",
33
+ "CocoDeprecationWarning",
34
+ "as_import_path",
35
+ "beta",
36
+ "deprecated",
37
+ "get_relative_path",
38
+ "suppress_coco_beta_warning",
39
+ "suppress_coco_deprecation_warning",
40
+ "surface_coco_beta_warnings",
41
+ "surface_coco_deprecation_warnings",
42
+ "warn_deprecated",
43
+ )
44
+
45
+ _dynamic_imports = {
46
+ "CocoBetaWarning": "beta_decorator",
47
+ "beta": "beta_decorator",
48
+ "suppress_coco_beta_warning": "beta_decorator",
49
+ "surface_coco_beta_warnings": "beta_decorator",
50
+ "as_import_path": "path",
51
+ "get_relative_path": "path",
52
+ "CocoDeprecationWarning": "deprecation",
53
+ "deprecated": "deprecation",
54
+ "surface_coco_deprecation_warnings": "deprecation",
55
+ "suppress_coco_deprecation_warning": "deprecation",
56
+ "warn_deprecated": "deprecation",
57
+ }
58
+
59
+
60
+ def __getattr__(attr_name: str) -> object:
61
+ """Dynamically import and return an attribute from a submodule.
62
+
63
+ This function enables lazy loading of API functions from submodules, reducing
64
+ initial import time and circular dependency issues.
65
+
66
+ Args:
67
+ attr_name: Name of the attribute to import.
68
+
69
+ Returns:
70
+ The imported attribute object.
71
+
72
+ Raises:
73
+ AttributeError: If the attribute is not a valid dynamic import.
74
+ """
75
+ module_name = _dynamic_imports.get(attr_name)
76
+ result = import_attr(attr_name, module_name, __spec__.parent)
77
+ globals()[attr_name] = result
78
+ return result
79
+
80
+
81
+ def __dir__() -> list[str]:
82
+ """Return a list of available attributes for this module.
83
+
84
+ Returns:
85
+ List of attribute names that can be imported from this module.
86
+ """
87
+ return list(__all__)