Spaces:
Runtime error
Runtime error
| # Experiment Lab β LangChain RAG Implementation | |
| This branch rewrites the core RAG pipeline using **LangChain** and its | |
| supporting frameworks, replacing the custom implementations in `master`. | |
| ## What Changed | |
| | Component | master branch | experiment-lab branch | | |
| |---|---|---| | |
| | **DOCX loading** | `python-docx` + manual XML block extraction | `langchain_community` `Docx2txtLoader` | | |
| | **PDF loading** | `pymupdf` + font-size heading heuristics | `langchain_community` `PyMuPDFLoader` | | |
| | **Text splitting** | Custom recursive token splitter | `langchain_text_splitters` `RecursiveCharacterTextSplitter` (tiktoken) | | |
| | **Embeddings** | Custom `EmbeddingClient` ABC + manual OpenAI/SentenceTransformer wrappers | `langchain_openai.OpenAIEmbeddings` / `langchain_huggingface.HuggingFaceEmbeddings` | | |
| | **Vector store** | Manual `chromadb` + FAISS with custom metadata filtering | `langchain_community.vectorstores.FAISS` only β local, no Chroma | | |
| | **Retrieval** | Manual query embedding β `vs.search(vector, tenant_id)` | `vs.search(text, tenant_id)` β store embeds internally | | |
| | **LLM generation** | Manual `openai.OpenAI().chat.completions.create()` | **LCEL chain**: `RICS_PROMPT \| ChatOpenAI \| StrOutputParser()` | | |
| | **Retry logic** | None | Built-in via `ChatOpenAI(max_retries=3)` | | |
| ## What Stayed the Same | |
| These components contain business logic specific to this system and were | |
| **not** replaced with LangChain equivalents: | |
| - `app/api/` β FastAPI endpoints, tenant middleware, upload/generate/status | |
| - `app/db/` β SQLAlchemy ORM models and session management | |
| - `app/cache/section_cache.py` β SHA-256 keyed JSON section cache | |
| - `app/generator/postprocess.py` β `[VERIFY]` non-invention enforcement | |
| - `app/generator/reranker.py` β Custom Jaccard + numeric overlap reranker | |
| - `app/templates/rics_templates.py` β RICS section skeleton templates | |
| - `app/models/schemas.py` β Pydantic API schemas | |
| - `app/config.py` β Settings management | |
| ## New Dependencies | |
| ```toml | |
| langchain>=0.3.0 | |
| langchain-core>=0.3.0 | |
| langchain-openai>=0.2.0 | |
| langchain-community>=0.3.0 | |
| faiss-cpu>=1.8.0 | |
| langchain-huggingface>=0.1.0 | |
| langchain-text-splitters>=0.3.0 | |
| docx2txt>=0.8 | |
| ``` | |
| ## Key Design Decisions | |
| ### Why keep a custom reranker? | |
| LangChain's built-in rerankers (`FlashrankRerank`, `CrossEncoderReranker`) | |
| require additional model downloads or paid APIs. The custom Jaccard+numeric | |
| reranker is lightweight, free, and tailored to RICS numeric fact verification. | |
| ### Why keep `[VERIFY]` post-processing? | |
| This is a domain-specific safety feature with no LangChain equivalent. | |
| It enforces the non-invention guarantee that is core to RICS report integrity. | |
| ### LCEL Chain Structure | |
| ```python | |
| from langchain_core.prompts import ChatPromptTemplate | |
| from langchain_openai import ChatOpenAI | |
| from langchain_core.output_parsers import StrOutputParser | |
| chain = RICS_PROMPT | ChatOpenAI(model="gpt-4o-mini", temperature=0.2) | StrOutputParser() | |
| text = chain.invoke({"skeleton": ..., "bullets": ..., "examples": ...}) | |
| ``` | |
| ### Vectorstore Pipeline | |
| ```python | |
| # Ingestion β store handles embedding internally | |
| vectorstore.add_documents(langchain_docs) | |
| # Retrieval β store embeds query internally | |
| results = vectorstore.search(query_text, tenant_id=tenant_id, k=10) | |
| ``` | |
| ## Merged product features (AI transparency + tenant RAG) | |
| The following were merged from the main-line feature work and are **in addition** | |
| to the LangChain stack above: | |
| - **`retrieve_for_report()`** β tenant-scoped retrieval with the reportβs source | |
| document prioritised. | |
| - **Provenance enrichment** β filenames, snippet previews, and section hints for | |
| citations in the API and UI. | |
| - **`compute_ai_transparency()`** β heuristic AI-involvement estimates stored in | |
| section metadata. | |
| - **Frontend** β aggregate AI involvement bar, per-section transparency, | |
| citation chips, and fixed proofread/enhance re-run handling. | |
| - **Generate mode** uses **LCEL** (`RICS_PROMPT | ChatOpenAI | StrOutputParser`) | |
| with the same rich `build_user_prompt` / style-profile content as before | |
| (single `user_content` variable in the chat template). | |
| Proofread and enhance modes still use the OpenAI Chat Completions client for | |
| stable, predictable behaviour. | |
| ## Running This Branch | |
| ```bash | |
| git checkout experiment-lab | |
| pip install -e ".[dev]" | |
| cp .env.example .env # add OPENAI_API_KEY if available | |
| uvicorn app.main:app --reload | |
| ``` | |
| Open http://localhost:8000 β the same UI as master. | |
| ## Trade-offs vs Master Branch | |
| | Aspect | Master (custom) | Experiment-lab (LangChain) | | |
| |---|---|---| | |
| | Code volume | ~800 lines of RAG pipeline code | ~400 lines (LangChain handles the rest) | | |
| | Structural fidelity | Heading/list/table block types preserved | Plain text only (heading levels lost) | | |
| | Debuggability | Full control, easy to step through | LangChain abstractions can obscure flow | | |
| | Observability | Manual logging | LangSmith tracing available | | |
| | Upgradability | Manual updates to each component | Single `pip upgrade langchain` | | |
| | Vendor lock-in | None | LangChain API surface | | |