HOPStudio commited on
Commit
0f9f4fe
·
verified ·
1 Parent(s): dc85679

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +834 -562
main.py CHANGED
@@ -1,514 +1,688 @@
1
- import os
2
- import subprocess
3
- import threading
4
- import shutil
5
- import json
6
- import requests
7
- import gradio as gr
8
 
 
9
 
10
- REPO_URL = "https://hf-mirror.com/HOPStudio/modelscope"
11
- REPO_PATH = "/root/modelscope"
12
 
13
- FRP_VERSION = "0.69.0"
14
- CURRENT_DIR = REPO_PATH
15
- LAST_OUTPUT = ""
16
 
 
17
 
18
- def clone_repository():
19
- if os.path.exists(REPO_PATH):
20
- print(f"检测到已有仓库,正在删除: {REPO_PATH}")
21
- try:
22
- shutil.rmtree(REPO_PATH)
23
- print("旧仓库已删除。")
24
- except Exception as e:
25
- print(f"删除旧仓库失败: {e}")
26
- return False
27
 
28
- try:
29
- print(f"正在克隆仓库到 {REPO_PATH}...")
30
 
31
- env = os.environ.copy()
32
- env["GIT_LFS_SKIP_SMUDGE"] = "1"
33
- env["HF_HUB_DISABLE_XET"] = "1"
34
 
35
- subprocess.run(
36
- ["git", "clone", REPO_URL, REPO_PATH],
37
- check=True,
38
- env=env,
39
- timeout=120
40
- )
41
 
42
- print("仓库克隆成功。")
43
- return True
44
 
45
- except subprocess.TimeoutExpired:
46
- print("克隆仓库超时。")
47
- return False
48
 
49
- except subprocess.CalledProcessError as e:
50
- print(f"克隆仓库失败: {e}")
51
- return False
52
 
53
- except Exception as e:
54
- print(f"克隆仓库异常: {e}")
55
- return False
 
 
 
 
 
 
 
56
 
 
 
57
 
58
- def execute_shell_command(command):
59
- global CURRENT_DIR
 
60
 
61
- command = command.strip()
 
 
62
 
63
- if command == "pwd":
64
- return CURRENT_DIR
 
65
 
66
- if command.startswith("cd "):
67
- path = command[3:].strip()
68
- path = os.path.expanduser(path)
69
 
70
- if not os.path.isabs(path):
71
- path = os.path.join(CURRENT_DIR, path)
72
 
73
- path = os.path.abspath(path)
 
74
 
75
- if os.path.isdir(path):
76
- CURRENT_DIR = path
77
- return f"已切换目录: {CURRENT_DIR}"
78
 
79
- return f"目录不存在: {path}"
 
80
 
81
- try:
82
- result = subprocess.run(
83
- command,
84
- shell=True,
85
- cwd=CURRENT_DIR,
86
- stdout=subprocess.PIPE,
87
- stderr=subprocess.PIPE,
88
- text=True,
89
- timeout=120
90
- )
91
 
92
- output = result.stdout + result.stderr
 
 
93
 
94
- if not output.strip():
95
- output = "命令执行完成,无输出。"
96
 
97
- return f"当前目录: {CURRENT_DIR}\n\n{output}"
 
 
 
 
 
 
 
 
 
98
 
99
- except subprocess.TimeoutExpired:
100
- return "命令执行超时。"
101
 
102
- except Exception as e:
103
- return f"执行命令失败: {e}"
104
 
 
105
 
106
- def _api_call_impl(input_text, model="internlm2.5-latest", temperature=0.8, top_p=0.9, n=1):
107
- if input_text.startswith("cmd_run "):
108
- command = input_text[len("cmd_run "):].strip()
109
- return execute_shell_command(command)
110
 
111
- api_token = os.getenv("API_TOKEN")
 
112
 
113
- if not api_token:
114
- return "API_TOKEN 未设置。"
115
 
116
- url = "https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions"
117
 
118
- headers = {
119
- "Content-Type": "application/json",
120
- "Authorization": f"Bearer {api_token}"
121
- }
122
 
123
- data = {
124
- "model": model,
125
- "messages": [
126
- {
127
- "role": "user",
128
- "content": input_text
129
- }
130
- ],
131
- "n": int(n),
132
- "temperature": float(temperature),
133
- "top_p": float(top_p)
134
- }
135
 
136
- try:
137
- response = requests.post(
138
- url,
139
- headers=headers,
140
- data=json.dumps(data),
141
- timeout=60
142
- )
143
 
144
- response.raise_for_status()
145
- return response.json()["choices"][0]["message"]["content"]
 
 
 
 
 
 
 
 
 
 
146
 
147
- except Exception as e:
148
- return f"API 请求失败: {e}"
 
 
 
 
 
149
 
 
 
150
 
151
- def api_call(input_text, model="internlm2.5-latest", temperature=0.8, top_p=0.9, n=1):
152
- global LAST_OUTPUT
153
 
154
- LAST_OUTPUT = _api_call_impl(
155
- input_text,
156
- model,
157
- temperature,
158
- top_p,
159
- n
160
- )
161
 
162
- return LAST_OUTPUT
 
 
 
 
 
 
163
 
 
164
 
165
- def refresh_frontend():
166
- return LAST_OUTPUT
167
 
 
168
 
169
- def create_frpc_toml():
170
- os.makedirs(REPO_PATH, exist_ok=True)
171
 
172
- frpc_toml_path = os.path.join(REPO_PATH, "frpc.toml")
173
-
174
- toml_content = """
175
- serverAddr = "103.71.69.203"
176
- serverPort = 7000
177
- auth.token = "hop20030219"
178
- [[proxies]]
179
- name = "jupyterlab"
180
- type = "tcp"
181
- localIP = "127.0.0.1"
182
- localPort = 18888
183
- remotePort = 18888
184
- [[proxies]]
185
- name = "comfyui"
186
- type = "tcp"
187
- localIP = "127.0.0.1"
188
- localPort = 8188
189
- remotePort = 8188
190
- """
191
 
192
- try:
193
- with open(frpc_toml_path, "w", encoding="utf-8") as f:
194
- f.write(toml_content)
195
 
196
- print(f"frpc.toml 已创建: {frpc_toml_path}")
 
 
197
 
198
- except Exception as e:
199
- print(f"创建 frpc.toml 失败: {e}")
200
 
 
 
201
 
202
- def install_frp():
203
- frp_tar_name = f"frp_{FRP_VERSION}_linux_amd64.tar.gz"
204
- frp_dir_name = f"frp_{FRP_VERSION}_linux_amd64"
205
 
206
- frp_tar_path = os.path.join(REPO_PATH, frp_tar_name)
207
- frp_install_path = os.path.join(REPO_PATH, frp_dir_name)
208
 
209
- frp_download_urls = [
210
- f"https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/{frp_tar_name}",
211
- f"https://gh.llkk.cc/https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/{frp_tar_name}",
212
- f"https://gh-proxy.com/https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/{frp_tar_name}"
213
- ]
214
 
215
- if os.path.exists(frp_install_path):
216
- print(f"frp 已安装: {frp_install_path}")
217
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
- def is_valid_tar_gz(path):
220
- if not os.path.exists(path):
221
- return False
222
 
 
223
  try:
 
 
224
  result = subprocess.run(
225
- ["tar", "-tzf", path],
 
 
 
 
 
 
 
 
226
  stdout=subprocess.PIPE,
227
  stderr=subprocess.PIPE,
228
  text=True,
229
- timeout=20
230
  )
231
- return result.returncode == 0
232
- except Exception:
233
- return False
234
 
235
- if os.path.exists(frp_tar_path) and not is_valid_tar_gz(frp_tar_path):
236
- print("检测到损坏的 frp 压缩包,正在删除。")
237
- os.remove(frp_tar_path)
 
238
 
239
- if not os.path.exists(frp_tar_path):
240
- print("开始下载 frp。")
241
 
242
- download_success = False
 
243
 
244
- for url in frp_download_urls:
245
- try:
246
- print(f"尝试下载: {url}")
247
-
248
- result = subprocess.run(
249
- [
250
- "wget",
251
- "--timeout=30",
252
- "--tries=1",
253
- "--no-check-certificate",
254
- "-O",
255
- frp_tar_path,
256
- url
257
- ],
258
- stdout=subprocess.PIPE,
259
- stderr=subprocess.PIPE,
260
- text=True,
261
- timeout=90
262
- )
263
 
264
- if result.returncode == 0 and is_valid_tar_gz(frp_tar_path):
265
- print("frp 下载成功。")
266
- download_success = True
267
- break
268
 
