@mcptoolshop/style-dataset-lab 3.1.0 → 3.3.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 +61 -0
- package/README.es.md +96 -63
- package/README.fr.md +97 -64
- package/README.hi.md +108 -77
- package/README.it.md +97 -64
- package/README.ja.md +105 -72
- package/README.md +33 -0
- package/README.pt-BR.md +94 -61
- package/README.zh.md +98 -65
- package/bin/sdlab.js +71 -0
- package/lib/adapters/ai-toolkit.js +209 -0
- package/lib/adapters/comfyui-runner.js +36 -2
- package/lib/adapters/comfyui-workflows.js +133 -2
- package/lib/adapters/diffusers-lora.js +46 -66
- package/lib/adapters/generic-image-caption.js +22 -50
- package/lib/canon-build/build.js +450 -0
- package/lib/canon-build/emitters/base.js +122 -0
- package/lib/canon-build/emitters/character.js +167 -0
- package/lib/canon-build/emitters/deity.js +165 -0
- package/lib/canon-build/emitters/faction.js +161 -0
- package/lib/canon-build/emitters/index.js +38 -0
- package/lib/canon-build/emitters/location.js +153 -0
- package/lib/canon-build/emitters/monster.js +131 -0
- package/lib/canon-build/emitters/relic.js +142 -0
- package/lib/canon-build/emitters/ship.js +154 -0
- package/lib/canon-build/emitters/species.js +165 -0
- package/lib/canon-build/frontmatter.js +76 -0
- package/lib/canon-build/lane-resolve.js +64 -0
- package/lib/canon-build/load-config.js +117 -0
- package/lib/canon-build/load-entry.js +62 -0
- package/lib/canon-build/load-schema.js +46 -0
- package/lib/captions.js +197 -0
- package/lib/config.js +19 -0
- package/lib/critique-engine.js +46 -1
- package/lib/critique-render.js +45 -0
- package/lib/freeze-events.js +109 -0
- package/lib/freeze-gate.js +208 -0
- package/lib/freeze-stamp.js +161 -0
- package/lib/rows.js +228 -0
- package/lib/run-manifest.js +308 -0
- package/lib/snapshot.js +7 -1
- package/lib/training-adapters.js +5 -0
- package/lib/training-packages.js +24 -18
- package/lib/training-profiles.js +33 -0
- package/lib/verifier.js +67 -0
- package/package.json +12 -3
- package/scripts/_batch_cut.py +71 -0
- package/scripts/_seam_cutout.py +74 -0
- package/scripts/canon-build.js +88 -0
- package/scripts/canon-drift.js +193 -0
- package/scripts/canon-freeze-status.js +85 -0
- package/scripts/canon-freeze.js +211 -0
- package/scripts/canon-unfreeze.js +137 -0
- package/scripts/critique.js +32 -7
- package/scripts/curate.js +33 -0
- package/scripts/generate-controlnet.js +12 -0
- package/scripts/generate-identity.js +20 -4
- package/scripts/generate-ipadapter.js +11 -0
- package/scripts/generate.js +262 -102
- package/scripts/qwen_generate.py +327 -0
- package/scripts/run-generate.js +3 -0
|
@@ -1,60 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Diffusers LoRA adapter.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Consumes canonical rows (per D6) — either produced in memory by
|
|
5
|
+
* training-packages.js from records, or read from a canon-build
|
|
6
|
+
* dataset.jsonl. The row shape is defined in lib/rows.js and pinned by
|
|
7
|
+
* ROW_SCHEMA_VERSION.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Output layout (unchanged from pre-refactor):
|
|
10
|
+
* dataset/<partition>/<entity_id>.<ext> — image (copied or symlinked)
|
|
11
|
+
* dataset/<partition>/<entity_id>.txt — caption sidecar
|
|
12
|
+
* metadata/<partition>.jsonl — per-row audit
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
15
|
import { writeFile, mkdir, symlink, copyFile } from 'node:fs/promises';
|
|
13
|
-
import { join, relative
|
|
16
|
+
import { join, relative } from 'node:path';
|
|
14
17
|
import { existsSync } from 'node:fs';
|
|
18
|
+
import { groupRowsByPartition } from '../rows.js';
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
|
-
* Build
|
|
21
|
+
* Build training package in diffusers LoRA format from canonical rows.
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} args
|
|
24
|
+
* @param {string} args.packageDir
|
|
25
|
+
* @param {Array<Object>} args.rows — canonical rows; partition field groups them
|
|
26
|
+
* @param {Object} args.profile
|
|
27
|
+
* @param {Object} args.manifest
|
|
28
|
+
* @param {Object} args.config
|
|
29
|
+
* @param {string} args.projectRoot
|
|
30
|
+
* @param {boolean} [args.copy]
|
|
18
31
|
*/
|
|
19
|
-
function
|
|
20
|
-
const
|
|
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);
|
|
32
|
+
export async function buildPackage({ packageDir, rows, profile, manifest, config, projectRoot, copy }) {
|
|
33
|
+
const partitioned = groupRowsByPartition(rows || []);
|
|
39
34
|
|
|
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
35
|
let imageCount = 0;
|
|
56
36
|
|
|
57
|
-
for (const [partition,
|
|
37
|
+
for (const [partition, partitionRows] of Object.entries(partitioned)) {
|
|
58
38
|
const datasetDir = join(packageDir, 'dataset', partition);
|
|
59
39
|
const metadataDir = join(packageDir, 'metadata');
|
|
60
40
|
await mkdir(datasetDir, { recursive: true });
|
|
@@ -62,19 +42,17 @@ export async function buildPackage({ packageDir, partitions, profile, manifest,
|
|
|
62
42
|
|
|
63
43
|
const metadataRows = [];
|
|
64
44
|
|
|
65
|
-
for (const
|
|
66
|
-
if (!
|
|
45
|
+
for (const row of partitionRows) {
|
|
46
|
+
if (!row.asset_path) continue;
|
|
67
47
|
|
|
68
|
-
const sourcePath = join(projectRoot,
|
|
48
|
+
const sourcePath = join(projectRoot, row.asset_path);
|
|
69
49
|
if (!existsSync(sourcePath)) continue;
|
|
70
50
|
|
|
71
|
-
|
|
72
|
-
// avoid silent collisions when two records share a filename stem.
|
|
73
|
-
const sourceBasename = record.asset_path.split(/[\\/]/).pop();
|
|
51
|
+
const sourceBasename = row.asset_path.split(/[\\/]/).pop();
|
|
74
52
|
const extMatch = sourceBasename.match(/\.[^.]+$/);
|
|
75
53
|
const ext = extMatch ? extMatch[0] : '';
|
|
76
|
-
const filename = `${
|
|
77
|
-
const stem =
|
|
54
|
+
const filename = `${row.entity_id}${ext}`;
|
|
55
|
+
const stem = row.entity_id;
|
|
78
56
|
const destPath = join(datasetDir, filename);
|
|
79
57
|
|
|
80
58
|
try {
|
|
@@ -88,28 +66,30 @@ export async function buildPackage({ packageDir, partitions, profile, manifest,
|
|
|
88
66
|
continue;
|
|
89
67
|
}
|
|
90
68
|
|
|
91
|
-
|
|
92
|
-
const caption = buildCaption(record, lane, group, profile);
|
|
93
|
-
await writeFile(join(datasetDir, `${stem}.txt`), caption + '\n');
|
|
69
|
+
await writeFile(join(datasetDir, `${stem}.txt`), row.caption + '\n');
|
|
94
70
|
|
|
95
|
-
metadataRows.push(
|
|
96
|
-
file: `dataset/${partition}/${filename}`,
|
|
97
|
-
caption_file: `dataset/${partition}/${stem}.txt`,
|
|
98
|
-
record_id: record.id,
|
|
99
|
-
caption,
|
|
100
|
-
lane,
|
|
101
|
-
group,
|
|
102
|
-
subject: record.identity?.subject_name || null,
|
|
103
|
-
pass_ratio: record.canon?.assertion_count > 0
|
|
104
|
-
? +(record.canon.pass_count / record.canon.assertion_count).toFixed(3) : null,
|
|
105
|
-
});
|
|
71
|
+
metadataRows.push(buildMetadataRow(row, partition, filename, stem));
|
|
106
72
|
}
|
|
107
73
|
|
|
108
74
|
await writeFile(
|
|
109
75
|
join(metadataDir, `${partition}.jsonl`),
|
|
110
|
-
metadataRows.map(r => JSON.stringify(r)).join('\n') + '\n'
|
|
76
|
+
metadataRows.map(r => JSON.stringify(r)).join('\n') + (metadataRows.length ? '\n' : '')
|
|
111
77
|
);
|
|
112
78
|
}
|
|
113
79
|
|
|
114
80
|
return { imageCount };
|
|
115
81
|
}
|
|
82
|
+
|
|
83
|
+
function buildMetadataRow(row, partition, filename, stem) {
|
|
84
|
+
const out = {
|
|
85
|
+
file: `dataset/${partition}/${filename}`,
|
|
86
|
+
caption_file: `dataset/${partition}/${stem}.txt`,
|
|
87
|
+
record_id: row.entity_id,
|
|
88
|
+
caption: row.caption,
|
|
89
|
+
lane: row.lane,
|
|
90
|
+
group: row.group ?? null,
|
|
91
|
+
subject: row.subject_filter_key ?? null,
|
|
92
|
+
pass_ratio: row.pass_ratio ?? null,
|
|
93
|
+
};
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
@@ -1,49 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic image+caption adapter.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Consumes canonical rows (per D6) and emits:
|
|
5
|
+
* dataset/<partition>/<entity_id>.<ext> — image (copied or symlinked)
|
|
6
|
+
* metadata/<partition>.jsonl — per-row audit (caption inline)
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* No caption sidecar files — this adapter keeps metadata flat and
|
|
9
|
+
* machine-readable. Use diffusers-lora or ai-toolkit for trainer-specific
|
|
10
|
+
* caption-file conventions.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { writeFile, mkdir, symlink, copyFile } from 'node:fs/promises';
|
|
12
14
|
import { join, relative } from 'node:path';
|
|
13
15
|
import { existsSync } from 'node:fs';
|
|
16
|
+
import { groupRowsByPartition } from '../rows.js';
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
}
|
|
18
|
+
export async function buildPackage({ packageDir, rows, profile, manifest, config, projectRoot, copy }) {
|
|
19
|
+
const partitioned = groupRowsByPartition(rows || []);
|
|
39
20
|
|
|
40
|
-
/**
|
|
41
|
-
* Build training package in generic image+caption format.
|
|
42
|
-
*/
|
|
43
|
-
export async function buildPackage({ packageDir, partitions, profile, manifest, config, projectRoot, copy }) {
|
|
44
21
|
let imageCount = 0;
|
|
45
22
|
|
|
46
|
-
for (const [partition,
|
|
23
|
+
for (const [partition, partitionRows] of Object.entries(partitioned)) {
|
|
47
24
|
const datasetDir = join(packageDir, 'dataset', partition);
|
|
48
25
|
const metadataDir = join(packageDir, 'metadata');
|
|
49
26
|
await mkdir(datasetDir, { recursive: true });
|
|
@@ -51,18 +28,16 @@ export async function buildPackage({ packageDir, partitions, profile, manifest,
|
|
|
51
28
|
|
|
52
29
|
const metadataRows = [];
|
|
53
30
|
|
|
54
|
-
for (const
|
|
55
|
-
if (!
|
|
31
|
+
for (const row of partitionRows) {
|
|
32
|
+
if (!row.asset_path) continue;
|
|
56
33
|
|
|
57
|
-
const sourcePath = join(projectRoot,
|
|
34
|
+
const sourcePath = join(projectRoot, row.asset_path);
|
|
58
35
|
if (!existsSync(sourcePath)) continue;
|
|
59
36
|
|
|
60
|
-
|
|
61
|
-
// avoid silent collisions when two records share a filename stem.
|
|
62
|
-
const sourceBasename = record.asset_path.split(/[\\/]/).pop();
|
|
37
|
+
const sourceBasename = row.asset_path.split(/[\\/]/).pop();
|
|
63
38
|
const extMatch = sourceBasename.match(/\.[^.]+$/);
|
|
64
39
|
const ext = extMatch ? extMatch[0] : '';
|
|
65
|
-
const filename = `${
|
|
40
|
+
const filename = `${row.entity_id}${ext}`;
|
|
66
41
|
const destPath = join(datasetDir, filename);
|
|
67
42
|
|
|
68
43
|
try {
|
|
@@ -73,25 +48,22 @@ export async function buildPackage({ packageDir, partitions, profile, manifest,
|
|
|
73
48
|
}
|
|
74
49
|
imageCount++;
|
|
75
50
|
} catch {
|
|
76
|
-
continue;
|
|
51
|
+
continue;
|
|
77
52
|
}
|
|
78
53
|
|
|
79
|
-
const caption = buildCaption(record, lane, group, profile);
|
|
80
|
-
|
|
81
54
|
metadataRows.push({
|
|
82
55
|
file: `dataset/${partition}/${filename}`,
|
|
83
|
-
record_id:
|
|
84
|
-
caption,
|
|
85
|
-
lane,
|
|
86
|
-
group,
|
|
87
|
-
pass_ratio:
|
|
88
|
-
? +(record.canon.pass_count / record.canon.assertion_count).toFixed(3) : null,
|
|
56
|
+
record_id: row.entity_id,
|
|
57
|
+
caption: row.caption,
|
|
58
|
+
lane: row.lane,
|
|
59
|
+
group: row.group ?? null,
|
|
60
|
+
pass_ratio: row.pass_ratio ?? null,
|
|
89
61
|
});
|
|
90
62
|
}
|
|
91
63
|
|
|
92
64
|
await writeFile(
|
|
93
65
|
join(metadataDir, `${partition}.jsonl`),
|
|
94
|
-
metadataRows.map(r => JSON.stringify(r)).join('\n') + '\n'
|
|
66
|
+
metadataRows.map(r => JSON.stringify(r)).join('\n') + (metadataRows.length ? '\n' : '')
|
|
95
67
|
);
|
|
96
68
|
}
|
|
97
69
|
|