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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ Optional settings:
| Key | Description |
|-----|-------------|
| `providers.<name>.auth_header` | Auth header: `x-api-key` or `authorization` (default: `authorization`) |
| `providers.<name>.top_p` | Nucleus sampling parameter (0.0–1.0) |
| `providers.<name>.top_k` | Top-K sampling parameter (non-negative integer) |
| `providers.<name>.temperature` | Temperature parameter (0.0–2.0) |
| `providers.<name>.extra_body` | Custom JSON fields merged into the request body |
| `providers.<name>.extra_headers` | Comma-separated `key=value` pairs of custom HTTP headers added to every request |
| `providers.<name>.models` | Model list for interactive selection |
Expand Down Expand Up @@ -440,6 +443,9 @@ See the [`examples/`](./examples/) directory for integration examples:
| `--audience` | — | `human` | `human` (show progress) or `agent` (summary only) |
| `--background` | `-b` | — | Optional requirement/business context for the review; auto-filled from commit message when using `--commit` |
| `--model` | — | — | Select or override the LLM model for this review |
| `--top-p` | — | 0 | Nucleus sampling parameter top_p (0 = use provider config default) |
| `--top-k` | — | 0 | Top-K sampling parameter (0 = use provider config default) |
| `--temperature` | — | 0 | Temperature parameter (0 = use provider config default) |
| `--rule` | — | — | Path to custom JSON review rules |
| `--max-tools` | — | built-in | Max tool call rounds per file; only takes effect when greater than template default |
| `--max-git-procs` | — | built-in | Max concurrent git subprocesses |
Expand All @@ -464,6 +470,9 @@ non-git directories too (it falls back to a filesystem walk that honors `.gitign
| `--format` | `-f` | `text` | Output format: `text` or `json` (JSON includes a `project_summary` field) |
| `--concurrency` | — | `8` | Max concurrent file scans |
| `--rule` | — | — | Path to custom JSON review rules |
| `--top-p` | — | 0 | Nucleus sampling parameter top_p (0 = use provider config default) |
| `--top-k` | — | 0 | Top-K sampling parameter (0 = use provider config default) |
| `--temperature` | — | 0 | Temperature parameter (0 = use provider config default) |
| `--repo` | — | current dir | Repository or directory root to scan |

Before each run, `ocr scan` prints a rough token-cost estimate. Use `--preview` to see the
Expand Down Expand Up @@ -497,6 +506,10 @@ ocr review --commit abc123 --format json --audience agent
ocr review --model claude-opus-4-6
ocr review --commit abc123 --model claude-sonnet-4-6

# Adjust LLM sampling parameters
ocr review --top-p 0.9 --temperature 0.3
ocr scan --top-p 0.95 --top-k 50 --temperature 0.5

# Provide requirement context for more targeted review
ocr review --background "Adding rate limiting to the login API"

Expand Down Expand Up @@ -667,6 +680,9 @@ Config file: `~/.opencodereview/config.json`
| `providers.<name>.model` | string | Model name for the provider |
| `providers.<name>.models` | array | Optional provider model list for interactive selection |
| `providers.<name>.auth_header` | string | `x-api-key` \| `authorization` |
| `providers.<name>.top_p` | float | Nucleus sampling parameter (0.0–1.0) |
| `providers.<name>.top_k` | integer | Top-K sampling parameter (non-negative) |
| `providers.<name>.temperature` | float | Temperature parameter (0.0–2.0) |
| `providers.<name>.extra_body` | object | JSON object merged into every request body |
| `providers.<name>.timeout_sec` | integer | Per-request HTTP timeout in seconds (default: `300`) |
| `providers.<name>.extra_headers` | string | Comma-separated `key=value` HTTP headers |
Expand Down
16 changes: 16 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ ocr config set custom_providers.my-gateway.model gpt-4o
| 键 | 描述 |
|----|------|
| `providers.<name>.auth_header` | 认证头:`x-api-key` 或 `authorization`(默认 `authorization`) |
| `providers.<name>.top_p` | Nucleus 采样参数 (0.0–1.0) |
| `providers.<name>.top_k` | Top-K 采样参数(非负整数) |
| `providers.<name>.temperature` | 温度参数 (0.0–2.0) |
| `providers.<name>.extra_body` | 合并到请求体的自定义 JSON 字段 |
| `providers.<name>.extra_headers` | 逗号分隔的 `key=value` 键值对,为每个请求添加自定义 HTTP 头 |
| `providers.<name>.models` | 用于交互式选择的模型列表 |
Expand Down Expand Up @@ -438,6 +441,9 @@ ocr review \
| `--audience` | — | `human` | `human`(显示进度)或 `agent`(仅输出摘要) |
| `--background` | `-b` | — | 可选的需求/业务背景信息;使用 `--commit` 时如未指定则自动从 commit message 中提取 |
| `--model` | — | — | 为本次审查选择或覆盖 LLM 模型 |
| `--top-p` | — | 0 | Nucleus 采样参数 top_p(0 = 使用供应商配置默认值) |
| `--top-k` | — | 0 | Top-K 采样参数(0 = 使用供应商配置默认值) |
| `--temperature` | — | 0 | 温度参数(0 = 使用供应商配置默认值) |
| `--rule` | — | — | 自定义 JSON 审查规则路径 |
| `--max-tools` | — | 内置默认 | 每个文件的最大工具调用轮次;仅在大于模板默认值时生效 |
| `--max-git-procs` | — | 内置默认 | 最大并发 git 子进程数 |
Expand All @@ -459,6 +465,9 @@ ocr review \
| `--batch` | — | `by-language` | 批处理策略:`none`、`by-language` 或 `by-directory` |
| `--format` | `-f` | `text` | 输出格式:`text` 或 `json`(JSON 包含 `project_summary` 字段) |
| `--concurrency` | — | `8` | 最大并发文件扫描数 |
| `--top-p` | — | 0 | Nucleus 采样参数 top_p(0 = 使用供应商配置默认值) |
| `--top-k` | — | 0 | Top-K 采样参数(0 = 使用供应商配置默认值) |
| `--temperature` | — | 0 | 温度参数(0 = 使用供应商配置默认值) |
| `--rule` | — | — | 自定义 JSON 审查规则路径 |
| `--repo` | — | 当前目录 | 要扫描的仓库或目录根路径 |

Expand Down Expand Up @@ -492,6 +501,10 @@ ocr review --commit abc123 --format json --audience agent
ocr review --model claude-opus-4-6
ocr review --commit abc123 --model claude-sonnet-4-6

# 修改模型采样参数
ocr review --top-p 0.9 --temperature 0.3
ocr scan --top-p 0.95 --top-k 50 --temperature 0.5

# 提供需求背景以获得更有针对性的审查
ocr review --background "为登录 API 添加限流"

Expand Down Expand Up @@ -652,6 +665,9 @@ OCR 通过四层优先级链解析评审规则。每层采用首次匹配原则
| `providers.<name>.model` | string | 供应商模型名称 |
| `providers.<name>.models` | array | 用于交互式选择的可选供应商模型列表 |
| `providers.<name>.auth_header` | string | `x-api-key` \| `authorization` |
| `providers.<name>.top_p` | float | Nucleus 采样参数 (0.0–1.0) |
| `providers.<name>.top_k` | integer | Top-K 采样参数(非负整数) |
| `providers.<name>.temperature` | float | 温度参数 (0.0–2.0) |
| `providers.<name>.extra_body` | object | 合并到每个请求体的 JSON 对象 |
| `providers.<name>.timeout_sec` | integer | 每次请求的 HTTP 超时时间(秒),默认 `300` |
| `providers.<name>.extra_headers` | string | 逗号分隔的 `key=value` HTTP 头 |
Expand Down
34 changes: 32 additions & 2 deletions cmd/opencodereview/config_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ type ProviderEntry struct {
Model string `json:"model,omitempty"`
Models []string `json:"models,omitempty"`
AuthHeader string `json:"auth_header,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
TopK *int `json:"top_k,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
ExtraBody map[string]any `json:"extra_body,omitempty"`
ExtraHeaders map[string]string `json:"extra_headers,omitempty"`
}
Expand Down Expand Up @@ -374,7 +377,7 @@ func setConfigValue(cfg *Config, key, value string) error {
}
cfg.Llm.ExtraBody = m
default:
return fmt.Errorf("unknown config key: %s\nSupported keys: provider, model, providers.<name>.<field>, custom_providers.<name>.<field>, mcp_servers.<name>.<field>, llm.url, llm.auth_token, llm.auth_header, llm.model, llm.use_anthropic, llm.extra_body, llm.extra_headers, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging\nProvider fields: api_key, url, protocol, model, models, auth_header, extra_body, extra_headers\nMCP server fields: command, args, env, tools, setup", key)
return fmt.Errorf("unknown config key: %s\nSupported keys: provider, model, providers.<name>.<field>, custom_providers.<name>.<field>, mcp_servers.<name>.<field>, llm.url, llm.auth_token, llm.auth_header, llm.model, llm.use_anthropic, llm.extra_body, llm.extra_headers, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging\nProvider fields: api_key, url, protocol, model, models, auth_header, top_p, top_k, temperature, extra_body, extra_headers\nMCP server fields: command, args, env, tools, setup", key)
}
return nil
}
Expand Down Expand Up @@ -416,8 +419,35 @@ func applyProviderField(entry *ProviderEntry, field, key, value string) error {
return fmt.Errorf("invalid extra headers for %s: %w", key, err)
}
entry.ExtraHeaders = parsed
case "top_p":
f, err := strconv.ParseFloat(value, 64)
if err != nil {
return fmt.Errorf("invalid float for %s: %w", key, err)
}
if f <= 0 || f > 1 {
return fmt.Errorf("top_p must be between 0 (exclusive) and 1, got %f", f)
}
entry.TopP = &f
case "top_k":
i, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("invalid integer for %s: %w", key, err)
}
if i < 0 {
return fmt.Errorf("top_k must be non-negative, got %d", i)
}
entry.TopK = &i
case "temperature":
f, err := strconv.ParseFloat(value, 64)
if err != nil {
return fmt.Errorf("invalid float for %s: %w", key, err)
}
if f < 0 || f > 2 {
return fmt.Errorf("temperature must be between 0 and 2, got %f", f)
}
entry.Temperature = &f
default:
return fmt.Errorf("unknown provider field %q: supported fields are api_key, url, protocol, model, models, auth_header, extra_body, extra_headers", field)
return fmt.Errorf("unknown provider field %q: supported fields are api_key, url, protocol, model, models, auth_header, top_p, top_k, temperature, extra_body, extra_headers", field)
}
return nil
}
Expand Down
92 changes: 92 additions & 0 deletions cmd/opencodereview/config_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,98 @@ func TestSetConfigValueProviderClearsModel(t *testing.T) {
}
}

func TestSetConfigValueProviderTopP(t *testing.T) {
cfg := &Config{
Provider: "anthropic",
Providers: map[string]ProviderEntry{"anthropic": {}},
}
if err := setConfigValue(cfg, "providers.anthropic.top_p", "0.9"); err != nil {
t.Fatalf("setConfigValue: %v", err)
}
entry := cfg.Providers["anthropic"]
if entry.TopP == nil || *entry.TopP != 0.9 {
t.Errorf("TopP = %v, want 0.9", entry.TopP)
}
}

func TestSetConfigValueProviderTopK(t *testing.T) {
cfg := &Config{
Provider: "anthropic",
Providers: map[string]ProviderEntry{"anthropic": {}},
}
if err := setConfigValue(cfg, "providers.anthropic.top_k", "50"); err != nil {
t.Fatalf("setConfigValue: %v", err)
}
entry := cfg.Providers["anthropic"]
if entry.TopK == nil || *entry.TopK != 50 {
t.Errorf("TopK = %v, want 50", entry.TopK)
}
}

func TestSetConfigValueProviderTemperature(t *testing.T) {
cfg := &Config{
Provider: "anthropic",
Providers: map[string]ProviderEntry{"anthropic": {}},
}
if err := setConfigValue(cfg, "providers.anthropic.temperature", "0.3"); err != nil {
t.Fatalf("setConfigValue: %v", err)
}
entry := cfg.Providers["anthropic"]
if entry.Temperature == nil || *entry.Temperature != 0.3 {
t.Errorf("Temperature = %v, want 0.3", entry.Temperature)
}
}

func TestSetConfigValueProviderTopPOutOfRange(t *testing.T) {
cfg := &Config{
Provider: "anthropic",
Providers: map[string]ProviderEntry{"anthropic": {}},
}
if err := setConfigValue(cfg, "providers.anthropic.top_p", "1.5"); err == nil {
t.Fatal("expected error for top_p > 1")
}
if err := setConfigValue(cfg, "providers.anthropic.top_p", "0"); err == nil {
t.Fatal("expected error for top_p == 0 (must be > 0)")
}
if err := setConfigValue(cfg, "providers.anthropic.top_p", "-0.1"); err == nil {
t.Fatal("expected error for negative top_p")
}
}

func TestSetConfigValueProviderTopKNegative(t *testing.T) {
cfg := &Config{
Provider: "anthropic",
Providers: map[string]ProviderEntry{"anthropic": {}},
}
if err := setConfigValue(cfg, "providers.anthropic.top_k", "-1"); err == nil {
t.Fatal("expected error for negative top_k")
}
}

func TestSetConfigValueProviderTemperatureOutOfRange(t *testing.T) {
cfg := &Config{
Provider: "anthropic",
Providers: map[string]ProviderEntry{"anthropic": {}},
}
if err := setConfigValue(cfg, "providers.anthropic.temperature", "3.0"); err == nil {
t.Fatal("expected error for temperature > 2")
}
}

func TestSetConfigValueCustomProviderTopP(t *testing.T) {
cfg := &Config{
Provider: "my-gateway",
CustomProviders: map[string]ProviderEntry{"my-gateway": {}},
}
if err := setConfigValue(cfg, "custom_providers.my-gateway.top_p", "0.8"); err != nil {
t.Fatalf("setConfigValue: %v", err)
}
entry := cfg.CustomProviders["my-gateway"]
if entry.TopP == nil || *entry.TopP != 0.8 {
t.Errorf("TopP = %v, want 0.8", entry.TopP)
}
}

func TestRunConfigUnset_InvalidKey(t *testing.T) {
if err := runConfigUnset("provider"); err == nil {
t.Fatal("expected error for non custom_providers key")
Expand Down
27 changes: 25 additions & 2 deletions cmd/opencodereview/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func (a *ocrFlagSet) IntVar(p *int, name string, value int, usage string) {
a.fs.IntVar(p, name, value, usage)
}

func (a *ocrFlagSet) Float64Var(p *float64, name string, value float64, usage string) {
a.fs.Float64Var(p, name, value, usage)
}

func (a *ocrFlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
a.fs.DurationVar(p, name, value, usage)
}
Expand Down Expand Up @@ -106,6 +110,9 @@ type reviewOptions struct {
audience string // --audience: "human" (default) or "agent"
background string // --background: optional requirement context
model string // --model: override resolved LLM model for this review
topP float64
topK int
temperature float64
concurrency int
perFileTimeout int
maxTools int
Expand All @@ -132,6 +139,9 @@ func parseReviewFlags(args []string) (reviewOptions, error) {
a.StringVar(&opts.audience, "audience", "human", "output audience: human (show progress) or agent (summary only)")
a.StringVarP(&opts.background, "background", "b", "", "optional requirement/business context for the review")
a.StringVar(&opts.model, "model", "", "override LLM model for this review (e.g., claude-opus-4-6)")
a.Float64Var(&opts.topP, "top-p", -1, "nucleus sampling parameter top_p (-1 = use provider default)")
a.IntVar(&opts.topK, "top-k", -1, "top-k sampling parameter (-1 = use provider default)")
a.Float64Var(&opts.temperature, "temperature", -1, "temperature parameter (-1 = use provider default)")
a.IntVar(&opts.maxTools, "max-tools", 0, "max tool call rounds per file (0 = template default; min 10)")
a.IntVar(&opts.maxGitProcs, "max-git-procs", 16, "max concurrent git subprocesses")
a.BoolVarP(&opts.preview, "preview", "p", false, "preview which files will be reviewed without running the LLM")
Expand Down Expand Up @@ -182,6 +192,16 @@ func parseReviewFlags(args []string) (reviewOptions, error) {
return opts, fmt.Errorf("--max-git-procs must be a non-negative integer (0 means use default 16)")
}

if opts.topP > 1 {
return opts, fmt.Errorf("--top-p must be <= 1, got %f", opts.topP)
}
if opts.temperature > 0 && opts.temperature > 2 {
return opts, fmt.Errorf("--temperature must be <= 2, got %f", opts.temperature)
}
if opts.topK > 0 && opts.topK > 100 {
fmt.Fprintf(os.Stderr, "[ocr] --top-k %d is unusually large, expected <= 100\n", opts.topK)
}

return opts, nil
}

Expand Down Expand Up @@ -229,7 +249,10 @@ Flags:
--rule string path to JSON file with system review rules
--timeout int concurrent task timeout in minutes (default 10)
--to string target ref to end diff at (e.g., 'feature-branch')
--tools string path to JSON tools config file (default: embedded)`)
--tools string path to JSON tools config file (default: embedded)
--top-p float nucleus sampling parameter top_p (0 = use provider default)
--top-k int top-k sampling parameter (0 = use provider default)
--temperature float temperature parameter (0 = use provider default)`)
}

// --- config subcommand ---
Expand Down Expand Up @@ -320,6 +343,6 @@ Examples:
ocr config set telemetry.enabled true

Supported keys: provider, model, providers.<name>.<field>, custom_providers.<name>.<field>, mcp_servers.<name>.<field>, llm.url, llm.auth_token, llm.auth_header, llm.model, llm.use_anthropic, llm.extra_body, llm.extra_headers, language, telemetry.enabled, telemetry.exporter, telemetry.otlp_endpoint, telemetry.content_logging
Provider fields: api_key, url, protocol, model, models, auth_header, extra_body, extra_headers
Provider fields: api_key, url, protocol, model, models, auth_header, top_p, top_k, temperature, extra_body, extra_headers
MCP server fields: command, args, env, tools, setup`)
}
Loading
Loading