Skip to content

Add Ollama provider that injects empty parameters for zero-argument tools - #12879

Open
Andrea-Bruno wants to merge 2 commits into
QwenLM:mainfrom
Andrea-Bruno:fix/ollama-parameterless-tools
Open

Andrea-Bruno wants to merge 2 commits into
QwenLM:mainfrom
Andrea-Bruno:fix/ollama-parameterless-tools

Conversation

@Andrea-Bruno

@Andrea-Bruno Andrea-Bruno commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Adds a provider for Ollama in the OpenAI-compatible content generator. When Qwen Code talks to a local Ollama server through the openai auth type, this provider adds an empty parameters object to any tool that has no arguments, so the request is accepted. It mirrors what the MiniMax provider already does for the same class of failure. The change is scoped to Ollama only, detected from the base URL.

Why it's needed

Ollama rejects a function tool that carries no parameters field at all. Every request that includes a zero-argument tool fails with:

400 Bad Request
{"error":"Unable to generate parser for this template. Automatic parser generation failed: JSON schema error at #: properties must be an object"}

The converter sets parameters = undefined for parameterless tools because llama.cpp, LM Studio and vLLM reject the empty-object shape. Ollama has the opposite requirement: it needs a parameters object on every function tool. Without this provider, tools like get_goal and list_agents break every turn against a local Ollama server.

Reviewer Test Plan

How to verify

Run a local Ollama server and point Qwen Code at it with auth-type: openai and baseUrl: http://localhost:11434/v1. Load a zero-argument tool such as get_goal or list_agents. Before this change the request returns the 400 shown above; after it the request returns 200 and the turn completes. The unit tests cover both the detection and the injection:

npx vitest run packages/core/src/core/openaiContentGenerator/provider/ollama.test.ts

Evidence (Before & After)

Before, a parameterless tool sent with no parameters field:

HTTP 400
{"error":"Unable to generate parser for this template. Automatic parser generation failed: JSON schema error at #: properties must be an object"}

After, the same tool with parameters: { "type": "object", "properties": {} } injected by the provider:

HTTP 200

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ✅ tested
🐧 Linux ⚠️ not tested

Environment (optional)

Local Ollama server on Windows, plus the unit tests run through vitest.

Risk & Scope

  • Main risk or tradeoff: the base-URL heuristic is broad. A hostname that contains "ollama" as a whole label, for example my-ollama.lan or ollama.example.com, matches on any port, and any loopback host on port 11434 matches regardless of what is actually serving it. A llama.cpp or vLLM server listening on 11434 would receive the empty-object shape that those backends reject.
  • Not validated / out of scope: a GLM model served by a local Ollama still routes to the Zai provider, because the Zai check matches any glm-* model name on any hostname and runs before the Ollama check. That is pre-existing routing and is not changed here, so such a setup keeps the 400. The other direction works in this PR's favor: Mistral-family models on Ollama now route to Ollama, because the new check runs before Mistral's model-name fallback.
  • Breaking changes / migration notes: none. The provider only activates for URLs that match the Ollama pattern.

Linked Issues

Fixes #12878

中文说明

本 PR 做了什么

在 OpenAI 兼容的内容生成器中新增一个面向 Ollama 的 provider。当 Qwen Code 通过 openai 认证类型连接本地 Ollama 服务时,该 provider 会为任何无参数的工具补上一个空的 parameters 对象,从而让请求被接受。这与 MiniMax provider 针对同类失败所做的事情一致。改动只作用于 Ollama,通过 base URL 识别。

为什么需要它

Ollama 会拒绝一个完全没有 parameters 字段的 function 工具。任何包含零参数工具的请求都会失败,报错如下:

400 Bad Request
{"error":"Unable to generate parser for this template. Automatic parser generation failed: JSON schema error at #: properties must be an object"}

转换器对无参工具会把 parameters 设为 undefined,因为 llama.cpp、LM Studio 和 vLLM 会拒绝空对象结构。Ollama 的要求恰好相反:它需要每个 function 工具都带一个 parameters 对象。没有这个 provider,像 get_goal 和 list_agents 这样的工具会在连接本地 Ollama 服务时让每一轮对话都失败。

测试者验证计划

如何验证

启动一个本地 Ollama 服务,并用 auth-type: openai 和 baseUrl: http://localhost:11434/v1 把 Qwen Code 指向它。加载一个零参数工具,例如 get_goal 或 list_agents。在本改动之前,请求返回上面那个 400;改动之后,请求返回 200,对话正常完成。单元测试同时覆盖识别与注入两部分:

npx vitest run packages/core/src/core/openaiContentGenerator/provider/ollama.test.ts

证据(改动前后)

改动前,一个无参工具在没有 parameters 字段的情况下发出:

HTTP 400
{"error":"Unable to generate parser for this template. Automatic parser generation failed: JSON schema error at #: properties must be an object"}

改动后,同一个工具由 provider 注入 parameters: { "type": "object", "properties": {} }:

HTTP 200

测试平台

OS 状态
🍏 macOS ⚠️ 未测试
🪟 Windows ✅ 已测试
🐧 Linux ⚠️ 未测试

环境(可选)

Windows 上的本地 Ollama 服务,以及通过 vitest 运行的单元测试。

