Spaces:
Runtime error
Runtime error
| # app.py | |
| import os | |
| import uuid | |
| import gradio as gr | |
| from PIL import Image | |
| from datetime import datetime | |
| from supabase import create_client | |
| from sentence_transformers import SentenceTransformer | |
| # ======================= | |
| # SUPABASE CONFIG | |
| # ======================= | |
| SUPABASE_URL = os.environ["https://pjrzisurkyeunhgbmvco.supabase.co"] # suprabase url | |
| SUPABASE_KEY = os.environ["sb_publishable_mYlT4zRks_3BF-Lqqw35og_wzvLnMg2"] # suprabase service key | |
| supabase = create_client(SUPABASE_URL, SUPABASE_KEY) | |
| # Table names | |
| CONTEXT_TABLE = "context_memory" | |
| CHAT_TABLE = "conversation_history" | |
| # ======================= | |
| # USER SESSION MANAGEMENT | |
| # ======================= | |
| def get_or_create_user_id(user_id_state): | |
| if user_id_state is None: | |
| return str(uuid.uuid4()) | |
| return user_id_state | |
| # ======================= | |
| # EMBEDDINGS SETUP | |
| # ======================= | |
| embedding_model = SentenceTransformer( | |
| "jinaai/jina-embeddings-v3", | |
| trust_remote_code=True | |
| ) | |
| TRUNCATE_DIM = 256 # matches your Supabase vector column | |
| def embed_query(text: str): | |
| return embedding_model.encode([text], task="retrieval.query", truncate_dim=TRUNCATE_DIM)[0] | |
| # ======================= | |
| # RAG RETRIEVAL | |
| # ======================= | |
| TOP_K = 3 | |
| def get_top_k_chunks(user_id: str, query: str, k: int = TOP_K): | |
| q_emb = embed_query(query) | |
| # call Supabase RPC or SQL for vector similarity search | |
| # assuming you have a match_context_memory RPC defined in Supabase | |
| result = supabase.rpc( | |
| "match_context_memory", | |
| { | |
| "query_embedding": q_emb, | |
| "match_count": k, | |
| "user_id": user_id | |
| } | |
| ).execute() | |
| if result.data: | |
| return [row["semantic_chunk"] for row in result.data] | |
| return [] | |
| # ======================= | |
| # LOAD RECENT CHAT | |
| # ======================= | |
| MAX_HISTORY = 10 | |
| def get_recent_history(user_id: str, limit: int = MAX_HISTORY): | |
| res = supabase.table(CHAT_TABLE) \ | |
| .select("query_input,llm_output") \ | |
| .eq("unique_user_id", user_id) \ | |
| .order("convo_num", desc=True) \ | |
| .limit(limit) \ | |
| .execute() | |
| history = [] | |
| for row in reversed(res.data): | |
| history.append(f"User: {row['query_input']}") | |
| if row["llm_output"]: | |
| history.append(f"Assistant: {row['llm_output']}") | |
| return "\n".join(history) | |
| # ======================= | |
| # SAVE / UPDATE CHAT | |
| # ======================= | |
| def insert_user_query(user_id, user_name, region, query): | |
| supabase.table(CHAT_TABLE).insert({ | |
| "unique_user_id": user_id, | |
| "user_name": user_name, | |
| "region": region, | |
| "query_input": query | |
| }).execute() | |
| def update_llm_output(user_id, llm_output): | |
| supabase.table(CHAT_TABLE) \ | |
| .update({"llm_output": llm_output}) \ | |
| .eq("unique_user_id", user_id) \ | |
| .is_("llm_output", None) \ | |
| .order("convo_num", desc=True) \ | |
| .limit(1) \ | |
| .execute() | |
| # ======================= | |
| # VISION + LLM HANDLER (Bangla LLaMA 3.2) | |
| # ======================= | |
| import requests | |
| HF_LLM_KEY = os.environ.get("HF_API_KEY") | |
| MODEL_ID = "hishab/titulm-llama-3.2-3b-v2.0" | |
| HEADERS = {"Authorization": f"Bearer {HF_LLM_KEY}"} | |
| def vision_llm(image: Image.Image | None, text: str, top_chunks: list, chat_history: str): | |
| """ | |
| Calls Bangla LLaMA 3.2 via Hugging Face API with: | |
| - top-k RAG chunks | |
| - recent chat history | |
| - user input | |
| - optional image info | |
| """ | |
| prompt_parts = [ | |
| "You are a helpful assistant that can analyze images and text.", | |
| "Follow these rules: answer concisely, prefer facts from context.", | |
| "\n--- Context chunks ---\n" + "\n".join(top_chunks) if top_chunks else "", | |
| "\n--- Chat History ---\n" + chat_history if chat_history else "", | |
| "\n--- User Input ---\n" + text | |
| ] | |
| if image: | |
| prompt_parts.append(f"\nUser uploaded an image of size {image.width}x{image.height}.") | |
| prompt = "\n".join(prompt_parts) | |
| # Call Hugging Face Inference API | |
| payload = {"inputs": prompt, "parameters": {"max_new_tokens": 512}} | |
| try: | |
| response = requests.post( | |
| f"https://api-inference.huggingface.co/models/{MODEL_ID}", | |
| headers=HEADERS, | |
| json=payload, | |
| timeout=60 | |
| ) | |
| response.raise_for_status() | |
| data = response.json() | |
| # HF API returns a list of dicts with "generated_text" | |
| if isinstance(data, list) and "generated_text" in data[0]: | |
| return data[0]["generated_text"] | |
| elif isinstance(data, dict) and "generated_text" in data: | |
| return data["generated_text"] | |
| else: | |
| return str(data) | |
| except Exception as e: | |
| return f"Error calling Bangla LLaMA 3.2: {e}" | |
| # ======================= | |
| # GRADIO HANDLER | |
| # ======================= | |
| def analyze_input(message, history, user_id_state): | |
| user_id = get_or_create_user_id(user_id_state) | |
| text = (message.get("text") or "").strip() | |
| files = message.get("files") or [] | |
| image = None | |
| if files: | |
| image_path = files[0]["path"] | |
| image = Image.open(image_path) | |
| else: | |
| image_path = None | |
| # Save user query in Supabase | |
| insert_user_query(user_id, "Anonymous", "Unknown", text) | |
| # Retrieve top-k chunks for RAG | |
| top_chunks = get_top_k_chunks(user_id, text, k=TOP_K) | |
| # Load recent chat | |
| recent_history = get_recent_history(user_id, limit=MAX_HISTORY) | |
| # Call LLM | |
| llm_reply = vision_llm(image, text, top_chunks, recent_history) | |
| # Update chat with LLM reply | |
| update_llm_output(user_id, llm_reply) | |
| return llm_reply, user_id | |
| # ======================= | |
| # GRADIO UI | |
| # ======================= | |
| with gr.Blocks() as demo: | |
| user_id_state = gr.State(None) | |
| chatbot = gr.ChatInterface( | |
| fn=analyze_input, | |
| multimodal=True, | |
| additional_inputs=[user_id_state], | |
| additional_outputs=[user_id_state], | |
| title="InfoJury – Vision + RAG + Chat", | |
| description="Upload an image, ask a question, and the system will reason using your personal knowledge base plus vision." | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |