|
import marimo |
|
|
|
__generated_with = "0.10.13" |
|
app = marimo.App(width="medium") |
|
|
|
|
|
@app.cell |
|
def _(): |
|
import marimo as mo |
|
import subprocess |
|
import os |
|
import logging |
|
from datetime import datetime |
|
return datetime, logging, mo, os, subprocess |
|
|
|
|
|
@app.cell |
|
def _(mo): |
|
"""UI 组件:工具选择""" |
|
ai_app = "ksa虚拟网络" |
|
aitool_name = mo.ui.dropdown( |
|
[f"{ai_app}"], |
|
value=f"{ai_app}", |
|
label="选择AI工具" |
|
) |
|
return ai_app, aitool_name |
|
|
|
@app.cell |
|
def _(aitool_name): |
|
"""虚拟环境所在文件夹、git REPO等常规变量设置""" |
|
uv_venv_dir = f"ai_{aitool_name.value}" |
|
git_repo_url = "https://openi.pcl.ac.cn/niubi/ComfyUI.git" |
|
repo_name = "ComfyUI" |
|
app_log = f"{aitool_name.value}.log" |
|
python_ver = "3.12.8" |
|
|
|
custom_node_1 = "comfyui-manager" |
|
custom_node_1_repo = "https://openi.pcl.ac.cn/niubi/comfyui-manager.git" |
|
return ( |
|
app_log, |
|
custom_node_1, |
|
custom_node_1_repo, |
|
git_repo_url, |
|
python_ver, |
|
repo_name, |
|
uv_venv_dir, |
|
) |
|
|
|
|
|
@app.cell |
|
def _(custom_node_1, custom_node_1_repo, python_ver): |
|
"""配置类""" |
|
class Config: |
|
SYSTEM_DEPS = [ |
|
{ |
|
"name": "系统依赖", |
|
"cmd": """ |
|
apt-get update |
|
apt install build-essential libgl1 ffmpeg python3-pip aria2 git git-lfs htop -y |
|
pip install uv -i https://mirrors.cloud.tencent.com/pypi/simple |
|
""" |
|
} |
|
] |
|
|
|
@staticmethod |
|
def get_venv_commands(venv_dir): |
|
return [ |
|
{ |
|
"name": "虚拟环境设置", |
|
"cmd": f""" |
|
echo "无需设置,直接跳过" |
|
""" |
|
} |
|
] |
|
|
|
@staticmethod |
|
def setup_repo_commands(venv_dir, git_url, repo_name): |
|
return [ |
|
{ |
|
"name": f"处理{repo_name}项目仓库", |
|
"cmd": f""" |
|
echo "无需设置,直接跳过" |
|
""" |
|
} |
|
] |
|
|
|
@staticmethod |
|
def get_start_command(venv_dir, repo_name, app_log): |
|
"""获取启动命令""" |
|
return { |
|
"name": "启动服务", |
|
"cmd": f""" |
|
echo "无需设置,直接跳过" |
|
""" |
|
} |
|
|
|
@staticmethod |
|
def get_stop_commands(): |
|
"""获取停止命令""" |
|
return [ |
|
{ |
|
"name": "停止服务", |
|
"cmd": "echo '无需设置,直接跳过'" |
|
} |
|
] |
|
|
|
@staticmethod |
|
def download_commands(venv_dir, repo_name): |
|
return [ |
|
{ |
|
"name": "模型下载", |
|
"cmd": f""" |
|
echo "无需设置,直接跳过" |
|
""" |
|
} |
|
] |
|
|
|
@staticmethod |
|
def get_end_show_info(venv_dir, repo_name): |
|
"""获取安装完成后的展示信息""" |
|
return f""" |
|
## 安装完成后的操作说明 |
|
|
|
Q&A及资讯: |
|
- 最新资讯及相关命令请查看 https://fuliai-ai2u.hf.space/ |
|
- 加群交流:https://qr61.cn/oohivs/qRp62U6 |
|
|
|
## 启动程序 |
|
|
|
```bash |
|
/workspace/apps/ksa/ksa_x64 |
|
export HF_ENDPOINT=https://hf-mirror.com |
|
``` |
|
之后使用ksa连接,比如在终端中运行了ollama,在浏览器中使用 10.0.0.1:11434 来访问ollama。 |
|
不会用的请查看视频教程:https://www.bilibili.com/video/BV13UBRYVEmX/ |
|
|
|
## 退出程序 |
|
```bash |
|
(使用 Ctrl + C) |
|
``` |
|
""" |
|
return (Config,) |
|
|
|
|
|
@app.cell |
|
def _(datetime, logging, os): |
|
"""日志处理类""" |
|
class Logger: |
|
def __init__(self, name): |
|
self.logger = self._setup_logger(name) |
|
|
|
def _setup_logger(self, name): |
|
os.makedirs("logs", exist_ok=True) |
|
log_file = f'logs/{name}_{datetime.now().strftime("%Y%m%d")}.log' |
|
|
|
logger = logging.getLogger(name) |
|
logging.basicConfig( |
|
level=logging.INFO, |
|
format="%(asctime)s - %(levelname)s - %(message)s", |
|
handlers=[ |
|
logging.FileHandler(log_file, encoding="utf-8"), |
|
logging.StreamHandler(), |
|
], |
|
) |
|
return logger |
|
|
|
def info(self, msg): self.logger.info(msg) |
|
def error(self, msg): self.logger.error(msg) |
|
def warning(self, msg): self.logger.warning(msg) |
|
return (Logger,) |
|
|
|
|
|
@app.cell |
|
def _(mo): |
|
"""UI 组件:功能开关""" |
|
switches = { |
|
"system": mo.ui.switch(label="系统依赖安装", value=True), |
|
"venv": mo.ui.switch(label="虚拟环境设置", value=True), |
|
"repo": mo.ui.switch(label="程序文件设置", value=True), |
|
"app": mo.ui.switch(label="启动应用程序", value=True), |
|
"download": mo.ui.switch(label="文件或模型下载", value=True), |
|
} |
|
return (switches,) |
|
|
|
|
|
@app.cell |
|
def _(Logger, aitool_name): |
|
"""初始化日志记录器""" |
|
logger = Logger(aitool_name.value) |
|
return (logger,) |
|
|
|
|
|
@app.cell |
|
def _(subprocess): |
|
"""命令执行工具类""" |
|
class CommandRunner: |
|
@staticmethod |
|
def run(cmd, logger, check=True, step_name=""): |
|
try: |
|
prefix = f"[{step_name}] " if step_name else "" |
|
logger.info(f"{prefix}正在执行命令...") |
|
result = subprocess.run( |
|
cmd, |
|
shell=True, |
|
check=check, |
|
text=True, |
|
capture_output=True |
|
) |
|
if result.stdout: |
|
logger.info(f"{prefix}输出: {result.stdout.strip()}") |
|
logger.info(f"{prefix}命令执行成功") |
|
return True, None |
|
except subprocess.CalledProcessError as e: |
|
error_msg = f"{prefix}命令执行失败: {e.stderr if e.stderr else '未知错误'}" |
|
logger.error(error_msg) |
|
return False, error_msg |
|
except KeyboardInterrupt: |
|
logger.info(f"{prefix}用户中断了命令执行") |
|
return False, "用户中断了命令执行" |
|
except Exception as e: |
|
error_msg = f"{prefix}执行出错: {str(e)}" |
|
logger.error(error_msg) |
|
return False, error_msg |
|
return (CommandRunner,) |
|
|
|
|
|
@app.cell |
|
def _(aitool_name): |
|
aitool_name |
|
return |
|
|
|
|
|
@app.cell |
|
def _(switches): |
|
switches |
|
return |
|
|
|
|
|
@app.cell |
|
def _(CommandRunner, Config, logger, mo, switches): |
|
"""系统依赖安装""" |
|
def install_system(): |
|
if not switches["system"].value: |
|
return mo.md("⚠️ 请先启用系统依赖安装").callout(kind="warn") |
|
|
|
for cmd_info in Config.SYSTEM_DEPS: |
|
success, error = CommandRunner.run(cmd_info["cmd"], logger, step_name=cmd_info["name"]) |
|
if not success: |
|
return mo.md(f"❌ {error}").callout(kind="danger") |
|
|
|
return mo.md("✅ 系统依赖安装完成").callout(kind="success") |
|
|
|
install_system() |
|
return (install_system,) |
|
|
|
|
|
@app.cell |
|
def _(CommandRunner, Config, logger, mo, os, switches, uv_venv_dir): |
|
"""虚拟环境设置""" |
|
def setup_venv(): |
|
if not switches["venv"].value: |
|
return mo.md("⚠️ 请先启用虚拟环境设置").callout(kind="warn") |
|
|
|
os.makedirs(uv_venv_dir, exist_ok=True) |
|
|
|
for cmd_info in Config.get_venv_commands(uv_venv_dir): |
|
success, error = CommandRunner.run(cmd_info["cmd"], logger, step_name=cmd_info["name"]) |
|
if not success: |
|
return mo.md(f"❌ {error}").callout(kind="danger") |
|
|
|
return mo.md("✅ 虚拟环境设置完成").callout(kind="success") |
|
|
|
setup_venv() |
|
return (setup_venv,) |
|
|
|
|
|
@app.cell |
|
def _( |
|
CommandRunner, |
|
Config, |
|
git_repo_url, |
|
logger, |
|
mo, |
|
repo_name, |
|
switches, |
|
uv_venv_dir, |
|
): |
|
"""程序文件安装设置""" |
|
def setup_repo(): |
|
if not switches["repo"].value: |
|
return mo.md("⚠️ 请先启用文件安装设置").callout(kind="warn") |
|
|
|
for cmd_info in Config.setup_repo_commands(uv_venv_dir, git_repo_url, repo_name): |
|
success, error = CommandRunner.run(cmd_info["cmd"], logger, step_name=cmd_info["name"]) |
|
if not success: |
|
return mo.md(f"❌ {error}").callout(kind="danger") |
|
|
|
return mo.md("✅ 程序文件设置完成").callout(kind="success") |
|
|
|
setup_repo() |
|
return (setup_repo,) |
|
|
|
|
|
@app.cell |
|
def _( |
|
CommandRunner, |
|
Config, |
|
end_show, |
|
logger, |
|
mo, |
|
post_end_show, |
|
repo_name, |
|
switches, |
|
uv_venv_dir, |
|
): |
|
"""下载:文件或模型""" |
|
def download_files(): |
|
if not switches["download"].value: |
|
return mo.md("⚠️ 请先启用下载开关").callout(kind="warn") |
|
|
|
try: |
|
for cmd_info in Config.download_commands(uv_venv_dir, repo_name): |
|
success, error = CommandRunner.run(cmd_info["cmd"], logger, step_name=cmd_info["name"]) |
|
if not success: |
|
return mo.md(f"❌ {error}").callout(kind="danger") |
|
|
|
|
|
download_success = mo.md("✅ 相关下载已经完成").callout(kind="success") |
|
|
|
install_info = end_show() |
|
post_info = post_end_show() |
|
|
|
return mo.hstack([download_success, install_info, post_info]) |
|
except KeyboardInterrupt: |
|
logger.info("用户中断了下载过程") |
|
return mo.md("⚠️ 下载已被用户中断").callout(kind="warn") |
|
except Exception as e: |
|
logger.error(f"下载过程出错: {str(e)}") |
|
return mo.md(f"❌ 下载出错: {str(e)}").callout(kind="danger") |
|
|
|
download_files() |
|
return (download_files,) |
|
|
|
|
|
@app.cell |
|
def _(Config, mo, repo_name, uv_venv_dir): |
|
"""主体完成后展示信息内容""" |
|
def end_show(): |
|
return mo.md(Config.get_end_show_info(uv_venv_dir, repo_name)).callout(kind="success") |
|
return (end_show,) |
|
|
|
|
|
@app.cell |
|
def _(Config, mo, ai_app, uv_venv_dir): |
|
"""将主体完成后展示信息内容写入说明文件""" |
|
def post_end_show(): |
|
|
|
info_content = Config.get_end_show_info(uv_venv_dir, ai_app) |
|
|
|
|
|
try: |
|
with open(f"{ai_app}说明文件.md", "w", encoding="utf-8") as f: |
|
f.write(info_content) |
|
return mo.md(f"✅ 安装成功,使用说明已写入 {ai_app}说明文件.md").callout(kind="success") |
|
except Exception as e: |
|
return mo.md(f"❌ 写入说明文件失败: {str(e)}").callout(kind="danger") |
|
return (post_end_show,) |
|
|
|
|
|
@app.cell |
|
def _(mo): |
|
"""创建启动和停止按钮""" |
|
btn = mo.ui.run_button(label="启动服务", kind="info") |
|
btn1 = mo.ui.run_button(label="停止服务", kind="info") |
|
return btn, btn1 |
|
|
|
|
|
@app.cell |
|
def _(btn): |
|
btn |
|
return |
|
|
|
|
|
@app.cell |
|
def _(btn1): |
|
btn1 |
|
return |
|
|
|
|
|
@app.cell |
|
def _( |
|
Config, |
|
app_log, |
|
btn, |
|
btn1, |
|
logger, |
|
repo_name, |
|
subprocess, |
|
switches, |
|
uv_venv_dir, |
|
): |
|
"""启动/停止应用程序""" |
|
def handle_app(): |
|
if not switches["app"].value: |
|
print("⚠️ 请先启用应用程序开关") |
|
return |
|
|
|
if btn.value: |
|
|
|
start_config = Config.get_start_command(uv_venv_dir, repo_name, app_log) |
|
|
|
|
|
check_result = subprocess.run(start_config["check_cmd"], shell=True) |
|
if check_result.returncode == 0: |
|
print("⚠️ 程序已在运行中") |
|
return |
|
|
|
|
|
logger.info("[启动服务] 正在启动应用程序...") |
|
subprocess.run(start_config["cmd"], shell=True) |
|
print("✅ 程序启动命令已执行") |
|
|
|
elif btn1.value: |
|
|
|
stop_config = Config.get_stop_commands()[0] |
|
|
|
|
|
logger.info("[停止服务] 正在停止应用程序...") |
|
subprocess.run(stop_config["cmd"], shell=True) |
|
print("✅ 程序停止命令已执行") |
|
|
|
handle_app() |
|
return (handle_app,) |
|
|
|
|
|
if __name__ == "__main__": |
|
app.run() |
|
|