Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.ja-JP.md
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,8 @@ OCRは4層の優先度チェーンを使ってレビュールールを解決し
| `providers.<name>.extra_body` | object | すべてのリクエストボディにマージされるJSONオブジェクト |
| `providers.<name>.extra_headers` | string | カンマ区切りの `key=value` HTTPヘッダー |
| `custom_providers.<name>.*` | — | 任意の`models`を含む`providers.<name>.*`と同じフィールド |
| `routing.models` | array | フェイルオーバー用の順序付きモデルプール:`[{provider, model}]`([マルチモデルフェイルオーバー](#マルチモデルフェイルオーバー)を参照) |
| `routing.policy` | string | 選択ポリシー;`priority`(デフォルト、現時点で唯一の値) |
| `llm.url` | string | `https://api.openai.com/v1/chat/completions` |
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.auth_header` | string | Anthropicのみ:`x-api-key` \| `authorization` |
Expand All @@ -688,6 +690,32 @@ OCRは4層の優先度チェーンを使ってレビュールールを解決し
| `OCR_LLM_MODEL` | モデル名 |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic、`false` = OpenAI |

### マルチモデルフェイルオーバー

デフォルトでは、レビューは単一のモデル(`provider` + `model`)を使用します。レート制限やプロバイダー障害に耐えるには、順序付きの `routing.models` プールを設定します。レビュアーは各モデルを順番に試し、あるモデルがレート制限・サーバーエラー・タイムアウトになると次のモデルへフェイルオーバーします:

```json
{
"providers": {
"anthropic": { "api_key": "sk-ant-...", "model": "claude-opus-4-6" },
"deepseek": { "api_key": "sk-...", "model": "deepseek-v3" }
},
"routing": {
"models": [
{ "provider": "anthropic", "model": "claude-opus-4-6" },
{ "provider": "deepseek", "model": "deepseek-v3" }
],
"policy": "priority"
}
}
```

- 各エントリは、設定済みのプロバイダー(認証情報 / エンドポイント用)とモデルを参照します。`model` を省略した場合はプロバイダーのデフォルトモデルを使用します。
- `routing.policy` はプールの順序付け方法を選択します。現在サポートされているのは `priority` のみ(最初のエントリがプライマリ)で、このフィールドは将来のポリシー(例:weighted)のために予約されており、未知の値は暗黙に無視されるのではなく拒否されます。
- レート制限中または利用不可のモデルは一時的に保留され、並行するファイル単位のレビューが再ヒットせずスキップできます。
- フェイルオーバーは可用性エラー(レート制限、5xx、ネットワーク / タイムアウト)で発生します。クライアント側エラー(不正なリクエスト、ペイロード過大)では発生しません。別のモデルでも同様に失敗するためです。
- `routing.models` がなければ動作は変わりません。`--model` は単一モデルを固定し、プールをバイパスします。


## テレメトリー

Expand Down
28 changes: 28 additions & 0 deletions README.ko-KR.md
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,8 @@ Config file: `~/.opencodereview/config.json`
| `providers.<name>.extra_body` | object | 모든 요청 본문에 병합되는 JSON 객체 |
| `providers.<name>.extra_headers` | string | 쉼표로 구분된 `key=value` HTTP 헤더 |
| `custom_providers.<name>.*` | — | optional `models`를 포함한 `providers.<name>.*`과 동일한 필드 |
| `routing.models` | array | 페일오버용 정렬된 모델 풀: `[{provider, model}]` ([다중 모델 페일오버](#다중-모델-페일오버) 참조) |
| `routing.policy` | string | 선택 정책; `priority` (기본값, 현재 유일한 값) |
| `llm.url` | string | `https://api.openai.com/v1/chat/completions` |
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.auth_header` | string | Anthropic only: `x-api-key` \| `authorization` |
Expand All @@ -646,6 +648,32 @@ Config file: `~/.opencodereview/config.json`
| `OCR_LLM_MODEL` | Model name |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic, `false` = OpenAI |

### 다중 모델 페일오버

기본적으로 리뷰는 단일 모델(`provider` + `model`)을 사용합니다. 속도 제한과 공급자 장애에 대응하려면 정렬된 `routing.models` 풀을 구성하세요. 리뷰어는 각 모델을 순서대로 시도하고, 어떤 모델이 속도 제한에 걸리거나 서버 오류를 반환하거나 타임아웃되면 다음 모델로 페일오버합니다:

```json
{
"providers": {
"anthropic": { "api_key": "sk-ant-...", "model": "claude-opus-4-6" },
"deepseek": { "api_key": "sk-...", "model": "deepseek-v3" }
},
"routing": {
"models": [
{ "provider": "anthropic", "model": "claude-opus-4-6" },
{ "provider": "deepseek", "model": "deepseek-v3" }
],
"policy": "priority"
}
}
```

- 각 항목은 구성된 공급자(자격 증명 / 엔드포인트용)와 모델을 참조합니다. `model`을 생략하면 공급자의 기본 모델을 사용합니다.
- `routing.policy`는 풀의 정렬 방식을 선택합니다. 현재는 `priority`만 지원되며(첫 번째 항목이 기본), 이 필드는 향후 정책(예: weighted)을 위해 예약되어 있고, 알 수 없는 값은 조용히 무시되지 않고 거부됩니다.
- 속도 제한에 걸렸거나 사용할 수 없는 모델은 잠시 보류되어, 동시에 실행되는 파일별 리뷰가 다시 시도하지 않고 건너뜁니다.
- 페일오버는 가용성 오류(속도 제한, 5xx, 네트워크 / 타임아웃)에서 발생합니다. 클라이언트 측 오류(잘못된 요청, 페이로드 초과)에서는 발생하지 않습니다. 다른 모델도 동일하게 실패하기 때문입니다.
- `routing.models`가 없으면 동작은 변경되지 않습니다. `--model`은 단일 모델을 고정하고 풀을 우회합니다.

## Telemetry

관측성을 위한 OpenTelemetry 통합(spans, metrics)입니다. 기본값은 disabled입니다.
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,8 @@ Config file: `~/.opencodereview/config.json`
| `providers.<name>.extra_body` | object | JSON object merged into every request body |
| `providers.<name>.extra_headers` | string | Comma-separated `key=value` HTTP headers |
| `custom_providers.<name>.*` | — | Same fields as `providers.<name>.*`, including optional `models` |
| `routing.models` | array | Ordered model pool for failover: `[{provider, model}]` (see [Multi-model fallback](#multi-model-fallback)) |
| `routing.policy` | string | Selection policy; `priority` (default, only value today) |
| `llm.url` | string | `https://api.openai.com/v1/chat/completions` |
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.auth_header` | string | Anthropic only: `x-api-key` \| `authorization` |
Expand All @@ -693,6 +695,32 @@ Environment variables take precedence over the config file.
| `OCR_LLM_MODEL` | Model name |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic, `false` = OpenAI |

### Multi-model fallback

By default a review uses a single model (`provider` + `model`). To survive rate limits and provider outages, configure an ordered `routing.models` pool — the reviewer tries each in order and falls over to the next when one is rate-limited, returns a server error, or times out:

```json
{
"providers": {
"anthropic": { "api_key": "sk-ant-...", "model": "claude-opus-4-6" },
"deepseek": { "api_key": "sk-...", "model": "deepseek-v3" }
},
"routing": {
"models": [
{ "provider": "anthropic", "model": "claude-opus-4-6" },
{ "provider": "deepseek", "model": "deepseek-v3" }
],
"policy": "priority"
}
}
```

- Each entry references a configured provider (for credentials / endpoint) and a model; an omitted `model` uses the provider's default.
- `routing.policy` selects how the pool is ordered. Only `priority` is supported today (first entry is primary); the field is reserved for future policies (e.g. weighted), and an unknown value is rejected rather than silently ignored.
- A rate-limited or unavailable model is briefly parked so concurrent per-file reviews skip it instead of each re-hitting it.
- Failover triggers on availability errors (rate limit, 5xx, network/timeout). Client-side errors (bad request, payload too large) do **not** trigger failover, since another model would fail identically.
- Without `routing.models`, behavior is unchanged. `--model` pins a single model and bypasses the pool.


## Telemetry

Expand Down
28 changes: 28 additions & 0 deletions README.ru-RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ OCR разрешает правила ревью по цепочке приор
| `providers.<name>.extra_body` | object | JSON-объект, добавляемый в каждое тело запроса |
| `providers.<name>.extra_headers` | string | HTTP-заголовки `key=value` через запятую |
| `custom_providers.<name>.*` | — | Те же поля, что и `providers.<name>.*`, включая необязательное `models` |
| `routing.models` | array | Упорядоченный пул моделей для отказоустойчивости: `[{provider, model}]` (см. [Отказоустойчивость с несколькими моделями](#отказоустойчивость-с-несколькими-моделями)) |
| `routing.policy` | string | Политика выбора; `priority` (по умолчанию, единственное значение на сегодня) |
| `llm.url` | string | `https://api.openai.com/v1/chat/completions` |
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.auth_header` | string | Только для Anthropic: `x-api-key` \| `authorization` |
Expand All @@ -690,6 +692,32 @@ OCR разрешает правила ревью по цепочке приор
| `OCR_LLM_MODEL` | Имя модели |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic, `false` = OpenAI |

### Отказоустойчивость с несколькими моделями

По умолчанию ревью использует одну модель (`provider` + `model`). Чтобы пережить ограничения частоты и сбои провайдеров, настройте упорядоченный пул `routing.models` — ревьюер пробует каждую по порядку и переключается на следующую, когда одна из них ограничена по частоте, возвращает серверную ошибку или истекает по тайм-ауту:

```json
{
"providers": {
"anthropic": { "api_key": "sk-ant-...", "model": "claude-opus-4-6" },
"deepseek": { "api_key": "sk-...", "model": "deepseek-v3" }
},
"routing": {
"models": [
{ "provider": "anthropic", "model": "claude-opus-4-6" },
{ "provider": "deepseek", "model": "deepseek-v3" }
],
"policy": "priority"
}
}
```

- Каждая запись ссылается на настроенного провайдера (для учётных данных / эндпоинта) и модель; пропущенное `model` использует модель провайдера по умолчанию.
- `routing.policy` определяет порядок пула. Сегодня поддерживается только `priority` (первая запись — основная); поле зарезервировано для будущих политик (например, weighted), и неизвестное значение отклоняется, а не игнорируется молча.
- Ограниченная по частоте или недоступная модель ненадолго откладывается, чтобы параллельные пофайловые ревью пропускали её, а не обращались к ней повторно.
- Переключение срабатывает при ошибках доступности (ограничение частоты, 5xx, сеть / тайм-аут). Ошибки на стороне клиента (неверный запрос, слишком большой payload) не вызывают переключение, поскольку другая модель завершится с той же ошибкой.
- Без `routing.models` поведение не меняется. `--model` фиксирует одну модель и обходит пул.


## Телеметрия

Expand Down
28 changes: 28 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,8 @@ OCR 通过四层优先级链解析评审规则。每层采用首次匹配原则
| `providers.<name>.extra_body` | object | 合并到每个请求体的 JSON 对象 |
| `providers.<name>.extra_headers` | string | 逗号分隔的 `key=value` HTTP 头 |
| `custom_providers.<name>.*` | — | 与 `providers.<name>.*` 相同的字段,包括可选的 `models` |
| `routing.models` | array | 用于故障转移的有序模型池:`[{provider, model}]`(见[多模型故障转移](#多模型故障转移)) |
| `routing.policy` | string | 选择策略;`priority`(默认,目前唯一取值) |
| `llm.url` | string | `https://api.openai.com/v1/chat/completions` |
| `llm.auth_token` | string | `sk-xxxxxxx` |
| `llm.auth_header` | string | 仅 Anthropic:`x-api-key` \| `authorization` |
Expand All @@ -678,6 +680,32 @@ OCR 通过四层优先级链解析评审规则。每层采用首次匹配原则
| `OCR_LLM_MODEL` | 模型名称 |
| `OCR_USE_ANTHROPIC` | `true` = Anthropic,`false` = OpenAI |

### 多模型故障转移

默认评审使用单一模型(`provider` + `model`)。为应对限流与供应商故障,可配置有序的 `routing.models` 池——评审按顺序尝试,当某个模型被限流、返回服务端错误或超时时,自动转移到下一个:

```json
{
"providers": {
"anthropic": { "api_key": "sk-ant-...", "model": "claude-opus-4-6" },
"deepseek": { "api_key": "sk-...", "model": "deepseek-v3" }
},
"routing": {
"models": [
{ "provider": "anthropic", "model": "claude-opus-4-6" },
{ "provider": "deepseek", "model": "deepseek-v3" }
],
"policy": "priority"
}
}
```

- 每个条目引用一个已配置的供应商(提供凭据 / 端点)及一个模型;省略 `model` 时使用该供应商的默认模型。
- `routing.policy` 决定池的排序方式。目前仅支持 `priority`(第一个为主模型);该字段为未来策略(如 weighted)预留,填入未知值会报错而非被静默忽略。
- 被限流或不可用的模型会被短暂搁置,使并发的逐文件评审跳过它,而非各自重复命中。
- 仅在可用性错误(限流、5xx、网络 / 超时)时转移。客户端错误(请求错误、负载过大)**不**触发转移,因为换个模型同样会失败。
- 不配置 `routing.models` 时行为不变。`--model` 固定单一模型并绕过该池。


## 遥测

Expand Down
6 changes: 3 additions & 3 deletions cmd/opencodereview/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,14 @@ func loadLLMRuntime(tpl *template.Template, toolConfigPath, modelOverride string
}
tpl.ApplyLanguage(lang)

ep, err := llm.ResolveEndpointWithModelOverride(cfgPath, modelOverride)
eps, err := llm.ResolveModelsWithModelOverride(cfgPath, modelOverride)
if err != nil {
return nil, fmt.Errorf("resolve LLM endpoint: %w", err)
}

return &llmRuntime{
Client: llm.NewLLMClient(ep),
Model: ep.Model,
Client: llm.NewLLMRouter(eps),
Model: eps[0].Model,
PlanToolDefs: planToolDefs,
MainToolDefs: mainToolDefs,
Collector: tool.NewCommentCollector(),
Expand Down
Loading