Skip to content

Commit 151cc75

Browse files
authored
docs(pages): document local-model setup, tool-calling requirement, and LLM timeouts (#400)
* docs(pages): add FAQ entry for local models without native tool calling Two changes per locale (en/zh/ja), addressing #234: - New "No tool calls parsed" entry under Configuration & startup: the symptom loop, the durable rule that the model must support native tool calling (deepseek-r1 narrates calls in content and can never work; qwen3 works), the Ollama tools-tag search link, and the maintainer's curl snippet to verify a model emits structured tool_calls without OCR in the loop. - The existing "Max tool requests reached" entry (where users actually land) gains a 4th cause bullet cross-linking the new entry. Anchors follow generateHeadingId (pages/src/utils/headingId.ts), the site's actual slugger, and were verified against the rendered DOM in all three locales. Code blocks are byte-identical across locales per i18n convention; heading counts stay in parity. * docs(pages): document Ollama custom-provider setup and LLM timeouts Two additions per locale (en/zh/ja), addressing #234: - Custom providers: a copy-paste Ollama example (127.0.0.1:11434/v1, protocol openai) with the note that custom providers require a non-empty api_key placeholder (resolver has no env fallback for them) and a pointer to the FAQ tool-calling rule. - New Timeouts subsection: providers.<name>.timeout_sec / llm.timeout_sec / OCR_LLM_TIMEOUT, the 300s default, and the caveat that timeout_sec is not supported by 'ocr config set' (config_cmd has no timeout handling) so config.json must be edited directly. The ja Timeouts heading is タイムアウト(Timeouts) so the site slugger (which strips katakana) still yields a linkable #timeouts anchor. Code blocks byte-identical across locales; heading parity kept. * fix(pages): decode percent-encoded anchor fragments before id lookup marked percent-encodes non-ASCII hrefs (#超时 renders as #%E8%B6%85%E6%97%B6), but heading ids are raw text from generateHeadingId, so handleContentClick's getElementById never matched for CJK anchors: same-page clicks silently no-oped and cross-page anchor scrolls exhausted their retries at the top of the page. This affected every pre-existing zh in-page anchor (e.g. faq 复用已有的环境变量) as well as the zh links added for #234. Decode the fragment (with a malformed-input guard) at both lookup sites.
1 parent d75f945 commit 151cc75

7 files changed

Lines changed: 241 additions & 2 deletions

File tree

pages/src/content/docs/en/configuration.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,47 @@ ocr config set custom_providers.my-gateway.model llama-3-70b
7272
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
7373
```
7474

75+
A local model served by Ollama is just a custom provider pointing at the
76+
local OpenAI-compatible endpoint:
77+
78+
```bash
79+
ocr config set provider ollama
80+
ocr config set custom_providers.ollama.url http://127.0.0.1:11434/v1
81+
ocr config set custom_providers.ollama.protocol openai
82+
ocr config set custom_providers.ollama.model qwen3:32b
83+
ocr config set custom_providers.ollama.api_key ollama
84+
```
85+
86+
Ollama ignores the API key, but custom providers require a non-empty
87+
`api_key` (there is no environment-variable fallback for them), so set
88+
any placeholder value. The model itself must support native tool
89+
calling — see
90+
["No tool calls parsed" (local models / Ollama)](../faq/#no-tool-calls-parsed-local-models-ollama)
91+
in the FAQ before picking one.
92+
93+
### Timeouts
94+
95+
Each LLM request has an HTTP timeout, defaulting to **300 seconds**.
96+
Slow local models (or large files) can need more. Three knobs, in
97+
increasing scope:
98+
99+
- `providers.<name>.timeout_sec` / `custom_providers.<name>.timeout_sec`
100+
— per-provider, in seconds.
101+
- `llm.timeout_sec` — for the legacy `llm` section, in seconds.
102+
- `OCR_LLM_TIMEOUT` environment variable — integer seconds; overrides
103+
the config-file value for every resolution path.
104+
105+
The `timeout_sec` keys are not supported by `ocr config set` — edit
106+
`~/.opencodereview/config.json` directly:
107+
108+
```json
109+
{
110+
"custom_providers": {
111+
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
112+
}
113+
}
114+
```
115+
75116
### Verify connectivity
76117

77118
```bash

pages/src/content/docs/en/faq.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,42 @@ OpenAI use different auth headers and different URL shapes — make sure
5353
against the current directory. If you're not inside a Git working tree,
5454
it exits early. Either `cd` into a repo, or pass `--repo /path/to/repo`.
5555

56+
### "No tool calls parsed" (local models / Ollama)
57+
58+
```
59+
[ocr] No tool calls parsed for src/foo.go, retrying...
60+
[ocr] Max tool requests reached for src/foo.go.
61+
```
62+
63+
If every review loops through `No tool calls parsed` retries and ends
64+
with "Max tool requests reached" and zero comments, the model — not the
65+
config — is the problem. OCR drives the review entirely through tool
66+
calls, so **the model must support native tool calling (function
67+
calling)**. A model that merely *narrates* tool calls in its text
68+
output (or inside `<think>` blocks) can never work with OCR, no matter
69+
how the prompt is tuned — `deepseek-r1` is a common example. Models
70+
with native tool support, such as `qwen3`, work fine. For Ollama, pick
71+
from the models tagged with tools support:
72+
<https://ollama.com/search?c=tools>.
73+
74+
Verify a local model directly, without OCR in the loop:
75+
76+
```bash
77+
curl http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
78+
"model": "qwen3:32b",
79+
"messages": [{"role": "user", "content": "The code below has a bug, use the report_bug tool to report it.\n\nfunc add(a, b int) int {\n return a - b\n}"}],
80+
"tools": [{"type": "function", "function": {"name": "report_bug", "description": "Report a bug in the code",
81+
"parameters": {"type": "object", "properties": {"line": {"type": "integer"}, "description": {"type": "string"}}, "required": ["description"]}}}]
82+
}'
83+
```
84+
85+
Pass: the response contains a structured `tool_calls` array naming
86+
`report_bug`. Fail: the "call" appears as text inside `content`.
87+
88+
If the model *does* support tools but responses are slow on local
89+
hardware, raise the LLM timeout instead — see
90+
[Timeouts](../configuration/#timeouts).
91+
5692
## Filtering & rules
5793

5894
### My file isn't being reviewed
@@ -173,6 +209,9 @@ usually one of:
173209
`--max-tools 40` for more, `--max-tools 15` for fewer). Values 1–9
174210
are clamped up to 10; `0` (the default) uses the template default of
175211
30.
212+
- The model does not support native tool calling at all (common with
213+
local models) — see
214+
["No tool calls parsed" (local models / Ollama)](#no-tool-calls-parsed-local-models-ollama).
176215

177216
### Some sub-agents fail; the run still exits 0
178217

pages/src/content/docs/ja/configuration.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,47 @@ ocr config set custom_providers.my-gateway.model llama-3-70b
7070
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
7171
```
7272

73+
Ollama で動かすローカルモデルは、ローカルの OpenAI 互換エンドポイントを
74+
指すカスタム provider にすぎません。
75+
76+
```bash
77+
ocr config set provider ollama
78+
ocr config set custom_providers.ollama.url http://127.0.0.1:11434/v1
79+
ocr config set custom_providers.ollama.protocol openai
80+
ocr config set custom_providers.ollama.model qwen3:32b
81+
ocr config set custom_providers.ollama.api_key ollama
82+
```
83+
84+
Ollama は API key を無視しますが、カスタム provider は空でない `api_key`
85+
必要とします(カスタム provider には環境変数のフォールバックがありません)。
86+
そのため任意のプレースホルダー値を設定してください。モデル自体はネイティブな
87+
ツール呼び出しをサポートしている必要があります——選ぶ前に FAQ の
88+
["No tool calls parsed"(ローカルモデル / Ollama)](../faq/#no-tool-calls-parsed-ollama)
89+
参照してください。
90+
91+
### タイムアウト(Timeouts)
92+
93+
各 LLM リクエストには HTTP タイムアウトがあり、デフォルトは **300 秒**です。
94+
遅いローカルモデル(あるいは大きなファイル)では、それ以上の時間が必要になることがあります。
95+
スコープの狭い順に、3 つの設定があります。
96+
97+
- `providers.<name>.timeout_sec` / `custom_providers.<name>.timeout_sec`
98+
——provider ごと、秒単位。
99+
- `llm.timeout_sec`——レガシーな `llm` セクション用、秒単位。
100+
- `OCR_LLM_TIMEOUT` 環境変数——整数(秒単位)。すべての解決パスで設定ファイルの
101+
値を上書きします。
102+
103+
`timeout_sec` key は `ocr config set` ではサポートされていません——
104+
`~/.opencodereview/config.json` を直接編集してください。
105+
106+
```json
107+
{
108+
"custom_providers": {
109+
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
110+
}
111+
}
112+
```
113+
73114
### 接続性を検証する
74115

75116
```bash

pages/src/content/docs/ja/faq.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,39 @@ OpenAI は異なる auth header と URL フォーマットを使います——`
5353
`git ls-files`)を実行します。Git ワークツリー内にいない場合は、早期に終了します。リポジトリに
5454
`cd` するか、`--repo /path/to/repo` を渡してください。
5555

56+
### "No tool calls parsed"(ローカルモデル / Ollama)
57+
58+
```
59+
[ocr] No tool calls parsed for src/foo.go, retrying...
60+
[ocr] Max tool requests reached for src/foo.go.
61+
```
62+
63+
すべてのレビューが `No tool calls parsed` のリトライをループし、"Max tool requests
64+
reached" とコメント 0 件で終わる場合、問題は設定ではなくモデルにあります。OCR はレビュー全体を
65+
ツール呼び出しで駆動するため、**モデルはネイティブなツール呼び出し(function calling)を
66+
サポートしている必要があります**。ツール呼び出しをテキスト出力(あるいは `<think>` ブロック内)で
67+
*語るだけ*のモデルは、prompt をどう調整しても OCR では決して動作しません——`deepseek-r1`
68+
よくある例です。`qwen3` のようなネイティブなツールサポートを持つモデルは問題なく動作します。
69+
Ollama の場合は、tools サポートのタグが付いたモデルから選んでください:
70+
<https://ollama.com/search?c=tools>
71+
72+
OCR を介さずに、ローカルモデルを直接検証するには:
73+
74+
```bash
75+
curl http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
76+
"model": "qwen3:32b",
77+
"messages": [{"role": "user", "content": "The code below has a bug, use the report_bug tool to report it.\n\nfunc add(a, b int) int {\n return a - b\n}"}],
78+
"tools": [{"type": "function", "function": {"name": "report_bug", "description": "Report a bug in the code",
79+
"parameters": {"type": "object", "properties": {"line": {"type": "integer"}, "description": {"type": "string"}}, "required": ["description"]}}}]
80+
}'
81+
```
82+
83+
合格: 応答に `report_bug` を指す構造化された `tool_calls` 配列が含まれる。不合格: 「呼び出し」が
84+
`content` 内のテキストとして現れる。
85+
86+
モデルがツールを*サポートしている*のに、ローカルハードウェアで応答が遅い場合は、代わりに
87+
LLM タイムアウトを引き上げてください——[タイムアウト](../configuration/#timeouts)を参照。
88+
5689
## フィルタリングとルール
5790

5891
### ファイルがレビューされない
@@ -163,6 +196,9 @@ JSON モードでは `warnings` にも表示されます。
163196
- ファイルが本当に大きい、あるいはコンテキストが重く、30 回では足りない。`--max-tools <n>`
164197
上げるか下げるか調整してください(例: `--max-tools 40` でより多く、`--max-tools 15` でより少なく)。
165198
1〜9 は 10 に引き上げられます。`0`(デフォルト)はテンプレートのデフォルト 30 を使います。
199+
- モデルがネイティブなツール呼び出しを全くサポートしていない(ローカルモデルでよくある)——
200+
["No tool calls parsed"(ローカルモデル / Ollama)](#no-tool-calls-parsed-ollama)
201+
参照してください。
166202

167203
### 一部のサブエージェントが失敗しても、実行は 0 で終了する
168204

pages/src/content/docs/zh/configuration.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,43 @@ ocr config set custom_providers.my-gateway.model llama-3-70b
6868
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
6969
```
7070

71+
用 Ollama 跑本地模型,就是一个指向本地 OpenAI 兼容端点的自定义 provider:
72+
73+
```bash
74+
ocr config set provider ollama
75+
ocr config set custom_providers.ollama.url http://127.0.0.1:11434/v1
76+
ocr config set custom_providers.ollama.protocol openai
77+
ocr config set custom_providers.ollama.model qwen3:32b
78+
ocr config set custom_providers.ollama.api_key ollama
79+
```
80+
81+
Ollama 会忽略 API key,但自定义 provider 要求非空的 `api_key`(自定义
82+
provider 没有环境变量回退),所以设任意占位值即可。模型本身必须支持原生
83+
工具调用——选型前请先看 FAQ 中的
84+
["No tool calls parsed"(本地模型 / Ollama)](../faq/#no-tool-calls-parsed-本地模型-ollama)
85+
86+
### 超时
87+
88+
每个 LLM 请求都有 HTTP 超时,默认 **300 秒**。慢的本地模型(或大文件)可能
89+
需要更长的时间。三个配置项,作用域递增:
90+
91+
- `providers.<name>.timeout_sec` / `custom_providers.<name>.timeout_sec`
92+
——per-provider,单位秒。
93+
- `llm.timeout_sec`——用于旧版 `llm` 配置段,单位秒。
94+
- `OCR_LLM_TIMEOUT` 环境变量——整数秒;对每条解析路径都覆盖配置文件里
95+
的值。
96+
97+
`ocr config set` 不支持 `timeout_sec` key——直接编辑
98+
`~/.opencodereview/config.json`
99+
100+
```json
101+
{
102+
"custom_providers": {
103+
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
104+
}
105+
}
106+
```
107+
71108
### 验证连通性
72109

73110
```bash

pages/src/content/docs/zh/faq.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,38 @@ URL 格式——确保 `llm.use_anthropic` 与你指向的 URL 相匹配:
4848
`ocr review` 对当前目录运行 `git diff`(以及对 untracked 文件的 `git ls-files`)。
4949
若你不在 Git 工作树内,它会提前退出。要么 `cd` 进仓库,要么传 `--repo /path/to/repo`
5050

51+
### "No tool calls parsed"(本地模型 / Ollama)
52+
53+
```
54+
[ocr] No tool calls parsed for src/foo.go, retrying...
55+
[ocr] Max tool requests reached for src/foo.go.
56+
```
57+
58+
若每次评审都在 `No tool calls parsed` 重试中循环,最终以 "Max tool requests
59+
reached" 结束且没有任何评论,问题出在模型——而非配置。OCR 完全通过工具调用驱动评审,
60+
因此**模型必须支持原生工具调用(function calling)**。只在文本输出(或
61+
`<think>` 块内)*叙述*工具调用的模型,无论怎么调 prompt 都永远无法与 OCR
62+
配合使用——`deepseek-r1` 是常见例子。具备原生工具支持的模型(如 `qwen3`)则工作
63+
正常。对 Ollama,请从带 tools 标签的模型中挑选:
64+
<https://ollama.com/search?c=tools>
65+
66+
绕开 OCR、直接验证本地模型:
67+
68+
```bash
69+
curl http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
70+
"model": "qwen3:32b",
71+
"messages": [{"role": "user", "content": "The code below has a bug, use the report_bug tool to report it.\n\nfunc add(a, b int) int {\n return a - b\n}"}],
72+
"tools": [{"type": "function", "function": {"name": "report_bug", "description": "Report a bug in the code",
73+
"parameters": {"type": "object", "properties": {"line": {"type": "integer"}, "description": {"type": "string"}}, "required": ["description"]}}}]
74+
}'
75+
```
76+
77+
通过:响应包含指向 `report_bug` 的结构化 `tool_calls` 数组。失败:“调用”以
78+
文本形式出现在 `content` 里。
79+
80+
若模型*确实*支持工具,只是在本地硬件上响应缓慢,请改为调高 LLM 超时——见
81+
[超时](../configuration/#超时)
82+
5183
## 过滤与规则
5284

5385
### 我的文件没被评审
@@ -150,6 +182,8 @@ diff 能从 plan 中受益。要为单次评审跳过它,用更小 diff 运行
150182
- 文件确实大或上下文重,30 轮不够。用 `--max-tools <n>` 调高或调低
151183
(如 `--max-tools 40` 更多,`--max-tools 15` 更少)。1–9 会被上调到 10;
152184
`0`(默认)用模板默认 30。
185+
- 模型完全不支持原生工具调用(本地模型常见)——见
186+
["No tool calls parsed"(本地模型 / Ollama)](#no-tool-calls-parsed-本地模型-ollama)
153187

154188
### 一些子 agent 失败;运行仍以 0 退出
155189

pages/src/pages/DocsPage.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ import docContentsIcon from '../assets/icons/doc-contents.svg';
1111
import searchIcon from '../assets/icons/icon-search.svg';
1212
import '../styles/docs-markdown.css';
1313

14+
// marked percent-encodes non-ASCII hrefs; heading ids are raw text from
15+
// generateHeadingId, so fragments must be decoded before lookup.
16+
function decodeFragment(fragment: string): string {
17+
try {
18+
return decodeURIComponent(fragment);
19+
} catch {
20+
return fragment;
21+
}
22+
}
23+
1424
/* ─── Sidebar tree data ─── */
1525
interface SidebarItem {
1626
id: string;
@@ -198,7 +208,7 @@ const DocsPage: React.FC = () => {
198208
// Skip pure anchors (same-page scroll)
199209
if (href.startsWith('#')) {
200210
e.preventDefault();
201-
const id = href.slice(1);
211+
const id = decodeFragment(href.slice(1));
202212
const el = document.getElementById(id);
203213
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
204214
return;
@@ -217,7 +227,8 @@ const DocsPage: React.FC = () => {
217227
e.preventDefault();
218228
navigateToDoc(slug);
219229
// Handle anchor scroll after navigation with reliable retry
220-
const anchor2 = href.split('#')[1];
230+
const anchor2raw = href.split('#')[1];
231+
const anchor2 = anchor2raw ? decodeFragment(anchor2raw) : undefined;
221232
if (anchor2) {
222233
const tryScroll = (attempts: number) => {
223234
const el = document.getElementById(anchor2);

0 commit comments

Comments
 (0)