smartrag / config.py
ShaunGves's picture
Initial commit: SmartRAG - Production AI Assistant for Programmers
1c58cca
Raw
History Blame Contribute Delete
11.2 kB
"""
config.py β€” Centralized configuration for SmartRAG.
All hyperparameters, paths, and model choices live here.
"""
from dataclasses import dataclass, field
from pathlib import Path
# ─── Project Root ────────────────────────────────────────────────
ROOT = Path(__file__).parent
# ─── Model Configuration ─────────────────────────────────────────
@dataclass
class ModelConfig:
# Base model to fine-tune (swap to any HuggingFace model ID)
base_model_id: str = "microsoft/phi-2"
# Where to save the fine-tuned adapter
output_dir: str = str(ROOT / "artifacts" / "finetuned_model")
# Embedding model for RAG retrieval
embedding_model_id: str = "BAAI/bge-base-en-v1.5"
# Max token lengths
max_seq_length: int = 2048
max_new_tokens: int = 512
# ─── QLoRA Configuration ─────────────────────────────────────────
@dataclass
class LoRAConfig:
r: int = 16 # LoRA rank (higher = more params)
lora_alpha: int = 32 # Scaling factor
target_modules: list = field( # Which layers to apply LoRA to
default_factory=lambda: [
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
]
)
lora_dropout: float = 0.05
bias: str = "none"
task_type: str = "CAUSAL_LM"
# ─── Training Configuration ──────────────────────────────────────
@dataclass
class TrainingConfig:
num_train_epochs: int = 3
per_device_train_batch_size: int = 2
gradient_accumulation_steps: int = 4 # Effective batch = 8
learning_rate: float = 2e-4
warmup_ratio: float = 0.03
lr_scheduler_type: str = "cosine"
fp16: bool = True
logging_steps: int = 10
save_steps: int = 100
eval_steps: int = 100
load_best_model_at_end: bool = True
report_to: str = "mlflow" # Experiment tracking
# ─── RAG Configuration ───────────────────────────────────────────
@dataclass
class RAGConfig:
# ChromaDB persistence path
chroma_persist_dir: str = str(ROOT / "artifacts" / "chroma_db")
collection_name: str = "smartrag_docs"
# Retrieval settings
top_k: int = 4 # Number of chunks to retrieve
chunk_size: int = 512 # Characters per chunk
chunk_overlap: int = 64 # Overlap between chunks
# Similarity threshold (0.0–1.0)
similarity_threshold: float = 0.3
# ─── Data Configuration ──────────────────────────────────────────
@dataclass
class DataConfig:
# HuggingFace dataset for fine-tuning
# Using medical QA as domain example β€” swap for any domain
dataset_name: str = "medalpaca/medical_meadow_wikidoc"
dataset_split: str = "train"
# Local paths
raw_data_dir: str = str(ROOT / "artifacts" / "raw_data")
processed_data_dir: str = str(ROOT / "artifacts" / "processed_data")
# Train/val split ratio
val_size: float = 0.1
seed: int = 42
# ─── Evaluation Configuration ────────────────────────────────────
@dataclass
class EvalConfig:
mlflow_experiment_name: str = "smartrag-evaluation"
results_dir: str = str(ROOT / "artifacts" / "eval_results")
# RAGAS metrics to compute
metrics: list = field(
default_factory=lambda: [
"faithfulness",
"answer_relevancy",
"context_precision",
"context_recall",
]
)
# ─── Use Case Configuration ──────────────────────────────────────
@dataclass
class UseCaseConfig:
"""
SmartRAG is focused on: AI Assistant for Programmers.
Helps developers query codebases, docs, Stack Overflow Q&A,
debug errors, and understand APIs β€” all grounded in real sources.
"""
name: str = "AI Assistant for Programmers"
domain: str = "software_engineering"
# Fine-tuning dataset β€” code-focused instruction pairs
finetune_dataset: str = "iamtarun/python_code_instructions_18k_alpaca"
# Documents to index (paths or URLs)
default_doc_sources: list = field(default_factory=lambda: [
"https://docs.python.org/3/",
"https://fastapi.tiangolo.com/",
])
# System prompt for this domain
system_prompt: str = (
"You are an expert programming assistant. "
"Answer questions about code, APIs, debugging, and software architecture "
"using ONLY the provided context. Show code examples where helpful. "
"If unsure, say so β€” never hallucinate function names or APIs."
)
# ─── Hybrid Search Configuration ─────────────────────────────────
@dataclass
class HybridSearchConfig:
"""BM25 (keyword) + Dense (embedding) hybrid retrieval."""
enabled: bool = True
# Weight blending: final_score = Ξ±*dense + (1-Ξ±)*bm25
alpha: float = 0.7 # 0.0 = pure BM25, 1.0 = pure dense
# BM25 parameters
bm25_k1: float = 1.5 # Term frequency saturation
bm25_b: float = 0.75 # Document length normalization
# How many candidates each retriever fetches before merging
dense_candidates: int = 20
bm25_candidates: int = 20
# Final top-k after blending
top_k_after_blend: int = 10
# ─── Reranker Configuration ──────────────────────────────────────
@dataclass
class RerankerConfig:
"""Cross-encoder reranking on top of hybrid retrieval."""
enabled: bool = True
# Cross-encoder model (much more accurate than bi-encoder for ranking)
model_id: str = "cross-encoder/ms-marco-MiniLM-L-6-v2"
# Take top-k from hybrid search β†’ rerank β†’ return top_k_final
top_k_final: int = 4
# Score threshold: discard chunks below this reranker score
score_threshold: float = -5.0
# Batch size for cross-encoder inference
batch_size: int = 16
# ─── Embedding Cache Configuration ───────────────────────────────
@dataclass
class CacheConfig:
"""
Embedding cache to avoid re-computing vectors for repeated queries.
Cuts latency by 80–95% on cache hits.
"""
enabled: bool = True
backend: str = "memory" # "memory" | "redis" | "disk"
# Redis settings (only used if backend="redis")
redis_host: str = "localhost"
redis_port: int = 6379
redis_db: int = 0
redis_ttl_seconds: int = 3600 # 1 hour TTL
# In-memory LRU cache size (number of embeddings)
max_memory_entries: int = 10_000
# Disk cache path (only used if backend="disk")
disk_cache_dir: str = str(ROOT / "artifacts" / "embedding_cache")
# ─── Rate Limiter Configuration ──────────────────────────────────
@dataclass
class RateLimitConfig:
"""Per-IP API rate limiting to prevent abuse and manage GPU cost."""
enabled: bool = True
# Requests per window
requests_per_minute: int = 20
requests_per_hour: int = 200
requests_per_day: int = 1000
# Burst allowance (allows short bursts above per-minute limit)
burst_multiplier: float = 1.5
# Storage backend for rate limit counters
backend: str = "memory" # "memory" | "redis"
# ─── Agent Configuration ─────────────────────────────────────────
@dataclass
class AgentConfig:
"""
Multi-step reasoning agent with tool calling.
Agent decides WHEN to retrieve, WHEN to search the web,
WHEN to execute code β€” producing richer answers than single-pass RAG.
"""
enabled: bool = True
max_iterations: int = 5 # Max reasoning steps before forcing an answer
max_tokens_per_step: int = 256
# Tools available to the agent
tools: list = field(default_factory=lambda: [
"vector_search", # Search the ChromaDB vector store
"hybrid_search", # BM25 + dense hybrid search
"web_search", # Real-time web search fallback
"code_executor", # Safe Python code execution (sandbox)
"calculator", # Math evaluation
])
# Temperature for agent reasoning steps (lower = more deterministic)
temperature: float = 0.05
# ─── System Design Configuration ─────────────────────────────────
@dataclass
class SystemConfig:
"""
Production system design parameters.
These control latency, throughput, and cost.
"""
# Latency targets (milliseconds)
target_p50_latency_ms: int = 500
target_p95_latency_ms: int = 2000
target_p99_latency_ms: int = 5000
# Concurrency
api_workers: int = 1 # Increase for multi-GPU setups
max_concurrent_requests: int = 10
# Vector DB tuning
chroma_hnsw_ef: int = 100 # Higher = better recall, slower
chroma_hnsw_m: int = 16 # Connections per node (16–64)
chroma_batch_size: int = 512 # Ingestion batch size
# Embedding optimization
embedding_batch_size: int = 32 # Batch queries for GPU efficiency
embedding_normalize: bool = True # L2 normalize for cosine similarity
# API gateway settings
request_timeout_seconds: int = 60
max_payload_size_mb: int = 10
# ─── Global Config Object ─────────────────────────────────────────
class Config:
model = ModelConfig()
lora = LoRAConfig()
training = TrainingConfig()
rag = RAGConfig()
data = DataConfig()
eval = EvalConfig()
usecase = UseCaseConfig()
hybrid = HybridSearchConfig()
reranker = RerankerConfig()
cache = CacheConfig()
ratelimit = RateLimitConfig()
agent = AgentConfig()
system = SystemConfig()
@staticmethod
def ensure_dirs():
"""Create all artifact directories if they don't exist."""
dirs = [
Path(Config.model.output_dir),
Path(Config.rag.chroma_persist_dir),
Path(Config.data.raw_data_dir),
Path(Config.data.processed_data_dir),
Path(Config.eval.results_dir),
Path(Config.cache.disk_cache_dir),
]
for d in dirs:
d.mkdir(parents=True, exist_ok=True)
cfg = Config()