Compare commits
2 Commits
1e5fa4e6ba
...
2b3c0194a6
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b3c0194a6 | |||
| 75f6692f28 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Run tests
|
||||
run: uv run pytest
|
||||
run: uv run --group blender pytest tests/anyimage tests/tools
|
||||
|
||||
- name: Pack extensions
|
||||
run: uv run pack
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -23,4 +23,4 @@ jobs:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Run tests
|
||||
run: uv run pytest
|
||||
run: uv run --group blender pytest tests/anyimage tests/tools
|
||||
|
||||
BIN
docs/assets/favicon.png
LFS
Normal file
BIN
docs/assets/favicon.png
LFS
Normal file
Binary file not shown.
BIN
docs/assets/logo.png
LFS
BIN
docs/assets/logo.png
LFS
Binary file not shown.
@@ -36,6 +36,7 @@ nav:
|
||||
|
||||
theme:
|
||||
name: material
|
||||
favicon: assets/favicon.png
|
||||
logo: assets/logo.png
|
||||
language: zh
|
||||
font:
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-19
|
||||
77
openspec/changes/split-capability-environments/design.md
Normal file
77
openspec/changes/split-capability-environments/design.md
Normal file
@@ -0,0 +1,77 @@
|
||||
## Context
|
||||
|
||||
项目当前只有一个默认 `dev` 依赖组。`uv run pytest` 因此会同步 Blender、AI 推理、模型导出和文档构建的全部工具,即使 CI 只需要运行不涉及真实模型的测试。测试也没有稳定的能力分类,工作流只能运行整个集合。
|
||||
|
||||
依赖和测试存在两条不同边界:产品中的 AI 调度、界面和协议可以通过 mock 在常规环境验证;真实模型实现、ONNX Runtime 行为及模型资产生产需要额外的大型依赖。分类应以执行所需能力为准,而不是看到 AI 业务名称就全部排除。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 依赖组名称表达可用能力,而不是单一测试用途。
|
||||
- CI 只同步核心与 Blender 能力所需依赖。
|
||||
- AI 模型执行测试由开发者在本地显式运行。
|
||||
- 模型资产生产和文档构建拥有独立环境。
|
||||
- 测试选择规则可见、可校验,新测试不易被意外遗漏。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不改变扩展安装后的 AI 运行环境及依赖版本。
|
||||
- 不移除依赖 Blender 的 CI 覆盖。
|
||||
- 不下载真实模型权重作为测试前置条件。
|
||||
- 不修改产品功能、模型格式或打包产物。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 使用能力名称划分依赖组
|
||||
|
||||
依赖组采用以下职责:
|
||||
|
||||
- `dev`:uv 默认加载的通用开发环境,包括 pytest 和通用图像测试依赖。
|
||||
- `blender`:Blender API、节点和几何验证所需的增量依赖。
|
||||
- `ai`:本地 AI 推理实现验证所需的增量依赖。
|
||||
- `models`:下载、转换和导出模型资产所需工具;它独立于日常 AI 推理验证。
|
||||
- `docs`:文档构建和发布工具。
|
||||
|
||||
选择能力名称可以让同一组依赖同时服务业务开发、调试和测试。备选方案 `test-core`、`test-ai` 会错误地把依赖限定为测试用途;继续把所有工具放在 `dev` 则无法减少 CI 下载。
|
||||
|
||||
### 以目录表达测试执行边界
|
||||
|
||||
将要求 AI 专用运行时的测试放入 `tests/ai`。CI 直接运行 `tests/anyimage` 和 `tests/tools`;本地 AI 验证直接运行 `tests/ai`;全量验证运行 `tests`。
|
||||
|
||||
AI 界面、Operator 调度、参数序列化、模型目录和下载流程等使用 mock 且不加载模型运行时的测试不标记为 `ai`,继续在 CI 中执行。这样排除的是昂贵执行环境,而不是 AI 业务覆盖。
|
||||
|
||||
目录名称直接表达执行环境,不增加 marker 和筛选表达式。实现时必须验证未安装 `ai` 和 `models` 组时,常规测试目录能够完成收集,避免顶层导入泄漏可选依赖。
|
||||
|
||||
### CI 显式选择环境
|
||||
|
||||
测试与发布工作流使用 `--group blender`,由 uv 自动加载默认 `dev`,并直接执行常规测试目录。打包只需默认 `dev`。所有命令均使用 uv 的默认组语义,不额外传入 `--no-default-groups`。
|
||||
|
||||
`dev` 只保存所有开发流程都需要的轻量依赖;大型能力必须留在显式组中。发布工作流采用同一测试命令,保证发布门禁与普通 CI 一致。
|
||||
|
||||
### 模型资产生产与 AI 运行分离
|
||||
|
||||
`torch`、`onnx` 及模型导出脚本的其他依赖归入 `models`;`onnxruntime` 和 AI 服务本地验证所需依赖归入 `ai`。只有两种能力确实共享的包才重复约束同一版本。
|
||||
|
||||
模型 CLI 测试若只验证调度和失败处理且使用 mock,可留在默认 `dev` 环境;只有实际导入或执行模型制作组件的测试进入 AI/模型相关本地集合。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [pytest 在筛选前导入测试模块,AI 顶层导入可能导致 CI 收集失败] → 将可选运行时导入保持在执行边界内,并用不安装 AI 组的干净环境验证收集与测试。
|
||||
- [测试放错目录使昂贵测试进入 CI] → 让 `tests/ai` 只承载真实依赖 AI 专用运行时的测试,并在干净 CI 环境验证常规目录。
|
||||
- [AI 业务回归失去 CI 保护] → mock 层协议与集成测试继续保留在常规业务目录。
|
||||
- [能力组之间重复依赖或版本漂移] → 复用默认 `dev` 环境,并由单一 `uv.lock` 锁定版本。
|
||||
- [本地全量测试命令变长] → 在项目常用命令和测试配置中形成唯一、明确的命令,不保留旧环境兼容层。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 建立能力依赖组并刷新锁文件。
|
||||
2. 将 AI 模型执行测试移动到独立目录,确认不安装 AI 依赖时可收集常规测试目录。
|
||||
3. 分别运行核心、Blender、AI 和全量验证。
|
||||
4. 更新测试与发布工作流,确认 CI 的同步计划不包含 AI、模型和文档组。
|
||||
|
||||
若迁移失败,可在提交合并前恢复原 dependency group 与工作流;本变更不涉及用户数据或线上状态迁移。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无。实现阶段根据实际 import 链决定个别包归属,但不得改变上述能力边界。
|
||||
28
openspec/changes/split-capability-environments/proposal.md
Normal file
28
openspec/changes/split-capability-environments/proposal.md
Normal file
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
当前开发依赖全部集中在 `dev` 组,常规测试和 CI 会下载模型导出、ONNX 推理及文档构建等无关的大型依赖。需要按业务能力拆分环境,并让 AI 模型测试只在本地显式运行,以降低 CI 的下载量和执行成本。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 将开发依赖按默认 `dev` 及 `blender`、`ai`、`models`、`docs` 能力分组,不使用仅体现测试用途的 `test-*` 命名。
|
||||
- 将 AI 模型执行测试集中到独立目录,常规测试保留在现有业务目录。
|
||||
- CI 和发布流程只安装并运行默认开发与 Blender 集成所需环境,不安装 AI 推理和模型导出依赖。
|
||||
- 提供明确的本地 AI 测试与全量测试命令。
|
||||
- 保留不依赖真实模型运行时的 AI 界面、参数、任务协议和错误处理测试在 CI 中。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `capability-environments`: 定义开发依赖的能力分组、测试分类,以及 CI 与本地测试环境的选择规则。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `developer-tool-cli`: 调整项目测试命令,使核心、Blender、AI 和全量验证可以分别执行。
|
||||
|
||||
## Impact
|
||||
|
||||
- 修改 `pyproject.toml` 的 dependency groups 和 pytest 配置,并刷新 `uv.lock`。
|
||||
- 调整测试标记及少量测试归属,不改变产品运行时代码和扩展依赖协议。
|
||||
- 修改 `.github/workflows/test.yml` 与 `.github/workflows/release.yml` 的依赖安装和测试命令。
|
||||
- 常规 CI 不再下载 `torch`、`onnx`、`onnxruntime`、`huggingface-hub` 和文档工具链;本地仍可运行 AI 与全量测试。
|
||||
@@ -0,0 +1,45 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Capability-oriented dependency groups
|
||||
|
||||
项目 SHALL 使用默认 `dev` 及 `blender`、`ai`、`models` 和 `docs` 依赖组分别表达通用开发、Blender 集成、AI 推理、模型资产生产和文档构建能力。非默认能力组 SHALL 仅包含其职责所需的增量依赖。
|
||||
|
||||
#### Scenario: Install Blender development environment
|
||||
- **WHEN** 开发者或 CI 仅选择 `blender` 能力组
|
||||
- **THEN** uv 自动加载默认 `dev` 及 Blender 验证所需依赖,不安装 AI 推理、模型资产生产或文档构建专用依赖
|
||||
|
||||
#### Scenario: Install AI development environment
|
||||
- **WHEN** 开发者选择 `ai` 能力组
|
||||
- **THEN** 环境包含本地 AI 推理及其测试所需依赖
|
||||
|
||||
#### Scenario: Install model production environment
|
||||
- **WHEN** 开发者选择 `models` 能力组
|
||||
- **THEN** 环境包含模型下载、转换和导出所需依赖,且该组不成为常规 CI 的前置条件
|
||||
|
||||
### Requirement: Test categories follow execution requirements
|
||||
|
||||
测试 SHALL 根据实际执行环境分目录。只有要求 AI 专用运行时或模型实现的测试 SHALL 位于 `tests/ai`;使用 mock 验证 AI 界面、任务协议、参数传递和失败处理的测试 SHALL 留在常规业务测试目录。
|
||||
|
||||
#### Scenario: Run CI test set
|
||||
- **WHEN** 在只安装 `blender` 及其基础能力的环境执行常规测试目录
|
||||
- **THEN** 测试收集和执行成功,且不要求 AI 或模型资产生产专用依赖
|
||||
|
||||
#### Scenario: Run local AI test set
|
||||
- **WHEN** 开发者安装 `ai` 能力并执行 `tests/ai`
|
||||
- **THEN** 只执行明确归类的 AI 模型运行与实现测试
|
||||
|
||||
#### Scenario: Protect mocked AI integration
|
||||
- **WHEN** AI 相关测试不加载真实模型运行时且只验证产品集成契约
|
||||
- **THEN** 该测试包含在常规 CI 集合中
|
||||
|
||||
### Requirement: Continuous integration excludes local-only capabilities
|
||||
|
||||
普通测试工作流和发布工作流 SHALL 使用默认 `dev`、显式选择 Blender 能力并直接运行常规测试目录,不得同步 `ai`、`models` 或 `docs` 能力组。项目命令 SHALL 不要求关闭默认依赖组。
|
||||
|
||||
#### Scenario: Pull request validation
|
||||
- **WHEN** 测试工作流在 pull request 或主分支运行
|
||||
- **THEN** 工作流完成非 AI 测试,且依赖同步不包含 AI、模型生产和文档专用包
|
||||
|
||||
#### Scenario: Release validation
|
||||
- **WHEN** 发布工作流构建扩展包
|
||||
- **THEN** 发布前运行与普通 CI 相同的非 AI 测试门禁,不运行本地 AI 测试
|
||||
@@ -0,0 +1,17 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Capability-specific test commands
|
||||
|
||||
项目 SHALL 提供明确的 uv 命令分别运行常规 CI 测试、本地 AI 测试和本地全量测试。命令 SHALL 使用默认 `dev` 并显式增加所需能力组,不要求传入 `--no-default-groups`。
|
||||
|
||||
#### Scenario: Run regular validation
|
||||
- **WHEN** 开发者运行常规验证命令
|
||||
- **THEN** 命令安装核心与 Blender 能力并执行所有非 AI 测试
|
||||
|
||||
#### Scenario: Run AI validation locally
|
||||
- **WHEN** 开发者运行 AI 验证命令
|
||||
- **THEN** 命令安装 AI 能力并仅执行 AI 测试
|
||||
|
||||
#### Scenario: Run full validation locally
|
||||
- **WHEN** 开发者运行全量验证命令
|
||||
- **THEN** 命令安装 Blender 与 AI 能力并执行全部测试
|
||||
23
openspec/changes/split-capability-environments/tasks.md
Normal file
23
openspec/changes/split-capability-environments/tasks.md
Normal file
@@ -0,0 +1,23 @@
|
||||
## 1. 拆分能力环境
|
||||
|
||||
- [x] 1.1 将 `pyproject.toml` 拆为默认 `dev` 及 `blender`、`ai`、`models` 和 `docs` 增量能力组,并保持现有版本约束
|
||||
- [x] 1.2 检查模型 CLI、打包和节点工具的真实 import 链,修正依赖归属并确保各能力组可独立同步
|
||||
- [x] 1.3 刷新 `uv.lock`,确认常规 Blender 环境不包含 AI、模型生产和文档专用包
|
||||
|
||||
## 2. 分类测试
|
||||
|
||||
- [x] 2.1 将模型实现、推理和后处理测试移动到独立的 `tests/ai` 目录
|
||||
- [x] 2.2 保留使用 mock 的 AI 界面、Operator、任务协议、模型目录、下载和失败处理测试在非 AI 集合
|
||||
- [x] 2.3 消除非 AI 测试收集阶段对 AI 与模型生产专用依赖的顶层导入
|
||||
- [x] 2.4 更新测试命令相关测试,使常规、AI 和全量验证的能力组与 pytest 选择条件受到覆盖
|
||||
|
||||
## 3. 精简 CI
|
||||
|
||||
- [x] 3.1 修改 `.github/workflows/test.yml`,只选择 `blender` 能力并直接运行常规测试目录
|
||||
- [x] 3.2 修改 `.github/workflows/release.yml`,使用与普通 CI 一致的测试环境和常规测试目录
|
||||
|
||||
## 4. 验证
|
||||
|
||||
- [x] 4.1 在不安装 `ai`、`models` 和 `docs` 能力的环境运行全部常规测试目录
|
||||
- [x] 4.2 在本地 AI 环境运行 AI 测试,并在 Blender 与 AI 能力同时启用时运行全量测试
|
||||
- [x] 4.3 检查最终差异和 uv 依赖树,确认 CI 路径不会下载 `torch`、`onnx`、`onnxruntime`、`huggingface-hub` 或文档工具链
|
||||
@@ -9,21 +9,30 @@ dependencies = ["blendjob==0.1.17", "numpy>=1.26,<2.0", "scipy==1.15.3"]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pillow==12.1.1",
|
||||
"pip>=25,<27",
|
||||
"pytest",
|
||||
]
|
||||
blender = [
|
||||
"bpy==4.5.3",
|
||||
]
|
||||
ai = [
|
||||
"huggingface-hub==1.4.1",
|
||||
"onnxruntime==1.24.4",
|
||||
"trimesh==4.11.2",
|
||||
]
|
||||
models = [
|
||||
"onnx==1.19.0",
|
||||
"onnxruntime==1.24.4",
|
||||
"torch==2.13.0",
|
||||
]
|
||||
docs = [
|
||||
"mike>=2.1.3,<3",
|
||||
"mkdocs>=1.6.1,<2",
|
||||
"mkdocs-click==0.9.0",
|
||||
"mkdocs-material>=9.6.16,<10",
|
||||
"mkdocs-static-i18n[material]>=1.3.0",
|
||||
"mkdocstrings[python]>=1.0.6",
|
||||
"onnx==1.19.0",
|
||||
"onnxruntime==1.24.4",
|
||||
"pillow==12.1.1",
|
||||
"pip>=25,<27",
|
||||
"pytest",
|
||||
"torch==2.13.0",
|
||||
"trimesh==4.11.2",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
1
tests/ai/__init__.py
Normal file
1
tests/ai/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""AI inference tests run locally."""
|
||||
56
tests/tools/test_environments.py
Normal file
56
tests/tools/test_environments.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import tomllib
|
||||
|
||||
from tests.support.paths import PROJECT_ROOT
|
||||
|
||||
|
||||
def project_configuration():
|
||||
return tomllib.loads(
|
||||
(PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8")
|
||||
)
|
||||
|
||||
|
||||
def group_packages(configuration, name):
|
||||
groups = configuration["dependency-groups"]
|
||||
packages = set()
|
||||
for item in groups[name]:
|
||||
if isinstance(item, dict):
|
||||
packages.update(group_packages(configuration, item["include-group"]))
|
||||
else:
|
||||
packages.add(item.split("==", 1)[0].split(">=", 1)[0])
|
||||
return packages
|
||||
|
||||
|
||||
def test_capability_groups_keep_ci_dependencies_small():
|
||||
configuration = project_configuration()
|
||||
groups = configuration["dependency-groups"]
|
||||
assert set(groups) == {"dev", "blender", "ai", "models", "docs"}
|
||||
ci_packages = group_packages(configuration, "dev") | group_packages(
|
||||
configuration, "blender"
|
||||
)
|
||||
assert ci_packages.isdisjoint(
|
||||
{"huggingface-hub", "onnx", "onnxruntime", "torch"}
|
||||
)
|
||||
|
||||
|
||||
def test_ai_and_model_groups_own_their_runtime_dependencies():
|
||||
configuration = project_configuration()
|
||||
assert {"huggingface-hub", "onnxruntime"} <= group_packages(
|
||||
configuration, "ai"
|
||||
)
|
||||
assert {"onnx", "onnxruntime", "torch"} <= group_packages(
|
||||
configuration, "models"
|
||||
)
|
||||
|
||||
|
||||
def test_workflows_run_the_regular_blender_suite():
|
||||
command = "uv run --group blender pytest tests/anyimage tests/tools"
|
||||
for name in ("test.yml", "release.yml"):
|
||||
workflow = (PROJECT_ROOT / ".github" / "workflows" / name).read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert command in workflow
|
||||
|
||||
release = (PROJECT_ROOT / ".github" / "workflows" / "release.yml").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert "uv run pack" in release
|
||||
@@ -14,7 +14,7 @@ def _require_build_dependencies():
|
||||
except ImportError as error:
|
||||
raise RuntimeError(
|
||||
"Model build dependencies are missing. Run with: "
|
||||
"uv sync"
|
||||
"uv sync --group models"
|
||||
) from error
|
||||
return onnx, onnxruntime, torch
|
||||
|
||||
|
||||
40
uv.lock
generated
40
uv.lock
generated
@@ -22,22 +22,31 @@ dependencies = [
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "bpy" },
|
||||
ai = [
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "onnxruntime" },
|
||||
{ name = "trimesh" },
|
||||
]
|
||||
blender = [
|
||||
{ name = "bpy" },
|
||||
]
|
||||
dev = [
|
||||
{ name = "pillow" },
|
||||
{ name = "pip" },
|
||||
{ name = "pytest" },
|
||||
]
|
||||
docs = [
|
||||
{ name = "mike" },
|
||||
{ name = "mkdocs" },
|
||||
{ name = "mkdocs-click" },
|
||||
{ name = "mkdocs-material" },
|
||||
{ name = "mkdocs-static-i18n", extra = ["material"] },
|
||||
{ name = "mkdocstrings", extra = ["python"] },
|
||||
]
|
||||
models = [
|
||||
{ name = "onnx" },
|
||||
{ name = "onnxruntime" },
|
||||
{ name = "pillow" },
|
||||
{ name = "pip" },
|
||||
{ name = "pytest" },
|
||||
{ name = "torch" },
|
||||
{ name = "trimesh" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -48,22 +57,29 @@ requires-dist = [
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "bpy", specifier = "==4.5.3" },
|
||||
ai = [
|
||||
{ name = "huggingface-hub", specifier = "==1.4.1" },
|
||||
{ name = "onnxruntime", specifier = "==1.24.4" },
|
||||
{ name = "trimesh", specifier = "==4.11.2" },
|
||||
]
|
||||
blender = [{ name = "bpy", specifier = "==4.5.3" }]
|
||||
dev = [
|
||||
{ name = "pillow", specifier = "==12.1.1" },
|
||||
{ name = "pip", specifier = ">=25,<27" },
|
||||
{ name = "pytest" },
|
||||
]
|
||||
docs = [
|
||||
{ name = "mike", specifier = ">=2.1.3,<3" },
|
||||
{ name = "mkdocs", specifier = ">=1.6.1,<2" },
|
||||
{ name = "mkdocs-click", specifier = "==0.9.0" },
|
||||
{ name = "mkdocs-material", specifier = ">=9.6.16,<10" },
|
||||
{ name = "mkdocs-static-i18n", extras = ["material"], specifier = ">=1.3.0" },
|
||||
{ name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.6" },
|
||||
]
|
||||
models = [
|
||||
{ name = "onnx", specifier = "==1.19.0" },
|
||||
{ name = "onnxruntime", specifier = "==1.24.4" },
|
||||
{ name = "pillow", specifier = "==12.1.1" },
|
||||
{ name = "pip", specifier = ">=25,<27" },
|
||||
{ name = "pytest" },
|
||||
{ name = "torch", specifier = "==2.13.0" },
|
||||
{ name = "trimesh", specifier = "==4.11.2" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user