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)
|