Spaces:
Running on Zero
Running on Zero
| import os | |
| import typing | |
| import base64 | |
| import gradio as gr | |
| import spaces | |
| from openai import OpenAI | |
| from datasets import load_dataset | |
| from sentence_transformers import SentenceTransformer | |
| import faiss | |
| import numpy as np | |
| # ---------- Secrets / OpenAI (HF Router) ---------- | |
| HF_AUTH = os.getenv("HF_AUTH", "") | |
| if not HF_AUTH: | |
| raise RuntimeError( | |
| "Missing HF auth secret in environment. " | |
| "In Spaces, create a secret with name HF_AUTH (or change app.py to match your secret name)." | |
| ) | |
| client = OpenAI( | |
| base_url="https://router.huggingface.co/v1", | |
| api_key=HF_AUTH, | |
| ) | |
| # ---------- Dataset config ---------- | |
| DATASET_ID = "gitrelief/Florida_Legislation_and_Summaries" | |
| PREFERRED_FIELDS = [ | |
| "full_text", | |
| "text", | |
| "content", | |
| "article", | |
| "body", | |
| "legislation", | |
| "summary", | |
| "summaries", | |
| "passage", | |
| ] | |
| # Lazy-loaded globals (so the app can start immediately) | |
| INDEX_READY = False | |
| documents: list[str] = [] | |
| index = None | |
| embed_model = None | |
| text_field = None | |
| def pick_text_field(example: dict) -> str: | |
| for f in PREFERRED_FIELDS: | |
| if f in example and isinstance(example[f], str) and example[f].strip(): | |
| return f | |
| for k, v in example.items(): | |
| if isinstance(v, str) and v.strip(): | |
| return k | |
| raise ValueError(f"Could not find a usable text field. Keys: {list(example.keys())}") | |
| def build_index(progress=gr.Progress(track_tqdm=True)): | |
| global INDEX_READY, documents, index, embed_model, text_field | |
| # 1) Load dataset | |
| progress(0.05, desc="Loading dataset…") | |
| ds = load_dataset(DATASET_ID, split="train") | |
| # 2) Decide which field to use | |
| text_field = pick_text_field(ds[0]) | |
| # 3) Extract documents | |
| progress(0.15, desc=f"Building document list from field: {text_field}") | |
| documents.clear() | |
| for ex in ds: | |
| t = ex.get(text_field, "") | |
| if isinstance(t, str) and t.strip(): | |
| documents.append(t) | |
| if not documents: | |
| raise RuntimeError("No documents found after extracting the text field.") | |
| # 4) Embed + FAISS | |
| progress(0.35, desc="Loading embedding model…") | |
| embed_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") | |
| progress(0.45, desc="Embedding documents…") | |
| embeddings = embed_model.encode( | |
| documents, | |
| batch_size=32, | |
| show_progress_bar=False, | |
| normalize_embeddings=True, | |
| ) | |
| embeddings = np.asarray(embeddings, dtype="float32") | |
| progress(0.80, desc="Building FAISS index…") | |
| index = faiss.IndexFlatIP(embeddings.shape[1]) | |
| index.add(embeddings) | |
| INDEX_READY = True | |
| progress(1.0, desc="Index ready") | |
| def ensure_index(): | |
| global INDEX_READY | |
| if not INDEX_READY: | |
| build_index() | |
| def retrieve(query: str, k: int = 5) -> list[str]: | |
| q = embed_model.encode([query], normalize_embeddings=True) | |
| q = np.asarray(q, dtype="float32") | |
| scores, ids = index.search(q, k) | |
| hits: list[str] = [] | |
| for i in ids[0]: | |
| if 0 <= i < len(documents): | |
| hits.append(documents[i]) | |
| return hits | |
| def build_messages(user_message: str, retrieved: typing.List[str]) -> list: | |
| system_prompt = ( | |
| "You are Kimi K2.6. Answer the user using the provided Florida legislation text as context. " | |
| "If the context is insufficient, say so and ask a clarifying question." | |
| ) | |
| context_block = "\n\n".join([f"[Context {i+1}]\n{txt}" for i, txt in enumerate(retrieved)]) | |
| return [ | |
| {"role": "system", "content": system_prompt}, | |
| { | |
| "role": "user", | |
| "content": f"Retrieved context:\n{context_block}\n\nUser question: {user_message}", | |
| }, | |
| ] | |
| # ---------- UI handlers ---------- | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## Kimi-K2.6 + Florida Legislation (RAG, full text preferred)") | |
| status = gr.Textbox(label="Status", interactive=False) | |
| inp = gr.Textbox(label="Your question", lines=4) | |
| out = gr.Textbox(label="Answer", lines=12) | |
| submit = gr.Button("Submit") | |
| build_btn = gr.Button("Build/Reload Index") | |
| def on_build(): | |
| nonlocal_status = "Building index (this may take a while)…" | |
| try: | |
| build_index() | |
| return "Index ready." | |
| except Exception as e: | |
| return f"Index build failed: {type(e).__name__}: {e}" | |
| build_btn.click(fn=on_build, inputs=None, outputs=status) | |
| def chat_handler(message: str) -> str: | |
| # Ensure index exists before doing GPU work | |
| if not message or not message.strip(): | |
| return "Please enter a question." | |
| if not INDEX_READY: | |
| # Build on first ask (still better than failing silently) | |
| build_index() | |
| retrieved = retrieve(message, k=5) | |
| messages = build_messages(message, retrieved) | |
| completion = client.chat.completions.create( | |
| model="moonshotai/Kimi-K2.6:fireworks-ai", | |
| messages=messages, | |
| stream=False, | |
| ) | |
| return completion.choices[0].message.content | |
| submit.click(fn=chat_handler, inputs=inp, outputs=out) | |
| if __name__ == "__main__": | |
| demo.launch() | |