@kaminos/webgpu-inference-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -0
- package/package.json +26 -0
- package/src/gpu-environment.js +139 -0
- package/src/index.js +106 -0
- package/src/kernel-profile.js +101 -0
- package/src/kimodo-route.js +84 -0
- package/src/moge-route.js +84 -0
- package/src/route-boundary.js +347 -0
- package/src/route-receipt-consumer.js +160 -0
- package/src/route-receipt-helper.js +86 -0
- package/src/route-receipt.js +147 -0
- package/src/route-schema-contract.js +29 -0
- package/src/runtime-profile.js +139 -0
- package/src/scheduler-backpressure.js +221 -0
- package/src/sf3d-route.js +96 -0
- package/src/sharp-route.js +87 -0
- package/src/staged-profile.js +87 -0
- package/src/tensor-manifest.js +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# @kaminos/webgpu-inference-kit
|
|
2
|
+
|
|
3
|
+
Native WebGPU inference route substrate for Kaminos.
|
|
4
|
+
|
|
5
|
+
Install:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @kaminos/webgpu-inference-kit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import {
|
|
15
|
+
createWebGpuRouteSchemaContract,
|
|
16
|
+
createWebGpuLocalRouteReceipt,
|
|
17
|
+
classifyWebGpuRouteReceiptEvidence,
|
|
18
|
+
} from "@kaminos/webgpu-inference-kit";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This package starts with contracts, not kernels. Its first job is to make
|
|
22
|
+
browser-local model routes prove what they actually ran before Kaminos treats
|
|
23
|
+
their outputs as asset evidence.
|
|
24
|
+
|
|
25
|
+
Current surface:
|
|
26
|
+
|
|
27
|
+
- `createWebGpuLocalRouteReceipt(input)`: creates a
|
|
28
|
+
`kaminos.webgpu-route-receipt.v0` receipt for a `webgpu-local` route.
|
|
29
|
+
- `createWebGpuRouteSchemaContract(input)`: exposes the kit-owned route
|
|
30
|
+
definition/request/result/receipt/runtime-profile/evidence-classification
|
|
31
|
+
schema strings as a compact contract object so route repos can run conformance
|
|
32
|
+
checks instead of manually mirroring hidden constants.
|
|
33
|
+
- `createWebGpuRouteReceiptFromArtifacts(input)` plus
|
|
34
|
+
`createRouteReceiptArtifacts`, `createRouteReceiptInputArtifact`,
|
|
35
|
+
`finishAndValidateRouteProfile`, and validation helpers: shared route receipt
|
|
36
|
+
substrate used by MoGE, SHARP, Kimodo, and SF3D factories to preserve artifact
|
|
37
|
+
identity, backend identity, and staged profile requirements without duplicating
|
|
38
|
+
false-closure-prone boilerplate.
|
|
39
|
+
- `validateRouteReceipt(receipt)`: validates requested/effective route identity,
|
|
40
|
+
backend/model/kernel identity, input/output artifact ids, timings, and
|
|
41
|
+
fallback status.
|
|
42
|
+
- `assertAuthoritativeRouteReceipt(receipt)`: rejects fallback, cached,
|
|
43
|
+
partial, missing, or non-real outputs before they can masquerade as
|
|
44
|
+
authoritative Kaminos evidence.
|
|
45
|
+
- `defineTensorManifest(input)` and `validateTensorManifest(manifest)`: normalize
|
|
46
|
+
and validate model tensor metadata, including dtype sizes and byte lengths.
|
|
47
|
+
- `createWebGpuDeviceRequest(adapter, options)`: derives requested WebGPU
|
|
48
|
+
features and max adapter limits for model inference without silently capping
|
|
49
|
+
below the adapter's own reported capacity.
|
|
50
|
+
- `requestBrowserWebGpuDevice(gpu, options)`: requests a browser WebGPU adapter
|
|
51
|
+
and device, then returns the effective device request and backend identity
|
|
52
|
+
that route receipts should preserve.
|
|
53
|
+
- `createWebGpuBackendIdentity(input)` and
|
|
54
|
+
`validateWebGpuBackendIdentity(identity)`: preserve effective browser,
|
|
55
|
+
adapter, feature, limit, and timestamp-query identity for route receipts.
|
|
56
|
+
- `createStagedSubmitProfile(input)`, `addStagedSubmitStage(profile, stage)`,
|
|
57
|
+
`finishStagedSubmitProfile(profile)`, and
|
|
58
|
+
`validateStagedSubmitProfile(profile)`: record staged-submit timing evidence
|
|
59
|
+
and reject timestamp-query timing unless it is validated against staged waits.
|
|
60
|
+
- `createKernelProfileMetadata(input)` and
|
|
61
|
+
`createRouteKernelProfileMetadata(input)`: normalize shared kit version,
|
|
62
|
+
kernel profile, commit, required stage, and timing-source metadata for route
|
|
63
|
+
definitions and receipts while keeping route-specific semantics local.
|
|
64
|
+
- `createWebGpuRuntimeProfileInput(input)`,
|
|
65
|
+
`createWebGpuRuntimeProfile(input)`, and
|
|
66
|
+
`validateWebGpuRuntimeProfile(profile)`: combine effective WebGPU backend
|
|
67
|
+
identity, kernel metadata, staged profile evidence, and evidence mode into a
|
|
68
|
+
single producer-side runtime profile object.
|
|
69
|
+
- `createWebGpuRouteSchedulerProfile(input)` and
|
|
70
|
+
`validateWebGpuRouteSchedulerProfile(profile)`: preserve requested versus
|
|
71
|
+
effective WebGPU route scheduling, including throughput/cooperative mode,
|
|
72
|
+
route-specific phase chunk sizes, submitted-work waits, yield cadence, and
|
|
73
|
+
unsupported scheduler fields so a route cannot claim cooperative behavior
|
|
74
|
+
without effective telemetry.
|
|
75
|
+
- `createWebGpuRouteBackpressureProfile(input)` and
|
|
76
|
+
`validateWebGpuRouteBackpressureProfile(profile)`: record route pressure
|
|
77
|
+
classification, warm/cache and memory-sharing posture, and frame-tail
|
|
78
|
+
evidence for visible-wait/furnace classification without turning internal
|
|
79
|
+
scheduler knobs into operator-facing controls.
|
|
80
|
+
- `classifyWebGpuRouteReceiptEvidence(receipt)` and
|
|
81
|
+
`classifyWebGpuRouteWorkerResultEvidence(result)`: commoner-side receipt
|
|
82
|
+
classification helpers that distinguish authoritative live WebGPU evidence
|
|
83
|
+
from fallback, partial, cache/demo, stale, route-mismatch, and invalid
|
|
84
|
+
receipts, while surfacing scheduler verification and frame-tail fields when a
|
|
85
|
+
route provides them.
|
|
86
|
+
- `createMogeDepthNormalRouteReceipt(input)`: first concrete `webgpu-local`
|
|
87
|
+
route receipt factory for `moge.depth-normal.webgpu-local.v0`.
|
|
88
|
+
- `defineWebGpuRoute(input)`, `createWebGpuRouteRegistry(routes)`,
|
|
89
|
+
`createRouteInvocationRequest(route, input)`, `createRouteWorkerResult(route,
|
|
90
|
+
input)`, and their validators: define worker-executable routes, create
|
|
91
|
+
invocation envelopes, and validate route results before Wake/Pipeline consume
|
|
92
|
+
them as Kaminos evidence.
|
|
93
|
+
- `createMogeDepthNormalRouteDefinition(input)`: first concrete route
|
|
94
|
+
definition for MoGE source-image to depth/normal/pointmap truth-layer output.
|
|
95
|
+
- `createSharpImageToSplatRouteReceipt(input)`: concrete receipt factory for
|
|
96
|
+
`sharp.image-to-splat.webgpu-local.v0`, preserving source image, browser
|
|
97
|
+
WebGPU backend identity, PLY splat candidate, depth map, SHARP metadata, and
|
|
98
|
+
optional splat autocrop evidence.
|
|
99
|
+
- `createSharpImageToSplatRouteDefinition(input)`: route definition aligned to
|
|
100
|
+
the native SHARP-WebGPU browser adapter surface used by Kaminos Pipeline:
|
|
101
|
+
source image in, splat candidate/depth/metadata out, with optional
|
|
102
|
+
`kaminos.splat-autocrop-evidence.v0` side evidence.
|
|
103
|
+
- `createKimodoTextToMotionRouteReceipt(input)` and
|
|
104
|
+
`createKimodoTextToMotionRouteDefinition(input)`: browser WebGPU
|
|
105
|
+
text-to-motion route contract for Kimodo SOMA-RP-v1.1, preserving prompt
|
|
106
|
+
identity, SOMA77 joint output, motion sidecar output, optional filmstrip, and
|
|
107
|
+
staged text-embedding/DDIM/FK/output-capture timing.
|
|
108
|
+
- `createSf3dImageToMeshRouteReceipt(input)` and
|
|
109
|
+
`createSf3dImageToMeshRouteDefinition(input)`: browser WebGPU image-to-mesh
|
|
110
|
+
route contract for Stable Fast 3D, preserving source image, GLB mesh, albedo
|
|
111
|
+
texture, normal map, optional OBJ, and DINOv2/two-stream/triplane/marching-tet
|
|
112
|
+
stage identity.
|
|
113
|
+
|
|
114
|
+
Near-term extraction order:
|
|
115
|
+
|
|
116
|
+
1. Route receipt and tensor manifest contracts. Done in the scaffold slice.
|
|
117
|
+
2. WebGPU device/feature/profiling identity helpers. First pure contract helpers
|
|
118
|
+
are in place; browser adapters should wire into these next.
|
|
119
|
+
3. MoGE depth/normal route receipt. First factory is in place and the MoGE
|
|
120
|
+
runtime emits this receipt from live inference.
|
|
121
|
+
4. Browser route boundary. Route registry, invocation request, worker result,
|
|
122
|
+
browser device request, and MoGE route definition contracts are in place.
|
|
123
|
+
5. SHARP image-to-splat route contract. First factory and route definition are
|
|
124
|
+
in place for the browser-native SHARP-WebGPU path; runtime emission remains
|
|
125
|
+
owned by SHARP/Pipeline adapter surfaces.
|
|
126
|
+
6. Kimodo and SF3D route contracts. First factories and route definitions are
|
|
127
|
+
in place for browser-native text-to-motion and image-to-mesh routes; runtime
|
|
128
|
+
emission remains owned by those route repos and Kaminos motion/pipeline
|
|
129
|
+
consumers.
|
|
130
|
+
7. MoGE schema mirror drift reduction. The kit exposes a schema contract object;
|
|
131
|
+
MoGE has a dev conformance test against that contract while the runtime still
|
|
132
|
+
avoids a brittle temporary worktree dependency.
|
|
133
|
+
8. Shared route receipt helper. Artifact normalization, backend identity
|
|
134
|
+
validation, staged profile validation, and receipt construction now live in
|
|
135
|
+
one helper consumed by all four concrete route factories.
|
|
136
|
+
9. Shared kernel/profile metadata helper. Kit version, kernel profile, commit,
|
|
137
|
+
required stage, and timing-source normalization now live in one helper
|
|
138
|
+
consumed by all four concrete route factories.
|
|
139
|
+
10. Runtime profile and commoner receipt classification helpers. Producers can
|
|
140
|
+
normalize effective WebGPU runtime evidence, and downstream commoners can
|
|
141
|
+
classify receipts before treating outputs as live route evidence.
|
|
142
|
+
11. Scheduler/backpressure contracts. Routes can now report throughput versus
|
|
143
|
+
cooperative scheduling, requested/effective phase chunking, unsupported
|
|
144
|
+
fields, visible-wait/furnace pressure, and frame-tail evidence without
|
|
145
|
+
implying browser GPU preemption that WebGPU does not provide.
|
|
146
|
+
12. Pipeline, bind-group, uniform, and buffer caches from MoGE/SHARP.
|
|
147
|
+
13. Shared kernels only when at least two real routes need them or a measured
|
|
148
|
+
kernel slice proves the extraction useful.
|
|
149
|
+
|
|
150
|
+
Non-goals:
|
|
151
|
+
|
|
152
|
+
- Generic ONNX import parity.
|
|
153
|
+
- General LLM runtime competition.
|
|
154
|
+
- Kaminos graph, scene, library, or promotion ownership.
|
|
155
|
+
- Any route that hides fallback, stale output, fixture data, partial output, or
|
|
156
|
+
effective backend identity.
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kaminos/webgpu-inference-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Native WebGPU inference route substrate for Kaminos.",
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/lyonsno/kaminos.git",
|
|
11
|
+
"directory": "webgpu-inference-kit"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"README.md",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./src/index.js"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node tests/receipt-contracts.mjs && node tests/tensor-manifest-contracts.mjs && node tests/gpu-environment-contracts.mjs && node tests/browser-device-context-contracts.mjs && node tests/staged-profile-contracts.mjs && node tests/kernel-profile-contracts.mjs && node tests/runtime-profile-contracts.mjs && node tests/scheduler-backpressure-contracts.mjs && node tests/route-schema-contracts.mjs && node tests/route-receipt-helper-contracts.mjs && node tests/route-receipt-consumer-contracts.mjs && node tests/moge-route-contracts.mjs && node tests/sharp-route-contracts.mjs && node tests/kimodo-route-contracts.mjs && node tests/sf3d-route-contracts.mjs && node tests/route-boundary-contracts.mjs"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const LIMIT_KEYS = [
|
|
2
|
+
'maxBufferSize',
|
|
3
|
+
'maxStorageBufferBindingSize',
|
|
4
|
+
'maxComputeWorkgroupStorageSize',
|
|
5
|
+
'maxComputeInvocationsPerWorkgroup',
|
|
6
|
+
'maxComputeWorkgroupSizeX',
|
|
7
|
+
'maxComputeWorkgroupSizeY',
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
function featureList(features) {
|
|
11
|
+
if (!features) return [];
|
|
12
|
+
return Array.from(features).map(String).sort();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function hasFeature(features, name) {
|
|
16
|
+
return featureList(features).includes(name);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isNonEmptyString(value) {
|
|
20
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function copyLimits(limits = {}) {
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const key of LIMIT_KEYS) {
|
|
26
|
+
if (Number.isFinite(limits[key])) out[key] = limits[key];
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createWebGpuDeviceRequest(adapter, options = {}) {
|
|
32
|
+
if (!adapter || typeof adapter !== 'object') {
|
|
33
|
+
throw new Error('adapter must be an object');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const timestampPreference = options.timestampQuery || 'prefer';
|
|
37
|
+
const requiredFeatures = [];
|
|
38
|
+
let timestampQuery = 'disabled';
|
|
39
|
+
|
|
40
|
+
if (timestampPreference !== 'disable') {
|
|
41
|
+
if (hasFeature(adapter.features, 'timestamp-query')) {
|
|
42
|
+
requiredFeatures.push('timestamp-query');
|
|
43
|
+
timestampQuery = 'requested';
|
|
44
|
+
} else if (timestampPreference === 'require') {
|
|
45
|
+
throw new Error('timestamp-query required but not supported by adapter');
|
|
46
|
+
} else {
|
|
47
|
+
timestampQuery = 'unavailable';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
requiredFeatures,
|
|
53
|
+
requiredLimits: copyLimits(adapter.limits),
|
|
54
|
+
timestampQuery,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function requestBrowserWebGpuDevice(gpu, options = {}) {
|
|
59
|
+
if (!gpu || typeof gpu.requestAdapter !== 'function') {
|
|
60
|
+
throw new Error('gpu.requestAdapter must be available');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (async () => {
|
|
64
|
+
const adapter = await gpu.requestAdapter(options.adapterOptions || {});
|
|
65
|
+
if (!adapter) throw new Error('WebGPU adapter unavailable');
|
|
66
|
+
|
|
67
|
+
const deviceRequest = createWebGpuDeviceRequest(adapter, options);
|
|
68
|
+
const descriptor = {
|
|
69
|
+
requiredFeatures: deviceRequest.requiredFeatures,
|
|
70
|
+
requiredLimits: deviceRequest.requiredLimits,
|
|
71
|
+
};
|
|
72
|
+
if (isNonEmptyString(options.label)) descriptor.label = options.label;
|
|
73
|
+
|
|
74
|
+
const device = await adapter.requestDevice(descriptor);
|
|
75
|
+
const backendIdentity = createWebGpuBackendIdentity({
|
|
76
|
+
adapterName: options.adapterName || adapter.info?.description || adapter.info?.device || adapter.info?.vendor || 'unknown-webgpu-adapter',
|
|
77
|
+
browser: options.browser || globalThis.navigator?.userAgent || null,
|
|
78
|
+
requestedFeatures: deviceRequest.requiredFeatures,
|
|
79
|
+
effectiveFeatures: device?.features || deviceRequest.requiredFeatures,
|
|
80
|
+
limits: device?.limits || adapter.limits,
|
|
81
|
+
timestampQuery: deviceRequest.timestampQuery,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
adapter,
|
|
86
|
+
device,
|
|
87
|
+
deviceRequest,
|
|
88
|
+
backendIdentity,
|
|
89
|
+
};
|
|
90
|
+
})();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function createWebGpuBackendIdentity(input) {
|
|
94
|
+
return {
|
|
95
|
+
kind: 'webgpu-local',
|
|
96
|
+
runtime: 'browser',
|
|
97
|
+
adapterName: input.adapterName || null,
|
|
98
|
+
browser: input.browser || null,
|
|
99
|
+
requestedFeatures: featureList(input.requestedFeatures),
|
|
100
|
+
features: featureList(input.effectiveFeatures || input.features),
|
|
101
|
+
limits: copyLimits(input.limits),
|
|
102
|
+
timestampQuery: input.timestampQuery || 'unavailable',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function validateWebGpuBackendIdentity(identity) {
|
|
107
|
+
const errors = [];
|
|
108
|
+
|
|
109
|
+
if (!identity || typeof identity !== 'object') {
|
|
110
|
+
return { ok: false, errors: ['identity must be an object'] };
|
|
111
|
+
}
|
|
112
|
+
if (identity.kind !== 'webgpu-local') errors.push('kind must be webgpu-local');
|
|
113
|
+
if (identity.runtime !== 'browser') errors.push('runtime must be browser');
|
|
114
|
+
if (!isNonEmptyString(identity.adapterName)) errors.push('adapterName must be a non-empty string');
|
|
115
|
+
if (!Array.isArray(identity.features) || identity.features.length === 0) {
|
|
116
|
+
errors.push('features must be a non-empty array');
|
|
117
|
+
}
|
|
118
|
+
if (!identity.limits || typeof identity.limits !== 'object' || Object.keys(identity.limits).length === 0) {
|
|
119
|
+
errors.push('limits must be a non-empty object');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const timestampStates = new Set(['requested', 'available', 'unavailable', 'disabled']);
|
|
123
|
+
if (!timestampStates.has(identity.timestampQuery)) {
|
|
124
|
+
errors.push('timestampQuery has unsupported state');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (identity.timestampQuery === 'requested') {
|
|
128
|
+
const requested = featureList(identity.requestedFeatures);
|
|
129
|
+
const effective = featureList(identity.features);
|
|
130
|
+
if (!requested.includes('timestamp-query')) {
|
|
131
|
+
errors.push('timestamp-query requested state must include timestamp-query in requestedFeatures');
|
|
132
|
+
}
|
|
133
|
+
if (!effective.includes('timestamp-query')) {
|
|
134
|
+
errors.push('timestamp-query requested state must include timestamp-query in features');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { ok: errors.length === 0, errors };
|
|
139
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export {
|
|
2
|
+
assertAuthoritativeRouteReceipt,
|
|
3
|
+
createWebGpuLocalRouteReceipt,
|
|
4
|
+
validateRouteReceipt,
|
|
5
|
+
WEBGPU_ROUTE_RECEIPT_SCHEMA,
|
|
6
|
+
} from './route-receipt.js';
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
defineTensorManifest,
|
|
10
|
+
validateTensorManifest,
|
|
11
|
+
} from './tensor-manifest.js';
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
createWebGpuBackendIdentity,
|
|
15
|
+
createWebGpuDeviceRequest,
|
|
16
|
+
requestBrowserWebGpuDevice,
|
|
17
|
+
validateWebGpuBackendIdentity,
|
|
18
|
+
} from './gpu-environment.js';
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
addStagedSubmitStage,
|
|
22
|
+
createStagedSubmitProfile,
|
|
23
|
+
finishStagedSubmitProfile,
|
|
24
|
+
validateStagedSubmitProfile,
|
|
25
|
+
} from './staged-profile.js';
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
createKernelProfileMetadata,
|
|
29
|
+
createRouteKernelProfileMetadata,
|
|
30
|
+
createRouteTimingMetadata,
|
|
31
|
+
validateKernelProfileMetadata,
|
|
32
|
+
validateRouteTimingMetadata,
|
|
33
|
+
} from './kernel-profile.js';
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
createWebGpuRuntimeProfile,
|
|
37
|
+
createWebGpuRuntimeProfileInput,
|
|
38
|
+
validateWebGpuRuntimeProfile,
|
|
39
|
+
WEBGPU_RUNTIME_PROFILE_SCHEMA,
|
|
40
|
+
} from './runtime-profile.js';
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
createWebGpuRouteBackpressureProfile,
|
|
44
|
+
createWebGpuRouteSchedulerProfile,
|
|
45
|
+
validateWebGpuRouteBackpressureProfile,
|
|
46
|
+
validateWebGpuRouteSchedulerProfile,
|
|
47
|
+
WEBGPU_ROUTE_BACKPRESSURE_SCHEMA,
|
|
48
|
+
WEBGPU_ROUTE_SCHEDULER_SCHEMA,
|
|
49
|
+
} from './scheduler-backpressure.js';
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
classifyWebGpuRouteReceiptEvidence,
|
|
53
|
+
classifyWebGpuRouteWorkerResultEvidence,
|
|
54
|
+
WEBGPU_ROUTE_EVIDENCE_CLASSIFICATION_SCHEMA,
|
|
55
|
+
} from './route-receipt-consumer.js';
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
MOGE_DEPTH_NORMAL_ROUTE_ID,
|
|
59
|
+
createMogeDepthNormalRouteDefinition,
|
|
60
|
+
createMogeDepthNormalRouteReceipt,
|
|
61
|
+
} from './moge-route.js';
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
SHARP_IMAGE_TO_SPLAT_ROUTE_ID,
|
|
65
|
+
createSharpImageToSplatRouteDefinition,
|
|
66
|
+
createSharpImageToSplatRouteReceipt,
|
|
67
|
+
} from './sharp-route.js';
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
KIMODO_TEXT_TO_MOTION_ROUTE_ID,
|
|
71
|
+
createKimodoTextToMotionRouteDefinition,
|
|
72
|
+
createKimodoTextToMotionRouteReceipt,
|
|
73
|
+
} from './kimodo-route.js';
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
SF3D_IMAGE_TO_MESH_ROUTE_ID,
|
|
77
|
+
createSf3dImageToMeshRouteDefinition,
|
|
78
|
+
createSf3dImageToMeshRouteReceipt,
|
|
79
|
+
} from './sf3d-route.js';
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
createWebGpuRouteSchemaContract,
|
|
83
|
+
} from './route-schema-contract.js';
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
createRouteReceiptArtifacts,
|
|
87
|
+
createRouteReceiptInputArtifact,
|
|
88
|
+
createWebGpuRouteReceiptFromArtifacts,
|
|
89
|
+
finishAndValidateRouteProfile,
|
|
90
|
+
validateRouteReceiptArtifact,
|
|
91
|
+
validateRouteReceiptBackendIdentity,
|
|
92
|
+
} from './route-receipt-helper.js';
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
assertAuthoritativeRouteWorkerResult,
|
|
96
|
+
createRouteInvocationRequest,
|
|
97
|
+
createRouteWorkerResult,
|
|
98
|
+
createWebGpuRouteRegistry,
|
|
99
|
+
defineWebGpuRoute,
|
|
100
|
+
validateRouteDefinition,
|
|
101
|
+
validateRouteInvocationRequest,
|
|
102
|
+
validateRouteWorkerResult,
|
|
103
|
+
WEBGPU_ROUTE_DEFINITION_SCHEMA,
|
|
104
|
+
WEBGPU_ROUTE_REQUEST_SCHEMA,
|
|
105
|
+
WEBGPU_ROUTE_RESULT_SCHEMA,
|
|
106
|
+
} from './route-boundary.js';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const DEFAULT_KIT_VERSION = '0.0.0';
|
|
2
|
+
const DEFAULT_TIMING_SOURCE = 'queue-submit-wait';
|
|
3
|
+
|
|
4
|
+
function isNonEmptyString(value) {
|
|
5
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function normalizeSource(input) {
|
|
9
|
+
return input && typeof input === 'object' ? input : {};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function stringOrDefault(value, fallback) {
|
|
13
|
+
return isNonEmptyString(value) ? value : fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function validateKernelProfileMetadata(kernel) {
|
|
17
|
+
const errors = [];
|
|
18
|
+
|
|
19
|
+
if (!kernel || typeof kernel !== 'object') {
|
|
20
|
+
return { ok: false, errors: ['kernel must be an object'] };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!isNonEmptyString(kernel.kitVersion)) errors.push('kernel.kitVersion must be a non-empty string');
|
|
24
|
+
if (!isNonEmptyString(kernel.profile)) errors.push('kernel.profile must be a non-empty string');
|
|
25
|
+
if (kernel.commit != null && typeof kernel.commit !== 'string') {
|
|
26
|
+
errors.push('kernel.commit must be a string or null');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { ok: errors.length === 0, errors };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createKernelProfileMetadata(input = {}, options = {}) {
|
|
33
|
+
const source = normalizeSource(input);
|
|
34
|
+
const kernel = {
|
|
35
|
+
kitVersion: stringOrDefault(source.kitVersion, options.defaultKitVersion || DEFAULT_KIT_VERSION),
|
|
36
|
+
profile: stringOrDefault(source.profile, options.defaultProfile),
|
|
37
|
+
commit: source.commit || null,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (options.requireProfile === true || options.validate === true) {
|
|
41
|
+
const result = validateKernelProfileMetadata(kernel);
|
|
42
|
+
if (!result.ok) throw new Error(result.errors.join('; '));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return kernel;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function validateRouteTimingMetadata(timing) {
|
|
49
|
+
const errors = [];
|
|
50
|
+
|
|
51
|
+
if (!timing || typeof timing !== 'object') {
|
|
52
|
+
return { ok: false, errors: ['timing metadata must be an object'] };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!Array.isArray(timing.requiredStages) || timing.requiredStages.length === 0) {
|
|
56
|
+
errors.push('requiredStages must be a non-empty array');
|
|
57
|
+
} else {
|
|
58
|
+
timing.requiredStages.forEach((stage, index) => {
|
|
59
|
+
if (!isNonEmptyString(stage)) errors.push(`requiredStages[${index}] must be a non-empty string`);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!isNonEmptyString(timing.timingSource)) errors.push('timingSource must be a non-empty string');
|
|
63
|
+
|
|
64
|
+
return { ok: errors.length === 0, errors };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createRouteTimingMetadata(input = {}, options = {}) {
|
|
68
|
+
const source = normalizeSource(input);
|
|
69
|
+
const requiredStages = Array.isArray(source.requiredStages)
|
|
70
|
+
? source.requiredStages
|
|
71
|
+
: (Array.isArray(options.requiredStages) ? options.requiredStages : []);
|
|
72
|
+
|
|
73
|
+
const timing = {
|
|
74
|
+
requiredStages: [...requiredStages],
|
|
75
|
+
timingSource: stringOrDefault(source.timingSource, options.timingSource || DEFAULT_TIMING_SOURCE),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (options.validate === true) {
|
|
79
|
+
const result = validateRouteTimingMetadata(timing);
|
|
80
|
+
if (!result.ok) throw new Error(result.errors.join('; '));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return timing;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function createRouteKernelProfileMetadata(input = {}, options = {}) {
|
|
87
|
+
const source = normalizeSource(input);
|
|
88
|
+
return {
|
|
89
|
+
kernel: createKernelProfileMetadata(source.kernel, {
|
|
90
|
+
defaultKitVersion: options.defaultKitVersion,
|
|
91
|
+
defaultProfile: options.defaultProfile,
|
|
92
|
+
requireProfile: options.requireProfile,
|
|
93
|
+
validate: options.validateKernel,
|
|
94
|
+
}),
|
|
95
|
+
...createRouteTimingMetadata(source, {
|
|
96
|
+
requiredStages: options.requiredStages,
|
|
97
|
+
timingSource: options.timingSource,
|
|
98
|
+
validate: options.validateTiming,
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { defineWebGpuRoute } from './route-boundary.js';
|
|
2
|
+
import {
|
|
3
|
+
createKernelProfileMetadata,
|
|
4
|
+
createRouteKernelProfileMetadata,
|
|
5
|
+
} from './kernel-profile.js';
|
|
6
|
+
import {
|
|
7
|
+
createRouteReceiptArtifacts,
|
|
8
|
+
createRouteReceiptInputArtifact,
|
|
9
|
+
createWebGpuRouteReceiptFromArtifacts,
|
|
10
|
+
} from './route-receipt-helper.js';
|
|
11
|
+
|
|
12
|
+
export const KIMODO_TEXT_TO_MOTION_ROUTE_ID = 'kimodo.text-to-motion.webgpu-local.v0';
|
|
13
|
+
const KIMODO_MODEL_ID = 'NVIDIA/Kimodo-SOMA-RP-v1.1';
|
|
14
|
+
const DEFAULT_KERNEL_PROFILE = 'twostage-denoiser-ddim50-fk';
|
|
15
|
+
const REQUIRED_STAGES = ['text-embedding', 'ddim-sampling', 'fk-decode', 'output-capture'];
|
|
16
|
+
const OUTPUT_ROLES = [
|
|
17
|
+
{ key: 'soma77Joints', role: 'soma77-joints', required: true },
|
|
18
|
+
{ key: 'motionClip', role: 'motion-clip', required: true },
|
|
19
|
+
{ key: 'filmstrip', role: 'filmstrip', required: false },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export function createKimodoTextToMotionRouteReceipt(input) {
|
|
23
|
+
if (!input || typeof input !== 'object') throw new Error('input must be an object');
|
|
24
|
+
if (!input.input?.artifactId || !input.input?.sha256) {
|
|
25
|
+
throw new Error('text prompt artifactId and sha256 are required');
|
|
26
|
+
}
|
|
27
|
+
if (!input.outputs?.soma77Joints) throw new Error('soma77Joints output is required');
|
|
28
|
+
if (!input.outputs?.motionClip) throw new Error('motionClip output is required');
|
|
29
|
+
|
|
30
|
+
return createWebGpuRouteReceiptFromArtifacts({
|
|
31
|
+
requestedRouteId: KIMODO_TEXT_TO_MOTION_ROUTE_ID,
|
|
32
|
+
effectiveRouteId: input.effectiveRouteId || KIMODO_TEXT_TO_MOTION_ROUTE_ID,
|
|
33
|
+
status: input.status || (input.fallbackReason ? 'fallback' : 'real'),
|
|
34
|
+
fallbackReason: input.fallbackReason || null,
|
|
35
|
+
backend: input.backend,
|
|
36
|
+
model: {
|
|
37
|
+
id: KIMODO_MODEL_ID,
|
|
38
|
+
revision: input.model?.revision,
|
|
39
|
+
weightsHash: input.model?.weightsHash,
|
|
40
|
+
dtype: input.model?.dtype || 'fp16',
|
|
41
|
+
},
|
|
42
|
+
kernel: createKernelProfileMetadata(input.kernel, { requireProfile: true }),
|
|
43
|
+
inputs: [
|
|
44
|
+
createRouteReceiptInputArtifact('text-prompt', input.input),
|
|
45
|
+
],
|
|
46
|
+
outputs: createRouteReceiptArtifacts({ artifacts: input.outputs, roles: OUTPUT_ROLES }),
|
|
47
|
+
profile: input.profile,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createKimodoTextToMotionRouteDefinition(input = {}) {
|
|
52
|
+
const routeMetadata = createRouteKernelProfileMetadata(input, {
|
|
53
|
+
defaultProfile: DEFAULT_KERNEL_PROFILE,
|
|
54
|
+
requiredStages: REQUIRED_STAGES,
|
|
55
|
+
timingSource: 'adapter-phase-wall-clock',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return defineWebGpuRoute({
|
|
59
|
+
routeId: KIMODO_TEXT_TO_MOTION_ROUTE_ID,
|
|
60
|
+
backendKind: 'webgpu-local',
|
|
61
|
+
model: {
|
|
62
|
+
id: KIMODO_MODEL_ID,
|
|
63
|
+
revision: input.model?.revision || 'SOMA-RP-v1.1',
|
|
64
|
+
dtype: input.model?.dtype || 'fp16',
|
|
65
|
+
},
|
|
66
|
+
kernel: routeMetadata.kernel,
|
|
67
|
+
inputs: [
|
|
68
|
+
{ role: 'text-prompt', required: true, artifactRequired: true, hashRequired: true },
|
|
69
|
+
],
|
|
70
|
+
outputs: [
|
|
71
|
+
{ role: 'soma77-joints', required: true, artifactRequired: true, hashRequired: true, shape: [90, 77, 3] },
|
|
72
|
+
{ role: 'motion-clip', required: true, artifactRequired: true, hashRequired: true, shape: [1] },
|
|
73
|
+
{ role: 'filmstrip', required: false, artifactRequired: true, hashRequired: true },
|
|
74
|
+
],
|
|
75
|
+
requiredFeatures: input.requiredFeatures || [],
|
|
76
|
+
requiredStages: routeMetadata.requiredStages,
|
|
77
|
+
timingSource: routeMetadata.timingSource,
|
|
78
|
+
worker: input.worker || {
|
|
79
|
+
exportName: 'runKimodoTextToMotionRoute',
|
|
80
|
+
textEmbedding: 'external-llama3-8b',
|
|
81
|
+
motionFormat: 'kimodo-soma77-explicit-joints',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|