@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
package/src/config/loader.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from './schema/index.js';
|
|
6
6
|
import { createDopplerError, ERROR_CODES } from '../errors/index.js';
|
|
7
7
|
import { loadJson } from '../utils/load-json.js';
|
|
8
|
+
import { chooseNullish, mergeLayeredShallowObjects } from './merge-helpers.js';
|
|
8
9
|
|
|
9
10
|
// Static imports keep presets bundled for browser use.
|
|
10
11
|
const transformerPreset = await loadJson('./presets/models/transformer.json', import.meta.url, 'Failed to load preset');
|
|
@@ -14,6 +15,7 @@ const gemma3Preset = await loadJson('./presets/models/gemma3.json', import.meta.
|
|
|
14
15
|
const translateGemmaPreset = await loadJson('./presets/models/translategemma.json', import.meta.url, 'Failed to load preset');
|
|
15
16
|
const embeddingGemmaPreset = await loadJson('./presets/models/embeddinggemma.json', import.meta.url, 'Failed to load preset');
|
|
16
17
|
const functiongemmaPreset = await loadJson('./presets/models/functiongemma.json', import.meta.url, 'Failed to load preset');
|
|
18
|
+
const janusTextPreset = await loadJson('./presets/models/janus-text.json', import.meta.url, 'Failed to load preset');
|
|
17
19
|
const llama3Preset = await loadJson('./presets/models/llama3.json', import.meta.url, 'Failed to load preset');
|
|
18
20
|
const mixtralPreset = await loadJson('./presets/models/mixtral.json', import.meta.url, 'Failed to load preset');
|
|
19
21
|
const deepseekPreset = await loadJson('./presets/models/deepseek.json', import.meta.url, 'Failed to load preset');
|
|
@@ -36,6 +38,7 @@ export const PRESET_REGISTRY = {
|
|
|
36
38
|
translategemma: translateGemmaPreset,
|
|
37
39
|
embeddinggemma: embeddingGemmaPreset,
|
|
38
40
|
functiongemma: functiongemmaPreset,
|
|
41
|
+
janus_text: janusTextPreset,
|
|
39
42
|
llama3: llama3Preset,
|
|
40
43
|
mixtral: mixtralPreset,
|
|
41
44
|
deepseek: deepseekPreset,
|
|
@@ -85,6 +88,7 @@ export const PRESET_DETECTION_ORDER = [
|
|
|
85
88
|
// Most specific first (model variants)
|
|
86
89
|
'functiongemma',
|
|
87
90
|
'embeddinggemma',
|
|
91
|
+
'janus_text',
|
|
88
92
|
'modernbert',
|
|
89
93
|
'diffusion',
|
|
90
94
|
// Model families (check more specific patterns first)
|
|
@@ -107,8 +111,9 @@ export function detectPreset(
|
|
|
107
111
|
config,
|
|
108
112
|
architecture
|
|
109
113
|
) {
|
|
114
|
+
const nestedTextConfig = getNestedTextConfig(config);
|
|
110
115
|
const archLower = (architecture || '').toLowerCase();
|
|
111
|
-
const modelType = (config.model_type
|
|
116
|
+
const modelType = String(nestedTextConfig?.model_type ?? config.model_type ?? '').toLowerCase();
|
|
112
117
|
// Weak hint case: architecture fallback is often model_type copy (e.g. qwen2).
|
|
113
118
|
const hintsAreWeak = !archLower || !modelType || archLower === modelType;
|
|
114
119
|
|
|
@@ -186,18 +191,21 @@ export function resolveConfig(
|
|
|
186
191
|
// Note: Uses nullish coalesce (??) so null values fall through to next level.
|
|
187
192
|
// This means explicit null in manifest = "use preset/default".
|
|
188
193
|
const presetArch = preset.architecture || {};
|
|
189
|
-
const numLayers = manifestArch.numLayers
|
|
190
|
-
const hiddenSize = manifestArch.hiddenSize
|
|
191
|
-
const intermediateSize = manifestArch.intermediateSize
|
|
192
|
-
const numAttentionHeads = manifestArch.numAttentionHeads
|
|
193
|
-
const numKeyValueHeads =
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
const numLayers = chooseNullish(manifestArch.numLayers, presetArch.numLayers);
|
|
195
|
+
const hiddenSize = chooseNullish(manifestArch.hiddenSize, presetArch.hiddenSize);
|
|
196
|
+
const intermediateSize = chooseNullish(manifestArch.intermediateSize, presetArch.intermediateSize);
|
|
197
|
+
const numAttentionHeads = chooseNullish(manifestArch.numAttentionHeads, presetArch.numAttentionHeads);
|
|
198
|
+
const numKeyValueHeads = chooseNullish(
|
|
199
|
+
manifestArch.numKeyValueHeads,
|
|
200
|
+
chooseNullish(presetArch.numKeyValueHeads, numAttentionHeads)
|
|
196
201
|
);
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
202
|
+
const headDim = chooseNullish(manifestArch.headDim, chooseNullish(presetArch.headDim, (
|
|
203
|
+
hiddenSize && numAttentionHeads ? hiddenSize / numAttentionHeads : undefined
|
|
204
|
+
)));
|
|
205
|
+
const vocabSize = chooseNullish(manifestArch.vocabSize, presetArch.vocabSize);
|
|
206
|
+
const maxSeqLen = chooseNullish(manifestArch.maxSeqLen, presetArch.maxSeqLen);
|
|
207
|
+
const ropeTheta = chooseNullish(manifestArch.ropeTheta, presetArch.ropeTheta);
|
|
208
|
+
const rmsNormEps = chooseNullish(manifestArch.rmsNormEps, presetArch.rmsNormEps);
|
|
201
209
|
|
|
202
210
|
const architecture = {
|
|
203
211
|
numLayers,
|
|
@@ -222,44 +230,44 @@ export function resolveConfig(
|
|
|
222
230
|
const manifestInference = extractInferenceFromConfig(manifest.config || {});
|
|
223
231
|
|
|
224
232
|
const inference = {
|
|
225
|
-
attention:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
normalization:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
ffn:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
moe: presetInference.moe
|
|
239
|
-
output:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
layerPattern: presetInference.layerPattern
|
|
245
|
-
rope:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
pipeline: manifestInference.pipeline
|
|
251
|
-
chatTemplate:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
kernelPath: presetInference.kernelPath
|
|
233
|
+
attention: mergeLayeredShallowObjects(
|
|
234
|
+
baseInference.attention,
|
|
235
|
+
presetInference.attention,
|
|
236
|
+
manifestInference.attention
|
|
237
|
+
),
|
|
238
|
+
normalization: mergeLayeredShallowObjects(
|
|
239
|
+
baseInference.normalization,
|
|
240
|
+
presetInference.normalization
|
|
241
|
+
),
|
|
242
|
+
ffn: mergeLayeredShallowObjects(
|
|
243
|
+
baseInference.ffn,
|
|
244
|
+
presetInference.ffn
|
|
245
|
+
),
|
|
246
|
+
moe: chooseNullish(presetInference.moe, chooseNullish(baseInference.moe, null)),
|
|
247
|
+
output: mergeLayeredShallowObjects(
|
|
248
|
+
baseInference.output,
|
|
249
|
+
presetInference.output,
|
|
250
|
+
manifestInference.output
|
|
251
|
+
),
|
|
252
|
+
layerPattern: chooseNullish(presetInference.layerPattern, baseInference.layerPattern),
|
|
253
|
+
rope: mergeLayeredShallowObjects(
|
|
254
|
+
baseInference.rope,
|
|
255
|
+
presetInference.rope,
|
|
256
|
+
manifestInference.rope
|
|
257
|
+
),
|
|
258
|
+
pipeline: chooseNullish(manifestInference.pipeline, chooseNullish(presetInference.pipeline, baseInference.pipeline)),
|
|
259
|
+
chatTemplate: mergeLayeredShallowObjects(
|
|
260
|
+
baseInference.chatTemplate,
|
|
261
|
+
presetInference.chatTemplate
|
|
262
|
+
),
|
|
263
|
+
kernelPath: chooseNullish(presetInference.kernelPath, baseInference.kernelPath),
|
|
256
264
|
};
|
|
257
265
|
|
|
258
266
|
// Merge tokenizer config
|
|
259
|
-
const tokenizer =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
267
|
+
const tokenizer = mergeLayeredShallowObjects(
|
|
268
|
+
preset.tokenizer,
|
|
269
|
+
extractTokenizerFromManifest(manifest)
|
|
270
|
+
);
|
|
263
271
|
|
|
264
272
|
// Sampling defaults
|
|
265
273
|
const sampling = preset.sampling ?? {
|
|
@@ -317,41 +325,56 @@ function assertArchitecture(manifest, architecture) {
|
|
|
317
325
|
// =============================================================================
|
|
318
326
|
|
|
319
327
|
function extractArchitectureFromConfig(config) {
|
|
328
|
+
const nestedTextConfig = getNestedTextConfig(config);
|
|
320
329
|
return {
|
|
321
|
-
numLayers: config.num_hidden_layers ?? config.n_layer ?? config.blockCount,
|
|
322
|
-
hiddenSize: config.hidden_size ?? config.n_embd ?? config.embeddingLength,
|
|
323
|
-
intermediateSize: config.intermediate_size ?? config.n_inner ?? config.feedForwardLength,
|
|
324
|
-
numAttentionHeads: config.num_attention_heads ?? config.n_head ?? config.attentionHeadCount,
|
|
325
|
-
numKeyValueHeads: config.num_key_value_heads ?? config.attentionHeadCountKV,
|
|
326
|
-
headDim: config.head_dim,
|
|
327
|
-
vocabSize: config.vocab_size ?? config.vocabSize,
|
|
328
|
-
maxSeqLen: config.max_position_embeddings ?? config.n_positions ?? config.contextLength,
|
|
329
|
-
ropeTheta: config.rope_theta ?? config.ropeFreqBase,
|
|
330
|
-
rmsNormEps: config.rms_norm_eps ?? config.attentionLayerNormRMSEpsilon,
|
|
330
|
+
numLayers: config.num_hidden_layers ?? nestedTextConfig?.num_hidden_layers ?? config.n_layer ?? config.blockCount,
|
|
331
|
+
hiddenSize: config.hidden_size ?? nestedTextConfig?.hidden_size ?? config.n_embd ?? config.embeddingLength,
|
|
332
|
+
intermediateSize: config.intermediate_size ?? nestedTextConfig?.intermediate_size ?? config.n_inner ?? config.feedForwardLength,
|
|
333
|
+
numAttentionHeads: config.num_attention_heads ?? nestedTextConfig?.num_attention_heads ?? config.n_head ?? config.attentionHeadCount,
|
|
334
|
+
numKeyValueHeads: config.num_key_value_heads ?? nestedTextConfig?.num_key_value_heads ?? config.attentionHeadCountKV,
|
|
335
|
+
headDim: config.head_dim ?? nestedTextConfig?.head_dim,
|
|
336
|
+
vocabSize: config.vocab_size ?? nestedTextConfig?.vocab_size ?? config.vocabSize,
|
|
337
|
+
maxSeqLen: config.max_position_embeddings ?? nestedTextConfig?.max_position_embeddings ?? config.n_positions ?? config.contextLength,
|
|
338
|
+
ropeTheta: config.rope_theta ?? nestedTextConfig?.rope_theta ?? config.ropeFreqBase,
|
|
339
|
+
rmsNormEps: config.rms_norm_eps ?? nestedTextConfig?.rms_norm_eps ?? config.attentionLayerNormRMSEpsilon,
|
|
331
340
|
};
|
|
332
341
|
}
|
|
333
342
|
|
|
334
343
|
function extractInferenceFromConfig(config) {
|
|
344
|
+
const nestedTextConfig = getNestedTextConfig(config);
|
|
335
345
|
return {
|
|
336
346
|
attention: {
|
|
337
|
-
slidingWindow: config.sliding_window,
|
|
338
|
-
attnLogitSoftcapping: config.attn_logit_softcapping,
|
|
339
|
-
attentionOutputGate: config.attn_output_gate,
|
|
347
|
+
slidingWindow: config.sliding_window ?? nestedTextConfig?.sliding_window,
|
|
348
|
+
attnLogitSoftcapping: config.attn_logit_softcapping ?? nestedTextConfig?.attn_logit_softcapping,
|
|
349
|
+
attentionOutputGate: config.attn_output_gate ?? nestedTextConfig?.attn_output_gate,
|
|
340
350
|
},
|
|
341
351
|
output: {
|
|
342
|
-
finalLogitSoftcapping: config.final_logit_softcapping,
|
|
343
|
-
tieWordEmbeddings: config.tie_word_embeddings,
|
|
344
|
-
scaleEmbeddings: config.scale_embeddings,
|
|
352
|
+
finalLogitSoftcapping: config.final_logit_softcapping ?? nestedTextConfig?.final_logit_softcapping,
|
|
353
|
+
tieWordEmbeddings: config.tie_word_embeddings ?? nestedTextConfig?.tie_word_embeddings,
|
|
354
|
+
scaleEmbeddings: config.scale_embeddings ?? nestedTextConfig?.scale_embeddings,
|
|
345
355
|
},
|
|
346
|
-
pipeline: config.pipeline,
|
|
356
|
+
pipeline: config.pipeline ?? nestedTextConfig?.pipeline,
|
|
347
357
|
rope: {
|
|
348
|
-
ropeTheta: config.rope_theta ?? config.ropeFreqBase,
|
|
349
|
-
ropeScalingType: config.rope_scaling_type,
|
|
350
|
-
ropeScalingFactor: config.rope_scaling_factor,
|
|
358
|
+
ropeTheta: config.rope_theta ?? nestedTextConfig?.rope_theta ?? config.ropeFreqBase,
|
|
359
|
+
ropeScalingType: config.rope_scaling_type ?? nestedTextConfig?.rope_scaling_type,
|
|
360
|
+
ropeScalingFactor: config.rope_scaling_factor ?? nestedTextConfig?.rope_scaling_factor,
|
|
351
361
|
},
|
|
352
362
|
};
|
|
353
363
|
}
|
|
354
364
|
|
|
365
|
+
function getNestedTextConfig(config) {
|
|
366
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
if (config.text_config && typeof config.text_config === 'object' && !Array.isArray(config.text_config)) {
|
|
370
|
+
return config.text_config;
|
|
371
|
+
}
|
|
372
|
+
if (config.language_config && typeof config.language_config === 'object' && !Array.isArray(config.language_config)) {
|
|
373
|
+
return config.language_config;
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
|
|
355
378
|
function extractTokenizerFromManifest(manifest) {
|
|
356
379
|
if (!manifest.tokenizer) return {};
|
|
357
380
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface MergeContractCheckResult {
|
|
2
|
+
id: string;
|
|
3
|
+
ok: boolean;
|
|
4
|
+
detail: string;
|
|
5
|
+
mode: 'actual' | 'modeled';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface MergeContractArtifact {
|
|
9
|
+
schemaVersion: 1;
|
|
10
|
+
source: 'doppler';
|
|
11
|
+
ok: boolean;
|
|
12
|
+
checks: MergeContractCheckResult[];
|
|
13
|
+
errors: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export declare function buildMergeContractArtifact(): MergeContractArtifact;
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { resolveConfig, resolvePreset } from './loader.js';
|
|
2
|
+
import {
|
|
3
|
+
chooseNullish,
|
|
4
|
+
chooseDefinedWithSource,
|
|
5
|
+
mergeExecutionPatchLists,
|
|
6
|
+
mergeKernelPathPolicy,
|
|
7
|
+
mergeLayeredShallowObjects,
|
|
8
|
+
mergeShallowObject,
|
|
9
|
+
replaceSubtree,
|
|
10
|
+
} from './merge-helpers.js';
|
|
11
|
+
import { mergeConfig } from './merge.js';
|
|
12
|
+
import { createDopplerConfig } from './schema/index.js';
|
|
13
|
+
|
|
14
|
+
function buildWitnessManifestForArchitecture() {
|
|
15
|
+
return {
|
|
16
|
+
modelId: 'merge-semantics-witness',
|
|
17
|
+
modelType: 'transformer',
|
|
18
|
+
architecture: {
|
|
19
|
+
numLayers: 2,
|
|
20
|
+
hiddenSize: 256,
|
|
21
|
+
intermediateSize: 512,
|
|
22
|
+
numAttentionHeads: 4,
|
|
23
|
+
numKeyValueHeads: 4,
|
|
24
|
+
headDim: 64,
|
|
25
|
+
vocabSize: 1024,
|
|
26
|
+
maxSeqLen: 2048,
|
|
27
|
+
ropeTheta: null,
|
|
28
|
+
rmsNormEps: 1e-6,
|
|
29
|
+
},
|
|
30
|
+
config: {},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildWitnessMergeManifest() {
|
|
35
|
+
return {
|
|
36
|
+
modelId: 'merge-overlay-witness',
|
|
37
|
+
inference: {
|
|
38
|
+
attention: {
|
|
39
|
+
queryPreAttnScalar: 1,
|
|
40
|
+
attentionBias: false,
|
|
41
|
+
attnLogitSoftcapping: null,
|
|
42
|
+
slidingWindow: 4096,
|
|
43
|
+
queryKeyNorm: false,
|
|
44
|
+
attentionOutputGate: false,
|
|
45
|
+
causal: true,
|
|
46
|
+
},
|
|
47
|
+
normalization: {
|
|
48
|
+
rmsNormEps: 1e-6,
|
|
49
|
+
rmsNormWeightOffset: 0,
|
|
50
|
+
postAttentionNorm: true,
|
|
51
|
+
preFeedforwardNorm: true,
|
|
52
|
+
postFeedforwardNorm: false,
|
|
53
|
+
},
|
|
54
|
+
ffn: {
|
|
55
|
+
activation: 'gelu',
|
|
56
|
+
gatedActivation: false,
|
|
57
|
+
swigluLimit: null,
|
|
58
|
+
},
|
|
59
|
+
rope: {
|
|
60
|
+
ropeTheta: 1000000,
|
|
61
|
+
ropeLocalTheta: null,
|
|
62
|
+
ropeScalingType: null,
|
|
63
|
+
ropeScalingFactor: null,
|
|
64
|
+
ropeLocalScalingType: null,
|
|
65
|
+
ropeLocalScalingFactor: null,
|
|
66
|
+
yarnBetaFast: null,
|
|
67
|
+
yarnBetaSlow: null,
|
|
68
|
+
yarnOriginalMaxPos: null,
|
|
69
|
+
ropeLocalYarnBetaFast: null,
|
|
70
|
+
ropeLocalYarnBetaSlow: null,
|
|
71
|
+
ropeLocalYarnOriginalMaxPos: null,
|
|
72
|
+
},
|
|
73
|
+
output: {
|
|
74
|
+
finalLogitSoftcapping: 30,
|
|
75
|
+
tieWordEmbeddings: true,
|
|
76
|
+
scaleEmbeddings: false,
|
|
77
|
+
embeddingTranspose: false,
|
|
78
|
+
embeddingVocabSize: 1024,
|
|
79
|
+
},
|
|
80
|
+
layerPattern: null,
|
|
81
|
+
chatTemplate: {
|
|
82
|
+
type: 'gemma',
|
|
83
|
+
enabled: true,
|
|
84
|
+
},
|
|
85
|
+
defaultKernelPath: 'gemma3-f16-fused-f16a-online',
|
|
86
|
+
},
|
|
87
|
+
architecture: {
|
|
88
|
+
headDim: 64,
|
|
89
|
+
maxSeqLen: 2048,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function recordCheck(results, id, ok, detail, mode = 'actual') {
|
|
95
|
+
results.push({ id, ok, detail, mode });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function buildMergeContractArtifact() {
|
|
99
|
+
const checks = [];
|
|
100
|
+
const preset = resolvePreset('gemma3');
|
|
101
|
+
const resolved = resolveConfig(buildWitnessManifestForArchitecture(), 'gemma3');
|
|
102
|
+
recordCheck(
|
|
103
|
+
checks,
|
|
104
|
+
'loader.architecture.nullish_null_falls_through',
|
|
105
|
+
resolved.architecture.ropeTheta === preset.architecture.ropeTheta,
|
|
106
|
+
`resolved ropeTheta=${resolved.architecture.ropeTheta}, preset ropeTheta=${preset.architecture.ropeTheta}`
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const mergedUndefined = mergeConfig(buildWitnessMergeManifest(), {});
|
|
110
|
+
recordCheck(
|
|
111
|
+
checks,
|
|
112
|
+
'runtime.mergeConfig.defined_overlay_missing_falls_through',
|
|
113
|
+
mergedUndefined.inference.defaultKernelPath === 'gemma3-f16-fused-f16a-online'
|
|
114
|
+
&& mergedUndefined._sources.get('inference.defaultKernelPath') === 'manifest',
|
|
115
|
+
`value=${mergedUndefined.inference.defaultKernelPath}, source=${mergedUndefined._sources.get('inference.defaultKernelPath')}`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const mergedNull = mergeConfig(buildWitnessMergeManifest(), {
|
|
119
|
+
defaultKernelPath: null,
|
|
120
|
+
chatTemplate: {
|
|
121
|
+
enabled: null,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
recordCheck(
|
|
125
|
+
checks,
|
|
126
|
+
'runtime.mergeConfig.defined_overlay_preserves_null',
|
|
127
|
+
mergedNull.inference.defaultKernelPath === null
|
|
128
|
+
&& mergedNull._sources.get('inference.defaultKernelPath') === 'runtime',
|
|
129
|
+
`value=${mergedNull.inference.defaultKernelPath}, source=${mergedNull._sources.get('inference.defaultKernelPath')}`
|
|
130
|
+
);
|
|
131
|
+
recordCheck(
|
|
132
|
+
checks,
|
|
133
|
+
'runtime.inference.chatTemplate.spread_preserves_null',
|
|
134
|
+
mergedNull.inference.chatTemplate.enabled === null
|
|
135
|
+
&& mergedNull._sources.get('inference.chatTemplate.enabled') === 'runtime',
|
|
136
|
+
`value=${String(mergedNull.inference.chatTemplate.enabled)}`
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const runtimeConfig = createDopplerConfig({
|
|
140
|
+
runtime: {
|
|
141
|
+
inference: {
|
|
142
|
+
chatTemplate: {
|
|
143
|
+
enabled: null,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
recordCheck(
|
|
149
|
+
checks,
|
|
150
|
+
'runtime.schema.chatTemplate.spread_preserves_null',
|
|
151
|
+
runtimeConfig.runtime.inference.chatTemplate.enabled === null,
|
|
152
|
+
`value=${String(runtimeConfig.runtime.inference.chatTemplate.enabled)}`
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const overlaySources = new Map();
|
|
156
|
+
const chosenRuntimeValue = chooseDefinedWithSource(
|
|
157
|
+
'inference.defaultKernelPath',
|
|
158
|
+
null,
|
|
159
|
+
'manifest-path',
|
|
160
|
+
overlaySources
|
|
161
|
+
);
|
|
162
|
+
recordCheck(
|
|
163
|
+
checks,
|
|
164
|
+
'runtime.mergeHelpers.chooseDefinedWithSource.runtime_marks_source',
|
|
165
|
+
chosenRuntimeValue === null && overlaySources.get('inference.defaultKernelPath') === 'runtime',
|
|
166
|
+
`value=${String(chosenRuntimeValue)}, source=${overlaySources.get('inference.defaultKernelPath')}`,
|
|
167
|
+
'actual'
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const manifestSources = new Map();
|
|
171
|
+
const chosenManifestValue = chooseDefinedWithSource(
|
|
172
|
+
'inference.defaultKernelPath',
|
|
173
|
+
undefined,
|
|
174
|
+
'manifest-path',
|
|
175
|
+
manifestSources
|
|
176
|
+
);
|
|
177
|
+
recordCheck(
|
|
178
|
+
checks,
|
|
179
|
+
'runtime.mergeHelpers.chooseDefinedWithSource.manifest_marks_source',
|
|
180
|
+
chosenManifestValue === 'manifest-path' && manifestSources.get('inference.defaultKernelPath') === 'manifest',
|
|
181
|
+
`value=${String(chosenManifestValue)}, source=${manifestSources.get('inference.defaultKernelPath')}`,
|
|
182
|
+
'actual'
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const executionPatchBase = {
|
|
186
|
+
set: [{ op: 'seed' }],
|
|
187
|
+
remove: ['old_step'],
|
|
188
|
+
add: [{ id: 'new_step' }],
|
|
189
|
+
};
|
|
190
|
+
const executionPatchOverride = {
|
|
191
|
+
set: null,
|
|
192
|
+
remove: [],
|
|
193
|
+
add: undefined,
|
|
194
|
+
};
|
|
195
|
+
const mergedExecutionPatch = {
|
|
196
|
+
...mergeExecutionPatchLists(executionPatchBase, executionPatchOverride),
|
|
197
|
+
};
|
|
198
|
+
recordCheck(
|
|
199
|
+
checks,
|
|
200
|
+
'runtime.inference.executionPatch.nullish_null_falls_through',
|
|
201
|
+
mergedExecutionPatch.set === executionPatchBase.set
|
|
202
|
+
&& mergedExecutionPatch.add === executionPatchBase.add
|
|
203
|
+
&& Array.isArray(mergedExecutionPatch.remove)
|
|
204
|
+
&& mergedExecutionPatch.remove.length === 0,
|
|
205
|
+
`setLength=${mergedExecutionPatch.set.length}, removeLength=${mergedExecutionPatch.remove.length}, addLength=${mergedExecutionPatch.add.length}`,
|
|
206
|
+
'actual'
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const sessionBase = {
|
|
210
|
+
kvcache: {
|
|
211
|
+
layout: 'paged',
|
|
212
|
+
maxSeqLen: 8192,
|
|
213
|
+
kvDtype: 'f16',
|
|
214
|
+
},
|
|
215
|
+
decodeLoop: {
|
|
216
|
+
batchSize: 16,
|
|
217
|
+
disableCommandBatching: false,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
const sessionOverride = {
|
|
221
|
+
kvcache: {
|
|
222
|
+
layout: 'tiered',
|
|
223
|
+
},
|
|
224
|
+
decodeLoop: {
|
|
225
|
+
batchSize: 1,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
const mergedSession = {
|
|
229
|
+
kvcache: replaceSubtree(sessionOverride.kvcache, sessionBase.kvcache),
|
|
230
|
+
decodeLoop: replaceSubtree(sessionOverride.decodeLoop, sessionBase.decodeLoop),
|
|
231
|
+
};
|
|
232
|
+
recordCheck(
|
|
233
|
+
checks,
|
|
234
|
+
'runtime.inference.session.subtree_override_replaces_base',
|
|
235
|
+
mergedSession.kvcache.layout === 'tiered'
|
|
236
|
+
&& mergedSession.kvcache.maxSeqLen === undefined
|
|
237
|
+
&& mergedSession.decodeLoop.batchSize === 1
|
|
238
|
+
&& mergedSession.decodeLoop.disableCommandBatching === undefined,
|
|
239
|
+
`kvcacheKeys=${Object.keys(mergedSession.kvcache).join(',')}; decodeLoopKeys=${Object.keys(mergedSession.decodeLoop).join(',')}`,
|
|
240
|
+
'actual'
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const mergedChatTemplate = mergeShallowObject(
|
|
244
|
+
{ type: 'base', enabled: true },
|
|
245
|
+
{ enabled: null }
|
|
246
|
+
);
|
|
247
|
+
recordCheck(
|
|
248
|
+
checks,
|
|
249
|
+
'runtime.mergeShallowObject.spread_preserves_null',
|
|
250
|
+
mergedChatTemplate.enabled === null && mergedChatTemplate.type === 'base',
|
|
251
|
+
`type=${mergedChatTemplate.type}, enabled=${String(mergedChatTemplate.enabled)}`,
|
|
252
|
+
'actual'
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const layeredAttention = mergeLayeredShallowObjects(
|
|
256
|
+
{ slidingWindow: 4096, attentionBias: false },
|
|
257
|
+
{ slidingWindow: 2048 },
|
|
258
|
+
{ slidingWindow: null }
|
|
259
|
+
);
|
|
260
|
+
recordCheck(
|
|
261
|
+
checks,
|
|
262
|
+
'loader.mergeLayeredShallowObjects.spread_preserves_null',
|
|
263
|
+
layeredAttention.slidingWindow === null && layeredAttention.attentionBias === false,
|
|
264
|
+
`slidingWindow=${String(layeredAttention.slidingWindow)}, attentionBias=${String(layeredAttention.attentionBias)}`,
|
|
265
|
+
'actual'
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const mergedKernelPathPolicy = mergeKernelPathPolicy(
|
|
269
|
+
{
|
|
270
|
+
mode: 'locked',
|
|
271
|
+
sourceScope: ['model', 'manifest'],
|
|
272
|
+
allowSources: ['model', 'manifest'],
|
|
273
|
+
onIncompatible: 'error',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
allowSources: ['runtime', 'execution-v0'],
|
|
277
|
+
onIncompatible: 'remap',
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
recordCheck(
|
|
281
|
+
checks,
|
|
282
|
+
'runtime.kernelPathPolicy.source_scope_mirrors_allow_sources',
|
|
283
|
+
Array.isArray(mergedKernelPathPolicy.sourceScope)
|
|
284
|
+
&& Array.isArray(mergedKernelPathPolicy.allowSources)
|
|
285
|
+
&& mergedKernelPathPolicy.sourceScope.length === 2
|
|
286
|
+
&& mergedKernelPathPolicy.sourceScope[0] === 'runtime'
|
|
287
|
+
&& mergedKernelPathPolicy.allowSources[1] === 'execution-v0'
|
|
288
|
+
&& mergedKernelPathPolicy.onIncompatible === 'remap',
|
|
289
|
+
`sourceScope=${JSON.stringify(mergedKernelPathPolicy.sourceScope)}, allowSources=${JSON.stringify(mergedKernelPathPolicy.allowSources)}`,
|
|
290
|
+
'actual'
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const runtimeConfigWithKernelPathPolicy = createDopplerConfig({
|
|
294
|
+
runtime: {
|
|
295
|
+
inference: {
|
|
296
|
+
kernelPathPolicy: {
|
|
297
|
+
allowSources: ['runtime', 'execution-v0'],
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
recordCheck(
|
|
303
|
+
checks,
|
|
304
|
+
'runtime.schema.kernelPathPolicy.helper_is_used',
|
|
305
|
+
Array.isArray(runtimeConfigWithKernelPathPolicy.runtime.inference.kernelPathPolicy.sourceScope)
|
|
306
|
+
&& runtimeConfigWithKernelPathPolicy.runtime.inference.kernelPathPolicy.sourceScope[0] === 'runtime'
|
|
307
|
+
&& runtimeConfigWithKernelPathPolicy.runtime.inference.kernelPathPolicy.allowSources[1] === 'execution-v0',
|
|
308
|
+
`policy=${JSON.stringify(runtimeConfigWithKernelPathPolicy.runtime.inference.kernelPathPolicy)}`,
|
|
309
|
+
'actual'
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const errors = checks.filter((entry) => !entry.ok).map((entry) => `[MergeContract] ${entry.id}: ${entry.detail}`);
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
schemaVersion: 1,
|
|
316
|
+
source: 'doppler',
|
|
317
|
+
ok: errors.length === 0,
|
|
318
|
+
checks,
|
|
319
|
+
errors,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare function chooseNullish<T>(
|
|
2
|
+
overrideValue: T | null | undefined,
|
|
3
|
+
fallbackValue: T
|
|
4
|
+
): T;
|
|
5
|
+
|
|
6
|
+
export declare function chooseDefined<T>(
|
|
7
|
+
overrideValue: T | undefined,
|
|
8
|
+
fallbackValue: T
|
|
9
|
+
): T;
|
|
10
|
+
|
|
11
|
+
export declare function chooseDefinedWithSource<T>(
|
|
12
|
+
path: string,
|
|
13
|
+
overrideValue: T | undefined,
|
|
14
|
+
fallbackValue: T,
|
|
15
|
+
sources: Map<string, string> | null | undefined
|
|
16
|
+
): T;
|
|
17
|
+
|
|
18
|
+
export declare function mergeShallowObject<T extends object>(
|
|
19
|
+
base: T,
|
|
20
|
+
override: Partial<T> | null | undefined
|
|
21
|
+
): T;
|
|
22
|
+
|
|
23
|
+
export declare function mergeLayeredShallowObjects<T extends object>(
|
|
24
|
+
...layers: Array<Partial<T> | null | undefined>
|
|
25
|
+
): T;
|
|
26
|
+
|
|
27
|
+
export declare function replaceSubtree<T>(
|
|
28
|
+
overrideValue: T | null | undefined,
|
|
29
|
+
fallbackValue: T
|
|
30
|
+
): T;
|
|
31
|
+
|
|
32
|
+
export declare function mergeKernelPathPolicy<T extends {
|
|
33
|
+
mode?: unknown;
|
|
34
|
+
sourceScope?: unknown;
|
|
35
|
+
allowSources?: unknown;
|
|
36
|
+
onIncompatible?: unknown;
|
|
37
|
+
}>(
|
|
38
|
+
basePolicy: T | null | undefined,
|
|
39
|
+
overridePolicy: T | null | undefined
|
|
40
|
+
): {
|
|
41
|
+
mode: unknown;
|
|
42
|
+
sourceScope: unknown;
|
|
43
|
+
allowSources: unknown;
|
|
44
|
+
onIncompatible: unknown;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export declare function mergeExecutionPatchLists<T extends {
|
|
48
|
+
set?: unknown;
|
|
49
|
+
remove?: unknown;
|
|
50
|
+
add?: unknown;
|
|
51
|
+
}>(
|
|
52
|
+
basePatch: T | null | undefined,
|
|
53
|
+
overridePatch: T | null | undefined
|
|
54
|
+
): {
|
|
55
|
+
set: unknown;
|
|
56
|
+
remove: unknown;
|
|
57
|
+
add: unknown;
|
|
58
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function chooseNullish(overrideValue, fallbackValue) {
|
|
2
|
+
return overrideValue ?? fallbackValue;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function chooseDefined(overrideValue, fallbackValue) {
|
|
6
|
+
return overrideValue !== undefined ? overrideValue : fallbackValue;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function chooseDefinedWithSource(path, overrideValue, fallbackValue, sources) {
|
|
10
|
+
const value = chooseDefined(overrideValue, fallbackValue);
|
|
11
|
+
if (sources && typeof sources.set === 'function') {
|
|
12
|
+
sources.set(path, overrideValue !== undefined ? 'runtime' : 'manifest');
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function mergeShallowObject(base, override) {
|
|
18
|
+
if (!override || typeof override !== 'object' || Array.isArray(override)) {
|
|
19
|
+
return base;
|
|
20
|
+
}
|
|
21
|
+
return { ...base, ...override };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function mergeLayeredShallowObjects(...layers) {
|
|
25
|
+
return layers.reduce((merged, layer) => mergeShallowObject(merged, layer), {});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function replaceSubtree(overrideValue, fallbackValue) {
|
|
29
|
+
return chooseNullish(overrideValue, fallbackValue);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function mergeKernelPathPolicy(basePolicy, overridePolicy) {
|
|
33
|
+
const base = basePolicy ?? {};
|
|
34
|
+
const override = overridePolicy ?? {};
|
|
35
|
+
const baseSourceScope = base.sourceScope ?? base.allowSources;
|
|
36
|
+
const overrideSourceScope = override.sourceScope ?? override.allowSources;
|
|
37
|
+
const sourceScope = overrideSourceScope ?? baseSourceScope;
|
|
38
|
+
return {
|
|
39
|
+
mode: override.mode ?? base.mode,
|
|
40
|
+
sourceScope,
|
|
41
|
+
allowSources: sourceScope,
|
|
42
|
+
onIncompatible: override.onIncompatible ?? base.onIncompatible,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function mergeExecutionPatchLists(basePatch, overridePatch) {
|
|
47
|
+
const base = basePatch ?? {};
|
|
48
|
+
const override = overridePatch ?? {};
|
|
49
|
+
return {
|
|
50
|
+
set: chooseNullish(override.set, chooseNullish(base.set, [])),
|
|
51
|
+
remove: chooseNullish(override.remove, chooseNullish(base.remove, [])),
|
|
52
|
+
add: chooseNullish(override.add, chooseNullish(base.add, [])),
|
|
53
|
+
};
|
|
54
|
+
}
|