aexyb commited on
Commit
f1bb98b
ยท
verified ยท
1 Parent(s): eea9003

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +318 -88
app.py CHANGED
@@ -1,94 +1,324 @@
1
- import os, sys, subprocess, time, json, shutil
2
- from pathlib import Path
3
- from datetime import datetime
 
4
 
5
- BASE = Path.cwd()
6
- COMFYUI = BASE / "ComfyUI"
7
- OUTPUTS = BASE / "outputs"
8
- MODEL_FILE = "flux-2-klein-4b-Q4_K_M.gguf"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- def log(msg):
11
- print(f"[{datetime.now().strftime('%H:%M:%S')}] {msg}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- def generate(prompt, neg="", w=1024, h=1024, steps=20, cfg=7.0, seed=-1):
14
- import requests
15
-
16
  if not prompt.strip():
17
- return None, "Enter a prompt!"
18
-
19
- if seed < 0:
20
- seed = int(time.time() * 1000) % (2**32)
21
-
22
- workflow = {
23
- "1": {"class_type": "CLIPTextEncode", "inputs": {"text": prompt, "clip": ["4", 0]}},
24
- "2": {"class_type": "CLIPTextEncode", "inputs": {"text": neg or "blurry", "clip": ["4", 0]}},
25
- "3": {"class_type": "VAEDecode", "inputs": {"samples": ["6", 0], "vae": ["5", 0]}},
26
- "4": {"class_type": "CLIPLoader", "inputs": {"clip_name": "t5xxl_fp16.safetensors", "type": "l"}},
27
- "5": {"class_type": "VAELoader", "inputs": {"vae_name": "ae.safetensors"}},
28
- "6": {"class_type": "KSampler", "inputs": {
29
- "model": ["7", 0], "positive": ["1", 0], "negative": ["2", 0],
30
- "latent_image": ["8", 0], "seed": seed, "steps": steps,
31
- "cfg": cfg, "sampler_name": "euler", "scheduler": "normal", "denoise": 1.0
32
- }},
33
- "7": {"class_type": "GGUFLoader", "inputs": {"unet_name": MODEL_FILE, "weight_dtype": "fp16_e4m3fn"}},
34
- "8": {"class_type": "EmptyLatentImage", "inputs": {"width": w, "height": h, "batch_size": 1}},
35
- "9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "output", "images": ["3", 0]}}
36
- }
37
-
38
- # Start ComfyUI if not running
39
- global proc
40
- if 'proc' not in globals():
41
- log("Starting ComfyUI...")
42
- proc = subprocess.Popen(
43
- [sys.executable, str(COMFYUI / "main.py"), "--listen", "127.0.0.1", "--port", "8188"],
44
- cwd=str(COMFYUI), stdout=subprocess.PIPE, stderr=subprocess.PIPE
45
- )
46
- for _ in range(30):
47
- try:
48
- requests.get("http://127.0.0.1:8188/system_stats", timeout=2)
49
- break
50
- except:
51
- time.sleep(1)
52
-
53
- # Queue and wait
54
- resp = requests.post("http://127.0.0.1:8188/prompt", json={"prompt": workflow}, timeout=10)
55
- pid = resp.json()["prompt_id"]
56
-
57
- for _ in range(150):
58
- time.sleep(2)
59
- hist = requests.get(f"http://127.0.0.1:8188/history/{pid}", timeout=5).json()
60
- if pid in hist:
61
- imgs = hist[pid].get("outputs", {}).get("9", {}).get("images", [])
62
- if imgs:
63
- src = COMFYUI / "output" / imgs[0]["filename"]
64
- OUTPUT.mkdir(exist_ok=True)
65
- dst = OUTPUT / f"{datetime.now().strftime('%H%M%S')}.png"
66
- shutil.copy2(src, dst)
67
- return str(dst), f"โœ… Done! ({dst.stat().st_size/(1024*1024):.1f}MB)"
68
- break
69
-
70
- return None, "Timeout or error"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- import gradio as gr
73
 
74
- with gr.Blocks(title="FLUX Generator") as demo:
75
- gr.Markdown("# ๐ŸŽจ FLUX.2-klein-4B (Q4_K_M)\n### ComfyUI Backend")
76
-
77
- with gr.Row():
78
- with gr.Column(scale=2):
79
- prompt = gr.Textbox(label="Prompt", lines=3, value="a red fox in snow, detailed, 8k")
80
- neg = gr.Textbox(label="Negative", lines=2)
81
- with gr.Column():
82
- w = gr.Slider(512, 1024, 1024, 64, label="Width")
83
- h = gr.Slider(512, 1024, 1024, 64, label="Height")
84
- steps = gr.Slider(1, 50, 20, 1, label="Steps")
85
- cfg = gr.Slider(1, 20, 7.0, 0.5, label="CFG")
86
- seed = gr.Number(-1, label="Seed")
87
-
88
- gen = gr.Button("Generate", variant="primary")
89
- img = gr.Image(label="Output")
90
- status = gr.Textbox(label="Status")
91
-
92
- gen.click(generate, [prompt, neg, w, h, steps, cfg, seed], [img, status])
93
-
94
- demo.launch(server_name="0.0.0.0", port=7860)
 
1
+ """
2
+ FLUX.2-klein-4B GGUF โ€” CPU-only Inference Space
3
+ ็กฌไปถ็บฆๆŸ๏ผšๆ—  GPU ยท CPU ยท 18 GB RAM
4
+ """
5
 
6
+ import os
7
+ import time
8
+ import torch
9
+ import gradio as gr
10
+ from diffusers import FluxPipeline
11
+
12
+ # โ”€โ”€ ็Žฏๅขƒ้…็ฝฎ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
14
+ os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
15
+ torch.set_num_threads(os.cpu_count() or 4)
16
+
17
+ MODEL_ID = "unsloth/FLUX-2-klein-4B-GGUF"
18
+
19
+ # โ”€โ”€ ๅŠ ่ฝฝๆจกๅž‹๏ผˆๅฏๅŠจๆ—ถๆ‰ง่กŒไธ€ๆฌก๏ผ‰ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
20
+ print("โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—")
21
+ print("โ•‘ FLUX.2-klein-4B GGUF โ€” CPU Inference โ•‘")
22
+ print("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")
23
+ print(f" Model : {MODEL_ID}")
24
+ print(f" Threads : {torch.get_num_threads()}")
25
+ print(f" PyTorch : {torch.__version__}")
26
+ print(" Loading...")
27
+
28
+ pipe = FluxPipeline.from_pretrained(
29
+ MODEL_ID,
30
+ torch_dtype=torch.float32, # CPU ไธŠๅฟ…้กป็”จ float32
31
+ )
32
+ pipe = pipe.to("cpu")
33
+
34
+ # ๅฆ‚ๆžœ diffusers ๆ”ฏๆŒ GGUF ๅŽŸ็”ŸๅŠ ่ฝฝๅคฑ่ดฅ๏ผŒๅ–ๆถˆไธ‹ๆ–นๆณจ้‡Šไฝฟ็”จๅค‡็”จๆ–นๆกˆ
35
+ # ๅค‡็”จๆ–นๆกˆ๏ผšไปŽๅŽŸๅง‹ๆจกๅž‹ๅŠ ่ฝฝ pipeline ้ชจๆžถ๏ผŒๅ†ๆ›ฟๆข transformer ไธบ GGUF ๆƒ้‡
36
+ #
37
+ # from diffusers import FluxPipeline
38
+ # BASE_ID = "black-forest-labs/FLUX.2-klein-4B" # ๅŽŸๅง‹้ž้‡ๅŒ–ๆจกๅž‹
39
+ # pipe = FluxPipeline.from_pretrained(BASE_ID, torch_dtype=torch.float32)
40
+ # # ็„ถๅŽ็”จ gguf ๅŒ…ๆ‰‹ๅŠจๅŠ ่ฝฝ้‡ๅŒ–ๆƒ้‡ๆ›ฟๆข pipe.transformer
41
 
42
+ print(" โœ“ Model loaded successfully!\n")
43
+
44
+
45
+ # โ”€โ”€ ๆŽจ็†ๅ‡ฝๆ•ฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
46
+ @torch.no_grad()
47
+ def generate(
48
+ prompt: str,
49
+ negative_prompt: str,
50
+ width: int,
51
+ height: int,
52
+ steps: int,
53
+ guidance: float,
54
+ seed: int,
55
+ progress=gr.Progress(track_tqdm=True),
56
+ ):
57
+ """ๆ–‡ๆœฌ็”Ÿๆˆๅ›พๅƒ"""
58
 
 
 
 
59
  if not prompt.strip():
60
+ raise gr.Error("่ฏท่พ“ๅ…ฅๆ็คบ่ฏ")
61
+
62
+ # ็งๅญๅค„็†
63
+ seed = seed if seed >= 0 else int(time.time() * 1000) % (2**32)
64
+ generator = torch.Generator("cpu").manual_seed(seed)
65
+
66
+ t0 = time.time()
67
+
68
+ result = pipe(
69
+ prompt=prompt,
70
+ negative_prompt=negative_prompt if negative_prompt.strip() else None,
71
+ width=width,
72
+ height=height,
73
+ num_inference_steps=steps,
74
+ guidance_scale=guidance,
75
+ generator=generator,
76
+ )
77
+
78
+ dt = time.time() - t0
79
+ img = result.images[0]
80
+
81
+ return img, seed, f"ๅฎŒๆˆ ยท ่€—ๆ—ถ {dt:.1f}s ยท seed {seed}"
82
+
83
+
84
+ # โ”€โ”€ ๆ ทๅผ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
85
+ CSS = """
86
+ :root {
87
+ --bg: #0a0a0f;
88
+ --card: #13131a;
89
+ --card-hover: #1a1a24;
90
+ --border: #23233a;
91
+ --fg: #e4e4ec;
92
+ --muted: #6b6b80;
93
+ --accent: #7c5cfc;
94
+ --accent-glow: rgba(124, 92, 252, .25);
95
+ }
96
+
97
+ /* ๅ…จๅฑ€่ƒŒๆ™ฏ */
98
+ .gradio-container {
99
+ background: var(--bg) !important;
100
+ color: var(--fg) !important;
101
+ max-width: 1080px !important;
102
+ padding-top: 1rem !important;
103
+ }
104
+
105
+ /* ๅก็‰‡ */
106
+ .gr-block, .gr-panel {
107
+ background: var(--card) !important;
108
+ border: 1px solid var(--border) !important;
109
+ border-radius: 14px !important;
110
+ }
111
+
112
+ /* ่พ“ๅ…ฅๆก† */
113
+ .gr-input, .gr-text-input, textarea.gr-textbox,
114
+ input[type=number].gr-number {
115
+ background: #0e0e16 !important;
116
+ border: 1px solid var(--border) !important;
117
+ color: var(--fg) !important;
118
+ border-radius: 10px !important;
119
+ font-size: .92rem !important;
120
+ }
121
+ textarea.gr-textbox { resize: vertical !important; }
122
+
123
+ /* ๆ ‡็ญพ */
124
+ .gr-label {
125
+ color: var(--muted) !important;
126
+ font-size: .78rem !important;
127
+ text-transform: uppercase !important;
128
+ letter-spacing: .06em !important;
129
+ font-weight: 600 !important;
130
+ }
131
+
132
+ /* ๆŒ‰้’ฎ */
133
+ .gr-button-primary {
134
+ background: linear-gradient(135deg, #7c5cfc, #a78bfa) !important;
135
+ border: none !important;
136
+ font-weight: 700 !important;
137
+ font-size: 1rem !important;
138
+ border-radius: 10px !important;
139
+ padding: .7rem 1.5rem !important;
140
+ box-shadow: 0 4px 24px var(--accent-glow) !important;
141
+ transition: all .2s ease !important;
142
+ }
143
+ .gr-button-primary:hover {
144
+ transform: translateY(-1px) !important;
145
+ box-shadow: 0 6px 32px var(--accent-glow) !important;
146
+ filter: brightness(1.1) !important;
147
+ }
148
+ .gr-button-primary:active {
149
+ transform: translateY(0) !important;
150
+ }
151
+
152
+ /* ๆป‘ๅ— */
153
+ .gr-slider input[type=range] { accent-color: var(--accent) !important; }
154
+
155
+ /* ๅ›พ็‰‡่พ“ๅ‡บ */
156
+ .gr-image { border-radius: 12px !important; overflow: hidden !important; }
157
+ .gr-image img { border-radius: 12px !important; }
158
+
159
+ /* Examples */
160
+ .gr-examples { border-radius: 12px !important; }
161
+
162
+ /* ้š่— footer */
163
+ footer { display: none !important; }
164
+
165
+ /* ๆปšๅŠจๆก */
166
+ ::-webkit-scrollbar { width: 6px; }
167
+ ::-webkit-scrollbar-track { background: transparent; }
168
+ ::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
169
+
170
+ /* ่ญฆๅ‘Šๆก */
171
+ .warn-bar {
172
+ background: linear-gradient(90deg, #1c1a2e, #1a1428);
173
+ border: 1px solid #2e2848;
174
+ border-radius: 10px;
175
+ padding: .7rem 1rem;
176
+ margin-bottom: 1.2rem;
177
+ font-size: .82rem;
178
+ color: #a0a0b8;
179
+ line-height: 1.5;
180
+ }
181
+ .warn-bar strong { color: #c4b5fd; }
182
+
183
+ /* ไฟกๆฏๆ  */
184
+ .info-bar {
185
+ background: #0e0e16;
186
+ border: 1px solid var(--border);
187
+ border-radius: 8px;
188
+ padding: .5rem .8rem;
189
+ font-size: .82rem;
190
+ color: var(--muted);
191
+ font-family: 'SF Mono', 'Fira Code', monospace;
192
+ }
193
+ """
194
+
195
+ # โ”€โ”€ ๆž„ๅปบ็•Œ้ข โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
196
+ with gr.Blocks(
197
+ title="FLUX.2-klein-4B GGUF",
198
+ css=CSS,
199
+ theme=gr.themes.Soft(primary_hue="violet"),
200
+ ) as demo:
201
+
202
+ # โ”€โ”€ ้กถ้ƒจๆ ‡้ข˜ โ”€โ”€
203
+ gr.HTML("""
204
+ <div style="text-align:center; padding:2rem 0 .6rem">
205
+ <h1 style="font-size:2.4rem; font-weight:800; letter-spacing:-.03em;
206
+ background:linear-gradient(135deg,#7c5cfc 0%,#c084fc 50%,#f0abfc 100%);
207
+ -webkit-background-clip:text; -webkit-text-fill-color:transparent;
208
+ margin:0; line-height:1.2">
209
+ FLUX.2-klein-4B
210
+ </h1>
211
+ <p style="color:#555; font-size:.88rem; margin-top:.5rem; font-weight:500;
212
+ letter-spacing:.08em; text-transform:uppercase">
213
+ GGUF Quantized ยท CPU Inference ยท 4B Parameters
214
+ </p>
215
+ </div>
216
+ """)
217
+
218
+ # โ”€โ”€ ่ญฆๅ‘Š โ”€โ”€
219
+ gr.HTML("""
220
+ <div class="warn-bar">
221
+ &#9888;&nbsp; ็บฏ CPU ๆŽจ็†๏ฟฝ๏ฟฝ๏ฟฝ้€Ÿๅบฆ่พƒๆ…ขใ€‚ๅปบ่ฎฎ <strong>512ร—512</strong> ไปฅไธ‹ๅˆ†่พจ็އ +
222
+ <strong>8~12 ๆญฅ</strong>๏ผŒ้ฆ–ๆฌก็”Ÿๆˆไผšๅ›  JIT ็ผ–่ฏ‘ๆ›ดๆ…ข๏ผŒๅŽ็ปญไผšๅŠ ้€Ÿใ€‚
223
+ ๅฆ‚ๆžœ่ถ…ๆ—ถ๏ผŒ่ฏท่ฟ›ไธ€ๆญฅ้™ไฝŽๅˆ†่พจ็އๆˆ–ๆญฅๆ•ฐใ€‚
224
+ </div>
225
+ """)
226
+
227
+ with gr.Row(equal_height=False):
228
+
229
+ # โ”€โ”€ ๅทฆๆ ๏ผšๆŽงๅˆถ้ขๆฟ โ”€โ”€
230
+ with gr.Column(scale=3):
231
+
232
+ prompt = gr.Textbox(
233
+ label="Prompt",
234
+ placeholder="A cyberpunk city at night with neon signs reflecting in rain-soaked streets, cinematic lighting...",
235
+ lines=3,
236
+ show_label=True,
237
+ )
238
+
239
+ negative_prompt = gr.Textbox(
240
+ label="Negative Prompt",
241
+ placeholder="blurry, low quality, distorted, watermark...",
242
+ lines=1,
243
+ show_label=True,
244
+ )
245
+
246
+ with gr.Row():
247
+ w = gr.Slider(256, 768, value=512, step=64, label="Width")
248
+ h = gr.Slider(256, 768, value=512, step=64, label="Height")
249
+
250
+ with gr.Row():
251
+ steps = gr.Slider(4, 30, value=10, step=1, label="Steps")
252
+ guidance = gr.Slider(1.0, 10.0, value=3.5, step=0.5, label="Guidance Scale")
253
+
254
+ seed = gr.Number(label="Seed๏ผˆ-1 ้šๆœบ๏ผ‰", value=-1, precision=0)
255
+
256
+ btn = gr.Button("โœฆ Generate", variant="primary", size="lg")
257
+
258
+ # ็คบไพ‹ๆ็คบ่ฏ
259
+ gr.Examples(
260
+ examples=[
261
+ [
262
+ "A serene Japanese garden with cherry blossoms falling over a stone bridge, koi pond below, golden hour lighting, 4K",
263
+ ""
264
+ ],
265
+ [
266
+ "Futuristic space station interior, holographic star maps floating in zero gravity, bioluminescent plants, cinematic wide shot",
267
+ ""
268
+ ],
269
+ [
270
+ "Oil painting of a cozy wooden cabin in snowy mountains, warm orange light from windows, smoke from chimney, impressionist style",
271
+ ""
272
+ ],
273
+ [
274
+ "Macro photograph of a dewdrop on a spider web at sunrise, prismatic light refraction, extreme shallow depth of field",
275
+ ""
276
+ ],
277
+ [
278
+ "A whimsical treehouse library at twilight, floating books with glowing pages, warm lanterns, fairy tale atmosphere",
279
+ "blurry, low quality"
280
+ ],
281
+ [
282
+ "Underwater coral reef city with bioluminescent architecture, schools of tropical fish, volumetric god rays, wide angle",
283
+ ""
284
+ ],
285
+ ],
286
+ inputs=[prompt, negative_prompt],
287
+ label="Quick Examples",
288
+ )
289
+
290
+ # โ”€โ”€ ๅณๆ ๏ผš่พ“ๅ‡บ โ”€โ”€
291
+ with gr.Column(scale=4):
292
+ output_img = gr.Image(
293
+ label="Output",
294
+ show_label=False,
295
+ height=540,
296
+ elem_classes="output-image",
297
+ )
298
+ info_text = gr.Textbox(
299
+ label="Status",
300
+ interactive=False,
301
+ show_label=True,
302
+ max_lines=1,
303
+ elem_classes="info-bar",
304
+ value="Ready โ€” enter a prompt and click Generate",
305
+ )
306
+
307
+ # โ”€โ”€ ็ป‘ๅฎšไบ‹ไปถ โ”€โ”€
308
+ btn.click(
309
+ fn=generate,
310
+ inputs=[prompt, negative_prompt, w, h, steps, guidance, seed],
311
+ outputs=[output_img, seed, info_text],
312
+ )
313
+
314
+ # ๅ›ž่ฝฆไนŸ่ƒฝ่งฆๅ‘
315
+ prompt.submit(
316
+ fn=generate,
317
+ inputs=[prompt, negative_prompt, w, h, steps, guidance, seed],
318
+ outputs=[output_img, seed, info_text],
319
+ )
320
 
 
321
 
322
+ # โ”€โ”€ ๅฏๅŠจ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€
323
+ if __name__ == "__main__":
324
+ demo.launch(server_name="0.0.0.0", server_port=7860)