reploid 0.0.0-stage → 0.1.1
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 +542 -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 +85 -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,74 @@
|
|
|
1
|
+
import envelopePolicy from './partition-route-envelope-policy.json' with { type: 'json' };
|
|
2
|
+
import { assertPartition as assert, partitionFailure } from './partition-contract.js';
|
|
3
|
+
|
|
4
|
+
export const ROUTE_ENVELOPE_POLICY_SCHEMA = 'reploid.route-envelope-policy/v1';
|
|
5
|
+
const digest = value => typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value);
|
|
6
|
+
const positive = value => Number.isSafeInteger(value) && value > 0;
|
|
7
|
+
const exact = (value, keys) => !!value && typeof value === 'object' && !Array.isArray(value)
|
|
8
|
+
&& Object.keys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key));
|
|
9
|
+
|
|
10
|
+
/** Every member is required and nothing is defaulted in JavaScript. A route entry declares the
|
|
11
|
+
* workloads its measurement supports: each cell bounds prompt and answer tokens (sampled tokens,
|
|
12
|
+
* the end token included) while at most `maxConcurrency` attempts run on the route.
|
|
13
|
+
* `undeclaredRoutes` states what applies to a route without an entry. */
|
|
14
|
+
export function validateRouteEnvelopePolicy(policy) {
|
|
15
|
+
assert(exact(policy, ['schema', 'undeclaredRoutes', 'routes']) && policy.schema === ROUTE_ENVELOPE_POLICY_SCHEMA
|
|
16
|
+
&& ['existing-limits', 'refuse'].includes(policy.undeclaredRoutes) && Array.isArray(policy.routes),
|
|
17
|
+
`Explicit ${ROUTE_ENVELOPE_POLICY_SCHEMA} required: schema, undeclaredRoutes, routes`);
|
|
18
|
+
const seen = new Set();
|
|
19
|
+
for (const entry of policy.routes) {
|
|
20
|
+
assert(exact(entry, ['modelIdentity', 'planId', 'evidence', 'cells']) && digest(entry.modelIdentity) && digest(entry.planId)
|
|
21
|
+
&& typeof entry.evidence === 'string' && entry.evidence.length > 0 && Array.isArray(entry.cells) && entry.cells.length > 0
|
|
22
|
+
&& entry.cells.every(cell => exact(cell, ['maxConcurrency', 'maxPromptTokens', 'maxAnswerTokens'])
|
|
23
|
+
&& positive(cell.maxConcurrency) && positive(cell.maxPromptTokens) && positive(cell.maxAnswerTokens)),
|
|
24
|
+
'Invalid route envelope entry');
|
|
25
|
+
const key = entry.modelIdentity + '/' + entry.planId;
|
|
26
|
+
assert(!seen.has(key), 'Duplicate route envelope entry'); seen.add(key);
|
|
27
|
+
}
|
|
28
|
+
return Object.freeze(structuredClone(policy));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The partitions component's own declared envelopes, checked when this module loads. */
|
|
32
|
+
export const ROUTE_ENVELOPE_POLICY = validateRouteEnvelopePolicy(envelopePolicy);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Admission against one route's declared envelope, before anything reaches its stages. An attempt
|
|
36
|
+
* is inside when some cell covering the route's concurrency with it admitted bounds its prompt,
|
|
37
|
+
* and admitting it keeps every attempt already running on the route inside a cell at that
|
|
38
|
+
* concurrency. Its answer allowance is the largest such cell's answer bound: a caller limit above
|
|
39
|
+
* it, a prompt above every cell, or a concurrency no cell covers refuses with
|
|
40
|
+
* WORKLOAD_OUTSIDE_ENVELOPE. An attempt without a caller limit receives the allowance as its
|
|
41
|
+
* generation bound, so an answer reaching it ends explicitly at the declared length.
|
|
42
|
+
*/
|
|
43
|
+
export function createRouteEnvelope({ policy, route }) {
|
|
44
|
+
const declared = validateRouteEnvelopePolicy(policy);
|
|
45
|
+
const entry = declared.routes.find(row => row.modelIdentity === route?.modelIdentity && row.planId === route?.planId) ?? null;
|
|
46
|
+
const running = new Map();
|
|
47
|
+
const fitting = (concurrency, promptTokens, answerTokens = 1) => (entry?.cells ?? []).filter(cell => concurrency <= cell.maxConcurrency
|
|
48
|
+
&& promptTokens <= cell.maxPromptTokens && answerTokens <= cell.maxAnswerTokens);
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
declared: entry !== null,
|
|
51
|
+
/** `answerTokens` is the attempt's generation bound; `explicit` marks a caller's own limit. */
|
|
52
|
+
admit({ attemptId, promptTokens, answerTokens, explicit }) {
|
|
53
|
+
assert(typeof attemptId === 'string' && !running.has(attemptId) && positive(promptTokens) && positive(answerTokens)
|
|
54
|
+
&& typeof explicit === 'boolean', 'Invalid route envelope admission');
|
|
55
|
+
if (!entry) {
|
|
56
|
+
if (declared.undeclaredRoutes === 'refuse') throw partitionFailure('WORKLOAD_OUTSIDE_ENVELOPE', { envelope: null });
|
|
57
|
+
running.set(attemptId, null);
|
|
58
|
+
return { answerTokens, envelope: null };
|
|
59
|
+
}
|
|
60
|
+
const concurrency = running.size + 1;
|
|
61
|
+
const cells = fitting(concurrency, promptTokens, explicit ? answerTokens : 1);
|
|
62
|
+
const others = [...running.values()].every(load => fitting(concurrency, load.promptTokens, load.answerTokens).length > 0);
|
|
63
|
+
if (!cells.length || !others) {
|
|
64
|
+
throw partitionFailure('WORKLOAD_OUTSIDE_ENVELOPE', { envelope: { concurrency, promptTokens, answerTokens, explicit } });
|
|
65
|
+
}
|
|
66
|
+
const allowance = Math.min(answerTokens, Math.max(...cells.map(cell => cell.maxAnswerTokens)));
|
|
67
|
+
const load = { promptTokens, answerTokens: allowance };
|
|
68
|
+
running.set(attemptId, load);
|
|
69
|
+
return { answerTokens: allowance, envelope: { concurrency, promptTokens, maxAnswerTokens: allowance, evidence: entry.evidence } };
|
|
70
|
+
},
|
|
71
|
+
release(attemptId) { running.delete(attemptId); },
|
|
72
|
+
getState: () => ({ declared: entry !== null, running: running.size })
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/** Ordinary ordered-stage execution. Doppler owns every numerical operation. */
|
|
2
|
+
import { canonicalPartitionJson, partitionActivationContract,
|
|
3
|
+
matchingPartitionActivation, validatePartitionRoute, canonicalRouteAttemptBinding,
|
|
4
|
+
sameRouteAttemptIdentity, assertCapacity, partitionFailure } from './partition-contract.js';
|
|
5
|
+
import failureMessages from '../../transport/partition-errors.json' with { type: 'json' };
|
|
6
|
+
import { MAX_UNACKNOWLEDGED_OUTPUTS, acknowledgmentOverdue } from './partition-attempt-evidence.js';
|
|
7
|
+
|
|
8
|
+
const assert = (ok, message) => { if (!ok) throw new Error(message); };
|
|
9
|
+
// A stage port reports CONNECTION_LOST only after its authenticated channel has
|
|
10
|
+
// closed. No later retry through this route can confirm that stage's cleanup.
|
|
11
|
+
const connectionLost = error => error?.code === 'CONNECTION_LOST';
|
|
12
|
+
const positive = value => Number.isSafeInteger(value) && value > 0;
|
|
13
|
+
const clone = value => structuredClone(value);
|
|
14
|
+
const same = (a, b) => canonicalPartitionJson(a) === canonicalPartitionJson(b);
|
|
15
|
+
function freeze(value) {
|
|
16
|
+
for (const child of Object.values(value)) if (child && typeof child === 'object') freeze(child);
|
|
17
|
+
return Object.freeze(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Called only by the existing runner's explicitly selected ordered-route path. */
|
|
21
|
+
export function createOrderedPartitionExecution({ runtime, plan: suppliedPlan, route: suppliedRoute,
|
|
22
|
+
stages, transport, authorize, limits, canonical = false }) {
|
|
23
|
+
assert(!canonical, 'Ordered ordinary execution does not enable canonical replay');
|
|
24
|
+
assert(typeof runtime?.serializeActivationFrame === 'function'
|
|
25
|
+
&& typeof runtime?.validateActivationFrameForPlan === 'function'
|
|
26
|
+
&& typeof runtime?.hashPartitionGeneration === 'function', 'Doppler ordered runtime required');
|
|
27
|
+
assert(typeof authorize === 'function' && typeof transport?.transferActivation === 'function',
|
|
28
|
+
'Ordered route authorization and activation transport required');
|
|
29
|
+
assert(limits && ['maxTokens', 'maxPromptTokens', 'maxActivationBytes', 'maxOutputCharacters',
|
|
30
|
+
'maxAttempts', 'maxConcurrentAttempts', 'maxStages'].every(key => positive(limits[key])),
|
|
31
|
+
'Explicit ordered route allocation limits required');
|
|
32
|
+
// The declared stage execution bound applies to every stage, the coordinator's own included.
|
|
33
|
+
assert(positive(limits.stageTimeoutMs) && limits.stageTimeoutMs <= 2147483647, 'Explicit stage execution bound required');
|
|
34
|
+
const policy = Object.freeze({ ...limits });
|
|
35
|
+
const plan = freeze(clone(suppliedPlan));
|
|
36
|
+
const route = freeze(clone(suppliedRoute));
|
|
37
|
+
assert(Array.isArray(stages) && stages.length >= 2 && stages.length <= policy.maxStages
|
|
38
|
+
&& route.stages?.length === stages.length, 'Ordered route stage count mismatch');
|
|
39
|
+
for (const [index, device] of stages.entries()) {
|
|
40
|
+
const stage = route.stages[index];
|
|
41
|
+
assert(stage.index === index && device?.id === stage.participantId
|
|
42
|
+
&& ['transportId', 'residentId', 'executionId'].every(key => device[key] === stage[key])
|
|
43
|
+
&& ['reserve', 'executeStage', 'closeAttempt'].every(key => typeof device[key] === 'function'),
|
|
44
|
+
'Ordered stage identity, admission, execution and settlement ports required');
|
|
45
|
+
}
|
|
46
|
+
const devices = stages.map(device => Object.freeze({ id: device.id,
|
|
47
|
+
transportId: device.transportId, residentId: device.residentId, executionId: device.executionId,
|
|
48
|
+
reserve: device.reserve.bind(device), executeStage: device.executeStage.bind(device),
|
|
49
|
+
closeAttempt: device.closeAttempt.bind(device) }));
|
|
50
|
+
const tensorContract = partitionActivationContract(runtime, plan);
|
|
51
|
+
const attempts = new Map();
|
|
52
|
+
let closed = false;
|
|
53
|
+
const tails = devices.map(() => Promise.resolve());
|
|
54
|
+
// Serialize calls to each stage while allowing independent conversations to
|
|
55
|
+
// occupy different stages. This does not schedule across separate runners.
|
|
56
|
+
async function lease(stage, task) {
|
|
57
|
+
const prior = tails[stage];
|
|
58
|
+
let release;
|
|
59
|
+
tails[stage] = new Promise(resolve => { release = resolve; });
|
|
60
|
+
await prior;
|
|
61
|
+
try { return await task(); } finally { release(); }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// A stage call the runner stopped waiting for (cancelled, past its bound or past the deadline)
|
|
65
|
+
// settles before that stage's cleanup, as a remote stage's own settlement waits for its
|
|
66
|
+
// execution. A call still running after another stage bound leaves that cleanup unsettled.
|
|
67
|
+
const settledCall = async (item, index) => {
|
|
68
|
+
const call = item.calls.get(index);
|
|
69
|
+
if (!call) return;
|
|
70
|
+
let timer;
|
|
71
|
+
const bound = new Promise((_, reject) => { timer = setTimeout(() => reject(partitionFailure('REQUEST_TIMEOUT')), policy.stageTimeoutMs); });
|
|
72
|
+
try { await Promise.race([call, bound]); } finally { clearTimeout(timer); }
|
|
73
|
+
item.calls.delete(index);
|
|
74
|
+
};
|
|
75
|
+
async function cleanup(item) {
|
|
76
|
+
if (item.cleaning) return item.cleaning;
|
|
77
|
+
item.cleaning = (async () => {
|
|
78
|
+
item.receipt.phase = 'settling';
|
|
79
|
+
// Acknowledgments already requested reach the stages with their closing request.
|
|
80
|
+
await item.acknowledging;
|
|
81
|
+
if (item.pendingOutputs.length) item.receipt.unacknowledgedOutputs = clone(item.pendingOutputs);
|
|
82
|
+
const acknowledged = item.acknowledged;
|
|
83
|
+
const results = await Promise.allSettled([...item.owned].map(async index => {
|
|
84
|
+
try {
|
|
85
|
+
await settledCall(item, index);
|
|
86
|
+
const closing = await devices[index].closeAttempt({ identity: item.binding, partitionIndex: index, ...(acknowledged ? { acknowledged } : {}) });
|
|
87
|
+
item.owned.delete(index);
|
|
88
|
+
item.receipt.cleanup[index] = { partitionIndex: index, status: 'settled', error: null,
|
|
89
|
+
...(closing?.receipt ? { receipt: structuredClone(closing.receipt) } : {}) };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// A lost stage stays unknown in this receipt; it is never reported as
|
|
92
|
+
// settled. It stops being retryable debt because its channel is gone.
|
|
93
|
+
const lost = connectionLost(error);
|
|
94
|
+
if (lost) { item.owned.delete(index); item.lost.add(index); }
|
|
95
|
+
item.receipt.cleanup[index] = { partitionIndex: index, status: 'unknown', error: String(error?.message || error),
|
|
96
|
+
...(lost ? { code: 'CONNECTION_LOST' } : {}) };
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
const unknown = item.owned.size + item.lost.size > 0;
|
|
101
|
+
item.receipt.phase = unknown ? 'cleanup-failed' : item.outcome;
|
|
102
|
+
item.receipt.cleanupSettledAt = unknown ? null : Date.now();
|
|
103
|
+
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
104
|
+
if (errors.length) throw Object.assign(new AggregateError(errors, 'Ordered route cleanup remains unsettled'),
|
|
105
|
+
{ code: item.owned.size ? 'CLEANUP_FAILED' : 'CONNECTION_LOST' });
|
|
106
|
+
})();
|
|
107
|
+
try { return await item.cleaning; } finally { item.cleaning = null; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* `charge` is the requester's signed binding of this attempt to its
|
|
112
|
+
* reservation; stages that sign receipts receive it with their reservation.
|
|
113
|
+
* `onOutput({ step, delta, done, receipt })` hands each final-stage output and
|
|
114
|
+
* its signed receipt to the requester side, which may answer with its signed
|
|
115
|
+
* acknowledgment pair. Acknowledgments are requested without holding the
|
|
116
|
+
* next step; the latest one travels with later stage requests and closing.
|
|
117
|
+
* A charged step s waits until acknowledgments cover s - K outputs (K from
|
|
118
|
+
* the receiver policy), at most until `acknowledgmentDeadlineAt` (ms epoch,
|
|
119
|
+
* the attempt's own deadline) and never past cancellation; then the attempt
|
|
120
|
+
* ends as ACKNOWLEDGMENT_OVERDUE and its stages close.
|
|
121
|
+
*/
|
|
122
|
+
/**
|
|
123
|
+
* `deadlineAt` (ms epoch) is the attempt's answer deadline: reaching it fails the attempt as
|
|
124
|
+
* ANSWER_DEADLINE_EXCEEDED and settles every stage, before any grant could expire.
|
|
125
|
+
*/
|
|
126
|
+
async function execute({ identity, tokenIds, generation, grants, maxTokens, signal, onDelta = () => {}, onActivity = () => {}, onStep = null,
|
|
127
|
+
charge = null, onOutput = null, acknowledgmentDeadlineAt = null, deadlineAt = null }) {
|
|
128
|
+
assert(!closed, 'Partition runner is closed');
|
|
129
|
+
signal?.throwIfAborted();
|
|
130
|
+
assert((charge === null || charge && typeof charge === 'object' && !Array.isArray(charge))
|
|
131
|
+
&& (onOutput === null || typeof onOutput === 'function' && charge !== null), 'Output acknowledgments need the attempt charge binding');
|
|
132
|
+
assert(charge === null || Number.isFinite(acknowledgmentDeadlineAt), 'A charged attempt needs its acknowledgment deadline');
|
|
133
|
+
assert(deadlineAt === null || Number.isFinite(deadlineAt), 'Invalid answer deadline');
|
|
134
|
+
// Freeze caller-owned request data before asynchronous plan validation.
|
|
135
|
+
({ identity, tokenIds, generation, grants, charge } = clone({ identity, tokenIds, generation, grants, charge }));
|
|
136
|
+
await validatePartitionRoute(runtime, plan, route);
|
|
137
|
+
assert(!closed, 'Partition runner is closed');
|
|
138
|
+
signal?.throwIfAborted();
|
|
139
|
+
const binding = canonicalRouteAttemptBinding(identity);
|
|
140
|
+
assert(same(binding.route, route), 'Attempt does not bind this complete ordered route');
|
|
141
|
+
assert(Array.isArray(tokenIds) && tokenIds.length > 0 && tokenIds.length <= policy.maxPromptTokens
|
|
142
|
+
&& tokenIds.every(id => Number.isSafeInteger(id) && id >= 0 && id < plan.vocabSize), 'Invalid ordered prompt allocation');
|
|
143
|
+
assert(positive(maxTokens) && maxTokens <= policy.maxTokens && generation?.maxTokens === maxTokens,
|
|
144
|
+
'Invalid ordered generation allocation');
|
|
145
|
+
assert(typeof onDelta === 'function' && typeof onActivity === 'function'
|
|
146
|
+
&& (onStep === null || typeof onStep === 'function'), 'Output delivery callback required');
|
|
147
|
+
assert(grants && ['execution', 'tokenContext'].every(key => Array.isArray(grants[key])
|
|
148
|
+
&& grants[key].length === devices.length && grants[key].every(value => value && typeof value === 'object'))
|
|
149
|
+
&& Array.isArray(grants.activation) && grants.activation.length === devices.length - 1
|
|
150
|
+
&& grants.activation.every(value => value && typeof value === 'object') && grants.output,
|
|
151
|
+
'Grants for every stage, adjacent edge and final output required');
|
|
152
|
+
assert(!attempts.has(binding.attemptId), 'Attempt already used; replacement requires a fresh authorized attempt');
|
|
153
|
+
// The route's attempt history and concurrent slots (unsettled cleanup keeps its slot) are capacity.
|
|
154
|
+
assertCapacity(attempts.size < policy.maxAttempts);
|
|
155
|
+
assertCapacity([...attempts.values()].filter(item => item.running || item.owned.size).length < policy.maxConcurrentAttempts);
|
|
156
|
+
const authority = freeze(clone(grants)), settings = freeze(clone(generation));
|
|
157
|
+
const coordinatorId = route.stages[0].participantId;
|
|
158
|
+
const recipients = (...ids) => [...new Set(ids)];
|
|
159
|
+
const controller = new AbortController(), deadline = new AbortController();
|
|
160
|
+
const combined = AbortSignal.any([...(signal ? [signal] : []), controller.signal, deadline.signal]);
|
|
161
|
+
const deadlineTimer = deadlineAt === null ? null
|
|
162
|
+
: setTimeout(() => deadline.abort(partitionFailure('ANSWER_DEADLINE_EXCEEDED')), Math.max(0, deadlineAt - Date.now()));
|
|
163
|
+
const receipt = { identity: binding, phase: 'admitting', reservations: [], cleanup: [], cleanupSettledAt: null, failure: null };
|
|
164
|
+
const completion = Promise.withResolvers();
|
|
165
|
+
const item = { binding, controller, receipt, owned: new Set(), lost: new Set(), calls: new Map(), running: true, settled: completion.promise,
|
|
166
|
+
outcome: 'completed', cleaning: null, pendingOutputs: [], acknowledging: Promise.resolve(), acknowledged: null, waiters: new Set() };
|
|
167
|
+
if (charge) receipt.acknowledgments = { bound: MAX_UNACKNOWLEDGED_OUTPUTS, waits: 0, waitedMs: 0 };
|
|
168
|
+
const acknowledgedCount = () => item.acknowledged?.acknowledgment.count ?? 0;
|
|
169
|
+
// Keep only a newer acknowledgment of this attempt's outputs; stages and the service verify signatures.
|
|
170
|
+
const accept = pair => {
|
|
171
|
+
const ack = pair?.acknowledgment;
|
|
172
|
+
if (ack?.attemptId === binding.attemptId && Number.isSafeInteger(ack.count) && ack.count > acknowledgedCount()
|
|
173
|
+
&& pair.receipt?.output?.count === ack.count) {
|
|
174
|
+
item.acknowledged = clone(pair);
|
|
175
|
+
item.pendingOutputs = item.pendingOutputs.filter(record => record.step >= ack.count);
|
|
176
|
+
}
|
|
177
|
+
for (const waiter of item.waiters) if (acknowledgedCount() >= waiter.target) waiter.done(null);
|
|
178
|
+
};
|
|
179
|
+
// Backpressure: the next step waits for the requester, bounded by the deadline and cancellation.
|
|
180
|
+
const awaitAcknowledged = target => new Promise((resolve, reject) => {
|
|
181
|
+
if (acknowledgedCount() >= target) { resolve(); return; }
|
|
182
|
+
const started = performance.now();
|
|
183
|
+
const waiter = { target, done: error => {
|
|
184
|
+
clearTimeout(timer); combined.removeEventListener('abort', aborted); item.waiters.delete(waiter);
|
|
185
|
+
receipt.acknowledgments.waits += 1; receipt.acknowledgments.waitedMs += performance.now() - started;
|
|
186
|
+
if (error) reject(error); else resolve();
|
|
187
|
+
} };
|
|
188
|
+
const aborted = () => waiter.done(combined.reason ?? new Error('Ordered attempt cancelled'));
|
|
189
|
+
const timer = setTimeout(() => waiter.done(acknowledgmentOverdue()), Math.max(0, acknowledgmentDeadlineAt - Date.now()));
|
|
190
|
+
item.waiters.add(waiter); combined.addEventListener('abort', aborted, { once: true });
|
|
191
|
+
if (combined.aborted) aborted();
|
|
192
|
+
});
|
|
193
|
+
const requestAcknowledgment = record => {
|
|
194
|
+
if (!onOutput) return;
|
|
195
|
+
item.pendingOutputs.push(clone(record));
|
|
196
|
+
item.acknowledging = item.acknowledging.then(async () => {
|
|
197
|
+
// Re-send the identical proof, never a new output. Recovery is independent of a UI stop,
|
|
198
|
+
// bounded by the existing stage timeout and acknowledgment deadline.
|
|
199
|
+
for (let retry = 0; retry < 3; retry++) {
|
|
200
|
+
const remaining = Math.min(policy.stageTimeoutMs, acknowledgmentDeadlineAt - Date.now());
|
|
201
|
+
if (remaining <= 0) break;
|
|
202
|
+
let timer;
|
|
203
|
+
try {
|
|
204
|
+
const pair = await Promise.race([Promise.resolve().then(() => onOutput(clone(record))),
|
|
205
|
+
new Promise((_, reject) => { timer = setTimeout(() => reject(acknowledgmentOverdue()), remaining); })]);
|
|
206
|
+
if (pair) accept(pair);
|
|
207
|
+
break;
|
|
208
|
+
} catch {} finally { clearTimeout(timer); }
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
attempts.set(binding.attemptId, item);
|
|
213
|
+
let input = [...tokenIds], position = 0, content = '', logits = null, stopReason = 'length', activationBytes = 0;
|
|
214
|
+
const continuations = devices.map(() => null), outputTokens = [], steps = [];
|
|
215
|
+
const activityStages = devices.map((_, partitionIndex) => ({partitionIndex,steps:0,stepMs:{count:0,total:0}}));
|
|
216
|
+
let failure;
|
|
217
|
+
const permit = async (action, grant, extra) => {
|
|
218
|
+
combined.throwIfAborted();
|
|
219
|
+
assert(await authorize({ action, grant, identity: binding, plan, coordinatorId, ...extra, signal: combined }) === true,
|
|
220
|
+
'Ordered route authorization declined: ' + action);
|
|
221
|
+
combined.throwIfAborted();
|
|
222
|
+
};
|
|
223
|
+
// One stage call, bounded by the declared stage execution time and stopped by cancellation or
|
|
224
|
+
// the deadline, whether or not the stage port honors its signal. The call itself is retained
|
|
225
|
+
// so the stage's cleanup waits for it to settle.
|
|
226
|
+
async function bounded(stage, call) {
|
|
227
|
+
const limit = new AbortController(), stageSignal = AbortSignal.any([combined, limit.signal]);
|
|
228
|
+
const timer = setTimeout(() => limit.abort(partitionFailure('REQUEST_TIMEOUT')), policy.stageTimeoutMs);
|
|
229
|
+
let stop;
|
|
230
|
+
const stopped = new Promise((_, reject) => { stop = () => reject(stageSignal.reason); });
|
|
231
|
+
stageSignal.addEventListener('abort', stop, { once: true });
|
|
232
|
+
const work = Promise.resolve().then(() => call(stageSignal));
|
|
233
|
+
item.calls.set(stage, work.then(() => {}, () => {}));
|
|
234
|
+
try { return await Promise.race([work, stopped]); }
|
|
235
|
+
catch (error) { throw stageSignal.aborted ? stageSignal.reason : error; }
|
|
236
|
+
finally { clearTimeout(timer); stageSignal.removeEventListener('abort', stop); }
|
|
237
|
+
}
|
|
238
|
+
async function permitRoute(step) {
|
|
239
|
+
for (let index = 0; index < devices.length; index++) {
|
|
240
|
+
await permit('mesh.execute_partition_stage', authority.execution[index], { ...step, partitionIndex: index });
|
|
241
|
+
await permit('mesh.transfer_token_context', authority.tokenContext[index], { ...step, partitionIndex: index,
|
|
242
|
+
recipients: recipients(coordinatorId, devices[index].id) });
|
|
243
|
+
if (index) await permit('mesh.transfer_intermediate_activation', authority.activation[index - 1],
|
|
244
|
+
{ ...step, fromStage: index - 1, toStage: index,
|
|
245
|
+
recipients: recipients(devices[index - 1].id, coordinatorId, devices[index].id) });
|
|
246
|
+
}
|
|
247
|
+
await permit('mesh.transfer_partition_output', authority.output, { ...step, partitionIndex: devices.length - 1,
|
|
248
|
+
recipients: recipients(devices.at(-1).id, coordinatorId, binding.requesterId) });
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const generationDigest = await runtime.hashPartitionGeneration(settings);
|
|
252
|
+
assert(/^sha256:[a-f0-9]{64}$/.test(generationDigest), 'Doppler generation digest required');
|
|
253
|
+
const initial = { step: 0, tokenPosition: 0, inputTokenCount: input.length, maxTokens, generationDigest };
|
|
254
|
+
await permitRoute(initial);
|
|
255
|
+
// Complete route admission before any model input is disclosed. Mark an
|
|
256
|
+
// owner BEFORE calling reserve: a lost acknowledgment is not a release.
|
|
257
|
+
for (let index = 0; index < devices.length; index++) {
|
|
258
|
+
combined.throwIfAborted();
|
|
259
|
+
await permit('mesh.execute_partition_stage', authority.execution[index], { ...initial, partitionIndex: index });
|
|
260
|
+
item.owned.add(index);
|
|
261
|
+
const admission = await devices[index].reserve({ identity: binding, partitionIndex: index,
|
|
262
|
+
inputTokenCount: input.length, maxTokens, generationDigest, generation: settings, executionGrant: authority.execution[index],
|
|
263
|
+
...(charge ? { charge } : {}), signal: combined });
|
|
264
|
+
assert(sameRouteAttemptIdentity(admission?.identity, binding) && admission.partitionIndex === index
|
|
265
|
+
&& admission.status === 'reserved', 'Ordered reservation acknowledgment mismatch');
|
|
266
|
+
receipt.reservations.push(clone(admission));
|
|
267
|
+
}
|
|
268
|
+
receipt.phase = 'executing';
|
|
269
|
+
for (let index = 0; index < maxTokens; index++) {
|
|
270
|
+
if (charge) await awaitAcknowledged(index - MAX_UNACKNOWLEDGED_OUTPUTS);
|
|
271
|
+
const delivery = await (async () => {
|
|
272
|
+
combined.throwIfAborted();
|
|
273
|
+
const step = { step: index, tokenPosition: position, inputTokenCount: input.length, maxTokens, generationDigest };
|
|
274
|
+
await permitRoute(step);
|
|
275
|
+
let activation, final;
|
|
276
|
+
const stageMetrics = [];
|
|
277
|
+
for (let stage = 0; stage < devices.length; stage++) {
|
|
278
|
+
let elapsedMs;
|
|
279
|
+
const result = await lease(stage, async () => {
|
|
280
|
+
// Recheck authority after queueing, immediately before disclosure.
|
|
281
|
+
await permit('mesh.execute_partition_stage', authority.execution[stage], { ...step, partitionIndex: stage });
|
|
282
|
+
await permit('mesh.transfer_token_context', authority.tokenContext[stage], { ...step, partitionIndex: stage,
|
|
283
|
+
recipients: recipients(coordinatorId, devices[stage].id) });
|
|
284
|
+
const started = performance.now();
|
|
285
|
+
const output = clone(await bounded(stage, stageSignal => devices[stage].executeStage({ identity: binding, partitionIndex: stage, ...step,
|
|
286
|
+
inputTokenIds: [...input], generation: settings, continuation: continuations[stage], activation,
|
|
287
|
+
executionGrant: authority.execution[stage], tokenContextGrant: authority.tokenContext[stage],
|
|
288
|
+
...(stage > 0 ? { incomingActivationGrant: authority.activation[stage - 1] } : {}),
|
|
289
|
+
...(stage < devices.length - 1 ? { outgoingActivationGrant: authority.activation[stage] } : {}),
|
|
290
|
+
...(stage === devices.length - 1 ? { outputGrant: authority.output } : {}),
|
|
291
|
+
...(item.acknowledged ? { acknowledged: item.acknowledged } : {}),
|
|
292
|
+
signal: stageSignal })));
|
|
293
|
+
elapsedMs = performance.now() - started;
|
|
294
|
+
return output;
|
|
295
|
+
});
|
|
296
|
+
combined.throwIfAborted();
|
|
297
|
+
assert(sameRouteAttemptIdentity(result?.identity, binding) && result.partitionIndex === stage
|
|
298
|
+
&& result.step === index && result.tokenPosition === position, 'Ordered stage result binding mismatch');
|
|
299
|
+
continuations[stage] = result.continuation;
|
|
300
|
+
stageMetrics.push({ partitionIndex: stage, elapsedMs, metrics: result.metrics ?? null });
|
|
301
|
+
if (stage === devices.length - 1) { final = result; break; }
|
|
302
|
+
assert(!('tokenId' in result) && !('delta' in result), 'Only final stage may sample or publish tokens');
|
|
303
|
+
const tensor = result.activationTensor;
|
|
304
|
+
assert(matchingPartitionActivation(tensor, tensorContract) && same(tensor.shape, [1, input.length, tensorContract.hiddenSize])
|
|
305
|
+
&& tensor.seqOffset === position && tensor.step === index && tensor.data?.byteLength <= policy.maxActivationBytes,
|
|
306
|
+
'Ordered activation shape or allocation mismatch');
|
|
307
|
+
const metadata = { identity: binding, ...step, fromStage: stage, toStage: stage + 1,
|
|
308
|
+
from: devices[stage].id, to: devices[stage + 1].id, coordinatorId,
|
|
309
|
+
recipients: recipients(devices[stage].id, coordinatorId, devices[stage + 1].id) };
|
|
310
|
+
const frame = runtime.serializeActivationFrame({ ...tensor, metadata });
|
|
311
|
+
assert(frame.byteLength <= policy.maxActivationBytes, 'Ordered activation exceeds allocation');
|
|
312
|
+
runtime.validateActivationFrameForPlan(frame, plan);
|
|
313
|
+
const sent = clone(frame);
|
|
314
|
+
await permit('mesh.transfer_intermediate_activation', authority.activation[stage], metadata);
|
|
315
|
+
const received = clone(await transport.transferActivation(frame, { identity: binding, ...step,
|
|
316
|
+
fromStage: stage, toStage: stage + 1, coordinatorId,
|
|
317
|
+
recipients: metadata.recipients, signal: combined }));
|
|
318
|
+
combined.throwIfAborted();
|
|
319
|
+
assert(received && same(received.metadata, metadata) && received.buffer instanceof ArrayBuffer
|
|
320
|
+
&& received.byteLength === sent.byteLength && received.buffer.byteLength === sent.buffer.byteLength
|
|
321
|
+
&& same(received.shape, sent.shape) && received.step === sent.step && received.seqOffset === sent.seqOffset,
|
|
322
|
+
'Ordered activation edge binding mismatch');
|
|
323
|
+
const receivedBytes = new Uint8Array(received.buffer);
|
|
324
|
+
assert(new Uint8Array(sent.buffer).every((byte, offset) => byte === receivedBytes[offset]),
|
|
325
|
+
'Ordered activation bytes differ');
|
|
326
|
+
activation = runtime.validateActivationFrameForPlan(received, plan);
|
|
327
|
+
activationBytes += sent.byteLength;
|
|
328
|
+
}
|
|
329
|
+
assert(final && Number.isSafeInteger(final.tokenId) && final.tokenId >= 0 && final.tokenId < plan.vocabSize
|
|
330
|
+
&& typeof final.delta === 'string' && typeof final.done === 'boolean'
|
|
331
|
+
&& !('tokenIds' in final) && !('content' in final), 'Final stage must return one selected token');
|
|
332
|
+
assert(content.length + final.delta.length <= policy.maxOutputCharacters, 'Ordered output allocation exceeded');
|
|
333
|
+
assert(!final.done || typeof final.stopReason === 'string' && final.stopReason.length > 0,
|
|
334
|
+
'Final stage stopping reason required');
|
|
335
|
+
assert(index + 1 < maxTokens || final.done, 'Final stage must finalize decoding at token limit');
|
|
336
|
+
await permit('mesh.transfer_partition_output', authority.output, { ...step, partitionIndex: devices.length - 1,
|
|
337
|
+
recipients: recipients(devices.at(-1).id, coordinatorId, binding.requesterId) });
|
|
338
|
+
content += final.delta;
|
|
339
|
+
outputTokens.push(final.tokenId);
|
|
340
|
+
logits = final.logits == null ? null : clone(final.logits);
|
|
341
|
+
steps.push({ ...step, tokenId: final.tokenId, stages: stageMetrics });
|
|
342
|
+
position += input.length;
|
|
343
|
+
input = [final.tokenId];
|
|
344
|
+
if (final.done) stopReason = final.stopReason;
|
|
345
|
+
return { delta: final.delta, done: final.done, receipt: final.receipt ?? null, step: { sequence: index, tokenPosition: step.tokenPosition,
|
|
346
|
+
inputTokenCount: step.inputTokenCount, tokenId: final.tokenId, delta: final.delta, done: final.done,
|
|
347
|
+
stopReason: final.done ? final.stopReason : null } };
|
|
348
|
+
})();
|
|
349
|
+
combined.throwIfAborted();
|
|
350
|
+
requestAcknowledgment({ step: index, delta: delivery.delta, done: delivery.done, receipt: delivery.receipt });
|
|
351
|
+
// Durable requester publication precedes activity, delivery and the next
|
|
352
|
+
// step; it holds no stage lease, so other conversations keep advancing.
|
|
353
|
+
if (onStep) { await onStep(clone(delivery.step)); combined.throwIfAborted(); }
|
|
354
|
+
for (const stage of steps.at(-1).stages) {
|
|
355
|
+
const row = activityStages[stage.partitionIndex];
|
|
356
|
+
row.steps++; row.stepMs.count++; row.stepMs.total += stage.elapsedMs;
|
|
357
|
+
}
|
|
358
|
+
await onActivity({ schema: 'reploid.partition-activity/v1', identity: clone(binding), sequence: index,
|
|
359
|
+
stepSummary: {count:index + 1,stages:clone(activityStages)} });
|
|
360
|
+
combined.throwIfAborted();
|
|
361
|
+
if (delivery.delta) await onDelta(delivery.delta);
|
|
362
|
+
combined.throwIfAborted();
|
|
363
|
+
if (delivery.done) break;
|
|
364
|
+
}
|
|
365
|
+
return { content, tokenIds: outputTokens, logits, stopReason,
|
|
366
|
+
execution: { schema: 'reploid.mesh.partition-execution/v3', identity: binding, activationBytes, steps, stopReason, settlement: receipt } };
|
|
367
|
+
} catch (error) {
|
|
368
|
+
failure = error;
|
|
369
|
+
// Reaching the deadline or a stage bound fails the attempt; only a caller's stop cancels it.
|
|
370
|
+
item.outcome = combined.aborted && !deadline.signal.aborted && error?.code !== 'REQUEST_TIMEOUT' ? 'cancelled' : 'failed';
|
|
371
|
+
receipt.failure = String(error?.message || error);
|
|
372
|
+
throw error;
|
|
373
|
+
} finally {
|
|
374
|
+
clearTimeout(deadlineTimer);
|
|
375
|
+
try { await cleanup(item); }
|
|
376
|
+
catch (error) {
|
|
377
|
+
// Report a disconnected contributor as such when loss is the only reason
|
|
378
|
+
// cleanup is unconfirmed; any retryable cleanup failure keeps CLEANUP_FAILED.
|
|
379
|
+
const lost = error.code === 'CONNECTION_LOST';
|
|
380
|
+
throw Object.assign(new AggregateError(failure ? [failure, error] : [error],
|
|
381
|
+
lost ? failureMessages.CONNECTION_LOST : 'Ordered route cleanup remains unsettled'), { code: lost ? 'CONNECTION_LOST' : 'CLEANUP_FAILED' }); }
|
|
382
|
+
finally {
|
|
383
|
+
if (failure && typeof failure === 'object') failure.partitionSettlement = clone(receipt);
|
|
384
|
+
item.running = false; completion.resolve();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return Object.freeze({ plan, route, execute,
|
|
389
|
+
getState: () => ({ closed, active: [...attempts.values()].filter(item => item.running).length,
|
|
390
|
+
unsettled: [...attempts.values()].filter(item => !item.running && item.owned.size).length,
|
|
391
|
+
unknown: [...attempts.values()].filter(item => item.lost.size).length,
|
|
392
|
+
attempts: clone([...attempts.values()].map(item => item.receipt)) }),
|
|
393
|
+
async retryCleanup(attemptId) {
|
|
394
|
+
const item = attempts.get(attemptId);
|
|
395
|
+
assert(item && !item.running, 'Only a finished attempt may retry cleanup');
|
|
396
|
+
await cleanup(item);
|
|
397
|
+
},
|
|
398
|
+
async close() {
|
|
399
|
+
closed = true;
|
|
400
|
+
const pending = [...attempts.values()].filter(item => item.running);
|
|
401
|
+
for (const item of pending) item.controller.abort(new Error('Partition runner closed'));
|
|
402
|
+
await Promise.all(pending.map(item => item.settled));
|
|
403
|
+
const unsettled = [...attempts.values()].filter(item => item.owned.size);
|
|
404
|
+
const results = await Promise.allSettled(unsettled.map(cleanup));
|
|
405
|
+
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
406
|
+
if (errors.length) throw Object.assign(new AggregateError(errors, 'Ordered route close remains unsettled'), { code: 'CLEANUP_FAILED' });
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|