269
- print("下载失败或文件无效。")
 
270
 
271
- if os.path.exists(frp_tar_path):
272
- os.remove(frp_tar_path)
273
 
274
- except subprocess.TimeoutExpired:
275
- print("frp 下载超时。")
 
276
 
277
- if os.path.exists(frp_tar_path):
278
- os.remove(frp_tar_path)
279
 
280
- except Exception as e:
281
- print(f"frp 下载异常: {e}")
 
 
 
282
 
283
- if os.path.exists(frp_tar_path):
284
- os.remove(frp_tar_path)
285
 
286
- if not download_success:
287
- print("frp 下载失败,跳过 frp。")
288
- return False
289
 
290
- try:
291
- print(f"正在解压 frp {REPO_PATH}...")
292
 
293
- subprocess.run(
294
- ["tar", "-xzf", frp_tar_path, "-C", REPO_PATH],
295
- check=True,
296
- timeout=30
297
- )
298
 
299
- frpc_path = os.path.join(frp_install_path, "frpc")
300
- frps_path = os.path.join(frp_install_path, "frps")
 
301
 
302
- if os.path.exists(frpc_path):
303
- subprocess.run(["chmod", "+x", frpc_path], check=False)
 
304
 
305
- if os.path.exists(frps_path):
306
- subprocess.run(["chmod", "+x", frps_path], check=False)
 
307
 
308
- print("frp 安装成功。")
309
- return True
310
 
311
- except subprocess.TimeoutExpired:
312
- print("frp 解压超时。")
313
- return False
314
 
315
- except subprocess.CalledProcessError as e:
316
- print(f"frp 解压失败: {e}")
317
- return False
318
 
319
- except Exception as e:
320
- print(f"frp 安装异常: {e}")
321
- return False
322
 
 
 
 
 
 
 
323
 
324
- def start_frp():
325
- frp_dir_name = f"frp_{FRP_VERSION}_linux_amd64"
326
 
327
- frpc_path = os.path.join(REPO_PATH, frp_dir_name, "frpc")
328
- frpc_config_path = os.path.join(REPO_PATH, "frpc.toml")
329
 
330
- if not os.path.exists(frpc_path):
331
- print(f"frpc 不存在: {frpc_path}")
332
- return
333
 
334
- if not os.path.exists(frpc_config_path):
335
- create_frpc_toml()
 
336
 
337
- try:
338
- subprocess.Popen(
339
- [frpc_path, "-c", frpc_config_path],
340
- stdout=subprocess.DEVNULL,
341
- stderr=subprocess.DEVNULL
342
- )
343
 
344
- print("frp 已启动。")
345
 
346
- except Exception as e:
347
- print(f"启动 frp 失败: {e}")
348
 
349
 
350
- def start_jupyterlab_process():
351
- jupyter_script = os.path.join(REPO_PATH, "start_jupyterlab.py")
352
 
353
- if not os.path.exists(jupyter_script):
354
- print(f"未找到 JupyterLab 启动脚本: {jupyter_script}")
355
- return
356
 
357
- try:
358
- subprocess.Popen(
359
- ["python3", jupyter_script],
360
- stdout=subprocess.DEVNULL,
361
- stderr=subprocess.DEVNULL
362
- )
363
 
364
- print("JupyterLab 已启动。")
 
 
 
 
 
 
365
 
366
- except Exception as e:
367
- print(f"启动 JupyterLab 失败: {e}")
368
 
 
369
 
370
- # ===== 新增:ModelScope 下载 comfy_portable.zip解压后使用自带 Python 启动 ComfyUI =====
371
- MODELSCOPE_MODEL_ID = "HOPStudio/forgeui"
372
- COMFY_PORTABLE_ZIP_NAME = "comfy_portable.zip"
373
- COMFY_PORTABLE_ZIP_PATH = os.path.join("/root", COMFY_PORTABLE_ZIP_NAME)
374
- COMFY_PORTABLE_DIR = "/root/comfy_portable"
375
- COMFY_PORTABLE_PYTHON = os.path.join(COMFY_PORTABLE_DIR, "python", "bin", "python")
376
- COMFYUI_MAIN = os.path.join(COMFY_PORTABLE_DIR, "ComfyUI", "main.py")
377
- COMFYUI_PORT = 8188
378
- COMFYUI_LOG_PATH = os.path.join(REPO_PATH, "comfyui.log")
379
- PROXY_URL = "http://103.71.69.203:17890"
380
- MODELSCOPE_DOWNLOAD_TIMEOUT = 21600
381
-
382
-
383
- def get_proxy_env():
384
- env = os.environ.copy()
385
 
 
 
 
 
 
 
 
 
 
 
 
386
  env["http_proxy"] = PROXY_URL
387
  env["https_proxy"] = PROXY_URL
388
  env["HTTP_PROXY"] = PROXY_URL
389
  env["HTTPS_PROXY"] = PROXY_URL
390
  env["ALL_PROXY"] = PROXY_URL
391
- env["no_proxy"] = "localhost,127.0.0.1"
392
- env["NO_PROXY"] = "localhost,127.0.0.1"
 
 
393
 
394
- return env
 
 
395
 
 
396
 
397
- def get_modelscope_env(use_proxy=False):
398
- env = os.environ.copy()
399
- env["MODELSCOPE_ENDPOINT"] = "https://www.modelscope.cn"
400
- env["MODELSCOPE_SDK_ENDPOINT"] = "https://www.modelscope.cn"
401
-
402
- proxy_keys = [
403
- "http_proxy",
404
- "https_proxy",
405
- "HTTP_PROXY",
406
- "HTTPS_PROXY",
407
- "ALL_PROXY",
408
- "all_proxy"
409
- ]
410
 
411
- if use_proxy:
412
- env["http_proxy"] = PROXY_URL
413
- env["https_proxy"] = PROXY_URL
414
- env["HTTP_PROXY"] = PROXY_URL
415
- env["HTTPS_PROXY"] = PROXY_URL
416
- env["ALL_PROXY"] = PROXY_URL
417
- env["all_proxy"] = PROXY_URL
418
- else:
419
- for key in proxy_keys:
420
- env.pop(key, None)
421
 
422
- env["no_proxy"] = "localhost,127.0.0.1"
423
- env["NO_PROXY"] = "localhost,127.0.0.1"
 
424
 
425
- return env
 
 
 
 
426
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
 
428
- def configure_git_proxy():
429
  try:
430
- subprocess.run(
431
- ["git", "config", "--global", "http.proxy", PROXY_URL],
432
- check=False,
433
- stdout=subprocess.DEVNULL,
434
- stderr=subprocess.DEVNULL,
435
- timeout=10
436
- )
437
- subprocess.run(
438
- ["git", "config", "--global", "https.proxy", PROXY_URL],
439
- check=False,
440
- stdout=subprocess.DEVNULL,
441
- stderr=subprocess.DEVNULL,
442
- timeout=10
443
  )
444
- print("git 代理已配置。")
 
 
 
 
 
 
 
 
 
 
 
 
445
  except Exception as e:
446
- print(f"配置 git ��理失败: {e}")
447
 
 
 
448
 
449
- def is_valid_zip(path):
450
- if not os.path.exists(path):
451
- return False
452
 
453
- try:
454
- import zipfile
455
- if not zipfile.is_zipfile(path):
456
- return False
457
 
458
- with zipfile.ZipFile(path, "r") as zf:
459
- return len(zf.namelist()) > 0
460
- except Exception:
461
- return False
462
 
 
463
 
464
- def is_comfy_portable_ready():
465
- return os.path.exists(COMFY_PORTABLE_PYTHON) and os.path.exists(COMFYUI_MAIN)
466
 
 
 
467
 
468
- def download_comfy_portable_from_modelscope():
469
- if is_comfy_portable_ready():
470
- print(f"检测到已解压的 comfy_portable: {COMFY_PORTABLE_DIR}")
471
- return True
472
 
473
- if is_valid_zip(COMFY_PORTABLE_ZIP_PATH):
474
- print(f"检测到已有 comfy_portable 压缩包: {COMFY_PORTABLE_ZIP_PATH}")
475
- return True
476
 
477
- if os.path.exists(COMFY_PORTABLE_ZIP_PATH):
478
- print(f"检测到无效 comfy_portable 压缩包,正在删除: {COMFY_PORTABLE_ZIP_PATH}")
479
- try:
480
- os.remove(COMFY_PORTABLE_ZIP_PATH)
481
- except Exception as e:
482
- print(f"删除无效压缩包失败: {e}")
483
- return False
484
 
