Python: Handlebars messages helper ignores its argument and always reads chat_history from the context #14535
Description
What happens
The Handlebars messages helper ignores its argument. {{messages my_history}} always reads this.context["chat_history"], whatever variable is passed. If no variable named chat_history exists it raises KeyError. If one exists with different content, the wrong history is rendered. The Jinja2 helper messages(history) uses the argument it is given, so the two template formats disagree.
Where
python/semantic_kernel/prompt_template/utils/handlebars_system_helpers.py (checked against current main, 9974625):
def _messages(this, options, *args, **kwargs):
from semantic_kernel.contents.chat_history import ChatHistory
if not isinstance(this.context["chat_history"], ChatHistory):
return ""
return this.context["chat_history"].to_prompt()args is never read. Compare the Jinja2 version in jinja2_system_helpers.py:
def _messages(chat_history):
...
if not isinstance(chat_history, ChatHistory):
return ""
return chat_history.to_prompt()Steps to reproduce
import asyncio
from semantic_kernel import Kernel
from semantic_kernel.contents import ChatHistory
from semantic_kernel.prompt_template import HandlebarsPromptTemplate, Jinja2PromptTemplate, PromptTemplateConfig
from semantic_kernel.functions import KernelArguments
def hist(t):
h = ChatHistory(); h.add_user_message(t); return h
async def run(cls, fmt, tpl, args):
cfg = PromptTemplateConfig(name="x", template=tpl, template_format=fmt, allow_dangerously_set_content=True)
try:
return repr(await cls(prompt_template_config=cfg).render(Kernel(), args))
except Exception as e:
return f"EXC {type(e).__name__}: {e}"
async def main():
print(await run(HandlebarsPromptTemplate, "handlebars", "{{messages history}}", KernelArguments(history=hist("hello"))))
print(await run(Jinja2PromptTemplate, "jinja2", "{{ messages(history) }}", KernelArguments(history=hist("hello"))))
print(await run(HandlebarsPromptTemplate, "handlebars", "{{messages other}}",
KernelArguments(chat_history=hist("WRONG"), other=hist("RIGHT"))))
asyncio.run(main())Actual:
EXC KeyError: 'chat_history'
'<chat_history><message role="user"><text>hello</text></message></chat_history>'
'<chat_history><message role="user"><text>WRONG</text></message></chat_history>'
Expected: the Handlebars output matches Jinja2 for the first case, and the third case renders RIGHT.
Notes
The existing tests only pass a variable literally named chat_history, so they do not catch this. I have a fix ready: use the first positional argument, and fall back to chat_history from the context when no argument is given so current templates keep working.
Activity