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.
- coco/__init__.py +3 -0
- coco/agents/__init__.py +9 -0
- coco/agents/_subagent_transformer.py +307 -0
- coco/agents/factory.py +2164 -0
- coco/agents/middleware/__init__.py +100 -0
- coco/agents/middleware/_execution.py +385 -0
- coco/agents/middleware/_redaction.py +454 -0
- coco/agents/middleware/_retry.py +134 -0
- coco/agents/middleware/_trace_policy.py +61 -0
- coco/agents/middleware/context_editing.py +306 -0
- coco/agents/middleware/file_search.py +433 -0
- coco/agents/middleware/human_in_the_loop.py +680 -0
- coco/agents/middleware/internal_call_transformer.py +108 -0
- coco/agents/middleware/model_call_limit.py +267 -0
- coco/agents/middleware/model_fallback.py +417 -0
- coco/agents/middleware/model_retry.py +327 -0
- coco/agents/middleware/pii.py +878 -0
- coco/agents/middleware/provider_tool_search.py +307 -0
- coco/agents/middleware/shell_tool.py +949 -0
- coco/agents/middleware/summarization.py +910 -0
- coco/agents/middleware/todo.py +357 -0
- coco/agents/middleware/tool_call_limit.py +495 -0
- coco/agents/middleware/tool_emulator.py +251 -0
- coco/agents/middleware/tool_error.py +209 -0
- coco/agents/middleware/tool_retry.py +421 -0
- coco/agents/middleware/tool_selection.py +515 -0
- coco/agents/middleware/types.py +2201 -0
- coco/agents/structured_output.py +472 -0
- coco/chat_models/__init__.py +7 -0
- coco/chat_models/base.py +1082 -0
- coco/embeddings/__init__.py +18 -0
- coco/embeddings/base.py +279 -0
- coco/mcp/__init__.py +36 -0
- coco/mcp/adapter.py +245 -0
- coco/mcp/elicitation.py +394 -0
- coco/mcp/tools.py +307 -0
- coco/messages/__init__.py +73 -0
- coco/py.typed +0 -0
- coco/rate_limiters/__init__.py +13 -0
- coco/tools/__init__.py +22 -0
- coco/tools/tool_node.py +20 -0
- coco_anthropic/__init__.py +15 -0
- coco_anthropic/_client_utils.py +81 -0
- coco_anthropic/_compat.py +228 -0
- coco_anthropic/_sdk_compat.py +124 -0
- coco_anthropic/_version.py +3 -0
- coco_anthropic/chat_models.py +3349 -0
- coco_anthropic/data/__init__.py +1 -0
- coco_anthropic/data/_profiles.py +491 -0
- coco_anthropic/data/profile_augmentations.toml +69 -0
- coco_anthropic/experimental.py +140 -0
- coco_anthropic/llms.py +428 -0
- coco_anthropic/middleware/__init__.py +25 -0
- coco_anthropic/middleware/anthropic_tools.py +1197 -0
- coco_anthropic/middleware/bash.py +84 -0
- coco_anthropic/middleware/file_search.py +360 -0
- coco_anthropic/middleware/prompt_caching.py +276 -0
- coco_anthropic/output_parsers.py +93 -0
- coco_anthropic/py.typed +0 -0
- coco_core/__init__.py +20 -0
- coco_core/_api/__init__.py +87 -0
- coco_core/_api/beta_decorator.py +266 -0
- coco_core/_api/deprecation.py +637 -0
- coco_core/_api/internal.py +23 -0
- coco_core/_api/path.py +50 -0
- coco_core/_import_utils.py +41 -0
- coco_core/_protocol.py +625 -0
- coco_core/_security/__init__.py +36 -0
- coco_core/_security/_exceptions.py +9 -0
- coco_core/_security/_policy.py +313 -0
- coco_core/_security/_ssrf_protection.py +155 -0
- coco_core/_security/_transport.py +254 -0
- coco_core/agents.py +258 -0
- coco_core/caches.py +272 -0
- coco_core/callbacks/__init__.py +132 -0
- coco_core/callbacks/base.py +1229 -0
- coco_core/callbacks/file.py +266 -0
- coco_core/callbacks/manager.py +2826 -0
- coco_core/callbacks/stdout.py +123 -0
- coco_core/callbacks/streaming_stdout.py +154 -0
- coco_core/callbacks/usage.py +124 -0
- coco_core/chat_history.py +267 -0
- coco_core/chat_loaders.py +26 -0
- coco_core/chat_sessions.py +19 -0
- coco_core/cross_encoders.py +18 -0
- coco_core/document_loaders/__init__.py +36 -0
- coco_core/document_loaders/base.py +155 -0
- coco_core/document_loaders/blob_loaders.py +38 -0
- coco_core/documents/__init__.py +55 -0
- coco_core/documents/base.py +347 -0
- coco_core/documents/compressor.py +74 -0
- coco_core/documents/transformers.py +79 -0
- coco_core/embeddings/__init__.py +31 -0
- coco_core/embeddings/embeddings.py +78 -0
- coco_core/embeddings/fake.py +129 -0
- coco_core/env.py +22 -0
- coco_core/example_selectors/__init__.py +47 -0
- coco_core/example_selectors/base.py +60 -0
- coco_core/example_selectors/length_based.py +131 -0
- coco_core/example_selectors/semantic_similarity.py +366 -0
- coco_core/exceptions.py +166 -0
- coco_core/globals.py +72 -0
- coco_core/indexing/__init__.py +53 -0
- coco_core/indexing/api.py +954 -0
- coco_core/indexing/base.py +661 -0
- coco_core/indexing/in_memory.py +104 -0
- coco_core/language_models/__init__.py +116 -0
- coco_core/language_models/_compat_bridge.py +844 -0
- coco_core/language_models/_utils.py +343 -0
- coco_core/language_models/base.py +500 -0
- coco_core/language_models/chat_model_stream.py +1525 -0
- coco_core/language_models/chat_models.py +2748 -0
- coco_core/language_models/fake.py +137 -0
- coco_core/language_models/fake_chat_models.py +396 -0
- coco_core/language_models/llms.py +1569 -0
- coco_core/language_models/model_profile.py +183 -0
- coco_core/load/__init__.py +44 -0
- coco_core/load/_validation.py +191 -0
- coco_core/load/dump.py +120 -0
- coco_core/load/load.py +828 -0
- coco_core/load/mapping.py +1097 -0
- coco_core/load/serializable.py +406 -0
- coco_core/messages/__init__.py +198 -0
- coco_core/messages/ai.py +854 -0
- coco_core/messages/base.py +518 -0
- coco_core/messages/block_translators/__init__.py +112 -0
- coco_core/messages/block_translators/anthropic.py +534 -0
- coco_core/messages/block_translators/bedrock.py +108 -0
- coco_core/messages/block_translators/bedrock_converse.py +329 -0
- coco_core/messages/block_translators/coco_v0.py +303 -0
- coco_core/messages/block_translators/google_genai.py +610 -0
- coco_core/messages/block_translators/google_vertexai.py +21 -0
- coco_core/messages/block_translators/groq.py +157 -0
- coco_core/messages/block_translators/openai.py +1089 -0
- coco_core/messages/chat.py +64 -0
- coco_core/messages/content.py +1488 -0
- coco_core/messages/function.py +62 -0
- coco_core/messages/human.py +70 -0
- coco_core/messages/modifier.py +33 -0
- coco_core/messages/system.py +70 -0
- coco_core/messages/tool.py +418 -0
- coco_core/messages/utils.py +2442 -0
- coco_core/output_parsers/__init__.py +101 -0
- coco_core/output_parsers/base.py +361 -0
- coco_core/output_parsers/format_instructions.py +16 -0
- coco_core/output_parsers/json.py +139 -0
- coco_core/output_parsers/list.py +249 -0
- coco_core/output_parsers/openai_functions.py +324 -0
- coco_core/output_parsers/openai_tools.py +383 -0
- coco_core/output_parsers/pydantic.py +141 -0
- coco_core/output_parsers/string.py +63 -0
- coco_core/output_parsers/transform.py +175 -0
- coco_core/output_parsers/xml.py +301 -0
- coco_core/outputs/__init__.py +66 -0
- coco_core/outputs/chat_generation.py +147 -0
- coco_core/outputs/chat_result.py +39 -0
- coco_core/outputs/generation.py +80 -0
- coco_core/outputs/llm_result.py +112 -0
- coco_core/outputs/run_info.py +22 -0
- coco_core/prompt_values.py +161 -0
- coco_core/prompts/__init__.py +101 -0
- coco_core/prompts/base.py +508 -0
- coco_core/prompts/chat.py +1495 -0
- coco_core/prompts/dict.py +177 -0
- coco_core/prompts/few_shot.py +483 -0
- coco_core/prompts/few_shot_with_templates.py +239 -0
- coco_core/prompts/image.py +177 -0
- coco_core/prompts/loading.py +296 -0
- coco_core/prompts/message.py +97 -0
- coco_core/prompts/prompt.py +312 -0
- coco_core/prompts/string.py +399 -0
- coco_core/prompts/structured.py +214 -0
- coco_core/py.typed +0 -0
- coco_core/rate_limiters.py +254 -0
- coco_core/retrievers.py +328 -0
- coco_core/runnables/__init__.py +136 -0
- coco_core/runnables/base.py +6725 -0
- coco_core/runnables/branch.py +457 -0
- coco_core/runnables/config.py +712 -0
- coco_core/runnables/configurable.py +710 -0
- coco_core/runnables/fallbacks.py +663 -0
- coco_core/runnables/graph.py +741 -0
- coco_core/runnables/graph_ascii.py +366 -0
- coco_core/runnables/graph_mermaid.py +503 -0
- coco_core/runnables/graph_png.py +215 -0
- coco_core/runnables/history.py +631 -0
- coco_core/runnables/passthrough.py +849 -0
- coco_core/runnables/retry.py +379 -0
- coco_core/runnables/router.py +239 -0
- coco_core/runnables/schema.py +188 -0
- coco_core/runnables/utils.py +798 -0
- coco_core/stores.py +291 -0
- coco_core/structured_query.py +203 -0
- coco_core/sys_info.py +154 -0
- coco_core/tools/__init__.py +95 -0
- coco_core/tools/base.py +1948 -0
- coco_core/tools/convert.py +497 -0
- coco_core/tools/render.py +67 -0
- coco_core/tools/retriever.py +94 -0
- coco_core/tools/simple.py +208 -0
- coco_core/tools/structured.py +322 -0
- coco_core/tracers/__init__.py +47 -0
- coco_core/tracers/_compat.py +95 -0
- coco_core/tracers/_context.py +337 -0
- coco_core/tracers/_streaming.py +53 -0
- coco_core/tracers/base.py +955 -0
- coco_core/tracers/coco.py +279 -0
- coco_core/tracers/context.py +205 -0
- coco_core/tracers/core.py +766 -0
- coco_core/tracers/event_stream.py +1101 -0
- coco_core/tracers/log_stream.py +765 -0
- coco_core/tracers/memory_stream.py +200 -0
- coco_core/tracers/root_listeners.py +130 -0
- coco_core/tracers/run_collector.py +40 -0
- coco_core/tracers/schemas.py +14 -0
- coco_core/tracers/stdout.py +205 -0
- coco_core/utils/__init__.py +111 -0
- coco_core/utils/_gateway.py +274 -0
- coco_core/utils/_merge.py +218 -0
- coco_core/utils/aiter.py +357 -0
- coco_core/utils/env.py +86 -0
- coco_core/utils/formatting.py +80 -0
- coco_core/utils/function_calling.py +869 -0
- coco_core/utils/html.py +132 -0
- coco_core/utils/image.py +15 -0
- coco_core/utils/input.py +82 -0
- coco_core/utils/interactive_env.py +12 -0
- coco_core/utils/iter.py +229 -0
- coco_core/utils/json.py +230 -0
- coco_core/utils/json_schema.py +273 -0
- coco_core/utils/mustache.py +706 -0
- coco_core/utils/pydantic.py +631 -0
- coco_core/utils/strings.py +69 -0
- coco_core/utils/usage.py +61 -0
- coco_core/utils/utils.py +528 -0
- coco_core/utils/uuid.py +57 -0
- coco_core/vectorstores/__init__.py +53 -0
- coco_core/vectorstores/base.py +1111 -0
- coco_core/vectorstores/in_memory.py +546 -0
- coco_core/vectorstores/utils.py +163 -0
- coco_core/version.py +3 -0
- coco_framework-0.1.0.dist-info/METADATA +184 -0
- coco_framework-0.1.0.dist-info/RECORD +400 -0
- coco_framework-0.1.0.dist-info/WHEEL +4 -0
- coco_framework-0.1.0.dist-info/licenses/LICENSE +24 -0
- coco_framework-0.1.0.dist-info/licenses/NOTICE +15 -0
- coco_graph/_internal/__init__.py +4 -0
- coco_graph/_internal/_cache.py +31 -0
- coco_graph/_internal/_config.py +474 -0
- coco_graph/_internal/_constants.py +140 -0
- coco_graph/_internal/_fields.py +213 -0
- coco_graph/_internal/_future.py +220 -0
- coco_graph/_internal/_pydantic.py +275 -0
- coco_graph/_internal/_queue.py +124 -0
- coco_graph/_internal/_replay.py +90 -0
- coco_graph/_internal/_retry.py +29 -0
- coco_graph/_internal/_runnable.py +969 -0
- coco_graph/_internal/_scratchpad.py +19 -0
- coco_graph/_internal/_serde.py +253 -0
- coco_graph/_internal/_timeout.py +25 -0
- coco_graph/_internal/_typing.py +54 -0
- coco_graph/cache/base/__init__.py +48 -0
- coco_graph/cache/base/py.typed +0 -0
- coco_graph/cache/memory/__init__.py +73 -0
- coco_graph/cache/redis/__init__.py +144 -0
- coco_graph/cache/sqlite/__init__.py +120 -0
- coco_graph/callbacks.py +394 -0
- coco_graph/channels/__init__.py +29 -0
- coco_graph/channels/any_value.py +72 -0
- coco_graph/channels/base.py +121 -0
- coco_graph/channels/binop.py +155 -0
- coco_graph/channels/delta.py +202 -0
- coco_graph/channels/ephemeral_value.py +79 -0
- coco_graph/channels/last_value.py +151 -0
- coco_graph/channels/named_barrier_value.py +167 -0
- coco_graph/channels/topic.py +94 -0
- coco_graph/channels/untracked_value.py +73 -0
- coco_graph/checkpoint/base/__init__.py +861 -0
- coco_graph/checkpoint/base/id.py +109 -0
- coco_graph/checkpoint/base/py.typed +0 -0
- coco_graph/checkpoint/memory/__init__.py +698 -0
- coco_graph/checkpoint/memory/py.typed +0 -0
- coco_graph/checkpoint/postgres/__init__.py +602 -0
- coco_graph/checkpoint/postgres/_ainternal.py +23 -0
- coco_graph/checkpoint/postgres/_internal.py +21 -0
- coco_graph/checkpoint/postgres/aio.py +691 -0
- coco_graph/checkpoint/postgres/base.py +666 -0
- coco_graph/checkpoint/postgres/py.typed +0 -0
- coco_graph/checkpoint/postgres/shallow.py +967 -0
- coco_graph/checkpoint/serde/__init__.py +0 -0
- coco_graph/checkpoint/serde/_msgpack.py +97 -0
- coco_graph/checkpoint/serde/base.py +64 -0
- coco_graph/checkpoint/serde/encrypted.py +80 -0
- coco_graph/checkpoint/serde/event_hooks.py +52 -0
- coco_graph/checkpoint/serde/jsonplus.py +883 -0
- coco_graph/checkpoint/serde/py.typed +0 -0
- coco_graph/checkpoint/serde/types.py +68 -0
- coco_graph/checkpoint/sqlite/__init__.py +645 -0
- coco_graph/checkpoint/sqlite/_delta.py +172 -0
- coco_graph/checkpoint/sqlite/aio.py +738 -0
- coco_graph/checkpoint/sqlite/py.typed +0 -0
- coco_graph/checkpoint/sqlite/utils.py +116 -0
- coco_graph/config.py +196 -0
- coco_graph/constants.py +64 -0
- coco_graph/errors.py +241 -0
- coco_graph/func/__init__.py +620 -0
- coco_graph/graph/__init__.py +12 -0
- coco_graph/graph/_branch.py +225 -0
- coco_graph/graph/_node.py +103 -0
- coco_graph/graph/message.py +437 -0
- coco_graph/graph/state.py +1978 -0
- coco_graph/graph/ui.py +227 -0
- coco_graph/managed/__init__.py +3 -0
- coco_graph/managed/base.py +31 -0
- coco_graph/managed/is_last_step.py +24 -0
- coco_graph/prebuilt/__init__.py +23 -0
- coco_graph/prebuilt/_tool_call_stream.py +117 -0
- coco_graph/prebuilt/_tool_call_transformer.py +165 -0
- coco_graph/prebuilt/chat_agent_executor.py +1015 -0
- coco_graph/prebuilt/interrupt.py +105 -0
- coco_graph/prebuilt/py.typed +0 -0
- coco_graph/prebuilt/tool_node.py +2030 -0
- coco_graph/prebuilt/tool_validator.py +221 -0
- coco_graph/pregel/__init__.py +3 -0
- coco_graph/pregel/_algo.py +1460 -0
- coco_graph/pregel/_call.py +298 -0
- coco_graph/pregel/_checkpoint.py +331 -0
- coco_graph/pregel/_config.py +0 -0
- coco_graph/pregel/_draw.py +294 -0
- coco_graph/pregel/_executor.py +223 -0
- coco_graph/pregel/_io.py +174 -0
- coco_graph/pregel/_log.py +3 -0
- coco_graph/pregel/_loop.py +1988 -0
- coco_graph/pregel/_messages.py +461 -0
- coco_graph/pregel/_read.py +312 -0
- coco_graph/pregel/_retry.py +854 -0
- coco_graph/pregel/_runner.py +941 -0
- coco_graph/pregel/_tools.py +268 -0
- coco_graph/pregel/_utils.py +221 -0
- coco_graph/pregel/_validate.py +120 -0
- coco_graph/pregel/_write.py +192 -0
- coco_graph/pregel/debug.py +302 -0
- coco_graph/pregel/main.py +4364 -0
- coco_graph/pregel/protocol.py +288 -0
- coco_graph/pregel/types.py +38 -0
- coco_graph/py.typed +0 -0
- coco_graph/runtime.py +350 -0
- coco_graph/store/base/__init__.py +1322 -0
- coco_graph/store/base/batch.py +371 -0
- coco_graph/store/base/embed.py +436 -0
- coco_graph/store/base/py.typed +0 -0
- coco_graph/store/memory/__init__.py +592 -0
- coco_graph/store/memory/py.typed +0 -0
- coco_graph/store/postgres/__init__.py +4 -0
- coco_graph/store/postgres/aio.py +592 -0
- coco_graph/store/postgres/base.py +1480 -0
- coco_graph/store/postgres/py.typed +0 -0
- coco_graph/store/sqlite/__init__.py +4 -0
- coco_graph/store/sqlite/aio.py +632 -0
- coco_graph/store/sqlite/base.py +1514 -0
- coco_graph/stream/__init__.py +45 -0
- coco_graph/stream/_convert.py +32 -0
- coco_graph/stream/_mux.py +523 -0
- coco_graph/stream/_types.py +330 -0
- coco_graph/stream/run_stream.py +735 -0
- coco_graph/stream/stream_channel.py +341 -0
- coco_graph/stream/transformers.py +1039 -0
- coco_graph/types.py +1079 -0
- coco_graph/typing.py +48 -0
- coco_graph/utils/__init__.py +1 -0
- coco_graph/utils/config.py +4 -0
- coco_graph/utils/runnable.py +3 -0
- coco_graph/version.py +13 -0
- coco_graph/warnings.py +69 -0
- coco_openai/__init__.py +20 -0
- coco_openai/_compat.py +45 -0
- coco_openai/_version.py +3 -0
- coco_openai/chat_models/__init__.py +6 -0
- coco_openai/chat_models/_client_utils.py +684 -0
- coco_openai/chat_models/_compat.py +525 -0
- coco_openai/chat_models/azure.py +1227 -0
- coco_openai/chat_models/base.py +5654 -0
- coco_openai/chat_models/codex.py +557 -0
- coco_openai/chatgpt_oauth.py +1063 -0
- coco_openai/data/__init__.py +1 -0
- coco_openai/data/_profiles.py +1655 -0
- coco_openai/data/profile_augmentations.toml +127 -0
- coco_openai/embeddings/__init__.py +6 -0
- coco_openai/embeddings/azure.py +240 -0
- coco_openai/embeddings/base.py +820 -0
- coco_openai/llms/__init__.py +6 -0
- coco_openai/llms/azure.py +249 -0
- coco_openai/llms/base.py +919 -0
- coco_openai/middleware/__init__.py +11 -0
- coco_openai/middleware/openai_moderation.py +484 -0
- coco_openai/output_parsers/__init__.py +9 -0
- coco_openai/output_parsers/tools.py +9 -0
- coco_openai/py.typed +0 -0
- coco_openai/tools/__init__.py +5 -0
- coco_openai/tools/custom_tool.py +111 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""Load prompts."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from coco_core._api import deprecated
|
|
12
|
+
from coco_core.output_parsers.string import StrOutputParser
|
|
13
|
+
from coco_core.prompts.base import BasePromptTemplate
|
|
14
|
+
from coco_core.prompts.chat import ChatPromptTemplate
|
|
15
|
+
from coco_core.prompts.few_shot import FewShotPromptTemplate
|
|
16
|
+
from coco_core.prompts.prompt import PromptTemplate
|
|
17
|
+
|
|
18
|
+
URL_BASE = "https://raw.githubusercontent.com/hwchase17/langchain-hub/master/prompts/"
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _validate_path(path: Path) -> None:
|
|
23
|
+
"""Reject absolute paths and `..` traversal components.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
path: The path to validate.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: If the path is absolute or contains `..` components.
|
|
30
|
+
"""
|
|
31
|
+
if path.is_absolute():
|
|
32
|
+
msg = (
|
|
33
|
+
f"Path '{path}' is absolute. Absolute paths are not allowed "
|
|
34
|
+
f"when loading prompt configurations to prevent path traversal "
|
|
35
|
+
f"attacks. Use relative paths instead, or pass "
|
|
36
|
+
f"`allow_dangerous_paths=True` if you trust the input."
|
|
37
|
+
)
|
|
38
|
+
raise ValueError(msg)
|
|
39
|
+
if ".." in path.parts:
|
|
40
|
+
msg = (
|
|
41
|
+
f"Path '{path}' contains '..' components. Directory traversal "
|
|
42
|
+
f"sequences are not allowed when loading prompt configurations. "
|
|
43
|
+
f"Use direct relative paths instead, or pass "
|
|
44
|
+
f"`allow_dangerous_paths=True` if you trust the input."
|
|
45
|
+
)
|
|
46
|
+
raise ValueError(msg)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@deprecated(
|
|
50
|
+
since="1.2.21",
|
|
51
|
+
removal="2.0.0",
|
|
52
|
+
alternative="Use `dumpd`/`dumps` from `coco_core.load` to serialize "
|
|
53
|
+
"prompts and `load`/`loads` to deserialize them.",
|
|
54
|
+
)
|
|
55
|
+
def load_prompt_from_config(
|
|
56
|
+
config: dict[str, Any], *, allow_dangerous_paths: bool = False
|
|
57
|
+
) -> BasePromptTemplate[str]:
|
|
58
|
+
"""Load prompt from config dict.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
config: Dict containing the prompt configuration.
|
|
62
|
+
allow_dangerous_paths: If `False` (default), file paths in the
|
|
63
|
+
config (such as `template_path`, `examples`, and
|
|
64
|
+
`example_prompt_path`) are validated to reject absolute paths
|
|
65
|
+
and directory traversal (`..`) sequences. Set to `True` only
|
|
66
|
+
if you trust the source of the config.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A `PromptTemplate` object.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: If the prompt type is not supported.
|
|
73
|
+
"""
|
|
74
|
+
if "_type" not in config:
|
|
75
|
+
logger.warning("No `_type` key found, defaulting to `prompt`.")
|
|
76
|
+
config_type = config.pop("_type", "prompt")
|
|
77
|
+
|
|
78
|
+
if config_type not in type_to_loader_dict:
|
|
79
|
+
msg = f"Loading {config_type} prompt not supported"
|
|
80
|
+
raise ValueError(msg)
|
|
81
|
+
|
|
82
|
+
prompt_loader = type_to_loader_dict[config_type]
|
|
83
|
+
return prompt_loader(config, allow_dangerous_paths=allow_dangerous_paths)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _load_template(
|
|
87
|
+
var_name: str, config: dict[str, Any], *, allow_dangerous_paths: bool = False
|
|
88
|
+
) -> dict[str, Any]:
|
|
89
|
+
"""Load template from the path if applicable."""
|
|
90
|
+
# Check if template_path exists in config.
|
|
91
|
+
if f"{var_name}_path" in config:
|
|
92
|
+
# If it does, make sure template variable doesn't also exist.
|
|
93
|
+
if var_name in config:
|
|
94
|
+
msg = f"Both `{var_name}_path` and `{var_name}` cannot be provided."
|
|
95
|
+
raise ValueError(msg)
|
|
96
|
+
# Pop the template path from the config.
|
|
97
|
+
template_path = Path(config.pop(f"{var_name}_path"))
|
|
98
|
+
if not allow_dangerous_paths:
|
|
99
|
+
_validate_path(template_path)
|
|
100
|
+
# Resolve symlinks before checking the suffix so that a symlink named
|
|
101
|
+
# "exploit.txt" pointing to a non-.txt file is caught.
|
|
102
|
+
resolved_path = template_path.resolve()
|
|
103
|
+
# Load the template.
|
|
104
|
+
if resolved_path.suffix == ".txt":
|
|
105
|
+
template = resolved_path.read_text(encoding="utf-8")
|
|
106
|
+
else:
|
|
107
|
+
msg = (
|
|
108
|
+
f"Unsupported template file format: '{resolved_path.suffix}'. "
|
|
109
|
+
"Only '.txt' files are supported."
|
|
110
|
+
)
|
|
111
|
+
raise ValueError(msg)
|
|
112
|
+
# Set the template variable to the extracted variable.
|
|
113
|
+
config[var_name] = template
|
|
114
|
+
return config
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _load_examples(
|
|
118
|
+
config: dict[str, Any], *, allow_dangerous_paths: bool = False
|
|
119
|
+
) -> dict[str, Any]:
|
|
120
|
+
"""Load examples if necessary."""
|
|
121
|
+
if isinstance(config["examples"], list):
|
|
122
|
+
pass
|
|
123
|
+
elif isinstance(config["examples"], str):
|
|
124
|
+
path = Path(config["examples"])
|
|
125
|
+
if not allow_dangerous_paths:
|
|
126
|
+
_validate_path(path)
|
|
127
|
+
with path.open(encoding="utf-8") as f:
|
|
128
|
+
if path.suffix == ".json":
|
|
129
|
+
examples = json.load(f)
|
|
130
|
+
elif path.suffix in {".yaml", ".yml"}:
|
|
131
|
+
examples = yaml.safe_load(f)
|
|
132
|
+
else:
|
|
133
|
+
msg = "Invalid file format. Only json or yaml formats are supported."
|
|
134
|
+
raise ValueError(msg)
|
|
135
|
+
config["examples"] = examples
|
|
136
|
+
else:
|
|
137
|
+
msg = "Invalid examples format. Only list or string are supported."
|
|
138
|
+
raise ValueError(msg) # noqa:TRY004
|
|
139
|
+
return config
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _load_output_parser(config: dict[str, Any]) -> dict[str, Any]:
|
|
143
|
+
"""Load output parser."""
|
|
144
|
+
if config_ := config.get("output_parser"):
|
|
145
|
+
if output_parser_type := config_.get("_type") != "default":
|
|
146
|
+
msg = f"Unsupported output parser {output_parser_type}"
|
|
147
|
+
raise ValueError(msg)
|
|
148
|
+
config["output_parser"] = StrOutputParser(**config_)
|
|
149
|
+
return config
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _load_few_shot_prompt(
|
|
153
|
+
config: dict[str, Any], *, allow_dangerous_paths: bool = False
|
|
154
|
+
) -> FewShotPromptTemplate:
|
|
155
|
+
"""Load the "few shot" prompt from the config."""
|
|
156
|
+
# Load the suffix and prefix templates.
|
|
157
|
+
config = _load_template(
|
|
158
|
+
"suffix", config, allow_dangerous_paths=allow_dangerous_paths
|
|
159
|
+
)
|
|
160
|
+
config = _load_template(
|
|
161
|
+
"prefix", config, allow_dangerous_paths=allow_dangerous_paths
|
|
162
|
+
)
|
|
163
|
+
# Load the example prompt.
|
|
164
|
+
if "example_prompt_path" in config:
|
|
165
|
+
if "example_prompt" in config:
|
|
166
|
+
msg = (
|
|
167
|
+
"Only one of example_prompt and example_prompt_path should "
|
|
168
|
+
"be specified."
|
|
169
|
+
)
|
|
170
|
+
raise ValueError(msg)
|
|
171
|
+
example_prompt_path = Path(config.pop("example_prompt_path"))
|
|
172
|
+
if not allow_dangerous_paths:
|
|
173
|
+
_validate_path(example_prompt_path)
|
|
174
|
+
config["example_prompt"] = load_prompt(
|
|
175
|
+
example_prompt_path, allow_dangerous_paths=allow_dangerous_paths
|
|
176
|
+
)
|
|
177
|
+
else:
|
|
178
|
+
config["example_prompt"] = load_prompt_from_config(
|
|
179
|
+
config["example_prompt"], allow_dangerous_paths=allow_dangerous_paths
|
|
180
|
+
)
|
|
181
|
+
# Load the examples.
|
|
182
|
+
config = _load_examples(config, allow_dangerous_paths=allow_dangerous_paths)
|
|
183
|
+
config = _load_output_parser(config)
|
|
184
|
+
return FewShotPromptTemplate(**config)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _load_prompt(
|
|
188
|
+
config: dict[str, Any], *, allow_dangerous_paths: bool = False
|
|
189
|
+
) -> PromptTemplate:
|
|
190
|
+
"""Load the prompt template from config."""
|
|
191
|
+
# Load the template from disk if necessary.
|
|
192
|
+
config = _load_template(
|
|
193
|
+
"template", config, allow_dangerous_paths=allow_dangerous_paths
|
|
194
|
+
)
|
|
195
|
+
config = _load_output_parser(config)
|
|
196
|
+
|
|
197
|
+
template_format = config.get("template_format", "f-string")
|
|
198
|
+
if template_format == "jinja2":
|
|
199
|
+
# Disabled due to:
|
|
200
|
+
# https://github.com/langchain-ai/langchain/issues/4394
|
|
201
|
+
msg = (
|
|
202
|
+
f"Loading templates with '{template_format}' format is no longer supported "
|
|
203
|
+
f"since it can lead to arbitrary code execution. Please migrate to using "
|
|
204
|
+
f"the 'f-string' template format, which does not suffer from this issue."
|
|
205
|
+
)
|
|
206
|
+
raise ValueError(msg)
|
|
207
|
+
|
|
208
|
+
return PromptTemplate(**config)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@deprecated(
|
|
212
|
+
since="1.2.21",
|
|
213
|
+
removal="2.0.0",
|
|
214
|
+
alternative="Use `dumpd`/`dumps` from `coco_core.load` to serialize "
|
|
215
|
+
"prompts and `load`/`loads` to deserialize them.",
|
|
216
|
+
)
|
|
217
|
+
def load_prompt(
|
|
218
|
+
path: str | Path,
|
|
219
|
+
encoding: str | None = None,
|
|
220
|
+
*,
|
|
221
|
+
allow_dangerous_paths: bool = False,
|
|
222
|
+
) -> BasePromptTemplate[str]:
|
|
223
|
+
"""Unified method for loading a prompt from CocoHub or local filesystem.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
path: Path to the prompt file.
|
|
227
|
+
encoding: Encoding of the file.
|
|
228
|
+
allow_dangerous_paths: If `False` (default), file paths referenced
|
|
229
|
+
inside the loaded config (such as `template_path`, `examples`,
|
|
230
|
+
and `example_prompt_path`) are validated to reject absolute paths
|
|
231
|
+
and directory traversal (`..`) sequences. Set to `True` only
|
|
232
|
+
if you trust the source of the config.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
A `PromptTemplate` object.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
RuntimeError: If the path is a CocoHub path.
|
|
239
|
+
"""
|
|
240
|
+
if isinstance(path, str) and path.startswith("lc://"):
|
|
241
|
+
msg = (
|
|
242
|
+
"Loading from the deprecated github-based Hub is no longer supported. "
|
|
243
|
+
"Please use the new Coco Hub at https://smith.langchain.com/hub "
|
|
244
|
+
"instead."
|
|
245
|
+
)
|
|
246
|
+
raise RuntimeError(msg)
|
|
247
|
+
return _load_prompt_from_file(
|
|
248
|
+
path, encoding, allow_dangerous_paths=allow_dangerous_paths
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _load_prompt_from_file(
|
|
253
|
+
file: str | Path,
|
|
254
|
+
encoding: str | None = None,
|
|
255
|
+
*,
|
|
256
|
+
allow_dangerous_paths: bool = False,
|
|
257
|
+
) -> BasePromptTemplate[str]:
|
|
258
|
+
"""Load prompt from file."""
|
|
259
|
+
# Convert file to a Path object.
|
|
260
|
+
file_path = Path(file)
|
|
261
|
+
# Load from either json or yaml.
|
|
262
|
+
if file_path.suffix == ".json":
|
|
263
|
+
with file_path.open(encoding=encoding) as f:
|
|
264
|
+
config = json.load(f)
|
|
265
|
+
elif file_path.suffix.endswith((".yaml", ".yml")):
|
|
266
|
+
with file_path.open(encoding=encoding) as f:
|
|
267
|
+
config = yaml.safe_load(f)
|
|
268
|
+
else:
|
|
269
|
+
msg = f"Got unsupported file type {file_path.suffix}"
|
|
270
|
+
raise ValueError(msg)
|
|
271
|
+
# Load the prompt from the config now.
|
|
272
|
+
return load_prompt_from_config(config, allow_dangerous_paths=allow_dangerous_paths)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _load_chat_prompt(
|
|
276
|
+
config: dict[str, Any],
|
|
277
|
+
*,
|
|
278
|
+
allow_dangerous_paths: bool = False, # noqa: ARG001
|
|
279
|
+
) -> ChatPromptTemplate:
|
|
280
|
+
"""Load chat prompt from config."""
|
|
281
|
+
messages = config.pop("messages")
|
|
282
|
+
template = messages[0]["prompt"].pop("template") if messages else None
|
|
283
|
+
config.pop("input_variables")
|
|
284
|
+
|
|
285
|
+
if not template:
|
|
286
|
+
msg = "Can't load chat prompt without template"
|
|
287
|
+
raise ValueError(msg)
|
|
288
|
+
|
|
289
|
+
return ChatPromptTemplate.from_template(template=template, **config)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
type_to_loader_dict: dict[str, Callable[..., BasePromptTemplate[str]]] = {
|
|
293
|
+
"prompt": _load_prompt,
|
|
294
|
+
"few_shot": _load_few_shot_prompt,
|
|
295
|
+
"chat": _load_chat_prompt,
|
|
296
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Message prompt templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from coco_core.load import Serializable
|
|
9
|
+
from coco_core.utils.interactive_env import is_interactive_env
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from coco_core.messages import BaseMessage
|
|
13
|
+
from coco_core.prompts.chat import ChatPromptTemplate
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseMessagePromptTemplate(Serializable, ABC):
|
|
17
|
+
"""Base class for message prompt templates."""
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def is_lc_serializable(cls) -> bool:
|
|
21
|
+
"""Return `True` as this class is serializable."""
|
|
22
|
+
return True
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def get_lc_namespace(cls) -> list[str]:
|
|
26
|
+
"""Get the namespace of the Coco object.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
`["coco", "prompts", "chat"]`
|
|
30
|
+
"""
|
|
31
|
+
return ["coco", "prompts", "chat"]
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def format_messages(self, **kwargs: Any) -> list[BaseMessage]:
|
|
35
|
+
"""Format messages from kwargs.
|
|
36
|
+
|
|
37
|
+
Should return a list of `BaseMessage` objects.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
**kwargs: Keyword arguments to use for formatting.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of `BaseMessage` objects.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
async def aformat_messages(self, **kwargs: Any) -> list[BaseMessage]:
|
|
47
|
+
"""Async format messages from kwargs.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
**kwargs: Keyword arguments to use for formatting.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
List of `BaseMessage` objects.
|
|
54
|
+
"""
|
|
55
|
+
return self.format_messages(**kwargs)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def input_variables(self) -> list[str]:
|
|
60
|
+
"""Input variables for this prompt template.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of input variables.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def pretty_repr(
|
|
67
|
+
self,
|
|
68
|
+
html: bool = False, # noqa: FBT001,FBT002
|
|
69
|
+
) -> str:
|
|
70
|
+
"""Human-readable representation.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
html: Whether to format as HTML.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Human-readable representation.
|
|
77
|
+
"""
|
|
78
|
+
raise NotImplementedError
|
|
79
|
+
|
|
80
|
+
def pretty_print(self) -> None:
|
|
81
|
+
"""Print a human-readable representation."""
|
|
82
|
+
print(self.pretty_repr(html=is_interactive_env())) # noqa: T201
|
|
83
|
+
|
|
84
|
+
def __add__(self, other: Any) -> ChatPromptTemplate:
|
|
85
|
+
"""Combine two prompt templates.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
other: Another prompt template.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Combined prompt template.
|
|
92
|
+
"""
|
|
93
|
+
# Import locally to avoid circular import.
|
|
94
|
+
from coco_core.prompts.chat import ChatPromptTemplate # noqa: PLC0415
|
|
95
|
+
|
|
96
|
+
prompt = ChatPromptTemplate(messages=[self])
|
|
97
|
+
return prompt.__add__(other)
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Prompt schema definition."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, model_validator
|
|
9
|
+
from typing_extensions import override
|
|
10
|
+
|
|
11
|
+
from coco_core.prompts.string import (
|
|
12
|
+
DEFAULT_FORMATTER_MAPPING,
|
|
13
|
+
PromptTemplateFormat,
|
|
14
|
+
StringPromptTemplate,
|
|
15
|
+
check_valid_template,
|
|
16
|
+
get_template_variables,
|
|
17
|
+
mustache_schema,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from coco_core.runnables.config import RunnableConfig
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PromptTemplate(StringPromptTemplate):
|
|
25
|
+
"""Prompt template for a language model.
|
|
26
|
+
|
|
27
|
+
A prompt template consists of a string template. It accepts a set of parameters
|
|
28
|
+
from the user that can be used to generate a prompt for a language model.
|
|
29
|
+
|
|
30
|
+
The template can be formatted using either f-strings (default), jinja2, or mustache
|
|
31
|
+
syntax.
|
|
32
|
+
|
|
33
|
+
!!! warning "Security"
|
|
34
|
+
|
|
35
|
+
Prefer using `template_format='f-string'` instead of `template_format='jinja2'`,
|
|
36
|
+
or make sure to NEVER accept jinja2 templates from untrusted sources as they may
|
|
37
|
+
lead to arbitrary Python code execution.
|
|
38
|
+
|
|
39
|
+
As of Coco 0.0.329, Jinja2 templates will be rendered using Jinja2's
|
|
40
|
+
SandboxedEnvironment by default. This sand-boxing should be treated as a
|
|
41
|
+
best-effort approach rather than a guarantee of security, as it is an opt-out
|
|
42
|
+
rather than opt-in approach.
|
|
43
|
+
|
|
44
|
+
Despite the sandboxing, we recommend to never use jinja2 templates from
|
|
45
|
+
untrusted sources.
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
```python
|
|
49
|
+
from coco_core.prompts import PromptTemplate
|
|
50
|
+
|
|
51
|
+
# Instantiation using from_template (recommended)
|
|
52
|
+
prompt = PromptTemplate.from_template("Say {foo}")
|
|
53
|
+
prompt.format(foo="bar")
|
|
54
|
+
|
|
55
|
+
# Instantiation using initializer
|
|
56
|
+
prompt = PromptTemplate(template="Say {foo}")
|
|
57
|
+
```
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@override
|
|
62
|
+
def lc_attributes(self) -> dict[str, Any]:
|
|
63
|
+
return {
|
|
64
|
+
"template_format": self.template_format,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
@override
|
|
69
|
+
def get_lc_namespace(cls) -> list[str]:
|
|
70
|
+
"""Get the namespace of the Coco object.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
`["coco", "prompts", "prompt"]`
|
|
74
|
+
"""
|
|
75
|
+
return ["coco", "prompts", "prompt"]
|
|
76
|
+
|
|
77
|
+
template: str
|
|
78
|
+
"""The prompt template."""
|
|
79
|
+
|
|
80
|
+
template_format: PromptTemplateFormat = "f-string"
|
|
81
|
+
"""The format of the prompt template.
|
|
82
|
+
|
|
83
|
+
Options are: `'f-string'`, `'mustache'`, `'jinja2'`.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
validate_template: bool = False
|
|
87
|
+
"""Whether or not to try validating the template."""
|
|
88
|
+
|
|
89
|
+
@model_validator(mode="before")
|
|
90
|
+
@classmethod
|
|
91
|
+
def pre_init_validation(cls, values: dict[str, Any]) -> Any:
|
|
92
|
+
"""Check that template and input variables are consistent."""
|
|
93
|
+
if values.get("template") is None:
|
|
94
|
+
# Will let pydantic fail with a ValidationError if template
|
|
95
|
+
# is not provided.
|
|
96
|
+
return values
|
|
97
|
+
|
|
98
|
+
# Set some default values based on the field defaults
|
|
99
|
+
values.setdefault("template_format", "f-string")
|
|
100
|
+
values.setdefault("partial_variables", {})
|
|
101
|
+
|
|
102
|
+
if values.get("validate_template"):
|
|
103
|
+
if values["template_format"] == "mustache":
|
|
104
|
+
msg = "Mustache templates cannot be validated."
|
|
105
|
+
raise ValueError(msg)
|
|
106
|
+
|
|
107
|
+
if "input_variables" not in values:
|
|
108
|
+
msg = "Input variables must be provided to validate the template."
|
|
109
|
+
raise ValueError(msg)
|
|
110
|
+
|
|
111
|
+
all_inputs = values["input_variables"] + list(values["partial_variables"])
|
|
112
|
+
check_valid_template(
|
|
113
|
+
values["template"], values["template_format"], all_inputs
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if values["template_format"]:
|
|
117
|
+
values["input_variables"] = [
|
|
118
|
+
var
|
|
119
|
+
for var in get_template_variables(
|
|
120
|
+
values["template"], values["template_format"]
|
|
121
|
+
)
|
|
122
|
+
if var not in values["partial_variables"]
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
return values
|
|
126
|
+
|
|
127
|
+
@override
|
|
128
|
+
def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]:
|
|
129
|
+
"""Get the input schema for the prompt.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
config: The runnable configuration.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The input schema for the prompt.
|
|
136
|
+
"""
|
|
137
|
+
if self.template_format != "mustache":
|
|
138
|
+
return super().get_input_schema(config)
|
|
139
|
+
|
|
140
|
+
return mustache_schema(self.template)
|
|
141
|
+
|
|
142
|
+
def __add__(self, other: Any) -> PromptTemplate:
|
|
143
|
+
"""Override the `+` operator to allow for combining prompt templates.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValueError: If the template formats are not f-string or if there are
|
|
147
|
+
conflicting partial variables.
|
|
148
|
+
NotImplementedError: If the other object is not a `PromptTemplate` or str.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
A new `PromptTemplate` that is the combination of the two.
|
|
152
|
+
"""
|
|
153
|
+
# Allow for easy combining
|
|
154
|
+
if isinstance(other, PromptTemplate):
|
|
155
|
+
if self.template_format != other.template_format:
|
|
156
|
+
msg = "Cannot add templates of different formats"
|
|
157
|
+
raise ValueError(msg)
|
|
158
|
+
input_variables = list(
|
|
159
|
+
set(self.input_variables) | set(other.input_variables)
|
|
160
|
+
)
|
|
161
|
+
template = self.template + other.template
|
|
162
|
+
# If any do not want to validate, then don't
|
|
163
|
+
validate_template = self.validate_template and other.validate_template
|
|
164
|
+
partial_variables = dict(self.partial_variables.items())
|
|
165
|
+
for k, v in other.partial_variables.items():
|
|
166
|
+
if k in partial_variables:
|
|
167
|
+
msg = "Cannot have same variable partialed twice."
|
|
168
|
+
raise ValueError(msg)
|
|
169
|
+
partial_variables[k] = v
|
|
170
|
+
return PromptTemplate(
|
|
171
|
+
template=template,
|
|
172
|
+
input_variables=input_variables,
|
|
173
|
+
partial_variables=partial_variables,
|
|
174
|
+
template_format=self.template_format,
|
|
175
|
+
validate_template=validate_template,
|
|
176
|
+
)
|
|
177
|
+
if isinstance(other, str):
|
|
178
|
+
prompt = PromptTemplate.from_template(
|
|
179
|
+
other,
|
|
180
|
+
template_format=self.template_format,
|
|
181
|
+
)
|
|
182
|
+
return self + prompt
|
|
183
|
+
msg = f"Unsupported operand type for +: {type(other)}"
|
|
184
|
+
raise NotImplementedError(msg)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def _prompt_type(self) -> str:
|
|
188
|
+
"""Return the prompt type key."""
|
|
189
|
+
return "prompt"
|
|
190
|
+
|
|
191
|
+
def format(self, **kwargs: Any) -> str:
|
|
192
|
+
"""Format the prompt with the inputs.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
**kwargs: Any arguments to be passed to the prompt template.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
A formatted string.
|
|
199
|
+
"""
|
|
200
|
+
kwargs = self._merge_partial_and_user_variables(**kwargs)
|
|
201
|
+
return DEFAULT_FORMATTER_MAPPING[self.template_format](self.template, **kwargs)
|
|
202
|
+
|
|
203
|
+
@classmethod
|
|
204
|
+
def from_examples(
|
|
205
|
+
cls,
|
|
206
|
+
examples: list[str],
|
|
207
|
+
suffix: str,
|
|
208
|
+
input_variables: list[str],
|
|
209
|
+
example_separator: str = "\n\n",
|
|
210
|
+
prefix: str = "",
|
|
211
|
+
**kwargs: Any,
|
|
212
|
+
) -> PromptTemplate:
|
|
213
|
+
"""Take examples in list format with prefix and suffix to create a prompt.
|
|
214
|
+
|
|
215
|
+
Intended to be used as a way to dynamically create a prompt from examples.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
examples: List of examples to use in the prompt.
|
|
219
|
+
suffix: String to go after the list of examples.
|
|
220
|
+
|
|
221
|
+
Should generally set up the user's input.
|
|
222
|
+
input_variables: A list of variable names the final prompt template will
|
|
223
|
+
expect.
|
|
224
|
+
example_separator: The separator to use in between examples.
|
|
225
|
+
prefix: String that should go before any examples.
|
|
226
|
+
|
|
227
|
+
Generally includes examples.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
The final prompt generated.
|
|
231
|
+
"""
|
|
232
|
+
template = example_separator.join([prefix, *examples, suffix])
|
|
233
|
+
return cls(input_variables=input_variables, template=template, **kwargs)
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def from_file(
|
|
237
|
+
cls,
|
|
238
|
+
template_file: str | Path,
|
|
239
|
+
encoding: str | None = None,
|
|
240
|
+
**kwargs: Any,
|
|
241
|
+
) -> PromptTemplate:
|
|
242
|
+
"""Load a prompt from a file.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
template_file: The path to the file containing the prompt template.
|
|
246
|
+
encoding: The encoding system for opening the template file.
|
|
247
|
+
|
|
248
|
+
If not provided, will use the OS default.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
The prompt loaded from the file.
|
|
252
|
+
"""
|
|
253
|
+
template = Path(template_file).read_text(encoding=encoding)
|
|
254
|
+
return cls.from_template(template=template, **kwargs)
|
|
255
|
+
|
|
256
|
+
@classmethod
|
|
257
|
+
def from_template(
|
|
258
|
+
cls,
|
|
259
|
+
template: str,
|
|
260
|
+
*,
|
|
261
|
+
template_format: PromptTemplateFormat = "f-string",
|
|
262
|
+
partial_variables: dict[str, Any] | None = None,
|
|
263
|
+
**kwargs: Any,
|
|
264
|
+
) -> PromptTemplate:
|
|
265
|
+
"""Load a prompt template from a template.
|
|
266
|
+
|
|
267
|
+
!!! warning "Security"
|
|
268
|
+
|
|
269
|
+
Prefer using `template_format='f-string'` instead of
|
|
270
|
+
`template_format='jinja2'`, or make sure to NEVER accept jinja2 templates
|
|
271
|
+
from untrusted sources as they may lead to arbitrary Python code execution.
|
|
272
|
+
|
|
273
|
+
As of Coco 0.0.329, Jinja2 templates will be rendered using Jinja2's
|
|
274
|
+
SandboxedEnvironment by default. This sand-boxing should be treated as a
|
|
275
|
+
best-effort approach rather than a guarantee of security, as it is an
|
|
276
|
+
opt-out rather than opt-in approach.
|
|
277
|
+
|
|
278
|
+
Despite the sandboxing, we recommend to never use jinja2 templates from
|
|
279
|
+
untrusted sources.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
template: The template to load.
|
|
283
|
+
template_format: The format of the template.
|
|
284
|
+
|
|
285
|
+
Use `jinja2` for jinja2, `mustache` for mustache, and `f-string` for
|
|
286
|
+
f-strings.
|
|
287
|
+
partial_variables: A dictionary of variables that can be used to partially
|
|
288
|
+
fill in the template.
|
|
289
|
+
|
|
290
|
+
For example, if the template is `'{variable1} {variable2}'`, and
|
|
291
|
+
`partial_variables` is `{"variable1": "foo"}`, then the final prompt
|
|
292
|
+
will be `'foo {variable2}'`.
|
|
293
|
+
**kwargs: Any other arguments to pass to the prompt template.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
The prompt template loaded from the template.
|
|
297
|
+
"""
|
|
298
|
+
input_variables = get_template_variables(template, template_format)
|
|
299
|
+
partial_variables_ = partial_variables or {}
|
|
300
|
+
|
|
301
|
+
if partial_variables_:
|
|
302
|
+
input_variables = [
|
|
303
|
+
var for var in input_variables if var not in partial_variables_
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
return cls(
|
|
307
|
+
input_variables=input_variables,
|
|
308
|
+
template=template,
|
|
309
|
+
template_format=template_format,
|
|
310
|
+
partial_variables=partial_variables_,
|
|
311
|
+
**kwargs,
|
|
312
|
+
)
|