vet

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
Mvet/cli/main.py | 133++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mvet/imbue_tools/types/vet_config.py | 3+++
Mvet/issue_identifiers/agentic_issue_collation.py | 2+-
Mvet/issue_identifiers/common.py | 36+++++++-----------------------------
Mvet/issue_identifiers/harnesses/agentic.py | 2+-
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, )