风险与范围

  • 主要风险或取舍:base-URL 的识别规则较宽。一个把 "ollama" 作为完整标签包含在内的主机名,例如 my-ollama.lan 或 ollama.example.com,在任意端口都会命中;而 11434 端口上的任意回环地址无论背后实际是什么服务都会命中。因此一个监听在 11434 的 llama.cpp 或 vLLM 服务会收到那些后端会拒绝的空对象结构。
  • 未验证 / 不在范围内:由本地 Ollama 提供的 GLM 模型仍会路由到 Zai provider,因为 Zai 的检查会在任意主机名上匹配任意 glm-* 模型名,且排在 Ollama 检查之前。这是既有的路由行为,本 PR 未作改动,所以这样的配置仍会遇到那个 400。另一个方向对本 PR 有利:跑在 Ollama 上的 Mistral 系模型现在会正确路由到 Ollama,因为新检查位于 Mistral 的模型名回退匹配之前。
  • 破坏性变更 / 迁移说明:无。该 provider 只对匹配 Ollama 模式的 URL 生效。

关联 Issue

Fixes #12878

Ollama rejects a function tool that carries no parameters field, failing the whole request with a 400 parser error. Add an Ollama provider that injects an empty object schema for parameterless tools, mirroring the MiniMax provider. Closes QwenLM#12878.

@doudouOUC doudouOUC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for this — the approach mirrors the MiniMax provider (#11834) faithfully, and the factory placement checks out: no existing check claims localhost or ollama-labelled hosts ahead of the new branch, the injection sits on the wire path (pipeline.ts calls provider.buildRequest and nothing re-processes tools afterwards), and the scoping stays Ollama-only as intended. Two items:

  1. packages/core/src/core/openaiContentGenerator/provider/ollama.ts:15 — '::1' in OLLAMA_LOCAL_HOSTS can never match. new URL('http://[::1]:11434/v1').hostname returns '[::1]' (WHATWG URL keeps the square brackets on IPv6 literals), so the comparison at line 31 never hits and an Ollama server reached via http://[::1]:11434/v1 silently misses this fix. A one-line change to '[::1]' (or stripping brackets before comparing) plus a positive test case in ollama.test.ts — the IPv6 path is currently untested, which is how this slipped through.
  2. A note on factory ordering (index.ts:110): the Zai check matches any glm-* model name on any hostname and runs before Ollama, so a GLM model served by a local Ollama routes to Zai and keeps the 400. Pre-existing routing, not introduced here — but worth one line in the description's scope section so the fix's coverage is stated precisely. (The other direction works in this PR's favor: Mistral-family models on Ollama now correctly route to Ollama, since the new check precedes Mistral's model-name fallback.)

The open item from the triage round also still stands: the description needs the PR template sections (Reviewer Test Plan, Evidence, Tested on, Risk & Scope — including the URL-heuristic breadth — Linked Issues, and the Chinese translation).


感谢提交——方案忠实地复用了 MiniMax provider(#11834)的既有模式,工厂中的放置位置也验证无误:新分支之前没有任何检查会认领 localhost 或带 ollama 标签的主机名;注入位于请求上链路径上(pipeline.ts 调用 provider.buildRequest,之后没有任何环节再处理 tools);作用域也如期只限于 Ollama。有两个问题:

  1. packages/core/src/core/openaiContentGenerator/provider/ollama.ts:15 —— OLLAMA_LOCAL_HOSTS 中的 '::1' 永远无法命中。new URL('http://[::1]:11434/v1').hostname 返回的是 '[::1]'(WHATWG URL 对 IPv6 字面量保留方括号),因此第 31 行的比较永远不会成立,通过 http://[::1]:11434/v1 访问的 Ollama 服务会静默地错过本次修复。修复只需一行:改为 '[::1]'(或在比较前去掉方括号),并在 ollama.test.ts 补一个正向用例——IPv6 路径目前没有测试覆盖,问题正是因此漏进来的。
  2. 关于工厂链顺序的一点说明(index.ts:110):Zai 的检查会按 glm-* 模型名匹配任意主机名,且排在 Ollama 之前,因此在本地 Ollama 上跑 GLM 模型的用户仍会被路由到 Zai、继续遇到那个 400。这是既有路由行为、并非本 PR 引入——但建议在描述的 scope 章节里写上一句,准确说明本次修复的覆盖范围。(另一个方向对本 PR 有利:Mistral 系模型跑在 Ollama 上现在会正确路由到 Ollama,因为新检查位于 Mistral 的模型名回退匹配之前。)

另外 triage 轮次中尚未完成的事项依然有效:描述需要按 PR 模板补齐各章节(Reviewer Test Plan、Evidence、Tested on、Risk & Scope——包括 URL 识别规则的覆盖面——Linked Issues,以及中文翻译)。

@Andrea-Bruno

Copy link
Copy Markdown
Contributor Author

Thanks for the precise review. I fixed the IPv6 case. The loopback list now holds the bracketed form, so a server reached over http://[::1]:11434/v1 is matched, and I added a positive test for that path. The unit tests pass locally, eight in all.

@Andrea-Bruno

Copy link
Copy Markdown
Contributor Author

We opened issue #13205 to note that this pull request is held up by the "review-pr" check timing out. That failure is inside the review job and not in our code, so it is outside our control and stops the pull request from completing.

@Andrea-Bruno

Copy link
Copy Markdown
Contributor Author

Thanks for the careful read. Both points are now addressed at the current head.

For the IPv6 case, the loopback list now holds the bracketed form '[::1]', which is what WHATWG URL.hostname actually returns for an IPv6 literal, so a server reached at http://[::1]:11434/v1 is matched. A positive test for that path is included in ollama.test.ts. This is in commit 28a9f23.

For the factory ordering note, the Risk and Scope section now states plainly that a GLM model served by a local Ollama still routes to the Zai provider because that check runs first, that this is pre-existing routing we did not change, and that the other direction (Mistral-family models on Ollama) now routes to Ollama as intended.

Could a maintainer take another look when there is time? Happy to adjust anything else.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ollama rejects zero-argument tools because the parameters field is omitted

3 participants