@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.
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * split.js — Build, show, and audit dataset splits.
5
+ *
6
+ * Usage:
7
+ * sdlab split build [--snapshot <id>] [--profile <name>] [--project <name>]
8
+ * sdlab split list [--project <name>]
9
+ * sdlab split show <split-id> [--project <name>]
10
+ * sdlab split audit <split-id> [--project <name>]
11
+ */
12
+
13
+ import { readFile } from 'node:fs/promises';
14
+ import { join } from 'node:path';
15
+ import { getProjectName } from '../lib/args.js';
16
+ import { REPO_ROOT } from '../lib/paths.js';
17
+ import { loadSplitProfile } from '../lib/config.js';
18
+ import { listSnapshots } from '../lib/snapshot.js';
19
+ import { createSplit, loadSplit, loadSplitAudit, listSplits } from '../lib/split.js';
20
+
21
+ export async function run(argv = process.argv.slice(2)) {
22
+ const projectName = getProjectName(argv);
23
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
24
+ const subcommand = argv.find(a => !a.startsWith('--')) || 'list';
25
+ const args = argv.filter(a => a !== subcommand);
26
+
27
+ if (subcommand === 'build') {
28
+ // Find snapshot ID
29
+ let snapshotId;
30
+ const snapIdx = args.indexOf('--snapshot');
31
+ if (snapIdx >= 0) {
32
+ snapshotId = args[snapIdx + 1];
33
+ } else {
34
+ // Use latest snapshot
35
+ const snapshots = await listSnapshots(projectRoot);
36
+ if (snapshots.length === 0) {
37
+ throw new Error('No snapshots found. Run: sdlab snapshot create');
38
+ }
39
+ snapshotId = snapshots[snapshots.length - 1].id;
40
+ }
41
+
42
+ const profileIdx = args.indexOf('--profile');
43
+ const profileId = profileIdx >= 0 ? args[profileIdx + 1] : null;
44
+ const profile = loadSplitProfile(projectRoot, profileId);
45
+
46
+ console.log(`\x1b[1msdlab split build\x1b[0m — ${projectName}`);
47
+ console.log(` Snapshot: ${snapshotId}`);
48
+ console.log(` Profile: ${profileId || 'balanced-default (built-in)'}`);
49
+ console.log(` Strategy: ${profile.strategy}`);
50
+ console.log(` Ratios: ${profile.train_ratio}/${profile.val_ratio}/${profile.test_ratio}`);
51
+ console.log(` Seed: ${profile.seed}`);
52
+ console.log('');
53
+
54
+ const result = await createSplit(projectRoot, snapshotId, profile);
55
+
56
+ console.log(` \x1b[32m✓\x1b[0m Split: ${result.splitId}`);
57
+ console.log(` Train: ${result.train}`);
58
+ console.log(` Val: ${result.val}`);
59
+ console.log(` Test: ${result.test}`);
60
+
61
+ // Show audit summary
62
+ const audit = await loadSplitAudit(projectRoot, result.splitId);
63
+ console.log('');
64
+ if (audit.leakage_check.passed) {
65
+ console.log(` \x1b[32m✓\x1b[0m No subject leakage detected`);
66
+ } else {
67
+ console.log(` \x1b[31m✗\x1b[0m Subject leakage detected:`);
68
+ for (const issue of audit.leakage_check.issues) {
69
+ console.log(` family="${issue.family}" (${issue.record_count} records) → ${issue.leaked_to.join(', ')}`);
70
+ }
71
+ }
72
+
73
+ console.log(` Families: ${audit.family_count} (train=${audit.families_per_split.train}, val=${audit.families_per_split.val}, test=${audit.families_per_split.test})`);
74
+
75
+ } else if (subcommand === 'list') {
76
+ const splits = await listSplits(projectRoot);
77
+ if (splits.length === 0) {
78
+ console.log('No splits found. Run: sdlab split build');
79
+ return;
80
+ }
81
+ console.log(`\x1b[1mSplits\x1b[0m — ${projectName}\n`);
82
+ for (const s of splits) {
83
+ console.log(` ${s.id} snap=${s.snapshot_id} train=${s.train} val=${s.val} test=${s.test} families=${s.families} ${s.created_at}`);
84
+ }
85
+
86
+ } else if (subcommand === 'show') {
87
+ const splitId = args.find(a => a.startsWith('split-'));
88
+ if (!splitId) throw new Error('Usage: sdlab split show <split-id>');
89
+
90
+ const manifest = await loadSplit(projectRoot, splitId);
91
+ console.log(`\x1b[1mSplit\x1b[0m — ${splitId}\n`);
92
+ console.log(` Created: ${manifest.created_at}`);
93
+ console.log(` Snapshot: ${manifest.snapshot_id}`);
94
+ console.log(` Records: ${manifest.counts.total_records}`);
95
+ console.log(` Families: ${manifest.counts.total_families}`);
96
+ console.log(` Train: ${manifest.counts.train}`);
97
+ console.log(` Val: ${manifest.counts.val}`);
98
+ console.log(` Test: ${manifest.counts.test}`);
99
+ console.log('');
100
+ console.log(' Profile:');
101
+ for (const [k, v] of Object.entries(manifest.profile)) {
102
+ console.log(` ${k}: ${JSON.stringify(v)}`);
103
+ }
104
+ if (manifest.warnings.length > 0) {
105
+ console.log('');
106
+ console.log(' Warnings:');
107
+ for (const w of manifest.warnings) {
108
+ console.log(` ⚠ ${w}`);
109
+ }
110
+ }
111
+
112
+ } else if (subcommand === 'audit') {
113
+ const splitId = args.find(a => a.startsWith('split-'));
114
+ if (!splitId) throw new Error('Usage: sdlab split audit <split-id>');
115
+
116
+ const audit = await loadSplitAudit(projectRoot, splitId);
117
+ console.log(`\x1b[1mSplit audit\x1b[0m — ${splitId}\n`);
118
+
119
+ // Leakage check
120
+ if (audit.leakage_check.passed) {
121
+ console.log(` \x1b[32m✓\x1b[0m Leakage check: PASSED`);
122
+ } else {
123
+ console.log(` \x1b[31m✗\x1b[0m Leakage check: FAILED`);
124
+ for (const issue of audit.leakage_check.issues) {
125
+ console.log(` family="${issue.family}" (${issue.record_count} records) leaked to: ${issue.leaked_to.join(', ')}`);
126
+ }
127
+ }
128
+
129
+ // Family distribution
130
+ console.log(`\n Families: ${audit.family_count}`);
131
+ console.log(` Train: ${audit.families_per_split.train}`);
132
+ console.log(` Val: ${audit.families_per_split.val}`);
133
+ console.log(` Test: ${audit.families_per_split.test}`);
134
+
135
+ // Lane balance
136
+ console.log('\n Lane balance:');
137
+ console.log(` ${'Lane'.padEnd(20)} ${'Total'.padStart(6)} ${'Train%'.padStart(8)} ${'Val%'.padStart(8)} ${'Test%'.padStart(8)}`);
138
+ console.log(` ${'─'.repeat(52)}`);
139
+ for (const [lane, b] of Object.entries(audit.lane_balance).sort((a, b) => b[1].total - a[1].total)) {
140
+ console.log(` ${lane.padEnd(20)} ${String(b.total).padStart(6)} ${String(b.train_pct + '%').padStart(8)} ${String(b.val_pct + '%').padStart(8)} ${String(b.test_pct + '%').padStart(8)}`);
141
+ }
142
+
143
+ } else {
144
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: build, list, show, audit`);
145
+ }
146
+ }
147
+
148
+ // Direct execution guard
149
+ if (process.argv[1] && (process.argv[1].endsWith('split.js') || process.argv[1].endsWith('split'))) {
150
+ run().catch((err) => {
151
+ console.error(err.message || err);
152
+ process.exit(1);
153
+ });
154
+ }
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * training-manifest.js — Create, validate, list, and show training manifests.
5
+ *
6
+ * Usage:
7
+ * sdlab training-manifest create --export <id> --profile <id> [--adapter <target>] [--base-model <name>] [--project <name>]
8
+ * sdlab training-manifest validate <manifest-id> [--project <name>]
9
+ * sdlab training-manifest show <manifest-id> [--project <name>]
10
+ * sdlab training-manifest list [--project <name>]
11
+ */
12
+
13
+ import { join } from 'node:path';
14
+ import { getProjectName } from '../lib/args.js';
15
+ import { REPO_ROOT } from '../lib/paths.js';
16
+ import { listExports } from '../lib/export.js';
17
+ import {
18
+ createTrainingManifest,
19
+ validateTrainingManifest,
20
+ loadTrainingManifest,
21
+ listTrainingManifests,
22
+ } from '../lib/training-manifests.js';
23
+
24
+ export async function run(argv = process.argv.slice(2)) {
25
+ const projectName = getProjectName(argv);
26
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
27
+ const subcommand = argv.find(a => !a.startsWith('--') && !a.startsWith('tm-')) || 'list';
28
+ const args = argv.filter(a => a !== subcommand);
29
+
30
+ if (subcommand === 'create') {
31
+ // Required: --export and --profile
32
+ const exportIdx = args.indexOf('--export');
33
+ if (exportIdx < 0) {
34
+ // Try latest export
35
+ const exports = await listExports(projectRoot);
36
+ if (exports.length === 0) throw new Error('No exports found. Run: sdlab export build');
37
+ }
38
+ const exportId = exportIdx >= 0 ? args[exportIdx + 1] : (await listExports(projectRoot)).pop().id;
39
+
40
+ const profileIdx = args.indexOf('--profile');
41
+ if (profileIdx < 0) throw new Error('--profile is required. Use: sdlab training-profile list');
42
+ const profileId = args[profileIdx + 1];
43
+
44
+ const adapterIdx = args.indexOf('--adapter');
45
+ const adapterTarget = adapterIdx >= 0 ? args[adapterIdx + 1] : undefined;
46
+
47
+ const baseModelIdx = args.indexOf('--base-model');
48
+ const baseModel = baseModelIdx >= 0 ? args[baseModelIdx + 1] : undefined;
49
+
50
+ console.log(`\x1b[1msdlab training-manifest create\x1b[0m — ${projectName}`);
51
+ console.log(` Export: ${exportId}`);
52
+ console.log(` Profile: ${profileId}`);
53
+ if (adapterTarget) console.log(` Adapter: ${adapterTarget}`);
54
+ if (baseModel) console.log(` Base: ${baseModel}`);
55
+ console.log('');
56
+
57
+ const result = await createTrainingManifest(projectRoot, exportId, profileId, {
58
+ adapterTarget,
59
+ baseModel,
60
+ });
61
+
62
+ console.log(` \x1b[32m✓\x1b[0m Manifest: ${result.manifestId}`);
63
+
64
+ } else if (subcommand === 'validate') {
65
+ const manifestId = args.find(a => a.startsWith('tm-'));
66
+ if (!manifestId) throw new Error('Usage: sdlab training-manifest validate <manifest-id>');
67
+
68
+ console.log(`\x1b[1msdlab training-manifest validate\x1b[0m — ${manifestId}\n`);
69
+ const { valid, issues } = await validateTrainingManifest(projectRoot, manifestId);
70
+
71
+ if (valid) {
72
+ console.log(` \x1b[32m✓\x1b[0m Manifest is valid — all references intact, fingerprints match`);
73
+ } else {
74
+ console.log(` \x1b[31m✗\x1b[0m Manifest has issues:`);
75
+ for (const issue of issues) {
76
+ console.log(` - ${issue}`);
77
+ }
78
+ }
79
+
80
+ } else if (subcommand === 'show') {
81
+ const manifestId = args.find(a => a.startsWith('tm-'));
82
+ if (!manifestId) throw new Error('Usage: sdlab training-manifest show <manifest-id>');
83
+
84
+ const manifest = await loadTrainingManifest(projectRoot, manifestId);
85
+ console.log(`\x1b[1mTraining manifest\x1b[0m — ${manifestId}\n`);
86
+ console.log(` Created: ${manifest.created_at}`);
87
+ console.log(` Profile: ${manifest.training_profile_id}`);
88
+ console.log(` Export: ${manifest.source_export_id}`);
89
+ console.log(` Snapshot: ${manifest.source_snapshot_id}`);
90
+ console.log(` Split: ${manifest.source_split_id}`);
91
+ console.log(` Adapter: ${manifest.adapter_target}`);
92
+ console.log(` Base model: ${manifest.base_model || '(not specified)'}`);
93
+ console.log(` Caption: ${manifest.caption_mode}`);
94
+ console.log(` Records: ${manifest.dataset_counts?.records || 0}`);
95
+ console.log(` Fingerprint: ${manifest.config_fingerprint.slice(0, 16)}...`);
96
+ console.log(` Export hash: ${manifest.export_manifest_hash.slice(0, 16)}...`);
97
+ if (manifest.linked_eval_pack_ids?.length) {
98
+ console.log(` Eval packs: ${manifest.linked_eval_pack_ids.length}`);
99
+ }
100
+
101
+ } else if (subcommand === 'list') {
102
+ const manifests = await listTrainingManifests(projectRoot);
103
+ if (manifests.length === 0) {
104
+ console.log('No training manifests found. Run: sdlab training-manifest create');
105
+ return;
106
+ }
107
+ console.log(`\x1b[1mTraining manifests\x1b[0m — ${projectName}\n`);
108
+ for (const m of manifests) {
109
+ console.log(` ${m.id} profile=${m.profile} export=${m.export_id} adapter=${m.adapter} records=${m.records} ${m.created_at}`);
110
+ }
111
+
112
+ } else {
113
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: create, validate, show, list`);
114
+ }
115
+ }
116
+
117
+ if (process.argv[1] && (process.argv[1].endsWith('training-manifest.js') || process.argv[1].endsWith('training-manifest'))) {
118
+ run().catch((err) => { console.error(err.message || err); process.exit(1); });
119
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * training-package.js — Build and inspect training packages.
5
+ *
6
+ * Usage:
7
+ * sdlab training-package build --manifest <id> [--adapter <target>] [--copy] [--project <name>]
8
+ * sdlab training-package show <package-id> [--project <name>]
9
+ * sdlab training-package list [--project <name>]
10
+ */
11
+
12
+ import { join } from 'node:path';
13
+ import { getProjectName } from '../lib/args.js';
14
+ import { REPO_ROOT } from '../lib/paths.js';
15
+ import { listTrainingManifests } from '../lib/training-manifests.js';
16
+ import { buildTrainingPackage, listTrainingPackages, loadTrainingPackage } from '../lib/training-packages.js';
17
+
18
+ export async function run(argv = process.argv.slice(2)) {
19
+ const projectName = getProjectName(argv);
20
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
21
+ const subcommand = argv.find(a => !a.startsWith('--') && !a.startsWith('tp-') && !a.startsWith('tm-')) || 'list';
22
+ const args = argv.filter(a => a !== subcommand);
23
+
24
+ if (subcommand === 'build') {
25
+ // Find manifest
26
+ let manifestId;
27
+ const manifestIdx = args.indexOf('--manifest');
28
+ if (manifestIdx >= 0) {
29
+ manifestId = args[manifestIdx + 1];
30
+ } else {
31
+ const manifests = await listTrainingManifests(projectRoot);
32
+ if (manifests.length === 0) throw new Error('No training manifests found. Run: sdlab training-manifest create');
33
+ manifestId = manifests[manifests.length - 1].id;
34
+ }
35
+
36
+ const adapterIdx = args.indexOf('--adapter');
37
+ const adapterOverride = adapterIdx >= 0 ? args[adapterIdx + 1] : undefined;
38
+ const copy = args.includes('--copy');
39
+
40
+ console.log(`\x1b[1msdlab training-package build\x1b[0m — ${projectName}`);
41
+ console.log(` Manifest: ${manifestId}`);
42
+ if (adapterOverride) console.log(` Adapter: ${adapterOverride}`);
43
+ console.log(` Images: ${copy ? 'copy' : 'symlink'}`);
44
+ console.log('');
45
+
46
+ const result = await buildTrainingPackage(projectRoot, manifestId, { copy, adapterOverride });
47
+
48
+ console.log(` \x1b[32m✓\x1b[0m Package: ${result.packageId}`);
49
+ console.log(` Records: ${result.records}`);
50
+ console.log(` Images: ${result.images}`);
51
+
52
+ } else if (subcommand === 'show') {
53
+ const packageId = args.find(a => a.startsWith('tp-'));
54
+ if (!packageId) throw new Error('Usage: sdlab training-package show <package-id>');
55
+
56
+ const pkg = await loadTrainingPackage(projectRoot, packageId);
57
+ console.log(`\x1b[1mTraining package\x1b[0m — ${packageId}\n`);
58
+ console.log(` Created: ${pkg.created_at}`);
59
+ console.log(` Profile: ${pkg.training_profile_id}`);
60
+ console.log(` Adapter: ${pkg.adapter_target}`);
61
+ console.log(` Manifest: ${pkg.training_manifest_id}`);
62
+ console.log(` Export: ${pkg.source_export_id}`);
63
+ console.log(` Records: ${pkg.counts.total_records} (train=${pkg.counts.train} val=${pkg.counts.val} test=${pkg.counts.test})`);
64
+ console.log(` Images: ${pkg.counts.images}`);
65
+
66
+ } else if (subcommand === 'list') {
67
+ const packages = await listTrainingPackages(projectRoot);
68
+ if (packages.length === 0) {
69
+ console.log('No training packages found. Run: sdlab training-package build');
70
+ return;
71
+ }
72
+ console.log(`\x1b[1mTraining packages\x1b[0m — ${projectName}\n`);
73
+ for (const p of packages) {
74
+ console.log(` ${p.id} profile=${p.profile} adapter=${p.adapter} records=${p.records} images=${p.images} ${p.created_at}`);
75
+ }
76
+
77
+ } else {
78
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: build, show, list`);
79
+ }
80
+ }
81
+
82
+ if (process.argv[1] && (process.argv[1].endsWith('training-package.js') || process.argv[1].endsWith('training-package'))) {
83
+ run().catch((err) => { console.error(err.message || err); process.exit(1); });
84
+ }
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * training-profile.js — List and show training profiles.
5
+ *
6
+ * Usage:
7
+ * sdlab training-profile list [--project <name>]
8
+ * sdlab training-profile show <profile-id> [--project <name>]
9
+ */
10
+
11
+ import { join } from 'node:path';
12
+ import { getProjectName } from '../lib/args.js';
13
+ import { REPO_ROOT } from '../lib/paths.js';
14
+ import { listTrainingProfiles, loadTrainingProfile } from '../lib/training-profiles.js';
15
+
16
+ export async function run(argv = process.argv.slice(2)) {
17
+ const projectName = getProjectName(argv);
18
+ const projectRoot = join(REPO_ROOT, 'projects', projectName);
19
+ const subcommand = argv.find(a => !a.startsWith('--')) || 'list';
20
+ const args = argv.filter(a => a !== subcommand);
21
+
22
+ if (subcommand === 'list') {
23
+ const profiles = await listTrainingProfiles(projectRoot);
24
+ if (profiles.length === 0) {
25
+ console.log('No training profiles found. Add JSON files to training/profiles/');
26
+ return;
27
+ }
28
+ console.log(`\x1b[1mTraining profiles\x1b[0m — ${projectName}\n`);
29
+ for (const p of profiles) {
30
+ console.log(` ${p.profile_id.padEnd(30)} ${p.target_family.padEnd(15)} ${p.asset_type.padEnd(10)} lanes=${p.lanes} adapters=${p.adapters.join(',')}`);
31
+ }
32
+
33
+ } else if (subcommand === 'show') {
34
+ const profileId = args.find(a => !a.startsWith('--'));
35
+ if (!profileId) throw new Error('Usage: sdlab training-profile show <profile-id>');
36
+
37
+ const profile = await loadTrainingProfile(projectRoot, profileId);
38
+ console.log(`\x1b[1mTraining profile\x1b[0m — ${profileId}\n`);
39
+ console.log(` Label: ${profile.label}`);
40
+ console.log(` Target family: ${profile.target_family}`);
41
+ console.log(` Asset type: ${profile.asset_type}`);
42
+ console.log(` Intended use: ${profile.intended_use || '(not specified)'}`);
43
+ console.log(` Eligible lanes: ${profile.eligible_lanes.join(', ')}`);
44
+ console.log(` Adapter targets: ${profile.adapter_targets.join(', ')}`);
45
+ if (profile.caption_strategy) console.log(` Caption: ${profile.caption_strategy}`);
46
+ if (profile.base_model_recommendations?.length) {
47
+ console.log(` Base models: ${profile.base_model_recommendations.join(', ')}`);
48
+ }
49
+ if (profile.excluded_tags?.length) {
50
+ console.log(` Excluded tags: ${profile.excluded_tags.join(', ')}`);
51
+ }
52
+ if (profile.required_eval_pack_profiles?.length) {
53
+ console.log(` Required evals: ${profile.required_eval_pack_profiles.join(', ')}`);
54
+ }
55
+
56
+ } else {
57
+ throw new Error(`Unknown subcommand: ${subcommand}. Use: list, show`);
58
+ }
59
+ }
60
+
61
+ if (process.argv[1] && (process.argv[1].endsWith('training-profile.js') || process.argv[1].endsWith('training-profile'))) {
62
+ run().catch((err) => { console.error(err.message || err); process.exit(1); });
63
+ }