Skip to main content
LFM2 and LFM2.5 emit tool calls between <|tool_call_start|> and <|tool_call_end|> control tokens (see Tool Use). llama.cpp ships a dedicated parser for both formats: start llama-server with --jinja, pass tools in the request, and tool calls come back as structured tool_calls on the OpenAI-compatible response. There is nothing to parse on the client.

Start the server

--jinja is mandatory: it renders the tool definitions through the model’s chat template and enables tool-call parsing. Use a recent llama.cpp release β€” LFM2/LFM2.5 template detection lives in common/chat.cpp and older builds fall back to a generic parser.

Define tools and make a call

Tools use the OpenAI JSON schema format. Passing them in the tools field is all the model needs; do not also paste them into the system prompt.
finish_reason is "tool_calls" when the model asked for a tool. tool_choice accepts "auto", "none", "required", or a specific function.

The agent loop

An agent is a loop: send the conversation, execute any tool calls, append the results as tool messages, and call the model again until it answers in plain text.
Guidelines that matter on small on-device models:
  • Keep the tool list short and the descriptions precise. Every tool adds prompt tokens on every turn; 3–8 well-described tools work far better than 30 vague ones.
  • Return compact results. Serialize only the fields the model needs; large JSON blobs consume context and dilute attention.
  • Cap the loop. Always bound the number of steps and handle unknown tool names or malformed arguments by returning an error message in the tool result rather than crashing.
  • Use the recommended sampling. Tool arguments are structured text; temperature 0.1 keeps them well-formed.
  • Prompt caching does the heavy lifting. The system prompt and tool definitions are identical every step, so llama-server prefills them once per conversation.

Streaming tool calls

With stream: true, tool calls arrive incrementally in choices[0].delta.tool_calls (index, id, function.name, then chunks of function.arguments). Accumulate the argument fragments per index and parse the JSON once finish_reason is "tool_calls".

Pythonic and JSON tool-call formats

LFM2.5 natively writes Pythonic calls (get_weather(city="Paris")); LFM2 wraps definitions in <|tool_list_start|> / <|tool_list_end|>. With --jinja, llama-server detects the template and normalizes either format into OpenAI tool_calls, so you never see the raw tokens. If you disable tool parsing (parse_tool_calls: false in the request) the raw <|tool_call_start|>…<|tool_call_end|> text is returned in content instead.

In-process (no server)

If you embed llama.cpp directly (for example on iOS & Android), link the common library and use the same machinery llama-server uses:
  • common_chat_templates_init(model, "") loads the GGUF’s Jinja template.
  • common_chat_templates_apply(tmpls, inputs) renders messages and tool definitions to a prompt, and returns the grammar/trigger configuration for the format.
  • common_chat_parse(text, is_partial, params) turns the generated text into a common_chat_msg with tool_calls.
See common/chat.h for the full API. Alternatively, format the prompt yourself following Tool Use and split on the <|tool_call_start|> / <|tool_call_end|> tokens (llama_token_to_piece with special = true so the control tokens are not filtered out).

Next steps

Structured Output

Force valid JSON for tool arguments or final answers.

Tool Use concepts

How LFM2.5 represents tools and calls at the token level.