feat: Update CoreML device support and server controls
- Replace 'mps' with 'coreml' as the identifier for Apple CoreML Execution Provider across the codebase. - Remove Apple chip generation checks and ensure CoreML is available on all macOS arm64 devices. - Adjust ONNX Runtime session creation to use default CoreML configurations without forcing 'MLProgram'. - Implement support for MoGe-2 to utilize CoreML with static input shape requirements. - Enhance server panel to display restart and stop controls consistently across running states, including 'BUSY'. - Update UI tests to verify new behavior for server controls and cached model display. - Bump blendjob dependency version to 0.1.16 in packaging tests and configurations.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-01
|
||||
68
openspec/changes/correct-coreml-device-support/design.md
Normal file
68
openspec/changes/correct-coreml-device-support/design.md
Normal file
@@ -0,0 +1,68 @@
|
||||
## Context
|
||||
|
||||
AnyImage 的 ONNX 推理把设备值 `mps` 映射到 `CoreMLExecutionProvider`,但该名称通常指 PyTorch Metal backend,与实际实现不一致。Blender 端还通过 CPU brand string 将 Apple 加速限制为 M4 及以上,而 macOS 扩展本身只发布 `macos-arm64`,支持范围已经限定为 Apple Silicon。
|
||||
|
||||
共享 Session 工厂目前为所有 CoreML 模型强制传入 `ModelFormat=MLProgram`。MoGe-2 的 ONNX 图具有动态空间尺寸、动态 `num_tokens`、控制流和大量 shape 运算,CoreML 在 MacBook M4 上会在 Session 创建阶段因执行计划构建失败;加入该配置前,同一 Provider 使用默认模型格式可以运行。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 让设备名称准确表达 CoreML Execution Provider。
|
||||
- 在全部受支持的 macOS arm64 设备上提供 CoreML。
|
||||
- 恢复 MoGe-2 在 MacBook M4 CoreML 模式下的 Session 创建与推理。
|
||||
- 保持共享 ONNX Session 工厂简单,并保留 CPU fallback。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 引入 PyTorch 或实现 PyTorch MPS backend。
|
||||
- 保证所有 CoreML 算子只在 GPU 上执行。
|
||||
- 支持 macOS x64、iOS 或新的模型格式。
|
||||
- 保留 `mps` 任务值、配置值或转发映射。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 使用 `coreml` 作为唯一 Apple 加速设备标识
|
||||
|
||||
Blender Enum、Job 参数、运行时设备清单和 Provider 选择表统一使用小写 `coreml`,界面显示 `CoreML`。旧 `mps` 标识直接删除,因为它描述的是不同的推理 API;增加别名会继续扩大协议边界并违反项目不保留兼容层的约束。
|
||||
|
||||
### 2. 按平台能力判断 CoreML,不按芯片代际判断
|
||||
|
||||
macOS 且机器架构为 `arm64` 或 `aarch64` 时提供 `coreml` 和 `cpu`,其他 Unix 平台只提供 `cpu`,Windows 维持 DirectML 与 CPU。删除 CPU brand string 正则、`sysctl` 子进程和 M4 代际测试。这样支持范围与 `macos-arm64` 扩展包以及 ONNX Runtime macOS arm64 wheel 一致。
|
||||
|
||||
保留 M4 门槛的替代方案被放弃,因为它不是 Provider 能力判断,只会让 M1–M3 在具备 CoreML 时被迫使用 CPU。
|
||||
|
||||
### 3. 默认 CoreML 配置与 MoGe-2 静态分区配置分离
|
||||
|
||||
共享 Session 工厂默认向 ONNX Runtime 传递普通的 `CoreMLExecutionProvider` 名称,不再为所有模型强制 `MLProgram`。它同时接受可选的模型级 CoreML options;只有调用方明确提供时才把 CoreML Provider 配置为 tuple。CUDA 的独立 arena 配置保持不变。
|
||||
|
||||
MoGe-2 传入 `RequireStaticInputShapes=1`,使 CoreML EP 只接管输入 shape 已静态确定的分区,动态空间尺寸、动态 `num_tokens` 和相关控制流留给 CPU fallback。BEN2、DA3 与 Upscale 不携带该选项,继续使用普通 CoreML 配置。
|
||||
|
||||
该决定取代“所有模型都使用普通 CoreML 配置”的原方案:M2 真机证明 NeuralNetwork 格式虽然能创建 MoGe-2 Session,但动态 CoreML 分区仍会在执行时失败。也不选择 `CPUAndGPU`,因为故障来自动态图兼容性,而不是 Neural Engine 选择;不选择整体强制 CPU,因为静态限制仍允许安全分区使用 CoreML。
|
||||
|
||||
### 4. macOS 界面默认 CPU,CoreML Session 保持 CPU fallback
|
||||
|
||||
macOS arm64 的设备枚举把 CPU 放在 CoreML 之前,使新安装和未保存设备选择时默认使用 CPU。CoreML 的性能取决于模型和设备,用户需要时可手动选择,不根据芯片代际自动启用。
|
||||
|
||||
显式选择 `coreml` 时仍以 `CoreMLExecutionProvider` 为首选并显式附带 `CPUExecutionProvider`;共享 Session 工厂的自动 Provider 顺序也保持不变。这样 MoGe-2 被静态限制排除的动态节点具有确定的 CPU fallback;明确选择 CPU 时则只请求 CPU Provider。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [CoreML 默认配置在未来 ONNX Runtime 版本中可能变化] → 固定 ONNX Runtime 版本,并以 Session 参数测试保证 AnyImage 不再主动强制 `MLProgram`。
|
||||
- [MoGe-2 静态分区限制可能减少 CoreML 加速比例] → 接受 CPU fallback 以换取正确性,并用真机 smoke test 验证;不把 CoreML 设备描述为纯 GPU。
|
||||
- [M1–M3 未覆盖真实设备集成测试] → 单元测试覆盖平台枚举,并在可用 Apple Silicon 设备上执行 MoGe-2 smoke test;真实失败仍可选择 CPU。
|
||||
- [旧 Blender 配置保存了 `MPS`] → 这是明确的 breaking rename;不解析旧值,用户在新版中选择 `CoreML` 或 `CPU`。
|
||||
- [CoreML 不保证纯 GPU] → 界面和文档只承诺 CoreML,不再使用 Metal/MPS 或纯 GPU 表述。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 同步替换设备协议与界面枚举中的 `mps` 为 `coreml`,删除芯片代际检测。
|
||||
2. 移除共享的强制 CoreML options,让普通模型使用默认配置,并让 MoGe-2 单独传入 `RequireStaticInputShapes=1`。
|
||||
3. 更新测试和内部文档,再运行相关单元测试。
|
||||
4. 在可用的 M2 或 M4 设备选择 CoreML,分别执行 MoGe-2、BEN2 和 Upscale smoke test;MoGe-2 必须完成 Session 创建和一次推理。
|
||||
|
||||
若真实设备验证失败,回滚本变更并使用 CPU;不重新引入 `mps` 兼容层。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无。
|
||||
30
openspec/changes/correct-coreml-device-support/proposal.md
Normal file
30
openspec/changes/correct-coreml-device-support/proposal.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
macOS 设备当前以 `mps` 表示实际的 ONNX Runtime CoreML Execution Provider,并人为限制为 M4 及以上;同时强制使用 `MLProgram` 会使具有动态输入和控制流的 MoGe-2 在 MacBook M4 上创建模型时触发 `Error in building plan`。需要让设备名称、支持范围和实际推理后端一致,并恢复 MoGe-2 可用性。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**:将公开任务参数、运行时设备清单和界面枚举中的设备标识由 `mps` 改为 `coreml`,不保留旧标识兼容层。
|
||||
- macOS arm64 构建对所有 Apple Silicon 设备提供 CoreML 与 CPU 选项,移除 Apple 芯片代际解析和 M4 最低代际限制。
|
||||
- Apple Silicon Mac 默认使用 CPU,CoreML 保留为用户手动选择的可选设备。
|
||||
- 通用 CoreML Session 使用 ONNX Runtime 的兼容默认模型格式,不再强制把所有模型转换为 `MLProgram`。
|
||||
- MoGe-2 为 CoreML 单独启用静态输入分区限制,只将静态 shape 分区交给 CoreML,并把其余节点交给 CPU Execution Provider。
|
||||
- 显式保留 CoreML 之后的 CPU fallback,并验证 MoGe-2、BEN2 与 Upscale 的设备选择不回归。
|
||||
- 同步更新设备相关测试和内部文档,使 `coreml` 明确表示 CoreML 调度的 Apple CPU、GPU 与 Neural Engine,而不是 PyTorch MPS backend。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `coreml-inference`: 规定 macOS arm64 的 CoreML 设备标识、支持范围、Provider 选择和 MoGe-2 兼容行为。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响 Blender 设备枚举、Job 参数、运行时 Manifest 和 ONNX Runtime Session 创建。
|
||||
- 影响 `properties.py`、`server/models/onnx_runtime.py`、`server/models/onnx_moge2.py` 及对应测试与内部文档。
|
||||
- `mps` 设备值会被删除;尚未完成或外部构造的旧任务参数必须改用 `coreml`。
|
||||
- 不新增依赖,不改变模型文件、模型输出协议或 Blender Operator 标识符。
|
||||
@@ -0,0 +1,69 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: CoreML 使用唯一设备标识
|
||||
系统 SHALL 在界面、任务参数、运行时设备清单和 ONNX Provider 选择中使用 `coreml` 表示 Apple CoreML Execution Provider,并 SHALL NOT 接受或生成 `mps` 设备标识。
|
||||
|
||||
#### Scenario: macOS 用户查看推理设备
|
||||
- **WHEN** 用户在受支持的 macOS arm64 设备上查看推理设备枚举
|
||||
- **THEN** 系统显示 `CoreML` 和 `CPU`,且不显示 `MPS`
|
||||
|
||||
#### Scenario: CoreML 任务参数
|
||||
- **WHEN** 用户选择 CoreML 并创建 AI 任务
|
||||
- **THEN** 系统提交的设备参数为 `coreml`
|
||||
|
||||
#### Scenario: 旧设备标识不再受支持
|
||||
- **WHEN** Provider 选择入口接收到 `mps`
|
||||
- **THEN** 系统拒绝该值,而不将其转换或转发为 CoreML
|
||||
|
||||
### Requirement: Apple Silicon 支持范围按平台确定
|
||||
系统 SHALL 在 macOS 的 `arm64` 或 `aarch64` 架构上提供 CoreML,不得根据 Apple 芯片代际限制该设备。
|
||||
|
||||
#### Scenario: M1 至 M3 设备
|
||||
- **WHEN** 系统运行于 M1、M2 或 M3 的 macOS arm64 环境
|
||||
- **THEN** 可用设备包含 `coreml` 和 `cpu`
|
||||
|
||||
#### Scenario: M4 或更新设备
|
||||
- **WHEN** 系统运行于 M4 或更新的 macOS arm64 环境
|
||||
- **THEN** 可用设备包含 `coreml` 和 `cpu`
|
||||
|
||||
#### Scenario: Apple Silicon 默认设备
|
||||
- **WHEN** macOS arm64 同时提供 CPU 与 CoreML
|
||||
- **THEN** 设备枚举默认选择 CPU,且用户仍可手动选择 CoreML
|
||||
|
||||
#### Scenario: 非 Apple Silicon 平台
|
||||
- **WHEN** 系统不运行于 macOS arm64 或 aarch64 环境
|
||||
- **THEN** 系统不因 CoreML 规则新增 `coreml` 设备
|
||||
|
||||
### Requirement: CoreML Session 使用模型兼容配置
|
||||
系统 SHALL 以 `CoreMLExecutionProvider` 创建 CoreML Session,并 SHALL NOT 强制设置 `ModelFormat=MLProgram`。普通模型 SHALL 使用默认 CoreML 配置;MoGe-2 SHALL 只允许 CoreML 接管静态输入 shape 分区;CoreML 不支持或未接管的执行 SHALL 由 CPU fallback 处理。
|
||||
|
||||
#### Scenario: 显式选择 CoreML
|
||||
- **WHEN** 任务请求设备 `coreml` 且 ONNX Runtime 提供 CoreML Execution Provider
|
||||
- **THEN** Session 以 CoreML 为首选 Provider、以 CPU 为后备 Provider,且创建参数不包含强制的 CoreML `MLProgram` 配置
|
||||
|
||||
#### Scenario: MoGe-2 限制 CoreML 动态分区
|
||||
- **WHEN** 系统以 CoreML 创建 MoGe-2 Session
|
||||
- **THEN** CoreML Provider options 包含 `RequireStaticInputShapes=1`,且不包含 `ModelFormat=MLProgram`
|
||||
|
||||
#### Scenario: 其他模型不继承 MoGe-2 限制
|
||||
- **WHEN** 系统以 CoreML 创建 BEN2、DA3 或 Upscale Session
|
||||
- **THEN** CoreML Provider 不包含 MoGe-2 的静态输入限制
|
||||
|
||||
#### Scenario: 自动选择 CoreML
|
||||
- **WHEN** 任务请求自动设备且 CoreML 与 CPU Provider 都可用
|
||||
- **THEN** CoreML 的优先级高于 CPU,并保留 CPU fallback
|
||||
|
||||
#### Scenario: CoreML 不可用
|
||||
- **WHEN** 任务请求 CoreML 但 ONNX Runtime 只提供 CPU Provider
|
||||
- **THEN** 系统使用 CPU Provider
|
||||
|
||||
### Requirement: MoGe-2 可在 CoreML 模式推理
|
||||
系统 MUST 支持 MoGe-2 ONNX 模型在兼容 Apple Silicon CoreML 环境中创建 Session 并完成推理,不得因 AnyImage 强制选择 MLProgram 而在创建模型时失败。
|
||||
|
||||
#### Scenario: Apple Silicon 执行 MoGe-2
|
||||
- **WHEN** M2 或 M4 用户选择 CoreML 并运行 MoGe-2 功能
|
||||
- **THEN** 系统成功创建 CoreML Session 并完成至少一帧 MoGe-2 推理
|
||||
|
||||
#### Scenario: 其他 AI 模型继续使用 CoreML
|
||||
- **WHEN** 用户选择 CoreML 并运行 BEN2 或 Upscale
|
||||
- **THEN** 对应模型继续完成 Session 创建和推理
|
||||
18
openspec/changes/correct-coreml-device-support/tasks.md
Normal file
18
openspec/changes/correct-coreml-device-support/tasks.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 1. 统一 CoreML 设备协议
|
||||
|
||||
- [x] 1.1 将 Blender 设备标签、Enum 值、任务参数和 ONNX Provider 映射中的 `mps` 全部替换为 `coreml`,删除旧值的兼容路径。
|
||||
- [x] 1.2 删除 Apple 芯片品牌解析、`sysctl` 调用和 M4 代际限制,按 macOS arm64/aarch64 平台提供 `coreml` 与 `cpu`。
|
||||
- [x] 1.3 调整设备枚举测试,覆盖 M1–M4 所在的 macOS arm64、非 Apple Silicon 平台、Manifest 设备清单和旧 `mps` 值拒绝行为。
|
||||
- [x] 1.4 在全部 Apple Silicon Mac 上默认选择 CPU,并保留 CoreML 手动选项。
|
||||
|
||||
## 2. 恢复 MoGe-2 CoreML 兼容性
|
||||
|
||||
- [x] 2.1 让共享 Session 工厂默认使用普通 `CoreMLExecutionProvider`,同时支持可选模型级 CoreML options;显式 CoreML 选择保留 CPU fallback,CUDA arena 配置不变。
|
||||
- [x] 2.2 更新共享 ONNX Runtime 测试,验证默认配置、可选 CoreML options、显式/自动 CPU fallback 以及 `mps` 拒绝行为。
|
||||
- [x] 2.3 让 MoGe-2 CoreML Session 传入 `RequireStaticInputShapes=1` 且不强制 `MLProgram`,更新测试并保持动态图片与 `num_tokens` 输入不变。
|
||||
|
||||
## 3. 文档与验证
|
||||
|
||||
- [x] 3.1 更新用户设备说明和 `docs/internals/runtime.md`,统一使用 CoreML,并说明它由 Apple CPU、GPU 与 Neural Engine 调度而非 PyTorch MPS。
|
||||
- [x] 3.2 运行 `uv run pytest tests/test_blender_addon.py tests/test_model_onnx_runtime.py tests/test_model_moge2_onnx.py tests/test_docs.py`。
|
||||
- [ ] 3.3 在可用的 M2 或 M4 设备选择 CoreML,分别完成 MoGe-2、BEN2 和 Upscale 的单次 smoke test,确认 MoGe-2 不再出现 CoreML 计划构建或预测执行错误。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-01
|
||||
@@ -0,0 +1,40 @@
|
||||
## Context
|
||||
|
||||
`ServerPanel.draw()` 目前把 `READY` 与 `BUSY` 都显示为 Running,但只在 `READY` 分支创建重启和停止按钮。Cached 模型名称则通过逗号拼接到单个 Label,模型较多时会形成过长的一行。
|
||||
|
||||
BlendJob 当前以 `READY` 表示等待任务、以 `BUSY` 表示执行任务;二者都是 Server 已运行的子状态。此次调整只改变 Blender 面板布局,不改变状态协议或生命周期 Operator。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 统一 Running 判定,使所有运行中子状态共享重启和停止按钮。
|
||||
- 保留各子状态原有的状态文本和图标。
|
||||
- Cached 包含多个模型时,将模型名称逐项分行显示。
|
||||
- 用 UI 测试锁定状态与布局行为。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不改变 Server 启动、重启、停止或任务取消语义。
|
||||
- 不新增 Server 状态、API 或依赖。
|
||||
- 不改变模型缓存的内容、顺序或清理行为。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 统一复用 Running 判定
|
||||
|
||||
在读取 `server_status()` 后,以同一个 Running 判定驱动状态标签、生命周期按钮和 Cached 区域。当前 Running 状态集合为 `READY` 与 `BUSY`:`READY` 表示运行并等待,`BUSY` 表示运行并处理任务。
|
||||
|
||||
相比在 `READY` 与 `BUSY` 分支分别创建按钮,集中判定可避免后续运行中展示再次分叉。非 Running 状态继续沿用现有 Start 或状态提示分支。
|
||||
|
||||
### Cached 多项使用纵向列表
|
||||
|
||||
无缓存和单个缓存模型保留紧凑的一行展示;两个及以上缓存模型使用纵向布局,每个模型名称占一行,并保留同一区域的清理按钮。
|
||||
|
||||
相比在 Label 文本中插入换行,使用 Blender Layout 的独立 Label 能稳定参与面板布局,也便于测试每个模型项。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Busy 时停止或重启会中断当前任务] → 这是用户主动触发的既有生命周期行为;本变更只恢复入口,不改变 Operator 语义。
|
||||
- [多个模型使面板变高] → 仅在确有多个缓存模型时展开,并以可读性优先。
|
||||
- [状态分支与按钮分支再次偏离] → 使用同一个 Running 判定,并覆盖 `READY`、`BUSY` 与非 Running 状态测试。
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
Server 处于 Busy 时仍然是 Running,但当前面板会隐藏重启和停止按钮,导致用户在任务执行期间无法直接管理 Server 生命周期。Running 的控制入口应保持一致,不应受 Busy、等待或其他运行中子状态影响。
|
||||
|
||||
## What Changes
|
||||
|
||||
- Server 只要处于 Running,就显示重启和停止按钮。
|
||||
- Busy、等待及其他运行中子状态继续显示各自的状态信息,但不再隐藏生命周期控制按钮。
|
||||
- Cached 列表包含多个模型时,每个模型换行显示,避免单行内容过长。
|
||||
- 补充 UI 测试,覆盖运行中状态的按钮可见性与 Cached 列表布局。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `server-lifecycle-controls`: 规定 Server 在所有 Running 子状态下提供一致的重启与停止入口。
|
||||
- `server-cache-display`: 规定 Server 面板中 Cached 模型列表的分行展示行为。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/anyimage/panel.py` 中 Server 状态与控制按钮的展示逻辑。
|
||||
- Blender Add-on UI 测试。
|
||||
- 不改变 Server API、状态协议或依赖。
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Cached model list layout
|
||||
Server 面板 SHALL 显示所有 Cached 模型名称,并 SHALL 在存在多个模型时让每个模型名称占据独立的视觉行。
|
||||
|
||||
#### Scenario: Multiple models are cached
|
||||
- **WHEN** Server 为 Running 且 Cached 列表包含两个或更多模型
|
||||
- **THEN** 面板逐行显示每个模型名称,并保留缓存清理按钮
|
||||
|
||||
#### Scenario: One model is cached
|
||||
- **WHEN** Server 为 Running 且 Cached 列表只包含一个模型
|
||||
- **THEN** 面板在一行内显示 `Cached` 与该模型名称,并保留缓存清理按钮
|
||||
|
||||
#### Scenario: No model is cached
|
||||
- **WHEN** Server 为 Running 且 Cached 列表为空
|
||||
- **THEN** 面板显示 `Cached: None`,并保留缓存清理按钮
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Running Server lifecycle controls
|
||||
Server 面板 SHALL 在 Server 的每个 Running 子状态下显示重启和停止控制,并 SHALL 保留该子状态对应的状态文本与图标。
|
||||
|
||||
#### Scenario: Server is running and waiting
|
||||
- **WHEN** Server 状态为 `READY`
|
||||
- **THEN** 面板显示 Running 状态、重启按钮和停止按钮
|
||||
|
||||
#### Scenario: Server is running and busy
|
||||
- **WHEN** Server 状态为 `BUSY`
|
||||
- **THEN** 面板显示 Running (Busy) 状态、重启按钮和停止按钮
|
||||
|
||||
#### Scenario: Server is not running
|
||||
- **WHEN** Server 状态不是 Running 子状态
|
||||
- **THEN** 面板不显示 Running 状态的重启和停止按钮,并沿用该状态对应的现有控制或提示
|
||||
10
openspec/changes/show-server-controls-while-running/tasks.md
Normal file
10
openspec/changes/show-server-controls-while-running/tasks.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## 1. Server Panel 布局
|
||||
|
||||
- [x] 1.1 统一 `READY` 与 `BUSY` 的 Running 判定,让状态标签、重启按钮、停止按钮和 Cached 区域复用同一分支
|
||||
- [x] 1.2 调整 Cached 模型布局:空列表和单模型保持单行,多个模型使用逐项纵向 Label,并保留清理按钮
|
||||
|
||||
## 2. UI 行为验证
|
||||
|
||||
- [x] 2.1 补充 Server Panel 测试,验证 `READY` 与 `BUSY` 都显示重启和停止按钮,非 Running 状态不显示这些按钮
|
||||
- [x] 2.2 补充 Cached 列表测试,验证零个、一个和多个模型的文本、分行与清理按钮
|
||||
- [x] 2.3 运行相关测试与完整测试套件,确认 Server Panel 和既有 Add-on 行为通过
|
||||
Reference in New Issue
Block a user