[GH-ISSUE #15497] OpenAI-compatible streaming: Function.Index still 0 for models without a registered parser (follow-up to #15457 / #15467) #9904

Open
opened 2026-04-12 22:45:14 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @CPIDLE on GitHub (Apr 11, 2026).
Original GitHub issue: https://github.com/ollama/ollama/issues/15497

Thanks for the fast turnaround on #15467! It fixes the 8 listed parsers, but the underlying issue still affects models that fall through the legacy tool-parsing path.

What PR #15467 fixed

PR #15467 added Function.Index = p.callIndex / p.callIndex++ to cogito, deepseek3, functiongemma, gemma4, lfm2, ministral, olmo3, and qwen3vl. qwen3-coder and qwen3 already got this in #14484 (2026-02-27).

What it didn't fix

Not every model uses a dedicated parser. Looking at model/parsers/parsers.go:ParserForName, models without an entry in that switch fall through to the legacy tool-parsing path at server/routes.go:2381 (the branch guarded by len(req.Tools) > 0 && (builtinParser == nil || !builtinParser.HasToolSupport())). That path never sets Function.Index, so every tool call in a streaming response emerges with index: 0 via ToToolCalls in openai/openai.go.

Reproduction (qwen2.5-coder:7b, which has no registered parser)

curl -s http://localhost:11434/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen2.5-coder:7b",
    "stream": true,
    "messages": [
      {"role": "system", "content": "Use the provided tools."},
      {"role": "user", "content": "Create hello.py with print(\"hello\") and world.py with print(\"world\")."}
    ],
    "tools": [{
      "type": "function",
      "function": {
        "name": "file_write",
        "parameters": {
          "type": "object",
          "properties": {
            "filePath": {"type":"string"},
            "content":  {"type":"string"}
          },
          "required": ["filePath","content"]
        }
      }
    }]
  }'

Both tool_calls chunks come back with "index": 0.

Suggested fix

Either:

(a) Set Function.Index in the legacy fallback tool-parsing path in server/routes.go (probably cleanest), or

(b) Post-normalize indices in openai/openai.go:ToToolCalls / toChunk when multiple tool calls arrive without distinct indices — essentially the "post-parse hook" already suggested in #15467's description.

I'd lean toward (b) as a safety net: it becomes a single source of truth regardless of which parser path fed it, and protects against future parsers forgetting the same boilerplate.

Side question

Does the current qwen3-coder:<tag> Modelfile on ollama.com actually declare PARSER qwen3-coder? I've been seeing index: 0 with qwen3-coder:30b on v0.20.4 despite Qwen3CoderParser setting callIndex correctly since #14484 — which would suggest the Modelfile isn't routing to that parser and is hitting the same legacy path. Happy to re-verify on v0.20.6 once released.

Environment

  • Ollama 0.20.2 / 0.20.4 (pre-#15467)
  • Models tested: qwen2.5-coder:7b, qwen3-coder:30b
Originally created by @CPIDLE on GitHub (Apr 11, 2026). Original GitHub issue: https://github.com/ollama/ollama/issues/15497 Thanks for the fast turnaround on #15467! It fixes the 8 listed parsers, but the underlying issue still affects models that fall through the legacy tool-parsing path. ## What PR #15467 fixed PR #15467 added `Function.Index = p.callIndex / p.callIndex++` to `cogito`, `deepseek3`, `functiongemma`, `gemma4`, `lfm2`, `ministral`, `olmo3`, and `qwen3vl`. `qwen3-coder` and `qwen3` already got this in #14484 (2026-02-27). ## What it didn't fix Not every model uses a dedicated parser. Looking at `model/parsers/parsers.go:ParserForName`, models without an entry in that switch fall through to the legacy tool-parsing path at `server/routes.go:2381` (the branch guarded by `len(req.Tools) > 0 && (builtinParser == nil || !builtinParser.HasToolSupport())`). That path never sets `Function.Index`, so every tool call in a streaming response emerges with `index: 0` via `ToToolCalls` in `openai/openai.go`. ## Reproduction (qwen2.5-coder:7b, which has no registered parser) ```bash curl -s http://localhost:11434/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{ "model": "qwen2.5-coder:7b", "stream": true, "messages": [ {"role": "system", "content": "Use the provided tools."}, {"role": "user", "content": "Create hello.py with print(\"hello\") and world.py with print(\"world\")."} ], "tools": [{ "type": "function", "function": { "name": "file_write", "parameters": { "type": "object", "properties": { "filePath": {"type":"string"}, "content": {"type":"string"} }, "required": ["filePath","content"] } } }] }' ``` Both `tool_calls` chunks come back with `"index": 0`. ## Suggested fix Either: (a) Set `Function.Index` in the legacy fallback tool-parsing path in `server/routes.go` (probably cleanest), or (b) Post-normalize indices in `openai/openai.go:ToToolCalls` / `toChunk` when multiple tool calls arrive without distinct indices — essentially the "post-parse hook" already suggested in #15467's description. I'd lean toward (b) as a safety net: it becomes a single source of truth regardless of which parser path fed it, and protects against future parsers forgetting the same boilerplate. ## Side question Does the current `qwen3-coder:<tag>` Modelfile on ollama.com actually declare `PARSER qwen3-coder`? I've been seeing `index: 0` with `qwen3-coder:30b` on v0.20.4 despite `Qwen3CoderParser` setting `callIndex` correctly since #14484 — which would suggest the Modelfile isn't routing to that parser and is hitting the same legacy path. Happy to re-verify on v0.20.6 once released. ## Environment - Ollama 0.20.2 / 0.20.4 (pre-#15467) - Models tested: `qwen2.5-coder:7b`, `qwen3-coder:30b`
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/ollama#9904