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,177 @@
1
+ """Dictionary prompt template."""
2
+
3
+ import warnings
4
+ from functools import cached_property
5
+ from typing import Any, Literal, cast
6
+
7
+ from pydantic import model_validator
8
+ from typing_extensions import override
9
+
10
+ from coco_core.load import dumpd
11
+ from coco_core.prompts.string import (
12
+ DEFAULT_FORMATTER_MAPPING,
13
+ get_template_variables,
14
+ )
15
+ from coco_core.runnables import RunnableConfig, RunnableSerializable
16
+ from coco_core.runnables.config import ensure_config
17
+
18
+
19
+ class DictPromptTemplate(RunnableSerializable[dict[str, Any], dict[str, Any]]):
20
+ """Template represented by a dictionary.
21
+
22
+ Recognizes variables in f-string or mustache formatted string dict values.
23
+
24
+ Does NOT recognize variables in dict keys. Applies recursively.
25
+
26
+ Example:
27
+ ```python
28
+ prompt = DictPromptTemplate(
29
+ template={
30
+ "type": "text",
31
+ "text": "Hello {name}",
32
+ "metadata": {"source": "{source}"},
33
+ },
34
+ template_format="f-string",
35
+ )
36
+ prompt.format(name="Alice", source="docs")
37
+ # {
38
+ # "type": "text",
39
+ # "text": "Hello Alice",
40
+ # "metadata": {"source": "docs"},
41
+ # }
42
+ ```
43
+ """
44
+
45
+ template: dict[str, Any]
46
+ template_format: Literal["f-string", "mustache"]
47
+
48
+ @model_validator(mode="after")
49
+ def validate_template(self) -> "DictPromptTemplate":
50
+ """Validate that the template structure contains only safe variables."""
51
+ _get_input_variables(self.template, self.template_format)
52
+ return self
53
+
54
+ @property
55
+ def input_variables(self) -> list[str]:
56
+ """Template input variables."""
57
+ return _get_input_variables(self.template, self.template_format)
58
+
59
+ def format(self, **kwargs: Any) -> dict[str, Any]:
60
+ """Format the prompt with the inputs.
61
+
62
+ Returns:
63
+ A formatted dict.
64
+ """
65
+ return _insert_input_variables(self.template, kwargs, self.template_format)
66
+
67
+ async def aformat(self, **kwargs: Any) -> dict[str, Any]:
68
+ """Format the prompt with the inputs.
69
+
70
+ Returns:
71
+ A formatted dict.
72
+ """
73
+ return self.format(**kwargs)
74
+
75
+ @override
76
+ def invoke(
77
+ self, input: dict[str, Any], config: RunnableConfig | None = None, **kwargs: Any
78
+ ) -> dict[str, Any]:
79
+ return self._call_with_config(
80
+ lambda x: self.format(**x),
81
+ input,
82
+ ensure_config(config),
83
+ run_type="prompt",
84
+ serialized=self._serialized,
85
+ **kwargs,
86
+ )
87
+
88
+ @property
89
+ def _prompt_type(self) -> str:
90
+ return "dict-prompt"
91
+
92
+ @cached_property
93
+ def _serialized(self) -> dict[str, Any]:
94
+ # self is always a Serializable object in this case, thus the result is
95
+ # guaranteed to be a dict since dumpd uses the default callback, which uses
96
+ # obj.to_json which always returns TypedDict subclasses
97
+ return cast("dict[str, Any]", dumpd(self))
98
+
99
+ @classmethod
100
+ def is_lc_serializable(cls) -> bool:
101
+ """Return `True` as this class is serializable."""
102
+ return True
103
+
104
+ @classmethod
105
+ def get_lc_namespace(cls) -> list[str]:
106
+ """Get the namespace of the Coco object.
107
+
108
+ Returns:
109
+ `["coco_core", "prompts", "dict"]`
110
+ """
111
+ return ["coco_core", "prompts", "dict"]
112
+
113
+ def pretty_repr(self, *, html: bool = False) -> str:
114
+ """Human-readable representation.
115
+
116
+ Args:
117
+ html: Whether to format as HTML.
118
+
119
+ Returns:
120
+ Human-readable representation.
121
+ """
122
+ raise NotImplementedError
123
+
124
+
125
+ def _get_input_variables(
126
+ template: dict[str, Any], template_format: Literal["f-string", "mustache"]
127
+ ) -> list[str]:
128
+ input_variables = []
129
+ for v in template.values():
130
+ if isinstance(v, str):
131
+ input_variables += get_template_variables(v, template_format)
132
+ elif isinstance(v, dict):
133
+ input_variables += _get_input_variables(v, template_format)
134
+ elif isinstance(v, (list, tuple)):
135
+ for x in v:
136
+ if isinstance(x, str):
137
+ input_variables += get_template_variables(x, template_format)
138
+ elif isinstance(x, dict):
139
+ input_variables += _get_input_variables(x, template_format)
140
+ return list(set(input_variables))
141
+
142
+
143
+ def _insert_input_variables(
144
+ template: dict[str, Any],
145
+ inputs: dict[str, Any],
146
+ template_format: Literal["f-string", "mustache"],
147
+ ) -> dict[str, Any]:
148
+ formatted: dict[str, Any] = {}
149
+ formatter = DEFAULT_FORMATTER_MAPPING[template_format]
150
+ for k, v in template.items():
151
+ if isinstance(v, str):
152
+ formatted[k] = formatter(v, **inputs)
153
+ elif isinstance(v, dict):
154
+ if k == "image_url" and "path" in v:
155
+ msg = (
156
+ "Specifying image inputs via file path in environments with "
157
+ "user-input paths is a security vulnerability. Out of an abundance "
158
+ "of caution, the utility has been removed to prevent possible "
159
+ "misuse."
160
+ )
161
+ warnings.warn(msg, stacklevel=2)
162
+ formatted[k] = _insert_input_variables(v, inputs, template_format)
163
+ elif isinstance(v, (list, tuple)):
164
+ formatted_v: list[Any] = []
165
+ for x in v:
166
+ if isinstance(x, str):
167
+ formatted_v.append(formatter(x, **inputs))
168
+ elif isinstance(x, dict):
169
+ formatted_v.append(
170
+ _insert_input_variables(x, inputs, template_format)
171
+ )
172
+ else:
173
+ formatted_v.append(x)
174
+ formatted[k] = type(v)(formatted_v)
175
+ else:
176
+ formatted[k] = v
177
+ return formatted
@@ -0,0 +1,483 @@
1
+ """Prompt template that contains few shot examples."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any, Literal
6
+
7
+ from pydantic import (
8
+ BaseModel,
9
+ ConfigDict,
10
+ Field,
11
+ model_validator,
12
+ )
13
+ from typing_extensions import override
14
+
15
+ from coco_core._api import deprecated
16
+ from coco_core.example_selectors import BaseExampleSelector
17
+ from coco_core.messages import BaseMessage, get_buffer_string
18
+ from coco_core.prompts.chat import BaseChatPromptTemplate
19
+ from coco_core.prompts.message import BaseMessagePromptTemplate
20
+ from coco_core.prompts.prompt import PromptTemplate
21
+ from coco_core.prompts.string import (
22
+ DEFAULT_FORMATTER_MAPPING,
23
+ StringPromptTemplate,
24
+ check_valid_template,
25
+ get_template_variables,
26
+ )
27
+
28
+ if TYPE_CHECKING:
29
+ from pathlib import Path
30
+
31
+ from typing_extensions import Self
32
+
33
+
34
+ class _FewShotPromptTemplateMixin(BaseModel):
35
+ """Prompt template that contains few shot examples."""
36
+
37
+ examples: list[dict[str, Any]] | None = None
38
+ """Examples to format into the prompt.
39
+
40
+ Either this or `example_selector` should be provided.
41
+ """
42
+
43
+ example_selector: BaseExampleSelector | None = None
44
+ """`ExampleSelector` to choose the examples to format into the prompt.
45
+
46
+ Either this or `examples` should be provided.
47
+ """
48
+
49
+ model_config = ConfigDict(
50
+ arbitrary_types_allowed=True,
51
+ extra="forbid",
52
+ )
53
+
54
+ @model_validator(mode="before")
55
+ @classmethod
56
+ def check_examples_and_selector(cls, values: dict[str, Any]) -> Any:
57
+ """Check that one and only one of `examples`/`example_selector` are provided.
58
+
59
+ Args:
60
+ values: The values to check.
61
+
62
+ Returns:
63
+ The values if they are valid.
64
+
65
+ Raises:
66
+ ValueError: If neither or both `examples` and `example_selector` are
67
+ provided.
68
+ ValueError: If both `examples` and `example_selector` are provided.
69
+ """
70
+ examples = values.get("examples")
71
+ example_selector = values.get("example_selector")
72
+ if examples and example_selector:
73
+ msg = "Only one of 'examples' and 'example_selector' should be provided"
74
+ raise ValueError(msg)
75
+
76
+ if examples is None and example_selector is None:
77
+ msg = "One of 'examples' and 'example_selector' should be provided"
78
+ raise ValueError(msg)
79
+
80
+ return values
81
+
82
+ def _get_examples(self, **kwargs: Any) -> list[dict[str, Any]]:
83
+ """Get the examples to use for formatting the prompt.
84
+
85
+ Args:
86
+ **kwargs: Keyword arguments to be passed to the example selector.
87
+
88
+ Returns:
89
+ List of examples.
90
+
91
+ Raises:
92
+ ValueError: If neither `examples` nor `example_selector` are provided.
93
+ """
94
+ if self.examples is not None:
95
+ return self.examples
96
+ if self.example_selector is not None:
97
+ return self.example_selector.select_examples(kwargs)
98
+ msg = "One of 'examples' and 'example_selector' should be provided"
99
+ raise ValueError(msg)
100
+
101
+ async def _aget_examples(self, **kwargs: Any) -> list[dict[str, Any]]:
102
+ """Async get the examples to use for formatting the prompt.
103
+
104
+ Args:
105
+ **kwargs: Keyword arguments to be passed to the example selector.
106
+
107
+ Returns:
108
+ List of examples.
109
+
110
+ Raises:
111
+ ValueError: If neither `examples` nor `example_selector` are provided.
112
+ """
113
+ if self.examples is not None:
114
+ return self.examples
115
+ if self.example_selector is not None:
116
+ return await self.example_selector.aselect_examples(kwargs)
117
+ msg = "One of 'examples' and 'example_selector' should be provided"
118
+ raise ValueError(msg)
119
+
120
+
121
+ class FewShotPromptTemplate(_FewShotPromptTemplateMixin, StringPromptTemplate):
122
+ """Prompt template that contains few shot examples."""
123
+
124
+ @classmethod
125
+ def is_lc_serializable(cls) -> bool:
126
+ """Return `False` as this class is not serializable."""
127
+ return False
128
+
129
+ validate_template: bool = False
130
+ """Whether or not to try validating the template."""
131
+
132
+ example_prompt: PromptTemplate
133
+ """`PromptTemplate` used to format an individual example."""
134
+
135
+ suffix: str
136
+ """A prompt template string to put after the examples."""
137
+
138
+ example_separator: str = "\n\n"
139
+ """String separator used to join the prefix, the examples, and suffix."""
140
+
141
+ prefix: str = ""
142
+ """A prompt template string to put before the examples."""
143
+
144
+ template_format: Literal["f-string", "jinja2"] = "f-string"
145
+ """The format of the prompt template.
146
+
147
+ Options are: `'f-string'`, `'jinja2'`.
148
+ """
149
+
150
+ def __init__(self, **kwargs: Any) -> None:
151
+ """Initialize the few shot prompt template."""
152
+ if "input_variables" not in kwargs and "example_prompt" in kwargs:
153
+ kwargs["input_variables"] = kwargs["example_prompt"].input_variables
154
+ super().__init__(**kwargs)
155
+
156
+ @model_validator(mode="after")
157
+ def template_is_valid(self) -> Self:
158
+ """Check that prefix, suffix, and input variables are consistent."""
159
+ if self.validate_template:
160
+ check_valid_template(
161
+ self.prefix + self.suffix,
162
+ self.template_format,
163
+ self.input_variables + list(self.partial_variables),
164
+ )
165
+ elif self.template_format:
166
+ self.input_variables = [
167
+ var
168
+ for var in get_template_variables(
169
+ self.prefix + self.suffix, self.template_format
170
+ )
171
+ if var not in self.partial_variables
172
+ ]
173
+ return self
174
+
175
+ model_config = ConfigDict(
176
+ arbitrary_types_allowed=True,
177
+ extra="forbid",
178
+ )
179
+
180
+ def format(self, **kwargs: Any) -> str:
181
+ """Format the prompt with inputs generating a string.
182
+
183
+ Use this method to generate a string representation of a prompt.
184
+
185
+ Args:
186
+ **kwargs: Keyword arguments to use for formatting.
187
+
188
+ Returns:
189
+ A string representation of the prompt.
190
+ """
191
+ kwargs = self._merge_partial_and_user_variables(**kwargs)
192
+ # Get the examples to use.
193
+ examples = self._get_examples(**kwargs)
194
+ examples = [
195
+ {k: e[k] for k in self.example_prompt.input_variables} for e in examples
196
+ ]
197
+ # Format the examples.
198
+ example_strings = [
199
+ self.example_prompt.format(**example) for example in examples
200
+ ]
201
+ # Create the overall template.
202
+ pieces = [self.prefix, *example_strings, self.suffix]
203
+ template = self.example_separator.join([piece for piece in pieces if piece])
204
+
205
+ # Format the template with the input variables.
206
+ return DEFAULT_FORMATTER_MAPPING[self.template_format](template, **kwargs)
207
+
208
+ async def aformat(self, **kwargs: Any) -> str:
209
+ """Async format the prompt with inputs generating a string.
210
+
211
+ Use this method to generate a string representation of a prompt.
212
+
213
+ Args:
214
+ **kwargs: Keyword arguments to use for formatting.
215
+
216
+ Returns:
217
+ A string representation of the prompt.
218
+ """
219
+ kwargs = self._merge_partial_and_user_variables(**kwargs)
220
+ # Get the examples to use.
221
+ examples = await self._aget_examples(**kwargs)
222
+ examples = [
223
+ {k: e[k] for k in self.example_prompt.input_variables} for e in examples
224
+ ]
225
+ # Format the examples.
226
+ example_strings = [
227
+ await self.example_prompt.aformat(**example) for example in examples
228
+ ]
229
+ # Create the overall template.
230
+ pieces = [self.prefix, *example_strings, self.suffix]
231
+ template = self.example_separator.join([piece for piece in pieces if piece])
232
+
233
+ # Format the template with the input variables.
234
+ return DEFAULT_FORMATTER_MAPPING[self.template_format](template, **kwargs)
235
+
236
+ @property
237
+ def _prompt_type(self) -> str:
238
+ """Return the prompt type key."""
239
+ return "few_shot"
240
+
241
+ @deprecated(
242
+ since="1.2.21",
243
+ removal="2.0.0",
244
+ alternative="Use `dumpd`/`dumps` from `coco_core.load` to serialize "
245
+ "prompts and `load`/`loads` to deserialize them.",
246
+ )
247
+ def save(self, file_path: Path | str) -> None:
248
+ """Save the prompt template to a file.
249
+
250
+ Args:
251
+ file_path: The path to save the prompt template to.
252
+
253
+ Raises:
254
+ ValueError: If `example_selector` is provided.
255
+ """
256
+ if self.example_selector:
257
+ msg = "Saving an example selector is not currently supported"
258
+ raise ValueError(msg)
259
+ return super().save(file_path)
260
+
261
+
262
+ class FewShotChatMessagePromptTemplate(
263
+ BaseChatPromptTemplate, _FewShotPromptTemplateMixin
264
+ ):
265
+ """Chat prompt template that supports few-shot examples.
266
+
267
+ The high level structure of produced by this prompt template is a list of messages
268
+ consisting of prefix message(s), example message(s), and suffix message(s).
269
+
270
+ This structure enables creating a conversation with intermediate examples like:
271
+
272
+ ```txt
273
+ System: You are a helpful AI Assistant
274
+
275
+ Human: What is 2+2?
276
+
277
+ AI: 4
278
+
279
+ Human: What is 2+3?
280
+
281
+ AI: 5
282
+
283
+ Human: What is 4+4?
284
+ ```
285
+
286
+ This prompt template can be used to generate a fixed list of examples or else to
287
+ dynamically select examples based on the input.
288
+
289
+ Examples:
290
+ Prompt template with a fixed list of examples (matching the sample
291
+ conversation above):
292
+
293
+ ```python
294
+ from coco_core.prompts import (
295
+ FewShotChatMessagePromptTemplate,
296
+ ChatPromptTemplate,
297
+ )
298
+
299
+ examples = [
300
+ {"input": "2+2", "output": "4"},
301
+ {"input": "2+3", "output": "5"},
302
+ ]
303
+
304
+ example_prompt = ChatPromptTemplate.from_messages(
305
+ [
306
+ ("human", "What is {input}?"),
307
+ ("ai", "{output}"),
308
+ ]
309
+ )
310
+
311
+ few_shot_prompt = FewShotChatMessagePromptTemplate(
312
+ examples=examples,
313
+ # This is a prompt template used to format each individual example.
314
+ example_prompt=example_prompt,
315
+ )
316
+
317
+ final_prompt = ChatPromptTemplate.from_messages(
318
+ [
319
+ ("system", "You are a helpful AI Assistant"),
320
+ few_shot_prompt,
321
+ ("human", "{input}"),
322
+ ]
323
+ )
324
+ final_prompt.format(input="What is 4+4?")
325
+ ```
326
+
327
+ Prompt template with dynamically selected examples:
328
+
329
+ ```python
330
+ from coco_core.prompts import SemanticSimilarityExampleSelector
331
+ from coco_core.embeddings import OpenAIEmbeddings
332
+ from coco_core.vectorstores import Chroma
333
+
334
+ examples = [
335
+ {"input": "2+2", "output": "4"},
336
+ {"input": "2+3", "output": "5"},
337
+ {"input": "2+4", "output": "6"},
338
+ # ...
339
+ ]
340
+
341
+ to_vectorize = [" ".join(example.values()) for example in examples]
342
+ embeddings = OpenAIEmbeddings()
343
+ vectorstore = Chroma.from_texts(to_vectorize, embeddings, metadatas=examples)
344
+ example_selector = SemanticSimilarityExampleSelector(vectorstore=vectorstore)
345
+
346
+ from coco_core import SystemMessage
347
+ from coco_core.prompts import HumanMessagePromptTemplate
348
+ from coco_core.prompts.few_shot import FewShotChatMessagePromptTemplate
349
+
350
+ few_shot_prompt = FewShotChatMessagePromptTemplate(
351
+ # Which variable(s) will be passed to the example selector.
352
+ input_variables=["input"],
353
+ example_selector=example_selector,
354
+ # Define how each example will be formatted.
355
+ # In this case, each example will become 2 messages:
356
+ # 1 human, and 1 AI
357
+ example_prompt=(
358
+ HumanMessagePromptTemplate.from_template("{input}")
359
+ + AIMessagePromptTemplate.from_template("{output}")
360
+ ),
361
+ )
362
+ # Define the overall prompt.
363
+ final_prompt = (
364
+ SystemMessagePromptTemplate.from_template("You are a helpful AI Assistant")
365
+ + few_shot_prompt
366
+ + HumanMessagePromptTemplate.from_template("{input}")
367
+ )
368
+ # Show the prompt
369
+ print(final_prompt.format_messages(input="What's 3+3?")) # noqa: T201
370
+
371
+ # Use within an LLM
372
+ from coco_anthropic import ChatAnthropic
373
+
374
+ chain = final_prompt | ChatAnthropic(model="claude-sonnet-5")
375
+ chain.invoke({"input": "What's 3+3?"})
376
+ ```
377
+ """
378
+
379
+ input_variables: list[str] = Field(default_factory=list)
380
+ """A list of the names of the variables the prompt template will use to pass to
381
+ the `example_selector`, if provided.
382
+ """
383
+
384
+ example_prompt: BaseMessagePromptTemplate | BaseChatPromptTemplate
385
+ """The class to format each example."""
386
+
387
+ @classmethod
388
+ def is_lc_serializable(cls) -> bool:
389
+ """Return `False` as this class is not serializable."""
390
+ return False
391
+
392
+ model_config = ConfigDict(
393
+ arbitrary_types_allowed=True,
394
+ extra="forbid",
395
+ )
396
+
397
+ def format_messages(self, **kwargs: Any) -> list[BaseMessage]:
398
+ """Format kwargs into a list of messages.
399
+
400
+ Args:
401
+ **kwargs: Keyword arguments to use for filling in templates in messages.
402
+
403
+ Returns:
404
+ A list of formatted messages with all template variables filled in.
405
+ """
406
+ # Get the examples to use.
407
+ examples = self._get_examples(**kwargs)
408
+ examples = [
409
+ {k: e[k] for k in self.example_prompt.input_variables} for e in examples
410
+ ]
411
+ # Format the examples.
412
+ return [
413
+ message
414
+ for example in examples
415
+ for message in self.example_prompt.format_messages(**example)
416
+ ]
417
+
418
+ async def aformat_messages(self, **kwargs: Any) -> list[BaseMessage]:
419
+ """Async format kwargs into a list of messages.
420
+
421
+ Args:
422
+ **kwargs: Keyword arguments to use for filling in templates in messages.
423
+
424
+ Returns:
425
+ A list of formatted messages with all template variables filled in.
426
+ """
427
+ # Get the examples to use.
428
+ examples = await self._aget_examples(**kwargs)
429
+ examples = [
430
+ {k: e[k] for k in self.example_prompt.input_variables} for e in examples
431
+ ]
432
+ # Format the examples.
433
+ return [
434
+ message
435
+ for example in examples
436
+ for message in await self.example_prompt.aformat_messages(**example)
437
+ ]
438
+
439
+ def format(self, **kwargs: Any) -> str:
440
+ """Format the prompt with inputs generating a string.
441
+
442
+ Use this method to generate a string representation of a prompt consisting of
443
+ chat messages.
444
+
445
+ Useful for feeding into a string-based completion language model or debugging.
446
+
447
+ Args:
448
+ **kwargs: Keyword arguments to use for formatting.
449
+
450
+ Returns:
451
+ A string representation of the prompt
452
+ """
453
+ messages = self.format_messages(**kwargs)
454
+ return get_buffer_string(messages)
455
+
456
+ async def aformat(self, **kwargs: Any) -> str:
457
+ """Async format the prompt with inputs generating a string.
458
+
459
+ Use this method to generate a string representation of a prompt consisting of
460
+ chat messages.
461
+
462
+ Useful for feeding into a string-based completion language model or debugging.
463
+
464
+ Args:
465
+ **kwargs: Keyword arguments to use for formatting.
466
+
467
+ Returns:
468
+ A string representation of the prompt
469
+ """
470
+ messages = await self.aformat_messages(**kwargs)
471
+ return get_buffer_string(messages)
472
+
473
+ @override
474
+ def pretty_repr(self, html: bool = False) -> str:
475
+ """Return a pretty representation of the prompt template.
476
+
477
+ Args:
478
+ html: Whether or not to return an HTML formatted string.
479
+
480
+ Returns:
481
+ A pretty representation of the prompt template.
482
+ """
483
+ raise NotImplementedError