Upload app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
import json
|
| 4 |
+
import gradio as gr
|
| 5 |
+
import requests
|
| 6 |
+
from groq import Groq
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
from io import BytesIO
|
| 9 |
+
from PIL import Image
|
| 10 |
+
|
| 11 |
+
load_dotenv()
|
| 12 |
+
|
| 13 |
+
# Setup clients and credentials
|
| 14 |
+
IMGFLIP_USERNAME = os.getenv("IMGFLIP_USERNAME")
|
| 15 |
+
IMGFLIP_PASSWORD = os.getenv("IMGFLIP_PASSWORD")
|
| 16 |
+
client = Groq(api_key=os.getenv("GROQ_API_KEY"))
|
| 17 |
+
|
| 18 |
+
# Load memes from Imgflip API
|
| 19 |
+
def list_memes():
|
| 20 |
+
# r = requests.get("https://api.imgflip.com/get_memes")
|
| 21 |
+
# data = r.json()
|
| 22 |
+
# if not data["success"]:
|
| 23 |
+
# return {}
|
| 24 |
+
data = json.loads(open("./metadata.json","r").read())
|
| 25 |
+
return {meme["name"]: {"id": meme["id"], "url": meme["url"]} for meme in data["data"]["memes"]}
|
| 26 |
+
|
| 27 |
+
MEME_DATABASE = list_memes()
|
| 28 |
+
|
| 29 |
+
# Llama-4-Scout selects the meme template + captions
|
| 30 |
+
def pick_meme_and_params(prompt: str) -> dict:
|
| 31 |
+
meme_names = list(MEME_DATABASE.keys())
|
| 32 |
+
meme_list_string = "\n".join(f"- {name}" for name in meme_names)
|
| 33 |
+
|
| 34 |
+
system_message = "You are a JSON-output assistant that selects the best meme template and generates top and bottom captions."
|
| 35 |
+
user_message = (
|
| 36 |
+
f"Here is the list of available meme templates:\n{meme_list_string}\n\n"
|
| 37 |
+
f"Based on this list, choose the most suitable meme template for the prompt: '{prompt}'.\n\n"
|
| 38 |
+
f"Return your answer in this JSON format:\n"
|
| 39 |
+
"{\n"
|
| 40 |
+
"\"template_name\": \"template name from the list\",\n"
|
| 41 |
+
"\"top_text\": \"top caption\",\n"
|
| 42 |
+
"\"bottom_text\": \"bottom caption\",\n"
|
| 43 |
+
"\"font\": \"optional font (default to impact)\",\n"
|
| 44 |
+
"\"max_font_size\": integer between 10 and 100 (optional)\n"
|
| 45 |
+
"}"
|
| 46 |
+
"Only return JSON, not extra information, no delimiter, only JSON"
|
| 47 |
+
)
|
| 48 |
+
print("preresponse")
|
| 49 |
+
response = client.chat.completions.create(
|
| 50 |
+
model="meta-llama/llama-4-scout-17b-16e-instruct",
|
| 51 |
+
messages=[{"role": "system", "content": system_message}, {"role": "user", "content": user_message}],
|
| 52 |
+
# response_format="json"
|
| 53 |
+
)
|
| 54 |
+
# print(response.choices[0].message.content)
|
| 55 |
+
json_response = json.loads(response.choices[0].message.content)
|
| 56 |
+
return json_response
|
| 57 |
+
|
| 58 |
+
# Call Imgflip API to generate the meme
|
| 59 |
+
# def make_meme(config: dict) -> str:
|
| 60 |
+
# template = MEME_DATABASE.get(config["template_name"])
|
| 61 |
+
# if not template:
|
| 62 |
+
# return f"Error: Meme template '{config['template_name']}' not found."
|
| 63 |
+
|
| 64 |
+
# payload = {
|
| 65 |
+
# "template_id": template["id"],
|
| 66 |
+
# "username": IMGFLIP_USERNAME,
|
| 67 |
+
# "password": IMGFLIP_PASSWORD,
|
| 68 |
+
# "text0": config["top_text"],
|
| 69 |
+
# "text1": config["bottom_text"]
|
| 70 |
+
# }
|
| 71 |
+
|
| 72 |
+
# if config.get("font"):
|
| 73 |
+
# payload["font"] = config["font"]
|
| 74 |
+
# if config.get("max_font_size"):
|
| 75 |
+
# payload["max_font_size"] = config["max_font_size"]
|
| 76 |
+
|
| 77 |
+
# r = requests.post("https://api.imgflip.com/caption_image", data=payload)
|
| 78 |
+
# d = r.json()
|
| 79 |
+
# return d["data"]["url"] if d.get("success") else f"Error: {d.get('error_message')}"
|
| 80 |
+
|
| 81 |
+
def make_meme(config: dict):
|
| 82 |
+
template = MEME_DATABASE.get(config["template_name"])
|
| 83 |
+
if not template:
|
| 84 |
+
raise ValueError(f"Meme template '{config['template_name']}' not found.")
|
| 85 |
+
|
| 86 |
+
payload = {
|
| 87 |
+
"template_id": template["id"],
|
| 88 |
+
"username": IMGFLIP_USERNAME,
|
| 89 |
+
"password": IMGFLIP_PASSWORD,
|
| 90 |
+
"text0": config["top_text"],
|
| 91 |
+
"text1": config["bottom_text"]
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
if config.get("font"):
|
| 95 |
+
payload["font"] = config["font"]
|
| 96 |
+
if config.get("max_font_size"):
|
| 97 |
+
payload["max_font_size"] = config["max_font_size"]
|
| 98 |
+
|
| 99 |
+
r = requests.post("https://api.imgflip.com/caption_image", data=payload)
|
| 100 |
+
d = r.json()
|
| 101 |
+
print(d)
|
| 102 |
+
# with open("response.json","w") as f:
|
| 103 |
+
# json.dump(d,f)
|
| 104 |
+
if not d.get("success"):
|
| 105 |
+
raise ValueError(f"Error from Imgflip API: {d.get('error_message')}")
|
| 106 |
+
|
| 107 |
+
meme_url = d["data"]["url"]
|
| 108 |
+
|
| 109 |
+
# Fetch the generated image from the returned URL
|
| 110 |
+
img_response = requests.get(meme_url)
|
| 111 |
+
if img_response.status_code != 200:
|
| 112 |
+
raise ValueError("Failed to fetch the generated meme image.")
|
| 113 |
+
|
| 114 |
+
# Convert image bytes to PIL image for Gradio
|
| 115 |
+
image = Image.open(BytesIO(img_response.content))
|
| 116 |
+
image.save("./img.png")
|
| 117 |
+
return image
|
| 118 |
+
|
| 119 |
+
# Full meme generation pipeline
|
| 120 |
+
def generate_meme(prompt):
|
| 121 |
+
try:
|
| 122 |
+
config = pick_meme_and_params(prompt)
|
| 123 |
+
print(type(config))
|
| 124 |
+
print(config)
|
| 125 |
+
return make_meme(config)
|
| 126 |
+
except Exception as e:
|
| 127 |
+
return f"Error generating meme: {e}"
|
| 128 |
+
|
| 129 |
+
# Gradio + MCP interface
|
| 130 |
+
with gr.Blocks() as demo:
|
| 131 |
+
gr.Markdown("# 🖼️ Meme Comtext Protocol Tool")
|
| 132 |
+
prompt_in = gr.Textbox(label="Enter your meme idea")
|
| 133 |
+
out_img = gr.Image(label="Generated Meme")
|
| 134 |
+
btn = gr.Button("Generate Meme")
|
| 135 |
+
|
| 136 |
+
btn.click(fn=generate_meme, inputs=prompt_in, outputs=out_img)
|
| 137 |
+
|
| 138 |
+
if __name__ == "__main__":
|
| 139 |
+
demo.launch(mcp_server=True)
|