reploid 0.0.0-stage → 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/apps/poolday/config/chat-files.json +27 -0
- package/apps/poolday/config/partition-generation.js +21 -0
- package/apps/poolday/config/partition-policy.json +74 -0
- package/apps/poolday/config/partition-releases.json +3 -0
- package/apps/poolday/config/work-profile.json +122 -0
- package/apps/poolday/host/chat-view.js +47 -0
- package/apps/poolday/host/earning/client.js +199 -0
- package/apps/poolday/host/earning/contribution.js +168 -0
- package/apps/poolday/host/partition-release.js +33 -0
- package/apps/poolday/host/work-partition-artifact-source.js +37 -0
- package/apps/poolday/host/work-partition-capsule.js +39 -0
- package/apps/poolday/host/work-partition-plan.js +167 -0
- package/apps/poolday/host/work-partition-tokenizer-guard.js +21 -0
- package/apps/poolday/infrastructure/model-storage-lock.js +22 -0
- package/apps/poolday/infrastructure/pack-release-storage.js +52 -0
- package/apps/poolday/pool/pack-operation.js +161 -0
- package/apps/poolday/pool/pack-release-policy.js +44 -0
- package/apps/poolday/pool/peer-pack-data-channel.js +217 -0
- package/apps/poolday/providers/work-adapter.js +67 -0
- package/apps/poolday/providers/work-device.js +33 -0
- package/apps/poolday/providers/work-generation-completion.js +55 -0
- package/apps/poolday/providers/work-provider.js +209 -0
- package/apps/poolday/providers/work-resident-provider.js +113 -0
- package/deploy/artifacts/doppler-gpu-0.7.1.tgz +0 -0
- package/docs/fleet-service.md +214 -0
- package/docs/native-participant.md +534 -0
- package/native-delivery.json +1174 -0
- package/package.json +29 -4
- package/packages/reploid/src/artifacts/binary.js +25 -0
- package/packages/reploid/src/artifacts/canonical-json.js +23 -0
- package/packages/reploid/src/artifacts/custody/exchange.js +402 -0
- package/packages/reploid/src/artifacts/custody/piece-acquisition.js +70 -0
- package/packages/reploid/src/artifacts/custody/runtime.js +404 -0
- package/packages/reploid/src/artifacts/custody/supply-budget.js +19 -0
- package/packages/reploid/src/artifacts/custody-certificate.js +123 -0
- package/packages/reploid/src/artifacts/delivery-attestation.js +64 -0
- package/packages/reploid/src/artifacts/generation-terminal.js +36 -0
- package/packages/reploid/src/artifacts/identity.js +91 -0
- package/packages/reploid/src/artifacts/participant-proofs.js +159 -0
- package/packages/reploid/src/artifacts/receipt.js +131 -0
- package/packages/reploid/src/artifacts/signatures.js +94 -0
- package/packages/reploid/src/artifacts/whole-model-receipt.js +42 -0
- package/packages/reploid/src/cancellation.js +44 -0
- package/packages/reploid/src/config/defaults.json +110 -0
- package/packages/reploid/src/config/index.js +209 -0
- package/packages/reploid/src/config/request-overrides.json +9 -0
- package/packages/reploid/src/config/transport-defaults.json +7 -0
- package/packages/reploid/src/hashing.js +30 -0
- package/packages/reploid/src/measurement/capacity-utilization.js +321 -0
- package/packages/reploid/src/measurement/index.js +5 -0
- package/packages/reploid/src/measurement/readiness-records.js +310 -0
- package/packages/reploid/src/mesh/computer-identity.js +50 -0
- package/packages/reploid/src/mesh/computer-identity.policy.json +8 -0
- package/packages/reploid/src/mesh/contribution.js +141 -0
- package/packages/reploid/src/mesh/generation-recovery.js +58 -0
- package/packages/reploid/src/mesh/gpu-signature-shader.js +27 -0
- package/packages/reploid/src/mesh/gpu-signature.js +136 -0
- package/packages/reploid/src/mesh/gpu-signature.policy.json +11 -0
- package/packages/reploid/src/mesh/index.js +66 -0
- package/packages/reploid/src/mesh/partitions/automatic-ordered-partitions.js +540 -0
- package/packages/reploid/src/mesh/partitions/automatic-partitions.js +298 -0
- package/packages/reploid/src/mesh/partitions/partition-attempt-evidence.js +282 -0
- package/packages/reploid/src/mesh/partitions/partition-attempt-queue-policy.json +6 -0
- package/packages/reploid/src/mesh/partitions/partition-attempt-queue.js +112 -0
- package/packages/reploid/src/mesh/partitions/partition-canonical-contract.js +167 -0
- package/packages/reploid/src/mesh/partitions/partition-canonical-execution.js +168 -0
- package/packages/reploid/src/mesh/partitions/partition-chat-canonical.js +49 -0
- package/packages/reploid/src/mesh/partitions/partition-chat.js +125 -0
- package/packages/reploid/src/mesh/partitions/partition-contract.js +192 -0
- package/packages/reploid/src/mesh/partitions/partition-device-admission.js +96 -0
- package/packages/reploid/src/mesh/partitions/partition-discovery.js +96 -0
- package/packages/reploid/src/mesh/partitions/partition-entry-publication.js +183 -0
- package/packages/reploid/src/mesh/partitions/partition-entry.js +434 -0
- package/packages/reploid/src/mesh/partitions/partition-generation-request.js +33 -0
- package/packages/reploid/src/mesh/partitions/partition-grants.js +277 -0
- package/packages/reploid/src/mesh/partitions/partition-link.js +103 -0
- package/packages/reploid/src/mesh/partitions/partition-network.js +109 -0
- package/packages/reploid/src/mesh/partitions/partition-ordered-chat.js +290 -0
- package/packages/reploid/src/mesh/partitions/partition-ordered-peer.js +415 -0
- package/packages/reploid/src/mesh/partitions/partition-ordered-publication.js +100 -0
- package/packages/reploid/src/mesh/partitions/partition-peer.js +314 -0
- package/packages/reploid/src/mesh/partitions/partition-placement.js +143 -0
- package/packages/reploid/src/mesh/partitions/partition-preparation-progress.js +25 -0
- package/packages/reploid/src/mesh/partitions/partition-receiver-policy.json +7 -0
- package/packages/reploid/src/mesh/partitions/partition-reservations.js +75 -0
- package/packages/reploid/src/mesh/partitions/partition-route-envelope-policy.json +16 -0
- package/packages/reploid/src/mesh/partitions/partition-route-envelope.js +74 -0
- package/packages/reploid/src/mesh/partitions/partition-route-execution.js +409 -0
- package/packages/reploid/src/mesh/partitions/partition-route-network.js +179 -0
- package/packages/reploid/src/mesh/partitions/partition-route-ranking.js +268 -0
- package/packages/reploid/src/mesh/partitions/partition-runner.js +319 -0
- package/packages/reploid/src/mesh/partitions/partition-stage-assignment.js +109 -0
- package/packages/reploid/src/mesh/partitions/partition-stage-commit.js +103 -0
- package/packages/reploid/src/mesh/partitions/partition-step-receiver.js +291 -0
- package/packages/reploid/src/mesh/partitions/partition-summary.js +122 -0
- package/packages/reploid/src/mesh/partitions/preparation-owner.js +40 -0
- package/packages/reploid/src/mesh/partitions/resident-error-codes.json +10 -0
- package/packages/reploid/src/mesh/partitions/resident-partition.js +333 -0
- package/packages/reploid/src/mesh/partitions/resident-route.js +46 -0
- package/packages/reploid/src/mesh/partitions/signed-resident-factory.js +59 -0
- package/packages/reploid/src/mesh/peer-generation.js +896 -0
- package/packages/reploid/src/mesh/peer-identity.js +87 -0
- package/packages/reploid/src/mesh/placement-beliefs.js +107 -0
- package/packages/reploid/src/mesh/preparation-progress.js +46 -0
- package/packages/reploid/src/mesh/provider-hardware.js +11 -0
- package/packages/reploid/src/mesh/remote-generation-requests.js +53 -0
- package/packages/reploid/src/mesh/swarm-coordination.js +140 -0
- package/packages/reploid/src/mesh/web-access-network.js +112 -0
- package/packages/reploid/src/mesh/web-access-policy.json +17 -0
- package/packages/reploid/src/retrieval/citations.js +156 -0
- package/packages/reploid/src/retrieval/claim-support.js +113 -0
- package/packages/reploid/src/retrieval/costs.js +101 -0
- package/packages/reploid/src/retrieval/fixed-provider.js +34 -0
- package/packages/reploid/src/retrieval/index.js +20 -0
- package/packages/reploid/src/retrieval/lexical-support.js +27 -0
- package/packages/reploid/src/retrieval/model-reviewer.js +67 -0
- package/packages/reploid/src/retrieval/page-fetch.json +10 -0
- package/packages/reploid/src/retrieval/page-text.js +182 -0
- package/packages/reploid/src/retrieval/passage.js +37 -0
- package/packages/reploid/src/retrieval/policy.json +14 -0
- package/packages/reploid/src/retrieval/retrieval.js +23 -0
- package/packages/reploid/src/retrieval/review-queue.js +76 -0
- package/packages/reploid/src/retrieval/sources.js +263 -0
- package/packages/reploid/src/retrieval/support-review.js +155 -0
- package/packages/reploid/src/retrieval/text.js +21 -0
- package/packages/reploid/src/rules/contribution.rules.json +8 -0
- package/packages/reploid/src/rules/routing.rules.json +24 -0
- package/packages/reploid/src/transport/assignment.js +571 -0
- package/packages/reploid/src/transport/bounded-channel-writer.js +46 -0
- package/packages/reploid/src/transport/index.js +11 -0
- package/packages/reploid/src/transport/introduction-admission.js +24 -0
- package/packages/reploid/src/transport/introduction-bootstrap.js +112 -0
- package/packages/reploid/src/transport/introduction-policy.json +12 -0
- package/packages/reploid/src/transport/introduction-records.js +90 -0
- package/packages/reploid/src/transport/introductions.js +209 -0
- package/packages/reploid/src/transport/partition-data-channel.js +421 -0
- package/packages/reploid/src/transport/partition-errors.json +16 -0
- package/packages/reploid/src/transport/retry-policy.js +36 -0
- package/packages/reploid/src/transport/room.js +502 -0
- package/packages/reploid/src/transport/sdp-fingerprint.js +5 -0
- package/packages/reploid/src/transport/signaling.js +571 -0
- package/packages/reploid/src/transport/swarm.js +1247 -0
- package/packages/reploid/src/transport/tool-offer-channel.js +137 -0
- package/server/browser-roots.js +42 -0
- package/server/browser-web-worker.js +94 -0
- package/server/chat-completions.js +45 -0
- package/server/fleet/browser-bridge.js +145 -0
- package/server/fleet/contract.js +42 -0
- package/server/fleet/credit-evidence.js +103 -0
- package/server/fleet/credit-recovery.js +175 -0
- package/server/fleet/credits.js +178 -0
- package/server/fleet/execution-admission.js +32 -0
- package/server/fleet/native-host.js +817 -0
- package/server/fleet/placement.js +132 -0
- package/server/fleet/requester.js +346 -0
- package/server/fleet/service.js +311 -0
- package/server/fleet/terminal-policy.js +83 -0
- package/server/fleet/text-api.js +94 -0
- package/server/hashing.js +35 -0
- package/server/http-request-owner.js +49 -0
- package/server/messages.js +57 -0
- package/server/native-canonical-epochs.js +90 -0
- package/server/native-change-log.js +40 -0
- package/server/native-earning.js +203 -0
- package/server/native-generation-journal.js +46 -0
- package/server/native-model-config.js +57 -0
- package/server/native-model-files.js +382 -0
- package/server/native-model-origin.js +54 -0
- package/server/native-model-origins.json +12 -0
- package/server/native-node.js +83 -0
- package/server/native-owner/controls.js +162 -0
- package/server/native-owner/monitor.js +251 -0
- package/server/native-owner/policy.js +273 -0
- package/server/native-owner/rate.js +92 -0
- package/server/native-participant.js +866 -0
- package/server/native-readiness-log.js +30 -0
- package/server/native-release-storage.js +64 -0
- package/server/native-shader-cache.js +369 -0
- package/server/native-supply-budget.js +66 -0
- package/server/native-whole-earning.js +67 -0
- package/server/native-whole-model-fill.js +182 -0
- package/server/native-whole-model.js +130 -0
- package/server/public-web-worker.js +66 -0
- package/server/responses.js +92 -0
- package/server/search/errors.js +47 -0
- package/server/search/page-fetch.js +185 -0
- package/server/text-api.js +83 -0
- package/server/text-recovery.js +45 -0
- package/server/text-request-options.js +23 -0
- package/server/text-requester.js +231 -0
- package/server/token-auth.js +9 -0
- package/server/whole-model-requester.js +99 -0
- package/shared/config/chat-models.json +4685 -0
- package/shared/config/doppler-execution-contracts.js +32 -0
- package/shared/config/doppler-execution-contracts.json +43 -0
- package/shared/config/doppler-generation-contract.js +223 -0
- package/shared/config/doppler-local-models.js +79 -0
- package/shared/config/doppler-package.json +7 -0
- package/shared/config/model-manifests/a0dc69e917133a82780823d17fc771c0f6b882d7c86c571deeaef45be2ff108b.json +6434 -0
- package/shared/config/model-manifests/e985e3cdad60d0cb57d84b436161e66ab5b3f6589a168de6f00b4610e5f1fb59.json +16534 -0
- package/shared/config/model-manifests/fa6eb7bbc3ba63571f8cede780b877d9272bde948697af93dd3d33837cb46137.json +7175 -0
- package/shared/config/model-pieces/qwen-0-8b-pieces.json +1506 -0
- package/shared/config/model-pieces/qwen-2b-pieces.json +4800 -0
- package/shared/config/model-pieces/qwen35b-q4_0-pieces.json +28880 -0
- package/shared/config/reploid-library.json +49 -0
- package/shared/config/swarm-bootstrap.json +21 -0
- package/shared/infrastructure/doppler-runtime-service.js +261 -0
- package/shared/pool/executable-pack.js +87 -0
- package/shared/pool/pack-operation-adapters.js +124 -0
- package/shared/pool/pack-operation-policy.js +75 -0
- package/shared/pool/pool-config.json +2280 -0
- package/shared/vendor/reploid/adapters/doppler-session.js +372 -0
- package/shared/vendor/reploid/artifacts/binary.js +25 -0
- package/shared/vendor/reploid/artifacts/canonical-json.js +23 -0
- package/shared/vendor/reploid/artifacts/delivery-attestation.js +64 -0
- package/shared/vendor/reploid/artifacts/identity.js +91 -0
- package/shared/vendor/reploid/artifacts/manifest-identity.js +17 -0
- package/shared/vendor/reploid/artifacts/participant-proofs.js +159 -0
- package/shared/vendor/reploid/artifacts/signatures.js +94 -0
- package/shared/vendor/reploid/config/defaults.json +110 -0
- package/shared/vendor/reploid/config/index.js +209 -0
- package/shared/vendor/reploid/config/request-overrides.json +9 -0
- package/shared/vendor/reploid/hashing.js +30 -0
- package/shared/vendor/reploid/mesh/generation-context-cache.js +97 -0
- package/shared/vendor/reploid/mesh/partitions/partition-attempt-evidence.js +282 -0
- package/shared/vendor/reploid/mesh/partitions/partition-contract.js +192 -0
- package/shared/vendor/reploid/mesh/partitions/partition-receiver-policy.json +7 -0
- package/shared/vendor/reploid/mesh/partitions/partition-route-ranking.js +268 -0
- package/shared/vendor/reploid/mesh/preparation-progress.js +46 -0
- package/shared/vendor/reploid/mesh/provider-hardware.js +11 -0
- package/shared/vendor/reploid/rules/routing.rules.json +24 -0
- package/shared/vendor/reploid/transport/partition-errors.json +16 -0
- package/README.md +0 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "reploid.application-library-profile/v1",
|
|
3
|
+
"profile": {
|
|
4
|
+
"schema": "reploid.profile/v1",
|
|
5
|
+
"id": "legacy-application",
|
|
6
|
+
"config": {
|
|
7
|
+
"tools": {
|
|
8
|
+
"allowed": [
|
|
9
|
+
"ReadFile",
|
|
10
|
+
"WriteFile",
|
|
11
|
+
"Promote",
|
|
12
|
+
"LoadModule"
|
|
13
|
+
],
|
|
14
|
+
"allowDynamic": true
|
|
15
|
+
},
|
|
16
|
+
"mesh": {
|
|
17
|
+
"executeJobs": true,
|
|
18
|
+
"generationTimeoutMs": 300000,
|
|
19
|
+
"routing": "local-first"
|
|
20
|
+
},
|
|
21
|
+
"webrtc": {
|
|
22
|
+
"protocol": "swarm/v1",
|
|
23
|
+
"maxMessageBytes": 262144,
|
|
24
|
+
"rtcConfig": {
|
|
25
|
+
"iceServers": [
|
|
26
|
+
{
|
|
27
|
+
"urls": "stun:stun.l.google.com:19302"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"urls": "stun:stun1.l.google.com:19302"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"iceTransportPolicy": "all"
|
|
34
|
+
},
|
|
35
|
+
"maxPendingRemoteIceCandidates": 32,
|
|
36
|
+
"dataChannelOptions": {
|
|
37
|
+
"ordered": true,
|
|
38
|
+
"maxRetransmits": null
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"persistence": {
|
|
44
|
+
"storeName": "files",
|
|
45
|
+
"version": 1,
|
|
46
|
+
"keyPath": "path",
|
|
47
|
+
"openTimeoutMs": 10000
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "reploid.swarm-bootstrap/v1",
|
|
3
|
+
"publicRoomId": "reploid-swarm-public",
|
|
4
|
+
"publicJoinMarker": "reploid-public-namespace-not-a-secret-v1",
|
|
5
|
+
"path": "/swarm",
|
|
6
|
+
"signalingUrl": "wss://reploid-swarm.reploid.workers.dev/swarm",
|
|
7
|
+
"rtcConfigUrl": "https://reploid-swarm.reploid.workers.dev/rtc-config",
|
|
8
|
+
"rtcRefreshSkewMs": 60000,
|
|
9
|
+
"autoConnect": true,
|
|
10
|
+
"retryMs": [1000, 2000, 4000, 8000, 16000, 30000],
|
|
11
|
+
"healthCheckMs": 10000,
|
|
12
|
+
"allowLoopbackOrigins": true,
|
|
13
|
+
"allowedOrigins": ["https://replo.id", "https://reploid.web.app", "https://reploid.firebaseapp.com", "http://localhost:8000", "http://127.0.0.1:8000"],
|
|
14
|
+
"server": {
|
|
15
|
+
"heartbeatInterval": 30000, "peerTimeout": 60000, "maxMessageBytes": 65536,
|
|
16
|
+
"maxPeersPerRoom": 64, "maxPeersTotal": 256, "maxRooms": 128,
|
|
17
|
+
"maxMessagesPerWindow": 512, "rateLimitWindowMs": 1000,
|
|
18
|
+
"maxConnections": 320, "maxConnectionsPerWindow": 128,
|
|
19
|
+
"connectionWindowMs": 1000, "joinTimeoutMs": 10000
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Reploid infrastructure owner for Doppler module imports and scoped sessions.
|
|
3
|
+
*
|
|
4
|
+
* Callers own policy. This service owns runtime identity, session lifetime,
|
|
5
|
+
* and one session per explicit Reploid scope.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
DOPPLER_BROWSER_RUNTIME_VERSION,
|
|
10
|
+
DOPPLER_KERNEL_BASE_URL,
|
|
11
|
+
DOPPLER_MODULE_URL
|
|
12
|
+
} from '../config/doppler-local-models.js';
|
|
13
|
+
import { resolveDopplerExecutionContract } from '../config/doppler-execution-contracts.js';
|
|
14
|
+
import { createDopplerModuleLoader, createDopplerStreamAccumulator, resolveDopplerRuntime } from '../vendor/reploid/adapters/doppler-session.js';
|
|
15
|
+
import { GENERATION_CONTRACT, validateGenerationInput, resolveGenerationOptions } from '../config/doppler-generation-contract.js';
|
|
16
|
+
|
|
17
|
+
// This generated, validation-only bundle comes from Doppler's installed public
|
|
18
|
+
// export. Reploid authors no sampling fields, defaults, or range rules.
|
|
19
|
+
export const DOPPLER_GENERATION_CONTRACT = GENERATION_CONTRACT;
|
|
20
|
+
export function validateDopplerGenerationRequest({ input, options }) {
|
|
21
|
+
validateGenerationInput(input);
|
|
22
|
+
return resolveGenerationOptions(options);
|
|
23
|
+
}
|
|
24
|
+
export function assertDopplerGenerationContract(session) {
|
|
25
|
+
if (JSON.stringify(session.generationContract) !== JSON.stringify(GENERATION_CONTRACT)) {
|
|
26
|
+
throw new Error('Doppler generation contract mismatch; install the qualified package matching Reploid before generation.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_SCOPE = 'reploid-default';
|
|
31
|
+
|
|
32
|
+
const normalizedScope = (value) => {
|
|
33
|
+
const scope = String(value || DEFAULT_SCOPE).trim();
|
|
34
|
+
if (!scope) throw new Error('Doppler session scope must be a non-empty string');
|
|
35
|
+
return scope;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const normalizedVersion = (module) => (
|
|
39
|
+
module?.DOPPLER_VERSION
|
|
40
|
+
|| module?.default?.DOPPLER_VERSION
|
|
41
|
+
|| null
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const isVitestRuntime = () => (
|
|
45
|
+
globalThis.process?.env?.VITEST === 'true'
|
|
46
|
+
|| Boolean(globalThis.__POOL_DOPPLER_RUNTIME_TEST)
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const legacyTestSession = (handle) => ({
|
|
50
|
+
...handle,
|
|
51
|
+
schema: 'doppler.scoped-session/v1',
|
|
52
|
+
async generate(input, options = {}) {
|
|
53
|
+
if (typeof handle.generate === 'function') return handle.generate(input, options);
|
|
54
|
+
if (typeof handle.chatText === 'function') return handle.chatText(input, options);
|
|
55
|
+
throw new Error('Legacy Doppler test handle does not expose generation');
|
|
56
|
+
},
|
|
57
|
+
async *stream(input, options = {}) {
|
|
58
|
+
const iterable = typeof handle.chat === 'function'
|
|
59
|
+
? handle.chat(input, options)
|
|
60
|
+
: handle.generate(input, options);
|
|
61
|
+
for await (const chunk of iterable) {
|
|
62
|
+
if (typeof chunk !== 'string') {
|
|
63
|
+
throw new Error('Doppler chat stream emitted a non-text chunk');
|
|
64
|
+
}
|
|
65
|
+
yield {
|
|
66
|
+
schema: 'doppler.generation-event/v1',
|
|
67
|
+
type: 'text-delta',
|
|
68
|
+
text: chunk,
|
|
69
|
+
observationPolicyId: 'demo/always-on'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
yield {
|
|
73
|
+
schema: 'doppler.generation-event/v1',
|
|
74
|
+
type: 'complete',
|
|
75
|
+
outputText: '',
|
|
76
|
+
observationPolicyId: 'demo/always-on'
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
async close() {
|
|
80
|
+
await handle.close?.();
|
|
81
|
+
await handle.unload?.();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const resolveLegacyTestRuntime = (module) => {
|
|
86
|
+
if (!isVitestRuntime()) return null;
|
|
87
|
+
const load = module?.load || module?.doppler?.load;
|
|
88
|
+
if (typeof load !== 'function') return null;
|
|
89
|
+
return {
|
|
90
|
+
async open(source, options) {
|
|
91
|
+
const loaded = await load(source, options);
|
|
92
|
+
return legacyTestSession(
|
|
93
|
+
loaded?.handle || loaded?.model || loaded?.session || loaded?.pipeline || loaded
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const defaultLoadModule = async () => {
|
|
100
|
+
const kernelBase = globalThis.REPLOID_DOPPLER_KERNEL_BASE_URL
|
|
101
|
+
|| DOPPLER_KERNEL_BASE_URL;
|
|
102
|
+
globalThis.__DOPPLER_KERNEL_BASE_PATH__ = String(kernelBase).replace(/\/+$/, '');
|
|
103
|
+
const moduleUrl = globalThis.REPLOID_DOPPLER_MODULE_URL
|
|
104
|
+
|| DOPPLER_MODULE_URL;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
return await import(moduleUrl);
|
|
108
|
+
} catch (cause) {
|
|
109
|
+
throw new Error(`Doppler import failed at ${moduleUrl}. Inspect the first failed module request.`, { cause });
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export function createReploidDopplerRuntimeService({
|
|
114
|
+
loadModule = defaultLoadModule,
|
|
115
|
+
expectedVersion = DOPPLER_BROWSER_RUNTIME_VERSION
|
|
116
|
+
} = {}) {
|
|
117
|
+
// The library's session contract memoizes the module and retries after a failed load.
|
|
118
|
+
const modules = createDopplerModuleLoader({ loadModule });
|
|
119
|
+
const sessions = new Map();
|
|
120
|
+
const inFlight = new Map();
|
|
121
|
+
|
|
122
|
+
const getModule = async (provided = null) => provided || modules.load();
|
|
123
|
+
|
|
124
|
+
const assertModule = (module) => {
|
|
125
|
+
const version = normalizedVersion(module);
|
|
126
|
+
const legacyTestRuntime = resolveLegacyTestRuntime(module);
|
|
127
|
+
if (legacyTestRuntime) {
|
|
128
|
+
return { runtime: legacyTestRuntime, version };
|
|
129
|
+
}
|
|
130
|
+
if (expectedVersion && version !== expectedVersion) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Reploid requires Doppler ${expectedVersion}; loaded ${version || 'unidentified'}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const runtime = resolveDopplerRuntime(module);
|
|
136
|
+
if (typeof runtime?.open !== 'function') {
|
|
137
|
+
throw new Error('Doppler runtime does not expose the scoped dr.open API');
|
|
138
|
+
}
|
|
139
|
+
return { runtime, version };
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const releaseEntry = async (key) => {
|
|
143
|
+
const entry = sessions.get(key);
|
|
144
|
+
if (!entry) return;
|
|
145
|
+
entry.quarantined = true;
|
|
146
|
+
await entry.session.close?.();
|
|
147
|
+
if (sessions.get(key) === entry) sessions.delete(key);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const close = (scope = DEFAULT_SCOPE) => {
|
|
151
|
+
const key = normalizedScope(scope);
|
|
152
|
+
const pending = inFlight.get(key);
|
|
153
|
+
const task = (async () => {
|
|
154
|
+
if (pending) await pending.catch(() => null);
|
|
155
|
+
await releaseEntry(key);
|
|
156
|
+
})();
|
|
157
|
+
inFlight.set(key, task);
|
|
158
|
+
task.finally(() => { if (inFlight.get(key) === task) inFlight.delete(key); }).catch(() => null);
|
|
159
|
+
return task;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const open = async ({
|
|
163
|
+
scope = DEFAULT_SCOPE,
|
|
164
|
+
source,
|
|
165
|
+
options = {},
|
|
166
|
+
module = null
|
|
167
|
+
} = {}) => {
|
|
168
|
+
const key = normalizedScope(scope);
|
|
169
|
+
if (source == null) throw new Error('Doppler scoped session source is required');
|
|
170
|
+
const previous = inFlight.get(key);
|
|
171
|
+
const task = (async () => {
|
|
172
|
+
if (previous) await previous.catch(() => null);
|
|
173
|
+
const sourceKey = typeof source === 'string' ? source : JSON.stringify(source);
|
|
174
|
+
const current = sessions.get(key);
|
|
175
|
+
if (current?.session?.loaded && !current.quarantined && current.sourceKey === sourceKey) return current.session;
|
|
176
|
+
await releaseEntry(key);
|
|
177
|
+
const loadedModule = await getModule(module);
|
|
178
|
+
const { runtime } = assertModule(loadedModule);
|
|
179
|
+
const session = await runtime.open(source, options);
|
|
180
|
+
if (!session || session.schema !== 'doppler.scoped-session/v1') {
|
|
181
|
+
await session?.close?.();
|
|
182
|
+
throw new Error('Doppler dr.open returned an invalid scoped session');
|
|
183
|
+
}
|
|
184
|
+
sessions.set(key, { session, sourceKey });
|
|
185
|
+
return session;
|
|
186
|
+
})();
|
|
187
|
+
inFlight.set(key, task);
|
|
188
|
+
task.finally(() => { if (inFlight.get(key) === task) inFlight.delete(key); }).catch(() => null);
|
|
189
|
+
return task;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const signedRuntime = (module, contract) => {
|
|
193
|
+
const version = normalizedVersion(module);
|
|
194
|
+
if (expectedVersion && version !== expectedVersion) throw new Error(`Reploid requires Doppler ${expectedVersion}; loaded ${version || 'unidentified'}`);
|
|
195
|
+
const runtime = typeof module?.[contract.openMethod] === 'function' ? module : resolveDopplerRuntime(module);
|
|
196
|
+
if (typeof runtime?.[contract.openMethod] !== 'function') throw new Error(`Doppler runtime does not expose public ${contract.openMethod}; signed model loading cannot fall back to dr.open`);
|
|
197
|
+
return { runtime, version };
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const openSigned = (contract, { scope = DEFAULT_SCOPE, source, options = {}, module = null } = {}) => {
|
|
201
|
+
const key = normalizedScope(scope);
|
|
202
|
+
const previous = inFlight.get(key);
|
|
203
|
+
const task = (async () => {
|
|
204
|
+
if (previous) await previous.catch(() => null);
|
|
205
|
+
if (source == null) throw new Error('Signed Pack source is required');
|
|
206
|
+
const loadedModule = await getModule(module);
|
|
207
|
+
const { runtime } = signedRuntime(loadedModule, contract);
|
|
208
|
+
await releaseEntry(key);
|
|
209
|
+
const session = await runtime[contract.openMethod](source, options);
|
|
210
|
+
if (session?.schema !== contract.sessionSchema || !session.loaded) {
|
|
211
|
+
await session?.close?.();
|
|
212
|
+
throw new Error(`Doppler ${contract.openMethod} returned an invalid signed model session`);
|
|
213
|
+
}
|
|
214
|
+
sessions.set(key, { session, sourceKey: null });
|
|
215
|
+
return session;
|
|
216
|
+
})();
|
|
217
|
+
inFlight.set(key, task);
|
|
218
|
+
task.finally(() => { if (inFlight.get(key) === task) inFlight.delete(key); }).catch(() => null);
|
|
219
|
+
return task;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return Object.freeze({
|
|
223
|
+
open,
|
|
224
|
+
// Reverify lifecycle policy on every Pack opening; never reuse stale eligibility.
|
|
225
|
+
openPack: options => openSigned(resolveDopplerExecutionContract('doppler.pack/v2'), options),
|
|
226
|
+
openCapsule: options => openSigned(resolveDopplerExecutionContract('doppler.capsule/v2'), options),
|
|
227
|
+
async createStreamAccumulator(request, { bindingSchema, runtimeVersion }) {
|
|
228
|
+
const module = await getModule();
|
|
229
|
+
const { runtime, version } = signedRuntime(module, resolveDopplerExecutionContract(bindingSchema));
|
|
230
|
+
if (version !== runtimeVersion) throw new Error('Stream consumer runtime version mismatch');
|
|
231
|
+
return createDopplerStreamAccumulator(runtime, request);
|
|
232
|
+
},
|
|
233
|
+
close,
|
|
234
|
+
get(scope = DEFAULT_SCOPE) {
|
|
235
|
+
const entry = sessions.get(normalizedScope(scope));
|
|
236
|
+
return entry?.quarantined ? null : entry?.session || null;
|
|
237
|
+
},
|
|
238
|
+
async closeAll() {
|
|
239
|
+
const pending = [...inFlight.values()];
|
|
240
|
+
if (pending.length) await Promise.allSettled(pending);
|
|
241
|
+
const results = await Promise.allSettled([...sessions.keys()].map(close));
|
|
242
|
+
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
243
|
+
if (errors.length) throw new AggregateError(errors, 'Doppler session cleanup failed');
|
|
244
|
+
},
|
|
245
|
+
async prepare(module = null, { bindingSchema = null } = {}) {
|
|
246
|
+
const loadedModule = await getModule(module);
|
|
247
|
+
const { version } = bindingSchema === null ? assertModule(loadedModule)
|
|
248
|
+
: signedRuntime(loadedModule, resolveDopplerExecutionContract(bindingSchema));
|
|
249
|
+
return { ok: true, version };
|
|
250
|
+
},
|
|
251
|
+
resetModuleForTests() {
|
|
252
|
+
modules.reset();
|
|
253
|
+
const active = [...sessions.values()].map((entry) => entry.session);
|
|
254
|
+
sessions.clear();
|
|
255
|
+
inFlight.clear();
|
|
256
|
+
void Promise.allSettled(active.map((session) => session.close?.()));
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export const DopplerRuntimeService = createReploidDopplerRuntimeService();
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Poolday's exact execution requirement. Doppler owns signature and byte verification. */
|
|
2
|
+
import { resolveDopplerExecutionContract } from '../config/doppler-execution-contracts.js';
|
|
3
|
+
|
|
4
|
+
const DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
5
|
+
|
|
6
|
+
// Doppler canonical observation encoding, including typed arrays as value arrays.
|
|
7
|
+
const canonical = (value) => {
|
|
8
|
+
if (value === null || typeof value !== 'object') {
|
|
9
|
+
if (typeof value === 'number' && !Number.isFinite(value)) throw new Error('Non-finite Pack evidence');
|
|
10
|
+
return JSON.stringify(value);
|
|
11
|
+
}
|
|
12
|
+
if (ArrayBuffer.isView(value)) return canonical(Array.from(value));
|
|
13
|
+
if (Array.isArray(value)) return `[${value.map((item) => canonical(item) ?? 'null').join(',')}]`;
|
|
14
|
+
return `{${Object.keys(value).filter((key) => value[key] !== undefined).sort((a, b) => a.localeCompare(b)).map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
|
|
15
|
+
};
|
|
16
|
+
export const hashDopplerEvidence = async (value) => {
|
|
17
|
+
const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonical(value))));
|
|
18
|
+
return `sha256:${Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function validateExecutablePack(binding) {
|
|
22
|
+
const reasons = [];
|
|
23
|
+
if (!binding || typeof binding !== 'object' || Array.isArray(binding)) return { ok: false, reasons: ['Signed executable Pack binding is required'] };
|
|
24
|
+
let contract;
|
|
25
|
+
try { contract = resolveDopplerExecutionContract(binding.schema); }
|
|
26
|
+
catch { return { ok: false, reasons: ['Unsupported executable Pack schema'] }; }
|
|
27
|
+
const IDENTITY_FIELDS = contract.identityFields;
|
|
28
|
+
if (Object.keys(binding).some((key) => ![...IDENTITY_FIELDS, 'requiredOperation', 'acceptedTargetPlanDigests', 'artifacts'].includes(key))) reasons.push('Unknown executable Pack binding field');
|
|
29
|
+
if (typeof binding[IDENTITY_FIELDS[1]] !== 'string' || !binding[IDENTITY_FIELDS[1]]) reasons.push('Pack id is required');
|
|
30
|
+
for (const field of IDENTITY_FIELDS.slice(2)) if (!DIGEST.test(binding[field])) reasons.push(`Invalid Pack ${field}`);
|
|
31
|
+
// Custody binds an operation name; the installed execution adapter admits it.
|
|
32
|
+
// Serving model bytes must not imply that a peer can execute their operations.
|
|
33
|
+
if (typeof binding.requiredOperation !== 'string' || !/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(binding.requiredOperation)) reasons.push('A public Pack operation name is required');
|
|
34
|
+
if (!Array.isArray(binding.acceptedTargetPlanDigests) || !binding.acceptedTargetPlanDigests.length
|
|
35
|
+
|| binding.acceptedTargetPlanDigests.some((digest) => !DIGEST.test(digest))
|
|
36
|
+
|| new Set(binding.acceptedTargetPlanDigests).size !== binding.acceptedTargetPlanDigests.length) reasons.push('Accepted TargetPlan digests must be explicit and unique');
|
|
37
|
+
if (!Array.isArray(binding.artifacts) || !binding.artifacts.length) reasons.push('Pack artifact closure is required');
|
|
38
|
+
else {
|
|
39
|
+
const ids = new Set();
|
|
40
|
+
for (const artifact of binding.artifacts) {
|
|
41
|
+
if (!artifact || typeof artifact.artifactId !== 'string' || !artifact.artifactId || ids.has(artifact.artifactId)
|
|
42
|
+
|| !DIGEST.test(artifact.hash) || !Number.isSafeInteger(artifact.sizeBytes) || artifact.sizeBytes < 0
|
|
43
|
+
|| typeof artifact.path !== 'string' || typeof artifact.role !== 'string') reasons.push('Invalid or duplicated Pack artifact');
|
|
44
|
+
ids.add(artifact?.artifactId);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { ok: !reasons.length, reasons };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function executablePacksMatch(left, right) {
|
|
51
|
+
return canonical(left ?? null) === canonical(right ?? null);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function assertPackExecutionEvidence(binding, evidence) {
|
|
55
|
+
const validation = validateExecutablePack(binding);
|
|
56
|
+
if (!validation.ok) throw new Error(validation.reasons.join('; '));
|
|
57
|
+
if (await hashDopplerEvidence(binding.artifacts) !== binding.artifactClosureDigest) throw new Error('Pack artifact closure digest mismatch');
|
|
58
|
+
const contract = resolveDopplerExecutionContract(binding.schema);
|
|
59
|
+
if (contract.identityFields.some((field) => binding[field] !== evidence?.[contract.receiptIdentity]?.[field])) throw new Error('Doppler did not execute the required exact Pack');
|
|
60
|
+
if (!binding.acceptedTargetPlanDigests.includes(evidence.targetPlanDigest)) throw new Error('Doppler selected an unaccepted TargetPlan');
|
|
61
|
+
const expected = binding.artifacts.map(({ artifactId, hash, sizeBytes }) => ({ artifactId, hash, sizeBytes }));
|
|
62
|
+
if (canonical(expected) !== canonical(evidence.artifactReceipts)) throw new Error('Doppler artifact receipts do not close the required Pack');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function assertPackSession(binding, session) {
|
|
66
|
+
const contract = resolveDopplerExecutionContract(binding?.schema);
|
|
67
|
+
if (session?.schema !== contract.sessionSchema || !session.loaded
|
|
68
|
+
|| (typeof session.executeOperation !== 'function' && typeof session[binding?.requiredOperation] !== 'function')) throw new Error('Doppler did not open the required public Pack session');
|
|
69
|
+
await assertPackExecutionEvidence(binding, { [contract.receiptIdentity]: session[contract.sessionIdentity], targetPlanDigest: session.selectedTargetPlanDigest, artifactReceipts: session.verification?.artifactReceipts });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function assertPackReceipt(binding, receipt, { assignment = null, sequence, options, result } = {}) {
|
|
73
|
+
await assertPackExecutionEvidence(binding, receipt);
|
|
74
|
+
if (receipt.schema !== resolveDopplerExecutionContract(binding.schema).sequenceReceiptSchema || binding.requiredOperation !== 'encodeSequence'
|
|
75
|
+
|| receipt.operation !== binding.requiredOperation) throw new Error('Invalid Doppler Pack operation receipt');
|
|
76
|
+
const { receiptDigest, ...payload } = receipt;
|
|
77
|
+
if (receiptDigest !== await hashDopplerEvidence(payload)) throw new Error('Doppler Pack receipt digest mismatch');
|
|
78
|
+
if (assignment && receipt.assignmentHash !== await hashDopplerEvidence(assignment)) throw new Error('Doppler Pack receipt assignment mismatch');
|
|
79
|
+
if (sequence !== undefined) {
|
|
80
|
+
const { signal, ...request } = options;
|
|
81
|
+
if (receipt.inputHash !== await hashDopplerEvidence({ sequence, options: request })) throw new Error('Doppler Pack receipt input mismatch');
|
|
82
|
+
}
|
|
83
|
+
if (result) {
|
|
84
|
+
const { phase, receipt: ignoredReceipt, dopplerProviderReceipt, ...output } = result;
|
|
85
|
+
if (receipt.outputHash !== await hashDopplerEvidence(output)) throw new Error('Doppler Pack receipt output mismatch');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/** Implementation functions bound to checked operation policy; no networking imports. */
|
|
2
|
+
import poolConfig from './pool-config.json' with { type: 'json' };
|
|
3
|
+
import { resolvePackOperationDefinitions, assertOperationFields } from './pack-operation-policy.js';
|
|
4
|
+
import { validateDopplerGenerationRequest } from '../infrastructure/doppler-runtime-service.js';
|
|
5
|
+
const requireValue = (value, message) => { if (!value) throw new Error(`Pack operation: ${message}`); };
|
|
6
|
+
const text = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
7
|
+
const vector = (value) => Array.isArray(value) && value.length > 0 && value.every(Number.isFinite);
|
|
8
|
+
const tokenIds = (value) => Array.isArray(value) && value.every((id) => Number.isSafeInteger(id) && id >= 0);
|
|
9
|
+
const closeVector = (left, right, policy) => vector(left) && vector(right) && left.length === right.length
|
|
10
|
+
&& left.every((value, index) => Math.abs(value - right[index]) <= policy.absoluteTolerance + policy.relativeTolerance * Math.abs(right[index]));
|
|
11
|
+
|
|
12
|
+
export const PACK_OPERATION_IMPLEMENTATIONS = Object.freeze({
|
|
13
|
+
'generate.v1': {
|
|
14
|
+
contractVersion: 1,
|
|
15
|
+
validateRequest: validateDopplerGenerationRequest,
|
|
16
|
+
validateOutput(output, request) {
|
|
17
|
+
requireValue(typeof output.text === 'string' && tokenIds(output.tokenIds) && output.tokenIds.length <= request.options.maxTokens, 'invalid generation output');
|
|
18
|
+
},
|
|
19
|
+
compare(output, reference, policy) {
|
|
20
|
+
return output.text === reference.text;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
'embed.v1': {
|
|
24
|
+
contractVersion: 1,
|
|
25
|
+
validateRequest({ input, options }) {
|
|
26
|
+
requireValue(Array.isArray(input.texts) && input.texts.length > 0 && input.texts.every(text), 'texts required');
|
|
27
|
+
requireValue(input.application && typeof input.application === 'object'
|
|
28
|
+
&& !Array.isArray(input.application), 'signed embedding application required');
|
|
29
|
+
},
|
|
30
|
+
validateOutput(output, request, { completed }) {
|
|
31
|
+
requireValue(Array.isArray(output.embeddings) && output.embeddings.length > 0
|
|
32
|
+
&& output.embeddings.length <= request.input.texts.length
|
|
33
|
+
&& (!completed || output.embeddings.length === request.input.texts.length)
|
|
34
|
+
&& output.embeddings.every((item) => vector(item.embedding)
|
|
35
|
+
&& item.embedding.length === output.embeddings[0].embedding.length), 'invalid embedding batch');
|
|
36
|
+
},
|
|
37
|
+
compare(output, reference, policy) {
|
|
38
|
+
return output.embeddings.length === reference.embeddings.length
|
|
39
|
+
&& output.embeddings.every((item, index) => closeVector(item.embedding, reference.embeddings[index].embedding, policy));
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
'rerank.v1': {
|
|
43
|
+
contractVersion: 1,
|
|
44
|
+
validateRequest({ input, options }) {
|
|
45
|
+
requireValue(text(input.query) && Array.isArray(input.documents) && input.documents.length > 0
|
|
46
|
+
&& input.documents.every(text) && input.application && typeof input.application === 'object', 'rerank input and signed application required');
|
|
47
|
+
},
|
|
48
|
+
validateOutput(output, request) {
|
|
49
|
+
const evidence = output.evidence;
|
|
50
|
+
const count = request.input.documents.length;
|
|
51
|
+
requireValue(evidence?.schema === 'doppler_rerank_evidence/v1'
|
|
52
|
+
&& Array.isArray(evidence.scores) && evidence.scores.length === count
|
|
53
|
+
&& evidence.scores.every((item, index) => item.index === index && Number.isFinite(item.score))
|
|
54
|
+
&& Array.isArray(evidence.ranking) && evidence.ranking.length === count
|
|
55
|
+
&& new Set(evidence.ranking.map((item) => item.index)).size === count
|
|
56
|
+
&& evidence.ranking.every((item, index) => Number.isSafeInteger(item.index) && item.index >= 0 && item.index < count
|
|
57
|
+
&& item.rank === index + 1 && item.score === evidence.scores[item.index].score), 'invalid rerank output');
|
|
58
|
+
},
|
|
59
|
+
compare(output, reference, policy) {
|
|
60
|
+
return JSON.stringify(output.evidence.ranking.map((item) => item.index)) === JSON.stringify(reference.evidence.ranking.map((item) => item.index))
|
|
61
|
+
&& closeVector(output.evidence.scores.map((item) => item.score), reference.evidence.scores.map((item) => item.score), policy);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
'encodeSequence.v1': {
|
|
65
|
+
contractVersion: 1,
|
|
66
|
+
validateRequest({ input, options }) {
|
|
67
|
+
requireValue(text(input.sequence) && typeof options.includeTokenEmbeddings === 'boolean'
|
|
68
|
+
&& typeof options.includeLogits === 'boolean', 'sequence and explicit output flags required');
|
|
69
|
+
},
|
|
70
|
+
validateOutput(output, request) {
|
|
71
|
+
requireValue(tokenIds(output.tokens) && output.tokens.length > 0
|
|
72
|
+
&& Number.isSafeInteger(output.embeddingDim) && output.embeddingDim > 0
|
|
73
|
+
&& vector(output.pooledEmbedding) && output.pooledEmbedding.length === output.embeddingDim, 'invalid sequence geometry');
|
|
74
|
+
requireValue(Array.isArray(output.tokenMask) && output.tokenMask.length === output.tokens.length
|
|
75
|
+
&& output.tokenMask.every((value) => value === 0 || value === 1), 'invalid sequence token mask');
|
|
76
|
+
requireValue(request.options.includeTokenEmbeddings
|
|
77
|
+
? vector(output.tokenEmbeddings) && output.tokenEmbeddings.length === output.tokens.length * output.embeddingDim
|
|
78
|
+
: output.tokenEmbeddings === null, 'sequence token output disagrees with requested outputs');
|
|
79
|
+
requireValue(request.options.includeLogits
|
|
80
|
+
? Number.isSafeInteger(output.vocabSize) && output.vocabSize > 0 && vector(output.logits) && output.logits.length === output.tokens.length * output.vocabSize
|
|
81
|
+
: output.logits === null, 'sequence logits disagree with requested outputs');
|
|
82
|
+
},
|
|
83
|
+
compare(output, reference, policy) {
|
|
84
|
+
return JSON.stringify(output.tokens) === JSON.stringify(reference.tokens)
|
|
85
|
+
&& JSON.stringify(output.tokenMask) === JSON.stringify(reference.tokenMask)
|
|
86
|
+
&& ['pooledEmbedding', 'tokenEmbeddings', 'logits'].every((key) => output[key] === null || reference[key] === null
|
|
87
|
+
? output[key] === reference[key] : closeVector(output[key], reference[key], policy));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export function createPackOperationRegistry({ definitions = poolConfig.operations,
|
|
93
|
+
comparisons = poolConfig.operationComparisonPolicies, implementations = PACK_OPERATION_IMPLEMENTATIONS } = {}) {
|
|
94
|
+
const resolved = resolvePackOperationDefinitions(definitions, comparisons);
|
|
95
|
+
const entries = Object.entries(resolved.definitions).map(([id, definition]) => {
|
|
96
|
+
const implementation = implementations[definition.adapterId] && Object.freeze({ ...implementations[definition.adapterId] });
|
|
97
|
+
requireValue(implementation && ['validateRequest', 'validateOutput', 'compare'].every(method => typeof implementation[method] === 'function'),
|
|
98
|
+
`unknown configured adapter ${definition.adapterId}`);
|
|
99
|
+
requireValue(['inputContract', 'optionsContract', 'outputContract'].every(name => definition[name].version === implementation.contractVersion),
|
|
100
|
+
`unsupported contract version for ${definition.adapterId}`);
|
|
101
|
+
const policy = Object.freeze({ definition, comparisons: Object.freeze(Object.fromEntries(
|
|
102
|
+
definition.comparisonPolicyIds.map(id => [id, resolved.comparisons[id]]))) });
|
|
103
|
+
const adapter = Object.freeze({ definition, policy, implementation, version: definition.version, workload: definition.workload,
|
|
104
|
+
validateRequest(request) {
|
|
105
|
+
assertOperationFields(request.input, definition.inputContract, 'input');
|
|
106
|
+
assertOperationFields(request.options, definition.optionsContract, 'options');
|
|
107
|
+
return implementation.validateRequest(request, definition);
|
|
108
|
+
},
|
|
109
|
+
validateOutput(output, request, context) { return implementation.validateOutput(output, request, context, definition); },
|
|
110
|
+
compare(output, reference, policy) {
|
|
111
|
+
requireValue(definition.comparisonPolicyIds.includes(policy.rule), 'comparison rule is outside configured operation policy');
|
|
112
|
+
const rule = resolved.comparisons[policy.rule];
|
|
113
|
+
for (const field of rule.requiredFields) requireValue(Object.hasOwn(policy, field), `${field} required`);
|
|
114
|
+
for (const field of rule.nonnegativeFields) requireValue(Number.isFinite(policy[field]) && policy[field] >= 0, `${field} must be finite and nonnegative`);
|
|
115
|
+
return implementation.compare(output, reference, policy, definition);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return [id, adapter];
|
|
119
|
+
});
|
|
120
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Missing implementations and malformed checked-in definitions fail at module startup.
|
|
124
|
+
createPackOperationRegistry();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Pure configuration validation. No execution, networking, or inferred policy. */
|
|
2
|
+
import { DOPPLER_GENERATION_CONTRACT } from '../infrastructure/doppler-runtime-service.js';
|
|
3
|
+
const assert = (ok, message) => { if (!ok) throw new Error(`Pack operation policy: ${message}`); };
|
|
4
|
+
const identifier = value => typeof value === 'string' && /^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(value);
|
|
5
|
+
const strings = value => Array.isArray(value) && value.every(identifier) && new Set(value).size === value.length;
|
|
6
|
+
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
const positive = value => Number.isSafeInteger(value) && value > 0;
|
|
8
|
+
const fieldTypes = Object.freeze({ 'positive-integer': positive, 'finite-number': Number.isFinite,
|
|
9
|
+
boolean: value => typeof value === 'boolean' });
|
|
10
|
+
|
|
11
|
+
export function freezeOperationPolicy(value, depth = 0) {
|
|
12
|
+
assert(depth <= 64, 'configuration depth exceeded');
|
|
13
|
+
if (value === null || ['string', 'boolean'].includes(typeof value)) return value;
|
|
14
|
+
if (typeof value === 'number') { assert(Number.isFinite(value), 'non-finite configuration'); return value; }
|
|
15
|
+
assert(object(value) || Array.isArray(value), 'JSON configuration required');
|
|
16
|
+
assert(Array.isArray(value) || [Object.prototype, null].includes(Object.getPrototypeOf(value)), 'plain configuration required');
|
|
17
|
+
return Object.freeze(Array.isArray(value) ? value.map(child => freezeOperationPolicy(child, depth + 1))
|
|
18
|
+
: Object.fromEntries(Object.entries(value).map(([key, child]) => [key, freezeOperationPolicy(child, depth + 1)])));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolvePackOperationDefinitions(definitions, comparisons) {
|
|
22
|
+
const resolved = freezeOperationPolicy(definitions), rules = freezeOperationPolicy(comparisons);
|
|
23
|
+
assert(object(resolved) && Object.keys(resolved).length > 0 && object(rules), 'operation definitions and comparison policies required');
|
|
24
|
+
for (const [id, rule] of Object.entries(rules)) {
|
|
25
|
+
assert(identifier(id) && object(rule) && strings(rule.requiredFields) && strings(rule.nonnegativeFields)
|
|
26
|
+
&& rule.nonnegativeFields.every(field => rule.requiredFields.includes(field)), `invalid comparison policy ${id}`);
|
|
27
|
+
}
|
|
28
|
+
for (const [id, definition] of Object.entries(resolved)) {
|
|
29
|
+
assert(identifier(id) && definition.schema === 'reploid.pool.operation-definition/v1'
|
|
30
|
+
&& positive(definition.version) && identifier(definition.adapterId) && identifier(definition.workload), `invalid definition ${id}`);
|
|
31
|
+
assert(definition.dopplerOperation?.name === id && definition.dopplerOperation.version === definition.version,
|
|
32
|
+
`${id}: operation aliases require an explicit Doppler contract; implicit renaming forbidden`);
|
|
33
|
+
for (const name of ['inputContract', 'optionsContract']) {
|
|
34
|
+
const contract = definition[name];
|
|
35
|
+
if (contract?.runtimeContract !== undefined) {
|
|
36
|
+
assert(id === 'generate' && positive(contract.version)
|
|
37
|
+
&& contract.runtimeContract === DOPPLER_GENERATION_CONTRACT.schema
|
|
38
|
+
&& Object.keys(contract).every(key => ['version', 'runtimeContract'].includes(key)), `${id}: invalid runtime-owned ${name}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
assert(positive(contract?.version) && strings(contract.allowedFields) && strings(contract.requiredFields)
|
|
42
|
+
&& contract.requiredFields.every(field => contract.allowedFields.includes(field)), `${id}: invalid ${name}`);
|
|
43
|
+
assert(object(contract.fieldTypes) && Object.entries(contract.fieldTypes).every(([field, type]) => contract.allowedFields.includes(field)
|
|
44
|
+
&& Object.hasOwn(fieldTypes, type)), `${id}: unknown field type`);
|
|
45
|
+
}
|
|
46
|
+
assert(positive(definition.outputContract?.version) && typeof definition.streaming?.partial === 'boolean', `${id}: output and streaming contracts required`);
|
|
47
|
+
for (const field of ['maxInputBytes', 'maxOutputBytes', 'maxStreamBytes', 'maxEvents', 'maxJobMs']) {
|
|
48
|
+
assert(positive(definition.maximumLimits?.[field]), `${id}: maximumLimits.${field} required`);
|
|
49
|
+
}
|
|
50
|
+
assert(strings(definition.comparisonPolicyIds) && definition.comparisonPolicyIds.length > 0
|
|
51
|
+
&& definition.comparisonPolicyIds.every(policyId => Object.hasOwn(rules, policyId)), `${id}: unknown comparison policy`);
|
|
52
|
+
assert(strings(definition.inputClasses?.local) && definition.inputClasses.local.length > 0 && strings(definition.inputClasses.remote)
|
|
53
|
+
&& definition.inputClasses.remote.every(value => definition.inputClasses.local.includes(value)), `${id}: explicit input classes required`);
|
|
54
|
+
assert(definition.inputClasses.defaultRemote === null || definition.inputClasses.remote.includes(definition.inputClasses.defaultRemote), `${id}: explicit remote input class or null required`);
|
|
55
|
+
}
|
|
56
|
+
return Object.freeze({ definitions: resolved, comparisons: rules });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function assertOperationFields(value, contract, name) {
|
|
60
|
+
if (contract.runtimeContract === DOPPLER_GENERATION_CONTRACT.schema) {
|
|
61
|
+
assert(object(value), `${name}: object required`);
|
|
62
|
+
return; // The operation adapter delegates input/options validation to Doppler.
|
|
63
|
+
}
|
|
64
|
+
assert(object(value) && Object.keys(value).every(field => contract.allowedFields.includes(field)), `${name}: unexpected input or option fields`);
|
|
65
|
+
for (const field of contract.requiredFields) assert(Object.hasOwn(value, field), `${name}.${field} required`);
|
|
66
|
+
for (const [field, type] of Object.entries(contract.fieldTypes)) {
|
|
67
|
+
if (Object.hasOwn(value, field)) assert(fieldTypes[type](value[field]), `${name}.${field}: ${type} required`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function assertOperationLimits(limits, definition) {
|
|
72
|
+
for (const [field, maximum] of Object.entries(definition.maximumLimits)) {
|
|
73
|
+
if (Object.hasOwn(limits, field)) assert(positive(limits[field]) && limits[field] <= maximum, `${field} exceeds configured operation limit`);
|
|
74
|
+
}
|
|
75
|
+
}
|