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.
This commit is contained in:
chethanuk 2026-07-21 07:57:35 +04:00 committed by GitHub
parent d75f945b46
commit 151cc7582e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 241 additions and 2 deletions

View file

@ -72,6 +72,47 @@ ocr config set custom_providers.my-gateway.model llama-3-70b
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
```
A local model served by Ollama is just a custom provider pointing at the
local OpenAI-compatible endpoint:
```bash
ocr config set provider ollama
ocr config set custom_providers.ollama.url http://127.0.0.1:11434/v1
ocr config set custom_providers.ollama.protocol openai
ocr config set custom_providers.ollama.model qwen3:32b
ocr config set custom_providers.ollama.api_key ollama
```
Ollama ignores the API key, but custom providers require a non-empty
`api_key` (there is no environment-variable fallback for them), so set
any placeholder value. The model itself must support native tool
calling — see
["No tool calls parsed" (local models / Ollama)](../faq/#no-tool-calls-parsed-local-models-ollama)
in the FAQ before picking one.
### Timeouts
Each LLM request has an HTTP timeout, defaulting to **300 seconds**.
Slow local models (or large files) can need more. Three knobs, in
increasing scope:
- `providers.<name>.timeout_sec` / `custom_providers.<name>.timeout_sec`
— per-provider, in seconds.
- `llm.timeout_sec` — for the legacy `llm` section, in seconds.
- `OCR_LLM_TIMEOUT` environment variable — integer seconds; overrides
the config-file value for every resolution path.
The `timeout_sec` keys are not supported by `ocr config set` — edit
`~/.opencodereview/config.json` directly:
```json
{
"custom_providers": {
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
}
}
```
### Verify connectivity
```bash

View file

