vet

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

commit c7997e040702fecf8ac7717d1d554304ab27ee69
parent a42bb58fad83ed8d2c6e1337656bad1fb74b1767
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Sun,  8 Feb 2026 09:21:49 +0000

Update max spend, fix opus long context (#25)

* Default no max spend

* Fix long context window
Diffstat:
Mvet/cli/config/cli_config_schema.py | 2++
Mvet/cli/config/cli_config_test.py | 4++++
Mvet/cli/main.py | 15+++++++++++++++
Mvet/imbue_core/agents/llm_apis/anthropic_api.py | 6++++++
Mvet/imbue_core/agents/primitives/resource_limits.py | 1-
Mvet/imbue_tools/types/vet_config.py | 2+-
Mvet/issue_identifiers/registry.py | 12+++++++++---
7 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/vet/cli/config/cli_config_schema.py b/vet/cli/config/cli_config_schema.py @@ -34,6 +34,7 @@ class CliConfigPreset(BaseModel): temperature: float | None = Field(default=None, ge=0.0, le=2.0) confidence_threshold: float | None = Field(default=None, ge=0.0, le=1.0) max_workers: int | None = Field(default=None, ge=1) + max_spend: float | None = Field(default=None, gt=0.0) output: str | None = None output_format: str | None = None output_fields: list[str] | None = None @@ -57,6 +58,7 @@ class CliDefaults(BaseModel): temperature: float = 0.0 confidence_threshold: float = 0.8 max_workers: int = 2 + max_spend: float | None = None output: str | None = None output_format: str = "text" output_fields: list[str] | None = None diff --git a/vet/cli/config/cli_config_test.py b/vet/cli/config/cli_config_test.py @@ -66,6 +66,7 @@ def test_parse_cli_config_from_dict_handles_all_fields() -> None: "temperature": 0.7, "confidence_threshold": 0.85, "max_workers": 8, + "max_spend": 10.0, "output": "results.json", "output_format": "json", "output_fields": ["file", "line", "message"], @@ -88,6 +89,7 @@ def test_parse_cli_config_from_dict_handles_all_fields() -> None: assert preset.temperature == 0.7 assert preset.confidence_threshold == 0.85 assert preset.max_workers == 8 + assert preset.max_spend == 10.0 assert preset.output == "results.json" assert preset.output_format == "json" assert preset.output_fields == ["file", "line", "message"] @@ -357,6 +359,7 @@ def _create_default_args() -> argparse.Namespace: temperature=CLI_DEFAULTS.temperature, confidence_threshold=CLI_DEFAULTS.confidence_threshold, max_workers=CLI_DEFAULTS.max_workers, + max_spend=CLI_DEFAULTS.max_spend, output_format=CLI_DEFAULTS.output_format, output_fields=CLI_DEFAULTS.output_fields, verbose=CLI_DEFAULTS.verbose, @@ -396,6 +399,7 @@ def test_apply_config_preset_cli_args_take_precedence() -> None: temperature=0.0, confidence_threshold=0.95, max_workers=2, + max_spend=None, output_format="text", output_fields=None, verbose=False, diff --git a/vet/cli/main.py b/vet/cli/main.py @@ -185,6 +185,13 @@ def create_parser() -> argparse.ArgumentParser: metavar="N", help=f"Maximum number of parallel workers for identification (default: {CLI_DEFAULTS.max_workers})", ) + parallel_group.add_argument( + "--max-spend", + type=float, + default=CLI_DEFAULTS.max_spend, + metavar="DOLLARS", + help="Maximum dollars to spend on API calls (default: no limit)", + ) output_group = parser.add_argument_group("output options") output_group.add_argument( @@ -416,6 +423,13 @@ def main(argv: list[str] | None = None) -> int: ) return 2 + if args.max_spend is not None and args.max_spend <= 0: + print( + f"Error: Max spend must be a positive number, got: {args.max_spend}", + file=sys.stderr, + ) + return 2 + configure_logging(args.verbose, args.quiet) conversation_history = None @@ -464,6 +478,7 @@ def main(argv: list[str] | None = None) -> int: 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, ) issues = find_issues( diff --git a/vet/imbue_core/agents/llm_apis/anthropic_api.py b/vet/imbue_core/agents/llm_apis/anthropic_api.py @@ -549,6 +549,7 @@ class AnthropicAPI(LanguageModelAPI): if self.model_name in ( AnthropicModelName.CLAUDE_4_5_SONNET_2025_09_29_LONG, AnthropicModelName.CLAUDE_4_SONNET_2025_05_14_LONG, + AnthropicModelName.CLAUDE_4_6_OPUS_LONG, ): # FIXME: Fix this once this is no longer beta or as this becomes required for more models # Map the name back to the actual model name for the API call @@ -556,6 +557,8 @@ class AnthropicAPI(LanguageModelAPI): model_name = AnthropicModelName.CLAUDE_4_5_SONNET_2025_09_29 elif self.model_name == AnthropicModelName.CLAUDE_4_SONNET_2025_05_14_LONG: model_name = AnthropicModelName.CLAUDE_4_SONNET_2025_05_14 + elif self.model_name == AnthropicModelName.CLAUDE_4_6_OPUS_LONG: + model_name = AnthropicModelName.CLAUDE_4_6_OPUS else: assert False, "unreachable" api_result = await client.beta.messages.create( @@ -631,6 +634,7 @@ class AnthropicAPI(LanguageModelAPI): if self.model_name in ( AnthropicModelName.CLAUDE_4_5_SONNET_2025_09_29_LONG, AnthropicModelName.CLAUDE_4_SONNET_2025_05_14_LONG, + AnthropicModelName.CLAUDE_4_6_OPUS_LONG, ): # FIXME: Fix this once this is no longer beta or as this becomes required for more models # Map the name back to the actual model name for the API call @@ -638,6 +642,8 @@ class AnthropicAPI(LanguageModelAPI): model_name = AnthropicModelName.CLAUDE_4_5_SONNET_2025_09_29 elif self.model_name == AnthropicModelName.CLAUDE_4_SONNET_2025_05_14_LONG: model_name = AnthropicModelName.CLAUDE_4_SONNET_2025_05_14 + elif self.model_name == AnthropicModelName.CLAUDE_4_6_OPUS_LONG: + model_name = AnthropicModelName.CLAUDE_4_6_OPUS else: assert False, "unreachable" stream_fn = lambda **kwargs: client.beta.messages.stream(**kwargs, betas=["context-1m-2025-08-07"]) diff --git a/vet/imbue_core/agents/primitives/resource_limits.py b/vet/imbue_core/agents/primitives/resource_limits.py @@ -107,7 +107,6 @@ class ResourceLimits: ) -> "ResourceLimits": if max_dollars is None and "DEFAULT_MAX_HAMMER_DOLLARS" in os.environ: max_dollars = _float_or_none(os.getenv("DEFAULT_MAX_HAMMER_DOLLARS")) - assert max_dollars != float("inf"), "max_dollars must be finite" if max_dollars is None: max_dollars = 0.0 assert max_dollars >= 0, "max_dollars must be non-negative" diff --git a/vet/imbue_tools/types/vet_config.py b/vet/imbue_tools/types/vet_config.py @@ -27,7 +27,7 @@ class VetConfig(SerializableModel): language_model_generation_config: LanguageModelGenerationConfig = LanguageModelGenerationConfig( model_name=AnthropicModelName.CLAUDE_4_6_OPUS ) - max_identifier_spend_dollars: float = 5.0 + max_identifier_spend_dollars: float | None = None max_output_tokens: int = 20000 enable_parallel_agentic_issue_identification: bool = False max_identify_workers: int | None = None diff --git a/vet/issue_identifiers/registry.py b/vet/issue_identifiers/registry.py @@ -11,7 +11,9 @@ from typing import TypeVar from loguru import logger -from vet.imbue_core.agents.primitives.resource_limits import ensure_global_resource_limits +from vet.imbue_core.agents.primitives.resource_limits import ( + ensure_global_resource_limits, +) from vet.imbue_core.data_types import IssueCode from vet.imbue_core.data_types import IssueIdentificationDebugInfo from vet.imbue_core.data_types import IssueIdentificationLLMResponseMetadata @@ -157,7 +159,7 @@ def _generate_with_name_in_debug_info( def _combine_issue_generator_debug_info( generator: Generator[GeneratedIssueSchema, None, tuple[tuple[str, IssueIdentificationDebugInfo], ...]], ) -> Generator[GeneratedIssueSchema, None, IssueIdentificationDebugInfo]: - collected_debug_info: tuple[tuple[str, IssueIdentificationDebugInfo], ...] = (yield from generator) + collected_debug_info: tuple[tuple[str, IssueIdentificationDebugInfo], ...] = yield from generator updated_llm_responses = [] for identifier_name, debug_info in collected_debug_info: @@ -179,7 +181,11 @@ def run( """ enabled_issue_codes = get_enabled_issue_codes(config) identifiers = _build_identifiers(_get_enabled_identifier_names(config), enabled_issue_codes) - ensure_global_resource_limits(max_dollars=config.max_identifier_spend_dollars) + ensure_global_resource_limits( + max_dollars=( + config.max_identifier_spend_dollars if config.max_identifier_spend_dollars is not None else float("inf") + ) + ) issue_generators: list[Generator[GeneratedIssueSchema, None, tuple[str, IssueIdentificationDebugInfo]]] = [] compatible_enabled_identifier_names: list[str] = []