core[patch]: allow "placeholder" type in from_messages tuples (#19152)

Co-authored-by: Erick Friis <erick@langchain.dev>
This commit is contained in:
William FH 2024-03-21 15:09:24 -07:00 committed by GitHub
parent f6bcd42421
commit e980c14d6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 1 deletions

View File

@ -551,7 +551,10 @@ MessageLike = Union[BaseMessagePromptTemplate, BaseMessage, BaseChatPromptTempla
MessageLikeRepresentation = Union[
MessageLike,
Tuple[Union[str, Type], Union[str, List[dict], List[object]]],
Tuple[
Union[str, Type],
Union[str, List[dict], List[object]],
],
str,
]
@ -590,6 +593,45 @@ class ChatPromptTemplate(BaseChatPromptTemplate):
# ]
#)
Messages Placeholder:
.. code-block:: python
# In addition to Human/AI/Tool/Function messages,
# you can initialize the template with a MessagesPlaceholder
# either using the class directly or with the shorthand tuple syntax:
template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI bot."),
# Means the template will receive an optional list of messages under
# the "conversation" key
("placeholder", "{conversation}")
# Equivalently:
# 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.")
]
}
)
# Output:
# 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.'),
# ]
#)
Single-variable template:
If your prompt has only a single input variable (i.e., 1 instance of "{variable_nams}"),
@ -949,6 +991,36 @@ def _create_template_from_message_type(
message = AIMessagePromptTemplate.from_template(cast(str, template))
elif message_type == "system":
message = SystemMessagePromptTemplate.from_template(cast(str, template))
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',"

View File

@ -535,6 +535,25 @@ def test_chat_prompt_message_placeholder_partial() -> None:
assert prompt.format_messages() == [SystemMessage(content="foo")]
def test_chat_prompt_message_placeholder_tuple() -> None:
prompt = ChatPromptTemplate.from_messages([("placeholder", "{convo}")])
assert prompt.format_messages(convo=[("user", "foo")]) == [
HumanMessage(content="foo")
]
assert prompt.format_messages() == []
# Is optional = True
optional_prompt = ChatPromptTemplate.from_messages(
[("placeholder", ["{convo}", False])]
)
assert optional_prompt.format_messages(convo=[("user", "foo")]) == [
HumanMessage(content="foo")
]
with pytest.raises(KeyError):
assert optional_prompt.format_messages() == []
def test_messages_prompt_accepts_list() -> None:
prompt = ChatPromptTemplate.from_messages([MessagesPlaceholder("history")])
value = prompt.invoke([("user", "Hi there")]) # type: ignore