@ -53,6 +53,42 @@ OpenAI use different auth headers and different URL shapes — make sure
against the current directory. If you're not inside a Git working tree,
it exits early. Either `cd` into a repo, or pass `--repo /path/to/repo`.
### "No tool calls parsed" (local models / Ollama)
```
[ocr] No tool calls parsed for src/foo.go, retrying...
[ocr] Max tool requests reached for src/foo.go.
```
If every review loops through `No tool calls parsed` retries and ends
with "Max tool requests reached" and zero comments, the model — not the
config — is the problem. OCR drives the review entirely through tool
calls, so **the model must support native tool calling (function
calling)**. A model that merely *narrates* tool calls in its text
output (or inside `<think>` blocks) can never work with OCR, no matter
how the prompt is tuned — `deepseek-r1` is a common example. Models
with native tool support, such as `qwen3`, work fine. For Ollama, pick
from the models tagged with tools support:
<https://ollama.com/search?c=tools>.
Verify a local model directly, without OCR in the loop:
```bash
curl http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "qwen3:32b",
"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}"}],
"tools": [{"type": "function", "function": {"name": "report_bug", "description": "Report a bug in the code",
"parameters": {"type": "object", "properties": {"line": {"type": "integer"}, "description": {"type": "string"}}, "required": ["description"]}}}]
}'
```
Pass: the response contains a structured `tool_calls` array naming
`report_bug`. Fail: the "call" appears as text inside `content`.
If the model *does* support tools but responses are slow on local
hardware, raise the LLM timeout instead — see
[Timeouts](../configuration/#timeouts).
## Filtering & rules
### My file isn't being reviewed
@ -173,6 +209,9 @@ usually one of:
`--max-tools 40` for more, `--max-tools 15` for fewer). Values 19
are clamped up to 10; `0` (the default) uses the template default of
30.
- The model does not support native tool calling at all (common with
local models) — see
["No tool calls parsed" (local models / Ollama)](#no-tool-calls-parsed-local-models-ollama).
### Some sub-agents fail; the run still exits 0

View file

@ -70,6 +70,47 @@ ocr config set custom_providers.my-gateway.model llama-3-70b
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
```
Ollama で動かすローカルモデルは、ローカルの OpenAI 互換エンドポイントを
指すカスタム provider にすぎません。
```bash
ocr config set provider ollama
ocr config set custom_providers.ollama.url http://127.0.0.1:11434/v1
ocr config set custom_providers.ollama.protocol openai
ocr config set custom_providers.ollama.model qwen3:32b
ocr config set custom_providers.ollama.api_key ollama
```
Ollama は API key を無視しますが、カスタム provider は空でない `api_key`
必要とします(カスタム provider には環境変数のフォールバックがありません)。
そのため任意のプレースホルダー値を設定してください。モデル自体はネイティブな
ツール呼び出しをサポートしている必要があります——選ぶ前に FAQ の
["No tool calls parsed"(ローカルモデル / Ollama](../faq/#no-tool-calls-parsed-ollama)を
参照してください。
### タイムアウトTimeouts
各 LLM リクエストには HTTP タイムアウトがあり、デフォルトは **300 秒**です。
遅いローカルモデル(あるいは大きなファイル)では、それ以上の時間が必要になることがあります。
スコープの狭い順に、3 つの設定があります。
- `providers.<name>.timeout_sec` / `custom_providers.<name>.timeout_sec`
——provider ごと、秒単位。
- `llm.timeout_sec`——レガシーな `llm` セクション用、秒単位。
- `OCR_LLM_TIMEOUT` 環境変数——整数(秒単位)。すべての解決パスで設定ファイルの
値を上書きします。
`timeout_sec` key は `ocr config set` ではサポートされていません——
`~/.opencodereview/config.json` を直接編集してください。
```json
{
"custom_providers": {
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
}
}
```
### 接続性を検証する
```bash

View file

@ -53,6 +53,39 @@ OpenAI は異なる auth header と URL フォーマットを使います——`
`git ls-files`を実行します。Git ワークツリー内にいない場合は、早期に終了します。リポジトリに
`cd` するか、`--repo /path/to/repo` を渡してください。
### "No tool calls parsed"(ローカルモデル / Ollama
```
[ocr] No tool calls parsed for src/foo.go, retrying...
[ocr] Max tool requests reached for src/foo.go.
```
すべてのレビューが `No tool calls parsed` のリトライをループし、"Max tool requests
reached" とコメント 0 件で終わる場合、問題は設定ではなくモデルにあります。OCR はレビュー全体を
ツール呼び出しで駆動するため、**モデルはネイティブなツール呼び出しfunction calling
サポートしている必要があります**。ツール呼び出しをテキスト出力(あるいは `<think>` ブロック内)で
*語るだけ*のモデルは、prompt をどう調整しても OCR では決して動作しません——`deepseek-r1`
よくある例です。`qwen3` のようなネイティブなツールサポートを持つモデルは問題なく動作します。
Ollama の場合は、tools サポートのタグが付いたモデルから選んでください:
<https://ollama.com/search?c=tools>
OCR を介さずに、ローカルモデルを直接検証するには:
```bash
curl http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "qwen3:32b",
"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}"}],
"tools": [{"type": "function", "function": {"name": "report_bug", "description": "Report a bug in the code",
"parameters": {"type": "object", "properties": {"line": {"type": "integer"}, "description": {"type": "string"}}, "required": ["description"]}}}]
}'
```
合格: 応答に `report_bug` を指す構造化された `tool_calls` 配列が含まれる。不合格: 「呼び出し」が
`content` 内のテキストとして現れる。
モデルがツールを*サポートしている*のに、ローカルハードウェアで応答が遅い場合は、代わりに
LLM タイムアウトを引き上げてください——[タイムアウト](../configuration/#timeouts)を参照。
## フィルタリングとルール
### ファイルがレビューされない
@ -163,6 +196,9 @@ JSON モードでは `warnings` にも表示されます。
- ファイルが本当に大きい、あるいはコンテキストが重く、30 回では足りない。`--max-tools <n>`
上げるか下げるか調整してください(例: `--max-tools 40` でより多く、`--max-tools 15` でより少なく)。
1〜9 は 10 に引き上げられます。`0`(デフォルト)はテンプレートのデフォルト 30 を使います。
- モデルがネイティブなツール呼び出しを全くサポートしていない(ローカルモデルでよくある)——
["No tool calls parsed"(ローカルモデル / Ollama](#no-tool-calls-parsed-ollama)を
参照してください。
### 一部のサブエージェントが失敗しても、実行は 0 で終了する

View file

@ -68,6 +68,43 @@ ocr config set custom_providers.my-gateway.model llama-3-70b
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
```
用 Ollama 跑本地模型,就是一个指向本地 OpenAI 兼容端点的自定义 provider
```bash
ocr config set provider ollama
ocr config set custom_providers.ollama.url http://127.0.0.1:11434/v1
ocr config set custom_providers.ollama.protocol openai
ocr config set custom_providers.ollama.model qwen3:32b
ocr config set custom_providers.ollama.api_key ollama
```
Ollama 会忽略 API key但自定义 provider 要求非空的 `api_key`(自定义
provider 没有环境变量回退),所以设任意占位值即可。模型本身必须支持原生
工具调用——选型前请先看 FAQ 中的
["No tool calls parsed"(本地模型 / Ollama](../faq/#no-tool-calls-parsed-本地模型-ollama)。
### 超时
每个 LLM 请求都有 HTTP 超时,默认 **300 秒**。慢的本地模型(或大文件)可能
需要更长的时间。三个配置项,作用域递增:
- `providers.<name>.timeout_sec` / `custom_providers.<name>.timeout_sec`
——per-provider单位秒。
- `llm.timeout_sec`——用于旧版 `llm` 配置段,单位秒。
- `OCR_LLM_TIMEOUT` 环境变量——整数秒;对每条解析路径都覆盖配置文件里
的值。
`ocr config set` 不支持 `timeout_sec` key——直接编辑
`~/.opencodereview/config.json`
```json
{
"custom_providers": {
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
}
}
```
### 验证连通性
```bash

View file

@ -48,6 +48,38 @@ URL 格式——确保 `llm.use_anthropic` 与你指向的 URL 相匹配:
`ocr review` 对当前目录运行 `git diff`(以及对 untracked 文件的 `git ls-files`)。
若你不在 Git 工作树内,它会提前退出。要么 `cd` 进仓库,要么传 `--repo /path/to/repo`
### "No tool calls parsed"(本地模型 / Ollama
```
[ocr] No tool calls parsed for src/foo.go, retrying...
[ocr] Max tool requests reached for src/foo.go.
```
若每次评审都在 `No tool calls parsed` 重试中循环,最终以 "Max tool requests
reached" 结束且没有任何评论问题出在模型——而非配置。OCR 完全通过工具调用驱动评审,
因此**模型必须支持原生工具调用function calling**。只在文本输出(或
`<think>` 块内)*叙述*工具调用的模型,无论怎么调 prompt 都永远无法与 OCR
配合使用——`deepseek-r1` 是常见例子。具备原生工具支持的模型(如 `qwen3`)则工作
正常。对 Ollama请从带 tools 标签的模型中挑选:
<https://ollama.com/search?c=tools>
绕开 OCR、直接验证本地模型
```bash
curl http://127.0.0.1:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "qwen3:32b",
"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}"}],
"tools": [{"type": "function", "function": {"name": "report_bug", "description": "Report a bug in the code",
"parameters": {"type": "object", "properties": {"line": {"type": "integer"}, "description": {"type": "string"}}, "required": ["description"]}}}]
}'
```
通过:响应包含指向 `report_bug` 的结构化 `tool_calls` 数组。失败:“调用”以
文本形式出现在 `content` 里。
若模型*确实*支持工具,只是在本地硬件上响应缓慢,请改为调高 LLM 超时——见
[超时](../configuration/#超时)。
## 过滤与规则
### 我的文件没被评审
@ -150,6 +182,8 @@ diff 能从 plan 中受益。要为单次评审跳过它,用更小 diff 运行
- 文件确实大或上下文重30 轮不够。用 `--max-tools <n>` 调高或调低
(如 `--max-tools 40` 更多,`--max-tools 15` 更少。19 会被上调到 10
`0`(默认)用模板默认 30。
- 模型完全不支持原生工具调用(本地模型常见)——见
["No tool calls parsed"(本地模型 / Ollama](#no-tool-calls-parsed-本地模型-ollama)。
### 一些子 agent 失败;运行仍以 0 退出

View file

@ -11,6 +11,16 @@ import docContentsIcon from '../assets/icons/doc-contents.svg';
import searchIcon from '../assets/icons/icon-search.svg';
import '../styles/docs-markdown.css';
// marked percent-encodes non-ASCII hrefs; heading ids are raw text from
// generateHeadingId, so fragments must be decoded before lookup.
function decodeFragment(fragment: string): string {
try {
return decodeURIComponent(fragment);
} catch {
return fragment;
}
}
/* ─── Sidebar tree data ─── */
interface SidebarItem {
id: string;
@ -198,7 +208,7 @@ const DocsPage: React.FC = () => {
// Skip pure anchors (same-page scroll)
if (href.startsWith('#')) {
e.preventDefault();
const id = href.slice(1);
const id = decodeFragment(href.slice(1));
const el = document.getElementById(id);
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
return;
@ -217,7 +227,8 @@ const DocsPage: React.FC = () => {
e.preventDefault();
navigateToDoc(slug);
// Handle anchor scroll after navigation with reliable retry
const anchor2 = href.split('#')[1];
const anchor2raw = href.split('#')[1];
const anchor2 = anchor2raw ? decodeFragment(anchor2raw) : undefined;
if (anchor2) {
const tryScroll = (attempts: number) => {
const el = document.getElementById(anchor2);