File size: 8,818 Bytes
135a99c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8dae95d
 
 
 
135a99c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9f014de
 
 
 
 
135a99c
 
 
 
 
9f014de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135a99c
 
 
8dae95d
 
 
 
 
 
 
 
 
 
 
 
 
 
135a99c
 
 
 
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
"""
Central config for the JanMat RAG chatbot backend.

Everything secret comes from the environment (HF Space repository secrets in
prod, .env locally — see .env.example). Nothing here should hold a real key.
"""
import os
from dotenv import load_dotenv

load_dotenv()  # harmless no-op if you never create a .env file

import os
from dotenv import load_dotenv

load_dotenv()  # harmless no-op if you never create a .env file — see note below

# ==============================================================================
# All secrets are read from environment variables, NEVER hardcoded here.
# This file is safe to commit to GitHub (public or private) as-is.
#
# Where the environment variables actually come from depends on where you run:
#   - Locally: `export SUPABASE_URL="..."` etc. in your terminal before running
#     `python main.py` (see MIGRATION_GUIDE.md for the exact commands).
#   - Hugging Face Space: Settings -> Variables and secrets -> add each one
#     under "Secrets". HF injects them as environment variables automatically
#     at runtime — nothing in this file needs to change.
# ==============================================================================

# --- Supabase (knowledge base + chat history) --------------------------------
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_KEY", "")  # service_role key, backend-only

# --- Groq (answer generation) -------------------------------------------------
GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
GROQ_CHAT_MODEL = os.getenv("GROQ_CHAT_MODEL", "openai/gpt-oss-20b")

# --- Sarvam AI (voice: speech-to-text + text-to-speech) -----------------------
SARVAM_API_KEY = os.getenv("SARVAM_API_KEY", "")
SARVAM_STT_MODEL = os.getenv("SARVAM_STT_MODEL", "saaras:v3")
SARVAM_TTS_MODEL = os.getenv("SARVAM_TTS_MODEL", "bulbul:v3")
SARVAM_DEFAULT_SPEAKER = os.getenv("SARVAM_DEFAULT_SPEAKER", "anushka")
SARVAM_DEFAULT_LANGUAGE = os.getenv("SARVAM_DEFAULT_LANGUAGE", "en-IN")

# --- TinyFish (live web: Search, Fetch, Agent) -------------------------------
# Optional. Leave unset and JanMat behaves exactly as before.
TINYFISH_API_KEY = os.getenv("TINYFISH_API_KEY", "")

# --- Cron / admin protection ---------------------------------------------------
CRON_SECRET = os.getenv("CRON_SECRET", "")

# --- Embedding model (local, free, no API key needed) -------------------------
EMBEDDING_MODEL_NAME = os.getenv("EMBEDDING_MODEL_NAME", "sentence-transformers/all-MiniLM-L6-v2")
EMBEDDING_DIM = 384

# --- Chunking -------------------------------------------------------------------
CHUNK_WORDS = int(os.getenv("CHUNK_WORDS", "220"))
CHUNK_OVERLAP_WORDS = int(os.getenv("CHUNK_OVERLAP_WORDS", "40"))

# --- Retrieval / generation -------------------------------------------------------
# Broader than a typical minimal RAG setup: more candidate chunks per query,
# and a larger context budget, so answers draw on more of the ingested
# material per question rather than a bare top-5. Still cheap — this is a
# few dozen KB of text per Groq call, not the whole knowledge base.
TOP_K_CHUNKS = int(os.getenv("TOP_K_CHUNKS", "20"))
MAX_CONTEXT_CHARS = int(os.getenv("MAX_CONTEXT_CHARS", "20000"))

# --- Sources to ingest ------------------------------------------------------------
CONFIG = {
    "prs": {
        "name": "PRS Legislative Research",
        "base_url": "https://prsindia.org",
        "listing_url": "https://prsindia.org/billtrack",
        "bill_link_pattern": r"^/billtrack/[^/]+$",
        "bill_link_exclude_prefixes": ("/billtrack/category/", "/billtrack/field_bill_category/"),
        "rate_limit_seconds": 2,
        # Rolling window: JanMat keeps the newest N bills (PRS lists newest first). Each ingestion
        # adds any new bills and then deletes the oldest ones beyond N, with their PDFs and text.
        "max_bills_kept": 25,
        "listing_pages": 6,                  # how many ?page=N listing pages to read to find N bills
        "delete_comments_with_bill": False,  # True = also delete user comments on bills that rotate out
        "license_note": (
            "PRS's disclaimer permits reproduction for non-commercial purposes "
            "with due acknowledgement of PRS Legislative Research."
        ),
    },
    # The rest of the PRS website (everything beyond the first page of bills).
    # The crawler visits each section's index pages (following ?page=N pagination),
    # collects links whose path matches `item_pattern`, and ingests each page plus
    # any PDFs it links to. Add or remove sections freely.
    "prs_library": {
        "enabled": True,
        # Each run ingests at most this many NEW documents, then stops. Documents
        # already stored are skipped without being fetched, so just click
        # "Run ingestion now" repeatedly and the library grows until it is complete.
        # (Free Supabase = 500 MB; roughly 4 KB per chunk, ~100 chunks per document.)
        "max_new_docs_per_run": 60,
        "max_index_pages": 5,        # pagination depth per index URL
        "sections": [
            {"key": "policy_review", "label": "Policy Reviews",
             "index_urls": ["https://prsindia.org/policy/monthly-policy-review",
                            "https://prsindia.org/policy/annual-policy-review"],
             "item_pattern": r"^/policy/(monthly|annual)-policy-review/[^/]+$"},
            {"key": "committee_report", "label": "Committee Reports",
             "index_urls": ["https://prsindia.org/policy/report-summaries",
                            "https://prsindia.org/parliamenttrack/report-summaries"],
             "item_pattern": r"^/(policy|parliamenttrack)/report-summaries/[^/]+$"},
            {"key": "session", "label": "Session Wraps",
             "index_urls": ["https://prsindia.org/sessiontrack"],
             "item_pattern": r"^/sessiontrack/[^/]+(/[^/]+)?$"},
            {"key": "discussion_paper", "label": "Discussion Papers",
             "index_urls": ["https://prsindia.org/policy/discussion-papers",
                            "https://prsindia.org/legislatures/discussion-papers",
                            "https://prsindia.org/budgets/discussionpapers"],
             "item_pattern": r"^/(policy|legislatures|budgets)/(discussion-papers|discussionpapers)/[^/]+$"},
            {"key": "blog", "label": "Blog",
             "index_urls": ["https://prsindia.org/theprsblog"],
             "item_pattern": r"^/theprsblog/(?!blog-author)[^/]+$"},
            {"key": "article", "label": "Articles",
             "index_urls": ["https://prsindia.org/articles-by-prs-team/2026",
                            "https://prsindia.org/articles-by-prs-team/2025",
                            "https://prsindia.org/articles-by-prs-team/2024"],
             "item_pattern": r"^/articles-by-prs-team/(?!\d{4}$)[^/]+$"},
            {"key": "act", "label": "Acts",
             "index_urls": ["https://prsindia.org/acts/parliament", "https://prsindia.org/recent-acts"],
             "item_pattern": r"^/(acts/[^/]+|recent-acts)/[^/]+$"},
            {"key": "state_bill", "label": "State Bills",
             "index_urls": ["https://prsindia.org/bills/states",
                            "https://prsindia.org/bills/state-legislative-briefs"],
             "item_pattern": r"^/bills/(states|state-legislative-briefs)/[^/]+$"},
            {"key": "budget", "label": "Budgets",
             "index_urls": ["https://prsindia.org/budgets/parliament", "https://prsindia.org/budgets/states"],
             "item_pattern": r"^/budgets/(parliament|states)/[^/]+$"},
        ],
        # "Comments invited on ..." notices (bill name, deadline, link to submit).
        "announcement_urls": ["https://prsindia.org/", "https://prsindia.org/announcements"],
    },
    # Add more sources here as plain "seed URL lists" — the ingestion pipeline
    # treats any HTTP(S) URL the same way: fetch, detect PDF vs HTML, extract
    # full text, chunk, embed, store. See scraper.py:ArticleSource.
    # Used only when TINYFISH_API_KEY is set.
    "tinyfish": {
        # Search API: finds drafts / news / stakeholder reactions beyond PRS.
        "search_queries": [
            "draft bill public consultation stakeholder comments India",
            "Ministry of Corporate Affairs draft amendment comments invited",
        ],
        "results_per_query": 5,
        # Agent API: pages whose public comments should be extracted. Add the
        # consultation pages you are allowed to read (e.g. MCA draft pages).
        "consultation_urls": [],
        # Also run the agent on this many PRS bill pages per run (Agent runs are billed).
        "agent_max_bill_pages": 3,
    },
    "article_seed_urls": [
        # "https://example-news-site.com/article-about-a-bill",
    ],
}