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:
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]})"