""" Viz LLM - Gradio App A RAG-powered assistant for data visualization guidance, powered by Jina-CLIP-v2 embeddings and research from the field of information graphics. """ import os import gradio as gr from dotenv import load_dotenv from src.rag_pipeline import create_pipeline from datetime import datetime, timedelta from collections import defaultdict # Load environment variables load_dotenv() # Rate limiting: Track requests per user (IP-based) # Format: {ip: [(timestamp1, timestamp2, ...)]} rate_limit_tracker = defaultdict(list) DAILY_LIMIT = 20 # Initialize the RAG pipeline print("Initializing Graphics Design Pipeline...") try: pipeline = create_pipeline( retrieval_k=5, model=os.getenv("LLM_MODEL", "meta-llama/Llama-3.1-8B-Instruct"), temperature=float(os.getenv("LLM_TEMPERATURE", "0.2")) ) print("✓ Pipeline initialized successfully") except Exception as e: print(f"✗ Error initializing pipeline: {e}") raise def check_rate_limit(request: gr.Request) -> tuple[bool, int]: """Check if user has exceeded rate limit""" if request is None: return True, DAILY_LIMIT # Allow if no request object user_id = request.client.host now = datetime.now() cutoff = now - timedelta(days=1) # Remove old requests (older than 24 hours) rate_limit_tracker[user_id] = [ ts for ts in rate_limit_tracker[user_id] if ts > cutoff ] remaining = DAILY_LIMIT - len(rate_limit_tracker[user_id]) if remaining <= 0: return False, 0 # Add current request rate_limit_tracker[user_id].append(now) return True, remaining - 1 def recommend_stream(message: str, history: list, request: gr.Request): """ Streaming version of design recommendation function Args: message: User's design query history: Chat history request: Gradio request object for rate limiting Yields: Response chunks """ # Check rate limit allowed, remaining = check_rate_limit(request) if not allowed: yield "⚠️ **Rate limit exceeded.** You've reached the maximum of 20 queries per day. Please try again in 24 hours." return try: response_stream = pipeline.generate_recommendations(message, stream=True) full_response = "" for chunk in response_stream: full_response += chunk yield full_response # Add rate limit info at the end if remaining <= 5: yield full_response + f"\n\n---\n*You have {remaining} queries remaining today.*" except Exception as e: yield f"Error generating response: {str(e)}\n\nPlease check your environment variables (HF_TOKEN, SUPABASE_URL, SUPABASE_KEY) and try again." # Minimal CSS to fix UI artifacts custom_css = """ /* Hide retry/undo buttons that appear as artifacts */ .chatbot button[aria-label="Retry"], .chatbot button[aria-label="Undo"] { display: none !important; } /* Remove overflow-y scroll from textarea */ textarea[data-testid="textbox"] { overflow-y: hidden !important; } """ # Create Gradio interface with gr.Blocks( title="Viz LLM", css=custom_css ) as demo: gr.Markdown(""" # 📊 Viz LLM Get design recommendations for creating effective data visualizations based on research and best practices. """) # Main chat interface chatbot = gr.ChatInterface( fn=recommend_stream, type="messages", examples=[ "What's the best chart type for showing trends over time?", "How do I create an effective infographic for complex data?", "What are best practices for data visualization accessibility?", "How should I design a dashboard for storytelling?", "What visualization works best for comparing categories?" ], cache_examples=False, api_name="recommend" ) # Knowledge base section (below chat interface) gr.Markdown(""" ### Knowledge Base This assistant draws on research papers, design principles, and examples from the field of information graphics and data visualization. **Credits:** Special thanks to the researchers whose work informed this model: Robert Kosara, Edward Segel, Jeffrey Heer, Matthew Conlen, John Maeda, Kennedy Elliott, Scott McCloud, and many others. --- **Usage Limits:** This service is limited to 20 queries per day per user to manage costs. Responses are optimized for English.
Embeddings: Jina-CLIP-v2
""") # Launch configuration if __name__ == "__main__": # Check for required environment variables required_vars = ["SUPABASE_URL", "SUPABASE_KEY", "HF_TOKEN"] missing_vars = [var for var in required_vars if not os.getenv(var)] if missing_vars: print(f"⚠️ Warning: Missing environment variables: {', '.join(missing_vars)}") print("Please set these in your .env file or as environment variables") # Launch the app demo.launch( server_name="0.0.0.0", server_port=7860, share=False, show_api=True )