vet

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

commit fb868ac903592f78303d4872f3728732f7b89bb1
parent 7976386d63388c49e8375cd0103795bacf1def68
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Thu, 19 Feb 2026 00:27:15 +0000

Make non-llm commands run faster (#103)

* Defer heavy imports to reduce CLI startup time

Move imports that transitively pull in LLM SDKs from module level
to function scope in main.py and loader.py. Early-exit commands
(--version, --list-issue-codes, etc.) drop from ~2.3s to ~0.17s.

* Added time based test to ensure slowness doesn't creep into the basic startup commands (~2x leway for upper bound assurances).

---------

Co-authored-by: Andrew Laack <andrew@laack.co>
Diffstat:
Mvet/cli/config/loader.py | 10++++++----
Mvet/cli/main.py | 37++++++++++++++++++++++---------------
Avet/cli/startup_time_test.py | 23+++++++++++++++++++++++
3 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/vet/cli/config/loader.py b/vet/cli/config/loader.py @@ -11,9 +11,6 @@ from vet.cli.config.cli_config_schema import merge_presets from vet.cli.config.cli_config_schema import parse_cli_config_from_dict from vet.cli.config.schema import ModelsConfig from vet.cli.config.schema import ProviderConfig -from vet.imbue_core.agents.configs import LanguageModelGenerationConfig -from vet.imbue_core.agents.configs import OpenAICompatibleModelConfig -from vet.imbue_core.agents.llm_apis.common import get_model_max_output_tokens from vet.imbue_core.data_types import CustomGuideConfig from vet.imbue_core.data_types import CustomGuidesConfig from vet.imbue_core.data_types import get_valid_issue_code_values @@ -140,12 +137,17 @@ def get_max_output_tokens_for_model(model_id: str, config: ModelsConfig) -> int return provider.models[model_id].max_output_tokens try: + from vet.imbue_core.agents.llm_apis.common import get_model_max_output_tokens + return get_model_max_output_tokens(model_id) except Exception: return None -def build_language_model_config(model_id: str, user_config: ModelsConfig) -> LanguageModelGenerationConfig: +def build_language_model_config(model_id: str, user_config: ModelsConfig): + from vet.imbue_core.agents.configs import LanguageModelGenerationConfig + from vet.imbue_core.agents.configs import OpenAICompatibleModelConfig + provider = get_provider_for_model(model_id, user_config) if provider is None: return LanguageModelGenerationConfig(model_name=model_id) diff --git a/vet/cli/main.py b/vet/cli/main.py @@ -11,35 +11,22 @@ from pathlib import Path from loguru import logger -from vet.api import find_issues from vet.cli.config.cli_config_schema import CLI_DEFAULTS from vet.cli.config.cli_config_schema import CliConfigPreset from vet.cli.config.loader import ConfigLoadError -from vet.cli.config.loader import build_language_model_config from vet.cli.config.loader import get_cli_config_file_paths from vet.cli.config.loader import get_config_preset -from vet.cli.config.loader import get_max_output_tokens_for_model from vet.cli.config.loader import load_cli_config from vet.cli.config.loader import load_custom_guides_config from vet.cli.config.loader import load_models_config from vet.cli.config.loader import validate_api_key_for_model from vet.cli.config.schema import ModelsConfig -from vet.cli.models import DEFAULT_MODEL_ID -from vet.cli.models import get_models_by_provider -from vet.cli.models import validate_model_id from vet.formatters import OUTPUT_FIELDS from vet.formatters import OUTPUT_FORMATS -from vet.formatters import format_github_review -from vet.formatters import format_issue_text -from vet.formatters import issue_to_dict from vet.formatters import validate_output_fields -from vet.imbue_core.agents.llm_apis.errors import BadAPIRequestError -from vet.imbue_core.agents.llm_apis.errors import PromptTooLongError from vet.imbue_core.data_types import AgentHarnessType from vet.imbue_core.data_types import IssueCode from vet.imbue_core.data_types import get_valid_issue_code_values -from vet.imbue_tools.get_conversation_history.get_conversation_history import parse_conversation_history -from vet.imbue_tools.types.vet_config import VetConfig VERSION = version("verify-everything") @@ -157,7 +144,8 @@ def create_parser() -> argparse.ArgumentParser: type=str, default=CLI_DEFAULTS.model, metavar="MODEL", - help=f"LLM to use for analysis (default: {DEFAULT_MODEL_ID}). ", + # Hardcoded to avoid importing cli.models at module level (~1s of SDK imports). + help="LLM to use for analysis (default: claude-opus-4-6). ", ) model_group.add_argument( "--list-models", @@ -270,6 +258,9 @@ def list_issue_codes() -> None: def list_models(user_config: ModelsConfig | None = None) -> None: + from vet.cli.models import DEFAULT_MODEL_ID + from vet.cli.models import get_models_by_provider + print("Available models:") print() models_by_provider = get_models_by_provider(user_config) @@ -323,6 +314,8 @@ def configure_logging(verbose: bool, quiet: bool) -> None: def load_conversation_from_command(command: str, cwd: Path) -> tuple: + from vet.imbue_tools.get_conversation_history.get_conversation_history import parse_conversation_history + logger.info("Running history loader command: {}", command) result = subprocess.run(command, shell=True, capture_output=True, text=True, cwd=cwd) if result.returncode != 0: @@ -367,7 +360,9 @@ _CONTEXT_OVERFLOW_PATTERNS = [ ] -def _is_context_overflow(e: PromptTooLongError | BadAPIRequestError) -> bool: +def _is_context_overflow(e) -> bool: + from vet.imbue_core.agents.llm_apis.errors import PromptTooLongError + if isinstance(e, PromptTooLongError): return True error_msg = e.error_message.lower() @@ -471,6 +466,18 @@ def main(argv: list[str] | None = None) -> int: configure_logging(args.verbose, args.quiet) + from vet.api import find_issues + from vet.cli.config.loader import build_language_model_config + from vet.cli.config.loader import get_max_output_tokens_for_model + from vet.cli.models import DEFAULT_MODEL_ID + from vet.cli.models import validate_model_id + from vet.formatters import format_github_review + from vet.formatters import format_issue_text + from vet.formatters import issue_to_dict + from vet.imbue_core.agents.llm_apis.errors import BadAPIRequestError + from vet.imbue_core.agents.llm_apis.errors import PromptTooLongError + from vet.imbue_tools.types.vet_config import VetConfig + conversation_history = None if args.history_loader is not None: conversation_history = load_conversation_from_command(args.history_loader, repo_path) diff --git a/vet/cli/startup_time_test.py b/vet/cli/startup_time_test.py @@ -0,0 +1,23 @@ +import statistics +import subprocess +import time + +import pytest + +MAX_STARTUP_SECONDS = 0.4 +RUNS_PER_COMMAND = 3 + + +@pytest.mark.parametrize("flag", ["--version", "--list-issue-codes", "--list-fields", "--list-configs"]) +def test_cli_startup_time(flag): + times = [] + for _ in range(RUNS_PER_COMMAND): + t0 = time.perf_counter() + result = subprocess.run(["uv", "run", "vet", flag], capture_output=True, text=True) + times.append(time.perf_counter() - t0) + + median = statistics.median(times) + assert result.returncode == 0, f"vet {flag} failed: {result.stderr}" + assert ( + median < MAX_STARTUP_SECONDS + ), f"vet {flag} too slow: median {median:.3f}s > {MAX_STARTUP_SECONDS}s (times: {[f'{t:.3f}s' for t in times]})"