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,508 @@
1
+ """Base class for prompt templates."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import builtins
6
+ import contextlib
7
+ import json
8
+ from abc import ABC, abstractmethod
9
+ from collections.abc import Callable, Mapping
10
+ from functools import cached_property
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
13
+
14
+ import yaml
15
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
16
+ from typing_extensions import Self, override
17
+
18
+ from coco_core._api import deprecated, suppress_coco_deprecation_warning
19
+ from coco_core.exceptions import ErrorCode, create_message
20
+ from coco_core.load import dumpd
21
+ from coco_core.output_parsers.base import BaseOutputParser
22
+ from coco_core.prompt_values import (
23
+ ChatPromptValueConcrete,
24
+ PromptValue,
25
+ StringPromptValue,
26
+ )
27
+ from coco_core.runnables.base import RunnableSerializable
28
+ from coco_core.runnables.config import RunnableConfig, ensure_config
29
+ from coco_core.utils.pydantic import create_model_v2
30
+
31
+ if TYPE_CHECKING:
32
+ from coco_core.documents import Document
33
+
34
+
35
+ FormatOutputType = TypeVar("FormatOutputType")
36
+
37
+
38
+ class BasePromptTemplate(
39
+ RunnableSerializable[dict[str, Any], PromptValue], ABC, Generic[FormatOutputType]
40
+ ):
41
+ """Base class for all prompt templates, returning a prompt."""
42
+
43
+ input_variables: list[str]
44
+ """A list of the names of the variables whose values are required as inputs to the
45
+ prompt.
46
+ """
47
+
48
+ optional_variables: list[str] = Field(default=[])
49
+ """A list of the names of the variables for placeholder or `MessagePlaceholder` that
50
+ are optional.
51
+
52
+ These variables are auto inferred from the prompt and user need not provide them.
53
+ """
54
+
55
+ input_types: builtins.dict[str, Any] = Field(default_factory=dict, exclude=True)
56
+ """A dictionary of the types of the variables the prompt template expects.
57
+
58
+ If not provided, all variables are assumed to be strings.
59
+ """
60
+
61
+ # Ideally we would type output_parser as BaseOutputParser[Any]
62
+ # but that makes Pydantic fail (Pydantic tries to instantiate BaseOutputParser
63
+ # instead of using the provided output_parser...)
64
+ output_parser: BaseOutputParser | None = None # type: ignore[type-arg]
65
+ """How to parse the output of calling an LLM on this formatted prompt."""
66
+
67
+ partial_variables: Mapping[str, Any] = Field(default_factory=dict)
68
+ """A dictionary of the partial variables the prompt template carries.
69
+
70
+ Partial variables populate the template so that you don't need to pass them in every
71
+ time you call the prompt.
72
+ """
73
+
74
+ metadata: builtins.dict[str, Any] | None = None
75
+ """Metadata to be used for tracing."""
76
+
77
+ tags: list[str] | None = None
78
+ """Tags to be used for tracing."""
79
+
80
+ @model_validator(mode="after")
81
+ def validate_variable_names(self) -> Self:
82
+ """Validate variable names do not include restricted names."""
83
+ if "stop" in self.input_variables:
84
+ msg = (
85
+ "Cannot have an input variable named 'stop', as it is used internally,"
86
+ " please rename."
87
+ )
88
+ raise ValueError(
89
+ create_message(message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT)
90
+ )
91
+ if "stop" in self.partial_variables:
92
+ msg = (
93
+ "Cannot have an partial variable named 'stop', as it is used "
94
+ "internally, please rename."
95
+ )
96
+ raise ValueError(
97
+ create_message(message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT)
98
+ )
99
+
100
+ overall = set(self.input_variables).intersection(self.partial_variables)
101
+ if overall:
102
+ msg = f"Found overlapping input and partial variables: {overall}"
103
+ raise ValueError(
104
+ create_message(message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT)
105
+ )
106
+ return self
107
+
108
+ @classmethod
109
+ def get_lc_namespace(cls) -> list[str]:
110
+ """Get the namespace of the Coco object.
111
+
112
+ Returns:
113
+ `["coco", "schema", "prompt_template"]`
114
+ """
115
+ return ["coco", "schema", "prompt_template"]
116
+
117
+ @classmethod
118
+ def is_lc_serializable(cls) -> bool:
119
+ """Return `True` as this class is serializable."""
120
+ return True
121
+
122
+ model_config = ConfigDict(
123
+ arbitrary_types_allowed=True,
124
+ )
125
+
126
+ @cached_property
127
+ def _serialized(self) -> builtins.dict[str, Any]:
128
+ # self is always a Serializable object in this case, thus the result is
129
+ # guaranteed to be a dict since dumpd uses the default callback, which uses
130
+ # obj.to_json which always returns TypedDict subclasses
131
+ return cast("builtins.dict[str, Any]", dumpd(self))
132
+
133
+ @property
134
+ @override
135
+ def OutputType(self) -> Any:
136
+ """Return the output type of the prompt."""
137
+ return StringPromptValue | ChatPromptValueConcrete
138
+
139
+ @override
140
+ def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]:
141
+ """Get the input schema for the prompt.
142
+
143
+ Args:
144
+ config: Configuration for the prompt.
145
+
146
+ Returns:
147
+ The input schema for the prompt.
148
+ """
149
+ # This is correct, but pydantic typings/mypy don't think so.
150
+ required_input_variables = {
151
+ k: (self.input_types.get(k, str), ...) for k in self.input_variables
152
+ }
153
+ optional_input_variables = {
154
+ k: (self.input_types.get(k, str), None) for k in self.optional_variables
155
+ }
156
+ return create_model_v2(
157
+ "PromptInput",
158
+ field_definitions={**required_input_variables, **optional_input_variables},
159
+ )
160
+
161
+ def _validate_input(self, inner_input: Any) -> builtins.dict[str, Any]:
162
+ if not isinstance(inner_input, dict):
163
+ if len(self.input_variables) == 1:
164
+ var_name = self.input_variables[0]
165
+ inner_input_ = {var_name: inner_input}
166
+
167
+ else:
168
+ msg = (
169
+ f"Expected mapping type as input to {self.__class__.__name__}. "
170
+ f"Received {type(inner_input)}."
171
+ )
172
+ raise TypeError(
173
+ create_message(
174
+ message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT
175
+ )
176
+ )
177
+ else:
178
+ inner_input_ = inner_input
179
+ missing = set(self.input_variables).difference(inner_input_)
180
+ if missing:
181
+ msg = (
182
+ f"Input to {self.__class__.__name__} is missing variables {missing}. "
183
+ f" Expected: {self.input_variables}"
184
+ f" Received: {list(inner_input_.keys())}"
185
+ )
186
+ example_key = missing.pop()
187
+ msg += (
188
+ f"\nNote: if you intended {{{example_key}}} to be part of the string"
189
+ " and not a variable, please escape it with double curly braces like: "
190
+ f"'{{{{{example_key}}}}}'."
191
+ )
192
+ raise KeyError(
193
+ create_message(message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT)
194
+ )
195
+ return inner_input_
196
+
197
+ def _format_prompt_with_error_handling(
198
+ self, inner_input: builtins.dict[str, Any]
199
+ ) -> PromptValue:
200
+ inner_input_ = self._validate_input(inner_input)
201
+ return self.format_prompt(**inner_input_)
202
+
203
+ async def _aformat_prompt_with_error_handling(
204
+ self, inner_input: builtins.dict[str, Any]
205
+ ) -> PromptValue:
206
+ inner_input_ = self._validate_input(inner_input)
207
+ return await self.aformat_prompt(**inner_input_)
208
+
209
+ @override
210
+ def invoke(
211
+ self,
212
+ input: builtins.dict[str, Any],
213
+ config: RunnableConfig | None = None,
214
+ **kwargs: Any,
215
+ ) -> PromptValue:
216
+ """Invoke the prompt.
217
+
218
+ Args:
219
+ input: Input to the prompt.
220
+ config: Configuration for the prompt.
221
+
222
+ Returns:
223
+ The output of the prompt.
224
+ """
225
+ config = ensure_config(config)
226
+ if self.metadata:
227
+ config["metadata"] = {**config["metadata"], **self.metadata}
228
+ if self.tags:
229
+ config["tags"] += self.tags
230
+ return self._call_with_config(
231
+ self._format_prompt_with_error_handling,
232
+ input,
233
+ config,
234
+ run_type="prompt",
235
+ serialized=self._serialized,
236
+ )
237
+
238
+ @override
239
+ async def ainvoke(
240
+ self,
241
+ input: builtins.dict[str, Any],
242
+ config: RunnableConfig | None = None,
243
+ **kwargs: Any,
244
+ ) -> PromptValue:
245
+ """Async invoke the prompt.
246
+
247
+ Args:
248
+ input: Input to the prompt.
249
+ config: Configuration for the prompt.
250
+
251
+ Returns:
252
+ The output of the prompt.
253
+ """
254
+ config = ensure_config(config)
255
+ if self.metadata:
256
+ config["metadata"].update(self.metadata)
257
+ if self.tags:
258
+ config["tags"].extend(self.tags)
259
+ return await self._acall_with_config(
260
+ self._aformat_prompt_with_error_handling,
261
+ input,
262
+ config,
263
+ run_type="prompt",
264
+ serialized=self._serialized,
265
+ )
266
+
267
+ @abstractmethod
268
+ def format_prompt(self, **kwargs: Any) -> PromptValue:
269
+ """Create `PromptValue`.
270
+
271
+ Args:
272
+ **kwargs: Any arguments to be passed to the prompt template.
273
+
274
+ Returns:
275
+ The output of the prompt.
276
+ """
277
+
278
+ async def aformat_prompt(self, **kwargs: Any) -> PromptValue:
279
+ """Async create `PromptValue`.
280
+
281
+ Args:
282
+ **kwargs: Any arguments to be passed to the prompt template.
283
+
284
+ Returns:
285
+ The output of the prompt.
286
+ """
287
+ return self.format_prompt(**kwargs)
288
+
289
+ def partial(
290
+ self, **kwargs: str | Callable[[], str]
291
+ ) -> BasePromptTemplate[FormatOutputType]:
292
+ """Return a partial of the prompt template.
293
+
294
+ Args:
295
+ **kwargs: Partial variables to set.
296
+
297
+ Returns:
298
+ A partial of the prompt template.
299
+ """
300
+ prompt_dict = self.__dict__.copy()
301
+ prompt_dict["input_variables"] = list(
302
+ set(self.input_variables).difference(kwargs)
303
+ )
304
+ prompt_dict["partial_variables"] = {**self.partial_variables, **kwargs}
305
+ return type(self)(**prompt_dict)
306
+
307
+ def _merge_partial_and_user_variables(
308
+ self, **kwargs: Any
309
+ ) -> builtins.dict[str, Any]:
310
+ # Get partial params:
311
+ partial_kwargs = {
312
+ k: v if not callable(v) else v() for k, v in self.partial_variables.items()
313
+ }
314
+ return {**partial_kwargs, **kwargs}
315
+
316
+ @abstractmethod
317
+ def format(self, **kwargs: Any) -> FormatOutputType:
318
+ """Format the prompt with the inputs.
319
+
320
+ Args:
321
+ **kwargs: Any arguments to be passed to the prompt template.
322
+
323
+ Returns:
324
+ A formatted string.
325
+
326
+ Example:
327
+ ```python
328
+ prompt.format(variable1="foo")
329
+ ```
330
+ """
331
+
332
+ async def aformat(self, **kwargs: Any) -> FormatOutputType:
333
+ """Async format the prompt with the inputs.
334
+
335
+ Args:
336
+ **kwargs: Any arguments to be passed to the prompt template.
337
+
338
+ Returns:
339
+ A formatted string.
340
+
341
+ Example:
342
+ ```python
343
+ await prompt.aformat(variable1="foo")
344
+ ```
345
+ """
346
+ return self.format(**kwargs)
347
+
348
+ @property
349
+ def _prompt_type(self) -> str:
350
+ """Return the prompt type key."""
351
+ raise NotImplementedError
352
+
353
+ @deprecated("1.4.2", alternative="asdict", removal="2.0.0")
354
+ @override
355
+ def dict(self, **kwargs: Any) -> builtins.dict[str, Any]:
356
+ """DEPRECATED - use `asdict()` instead.
357
+
358
+ Return a dictionary representation of the prompt.
359
+ """
360
+ return self.asdict(**kwargs)
361
+
362
+ def asdict(self, **kwargs: Any) -> builtins.dict[str, Any]:
363
+ """Return a dictionary representation of the prompt.
364
+
365
+ Args:
366
+ **kwargs: Any additional arguments to pass to the dictionary.
367
+
368
+ Returns:
369
+ Dictionary representation of the prompt.
370
+ """
371
+ prompt_dict = super().model_dump(**kwargs)
372
+ with contextlib.suppress(NotImplementedError):
373
+ prompt_dict["_type"] = self._prompt_type
374
+ return prompt_dict
375
+
376
+ def _dict_for_compat(self) -> builtins.dict[str, Any]:
377
+ """Return the prompt dictionary while preserving deprecated overrides."""
378
+ with suppress_coco_deprecation_warning():
379
+ return self.dict()
380
+
381
+ @deprecated(
382
+ since="1.2.21",
383
+ removal="2.0.0",
384
+ alternative="Use `dumpd`/`dumps` from `coco_core.load` to serialize "
385
+ "prompts and `load`/`loads` to deserialize them.",
386
+ )
387
+ def save(self, file_path: Path | str) -> None:
388
+ """Save the prompt.
389
+
390
+ Args:
391
+ file_path: Path to directory to save prompt to.
392
+
393
+ Raises:
394
+ ValueError: If the prompt has partial variables.
395
+ ValueError: If the file path is not json or yaml.
396
+ NotImplementedError: If the prompt type is not implemented.
397
+
398
+ Example:
399
+ ```python
400
+ prompt.save(file_path="path/prompt.yaml")
401
+ ```
402
+ """
403
+ if self.partial_variables:
404
+ msg = "Cannot save prompt with partial variables."
405
+ raise ValueError(msg)
406
+
407
+ # Fetch dictionary to save. Preserve deprecated `dict()` overrides until
408
+ # `dict()` is removed.
409
+ prompt_dict = self._dict_for_compat()
410
+ if "_type" not in prompt_dict:
411
+ msg = f"Prompt {self} does not support saving."
412
+ raise NotImplementedError(msg)
413
+
414
+ # Convert file to Path object.
415
+ save_path = Path(file_path)
416
+
417
+ directory_path = save_path.parent
418
+ directory_path.mkdir(parents=True, exist_ok=True)
419
+
420
+ resolved_path = save_path.resolve()
421
+ if resolved_path.suffix == ".json":
422
+ with resolved_path.open("w", encoding="utf-8") as f:
423
+ json.dump(prompt_dict, f, indent=4)
424
+ elif resolved_path.suffix.endswith((".yaml", ".yml")):
425
+ with resolved_path.open("w", encoding="utf-8") as f:
426
+ yaml.dump(prompt_dict, f, default_flow_style=False)
427
+ else:
428
+ msg = f"{save_path} must be json or yaml"
429
+ raise ValueError(msg)
430
+
431
+
432
+ def _get_document_info(
433
+ doc: Document, prompt: BasePromptTemplate[str]
434
+ ) -> dict[str, Any]:
435
+ base_info = {"page_content": doc.page_content, **doc.metadata}
436
+ missing_metadata = set(prompt.input_variables).difference(base_info)
437
+ if len(missing_metadata) > 0:
438
+ required_metadata = [
439
+ iv for iv in prompt.input_variables if iv != "page_content"
440
+ ]
441
+ msg = (
442
+ f"Document prompt requires documents to have metadata variables: "
443
+ f"{required_metadata}. Received document with missing metadata: "
444
+ f"{list(missing_metadata)}."
445
+ )
446
+ raise ValueError(
447
+ create_message(message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT)
448
+ )
449
+ return {k: base_info[k] for k in prompt.input_variables}
450
+
451
+
452
+ def format_document(doc: Document, prompt: BasePromptTemplate[str]) -> str:
453
+ """Format a document into a string based on a prompt template.
454
+
455
+ First, this pulls information from the document from two sources:
456
+
457
+ 1. `page_content`: This takes the information from the `document.page_content` and
458
+ assigns it to a variable named `page_content`.
459
+ 2. `metadata`: This takes information from `document.metadata` and assigns it to
460
+ variables of the same name.
461
+
462
+ Those variables are then passed into the `prompt` to produce a formatted string.
463
+
464
+ Args:
465
+ doc: `Document`, the `page_content` and `metadata` will be used to create the
466
+ final string.
467
+ prompt: `BasePromptTemplate`, will be used to format the `page_content` and
468
+ `metadata` into the final string.
469
+
470
+ Returns:
471
+ String of the document formatted.
472
+
473
+ Example:
474
+ ```python
475
+ from coco_core.documents import Document
476
+ from coco_core.prompts import PromptTemplate
477
+
478
+ doc = Document(page_content="This is a joke", metadata={"page": "1"})
479
+ prompt = PromptTemplate.from_template("Page {page}: {page_content}")
480
+ format_document(doc, prompt)
481
+ # -> "Page 1: This is a joke"
482
+ ```
483
+ """
484
+ return prompt.format(**_get_document_info(doc, prompt))
485
+
486
+
487
+ async def aformat_document(doc: Document, prompt: BasePromptTemplate[str]) -> str:
488
+ """Async format a document into a string based on a prompt template.
489
+
490
+ First, this pulls information from the document from two sources:
491
+
492
+ 1. `page_content`: This takes the information from the `document.page_content` and
493
+ assigns it to a variable named `page_content`.
494
+ 2. `metadata`: This takes information from `document.metadata` and assigns it to
495
+ variables of the same name.
496
+
497
+ Those variables are then passed into the `prompt` to produce a formatted string.
498
+
499
+ Args:
500
+ doc: `Document`, the `page_content` and `metadata` will be used to create the
501
+ final string.
502
+ prompt: `BasePromptTemplate`, will be used to format the `page_content` and
503
+ `metadata` into the final string.
504
+
505
+ Returns:
506
+ String of the document formatted.
507
+ """
508
+ return await prompt.aformat(**_get_document_info(doc, prompt))