"""Configuration management for local-deepwiki."""
import os
from pathlib import Path
from typing import Any, Literal
import yaml
from pydantic import BaseModel, Field
class LocalEmbeddingConfig(BaseModel):
"""Configuration for local embedding model."""
model: str = Field(default="all-MiniLM-L6-v2", description="Model name for sentence-transformers")
class OpenAIEmbeddingConfig(BaseModel):
"""Configuration for OpenAI embedding model."""
model: str = Field(default="text-embedding-3-small", description="OpenAI embedding model")
class EmbeddingConfig(BaseModel):
"""Embedding provider configuration."""
provider: Literal["local", "openai"] = Field(default="local", description="Embedding provider")
local: LocalEmbeddingConfig = Field(default_factory=LocalEmbeddingConfig)
openai: OpenAIEmbeddingConfig = Field(default_factory=OpenAIEmbeddingConfig)
class OllamaConfig(BaseModel):
"""Configuration for Ollama LLM."""
model: str = Field(default="qwen3-coder:30b", description="Ollama model name")
base_url: str = Field(default="http://localhost:11434", description="Ollama API URL")
class AnthropicConfig(BaseModel):
"""Configuration for Anthropic LLM."""
model: str = Field(default="claude-sonnet-4-20250514", description="Anthropic model name")
class OpenAILLMConfig(BaseModel):
"""Configuration for OpenAI LLM."""
model: str = Field(default="gpt-4o", description="OpenAI model name")
class LLMConfig(BaseModel):
"""LLM provider configuration."""
provider: Literal["ollama", "anthropic", "openai"] = Field(
default="ollama", description="LLM provider"
)
ollama: OllamaConfig = Field(default_factory=OllamaConfig)
anthropic: AnthropicConfig = Field(default_factory=AnthropicConfig)
openai: OpenAILLMConfig = Field(default_factory=OpenAILLMConfig)
class ParsingConfig(BaseModel):
"""Code parsing configuration."""
languages: list[str] = Field(
default=["python", "typescript", "javascript", "go", "rust", "java", "c", "cpp", "swift"],
description="Languages to parse"
)
max_file_size: int = Field(default=1048576, description="Max file size in bytes (1MB)")
exclude_patterns: list[str] = Field(
default=[
"node_modules/**",
"venv/**",
".venv/**",
"__pycache__/**",
".git/**",
"*.min.js",
"*.min.css",
"dist/**",
"build/**",
".next/**",
"target/**",
"vendor/**",
],
description="Glob patterns to exclude"
)
class ChunkingConfig(BaseModel):
"""Chunking configuration."""
max_chunk_tokens: int = Field(default=512, description="Max tokens per chunk")
overlap_tokens: int = Field(default=50, description="Overlap between chunks")
class OutputConfig(BaseModel):
"""Output configuration."""
wiki_dir: str = Field(default=".deepwiki", description="Wiki output directory name")
vector_db_name: str = Field(default="vectors.lance", description="Vector DB filename")
class Config(BaseModel):
"""Main configuration."""
embedding: EmbeddingConfig = Field(default_factory=EmbeddingConfig)
llm: LLMConfig = Field(default_factory=LLMConfig)
parsing: ParsingConfig = Field(default_factory=ParsingConfig)
chunking: ChunkingConfig = Field(default_factory=ChunkingConfig)
output: OutputConfig = Field(default_factory=OutputConfig)
@classmethod
def load(cls, config_path: Path | None = None) -> "Config":
"""Load configuration from file or defaults."""
if config_path and config_path.exists():
with open(config_path) as f:
data = yaml.safe_load(f)
return cls.model_validate(data)
# Check default locations
default_paths = [
Path.home() / ".config" / "local-deepwiki" / "config.yaml",
Path.home() / ".local-deepwiki.yaml",
]
for path in default_paths:
if path.exists():
with open(path) as f:
data = yaml.safe_load(f)
return cls.model_validate(data)
return cls()
def get_wiki_path(self, repo_path: Path) -> Path:
"""Get the wiki output path for a repository."""
return repo_path / self.output.wiki_dir
def get_vector_db_path(self, repo_path: Path) -> Path:
"""Get the vector database path for a repository."""
return self.get_wiki_path(repo_path) / self.output.vector_db_name
# Global config instance
_config: Config | None = None
def get_config() -> Config:
"""Get the global configuration instance."""
global _config
if _config is None:
_config = Config.load()
return _config
def set_config(config: Config) -> None:
"""Set the global configuration instance."""
global _config
_config = config