Add workaround for templates requiring non-null content#19056
Add workaround for templates requiring non-null content#19056pwilkin wants to merge 5 commits intoggml-org:masterfrom
Conversation
|
This was a problem with a few templates, #18485 defaults to the empty string when null. I'm guessing the problem is during capability evaluation? If so, we should just pass in |
If I remember correctly, other templates on the other hand DO expect content to be null on tool_call messages because that's the OpenAI spec, so I would settle for a workaround. |
|
Although if I'm wrong, then yeah, scrapping the capability check and just keeping the workaround would be fine IMO. |
|
It's already happening, and no one has complained so far 😊 |
|
Aight, will keep just the workaround. |
|
|
||
| test_template(t, "null is undefined", | ||
| "{% if null is not defined %}yes{% else %}no{% endif %}", | ||
| json::object(), | ||
| "yes" | ||
| ); | ||
|
|
||
| } |
There was a problem hiding this comment.
| test_template(t, "null is undefined", | |
| "{% if null is not defined %}yes{% else %}no{% endif %}", | |
| json::object(), | |
| "yes" | |
| ); | |
| } | |
| } |
What are you trying to test here, we already have this test (null is a JSON thing), you can change y to null if you want:
llama.cpp/tests/test-jinja.cpp
Lines 189 to 193 in 20db3d4
There was a problem hiding this comment.
Yeah, I wanted to do the explicit "null constant is alias for undefined" check.
| {"tool_calls", json::array({ | ||
| { | ||
| {"id", "call1"}, | ||
| {"id", "call00001"}, |
There was a problem hiding this comment.
probably useful to leave a comment header explaining why the ID must be at a certain length (IIRC there was one or two templates enforces this?)
just in case we may come up with a better way in the future, we can come back and test the problematic template
| if (tmpl.original_caps().supports_tool_calls) { | ||
| // some templates will require the content field in tool call messages | ||
| // to still be non-null, this puts an empty string everywhere where the | ||
| // content field is null | ||
| workaround::requires_non_null_content(params.messages); | ||
| } |
There was a problem hiding this comment.
I'm not 100% sure if this is expected by all templates. in theory, there can be regression in these cases:
- template check:
if content is not none: (do something) else (do other things) - template that wraps content inside special tokens, example:
<message><tool>...</tool><content></content></message>--> in such case, content maybe expected to be none
but that's just in theory, for now I can't think of a way to verify it.
There was a problem hiding this comment.
I thought the same, but @aldehir talked me out of it 😀 can always add the cap code if we change our mind.
There was a problem hiding this comment.
Trying to recall, but I believe this is where simply checking "non-null" is insufficient:
llama.cpp/models/templates/Qwen-Qwen3-0.6B.jinja
Lines 40 to 42 in bb02f74
This branch is only explored with a tool call + reasoning content. The Qwen3 template was not triggering the
requires non-null check in Minja, so we were passing null content and it was throwing an exception. I think most templates are defensive against empty content, more than null content. A default of an empty string seems more reasonable to me. We are already passing in an empty string as of #18485. The capability checks do not align with this.
As in topic, even though OpenAI standard is null content when assistant message contains tool calls, some templates explicitly require it to be non-null or they'll fail with an error.