Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tool-call: fix Qwen 2.5 Coder support, add micro benchmarks, support trigger patterns for lazy grammars #12034

Open
wants to merge 36 commits into
base: master
Choose a base branch
from

Conversation

ochafik
Copy link
Collaborator

@ochafik ochafik commented Feb 22, 2025

TL;DR: fixes tool calling of Qwen 2.5 Coder 0.5B/1.5B/3B/7B/32B... at any temperature

instructions to build this branch
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
git remote add ochafik https://github.com/ochafik/llama.cpp
git fetch ochafik
git checkout ochafik/tool-bench-prod
cmake -B build -DLLAMA_CURL=1
cmake --build build -t llama-server --parallel --config Release
alias llama-server=./build/bin/llama-server
llama-server --jinja -fa -c 0 -hf unsloth/Qwen2.5-Coder-7B-Instruct-128K-GGUF
  • Added support for regex grammar triggers, and respect when they should be matching at the start only (was already declared but not implemented; should avoid spurious triggering when the triggers were defined as wide-catches).
    • In llama.h, deprecating llama_sampler_init_grammar_lazy (which used to take tokens or words) in favour of llama_sampler_init_grammar_lazy_patterns (which takes tokens or full-string regex patterns w/ a group that marks from where the grammar is triggered)
  • Dramatically improved tool calls success rate of Qwen 2.5 Coder (Hermes 2 format) w/ more triggers that match what the models tends to output (esp. at higher temperatures) / looser triggers w/ regular expressions
  • Added scripts/tool_bench.py to evaluate tool call compliance probability of llama-server & ollama on different models, at different temperatures

The following heatmap shows compliance ratio on two super basic tool call tests (hello world & weather tests from examples/server/tests/unit/test_tool_call.py, now shared w/ the bench tool). 3 pairs of columns for llama-server of this PR, baseline llama-server (master) and ollama.

image

qwenc1 5b

export ARGS=( --n 30 --llama-baseline="$(which llama-server)" --temp -1 --temp 0 --temp 0.5 --temp 0.75 --temp 1 --temp 1.5 --temp 2 --temp 5 ) 

./scripts/tool_bench.py run ${ARGS[@]} --model "Qwen 2.5 Coder 7B Q4_K_M"             --output ../qwenc7b.jsonl   --hf unsloth/Qwen2.5-Coder-7B-Instruct-128K-GGUF:Q4_K_M   --ollama qwen2.5-coder:7b-instruct-q4_K_M
./scripts/tool_bench.py run ${ARGS[@]} --model "Qwen 2.5 Coder 1.5B Q4_K_M"           --output ../qwenc1.5b.jsonl --hf unsloth/Qwen2.5-Coder-1.5B-Instruct-128K-GGUF:Q4_K_M --ollama qwen2.5-coder:1.5b-instruct-q4_K_M

See gist with results for many more models

Notes about results:

  • the failures of llama-server at temp = 2 are model humour / stylistic choice (Sure! You can use the following Python code... instead of tool call)
  • ollama seems to only recognize the tool call format of the template, but models like Qwen 2.5 Coder 7B is quite... creative in its tool call outputs, esp. at higher temperatures.
  • ollama's default temperature seems to be 0.6 (hence why the row w/ @ None kinda fits results of lower rows)
  • The tests may need further tweaking to accept arguably “correct” answers. The framing of the hello world test is questionable, sometimes models just explain how they would write the code.
  • The benchmark tool also supports running test_calc_results which evaluates how well a model follows up on tool results. This seems to have more varied failure modes so it's not evaluated by default.

TODO:

  • Run & share more bench results (esp. other Qwen Coder variants!)
  • Stabilize tests / ci
  • Analyze bench times

Update llama-grammar.h

update

Update llama-grammar.h

Update common.h

Update common.h

Update sampling.cpp

Update chat.cpp

update test_tool_call.py

Update server.cpp

Update utils.hpp

Update chat.cpp

Update test_tool_call.py

Update fetch_server_test_models.py
@github-actions github-actions bot added script Script related testing Everything test related examples python python script changes server labels Feb 22, 2025
@GuuD
Copy link

GuuD commented Feb 22, 2025

Was Qwen 2.5 Coder even trained for tool use? 🤯

