Download backend/app/config.py from fardeen16/finrag: direct link, hf CLI and curl.
- Browser
- Download file 5.08 kB
-
https://huggingface.co/spaces/fardeen16/finrag/resolve/main/backend/app/config.py
- Command line
-
hf download hf://spaces/fardeen16/finrag/backend/app/config.py
-
curl -L -o config.py https://huggingface.co/spaces/fardeen16/finrag/resolve/main/backend/app/config.py
5.08 kB
| """Environment-driven configuration. | |
| Replaces the hardcoded API key in `agent_tools.py`. Nothing in the new code | |
| path reads a literal credential. | |
| """ | |
| from __future__ import annotations | |
| from functools import lru_cache | |
| from pathlib import Path | |
| from typing import Literal, Optional | |
| from pydantic import Field, field_validator | |
| from pydantic_settings import BaseSettings, SettingsConfigDict | |
| PROJECT_ROOT = Path(__file__).resolve().parents[2] | |
| class Settings(BaseSettings): | |
| model_config = SettingsConfigDict( | |
| env_file=PROJECT_ROOT / ".env", | |
| env_file_encoding="utf-8", | |
| extra="ignore", | |
| ) | |
| # --- Inference ----------------------------------------------------------- | |
| # Points at local vLLM in development and at the RunPod endpoint in | |
| # deployment. Nothing else changes between the two. | |
| vllm_base_url: str = "http://localhost:8000/v1" | |
| vllm_api_key: str = "EMPTY" # vLLM ignores it, but the client requires one. | |
| vllm_model: str = "Qwen/Qwen2.5-7B-Instruct-AWQ" | |
| # First token after a RunPod cold start is often 2–3 minutes. The LangChain | |
| # stream-chunk default of 120s aborts that wait and the 300s agent ceiling | |
| # then fires while the Planner is still on its first call. | |
| llm_timeout_s: float = 240.0 | |
| # The openai client retries with backoff by default, which turns one dead | |
| # request into minutes of silence. Keep it low and let the agent's own | |
| # wall-clock ceiling be the real backstop. | |
| llm_max_retries: int = 1 | |
| # --- Embeddings ---------------------------------------------------------- | |
| # 'google' matches the vectors already in Qdrant (text-embedding-004, 768d). | |
| # 'local' is where this is going, but flipping it requires a full re-index — | |
| # vectors from different models are not comparable, so a mismatched | |
| # collection returns plausible-looking nonsense rather than an error. | |
| embedding_provider: Literal["google", "local"] = "local" | |
| google_api_key: Optional[str] = None | |
| local_embedding_model: str = "BAAI/bge-small-en-v1.5" | |
| # --- Stores -------------------------------------------------------------- | |
| # Embedded (path) mode is single-process and lock-protected: fine for a CLI, | |
| # unusable behind a multi-worker server or on ephemeral container storage. | |
| # Set qdrant_url to move to server mode. | |
| qdrant_url: Optional[str] = None | |
| qdrant_api_key: Optional[str] = None | |
| qdrant_path: Path = PROJECT_ROOT / "data" / "qdrant_db" | |
| collection_name: str = "financial_docs_alphabet" | |
| sqlite_path: Path = PROJECT_ROOT / "data" / "financials.db" | |
| table_name: str = "financials_summary" | |
| def _strip_secrets(cls, value: object) -> object: | |
| if isinstance(value, str): | |
| stripped = value.strip() | |
| return stripped or None | |
| return value | |
| reranker_model: str = "cross-encoder/ms-marco-MiniLM-L-6-v2" | |
| retrieval_candidates: int = 20 | |
| retrieval_top_k: int = 5 | |
| # Tenant tag written into every Qdrant payload. Shared reference filings are | |
| # owned by this pseudo-tenant; per-user uploads will carry their own user_id. | |
| # Present from the first index so enabling isolation never needs a re-index. | |
| public_tenant: str = "public" | |
| # --- Agent ceilings ------------------------------------------------------ | |
| # Auditor-driven replans, hard capped. Without this the router replans | |
| # forever whenever the model cannot satisfy the Auditor. | |
| max_replans: int = 2 | |
| audit_pass_score: int = 3 | |
| # Plans longer than this are truncated, which is what makes the superstep | |
| # budget below a bounded quantity rather than a guess. | |
| max_plan_steps: int = 3 | |
| # recursion_limit is a superstep budget, not a loop guard — max_replans is | |
| # the loop guard. Worst case is: | |
| # gatekeeper + (max_replans + 1) * (planner + max_plan_steps * 2) + synthesize | |
| # which is 1 + 3 * (1 + 10) + 1 = 35 at the defaults. 50 leaves headroom. | |
| # Setting it near the worst case is what produced GraphRecursionError before. | |
| recursion_limit: int = 50 | |
| agent_timeout_s: float = 600.0 | |
| # 'mock' replays canned traces so the UI can be developed without a GPU. | |
| # 'live' runs the LangGraph supervisor against vLLM + Qdrant. | |
| agent_mode: Literal["mock", "live"] = "mock" | |
| # Load BGE + CrossEncoder during lifespan. Turn off on 512 MB hosts | |
| # (Render Free) so uvicorn can bind before the first Librarian call. | |
| warmup_on_start: bool = True | |
| def require_google_key(self) -> str: | |
| if not self.google_api_key: | |
| raise RuntimeError( | |
| "embedding_provider='google' needs GOOGLE_API_KEY set. Either export it " | |
| "or switch EMBEDDING_PROVIDER=local (which requires re-indexing Qdrant)." | |
| ) | |
| return self.google_api_key | |
| def get_settings() -> Settings: | |
| return Settings() | |