@mcptoolshop/style-dataset-lab 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.es.md +137 -108
- package/README.fr.md +137 -108
- package/README.hi.md +137 -108
- package/README.it.md +137 -108
- package/README.ja.md +137 -108
- package/README.md +87 -91
- package/README.pt-BR.md +137 -108
- package/README.zh.md +137 -108
- package/bin/sdlab.js +49 -0
- package/lib/adapters/diffusers-lora.js +110 -0
- package/lib/adapters/generic-image-caption.js +94 -0
- package/lib/card.js +145 -0
- package/lib/config.js +144 -0
- package/lib/eligibility.js +110 -0
- package/lib/eval-pack.js +283 -0
- package/lib/eval-runs.js +285 -0
- package/lib/export.js +259 -0
- package/lib/implementation-packs.js +316 -0
- package/lib/reingest.js +131 -0
- package/lib/snapshot.js +211 -0
- package/lib/split.js +409 -0
- package/lib/training-manifests.js +172 -0
- package/lib/training-packages.js +241 -0
- package/lib/training-profiles.js +97 -0
- package/package.json +12 -10
- package/scripts/canon-bind.js +1 -87
- package/scripts/card.js +73 -0
- package/scripts/doctor.js +8 -0
- package/scripts/eligibility.js +91 -0
- package/scripts/eval-pack.js +92 -0
- package/scripts/eval-run.js +121 -0
- package/scripts/export.js +85 -0
- package/scripts/implementation-pack.js +78 -0
- package/scripts/init.js +8 -0
- package/scripts/reingest.js +86 -0
- package/scripts/snapshot.js +132 -0
- package/scripts/split.js +154 -0
- package/scripts/training-manifest.js +119 -0
- package/scripts/training-package.js +84 -0
- package/scripts/training-profile.js +63 -0
package/README.zh.md
CHANGED
|
@@ -13,158 +13,187 @@
|
|
|
13
13
|
|
|
14
14
|
# style-dataset-lab
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
将已批准的视觉素材转化为版本控制、经过审核的数据集、分片、导出包和评估包。
|
|
17
17
|
|
|
18
18
|
## 这是什么
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
一个用于视觉数据规范和数据集生成的**流水线**。明确您的项目外观。根据既定规则进行筛选。生成可重复的数据集包,并确保数据分割的安全性。生成评估数据集,用于未来模型的验证。
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
流水线会生成四个产出物:
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
| 文物。 | 它是什么。 |
|
|
25
|
+
|----------|-----------|
|
|
26
|
+
| **Snapshot** | 经过筛选,符合条件的记录已冻结,并进行了指纹识别。所有被选入的记录都有明确的纳入理由。 |
|
|
27
|
+
| **Split** | 安全的训练/验证/测试数据集划分,防止数据泄露。共享相同主题的数据记录始终会被划分到同一个子集。 |
|
|
28
|
+
| **Export package** | 自包含的数据集:包含清单、元数据、图像、数据集划分、数据集描述文档以及校验和。 |
|
|
29
|
+
| **Eval pack** | 符合佳能标准的验证任务包括:车道覆盖、禁止漂移、锚点/黄金区域、主体连续性。 |
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
**仅本地运行。** `style-dataset-lab` 只与本地 `localhost:8188` 上的 ComfyUI 进行通信,不会进行任何外部网络请求。 没有遥测数据,没有分析功能,不会向外部发送数据。 图像生成完全在您的 GPU 上进行。 记录和规范数据都存储在您的文件系统中。
|
|
31
|
-
|
|
32
|
-
## npm 包包含的内容
|
|
33
|
-
|
|
34
|
-
`npm install @mcptoolshop/style-dataset-lab` 会为您提供:
|
|
31
|
+
流水线中的每个资产都包含以下三个要素:
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
1. **来源信息** -- 完整的生成历史记录(检查点、LoRA、种子、采样器、配置参数、生成时间)。
|
|
34
|
+
2. **合规性** -- 指示该资产是否符合相关规定,以及是否完全符合、不符合或部分符合。
|
|
35
|
+
3. **质量评估** -- 采用每维度的评分标准,对质量进行评估,结果分为:通过/未通过/临界。
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
主要从事游戏美术、角色设计、生物设计、建筑设计、车辆/机械概念设计等工作,以及任何需要保持视觉风格一致性的领域。
|
|
40
38
|
|
|
41
|
-
##
|
|
39
|
+
## 快速入门
|
|
42
40
|
|
|
43
41
|
```bash
|
|
44
|
-
#
|
|
45
|
-
npm install @mcptoolshop/style-dataset-lab
|
|
42
|
+
# Install the CLI + pipeline
|
|
43
|
+
npm install -g @mcptoolshop/style-dataset-lab
|
|
46
44
|
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
# Scaffold a new project
|
|
46
|
+
sdlab init my-project --domain character-design
|
|
47
|
+
|
|
48
|
+
# Validate the project structure
|
|
49
|
+
sdlab project doctor --project my-project
|
|
51
50
|
```
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
可用的领域包括:`game-art`(游戏美术)、`character-design`(角色设计)、`creature-design`(生物设计)、`architecture`(架构)、`vehicle-mech`(车辆机械设计)或`generic`(通用)。
|
|
53
|
+
|
|
54
|
+
## 命令行界面
|
|
54
55
|
|
|
55
56
|
```bash
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
sdlab init <name> [--domain <domain>] # Scaffold a new project
|
|
58
|
+
sdlab project doctor [--project <name>] # Validate project config
|
|
59
|
+
|
|
60
|
+
sdlab generate <pack> [--project <name>] # Generate candidates via ComfyUI
|
|
61
|
+
sdlab generate:identity <packet> # Named-subject identity images
|
|
62
|
+
sdlab generate:controlnet # ControlNet-guided generation
|
|
63
|
+
sdlab generate:ipadapter # IP-Adapter reference-guided
|
|
64
|
+
|
|
65
|
+
sdlab curate <id> <status> <explanation> # Record review judgment
|
|
66
|
+
sdlab compare <a> <b> <winner> <reason> # Pairwise A-vs-B comparison
|
|
67
|
+
sdlab bind [--project <name>] # Bind records to constitution rules
|
|
68
|
+
sdlab painterly [--project <name>] # Post-processing style pass
|
|
69
|
+
|
|
70
|
+
sdlab snapshot create [--profile <name>] # Create frozen dataset snapshot
|
|
71
|
+
sdlab snapshot list # List all snapshots
|
|
72
|
+
sdlab snapshot diff <a> <b> # Compare two snapshots
|
|
73
|
+
sdlab eligibility audit # Audit record training eligibility
|
|
74
|
+
sdlab split build [--snapshot <id>] # Build train/val/test split
|
|
75
|
+
sdlab split audit <id> # Audit split for leakage + balance
|
|
76
|
+
sdlab card generate # Generate dataset card (md + JSON)
|
|
77
|
+
sdlab export build [--snapshot <id>] # Build versioned export package
|
|
78
|
+
sdlab eval-pack build # Build canon-aware eval pack
|
|
61
79
|
```
|
|
62
80
|
|
|
63
|
-
|
|
81
|
+
所有命令都支持 `--project <名称>` 参数,默认值为 `star-freight`。
|
|
82
|
+
|
|
83
|
+
## 项目模型
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
每个项目都位于 `projects/` 目录下,作为一个独立的目录,拥有自己的规范、配置文件和数据。
|
|
66
86
|
|
|
67
87
|
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
projects/
|
|
89
|
+
my-project/
|
|
90
|
+
project.json Project identity + generation defaults
|
|
91
|
+
constitution.json Rules array with rationale templates
|
|
92
|
+
lanes.json Subject lanes with detection patterns
|
|
93
|
+
rubric.json Scoring dimensions + thresholds
|
|
94
|
+
terminology.json Group vocabulary + detection order
|
|
95
|
+
canon/ Style constitution (markdown)
|
|
96
|
+
records/ Per-asset JSON (provenance + judgment + canon)
|
|
97
|
+
inputs/prompts/ Prompt packs (JSON)
|
|
98
|
+
outputs/ Generated images (gitignored)
|
|
99
|
+
comparisons/ A-vs-B preference judgments
|
|
100
|
+
snapshots/ Frozen dataset snapshots
|
|
101
|
+
splits/ Train/val/test partitions
|
|
102
|
+
exports/ Versioned export packages
|
|
103
|
+
eval-packs/ Canon-aware eval instruments
|
|
82
104
|
```
|
|
83
105
|
|
|
84
|
-
##
|
|
106
|
+
## 流水线
|
|
85
107
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
108
|
+
```
|
|
109
|
+
canon → generate → curate → bind → snapshot → split → export → eval
|
|
110
|
+
| | | | | | | |
|
|
111
|
+
rules ComfyUI judgment rules frozen subject package verify
|
|
112
|
+
selection isolation
|
|
113
|
+
```
|
|
91
114
|
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
1. **定义规范**:编写您的风格指南和评估标准。
|
|
116
|
+
2. **生成**:ComfyUI 生成具有完整溯源信息的候选结果。
|
|
117
|
+
3. **筛选**:根据每个维度的评分和失败模式,进行批准/拒绝。
|
|
118
|
+
4. **关联**:将每个资源与规范规则关联,并给出通过/失败/部分通过的判定。
|
|
119
|
+
5. **快照**:将符合条件的记录冻结为具有确定性和唯一标识的选择。
|
|
120
|
+
6. **划分**:划分为训练集/验证集/测试集,同时进行主题隔离和类别平衡。
|
|
121
|
+
7. **导出**:构建一个独立的软件包,包含清单、元数据、图像和校验和。
|
|
122
|
+
8. **评估**:生成符合规范要求的测试工具,用于模型验证。
|
|
94
123
|
|
|
95
|
-
|
|
96
|
-
npm run generate -- --game star-freight inputs/prompts/wave1.json
|
|
97
|
-
npm run generate -- --game star-freight inputs/prompts/wave1.json --dry-run
|
|
124
|
+
下游格式转换(例如:TRL、LLaVA、Parquet等)由[`repo-dataset`](https://github.com/mcp-tool-shop-org/repo-dataset) 负责。`sdlab` 拥有数据集的原始数据,而 `repo-dataset` 将其转换为各种特定格式。
|
|
98
125
|
|
|
99
|
-
|
|
100
|
-
npm run generate:identity -- --game star-freight inputs/identity-packets/wave27a.json
|
|
126
|
+
## 域名模板
|
|
101
127
|
|
|
102
|
-
|
|
103
|
-
npm run curate -- --game star-freight <asset_id> approved "explanation"
|
|
104
|
-
npm run curate -- --game star-freight <asset_id> rejected "explanation" --failures "too_clean"
|
|
128
|
+
每个领域模板都包含针对特定生产环境设计的车道定义、规则、评分标准和术语结构。
|
|
105
129
|
|
|
106
|
-
|
|
107
|
-
|
|
130
|
+
| 域名。 | 车道。 | 主要关注点。 |
|
|
131
|
+
|--------|-------|-------------|
|
|
132
|
+
| **game-art** | 角色、环境、道具、用户界面、飞船、内部、设备。 | 游戏中的角色轮廓、阵营区分、以及磨损/老化效果。 |
|
|
133
|
+
| **character-design** | 人像、全身照、360度展示图、表情图、动作姿势图。 | 比例准确性、服装的合理性、人物性格的刻画、动作的清晰度。 |
|
|
134
|
+
| **creature-design** | 概念、正字法、详细研究、动作、比例参考、栖息地。 | 解剖学上的合理性、进化逻辑、轮廓的独特性。 |
|
|
135
|
+
| **architecture** | 外观、内部、街道景观、结构细节、废墟、景观。 | 结构合理性、材料一致性、视角、时代背景的协调性。 |
|
|
136
|
+
| **vehicle-mech** | 外观、驾驶舱、部件、示意图、轮廓图、损坏类型。 | 机械逻辑,功能性设计语言,访问点,损坏描述。 |
|
|
108
137
|
|
|
109
|
-
|
|
110
|
-
npm run canon-bind -- --game star-freight
|
|
138
|
+
## 数据集生成
|
|
111
139
|
|
|
112
|
-
|
|
113
|
-
npm run compare -- --game star-freight <asset_a> <asset_b> a "A has better faction read"
|
|
140
|
+
完整的数据集流程:快照、分割、导出、评估。
|
|
114
141
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
142
|
+
```
|
|
143
|
+
snapshot --> split --> export --> eval-pack
|
|
144
|
+
| | | |
|
|
145
|
+
frozen subject package canon-aware
|
|
146
|
+
selection isolation (manifest, test instruments
|
|
147
|
+
+ lane metadata, (4 task types)
|
|
148
|
+
balance images,
|
|
149
|
+
checksums,
|
|
150
|
+
card)
|
|
118
151
|
```
|
|
119
152
|
|
|
120
|
-
|
|
153
|
+
**快照**:冻结符合条件的记录的确定性选择。每个包含记录都有其原因的追踪。配置指纹确保可重复性。
|
|
154
|
+
|
|
155
|
+
**分割**:将记录分配到训练集/验证集/测试集,同时保证样本隔离(同一个样本家族不会出现在多个分割中),并实现通道平衡的分布。使用种子伪随机数生成器,确保从相同的种子生成相同的结果。
|
|
156
|
+
|
|
157
|
+
**导出包**:是自包含的:清单、metadata.jsonl、图像(符号链接或复制)、分割、数据集卡(Markdown + JSON)以及 BSD 格式的校验和。 包含重建数据集所需的所有内容。
|
|
158
|
+
|
|
159
|
+
**评估包**:是考虑规范的测试工具,包含四种任务类型:通道覆盖、禁止漂移、锚定/黄金标准、以及样本连续性。 它们证明数据集流程正在为未来的模型评估提供支持,而不仅仅是导出文件。
|
|
160
|
+
|
|
161
|
+
通过 [`repo-dataset`](https://github.com/mcp-tool-shop-org/repo-dataset) 导出到下游格式(TRL, LLaVA, Qwen2-VL, JSONL, Parquet 等)。 `repo-dataset` 负责格式转换;`sdlab` 负责数据集的真实性。
|
|
162
|
+
|
|
163
|
+
## Star Freight 示例
|
|
164
|
+
|
|
165
|
+
克隆仓库以获取完整的示例:1182 条记录,28 个提示序列,5 个派系,7 个通道,24 条宪法规则,以及来自一个硬核科幻角色扮演游戏的 892 个已批准的资源。
|
|
121
166
|
|
|
122
167
|
```bash
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
168
|
+
git clone https://github.com/mcp-tool-shop-org/style-dataset-lab
|
|
169
|
+
cd style-dataset-lab
|
|
170
|
+
|
|
171
|
+
# Validate the project
|
|
172
|
+
sdlab project doctor --project star-freight
|
|
127
173
|
|
|
128
|
-
#
|
|
129
|
-
|
|
174
|
+
# Run the full dataset spine
|
|
175
|
+
sdlab snapshot create --project star-freight # 839 eligible records
|
|
176
|
+
sdlab split build --project star-freight # ~80/10/10, zero leakage
|
|
177
|
+
sdlab export build --project star-freight # package with checksums
|
|
178
|
+
sdlab eval-pack build --project star-freight # 78 eval records
|
|
130
179
|
```
|
|
131
180
|
|
|
132
|
-
##
|
|
181
|
+
## 从 v1.x 迁移
|
|
133
182
|
|
|
134
|
-
|
|
183
|
+
v2.0 将 `games/` 重命名为 `projects/`,并将 `--game` 重命名为 `--project`。
|
|
135
184
|
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
prompts/ Prompt packs per wave (JSON: subjects, variations, defaults)
|
|
140
|
-
references/ IP-Adapter reference images
|
|
141
|
-
control-guides/ ControlNet guide overlays
|
|
142
|
-
identity-packets/ Named character identity spines
|
|
143
|
-
outputs/
|
|
144
|
-
candidates/ Raw generations (gitignored)
|
|
145
|
-
approved/ Curated approved (gitignored)
|
|
146
|
-
rejected/ Curated rejected (gitignored)
|
|
147
|
-
borderline/ Curated borderline (gitignored)
|
|
148
|
-
painterly/ Painterly-style variants (gitignored)
|
|
149
|
-
records/ Per-asset JSON (provenance + judgment + canon binding)
|
|
150
|
-
comparisons/ A-vs-B preference judgments
|
|
151
|
-
exports/ repo-dataset output (gitignored)
|
|
152
|
-
```
|
|
185
|
+
```bash
|
|
186
|
+
# Rename your data directory
|
|
187
|
+
mv games projects
|
|
153
188
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
checkpoint: dreamshaperXL_v21TurboDPMSDE.safetensors
|
|
158
|
-
lora: classipeintxl_v21.safetensors (weight: 1.0)
|
|
159
|
-
resolution: 1024x1024
|
|
160
|
-
steps: 8
|
|
161
|
-
cfg: 2.0
|
|
162
|
-
sampler: dpmpp_sde
|
|
163
|
-
scheduler: karras
|
|
164
|
-
speed: ~9s per image (RTX 5080)
|
|
189
|
+
# --game still works with a deprecation warning (removed in v3.0)
|
|
190
|
+
sdlab bind --game star-freight # works, prints warning
|
|
191
|
+
sdlab bind --project star-freight # canonical form
|
|
165
192
|
```
|
|
166
193
|
|
|
167
|
-
|
|
194
|
+
## 安全模型
|
|
195
|
+
|
|
196
|
+
**仅本地。** 与运行在 `localhost:8188` 上的 ComfyUI 通信。 没有遥测,没有分析,没有外部请求。 图像保留在您的 GPU 和文件系统中。
|
|
168
197
|
|
|
169
198
|
## 要求
|
|
170
199
|
|
package/bin/sdlab.js
CHANGED
|
@@ -20,6 +20,18 @@ const COMMANDS = {
|
|
|
20
20
|
'painterly': '../scripts/painterly.js',
|
|
21
21
|
'painterly:test': '../scripts/painterly-test.js',
|
|
22
22
|
'migrate': '../scripts/migrate-records.js',
|
|
23
|
+
'snapshot': '../scripts/snapshot.js',
|
|
24
|
+
'eligibility': '../scripts/eligibility.js',
|
|
25
|
+
'split': '../scripts/split.js',
|
|
26
|
+
'card': '../scripts/card.js',
|
|
27
|
+
'export': '../scripts/export.js',
|
|
28
|
+
'eval-pack': '../scripts/eval-pack.js',
|
|
29
|
+
'training-profile': '../scripts/training-profile.js',
|
|
30
|
+
'training-manifest': '../scripts/training-manifest.js',
|
|
31
|
+
'training-package': '../scripts/training-package.js',
|
|
32
|
+
'eval-run': '../scripts/eval-run.js',
|
|
33
|
+
'implementation-pack': '../scripts/implementation-pack.js',
|
|
34
|
+
'reingest': '../scripts/reingest.js',
|
|
23
35
|
};
|
|
24
36
|
|
|
25
37
|
// Two-word commands under "project" namespace
|
|
@@ -47,6 +59,43 @@ function printHelp() {
|
|
|
47
59
|
console.log(' painterly Post-processing painterly style pass');
|
|
48
60
|
console.log(' painterly:test Test denoise levels on reference images');
|
|
49
61
|
console.log('');
|
|
62
|
+
console.log('Dataset:');
|
|
63
|
+
console.log(' snapshot create Create a frozen dataset snapshot');
|
|
64
|
+
console.log(' snapshot list List all snapshots');
|
|
65
|
+
console.log(' snapshot show <id> Show snapshot details');
|
|
66
|
+
console.log(' snapshot diff <a> <b> Compare two snapshots');
|
|
67
|
+
console.log(' eligibility audit Audit record training eligibility');
|
|
68
|
+
console.log(' split build Build train/val/test split from snapshot');
|
|
69
|
+
console.log(' split list List all splits');
|
|
70
|
+
console.log(' split show <id> Show split details');
|
|
71
|
+
console.log(' split audit <id> Audit split for leakage and balance');
|
|
72
|
+
console.log(' card generate Generate dataset card (markdown + JSON)');
|
|
73
|
+
console.log(' export build Build self-contained export package');
|
|
74
|
+
console.log(' export list List all export packages');
|
|
75
|
+
console.log(' eval-pack build Build canon-aware eval pack');
|
|
76
|
+
console.log(' eval-pack list List eval packs');
|
|
77
|
+
console.log(' eval-pack show <id> Show eval pack details');
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log('Training:');
|
|
80
|
+
console.log(' training-profile list List training profiles');
|
|
81
|
+
console.log(' training-profile show <id> Show training profile');
|
|
82
|
+
console.log(' training-manifest create Create training manifest');
|
|
83
|
+
console.log(' training-manifest validate <id> Validate manifest integrity');
|
|
84
|
+
console.log(' training-manifest show <id> Show manifest details');
|
|
85
|
+
console.log(' training-manifest list List all manifests');
|
|
86
|
+
console.log(' training-package build Build trainer-ready package');
|
|
87
|
+
console.log(' training-package show <id> Show package details');
|
|
88
|
+
console.log(' training-package list List all packages');
|
|
89
|
+
console.log(' eval-run create Create eval run');
|
|
90
|
+
console.log(' eval-run score <id> --outputs <p> Score against eval pack');
|
|
91
|
+
console.log(' eval-run show <id> Show eval run details');
|
|
92
|
+
console.log(' eval-run list List all eval runs');
|
|
93
|
+
console.log(' implementation-pack build Build implementation example pack');
|
|
94
|
+
console.log(' implementation-pack show <id> Show pack details');
|
|
95
|
+
console.log(' implementation-pack list List all packs');
|
|
96
|
+
console.log(' reingest generated --source <dir> Re-ingest generated outputs');
|
|
97
|
+
console.log(' reingest audit Audit re-ingested record status');
|
|
98
|
+
console.log('');
|
|
50
99
|
console.log('Options:');
|
|
51
100
|
console.log(' --project <name> Project to operate on (default: star-freight)');
|
|
52
101
|
console.log(' --game <name> Deprecated alias for --project');
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diffusers LoRA adapter.
|
|
3
|
+
*
|
|
4
|
+
* Produces a layout compatible with diffusers fine-tuning scripts:
|
|
5
|
+
* - dataset/<partition>/ with image files
|
|
6
|
+
* - dataset/<partition>/<image>.txt caption sidecar files
|
|
7
|
+
* - metadata/<partition>.jsonl with structured rows
|
|
8
|
+
*
|
|
9
|
+
* Does not mutate inclusion or split truth.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { writeFile, mkdir, symlink, copyFile } from 'node:fs/promises';
|
|
13
|
+
import { join, relative, basename } from 'node:path';
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build a diffusers-style caption from record metadata.
|
|
18
|
+
*/
|
|
19
|
+
function buildCaption(record, lane, group, profile) {
|
|
20
|
+
const parts = [];
|
|
21
|
+
|
|
22
|
+
// Trigger word for LoRA activation
|
|
23
|
+
if (profile.prompt_strategy === 'trigger-word' && profile.profile_id) {
|
|
24
|
+
parts.push(profile.profile_id.replace(/-/g, '_'));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Subject identity if available
|
|
28
|
+
if (record.identity?.subject_name) {
|
|
29
|
+
parts.push(record.identity.subject_name.replace(/_/g, ' '));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Faction / group
|
|
33
|
+
if (group && group !== 'unknown') {
|
|
34
|
+
parts.push(`${group} faction`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Lane context
|
|
38
|
+
if (lane) parts.push(lane);
|
|
39
|
+
|
|
40
|
+
// Use original prompt if available, otherwise build from ID
|
|
41
|
+
const prompt = record.provenance?.prompt;
|
|
42
|
+
if (prompt) {
|
|
43
|
+
parts.push(prompt.slice(0, 300));
|
|
44
|
+
} else {
|
|
45
|
+
parts.push(record.id.replace(/_/g, ' '));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return parts.join(', ');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build training package in diffusers LoRA format.
|
|
53
|
+
*/
|
|
54
|
+
export async function buildPackage({ packageDir, partitions, profile, manifest, config, projectRoot, copy }) {
|
|
55
|
+
let imageCount = 0;
|
|
56
|
+
|
|
57
|
+
for (const [partition, records] of Object.entries(partitions)) {
|
|
58
|
+
const datasetDir = join(packageDir, 'dataset', partition);
|
|
59
|
+
const metadataDir = join(packageDir, 'metadata');
|
|
60
|
+
await mkdir(datasetDir, { recursive: true });
|
|
61
|
+
await mkdir(metadataDir, { recursive: true });
|
|
62
|
+
|
|
63
|
+
const metadataRows = [];
|
|
64
|
+
|
|
65
|
+
for (const { record, lane, group } of records) {
|
|
66
|
+
if (!record.asset_path) continue;
|
|
67
|
+
|
|
68
|
+
const sourcePath = join(projectRoot, record.asset_path);
|
|
69
|
+
if (!existsSync(sourcePath)) continue;
|
|
70
|
+
|
|
71
|
+
const filename = record.asset_path.split('/').pop();
|
|
72
|
+
const stem = filename.replace(/\.[^.]+$/, '');
|
|
73
|
+
const destPath = join(datasetDir, filename);
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
if (copy) {
|
|
77
|
+
await copyFile(sourcePath, destPath);
|
|
78
|
+
} else {
|
|
79
|
+
await symlink(relative(datasetDir, sourcePath), destPath);
|
|
80
|
+
}
|
|
81
|
+
imageCount++;
|
|
82
|
+
} catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Write caption sidecar (.txt next to image)
|
|
87
|
+
const caption = buildCaption(record, lane, group, profile);
|
|
88
|
+
await writeFile(join(datasetDir, `${stem}.txt`), caption + '\n');
|
|
89
|
+
|
|
90
|
+
metadataRows.push({
|
|
91
|
+
file: `dataset/${partition}/${filename}`,
|
|
92
|
+
caption_file: `dataset/${partition}/${stem}.txt`,
|
|
93
|
+
record_id: record.id,
|
|
94
|
+
caption,
|
|
95
|
+
lane,
|
|
96
|
+
group,
|
|
97
|
+
subject: record.identity?.subject_name || null,
|
|
98
|
+
pass_ratio: record.canon?.assertion_count > 0
|
|
99
|
+
? +(record.canon.pass_count / record.canon.assertion_count).toFixed(3) : null,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await writeFile(
|
|
104
|
+
join(metadataDir, `${partition}.jsonl`),
|
|
105
|
+
metadataRows.map(r => JSON.stringify(r)).join('\n') + '\n'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { imageCount };
|
|
110
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic image+caption adapter.
|
|
3
|
+
*
|
|
4
|
+
* Simple, inspectable, durable baseline:
|
|
5
|
+
* - dataset/<partition>/ with image files
|
|
6
|
+
* - metadata/<partition>.jsonl with one row per image
|
|
7
|
+
*
|
|
8
|
+
* Does not mutate inclusion or split truth.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { writeFile, mkdir, symlink, copyFile } from 'node:fs/promises';
|
|
12
|
+
import { join, relative } from 'node:path';
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Build a caption string from a record and profile.
|
|
17
|
+
*/
|
|
18
|
+
function buildCaption(record, lane, group, profile) {
|
|
19
|
+
const parts = [];
|
|
20
|
+
|
|
21
|
+
if (profile.prompt_strategy === 'trigger-word' && profile.profile_id) {
|
|
22
|
+
parts.push(profile.profile_id.replace(/-/g, '_'));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (group) parts.push(group);
|
|
26
|
+
if (lane) parts.push(lane);
|
|
27
|
+
|
|
28
|
+
// Add key provenance if available
|
|
29
|
+
const prompt = record.provenance?.prompt;
|
|
30
|
+
if (prompt) {
|
|
31
|
+
// Use first 200 chars of prompt as caption body
|
|
32
|
+
parts.push(prompt.slice(0, 200));
|
|
33
|
+
} else {
|
|
34
|
+
parts.push(record.id.replace(/_/g, ' '));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return parts.join(', ');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build training package in generic image+caption format.
|
|
42
|
+
*/
|
|
43
|
+
export async function buildPackage({ packageDir, partitions, profile, manifest, config, projectRoot, copy }) {
|
|
44
|
+
let imageCount = 0;
|
|
45
|
+
|
|
46
|
+
for (const [partition, records] of Object.entries(partitions)) {
|
|
47
|
+
const datasetDir = join(packageDir, 'dataset', partition);
|
|
48
|
+
const metadataDir = join(packageDir, 'metadata');
|
|
49
|
+
await mkdir(datasetDir, { recursive: true });
|
|
50
|
+
await mkdir(metadataDir, { recursive: true });
|
|
51
|
+
|
|
52
|
+
const metadataRows = [];
|
|
53
|
+
|
|
54
|
+
for (const { record, lane, group } of records) {
|
|
55
|
+
if (!record.asset_path) continue;
|
|
56
|
+
|
|
57
|
+
const sourcePath = join(projectRoot, record.asset_path);
|
|
58
|
+
if (!existsSync(sourcePath)) continue;
|
|
59
|
+
|
|
60
|
+
const filename = record.asset_path.split('/').pop();
|
|
61
|
+
const destPath = join(datasetDir, filename);
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
if (copy) {
|
|
65
|
+
await copyFile(sourcePath, destPath);
|
|
66
|
+
} else {
|
|
67
|
+
await symlink(relative(datasetDir, sourcePath), destPath);
|
|
68
|
+
}
|
|
69
|
+
imageCount++;
|
|
70
|
+
} catch {
|
|
71
|
+
continue; // skip duplicates or broken links
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const caption = buildCaption(record, lane, group, profile);
|
|
75
|
+
|
|
76
|
+
metadataRows.push({
|
|
77
|
+
file: `dataset/${partition}/${filename}`,
|
|
78
|
+
record_id: record.id,
|
|
79
|
+
caption,
|
|
80
|
+
lane,
|
|
81
|
+
group,
|
|
82
|
+
pass_ratio: record.canon?.assertion_count > 0
|
|
83
|
+
? +(record.canon.pass_count / record.canon.assertion_count).toFixed(3) : null,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await writeFile(
|
|
88
|
+
join(metadataDir, `${partition}.jsonl`),
|
|
89
|
+
metadataRows.map(r => JSON.stringify(r)).join('\n') + '\n'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { imageCount };
|
|
94
|
+
}
|