@ochafik
Copy link
Collaborator Author

ochafik commented Feb 23, 2025

Was Qwen 2.5 Coder even trained for tool use? 🤯

@GuuD I guess all models must be to some extent, these days. Their technical report only mentions in passing the fact that BigCodeBench is "primarily aimed at evaluating the ability of tool-use and complex instruction following" and their results on that benchmark look quite decent. But given the variety of outputs the model wraps tool calls in, I doubt they stuck to the syntax used in their jinja template.

@ochafik ochafik marked this pull request as ready for review February 25, 2025 12:01
@ochafik ochafik requested a review from ngxson as a code owner February 25, 2025 12:01
@Mushoz
Copy link

Mushoz commented Mar 2, 2025

I see you also opened a PR for Cline to actually utilize this. Is there any chance you could do the same for Roo Code? I have been using both Cline and Roo Code with Qwen2.5-Coder-32b with moderate success, so any improvements to that workflow are more than welcome!

@ochafik
Copy link
Collaborator Author

ochafik commented Mar 2, 2025

I see you also opened a PR for Cline to actually utilize this. Is there any chance you could do the same for Roo Code? I have been using both Cline and Roo Code with Qwen2.5-Coder-32b with moderate success, so any improvements to that workflow are more than welcome!

Hey @Mushoz, I'll check Roo Code out, but probably won't be before a few weeks. Also, my PR on Cline was somewhat promptly rejected 😓 (cline/cline#1946). Maybe I should have opened with a screencast of the very decent results I've been getting 🫣.

I've started looking at llama-vscode instead (cc/ @ggerganov @igardev ). I'm trying to get streamed tool calls to work on both ends as a follow up to this PR (requires some partial JSON decoding on llama-server's side - nearly done, and streamed / partial JSON decoding on the IDE side - fully done - to get the same kind of streamed diffs as Cline has).

Hey @ngxson, would you have time to take a look at this one?

@Mushoz
Copy link

Mushoz commented Mar 2, 2025

@ochafik Roo Code is a fork of cline rapidly growing in popularity. Since it's a fork, I am hopeful it's relatively "easy" to port your PR to Roo Code instead

@ggerganov
Copy link
Member

@ochafik I saw the PR in Cline, but I don't have much to add as I am neither familiar with Cline nor with the tools/MCP support yet. It seems to me the functionality is powerful, but I need to dedicate some time to understand the details and how it works in order to provide any meaningful feedback. In any case, I think adding this kind of functionality to llama-vscode would be welcome as I believe @igardev (the maintainer of the extension) is interested in such high-level/agentic workflows.

Copy link
Member

@ggerganov ggerganov left a comment

Choose a reason for hiding this comment

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

Overall, I think the changes are good, though I haven't ran any tests yet and mostly looked at the syntax. Let's wait for @ngxson review.

grammar.trigger_buffer.clear();
llama_grammar_accept_str(grammar, constrained_str);
LLAMA_LOG_DEBUG("Grammar triggered on word `%s`", word.c_str());
LLAMA_LOG_DEBUG("Grammar triggered on regex: %s", constrained_str.c_str());
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
LLAMA_LOG_DEBUG("Grammar triggered on regex: %s", constrained_str.c_str());
LLAMA_LOG_DEBUG("Grammar triggered on regex: '%s'\n", constrained_str.c_str());

Comment on lines +126 to +127
std::vector<std::pair<std::string, std::regex>>
trigger_patterns; // Regular expressions that trigger a lazy grammar. Must be a full match of the entire generated
Copy link
Member

Choose a reason for hiding this comment

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

Make a struct here. I don't like the std:pair - it always ends up not enough and it's too heavy to read.

std::string word;
bool at_start;
common_grammar_trigger_type type;
std::variant<llama_token, std::string> value;
Copy link
Member

Choose a reason for hiding this comment

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

A bit hesitating about the usage of std::variant here. Overall, it's OK, but it's the first time we use it in the codebase, and I'm not sure it is worth introducing it as a pattern. Having 2 separate members value_token and value_string is the alternative.

@ngxson
Copy link
Collaborator

ngxson commented Mar 3, 2025

My backlog is quite full today, I'll do a review tomorrow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
examples python python script changes script Script related server testing Everything test related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants