Spaces:
Sleeping
Sleeping
File size: 6,568 Bytes
454f118 923d3a8 454f118 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | """
config/settings.py
==================
Central configuration for the entire RAG pipeline.
All tunable hyperparameters live here — no magic numbers scattered in code.
Uses pydantic-settings to load from .env file automatically.
IMPORTANT: This file sets USE_TF=0 / USE_TORCH=1 at import time to prevent
the broken TensorFlow installation on this machine from crashing sentence_transformers.
Always import config.settings BEFORE importing sentence_transformers or transformers.
"""
import os
# Must be set BEFORE importing transformers / sentence_transformers
os.environ.setdefault("USE_TF", "0")
os.environ.setdefault("USE_TORCH", "1")
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field
from pathlib import Path
class Settings(BaseSettings):
"""
Production RAG Configuration.
Values are loaded from .env file (override defaults by editing .env).
"""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore",
)
# ------------------------------------------------------------------ #
# Paths
# ------------------------------------------------------------------ #
raw_pdfs_path: Path = Field(default=Path("./raw_pdfs"))
qdrant_path: Path = Field(default=Path("./data/qdrant_store"))
bm25_index_path: Path = Field(default=Path("./data/bm25_index"))
processed_docs_path: Path = Field(default=Path("./data/processed"))
# ------------------------------------------------------------------ #
# Models
# ------------------------------------------------------------------ #
groq_api_key: str = Field(default="")
groq_model: str = Field(default="openai/gpt-oss-120b")
embedding_model: str = Field(default="BAAI/bge-large-en-v1.5")
embedding_dim: int = Field(default=1024) # bge-large output dimension (NOT 768 — that's bge-base)
reranker_model: str = Field(default="BAAI/bge-reranker-base")
device: str = Field(default="auto") # auto | cuda | cpu
# ------------------------------------------------------------------ #
# Qdrant / Vector Store
# ------------------------------------------------------------------ #
qdrant_url: str = Field(default="")
qdrant_api_key: str = Field(default="")
qdrant_collection: str = Field(default="financial_rag")
# HNSW index params
hnsw_m: int = Field(default=16) # connections per node
hnsw_ef_construct: int = Field(default=200) # build accuracy
# ------------------------------------------------------------------ #
# Chunking Hyperparameters
# ------------------------------------------------------------------ #
# Child chunks (used for retrieval / indexing)
child_chunk_size: int = Field(default=350) # tokens
child_chunk_overlap: int = Field(default=50) # tokens (~14% overlap)
# Parent chunks (returned to LLM as context)
parent_chunk_size: int = Field(default=1800) # tokens
parent_chunk_overlap: int = Field(default=100) # tokens
# Minimum chunk size — discard orphan fragments
min_chunk_tokens: int = Field(default=80)
# Font size threshold to detect headings in PyMuPDF
heading_font_size_threshold: float = Field(default=12.0)
# ------------------------------------------------------------------ #
# Retrieval Hyperparameters
# ------------------------------------------------------------------ #
# How many candidates to retrieve at each stage
dense_top_k: int = Field(default=30) # dense retrieval candidates
sparse_top_k: int = Field(default=30) # BM25 candidates
rrf_k: int = Field(default=60) # RRF constant (standard = 60)
rerank_top_n: int = Field(default=5) # final chunks after reranking
# Query expansion: number of paraphrased queries to generate
num_expanded_queries: int = Field(default=3)
# Score threshold — discard chunks below this similarity
score_threshold: float = Field(default=0.35)
# ------------------------------------------------------------------ #
# LLM Generation
# ------------------------------------------------------------------ #
llm_temperature: float = Field(default=0.1) # low = more factual
llm_max_tokens: int = Field(default=2048)
llm_context_window: int = Field(default=8192)
# ------------------------------------------------------------------ #
# Embedding Batch Processing
# ------------------------------------------------------------------ #
embed_batch_size: int = Field(default=32)
# ------------------------------------------------------------------ #
# Metadata / Fiscal Year
# ------------------------------------------------------------------ #
# BSE India: Financial year = April to March
# Reports filed Apr–Jun 2025 → FY2024-25
default_fiscal_year: str = Field(default="FY2025")
# Known company name → ticker mapping (for metadata tagging)
company_ticker_map: dict = Field(default={
"AIRTEL": "BHARTIARTL",
"AXIS": "AXISBANK",
"BAJAJ": "BAJFINANCE",
"HCL": "HCLTECH",
"HDFC": "HDFCBANK",
"HU": "HINDUNILVR",
"ICICI": "ICICIBANK",
"INFOSYS": "INFY",
"ITC": "ITC",
"KOTAK": "KOTAKBANK",
"KVB": "KARURVYSYA",
"L&T": "LT",
"MARUTI": "MARUTI",
"MRF": "MRF",
"ONGC": "ONGC",
"REL": "RELIANCE",
"SBI": "SBIN",
"TCS": "TCS",
})
# ------------------------------------------------------------------ #
# Logging
# ------------------------------------------------------------------ #
log_level: str = Field(default="INFO")
def get_device(self) -> str:
"""Resolve 'auto' to actual device string."""
if self.device == "auto":
try:
import torch
return "cuda" if torch.cuda.is_available() else "cpu"
except ImportError:
return "cpu"
return self.device
def ensure_dirs(self) -> None:
"""Create all required data directories if they don't exist."""
dirs = [
self.qdrant_path,
self.bm25_index_path,
self.processed_docs_path,
]
for d in dirs:
d.mkdir(parents=True, exist_ok=True)
# Singleton settings instance — import this everywhere
settings = Settings()
|