commit b6eac85c107c5e23cd051626900c4b70fb46ca82
parent 27acbd08842dc4307bbf483f9263fd082fce7d4a
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date: Wed, 25 Feb 2026 19:57:18 -0600
make list-models respect current run configuration (#144)
* Don't version cast
* Better listing
* undo
* Updated listing logic
* Updated messaging
---------
Co-authored-by: Andrew Laack <andrew@laack.co>
Co-authored-by: merge <merge@local>
Diffstat:
5 files changed, 106 insertions(+), 70 deletions(-)
diff --git a/vet/cli/main.py b/vet/cli/main.py
@@ -265,12 +265,40 @@ def list_issue_codes() -> None:
print(f" {code}")
-def list_models(user_config: ModelsConfig | None = None) -> None:
+_HARNESS_ISSUE_URLS: dict[AgentHarnessType, str] = {
+ AgentHarnessType.CLAUDE: "https://github.com/anthropics/claude-code/issues",
+ AgentHarnessType.CODEX: "https://github.com/openai/codex/issues",
+}
+
+
+def list_models(
+ user_config: ModelsConfig | None = None,
+ *,
+ agentic: bool = False,
+ agent_harness: AgentHarnessType | None = None,
+) -> None:
from vet.cli.models import DEFAULT_MODEL_ID
from vet.cli.models import get_models_by_provider
- print("Available models:")
- print()
+ if agentic and agent_harness is not None:
+ harness_name = agent_harness.value
+ print(f"Model listing for agentic mode ({harness_name} harness):")
+ print()
+ print(f" In agentic mode, --model is passed directly to the agent harness CLI.")
+ print(f" vet does not know which models the current harness supports. Some")
+ print(f" models listed below may not work, and the harness may accept models")
+ print(f" not listed here. If --model is omitted, the harness uses its own default.")
+ print()
+ issue_url = _HARNESS_ISSUE_URLS.get(agent_harness)
+ if issue_url:
+ print(f" If better model listing support would be useful, consider requesting")
+ print(f" a model listing feature from the {harness_name} CLI maintainers:")
+ print(f" {issue_url}")
+ print()
+ else:
+ print("Available models:")
+ print()
+
models_by_provider = get_models_by_provider(user_config)
for provider, model_ids in sorted(models_by_provider.items()):
print(f" {provider}:")
@@ -416,7 +444,11 @@ def main(argv: list[str] | None = None) -> int:
return 0
if args.list_models:
- list_models(user_config)
+ list_models(
+ user_config,
+ agentic=args.agentic,
+ agent_harness=args.agent_harness if args.agentic else None,
+ )
return 0
if args.list_fields:
@@ -519,44 +551,67 @@ def main(argv: list[str] | None = None) -> int:
print(f"vet: {e}", file=sys.stderr)
return 2
- model_id = args.model or DEFAULT_MODEL_ID
-
- try:
- model_id = validate_model_id(model_id, user_config)
- except ValueError as e:
- print(f"vet: {e}", file=sys.stderr)
- return 2
-
- try:
- validate_api_key_for_model(model_id, user_config)
- except Exception as e:
- print(f"vet: {e}", file=sys.stderr)
- return 2
-
- # TODO: Support OFFLINE, UPDATE_SNAPSHOT, and MOCKED modes.
- language_model_config = build_language_model_config(model_id, user_config)
- max_output_tokens = get_max_output_tokens_for_model(model_id, user_config)
-
enabled_identifiers = ("agentic_issue_identifier",) if args.agentic else None
disabled_identifiers = None if args.agentic else ("agentic_issue_identifier",)
+ enabled_issue_codes = tuple(args.enabled_issue_codes) if args.enabled_issue_codes else None
+ disabled_issue_codes = tuple(args.disabled_issue_codes) if args.disabled_issue_codes else None
+
+ if args.agentic:
+ # In agentic mode the model string is passed directly to the external CLI
+ # (e.g. Claude Code, Codex). We skip vet's own model validation because
+ # the CLI is the authority on which models it supports. When the user
+ # doesn't specify --model, we pass None so the CLI uses its own default.
+ config = VetConfig(
+ enabled_identifiers=enabled_identifiers,
+ disabled_identifiers=disabled_identifiers,
+ agent_model_name=args.model,
+ enabled_issue_codes=enabled_issue_codes,
+ disabled_issue_codes=disabled_issue_codes,
+ temperature=args.temperature,
+ filter_issues_below_confidence=args.confidence_threshold,
+ max_identify_workers=args.max_workers,
+ max_identifier_spend_dollars=args.max_spend,
+ custom_guides_config=custom_guides_config,
+ agent_harness_type=args.agent_harness,
+ filter_issues_through_llm_evaluator=False,
+ enable_deduplication=False,
+ )
+ else:
+ model_id = args.model or DEFAULT_MODEL_ID
- config = VetConfig(
- enabled_identifiers=enabled_identifiers,
- disabled_identifiers=disabled_identifiers,
- language_model_generation_config=language_model_config,
- enabled_issue_codes=(tuple(args.enabled_issue_codes) if args.enabled_issue_codes else None),
- disabled_issue_codes=(tuple(args.disabled_issue_codes) if args.disabled_issue_codes else None),
- temperature=args.temperature,
- filter_issues_below_confidence=args.confidence_threshold,
- max_identify_workers=args.max_workers,
- max_output_tokens=max_output_tokens or 20000,
- max_identifier_spend_dollars=args.max_spend,
- custom_guides_config=custom_guides_config,
- agent_harness_type=args.agent_harness,
- # TODO: Evaluate if routing filtration/dedup through the agent harness is worth the tradeoff.
- filter_issues_through_llm_evaluator=not args.agentic,
- enable_deduplication=not args.agentic,
- )
+ try:
+ model_id = validate_model_id(model_id, user_config)
+ except ValueError as e:
+ print(f"vet: {e}", file=sys.stderr)
+ return 2
+
+ try:
+ validate_api_key_for_model(model_id, user_config)
+ except Exception as e:
+ print(f"vet: {e}", file=sys.stderr)
+ return 2
+
+ # TODO: Support OFFLINE, UPDATE_SNAPSHOT, and MOCKED modes.
+ language_model_config = build_language_model_config(model_id, user_config)
+ max_output_tokens = get_max_output_tokens_for_model(model_id, user_config)
+
+ config = VetConfig(
+ enabled_identifiers=enabled_identifiers,
+ disabled_identifiers=disabled_identifiers,
+ language_model_generation_config=language_model_config,
+ enabled_issue_codes=enabled_issue_codes,
+ disabled_issue_codes=disabled_issue_codes,
+ temperature=args.temperature,
+ filter_issues_below_confidence=args.confidence_threshold,
+ max_identify_workers=args.max_workers,
+ max_output_tokens=max_output_tokens or 20000,
+ max_identifier_spend_dollars=args.max_spend,
+ custom_guides_config=custom_guides_config,
+ agent_harness_type=args.agent_harness,
+ # TODO: Evaluate if routing filtration/dedup through the agent harness is worth the tradeoff.
+ filter_issues_through_llm_evaluator=True,
+ enable_deduplication=True,
+ )
if not args.quiet:
print(
diff --git a/vet/imbue_tools/types/vet_config.py b/vet/imbue_tools/types/vet_config.py
@@ -37,6 +37,9 @@ class VetConfig(SerializableModel):
max_output_tokens: int = 20000
enable_parallel_agentic_issue_identification: bool = False
agent_harness_type: AgentHarnessType = AgentHarnessType.CLAUDE
+ # Raw model name passed to the external agent CLI in agentic mode.
+ # When None the CLI uses its own configured default.
+ agent_model_name: str | None = None
max_identify_workers: int | None = None
temperature: float = 0.5
diff --git a/vet/issue_identifiers/agentic_issue_collation.py b/vet/issue_identifiers/agentic_issue_collation.py
@@ -147,7 +147,7 @@ def collate_issues_with_agent(
options = get_agent_options(
cwd=project_context.repo_path,
- model_name=config.language_model_generation_config.model_name,
+ model_name=config.agent_model_name,
agent_harness_type=config.agent_harness_type,
)
combined_issues_string = _convert_parsed_issues_to_combined_string(all_issues)
diff --git a/vet/issue_identifiers/common.py b/vet/issue_identifiers/common.py
@@ -21,10 +21,8 @@ from vet.imbue_core.agents.agent_api.data_types import AgentResultMessage
from vet.imbue_core.agents.agent_api.data_types import AgentTextBlock
from vet.imbue_core.agents.agent_api.data_types import AgentToolName
from vet.imbue_core.agents.agent_api.data_types import READ_ONLY_TOOLS
-from vet.imbue_core.agents.llm_apis.anthropic_api import AnthropicModelName
from vet.imbue_core.agents.llm_apis.anthropic_data_types import AnthropicCachingInfo
from vet.imbue_core.agents.llm_apis.data_types import CostedLanguageModelResponse
-from vet.imbue_core.agents.llm_apis.openai_api import OpenAIModelName
from vet.imbue_core.async_monkey_patches import log_exception
from vet.imbue_core.data_types import AgentHarnessType
from vet.imbue_core.data_types import ConfidenceScore
@@ -194,42 +192,22 @@ def convert_to_issue_identifier_result(
return generator_with_capture.return_value
-_ANTHROPIC_MODEL_NAMES = {m.value for m in AnthropicModelName}
-_OPENAI_MODEL_NAMES = {m.value for m in OpenAIModelName}
-_DEFAULT_CODEX_MODEL = "gpt-5.2-codex"
-_DEFAULT_CLAUDE_MODEL = AnthropicModelName.CLAUDE_4_6_OPUS
+def get_agent_options(cwd: Path | None, model_name: str | None, agent_harness_type: AgentHarnessType) -> AgentOptions:
+ """Build agent options for the given harness type.
-
-def get_agent_options(cwd: Path | None, model_name: str, agent_harness_type: AgentHarnessType) -> AgentOptions:
+ *model_name* is passed through to the underlying CLI as-is. When ``None``,
+ the CLI will use whatever default the user has configured. We intentionally
+ do **not** validate or remap the model here — the external CLI is the
+ authority on which models it supports.
+ """
# NOTE: This if/else is intentionally simple. We're unlikely to support many harness types,
# but if we do, this should be refactored into a registry or factory pattern.
if agent_harness_type == AgentHarnessType.CODEX:
- if model_name in _ANTHROPIC_MODEL_NAMES:
- logger.debug(
- "Config model_name {config_model_name} is an Anthropic model, using default Codex model ({model_name}).",
- config_model_name=model_name,
- model_name=_DEFAULT_CODEX_MODEL,
- )
- model_name = _DEFAULT_CODEX_MODEL
return CodexOptions(
cwd=cwd,
model=model_name,
sandbox_mode="read-only",
)
- if model_name in _OPENAI_MODEL_NAMES:
- logger.debug(
- "Config model_name {config_model_name} is an OpenAI model, using default Claude model ({model_name}).",
- config_model_name=model_name,
- model_name=_DEFAULT_CLAUDE_MODEL,
- )
- model_name = _DEFAULT_CLAUDE_MODEL
- elif model_name not in _ANTHROPIC_MODEL_NAMES:
- logger.warning(
- "Config model_name {config_model_name} is not a valid Anthropic model, using default ({model_name}).",
- config_model_name=model_name,
- model_name=_DEFAULT_CLAUDE_MODEL,
- )
- model_name = _DEFAULT_CLAUDE_MODEL
return ClaudeCodeOptions(
cwd=cwd,
permission_mode="dontAsk",
diff --git a/vet/issue_identifiers/harnesses/agentic.py b/vet/issue_identifiers/harnesses/agentic.py
@@ -246,7 +246,7 @@ class _AgenticIssueIdentifier(IssueIdentifier[CommitInputs]):
options = get_agent_options(
cwd=project_context.repo_path,
- model_name=config.language_model_generation_config.model_name,
+ model_name=config.agent_model_name,
agent_harness_type=config.agent_harness_type,
)