"""聊天提示模板。"""
from __future__ import annotations
from abc import ABC, abstractmethod
from pathlib import Path
from typing import (
Any,
Dict,
List,
Literal,
Optional,
Sequence,
Set,
Tuple,
Type,
TypedDict,
TypeVar,
Union,
cast,
overload,
)
from langchain_core._api import deprecated
from langchain_core.load import Serializable
from langchain_core.messages import (
AIMessage,
AnyMessage,
BaseMessage,
ChatMessage,
HumanMessage,
SystemMessage,
convert_to_messages,
)
from langchain_core.messages.base import get_msg_title_repr
from langchain_core.prompt_values import ChatPromptValue, ImageURL, PromptValue
from langchain_core.prompts.base import BasePromptTemplate
from langchain_core.prompts.image import ImagePromptTemplate
from langchain_core.prompts.prompt import PromptTemplate
from langchain_core.prompts.string import StringPromptTemplate, get_template_variables
from langchain_core.pydantic_v1 import Field, root_validator
from langchain_core.utils import get_colored_text
from langchain_core.utils.interactive_env import is_interactive_env
[docs]class BaseMessagePromptTemplate(Serializable, ABC):
"""消息提示模板的基类。"""
[docs] @classmethod
def is_lc_serializable(cls) -> bool:
"""返回类是否可序列化。"""
return True
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
@property
@abstractmethod
def input_variables(self) -> List[str]:
"""这个提示模板的输入变量。
返回:
输入变量的列表。
"""
[docs] def pretty_repr(self, html: bool = False) -> str:
"""可读性强的表示。"""
raise NotImplementedError
[docs] def pretty_print(self) -> None:
print(self.pretty_repr(html=is_interactive_env())) # noqa: T201
def __add__(self, other: Any) -> ChatPromptTemplate:
"""合并两个提示模板。
参数:
other: 另一个提示模板。
返回:
合并后的提示模板。
"""
prompt = ChatPromptTemplate(messages=[self]) # type: ignore[call-arg]
return prompt + other
[docs]class MessagesPlaceholder(BaseMessagePromptTemplate):
"""提示模板,假设变量已经是消息列表。
一个占位符,可用于传递消息列表。
直接使用:
.. code-block:: python
from langchain_core.prompts import MessagesPlaceholder
prompt = MessagesPlaceholder("history")
prompt.format_messages() # raises KeyError
prompt = MessagesPlaceholder("history", optional=True)
prompt.format_messages() # returns empty list []
prompt.format_messages(
history=[
("system", "You are an AI assistant."),
("human", "Hello!"),
]
)
# -> [
# SystemMessage(content="You are an AI assistant."),
# HumanMessage(content="Hello!"),
# ]
构建具有聊天历史的提示:
.. code-block:: python
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful assistant."),
MessagesPlaceholder("history"),
("human", "{question}")
]
)
prompt.invoke(
{
"history": [("human", "what's 5 + 2"), ("ai", "5 + 2 is 7")],
"question": "now multiply that by 4"
}
)
# -> ChatPromptValue(messages=[
# SystemMessage(content="You are a helpful assistant."),
# HumanMessage(content="what's 5 + 2"),
# AIMessage(content="5 + 2 is 7"),
# HumanMessage(content="now multiply that by 4"),
# ])
"""
variable_name: str
"""用作消息的变量名称。"""
optional: bool = False
"""如果为True,则可以无需参数调用format_messages,并返回一个空列表。如果为False,则必须传入一个名为`variable_name`的命名参数,即使其值为空列表。"""
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
def __init__(self, variable_name: str, *, optional: bool = False, **kwargs: Any):
super().__init__(variable_name=variable_name, optional=optional, **kwargs)
@property
def input_variables(self) -> List[str]:
"""这个提示模板的输入变量。
返回:
输入变量名称的列表。
"""
return [self.variable_name] if not self.optional else []
[docs] def pretty_repr(self, html: bool = False) -> str:
var = "{" + self.variable_name + "}"
if html:
title = get_msg_title_repr("Messages Placeholder", bold=True)
var = get_colored_text(var, "yellow")
else:
title = get_msg_title_repr("Messages Placeholder")
return f"{title}\n\n{var}"
MessagePromptTemplateT = TypeVar(
"MessagePromptTemplateT", bound="BaseStringMessagePromptTemplate"
)
"""Type variable for message prompt templates."""
[docs]class BaseStringMessagePromptTemplate(BaseMessagePromptTemplate, ABC):
"""用于使用字符串提示模板的消息提示模板的基类。"""
prompt: StringPromptTemplate
"""字符串提示模板。"""
additional_kwargs: dict = Field(default_factory=dict)
"""传递给提示模板的额外关键字参数。"""
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
[docs] @classmethod
def from_template(
cls: Type[MessagePromptTemplateT],
template: str,
template_format: str = "f-string",
partial_variables: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> MessagePromptTemplateT:
"""从字符串模板创建一个类。
参数:
template: 一个模板。
template_format: 模板的格式。
partial_variables: 一个变量字典,可以用来部分填充模板。例如,如果模板是`"{variable1} {variable2}"`,而`partial_variables`是`{"variable1": "foo"}`,那么最终的提示将是`"foo {variable2}"`。
**kwargs: 传递给构造函数的关键字参数。
返回:
这个类的一个新实例。
"""
prompt = PromptTemplate.from_template(
template,
template_format=template_format,
partial_variables=partial_variables,
)
return cls(prompt=prompt, **kwargs)
[docs] @classmethod
def from_template_file(
cls: Type[MessagePromptTemplateT],
template_file: Union[str, Path],
input_variables: List[str],
**kwargs: Any,
) -> MessagePromptTemplateT:
"""从模板文件创建一个类。
参数:
template_file: 模板文件的路径。字符串或路径。
input_variables: 输入变量的列表。
**kwargs: 传递给构造函数的关键字参数。
返回:
这个类的一个新实例。
"""
prompt = PromptTemplate.from_file(template_file, input_variables)
return cls(prompt=prompt, **kwargs)
@property
def input_variables(self) -> List[str]:
"""这个提示模板的输入变量。
返回:
输入变量名称的列表。
"""
return self.prompt.input_variables
[docs] def pretty_repr(self, html: bool = False) -> str:
# TODO: Handle partials
title = self.__class__.__name__.replace("MessagePromptTemplate", " Message")
title = get_msg_title_repr(title, bold=html)
return f"{title}\n\n{self.prompt.pretty_repr(html=html)}"
[docs]class ChatMessagePromptTemplate(BaseStringMessagePromptTemplate):
"""聊天消息提示模板。"""
role: str
"""消息的作用。"""
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
_StringImageMessagePromptTemplateT = TypeVar(
"_StringImageMessagePromptTemplateT", bound="_StringImageMessagePromptTemplate"
)
class _TextTemplateParam(TypedDict, total=False):
text: Union[str, Dict]
class _ImageTemplateParam(TypedDict, total=False):
image_url: Union[str, Dict]
class _StringImageMessagePromptTemplate(BaseMessagePromptTemplate):
"""人类消息提示模板。这是用户发送的消息。"""
prompt: Union[
StringPromptTemplate, List[Union[StringPromptTemplate, ImagePromptTemplate]]
]
"""提示模板。"""
additional_kwargs: dict = Field(default_factory=dict)
"""传递给提示模板的额外关键字参数。"""
_msg_class: Type[BaseMessage]
@classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
@classmethod
def from_template(
cls: Type[_StringImageMessagePromptTemplateT],
template: Union[str, List[Union[str, _TextTemplateParam, _ImageTemplateParam]]],
template_format: str = "f-string",
**kwargs: Any,
) -> _StringImageMessagePromptTemplateT:
"""从字符串模板创建一个类。
参数:
template: 一个模板。
template_format: 模板的格式。
**kwargs: 传递给构造函数的关键字参数。
返回:
此类的一个新实例。
"""
if isinstance(template, str):
prompt: Union[StringPromptTemplate, List] = PromptTemplate.from_template(
template, template_format=template_format
)
return cls(prompt=prompt, **kwargs)
elif isinstance(template, list):
prompt = []
for tmpl in template:
if isinstance(tmpl, str) or isinstance(tmpl, dict) and "text" in tmpl:
if isinstance(tmpl, str):
text: str = tmpl
else:
text = cast(_TextTemplateParam, tmpl)["text"] # type: ignore[assignment] # noqa: E501
prompt.append(
PromptTemplate.from_template(
text, template_format=template_format
)
)
elif isinstance(tmpl, dict) and "image_url" in tmpl:
img_template = cast(_ImageTemplateParam, tmpl)["image_url"]
if isinstance(img_template, str):
vars = get_template_variables(img_template, "f-string")
if vars:
if len(vars) > 1:
raise ValueError(
"Only one format variable allowed per image"
f" template.\nGot: {vars}"
f"\nFrom: {tmpl}"
)
input_variables = [vars[0]]
else:
input_variables = None
img_template = {"url": img_template}
img_template_obj = ImagePromptTemplate(
input_variables=input_variables, template=img_template
)
elif isinstance(img_template, dict):
img_template = dict(img_template)
if "url" in img_template:
input_variables = get_template_variables(
img_template["url"], "f-string"
)
else:
input_variables = None
img_template_obj = ImagePromptTemplate(
input_variables=input_variables, template=img_template
)
else:
raise ValueError()
prompt.append(img_template_obj)
else:
raise ValueError()
return cls(prompt=prompt, **kwargs)
else:
raise ValueError()
@classmethod
def from_template_file(
cls: Type[_StringImageMessagePromptTemplateT],
template_file: Union[str, Path],
input_variables: List[str],
**kwargs: Any,
) -> _StringImageMessagePromptTemplateT:
"""从模板文件创建一个类。
参数:
template_file: 模板文件的路径。字符串或路径。
input_variables: 输入变量的列表。
**kwargs: 传递给构造函数的关键字参数。
返回:
这个类的一个新实例。
"""
with open(str(template_file), "r") as f:
template = f.read()
return cls.from_template(template, input_variables=input_variables, **kwargs)
def format_messages(self, **kwargs: Any) -> List[BaseMessage]:
"""格式化kwargs中的消息。
参数:
**kwargs:用于格式化的关键字参数。
返回:
BaseMessages的列表。
"""
return [self.format(**kwargs)]
async def aformat_messages(self, **kwargs: Any) -> List[BaseMessage]:
return [await self.aformat(**kwargs)]
@property
def input_variables(self) -> List[str]:
"""这个提示模板的输入变量。
返回:
输入变量名称的列表。
"""
prompts = self.prompt if isinstance(self.prompt, list) else [self.prompt]
input_variables = [iv for prompt in prompts for iv in prompt.input_variables]
return input_variables
def format(self, **kwargs: Any) -> BaseMessage:
"""格式化提示模板。
参数:
**kwargs: 用于格式化的关键字参数。
返回:
格式化后的消息。
"""
if isinstance(self.prompt, StringPromptTemplate):
text = self.prompt.format(**kwargs)
return self._msg_class(
content=text, additional_kwargs=self.additional_kwargs
)
else:
content: List = []
for prompt in self.prompt:
inputs = {var: kwargs[var] for var in prompt.input_variables}
if isinstance(prompt, StringPromptTemplate):
formatted: Union[str, ImageURL] = prompt.format(**inputs)
content.append({"type": "text", "text": formatted})
elif isinstance(prompt, ImagePromptTemplate):
formatted = prompt.format(**inputs)
content.append({"type": "image_url", "image_url": formatted})
return self._msg_class(
content=content, additional_kwargs=self.additional_kwargs
)
async def aformat(self, **kwargs: Any) -> BaseMessage:
"""格式化提示模板。
参数:
**kwargs: 用于格式化的关键字参数。
返回:
格式化后的消息。
"""
if isinstance(self.prompt, StringPromptTemplate):
text = await self.prompt.aformat(**kwargs)
return self._msg_class(
content=text, additional_kwargs=self.additional_kwargs
)
else:
content: List = []
for prompt in self.prompt:
inputs = {var: kwargs[var] for var in prompt.input_variables}
if isinstance(prompt, StringPromptTemplate):
formatted: Union[str, ImageURL] = await prompt.aformat(**inputs)
content.append({"type": "text", "text": formatted})
elif isinstance(prompt, ImagePromptTemplate):
formatted = await prompt.aformat(**inputs)
content.append({"type": "image_url", "image_url": formatted})
return self._msg_class(
content=content, additional_kwargs=self.additional_kwargs
)
def pretty_repr(self, html: bool = False) -> str:
# TODO: Handle partials
title = self.__class__.__name__.replace("MessagePromptTemplate", " Message")
title = get_msg_title_repr(title, bold=html)
prompts = self.prompt if isinstance(self.prompt, list) else [self.prompt]
prompt_reprs = "\n\n".join(prompt.pretty_repr(html=html) for prompt in prompts)
return f"{title}\n\n{prompt_reprs}"
[docs]class HumanMessagePromptTemplate(_StringImageMessagePromptTemplate):
"""人类消息提示模板。这是用户发送的消息。"""
_msg_class: Type[BaseMessage] = HumanMessage
[docs]class AIMessagePromptTemplate(_StringImageMessagePromptTemplate):
"""AI消息提示模板。这是AI发送的消息。"""
_msg_class: Type[BaseMessage] = AIMessage
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
[docs]class SystemMessagePromptTemplate(_StringImageMessagePromptTemplate):
"""系统消息提示模板。
这是一条不发送给用户的消息。
"""
_msg_class: Type[BaseMessage] = SystemMessage
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
[docs]class BaseChatPromptTemplate(BasePromptTemplate, ABC):
"""用于聊天提示模板的基类。"""
@property
def lc_attributes(self) -> Dict:
"""返回一个属性名称列表,这些属性应该包含在序列化的kwargs中。这些属性必须被构造函数接受。
"""
return {"input_variables": self.input_variables}
[docs] def pretty_repr(self, html: bool = False) -> str:
"""可读性强的表示。"""
raise NotImplementedError
[docs] def pretty_print(self) -> None:
print(self.pretty_repr(html=is_interactive_env())) # noqa: T201
MessageLike = Union[BaseMessagePromptTemplate, BaseMessage, BaseChatPromptTemplate]
MessageLikeRepresentation = Union[
MessageLike,
Tuple[
Union[str, Type],
Union[str, List[dict], List[object]],
],
str,
]
[docs]class ChatPromptTemplate(BaseChatPromptTemplate):
"""聊天模型的提示模板。
用于为聊天模型创建灵活的模板化提示。
示例:
.. code-block:: python
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI bot. Your name is {name}."),
("human", "Hello, how are you doing?"),
("ai", "I'm doing well, thanks!"),
("human", "{user_input}"),
])
prompt_value = template.invoke(
{
"name": "Bob",
"user_input": "What is your name?"
}
)
# 输出:
# ChatPromptValue(
# messages=[
# SystemMessage(content='You are a helpful AI bot. Your name is Bob.'),
# HumanMessage(content='Hello, how are you doing?'),
# AIMessage(content="I'm doing well, thanks!"),
# HumanMessage(content='What is your name?')
# ]
#)
消息占位符:
.. code-block:: python
# 除了Human/AI/Tool/Function消息外,
# 您还可以使用MessagesPlaceholder初始化模板,
# 可以直接使用类或使用简写元组语法:
template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI bot."),
# 意味着模板将在“conversation”键下接收一个可选的消息列表
("placeholder", "{conversation}")
# 等效写法:
# MessagesPlaceholder(variable_name="conversation", optional=True)
])
prompt_value = template.invoke(
{
"conversation": [
("human", "Hi!"),
("ai", "How can I assist you today?"),
("human", "Can you make me an ice cream sundae?"),
("ai", "No.")
]
}
)
# 输出:
# ChatPromptValue(
# messages=[
# SystemMessage(content='You are a helpful AI bot.'),
# HumanMessage(content='Hi!'),
# AIMessage(content='How can I assist you today?'),
# HumanMessage(content='Can you make me an ice cream sundae?'),
# AIMessage(content='No.'),
# ]
#)
单变量模板:
如果您的提示只有一个输入变量(即,1个实例的“{variable_nams}”),
并且您使用非字典对象调用模板,则提示模板将将提供的参数注入到该变量位置。
.. code-block:: python
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI bot. Your name is Carl."),
("human", "{user_input}"),
])
prompt_value = template.invoke("Hello, there!")
# 等效于
# prompt_value = template.invoke({"user_input": "Hello, there!"})
# 输出:
# ChatPromptValue(
# messages=[
# SystemMessage(content='You are a helpful AI bot. Your name is Carl.'),
# HumanMessage(content='Hello, there!'),
# ]
# )
""" # noqa: E501
input_variables: List[str]
"""模板消息中的输入变量列表。用于验证。"""
messages: List[MessageLike]
"""消息列表,包括消息提示模板或消息。"""
validate_template: bool = False
"""是否尝试验证模板。"""
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "prompts", "chat"]
def __add__(self, other: Any) -> ChatPromptTemplate:
"""合并两个提示模板。
参数:
other: 另一个提示模板。
返回:
合并后的提示模板。
"""
# Allow for easy combining
if isinstance(other, ChatPromptTemplate):
return ChatPromptTemplate(messages=self.messages + other.messages) # type: ignore[call-arg]
elif isinstance(
other, (BaseMessagePromptTemplate, BaseMessage, BaseChatPromptTemplate)
):
return ChatPromptTemplate(messages=self.messages + [other]) # type: ignore[call-arg]
elif isinstance(other, (list, tuple)):
_other = ChatPromptTemplate.from_messages(other)
return ChatPromptTemplate(messages=self.messages + _other.messages) # type: ignore[call-arg]
elif isinstance(other, str):
prompt = HumanMessagePromptTemplate.from_template(other)
return ChatPromptTemplate(messages=self.messages + [prompt]) # type: ignore[call-arg]
else:
raise NotImplementedError(f"Unsupported operand type for +: {type(other)}")
@root_validator(pre=True)
def validate_input_variables(cls, values: dict) -> dict:
"""验证输入变量。
如果未设置input_variables,则将其设置为消息中所有输入变量的并集。
参数:
values:要验证的值。
返回:
经过验证的值。
"""
messages = values["messages"]
input_vars = set()
input_types: Dict[str, Any] = values.get("input_types", {})
for message in messages:
if isinstance(message, (BaseMessagePromptTemplate, BaseChatPromptTemplate)):
input_vars.update(message.input_variables)
if isinstance(message, MessagesPlaceholder):
if message.variable_name not in input_types:
input_types[message.variable_name] = List[AnyMessage]
if "partial_variables" in values:
input_vars = input_vars - set(values["partial_variables"])
if "input_variables" in values and values.get("validate_template"):
if input_vars != set(values["input_variables"]):
raise ValueError(
"Got mismatched input_variables. "
f"Expected: {input_vars}. "
f"Got: {values['input_variables']}"
)
else:
values["input_variables"] = sorted(input_vars)
values["input_types"] = input_types
return values
[docs] @classmethod
def from_template(cls, template: str, **kwargs: Any) -> ChatPromptTemplate:
"""从模板字符串创建一个聊天提示模板。
创建一个聊天模板,由假定来自人类的单个消息组成。
参数:
模板:模板字符串
**kwargs:传递给构造函数的关键字参数。
返回:
此类的一个新实例。
"""
prompt_template = PromptTemplate.from_template(template, **kwargs)
message = HumanMessagePromptTemplate(prompt=prompt_template)
return cls.from_messages([message])
[docs] @classmethod
@deprecated("0.0.1", alternative="from_messages classmethod", pending=True)
def from_role_strings(
cls, string_messages: List[Tuple[str, str]]
) -> ChatPromptTemplate:
"""从(角色,模板)元组列表中创建一个聊天提示模板。
参数:
string_messages:(角色,模板)元组列表。
返回:
一个聊天提示模板。
"""
return cls( # type: ignore[call-arg]
messages=[
ChatMessagePromptTemplate.from_template(template, role=role)
for role, template in string_messages
]
)
[docs] @classmethod
@deprecated("0.0.1", alternative="from_messages classmethod", pending=True)
def from_strings(
cls, string_messages: List[Tuple[Type[BaseMessagePromptTemplate], str]]
) -> ChatPromptTemplate:
"""从(角色类,模板)元组列表中创建一个聊天提示模板。
参数:
string_messages:(角色类,模板)元组列表。
返回:
一个聊天提示模板。
"""
return cls.from_messages(string_messages)
[docs] @classmethod
def from_messages(
cls,
messages: Sequence[MessageLikeRepresentation],
template_format: Literal["f-string", "mustache"] = "f-string",
) -> ChatPromptTemplate:
"""从各种消息格式创建一个聊天提示模板。
示例:
从消息模板列表实例化:
.. code-block:: python
template = ChatPromptTemplate.from_messages([
("human", "你好,你好吗?"),
("ai", "我很好,谢谢!"),
("human", "听到这个很高兴。"),
])
从混合消息格式实例化:
.. code-block:: python
template = ChatPromptTemplate.from_messages([
SystemMessage(content="你好"),
("human", "你好,你好吗?"),
])
参数:
messages: 消息表示的序列。
可以使用以下格式表示消息:
(1) BaseMessagePromptTemplate,(2) BaseMessage,(3) 2元组
(消息类型,模板); 例如,("human", "{user_input}"),
(4) 2元组 (消息类,模板),(4) 一个字符串,简写为("human", 模板); 例如,"{user_input}"
返回:
一个聊天提示模板
"""
_messages = [
_convert_to_message(message, template_format) for message in messages
]
# Automatically infer input variables from messages
input_vars: Set[str] = set()
partial_vars: Dict[str, Any] = {}
for _message in _messages:
if isinstance(_message, MessagesPlaceholder) and _message.optional:
partial_vars[_message.variable_name] = []
elif isinstance(
_message, (BaseChatPromptTemplate, BaseMessagePromptTemplate)
):
input_vars.update(_message.input_variables)
return cls(
input_variables=sorted(input_vars),
messages=_messages,
partial_variables=partial_vars,
)
[docs] def partial(self, **kwargs: Any) -> ChatPromptTemplate:
"""获取一个新的ChatPromptTemplate,其中已经填充了一些输入变量。
参数:
**kwargs: 用于填充模板变量的关键字参数。应该是输入变量的子集。
返回:
一个新的ChatPromptTemplate。
示例:
.. code-block:: python
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages(
[
("system", "You are an AI assistant named {name}."),
("human", "Hi I'm {user}"),
("ai", "Hi there, {user}, I'm {name}."),
("human", "{input}"),
]
)
template2 = template.partial(user="Lucy", name="R2D2")
template2.format_messages(input="hello")
"""
prompt_dict = self.__dict__.copy()
prompt_dict["input_variables"] = list(
set(self.input_variables).difference(kwargs)
)
prompt_dict["partial_variables"] = {**self.partial_variables, **kwargs}
return type(self)(**prompt_dict)
[docs] def append(self, message: MessageLikeRepresentation) -> None:
"""将消息附加到聊天模板的末尾。
参数:
message:要附加的消息的表示。
"""
self.messages.append(_convert_to_message(message))
[docs] def extend(self, messages: Sequence[MessageLikeRepresentation]) -> None:
"""使用一系列消息扩展聊天模板。"""
self.messages.extend([_convert_to_message(message) for message in messages])
@overload
def __getitem__(self, index: int) -> MessageLike:
...
@overload
def __getitem__(self, index: slice) -> ChatPromptTemplate:
...
def __getitem__(
self, index: Union[int, slice]
) -> Union[MessageLike, ChatPromptTemplate]:
"""用于索引到聊天模板中。"""
if isinstance(index, slice):
start, stop, step = index.indices(len(self.messages))
messages = self.messages[start:stop:step]
return ChatPromptTemplate.from_messages(messages)
else:
return self.messages[index]
def __len__(self) -> int:
"""获取聊天模板的长度。"""
return len(self.messages)
@property
def _prompt_type(self) -> str:
"""提示类型的名称。"""
return "chat"
[docs] def save(self, file_path: Union[Path, str]) -> None:
"""将提示保存到文件中。
参数:
file_path:文件路径。
"""
raise NotImplementedError()
[docs] def pretty_repr(self, html: bool = False) -> str:
# TODO: handle partials
return "\n\n".join(msg.pretty_repr(html=html) for msg in self.messages)
def _create_template_from_message_type(
message_type: str,
template: Union[str, list],
template_format: Literal["f-string", "mustache"] = "f-string",
) -> BaseMessagePromptTemplate:
"""从消息类型和模板字符串创建一个消息提示模板。
参数:
message_type: str 消息模板的类型(例如,"human","ai"等)。
template: str 模板字符串。
返回:
适当类型的消息提示模板。
"""
if message_type in ("human", "user"):
message: BaseMessagePromptTemplate = HumanMessagePromptTemplate.from_template(
template, template_format=template_format
)
elif message_type in ("ai", "assistant"):
message = AIMessagePromptTemplate.from_template(
cast(str, template), template_format=template_format
)
elif message_type == "system":
message = SystemMessagePromptTemplate.from_template(
cast(str, template), template_format=template_format
)
elif message_type == "placeholder":
if isinstance(template, str):
if template[0] != "{" or template[-1] != "}":
raise ValueError(
f"Invalid placeholder template: {template}."
" Expected a variable name surrounded by curly braces."
)
var_name = template[1:-1]
message = MessagesPlaceholder(variable_name=var_name, optional=True)
elif len(template) == 2 and isinstance(template[1], bool):
var_name_wrapped, is_optional = template
if not isinstance(var_name_wrapped, str):
raise ValueError(
"Expected variable name to be a string." f" Got: {var_name_wrapped}"
)
if var_name_wrapped[0] != "{" or var_name_wrapped[-1] != "}":
raise ValueError(
f"Invalid placeholder template: {var_name_wrapped}."
" Expected a variable name surrounded by curly braces."
)
var_name = var_name_wrapped[1:-1]
message = MessagesPlaceholder(variable_name=var_name, optional=is_optional)
else:
raise ValueError(
"Unexpected arguments for placeholder message type."
" Expected either a single string variable name"
" or a list of [variable_name: str, is_optional: bool]."
f" Got: {template}"
)
else:
raise ValueError(
f"Unexpected message type: {message_type}. Use one of 'human',"
f" 'user', 'ai', 'assistant', or 'system'."
)
return message
def _convert_to_message(
message: MessageLikeRepresentation,
template_format: Literal["f-string", "mustache"] = "f-string",
) -> Union[BaseMessage, BaseMessagePromptTemplate, BaseChatPromptTemplate]:
"""实例化来自各种消息格式的消息。
消息格式可以是以下之一:
- BaseMessagePromptTemplate
- BaseMessage
- 2元组(角色字符串,模板);例如,("human", "{user_input}")
- 2元组(消息类,模板)
- 字符串:简写为("human",模板);例如,"{user_input}"
参数:
message:支持格式之一中的消息表示
返回:
消息实例或消息模板的实例
"""
if isinstance(message, (BaseMessagePromptTemplate, BaseChatPromptTemplate)):
_message: Union[
BaseMessage, BaseMessagePromptTemplate, BaseChatPromptTemplate
] = message
elif isinstance(message, BaseMessage):
_message = message
elif isinstance(message, str):
_message = _create_template_from_message_type(
"human", message, template_format=template_format
)
elif isinstance(message, tuple):
if len(message) != 2:
raise ValueError(f"Expected 2-tuple of (role, template), got {message}")
message_type_str, template = message
if isinstance(message_type_str, str):
_message = _create_template_from_message_type(
message_type_str, template, template_format=template_format
)
else:
_message = message_type_str(
prompt=PromptTemplate.from_template(
cast(str, template), template_format=template_format
)
)
else:
raise NotImplementedError(f"Unsupported message type: {type(message)}")
return _message