core: fix Image prompt template hardcoded template format (#27495)

Fixes #27411 

**Description:** Adds `template_format` to the `ImagePromptTemplate`
class and updates passing in the `template_format` parameter from
ChatPromptTemplate instead of the hardcoded "f-string".
Also updated docs and typing related to `template_format` to be more
up-to-date and specific.

**Dependencies:** None

**Add tests and docs**: Added unit tests to validate fix. Needed to
update `test_chat` snapshot due to adding new attribute
`template_format` in `ImagePromptTemplate`.

---------

Co-authored-by: Vadym Barda <vadym@langchain.dev>
This commit is contained in:
Chun Kang Lu
2024-10-21 17:31:40 -04:00
committed by GitHub
parent 403c0ea801
commit 380449a7a9
10 changed files with 131 additions and 25 deletions

View File

@@ -3119,6 +3119,7 @@
'template': dict({
'url': 'data:image/jpeg;base64,{my_image}',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',
@@ -3138,6 +3139,7 @@
'template': dict({
'url': 'data:image/jpeg;base64,{my_image}',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',
@@ -3157,6 +3159,7 @@
'template': dict({
'url': '{my_other_image}',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',
@@ -3177,6 +3180,7 @@
'detail': 'medium',
'url': '{my_other_image}',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',
@@ -3195,6 +3199,7 @@
'template': dict({
'url': 'https://www.langchain.com/image.png',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',
@@ -3213,6 +3218,7 @@
'template': dict({
'url': 'data:image/jpeg;base64,foobar',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',
@@ -3231,6 +3237,7 @@
'template': dict({
'url': 'data:image/jpeg;base64,foobar',
}),
'template_format': 'f-string',
}),
'lc': 1,
'name': 'ImagePromptTemplate',

View File

@@ -31,6 +31,7 @@ from langchain_core.prompts.chat import (
SystemMessagePromptTemplate,
_convert_to_message,
)
from langchain_core.prompts.string import PromptTemplateFormat
from tests.unit_tests.pydantic_utils import _normalize_schema
@@ -298,6 +299,77 @@ def test_chat_prompt_template_from_messages_mustache() -> None:
]
@pytest.mark.requires("jinja2")
def test_chat_prompt_template_from_messages_jinja2() -> None:
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 }}"),
],
"jinja2",
)
messages = template.format_messages(name="Bob", user_input="What is your name?")
assert messages == [
SystemMessage(
content="You are a helpful AI bot. Your name is Bob.", additional_kwargs={}
),
HumanMessage(
content="Hello, how are you doing?", additional_kwargs={}, example=False
),
AIMessage(
content="I'm doing well, thanks!", additional_kwargs={}, example=False
),
HumanMessage(content="What is your name?", additional_kwargs={}, example=False),
]
@pytest.mark.requires("jinja2")
@pytest.mark.requires("mustache")
@pytest.mark.parametrize(
"template_format,image_type_placeholder,image_data_placeholder",
[
("f-string", "{image_type}", "{image_data}"),
("mustache", "{{image_type}}", "{{image_data}}"),
("jinja2", "{{ image_type }}", "{{ image_data }}"),
],
)
def test_chat_prompt_template_image_prompt_from_message(
template_format: PromptTemplateFormat,
image_type_placeholder: str,
image_data_placeholder: str,
) -> None:
prompt = {
"type": "image_url",
"image_url": {
"url": f"data:{image_type_placeholder};base64, {image_data_placeholder}",
"detail": "low",
},
}
template = ChatPromptTemplate.from_messages(
[("human", [prompt])], template_format=template_format
)
assert template.format_messages(
image_type="image/png", image_data="base64data"
) == [
HumanMessage(
content=[
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64, base64data",
"detail": "low",
},
}
]
)
]
def test_chat_prompt_template_with_messages(
messages: list[BaseMessagePromptTemplate],
) -> None:

View File

@@ -8,6 +8,7 @@ import pytest
from syrupy import SnapshotAssertion
from langchain_core.prompts.prompt import PromptTemplate
from langchain_core.prompts.string import PromptTemplateFormat
from langchain_core.tracers.run_collector import RunCollectorCallbackHandler
from tests.unit_tests.pydantic_utils import _normalize_schema
@@ -610,7 +611,9 @@ async def test_prompt_ainvoke_with_metadata() -> None:
)
@pytest.mark.parametrize("template_format", ["f-string", "mustache"])
def test_prompt_falsy_vars(
template_format: str, value: Any, expected: Union[str, dict[str, str]]
template_format: PromptTemplateFormat,
value: Any,
expected: Union[str, dict[str, str]],
) -> None:
# each line is value, f-string, mustache
if template_format == "f-string":