485
- if not shutil.which("modelscope"):
486
- print("未检测到 modelscope 命令。为避免改动系统 Python 依赖,本脚本不会自动安装 modelscope。")
487
- print("请先在外部环境安装好 modelscope CLI,或手动把 comfy_portable.zip 放到 /root/comfy_portable.zip。")
488
- return False
489
 
490
- commands = [
491
- (False, "直连"),
492
- (True, "代理")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
 
495
- for use_proxy, mode_name in commands:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  command = [
497
- "modelscope",
498
- "download",
499
- "--model",
500
- MODELSCOPE_MODEL_ID,
501
- COMFY_PORTABLE_ZIP_NAME,
502
- "--local_dir",
503
- "/root"
504
  ]
505
 
506
  try:
507
- print(f"执行 ModelScope 下载 comfy_portable.zip [{mode_name}]: " + " ".join(command))
508
 
509
  result = subprocess.run(
510
  command,
511
- env=get_modelscope_env(use_proxy=use_proxy),
512
  stdout=subprocess.PIPE,
513
  stderr=subprocess.PIPE,
514
  text=True,
@@ -520,248 +694,346 @@ def download_comfy_portable_from_modelscope():
520
  if result.stderr.strip():
521
  print(result.stderr[-4000:])
522
 
523
- if result.returncode == 0 and is_valid_zip(COMFY_PORTABLE_ZIP_PATH):
524
- print(f"comfy_portable.zip 下载完成: {COMFY_PORTABLE_ZIP_PATH}")
525
- return True
526
 
527
- print("本次 ModelScope 下载失败或压缩包无效,继续尝试下一个方式。")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528
 
529
  except subprocess.TimeoutExpired:
530
- print("ModelScope 下载 comfy_portable.zip 超时,继续尝试下一个方式。")
531
  except Exception as e:
532
- print(f"ModelScope 下载 comfy_portable.zip 异常: {e}")
533
 
534
- print("ModelScope 下载 comfy_portable.zip 失败。")
535
- return False
536
 
 
537
 
538
- def extract_comfy_portable():
539
- if is_comfy_portable_ready():
540
- print(f"comfy_portable 已存在,跳过解压: {COMFY_PORTABLE_DIR}")
541
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
543
- if not is_valid_zip(COMFY_PORTABLE_ZIP_PATH):
544
- print(f"comfy_portable.zip 不存在或无效: {COMFY_PORTABLE_ZIP_PATH}")
545
- return False
546
 
547
- if os.path.exists(COMFY_PORTABLE_DIR):
548
- print(f"检测到不完整的 comfy_portable 目录,正在删除后重新解压: {COMFY_PORTABLE_DIR}")
549
- try:
550
- shutil.rmtree(COMFY_PORTABLE_DIR)
551
- except Exception as e:
552
- print(f"删除不完整 comfy_portable 目录失败: {e}")
553
- return False
 
554
 
555
- try:
556
- print(f"正在解压 comfy_portable.zip 到 /root: {COMFY_PORTABLE_ZIP_PATH}")
 
 
 
 
 
557
 
558
- import zipfile
559
- with zipfile.ZipFile(COMFY_PORTABLE_ZIP_PATH, "r") as zf:
560
- zf.extractall("/root")
 
561
 
562
- if is_comfy_portable_ready():
563
- print(f"comfy_portable 解压完成: {COMFY_PORTABLE_DIR}")
564
- return True
 
 
565
 
566
- print("解压完成,但未找到 /root/comfy_portable/python/bin/python 或 /root/comfy_portable/ComfyUI/main.py。")
567
- return False
568
 
 
 
569
  except Exception as e:
570
- print(f"解压 comfy_portable.zip 失败: {e}")
571
- return False
572
 
 
573
 
574
- def prepare_comfy_portable():
575
- if is_comfy_portable_ready():
576
- print(f"comfy_portable 已准备完成: {COMFY_PORTABLE_DIR}")
577
- return True
578
 
579
- if not download_comfy_portable_from_modelscope():
580
- return False
 
 
581
 
582
- return extract_comfy_portable()
583
 
 
 
 
584
 
585
- def start_comfyui_process():
586
- if not prepare_comfy_portable():
587
- print("comfy_portable 准备失败,跳过 ComfyUI 启动。")
588
- return
589
 
590
- if not os.path.exists(COMFY_PORTABLE_PYTHON):
591
- print(f"未找到便携 Python: {COMFY_PORTABLE_PYTHON}")
592
- return
593
 
594
- if not os.path.exists(COMFYUI_MAIN):
595
- print(f"未找到 ComfyUI main.py: {COMFYUI_MAIN}")
596
- return
597
 
598
- configure_git_proxy()
599
 
600
- python_lib_dir = os.path.join(COMFY_PORTABLE_DIR, "python", "lib")
601
- comfyui_dir = os.path.join(COMFY_PORTABLE_DIR, "ComfyUI")
 
 
 
 
602
 
603
- command = f"""
604
- set -e
605
- export LD_LIBRARY_PATH=\"{python_lib_dir}:${{LD_LIBRARY_PATH:-}}\"
606
- export http_proxy={PROXY_URL}
607
- export https_proxy={PROXY_URL}
608
- export HTTP_PROXY={PROXY_URL}
609
- export HTTPS_PROXY={PROXY_URL}
610
- export ALL_PROXY={PROXY_URL}
611
- export no_proxy=localhost,127.0.0.1
612
- export NO_PROXY=localhost,127.0.0.1
613
- git config --global http.proxy {PROXY_URL}
614
- git config --global https.proxy {PROXY_URL}
615
- cd \"{comfyui_dir}\"
616
- \"{COMFY_PORTABLE_PYTHON}\" main.py --listen 0.0.0.0 --port {COMFYUI_PORT}
617
- """
618
 
619
- try:
620
- os.makedirs(REPO_PATH, exist_ok=True)
621
- log_file = open(COMFYUI_LOG_PATH, "a", encoding="utf-8")
622
-
623
- process = subprocess.Popen(
624
- ["bash", "-lc", command],
625
- cwd=COMFY_PORTABLE_DIR,
626
- env=get_proxy_env(),
627
- stdout=log_file,
628
- stderr=subprocess.STDOUT
629
- )
630
 
631
- import time
632
- time.sleep(3)
 
633
 
634
- if process.poll() is None:
635
- print(f"ComfyUI 已启动,端口: {COMFYUI_PORT},日志: {COMFYUI_LOG_PATH}")
636
- else:
637
- print(f"ComfyUI 启动后立即退出,返回码: {process.returncode},请查看日志: {COMFYUI_LOG_PATH}")
638
 
639
- except Exception as e:
640
- print(f"启动 ComfyUI 失败: {e}")
641
 
 
 
 
 
642
 
643
- def start_gradio():
644
- with gr.Blocks() as demo:
645
- gr.Markdown("# InternLM 控制台")
646
 
647
- input_box = gr.Textbox(
648
- label="输入",
649
- placeholder="输入对话内容"
650
- )
651
 
652
- model_box = gr.Textbox(
653
- value="internlm2.5-latest",
654
- label="模型"
655
- )
656
 
657
- temperature_slider = gr.Slider(
658
- minimum=0,
659
- maximum=1,
660
- value=0.8,
661
- step=0.01,
662
- label="Temperature"
663
- )
664
 
665
- top_p_slider = gr.Slider(
666
- minimum=0,
667
- maximum=1,
668
- value=0.9,
669
- step=0.01,
670
- label="Top-p"
671
- )
672
 
673
- n_slider = gr.Slider(
674
- minimum=1,
675
- maximum=10,
676
- value=1,
677
- step=1,
678
- label="生成回复数量"
679
- )
680
 
681
- with gr.Row():
682
- send_button = gr.Button("发送", variant="primary")
683
- refresh_button = gr.Button("刷新前端")
684
 
685
- output_box = gr.Textbox(
686
- label="回复",
687
- lines=20,
688
- interactive=False
689
- )
690
 
691
- send_button.click(
692
- api_call,
693
- inputs=[
694
- input_box,
695
- model_box,
696
- temperature_slider,
697
- top_p_slider,
698
- n_slider
699
- ],
700
- outputs=output_box
701
- )
702
 
703
- refresh_button.click(
704
- refresh_frontend,
705
- outputs=output_box
706
- )
707
 
708
- timer = gr.Timer(5)
709
 
710
- timer.tick(
711
- refresh_frontend,
712
- outputs=output_box
713
- )
714
 
715
- print("Gradio 正在启动。")
716
 
717
- demo.launch(
718
- server_name="0.0.0.0",
719
- server_port=7860,
720
- share=False
 
 
 
 
 
 
721
  )
722
 
 
723
 
724
- def main():
725
- global CURRENT_DIR
 
 
726
 
727
- clone_success = clone_repository()
 
728
 
729
- if clone_success:
730
- CURRENT_DIR = REPO_PATH
731
 
732
- install_frp()
733
- create_frpc_toml()
 
 
734
 
735
- try:
736
- frp_thread = threading.Thread(
737
- target=start_frp,
738
- daemon=True
739
- )
740
- frp_thread.start()
741
- except Exception as e:
742
- print(f"frp 线程启动失败: {e}")
743
 
744
- try:
745
- jupyter_thread = threading.Thread(
746
- target=start_jupyterlab_process,
747
- daemon=True
748
- )
749
- jupyter_thread.start()
750
- except Exception as e:
751
- print(f"JupyterLab 线程启动失败: {e}")
752
 
753
- try:
754
- comfyui_thread = threading.Thread(
755
- target=start_comfyui_process,
756
- daemon=True
757
- )
758
- comfyui_thread.start()
759
- except Exception as e:
760
- print(f"ComfyUI 线程启动失败: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
 
762
- print("准备启动 Gradio...")
763
- start_gradio()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
 
 
 
765
 
766
- if __name__ == "__main__":
767
- main()
 
1
+ import osimport subprocessimport threadingimport shutilimport jsonimport sysimport timeimport zipfileimport reimport requestsimport gradio as gr
 
 
 
 
 
 
2
 
3
+ REPO_URL = "https://hf-mirror.com/HOPStudio/modelscope"REPO_PATH = "/root/modelscope"
4
 
5
+ FRP_VERSION = "0.69.0"CURRENT_DIR = REPO_PATHLAST_OUTPUT = ""
 
6
 
7
+ ===== 新增:ModelScope 整库克隆/下载并启动 ComfyUI 的配置 =====
 
 
8
 
9
+ MODELSCOPE_MODEL_ID = "HOPStudio/forgeui"
10
 
11
+ forgeui 整库和 offline_build 下载包都很大,不放在 REPO_PATH 里,避免 clone_repository() 删除 /root/modelscope 时把断点文件删掉。
 
 
 
 
 
 
 
 
12
 
13
+ OFFLINE_BUILD_CACHE_DIR = "/root/offline_build_cache"MODELSCOPE_DOWNLOAD_DIR = os.path.join(OFFLINE_BUILD_CACHE_DIR, "modelscope_download")MODELSCOPE_CACHE_DIR = os.path.join(OFFLINE_BUILD_CACHE_DIR, "modelscope_cache")OFFLINE_BUILD_DIR = os.path.join(REPO_PATH, "offline_build")COMFYUI_PORT = 8188COMFYUI_LOG_PATH = os.path.join(REPO_PATH, "comfyui.log")PROXY_URL = "http://103.71.69.203:17890"MODELSCOPE_DOWNLOAD_TIMEOUT = 21600
 
14
 
15
+ 兼容可能的文件名拼写:用户当前指定 offlinbuild.zip,
 
 
16
 
17
+ 同时兼容 offlinebuild.zip / offline_build.zip,避免文件名略有差异时直接失败。
 
 
 
 
 
18
 
19
+ OFFLINE_BUILD_ZIP_CANDIDATES = ["offlinbuild.zip","offlinebuild.zip","offline_build.zip"]
 
20
 
21
+ def clone_repository():if os.path.exists(REPO_PATH):print(f"检测到已有仓库,正在删除: {REPO_PATH}")try:shutil.rmtree(REPO_PATH)print("旧仓库已删除。")except Exception as e:print(f"删除旧仓库失败: {e}")return False
 
 
22
 
23
+ try:
24
+ print(f"正在克隆仓库 {REPO_PATH}...")
 
25
 
26
+ env = os.environ.copy()
27
+ env["GIT_LFS_SKIP_SMUDGE"] = "1"
28
+ env["HF_HUB_DISABLE_XET"] = "1"
29
+
30
+ subprocess.run(
31
+ ["git", "clone", REPO_URL, REPO_PATH],
32
+ check=True,
33
+ env=env,
34
+ timeout=120
35
+ )
36
 
37
+ print("仓库克隆成功。")
38
+ return True
39
 
40
+ except subprocess.TimeoutExpired:
41
+ print("克隆仓库超时。")
42
+ return False
43
 
44
+ except subprocess.CalledProcessError as e:
45
+ print(f"克隆仓库失败: {e}")
46
+ return False
47
 
48
+ except Exception as e:
49
+ print(f"克隆仓库异常: {e}")
50
+ return False
51
 
52
+ def execute_shell_command(command):global CURRENT_DIR
 
 
53
 
54
+ command = command.strip()
 
55
 
56
+ if command == "pwd":
57
+ return CURRENT_DIR
58
 
59
+ if command.startswith("cd "):
60
+ path = command[3:].strip()
61
+ path = os.path.expanduser(path)
62
 
63
+ if not os.path.isabs(path):
64
+ path = os.path.join(CURRENT_DIR, path)
65
 
66
+ path = os.path.abspath(path)
 
 
 
 
 
 
 
 
 
67
 
68
+ if os.path.isdir(path):
69
+ CURRENT_DIR = path
70
+ return f"已切换目录: {CURRENT_DIR}"
71
 
72
+ return f"目录不存在: {path}"
 
73
 
74
+ try:
75
+ result = subprocess.run(
76
+ command,
77
+ shell=True,
78
+ cwd=CURRENT_DIR,
79
+ stdout=subprocess.PIPE,
80
+ stderr=subprocess.PIPE,
81
+ text=True,
82
+ timeout=120
83
+ )
84
 
85
+ output = result.stdout + result.stderr
 
86
 
87
+ if not output.strip():
88
+ output = "命令执行完成,无输出。"
89
 
90
+ return f"当前目录: {CURRENT_DIR}\n\n{output}"
91
 
92
+ except subprocess.TimeoutExpired:
93
+ return "命令执行超时。"
 
 
94
 
95
+ except Exception as e:
96
+ return f"执行命令失败: {e}"
97
 
98
+ def _api_call_impl(input_text, model="internlm2.5-latest", temperature=0.8, top_p=0.9, n=1):if input_text.startswith("cmd_run "):command = input_text[len("cmd_run "):].strip()return execute_shell_command(command)
 
99
 
100
+ api_token = os.getenv("API_TOKEN")
101
 
102
+ if not api_token:
103
+ return "API_TOKEN 未设置。"
 
 
104
 
105
+ url = "https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions"
 
 
 
 
 
 
 
 
 
 
 
106
 
107
+ headers = {
108
+ "Content-Type": "application/json",
109
+ "Authorization": f"Bearer {api_token}"
110
+ }
 
 
 
111
 
112
+ data = {
113
+ "model": model,
114
+ "messages": [
115
+ {
116
+ "role": "user",
117
+ "content": input_text
118
+ }
119
+ ],
120
+ "n": int(n),
121
+ "temperature": float(temperature),
122
+ "top_p": float(top_p)
123
+ }
124
 
125
+ try:
126
+ response = requests.post(
127
+ url,
128
+ headers=headers,
129
+ data=json.dumps(data),
130
+ timeout=60
131
+ )
132
 
133
+ response.raise_for_status()
134
+ return response.json()["choices"][0]["message"]["content"]
135
 
136
+ except Exception as e:
137
+ return f"API 请求失败: {e}"
138
 
139
+ def api_call(input_text, model="internlm2.5-latest", temperature=0.8, top_p=0.9, n=1):global LAST_OUTPUT
 
 
 
 
 
 
140
 
141
+ LAST_OUTPUT = _api_call_impl(
142
+ input_text,
143
+ model,
144
+ temperature,
145
+ top_p,
146
+ n
147
+ )
148
 
149
+ return LAST_OUTPUT
150
 
151
+ def refresh_frontend():return LAST_OUTPUT
 
152
 
153
+ def create_frpc_toml():os.makedirs(REPO_PATH, exist_ok=True)
154
 
155
+ frpc_toml_path = os.path.join(REPO_PATH, "frpc.toml")
 
156
 
157
+ toml_content = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ serverAddr = "103.71.69.203"serverPort = 7000auth.token = "hop20030219"[[proxies]]name = "jupyterlab"type = "tcp"localIP = "127.0.0.1"localPort = 18888remotePort = 18888[[proxies]]name = "comfyui"type = "tcp"localIP = "127.0.0.1"localPort = 8188remotePort = 8188"""
 
 
160
 
161
+ try:
162
+ with open(frpc_toml_path, "w", encoding="utf-8") as f:
163
+ f.write(toml_content)
164
 
165
+ print(f"frpc.toml 已创建: {frpc_toml_path}")
 
166
 
167
+ except Exception as e:
168
+ print(f"创建 frpc.toml 失败: {e}")
169
 
170
+ def install_frp():frp_tar_name = f"frp_{FRP_VERSION}linux_amd64.tar.gz"frp_dir_name = f"frp{FRP_VERSION}_linux_amd64"
 
 
171
 
172
+ frp_tar_path = os.path.join(REPO_PATH, frp_tar_name)
173
+ frp_install_path = os.path.join(REPO_PATH, frp_dir_name)
174
 
175
+ frp_download_urls = [
176
+ f"https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/{frp_tar_name}",
177
+ f"https://gh.llkk.cc/https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/{frp_tar_name}",
178
+ f"https://gh-proxy.com/https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/{frp_tar_name}"
179
+ ]
180
 
181
+ if os.path.exists(frp_install_path):
182
+ print(f"frp 已安装: {frp_install_path}")
183
+ return True
184
+
185
+ def is_valid_tar_gz(path):
186
+ if not os.path.exists(path):
187
+ return False
188
+
189
+ try:
190
+ result = subprocess.run(
191
+ ["tar", "-tzf", path],
192
+ stdout=subprocess.PIPE,
193
+ stderr=subprocess.PIPE,
194
+ text=True,
195
+ timeout=20
196
+ )
197
+ return result.returncode == 0
198
+ except Exception:
199
+ return False
200
+
201
+ if os.path.exists(frp_tar_path) and not is_valid_tar_gz(frp_tar_path):
202
+ print("检测到损坏的 frp 压缩包,正在删除。")
203
+ os.remove(frp_tar_path)
204
+
205
+ if not os.path.exists(frp_tar_path):
206
+ print("开始下载 frp。")
207
 
208
+ download_success = False
 
 
209
 
210
+ for url in frp_download_urls:
211
  try:
212
+ print(f"尝试下载: {url}")
213
+
214
  result = subprocess.run(
215
+ [
216
+ "wget",
217
+ "--timeout=30",
218
+ "--tries=1",
219
+ "--no-check-certificate",
220
+ "-O",
221
+ frp_tar_path,
222
+ url
223
+ ],
224
  stdout=subprocess.PIPE,
225
  stderr=subprocess.PIPE,
226
  text=True,
227
+ timeout=90
228
  )
 
 
 
229
 
230
+ if result.returncode == 0 and is_valid_tar_gz(frp_tar_path):
231
+ print("frp 下载成功。")
232
+ download_success = True
233
+ break
234
 
235
+ print("下载失败或文件无效。")
 
236
 
237
+ if os.path.exists(frp_tar_path):
238
+ os.remove(frp_tar_path)
239
 
240
+ except subprocess.TimeoutExpired:
241
+ print("frp 下载超时。")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
+ if os.path.exists(frp_tar_path):
244
+ os.remove(frp_tar_path)
 
 
245
 
246
+ except Exception as e:
247
+ print(f"frp 下载异常: {e}")
248
 
249
+ if os.path.exists(frp_tar_path):
250
+ os.remove(frp_tar_path)
251
 
252
+ if not download_success:
253
+ print("frp 下载失败,跳过 frp。")
254
+ return False
255
 
256
+ try:
257
+ print(f"正在解压 frp 到 {REPO_PATH}...")
258
 
259
+ subprocess.run(
260
+ ["tar", "-xzf", frp_tar_path, "-C", REPO_PATH],
261
+ check=True,
262
+ timeout=30
263
+ )
264
 
265
+ frpc_path = os.path.join(frp_install_path, "frpc")
266
+ frps_path = os.path.join(frp_install_path, "frps")
267
 
268
+ if os.path.exists(frpc_path):
269
+ subprocess.run(["chmod", "+x", frpc_path], check=False)
 
270
 
271
+ if os.path.exists(frps_path):
272
+ subprocess.run(["chmod", "+x", frps_path], check=False)
273
 
274
+ print("frp 安装成功。")
275
+ return True
 
 
 
276
 
277
+ except subprocess.TimeoutExpired:
278
+ print("frp 解压超时。")
279
+ return False
280
 
281
+ except subprocess.CalledProcessError as e:
282
+ print(f"frp 解压失败: {e}")
283
+ return False
284
 
285
+ except Exception as e:
286
+ print(f"frp 安装异常: {e}")
287
+ return False
288
 
289
+ def start_frp():frp_dir_name = f"frp_{FRP_VERSION}_linux_amd64"
 
290
 
291
+ frpc_path = os.path.join(REPO_PATH, frp_dir_name, "frpc")
292
+ frpc_config_path = os.path.join(REPO_PATH, "frpc.toml")
 
293
 
294
+ if not os.path.exists(frpc_path):
295
+ print(f"frpc 不存在: {frpc_path}")
296
+ return
297
 
298
+ if not os.path.exists(frpc_config_path):
299
+ create_frpc_toml()
 
300
 
301
+ try:
302
+ subprocess.Popen(
303
+ [frpc_path, "-c", frpc_config_path],
304
+ stdout=subprocess.DEVNULL,
305
+ stderr=subprocess.DEVNULL
306
+ )
307
 
308
+ print("frp 已启动。")
 
309
 
310
+ except Exception as e:
311
+ print(f"启动 frp 失败: {e}")
312
 
313
+ def start_jupyterlab_process():jupyter_script = os.path.join(REPO_PATH, "start_jupyterlab.py")
 
 
314
 
315
+ if not os.path.exists(jupyter_script):
316
+ print(f"未找到 JupyterLab 启动脚本: {jupyter_script}")
317
+ return
318
 
319
+ try:
320
+ subprocess.Popen(
321
+ ["python3", jupyter_script],
322
+ stdout=subprocess.DEVNULL,
323
+ stderr=subprocess.DEVNULL
324
+ )
325
 
326
+ print("JupyterLab 已启动。")
327
 
328
+ except Exception as e:
329
+ print(f"启动 JupyterLab 失败: {e}")
330
 
331
 
 
 
332
 
333
+ ===== 新增:ModelScope 整库克隆/下载 offline_build 并启动 ComfyUI =====
 
 
334
 
335
+ def get_proxy_env():env = os.environ.copy()
 
 
 
 
 
336
 
337
+ env["http_proxy"] = PROXY_URL
338
+ env["https_proxy"] = PROXY_URL
339
+ env["HTTP_PROXY"] = PROXY_URL
340
+ env["HTTPS_PROXY"] = PROXY_URL
341
+ env["ALL_PROXY"] = PROXY_URL
342
+ env["no_proxy"] = "localhost,127.0.0.1"
343
+ env["NO_PROXY"] = "localhost,127.0.0.1"
344
 
345
+ return env
 
346
 
347
+ def get_modelscope_download_env(use_proxy=False):env = os.environ.copy()
348
 
349
+ # 固定 ModelScope 缓存目录,避免默认缓存散落到 ~/.cache且方便断点续传。
350
+ os.makedirs(MODELSCOPE_CACHE_DIR, exist_ok=True)
351
+ env["MODELSCOPE_CACHE"] = MODELSCOPE_CACHE_DIR
352
+ env["MODELSCOPE_ENDPOINT"] = "https://www.modelscope.cn"
353
+ env["MODELSCOPE_SDK_ENDPOINT"] = "https://www.modelscope.cn"
 
 
 
 
 
 
 
 
 
 
354
 
355
+ # ModelScope 下载大文件时会跳到阿里云 OSS。这里默认直连,避免代理把 OSS 内网地址转坏。
356
+ proxy_keys = [
357
+ "http_proxy",
358
+ "https_proxy",
359
+ "HTTP_PROXY",
360
+ "HTTPS_PROXY",
361
+ "ALL_PROXY",
362
+ "all_proxy"
363
+ ]
364
+
365
+ if use_proxy:
366
  env["http_proxy"] = PROXY_URL
367
  env["https_proxy"] = PROXY_URL
368
  env["HTTP_PROXY"] = PROXY_URL
369
  env["HTTPS_PROXY"] = PROXY_URL
370
  env["ALL_PROXY"] = PROXY_URL
371
+ env["all_proxy"] = PROXY_URL
372
+ else:
373
+ for key in proxy_keys:
374
+ env.pop(key, None)
375
 
376
+ no_proxy_value = "localhost,127.0.0.1,modelscope.cn,www.modelscope.cn,.modelscope.cn,aliyuncs.com,.aliyuncs.com"
377
+ env["no_proxy"] = no_proxy_value
378
+ env["NO_PROXY"] = no_proxy_value
379
 
380
+ return env
381
 
382
+ def configure_git_proxy():try:subprocess.run(["git", "config", "--global", "http.proxy", PROXY_URL],check=False,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=10)subprocess.run(["git", "config", "--global", "https.proxy", PROXY_URL],check=False,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=10)print("git 代理已配置。")except Exception as e:print(f"配置 git 代理失败: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
383
 
384
+ def is_valid_zip(path):if not os.path.exists(path):return False
 
 
 
 
 
 
 
 
 
385
 
386
+ try:
387
+ if not zipfile.is_zipfile(path):
388
+ return False
389
 
390
+ with zipfile.ZipFile(path, "r") as zf:
391
+ # 只读取目录,不做全量 testzip,避免大压缩包校验耗时过长。
392
+ return len(zf.namelist()) > 0
393
+ except Exception:
394
+ return False
395
 
396
+ def find_existing_offline_zip():search_dirs = [MODELSCOPE_DOWNLOAD_DIR, OFFLINE_BUILD_CACHE_DIR, REPO_PATH]
397
+
398
+ for directory in search_dirs:
399
+ if not os.path.isdir(directory):
400
+ continue
401
+
402
+ for zip_name in OFFLINE_BUILD_ZIP_CANDIDATES:
403
+ zip_path = os.path.join(directory, zip_name)
404
+ if is_valid_zip(zip_path):
405
+ return zip_path
406
+
407
+ for root, _, files in os.walk(directory):
408
+ for filename in files:
409
+ lower_name = filename.lower()
410
+ if lower_name.endswith(".zip") and "offlin" in lower_name:
411
+ zip_path = os.path.join(root, filename)
412
+ if is_valid_zip(zip_path):
413
+ return zip_path
414
+
415
+ return None
416
+
417
+ def install_modelscope_cli():if shutil.which("modelscope"):print("检测到 modelscope 命令。")return True
418
+
419
+ print("未检测到 modelscope 命令,尝试安装 modelscope。")
420
+
421
+ install_commands = [
422
+ [
423
+ sys.executable,
424
+ "-m",
425
+ "pip",
426
+ "install",
427
+ "-U",
428
+ "modelscope",
429
+ "-i",
430
+ "https://pypi.tuna.tsinghua.edu.cn/simple"
431
+ ],
432
+ [
433
+ sys.executable,
434
+ "-m",
435
+ "pip",
436
+ "install",
437
+ "-U",
438
+ "modelscope"
439
+ ]
440
+ ]
441
 
442
+ for command in install_commands:
443
  try:
444
+ result = subprocess.run(
445
+ command,
446
+ stdout=subprocess.PIPE,
447
+ stderr=subprocess.PIPE,
448
+ text=True,
449
+ timeout=300
 
 
 
 
 
 
 
450
  )
451
+
452
+ if result.returncode == 0 and shutil.which("modelscope"):
453
+ print("modelscope 安装成功。")
454
+ return True
455
+
456
+ print("modelscope 安装尝试失败。")
457
+ if result.stdout.strip():
458
+ print(result.stdout[-1000:])
459
+ if result.stderr.strip():
460
+ print(result.stderr[-1000:])
461
+
462
+ except subprocess.TimeoutExpired:
463
+ print("modelscope 安装超时。")
464
  except Exception as e:
465
+ print(f"modelscope 安装异常: {e}")
466
 
467
+ print("modelscope 不可用,无法继续下载 offline_build。")
468
+ return False
469
 
470
+ def extract_urls_from_text(text):if not text:return []
 
 
471
 
472
+ urls = re.findall(r"https?://[^\s'\")<>]+", text)
473
+ cleaned_urls = []
 
 
474
 
475
+ for url in urls:
476
+ cleaned_urls.append(url.rstrip(".,;"))
 
 
477
 
478
+ return cleaned_urls
479
 
480
+ def convert_internal_oss_url_to_public(url):if not url:return None
 
481
 
482
+ public_url = url.replace("-internal.aliyuncs.com", ".aliyuncs.com")
483
+ public_url = public_url.replace("http://", "https://", 1)
484
 
485
+ if public_url == url:
486
+ return None
 
 
487
 
488
+ return public_url
 
 
489
 
490
+ def download_url_to_file(url, output_path, use_proxy=False):os.makedirs(os.path.dirname(output_path), exist_ok=True)
 
 
 
 
 
 
491
 
492
+ env = get_modelscope_download_env(use_proxy=use_proxy)
 
 
 
493
 
494
+ if shutil.which("aria2c"):
495
+ command = [
496
+ "aria2c",
497
+ "--continue=true",
498
+ "--max-connection-per-server=8",
499
+ "--split=8",
500
+ "--min-split-size=16M",
501
+ "--check-certificate=false",
502
+ "--summary-interval=30",
503
+ "--timeout=30",
504
+ "--retry-wait=10",
505
+ "--max-tries=20",
506
+ "--dir",
507
+ os.path.dirname(output_path),
508
+ "--out",
509
+ os.path.basename(output_path),
510
+ url
511
  ]
512
+ else:
513
+ command = [
514
+ "wget",
515
+ "--continue",
516
+ "--timeout=30",
517
+ "--tries=20",
518
+ "--no-check-certificate",
519
+ "--output-document",
520
+ output_path,
521
+ url
522
+ ]
523
+
524
+ try:
525
+ print("执行直链下载命令: " + " ".join(command[:1]) + " ...")
526
+
527
+ result = subprocess.run(
528
+ command,
529
+ env=env,
530
+ stdout=subprocess.PIPE,
531
+ stderr=subprocess.PIPE,
532
+ text=True,
533
+ timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
534
+ )
535
+
536
+ if result.stdout.strip():
537
+ print(result.stdout[-2000:])
538
+ if result.stderr.strip():
539
+ print(result.stderr[-2000:])
540
+
541
+ if result.returncode == 0 and is_valid_zip(output_path):
542
+ print(f"直链下载成功: {output_path}")
543
+ return True
544
+
545
+ print(f"直链下载失败或压缩包无效: {output_path}")
546
+ return False
547
+
548
+ except subprocess.TimeoutExpired:
549
+ print("直链下载超时。")
550
+ return False
551
+ except Exception as e:
552
+ print(f"直链下载异常: {e}")
553
+ return False
554
+
555
+ def try_public_oss_fallback(download_output, zip_name):urls = extract_urls_from_text(download_output)
556
+
557
+ for url in urls:
558
+ public_url = convert_internal_oss_url_to_public(url)
559
+ if not public_url:
560
+ continue
561
+
562
+ print("检测到 ModelScope 返回 OSS 内网地址,尝试改用公网 OSS 地址继续下载。")
563
+ target_path = os.path.join(MODELSCOPE_DOWNLOAD_DIR, zip_name)
564
+
565
+ # 先直连公网 OSS;失败后才走代理。直连更适合国内服务器访问阿里云 OSS。
566
+ if download_url_to_file(public_url, target_path, use_proxy=False):
567
+ return target_path
568
+
569
+ if download_url_to_file(public_url, target_path, use_proxy=True):
570
+ return target_path
571
+
572
+ return None
573
+
574
+ def ensure_git_lfs_available():try:result = subprocess.run(["git", "lfs", "version"],stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True,timeout=20)if result.returncode == 0:print("检测到 git-lfs。")return Trueexcept Exception:pass
575
+
576
+ print("未检测到 git-lfs,尝试安装 git-lfs。")
577
+
578
+ install_commands = []
579
+ if shutil.which("apt-get"):
580
+ install_commands.append(["bash", "-lc", "apt-get update && apt-get install -y git-lfs"])
581
+ if shutil.which("yum"):
582
+ install_commands.append(["bash", "-lc", "yum install -y git-lfs"])
583
 
584
+ for command in install_commands:
585
+ try:
586
+ result = subprocess.run(
587
+ command,
588
+ stdout=subprocess.PIPE,
589
+ stderr=subprocess.PIPE,
590
+ text=True,
591
+ timeout=600
592
+ )
593
+ if result.stdout.strip():
594
+ print(result.stdout[-2000:])
595
+ if result.stderr.strip():
596
+ print(result.stderr[-2000:])
597
+
598
+ check = subprocess.run(
599
+ ["git", "lfs", "version"],
600
+ stdout=subprocess.PIPE,
601
+ stderr=subprocess.PIPE,
602
+ text=True,
603
+ timeout=20
604
+ )
605
+ if check.returncode == 0:
606
+ print("git-lfs 安装成功。")
607
+ return True
608
+
609
+ except subprocess.TimeoutExpired:
610
+ print("git-lfs 安装超时。")
611
+ except Exception as e:
612
+ print(f"git-lfs 安装异常: {e}")
613
+
614
+ print("git-lfs 不可用,后续 git clone 可能只能拿到 LFS 指针文件。")
615
+ return False
616
+
617
+ def clone_whole_forgeui_repo_with_git():os.makedirs(OFFLINE_BUILD_CACHE_DIR, exist_ok=True)
618
+
619
+ existing_zip = find_existing_offline_zip()
620
+ if existing_zip:
621
+ print(f"检测到已有 offline_build 压缩包: {existing_zip}")
622
+ return existing_zip
623
+
624
+ if not shutil.which("git"):
625
+ print("未检测到 git,跳过 git 整库克隆。")
626
+ return None
627
+
628
+ ensure_git_lfs_available()
629
+
630
+ repo_urls = [
631
+ "https://www.modelscope.cn/HOPStudio/forgeui.git",
632
+ "https://modelscope.cn/HOPStudio/forgeui.git"
633
+ ]
634
+
635
+ if os.path.isdir(os.path.join(MODELSCOPE_DOWNLOAD_DIR, ".git")):
636
+ print(f"检测到已存在 ModelScope 整库仓库: {MODELSCOPE_DOWNLOAD_DIR}")
637
+ try:
638
+ subprocess.run(
639
+ ["git", "-C", MODELSCOPE_DOWNLOAD_DIR, "lfs", "pull"],
640
+ env=get_modelscope_download_env(use_proxy=False),
641
+ stdout=subprocess.PIPE,
642
+ stderr=subprocess.PIPE,
643
+ text=True,
644
+ timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
645
+ )
646
+ except Exception as e:
647
+ print(f"git lfs pull 执行异常: {e}")
648
+
649
+ existing_zip = find_existing_offline_zip()
650
+ if existing_zip:
651
+ print(f"在已克隆仓库中找到 offline_build 压缩包: {existing_zip}")
652
+ return existing_zip
653
+
654
+ elif os.path.exists(MODELSCOPE_DOWNLOAD_DIR):
655
+ # 这里是旧版 modelscope download 留下的目录。整库 git clone 需要空目录,
656
+ # 因此先改名备份,不直接删除,避免误删已有断点文件。
657
+ backup_dir = MODELSCOPE_DOWNLOAD_DIR + f".bak_{int(time.time())}"
658
+ try:
659
+ shutil.move(MODELSCOPE_DOWNLOAD_DIR, backup_dir)
660
+ print(f"检测到非 git 下载目录,已备份为: {backup_dir}")
661
+ except Exception as e:
662
+ print(f"备份旧下载目录失败: {e}")
663
+ return None
664
+
665
+ for repo_url in repo_urls:
666
+ for use_proxy, mode_name in [(False, "直连"), (True, "代理")]:
667
+ if os.path.exists(MODELSCOPE_DOWNLOAD_DIR):
668
+ shutil.rmtree(MODELSCOPE_DOWNLOAD_DIR, ignore_errors=True)
669
+
670
+ env = get_modelscope_download_env(use_proxy=use_proxy)
671
  command = [
672
+ "git",
673
+ "clone",
674
+ "--depth",
675
+ "1",
676
+ repo_url,
677
+ MODELSCOPE_DOWNLOAD_DIR
 
678
  ]
679
 
680
  try:
681
+ print(f"执行 ModelScope 整库 git clone [{mode_name}]: " + " ".join(command))
682
 
683
  result = subprocess.run(
684
  command,
685
+ env=env,
686
  stdout=subprocess.PIPE,
687
  stderr=subprocess.PIPE,
688
  text=True,
 
694
  if result.stderr.strip():
695
  print(result.stderr[-4000:])
696
 
697
+ if result.returncode != 0:
698
+ print("本次 git clone 失败,继续尝试下一个方式。")
699
+ continue
700
 
701
+ try:
702
+ subprocess.run(
703
+ ["git", "-C", MODELSCOPE_DOWNLOAD_DIR, "lfs", "pull"],
704
+ env=env,
705
+ stdout=subprocess.PIPE,
706
+ stderr=subprocess.PIPE,
707
+ text=True,
708
+ timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
709
+ )
710
+ except Exception as e:
711
+ print(f"git lfs pull 执行异常: {e}")
712
+
713
+ zip_path = find_existing_offline_zip()
714
+ if zip_path:
715
+ print(f"ModelScope 整库克隆完成,找到 offline_build 压缩包: {zip_path}")
716
+ return zip_path
717
+
718
+ print("整库 git clone 完成,但未找到有效 offline_build 压缩包。")
719
 
720
  except subprocess.TimeoutExpired:
721
+ print("ModelScope 整库 git clone 超时,继续尝试下一个方式。")
722
  except Exception as e:
723
+ print(f"ModelScope 整库 git clone 异常: {e}")
724
 
725
+ return None
 
726
 
727
+ def download_whole_forgeui_repo_with_modelscope_cli():if not install_modelscope_cli():return None
728
 
729
+ commands = [
730
+ (
731
+ [
732
+ "modelscope",
733
+ "download",
734
+ "--model",
735
+ MODELSCOPE_MODEL_ID,
736
+ "--local_dir",
737
+ MODELSCOPE_DOWNLOAD_DIR
738
+ ],
739
+ False,
740
+ "整库直连"
741
+ ),
742
+ (
743
+ [
744
+ "modelscope",
745
+ "download",
746
+ "--model",
747
+ MODELSCOPE_MODEL_ID,
748
+ "--local_dir",
749
+ MODELSCOPE_DOWNLOAD_DIR,
750
+ "--endpoint",
751
+ "https://www.modelscope.cn"
752
+ ],
753
+ False,
754
+ "整库直连+显式 endpoint"
755
+ ),
756
+ (
757
+ [
758
+ "modelscope",
759
+ "download",
760
+ "--model",
761
+ MODELSCOPE_MODEL_ID,
762
+ "--local_dir",
763
+ MODELSCOPE_DOWNLOAD_DIR
764
+ ],
765
+ True,
766
+ "整库代理"
767
+ )
768
+ ]
769
 
770
+ for command, use_proxy, mode_name in commands:
771
+ try:
772
+ print(f"执行 ModelScope CLI 整库下载 [{mode_name}]: " + " ".join(command))
773
 
774
+ result = subprocess.run(
775
+ command,
776
+ env=get_modelscope_download_env(use_proxy=use_proxy),
777
+ stdout=subprocess.PIPE,
778
+ stderr=subprocess.PIPE,
779
+ text=True,
780
+ timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
781
+ )
782
 
783
+ output_text = ""
784
+ if result.stdout.strip():
785
+ output_text += result.stdout + "\n"
786
+ print(result.stdout[-2000:])
787
+ if result.stderr.strip():
788
+ output_text += result.stderr + "\n"
789
+ print(result.stderr[-4000:])
790
 
791
+ zip_path = find_existing_offline_zip()
792
+ if result.returncode == 0 and zip_path:
793
+ print(f"ModelScope CLI 整库下载完成,找到 offline_build 压缩包: {zip_path}")
794
+ return zip_path
795
 
796
+ # 这里不再按文件名调用 modelscope download,只在 CLI 日志暴露 OSS 直链时做公网地址回退。
797
+ fallback_zip = try_public_oss_fallback(output_text, "offlinbuild.zip")
798
+ if fallback_zip and is_valid_zip(fallback_zip):
799
+ print(f"offline_build 压缩包通过公网 OSS 回退下载成功: {fallback_zip}")
800
+ return fallback_zip
801
 
802
+ print("本次整库下载未找到有效 offline_build 压缩包,继续尝试下一个命令。")
 
803
 
804
+ except subprocess.TimeoutExpired:
805
+ print("ModelScope CLI 整库下载超时,继续尝试下一个命令。")
806
  except Exception as e:
807
+ print(f"ModelScope CLI 整库下载异常: {e}")
 
808
 
809
+ return None
810
 
811
+ def download_offline_build_from_modelscope():os.makedirs(MODELSCOPE_DOWNLOAD_DIR, exist_ok=True)os.makedirs(MODELSCOPE_CACHE_DIR, exist_ok=True)
 
 
 
812
 
813
+ existing_zip = find_existing_offline_zip()
814
+ if existing_zip:
815
+ print(f"检测到已有 offline_build 压缩包: {existing_zip}")
816
+ return existing_zip
817
 
818
+ print(f"开始从 ModelScope 整库克隆/下载 {MODELSCOPE_MODEL_ID}。")
819
 
820
+ zip_path = clone_whole_forgeui_repo_with_git()
821
+ if zip_path:
822
+ return zip_path
823
 
824
+ print("git 整库克隆未成功,改用 ModelScope CLI 整库下载。")
 
 
 
825
 
826
+ zip_path = download_whole_forgeui_repo_with_modelscope_cli()
827
+ if zip_path:
828
+ return zip_path
829
 
830
+ print("ModelScope 整库克隆/下载失败,未找到有效 offline_build 压缩包。")
831
+ return None
 
832
 
833
+ def locate_offline_build_dir():expected_main = os.path.join(OFFLINE_BUILD_DIR, "ComfyUI", "main.py")if os.path.exists(expected_main):return OFFLINE_BUILD_DIR
834
 
835
+ for root, dirs, files in os.walk(REPO_PATH):
836
+ if "main.py" in files and os.path.basename(root) == "ComfyUI":
837
+ parent_dir = os.path.dirname(root)
838
+ venv_activate = os.path.join(parent_dir, "venv", "bin", "activate")
839
+ if os.path.exists(venv_activate):
840
+ return parent_dir
841
 
842
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843
 
844
+ def extract_offline_build(zip_path):existing_dir = locate_offline_build_dir()if existing_dir:print(f"检测到已解压的 offline_build: {existing_dir}")return existing_dir
 
 
 
 
 
 
 
 
 
 
845
 
846
+ if not zip_path or not is_valid_zip(zip_path):
847
+ print(f"offline_build 压缩包无效: {zip_path}")
848
+ return None
849
 
850
+ try:
851
+ print(f"正在解压 offline_build: {zip_path}")
 
 
852
 
853
+ with zipfile.ZipFile(zip_path, "r") as zf:
854
+ zf.extractall(REPO_PATH)
855
 
856
+ extracted_dir = locate_offline_build_dir()
857
+ if extracted_dir:
858
+ print(f"offline_build 解压完成: {extracted_dir}")
859
+ return extracted_dir
860
 
861
+ print("解压完成,但未找到 offline_build/ComfyUI/main.py。")
862
+ return None
 
863
 
864
+ except Exception as e:
865
+ print(f"解压 offline_build 失败: {e}")
866
+ return None
 
867
 
868
+ def prepare_offline_build():existing_dir = locate_offline_build_dir()if existing_dir:print(f"offline_build 已存在,跳过下载解压: {existing_dir}")return existing_dir
 
 
 
869
 
870
+ zip_path = download_offline_build_from_modelscope()
871
+ if not zip_path:
872
+ return None
 
 
 
 
873
 
874
+ return extract_offline_build(zip_path)
 
 
 
 
 
 
875
 
876
+ def start_comfyui_process():offline_build_dir = prepare_offline_build()
 
 
 
 
 
 
877
 
878
+ if not offline_build_dir:
879
+ print("offline_build 准备失败,跳过 ComfyUI 启动。")
880
+ return
881
 
882
+ venv_activate = os.path.join(offline_build_dir, "venv", "bin", "activate")
883
+ comfyui_main = os.path.join(offline_build_dir, "ComfyUI", "main.py")
 
 
 
884
 
885
+ if not os.path.exists(venv_activate):
886
+ print(f"未找到虚拟环境激活脚本: {venv_activate}")
887
+ return
 
 
 
 
 
 
 
 
888
 
889
+ if not os.path.exists(comfyui_main):
890
+ print(f"未找到 ComfyUI main.py: {comfyui_main}")
891
+ return
 
892
 
893
+ configure_git_proxy()
894
 
895
+ command = f'''
 
 
 
896
 
897
+ set -esource "{venv_activate}"export http_proxy={PROXY_URL}export https_proxy={PROXY_URL}export HTTP_PROXY={PROXY_URL}export HTTPS_PROXY={PROXY_URL}export ALL_PROXY={PROXY_URL}export no_proxy=localhost,127.0.0.1export NO_PROXY=localhost,127.0.0.1git config --global http.proxy {PROXY_URL}git config --global https.proxy {PROXY_URL}cd "{os.path.join(offline_build_dir, "ComfyUI")}"python main.py --listen 0.0.0.0 --port {COMFYUI_PORT}'''
898
 
899
+ try:
900
+ os.makedirs(REPO_PATH, exist_ok=True)
901
+ log_file = open(COMFYUI_LOG_PATH, "a", encoding="utf-8")
902
+
903
+ process = subprocess.Popen(
904
+ ["bash", "-lc", command],
905
+ cwd=offline_build_dir,
906
+ env=get_proxy_env(),
907
+ stdout=log_file,
908
+ stderr=subprocess.STDOUT
909
  )
910
 
911
+ time.sleep(3)
912
 
913
+ if process.poll() is None:
914
+ print(f"ComfyUI 已启动,端口: {COMFYUI_PORT},日志: {COMFYUI_LOG_PATH}")
915
+ else:
916
+ print(f"ComfyUI 启动后立即退出,返回码: {process.returncode},请查看日志: {COMFYUI_LOG_PATH}")
917
 
918
+ except Exception as e:
919
+ print(f"启动 ComfyUI 失败: {e}")
920
 
921
+ def start_gradio():with gr.Blocks() as demo:gr.Markdown("# InternLM 控制台")
 
922
 
923
+ input_box = gr.Textbox(
924
+ label="输入",
925
+ placeholder="输入对话内容"
926
+ )
927
 
928
+ model_box = gr.Textbox(
929
+ value="internlm2.5-latest",
930
+ label="模型"
931
+ )
 
 
 
 
932
 
933
+ temperature_slider = gr.Slider(
934
+ minimum=0,
935
+ maximum=1,
936
+ value=0.8,
937
+ step=0.01,
938
+ label="Temperature"
939
+ )
 
940
 
941
+ top_p_slider = gr.Slider(
942
+ minimum=0,
943
+ maximum=1,
944
+ value=0.9,
945
+ step=0.01,
946
+ label="Top-p"
947
+ )
948
+
949
+ n_slider = gr.Slider(
950
+ minimum=1,
951
+ maximum=10,
952
+ value=1,
953
+ step=1,
954
+ label="生成回复数量"
955
+ )
956
+
957
+ with gr.Row():
958
+ send_button = gr.Button("发送", variant="primary")
959
+ refresh_button = gr.Button("刷新前端")
960
+
961
+ output_box = gr.Textbox(
962
+ label="回复",
963
+ lines=20,
964
+ interactive=False
965
+ )
966
 
967
+ send_button.click(
968
+ api_call,
969
+ inputs=[
970
+ input_box,
971
+ model_box,
972
+ temperature_slider,
973
+ top_p_slider,
974
+ n_slider
975
+ ],
976
+ outputs=output_box
977
+ )
978
+
979
+ refresh_button.click(
980
+ refresh_frontend,
981
+ outputs=output_box
982
+ )
983
+
984
+ timer = gr.Timer(5)
985
+
986
+ timer.tick(
987
+ refresh_frontend,
988
+ outputs=output_box
989
+ )
990
+
991
+ print("Gradio 正在启动。")
992
+
993
+ demo.launch(
994
+ server_name="0.0.0.0",
995
+ server_port=7860,
996
+ share=False
997
+ )
998
+
999
+ def main():global CURRENT_DIR
1000
+
1001
+ clone_success = clone_repository()
1002
+
1003
+ if clone_success:
1004
+ CURRENT_DIR = REPO_PATH
1005
+
1006
+ install_frp()
1007
+ create_frpc_toml()
1008
+
1009
+ try:
1010
+ frp_thread = threading.Thread(
1011
+ target=start_frp,
1012
+ daemon=True
1013
+ )
1014
+ frp_thread.start()
1015
+ except Exception as e:
1016
+ print(f"frp 线程启动失败: {e}")
1017
+
1018
+ try:
1019
+ jupyter_thread = threading.Thread(
1020
+ target=start_jupyterlab_process,
1021
+ daemon=True
1022
+ )
1023
+ jupyter_thread.start()
1024
+ except Exception as e:
1025
+ print(f"JupyterLab 线程启动失败: {e}")
1026
+
1027
+ try:
1028
+ comfyui_thread = threading.Thread(
1029
+ target=start_comfyui_process,
1030
+ daemon=True
1031
+ )
1032
+ comfyui_thread.start()
1033
+ except Exception as e:
1034
+ print(f"ComfyUI 线程启动失败: {e}")
1035
 
1036
+ print("准备启动 Gradio...")
1037
+ start_gradio()
1038
 
1039
+ if name == "main":main()