Skip to content

Python: Handlebars messages helper ignores its argument and always reads chat_history from the context #14535

Open

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonPull requests for the Python Semantic Kerneltriage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions