147 lines
3.9 KiB
Python
147 lines
3.9 KiB
Python
|
import argparse
|
||
|
from dataclasses import dataclass
|
||
|
from typing import List, Optional
|
||
|
from pathlib import Path
|
||
|
import sys
|
||
|
|
||
|
|
||
|
@dataclass(frozen=True)
|
||
|
class OllamaConfig:
|
||
|
"""Configuration for Ollama client."""
|
||
|
|
||
|
model: str
|
||
|
server_url: str
|
||
|
timeout: int
|
||
|
max_retries: int
|
||
|
|
||
|
|
||
|
@dataclass(frozen=True)
|
||
|
class ReviewConfig:
|
||
|
"""Complete configuration for ReviewLlama."""
|
||
|
|
||
|
paths: List[Path]
|
||
|
ollama: OllamaConfig
|
||
|
|
||
|
|
||
|
def normalize_server_url(url: str) -> str:
|
||
|
"""Normalize Ollama server URL to ensure proper format."""
|
||
|
if not url.startswith(("http://", "https://")):
|
||
|
return f"http://{url}"
|
||
|
return url.rstrip("/")
|
||
|
|
||
|
|
||
|
def create_ollama_config(
|
||
|
model: str, server_url: str, timeout: int, max_retries: int
|
||
|
) -> OllamaConfig:
|
||
|
"""Create OllamaConfig with validated parameters."""
|
||
|
return OllamaConfig(
|
||
|
model=model,
|
||
|
server_url=normalize_server_url(server_url),
|
||
|
timeout=timeout,
|
||
|
max_retries=max_retries,
|
||
|
)
|
||
|
|
||
|
|
||
|
def create_review_config(
|
||
|
paths: List[Path], ollama_config: OllamaConfig
|
||
|
) -> ReviewConfig:
|
||
|
"""Create complete ReviewConfig from validated components."""
|
||
|
return ReviewConfig(paths=paths, ollama=ollama_config)
|
||
|
|
||
|
|
||
|
def create_argument_parser() -> argparse.ArgumentParser:
|
||
|
"""Create and configure the argument parser."""
|
||
|
parser = argparse.ArgumentParser(
|
||
|
prog="reviewllama",
|
||
|
description="AI-powered code review assistant",
|
||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
|
epilog="""
|
||
|
Examples:
|
||
|
reviewllama . --model gemma3:27b --server localhost:11434
|
||
|
reviewllama src/ tests/ --model llama3.2:7b --timeout 60
|
||
|
""",
|
||
|
)
|
||
|
|
||
|
parser.add_argument(
|
||
|
"paths",
|
||
|
nargs="+",
|
||
|
metavar="PATH",
|
||
|
help="One or more file paths or git directories to review",
|
||
|
)
|
||
|
|
||
|
parser.add_argument(
|
||
|
"--model",
|
||
|
default="llama3.2:3b",
|
||
|
help="Ollama model to use for code review (default: %(default)s)",
|
||
|
)
|
||
|
|
||
|
parser.add_argument(
|
||
|
"--server",
|
||
|
dest="server_url",
|
||
|
default="localhost:11434",
|
||
|
help="Ollama server URL (default: %(default)s)",
|
||
|
)
|
||
|
|
||
|
parser.add_argument(
|
||
|
"--timeout",
|
||
|
type=int,
|
||
|
default=30,
|
||
|
help="Request timeout in seconds (default: %(default)s)",
|
||
|
)
|
||
|
|
||
|
parser.add_argument(
|
||
|
"--max-retries",
|
||
|
dest="max_retries",
|
||
|
type=int,
|
||
|
default=3,
|
||
|
help="Maximum number of retry attempts (default: %(default)s)",
|
||
|
)
|
||
|
|
||
|
return parser
|
||
|
|
||
|
|
||
|
def parse_raw_arguments(args: Optional[List[str]] = None) -> argparse.Namespace:
|
||
|
"""Parse command line arguments into raw namespace."""
|
||
|
parser = create_argument_parser()
|
||
|
return parser.parse_args(args)
|
||
|
|
||
|
|
||
|
def transform_namespace_to_config(namespace: argparse.Namespace) -> ReviewConfig:
|
||
|
"""Transform argparse namespace into ReviewConfig."""
|
||
|
paths = [Path(path_str) for path_str in namespace.paths]
|
||
|
|
||
|
ollama_config = create_ollama_config(
|
||
|
model=namespace.model,
|
||
|
server_url=namespace.server_url,
|
||
|
timeout=namespace.timeout,
|
||
|
max_retries=namespace.max_retries,
|
||
|
)
|
||
|
|
||
|
return create_review_config(paths=paths, ollama_config=ollama_config)
|
||
|
|
||
|
|
||
|
def parse_arguments(args: Optional[List[str]] = None) -> ReviewConfig:
|
||
|
"""Parse command line arguments and return validated configuration."""
|
||
|
raw_namespace = parse_raw_arguments(args)
|
||
|
return transform_namespace_to_config(raw_namespace)
|
||
|
|
||
|
|
||
|
def cli() -> None:
|
||
|
"""Main entry point for the CLI."""
|
||
|
try:
|
||
|
config = parse_arguments()
|
||
|
# TODO: Pass config to review engine
|
||
|
print(f"Reviewing {len(config.paths)} path(s) with model {config.ollama.model}")
|
||
|
for path in config.paths:
|
||
|
print(f" - {path}")
|
||
|
except SystemExit:
|
||
|
# argparse calls sys.exit on error, let it propagate
|
||
|
raise
|
||
|
except Exception as e:
|
||
|
print(f"Error: {e}", file=sys.stderr)
|
||
|
sys.exit(1)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|