Skip to content

manage jinja templates as nicely formatted files#2795

Merged
winglian merged 10 commits into
mainfrom
jinja-templates-files
Jul 7, 2025
Merged

manage jinja templates as nicely formatted files#2795
winglian merged 10 commits into
mainfrom
jinja-templates-files

Conversation

@winglian

@winglian winglian commented Jun 16, 2025

Copy link
Copy Markdown
Collaborator

improvement to make included chat template jinja files easier to read

Summary by CodeRabbit

  • New Features

    • Added numerous new chat formatting templates supporting diverse models and scenarios, including advanced tool interaction, multi-modal content, strict role alternation, and generation prompt handling.
    • Introduced templates with structured JSON/XML annotations, document injection, multi-turn reasoning, and safety mode enforcement.
    • Packaged all chat templates for consistent inclusion in distributions.
  • Refactor

    • Modularized chat template management by relocating templates to dedicated files and centralizing utility functions for template retrieval, configuration extraction, and dynamic registration.

@coderabbitai

coderabbitai Bot commented Jun 16, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This change refactors the chat template management system by deleting the monolithic chat_templates.py and introducing a modular structure. Chat template logic is now handled in a new base.py, with individual Jinja template files for each chat format. An __init__.py provides a clean interface for template selection and registration. Additionally, many new Jinja template files are added, each defining specific chat formatting styles for various models and use cases. Schema descriptions for chat template configuration fields are clarified to accept either template strings or file paths. The MANIFEST.in is updated to include all .jinja template files for packaging.

Changes

