extractToolCalls static method
Extract all function calls from an SDK response JSON string.
Accepts the structural variants observed in the wild:
- Top-level
tool_callslist (Gemma 4 standard path). contentarray entries withtype: "tool_call"(multimodal).- Concatenated multi-document JSON — when the model emits two or more
<|tool_call>...<tool_call|>blocks, the SDK serializes them as separate{role:assistant,tool_calls:[...]}{role:assistant,tool_calls:[...]}documents back-to-back rather than wrapping them in an array. We split on top-level}{boundaries and parse each fragment.
Each call element may be either OpenAI-style
({type: "function", function: {name, arguments}}) or flat
({name, arguments}) — both accepted.
String values inside arguments (and nested maps/lists) are stripped of
the <|"|> Gemma 4 escape token, which leaks through SDK parsing.
Implementation
static List<FunctionCallResponse> extractToolCalls(String jsonStr) {
final result = <FunctionCallResponse>[];
for (final fragment in _splitConcatenatedJson(jsonStr)) {
final Map<String, dynamic> json;
try {
final parsed = jsonDecode(fragment);
if (parsed is! Map<String, dynamic>) continue;
json = parsed;
} on FormatException {
continue;
}
_harvestCalls(json, result);
}
// Raw Gemma 4 `<|tool_call>call:NAME{...}<tool_call|>` tokens, when the
// structured pass found nothing. `@litert-lm/core` 0.12.1 and 0.14.0 left
// every web call as text (verified with Gemma 4 E4B); from 0.18.0 the web
// runtime, like native, converts a complete call itself, so this reads a
// block the runtime did not convert (cut off or malformed) and history
// text that carries the tokens.
if (result.isEmpty && jsonStr.contains(_rawToolCallOpen)) {
result.addAll(_rawTokenCalls(_responseText(jsonStr)));
}
// FunctionGemma, same reason: when its call comes back as text rather than
// `tool_calls` — the web SDK, or a `.litertlm` exported without the
// function_gemma model type, whose runtime creates no tool-call channel —
// parse the wire format the model wrote.
if (result.isEmpty && jsonStr.contains(functionGemmaStartCall)) {
result.addAll(FunctionGemmaCallFormat().parseAll(_responseText(jsonStr)));
}
return result;
}