Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -2,128 +2,54 @@ import os
|
|
2 |
import random
|
3 |
import uuid
|
4 |
import base64
|
|
|
|
|
5 |
import gradio as gr
|
6 |
import numpy as np
|
7 |
-
|
8 |
-
import spaces
|
9 |
import torch
|
10 |
-
import
|
11 |
from datetime import datetime
|
12 |
-
import pandas as pd
|
13 |
-
import json
|
14 |
-
import re
|
15 |
-
|
16 |
from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
|
|
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
"""
|
22 |
-
|
23 |
-
# Global variables
|
24 |
-
image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
|
25 |
LIKES_CACHE_FILE = "likes_cache.json"
|
|
|
|
|
|
|
|
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
30 |
return json.load(f)
|
31 |
return {}
|
32 |
|
33 |
-
def
|
34 |
-
with open(
|
35 |
-
json.dump(
|
36 |
-
|
37 |
-
likes_cache = load_likes_cache()
|
38 |
-
|
39 |
-
def create_download_link(filename):
|
40 |
-
with open(filename, "rb") as file:
|
41 |
-
encoded_string = base64.b64encode(file.read()).decode('utf-8')
|
42 |
-
download_link = f'<a href="data:image/png;base64,{encoded_string}" download="{filename}">Download Image</a>'
|
43 |
-
return download_link
|
44 |
-
|
45 |
-
def save_image(img, prompt):
|
46 |
-
global image_metadata, likes_cache
|
47 |
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
48 |
-
safe_prompt = re.sub(r'[^\w\s-]', '', prompt.lower())[:50] # Limit to 50 characters
|
49 |
-
safe_prompt = re.sub(r'[-\s]+', '-', safe_prompt).strip('-')
|
50 |
-
filename = f"{timestamp}_{safe_prompt}.png"
|
51 |
-
img.save(filename)
|
52 |
-
new_row = pd.DataFrame({
|
53 |
-
'Filename': [filename],
|
54 |
-
'Prompt': [prompt],
|
55 |
-
'Likes': [0],
|
56 |
-
'Dislikes': [0],
|
57 |
-
'Hearts': [0],
|
58 |
-
'Created': [datetime.now()]
|
59 |
-
})
|
60 |
-
image_metadata = pd.concat([image_metadata, new_row], ignore_index=True)
|
61 |
-
likes_cache[filename] = {'likes': 0, 'dislikes': 0, 'hearts': 0}
|
62 |
-
save_likes_cache(likes_cache)
|
63 |
-
return filename
|
64 |
-
|
65 |
-
def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
|
66 |
-
if randomize_seed:
|
67 |
-
seed = random.randint(0, MAX_SEED)
|
68 |
-
return seed
|
69 |
-
|
70 |
-
def get_image_gallery():
|
71 |
-
global image_metadata
|
72 |
-
image_files = image_metadata['Filename'].tolist()
|
73 |
-
return [(file, get_image_caption(file)) for file in image_files if os.path.exists(file)]
|
74 |
-
|
75 |
-
def get_image_caption(filename):
|
76 |
-
global likes_cache, image_metadata
|
77 |
-
if filename in likes_cache:
|
78 |
-
likes = likes_cache[filename]['likes']
|
79 |
-
dislikes = likes_cache[filename]['dislikes']
|
80 |
-
hearts = likes_cache[filename]['hearts']
|
81 |
-
prompt = image_metadata[image_metadata['Filename'] == filename]['Prompt'].values[0]
|
82 |
-
return f"{filename}\nPrompt: {prompt}\n👍 {likes} 👎 {dislikes} ❤️ {hearts}"
|
83 |
-
return filename
|
84 |
-
|
85 |
-
def delete_all_images():
|
86 |
-
global image_metadata, likes_cache
|
87 |
-
for file in image_metadata['Filename']:
|
88 |
-
if os.path.exists(file):
|
89 |
-
os.remove(file)
|
90 |
-
image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
|
91 |
-
likes_cache = {}
|
92 |
-
save_likes_cache(likes_cache)
|
93 |
-
return get_image_gallery(), image_metadata.values.tolist()
|
94 |
-
|
95 |
-
def delete_image(filename):
|
96 |
-
global image_metadata, likes_cache
|
97 |
-
if filename and os.path.exists(filename):
|
98 |
-
os.remove(filename)
|
99 |
-
image_metadata = image_metadata[image_metadata['Filename'] != filename]
|
100 |
-
if filename in likes_cache:
|
101 |
-
del likes_cache[filename]
|
102 |
-
save_likes_cache(likes_cache)
|
103 |
-
return get_image_gallery(), image_metadata.values.tolist()
|
104 |
-
|
105 |
-
def vote(filename, vote_type):
|
106 |
-
global likes_cache
|
107 |
-
if filename in likes_cache:
|
108 |
-
likes_cache[filename][vote_type.lower()] += 1
|
109 |
-
save_likes_cache(likes_cache)
|
110 |
-
return get_image_gallery(), image_metadata.values.tolist()
|
111 |
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
"Pop Art", "Minimalist", "Baroque", "Art Nouveau", "Pointillist", "Fauvism"
|
116 |
-
]
|
117 |
-
return random.choice(styles)
|
118 |
|
119 |
-
|
|
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
|
|
|
|
|
|
|
|
|
127 |
if torch.cuda.is_available():
|
128 |
pipe = StableDiffusionXLPipeline.from_pretrained(
|
129 |
"fluently/Fluently-XL-v4",
|
@@ -131,26 +57,60 @@ if torch.cuda.is_available():
|
|
131 |
use_safetensors=True,
|
132 |
)
|
133 |
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
134 |
-
|
135 |
pipe.load_lora_weights("ehristoforu/dalle-3-xl-v2", weight_name="dalle-3-xl-lora-v2.safetensors", adapter_name="dalle")
|
136 |
pipe.set_adapters("dalle")
|
137 |
-
|
138 |
pipe.to("cuda")
|
139 |
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
):
|
152 |
-
|
|
|
153 |
|
|
|
154 |
if not use_negative_prompt:
|
155 |
negative_prompt = ""
|
156 |
|
@@ -165,185 +125,183 @@ def generate(
|
|
165 |
cross_attention_kwargs={"scale": 0.65},
|
166 |
output_type="pil",
|
167 |
).images
|
168 |
-
image_paths = [save_image(img, prompt) for img in images]
|
169 |
-
download_links = [create_download_link(path) for path in image_paths]
|
170 |
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
examples = [
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
f"{get_random_style()} scene viewed through an ornate window frame. Contrast the intricate details of the window with a dreamy, soft-focus landscape beyond, using a palette that transitions from cool interior tones to warm exterior hues.",
|
178 |
-
f"{get_random_style()} close-up study of interlaced fingers. Use a monochromatic color scheme to emphasize the form and texture of the hands, with dramatic lighting to create depth and emotion.",
|
179 |
-
f"{get_random_style()} composition featuring a set of dice in motion. Capture the energy and randomness of the throw, using a dynamic color palette and blurred lines to convey movement.",
|
180 |
-
f"{get_random_style()} interpretation of heaven. Create an ethereal atmosphere with soft, billowing clouds and radiant light, using a palette of celestial blues, golds, and whites.",
|
181 |
-
f"{get_random_style()} portrayal of an ancient, mystical gate. Combine architectural details with elements of fantasy, using a rich, jewel-toned palette to create an air of mystery and magic.",
|
182 |
-
f"{get_random_style()} portrait of a curious cat. Focus on capturing the feline's expressive eyes and sleek form, using a mix of bold and subtle colors to bring out the cat's personality.",
|
183 |
-
f"{get_random_style()} abstract representation of toes in sand. Use textured brushstrokes to convey the feeling of warm sand, with a palette inspired by a sun-drenched beach."
|
184 |
]
|
185 |
|
186 |
-
css
|
187 |
-
.gradio-container{max-width: 1024px !important}
|
188 |
-
h1{text-align:center}
|
189 |
-
footer {
|
190 |
-
visibility: hidden
|
191 |
-
}
|
192 |
-
'''
|
193 |
-
|
194 |
-
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
195 |
gr.Markdown(DESCRIPTION)
|
196 |
|
197 |
with gr.Tab("Generate Images"):
|
198 |
-
with gr.Group():
|
199 |
-
with gr.Row():
|
200 |
-
prompt = gr.Text(
|
201 |
-
label="Prompt",
|
202 |
-
show_label=False,
|
203 |
-
max_lines=1,
|
204 |
-
placeholder="Enter your prompt",
|
205 |
-
container=False,
|
206 |
-
)
|
207 |
-
run_button = gr.Button("Run", scale=0)
|
208 |
-
result = gr.Gallery(label="Result", columns=1, preview=True, show_label=False)
|
209 |
-
with gr.Accordion("Advanced options", open=False):
|
210 |
-
use_negative_prompt = gr.Checkbox(label="Use negative prompt", value=True)
|
211 |
-
negative_prompt = gr.Text(
|
212 |
-
label="Negative prompt",
|
213 |
-
lines=4,
|
214 |
-
max_lines=6,
|
215 |
-
value="""(deformed, distorted, disfigured:1.3), poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, (mutated hands and fingers:1.4), disconnected limbs, mutation, mutated, ugly, disgusting, blurry, amputation, (NSFW:1.25)""",
|
216 |
-
placeholder="Enter a negative prompt",
|
217 |
-
visible=True,
|
218 |
-
)
|
219 |
-
seed = gr.Slider(
|
220 |
-
label="Seed",
|
221 |
-
minimum=0,
|
222 |
-
maximum=MAX_SEED,
|
223 |
-
step=1,
|
224 |
-
value=0,
|
225 |
-
visible=True
|
226 |
-
)
|
227 |
-
randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
|
228 |
-
with gr.Row(visible=True):
|
229 |
-
width = gr.Slider(
|
230 |
-
label="Width",
|
231 |
-
minimum=512,
|
232 |
-
maximum=2048,
|
233 |
-
step=8,
|
234 |
-
value=1920,
|
235 |
-
)
|
236 |
-
height = gr.Slider(
|
237 |
-
label="Height",
|
238 |
-
minimum=512,
|
239 |
-
maximum=2048,
|
240 |
-
step=8,
|
241 |
-
value=1080,
|
242 |
-
)
|
243 |
-
with gr.Row():
|
244 |
-
guidance_scale = gr.Slider(
|
245 |
-
label="Guidance Scale",
|
246 |
-
minimum=0.1,
|
247 |
-
maximum=20.0,
|
248 |
-
step=0.1,
|
249 |
-
value=20.0,
|
250 |
-
)
|
251 |
-
|
252 |
-
gr.Examples(
|
253 |
-
examples=examples,
|
254 |
-
inputs=prompt,
|
255 |
-
outputs=[result, seed],
|
256 |
-
fn=generate,
|
257 |
-
cache_examples=False,
|
258 |
-
)
|
259 |
-
|
260 |
-
with gr.Tab("Gallery and Voting"):
|
261 |
-
image_gallery = gr.Gallery(label="Generated Images", show_label=True, columns=4, height="auto")
|
262 |
-
|
263 |
with gr.Row():
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
|
271 |
-
with gr.Tab("
|
|
|
272 |
metadata_df = gr.Dataframe(
|
273 |
label="Image Metadata",
|
274 |
headers=["Filename", "Prompt", "Likes", "Dislikes", "Hearts", "Created"],
|
275 |
interactive=False
|
276 |
)
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
fn=
|
288 |
-
inputs=[]
|
289 |
-
outputs=[image_gallery, metadata_df]
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
fn=
|
300 |
-
|
301 |
-
|
302 |
-
)
|
303 |
|
304 |
-
|
305 |
-
|
306 |
-
inputs=[selected_image],
|
307 |
-
outputs=[image_gallery, metadata_df],
|
308 |
-
)
|
309 |
|
310 |
-
heart_button.click(
|
311 |
-
fn=lambda x: vote(x, 'hearts'),
|
312 |
-
inputs=[selected_image],
|
313 |
-
outputs=[image_gallery, metadata_df],
|
314 |
-
)
|
315 |
|
316 |
-
delete_image_button.click(
|
317 |
-
fn=delete_image,
|
318 |
-
inputs=[selected_image],
|
319 |
-
outputs=[image_gallery, metadata_df],
|
320 |
-
)
|
321 |
|
322 |
-
def update_gallery_and_metadata():
|
323 |
-
return gr.update(value=get_image_gallery()), gr.update(value=image_metadata.values.tolist())
|
324 |
-
|
325 |
-
gr.on(
|
326 |
-
triggers=[
|
327 |
-
prompt.submit,
|
328 |
-
negative_prompt.submit,
|
329 |
-
run_button.click,
|
330 |
-
],
|
331 |
-
fn=generate,
|
332 |
-
inputs=[
|
333 |
-
prompt,
|
334 |
-
negative_prompt,
|
335 |
-
use_negative_prompt,
|
336 |
-
seed,
|
337 |
-
width,
|
338 |
-
height,
|
339 |
-
guidance_scale,
|
340 |
-
randomize_seed,
|
341 |
-
],
|
342 |
-
outputs=[result, seed, gr.HTML(visible=False), image_gallery, metadata_df],
|
343 |
-
api_name="run",
|
344 |
-
)
|
345 |
|
346 |
-
demo.load(fn=update_gallery_and_metadata, outputs=[image_gallery, metadata_df])
|
347 |
|
348 |
-
if __name__ == "__main__":
|
349 |
-
demo.queue(max_size=20).launch(share=True, debug=False)
|
|
|
2 |
import random
|
3 |
import uuid
|
4 |
import base64
|
5 |
+
import json
|
6 |
+
import re
|
7 |
import gradio as gr
|
8 |
import numpy as np
|
9 |
+
import pandas as pd
|
|
|
10 |
import torch
|
11 |
+
from PIL import Image
|
12 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
13 |
from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
|
14 |
+
import anthropic
|
15 |
|
16 |
+
# ============================================================
|
17 |
+
# === GLOBALS & DATA STORAGE FILES
|
18 |
+
# ============================================================
|
|
|
|
|
|
|
|
|
19 |
LIKES_CACHE_FILE = "likes_cache.json"
|
20 |
+
LOG_CACHE_FILE = "log_cache.json"
|
21 |
+
QUOTE_CACHE_FILE = "quotes_cache.json"
|
22 |
+
|
23 |
+
STATIC_URL_PREFIX = "https://huggingface.co/spaces/awacke1/dalle-3-xl-lora-v2/file="
|
24 |
|
25 |
+
# Initialize caches / load from JSON
|
26 |
+
def load_json(file):
|
27 |
+
if os.path.exists(file):
|
28 |
+
with open(file, 'r', encoding='utf-8') as f:
|
29 |
return json.load(f)
|
30 |
return {}
|
31 |
|
32 |
+
def save_json(file, data):
|
33 |
+
with open(file, 'w', encoding='utf-8') as f:
|
34 |
+
json.dump(data, f, indent=4)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
+
likes_cache = load_json(LIKES_CACHE_FILE) or {}
|
37 |
+
chat_logs = load_json(LOG_CACHE_FILE) if os.path.exists(LOG_CACHE_FILE) else []
|
38 |
+
quotes = load_json(QUOTE_CACHE_FILE) if os.path.exists(QUOTE_CACHE_FILE) else []
|
|
|
|
|
|
|
39 |
|
40 |
+
# DataFrame for images
|
41 |
+
image_metadata = pd.DataFrame(columns=['Filename','Prompt','Likes','Dislikes','Hearts','Created'])
|
42 |
|
43 |
+
# ============================================================
|
44 |
+
# === ANTHROPIC CLIENT (Claude)
|
45 |
+
# ============================================================
|
46 |
+
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY", None)
|
47 |
+
claude_client = anthropic.Anthropic(api_key=anthropic_api_key) if anthropic_api_key else None
|
48 |
|
49 |
+
# ============================================================
|
50 |
+
# === IMAGE PIPELINE
|
51 |
+
# ============================================================
|
52 |
+
pipe = None
|
53 |
if torch.cuda.is_available():
|
54 |
pipe = StableDiffusionXLPipeline.from_pretrained(
|
55 |
"fluently/Fluently-XL-v4",
|
|
|
57 |
use_safetensors=True,
|
58 |
)
|
59 |
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
|
|
60 |
pipe.load_lora_weights("ehristoforu/dalle-3-xl-v2", weight_name="dalle-3-xl-lora-v2.safetensors", adapter_name="dalle")
|
61 |
pipe.set_adapters("dalle")
|
|
|
62 |
pipe.to("cuda")
|
63 |
|
64 |
+
MAX_SEED = np.iinfo(np.int32).max
|
65 |
+
|
66 |
+
# ============================================================
|
67 |
+
# === HELPER FUNCTIONS
|
68 |
+
# ============================================================
|
69 |
+
def randomize_seed_fn(seed: int, randomize_seed: bool):
|
70 |
+
if randomize_seed:
|
71 |
+
seed = random.randint(0, MAX_SEED)
|
72 |
+
return int(seed)
|
73 |
+
|
74 |
+
def sanitize_prompt(prompt):
|
75 |
+
return re.sub(r'[^\w\s-]', '', prompt.lower())[:50]
|
76 |
+
|
77 |
+
def save_image_locally(img, prompt):
|
78 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
79 |
+
safe_prompt = sanitize_prompt(prompt)
|
80 |
+
filename = f"{timestamp}_{safe_prompt}.png"
|
81 |
+
img.save(filename)
|
82 |
+
if filename not in likes_cache:
|
83 |
+
likes_cache[filename] = {'likes': 0, 'dislikes': 0, 'hearts': 0}
|
84 |
+
save_json(LIKES_CACHE_FILE, likes_cache)
|
85 |
+
global image_metadata
|
86 |
+
new_row = {
|
87 |
+
'Filename': filename,
|
88 |
+
'Prompt': prompt,
|
89 |
+
'Likes': 0,
|
90 |
+
'Dislikes': 0,
|
91 |
+
'Hearts': 0,
|
92 |
+
'Created': str(datetime.now())
|
93 |
+
}
|
94 |
+
image_metadata = pd.concat([image_metadata, pd.DataFrame([new_row])], ignore_index=True)
|
95 |
+
return filename
|
96 |
+
|
97 |
+
def log_input_output(user_input, model_output, link=""):
|
98 |
+
global chat_logs
|
99 |
+
chat_logs.append({
|
100 |
+
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
101 |
+
"input": user_input,
|
102 |
+
"output": model_output,
|
103 |
+
"file_link": link
|
104 |
+
})
|
105 |
+
save_json(LOG_CACHE_FILE, chat_logs)
|
106 |
+
|
107 |
+
def generate_image(
|
108 |
+
prompt, negative_prompt, use_negative_prompt, seed, width, height, guidance_scale, randomize_seed
|
109 |
):
|
110 |
+
if pipe is None:
|
111 |
+
return ["No GPU available, cannot generate images."], 0, [], [], []
|
112 |
|
113 |
+
seed = randomize_seed_fn(seed, randomize_seed)
|
114 |
if not use_negative_prompt:
|
115 |
negative_prompt = ""
|
116 |
|
|
|
125 |
cross_attention_kwargs={"scale": 0.65},
|
126 |
output_type="pil",
|
127 |
).images
|
|
|
|
|
128 |
|
129 |
+
filenames = []
|
130 |
+
for img in images:
|
131 |
+
fname = save_image_locally(img, prompt)
|
132 |
+
filenames.append(fname)
|
133 |
+
|
134 |
+
links = [f"{STATIC_URL_PREFIX}{f}" for f in filenames]
|
135 |
+
# Log the generation
|
136 |
+
log_input_output(user_input=prompt, model_output="(image generated)", link=", ".join(links))
|
137 |
+
|
138 |
+
# Return Gradio objects
|
139 |
+
return filenames, seed, links, get_image_gallery(), image_metadata.values.tolist()
|
140 |
+
|
141 |
+
def get_image_gallery():
|
142 |
+
return [
|
143 |
+
(row["Filename"], f"{row['Filename']}\nPrompt: {row['Prompt']}\n👍 {row['Likes']} 👎 {row['Dislikes']} ❤️ {row['Hearts']}")
|
144 |
+
for _, row in image_metadata.iterrows()
|
145 |
+
if os.path.exists(row["Filename"])
|
146 |
+
]
|
147 |
+
|
148 |
+
def vote_image(filename, vote_type):
|
149 |
+
if filename and filename in likes_cache:
|
150 |
+
likes_cache[filename][vote_type] += 1
|
151 |
+
save_json(LIKES_CACHE_FILE, likes_cache)
|
152 |
+
idx = image_metadata.index[image_metadata['Filename'] == filename]
|
153 |
+
if not idx.empty:
|
154 |
+
image_metadata.at[idx, vote_type.capitalize()] = image_metadata.at[idx, vote_type.capitalize()] + 1
|
155 |
+
return get_image_gallery(), image_metadata.values.tolist()
|
156 |
+
|
157 |
+
def delete_image(filename):
|
158 |
+
if filename and os.path.exists(filename):
|
159 |
+
os.remove(filename)
|
160 |
+
if filename in likes_cache:
|
161 |
+
del likes_cache[filename]
|
162 |
+
save_json(LIKES_CACHE_FILE, likes_cache)
|
163 |
+
global image_metadata
|
164 |
+
image_metadata = image_metadata[image_metadata['Filename'] != filename]
|
165 |
+
return get_image_gallery(), image_metadata.values.tolist()
|
166 |
+
|
167 |
+
def delete_all_images():
|
168 |
+
global image_metadata, likes_cache
|
169 |
+
for f in image_metadata["Filename"].tolist():
|
170 |
+
if os.path.exists(f):
|
171 |
+
os.remove(f)
|
172 |
+
image_metadata = pd.DataFrame(columns=['Filename','Prompt','Likes','Dislikes','Hearts','Created'])
|
173 |
+
likes_cache.clear()
|
174 |
+
save_json(LIKES_CACHE_FILE, likes_cache)
|
175 |
+
return get_image_gallery(), image_metadata.values.tolist()
|
176 |
+
|
177 |
+
# === QUOTES Demo (Optional) ===
|
178 |
+
def add_quote(q):
|
179 |
+
if q.strip():
|
180 |
+
quotes.append({
|
181 |
+
"text": q,
|
182 |
+
"likes": 0,
|
183 |
+
"created": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
184 |
+
})
|
185 |
+
save_json(QUOTE_CACHE_FILE, quotes)
|
186 |
+
return [[idx, itm["text"], itm["likes"], itm["created"]] for idx, itm in enumerate(quotes)]
|
187 |
+
|
188 |
+
def like_quote(idx):
|
189 |
+
if 0 <= idx < len(quotes):
|
190 |
+
quotes[idx]["likes"] += 1
|
191 |
+
save_json(QUOTE_CACHE_FILE, quotes)
|
192 |
+
return [[i, itm["text"], itm["likes"], itm["created"]] for i, itm in enumerate(quotes)]
|
193 |
+
|
194 |
+
# === CLAUDE Chat ===
|
195 |
+
def chat_claude(user_message):
|
196 |
+
if not claude_client:
|
197 |
+
return "No Anthropic API key configured."
|
198 |
+
if not user_message.strip():
|
199 |
+
return "Empty message."
|
200 |
+
resp = claude_client.messages.create(
|
201 |
+
model="claude-3-sonnet-20240229",
|
202 |
+
max_tokens=1000,
|
203 |
+
messages=[{"role": "user", "content": user_message}],
|
204 |
+
)
|
205 |
+
text = resp.content[0].text
|
206 |
+
log_input_output(user_input=user_message, model_output=text, link="")
|
207 |
+
return text
|
208 |
+
|
209 |
+
# === Refresh gallery + DF
|
210 |
+
def refresh_gallery_and_df():
|
211 |
+
return gr.update(value=get_image_gallery()), gr.update(value=image_metadata.values.tolist())
|
212 |
+
|
213 |
+
# ============================================================
|
214 |
+
# === BUILD GRADIO UI
|
215 |
+
# ============================================================
|
216 |
+
DESCRIPTION = """# 🎨 ArtForge & Claude Chat
|
217 |
+
Generate AI art, chat with Claude, log everything, and vote on images.
|
218 |
+
"""
|
219 |
|
220 |
examples = [
|
221 |
+
"Futuristic cityscape in neon lighting",
|
222 |
+
"Cute cat wearing a wizard hat",
|
223 |
+
"Surreal landscape with floating islands",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
]
|
225 |
|
226 |
+
with gr.Blocks(css=".gradio-container {max-width: 1024px !important}") as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
gr.Markdown(DESCRIPTION)
|
228 |
|
229 |
with gr.Tab("Generate Images"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
with gr.Row():
|
231 |
+
prompt = gr.Text(label="Prompt", max_lines=1)
|
232 |
+
run_button = gr.Button("Run")
|
233 |
+
result = gr.Gallery(label="Result", columns=1, preview=True)
|
234 |
+
use_negative_prompt = gr.Checkbox(label="Use negative prompt", value=True)
|
235 |
+
negative_prompt = gr.Text(
|
236 |
+
label="Negative prompt",
|
237 |
+
lines=3,
|
238 |
+
value="(deformed, distorted:1.3), poorly drawn, bad anatomy",
|
239 |
+
)
|
240 |
+
seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
|
241 |
+
randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
|
242 |
+
width = gr.Slider(label="Width", minimum=512, maximum=2048, step=64, value=1024)
|
243 |
+
height = gr.Slider(label="Height", minimum=512, maximum=2048, step=64, value=1024)
|
244 |
+
guidance_scale = gr.Slider(label="Guidance Scale", minimum=1, maximum=20, step=0.5, value=7)
|
245 |
+
run_button.click(
|
246 |
+
fn=generate_image,
|
247 |
+
inputs=[prompt, negative_prompt, use_negative_prompt, seed, width, height, guidance_scale, randomize_seed],
|
248 |
+
outputs=[result, seed, gr.HTML(visible=False), gr.Gallery(), gr.Dataframe()],
|
249 |
+
api_name="run"
|
250 |
+
)
|
251 |
+
gr.Examples(examples=examples, inputs=prompt)
|
252 |
+
|
253 |
+
with gr.Tab("Chat with Claude"):
|
254 |
+
claude_input = gr.Textbox(label="Your Message")
|
255 |
+
claude_output = gr.Textbox(label="Claude's Reply", lines=4)
|
256 |
+
send_claude = gr.Button("Send to Claude")
|
257 |
+
send_claude.click(chat_claude, inputs=claude_input, outputs=claude_output)
|
258 |
+
|
259 |
+
with gr.Tab("Logs & Management"):
|
260 |
+
with gr.Accordion("All Logs", open=False):
|
261 |
+
logs_data = gr.Dataframe(
|
262 |
+
value=pd.DataFrame(chat_logs),
|
263 |
+
label="Input/Output Logs",
|
264 |
+
interactive=False,
|
265 |
+
wrap=True
|
266 |
+
)
|
267 |
|
268 |
+
with gr.Tab("Gallery & Voting"):
|
269 |
+
image_gallery = gr.Gallery(label="Generated Images", columns=4)
|
270 |
metadata_df = gr.Dataframe(
|
271 |
label="Image Metadata",
|
272 |
headers=["Filename", "Prompt", "Likes", "Dislikes", "Hearts", "Created"],
|
273 |
interactive=False
|
274 |
)
|
275 |
+
selected_image = gr.State()
|
276 |
+
with gr.Row():
|
277 |
+
like_button = gr.Button("👍 Like")
|
278 |
+
dislike_button = gr.Button("👎 Dislike")
|
279 |
+
heart_button = gr.Button("❤️ Heart")
|
280 |
+
delete_image_button = gr.Button("🗑️ Delete Image")
|
281 |
+
delete_all_button = gr.Button("🗑️ Delete All")
|
282 |
+
image_gallery.select(fn=lambda evt: evt, inputs=[], outputs=[selected_image])
|
283 |
+
like_button.click(fn=lambda x: vote_image(x, 'likes'), inputs=selected_image, outputs=[image_gallery, metadata_df])
|
284 |
+
dislike_button.click(fn=lambda x: vote_image(x, 'dislikes'), inputs=selected_image, outputs=[image_gallery, metadata_df])
|
285 |
+
heart_button.click(fn=lambda x: vote_image(x, 'hearts'), inputs=selected_image, outputs=[image_gallery, metadata_df])
|
286 |
+
delete_image_button.click(fn=delete_image, inputs=selected_image, outputs=[image_gallery, metadata_df])
|
287 |
+
delete_all_button.click(fn=delete_all_images, outputs=[image_gallery, metadata_df])
|
288 |
+
|
289 |
+
with gr.Tab("Quotes (Optional)"):
|
290 |
+
quote_input = gr.Textbox(label="Enter a quote")
|
291 |
+
add_q_button = gr.Button("Add Quote")
|
292 |
+
quote_df = gr.Dataframe(value=[(idx, q['text'], q['likes'], q['created']) for idx,q in enumerate(quotes)],
|
293 |
+
headers=["Index","Text","Likes","Created"], interactive=False)
|
294 |
+
selected_quote = gr.Number(label="Index to Like")
|
295 |
+
like_q_button = gr.Button("Like Quote")
|
296 |
+
add_q_button.click(fn=add_quote, inputs=quote_input, outputs=quote_df)
|
297 |
+
like_q_button.click(fn=like_quote, inputs=selected_quote, outputs=quote_df)
|
298 |
+
|
299 |
+
demo.load(fn=refresh_gallery_and_df, outputs=[image_gallery, metadata_df])
|
|
|
300 |
|
301 |
+
if __name__ == "__main__":
|
302 |
+
demo.queue(max_size=20).launch()
|
|
|
|
|
|
|
303 |
|
|
|
|
|
|
|
|
|
|
|
304 |
|
|
|
|
|
|
|
|
|
|
|
305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
|
|
|
307 |
|
|
|
|