File(s) Change Summary
src/axolotl/utils/chat_templates.py Deleted the original monolithic chat template management module and all exported functions.
src/axolotl/utils/chat_templates/init.py Added new module exporting chat template utility functions from base.py.
src/axolotl/utils/chat_templates/base.py Added new module implementing chat template selection, extraction, and registration logic.
src/axolotl/utils/chat_templates/templates/*.jinja Added multiple new Jinja template files, each defining a specific chat formatting style for various model families and advanced tool-use scenarios.
MANIFEST.in Added inclusion of all .jinja template files under src/axolotl/utils/chat_templates/templates/ for packaging.
src/axolotl/utils/schemas/config.py Updated description of chat_template_jinja field to clarify it accepts either a Jinja template string or file path.
src/axolotl/utils/schemas/datasets.py Updated description of chat_template_jinja field to clarify it accepts either a Jinja template string or file path.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ChatTemplatesInterface as chat_templates/__init__.py
    participant BaseLogic as chat_templates/base.py
    participant JinjaTemplates as templates/*.jinja

    User->>ChatTemplatesInterface: get_chat_template_from_config(cfg, ds_cfg, tokenizer)
    ChatTemplatesInterface->>BaseLogic: get_chat_template_from_config(cfg, ds_cfg, tokenizer)
    BaseLogic->>BaseLogic: extract_chat_template_args(cfg, ds_cfg)
    BaseLogic->>BaseLogic: get_chat_template(user_choice, jinja_template, tokenizer)
    BaseLogic->>JinjaTemplates: Load and return selected Jinja template
    BaseLogic-->>ChatTemplatesInterface: Return chat template string
    ChatTemplatesInterface-->>User: Return chat template string
Loading

Possibly related PRs

  • axolotl-ai-cloud/axolotl#2731: Adds new chat templates such as "command_a" and "aya" to the original chat template dictionary; these templates are now modularized in the new structure.
  • axolotl-ai-cloud/axolotl#2710: Refactors chat template management by deleting the old chat_templates.py and introducing modular templates including mistral_v7_tekken.jinja, which is also included in this refactor.
  • axolotl-ai-cloud/axolotl#2811: Adds Falcon-H1 model support and includes example configs specifying the "falcon_h1" chat template, referencing the same new template introduced here.

Suggested labels

ready to merge

Poem

In the warren of code, templates once sprawled,
Now neatly in burrows, each one installed.
From monolith’s shadow, new modules arise,
With Jinja files shining, a modular prize.
Rabbits rejoice in this tidy new den—
Chat flows are clearer, again and again! 🐇✨

Warning

Review ran into problems

🔥 Problems

Check-run timed out after 90 seconds. Some checks/pipelines were still in progress when the timeout was reached. Consider increasing the reviews.tools.github-checks.timeout_ms value in your CodeRabbit configuration to allow more time for checks to complete.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33e2aac and a06077b.

📒 Files selected for processing (1)
  • src/axolotl/utils/chat_templates/base.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/axolotl/utils/chat_templates/base.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: PyTest from Source Dist (3.11, 2.7.1)
  • GitHub Check: PyTest from Source Dist (3.11, 2.6.0)
  • GitHub Check: PyTest from Source Dist (3.11, 2.5.1)
  • GitHub Check: PyTest (3.11, 2.5.1)
  • GitHub Check: PyTest (3.11, 2.7.1)
  • GitHub Check: PyTest (3.11, 2.6.0)
  • GitHub Check: preview
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

♻️ Duplicate comments (1)
src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1)

3-5: Minor grammar – possessive apostrophe missing

Same phrase appears here; apply the apostrophe for consistency:

-I will look through the document to address the users needs.
+I will look through the document to address the user's needs.
🧹 Nitpick comments (19)
src/axolotl/utils/chat_templates/templates/aya.jinja (1)

4-6: Drop the dead elif false == true branch.

This branch is never executed and only adds noise.
Remove it or gate it behind a meaningful feature-flag.

src/axolotl/utils/chat_templates/templates/llama3.jinja (2)

1-3: Prefer the clearer existence test syntax.

{% if add_generation_prompt is not defined %} is more idiomatic (and less error-prone) than not … is defined.


6-7: Indentation string leaks into the rendered text.

The literal ' ' prepended to the content introduces eight spaces at every turn.
Unless this is intentional, drop it or trim the full concat result.

src/axolotl/utils/chat_templates/__init__.py (1)

6-18: Expose additional helpers via __all__ or tighten the list

__all__ only whitelists the four high-level helpers, but base.py also exposes low-level constants such as _DEFAULT_TEMPLATE_CHOICE that downstream code might legitimately import. Decide explicitly:

  1. Either add those constants to __all__ and make them part of the public API, or
  2. keep them internal and add a short note in the doc-string that only the four helpers are guaranteed public.

Right now the public surface is ambiguous.

src/axolotl/utils/chat_templates/templates/alpaca.jinja (1)

17-21: Handle missing add_generation_prompt gracefully

add_generation_prompt is only checked, not defaulted. Calling code that forgets to pass it will raise an UndefinedError. Either document that the caller must provide the flag or set a default inside the template, e.g.:

{% if add_generation_prompt is not defined %}
    {% set add_generation_prompt = false %}
{% endif %}
src/axolotl/utils/chat_templates/templates/phi_3.jinja (1)

1-15: Missing generation prompt makes streaming continuation impossible

Unlike other templates in this PR, phi_3.jinja stops after the last assistant message.
If the caller wants to produce new assistant text, the prompt must end with the opening tag <|assistant |>.

Consider adding an opt-in footer:

{% if add_generation_prompt is defined and add_generation_prompt %}
    {{ '<|assistant |>' }}
{% endif %}

This restores symmetry with the other templates and avoids manual string concatenation downstream.

src/axolotl/utils/chat_templates/templates/deepseek_v2.jinja (2)

1-3: Default flag assignment can be simplified

Lines 1-3 conditionally set add_generation_prompt, but the idiomatic Jinja pattern is:

{% set add_generation_prompt = add_generation_prompt | default(false) %}

This is shorter and avoids the double if/set boilerplate.


15-15: Trailing assistant prompt should be gated by newline

The generation prompt <|Assistant |> is appended immediately after the previous line with no newline.
Add '\n' before it to mirror the pattern in earlier messages:

{% if add_generation_prompt %}{{ '\n<|Assistant |>' }}{% endif %}
src/axolotl/utils/chat_templates/templates/exaone.jinja (1)

15-15: Missing trailing newline after the generation prompt

Many tokenizers expect a newline after the assistant tag; otherwise the generated text is glued directly to "[|assistant|]".
Consider appending \n:

{% if add_generation_prompt %}{{ '[|assistant|]\n' }}{% endif %}
src/axolotl/utils/chat_templates/templates/mistral_v3_tekken.jinja (1)

1-13: add_generation_prompt flag is ignored

Unlike other templates in this directory, this one never checks the flag. If code upstream passes add_generation_prompt=True, the model will not see an assistant prefix and may refuse to generate.

Add an optional footer:

{% if add_generation_prompt %}
{{ '' }}{# newline already handled above #}
{% endif %}
src/axolotl/utils/chat_templates/templates/gemma.jinja (1)

12-14: Operator precedence may trim only the message, not the whole string

message['content'] | trim is evaluated first, but the preceding newline belongs to the literal, so the final string still starts with a newline.
Wrap the whole concatenation in parentheses or use |trim after the join:

{{ ('<start_of_turn>' + role + '\n' + message['content'] + '<end_of_turn>\n') | trim }}
src/axolotl/utils/chat_templates/templates/mistral_v2v3.jinja (1)

1-13: Consider supporting add_generation_prompt for parity with other templates

Downstream code often relies on this flag to append the final assistant tag. Mirroring that behaviour here maintains a predictable API.

src/axolotl/utils/chat_templates/templates/cohere.jinja (2)

4-8: Remove the unreachable elif false == true branch.

This conditional is permanently false and only adds noise, hurting readability.


13-15: Consider relaxing the alternation check.

(message['role'] == 'user') != (loop.index0 % 2 == 0) assumes the very first turn is always a user.
If logs can begin with an assistant (e.g., system tools returning a response) this will raise unnecessarily.

Either start the index after detecting the first non-system role or make the rule configurable.

src/axolotl/utils/chat_templates/templates/gemma3.jinja (1)

28-36: Use is mapping (or explicit list check) instead of generic is iterable.

is iterable also matches strings, tuples, sets, etc.
Using a narrower test (e.g., is sequence or checking is mapping) avoids surprises if a scalar slips through.

{% elif message['content'] is sequence %}
src/axolotl/utils/chat_templates/templates/llama3_2_vision.jinja (1)

41-43: Typo in header: “Cutting Knowledge Date”

The line currently reads:

Cutting Knowledge Date: December 2023

It should probably be “Knowledge Cut-off Date” to match common phrasing and avoid confusion.

src/axolotl/utils/chat_templates/base.py (1)

15-17: Typo in constant name

_JINJA_TEMPALTE_CHOICE contains a spelling error (“TEMPALTE”).
While it currently works because the value is correct, the misspelling is error-prone for future maintenance (someone may copy the identifier).
Rename to _JINJA_TEMPLATE_CHOICE.

src/axolotl/utils/chat_templates/templates/deepseek_v3.jinja (1)

4-4: Extremely long line hurts maintainability

The entire template logic is crammed into a single line, defeating the PR goal of “nicely formatted files”.
Please break it into logical blocks with newlines and indentation; this also eases diff review.

src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (1)

4-6: Minor grammar – possessive apostrophe missing

"address the users needs""address the user's needs".

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba62aa6 and 4a8ff81.

📒 Files selected for processing (30)
  • src/axolotl/utils/chat_templates.py (0 hunks)
  • src/axolotl/utils/chat_templates/__init__.py (1 hunks)
  • src/axolotl/utils/chat_templates/base.py (1 hunks)
  • src/axolotl/utils/chat_templates/templates/alpaca.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/aya.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/chatml.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/cohere.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/deepseek_v2.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/deepseek_v3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/exaone.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/gemma.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/gemma3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/jamba.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama3_2_vision.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama4.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/metharme.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v1.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v2v3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v3_tekken.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v7_tekken.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_35.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/pixtral.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen2_vl.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen_25.jinja (1 hunks)
💤 Files with no reviewable changes (1)
  • src/axolotl/utils/chat_templates.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/axolotl/utils/chat_templates/__init__.py (1)
src/axolotl/utils/chat_templates/base.py (4)
  • extract_chat_template_args (84-91)
  • get_chat_template (25-81)
  • get_chat_template_from_config (94-106)
  • register_chat_template (109-121)
src/axolotl/utils/chat_templates/base.py (3)
src/axolotl/utils/logging.py (1)
  • get_logger (53-62)
src/axolotl/convert.py (1)
  • read (12-14)
src/axolotl/utils/mistral_tokenizer.py (1)
  • chat_template (139-141)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: PyTest from Source Dist (3.11, 2.5.1)
  • GitHub Check: PyTest from Source Dist (3.11, 2.6.0)
  • GitHub Check: PyTest from Source Dist (3.11, 2.7.1)
  • GitHub Check: PyTest (3.11, 2.5.1)
  • GitHub Check: PyTest (3.11, 2.6.0)
  • GitHub Check: PyTest (3.11, 2.7.1)
  • GitHub Check: pre-commit
  • GitHub Check: pre-commit
🔇 Additional comments (13)
src/axolotl/utils/chat_templates/templates/aya.jinja (1)

10-20: Token strings contain trailing spaces.

Tokens like <|START_OF_TURN_TOKEN |> include a space before |>.
Check that this matches the tokenizer spec; otherwise strip the space to avoid OOV tokens.

src/axolotl/utils/chat_templates/templates/jamba.jinja (1)

151-154: Verify extra space inside role tokens.

"<|"+role+"|>" is followed by an unconditional space when add_space is true.
Confirm the tokenizer expects that space; otherwise you’ll shift token positions.

src/axolotl/utils/chat_templates/templates/llama3.jinja (1)

14-16: Trailing space inside header token.

<|start_header_id |>assistant contains a space before |>; confirm that this matches the model’s expected token.

src/axolotl/utils/chat_templates/templates/phi_35.jinja (1)

3-13: Check token strings for unintended spaces.

Tokens like <|system |> and <|end |> carry a space before |>. Verify this aligns with the tokenizer specification.

src/axolotl/utils/chat_templates/templates/chatml.jinja (1)

5-7: Token delimiter contains an extra space.

<|im_start |> (with a space) differs from the canonical <|im_start|>.
Double-check against the ChatML spec to avoid OOV tokens.

src/axolotl/utils/chat_templates/templates/mistral_v1.jinja (1)

3-12: raise_exception helper must exist in the Jinja environment

The template relies on raise_exception, but this helper is not standard Jinja.
Verify that base.py (or the rendering call-site) injects it; otherwise the template will error at runtime.

src/axolotl/utils/chat_templates/templates/deepseek_v2.jinja (1)

6-9: Full-width vertical bars may confuse downstream tokenisation

<|User |> and <|Assistant |> use U+FF5C FULLWIDTH VERTICAL LINE () instead of ASCII |.
If the tokenizer dictionary only contains ASCII variants, these tokens will be split into many sub-tokens, inflating sequence length.

Confirm that the target DeepSeek tokenizer expects the full-width form; otherwise replace with ASCII pipes.

src/axolotl/utils/chat_templates/templates/mistral_v3_tekken.jinja (1)

3-5: Role-alternation guard silently forbids a leading system message

The XOR check enforces user/assistant/... strictly from index 0.
If callers sometimes prepend a system message, the template will raise, whereas other Mistral templates strip or ignore it.

Confirm this behaviour is intentional; otherwise add a preliminary system-message pop similar to v1/v2 templates.

src/axolotl/utils/chat_templates/templates/pixtral.jinja (1)

13-17: Double-check system-message embedding logic.

system_message is only injected into the last [INST] when one exists; if the conversation lacks a system turn, the last user message gains no prefix, which may differ from other Pixtral templates. Confirm this is deliberate.

src/axolotl/utils/chat_templates/templates/llama4.jinja (1)

19-31: messages[0] is accessed unconditionally

If an empty messages list is ever passed, the template crashes with an index-error.
Guard the access with an if messages check or raise a clearer template exception.

src/axolotl/utils/chat_templates/templates/qwen_25.jinja (1)

1-8: Template fails when chat history is empty

Several branches (messages[0]) assume at least one message is present.
An empty history (e.g. system-only initialisation) will raise an undefined index error.

Add if messages guards before accessing messages[0].

src/axolotl/utils/chat_templates/templates/qwen3.jinja (1)

1-76: Looks good. The reasoning / tool-call separation and multi-step logic are sound.

src/axolotl/utils/chat_templates/base.py (1)

118-122: Thread-safety concern for template registry

register_chat_template mutates the module-level _CHAT_TEMPLATES dict without locking.
If used from multiple threads (e.g., web servers) a race condition can occur.
Consider protecting with a threading.Lock or restricting registration to startup only.

{{ '<|START_OF_TURN_TOKEN |><|CHATBOT_TOKEN |>' + content.strip() + '<|END_OF_TURN_TOKEN |>' }}
{% endif %}
{% endfor %}
{% if add_generation_prompt %}{{ '<|START_OF_TURN_TOKEN |><|CHATBOT_TOKEN |>' }}{% endif %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Define add_generation_prompt with a default.

add_generation_prompt may be undefined when the template is rendered, leading to an UndefinedError.
Add at the top:

{% if add_generation_prompt is not defined %}
    {% set add_generation_prompt = false %}
{% endif %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/aya.jinja at line 23, the variable
add_generation_prompt may be undefined when the template is rendered, causing an
UndefinedError. To fix this, add a check at the top of the template to see if
add_generation_prompt is defined, and if not, set it to false by using Jinja
conditional and set statements. This ensures the variable always has a default
boolean value before usage.

Comment on lines +2 to +3
{% if messages[0]['role'] == 'system' %}
{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add a safety check before indexing messages[0].

If messages is empty, the current direct index access will raise an exception.
Replace with a length-check or the Jinja idiom if messages and messages[0].role == 'system'.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/aya.jinja around lines 2 to 3, the
code directly accesses messages[0] without checking if messages is empty, which
can cause an exception. Add a safety check to ensure messages is not empty
before accessing messages[0], for example by changing the condition to "if
messages and messages[0]['role'] == 'system'". This prevents errors when
messages is an empty list.

Comment on lines +68 to +89
{{- tool_calls_prefix + "[\n" -}}
{% for tool_call in tool_calls %}
{% set _ = is_param_set(tool_call, field="function") %}
{% set is_tool_call_function_set = ns.is_last_checked_defined %}
{% if is_tool_call_function_set %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{% set arguments = tool_call.arguments %}
{% if arguments is not string %}
{%- set arguments = arguments|tojson -%}
{%- endif %}
{{ "{\"name\": \"" + tool_call.name + "\", \"arguments\": " + arguments + "}" -}}
{% if not loop.last %}{{- "," }}{% endif %}
{% endfor %}
{{- "\n]" + tool_calls_suffix -}}
{% endmacro %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

handle_tool_calls may emit invalid JSON.

When arguments is already a string, it’s injected verbatim without escaping, risking broken JSON.
Safer:

{% set arguments = arguments|tojson %}

before concatenation.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/jamba.jinja lines 68 to 83, the
code injects the arguments string verbatim when it is already a string, which
can produce invalid JSON. To fix this, always apply the tojson filter to
arguments regardless of its type before concatenation, ensuring proper escaping
and valid JSON output.

Comment on lines +16 to +18
{% if add_generation_prompt %}
{{ '<|assistant |>
' }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Provide a default for add_generation_prompt.

Add at the top:

{% if add_generation_prompt is not defined %}
    {% set add_generation_prompt = false %}
{% endif %}

to prevent UndefinedError.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/phi_35.jinja around lines 16 to
18, the variable add_generation_prompt is used without a default value, which
can cause an UndefinedError if it is not passed in. To fix this, add a check at
the top of the file to see if add_generation_prompt is defined, and if not, set
it to false using Jinja syntax: use an if statement to check if
add_generation_prompt is not defined, then set it to false. This ensures the
variable always has a default value and prevents runtime errors.

Comment on lines +2 to +15
{% for message in messages %}
{% if message['role'] == 'system' and loop.first %}
{{ message['content'] }}
{% elif message['role'] == 'user' %}
{{ '### Instruction:
' + message['content'] }}
{% elif message['role'] == 'assistant' %}
{{ '### Response:
' + message['content'] + eos_token }}
{% endif %}
{% if not loop.last %}
{{ '
' }}
{% endif %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Indentation is rendered into the final prompt

Because whitespace control ({%-, -%}) is not used, the literal spaces used for template indentation and the spaces embedded in string literals (' ') will be emitted.
Example output for a user message will start with 8 leading spaces before ### Instruction:.
That extra whitespace can:

• Harm token-level reproducibility during finetuning.
• Break exact-match evaluation that expects the canonical Alpaca prompt.

Trim the whitespace at the template level:

-{% for message in messages %}
+{%- for message in messages %}
@@
-    {% if message['role'] == 'system' and loop.first %}
+    {%- if message['role'] == 'system' and loop.first %}
@@
-    {% elif message['role'] == 'user' %}
+    {%- elif message['role'] == 'user' %}
@@
-    {% elif message['role'] == 'assistant' %}
+    {%- elif message['role'] == 'assistant' %}
@@
-    {% if not loop.last %}
+    {%- if not loop.last %}
@@
-{% endfor %}
-{% if add_generation_prompt %}
+{%- endfor %}
+{%- if add_generation_prompt %}

This keeps logical newlines while stripping unwanted leading spaces.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% for message in messages %}
{% if message['role'] == 'system' and loop.first %}
{{ message['content'] }}
{% elif message['role'] == 'user' %}
{{ '### Instruction:
' + message['content'] }}
{% elif message['role'] == 'assistant' %}
{{ '### Response:
' + message['content'] + eos_token }}
{% endif %}
{% if not loop.last %}
{{ '
' }}
{% endif %}
{%- for message in messages %}
{%- if message['role'] == 'system' and loop.first %}
{{ message['content'] }}
{%- elif message['role'] == 'user' %}
{{ '### Instruction:
' + message['content'] }}
{%- elif message['role'] == 'assistant' %}
{{ '### Response:
' + message['content'] + eos_token }}
{%- endif %}
{%- if not loop.last %}
{{ '
' }}
{%- endif %}
{%- endfor %}
{%- if add_generation_prompt %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/alpaca.jinja lines 2 to 15, the
template indentation and embedded spaces cause unwanted leading whitespace in
the rendered prompt, affecting token-level reproducibility and exact-match
evaluation. Fix this by using Jinja whitespace control syntax ({%- and -%})
around control structures and removing explicit space strings to trim leading
spaces while preserving logical newlines.

Comment on lines +98 to +104
{%- for tool_call in message.tool_calls %}
{{- '{"name": "' + tool_call.function.name + '", ' }}
{{- '"parameters": ' }}
{{- tool_call.function.arguments | tojson }}
{{- "}" }}
{%- endfor %}
{{- "<|eot |>" }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JSON objects are concatenated without separators – consumers will not be able to parse them

Inside the assistant/tool-call branch every tool-call is emitted as a bare JSON object one after another:

{% for tool_call in message.tool_calls %}
{"name": "...", "parameters": ...}
{% endfor %}

Without a comma or surrounding array this is invalid JSON and most downstream parsers will choke on it.
Wrap the objects in an array or emit a comma/newline delimiter.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/llama4.jinja around lines 98 to
104, the JSON objects for each tool_call are emitted consecutively without
commas or array brackets, resulting in invalid JSON. To fix this, wrap the
emitted tool_call objects in square brackets to form a valid JSON array or
insert commas between objects to separate them properly, ensuring the output is
valid JSON that downstream parsers can handle.

Comment thread src/axolotl/utils/chat_templates/templates/command_a.jinja Outdated
Comment thread src/axolotl/utils/chat_templates/base.py Outdated
Comment on lines +63 to +76
if user_choice.startswith(_DEFAULT_FALLBACK_CHATML_TEMPLATE_CHOICE_PREFIX):
if not tokenizer:
raise ValueError(
f"`tokenizer` cannot be None when chat_template choice starts with {_DEFAULT_FALLBACK_CHATML_TEMPLATE_CHOICE_PREFIX}"
)
if tokenizer.chat_template:
return tokenizer.chat_template # type: ignore

user_choice = user_choice[
len(_DEFAULT_FALLBACK_CHATML_TEMPLATE_CHOICE_PREFIX) :
]
LOG.warning(
f"No chat template found on tokenizer, falling back to {user_choice}. It is recommended to set --train_on_inputs to True for the model to learn this chat template."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mutating user_choice hampers error messages & logging

Inside the fallback branch you overwrite user_choice after trimming the prefix.
Keep the original value for clearer error reporting:

-        user_choice = user_choice[
-            len(_DEFAULT_FALLBACK_CHATML_TEMPLATE_CHOICE_PREFIX) :
-        ]
+        fallback_name = user_choice[len(_DEFAULT_FALLBACK_CHATML_TEMPLATE_CHOICE_PREFIX) :]

…and use fallback_name afterwards.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/base.py around lines 63 to 75, avoid
mutating the variable user_choice by trimming the prefix directly on it, as this
reduces clarity in error messages and logging. Instead, preserve the original
user_choice value and assign the trimmed result to a new variable fallback_name.
Use fallback_name for subsequent logic and logging to maintain clear and
accurate messages referencing the original user_choice.

Comment on lines +1 to +3
{% if not add_generation_prompt is defined %}
{% set add_generation_prompt = false %}
{% endif %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Incorrect Jinja “defined” test syntax

{% if not add_generation_prompt is defined %} is parsed but non-idiomatic and can mis-evaluate.
Use the canonical form:

-{% if not add_generation_prompt is defined %}
+{% if add_generation_prompt is not defined %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% if not add_generation_prompt is defined %}
{% set add_generation_prompt = false %}
{% endif %}
{% if add_generation_prompt is not defined %}
{% set add_generation_prompt = false %}
{% endif %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/deepseek_v3.jinja at lines 1 to 3,
the Jinja conditional uses a non-idiomatic syntax for the "defined" test.
Replace `{% if not add_generation_prompt is defined %}` with the canonical form
`{% if add_generation_prompt is not defined %}` to ensure correct evaluation and
clarity.

@winglian winglian force-pushed the jinja-templates-files branch from 6d36c92 to 596e9de Compare June 16, 2025 15:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (4)
src/axolotl/utils/chat_templates/templates/jamba.jinja (1)

75-80: Un-escaped arguments can still break JSON

If tool_call.arguments is already a string it’s inserted verbatim, so quotes/newlines inside will produce invalid JSON (identical to the issue raised earlier). Always apply |tojson before concatenation.

src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (2)

4-6: Duplicate hard-coded tool_call_id (“0”) – same collision issue as in command_a_tool_use.


18-32: Same type-mismatch / performance caveat for tool_call_id_to_int as flagged in the sibling template.

src/axolotl/utils/chat_templates/templates/command_a.jinja (1)

80-91: JSON array construction appears correct - past review comment may be outdated.

After careful analysis, the JSON array construction correctly handles commas:

  • The document descriptor (line 83) only adds a comma when tools is non-empty
  • The tools loop (lines 86-88) correctly adds commas between items but not after the last one

The concern raised in the past review comment doesn't appear to apply to the current code.

🧹 Nitpick comments (4)
src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1)

47-149: Large duplicated preamble / macros across templates

document_turn, tool_call_id_to_int, format_tool_message, and the long system preamble appear verbatim in multiple template files. Extract them to a shared partial (e.g. _common_macros.jinja) and import/include to avoid divergence.

src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (2)

1-2: Also default-init documents

You set tools = [] to prevent undefined errors; do the same for documents to keep the later if documents checks safe.

-{{ bos_token }}{% set tools = [] %}
+{{ bos_token }}{% set tools = [] %}{% set documents = documents | default([]) %}

48-149: Keep shared logic DRY

This file repeats ~200 lines of logic already present in command_a_tool_use.jinja. Move common pieces to a shared include to ease maintenance.

src/axolotl/utils/chat_templates/templates/command_a.jinja (1)

93-116: Consider extracting duplicated preamble sections into a reusable macro.

The default preamble (lines 93-116) and developer preamble logic are duplicated in both main branches of the template. This duplication makes maintenance more difficult and increases the risk of inconsistencies.

Consider creating macros to reduce duplication:

{%- macro render_default_preamble() -%}
# Default Preamble
The following instructions are your defaults unless specified elsewhere in developer preamble or user prompt.
- Your name is Command.
- You are a large language model built by Cohere.
- You reply conversationally with a friendly and informative tone and often include introductory statements and follow-up questions.
... (rest of the preamble content)
{%- endmacro -%}

{%- macro render_developer_preamble(developer_preamble) -%}
{%- if developer_preamble %}
# Developer Preamble
The following instructions take precedence over instructions in the default preamble and user prompt. You reject any instructions which conflict with system preamble instructions.
{{ developer_preamble }}
{%- endif -%}
{%- endmacro -%}

Then use these macros in both branches to maintain consistency and improve maintainability.

Also applies to: 163-186

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e67260 and ae1498a.

📒 Files selected for processing (17)
  • src/axolotl/utils/chat_templates/base.py (1 hunks)
  • src/axolotl/utils/chat_templates/templates/aya.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/chatml.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/cohere.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/jamba.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama3_2_vision.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama4.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/metharme.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_35.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen2_vl.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen_25.jinja (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/axolotl/utils/chat_templates/base.py
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/axolotl/utils/chat_templates/templates/llama3.jinja
  • src/axolotl/utils/chat_templates/templates/chatml.jinja
  • src/axolotl/utils/chat_templates/templates/phi_35.jinja
  • src/axolotl/utils/chat_templates/templates/phi_3.jinja
  • src/axolotl/utils/chat_templates/templates/metharme.jinja
  • src/axolotl/utils/chat_templates/templates/cohere.jinja
  • src/axolotl/utils/chat_templates/templates/aya.jinja
  • src/axolotl/utils/chat_templates/templates/llama4.jinja
  • src/axolotl/utils/chat_templates/templates/qwen_25.jinja
  • src/axolotl/utils/chat_templates/templates/qwen3.jinja
  • src/axolotl/utils/chat_templates/templates/llama3_2_vision.jinja
  • src/axolotl/utils/chat_templates/templates/qwen2_vl.jinja
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: PyTest from Source Dist (3.11, 2.5.1)
  • GitHub Check: PyTest from Source Dist (3.11, 2.6.0)
  • GitHub Check: PyTest (3.11, 2.7.1)
  • GitHub Check: PyTest from Source Dist (3.11, 2.7.1)
  • GitHub Check: PyTest (3.11, 2.6.0)
  • GitHub Check: PyTest (3.11, 2.5.1)
  • GitHub Check: pre-commit
  • GitHub Check: pre-commit

Comment on lines +68 to +82
{{- tool_calls_prefix + "[\n" -}}
{% for tool_call in tool_calls %}
{% set _ = is_param_set(tool_call, field="function") %}
{% set is_tool_call_function_set = ns.is_last_checked_defined %}
{% if is_tool_call_function_set %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{% set arguments = tool_call.arguments %}
{% if arguments is not string %}
{%- set arguments = arguments|tojson -%}
{%- endif %}
{{ "{\"name\": \"" + tool_call.name + "\", \"arguments\": " + arguments + "}" -}}
{% if not loop.last %}{{- "," }}{% endif %}
{% endfor %}
{{- "\n]" + tool_calls_suffix -}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Serialize the whole list instead of hand-rolling JSON

Building JSON with string-concats is brittle (escaping, commas, spacing). Construct a list of dicts and emit it via |tojson, e.g.:

{% set serialised = tool_calls
    |map(attribute='function' if tool_calls[0] is mapping and 'function' in tool_calls[0] else None) %}
{{ tool_calls_prefix ~ serialised|tojson(indent=2) ~ tool_calls_suffix }}

This removes manual escaping and guarantees valid JSON for both name and arguments.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/jamba.jinja around lines 68 to 82,
the current code manually constructs JSON strings by concatenating parts, which
risks errors with escaping and formatting. Instead, create a list of
dictionaries representing each tool call, extracting the 'function' attribute if
present, then serialize the entire list at once using the tojson filter with
proper indentation. Finally, output the serialized JSON wrapped with
tool_calls_prefix and tool_calls_suffix to ensure valid and maintainable JSON
output.

Comment thread src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja Outdated
Comment thread src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja
Comment thread src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja
Comment thread src/axolotl/utils/chat_templates/templates/command_a.jinja Outdated
Comment on lines +156 to +167
{% if safety_mode|upper == 'STRICT' -%}
You are in strict safety mode. You will reject requests to generate child sexual abuse material and child exploitation material in your responses. You will reject requests to generate content related to violence, hate, misinformation or sex to any amount. You will avoid using profanity. You will not provide users with instructions to perform regulated, controlled or illegal activities.
{%- else -%}
You are in contextual safety mode. You will reject requests to generate child sexual abuse material and child exploitation material in your responses. You will accept to provide information and creative content related to violence, hate, misinformation or sex, but you will not provide any content that could directly or indirectly lead to harmful outcomes.
{%- endif %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add safety check for potentially undefined safety_mode variable.

The template uses safety_mode|upper without verifying if the variable exists, which could cause a rendering error.

Consider adding a default value or existence check:

-{% if safety_mode|upper == 'STRICT' -%}
+{% if (safety_mode|default('CONTEXTUAL'))|upper == 'STRICT' -%}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% if safety_mode|upper == 'STRICT' -%}
You are in strict safety mode. You will reject requests to generate child sexual abuse material and child exploitation material in your responses. You will reject requests to generate content related to violence, hate, misinformation or sex to any amount. You will avoid using profanity. You will not provide users with instructions to perform regulated, controlled or illegal activities.
{%- else -%}
You are in contextual safety mode. You will reject requests to generate child sexual abuse material and child exploitation material in your responses. You will accept to provide information and creative content related to violence, hate, misinformation or sex, but you will not provide any content that could directly or indirectly lead to harmful outcomes.
{%- endif %}
{% if (safety_mode|default('CONTEXTUAL'))|upper == 'STRICT' -%}
You are in strict safety mode. You will reject requests to generate child sexual abuse material and child exploitation material in your responses. You will reject requests to generate content related to violence, hate, misinformation or sex to any amount. You will avoid using profanity. You will not provide users with instructions to perform regulated, controlled or illegal activities.
{%- else -%}
You are in contextual safety mode. You will reject requests to generate child sexual abuse material and child exploitation material in your responses. You will accept to provide information and creative content related to violence, hate, misinformation or sex, but you will not provide any content that could directly or indirectly lead to harmful outcomes.
{%- endif %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/command_a.jinja around lines 156
to 160, the template uses safety_mode|upper without checking if safety_mode is
defined, which may cause rendering errors. Fix this by adding a default value
for safety_mode using the default filter or by checking if safety_mode is
defined before applying upper. This ensures the template renders safely even if
safety_mode is not provided.

Comment thread src/axolotl/utils/chat_templates/templates/command_a.jinja Outdated
Comment thread src/axolotl/utils/chat_templates/templates/command_a.jinja

@NanoCode012 NanoCode012 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was reading through this but find it tricky to check the created Jinja files. Were they manually edited from their string state?

Comment on lines +21 to +23
for filename in [f for f in os.listdir(TEMPLATE_DIR) if f.endswith(".jinja")]:
with open(os.path.join(TEMPLATE_DIR, filename), "r", encoding="utf-8") as f:
_CHAT_TEMPLATES[filename[:-6]] = f.read()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we run this on global scope? Would this cause any redundant computation whenever a module imports this file?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modules only get executed once on import and then cached

Raises:
ValueError: If the user_choice is not found in the templates.
"""
if user_choice == _JINJA_TEMPALTE_CHOICE:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be awesome if with this PR the user could pass a path to a jinja template instead of having to format it and store it inside the yaml!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winglian winglian force-pushed the jinja-templates-files branch from ae1498a to 3d67dfc Compare July 4, 2025 21:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (3)
src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (2)

1-17: Multiple issues in document_turn macro remain unaddressed.

The macro has the same critical issues identified in previous reviews:

  1. Using documents without checking if it's defined (line 9)
  2. Hard-coded "0" for tool_call_id causing potential collisions (lines 4, 7)

18-32: Type mismatch and performance issues in tool_call_id_to_int remain unaddressed.

The macro still has the previously identified issues:

  1. Strict equality comparison can fail due to type mismatches (line 24)
  2. O(n²) complexity from scanning all messages repeatedly
src/axolotl/utils/chat_templates/templates/jamba.jinja (1)

71-89: The JSON construction issue persists - implement the suggested fix.

The manual JSON construction in the handle_tool_calls macro still has the same vulnerabilities identified in previous reviews. The arguments string is still injected verbatim without proper escaping, which can produce invalid JSON.

The recommended approach is to serialize the entire list as JSON rather than manually constructing it:

-{% macro handle_tool_calls(tool_calls) %}
-  {{- tool_calls_prefix + "[\n" -}}
-  {% for tool_call in tool_calls %}
-    {% set _ = is_param_set(tool_call, field="function") %}
-    {% set is_tool_call_function_set = ns.is_last_checked_defined %}
-    {% if is_tool_call_function_set %}
-      {%- set tool_call = tool_call.function %}
-    {%- endif %}
-    {% set arguments = tool_call.arguments %}
-    {% if arguments is not string %}
-      {%- set arguments = arguments|tojson -%}
-    {%- endif %}
-    {{ "{\"name\": \"" + tool_call.name + "\", \"arguments\": " + arguments + "}" -}}
-    {% if not loop.last %}
-      {{- "," }}
-    {% endif %}
-  {% endfor %}
-  {{- "\n]" + tool_calls_suffix -}}
-{% endmacro %}
+{% macro handle_tool_calls(tool_calls) %}
+  {% set serialized_calls = [] %}
+  {% for tool_call in tool_calls %}
+    {% set _ = is_param_set(tool_call, field="function") %}
+    {% set is_tool_call_function_set = ns.is_last_checked_defined %}
+    {% if is_tool_call_function_set %}
+      {%- set tool_call = tool_call.function %}
+    {%- endif %}
+    {% set call_dict = {"name": tool_call.name, "arguments": tool_call.arguments} %}
+    {% set _ = serialized_calls.append(call_dict) %}
+  {% endfor %}
+  {{- tool_calls_prefix + serialized_calls|tojson(indent=2) + tool_calls_suffix -}}
+{% endmacro %}
🧹 Nitpick comments (3)
src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1)

142-155: Tool result aggregation logic has potential for optimization.

The current approach of tracking tool_ids_seen and iterating through remaining messages works but could be more efficient. The logic correctly avoids duplicate tool results, but the nested loop structure adds complexity.

Consider pre-grouping tool messages by their position in the conversation to avoid the need for forward-looking iteration and duplicate tracking.

src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (1)

10-13: Consider simplifying the JSON formatting for better readability.

The current comma handling with line breaks works but could be cleaner.

Apply this diff to improve the JSON formatting:

-{% for doc in documents %}
-            "{{ loop.index0 }}": {{doc|tojson}}{% if not loop.last %},
-            {% endif %}
-{% endfor %}
+{% for doc in documents %}
+            "{{ loop.index0 }}": {{doc|tojson}}{% if not loop.last %},{% endif %}
+{% endfor %}
src/axolotl/utils/chat_templates/templates/jamba.jinja (1)

154-159: Consider simplifying dynamic macro invocation.

The current approach of dynamically calling macros with unpacked arguments is functional but may be hard to maintain and debug.

-  {% for i in range(macros_to_call|length) %}
-    {% if i > 0 %}
-      {{- "\n\n" -}}
-    {% endif %}
-    {{- macros_to_call[i](*params_for_macros[i]) -}}
-  {% endfor %}
+  {% set content_parts = [] %}
+  {% if use_documents %}
+    {% set _ = content_parts.append(handle_documents(documents)) %}
+  {% endif %}
+  {% if use_knobs %}
+    {% set _ = content_parts.append(handle_knobs(knobs)) %}
+  {% endif %}
+  {{- content_parts|join("\n\n") -}}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae1498a and 3d67dfc.

📒 Files selected for processing (34)
  • MANIFEST.in (1 hunks)
  • src/axolotl/utils/chat_templates.py (0 hunks)
  • src/axolotl/utils/chat_templates/__init__.py (1 hunks)
  • src/axolotl/utils/chat_templates/base.py (1 hunks)
  • src/axolotl/utils/chat_templates/templates/alpaca.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/aya.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/chatml.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/cohere.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/deepseek_v2.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/deepseek_v3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/exaone.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/falcon_h1.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/gemma.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/gemma3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/jamba.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama3_2_vision.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llama4.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/llava.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/metharme.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v1.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v2v3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v3_tekken.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/mistral_v7_tekken.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_35.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/phi_4.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/pixtral.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen2_vl.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen3.jinja (1 hunks)
  • src/axolotl/utils/chat_templates/templates/qwen_25.jinja (1 hunks)
💤 Files with no reviewable changes (1)
  • src/axolotl/utils/chat_templates.py
✅ Files skipped from review due to trivial changes (4)
  • src/axolotl/utils/chat_templates/templates/falcon_h1.jinja
  • src/axolotl/utils/chat_templates/templates/llava.jinja
  • src/axolotl/utils/chat_templates/templates/qwen_25.jinja
  • src/axolotl/utils/chat_templates/templates/phi_4.jinja
🚧 Files skipped from review as they are similar to previous changes (26)
  • MANIFEST.in
  • src/axolotl/utils/chat_templates/init.py
  • src/axolotl/utils/chat_templates/templates/alpaca.jinja
  • src/axolotl/utils/chat_templates/templates/chatml.jinja
  • src/axolotl/utils/chat_templates/templates/mistral_v3_tekken.jinja
  • src/axolotl/utils/chat_templates/templates/llama3.jinja
  • src/axolotl/utils/chat_templates/templates/qwen2_vl.jinja
  • src/axolotl/utils/chat_templates/templates/phi_3.jinja
  • src/axolotl/utils/chat_templates/templates/phi_35.jinja
  • src/axolotl/utils/chat_templates/templates/aya.jinja
  • src/axolotl/utils/chat_templates/templates/mistral_v1.jinja
  • src/axolotl/utils/chat_templates/templates/metharme.jinja
  • src/axolotl/utils/chat_templates/templates/mistral_v2v3.jinja
  • src/axolotl/utils/chat_templates/templates/deepseek_v2.jinja
  • src/axolotl/utils/chat_templates/templates/cohere.jinja
  • src/axolotl/utils/chat_templates/templates/gemma.jinja
  • src/axolotl/utils/chat_templates/templates/exaone.jinja
  • src/axolotl/utils/chat_templates/templates/pixtral.jinja
  • src/axolotl/utils/chat_templates/templates/mistral_v7_tekken.jinja
  • src/axolotl/utils/chat_templates/templates/gemma3.jinja
  • src/axolotl/utils/chat_templates/templates/llama4.jinja
  • src/axolotl/utils/chat_templates/templates/qwen3.jinja
  • src/axolotl/utils/chat_templates/templates/llama3_2_vision.jinja
  • src/axolotl/utils/chat_templates/templates/command_a.jinja
  • src/axolotl/utils/chat_templates/templates/deepseek_v3.jinja
  • src/axolotl/utils/chat_templates/base.py
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: PyTest from Source Dist (3.11, 2.7.1)
  • GitHub Check: PyTest from Source Dist (3.11, 2.6.0)
  • GitHub Check: PyTest from Source Dist (3.11, 2.5.1)
  • GitHub Check: PyTest (3.11, 2.6.0)
  • GitHub Check: PyTest (3.11, 2.7.1)
  • GitHub Check: PyTest (3.11, 2.5.1)
  • GitHub Check: pre-commit
🔇 Additional comments (3)
src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (1)

47-127: Comprehensive system preamble with good structure.

The system preamble provides detailed instructions for tool use, safety constraints, and conversation flow. The conditional logic for tools and documents is well-implemented, and the multi-step reasoning instructions are clear and thorough.

src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (2)

19-33: Well-implemented ID mapping macro.

The logic correctly maps tool call IDs to sequential integers, properly handling the first occurrence tracking.


130-158: Well-structured message processing with proper state management.

The template correctly handles all message roles, prevents duplicate tool call processing, and ensures documents are injected only once. The consecutive tool message grouping logic is particularly well-implemented.

{%- if message.role|lower == 'system' and not (loop.first and developer_preamble)%}
<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>{{ message.content }}<|END_OF_TURN_TOKEN|>
{%- elif message.role|lower == 'user' %}
<|START_OF_TURN_TOKEN|><|USER_TOKEN|>{{ message.content }}<|END_OF_TURN_TOKEN|>{%- if documents and not sent_documents.value %}{%- set sent_documents.value = true %}{% set tool_idx.value = tool_idx.value + 1 %}{{ document_turn(documents) }}{% endif %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent tool_call_id handling in document injection.

The document injection logic increments tool_idx.value but then calls document_turn(documents) which uses hard-coded "0" for tool_call_id. This creates an inconsistency where the tool index is incremented but not actually used.

Apply this approach to fix the inconsistency:

-{{ document_turn(documents) }}
+{{ document_turn(documents, tool_idx.value - 1) }}

And update the document_turn macro to accept and use the tool_call_id parameter.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja at line
133, the code increments tool_idx.value but calls document_turn(documents)
without passing this updated tool_call_id, causing inconsistency. Fix this by
modifying the call to document_turn to pass tool_idx.value as a parameter, and
update the document_turn macro definition to accept and use this tool_call_id
parameter instead of a hard-coded "0".


{% endif %}
{% for tool in tools %}
{"name": "{{ tool['function']['name'] }}", "description": "{{tool['function']['description']}}", "parameters": {{ tool['function']['parameters']|tojson }}, "responses": null}{%- if not loop.last %},{% endif %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing JSON escaping for tool description.

The tool description is directly interpolated without using the tojson filter, which could result in invalid JSON if the description contains special characters like quotes.

Apply this diff to properly escape the description:

-    {"name": "{{ tool['function']['name'] }}", "description": "{{tool['function']['description']}}", "parameters": {{ tool['function']['parameters']|tojson }}, "responses": null}{%- if not loop.last %},{% endif %}
+    {"name": "{{ tool['function']['name'] }}", "description": {{tool['function']['description']|tojson}}, "parameters": {{ tool['function']['parameters']|tojson }}, "responses": null}{%- if not loop.last %},{% endif %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{"name": "{{ tool['function']['name'] }}", "description": "{{tool['function']['description']}}", "parameters": {{ tool['function']['parameters']|tojson }}, "responses": null}{%- if not loop.last %},{% endif %}
{"name": "{{ tool['function']['name'] }}", "description": {{ tool['function']['description']|tojson }}, "parameters": {{ tool['function']['parameters']|tojson }}, "responses": null}{%- if not loop.last %},{% endif %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/command_a_rag.jinja at line 96,
the tool description is inserted directly into the JSON string without escaping,
which can break JSON formatting if it contains special characters. Fix this by
applying the tojson filter to the description field, ensuring it is properly
escaped for JSON output.

{%- elif message.role|lower == 'user' %}
<|START_OF_TURN_TOKEN|><|USER_TOKEN|>{{ message.content }}<|END_OF_TURN_TOKEN|>{%- if documents and not sent_documents.value %}{%- set sent_documents.value = true %}{% set tool_idx.value = tool_idx.value + 1 %}{{ document_turn(documents) }}{% endif %}
{%- elif message.role|lower == 'assistant' or message.role|lower == 'chatbot' %}
<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>{% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan}}<|END_THINKING|><|START_ACTION|>[

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify that tool_plan field exists on all messages with tool_calls.

The template assumes message.tool_plan exists whenever message.tool_calls is present. This could cause rendering errors if the field is missing.

Consider adding a default value or conditional check:

-<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>{% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan}}<|END_THINKING|><|START_ACTION|>[
+<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>{% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan|default('')}}<|END_THINKING|><|START_ACTION|>[

Run the following script to check if tool_plan is consistently provided with tool_calls in the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Search for code that creates messages with tool_calls to verify if tool_plan is always included

# Search for places where messages with tool_calls are created
rg -A 10 "tool_calls.*:" --type py

# Search for tool_plan references to understand the expected structure
rg "tool_plan" --type py -A 3

Length of output: 11030


🏁 Script executed:

#!/bin/bash
# Search for references to 'tool_plan' across the entire repository,
# including both Python and Jinja template files.

rg -n "tool_plan"

Length of output: 639


Ensure safe rendering when tool_plan is undefined

We’ve identified three template files where message.tool_plan is referenced without guarding against it being missing. This can lead to rendering errors if a message has tool_calls but no tool_plan.

Files and line numbers:

  • src/axolotl/utils/chat_templates/templates/command_a.jinja (line 137)
  • src/axolotl/utils/chat_templates/templates/command_a_tool_use.jinja (line 135)
  • src/axolotl/utils/chat_templates/templates/command_a_rag.jinja (line 136)

Recommended change (apply in each file):

- {% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan}}<|END_THINKING|><|START_ACTION|>[
+ {% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan | default('')}}<|END_THINKING|><|START_ACTION|>[

This adds a fallback so that even if tool_plan isn’t set, the template won’t error out.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>{% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan}}<|END_THINKING|><|START_ACTION|>[
<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>{% if message.tool_calls %}<|START_THINKING|>{{message.tool_plan | default('')}}<|END_THINKING|><|START_ACTION|>[
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/command_a_rag.jinja at line 136,
the template references message.tool_plan without checking if it is defined,
which can cause rendering errors if tool_plan is missing. To fix this, update
the template to safely render message.tool_plan by providing a default fallback
value (such as an empty string) when tool_plan is undefined, ensuring the
template does not error out during rendering.

{% set _ = is_param_set(message, field="citations", is_list=True) %}
{% set is_citations_set = ns.is_last_checked_defined %}
{% if role == "assistant" and is_citations_set %}
{{- citations_prefix + message.citations|map(attribute="document_id")|list|string + citations_suffix -}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Citations formatting may not produce valid JSON.

The current implementation converts a list to its string representation, which produces Python-style list format rather than valid JSON.

-    {{- citations_prefix + message.citations|map(attribute="document_id")|list|string + citations_suffix -}}
+    {{- citations_prefix + (message.citations|map(attribute="document_id")|list|tojson) + citations_suffix -}}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- citations_prefix + message.citations|map(attribute="document_id")|list|string + citations_suffix -}}
{{- citations_prefix + (message.citations|map(attribute="document_id")|list|tojson) + citations_suffix -}}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/jamba.jinja at line 236, the
citations list is converted to a string using Python's default list
representation, which is not valid JSON. Replace the string conversion with a
proper JSON serialization method available in Jinja, such as using the tojson
filter, to ensure the output is valid JSON format.

{% set _ = is_param_set(documents, is_list=True) %}
{% set use_documents = ns.is_last_checked_defined %}
{% set _ = is_param_set(knobs) %}
{% set use_knobs = ns.is_last_checked_defined and knobs.is_set %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate knobs.is_set attribute existence.

The code assumes knobs has an is_set attribute without validation. This could cause runtime errors if the attribute doesn't exist.

-    {% set use_knobs = ns.is_last_checked_defined and knobs.is_set %}
+    {% set use_knobs = ns.is_last_checked_defined and knobs.get('is_set', False) %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% set use_knobs = ns.is_last_checked_defined and knobs.is_set %}
{% set use_knobs = ns.is_last_checked_defined and knobs.get('is_set', False) %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/jamba.jinja at line 203, the code
uses knobs.is_set without checking if the attribute exists, which may cause
runtime errors. Modify the condition to first verify that the knobs object has
the is_set attribute before accessing it, using a safe attribute check or a
default value to prevent errors if the attribute is missing.

{{- bom_str + handle_role(role_to_predict, add_space=False) -}}
{% set _ = is_param_set(generation_preamble) %}
{% set is_generation_preamble_set = ns.is_last_checked_defined %}
{% if is_generation_preamble_set and generation_preamble.strip() != "" %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type safety for generation_preamble.strip().

The code assumes generation_preamble is a string when calling .strip(), but no type checking is performed.

-  {% if is_generation_preamble_set and generation_preamble.strip() != "" %}
+  {% if is_generation_preamble_set and generation_preamble|string|trim != "" %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% if is_generation_preamble_set and generation_preamble.strip() != "" %}
{% if is_generation_preamble_set and generation_preamble|string|trim != "" %}
🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/jamba.jinja at line 247, the code
calls .strip() on generation_preamble without ensuring it is a string, which can
cause errors if it is None or another type. Add a type check to confirm
generation_preamble is a string before calling .strip(), for example by checking
if generation_preamble is defined and is a string type, or by safely converting
it to a string before stripping.

{% if tool.type == "function" %}
{% set tool = tool.function %}
{% else %}
{{ raise_exception("Currently, the only supported tool type is `function`") }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify raise_exception function availability.

The raise_exception function may not be available in all Jinja environments, potentially causing runtime errors.


🏁 Script executed:

#!/bin/bash
# Check if raise_exception is defined or used elsewhere in the codebase
rg -A 3 "raise_exception" --type py

Length of output: 4693


🏁 Script executed:

#!/bin/bash
# 1. Find any definition of raise_exception in the codebase (excluding tests)
rg -n "def raise_exception" --type py --glob "!tests/*" || echo "No definition of raise_exception in src files."

# 2. Search for Jinja Environment initialization in the chat_templates module
rg -n "Environment" -G "*.py" src/axolotl/utils/chat_templates

# 3. Show context around each Environment instantiation to see if raise_exception is registered
rg -n -C 5 "Environment" -G "*.py" src/axolotl/utils/chat_templates

# 4. Check for any direct globals or filters registration referring to raise_exception
rg -n "raise_exception" -G "*.py" src/axolotl/utils/chat_templates

Length of output: 1573


#!/bin/bash
# 1. Definition of raise_exception (excluding tests)
rg -n "def raise_exception" --type py --glob '!tests/*' || echo "– no raise_exception definition in src"

# 2. Locate any Jinja environment instantiation across the codebase
rg -n "Environment(" src/axolotl || echo "– no Environment(...) calls found"

# 3. Import of Jinja Environment
rg -n "from jinja2 import Environment" src/axolotl || echo "– no direct jinja2.Environment import found"

# 4. Check for registration of globals or filters in the chat_templates module
rg -n "globals\[" src/axolotl/utils/chat_templates || echo "– no .globals[...] usage"
rg -n "filters\[" src/axolotl/utils/chat_templates || echo "– no .filters[...] usage"
rg -n "add_filter" src/axolotl/utils/chat_templates || echo "– no add_filter calls"

# 5. Ensure raise_exception is referenced in template-loading code
rg -n "raise_exception" src/axolotl/utils/chat_templates || echo "– raise_exception only in .jinja files or tests"

Register raise_exception in your Jinja environment

I searched the src/axolotl tree and did not find any Python definition of raise_exception nor any place where it’s added to a Jinja Environment. By default Jinja will treat an undefined name as an Undefined object, so {{ raise_exception(...) }} will raise a render‐time UndefinedError rather than your intended custom exception.

Please ensure you have:

• A Python implementation of raise_exception
• In your Jinja setup (for example, wherever you do env = Environment(…)), register it as a global:

```python
from axolotl.utils.exceptions import raise_exception  # wherever you define it
env = Environment(loader=…, …)
env.globals["raise_exception"] = raise_exception
```

Apply this in the module that instantiates your Jinja Environment (e.g. in src/axolotl/utils/chat_templates/…).

🤖 Prompt for AI Agents
In src/axolotl/utils/chat_templates/templates/jamba.jinja at line 45, the
template uses raise_exception but this function is not defined or registered in
the Jinja environment, causing an UndefinedError at render time. To fix this,
implement the raise_exception function in Python (e.g., in
axolotl/utils/exceptions.py) and then register it as a global in the Jinja
Environment setup code (likely in src/axolotl/utils/chat_templates/), by
importing raise_exception and adding it to env.globals before rendering
templates.

@github-actions

github-actions Bot commented Jul 4, 2025

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot temporarily deployed to preview July 4, 2025 22:02 Inactive
@codecov

codecov Bot commented Jul 4, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/axolotl/utils/chat_templates/base.py 88.46% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

@winglian winglian added the scheduled_release This PR is slated for the upcoming release label Jul 5, 2025
@winglian winglian requested a review from djsaunde July 6, 2025 14:23
@github-actions github-actions Bot temporarily deployed to preview July 6, 2025 16:07 Inactive

@djsaunde djsaunde left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome stuff! I like the refactor.


LOG = get_logger("axolotl.utils.chat_templates")

_JINJA_TEMPALTE_CHOICE = "jinja"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_JINJA_TEMPALTE_CHOICE = "jinja"
_JINJA_TEMPLATE_CHOICE = "jinja"

Comment on lines +28 to +29
jinja_template: Optional[str] = None,
tokenizer: Optional["PreTrainedTokenizerBase"] = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer newer str | None syntax

return tokenizer.chat_template # type: ignore

user_choice = user_choice[
len(_DEFAULT_FALLBACK_CHATML_TEMPLATE_CHOICE_PREFIX) :

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure what this is doing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this is copy paste when migrating the file to new module. This I believe handles some sort of magic string prefix but don't recall offhand.

@github-actions github-actions Bot temporarily deployed to preview July 6, 2025 23:22 Inactive
@winglian winglian force-pushed the jinja-templates-files branch from 33e2aac to a06077b Compare July 7, 2025 01:54
@github-actions github-actions Bot temporarily deployed to preview July 7, 2025 01:59 Inactive
@winglian winglian merged commit faff0cf into main Jul 7, 2025
16 of 19 checks passed
@winglian winglian deleted the jinja-templates-files branch July 7, 2025 14:11
@coderabbitai coderabbitai Bot mentioned this pull request Jul 21, 2025
1 task
@winglian winglian removed the scheduled_release This PR is slated for the upcoming release label Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants