Spaces:
Sleeping
Sleeping
| """UI GreenMetric RAG Assistant v1.0 — Gradio web interface.""" | |
| import os | |
| import queue | |
| import threading | |
| import gradio as gr | |
| from openai import APIError | |
| from src.pipeline import ask, _budget | |
| # --------------------------------------------------------------------------- | |
| # Startup check | |
| # --------------------------------------------------------------------------- | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| if not os.getenv("DEEPSEEK_API_KEY"): | |
| raise RuntimeError( | |
| "DEEPSEEK_API_KEY not found. " | |
| "Set it in your .env file or as an environment variable." | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # ChromaDB startup check — auto-build if missing (HF Spaces deployment) | |
| # --------------------------------------------------------------------------- | |
| def _ensure_chromadb() -> None: | |
| """Build ChromaDB collection if it doesn't exist on disk. | |
| Handles the case where ``chroma_db/`` was not properly deployed to | |
| HF Spaces, or the collection was accidentally dropped. | |
| """ | |
| import chromadb | |
| collection_name = os.getenv("RAG_COLLECTION", "greenmetric_bgem3") | |
| client = chromadb.PersistentClient(path="./chroma_db") | |
| try: | |
| client.get_collection(collection_name) | |
| print(f"ChromaDB collection '{collection_name}' found.") | |
| except ValueError: | |
| print(f"ChromaDB collection '{collection_name}' not found — building...") | |
| from build_collection import chunk_all | |
| from src.embedder import store | |
| sources = chunk_all() | |
| store(sources, collection_name=collection_name) | |
| print("ChromaDB build complete.") | |
| _ensure_chromadb() | |
| # --------------------------------------------------------------------------- | |
| # Budget helpers | |
| # --------------------------------------------------------------------------- | |
| def _budget_display() -> str: | |
| used = _budget.used() | |
| cap = _budget.daily_cap | |
| pct = min(100, used / cap * 100) if cap else 0 | |
| return ( | |
| f"{used:,} / {cap:,} tokens used today ({pct:.0f}%)\n\n" | |
| "*Token usage is shared across all users. Resets daily at midnight UTC.*" | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Chat handler | |
| # --------------------------------------------------------------------------- | |
| def respond(message: str, chat_history: list[dict]): | |
| chat_history.append({"role": "user", "content": message}) | |
| chat_history.append({"role": "assistant", "content": "Thinking..."}) | |
| yield chat_history | |
| result_container: dict = {} | |
| status_queue: "queue.Queue[str]" = queue.Queue() | |
| def _on_status(msg: str) -> None: | |
| status_queue.put(msg) | |
| def _run() -> None: | |
| try: | |
| result_container["result"] = ask(message, _on_status=_on_status) | |
| except APIError: | |
| result_container["api_error"] = True | |
| except Exception as exc: | |
| result_container["error"] = exc | |
| thread = threading.Thread(target=_run, daemon=True) | |
| thread.start() | |
| while True: | |
| try: | |
| status = status_queue.get(timeout=0.3) | |
| chat_history[-1]["content"] = status | |
| yield chat_history | |
| except queue.Empty: | |
| if not thread.is_alive(): | |
| break | |
| thread.join() | |
| if "api_error" in result_container: | |
| chat_history[-1]["content"] = ( | |
| "The AI service is temporarily unavailable. " | |
| "This may be due to rate limits or API downtime. " | |
| "Please try again in a moment." | |
| ) | |
| yield chat_history | |
| return | |
| if "error" in result_container: | |
| chat_history[-1]["content"] = ( | |
| f"Something went wrong. Please try again.\n\n" | |
| f"Details: {result_container['error']!s}" | |
| ) | |
| yield chat_history | |
| return | |
| result = result_container.get("result") | |
| if result is None: | |
| chat_history[-1]["content"] = "Something went wrong. Please try again." | |
| yield chat_history | |
| return | |
| answer = result["answer"] | |
| if result["low_confidence"]: | |
| answer += "\n\nLow confidence: Retrieved context scored near the relevance threshold." | |
| chat_history[-1]["content"] = answer | |
| yield chat_history | |
| # --------------------------------------------------------------------------- | |
| # Layout | |
| # --------------------------------------------------------------------------- | |
| with gr.Blocks(title="UI GreenMetric RAG Assistant v1.0") as app: | |
| gr.Markdown("# UI GreenMetric RAG Assistant v1.0") | |
| chatbot = gr.Chatbot(label="Chat") | |
| with gr.Row(): | |
| msg_input = gr.Textbox( | |
| placeholder="Ask a question about UI GreenMetric...", | |
| show_label=False, | |
| scale=4, | |
| ) | |
| send_btn = gr.Button("Send", variant="primary", scale=1) | |
| budget_display = gr.Markdown(_budget_display() + "\n\n*Conversations may be logged for quality monitoring.*") | |
| # --- event bindings --- | |
| def _on_send(msg, hist): | |
| for chat_out in respond(msg, hist): | |
| yield chat_out, _budget_display(), "" | |
| send_btn.click( | |
| fn=_on_send, | |
| inputs=[msg_input, chatbot], | |
| outputs=[chatbot, budget_display, msg_input], | |
| ) | |
| msg_input.submit( | |
| fn=_on_send, | |
| inputs=[msg_input, chatbot], | |
| outputs=[chatbot, budget_display, msg_input], | |
| ) | |
| if __name__ == "__main__": | |
| app.launch() | |