@mcptoolshop/style-dataset-lab 3.3.0 → 3.5.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 +60 -0
- package/README.es.md +117 -55
- package/README.fr.md +125 -65
- package/README.hi.md +117 -68
- package/README.it.md +119 -58
- package/README.ja.md +124 -74
- package/README.md +73 -7
- package/README.pt-BR.md +128 -63
- package/README.zh.md +116 -65
- package/bin/sdlab.js +100 -4
- package/lib/adapters/ai-toolkit.js +224 -35
- package/lib/adapters/comfyui-runner.js +79 -26
- package/lib/adapters/comfyui-workflows.js +94 -11
- package/lib/adapters/diffusers-lora.js +37 -5
- package/lib/adapters/generic-image-caption.js +43 -5
- package/lib/args.js +16 -1
- package/lib/asset-ingest.js +575 -0
- package/lib/asset-masks.js +323 -0
- package/lib/asset-source.js +1176 -0
- package/lib/batch-modes.js +29 -8
- package/lib/batch-runs.js +29 -1
- package/lib/batch-sheet-render.js +13 -3
- package/lib/brief-compiler.js +19 -0
- package/lib/candidate-sheet.js +327 -0
- package/lib/canon-author.js +350 -0
- package/lib/canon-build/build.js +146 -93
- package/lib/canon-review-gate.js +205 -0
- package/lib/captions.js +57 -0
- package/lib/card.js +74 -2
- package/lib/colab-launcher-guards.js +285 -0
- package/lib/comfyui.js +116 -18
- package/lib/config.js +116 -12
- package/lib/critique-engine.js +36 -2
- package/lib/eligibility.js +121 -9
- package/lib/eval-pack.js +14 -14
- package/lib/eval-runs.js +33 -0
- package/lib/export.js +122 -34
- package/lib/freeze-gate.js +16 -3
- package/lib/freeze-stamp.js +28 -3
- package/lib/generated-provenance.js +194 -6
- package/lib/implementation-packs.js +95 -17
- package/lib/ingest.js +396 -0
- package/lib/judgment-provenance.js +220 -0
- package/lib/lane1-render.js +668 -0
- package/lib/measure.js +422 -0
- package/lib/memory-store.js +663 -0
- package/lib/model-licences.js +305 -0
- package/lib/npy-meta.js +130 -0
- package/lib/paths.js +196 -7
- package/lib/png-meta.js +512 -0
- package/lib/records.js +54 -8
- package/lib/reingest-selected.js +113 -17
- package/lib/reingest.js +27 -3
- package/lib/run-manifest.js +159 -25
- package/lib/run-summary.js +12 -6
- package/lib/runtime-runs.js +45 -4
- package/lib/snapshot.js +114 -15
- package/lib/split.js +364 -48
- package/lib/train-runs.js +214 -0
- package/lib/training-manifests.js +114 -21
- package/lib/training-packages.js +147 -15
- package/lib/verifier.js +3 -0
- package/lib/verify-chain.js +367 -0
- package/lib/workflow-profiles.js +24 -3
- package/package.json +4 -3
- package/runtime/comfyui/txt2img-qwen.json +38 -0
- package/runtime/comfyui/txt2img-standard.json +21 -0
- package/scripts/_resume.js +62 -0
- package/scripts/_shared-args.js +44 -0
- package/scripts/asset-ingest.js +160 -0
- package/scripts/brief-compile.js +2 -1
- package/scripts/brief-show.js +2 -1
- package/scripts/canon-bind.js +2 -2
- package/scripts/canon-build.js +27 -20
- package/scripts/canon-draft.js +82 -0
- package/scripts/canon-drift.js +16 -7
- package/scripts/canon-freeze-status.js +14 -6
- package/scripts/canon-freeze.js +31 -11
- package/scripts/canon-unfreeze.js +17 -9
- package/scripts/card.js +2 -2
- package/scripts/colab/REAL_SERVICE_ASSUMPTIONS.md +14 -0
- package/scripts/colab/ai-toolkit-override-py315.txt +45 -0
- package/scripts/colab/colab_window.sh +587 -0
- package/scripts/colab/lane1_render.py +970 -0
- package/scripts/colab/launch_guards.py +564 -0
- package/scripts/colab/launch_lane1_stub.sh +309 -0
- package/scripts/colab/launch_lane3_stub.sh +366 -0
- package/scripts/colab/remote_runner.py +59 -0
- package/scripts/compare.js +2 -2
- package/scripts/critique.js +16 -1
- package/scripts/curate.js +2 -2
- package/scripts/doctor.js +182 -55
- package/scripts/eligibility.js +42 -2
- package/scripts/eval-pack.js +2 -2
- package/scripts/eval-run.js +2 -2
- package/scripts/export.js +2 -2
- package/scripts/generate-controlnet.js +45 -25
- package/scripts/generate-identity.js +53 -23
- package/scripts/generate-ipadapter.js +44 -23
- package/scripts/generate.js +86 -17
- package/scripts/implementation-pack.js +2 -2
- package/scripts/ingest.js +145 -0
- package/scripts/init.js +73 -13
- package/scripts/measure.js +170 -0
- package/scripts/measure_image.py +458 -0
- package/scripts/migrate-records.js +142 -0
- package/scripts/painterly-test.js +4 -3
- package/scripts/painterly.js +7 -3
- package/scripts/qwen_generate.py +138 -20
- package/scripts/reingest.js +2 -2
- package/scripts/sheet.js +142 -0
- package/scripts/snapshot.js +2 -2
- package/scripts/split.js +2 -2
- package/scripts/training-manifest.js +2 -2
- package/scripts/training-package.js +2 -2
- package/scripts/training-profile.js +2 -2
- package/scripts/verify.js +67 -0
- package/scripts/workflow-list.js +2 -1
- package/scripts/workflow-show.js +2 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* asset-ingest.js — `sdlab asset ingest <dir>`: register a provenance-carrying
|
|
5
|
+
* asset export (asset-source.json + renders + masks + channels) into a
|
|
6
|
+
* project, through the full contract of lib/asset-source.js and the
|
|
7
|
+
* receiving-dock gates of lib/asset-ingest.js.
|
|
8
|
+
*
|
|
9
|
+
* The manifest-driven, gated sibling of `sdlab ingest` (which takes bare
|
|
10
|
+
* images with no contract). See docs/asset-lane-design.md.
|
|
11
|
+
*
|
|
12
|
+
* PATH NOTE (deliberate deviation from resolveSafeProjectPath): the export
|
|
13
|
+
* directory is allowed to live OUTSIDE the sdlab workspace — asset exports
|
|
14
|
+
* live where the asset pipeline puts them (e.g. a training area on another
|
|
15
|
+
* part of the disk). The source is READ-ONLY here; every path the manifest
|
|
16
|
+
* references is contained to the export directory by lib/asset-source.js,
|
|
17
|
+
* and every WRITE stays inside the project. Containment where it protects,
|
|
18
|
+
* reach where the user explicitly points.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* sdlab asset ingest <dir> --project <name>
|
|
22
|
+
* sdlab asset ingest <dir> --project <name> --dry-run
|
|
23
|
+
* sdlab asset ingest <dir> --project <name> --json
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
28
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
29
|
+
import { inputError, handleCliError } from '../lib/errors.js';
|
|
30
|
+
import { ingestAssetSource } from '../lib/asset-ingest.js';
|
|
31
|
+
import { result } from '../lib/log.js';
|
|
32
|
+
|
|
33
|
+
const USAGE = `Usage: sdlab asset ingest <dir> --project <name> [--dry-run] [--json]
|
|
34
|
+
|
|
35
|
+
Validate an asset export's asset-source.json (schema, containment, encoding
|
|
36
|
+
proofs, categorical palette proofs, the acceptance verdict) and register its
|
|
37
|
+
renders as candidate records. Refuses loudly on any contract violation —
|
|
38
|
+
nothing registers on a bad manifest.
|
|
39
|
+
|
|
40
|
+
Positional:
|
|
41
|
+
dir Asset export directory containing asset-source.json.
|
|
42
|
+
May live outside the workspace (read-only source).
|
|
43
|
+
|
|
44
|
+
Flags:
|
|
45
|
+
--project <name> Project to register into
|
|
46
|
+
--dry-run Validate + run gates + report the plan; write nothing
|
|
47
|
+
--json Emit the result as JSON on stdout
|
|
48
|
+
|
|
49
|
+
What lands on success:
|
|
50
|
+
records/<asset>__<render>.json uncurated records (judgment: null),
|
|
51
|
+
provenance.source: "asset", hashes,
|
|
52
|
+
camera/facing, gate measurements
|
|
53
|
+
outputs/candidates/<asset>__<render>.png
|
|
54
|
+
assets/<asset>/ manifest copy, silhouettes, channel
|
|
55
|
+
instances, pairs, ingest-receipt.json
|
|
56
|
+
(the receipt lists every written path —
|
|
57
|
+
it is the undo checklist)
|
|
58
|
+
|
|
59
|
+
Next steps after ingesting:
|
|
60
|
+
1. Review: sdlab curate <id> approved/rejected "explanation"
|
|
61
|
+
2. Bind: sdlab bind --project <name>`;
|
|
62
|
+
|
|
63
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
64
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
65
|
+
console.log(USAGE);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { flags, positionals } = parseArgs(argv, {
|
|
70
|
+
flags: {
|
|
71
|
+
project: { type: 'string' },
|
|
72
|
+
'dry-run': { type: 'boolean', default: false },
|
|
73
|
+
json: { type: 'boolean', default: false },
|
|
74
|
+
},
|
|
75
|
+
deprecated: { game: 'project' },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const dirArg = positionals[0];
|
|
79
|
+
if (!dirArg) {
|
|
80
|
+
throw inputError('INPUT_MISSING_ARGS', 'An asset export directory is required.', USAGE);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const projectName = flags.project || getProjectName(argv);
|
|
84
|
+
const projectRoot = getProjectRoot(projectName);
|
|
85
|
+
const dryRun = flags['dry-run'] || flags.dryRun;
|
|
86
|
+
const sourceDir = resolve(dirArg);
|
|
87
|
+
|
|
88
|
+
if (!flags.json) {
|
|
89
|
+
console.log(`\x1b[1msdlab asset ingest\x1b[0m — ${projectName}`);
|
|
90
|
+
console.log(` Source: ${sourceDir}`);
|
|
91
|
+
if (dryRun) console.log(` Mode: DRY RUN (no files written)`);
|
|
92
|
+
console.log('');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const report = await ingestAssetSource(projectRoot, sourceDir, { dryRun });
|
|
96
|
+
|
|
97
|
+
if (flags.json) {
|
|
98
|
+
console.log(JSON.stringify(report, null, 2));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (report.alreadyIngested) {
|
|
103
|
+
console.log(` ○ Asset "${report.assetId}" is already ingested (same manifest hash) — nothing to do.`);
|
|
104
|
+
console.log(` Receipt: ${report.receiptPath}`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(` \x1b[32m✓\x1b[0m Registered: ${report.created.length} render record(s)${dryRun ? ' (dry run — nothing written)' : ''}`);
|
|
109
|
+
for (const id of report.created) {
|
|
110
|
+
const gate = report.gates[id];
|
|
111
|
+
console.log(` ✓ ${id} (off-palette ${gate.offpalette_pct}%${gate.withdrawn_pct_bound ? ' [diagnostic]' : ''}, blob ${gate.largest_blob_px}px)`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (report.skipped.length > 0) {
|
|
115
|
+
console.log(` Skipped: ${report.skipped.length} (already registered by this ingest)`);
|
|
116
|
+
for (const id of report.skipped) console.log(` ○ ${id}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (report.rejected.length > 0) {
|
|
120
|
+
console.log(` \x1b[33m⚠\x1b[0m Rejected by the asset's own palette gate: ${report.rejected.length}`);
|
|
121
|
+
for (const r of report.rejected) {
|
|
122
|
+
const d = r.gate.dominant;
|
|
123
|
+
console.log(` ✗ ${r.render_id}: blob ${r.gate.largest_blob_px}px (limit ${'≤'} manifest gate)` +
|
|
124
|
+
(d ? ` — dominant hue ${d.hue_lo}-${d.hue_hi}°, median C* ${d.median_chroma}, rgb(${d.median_rgb})` : ''));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Provenance notices — what the manifest left undeclared. Not failures:
|
|
129
|
+
// the ingest succeeded. These are facts about the training data that only
|
|
130
|
+
// the manifest could have supplied, printed so they are not discovered
|
|
131
|
+
// later by a split or a training run that quietly assumed them.
|
|
132
|
+
if (report.notices?.length > 0) {
|
|
133
|
+
const gaps = report.notices.filter((n) => n.kind === 'gap');
|
|
134
|
+
const infos = report.notices.filter((n) => n.kind !== 'gap');
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(` \x1b[36mProvenance notices\x1b[0m (${report.notices.length}) — the ingest SUCCEEDED; these describe the records it created:`);
|
|
137
|
+
if (gaps.length > 0) {
|
|
138
|
+
console.log(` \x1b[33m gaps\x1b[0m (${gaps.length}) — declarations the manifest did not make; closing them is export-side work:`);
|
|
139
|
+
for (const n of gaps) console.log(` • [${n.code}] ${n.message}`);
|
|
140
|
+
}
|
|
141
|
+
if (infos.length > 0) {
|
|
142
|
+
console.log(` info (${infos.length}) — declarations it DID make that change how these records read:`);
|
|
143
|
+
for (const n of infos) console.log(` • [${n.code}] ${n.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!dryRun && report.created.length > 0) {
|
|
148
|
+
console.log('');
|
|
149
|
+
result(`Receipt: ${report.receiptPath}`);
|
|
150
|
+
console.log('');
|
|
151
|
+
console.log(' Next steps:');
|
|
152
|
+
console.log(' 1. Review: sdlab curate <id> approved/rejected "explanation"');
|
|
153
|
+
console.log(' 2. Bind: sdlab bind --project ' + projectName);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Direct execution guard
|
|
158
|
+
if (process.argv[1] && (process.argv[1].endsWith('asset-ingest.js') || process.argv[1].endsWith('asset-ingest'))) {
|
|
159
|
+
run().catch(handleCliError);
|
|
160
|
+
}
|
package/scripts/brief-compile.js
CHANGED
|
@@ -14,6 +14,7 @@ import { parseArgs, getProjectName, parseNumberFlag } from '../lib/args.js';
|
|
|
14
14
|
import { getProjectRoot } from '../lib/paths.js';
|
|
15
15
|
import { compileBrief, saveCompiledBrief } from '../lib/brief-compiler.js';
|
|
16
16
|
import { info } from '../lib/log.js';
|
|
17
|
+
import { handleCliError } from '../lib/errors.js';
|
|
17
18
|
|
|
18
19
|
export async function run(argv = process.argv.slice(2)) {
|
|
19
20
|
const { flags } = parseArgs(argv, {
|
|
@@ -130,5 +131,5 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
if (process.argv[1] && (process.argv[1].endsWith('brief-compile.js') || process.argv[1].endsWith('brief-compile'))) {
|
|
133
|
-
run().catch(
|
|
134
|
+
run().catch(handleCliError);
|
|
134
135
|
}
|
package/scripts/brief-show.js
CHANGED
|
@@ -13,6 +13,7 @@ import { parseArgs, getProjectName } from '../lib/args.js';
|
|
|
13
13
|
import { getProjectRoot } from '../lib/paths.js';
|
|
14
14
|
import { loadCompiledBrief } from '../lib/brief-compiler.js';
|
|
15
15
|
import { renderBriefText, renderBriefMarkdown } from '../lib/brief-render.js';
|
|
16
|
+
import { handleCliError } from '../lib/errors.js';
|
|
16
17
|
|
|
17
18
|
export async function run(argv = process.argv.slice(2)) {
|
|
18
19
|
const { flags, positionals } = parseArgs(argv, {
|
|
@@ -49,5 +50,5 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
if (process.argv[1] && (process.argv[1].endsWith('brief-show.js') || process.argv[1].endsWith('brief-show'))) {
|
|
52
|
-
run().catch(
|
|
53
|
+
run().catch(handleCliError);
|
|
53
54
|
}
|
package/scripts/canon-bind.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'fs';
|
|
20
20
|
import { join } from 'path';
|
|
21
21
|
import { getProjectName } from '../lib/args.js';
|
|
22
|
-
import {
|
|
22
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
23
23
|
import { loadConstitution, loadLanes, loadRubric, loadTerminology, detectLane, detectGroup } from '../lib/config.js';
|
|
24
24
|
import { inputError, handleCliError } from '../lib/errors.js';
|
|
25
25
|
|
|
@@ -155,7 +155,7 @@ function generateAssertions(record, constitutionRules, lanesConfig, rubricConfig
|
|
|
155
155
|
|
|
156
156
|
export async function run(argv = process.argv.slice(2)) {
|
|
157
157
|
const projectName = getProjectName(argv);
|
|
158
|
-
const PROJECT_ROOT =
|
|
158
|
+
const PROJECT_ROOT = getProjectRoot(projectName);
|
|
159
159
|
const RECORDS_DIR = join(PROJECT_ROOT, 'records');
|
|
160
160
|
const DRY_RUN = argv.includes('--dry-run');
|
|
161
161
|
const STATS_ONLY = argv.includes('--stats');
|
package/scripts/canon-build.js
CHANGED
|
@@ -7,27 +7,35 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { parseArgs } from 'node:util';
|
|
10
|
-
import {
|
|
11
|
-
import { existsSync } from 'node:fs';
|
|
10
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
12
11
|
import { runBuild } from '../lib/canon-build/build.js';
|
|
13
12
|
import { inputError } from '../lib/errors.js';
|
|
13
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
14
14
|
|
|
15
15
|
export async function run(argv) {
|
|
16
|
+
const options = {
|
|
17
|
+
project: { type: 'string' },
|
|
18
|
+
game: { type: 'string' }, // deprecated alias
|
|
19
|
+
full: { type: 'boolean', default: false },
|
|
20
|
+
'no-cache': { type: 'boolean', default: false },
|
|
21
|
+
'dry-run': { type: 'boolean', default: false },
|
|
22
|
+
only: { type: 'string' },
|
|
23
|
+
json: { type: 'boolean', default: false },
|
|
24
|
+
quiet: { type: 'boolean', default: false },
|
|
25
|
+
};
|
|
16
26
|
const parsed = parseArgs({
|
|
17
27
|
args: argv,
|
|
18
|
-
options
|
|
19
|
-
project: { type: 'string' },
|
|
20
|
-
game: { type: 'string' }, // deprecated alias
|
|
21
|
-
full: { type: 'boolean', default: false },
|
|
22
|
-
'no-cache': { type: 'boolean', default: false },
|
|
23
|
-
'dry-run': { type: 'boolean', default: false },
|
|
24
|
-
only: { type: 'string' },
|
|
25
|
-
json: { type: 'boolean', default: false },
|
|
26
|
-
quiet: { type: 'boolean', default: false },
|
|
27
|
-
},
|
|
28
|
+
options,
|
|
28
29
|
allowPositionals: true,
|
|
29
30
|
strict: false,
|
|
30
31
|
});
|
|
32
|
+
// H4: strict:false is deliberate (it lets the entity-id positional through
|
|
33
|
+
// unrejected) but its side effect is that ANY unrecognized --flag is
|
|
34
|
+
// silently accepted too — a misspelled OPTIONAL flag (--ful instead of
|
|
35
|
+
// --full, --dry-Run) produces no error, no warning, and the option you
|
|
36
|
+
// meant to set just quietly keeps its default. Post-validate instead of
|
|
37
|
+
// trusting strict:false's leniency for everything.
|
|
38
|
+
assertKnownOptions(parsed.values, options, { command: 'canon build' });
|
|
31
39
|
|
|
32
40
|
const projectName = parsed.values.project || parsed.values.game;
|
|
33
41
|
if (!projectName) {
|
|
@@ -38,14 +46,13 @@ export async function run(argv) {
|
|
|
38
46
|
);
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver instead of
|
|
50
|
+
// join(process.cwd(), 'projects', projectName) — for a globally-installed
|
|
51
|
+
// sdlab the user is never inside the package directory, so process.cwd()
|
|
52
|
+
// silently pointed at the wrong place. getProjectRoot() also subsumes the
|
|
53
|
+
// existence check this used to do by hand (same INPUT_UNKNOWN_PROJECT shape
|
|
54
|
+
// as every other command, with an accurate "sdlab init" hint).
|
|
55
|
+
const projectRoot = getProjectRoot(projectName);
|
|
49
56
|
|
|
50
57
|
const only = parsed.values.only
|
|
51
58
|
? parsed.values.only.split(',').map((s) => s.trim()).filter(Boolean)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sdlab canon draft <class_id> --endpoint <url> --brief "<text>" --out <path>`
|
|
3
|
+
*
|
|
4
|
+
* CLI wrapper for the Lane 2 canon and prompt authoring adapter (DESIGN-v3 §3.2,
|
|
5
|
+
* §11, G10). Calls a loopback-only OpenAI-compatible server (e.g., llama-server
|
|
6
|
+
* serving Muse-Glimmer-30B-A3B under Apache-2.0) and writes `prompts.jsonl`
|
|
7
|
+
* (+ `prompts.jsonl.sha256`) with `status: "draft"` and `judgment: null`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parseArgs } from 'node:util';
|
|
11
|
+
import {
|
|
12
|
+
draftCanonClassViaEndpoint,
|
|
13
|
+
verifyGlimmerRuntime,
|
|
14
|
+
writePromptsJsonl,
|
|
15
|
+
} from '../lib/canon-author.js';
|
|
16
|
+
import { inputError } from '../lib/errors.js';
|
|
17
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
18
|
+
|
|
19
|
+
export async function run(argv) {
|
|
20
|
+
const options = {
|
|
21
|
+
endpoint: { type: 'string', default: 'http://127.0.0.1:8080/v1/chat/completions' },
|
|
22
|
+
brief: { type: 'string' },
|
|
23
|
+
out: { type: 'string' },
|
|
24
|
+
build: { type: 'string', default: 'b10353' },
|
|
25
|
+
vision: { type: 'boolean', default: false },
|
|
26
|
+
mmproj: { type: 'string' },
|
|
27
|
+
'mmproj-sha': { type: 'string' },
|
|
28
|
+
json: { type: 'boolean', default: false },
|
|
29
|
+
};
|
|
30
|
+
const parsed = parseArgs({
|
|
31
|
+
args: argv,
|
|
32
|
+
options,
|
|
33
|
+
allowPositionals: true,
|
|
34
|
+
strict: false,
|
|
35
|
+
});
|
|
36
|
+
assertKnownOptions(parsed.values, options, { command: 'canon draft' });
|
|
37
|
+
|
|
38
|
+
const classId = parsed.positionals?.[0];
|
|
39
|
+
if (!classId) {
|
|
40
|
+
throw inputError(
|
|
41
|
+
'INPUT_MISSING_ARG',
|
|
42
|
+
'sdlab canon draft requires a <class_id> positional argument.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (!parsed.values.brief) {
|
|
46
|
+
throw inputError(
|
|
47
|
+
'INPUT_MISSING_FLAG',
|
|
48
|
+
'sdlab canon draft requires --brief "<description>".'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
verifyGlimmerRuntime({
|
|
53
|
+
llamaServerBuild: parsed.values.build,
|
|
54
|
+
vision: Boolean(parsed.values.vision),
|
|
55
|
+
mmprojPath: parsed.values.mmproj || null,
|
|
56
|
+
mmprojSha256: parsed.values['mmproj-sha'] || null,
|
|
57
|
+
verifyFiles: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const draft = await draftCanonClassViaEndpoint({
|
|
61
|
+
endpoint: parsed.values.endpoint,
|
|
62
|
+
classId,
|
|
63
|
+
brief: parsed.values.brief,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
let written = null;
|
|
67
|
+
if (parsed.values.out) {
|
|
68
|
+
written = writePromptsJsonl(parsed.values.out, [draft]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (parsed.values.json) {
|
|
72
|
+
console.log(JSON.stringify({ ok: true, draft, written }, null, 2));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`drafted "${classId}" (status=${draft.status}, judgment=${draft.judgment})`);
|
|
77
|
+
console.log(` draft_sha256: ${draft.draft_sha256}`);
|
|
78
|
+
console.log(` messages_sha256: ${draft.messages_sha256}`);
|
|
79
|
+
if (written) {
|
|
80
|
+
console.log(` prompts.jsonl: ${written.promptsPath} (${written.sha256})`);
|
|
81
|
+
}
|
|
82
|
+
}
|
package/scripts/canon-drift.js
CHANGED
|
@@ -19,24 +19,33 @@ import { loadSchema } from '../lib/canon-build/load-schema.js';
|
|
|
19
19
|
import { readFreezeStatus, resolveWatchFields, computeWatchHash } from '../lib/freeze-stamp.js';
|
|
20
20
|
import { readEventsSince } from '../lib/freeze-events.js';
|
|
21
21
|
import { inputError } from '../lib/errors.js';
|
|
22
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
23
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
22
24
|
|
|
23
25
|
export async function run(argv) {
|
|
26
|
+
const options = {
|
|
27
|
+
project: { type: 'string' },
|
|
28
|
+
game: { type: 'string' },
|
|
29
|
+
since: { type: 'string' },
|
|
30
|
+
json: { type: 'boolean', default: false },
|
|
31
|
+
};
|
|
24
32
|
const parsed = parseArgs({
|
|
25
33
|
args: argv,
|
|
26
|
-
options
|
|
27
|
-
project: { type: 'string' },
|
|
28
|
-
game: { type: 'string' },
|
|
29
|
-
since: { type: 'string' },
|
|
30
|
-
json: { type: 'boolean', default: false },
|
|
31
|
-
},
|
|
34
|
+
options,
|
|
32
35
|
allowPositionals: true,
|
|
33
36
|
strict: false,
|
|
34
37
|
});
|
|
38
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
39
|
+
// flags means a typo'd optional flag (e.g. --sinse instead of --since)
|
|
40
|
+
// silently no-ops instead of erroring.
|
|
41
|
+
assertKnownOptions(parsed.values, options, { command: 'canon drift' });
|
|
35
42
|
|
|
36
43
|
const projectName = parsed.values.project || parsed.values.game;
|
|
37
44
|
if (!projectName) throw inputError('INPUT_MISSING_FLAG', '--project required');
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver, not
|
|
47
|
+
// process.cwd() (wrong base for a globally-installed sdlab).
|
|
48
|
+
const projectRoot = getProjectRoot(projectName);
|
|
40
49
|
const config = await loadBuildConfig(join(projectRoot, 'canon-build', 'config.json'), projectRoot);
|
|
41
50
|
|
|
42
51
|
// Load latest build manifest for the stamped watch-hashes
|
|
@@ -10,25 +10,33 @@ import { loadCanonEntriesInDir } from '../lib/canon-build/load-entry.js';
|
|
|
10
10
|
import { readFreezeStatus, resolveWatchFields } from '../lib/freeze-stamp.js';
|
|
11
11
|
import { readEventsFor } from '../lib/freeze-events.js';
|
|
12
12
|
import { inputError } from '../lib/errors.js';
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
13
15
|
|
|
14
16
|
export async function run(argv) {
|
|
17
|
+
const options = {
|
|
18
|
+
project: { type: 'string' },
|
|
19
|
+
game: { type: 'string' },
|
|
20
|
+
json: { type: 'boolean', default: false },
|
|
21
|
+
};
|
|
15
22
|
const parsed = parseArgs({
|
|
16
23
|
args: argv,
|
|
17
|
-
options
|
|
18
|
-
project: { type: 'string' },
|
|
19
|
-
game: { type: 'string' },
|
|
20
|
-
json: { type: 'boolean', default: false },
|
|
21
|
-
},
|
|
24
|
+
options,
|
|
22
25
|
allowPositionals: true,
|
|
23
26
|
strict: false,
|
|
24
27
|
});
|
|
28
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
29
|
+
// flags means a typo'd optional flag silently no-ops instead of erroring.
|
|
30
|
+
assertKnownOptions(parsed.values, options, { command: 'canon freeze-status' });
|
|
25
31
|
|
|
26
32
|
const entityIdArg = (parsed.positionals || [])[0];
|
|
27
33
|
if (!entityIdArg) throw inputError('INPUT_MISSING_ARG', 'sdlab canon freeze-status requires an entity id.');
|
|
28
34
|
const projectName = parsed.values.project || parsed.values.game;
|
|
29
35
|
if (!projectName) throw inputError('INPUT_MISSING_FLAG', '--project required');
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver, not
|
|
38
|
+
// process.cwd() (wrong base for a globally-installed sdlab).
|
|
39
|
+
const projectRoot = getProjectRoot(projectName);
|
|
32
40
|
const config = await loadBuildConfig(join(projectRoot, 'canon-build', 'config.json'), projectRoot);
|
|
33
41
|
|
|
34
42
|
let resolved = null;
|
package/scripts/canon-freeze.js
CHANGED
|
@@ -17,24 +17,35 @@ import { loadBuildConfig } from '../lib/canon-build/load-config.js';
|
|
|
17
17
|
import { loadCanonEntriesInDir, entryId } from '../lib/canon-build/load-entry.js';
|
|
18
18
|
import { buildFreezeBlock, FREEZE_STATUSES, resolveWatchFields } from '../lib/freeze-stamp.js';
|
|
19
19
|
import { appendEvent } from '../lib/freeze-events.js';
|
|
20
|
+
import { assertDirectorRatifiedForFreeze } from '../lib/canon-review-gate.js';
|
|
20
21
|
import { inputError } from '../lib/errors.js';
|
|
22
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
23
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
21
24
|
|
|
22
25
|
export async function run(argv) {
|
|
26
|
+
const options = {
|
|
27
|
+
project: { type: 'string' },
|
|
28
|
+
game: { type: 'string' },
|
|
29
|
+
reason: { type: 'string' },
|
|
30
|
+
status: { type: 'string', default: 'frozen' },
|
|
31
|
+
watch: { type: 'string' },
|
|
32
|
+
build: { type: 'string' },
|
|
33
|
+
by: { type: 'string', default: 'mike' },
|
|
34
|
+
json: { type: 'boolean', default: false },
|
|
35
|
+
};
|
|
23
36
|
const parsed = parseArgs({
|
|
24
37
|
args: argv,
|
|
25
|
-
options
|
|
26
|
-
project: { type: 'string' },
|
|
27
|
-
game: { type: 'string' },
|
|
28
|
-
reason: { type: 'string' },
|
|
29
|
-
status: { type: 'string', default: 'frozen' },
|
|
30
|
-
watch: { type: 'string' },
|
|
31
|
-
build: { type: 'string' },
|
|
32
|
-
by: { type: 'string', default: 'mike' },
|
|
33
|
-
json: { type: 'boolean', default: false },
|
|
34
|
-
},
|
|
38
|
+
options,
|
|
35
39
|
allowPositionals: true,
|
|
36
40
|
strict: false,
|
|
37
41
|
});
|
|
42
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
43
|
+
// flags means a typo'd optional flag silently no-ops instead of erroring.
|
|
44
|
+
// This one matters more than most: `sdlab canon freeze X --staus
|
|
45
|
+
// soft-advisory` (typo) would silently keep status at its default
|
|
46
|
+
// 'frozen' — a HARD block — instead of the 'soft-advisory' the operator
|
|
47
|
+
// typed, and a typo'd --watch/--build silently drops a real override too.
|
|
48
|
+
assertKnownOptions(parsed.values, options, { command: 'canon freeze' });
|
|
38
49
|
|
|
39
50
|
const positional = parsed.positionals || [];
|
|
40
51
|
const entityIdArg = positional[0];
|
|
@@ -72,7 +83,13 @@ export async function run(argv) {
|
|
|
72
83
|
);
|
|
73
84
|
}
|
|
74
85
|
|
|
75
|
-
|
|
86
|
+
// SDL-H12: was join(process.cwd(), 'projects', projectName) — wrong base for
|
|
87
|
+
// a globally-installed sdlab. getProjectRoot() resolves from the workspace
|
|
88
|
+
// root (not cwd) and throws a clear INPUT_UNKNOWN_PROJECT before ever
|
|
89
|
+
// reaching loadBuildConfig(), instead of a deeper, misleading
|
|
90
|
+
// CANON_BUILD_CONFIG_NOT_FOUND pointed at a config that "already exists" —
|
|
91
|
+
// just under a different (correct) project root.
|
|
92
|
+
const projectRoot = getProjectRoot(projectName);
|
|
76
93
|
const configPath = join(projectRoot, 'canon-build', 'config.json');
|
|
77
94
|
const config = await loadBuildConfig(configPath, projectRoot);
|
|
78
95
|
|
|
@@ -86,6 +103,9 @@ export async function run(argv) {
|
|
|
86
103
|
);
|
|
87
104
|
}
|
|
88
105
|
|
|
106
|
+
// G11: Refuse to freeze a model-drafted canon entry unless explicitly ratified by the Director
|
|
107
|
+
assertDirectorRatifiedForFreeze(resolved.entry.frontmatter);
|
|
108
|
+
|
|
89
109
|
// Resolve the witness build hash
|
|
90
110
|
const lockedAtBuild = parsed.values.build || await findLatestBuildHash(projectRoot);
|
|
91
111
|
if (!lockedAtBuild) {
|
|
@@ -18,21 +18,27 @@ import { loadCanonEntriesInDir } from '../lib/canon-build/load-entry.js';
|
|
|
18
18
|
import { appendOverrideToFreezeBlock, readFreezeStatus } from '../lib/freeze-stamp.js';
|
|
19
19
|
import { appendEvent } from '../lib/freeze-events.js';
|
|
20
20
|
import { inputError } from '../lib/errors.js';
|
|
21
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
22
|
+
import { assertKnownOptions } from './_shared-args.js';
|
|
21
23
|
|
|
22
24
|
export async function run(argv) {
|
|
25
|
+
const options = {
|
|
26
|
+
project: { type: 'string' },
|
|
27
|
+
game: { type: 'string' },
|
|
28
|
+
reason: { type: 'string' },
|
|
29
|
+
by: { type: 'string', default: 'mike' },
|
|
30
|
+
build: { type: 'string' },
|
|
31
|
+
json: { type: 'boolean', default: false },
|
|
32
|
+
};
|
|
23
33
|
const parsed = parseArgs({
|
|
24
34
|
args: argv,
|
|
25
|
-
options
|
|
26
|
-
project: { type: 'string' },
|
|
27
|
-
game: { type: 'string' },
|
|
28
|
-
reason: { type: 'string' },
|
|
29
|
-
by: { type: 'string', default: 'mike' },
|
|
30
|
-
build: { type: 'string' },
|
|
31
|
-
json: { type: 'boolean', default: false },
|
|
32
|
-
},
|
|
35
|
+
options,
|
|
33
36
|
allowPositionals: true,
|
|
34
37
|
strict: false,
|
|
35
38
|
});
|
|
39
|
+
// H4: see canon-build.js's comment — strict:false's leniency on unknown
|
|
40
|
+
// flags means a typo'd optional flag silently no-ops instead of erroring.
|
|
41
|
+
assertKnownOptions(parsed.values, options, { command: 'canon unfreeze' });
|
|
36
42
|
|
|
37
43
|
const entityIdArg = (parsed.positionals || [])[0];
|
|
38
44
|
if (!entityIdArg) {
|
|
@@ -48,7 +54,9 @@ export async function run(argv) {
|
|
|
48
54
|
);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
// SDL-H12: resolve via the hardened workspace-root-aware resolver, not
|
|
58
|
+
// process.cwd() (wrong base for a globally-installed sdlab).
|
|
59
|
+
const projectRoot = getProjectRoot(projectName);
|
|
52
60
|
const config = await loadBuildConfig(join(projectRoot, 'canon-build', 'config.json'), projectRoot);
|
|
53
61
|
const resolved = await findEntryFile(config, entityIdArg);
|
|
54
62
|
if (!resolved) {
|
package/scripts/card.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { writeFile } from 'node:fs/promises';
|
|
12
12
|
import { parseArgs, getProjectName } from '../lib/args.js';
|
|
13
|
-
import {
|
|
13
|
+
import { getProjectRoot } from '../lib/paths.js';
|
|
14
14
|
import { inputError, handleCliError } from '../lib/errors.js';
|
|
15
15
|
import { listSnapshots } from '../lib/snapshot.js';
|
|
16
16
|
import { listSplits } from '../lib/split.js';
|
|
@@ -27,7 +27,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
const projectName = flags.project || getProjectName(argv);
|
|
30
|
-
const projectRoot =
|
|
30
|
+
const projectRoot = getProjectRoot(projectName);
|
|
31
31
|
const subcommand = positionals[0] || 'generate';
|
|
32
32
|
|
|
33
33
|
if (subcommand === 'generate') {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Real-Service Assumption Audit (`scripts/colab/colab_window.sh`, `scripts/colab/launch_lane1_stub.sh`, `scripts/colab/launch_lane3_stub.sh`, `scripts/colab/remote_runner.py`, & `scripts/colab/launch_guards.py`)
|
|
2
|
+
|
|
3
|
+
This document audits every interaction the style-dataset-lab Colab kits (`scripts/colab/colab_window.sh`, `scripts/colab/launch_lane1_stub.sh`, `scripts/colab/launch_lane3_stub.sh`, `scripts/colab/remote_runner.py`, and `scripts/colab/launch_guards.py`) have with the `colab` CLI (`google-colab-cli` v0.7.4, captured in `tests/colab/fixtures/colab_help.txt` and `tests/colab/fixtures/colab-cli-contract.json`) and the remote Colab IPython runtime.
|
|
4
|
+
|
|
5
|
+
| # | Subcommand / Area | Help Line Relied On (`tests/colab/fixtures/colab_help.txt`) | Where Code Calls / Relies On It (`path:line`) | Fake Enforcement (`tests/colab/fixtures/fake_colab.py`) |
|
|
6
|
+
|---:|---|---|---|---|
|
|
7
|
+
| 1 | `usage` (text format, balance, and burn rate) | `Usage: colab usage [OPTIONS]` (`tests/colab/fixtures/colab_help.txt:143`); live output lines `Current balance: <float> compute units`, `Usage rate: <float>/hr` (or `/h`), `Active assignments: <int>`. | `scripts/colab/colab_window.sh:43-128`, `scripts/colab/launch_guards.py:122-177`, `scripts/colab/launch_guards.py:180-217`, `scripts/colab/launch_lane1_stub.sh:109`, `scripts/colab/launch_lane3_stub.sh:110` | **Enforced** (`tests/colab/fixtures/fake_colab.py:390-416`): rejects any positionals or unknown flags; prints the 3-line text format; supports `usage_fail_start`, `usage_fail_midrun`, and `cap_burn` modes. `launch_guards.py:122-177` parses with anchored regexes (`CURRENT_BALANCE_RE`, `USAGE_RATE_RE`), removes any `10.0` fallback, records `null` plus `usage_parse_error` in `window.json`, and refuses/stops with `COLAB_USAGE_PARSE_FAILED` (including before `colab new` if the initial read fails). |
|
|
8
|
+
| 2 | `new` (session open under lock) | `Usage: colab new [OPTIONS]` (`tests/colab/fixtures/colab_help.txt:54`); options `--session -s <str>`, `--gpu <str>`, `--high-mem` (`tests/colab/fixtures/colab_help.txt:59-67`). | `scripts/colab/colab_window.sh:292-327`, `scripts/colab/launch_lane1_stub.sh:125-126`, `scripts/colab/launch_lane3_stub.sh:126-127` | **Enforced** (`tests/colab/fixtures/fake_colab.py:424-441`): validates options against `colab-cli-contract.json`, rejects any positional arguments, requires `-s/--session`, and records whether `/tmp/colab-window-open.lock` was held. |
|
|
9
|
+
| 3 | `upload` (stage scripts and overrides to `/content/`) | `Usage: colab upload [OPTIONS] {local_path} {remote_path}` (`tests/colab/fixtures/colab_help.txt:95`); option `--session -s <str>` (`tests/colab/fixtures/colab_help.txt:104`). | `scripts/colab/launch_lane1_stub.sh:248-250`, `scripts/colab/launch_lane3_stub.sh:308-310` | **Enforced** (`tests/colab/fixtures/fake_colab.py:443-467`): requires `-s/--session` and exactly 2 positional arguments (`{local_path}` `{remote_path}`), verifies the local file exists and `{remote_path}` starts with `/content/`, and copies the file into the fake `/content` directory. |
|
|
10
|
+
| 4 | `exec` (execute local `.py` file via `-f` with `--timeout` and `--env`) | `Usage: colab exec [OPTIONS]` (`tests/colab/fixtures/colab_help.txt:80`); options `--session -s <str>`, `--file -f <str>`, `--timeout <float>`, `--env <str>` (`KEY=VALUE`, repeatable), `--output-image <str>` (`tests/colab/fixtures/colab_help.txt:85-90`). | `scripts/colab/colab_window.sh:181-282`, `scripts/colab/remote_runner.py:16-47`, `scripts/colab/launch_lane1_stub.sh:252-260`, `scripts/colab/launch_lane3_stub.sh:312-319` | **Enforced** (`tests/colab/fixtures/fake_colab.py:368-373` & `498-563`): refuses any positional command (`expected 0 positional arguments`), requires `-s/--session` and an existing `.py` file for `-f/--file`, validates `--timeout > 0` and `--env KEY=VALUE`, strips rig `PINNED_*` / `SDLAB_*` variables so only `--env` values reach the remote kernel, and executes the `.py` source with `cwd` set to the fake `/content` directory and `__file__` undefined. |
|
|
11
|
+
| 5 | `exec` (unreliable CLI exit code; marker and bundle verification) | Inside `colab exec -f`, `__file__` is undefined, `cwd` is `/content`, and the CLI exit code is not reliable on remote exceptions or `sys.exit`. | `scripts/colab/remote_runner.py:30-46`, `scripts/colab/colab_window.sh:259-272`, `scripts/colab/launch_lane1_stub.sh:263-267`, `scripts/colab/launch_lane3_stub.sh:322-326` | **Enforced** (`tests/colab/fixtures/fake_colab.py:559-561`): `FAKE_COLAB_FORCE_EXEC_RC_ZERO` defaults to `"1"` (returning `0` from `colab exec` even when the remote script exits non-zero), so `colab_window.sh` and the kits decide success strictly from the streamed `SDLAB_REMOTE_RC=<rc>` marker line and the downloaded non-empty result bundle. |
|
|
12
|
+
| 6 | `download` (fetch result bundle from `/content/` to rig) | `Usage: colab download [OPTIONS] {remote_path} {local_path}` (`tests/colab/fixtures/colab_help.txt:109`); option `--session -s <str>` (`tests/colab/fixtures/colab_help.txt:118`). | `scripts/colab/launch_lane1_stub.sh:263`, `scripts/colab/launch_lane3_stub.sh:322` | **Enforced** (`tests/colab/fixtures/fake_colab.py:469-486`): requires `-s/--session` and exactly 2 positional arguments (`{remote_path}` `{local_path}`), verifies `{remote_path}` is under `/content/`, and copies the artifact to `{local_path}`. |
|
|
13
|
+
| 7 | `stop` (close session in `EXIT` trap) | `Usage: colab stop [OPTIONS]` (`tests/colab/fixtures/colab_help.txt:123`); option `--session -s <str>` (`tests/colab/fixtures/colab_help.txt:128`). | `scripts/colab/colab_window.sh:284-290`, `scripts/colab/launch_lane1_stub.sh:93`, `scripts/colab/launch_lane3_stub.sh:94` | **Enforced** (`tests/colab/fixtures/fake_colab.py:488-496`): rejects any positional arguments, requires `-s/--session`, and logs `STOP -s <session>` to `stops.log`. |
|
|
14
|
+
| 8 | `sessions` (verify active sessions after stop) | `Usage: colab sessions [OPTIONS]` (`tests/colab/fixtures/colab_help.txt:71`); options `--help -h` (`tests/colab/fixtures/colab_help.txt:76`). | `scripts/colab/colab_window.sh:288` | **Enforced** (`tests/colab/fixtures/fake_colab.py:418-422`): rejects any positional arguments or unknown options and prints `[colab] No active sessions found on server.`. |
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# CPython 3.15.1 dependency override file for ostris/ai-toolkit (Lane 3 LoRA training)
|
|
2
|
+
# Upstream tracking issue: https://github.com/ostris/ai-toolkit/issues/1087
|
|
3
|
+
# Reference: DESIGN-v3.md section 3.5 (CPython 3.15.1 wheel audit)
|
|
4
|
+
# Note: av==16.0.1 and torchcodec==0.9.1 are dropped for image LoRA lanes.
|
|
5
|
+
scipy==1.18.1
|
|
6
|
+
matplotlib==3.11.2
|
|
7
|
+
torchao==0.18.0
|
|
8
|
+
safetensors==0.7.0
|
|
9
|
+
transformers==5.5.3
|
|
10
|
+
lycoris-lora==1.8.3
|
|
11
|
+
flatten_json==0.1.14
|
|
12
|
+
pyyaml==6.0.3
|
|
13
|
+
oyaml==1.0
|
|
14
|
+
tensorboard==2.20.0
|
|
15
|
+
kornia==0.9.0
|
|
16
|
+
invisible-watermark==0.2.0
|
|
17
|
+
einops==0.8.2
|
|
18
|
+
accelerate==1.13.0
|
|
19
|
+
toml==0.10.2
|
|
20
|
+
albumentations==1.4.15
|
|
21
|
+
albucore==0.0.16
|
|
22
|
+
pydantic==2.13.2
|
|
23
|
+
omegaconf==2.3.0
|
|
24
|
+
k-diffusion==0.1.1.post1
|
|
25
|
+
open_clip_torch==3.3.0
|
|
26
|
+
timm==1.0.22
|
|
27
|
+
prodigyopt==1.1.2
|
|
28
|
+
controlnet_aux==0.0.10
|
|
29
|
+
python-dotenv==1.2.2
|
|
30
|
+
bitsandbytes==0.49.2
|
|
31
|
+
hf_transfer==0.1.9
|
|
32
|
+
lpips==0.1.4
|
|
33
|
+
pytorch_fid==0.3.0
|
|
34
|
+
optimum-quanto==0.2.4
|
|
35
|
+
sentencepiece==0.2.1
|
|
36
|
+
huggingface_hub==1.10.1
|
|
37
|
+
peft==0.18.1
|
|
38
|
+
python-slugify==8.0.4
|
|
39
|
+
opencv-python==4.13.0.92
|
|
40
|
+
pytorch-wavelets==1.3.0
|
|
41
|
+
setuptools==82.0.1
|
|
42
|
+
librosa==0.11.0
|
|
43
|
+
mutagen==1.47.0
|
|
44
|
+
pillow==12.2.0
|
|
45
|
+
numpy==2.4.4
|