Spaces:
Running
Running
| CSV_PATH = "memes_descriptions.csv" | |
| from pydantic import BaseModel, Field | |
| from enum import Enum | |
| import outlines | |
| import openai | |
| import pandas as pd | |
| import spaces | |
| from langchain.docstore.document import Document | |
| from langchain.embeddings import HuggingFaceEmbeddings | |
| from langchain.vectorstores import FAISS | |
| import gradio as gr | |
| from dotenv import load_dotenv | |
| import os | |
| load_dotenv() | |
| class Meme(BaseModel): | |
| link: str = Field(..., description="The URL of the meme") | |
| description: str = Field(..., description="The description of the meme") | |
| # --- CONFIG --- | |
| MODEL_NAME = "qwen/qwen3-32b:free" | |
| CSV_PATH = "memes_descriptions.csv" # Updated CSV_PATH to reflect the data directory | |
| API_KEY=os.getenv("OPENROUTER_API_KEY") | |
| try: | |
| client = openai.OpenAI(base_url="https://openrouter.ai/api/v1", api_key=API_KEY) | |
| model = outlines.from_openai(client, MODEL_NAME) | |
| except Exception as e: | |
| print(f"β Failed to initialize OpenRouter client: {e}") | |
| client = None | |
| # --- LOAD DATA --- | |
| def load_data_and_create_vectorstore(): | |
| global documents, vectorstore, retriever | |
| try: | |
| df = pd.read_csv(CSV_PATH).fillna({"description": "", "link": ""}) | |
| df.columns = df.columns.str.strip().str.lower() # Normalize header names | |
| documents = [ | |
| Document( | |
| page_content=row["description"], | |
| metadata={"url": str(row["link"]).strip()} | |
| ) | |
| for _, row in df.iterrows() | |
| ] | |
| embedding_model = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large-instruct") | |
| vectorstore = FAISS.from_documents(documents, embedding_model) | |
| retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) | |
| print("β Data loaded and vectorstore created.") | |
| except FileNotFoundError: | |
| print(f"β Data file not found at {CSV_PATH}") | |
| documents, vectorstore, retriever = [], None, None | |
| except Exception as e: | |
| print(f"β Error loading data or creating vectorstore: {e}") | |
| documents, vectorstore, retriever = [], None, None | |
| load_data_and_create_vectorstore() | |
| # --- LLM --- | |
| def ask_llm(question: str, docs: list) -> Meme: | |
| if client is None: | |
| return "β LLM client not initialized." | |
| context = "\n\n".join( | |
| f"Meme {i+1}:\nDescription: {doc.page_content}\nLink: {doc.metadata.get('url', 'N/A')}" | |
| for i, doc in enumerate(docs) | |
| ) | |
| messages = f"""You're a meme expert. The user will ask something, and your job is to select the most relevant meme from the following, keep the url link as it is :\n{context} \n\n === example generation === \n link='https://drive.google.com/file/d/1DrXjMZDn-fd7mJDKxZpIbYq1zuatLAQ2/view?usp=drive_link' description="This meme is perhaps the most relevant to situations where someone is trying to distance themselves from trouble, responsibility or confusion. The innocent defense, along with the exaggerated seriousness of the context, captures this human emotion perfectly. This clip is particularly versatile, since it can be used for everything from dodging blame in a work environment to the cartoonish helplessness of dealing with everyday chaos. The child's exclamation is a concise and universally understood expression of disassociation, making it an ideal visual accompaniment to anything from minor mishaps to adventurous tales of escape. It's a strong contender for embodying the act of avoiding situations with humor. The rooting for the 'underdog' (or in this case, the underkid) provides both light-hearted commentary on life's inevitable awkward moments and a shared sense of solidarity in the face of confusion." | |
| \n\n User : {question}""" | |
| print(messages) | |
| try: | |
| meme = model(messages,Meme, stream=False) | |
| print(meme) | |
| meme = Meme.model_validate_json(meme) | |
| print(meme) | |
| return meme | |
| except Exception as e: | |
| return f"β LLM Error: {e}" | |
| # --- MAIN QUERY --- | |
| def query_memes(user_input: str): | |
| if retriever is None: | |
| return "β RAG system not initialized due to errors.", "" | |
| src_docs = retriever.invoke(user_input) | |
| meme = ask_llm(user_input, src_docs) | |
| raw_url = meme.link | |
| description = meme.description | |
| file_id = raw_url.split("/file/d/")[1].split("/")[0] | |
| embed_url = f"https://drive.google.com/file/d/{file_id}/preview" | |
| embed_html = f''' | |
| <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; border-radius: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); margin-top: 1em;"> | |
| <iframe src="{embed_url}" | |
| style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" | |
| frameborder="0" allow="autoplay" allowfullscreen> | |
| </iframe> | |
| </div> | |
| ''' | |
| return embed_html, description | |
| # --- UPLOAD FUNCTION --- | |
| def upload_meme(video_file, description): | |
| if video_file is None or description is None: | |
| return "Please provide both a video file and a description." | |
| try: | |
| # This is a placeholder. In a real application, you would upload the video | |
| # to a hosting service (like Google Drive, S3, etc.) and get a shareable link. | |
| # For this example, we'll just create a dummy link. | |
| # You might need to implement actual file upload logic here. | |
| dummy_link = f"https://dummy-hosting.com/videos/{os.path.basename(video_file.name)}" | |
| new_meme = pd.DataFrame({"link": [dummy_link], "description": [description]}) | |
| # Append to the CSV file | |
| new_meme.to_csv(CSV_PATH, mode='a', header=False, index=False) | |
| # Reload data and update vectorstore | |
| load_data_and_create_vectorstore() | |
| return "Meme uploaded successfully!" | |
| except Exception as e: | |
| return f"Error uploading meme: {e}" | |
| # --- GRADIO INTERFACE --- | |
| if __name__ == "__main__": | |
| if retriever is None or client is None: | |
| print("Gradio interface will not run due to RAG/LLM initialization errors.") | |
| else: | |
| with gr.Blocks(title="Moul Lmemes π¬") as demo: | |
| gr.Markdown("# Moul Lmemes π¬") | |
| gr.Markdown("Write what you are looking for and i will find the most revelant moroccan meme for you π") | |
| with gr.Tab("Search Memes"): | |
| search_input = gr.Textbox(label="Type something") | |
| search_button = gr.Button("Find Meme") | |
| search_output_video = gr.HTML(label="Top Meme Video") | |
| search_output_text = gr.Textbox(label="Results") | |
| search_button.click( | |
| fn=query_memes, | |
| inputs=search_input, | |
| outputs=[search_output_video, search_output_text] | |
| ) | |
| gr.Examples( | |
| examples=[["a man running"],["sharing"],["immigrant in france"]], | |
| inputs=search_input, | |
| outputs=[search_output_video, search_output_text], | |
| fn=query_memes, | |
| cache_examples=True, | |
| ) | |
| with gr.Tab("Upload Meme"): | |
| upload_video = gr.Video(label="Upload Video") | |
| upload_description = gr.Textbox(label="Description") | |
| upload_button = gr.Button("Upload Meme") | |
| upload_output = gr.Textbox(label="Upload Status") | |
| upload_button.click( | |
| fn=upload_meme, | |
| inputs=[upload_video, upload_description], | |
| outputs=upload_output | |
| ) | |
| demo.launch(debug=True) |