Skip to content

feat:add --model flag to override LLM model per review#122

Merged
lizhengfeng101 merged 4 commits into
alibaba:mainfrom
1parado:add-custom-model-flag
Jun 16, 2026
Merged

feat:add --model flag to override LLM model per review#122
lizhengfeng101 merged 4 commits into
alibaba:mainfrom
1parado:add-custom-model-flag

Conversation

@1parado

@1parado 1parado commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a --model flag to ocr review, allowing users to select or override the configured LLM model for a single review run without changing ~/.opencodereview/config.json.

This builds on the current provider-based configuration flow by adding support for optional model lists on provider entries, including custom providers. Users can declare which models a custom provider supports, switch models interactively via ocr config model, or override the model per review run.

Examples:

ocr review --model claude-opus-4-6
ocr review --commit abc123 --model claude-sonnet-4-6

Custom provider model list example:

ocr config set custom_providers.my-gateway.models "llama-3-70b,llama-3-8b"
ocr config model
ocr review --model llama-3-8b

The flag and provider model list configuration are documented in all README language variants.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring (no functional changes)
  • Documentation update
  • CI / Build / Tooling

How Has This Been Tested?

  • Manual testing
  • Unit tests

Tested locally with:

go test ./cmd/opencodereview
go test ./internal/llm
go test ./...

Also manually verified:

  • ocr review --help includes the new --model flag.
  • custom_providers.<name>.models can be configured.
  • ocr config model supports custom providers with model lists.
  • ocr review --model <model> overrides the configured model for a single review run.

Checklist

  • My code follows the project's coding style (gofmt)
  • I have performed a self-review of my code
  • I have added tests that prove my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly
  • I have signed the CLA

Related Issues

Closes #83

Verify

Windows 11

Support switching models via TUI—— ocr config model.

image

Use the true gateway.

image

Test model——ocr llm test.

image image

Test review
image

image

If you use an unsupported model name, an error will be reported.

image image image

Support switching provider via TUI——ocr config provider

image image image image

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 OpenCodeReview found 3 issue(s) in this PR.

  • ✅ 3 posted as inline comment(s)
  • 📝 0 posted as summary

entry := cfg.CustomProviders[result.provider]
entry.Model = result.model
if len(result.models) > 0 {
entry.Models = mergeModelLists([]string{result.model}, result.models)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: This discards any previously configured models for this custom provider. The first argument should be entry.Models (the existing models) rather than []string{result.model}, to match the pattern used in applyOfficialProviderConfig (line 161). As written, every time the user reconfigures a custom provider through the TUI, any models that were previously in entry.Models but not in result.models will be lost.

For example, if a custom provider had models ["a", "b", "c"] and the user selects model "d" with result.models = ["d"], the result would be ["d"] instead of ["a", "b", "c", "d"].

Suggestion:

Suggested change
entry.Models = mergeModelLists([]string{result.model}, result.models)
entry.Models = mergeModelLists(entry.Models, result.models)

Comment on lines +353 to +355
if _, isPreset := llm.LookupProvider(parts[1]); !isPreset {
return setCustomProviderField(cfg, parts[1], parts[2], key, value)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Potential misrouting of provider configuration: When providers.<name>.<field> is used with a name that is NOT a recognized preset (via llm.LookupProvider), the value is silently written to cfg.CustomProviders instead of cfg.Providers. This means:

  1. If a user typos a preset provider name (e.g., providers.anthopic.model), the config goes into CustomProviders without any warning, and the intended preset override is lost.
  2. The providers.* prefix semantically implies preset providers, while custom_providers.* is for custom ones. Mixing them silently can lead to confusing behavior where the user sets providers.foo.model but later finds it under custom_providers in the saved config.

Consider returning an error when the name is not a known preset, guiding users to use custom_providers.<name>.<field> instead. Alternatively, at minimum log a warning.

Comment on lines +298 to +308
if strings.HasPrefix(value, "[") {
var models []string
if err := json.Unmarshal([]byte(value), &models); err != nil {
if !strings.HasSuffix(value, "]") {
return nil, err
}
value = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(value, "["), "]"))
return normalizeModelList(strings.Split(value, ",")), nil
}
return normalizeModelList(models), nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fallback parsing may silently corrupt data: When JSON unmarshalling fails AND the value ends with ], the code falls back to stripping brackets and splitting by comma. This means inputs like [model-a, "model-b"] (partially valid JSON) or [model with spaces] would pass through the fallback and produce unexpected results (e.g., '"model-b"' with literal quotes).

The intent seems to be supporting both JSON arrays (["a","b"]) and simple bracketed comma lists ([a, b]). Consider being more explicit: try JSON first, and only fall back if the content between brackets contains no JSON-specific characters (quotes, nested structures). Or simply require one format and return a clear error for invalid input.

@1parado

1parado commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

config.json template:

{
"provider": "my_llm or my-gateway",
"custom_providers": {
"my-gateway": {
"api_key": "sk-",
"url": "your endpoint/v1",
"protocol": "openai",
"model": "qwen3.7-plus",
"models": [
"qwen3.7-plus",
"deepseek-v4-pro",
"kimi-k2.6",
"glm-5.1"
]
},
"my_llm": {
"api_key": "sk-",
"url": "your endpoint",
"protocol": "anthropic",
"model": "claude-opus-4-7",
"models": [
"claude-opus-4-7",
"claude-sonnet-4-6"
]
}
}
}

@1parado 1parado marked this pull request as ready for review June 14, 2026 13:26
@1parado

1parado commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi @lizhengfeng101,referring to your previous reply, I have added the --model option in the latest version and performed local verification. If you have time, could you please review the code? Please let me know if there are any issues.

@lizhengfeng101

Copy link
Copy Markdown
Collaborator

Hi @1parado, I noticed you have two similar PRs open — this one (#122) and #108. Are we going with this one as the latest version? If so, could you close #108 to avoid confusion? Thanks!

@1parado

1parado commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@lizhengfeng101,I have closed the other PR, thank you.

@1parado

1parado commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Hi @lizhengfeng101,I've closed the other PR, which was based on the new version of the project. If you have some time, could you please help review this one? Feel free to reach out if you have any questions. Looking forward to your reply.

@lizhengfeng101 lizhengfeng101 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.

Nit on parseModelListValue (config_cmd.go): the fallback logic when the value starts with [ but is not valid JSON has a subtle branch — it checks whether the string ends with ] to decide between returning the raw json.Unmarshal error or falling through to comma-split. This means [foo,bar (missing closing bracket) surfaces a raw JSON error, which is not very user-friendly.

Consider always stripping brackets and comma-splitting when JSON parsing fails, which removes one branch and produces consistent behavior:

func parseModelListValue(value string) ([]string, error) {
	value = strings.TrimSpace(value)
	if value == "" {
		return nil, nil
	}

	if strings.HasPrefix(value, "[") {
		var models []string
		if err := json.Unmarshal([]byte(value), &models); err == nil {
			return normalizeModelList(models), nil
		}
		// Not valid JSON — strip brackets and fall through to comma-split.
		value = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(value, "["), "]"))
	}

	return normalizeModelList(strings.Split(value, ",")), nil
}

This is simpler and more forgiving for shell users who might type [a,b without the closing bracket. The existing tests still pass with this change.

@lizhengfeng101 lizhengfeng101 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.

Suggestion: validate --model against the provider's known model list before sending the request

Currently, tryProviderConfig applies the model override unconditionally (line 228–229):

if modelOverride != "" {
    model = modelOverride
}

No validation is performed against preset.Models or entry.Models. If the user passes a typo like --model claude-opsu-4-6, it silently goes through to the API and fails with a cryptic remote error (e.g., HTTP 404).

The expected behavior should be:

  1. Provider-based configuration (preset or custom with a models list): If modelOverride is not empty and the provider has a known model list (preset.Models merged with entry.Models), check whether the override is in that list. If not, return an early, clear error such as:

    model "claude-opsu-4-6" is not available for provider "anthropic";
    available models: claude-opus-4-6, claude-sonnet-4-6, ...
    

    This catches typos and misconfiguration before making any network call.

  2. Manual / legacy configuration (tryLegacyLlmConfig), environment variable strategies (tryOCREnv, tryCCEnv, tryShellRC): These have no model list to validate against, so the override should be passed through as-is — the remote API is the only authority.

This gives users fast, actionable feedback when a known model list is available, while still allowing unconstrained override in contexts where no list exists.

@lizhengfeng101 lizhengfeng101 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.

The main branch has had quite a few updates recently. Could you rebase or merge main into your branch to bring it up to date? This will help avoid potential conflicts and make the final merge smoother.

@1parado

1parado commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

I have made modifications on the latest main branch, ensuring that no conflicts will occur during the merge.

feat: simplify the parseModelListValue function

- Remove conditional branch checking for closing bracket
- Always strip brackets and fall through to comma-split when JSON parsing fails
- Produces more user-friendly behavior for malformed bracket lists like [foo,bar

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
@1parado

1parado commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Hi,@lizhengfeng101. I have made code modifications based on your suggestions. Just simplify the parseModelListValue function.

image

And I am working on your other suggestion. It might take some time. Once I have completed the modifications, I will ask you to review the code.

feat: validate --model flag against provider model list

- Validate model override against preset.Models and entry.Models before sending request
- Return early, clear error with available models when validation fails
- Only validate when a known model list exists (provider-based config)
- Skip validation for legacy llm config and env-based strategies (no model list available)
- Add comprehensive test coverage for validation scenarios

This catches typos and misconfigurations before making network calls, providing fast,
actionable feedback to users.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
@1parado

1parado commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Suggestion: validate --model against the provider's known model list before sending the request

Currently, tryProviderConfig applies the model override unconditionally (line 228–229):

if modelOverride != "" {
    model = modelOverride
}

No validation is performed against preset.Models or entry.Models. If the user passes a typo like --model claude-opsu-4-6, it silently goes through to the API and fails with a cryptic remote error (e.g., HTTP 404).

The expected behavior should be:

  1. Provider-based configuration (preset or custom with a models list): If modelOverride is not empty and the provider has a known model list (preset.Models merged with entry.Models), check whether the override is in that list. If not, return an early, clear error such as:

    model "claude-opsu-4-6" is not available for provider "anthropic";
    available models: claude-opus-4-6, claude-sonnet-4-6, ...
    

    This catches typos and misconfiguration before making any network call.

  2. Manual / legacy configuration (tryLegacyLlmConfig), environment variable strategies (tryOCREnv, tryCCEnv, tryShellRC): These have no model list to validate against, so the override should be passed through as-is — the remote API is the only authority.

This gives users fast, actionable feedback when a known model list is available, while still allowing unconstrained override in contexts where no list exists.

image

Hi,@lizhengfeng101. Thank you for your suggestions, Now, invalid models be caught prior to making the API call, accompanied by a clear error message and the list of available models. If you have any questions, please feel free to contact me.

@lizhengfeng101 lizhengfeng101 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.

LGTM

@lizhengfeng101 lizhengfeng101 merged commit c673c94 into alibaba:main Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add --model flag to override LLM model per review run

2 participants