fix: resolve CI/CD issues with proper package structure and imports
This commit is contained in:
133
src/local_api_docs_search/utils/config.py
Normal file
133
src/local_api_docs_search/utils/config.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""Configuration management for the application."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
import yaml
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
class Config:
|
||||
"""Configuration management class supporting env vars and YAML config."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config_path: Optional[Path] = None,
|
||||
env_path: Optional[Path] = None,
|
||||
):
|
||||
self._config: dict[str, Any] = {}
|
||||
self._config_path = config_path or Path.cwd() / "config.yaml"
|
||||
self._load_env(env_path)
|
||||
self._load_config()
|
||||
|
||||
def _load_env(self, env_path: Optional[Path] = None) -> None:
|
||||
"""Load environment variables from .env file."""
|
||||
env_file = env_path or Path.cwd() / ".env"
|
||||
if env_file.exists():
|
||||
load_dotenv(env_file)
|
||||
|
||||
def _load_config(self) -> None:
|
||||
"""Load configuration from YAML file."""
|
||||
if self._config_path.exists():
|
||||
with open(self._config_path, "r") as f:
|
||||
self._config = yaml.safe_load(f) or {}
|
||||
else:
|
||||
self._config = {}
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
"""Get configuration value with environment variable override."""
|
||||
env_key = f"API_DOCS_{key.upper()}"
|
||||
env_value = os.environ.get(env_key)
|
||||
|
||||
if env_value is not None:
|
||||
return self._cast_env_value(env_value)
|
||||
|
||||
return self._config.get(key, default)
|
||||
|
||||
def _cast_env_value(self, value: str) -> Any:
|
||||
"""Cast environment variable string to appropriate type."""
|
||||
if value.lower() in ("true", "false"):
|
||||
return value.lower() == "true"
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
return value
|
||||
|
||||
@property
|
||||
def index_path(self) -> Path:
|
||||
"""Get the documentation index path."""
|
||||
return Path(self.get("index_path", "./docs"))
|
||||
|
||||
@property
|
||||
def model_name(self) -> str:
|
||||
"""Get the embedding model name."""
|
||||
return self.get("model_name", "all-MiniLM-L6-v2")
|
||||
|
||||
@property
|
||||
def embedding_device(self) -> str:
|
||||
"""Get the embedding device."""
|
||||
return self.get("embedding_device", "cpu")
|
||||
|
||||
@property
|
||||
def chroma_persist_dir(self) -> Path:
|
||||
"""Get the ChromaDB persistence directory."""
|
||||
return Path(self.get("chroma_persist_dir", ".api-docs/chroma"))
|
||||
|
||||
@property
|
||||
def default_limit(self) -> int:
|
||||
"""Get the default search result limit."""
|
||||
return int(self.get("default_limit", 10))
|
||||
|
||||
@property
|
||||
def verbose(self) -> bool:
|
||||
"""Get verbose mode setting."""
|
||||
return self.get("verbose", False)
|
||||
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""Set a configuration value."""
|
||||
self._config[key] = value
|
||||
|
||||
def save(self) -> None:
|
||||
"""Save configuration to YAML file."""
|
||||
with open(self._config_path, "w") as f:
|
||||
yaml.dump(self._config, f, default_flow_style=False)
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset configuration to defaults."""
|
||||
self._config = {}
|
||||
if self._config_path.exists():
|
||||
self._config_path.unlink()
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Return configuration as dictionary."""
|
||||
return {
|
||||
"index_path": str(self.index_path),
|
||||
"model_name": self.model_name,
|
||||
"embedding_device": self.embedding_device,
|
||||
"chroma_persist_dir": str(self.chroma_persist_dir),
|
||||
"default_limit": self.default_limit,
|
||||
"verbose": self.verbose,
|
||||
}
|
||||
|
||||
|
||||
_config: Optional[Config] = None
|
||||
|
||||
|
||||
def get_config(config_path: Optional[Path] = None) -> Config:
|
||||
"""Get or create the global configuration instance."""
|
||||
global _config
|
||||
if _config is None:
|
||||
_config = Config(config_path)
|
||||
return _config
|
||||
|
||||
|
||||
def reset_config() -> None:
|
||||
"""Reset the global configuration instance."""
|
||||
global _config
|
||||
_config = None
|
||||
Reference in New Issue
Block a user