@simulatte/doppler 0.1.4 → 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.
Files changed (103) hide show
  1. package/README.md +4 -3
  2. package/package.json +25 -4
  3. package/src/client/doppler-api.browser.d.ts +1 -0
  4. package/src/client/doppler-api.browser.js +288 -0
  5. package/src/client/doppler-api.js +1 -1
  6. package/src/client/doppler-provider/types.js +1 -1
  7. package/src/config/execution-contract-check.d.ts +33 -0
  8. package/src/config/execution-contract-check.js +72 -0
  9. package/src/config/execution-v0-contract-check.d.ts +94 -0
  10. package/src/config/execution-v0-contract-check.js +251 -0
  11. package/src/config/execution-v0-graph-contract-check.d.ts +20 -0
  12. package/src/config/execution-v0-graph-contract-check.js +64 -0
  13. package/src/config/kernel-path-contract-check.d.ts +76 -0
  14. package/src/config/kernel-path-contract-check.js +479 -0
  15. package/src/config/kernel-path-loader.d.ts +16 -0
  16. package/src/config/kernel-path-loader.js +54 -0
  17. package/src/config/kernels/kernel-ref-digests.js +12 -0
  18. package/src/config/kernels/registry.json +556 -0
  19. package/src/config/loader.js +50 -46
  20. package/src/config/merge-contract-check.d.ts +16 -0
  21. package/src/config/merge-contract-check.js +321 -0
  22. package/src/config/merge-helpers.d.ts +58 -0
  23. package/src/config/merge-helpers.js +54 -0
  24. package/src/config/merge.js +3 -6
  25. package/src/config/presets/models/janus-text.json +2 -0
  26. package/src/config/quantization-contract-check.d.ts +12 -0
  27. package/src/config/quantization-contract-check.js +91 -0
  28. package/src/config/required-inference-fields-contract-check.d.ts +24 -0
  29. package/src/config/required-inference-fields-contract-check.js +231 -0
  30. package/src/config/schema/browser-suite-metrics.schema.d.ts +17 -0
  31. package/src/config/schema/browser-suite-metrics.schema.js +46 -0
  32. package/src/config/schema/conversion-report.schema.d.ts +40 -0
  33. package/src/config/schema/conversion-report.schema.js +108 -0
  34. package/src/config/schema/doppler.schema.js +12 -18
  35. package/src/config/schema/index.d.ts +22 -0
  36. package/src/config/schema/index.js +18 -0
  37. package/src/converter/core.d.ts +10 -0
  38. package/src/converter/core.js +27 -2
  39. package/src/converter/parsers/diffusion.js +63 -3
  40. package/src/gpu/kernels/depthwise_conv2d.d.ts +29 -0
  41. package/src/gpu/kernels/depthwise_conv2d.js +98 -0
  42. package/src/gpu/kernels/depthwise_conv2d.wgsl +58 -0
  43. package/src/gpu/kernels/depthwise_conv2d_f16.wgsl +62 -0
  44. package/src/gpu/kernels/grouped_pointwise_conv2d.d.ts +27 -0
  45. package/src/gpu/kernels/grouped_pointwise_conv2d.js +92 -0
  46. package/src/gpu/kernels/grouped_pointwise_conv2d.wgsl +47 -0
  47. package/src/gpu/kernels/grouped_pointwise_conv2d_f16.wgsl +51 -0
  48. package/src/gpu/kernels/index.d.ts +30 -0
  49. package/src/gpu/kernels/index.js +25 -0
  50. package/src/gpu/kernels/relu.d.ts +18 -0
  51. package/src/gpu/kernels/relu.js +45 -0
  52. package/src/gpu/kernels/relu.wgsl +21 -0
  53. package/src/gpu/kernels/relu_f16.wgsl +23 -0
  54. package/src/gpu/kernels/repeat_channels.d.ts +21 -0
  55. package/src/gpu/kernels/repeat_channels.js +60 -0
  56. package/src/gpu/kernels/repeat_channels.wgsl +29 -0
  57. package/src/gpu/kernels/repeat_channels_f16.wgsl +31 -0
  58. package/src/gpu/kernels/sana_linear_attention.d.ts +27 -0
  59. package/src/gpu/kernels/sana_linear_attention.js +122 -0
  60. package/src/gpu/kernels/sana_linear_attention_apply.wgsl +44 -0
  61. package/src/gpu/kernels/sana_linear_attention_apply_f16.wgsl +47 -0
  62. package/src/gpu/kernels/sana_linear_attention_summary.wgsl +47 -0
  63. package/src/gpu/kernels/sana_linear_attention_summary_f16.wgsl +49 -0
  64. package/src/index-browser.d.ts +1 -1
  65. package/src/index-browser.js +2 -2
  66. package/src/index.js +1 -1
  67. package/src/inference/browser-harness.js +62 -22
  68. package/src/inference/pipelines/diffusion/init.js +14 -0
  69. package/src/inference/pipelines/diffusion/pipeline.js +206 -77
  70. package/src/inference/pipelines/diffusion/sana-transformer.d.ts +53 -0
  71. package/src/inference/pipelines/diffusion/sana-transformer.js +738 -0
  72. package/src/inference/pipelines/diffusion/scheduler.d.ts +17 -1
  73. package/src/inference/pipelines/diffusion/scheduler.js +91 -3
  74. package/src/inference/pipelines/diffusion/text-encoder-gpu.d.ts +6 -4
  75. package/src/inference/pipelines/diffusion/text-encoder-gpu.js +270 -0
  76. package/src/inference/pipelines/diffusion/text-encoder.js +18 -1
  77. package/src/inference/pipelines/diffusion/types.d.ts +4 -0
  78. package/src/inference/pipelines/diffusion/vae.js +782 -78
  79. package/src/inference/pipelines/text/config.d.ts +5 -0
  80. package/src/inference/pipelines/text/config.js +1 -1
  81. package/src/inference/pipelines/text/execution-v0.js +14 -93
  82. package/src/rules/execution-rules-contract-check.d.ts +17 -0
  83. package/src/rules/execution-rules-contract-check.js +245 -0
  84. package/src/rules/kernels/depthwise-conv2d.rules.json +6 -0
  85. package/src/rules/kernels/grouped-pointwise-conv2d.rules.json +6 -0
  86. package/src/rules/kernels/relu.rules.json +6 -0
  87. package/src/rules/kernels/repeat-channels.rules.json +6 -0
  88. package/src/rules/kernels/sana-linear-attention.rules.json +6 -0
  89. package/src/rules/layer-pattern-contract-check.d.ts +17 -0
  90. package/src/rules/layer-pattern-contract-check.js +231 -0
  91. package/src/rules/rule-registry.d.ts +28 -0
  92. package/src/rules/rule-registry.js +38 -0
  93. package/src/tooling/conversion-config-materializer.d.ts +24 -0
  94. package/src/tooling/conversion-config-materializer.js +99 -0
  95. package/src/tooling/lean-execution-contract-runner.d.ts +43 -0
  96. package/src/tooling/lean-execution-contract-runner.js +158 -0
  97. package/src/tooling/node-convert.d.ts +10 -0
  98. package/src/tooling/node-converter.js +59 -0
  99. package/src/tooling/node-webgpu.js +9 -9
  100. package/src/version.d.ts +2 -0
  101. package/src/version.js +2 -0
  102. package/tools/convert-safetensors-node.js +47 -0
  103. package/tools/doppler-cli.js +115 -1
