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:
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] = []