openfree commited on
Commit
d468bc6
·
verified ·
1 Parent(s): d26ec85

Delete app-backup1.py

Browse files
Files changed (1) hide show
  1. app-backup1.py +0 -1214
app-backup1.py DELETED
@@ -1,1214 +0,0 @@
1
- import os
2
- import gradio as gr
3
- import json
4
- import logging
5
- import torch
6
- from PIL import Image
7
- import spaces
8
- from diffusers import DiffusionPipeline, AutoencoderTiny, AutoencoderKL, AutoPipelineForImage2Image
9
- from live_preview_helpers import calculate_shift, retrieve_timesteps, flux_pipe_call_that_returns_an_iterable_of_images
10
- from diffusers.utils import load_image
11
- from huggingface_hub import hf_hub_download, HfFileSystem, ModelCard, snapshot_download
12
- import copy
13
- import random
14
- import time
15
- import requests
16
- import pandas as pd
17
- from transformers import pipeline
18
- from gradio_imageslider import ImageSlider
19
- import numpy as np
20
- import warnings
21
-
22
- # 상단에 허깅페이스 USERNAME (해당 계정) 반드시 개별 지정할것
23
- USERNAME = "openfree"
24
-
25
- huggingface_token = os.getenv("HF_TOKEN")
26
-
27
-
28
- translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ko-en", device="cpu")
29
-
30
-
31
-
32
- #Load prompts for randomization
33
- df = pd.read_csv('prompts.csv', header=None)
34
- prompt_values = df.values.flatten()
35
-
36
- # Load LoRAs from JSON file
37
- with open('loras.json', 'r') as f:
38
- loras = json.load(f)
39
-
40
- # Initialize the base model
41
- dtype = torch.bfloat16
42
-
43
- device = "cuda" if torch.cuda.is_available() else "cpu"
44
-
45
- # 공통 FLUX 모델 로드
46
- base_model = "black-forest-labs/FLUX.1-dev"
47
- pipe = DiffusionPipeline.from_pretrained(base_model, torch_dtype=dtype).to(device)
48
-
49
- # LoRA를 위한 설정
50
- taef1 = AutoencoderTiny.from_pretrained("madebyollin/taef1", torch_dtype=dtype).to(device)
51
- good_vae = AutoencoderKL.from_pretrained(base_model, subfolder="vae", torch_dtype=dtype).to(device)
52
-
53
- # Image-to-Image 파이프라인 설정
54
- pipe_i2i = AutoPipelineForImage2Image.from_pretrained(
55
- base_model,
56
- vae=good_vae,
57
- transformer=pipe.transformer,
58
- text_encoder=pipe.text_encoder,
59
- tokenizer=pipe.tokenizer,
60
- text_encoder_2=pipe.text_encoder_2,
61
- tokenizer_2=pipe.tokenizer_2,
62
- torch_dtype=dtype
63
- ).to(device)
64
-
65
- MAX_SEED = 2**32 - 1
66
- MAX_PIXEL_BUDGET = 1024 * 1024
67
-
68
- pipe.flux_pipe_call_that_returns_an_iterable_of_images = flux_pipe_call_that_returns_an_iterable_of_images.__get__(pipe)
69
-
70
- class calculateDuration:
71
- def __init__(self, activity_name=""):
72
- self.activity_name = activity_name
73
-
74
- def __enter__(self):
75
- self.start_time = time.time()
76
- return self
77
-
78
- def __exit__(self, exc_type, exc_value, traceback):
79
- self.end_time = time.time()
80
- self.elapsed_time = self.end_time - self.start_time
81
- if self.activity_name:
82
- print(f"Elapsed time for {self.activity_name}: {self.elapsed_time:.6f} seconds")
83
- else:
84
- print(f"Elapsed time: {self.elapsed_time:.6f} seconds")
85
-
86
- def download_file(url, directory=None):
87
- if directory is None:
88
- directory = os.getcwd() # Use current working directory if not specified
89
-
90
- # Get the filename from the URL
91
- filename = url.split('/')[-1]
92
-
93
- # Full path for the downloaded file
94
- filepath = os.path.join(directory, filename)
95
-
96
- # Download the file
97
- response = requests.get(url)
98
- response.raise_for_status() # Raise an exception for bad status codes
99
-
100
- # Write the content to the file
101
- with open(filepath, 'wb') as file:
102
- file.write(response.content)
103
-
104
- return filepath
105
-
106
- def update_selection(evt: gr.SelectData, selected_indices, loras_state, width, height):
107
- selected_index = evt.index
108
- selected_indices = selected_indices or []
109
- if selected_index in selected_indices:
110
- selected_indices.remove(selected_index)
111
- else:
112
- if len(selected_indices) < 3:
113
- selected_indices.append(selected_index)
114
- else:
115
- gr.Warning("You can select up to 3 LoRAs, remove one to select a new one.")
116
- return gr.update(), gr.update(), gr.update(), gr.update(), selected_indices, gr.update(), gr.update(), gr.update(), width, height, gr.update(), gr.update(), gr.update()
117
-
118
- selected_info_1 = "Select LoRA 1"
119
- selected_info_2 = "Select LoRA 2"
120
- selected_info_3 = "Select LoRA 3"
121
-
122
- lora_scale_1 = 1.15
123
- lora_scale_2 = 1.15
124
- lora_scale_3 = 1.15
125
- lora_image_1 = None
126
- lora_image_2 = None
127
- lora_image_3 = None
128
-
129
- if len(selected_indices) >= 1:
130
- lora1 = loras_state[selected_indices[0]]
131
- selected_info_1 = f"### LoRA 1 Selected: [{lora1['title']}](https://huggingface.co/{lora1['repo']}) ✨"
132
- lora_image_1 = lora1['image']
133
- if len(selected_indices) >= 2:
134
- lora2 = loras_state[selected_indices[1]]
135
- selected_info_2 = f"### LoRA 2 Selected: [{lora2['title']}](https://huggingface.co/{lora2['repo']}) ✨"
136
- lora_image_2 = lora2['image']
137
- if len(selected_indices) >= 3:
138
- lora3 = loras_state[selected_indices[2]]
139
- selected_info_3 = f"### LoRA 3 Selected: [{lora3['title']}](https://huggingface.co/{lora3['repo']}) ✨"
140
- lora_image_3 = lora3['image']
141
-
142
- if selected_indices:
143
- last_selected_lora = loras_state[selected_indices[-1]]
144
- new_placeholder = f"Type a prompt for {last_selected_lora['title']}"
145
- else:
146
- new_placeholder = "Type a prompt after selecting a LoRA"
147
-
148
- return gr.update(placeholder=new_placeholder), selected_info_1, selected_info_2, selected_info_3, selected_indices, lora_scale_1, lora_scale_2, lora_scale_3, width, height, lora_image_1, lora_image_2, lora_image_3
149
-
150
- def remove_lora(selected_indices, loras_state, index_to_remove):
151
- if len(selected_indices) > index_to_remove:
152
- selected_indices.pop(index_to_remove)
153
-
154
- selected_info_1 = "Select LoRA 1"
155
- selected_info_2 = "Select LoRA 2"
156
- selected_info_3 = "Select LoRA 3"
157
- lora_scale_1 = 1.15
158
- lora_scale_2 = 1.15
159
- lora_scale_3 = 1.15
160
- lora_image_1 = None
161
- lora_image_2 = None
162
- lora_image_3 = None
163
-
164
- for i, idx in enumerate(selected_indices):
165
- lora = loras_state[idx]
166
- if i == 0:
167
- selected_info_1 = f"### LoRA 1 Selected: [{lora['title']}]({lora['repo']}) ✨"
168
- lora_image_1 = lora['image']
169
- elif i == 1:
170
- selected_info_2 = f"### LoRA 2 Selected: [{lora['title']}]({lora['repo']}) ✨"
171
- lora_image_2 = lora['image']
172
- elif i == 2:
173
- selected_info_3 = f"### LoRA 3 Selected: [{lora['title']}]({lora['repo']}) ✨"
174
- lora_image_3 = lora['image']
175
-
176
- return selected_info_1, selected_info_2, selected_info_3, selected_indices, lora_scale_1, lora_scale_2, lora_scale_3, lora_image_1, lora_image_2, lora_image_3
177
-
178
- def remove_lora_1(selected_indices, loras_state):
179
- return remove_lora(selected_indices, loras_state, 0)
180
-
181
- def remove_lora_2(selected_indices, loras_state):
182
- return remove_lora(selected_indices, loras_state, 1)
183
-
184
- def remove_lora_3(selected_indices, loras_state):
185
- return remove_lora(selected_indices, loras_state, 2)
186
-
187
- def randomize_loras(selected_indices, loras_state):
188
- try:
189
- if len(loras_state) < 3:
190
- raise gr.Error("Not enough LoRAs to randomize.")
191
- selected_indices = random.sample(range(len(loras_state)), 3)
192
- lora1 = loras_state[selected_indices[0]]
193
- lora2 = loras_state[selected_indices[1]]
194
- lora3 = loras_state[selected_indices[2]]
195
- selected_info_1 = f"### LoRA 1 Selected: [{lora1['title']}](https://huggingface.co/{lora1['repo']}) ✨"
196
- selected_info_2 = f"### LoRA 2 Selected: [{lora2['title']}](https://huggingface.co/{lora2['repo']}) ✨"
197
- selected_info_3 = f"### LoRA 3 Selected: [{lora3['title']}](https://huggingface.co/{lora3['repo']}) ✨"
198
- lora_scale_1 = 1.15
199
- lora_scale_2 = 1.15
200
- lora_scale_3 = 1.15
201
- lora_image_1 = lora1.get('image', 'path/to/default/image.png')
202
- lora_image_2 = lora2.get('image', 'path/to/default/image.png')
203
- lora_image_3 = lora3.get('image', 'path/to/default/image.png')
204
- random_prompt = random.choice(prompt_values)
205
- return selected_info_1, selected_info_2, selected_info_3, selected_indices, lora_scale_1, lora_scale_2, lora_scale_3, lora_image_1, lora_image_2, lora_image_3, random_prompt
206
- except Exception as e:
207
- print(f"Error in randomize_loras: {str(e)}")
208
- return "Error", "Error", "Error", [], 1.15, 1.15, 1.15, 'path/to/default/image.png', 'path/to/default/image.png', 'path/to/default/image.png', ""
209
-
210
- def add_custom_lora(custom_lora, selected_indices, current_loras):
211
- if custom_lora:
212
- try:
213
- title, repo, path, trigger_word, image = check_custom_model(custom_lora)
214
- print(f"Loaded custom LoRA: {repo}")
215
- existing_item_index = next((index for (index, item) in enumerate(current_loras) if item['repo'] == repo), None)
216
- if existing_item_index is None:
217
- if repo.endswith(".safetensors") and repo.startswith("http"):
218
- repo = download_file(repo)
219
- new_item = {
220
- "image": image if image else "/home/user/app/custom.png",
221
- "title": title,
222
- "repo": repo,
223
- "weights": path,
224
- "trigger_word": trigger_word
225
- }
226
- print(f"New LoRA: {new_item}")
227
- existing_item_index = len(current_loras)
228
- current_loras.append(new_item)
229
-
230
- # Update gallery
231
- gallery_items = [(item["image"], item["title"]) for item in current_loras]
232
- # Update selected_indices if there's room
233
- if len(selected_indices) < 3:
234
- selected_indices.append(existing_item_index)
235
- else:
236
- gr.Warning("You can select up to 3 LoRAs, remove one to select a new one.")
237
-
238
- # Update selected_info and images
239
- selected_info_1 = "Select a LoRA 1"
240
- selected_info_2 = "Select a LoRA 2"
241
- selected_info_3 = "Select a LoRA 3"
242
- lora_scale_1 = 1.15
243
- lora_scale_2 = 1.15
244
- lora_scale_3 = 1.15
245
- lora_image_1 = None
246
- lora_image_2 = None
247
- lora_image_3 = None
248
- if len(selected_indices) >= 1:
249
- lora1 = current_loras[selected_indices[0]]
250
- selected_info_1 = f"### LoRA 1 Selected: {lora1['title']} ✨"
251
- lora_image_1 = lora1['image'] if lora1['image'] else None
252
- if len(selected_indices) >= 2:
253
- lora2 = current_loras[selected_indices[1]]
254
- selected_info_2 = f"### LoRA 2 Selected: {lora2['title']} ✨"
255
- lora_image_2 = lora2['image'] if lora2['image'] else None
256
- if len(selected_indices) >= 3:
257
- lora3 = current_loras[selected_indices[2]]
258
- selected_info_3 = f"### LoRA 3 Selected: {lora3['title']} ✨"
259
- lora_image_3 = lora3['image'] if lora3['image'] else None
260
- print("Finished adding custom LoRA")
261
- return (
262
- current_loras,
263
- gr.update(value=gallery_items),
264
- selected_info_1,
265
- selected_info_2,
266
- selected_info_3,
267
- selected_indices,
268
- lora_scale_1,
269
- lora_scale_2,
270
- lora_scale_3,
271
- lora_image_1,
272
- lora_image_2,
273
- lora_image_3
274
- )
275
- except Exception as e:
276
- print(e)
277
- gr.Warning(str(e))
278
- return current_loras, gr.update(), gr.update(), gr.update(), gr.update(), selected_indices, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
279
- else:
280
- return current_loras, gr.update(), gr.update(), gr.update(), gr.update(), selected_indices, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
281
-
282
- def remove_custom_lora(selected_indices, current_loras):
283
- if current_loras:
284
- custom_lora_repo = current_loras[-1]['repo']
285
- # Remove from loras list
286
- current_loras = current_loras[:-1]
287
- # Remove from selected_indices if selected
288
- custom_lora_index = len(current_loras)
289
- if custom_lora_index in selected_indices:
290
- selected_indices.remove(custom_lora_index)
291
- # Update gallery
292
- gallery_items = [(item["image"], item["title"]) for item in current_loras]
293
- # Update selected_info and images
294
- selected_info_1 = "Select a LoRA 1"
295
- selected_info_2 = "Select a LoRA 2"
296
- selected_info_3 = "Select a LoRA 3"
297
- lora_scale_1 = 1.15
298
- lora_scale_2 = 1.15
299
- lora_scale_3 = 1.15
300
- lora_image_1 = None
301
- lora_image_2 = None
302
- lora_image_3 = None
303
- if len(selected_indices) >= 1:
304
- lora1 = current_loras[selected_indices[0]]
305
- selected_info_1 = f"### LoRA 1 Selected: [{lora1['title']}]({lora1['repo']}) ✨"
306
- lora_image_1 = lora1['image']
307
- if len(selected_indices) >= 2:
308
- lora2 = current_loras[selected_indices[1]]
309
- selected_info_2 = f"### LoRA 2 Selected: [{lora2['title']}]({lora2['repo']}) ✨"
310
- lora_image_2 = lora2['image']
311
- if len(selected_indices) >= 3:
312
- lora3 = current_loras[selected_indices[2]]
313
- selected_info_3 = f"### LoRA 3 Selected: [{lora3['title']}]({lora3['repo']}) ✨"
314
- lora_image_3 = lora3['image']
315
- return (
316
- current_loras,
317
- gr.update(value=gallery_items),
318
- selected_info_1,
319
- selected_info_2,
320
- selected_info_3,
321
- selected_indices,
322
- lora_scale_1,
323
- lora_scale_2,
324
- lora_scale_3,
325
- lora_image_1,
326
- lora_image_2,
327
- lora_image_3
328
- )
329
-
330
-
331
- def generate_image(prompt_mash, steps, seed, cfg_scale, width, height, progress):
332
- print("Generating image...")
333
- pipe.to("cuda")
334
- generator = torch.Generator(device="cuda").manual_seed(seed)
335
- with calculateDuration("Generating image"):
336
- # Generate image
337
- for img in pipe.flux_pipe_call_that_returns_an_iterable_of_images(
338
- prompt=prompt_mash,
339
- num_inference_steps=steps,
340
- guidance_scale=cfg_scale,
341
- width=width,
342
- height=height,
343
- generator=generator,
344
- joint_attention_kwargs={"scale": 1.0},
345
- output_type="pil",
346
- good_vae=good_vae,
347
- ):
348
- yield img
349
-
350
-
351
- def generate_image_to_image(prompt_mash, image_input_path, image_strength, steps, cfg_scale, width, height, seed):
352
- pipe_i2i.to("cuda")
353
- generator = torch.Generator(device="cuda").manual_seed(seed)
354
- image_input = load_image(image_input_path)
355
- final_image = pipe_i2i(
356
- prompt=prompt_mash,
357
- image=image_input,
358
- strength=image_strength,
359
- num_inference_steps=steps,
360
- guidance_scale=cfg_scale,
361
- width=width,
362
- height=height,
363
- generator=generator,
364
- joint_attention_kwargs={"scale": 1.0},
365
- output_type="pil",
366
- ).images[0]
367
- return final_image
368
-
369
- def run_lora(prompt, image_input, image_strength, cfg_scale, steps, selected_indices,
370
- lora_scale_1, lora_scale_2, lora_scale_3, randomize_seed, seed,
371
- width, height, loras_state, progress=gr.Progress(track_tqdm=True)):
372
- try:
373
- # 한글 감지 및 번역
374
- if any('\u3131' <= char <= '\u318E' or '\uAC00' <= char <= '\uD7A3' for char in prompt):
375
- translated = translator(prompt, max_length=512)[0]['translation_text']
376
- print(f"Original prompt: {prompt}")
377
- print(f"Translated prompt: {translated}")
378
- prompt = translated
379
-
380
- if not selected_indices:
381
- raise gr.Error("You must select at least one LoRA before proceeding.")
382
-
383
- selected_loras = [loras_state[idx] for idx in selected_indices]
384
-
385
- # Build the prompt with trigger words
386
- prepends = []
387
- appends = []
388
- for lora in selected_loras:
389
- trigger_word = lora.get('trigger_word', '')
390
- if trigger_word:
391
- if lora.get("trigger_position") == "prepend":
392
- prepends.append(trigger_word)
393
- else:
394
- appends.append(trigger_word)
395
- prompt_mash = " ".join(prepends + [prompt] + appends)
396
- print("Prompt Mash: ", prompt_mash)
397
-
398
- # Unload previous LoRA weights
399
- with calculateDuration("Unloading LoRA"):
400
- pipe.unload_lora_weights()
401
- pipe_i2i.unload_lora_weights()
402
-
403
- print(f"Active adapters before loading: {pipe.get_active_adapters()}")
404
-
405
- # Load LoRA weights with respective scales
406
- lora_names = []
407
- lora_weights = []
408
-
409
- with calculateDuration("Loading LoRA weights"):
410
- for idx, lora in enumerate(selected_loras):
411
- try:
412
- lora_name = f"lora_{idx}"
413
- lora_path = lora['repo']
414
-
415
- # Private 모델인 경우 특별 처리
416
- if lora.get('private', False):
417
- lora_path = load_private_model(lora_path, huggingface_token)
418
- print(f"Using private model path: {lora_path}")
419
-
420
- if image_input is not None:
421
- pipe_i2i.load_lora_weights(
422
- lora_path,
423
- adapter_name=lora_name,
424
- token=huggingface_token
425
- )
426
- else:
427
- pipe.load_lora_weights(
428
- lora_path,
429
- adapter_name=lora_name,
430
- token=huggingface_token
431
- )
432
-
433
- lora_names.append(lora_name)
434
- lora_weights.append(lora_scale_1 if idx == 0 else lora_scale_2 if idx == 1 else lora_scale_3)
435
- print(f"Successfully loaded LoRA {lora_name} from {lora_path}")
436
-
437
- except Exception as e:
438
- print(f"Failed to load LoRA {lora_name}: {str(e)}")
439
- continue
440
-
441
-
442
-
443
- print("Loaded LoRAs:", lora_names)
444
- print("Adapter weights:", lora_weights)
445
-
446
- if lora_names:
447
- if image_input is not None:
448
- pipe_i2i.set_adapters(lora_names, adapter_weights=lora_weights)
449
- else:
450
- pipe.set_adapters(lora_names, adapter_weights=lora_weights)
451
- else:
452
- print("No LoRAs were successfully loaded.")
453
- return None, seed, gr.update(visible=False)
454
-
455
- print(f"Active adapters after loading: {pipe.get_active_adapters()}")
456
-
457
- # Randomize seed if needed
458
- with calculateDuration("Randomizing seed"):
459
- if randomize_seed:
460
- seed = random.randint(0, MAX_SEED)
461
-
462
- # Generate image
463
- if image_input is not None:
464
- final_image = generate_image_to_image(prompt_mash, image_input, image_strength, steps, cfg_scale, width, height, seed)
465
- else:
466
- image_generator = generate_image(prompt_mash, steps, seed, cfg_scale, width, height, progress)
467
- final_image = None
468
- step_counter = 0
469
- for image in image_generator:
470
- step_counter += 1
471
- final_image = image
472
- progress_bar = f'<div class="progress-container"><div class="progress-bar" style="--current: {step_counter}; --total: {steps};"></div></div>'
473
- yield image, seed, gr.update(value=progress_bar, visible=True)
474
-
475
- if final_image is None:
476
- raise Exception("Failed to generate image")
477
-
478
- return final_image, seed, gr.update(visible=False)
479
-
480
- except Exception as e:
481
- print(f"Error in run_lora: {str(e)}")
482
- return None, seed, gr.update(visible=False)
483
-
484
- run_lora.zerogpu = True
485
-
486
- def get_huggingface_safetensors(link):
487
- split_link = link.split("/")
488
- if len(split_link) == 2:
489
- model_card = ModelCard.load(link)
490
- base_model = model_card.data.get("base_model")
491
- print(f"Base model: {base_model}")
492
- if base_model not in ["black-forest-labs/FLUX.1-dev", "black-forest-labs/FLUX.1-schnell"]:
493
- raise Exception("Not a FLUX LoRA!")
494
- image_path = model_card.data.get("widget", [{}])[0].get("output", {}).get("url", None)
495
- trigger_word = model_card.data.get("instance_prompt", "")
496
- image_url = f"https://huggingface.co/{link}/resolve/main/{image_path}" if image_path else None
497
- fs = HfFileSystem()
498
- safetensors_name = None
499
- try:
500
- list_of_files = fs.ls(link, detail=False)
501
- for file in list_of_files:
502
- if file.endswith(".safetensors"):
503
- safetensors_name = file.split("/")[-1]
504
- if not image_url and file.lower().endswith((".jpg", ".jpeg", ".png", ".webp")):
505
- image_elements = file.split("/")
506
- image_url = f"https://huggingface.co/{link}/resolve/main/{image_elements[-1]}"
507
- except Exception as e:
508
- print(e)
509
- raise gr.Error("Invalid Hugging Face repository with a *.safetensors LoRA")
510
- if not safetensors_name:
511
- raise gr.Error("No *.safetensors file found in the repository")
512
- return split_link[1], link, safetensors_name, trigger_word, image_url
513
- else:
514
- raise gr.Error("Invalid Hugging Face repository link")
515
-
516
- def check_custom_model(link):
517
- if link.endswith(".safetensors"):
518
- # Treat as direct link to the LoRA weights
519
- title = os.path.basename(link)
520
- repo = link
521
- path = None # No specific weight name
522
- trigger_word = ""
523
- image_url = None
524
- return title, repo, path, trigger_word, image_url
525
- elif link.startswith("https://"):
526
- if "huggingface.co" in link:
527
- link_split = link.split("huggingface.co/")
528
- return get_huggingface_safetensors(link_split[1])
529
- else:
530
- raise Exception("Unsupported URL")
531
- else:
532
- # Assume it's a Hugging Face model path
533
- return get_huggingface_safetensors(link)
534
-
535
- def update_history(new_image, history):
536
- """Updates the history gallery with the new image."""
537
- if history is None:
538
- history = []
539
- if new_image is not None:
540
- history.insert(0, new_image)
541
- return history
542
-
543
-
544
-
545
- def refresh_models(huggingface_token):
546
- try:
547
- headers = {
548
- "Authorization": f"Bearer {huggingface_token}",
549
- "Accept": "application/json"
550
- }
551
-
552
- username = USERNAME
553
- api_url = f"https://huggingface.co/api/models?author={username}"
554
- response = requests.get(api_url, headers=headers)
555
- if response.status_code != 200:
556
- raise Exception(f"Failed to fetch models from HuggingFace. Status code: {response.status_code}")
557
-
558
- all_models = response.json()
559
- print(f"Found {len(all_models)} models for user {username}")
560
-
561
- user_models = [
562
- model for model in all_models
563
- if model.get('tags') and ('flux' in [tag.lower() for tag in model.get('tags', [])] or
564
- 'flux-lora' in [tag.lower() for tag in model.get('tags', [])])
565
- ]
566
-
567
- print(f"Found {len(user_models)} FLUX models")
568
-
569
- new_models = []
570
- for model in user_models:
571
- try:
572
- model_id = model['id']
573
- model_card_url = f"https://huggingface.co/api/models/{model_id}"
574
- model_info_response = requests.get(model_card_url, headers=headers)
575
- model_info = model_info_response.json()
576
-
577
- # 이미지 URL에 토큰을 포함시키는 방식으로 변경
578
- is_private = model.get('private', False)
579
- base_image_name = "1732195028106__000001000_0.jpg" # 기본 이미지 이름
580
-
581
- try:
582
- # 실제 이미지 파일 확인
583
- fs = HfFileSystem(token=huggingface_token)
584
- samples_path = f"{model_id}/samples"
585
- files = fs.ls(samples_path, detail=True)
586
- jpg_files = [
587
- f['name'] for f in files
588
- if isinstance(f, dict) and
589
- 'name' in f and
590
- f['name'].lower().endswith('.jpg') and
591
- any(char.isdigit() for char in os.path.basename(f['name']))
592
- ]
593
-
594
- if jpg_files:
595
- base_image_name = os.path.basename(jpg_files[0])
596
- except Exception as e:
597
- print(f"Error accessing samples folder for {model_id}: {str(e)}")
598
-
599
- # 이미지 URL 구성 (토큰 포함)
600
- if is_private:
601
- # Private 모델의 경우 로컬 캐시 경로 사용
602
- cache_dir = f"models/{model_id.replace('/', '_')}/samples"
603
- os.makedirs(cache_dir, exist_ok=True)
604
-
605
- # 이미지 다운로드
606
- image_url = f"https://huggingface.co/{model_id}/resolve/main/samples/{base_image_name}"
607
- local_image_path = os.path.join(cache_dir, base_image_name)
608
-
609
- if not os.path.exists(local_image_path):
610
- response = requests.get(image_url, headers=headers)
611
- if response.status_code == 200:
612
- with open(local_image_path, 'wb') as f:
613
- f.write(response.content)
614
-
615
- image_url = local_image_path
616
- else:
617
- image_url = f"https://huggingface.co/{model_id}/resolve/main/samples/{base_image_name}"
618
-
619
- model_info = {
620
- "image": image_url,
621
- "title": f"[Private] {model_id.split('/')[-1]}" if is_private else model_id.split('/')[-1],
622
- "repo": model_id,
623
- "weights": "pytorch_lora_weights.safetensors",
624
- "trigger_word": model_info.get('instance_prompt', ''),
625
- "private": is_private
626
- }
627
- new_models.append(model_info)
628
- print(f"Added model: {model_id} with image: {image_url}")
629
-
630
- except Exception as e:
631
- print(f"Error processing model {model['id']}: {str(e)}")
632
- continue
633
-
634
- updated_loras = new_models + [lora for lora in loras if lora['repo'] not in [m['repo'] for m in new_models]]
635
-
636
- print(f"Total models after refresh: {len(updated_loras)}")
637
- return updated_loras
638
- except Exception as e:
639
- print(f"Error refreshing models: {str(e)}")
640
- return loras
641
-
642
- def load_private_model(model_id, huggingface_token):
643
- """Private 모델을 로드하는 함수"""
644
- try:
645
- headers = {"Authorization": f"Bearer {huggingface_token}"}
646
-
647
- # 모델 다운로드
648
- local_dir = snapshot_download(
649
- repo_id=model_id,
650
- token=huggingface_token,
651
- local_dir=f"models/{model_id.replace('/', '_')}",
652
- local_dir_use_symlinks=False
653
- )
654
-
655
- # safetensors 파일 찾기
656
- safetensors_file = None
657
- for root, dirs, files in os.walk(local_dir):
658
- for file in files:
659
- if file.endswith('.safetensors'):
660
- safetensors_file = os.path.join(root, file)
661
- break
662
- if safetensors_file:
663
- break
664
-
665
- if not safetensors_file:
666
- raise Exception(f"No .safetensors file found in {local_dir}")
667
-
668
- print(f"Found safetensors file: {safetensors_file}")
669
- return safetensors_file # 전체 경로를 반환
670
-
671
- except Exception as e:
672
- print(f"Error loading private model {model_id}: {str(e)}")
673
- raise e
674
-
675
- custom_theme = gr.themes.Base(
676
- primary_hue="blue",
677
- secondary_hue="purple",
678
- neutral_hue="slate",
679
- ).set(
680
- button_primary_background_fill="*primary_500",
681
- button_primary_background_fill_dark="*primary_600",
682
- button_primary_background_fill_hover="*primary_400",
683
- button_primary_border_color="*primary_500",
684
- button_primary_border_color_dark="*primary_600",
685
- button_primary_text_color="white",
686
- button_primary_text_color_dark="white",
687
- button_secondary_background_fill="*neutral_100",
688
- button_secondary_background_fill_dark="*neutral_700",
689
- button_secondary_background_fill_hover="*neutral_50",
690
- button_secondary_text_color="*neutral_800",
691
- button_secondary_text_color_dark="white",
692
- background_fill_primary="*neutral_50",
693
- background_fill_primary_dark="*neutral_900",
694
- block_background_fill="white",
695
- block_background_fill_dark="*neutral_800",
696
- block_label_background_fill="*primary_500",
697
- block_label_background_fill_dark="*primary_600",
698
- block_label_text_color="white",
699
- block_label_text_color_dark="white",
700
- block_title_text_color="*neutral_800",
701
- block_title_text_color_dark="white",
702
- input_background_fill="white",
703
- input_background_fill_dark="*neutral_800",
704
- input_border_color="*neutral_200",
705
- input_border_color_dark="*neutral_700",
706
- input_placeholder_color="*neutral_400",
707
- input_placeholder_color_dark="*neutral_400",
708
- shadow_spread="8px",
709
- shadow_inset="0px 2px 4px 0px rgba(0,0,0,0.05)"
710
- )
711
-
712
- css = '''
713
- /* 기본 버튼 및 컴포넌트 스타일 */
714
- #gen_btn {
715
- height: 100%
716
- }
717
-
718
- #title {
719
- text-align: center
720
- }
721
-
722
- #title h1 {
723
- font-size: 3em;
724
- display: inline-flex;
725
- align-items: center
726
- }
727
-
728
- #title img {
729
- width: 100px;
730
- margin-right: 0.25em
731
- }
732
-
733
- #lora_list {
734
- background: var(--block-background-fill);
735
- padding: 0 1em .3em;
736
- font-size: 90%
737
- }
738
-
739
- /* 커스텀 LoRA 카드 스타일 */
740
- .custom_lora_card {
741
- margin-bottom: 1em
742
- }
743
-
744
- .card_internal {
745
- display: flex;
746
- height: 100px;
747
- margin-top: .5em
748
- }
749
-
750
- .card_internal img {
751
- margin-right: 1em
752
- }
753
-
754
- /* 유틸리티 클래스 */
755
- .styler {
756
- --form-gap-width: 0px !important
757
- }
758
-
759
- /* 프로그레스 바 스타일 */
760
- #progress {
761
- height: 30px;
762
- width: 90% !important;
763
- margin: 0 auto !important;
764
- }
765
-
766
- #progress .generating {
767
- display: none
768
- }
769
-
770
- .progress-container {
771
- width: 100%;
772
- height: 30px;
773
- background-color: #f0f0f0;
774
- border-radius: 15px;
775
- overflow: hidden;
776
- margin-bottom: 20px
777
- }
778
-
779
- .progress-bar {
780
- height: 100%;
781
- background-color: #4f46e5;
782
- width: calc(var(--current) / var(--total) * 100%);
783
- transition: width 0.5s ease-in-out
784
- }
785
-
786
- /* 컴포넌트 특정 스타일 */
787
- #component-8, .button_total {
788
- height: 100%;
789
- align-self: stretch;
790
- }
791
-
792
- #loaded_loras [data-testid="block-info"] {
793
- font-size: 80%
794
- }
795
-
796
- #custom_lora_structure {
797
- background: var(--block-background-fill)
798
- }
799
-
800
- #custom_lora_btn {
801
- margin-top: auto;
802
- margin-bottom: 11px
803
- }
804
-
805
- #random_btn {
806
- font-size: 300%
807
- }
808
-
809
- #component-11 {
810
- align-self: stretch;
811
- }
812
-
813
- /* 갤러리 메인 스타일 */
814
- #lora_gallery {
815
- margin: 20px 0;
816
- padding: 10px;
817
- border: 1px solid #ddd;
818
- border-radius: 12px;
819
- background: linear-gradient(to bottom right, #ffffff, #f8f9fa);
820
- width: 100% !important;
821
- height: 800px !important;
822
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
823
- display: block !important;
824
- }
825
-
826
- /* 갤러리 그리드 스타일 */
827
- #gallery {
828
- display: grid !important;
829
- grid-template-columns: repeat(10, 1fr) !important;
830
- gap: 10px !important;
831
- padding: 10px !important;
832
- width: 100% !important;
833
- height: 100% !important;
834
- overflow-y: auto !important;
835
- max-width: 100% !important;
836
- }
837
-
838
- /* 갤러리 아이템 스타일 */
839
- .gallery-item {
840
- position: relative !important;
841
- width: 100% !important;
842
- aspect-ratio: 1 !important;
843
- margin: 0 !important;
844
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
845
- transition: transform 0.3s ease, box-shadow 0.3s ease;
846
- border-radius: 12px;
847
- overflow: hidden;
848
- }
849
-
850
- .gallery-item img {
851
- width: 100% !important;
852
- height: 100% !important;
853
- object-fit: cover !important;
854
- border-radius: 12px !important;
855
- }
856
-
857
- /* 갤러리 그리드 래퍼 */
858
- .wrap, .svelte-w6dy5e {
859
- display: grid !important;
860
- grid-template-columns: repeat(10, 1fr) !important;
861
- gap: 10px !important;
862
- width: 100% !important;
863
- max-width: 100% !important;
864
- }
865
-
866
- /* 컨테이너 공통 스타일 */
867
- .container, .content, .block, .contain {
868
- width: 100% !important;
869
- max-width: 100% !important;
870
- margin: 0 !important;
871
- padding: 0 !important;
872
- }
873
-
874
- .row {
875
- width: 100% !important;
876
- margin: 0 !important;
877
- padding: 0 !important;
878
- }
879
-
880
- /* 버튼 스타일 */
881
- .button_total {
882
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
883
- transition: all 0.3s ease;
884
- }
885
-
886
- .button_total:hover {
887
- transform: translateY(-2px);
888
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
889
- }
890
-
891
- /* 입력 필드 스타일 */
892
- input, textarea {
893
- box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
894
- transition: all 0.3s ease;
895
- }
896
-
897
- input:focus, textarea:focus {
898
- box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
899
- }
900
-
901
- /* 컴포넌트 border-radius */
902
- .gradio-container .input,
903
- .gradio-container .button,
904
- .gradio-container .block {
905
- border-radius: 12px;
906
- }
907
-
908
- /* 스크롤바 스타일 */
909
- #gallery::-webkit-scrollbar {
910
- width: 8px;
911
- }
912
-
913
- #gallery::-webkit-scrollbar-track {
914
- background: #f1f1f1;
915
- border-radius: 4px;
916
- }
917
-
918
- #gallery::-webkit-scrollbar-thumb {
919
- background: #888;
920
- border-radius: 4px;
921
- }
922
-
923
- #gallery::-webkit-scrollbar-thumb:hover {
924
- background: #555;
925
- }
926
-
927
- /* Flex 컨테이너 */
928
- .flex {
929
- width: 100% !important;
930
- max-width: 100% !important;
931
- display: flex !important;
932
- }
933
-
934
- /* Svelte 특정 클래스 */
935
- .svelte-1p9xokt {
936
- width: 100% !important;
937
- max-width: 100% !important;
938
- }
939
-
940
- /* Footer 숨김 */
941
- #footer {
942
- visibility: hidden;
943
- }
944
-
945
- /* 결과 이미지 및 컨테이너 스타일 */
946
- #result_column, #result_column > div {
947
- display: flex !important;
948
- flex-direction: column !important;
949
- align-items: flex-start !important; /* center에서 flex-start로 변경 */
950
- width: 100% !important;
951
- margin: 0 !important; /* auto에서 0으로 변경 */
952
- }
953
-
954
- .generated-image, .generated-image > div {
955
- display: flex !important;
956
- justify-content: flex-start !important; /* center에서 flex-start로 변경 */
957
- align-items: flex-start !important; /* center에서 flex-start로 변경 */
958
- width: 90% !important;
959
- max-width: 768px !important;
960
- margin: 0 !important; /* auto에서 0으로 변경 */
961
- margin-left: 20px !important; /* 왼쪽 여백 추가 */
962
- }
963
-
964
- .generated-image img {
965
- margin: 0 !important; /* auto에서 0으로 변경 */
966
- display: block !important;
967
- max-width: 100% !important;
968
- }
969
-
970
- /* 히스토리 갤러리�� 좌측 정렬로 변경 */
971
- .history-gallery {
972
- display: flex !important;
973
- justify-content: flex-start !important; /* center에서 flex-start로 변경 */
974
- width: 90% !important;
975
- max-width: 90% !important;
976
- margin: 0 !important; /* auto에서 0으로 변경 */
977
- margin-left: 20px !important; /* 왼쪽 여백 추가 */
978
-
979
- /* 새로고침 버튼 스타일 */
980
- #refresh-button {
981
- margin: 10px;
982
- padding: 8px 16px;
983
- background-color: #4a5568;
984
- color: white;
985
- border-radius: 8px;
986
- transition: all 0.3s ease;
987
- }
988
-
989
- #refresh-button:hover {
990
- background-color: #2d3748;
991
- transform: scale(1.05);
992
- }
993
-
994
- #refresh-button:active {
995
- transform: scale(0.95);
996
- }
997
- }
998
- '''
999
-
1000
- with gr.Blocks(theme=custom_theme, css=css, delete_cache=(60, 3600)) as app:
1001
- loras_state = gr.State(loras)
1002
- selected_indices = gr.State([])
1003
-
1004
- gr.Markdown(
1005
- """
1006
- # MixGen3: 멀티 Lora(이미지 학습) 통합 생성 모델
1007
- ### 사용 안내:
1008
- 갤러리에서 원하는 모델을 선택(최대 3개까지) < 프롬프트에 한글 또는 영문으로 원하는 내용을 입력 < Generate 버튼 실행
1009
- """
1010
- )
1011
- # 새로고침 버튼 추가
1012
- with gr.Row():
1013
- refresh_button = gr.Button("🔄 모델 새로고침(나만의 맞춤 학습된 Private 모델 불러오기)", variant="secondary")
1014
-
1015
- with gr.Row(elem_id="lora_gallery", equal_height=True):
1016
- gallery = gr.Gallery(
1017
- value=[(item["image"], item["title"]) for item in loras],
1018
- label="LoRA Explorer Gallery",
1019
- columns=11,
1020
- elem_id="gallery",
1021
- height=800,
1022
- object_fit="cover",
1023
- show_label=True,
1024
- allow_preview=False,
1025
- show_share_button=False,
1026
- container=True,
1027
- preview=False
1028
- )
1029
-
1030
-
1031
- with gr.Tab(label="Generate"):
1032
- # Prompt and Generate Button
1033
- with gr.Row():
1034
- with gr.Column(scale=3):
1035
- prompt = gr.Textbox(label="Prompt", lines=1, placeholder="Type a prompt after selecting a LoRA")
1036
- with gr.Column(scale=1):
1037
- generate_button = gr.Button("Generate", variant="primary", elem_classes=["button_total"])
1038
-
1039
- # LoRA Selection Area
1040
- with gr.Row(elem_id="loaded_loras"):
1041
- # Randomize Button
1042
- with gr.Column(scale=1, min_width=25):
1043
- randomize_button = gr.Button("🎲", variant="secondary", scale=1, elem_id="random_btn")
1044
-
1045
- # LoRA 1
1046
- with gr.Column(scale=8):
1047
- with gr.Row():
1048
- with gr.Column(scale=0, min_width=50):
1049
- lora_image_1 = gr.Image(label="LoRA 1 Image", interactive=False, min_width=50, width=50, show_label=False, show_share_button=False, show_download_button=False, show_fullscreen_button=False, height=50)
1050
- with gr.Column(scale=3, min_width=100):
1051
- selected_info_1 = gr.Markdown("Select a LoRA 1")
1052
- with gr.Column(scale=5, min_width=50):
1053
- lora_scale_1 = gr.Slider(label="LoRA 1 Scale", minimum=0, maximum=3, step=0.01, value=1.15)
1054
- with gr.Row():
1055
- remove_button_1 = gr.Button("Remove", size="sm")
1056
-
1057
- # LoRA 2
1058
- with gr.Column(scale=8):
1059
- with gr.Row():
1060
- with gr.Column(scale=0, min_width=50):
1061
- lora_image_2 = gr.Image(label="LoRA 2 Image", interactive=False, min_width=50, width=50, show_label=False, show_share_button=False, show_download_button=False, show_fullscreen_button=False, height=50)
1062
- with gr.Column(scale=3, min_width=100):
1063
- selected_info_2 = gr.Markdown("Select a LoRA 2")
1064
- with gr.Column(scale=5, min_width=50):
1065
- lora_scale_2 = gr.Slider(label="LoRA 2 Scale", minimum=0, maximum=3, step=0.01, value=1.15)
1066
- with gr.Row():
1067
- remove_button_2 = gr.Button("Remove", size="sm")
1068
-
1069
- # LoRA 3
1070
- with gr.Column(scale=8):
1071
- with gr.Row():
1072
- with gr.Column(scale=0, min_width=50):
1073
- lora_image_3 = gr.Image(label="LoRA 3 Image", interactive=False, min_width=50, width=50, show_label=False, show_share_button=False, show_download_button=False, show_fullscreen_button=False, height=50)
1074
- with gr.Column(scale=3, min_width=100):
1075
- selected_info_3 = gr.Markdown("Select a LoRA 3")
1076
- with gr.Column(scale=5, min_width=50):
1077
- lora_scale_3 = gr.Slider(label="LoRA 3 Scale", minimum=0, maximum=3, step=0.01, value=1.15)
1078
- with gr.Row():
1079
- remove_button_3 = gr.Button("Remove", size="sm")
1080
-
1081
- # Result and Progress Area
1082
- with gr.Column(elem_id="result_column"):
1083
- progress_bar = gr.Markdown(elem_id="progress", visible=False)
1084
- with gr.Column(elem_id="result_box"): # Box를 Column으로 변경
1085
- result = gr.Image(
1086
- label="Generated Image",
1087
- interactive=False,
1088
- elem_classes=["generated-image"],
1089
- container=True,
1090
- elem_id="result_image",
1091
- width="100%"
1092
- )
1093
- with gr.Accordion("History", open=False):
1094
- history_gallery = gr.Gallery(
1095
- label="History",
1096
- columns=6,
1097
- object_fit="contain",
1098
- interactive=False,
1099
- elem_classes=["history-gallery"]
1100
- )
1101
-
1102
-
1103
- # Advanced Settings
1104
- with gr.Row():
1105
- with gr.Accordion("Advanced Settings", open=False):
1106
- with gr.Row():
1107
- input_image = gr.Image(label="Input image", type="filepath")
1108
- image_strength = gr.Slider(label="Denoise Strength", info="Lower means more image influence", minimum=0.1, maximum=1.0, step=0.01, value=0.75)
1109
- with gr.Column():
1110
- with gr.Row():
1111
- cfg_scale = gr.Slider(label="CFG Scale", minimum=1, maximum=20, step=0.5, value=3.5)
1112
- steps = gr.Slider(label="Steps", minimum=1, maximum=50, step=1, value=28)
1113
- with gr.Row():
1114
- width = gr.Slider(label="Width", minimum=256, maximum=1536, step=64, value=1024)
1115
- height = gr.Slider(label="Height", minimum=256, maximum=1536, step=64, value=1024)
1116
- with gr.Row():
1117
- randomize_seed = gr.Checkbox(True, label="Randomize seed")
1118
- seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0, randomize=True)
1119
-
1120
- # Custom LoRA Section
1121
- with gr.Column():
1122
- with gr.Group():
1123
- with gr.Row(elem_id="custom_lora_structure"):
1124
- custom_lora = gr.Textbox(label="Custom LoRA", info="LoRA Hugging Face path or *.safetensors public URL", placeholder="ginipick/flux-lora-eric-cat", scale=3, min_width=150)
1125
- add_custom_lora_button = gr.Button("Add Custom LoRA", elem_id="custom_lora_btn", scale=2, min_width=150)
1126
- remove_custom_lora_button = gr.Button("Remove Custom LoRA", visible=False)
1127
- gr.Markdown("[Check the list of FLUX LoRAs](https://huggingface.co/models?other=base_model:adapter:black-forest-labs/FLUX.1-dev)", elem_id="lora_list")
1128
-
1129
- # Event Handlers
1130
- gallery.select(
1131
- update_selection,
1132
- inputs=[selected_indices, loras_state, width, height],
1133
- outputs=[prompt, selected_info_1, selected_info_2, selected_info_3, selected_indices,
1134
- lora_scale_1, lora_scale_2, lora_scale_3, width, height,
1135
- lora_image_1, lora_image_2, lora_image_3]
1136
- )
1137
-
1138
- remove_button_1.click(
1139
- remove_lora_1,
1140
- inputs=[selected_indices, loras_state],
1141
- outputs=[selected_info_1, selected_info_2, selected_info_3, selected_indices,
1142
- lora_scale_1, lora_scale_2, lora_scale_3,
1143
- lora_image_1, lora_image_2, lora_image_3]
1144
- )
1145
-
1146
- remove_button_2.click(
1147
- remove_lora_2,
1148
- inputs=[selected_indices, loras_state],
1149
- outputs=[selected_info_1, selected_info_2, selected_info_3, selected_indices,
1150
- lora_scale_1, lora_scale_2, lora_scale_3,
1151
- lora_image_1, lora_image_2, lora_image_3]
1152
- )
1153
-
1154
- remove_button_3.click(
1155
- remove_lora_3,
1156
- inputs=[selected_indices, loras_state],
1157
- outputs=[selected_info_1, selected_info_2, selected_info_3, selected_indices,
1158
- lora_scale_1, lora_scale_2, lora_scale_3,
1159
- lora_image_1, lora_image_2, lora_image_3]
1160
- )
1161
-
1162
- randomize_button.click(
1163
- randomize_loras,
1164
- inputs=[selected_indices, loras_state],
1165
- outputs=[selected_info_1, selected_info_2, selected_info_3, selected_indices,
1166
- lora_scale_1, lora_scale_2, lora_scale_3,
1167
- lora_image_1, lora_image_2, lora_image_3, prompt]
1168
- )
1169
-
1170
- add_custom_lora_button.click(
1171
- add_custom_lora,
1172
- inputs=[custom_lora, selected_indices, loras_state],
1173
- outputs=[loras_state, gallery, selected_info_1, selected_info_2, selected_info_3,
1174
- selected_indices, lora_scale_1, lora_scale_2, lora_scale_3,
1175
- lora_image_1, lora_image_2, lora_image_3]
1176
- )
1177
-
1178
- remove_custom_lora_button.click(
1179
- remove_custom_lora,
1180
- inputs=[selected_indices, loras_state],
1181
- outputs=[loras_state, gallery, selected_info_1, selected_info_2, selected_info_3,
1182
- selected_indices, lora_scale_1, lora_scale_2, lora_scale_3,
1183
- lora_image_1, lora_image_2, lora_image_3]
1184
- )
1185
-
1186
- gr.on(
1187
- triggers=[generate_button.click, prompt.submit],
1188
- fn=run_lora,
1189
- inputs=[prompt, input_image, image_strength, cfg_scale, steps,
1190
- selected_indices, lora_scale_1, lora_scale_2, lora_scale_3,
1191
- randomize_seed, seed, width, height, loras_state],
1192
- outputs=[result, seed, progress_bar]
1193
- ).then(
1194
- fn=lambda x, history: update_history(x, history) if x is not None else history,
1195
- inputs=[result, history_gallery],
1196
- outputs=history_gallery
1197
- )
1198
-
1199
- # 새로고침 버튼 이벤트 핸들러
1200
- def refresh_gallery():
1201
- updated_loras = refresh_models(huggingface_token)
1202
- return (
1203
- gr.update(value=[(item["image"], item["title"]) for item in updated_loras]),
1204
- updated_loras
1205
- )
1206
-
1207
- refresh_button.click(
1208
- refresh_gallery,
1209
- outputs=[gallery, loras_state]
1210
- )
1211
-
1212
- if __name__ == "__main__":
1213
- app.queue(max_size=20)
1214
- app.launch(debug=True)