sadickam's picture
Prepare for HF Space deployment
d01a7e3
"""RAG Chatbot for pythermalcomfort Documentation.
This package provides a Retrieval-Augmented Generation (RAG) chatbot
for the pythermalcomfort library documentation. The chatbot can answer
questions about thermal comfort standards, calculations, and the
pythermalcomfort Python library.
Architecture:
The package is organized into the following submodules:
- config/ : Configuration management using Pydantic settings
- extraction/ : PDF to Markdown conversion pipeline
- chunking/ : Structure-aware text chunking with heading inheritance
- embeddings/ : BGE encoder and embedding storage
- retrieval/ : Hybrid retrieval (FAISS + BM25 with RRF fusion)
- llm/ : Multi-provider LLM integration (Gemini, Groq, DeepSeek)
- api/ : FastAPI endpoints with SSE streaming
- qlog/ : Async query logging to HuggingFace dataset
Lazy Loading:
Heavy dependencies (torch, faiss, sentence-transformers) are loaded
lazily on first access using the __getattr__ pattern. This ensures
fast import times and minimal memory usage until features are needed.
Exports:
config:
- Settings: Main configuration class with all application settings
extraction:
- ExtractedPage: Represents a single extracted PDF page
- ExtractedDocument: Represents a fully extracted PDF document
- Extractor: Protocol for synchronous PDF extractors
- AsyncExtractor: Protocol for asynchronous PDF extractors
- PDFExtractor: Main class for PDF document processing
- MarkdownConverter: Converts extracted content to Markdown format
chunking:
- Chunker: Main class for document chunking
- Chunk: Data class representing a single chunk
- ChunkingStrategy: Base class for chunking strategies
embeddings:
- BGEEncoder: Generates embeddings using BGE models
- EmbeddingStorage: Manages embedding persistence and loading
retrieval:
- FAISSIndex: Dense vector similarity search
- BM25Retriever: Sparse keyword-based retrieval
- HybridRetriever: Combines dense and sparse methods
- RetrievalResult: Data class for retrieval results
llm:
- BaseLLM: Abstract base class for LLM providers
- GeminiLLM: Gemini provider implementation
- GroqLLM: Groq provider implementation
- DeepSeekLLM: DeepSeek provider implementation
- QuotaManager: Tracks provider usage and limits
- ProviderStatus: Enum for provider status states
api:
- create_app: Factory function for FastAPI application
qlog:
- QueryLog: Pydantic model for log entries
- HFDatasetWriter: Async writer for HuggingFace datasets
Example:
-------
>>> import rag_chatbot
>>> # Package imports quickly without loading heavy dependencies
>>> rag_chatbot.__version__
'0.1.0'
>>> # Access exports lazily
>>> from rag_chatbot import Settings, PDFExtractor, Chunker
>>> settings = Settings()
"""
from __future__ import annotations
from typing import TYPE_CHECKING
# =============================================================================
# Type Checking Imports
# =============================================================================
# These imports are only processed by type checkers (mypy, pyright) and IDEs.
# They enable proper type hints and autocompletion without runtime overhead.
# =============================================================================
if TYPE_CHECKING:
from rag_chatbot.api import create_app
from rag_chatbot.chunking import Chunker, ChunkingStrategy
from rag_chatbot.chunking.models import Chunk
from rag_chatbot.config import Settings
from rag_chatbot.embeddings import BGEEncoder, EmbeddingStorage
from rag_chatbot.extraction import (
AsyncExtractor,
ExtractedDocument,
ExtractedPage,
Extractor,
MarkdownConverter,
PDFExtractor,
)
from rag_chatbot.llm import (
BaseLLM,
DeepSeekLLM,
GeminiLLM,
GroqLLM,
QuotaManager,
)
from rag_chatbot.llm.quota import ProviderStatus
from rag_chatbot.qlog import HFDatasetWriter, QueryLog
from rag_chatbot.retrieval import BM25Retriever, FAISSIndex, HybridRetriever
from rag_chatbot.retrieval.models import RetrievalResult
# =============================================================================
# Package Metadata
# =============================================================================
# These are standard package metadata fields that tools and IDEs can use
# to get information about the package.
# =============================================================================
# Package version - follows semantic versioning (MAJOR.MINOR.PATCH)
# This should be kept in sync with pyproject.toml version
__version__: str = "0.1.0"
# Author information
__author__: str = "sadickam"
# =============================================================================
# Public API
# =============================================================================
# The __all__ list defines what is exported when using `from rag_chatbot import *`
# All exports are lazy-loaded on first access for fast import times.
# =============================================================================
__all__: list[str] = [
# Metadata
"__version__",
"__author__",
# config
"Settings",
# extraction - data models
"ExtractedPage",
"ExtractedDocument",
# extraction - protocols
"Extractor",
"AsyncExtractor",
# extraction - implementations
"PDFExtractor",
"MarkdownConverter",
# chunking
"Chunker",
"Chunk",
"ChunkingStrategy",
# embeddings
"BGEEncoder",
"EmbeddingStorage",
# retrieval
"FAISSIndex",
"BM25Retriever",
"HybridRetriever",
"RetrievalResult",
# llm
"BaseLLM",
"GeminiLLM",
"GroqLLM",
"DeepSeekLLM",
"QuotaManager",
"ProviderStatus",
# api
"create_app",
# qlog
"QueryLog",
"HFDatasetWriter",
]
# =============================================================================
# Lazy Loading Registry
# =============================================================================
# Maps export names to their (module_path, attribute_name) for lazy loading.
# This approach avoids excessive branches/returns in __getattr__.
# =============================================================================
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
# config
"Settings": ("rag_chatbot.config", "Settings"),
# extraction - data models
"ExtractedPage": ("rag_chatbot.extraction", "ExtractedPage"),
"ExtractedDocument": ("rag_chatbot.extraction", "ExtractedDocument"),
# extraction - protocols
"Extractor": ("rag_chatbot.extraction", "Extractor"),
"AsyncExtractor": ("rag_chatbot.extraction", "AsyncExtractor"),
# extraction - implementations
"PDFExtractor": ("rag_chatbot.extraction", "PDFExtractor"),
"MarkdownConverter": ("rag_chatbot.extraction", "MarkdownConverter"),
# chunking
"Chunker": ("rag_chatbot.chunking", "Chunker"),
"Chunk": ("rag_chatbot.chunking.models", "Chunk"),
"ChunkingStrategy": ("rag_chatbot.chunking", "ChunkingStrategy"),
# embeddings
"BGEEncoder": ("rag_chatbot.embeddings", "BGEEncoder"),
"EmbeddingStorage": ("rag_chatbot.embeddings", "EmbeddingStorage"),
# retrieval
"FAISSIndex": ("rag_chatbot.retrieval", "FAISSIndex"),
"BM25Retriever": ("rag_chatbot.retrieval", "BM25Retriever"),
"HybridRetriever": ("rag_chatbot.retrieval", "HybridRetriever"),
"RetrievalResult": ("rag_chatbot.retrieval.models", "RetrievalResult"),
# llm
"BaseLLM": ("rag_chatbot.llm", "BaseLLM"),
"GeminiLLM": ("rag_chatbot.llm", "GeminiLLM"),
"GroqLLM": ("rag_chatbot.llm", "GroqLLM"),
"DeepSeekLLM": ("rag_chatbot.llm", "DeepSeekLLM"),
"QuotaManager": ("rag_chatbot.llm", "QuotaManager"),
"ProviderStatus": ("rag_chatbot.llm.quota", "ProviderStatus"),
# api
"create_app": ("rag_chatbot.api", "create_app"),
# qlog
"QueryLog": ("rag_chatbot.qlog", "QueryLog"),
"HFDatasetWriter": ("rag_chatbot.qlog", "HFDatasetWriter"),
}
def __getattr__(name: str) -> object:
"""Lazy load module exports on first access.
This function is called when an attribute is not found in the module's
namespace. It enables lazy loading of heavy dependencies like torch,
faiss, and sentence-transformers, ensuring fast import times.
Args:
----
name: The name of the attribute being accessed.
Returns:
-------
The requested attribute if it exists in __all__.
Raises:
------
AttributeError: If the attribute is not a valid export.
"""
if name in _LAZY_IMPORTS:
module_path, attr_name = _LAZY_IMPORTS[name]
import importlib
module = importlib.import_module(module_path)
return getattr(module, attr_name)
msg = f"module {__name__!r} has no attribute {name!r}"
raise AttributeError(msg)