@simulatte/doppler 0.1.3 → 0.1.5
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/README.md +11 -5
- package/package.json +27 -4
- package/src/client/doppler-api.browser.d.ts +1 -0
- package/src/client/doppler-api.browser.js +288 -0
- package/src/client/doppler-api.d.ts +80 -0
- package/src/client/doppler-api.js +298 -0
- package/src/client/doppler-provider/types.js +1 -1
- package/src/client/doppler-registry.d.ts +23 -0
- package/src/client/doppler-registry.js +88 -0
- package/src/client/doppler-registry.json +39 -0
- package/src/config/execution-contract-check.d.ts +82 -0
- package/src/config/execution-contract-check.js +317 -0
- package/src/config/execution-v0-contract-check.d.ts +94 -0
- package/src/config/execution-v0-contract-check.js +251 -0
- package/src/config/execution-v0-graph-contract-check.d.ts +20 -0
- package/src/config/execution-v0-graph-contract-check.js +64 -0
- package/src/config/kernel-path-contract-check.d.ts +76 -0
- package/src/config/kernel-path-contract-check.js +479 -0
- package/src/config/kernel-path-loader.d.ts +16 -0
- package/src/config/kernel-path-loader.js +54 -0
- package/src/config/kernels/kernel-ref-digests.js +12 -0
- package/src/config/kernels/registry.json +556 -0
- package/src/config/loader.js +90 -67
- package/src/config/merge-contract-check.d.ts +16 -0
- package/src/config/merge-contract-check.js +321 -0
- package/src/config/merge-helpers.d.ts +58 -0
- package/src/config/merge-helpers.js +54 -0
- package/src/config/merge.js +3 -6
- package/src/config/presets/models/janus-text.json +27 -0
- package/src/config/quantization-contract-check.d.ts +12 -0
- package/src/config/quantization-contract-check.js +91 -0
- package/src/config/required-inference-fields-contract-check.d.ts +24 -0
- package/src/config/required-inference-fields-contract-check.js +231 -0
- package/src/config/schema/browser-suite-metrics.schema.d.ts +17 -0
- package/src/config/schema/browser-suite-metrics.schema.js +46 -0
- package/src/config/schema/conversion-report.schema.d.ts +40 -0
- package/src/config/schema/conversion-report.schema.js +108 -0
- package/src/config/schema/doppler.schema.js +12 -18
- package/src/config/schema/index.d.ts +22 -0
- package/src/config/schema/index.js +18 -0
- package/src/converter/core.d.ts +10 -0
- package/src/converter/core.js +49 -11
- package/src/converter/parsers/diffusion.js +63 -3
- package/src/converter/tokenizer-utils.js +17 -3
- package/src/formats/rdrr/validation.js +13 -0
- package/src/gpu/kernels/depthwise_conv2d.d.ts +29 -0
- package/src/gpu/kernels/depthwise_conv2d.js +98 -0
- package/src/gpu/kernels/depthwise_conv2d.wgsl +58 -0
- package/src/gpu/kernels/depthwise_conv2d_f16.wgsl +62 -0
- package/src/gpu/kernels/grouped_pointwise_conv2d.d.ts +27 -0
- package/src/gpu/kernels/grouped_pointwise_conv2d.js +92 -0
- package/src/gpu/kernels/grouped_pointwise_conv2d.wgsl +47 -0
- package/src/gpu/kernels/grouped_pointwise_conv2d_f16.wgsl +51 -0
- package/src/gpu/kernels/index.d.ts +30 -0
- package/src/gpu/kernels/index.js +25 -0
- package/src/gpu/kernels/relu.d.ts +18 -0
- package/src/gpu/kernels/relu.js +45 -0
- package/src/gpu/kernels/relu.wgsl +21 -0
- package/src/gpu/kernels/relu_f16.wgsl +23 -0
- package/src/gpu/kernels/repeat_channels.d.ts +21 -0
- package/src/gpu/kernels/repeat_channels.js +60 -0
- package/src/gpu/kernels/repeat_channels.wgsl +29 -0
- package/src/gpu/kernels/repeat_channels_f16.wgsl +31 -0
- package/src/gpu/kernels/sana_linear_attention.d.ts +27 -0
- package/src/gpu/kernels/sana_linear_attention.js +122 -0
- package/src/gpu/kernels/sana_linear_attention_apply.wgsl +44 -0
- package/src/gpu/kernels/sana_linear_attention_apply_f16.wgsl +47 -0
- package/src/gpu/kernels/sana_linear_attention_summary.wgsl +47 -0
- package/src/gpu/kernels/sana_linear_attention_summary_f16.wgsl +49 -0
- package/src/index-browser.d.ts +1 -0
- package/src/index-browser.js +2 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +2 -1
- package/src/inference/browser-harness.js +164 -38
- package/src/inference/pipelines/diffusion/init.js +14 -0
- package/src/inference/pipelines/diffusion/pipeline.js +206 -77
- package/src/inference/pipelines/diffusion/sana-transformer.d.ts +53 -0
- package/src/inference/pipelines/diffusion/sana-transformer.js +738 -0
- package/src/inference/pipelines/diffusion/scheduler.d.ts +17 -1
- package/src/inference/pipelines/diffusion/scheduler.js +91 -3
- package/src/inference/pipelines/diffusion/text-encoder-gpu.d.ts +6 -4
- package/src/inference/pipelines/diffusion/text-encoder-gpu.js +270 -0
- package/src/inference/pipelines/diffusion/text-encoder.js +18 -1
- package/src/inference/pipelines/diffusion/types.d.ts +4 -0
- package/src/inference/pipelines/diffusion/vae.js +782 -78
- package/src/inference/pipelines/text/config.d.ts +5 -0
- package/src/inference/pipelines/text/config.js +1 -1
- package/src/inference/pipelines/text/execution-v0.js +141 -101
- package/src/inference/pipelines/text/init.js +41 -10
- package/src/inference/pipelines/text.js +7 -1
- package/src/rules/execution-rules-contract-check.d.ts +17 -0
- package/src/rules/execution-rules-contract-check.js +245 -0
- package/src/rules/kernels/depthwise-conv2d.rules.json +6 -0
- package/src/rules/kernels/grouped-pointwise-conv2d.rules.json +6 -0
- package/src/rules/kernels/relu.rules.json +6 -0
- package/src/rules/kernels/repeat-channels.rules.json +6 -0
- package/src/rules/kernels/sana-linear-attention.rules.json +6 -0
- package/src/rules/layer-pattern-contract-check.d.ts +17 -0
- package/src/rules/layer-pattern-contract-check.js +231 -0
- package/src/rules/rule-registry.d.ts +28 -0
- package/src/rules/rule-registry.js +38 -0
- package/src/tooling/conversion-config-materializer.d.ts +24 -0
- package/src/tooling/conversion-config-materializer.js +99 -0
- package/src/tooling/lean-execution-contract-runner.d.ts +43 -0
- package/src/tooling/lean-execution-contract-runner.js +158 -0
- package/src/tooling/lean-execution-contract.d.ts +16 -0
- package/src/tooling/lean-execution-contract.js +81 -0
- package/src/tooling/node-convert.d.ts +10 -0
- package/src/tooling/node-converter.js +59 -0
- package/src/tooling/node-webgpu.js +30 -9
- package/src/version.d.ts +2 -0
- package/src/version.js +2 -0
- package/tools/convert-safetensors-node.js +47 -0
- package/tools/doppler-cli.js +167 -6
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { createConverterConfig } from '../config/schema/index.js';
|
|
4
|
+
import { resolveConversionPlan } from '../converter/conversion-plan.js';
|
|
5
|
+
|
|
6
|
+
function toSafeString(value) {
|
|
7
|
+
if (typeof value !== 'string') return '';
|
|
8
|
+
const trimmed = value.trim();
|
|
9
|
+
return trimmed || '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeQuantizationTag(value) {
|
|
13
|
+
const raw = toSafeString(value).toUpperCase();
|
|
14
|
+
if (!raw) return 'f16';
|
|
15
|
+
if (raw === 'Q4_K_M' || raw === 'Q4_K') return 'q4k';
|
|
16
|
+
return raw.toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveArchitectureHint(architecture) {
|
|
20
|
+
if (!architecture) return '';
|
|
21
|
+
if (typeof architecture === 'string') return architecture;
|
|
22
|
+
return (
|
|
23
|
+
toSafeString(architecture.id)
|
|
24
|
+
|| toSafeString(architecture.name)
|
|
25
|
+
|| toSafeString(architecture.type)
|
|
26
|
+
|| ''
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveHeadDim(architecture) {
|
|
31
|
+
const headDim = Number(architecture?.headDim ?? architecture?.head_dim);
|
|
32
|
+
return Number.isFinite(headDim) && headDim > 0 ? headDim : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function extractSourceQuantization(manifest) {
|
|
36
|
+
const explicitWeights = toSafeString(manifest?.quantizationInfo?.weights);
|
|
37
|
+
if (explicitWeights) return explicitWeights;
|
|
38
|
+
const explicitQuant = toSafeString(manifest?.quantization);
|
|
39
|
+
if (explicitQuant) return explicitQuant;
|
|
40
|
+
return 'f16';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildRefreshRawConfig(manifest) {
|
|
44
|
+
const baseConfig = (manifest?.config && typeof manifest.config === 'object')
|
|
45
|
+
? { ...manifest.config }
|
|
46
|
+
: {};
|
|
47
|
+
const manifestLayerTypes = manifest?.inference?.layerPattern?.layerTypes;
|
|
48
|
+
if (Array.isArray(manifestLayerTypes) && manifestLayerTypes.length > 0) {
|
|
49
|
+
return {
|
|
50
|
+
...baseConfig,
|
|
51
|
+
layer_types: [...manifestLayerTypes],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return baseConfig;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function extractTensorEntriesFromManifest(manifest) {
|
|
58
|
+
if (!(manifest?.tensors && typeof manifest.tensors === 'object' && !Array.isArray(manifest.tensors))) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
return Object.entries(manifest.tensors).map(([name, tensor]) => ({
|
|
62
|
+
name,
|
|
63
|
+
dtype: tensor?.dtype ?? null,
|
|
64
|
+
shape: tensor?.shape ?? null,
|
|
65
|
+
role: tensor?.role ?? null,
|
|
66
|
+
layout: tensor?.layout ?? null,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function resolveMaterializedManifestFromConversionConfig(conversionConfigInput, manifest) {
|
|
71
|
+
const converterConfig = createConverterConfig(conversionConfigInput);
|
|
72
|
+
const tensorEntries = extractTensorEntriesFromManifest(manifest);
|
|
73
|
+
const architecture = manifest?.architecture && typeof manifest.architecture === 'object'
|
|
74
|
+
? manifest.architecture
|
|
75
|
+
: null;
|
|
76
|
+
const plan = resolveConversionPlan({
|
|
77
|
+
rawConfig: buildRefreshRawConfig(manifest),
|
|
78
|
+
tensors: tensorEntries,
|
|
79
|
+
converterConfig,
|
|
80
|
+
sourceQuantization: normalizeQuantizationTag(extractSourceQuantization(manifest)),
|
|
81
|
+
modelKind: manifest?.modelType === 'diffusion' ? 'diffusion' : 'transformer',
|
|
82
|
+
architectureHint: resolveArchitectureHint(manifest?.architecture),
|
|
83
|
+
architectureConfig: architecture,
|
|
84
|
+
headDim: resolveHeadDim(architecture),
|
|
85
|
+
presetOverride: converterConfig?.presets?.model || manifest?.inference?.presetId || null,
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
modelId: manifest?.modelId ?? converterConfig?.output?.modelBaseId ?? 'unknown',
|
|
89
|
+
modelType: manifest?.modelType ?? plan?.modelType ?? 'transformer',
|
|
90
|
+
architecture: manifest?.architecture ?? null,
|
|
91
|
+
inference: plan?.manifestInference ?? null,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function inferConversionConfigModelId(configPath, conversionConfigInput) {
|
|
96
|
+
const configuredId = toSafeString(conversionConfigInput?.output?.modelBaseId);
|
|
97
|
+
if (configuredId) return configuredId;
|
|
98
|
+
return path.basename(String(configPath), path.extname(String(configPath)));
|
|
99
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface LeanExecutionContractRunResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
toolchainRef: string | null;
|
|
4
|
+
generatedPath: string;
|
|
5
|
+
moduleName: string;
|
|
6
|
+
facts: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare function resolveLeanBinary(): string;
|
|
10
|
+
|
|
11
|
+
export declare function runLeanCheck(options: {
|
|
12
|
+
sourcePath: string;
|
|
13
|
+
rootDir: string;
|
|
14
|
+
}): {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
toolchainRef: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export declare function writeExecutionContractLeanModuleForManifest(
|
|
20
|
+
manifest: Record<string, unknown>,
|
|
21
|
+
options?: {
|
|
22
|
+
rootDir?: string;
|
|
23
|
+
moduleName?: string;
|
|
24
|
+
emitPath?: string | null;
|
|
25
|
+
}
|
|
26
|
+
): {
|
|
27
|
+
rootDir: string;
|
|
28
|
+
facts: Record<string, unknown>;
|
|
29
|
+
moduleName: string;
|
|
30
|
+
source: string;
|
|
31
|
+
generatedPath: string;
|
|
32
|
+
tempDir: string | null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export declare function runLeanExecutionContractForManifest(
|
|
36
|
+
manifest: Record<string, unknown>,
|
|
37
|
+
options?: {
|
|
38
|
+
rootDir?: string;
|
|
39
|
+
moduleName?: string;
|
|
40
|
+
emitPath?: string | null;
|
|
41
|
+
check?: boolean;
|
|
42
|
+
}
|
|
43
|
+
): LeanExecutionContractRunResult;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
extractExecutionContractFacts,
|
|
8
|
+
renderExecutionContractLeanModule,
|
|
9
|
+
sanitizeLeanModuleName,
|
|
10
|
+
} from './lean-execution-contract.js';
|
|
11
|
+
|
|
12
|
+
export function resolveLeanBinary() {
|
|
13
|
+
const elanLean = path.join(os.homedir(), '.elan', 'bin', 'lean');
|
|
14
|
+
if (fs.existsSync(elanLean)) {
|
|
15
|
+
return elanLean;
|
|
16
|
+
}
|
|
17
|
+
const probe = spawnSync('bash', ['-lc', 'command -v lean'], { encoding: 'utf8' });
|
|
18
|
+
if (probe.status === 0) {
|
|
19
|
+
const resolved = probe.stdout.trim();
|
|
20
|
+
if (resolved) {
|
|
21
|
+
return resolved;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error('lean binary not found. Install Lean with elan first.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runLeanCommand({ leanBin, toolchainRef, buildDir, rootDir, sourcePath, outputPath }) {
|
|
28
|
+
const result = spawnSync(
|
|
29
|
+
leanBin,
|
|
30
|
+
[`+${toolchainRef}`, '-o', outputPath, sourcePath],
|
|
31
|
+
{
|
|
32
|
+
cwd: rootDir,
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
LEAN_PATH: `${buildDir}:${path.join(rootDir, 'lean')}`,
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
if (result.stdout) {
|
|
41
|
+
process.stdout.write(result.stdout);
|
|
42
|
+
}
|
|
43
|
+
if (result.stderr) {
|
|
44
|
+
process.stderr.write(result.stderr);
|
|
45
|
+
}
|
|
46
|
+
if (result.status !== 0) {
|
|
47
|
+
throw new Error(`lean exited with status ${result.status}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function runLeanCheck({ sourcePath, rootDir }) {
|
|
52
|
+
const toolchainVersion = process.env.DOPPLER_LEAN_VERSION ?? '4.16.0';
|
|
53
|
+
const toolchainRef = toolchainVersion.startsWith('v')
|
|
54
|
+
? `leanprover/lean4:${toolchainVersion}`
|
|
55
|
+
: `leanprover/lean4:v${toolchainVersion}`;
|
|
56
|
+
const leanBin = resolveLeanBinary();
|
|
57
|
+
const buildDir = fs.mkdtempSync(path.join(os.tmpdir(), 'doppler-lean-execution-contract-'));
|
|
58
|
+
try {
|
|
59
|
+
fs.mkdirSync(path.join(buildDir, 'Doppler'), { recursive: true });
|
|
60
|
+
runLeanCommand({
|
|
61
|
+
leanBin,
|
|
62
|
+
toolchainRef,
|
|
63
|
+
buildDir,
|
|
64
|
+
rootDir,
|
|
65
|
+
sourcePath: path.join(rootDir, 'lean', 'Doppler', 'Model.lean'),
|
|
66
|
+
outputPath: path.join(buildDir, 'Doppler', 'Model.olean'),
|
|
67
|
+
});
|
|
68
|
+
runLeanCommand({
|
|
69
|
+
leanBin,
|
|
70
|
+
toolchainRef,
|
|
71
|
+
buildDir,
|
|
72
|
+
rootDir,
|
|
73
|
+
sourcePath: path.join(rootDir, 'lean', 'Doppler', 'ExecutionContract.lean'),
|
|
74
|
+
outputPath: path.join(buildDir, 'Doppler', 'ExecutionContract.olean'),
|
|
75
|
+
});
|
|
76
|
+
const generatedOutput = path.join(buildDir, 'GeneratedExecutionContractCheck.olean');
|
|
77
|
+
const result = spawnSync(
|
|
78
|
+
leanBin,
|
|
79
|
+
[`+${toolchainRef}`, '-o', generatedOutput, sourcePath],
|
|
80
|
+
{
|
|
81
|
+
cwd: rootDir,
|
|
82
|
+
encoding: 'utf8',
|
|
83
|
+
env: {
|
|
84
|
+
...process.env,
|
|
85
|
+
LEAN_PATH: `${buildDir}:${path.join(rootDir, 'lean')}`,
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
if (result.stdout) {
|
|
90
|
+
process.stdout.write(result.stdout);
|
|
91
|
+
}
|
|
92
|
+
if (result.stderr) {
|
|
93
|
+
process.stderr.write(result.stderr);
|
|
94
|
+
}
|
|
95
|
+
if (result.status !== 0) {
|
|
96
|
+
throw new Error(`lean exited with status ${result.status}`);
|
|
97
|
+
}
|
|
98
|
+
const overallMatch = result.stdout.match(/executionContractOverall:(pass|fail)/);
|
|
99
|
+
if (!overallMatch) {
|
|
100
|
+
throw new Error('unable to parse executionContractOverall from Lean output.');
|
|
101
|
+
}
|
|
102
|
+
return { ok: overallMatch[1] === 'pass', toolchainRef };
|
|
103
|
+
} finally {
|
|
104
|
+
fs.rmSync(buildDir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function writeExecutionContractLeanModuleForManifest(manifest, options = {}) {
|
|
109
|
+
const rootDir = path.resolve(String(options.rootDir ?? process.cwd()));
|
|
110
|
+
const facts = extractExecutionContractFacts(manifest);
|
|
111
|
+
const moduleName = sanitizeLeanModuleName(options.moduleName ?? `${facts.modelId}_ExecutionContractCheck`);
|
|
112
|
+
const source = renderExecutionContractLeanModule(facts, { moduleName });
|
|
113
|
+
const tempDir = options.emitPath
|
|
114
|
+
? null
|
|
115
|
+
: fs.mkdtempSync(path.join(rootDir, 'lean', '.generated-'));
|
|
116
|
+
const generatedPath = options.emitPath
|
|
117
|
+
? path.resolve(rootDir, String(options.emitPath))
|
|
118
|
+
: path.join(tempDir, `${moduleName}.lean`);
|
|
119
|
+
fs.mkdirSync(path.dirname(generatedPath), { recursive: true });
|
|
120
|
+
fs.writeFileSync(generatedPath, source);
|
|
121
|
+
return {
|
|
122
|
+
rootDir,
|
|
123
|
+
facts,
|
|
124
|
+
moduleName,
|
|
125
|
+
source,
|
|
126
|
+
generatedPath,
|
|
127
|
+
tempDir,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function runLeanExecutionContractForManifest(manifest, options = {}) {
|
|
132
|
+
const generated = writeExecutionContractLeanModuleForManifest(manifest, options);
|
|
133
|
+
try {
|
|
134
|
+
if (options.check === false) {
|
|
135
|
+
return {
|
|
136
|
+
ok: true,
|
|
137
|
+
toolchainRef: null,
|
|
138
|
+
generatedPath: generated.generatedPath,
|
|
139
|
+
moduleName: generated.moduleName,
|
|
140
|
+
facts: generated.facts,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const result = runLeanCheck({
|
|
144
|
+
sourcePath: generated.generatedPath,
|
|
145
|
+
rootDir: generated.rootDir,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
...result,
|
|
149
|
+
generatedPath: generated.generatedPath,
|
|
150
|
+
moduleName: generated.moduleName,
|
|
151
|
+
facts: generated.facts,
|
|
152
|
+
};
|
|
153
|
+
} finally {
|
|
154
|
+
if (!options.emitPath && generated.tempDir) {
|
|
155
|
+
fs.rmSync(generated.tempDir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExecutionContractFacts } from '../config/execution-contract-check.js';
|
|
2
|
+
|
|
3
|
+
export interface RenderLeanExecutionContractOptions {
|
|
4
|
+
moduleName?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export declare function sanitizeLeanModuleName(value: unknown): string;
|
|
8
|
+
|
|
9
|
+
export declare function extractExecutionContractFacts(
|
|
10
|
+
manifest: Record<string, unknown>
|
|
11
|
+
): ExecutionContractFacts;
|
|
12
|
+
|
|
13
|
+
export declare function renderExecutionContractLeanModule(
|
|
14
|
+
facts: LeanExecutionContractFacts,
|
|
15
|
+
options?: RenderLeanExecutionContractOptions
|
|
16
|
+
): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractExecutionContractFacts,
|
|
3
|
+
sanitizeLeanModuleName,
|
|
4
|
+
} from '../config/execution-contract-check.js';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
extractExecutionContractFacts,
|
|
8
|
+
sanitizeLeanModuleName,
|
|
9
|
+
} from '../config/execution-contract-check.js';
|
|
10
|
+
|
|
11
|
+
function asLeanString(value) {
|
|
12
|
+
return JSON.stringify(String(value));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function renderExecutionStep(step) {
|
|
16
|
+
return [
|
|
17
|
+
' {',
|
|
18
|
+
` id := ${asLeanString(step.id)},`,
|
|
19
|
+
` phase := .${step.phase},`,
|
|
20
|
+
` opClass := .${step.opClass},`,
|
|
21
|
+
' }',
|
|
22
|
+
].join('\n');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderCheckName(modelId, suffix) {
|
|
26
|
+
return `${modelId}.${suffix}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function renderExecutionContractLeanModule(facts, options = {}) {
|
|
30
|
+
const moduleName = sanitizeLeanModuleName(options.moduleName ?? facts?.modelId ?? 'GeneratedExecutionContractCheck');
|
|
31
|
+
const modelId = String(facts?.modelId ?? 'model').trim() || 'model';
|
|
32
|
+
const session = facts?.session;
|
|
33
|
+
const steps = Array.isArray(facts?.steps) ? facts.steps : [];
|
|
34
|
+
if (!session || typeof session !== 'object') {
|
|
35
|
+
throw new Error('lean execution contract: facts.session is required.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const renderedSteps = steps.length > 0
|
|
39
|
+
? steps.map(renderExecutionStep).join(',\n')
|
|
40
|
+
: '';
|
|
41
|
+
const stepsLiteral = steps.length > 0
|
|
42
|
+
? `[\n${renderedSteps}\n]`
|
|
43
|
+
: '[]';
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
'import Doppler.ExecutionContract',
|
|
47
|
+
'',
|
|
48
|
+
`def extractedModelId : String := ${asLeanString(modelId)}`,
|
|
49
|
+
'',
|
|
50
|
+
'def extractedSession : SessionConfig := {',
|
|
51
|
+
` layout := .${session.layout},`,
|
|
52
|
+
` disableCommandBatching := ${session.disableCommandBatching ? 'true' : 'false'},`,
|
|
53
|
+
` decodeBatchSize := ${session.decodeBatchSize},`,
|
|
54
|
+
` headDim := ${session.headDim},`,
|
|
55
|
+
` kvLen := ${session.kvLen},`,
|
|
56
|
+
` coldQuantMode := .${session.coldQuantMode},`,
|
|
57
|
+
'}',
|
|
58
|
+
'',
|
|
59
|
+
`def extractedSteps : List ExecutionStep := ${stepsLiteral}`,
|
|
60
|
+
'',
|
|
61
|
+
'def executionContractChecks : List (String × Bool) := [',
|
|
62
|
+
` (${asLeanString(renderCheckName(modelId, 'steps'))}, allStepsCompatible extractedSteps extractedSession),`,
|
|
63
|
+
` (${asLeanString(renderCheckName(modelId, 'session'))}, sessionConsistent extractedSession)`,
|
|
64
|
+
']',
|
|
65
|
+
'',
|
|
66
|
+
'def renderCheck (entry : String × Bool) : String :=',
|
|
67
|
+
' let status := if entry.2 then "pass" else "fail"',
|
|
68
|
+
' s!"{entry.1}: {status}"',
|
|
69
|
+
'',
|
|
70
|
+
'def renderedChecks : List String :=',
|
|
71
|
+
' executionContractChecks.map renderCheck',
|
|
72
|
+
'',
|
|
73
|
+
'def executionContractOverall : Bool :=',
|
|
74
|
+
' executionContractChecks.all (fun entry => entry.2)',
|
|
75
|
+
'',
|
|
76
|
+
'#eval s!"executionContractModule:' + moduleName + '"',
|
|
77
|
+
'#eval s!"executionContractOverall:{if executionContractOverall then "pass" else "fail"}"',
|
|
78
|
+
'#eval renderedChecks',
|
|
79
|
+
'',
|
|
80
|
+
].join('\n');
|
|
81
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ConverterConfigSchema } from '../config/schema/converter.schema.js';
|
|
2
|
+
import type { ExecutionContractArtifact } from '../config/execution-contract-check.js';
|
|
3
|
+
import type { ExecutionV0GraphContractArtifact } from '../config/execution-v0-graph-contract-check.js';
|
|
4
|
+
import type { ManifestRequiredInferenceFieldsArtifact } from '../config/required-inference-fields-contract-check.js';
|
|
5
|
+
import type { SavedReportInfo } from '../storage/reports.js';
|
|
2
6
|
|
|
3
7
|
export interface NodeConvertProgress {
|
|
4
8
|
stage: string | null;
|
|
@@ -34,6 +38,12 @@ export interface ConvertSafetensorsDirectoryResult {
|
|
|
34
38
|
manifest: Record<string, unknown>;
|
|
35
39
|
shardCount: number;
|
|
36
40
|
tensorCount: number;
|
|
41
|
+
executionContractArtifact: ExecutionContractArtifact | null;
|
|
42
|
+
executionV0GraphContractArtifact: ExecutionV0GraphContractArtifact | null;
|
|
43
|
+
layerPatternContractArtifact: Record<string, unknown> | null;
|
|
44
|
+
requiredInferenceFieldsArtifact: ManifestRequiredInferenceFieldsArtifact | null;
|
|
45
|
+
report: Record<string, unknown>;
|
|
46
|
+
reportInfo: SavedReportInfo;
|
|
37
47
|
presetId: string;
|
|
38
48
|
modelType: string;
|
|
39
49
|
outputDir: string;
|
|
@@ -7,6 +7,11 @@ import { bootstrapNodeWebGPU } from './node-webgpu.js';
|
|
|
7
7
|
import { isPlainObject } from '../utils/plain-object.js';
|
|
8
8
|
import { selectRuleValue } from '../rules/rule-registry.js';
|
|
9
9
|
import { log, trace } from '../debug/index.js';
|
|
10
|
+
import { saveReport } from '../storage/reports.js';
|
|
11
|
+
import {
|
|
12
|
+
CONVERSION_REPORT_SCHEMA_VERSION,
|
|
13
|
+
validateConversionReport,
|
|
14
|
+
} from '../config/schema/conversion-report.schema.js';
|
|
10
15
|
|
|
11
16
|
function asPositiveInteger(value, label) {
|
|
12
17
|
if (!Number.isInteger(value) || value < 1) {
|
|
@@ -594,6 +599,44 @@ function normalizeTokenizerManifest(manifest) {
|
|
|
594
599
|
return manifest;
|
|
595
600
|
}
|
|
596
601
|
|
|
602
|
+
function buildConvertReport(result, context) {
|
|
603
|
+
const manifest = result?.manifest ?? null;
|
|
604
|
+
const inference = manifest?.inference && typeof manifest.inference === 'object'
|
|
605
|
+
? manifest.inference
|
|
606
|
+
: null;
|
|
607
|
+
return validateConversionReport({
|
|
608
|
+
schemaVersion: CONVERSION_REPORT_SCHEMA_VERSION,
|
|
609
|
+
suite: 'convert',
|
|
610
|
+
command: 'convert',
|
|
611
|
+
modelId: manifest?.modelId ?? context.modelId ?? 'unknown',
|
|
612
|
+
timestamp: manifest?.metadata?.convertedAt ?? new Date().toISOString(),
|
|
613
|
+
source: 'doppler',
|
|
614
|
+
result: {
|
|
615
|
+
presetId: context.presetId ?? null,
|
|
616
|
+
modelType: context.modelType ?? null,
|
|
617
|
+
outputDir: context.outputDir ?? null,
|
|
618
|
+
shardCount: result?.shardCount ?? null,
|
|
619
|
+
tensorCount: result?.tensorCount ?? null,
|
|
620
|
+
totalSize: result?.totalSize ?? null,
|
|
621
|
+
},
|
|
622
|
+
manifest: manifest
|
|
623
|
+
? {
|
|
624
|
+
quantization: manifest.quantization ?? null,
|
|
625
|
+
quantizationInfo: manifest.quantizationInfo ?? null,
|
|
626
|
+
inference: {
|
|
627
|
+
presetId: inference?.presetId ?? null,
|
|
628
|
+
schema: inference?.schema ?? null,
|
|
629
|
+
defaultKernelPath: inference?.defaultKernelPath ?? null,
|
|
630
|
+
},
|
|
631
|
+
}
|
|
632
|
+
: null,
|
|
633
|
+
executionContractArtifact: result?.executionContractArtifact ?? null,
|
|
634
|
+
executionV0GraphContractArtifact: result?.executionV0GraphContractArtifact ?? null,
|
|
635
|
+
layerPatternContractArtifact: result?.layerPatternContractArtifact ?? null,
|
|
636
|
+
requiredInferenceFieldsArtifact: result?.requiredInferenceFieldsArtifact ?? null,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
597
640
|
function createNodeTensorTransformer(options) {
|
|
598
641
|
const pool = options?.pool;
|
|
599
642
|
const execution = options?.execution;
|
|
@@ -1212,10 +1255,26 @@ export async function convertSafetensorsDirectory(options) {
|
|
|
1212
1255
|
normalizeTokenizerManifest(result.manifest);
|
|
1213
1256
|
await io.writeManifest(result.manifest);
|
|
1214
1257
|
|
|
1258
|
+
const report = buildConvertReport(result, {
|
|
1259
|
+
presetId,
|
|
1260
|
+
modelType: resolvedModelType,
|
|
1261
|
+
outputDir,
|
|
1262
|
+
modelId: result.manifest?.modelId ?? modelId,
|
|
1263
|
+
});
|
|
1264
|
+
const reportInfo = await saveReport(report.modelId, report, {
|
|
1265
|
+
timestamp: report.timestamp,
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1215
1268
|
return {
|
|
1216
1269
|
manifest: result.manifest,
|
|
1217
1270
|
shardCount: result.shardCount,
|
|
1218
1271
|
tensorCount: result.tensorCount,
|
|
1272
|
+
executionContractArtifact: result.executionContractArtifact ?? null,
|
|
1273
|
+
executionV0GraphContractArtifact: result.executionV0GraphContractArtifact ?? null,
|
|
1274
|
+
layerPatternContractArtifact: result.layerPatternContractArtifact ?? null,
|
|
1275
|
+
requiredInferenceFieldsArtifact: result.requiredInferenceFieldsArtifact ?? null,
|
|
1276
|
+
report,
|
|
1277
|
+
reportInfo,
|
|
1219
1278
|
presetId,
|
|
1220
1279
|
modelType: resolvedModelType,
|
|
1221
1280
|
outputDir,
|
|
@@ -11,8 +11,9 @@ const DEFAULT_LOCAL_DOE_PROVIDER_PATH = resolve(
|
|
|
11
11
|
'..',
|
|
12
12
|
'fawn',
|
|
13
13
|
'nursery',
|
|
14
|
-
'webgpu-
|
|
14
|
+
'webgpu-doe',
|
|
15
15
|
);
|
|
16
|
+
const DEFAULT_DOE_PROVIDER_CREATE_ARGS = 'enable-dawn-features=allow_unsafe_apis';
|
|
16
17
|
|
|
17
18
|
function hasNavigatorGpu() {
|
|
18
19
|
return typeof globalThis.navigator !== 'undefined' && !!globalThis.navigator?.gpu;
|
|
@@ -60,7 +61,7 @@ function resolveCandidateModuleSpecifier(candidate) {
|
|
|
60
61
|
function resolveDefaultWebgpuModuleSpecifiers() {
|
|
61
62
|
const specifiers = [];
|
|
62
63
|
const localCandidates = [
|
|
63
|
-
resolve(process.cwd(), '..', 'fawn', 'nursery', 'webgpu-
|
|
64
|
+
resolve(process.cwd(), '..', 'fawn', 'nursery', 'webgpu-doe'),
|
|
64
65
|
DEFAULT_LOCAL_DOE_PROVIDER_PATH,
|
|
65
66
|
];
|
|
66
67
|
for (const localCandidate of localCandidates) {
|
|
@@ -69,7 +70,7 @@ function resolveDefaultWebgpuModuleSpecifiers() {
|
|
|
69
70
|
specifiers.push(pathToFileURL(resolvedPath).href);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
specifiers.push('@
|
|
73
|
+
specifiers.push('@simulatte/webgpu-doe');
|
|
73
74
|
specifiers.push('webgpu');
|
|
74
75
|
return [...new Set(specifiers)];
|
|
75
76
|
}
|
|
@@ -102,17 +103,17 @@ function resolveWorkspaceWebgpuProviderPath() {
|
|
|
102
103
|
return null;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
function
|
|
106
|
-
if (specifier === '@
|
|
106
|
+
function isDoeWebgpuSpecifier(specifier) {
|
|
107
|
+
if (specifier === '@simulatte/webgpu-doe') {
|
|
107
108
|
return true;
|
|
108
109
|
}
|
|
109
110
|
if (typeof specifier !== 'string') {
|
|
110
111
|
return false;
|
|
111
112
|
}
|
|
112
|
-
if (specifier.includes('/webgpu-
|
|
113
|
+
if (specifier.includes('/webgpu-doe/')) {
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
115
|
-
return specifier.includes('webgpu-
|
|
116
|
+
return specifier.includes('webgpu-doe') && specifier.startsWith('file://');
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
function resolveDoeProviderOverride(specifier) {
|
|
@@ -120,7 +121,7 @@ function resolveDoeProviderOverride(specifier) {
|
|
|
120
121
|
if (typeof explicitProvider === 'string' && explicitProvider.trim().length > 0) {
|
|
121
122
|
return null;
|
|
122
123
|
}
|
|
123
|
-
if (!
|
|
124
|
+
if (!isDoeWebgpuSpecifier(specifier)) {
|
|
124
125
|
return null;
|
|
125
126
|
}
|
|
126
127
|
return resolveWorkspaceWebgpuProviderPath();
|
|
@@ -128,12 +129,27 @@ function resolveDoeProviderOverride(specifier) {
|
|
|
128
129
|
|
|
129
130
|
async function importWithProviderOverride(specifier) {
|
|
130
131
|
const providerOverride = resolveDoeProviderOverride(specifier);
|
|
132
|
+
const shouldApplyCreateArgsDefault = isDoeWebgpuSpecifier(specifier)
|
|
133
|
+
&& !(typeof process.env.FAWN_WEBGPU_CREATE_ARGS === 'string' && process.env.FAWN_WEBGPU_CREATE_ARGS.trim().length > 0);
|
|
131
134
|
if (!providerOverride) {
|
|
132
|
-
|
|
135
|
+
if (!shouldApplyCreateArgsDefault) {
|
|
136
|
+
return import(specifier);
|
|
137
|
+
}
|
|
138
|
+
process.env.FAWN_WEBGPU_CREATE_ARGS = DEFAULT_DOE_PROVIDER_CREATE_ARGS;
|
|
139
|
+
try {
|
|
140
|
+
return await import(specifier);
|
|
141
|
+
} finally {
|
|
142
|
+
delete process.env.FAWN_WEBGPU_CREATE_ARGS;
|
|
143
|
+
}
|
|
133
144
|
}
|
|
134
145
|
const hadProvider = Object.prototype.hasOwnProperty.call(process.env, 'FAWN_WEBGPU_NODE_PROVIDER_MODULE');
|
|
135
146
|
const previousProvider = process.env.FAWN_WEBGPU_NODE_PROVIDER_MODULE;
|
|
147
|
+
const hadCreateArgs = Object.prototype.hasOwnProperty.call(process.env, 'FAWN_WEBGPU_CREATE_ARGS');
|
|
148
|
+
const previousCreateArgs = process.env.FAWN_WEBGPU_CREATE_ARGS;
|
|
136
149
|
process.env.FAWN_WEBGPU_NODE_PROVIDER_MODULE = providerOverride;
|
|
150
|
+
if (!hadCreateArgs) {
|
|
151
|
+
process.env.FAWN_WEBGPU_CREATE_ARGS = DEFAULT_DOE_PROVIDER_CREATE_ARGS;
|
|
152
|
+
}
|
|
137
153
|
try {
|
|
138
154
|
return await import(specifier);
|
|
139
155
|
} finally {
|
|
@@ -142,6 +158,11 @@ async function importWithProviderOverride(specifier) {
|
|
|
142
158
|
} else {
|
|
143
159
|
delete process.env.FAWN_WEBGPU_NODE_PROVIDER_MODULE;
|
|
144
160
|
}
|
|
161
|
+
if (hadCreateArgs) {
|
|
162
|
+
process.env.FAWN_WEBGPU_CREATE_ARGS = previousCreateArgs;
|
|
163
|
+
} else {
|
|
164
|
+
delete process.env.FAWN_WEBGPU_CREATE_ARGS;
|
|
165
|
+
}
|
|
145
166
|
}
|
|
146
167
|
}
|
|
147
168
|
|
package/src/version.d.ts
ADDED
package/src/version.js
ADDED
|
@@ -126,6 +126,51 @@ function normalizeExecutionConfig(rawExecution) {
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
function printConvertContractSummary(result) {
|
|
130
|
+
const artifact = result?.executionContractArtifact;
|
|
131
|
+
if (!artifact || typeof artifact !== 'object') return;
|
|
132
|
+
const checks = Array.isArray(artifact.checks) ? artifact.checks : [];
|
|
133
|
+
const passedChecks = checks.filter((entry) => entry?.ok === true).length;
|
|
134
|
+
const layout = artifact.session?.layout ?? 'n/a';
|
|
135
|
+
console.log(
|
|
136
|
+
`[contract] status=${artifact.ok === true ? 'pass' : 'fail'} ` +
|
|
137
|
+
`checks=${checks.length > 0 ? `${passedChecks}/${checks.length}` : 'n/a'} layout=${layout}`
|
|
138
|
+
);
|
|
139
|
+
if (artifact.ok !== true && Array.isArray(artifact.errors)) {
|
|
140
|
+
for (const error of artifact.errors.slice(0, 3)) {
|
|
141
|
+
console.log(`[contract] error=${String(error)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const graphArtifact = result?.executionV0GraphContractArtifact;
|
|
145
|
+
if (graphArtifact && typeof graphArtifact === 'object') {
|
|
146
|
+
const checks = Array.isArray(graphArtifact.checks) ? graphArtifact.checks : [];
|
|
147
|
+
const passedChecks = checks.filter((entry) => entry?.ok === true).length;
|
|
148
|
+
console.log(
|
|
149
|
+
`[graph] status=${graphArtifact.ok === true ? 'pass' : 'fail'} ` +
|
|
150
|
+
`checks=${checks.length > 0 ? `${passedChecks}/${checks.length}` : 'n/a'}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
for (const [label, extraArtifact] of [
|
|
154
|
+
['layer-pattern', result?.layerPatternContractArtifact],
|
|
155
|
+
['required-inference', result?.requiredInferenceFieldsArtifact],
|
|
156
|
+
]) {
|
|
157
|
+
if (!extraArtifact || typeof extraArtifact !== 'object') continue;
|
|
158
|
+
const checks = Array.isArray(extraArtifact.checks) ? extraArtifact.checks : [];
|
|
159
|
+
const passedChecks = checks.filter((entry) => entry?.ok === true).length;
|
|
160
|
+
console.log(
|
|
161
|
+
`[${label}] status=${extraArtifact.ok === true ? 'pass' : 'fail'} ` +
|
|
162
|
+
`checks=${checks.length > 0 ? `${passedChecks}/${checks.length}` : 'n/a'}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function printConvertReportSummary(result) {
|
|
168
|
+
const reportInfo = result?.reportInfo;
|
|
169
|
+
if (!reportInfo || typeof reportInfo !== 'object') return;
|
|
170
|
+
if (typeof reportInfo.path !== 'string' || reportInfo.path.length === 0) return;
|
|
171
|
+
console.log(`[report] ${reportInfo.path}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
129
174
|
async function readJsonFile(filePath) {
|
|
130
175
|
if (!filePath) return null;
|
|
131
176
|
const raw = await fs.readFile(filePath, 'utf8');
|
|
@@ -172,6 +217,8 @@ async function main() {
|
|
|
172
217
|
console.log(
|
|
173
218
|
`[done] modelId=${result.manifest?.modelId ?? 'unknown'} preset=${result.presetId} modelType=${result.modelType} shards=${result.shardCount} tensors=${result.tensorCount}`
|
|
174
219
|
);
|
|
220
|
+
printConvertContractSummary(result);
|
|
221
|
+
printConvertReportSummary(result);
|
|
175
222
|
}
|
|
176
223
|
|
|
177
224
|
main().catch((err) => {
|