Skip to content

Commit f3063e1

Browse files
committed
Merge branch 'main' of github.com:huggingface/chat-ui
2 parents 0eec37f + 16e29d9 commit f3063e1

File tree

4 files changed

+92
-4
lines changed

4 files changed

+92
-4
lines changed

PROMPTS.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Prompt templates
22

3+
> [!WARNING]
4+
> We now recommend using the `tokenizer` field to get the chat template directly from the hub. Just set it to your model id on the hub to automatically get the template.
5+
36
These are the templates used to format the conversation history for different models used in HuggingChat. Set them in your `.env.local` [like so](https://github.com/huggingface/chat-ui#chatprompttemplate).
47

58
## Llama 2

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ The following is the default `chatPromptTemplate`, although newlines and indenti
314314
{{assistantMessageToken}}
315315
```
316316

317+
> [!INFO]
318+
> We also support Jinja2 templates for the `chatPromptTemplate` in addition to Handlebars templates. On startup we first try to compile with Jinja and if that fails we fall back to interpreting `chatPromptTemplate` as handlebars.
319+
317320
#### Multi modal model
318321

319322
We currently support [IDEFICS](https://huggingface.co/blog/idefics) (hosted on TGI), OpenAI and Claude 3 as multimodal models. You can enable it by setting `multimodal: true` in your `MODELS` configuration. For IDEFICS, you must have a [PRO HF Api token](https://huggingface.co/settings/tokens). For OpenAI, see the [OpenAI section](#openai-api-compatible-models). For Anthropic, see the [Anthropic section](#anthropic).

src/lib/utils/template.spec.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { describe, test, expect } from "vitest";
2+
import { compileTemplate } from "./template";
3+
4+
// Test data for simple templates
5+
const modelData = {
6+
preprompt: "Hello",
7+
};
8+
9+
const simpleTemplate = "Test: {{preprompt}} and {{foo}}";
10+
11+
// Additional realistic test data for Llama 70B templates
12+
const messages = [
13+
{ from: "user", content: "Hello there" },
14+
{ from: "assistant", content: "Hi, how can I help?" },
15+
];
16+
17+
// Handlebars Llama 70B Template
18+
const llama70bTemplateHB = `<s>{{#if preprompt}}Source: system\n\n{{preprompt}}<step>{{/if}}{{#each messages}}{{#ifUser}}Source: user\n\n{{content}}<step>{{/ifUser}}{{#ifAssistant}}Source: assistant\n\n{{content}}<step>{{/ifAssistant}}{{/each}}Source: assistant\nDestination: user\n\n`;
19+
20+
// Expected output for Handlebars Llama 70B Template
21+
const expectedHB =
22+
"<s>Source: system\n\nSystem Message<step>Source: user\n\nHello there<step>Source: assistant\n\nHi, how can I help?<step>Source: assistant\nDestination: user\n\n";
23+
24+
// Jinja Llama 70B Template
25+
const llama70bTemplateJinja = `<s>{% if preprompt %}Source: system\n\n{{ preprompt }}<step>{% endif %}{% for message in messages %}{% if message.from == 'user' %}Source: user\n\n{{ message.content }}<step>{% elif message.from == 'assistant' %}Source: assistant\n\n{{ message.content }}<step>{% endif %}{% endfor %}Source: assistant\nDestination: user\n\n`;
26+
27+
// Expected output for Jinja Llama 70B Template
28+
const expectedJinja =
29+
"<s>Source: system\n\nSystem Message<step>Source: user\n\nHello there<step>Source: assistant\n\nHi, how can I help?<step>Source: assistant\nDestination: user\n\n";
30+
31+
describe("Template Engine Rendering", () => {
32+
test("should render using Handlebars fallback when no templateEngine is specified", () => {
33+
const render = compileTemplate(simpleTemplate, modelData);
34+
const result = render({ foo: "World" });
35+
expect(result).toBe("Test: Hello and World");
36+
});
37+
38+
test('should render using Jinja when templateEngine is set to "jinja"', () => {
39+
const render = compileTemplate(simpleTemplate, modelData);
40+
const result = render({ foo: "World" });
41+
expect(result).toBe("Test: Hello and World");
42+
});
43+
44+
// Realistic Llama 70B template tests
45+
test("should render realistic Llama 70B template using Handlebars", () => {
46+
const render = compileTemplate(llama70bTemplateHB, { preprompt: "System Message" });
47+
const result = render({ messages });
48+
expect(result).toBe(expectedHB);
49+
});
50+
51+
test("should render realistic Llama 70B template using Jinja", () => {
52+
const render = compileTemplate(llama70bTemplateJinja, {
53+
preprompt: "System Message",
54+
});
55+
const result = render({ messages });
56+
// Trim both outputs to account for whitespace differences in Jinja engine
57+
expect(result.trim()).toBe(expectedJinja.trim());
58+
});
59+
});

src/lib/utils/template.ts

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { Message } from "$lib/types/Message";
22
import Handlebars from "handlebars";
3+
import { Template } from "@huggingface/jinja";
34

5+
// Register Handlebars helpers
46
Handlebars.registerHelper("ifUser", function (this: Pick<Message, "from" | "content">, options) {
57
if (this.from == "user") return options.fn(this);
68
});
@@ -12,16 +14,37 @@ Handlebars.registerHelper(
1214
}
1315
);
1416

15-
export function compileTemplate<T>(input: string, model: { preprompt: string }) {
16-
const template = Handlebars.compile<T>(input, {
17+
// Updated compileTemplate to try Jinja and fallback to Handlebars if Jinja fails
18+
export function compileTemplate<T>(
19+
input: string,
20+
model: { preprompt: string; templateEngine?: string }
21+
) {
22+
let jinjaTemplate: Template | undefined;
23+
try {
24+
// Try to compile with Jinja
25+
jinjaTemplate = new Template(input);
26+
} catch (e) {
27+
// Could not compile with Jinja
28+
jinjaTemplate = undefined;
29+
}
30+
31+
const hbTemplate = Handlebars.compile<T>(input, {
1732
knownHelpers: { ifUser: true, ifAssistant: true },
1833
knownHelpersOnly: true,
1934
noEscape: true,
2035
strict: true,
2136
preventIndent: true,
2237
});
2338

24-
return function render(inputs: T, options?: RuntimeOptions) {
25-
return template({ ...model, ...inputs }, options);
39+
return function render(inputs: T) {
40+
if (jinjaTemplate) {
41+
try {
42+
return jinjaTemplate.render({ ...model, ...inputs });
43+
} catch (e) {
44+
// Fallback to Handlebars if Jinja rendering fails
45+
return hbTemplate({ ...model, ...inputs });
46+
}
47+
}
48+
return hbTemplate({ ...model, ...inputs });
2649
};
2750
}

0 commit comments

Comments
 (0)