Initial upload: Local AI Commit Reviewer CLI with CI/CD workflow
This commit is contained in:
164
src/config/config.py
Normal file
164
src/config/config.py
Normal file
@@ -0,0 +1,164 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class LLMConfig(BaseModel):
|
||||
endpoint: str = "http://localhost:11434"
|
||||
model: str = "codellama"
|
||||
timeout: int = 120
|
||||
max_tokens: int = 2048
|
||||
temperature: float = 0.3
|
||||
|
||||
|
||||
class ReviewSettings(BaseModel):
|
||||
strictness: str = "balanced"
|
||||
max_issues_per_file: int = 20
|
||||
syntax_highlighting: bool = True
|
||||
show_line_numbers: bool = True
|
||||
|
||||
|
||||
class LanguageConfig(BaseModel):
|
||||
enabled: bool = True
|
||||
review_rules: list[str] = Field(default_factory=list)
|
||||
max_line_length: int = 100
|
||||
|
||||
|
||||
class Languages(BaseModel):
|
||||
python: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["pep8", "type-hints", "docstrings"]))
|
||||
javascript: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["airbnb"]))
|
||||
typescript: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["airbnb"]))
|
||||
go: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["golint", "staticcheck"]))
|
||||
rust: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["clippy"]))
|
||||
java: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["google-java"]))
|
||||
c: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["cppcheck"]))
|
||||
cpp: LanguageConfig = Field(default_factory=lambda: LanguageConfig(review_rules=["cppcheck"]))
|
||||
|
||||
def get_language_config(self, language: str) -> LanguageConfig | None:
|
||||
return getattr(self, language.lower(), None)
|
||||
|
||||
|
||||
class StrictnessProfile(BaseModel):
|
||||
description: str = ""
|
||||
check_security: bool = True
|
||||
check_bugs: bool = True
|
||||
check_style: bool = True
|
||||
check_performance: bool = False
|
||||
check_documentation: bool = False
|
||||
min_severity: str = "info"
|
||||
|
||||
|
||||
class StrictnessProfiles(BaseModel):
|
||||
permissive: StrictnessProfile = Field(default_factory=lambda: StrictnessProfile(
|
||||
description="Focus on critical issues only",
|
||||
check_security=True,
|
||||
check_bugs=True,
|
||||
check_style=False,
|
||||
check_performance=False,
|
||||
check_documentation=False,
|
||||
min_severity="warning"
|
||||
))
|
||||
balanced: StrictnessProfile = Field(default_factory=lambda: StrictnessProfile(
|
||||
description="Balanced review of common issues",
|
||||
check_security=True,
|
||||
check_bugs=True,
|
||||
check_style=True,
|
||||
check_performance=False,
|
||||
check_documentation=False,
|
||||
min_severity="info"
|
||||
))
|
||||
strict: StrictnessProfile = Field(default_factory=lambda: StrictnessProfile(
|
||||
description="Comprehensive review of all issues",
|
||||
check_security=True,
|
||||
check_bugs=True,
|
||||
check_style=True,
|
||||
check_performance=True,
|
||||
check_documentation=True,
|
||||
min_severity="info"
|
||||
))
|
||||
|
||||
def get_profile(self, name: str) -> StrictnessProfile:
|
||||
return getattr(self, name.lower(), self.balanced)
|
||||
|
||||
|
||||
class HooksConfig(BaseModel):
|
||||
enabled: bool = True
|
||||
fail_on_critical: bool = True
|
||||
allow_bypass: bool = True
|
||||
|
||||
|
||||
class OutputConfig(BaseModel):
|
||||
format: str = "terminal"
|
||||
theme: str = "auto"
|
||||
show_suggestions: bool = True
|
||||
|
||||
|
||||
class LoggingConfig(BaseModel):
|
||||
level: str = "info"
|
||||
log_file: str = ""
|
||||
structured: bool = False
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
llm: LLMConfig = Field(default_factory=LLMConfig)
|
||||
review: ReviewSettings = Field(default_factory=ReviewSettings)
|
||||
languages: Languages = Field(default_factory=Languages)
|
||||
strictness_profiles: StrictnessProfiles = Field(default_factory=StrictnessProfiles)
|
||||
hooks: HooksConfig = Field(default_factory=HooksConfig)
|
||||
output: OutputConfig = Field(default_factory=OutputConfig)
|
||||
logging: LoggingConfig = Field(default_factory=LoggingConfig)
|
||||
|
||||
|
||||
class ConfigLoader:
|
||||
def __init__(self, config_path: str | None = None):
|
||||
self.config_path = config_path
|
||||
self.global_config: Path | None = None
|
||||
self.project_config: Path | None = None
|
||||
|
||||
def find_config_files(self) -> tuple[Path | None, Path | None]:
|
||||
env_config_path = os.environ.get("AICR_CONFIG_PATH")
|
||||
|
||||
if env_config_path:
|
||||
env_path = Path(env_config_path)
|
||||
if env_path.exists():
|
||||
return env_path, None
|
||||
|
||||
self.global_config = Path.home() / ".aicr.yaml"
|
||||
self.project_config = Path.cwd() / ".aicr.yaml"
|
||||
|
||||
if self.project_config.exists():
|
||||
return self.project_config, self.global_config
|
||||
|
||||
if self.global_config.exists():
|
||||
return self.global_config, None
|
||||
|
||||
return None, None
|
||||
|
||||
def load(self) -> Config:
|
||||
config_path, global_path = self.find_config_files()
|
||||
|
||||
config_data: dict[str, Any] = {}
|
||||
|
||||
if global_path and global_path.exists():
|
||||
with open(global_path) as f:
|
||||
global_data = yaml.safe_load(f) or {}
|
||||
config_data.update(global_data)
|
||||
|
||||
if config_path and config_path.exists():
|
||||
with open(config_path) as f:
|
||||
project_data = yaml.safe_load(f) or {}
|
||||
config_data.update(project_data)
|
||||
|
||||
return Config(**config_data)
|
||||
|
||||
def save(self, config: Config, path: Path) -> None:
|
||||
with open(path, "w") as f:
|
||||
yaml.dump(config.model_dump(), f, default_flow_style=False)
|
||||
|
||||
|
||||
def get_config(config_path: str | None = None) -> Config:
|
||||
loader = ConfigLoader(config_path)
|
||||
return loader.load()
|
||||
Reference in New Issue
Block a user