@@ -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
+ }
@@ -1,3 +1,5 @@
1
+ import { chooseDefined, chooseDefinedWithSource } from './merge-helpers.js';
2
+
1
3
  // =============================================================================
2
4
  // Merge Implementation
3
5
  // =============================================================================
@@ -8,12 +10,7 @@ function overlay(
8
10
  runtimeValue,
9
11
  sources
10
12
  ) {
11
- if (runtimeValue !== undefined) {
12
- sources.set(path, 'runtime');
13
- return runtimeValue;
14
- }
15
- sources.set(path, 'manifest');
16
- return manifestValue;
13
+ return chooseDefinedWithSource(path, runtimeValue, manifestValue, sources);
17
14
  }
18
15
 
19
16
  function mergeAttention(
@@ -14,6 +14,8 @@
14
14
  },
15
15
 
16
16
  "tokenizer": {
17
+ "bosToken": "<|begin▁of▁sentence|>",
18
+ "eosTokens": ["<|end▁of▁sentence|>"],
17
19
  "addBosToken": true,
18
20
  "addEosToken": false
19
21
  },
@@ -0,0 +1,12 @@
1
+ export interface QuantizationContractArtifact {
2
+ schemaVersion: 1;
3
+ source: 'doppler';
4
+ ok: boolean;
5
+ checks: Array<{ id: string; ok: boolean }>;
6
+ errors: string[];
7
+ stats: {
8
+ sampledSizes: number;
9
+ };
10
+ }
11
+
12
+ export declare function buildQuantizationContractArtifact(): QuantizationContractArtifact;
@@ -0,0 +1,91 @@
1
+ import {
2
+ K_SCALE_SIZE,
3
+ Q4K_BLOCK_BYTES,
4
+ Q6K_BLOCK_BYTES,
5
+ Q8_0_BLOCK_BYTES,
6
+ Q8_0_BLOCK_SIZE,
7
+ QK4_K_BLOCK_SIZE,
8
+ QK_K,
9
+ padToQ4KBlock,
10
+ q4kBlockCount,
11
+ } from './schema/quantization.schema.js';
12
+ import {
13
+ TILE_SIZES,
14
+ QUANTIZATION,
15
+ } from '../gpu/kernels/constants.js';
16
+ import * as loaderQuantization from '../loader/quantization-constants.js';
17
+
18
+ const EXPECTED_CONSTANTS = Object.freeze({
19
+ QK_K: 256,
20
+ Q4K_BLOCK_BYTES: 144,
21
+ Q6K_BLOCK_BYTES: 210,
22
+ Q8_0_BLOCK_BYTES: 34,
23
+ Q8_0_BLOCK_SIZE: 32,
24
+ K_SCALE_SIZE: 12,
25
+ });
26
+
27
+ export function buildQuantizationContractArtifact() {
28
+ const errors = [];
29
+ const checks = [];
30
+
31
+ const literalConstantsOk =
32
+ QK_K === EXPECTED_CONSTANTS.QK_K
33
+ && Q4K_BLOCK_BYTES === EXPECTED_CONSTANTS.Q4K_BLOCK_BYTES
34
+ && Q6K_BLOCK_BYTES === EXPECTED_CONSTANTS.Q6K_BLOCK_BYTES
35
+ && Q8_0_BLOCK_BYTES === EXPECTED_CONSTANTS.Q8_0_BLOCK_BYTES
36
+ && Q8_0_BLOCK_SIZE === EXPECTED_CONSTANTS.Q8_0_BLOCK_SIZE
37
+ && K_SCALE_SIZE === EXPECTED_CONSTANTS.K_SCALE_SIZE
38
+ && QK4_K_BLOCK_SIZE === Q4K_BLOCK_BYTES;
39
+ if (!literalConstantsOk) {
40
+ errors.push('[QuantizationContract] schema constants drifted from the expected Q4K/Q6K/Q8 values.');
41
+ }
42
+ checks.push({ id: 'quantization.constants.schema', ok: literalConstantsOk });
43
+
44
+ const crossModuleOk =
45
+ loaderQuantization.QK_K === QK_K
46
+ && loaderQuantization.Q4K_BLOCK_BYTES === Q4K_BLOCK_BYTES
47
+ && loaderQuantization.Q6K_BLOCK_BYTES === Q6K_BLOCK_BYTES
48
+ && loaderQuantization.Q8_0_BLOCK_BYTES === Q8_0_BLOCK_BYTES
49
+ && loaderQuantization.Q8_0_BLOCK_SIZE === Q8_0_BLOCK_SIZE
50
+ && TILE_SIZES.Q4K_SUPER_BLOCK_SIZE === QK_K
51
+ && QUANTIZATION.Q4K_BLOCK_BYTES === Q4K_BLOCK_BYTES;
52
+ if (!crossModuleOk) {
53
+ errors.push('[QuantizationContract] loader/GPU quantization constants drifted from schema constants.');
54
+ }
55
+ checks.push({ id: 'quantization.constants.crossModule', ok: crossModuleOk });
56
+
57
+ let padPropertiesOk = true;
58
+ let q4kCoverageOk = true;
59
+ let previous = -1;
60
+ for (let size = 0; size <= QK_K * 2 + 7; size += 1) {
61
+ const padded = padToQ4KBlock(size);
62
+ if (padded < size || padded % QK_K !== 0 || padToQ4KBlock(padded) !== padded || padded < previous) {
63
+ padPropertiesOk = false;
64
+ break;
65
+ }
66
+ previous = padded;
67
+ if (q4kBlockCount(size) * QK_K < size) {
68
+ q4kCoverageOk = false;
69
+ break;
70
+ }
71
+ }
72
+ if (!padPropertiesOk) {
73
+ errors.push('[QuantizationContract] padToQ4KBlock must be monotone, aligned, and idempotent.');
74
+ }
75
+ checks.push({ id: 'quantization.padToQ4KBlock.properties', ok: padPropertiesOk });
76
+ if (!q4kCoverageOk) {
77
+ errors.push('[QuantizationContract] q4kBlockCount must cover the requested element count.');
78
+ }
79
+ checks.push({ id: 'quantization.q4kBlockCount.coverage', ok: q4kCoverageOk });
80
+
81
+ return {
82
+ schemaVersion: 1,
83
+ source: 'doppler',
84
+ ok: errors.length === 0,
85
+ checks,
86
+ errors,
87
+ stats: {
88
+ sampledSizes: QK_K * 2 + 8,
89
+ },
90
+ };
91
+ }
@@ -0,0 +1,24 @@
1
+ export interface RequiredInferenceFieldsContractArtifact {
2
+ schemaVersion: 1;
3
+ source: 'doppler';
4
+ ok: boolean;
5
+ checks: Array<{ id: string; ok: boolean }>;
6
+ errors: string[];
7
+ stats: {
8
+ fieldCases: number;
9
+ nullableCases: number;
10
+ nonNullableCases: number;
11
+ };
12
+ }
13
+
14
+ export declare function buildRequiredInferenceFieldsContractArtifact(): RequiredInferenceFieldsContractArtifact;
15
+
16
+ export interface ManifestRequiredInferenceFieldsArtifact extends RequiredInferenceFieldsContractArtifact {
17
+ scope: 'manifest';
18
+ label: string;
19
+ }
20
+
21
+ export declare function buildManifestRequiredInferenceFieldsArtifact(
22
+ inference: Record<string, unknown> | null | undefined,
23
+ label?: string
24
+ ): ManifestRequiredInferenceFieldsArtifact;