@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
@@ -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,7 +11,7 @@ const DEFAULT_LOCAL_DOE_PROVIDER_PATH = resolve(
11
11
  '..',
12
12
  'fawn',
13
13
  'nursery',
14
- 'webgpu-core',
14
+ 'webgpu-doe',
15
15
  );
16
16
  const DEFAULT_DOE_PROVIDER_CREATE_ARGS = 'enable-dawn-features=allow_unsafe_apis';
17
17
 
@@ -61,7 +61,7 @@ function resolveCandidateModuleSpecifier(candidate) {
61
61
  function resolveDefaultWebgpuModuleSpecifiers() {
62
62
  const specifiers = [];
63
63
  const localCandidates = [
64
- resolve(process.cwd(), '..', 'fawn', 'nursery', 'webgpu-core'),
64
+ resolve(process.cwd(), '..', 'fawn', 'nursery', 'webgpu-doe'),
65
65
  DEFAULT_LOCAL_DOE_PROVIDER_PATH,
66
66
  ];
67
67
  for (const localCandidate of localCandidates) {
@@ -70,7 +70,7 @@ function resolveDefaultWebgpuModuleSpecifiers() {
70
70
  specifiers.push(pathToFileURL(resolvedPath).href);
71
71
  }
72
72
  }
73
- specifiers.push('@doe/webgpu-core');
73
+ specifiers.push('@simulatte/webgpu-doe');
74
74
  specifiers.push('webgpu');
75
75
  return [...new Set(specifiers)];
76
76
  }
@@ -103,17 +103,17 @@ function resolveWorkspaceWebgpuProviderPath() {
103
103
  return null;
104
104
  }
105
105
 
106
- function isDoeWebgpuCoreSpecifier(specifier) {
107
- if (specifier === '@doe/webgpu-core') {
106
+ function isDoeWebgpuSpecifier(specifier) {
107
+ if (specifier === '@simulatte/webgpu-doe') {
108
108
  return true;
109
109
  }
110
110
  if (typeof specifier !== 'string') {
111
111
  return false;
112
112
  }
113
- if (specifier.includes('/webgpu-core/')) {
113
+ if (specifier.includes('/webgpu-doe/')) {
114
114
  return true;
115
115
  }
116
- return specifier.includes('webgpu-core') && specifier.startsWith('file://');
116
+ return specifier.includes('webgpu-doe') && specifier.startsWith('file://');
117
117
  }
118
118
 
119
119
  function resolveDoeProviderOverride(specifier) {
@@ -121,7 +121,7 @@ function resolveDoeProviderOverride(specifier) {
121
121
  if (typeof explicitProvider === 'string' && explicitProvider.trim().length > 0) {
122
122
  return null;
123
123
  }
124
- if (!isDoeWebgpuCoreSpecifier(specifier)) {
124
+ if (!isDoeWebgpuSpecifier(specifier)) {
125
125
  return null;
126
126
  }
127
127
  return resolveWorkspaceWebgpuProviderPath();
@@ -129,7 +129,7 @@ function resolveDoeProviderOverride(specifier) {
129
129
 
130
130
  async function importWithProviderOverride(specifier) {
131
131
  const providerOverride = resolveDoeProviderOverride(specifier);
132
- const shouldApplyCreateArgsDefault = isDoeWebgpuCoreSpecifier(specifier)
132
+ const shouldApplyCreateArgsDefault = isDoeWebgpuSpecifier(specifier)
133
133
  && !(typeof process.env.FAWN_WEBGPU_CREATE_ARGS === 'string' && process.env.FAWN_WEBGPU_CREATE_ARGS.trim().length > 0);
134
134
  if (!providerOverride) {
135
135
  if (!shouldApplyCreateArgsDefault) {
@@ -0,0 +1,2 @@
1
+ export declare const DOPPLER_VERSION: string;
2
+ export declare const DOPPLER_PROVIDER_VERSION: string;
package/src/version.js ADDED
@@ -0,0 +1,2 @@
1
+ export const DOPPLER_VERSION = '0.1.5';
2
+ export const DOPPLER_PROVIDER_VERSION = DOPPLER_VERSION;
@@ -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) => {
@@ -713,7 +713,17 @@ function toSummary(result) {
713
713
  }
714
714
 
715
715
  if (result.manifest?.modelId) {
716
- return `converted ${result.manifest.modelId} (${result.tensorCount} tensors, ${result.shardCount} shards)`;
716
+ const contractStatus = result.executionContractArtifact?.ok === true
717
+ ? ' contract=pass'
718
+ : result.executionContractArtifact
719
+ ? ' contract=fail'
720
+ : '';
721
+ const graphStatus = result.executionV0GraphContractArtifact?.ok === true
722
+ ? ' graph=pass'
723
+ : result.executionV0GraphContractArtifact
724
+ ? ' graph=fail'
725
+ : '';
726
+ return `converted ${result.manifest.modelId} (${result.tensorCount} tensors, ${result.shardCount} shards)${contractStatus}${graphStatus}`;
717
727
  }
718
728
 
719
729
  const suite = result.suite || result.report?.suite || 'suite';
@@ -1014,6 +1024,102 @@ function printMemoryReport(result) {
1014
1024
  console.log(`[memory] ${parts.join(' ')}`);
1015
1025
  }
1016
1026
 
1027
+ function printExecutionContractSummary(result) {
1028
+ const artifact = result?.metrics?.executionContractArtifact;
1029
+ if (!artifact || typeof artifact !== 'object') return;
1030
+ const checks = Array.isArray(artifact.checks) ? artifact.checks : [];
1031
+ const passedChecks = checks.filter((entry) => entry?.ok === true).length;
1032
+ const session = artifact.session && typeof artifact.session === 'object'
1033
+ ? artifact.session
1034
+ : null;
1035
+ const attentionPhases = artifact.steps?.attentionPhases && typeof artifact.steps.attentionPhases === 'object'
1036
+ ? artifact.steps.attentionPhases
1037
+ : null;
1038
+ const parts = [
1039
+ `status=${artifact.ok === true ? 'pass' : 'fail'}`,
1040
+ checks.length > 0 ? `checks=${passedChecks}/${checks.length}` : 'checks=n/a',
1041
+ ];
1042
+ if (session?.layout) {
1043
+ parts.push(`layout=${session.layout}`);
1044
+ }
1045
+ if (attentionPhases) {
1046
+ parts.push(
1047
+ `attn(prefill=${attentionPhases.prefill ?? 'n/a'},decode=${attentionPhases.decode ?? 'n/a'},both=${attentionPhases.both ?? 'n/a'})`
1048
+ );
1049
+ }
1050
+ console.log(`[contract] ${parts.join(' ')}`);
1051
+ if (artifact.ok !== true && Array.isArray(artifact.errors)) {
1052
+ for (const error of artifact.errors.slice(0, 3)) {
1053
+ console.log(`[contract] error=${quoteOneLine(error)}`);
1054
+ }
1055
+ }
1056
+ }
1057
+
1058
+ function printExecutionV0GraphSummary(artifact) {
1059
+ if (!artifact || typeof artifact !== 'object') return;
1060
+ const checks = Array.isArray(artifact.checks) ? artifact.checks : [];
1061
+ const passedChecks = checks.filter((entry) => entry?.ok === true).length;
1062
+ const stats = artifact.stats && typeof artifact.stats === 'object' ? artifact.stats : null;
1063
+ const parts = [
1064
+ `status=${artifact.ok === true ? 'pass' : 'fail'}`,
1065
+ checks.length > 0 ? `checks=${passedChecks}/${checks.length}` : 'checks=n/a',
1066
+ ];
1067
+ if (Number.isFinite(stats?.prefillSteps) || Number.isFinite(stats?.decodeSteps)) {
1068
+ parts.push(`steps(prefill=${stats?.prefillSteps ?? 'n/a'},decode=${stats?.decodeSteps ?? 'n/a'})`);
1069
+ }
1070
+ console.log(`[graph] ${parts.join(' ')}`);
1071
+ if (artifact.ok !== true && Array.isArray(artifact.errors)) {
1072
+ for (const error of artifact.errors.slice(0, 3)) {
1073
+ console.log(`[graph] error=${quoteOneLine(error)}`);
1074
+ }
1075
+ }
1076
+ }
1077
+
1078
+ function printSimpleArtifactSummary(label, artifact) {
1079
+ if (!artifact || typeof artifact !== 'object') return;
1080
+ const checks = Array.isArray(artifact.checks) ? artifact.checks : [];
1081
+ const passedChecks = checks.filter((entry) => entry?.ok === true).length;
1082
+ console.log(
1083
+ `[${label}] status=${artifact.ok === true ? 'pass' : 'fail'} ` +
1084
+ `checks=${checks.length > 0 ? `${passedChecks}/${checks.length}` : 'n/a'}`
1085
+ );
1086
+ if (artifact.ok !== true && Array.isArray(artifact.errors)) {
1087
+ for (const error of artifact.errors.slice(0, 2)) {
1088
+ console.log(`[${label}] error=${quoteOneLine(error)}`);
1089
+ }
1090
+ }
1091
+ }
1092
+
1093
+ function printConvertContractSummary(result) {
1094
+ const artifact = result?.executionContractArtifact;
1095
+ if (!artifact || typeof artifact !== 'object') return;
1096
+ const checks = Array.isArray(artifact.checks) ? artifact.checks : [];
1097
+ const passedChecks = checks.filter((entry) => entry?.ok === true).length;
1098
+ const session = artifact.session && typeof artifact.session === 'object'
1099
+ ? artifact.session
1100
+ : null;
1101
+ console.log(
1102
+ `[contract] status=${artifact.ok === true ? 'pass' : 'fail'} ` +
1103
+ `checks=${checks.length > 0 ? `${passedChecks}/${checks.length}` : 'n/a'} ` +
1104
+ `layout=${session?.layout ?? 'n/a'}`
1105
+ );
1106
+ if (artifact.ok !== true && Array.isArray(artifact.errors)) {
1107
+ for (const error of artifact.errors.slice(0, 3)) {
1108
+ console.log(`[contract] error=${quoteOneLine(error)}`);
1109
+ }
1110
+ }
1111
+ printExecutionV0GraphSummary(result?.executionV0GraphContractArtifact);
1112
+ printSimpleArtifactSummary('layer-pattern', result?.layerPatternContractArtifact);
1113
+ printSimpleArtifactSummary('required-inference', result?.requiredInferenceFieldsArtifact);
1114
+ }
1115
+
1116
+ function printConvertReportSummary(result) {
1117
+ const reportInfo = result?.reportInfo;
1118
+ if (!reportInfo || typeof reportInfo !== 'object') return;
1119
+ if (typeof reportInfo.path !== 'string' || reportInfo.path.length === 0) return;
1120
+ console.log(`[report] ${reportInfo.path}`);
1121
+ }
1122
+
1017
1123
  function printMetricsSummary(result) {
1018
1124
  if (!result || typeof result !== 'object') return;
1019
1125
  const suite = String(result.suite || '');
@@ -1038,6 +1144,8 @@ function printMetricsSummary(result) {
1038
1144
  `prefill=${formatNumber(metrics.prefillTokensPerSec)} ` +
1039
1145
  `decode=${formatNumber(metrics.decodeTokensPerSec)}`
1040
1146
  );
1147
+ printExecutionContractSummary(result);
1148
+ printExecutionV0GraphSummary(metrics.executionV0GraphContractArtifact);
1041
1149
  return;
1042
1150
  }
1043
1151
 
@@ -1052,6 +1160,8 @@ function printMetricsSummary(result) {
1052
1160
  `median=${formatMs(metrics.medianEmbeddingMs)} avg=${formatMs(metrics.avgEmbeddingMs)} ` +
1053
1161
  `eps=${formatNumber(metrics.avgEmbeddingsPerSec)}`
1054
1162
  );
1163
+ printExecutionContractSummary(result);
1164
+ printExecutionV0GraphSummary(metrics.executionV0GraphContractArtifact);
1055
1165
  return;
1056
1166
  }
1057
1167
 
@@ -1073,6 +1183,8 @@ function printMetricsSummary(result) {
1073
1183
  `[metrics] latency first=${formatMs(metrics.firstTokenMs)} ` +
1074
1184
  `prefill=${formatMs(metrics.prefillMs)} decode=${formatMs(metrics.decodeMs)}`
1075
1185
  );
1186
+ printExecutionContractSummary(result);
1187
+ printExecutionV0GraphSummary(metrics.executionV0GraphContractArtifact);
1076
1188
  printDeviceInfo(result);
1077
1189
  printGpuPhases(metrics);
1078
1190
  printMemoryReport(result);
@@ -1190,6 +1302,8 @@ async function main() {
1190
1302
  }
1191
1303
 
1192
1304
  console.log(`[ok] ${toSummary(response.result)}`);
1305
+ printConvertContractSummary(response.result);
1306
+ printConvertReportSummary(response.result);
1193
1307
  printMetricsSummary(response.result);
1194
1308
  } catch (error) {
1195
1309
  if (jsonOutputRequested) {