架构说明
Pyruns 当前的核心设计其实可以浓缩成一句话:
React 前端负责把体验做顺,FastAPI 运行时负责把状态串起来,core 层负责把任务真正落到磁盘并执行,磁盘才是最终真实来源。
这不是一个“先有页面、再去猜状态”的系统。
它是一个“先把任务结构稳稳落下,再让 UI 把它看清楚”的系统。
1. 整体结构
text
CLI / Launcher
↓
FastAPI Runtime (pyruns/web)
↓
Core Services (pyruns/core)
↓
Utils / File IO / Settings (pyruns/utils)
↓
Workspace on disk (_pyruns_/...)前端交付链路则是:
text
frontend/ --build--> pyruns/web/static/2. 这套分层为什么顺
frontend/
负责:
- 页面和交互
- Zustand 状态管理
- 轮询与 websocket
- xterm.js / CodeMirror 等展示层
关键页面:
dashboardgeneratormanagermonitor
这层的重点是体验,不负责伪造真实状态。
pyruns/web/
负责:
- FastAPI app 组装
- API 路由
- 当前 workspace runtime
- 聚合 dashboard、task list、task logs、workspace info
关键文件:
pyruns/web/app.pypyruns/web/runtime.py
其中 runtime.py 是 Web 层最重要的状态枢纽之一,负责把“当前打开的是哪个工作区、当前使用什么 shell runtime、当前 tasks 目录在哪里”这些运行时信息统一组织起来。
pyruns/core/
这是 Pyruns 真正干活的地方。
task_generator.py
负责把“用户想生成什么任务”真正写到磁盘:
- config task 写
config.yaml - shell task 写
config.sh - 同时生成
task_info.json
task_manager.py
负责任务管理:
- 扫描磁盘任务
- 刷新任务状态
- 批量运行、删除、pin
- 让内存视图始终跟磁盘文件对齐
executor.py
负责实际执行任务:
- 根据
task_kind选择命令构造方式 - config task 注入
__PYRUNS_CONFIG__ - shell task 解析 shell runtime
- 采集 stdout / stderr 到
run_logs - 把实时日志推给 Monitor websocket
system_metrics.py
负责系统指标:
- CPU
- RAM
- NVIDIA GPU 利用率
- 显存占用 / 总量
- GPU 进程明细
pyruns/utils/
这一层不高调,但很关键。它让整个系统不用数据库也能稳定工作。
settings.py
- 读写
_pyruns_settings.yaml - 合并默认值
- 提供 save / load / get
shell_runtime.py
- 解析
shell_mode - 区分
follow/custom - 检测当前启动终端
- 给 executor、runtime、frontend 提供统一 shell runtime 信息
task_files.py
- 统一 task payload 文件读写
- 归一化
workspace_kind/task_kind
info_io.py
- 读写
task_info.json - 读写
script_info.json - 解析日志路径
log_io.py
- 读取日志文件
- 处理编码回退
- 标准化终端换行
sort_utils.py
- 搜索和排序逻辑
- Manager / Monitor 多行搜索的 AND 语义
3. 磁盘是最终状态源
这是 Pyruns 很重要的设计原则。
前端状态和运行时内存都只是视图。
真正能被信任、能被恢复、能被共享的是磁盘上的这些文件:
task_info.jsonconfig.yaml/config.shrun_logs/
这带来的好处很直接:
- 页面刷新后状态可恢复
- 不依赖数据库
- CLI / Web UI 可以共用同一套任务状态
- 任务本身很容易检查、备份、迁移
4. 两条工作流
Script Workspace 工作流
text
pyr train.py
→ bootstrap_workspace(...)
→ _pyruns_/train/
→ Generator(form / yaml)
→ TaskGenerator.create_tasks(..., task_kind="config")
→ tasks/<task_name>/config.yaml
→ executor injects __PYRUNS_CONFIG__Shell Workspace 工作流
text
Open Shell Mode
→ _pyruns_/_shell_/
→ Generator(shell)
→ TaskGenerator.create_shell_task(...)
→ tasks/<task_name>/config.sh
→ executor resolves shell runtime
→ shell task runs with follow/custom semantics5. shell 执行策略
Pyruns 对 shell 模式的理解不是“模拟 bash”,而是:
尽量跟随你启动
pyr的原终端
默认值:
shell_mode = follow
只有显式切到:
shell_mode = custom
才会启用 shell_executable。
Windows
- 跟随 PowerShell:按 PowerShell 语义执行
- 跟随 cmd:按 cmd 语义执行
- wrapper 只负责把任务文本交给原生终端
- 不做跨 shell 翻译
Linux / macOS
- 默认跟随当前 shell
- 强调 follow,而不是“bash 优先”
6. 前端日志流为什么稳定
Monitor 页面依赖两种数据源:
初始历史加载
GET /api/tasks/{name}/logs
运行中增量流
WS /api/tasks/{name}/logs/stream
当前实现重点包括:
- 切任务时会重置日志选择状态
- websocket chunk 会校验任务身份,避免串到错误任务
- 历史日志会做终端换行标准化,避免 xterm 出现“逐行右移”的假缩进
7. UI 为什么适合做 GitHub Pages 展示
现在的 React UI 已经足够完整,完全可以做一个静态展示版:
- Dashboard 展示假系统指标
- Generator 展示表单 / YAML / Shell 三种态
- Manager 展示任务卡片和详情抽屉
- Monitor 展示模拟日志流和终端界面
也就是说,GitHub Pages 很适合承载:
- 文档站
- 截图展示
- mock 数据驱动的静态 UI demo
不适合承载的部分则是:
- 真正执行任务
- 实时 FastAPI runtime
- 真实任务调度
8. 推荐补图位置
如果你想让这一页更有展示感,最适合补的图是:
- 一张整体架构流程图
- 一张前端到
pyruns/web/static/的交付链路图 - 一张 shell follow/custom 语义的流程示意图
如果后面要补图,建议统一放在:
https://raw.githubusercontent.com/LthreeC/pyruns/main/docs/assets/architecture_overview.pnghttps://raw.githubusercontent.com/LthreeC/pyruns/main/docs/assets/build_pipeline.pnghttps://raw.githubusercontent.com/LthreeC/pyruns/main/docs/assets/shell_runtime_flow.png