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,314 @@
|
|
|
1
|
+
import { createOrderedPartitionPeer } from './partition-ordered-peer.js';
|
|
2
|
+
import failureMessages from '../../transport/partition-errors.json' with { type: 'json' };
|
|
3
|
+
import { createPartitionDataChannel } from '../../transport/partition-data-channel.js';
|
|
4
|
+
import { createCanonicalPartitionContract } from './partition-canonical-contract.js';
|
|
5
|
+
import { createPartitionStepReceiver } from './partition-step-receiver.js';
|
|
6
|
+
import { assertPartition as assert, samePartitionIdentity, partitionFingerprint,
|
|
7
|
+
partitionActivationContract, matchingPartitionActivation } from './partition-contract.js';
|
|
8
|
+
|
|
9
|
+
/** Authenticated A-to-B partition RPC. Hosts establish channel identities before constructing it. */
|
|
10
|
+
export function createPartitionPeer({ channel, localParticipantId, remoteParticipantId, runtime, plan, planId,
|
|
11
|
+
modelIdentity, authority, contributor = null, limits, receiverLimits, canonical = false, ordered = null }) {
|
|
12
|
+
if (ordered) return createOrderedPartitionPeer({ channel, localParticipantId, remoteParticipantId, runtime, plan, planId,
|
|
13
|
+
modelIdentity, authority, contributor, limits, receiverLimits, canonical, ordered });
|
|
14
|
+
plan = structuredClone(plan); receiverLimits = structuredClone(receiverLimits);
|
|
15
|
+
assert(runtime?.deserializeActivationFrame && authority?.verify, 'Partition codecs and grant verifier required');
|
|
16
|
+
const activationContract = partitionActivationContract(runtime, plan);
|
|
17
|
+
assert(!contributor || contributor.index === 1 && contributor.id === localParticipantId, 'Remote serving requires the local B contributor');
|
|
18
|
+
assert(typeof canonical === 'boolean', 'Explicit canonical peer capability flag required');
|
|
19
|
+
const canonicalContract = canonical ? createCanonicalPartitionContract({ runtime, plan, planId, modelIdentity }) : null;
|
|
20
|
+
const canonicalStage = canonical && contributor ? contributor.getCanonicalStage() : null;
|
|
21
|
+
assert(!canonical || !contributor || canonicalStage?.participantId === localParticipantId && canonicalStage.partitionIndex === 1,
|
|
22
|
+
'Canonical serving requires the bound stage signing authority');
|
|
23
|
+
const active = new Map(), outputCharacters = new Map(), listeners = new Set();
|
|
24
|
+
const reservations = new Map();
|
|
25
|
+
let remoteDescriptor = null, remoteCanonical = null, closing = null;
|
|
26
|
+
const canonicalRequest = metadata => canonicalContract !== null && metadata.schema === canonicalContract.schema;
|
|
27
|
+
const binding = identity => identity?.modelId === plan.modelId && identity.modelIdentity === modelIdentity
|
|
28
|
+
&& identity.planId === planId;
|
|
29
|
+
const validFrame = metadata => {
|
|
30
|
+
const frame = metadata.frame;
|
|
31
|
+
return Number.isSafeInteger(metadata.maxTokens) && metadata.maxTokens > 0
|
|
32
|
+
&& metadata.maxTokens <= metadata.grant?.claim?.limits?.maxTokens && metadata.step < metadata.maxTokens
|
|
33
|
+
&& frame?.schema === activationContract.schema && matchingPartitionActivation(frame, activationContract, true)
|
|
34
|
+
&& frame.step === metadata.step && frame.seqOffset === metadata.tokenPosition
|
|
35
|
+
&& Array.isArray(frame.shape) && frame.shape.length === 3 && frame.shape[0] === 1
|
|
36
|
+
&& frame.shape[1] === metadata.inputTokenCount && frame.shape[2] === plan.hiddenSize
|
|
37
|
+
&& Number.isSafeInteger(frame.byteLength) && frame.byteLength > 0
|
|
38
|
+
&& frame.byteLength === frame.shape[1] * frame.shape[2] * activationContract.elementStrideBytes
|
|
39
|
+
&& samePartitionIdentity(frame.metadata, metadata.identity)
|
|
40
|
+
&& frame.metadata.maxTokens === metadata.maxTokens
|
|
41
|
+
&& frame.metadata.generationDigest === metadata.generationDigest
|
|
42
|
+
&& metadata.generation?.maxTokens === metadata.maxTokens
|
|
43
|
+
&& Array.isArray(metadata.inputTokenIds) && metadata.inputTokenIds.length === metadata.inputTokenCount
|
|
44
|
+
&& metadata.inputTokenIds.every(id => Number.isSafeInteger(id) && id >= 0 && id < plan.vocabSize)
|
|
45
|
+
&& frame.metadata.from === metadata.identity.participantA && frame.metadata.to === metadata.identity.participantB;
|
|
46
|
+
};
|
|
47
|
+
const verify = async (metadata, action, settlement = false) => {
|
|
48
|
+
if (metadata.schema !== undefined) {
|
|
49
|
+
if (!canonicalRequest(metadata)) return false;
|
|
50
|
+
try {
|
|
51
|
+
const attempt = canonicalContract.validateBinding(metadata);
|
|
52
|
+
const observed = contributor ? { stage: canonicalStage, generation: contributor.getCanonicalGeneration() } : remoteCanonical;
|
|
53
|
+
if (!observed || runtime.metadataDigest('reploid.canonical-stage/v1', attempt.stages[1])
|
|
54
|
+
!== runtime.metadataDigest('reploid.canonical-stage/v1', observed.stage)
|
|
55
|
+
|| runtime.metadataDigest('reploid.canonical-generation/v1', metadata.grant.claim.generation)
|
|
56
|
+
!== runtime.metadataDigest('reploid.canonical-generation/v1', observed.generation)) return false;
|
|
57
|
+
} catch { return false; }
|
|
58
|
+
const phase = metadata.operation === 'reserve' || metadata.operation === 'admit'
|
|
59
|
+
|| metadata.publication?.schema === 'doppler.admission-publication/v1' ? 'admit' : 'step';
|
|
60
|
+
const request = { identity: metadata.identity, canonicalAttempt: metadata.canonicalAttempt,
|
|
61
|
+
generationId: metadata.generationId, phase, step: metadata.step,
|
|
62
|
+
...(metadata.inputTokenCount === undefined ? {} : { inputTokenCount: metadata.inputTokenCount }),
|
|
63
|
+
...(metadata.frame ? { activationBytes: metadata.frame.byteLength } : {}) };
|
|
64
|
+
if (!await authority.verify(metadata.grant, { ...request, action }, { settlement })) return false;
|
|
65
|
+
return settlement || authority.verify(metadata.grant, { ...request, action: 'mesh.transfer_token_context' });
|
|
66
|
+
}
|
|
67
|
+
if (!settlement && (!metadata.generation || await partitionFingerprint(metadata.generation) !== metadata.generationDigest)) return false;
|
|
68
|
+
const request = { identity: metadata.identity, step: metadata.step, inputTokenCount: metadata.inputTokenCount,
|
|
69
|
+
generationDigest: metadata.generationDigest, activationBytes: metadata.frame?.byteLength };
|
|
70
|
+
if (!await authority.verify(metadata.grant, { ...request, action }, { settlement })) return false;
|
|
71
|
+
return settlement || authority.verify(metadata.grant, { ...request, action: 'mesh.transfer_token_context' });
|
|
72
|
+
};
|
|
73
|
+
const receiver = createPartitionStepReceiver({ limits: receiverLimits,
|
|
74
|
+
canonical: canonicalStage ? { runtime, stage: canonicalStage, now: () => performance.now(),
|
|
75
|
+
verifyPublication: (request, retained) => canonicalContract.verifyPublication(request, retained),
|
|
76
|
+
execute: async (request, { signal }) => {
|
|
77
|
+
assert(contributor.canAccept(request.identity), 'Canonical output contributor is not ready');
|
|
78
|
+
if (request.phase === 'admit') return contributor.admitCanonicalPrompt({ identity: request.identity,
|
|
79
|
+
tokenIds: request.inputTokenIds, generationId: request.generationId, signal });
|
|
80
|
+
const activation = canonicalContract.validatePayload(request, request.payload);
|
|
81
|
+
const result = await contributor.executeCanonicalGroup1({ identity: request.identity, canonicalAttempt: request.canonicalAttempt,
|
|
82
|
+
sequence: request.sequence, generationId: request.generationId, baseCursorId: request.baseCursorId,
|
|
83
|
+
step: request.step, tokenPosition: request.tokenPosition, inputTokenCount: request.inputTokenCount,
|
|
84
|
+
inputTokenIds: request.inputTokenIds, activation, continuation: request.continuation, signal,
|
|
85
|
+
executionGrant: request.grant, outputGrant: request.grant });
|
|
86
|
+
assert(result?.commit && result.transition, 'Canonical output stage did not return its authenticated transition');
|
|
87
|
+
const { logits: _logits, ...output } = result;
|
|
88
|
+
return output;
|
|
89
|
+
} } : undefined,
|
|
90
|
+
authorize: request => verify(request, 'mesh.execute_partition_b'),
|
|
91
|
+
fingerprint: request => {
|
|
92
|
+
if (canonicalRequest(request)) return canonicalContract.fingerprint(request);
|
|
93
|
+
const { payload, ...metadata } = request;
|
|
94
|
+
return partitionFingerprint(metadata, payload);
|
|
95
|
+
},
|
|
96
|
+
executeStep: async (request, { signal }) => {
|
|
97
|
+
assert(contributor?.canAccept ? contributor.canAccept(request.identity) : contributor?.getState().ready, 'Partition B is not ready');
|
|
98
|
+
const frame = { ...request.frame, buffer: request.payload.slice().buffer };
|
|
99
|
+
const activation = runtime.validateActivationFrameForPlan
|
|
100
|
+
? runtime.validateActivationFrameForPlan(frame, plan) : runtime.deserializeActivationFrame(frame);
|
|
101
|
+
const result = await contributor.executeGroup1({ identity: request.identity,
|
|
102
|
+
step: request.step, tokenPosition: request.tokenPosition, inputTokenCount: request.inputTokenCount,
|
|
103
|
+
maxTokens: request.maxTokens,
|
|
104
|
+
generation: request.generation, inputTokenIds: request.inputTokenIds,
|
|
105
|
+
activation, continuation: request.continuation, signal,
|
|
106
|
+
executionGrant: request.grant, outputGrant: request.grant });
|
|
107
|
+
assert(samePartitionIdentity(result?.identity, request.identity) && result.step === request.step
|
|
108
|
+
&& result.tokenPosition === request.tokenPosition && Number.isSafeInteger(result.tokenId)
|
|
109
|
+
&& result.tokenId >= 0 && result.tokenId < plan.vocabSize && typeof result.done === 'boolean'
|
|
110
|
+
&& typeof result.delta === 'string' && result.delta.length <= request.grant.claim.limits.maxOutputCharacters,
|
|
111
|
+
'Doppler partition response identity or token mismatch');
|
|
112
|
+
const total = (outputCharacters.get(request.identity.attemptId) || 0) + result.delta.length;
|
|
113
|
+
if (total > request.grant.claim.limits.maxOutputCharacters) {
|
|
114
|
+
throw Object.assign(new Error('Partition output budget exhausted'), { code: 'OUTPUT_LIMIT_EXCEEDED' });
|
|
115
|
+
}
|
|
116
|
+
outputCharacters.set(request.identity.attemptId, total);
|
|
117
|
+
// Large diagnostic logits stay at B; the peer protocol returns one token and bounded continuation.
|
|
118
|
+
const { logits: _logits, ...output } = result;
|
|
119
|
+
return output;
|
|
120
|
+
},
|
|
121
|
+
settleAttempt: async identity => {
|
|
122
|
+
try { await contributor?.closeAttempt({ identity }); }
|
|
123
|
+
finally { outputCharacters.delete(identity.attemptId); }
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
const notify = () => { for (const listener of listeners) { try { listener(getState()); } catch {} } };
|
|
127
|
+
const endpoint = createPartitionDataChannel({ channel, localParticipantId, remoteParticipantId, limits,
|
|
128
|
+
async authorize({ action, metadata, byteLength }) {
|
|
129
|
+
if (metadata.operation === 'describe') return byteLength === 0;
|
|
130
|
+
if (!binding(metadata.identity)) return false;
|
|
131
|
+
const outbound = action === 'send' || action === 'accept';
|
|
132
|
+
if (metadata.identity.participantA !== (outbound ? localParticipantId : remoteParticipantId)
|
|
133
|
+
|| metadata.identity.participantB !== (outbound ? remoteParticipantId : localParticipantId)) return false;
|
|
134
|
+
if (metadata.schema !== undefined) {
|
|
135
|
+
if (!canonicalRequest(metadata) || metadata.operation !== 'step' && !canonicalContract.validateControl(metadata)) return false;
|
|
136
|
+
if (metadata.operation === 'settle') return byteLength === 0 && verify(metadata, 'mesh.execute_partition_b', true);
|
|
137
|
+
if (!['reserve', 'admit', 'step', 'published'].includes(metadata.operation)) return false;
|
|
138
|
+
if (metadata.operation === 'step') {
|
|
139
|
+
if (!canonicalContract.validateHeader(metadata, byteLength)) return false;
|
|
140
|
+
} else if (byteLength !== 0) return false;
|
|
141
|
+
if (metadata.operation === 'admit') {
|
|
142
|
+
try {
|
|
143
|
+
if (metadata.phase !== 'admit' || metadata.sequence !== 0 || metadata.tokenPosition !== 0
|
|
144
|
+
|| metadata.inputTokenIds?.length !== metadata.inputTokenCount
|
|
145
|
+
|| runtime.tokenDigest(metadata.inputTokenIds, plan.vocabSize, metadata.grant.claim.limits.maxPromptTokens)
|
|
146
|
+
!== metadata.canonicalAttempt.inputDigest) return false;
|
|
147
|
+
} catch { return false; }
|
|
148
|
+
}
|
|
149
|
+
if (!outbound && metadata.operation !== 'reserve' && !reservations.has(metadata.identity.attemptId)) return false;
|
|
150
|
+
return verify(metadata, action === 'respond' || action === 'accept' ? 'mesh.transfer_partition_output'
|
|
151
|
+
: metadata.operation === 'step' ? 'mesh.transfer_intermediate_activation' : 'mesh.execute_partition_b');
|
|
152
|
+
}
|
|
153
|
+
if (metadata.operation === 'settle') return byteLength === 0 && verify(metadata, 'mesh.execute_partition_b', true);
|
|
154
|
+
if (metadata.operation === 'reserve') return byteLength === 0 && verify(metadata, 'mesh.execute_partition_b');
|
|
155
|
+
if (metadata.operation !== 'step' || !validFrame(metadata) || metadata.frame.byteLength !== byteLength) return false;
|
|
156
|
+
const allowed = await verify(metadata, action === 'respond' || action === 'accept'
|
|
157
|
+
? 'mesh.transfer_partition_output' : 'mesh.transfer_intermediate_activation');
|
|
158
|
+
if (!allowed) return false;
|
|
159
|
+
if (!outbound && !(contributor?.canAccept ? contributor.canAccept(metadata.identity) : contributor?.getState().ready)) {
|
|
160
|
+
throw Object.assign(new Error(failureMessages.CAPACITY_UNAVAILABLE), { code: 'CAPACITY_UNAVAILABLE' });
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
},
|
|
164
|
+
async serve(metadata, payload, { signal }) {
|
|
165
|
+
if (metadata.operation === 'describe') return { descriptor: contributor?.getState().ready ? contributor.getState().descriptor : null,
|
|
166
|
+
...(canonical ? { canonical: contributor?.getState().ready ? { stage: canonicalStage,
|
|
167
|
+
generation: contributor.getCanonicalGeneration() } : null } : {}) };
|
|
168
|
+
if (metadata.operation === 'reserve') {
|
|
169
|
+
if (closing || !reservations.has(metadata.identity.attemptId) && reservations.size >= receiverLimits.maxAttempts) {
|
|
170
|
+
return { accepted: false, code: 'CAPACITY_UNAVAILABLE' };
|
|
171
|
+
}
|
|
172
|
+
signal.throwIfAborted();
|
|
173
|
+
let reservation;
|
|
174
|
+
try { reservation = contributor.reserve(metadata.identity); }
|
|
175
|
+
catch (cause) {
|
|
176
|
+
if (cause?.code === 'CAPACITY_UNAVAILABLE') return { accepted: false, code: 'CAPACITY_UNAVAILABLE' };
|
|
177
|
+
throw cause;
|
|
178
|
+
}
|
|
179
|
+
reservations.set(metadata.identity.attemptId, structuredClone(metadata.identity));
|
|
180
|
+
return { accepted: true, reservation };
|
|
181
|
+
}
|
|
182
|
+
if (metadata.operation === 'settle') {
|
|
183
|
+
try {
|
|
184
|
+
await receiver.closeAttempt(metadata.identity);
|
|
185
|
+
await contributor.closeAttempt({ identity: metadata.identity });
|
|
186
|
+
} catch (cause) {
|
|
187
|
+
throw Object.assign(new Error(failureMessages.CLEANUP_FAILED, { cause }), { code: 'CLEANUP_FAILED' });
|
|
188
|
+
}
|
|
189
|
+
reservations.delete(metadata.identity.attemptId);
|
|
190
|
+
return { settled: true };
|
|
191
|
+
}
|
|
192
|
+
return canonicalRequest(metadata) ? receiver.receiveCanonical(metadata.operation === 'step' ? { ...metadata, payload } : metadata, { signal })
|
|
193
|
+
: receiver.receive({ ...metadata, payload }, { signal });
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
const getState = () => ({ ready: channel.readyState === 'open' && remoteDescriptor?.ready === true && (!canonical || remoteCanonical !== null),
|
|
197
|
+
descriptor: structuredClone(remoteDescriptor), participantId: remoteParticipantId, receipt: endpoint.getReceipt() });
|
|
198
|
+
const close = () => {
|
|
199
|
+
if (closing) return closing;
|
|
200
|
+
closing = (async () => {
|
|
201
|
+
const results = await Promise.allSettled([receiver.close(), ...[...reservations.values()]
|
|
202
|
+
.map(identity => Promise.resolve().then(async () => {
|
|
203
|
+
await contributor.closeAttempt({ identity }); reservations.delete(identity.attemptId);
|
|
204
|
+
}))]);
|
|
205
|
+
const failures = results.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
206
|
+
if (failures.length) throw new AggregateError(failures, 'Partition peer settlement failed');
|
|
207
|
+
})();
|
|
208
|
+
const operation = closing;
|
|
209
|
+
operation.catch(() => { if (closing === operation) closing = null; });
|
|
210
|
+
endpoint.close(); remoteDescriptor = null; remoteCanonical = null; active.clear();
|
|
211
|
+
notify(); return operation;
|
|
212
|
+
};
|
|
213
|
+
const disconnected = () => { close().catch(() => {}); };
|
|
214
|
+
channel.addEventListener('close', disconnected, { once: true });
|
|
215
|
+
return Object.freeze({
|
|
216
|
+
id: remoteParticipantId, getState,
|
|
217
|
+
getCanonicalGeneration: () => structuredClone(remoteCanonical?.generation ?? null),
|
|
218
|
+
getCanonicalStage: () => structuredClone(remoteCanonical?.stage ?? null),
|
|
219
|
+
async reserveCanonical(request) {
|
|
220
|
+
assert(canonical && remoteCanonical && !active.has(request.identity.attemptId) && active.size < receiverLimits.maxAttempts,
|
|
221
|
+
'Canonical remote reservation unavailable');
|
|
222
|
+
const { signal, ...input } = request;
|
|
223
|
+
const metadata = { ...structuredClone(input), schema: canonicalContract.schema, operation: 'reserve', phase: 'admit' };
|
|
224
|
+
canonicalContract.validateBinding(metadata);
|
|
225
|
+
active.set(request.identity.attemptId, structuredClone(metadata));
|
|
226
|
+
const result = await endpoint.request(metadata, new Uint8Array(), { signal });
|
|
227
|
+
assert(result.accepted === true && samePartitionIdentity(result.reservation?.identity, request.identity)
|
|
228
|
+
&& result.reservation.participantId === remoteParticipantId && result.reservation.index === 1
|
|
229
|
+
&& result.reservation.phase === 'reserved' && result.reservation.resources?.attemptSlots === 1,
|
|
230
|
+
'Canonical remote reservation rejected');
|
|
231
|
+
return { reservation: result.reservation };
|
|
232
|
+
},
|
|
233
|
+
async admitCanonicalPrompt(request) {
|
|
234
|
+
const { signal, ...input } = request;
|
|
235
|
+
assert(canonical && active.has(input.identity.attemptId), 'Canonical remote attempt is not reserved');
|
|
236
|
+
const metadata = { ...structuredClone(input), schema: canonicalContract.schema, operation: 'admit', phase: 'admit' };
|
|
237
|
+
return endpoint.request(metadata, new Uint8Array(), { signal });
|
|
238
|
+
},
|
|
239
|
+
async executeCanonicalFrame({ frame, signal, ...input }) {
|
|
240
|
+
assert(canonical && active.has(input.identity.attemptId), 'Canonical remote attempt is not reserved');
|
|
241
|
+
const { buffer, ...header } = frame;
|
|
242
|
+
const metadata = { ...structuredClone(input), schema: canonicalContract.schema, operation: 'step', phase: 'step', frame: header };
|
|
243
|
+
assert(canonicalContract.validateHeader(metadata, buffer.byteLength), 'Canonical outgoing activation rejected');
|
|
244
|
+
return endpoint.request(metadata, new Uint8Array(buffer), { signal });
|
|
245
|
+
},
|
|
246
|
+
async acknowledgeCanonicalPublication(request) {
|
|
247
|
+
const { signal, ...input } = request;
|
|
248
|
+
assert(canonical && active.has(input.identity.attemptId), 'Canonical remote attempt is not reserved');
|
|
249
|
+
const metadata = { ...structuredClone(input), schema: canonicalContract.schema, operation: 'published', phase: 'published' };
|
|
250
|
+
const receipt = await endpoint.request(metadata, new Uint8Array(), { signal });
|
|
251
|
+
assert(runtime.metadataDigest('reploid.publication-ack/v1', receipt)
|
|
252
|
+
=== runtime.metadataDigest('reploid.publication-ack/v1', metadata.receipt), 'Canonical publication acknowledgment mismatch');
|
|
253
|
+
return receipt;
|
|
254
|
+
},
|
|
255
|
+
async reserve({ identity, grant, generation, generationDigest, inputTokenCount, signal }) {
|
|
256
|
+
assert(!active.has(identity.attemptId), 'Remote attempt already reserved');
|
|
257
|
+
assert(active.size < receiverLimits.maxAttempts, 'Remote attempt budget exhausted');
|
|
258
|
+
const metadata = { operation: 'reserve', identity, grant, generation, generationDigest, inputTokenCount, step: 0 };
|
|
259
|
+
active.set(identity.attemptId, structuredClone(metadata));
|
|
260
|
+
const result = await endpoint.request(metadata, new Uint8Array(), { signal });
|
|
261
|
+
if (result.accepted !== true) {
|
|
262
|
+
// Old peers may return a reason string; never expose remote exception text.
|
|
263
|
+
const code = result.code === 'CAPACITY_UNAVAILABLE' ? result.code : 'EXECUTION_FAILED';
|
|
264
|
+
throw Object.assign(new Error(failureMessages[code]), { code });
|
|
265
|
+
}
|
|
266
|
+
assert(samePartitionIdentity(result.reservation?.identity, identity)
|
|
267
|
+
&& result.reservation.participantId === remoteParticipantId && result.reservation.index === 1
|
|
268
|
+
&& result.reservation.phase === 'reserved' && result.reservation.resources?.attemptSlots === 1,
|
|
269
|
+
'Remote partition reservation identity mismatch');
|
|
270
|
+
return { reservation: result.reservation };
|
|
271
|
+
},
|
|
272
|
+
subscribe(listener) { listeners.add(listener); listener(getState()); return () => listeners.delete(listener); },
|
|
273
|
+
async refresh({ signal } = {}) {
|
|
274
|
+
const { descriptor, canonical: observedCanonical } = await endpoint.request({ operation: 'describe' }, new Uint8Array(), { signal });
|
|
275
|
+
assert(!descriptor || descriptor.schema === 'doppler.resident-partition/v1' && descriptor.ready === true
|
|
276
|
+
&& descriptor.index === 1 && descriptor.modelId === plan.modelId && descriptor.modelIdentity === modelIdentity
|
|
277
|
+
&& descriptor.planId === planId && JSON.stringify(descriptor.layerRange) === JSON.stringify(plan.partitions[1].layerRange),
|
|
278
|
+
'Remote partition descriptor mismatch');
|
|
279
|
+
if (canonical && descriptor) {
|
|
280
|
+
assert(observedCanonical?.stage?.participantId === remoteParticipantId && observedCanonical.stage.partitionIndex === 1
|
|
281
|
+
&& observedCanonical.generation?.generationId === descriptor.canonicalGenerationId,
|
|
282
|
+
'Remote canonical capability is unavailable or changed');
|
|
283
|
+
remoteCanonical = structuredClone(observedCanonical);
|
|
284
|
+
} else remoteCanonical = null;
|
|
285
|
+
remoteDescriptor = descriptor; notify(); return getState();
|
|
286
|
+
},
|
|
287
|
+
async executeFrame({ frame, signal, executionGrant, outputGrant, ...request }) {
|
|
288
|
+
assert(outputGrant?.claim?.id === executionGrant?.claim?.id, 'Partition output grant differs from execution grant');
|
|
289
|
+
const { buffer, ...header } = frame;
|
|
290
|
+
const metadata = { ...request, operation: 'step', frame: header, grant: executionGrant };
|
|
291
|
+
const prior = active.get(request.identity.attemptId);
|
|
292
|
+
assert(!prior || samePartitionIdentity(prior.identity, request.identity), 'Partition attempt identity collision');
|
|
293
|
+
assert(prior || active.size < receiverLimits.maxAttempts, 'Remote attempt budget exhausted');
|
|
294
|
+
active.set(request.identity.attemptId, structuredClone(metadata));
|
|
295
|
+
let transportTiming = null;
|
|
296
|
+
const result = await endpoint.request(metadata, new Uint8Array(buffer), {
|
|
297
|
+
signal, onTiming: value => { transportTiming = value; }
|
|
298
|
+
});
|
|
299
|
+
return { ...result, transportTiming };
|
|
300
|
+
},
|
|
301
|
+
async closeAttempt({ identity }) {
|
|
302
|
+
const metadata = active.get(identity.attemptId);
|
|
303
|
+
if (!metadata) return;
|
|
304
|
+
assert(samePartitionIdentity(metadata.identity, identity), 'Partition settlement identity collision');
|
|
305
|
+
const settlement = canonicalRequest(metadata) ? { schema: canonicalContract.schema, operation: 'settle', identity,
|
|
306
|
+
grant: metadata.grant, canonicalAttempt: metadata.canonicalAttempt, generationId: metadata.generationId,
|
|
307
|
+
sequence: metadata.sequence, step: metadata.step } : { operation: 'settle', identity, grant: metadata.grant };
|
|
308
|
+
const result = await endpoint.request(settlement, new Uint8Array());
|
|
309
|
+
assert(result.settled === true, 'Remote partition settlement not acknowledged');
|
|
310
|
+
active.delete(identity.attemptId);
|
|
311
|
+
},
|
|
312
|
+
close,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { samePartitionAdapters } from './partition-contract.js';
|
|
2
|
+
import { assertPartition as assert, canonicalPartitionJson, canonicalPartitionRoute, partitionActivationContract, validatePartitionRoute, PARTITION_ROUTE_SCHEMA, validateOrderedPartitionCapability, isOrderedPartitionEntryModel } from './partition-contract.js';
|
|
3
|
+
export { validateOrderedPartitionCapability, isOrderedPartitionEntryModel } from './partition-contract.js';
|
|
4
|
+
|
|
5
|
+
/** Pure selection over observed capabilities. No transport or resource ownership. */
|
|
6
|
+
export function selectPartitionPlacement({ participantId, offer, localIndex, previous, capabilities }) {
|
|
7
|
+
const eligibility = capabilities.map(peer => ({ participantId: peer.participantId, transportId: peer.transportId,
|
|
8
|
+
observedAt: peer.observedAt, expiresAt: peer.expiresAt,
|
|
9
|
+
reason: !peer.available ? peer.reason : peer.description.phase === 'failed' ? 'contributor-failed'
|
|
10
|
+
: peer.description.offer?.id !== offer.id || peer.description.offer?.identity !== offer.identity || !samePartitionAdapters(peer.description.offer, offer)
|
|
11
|
+
? 'model-mismatch' : 'approved-model-contribution',
|
|
12
|
+
index: peer.description?.index ?? null, indexes: peer.description?.eligibleIndexes ?? [0, 1] }));
|
|
13
|
+
const candidates = [{ id: participantId, index: localIndex, indexes: offer.partitionContributionIndexes ?? [0, 1] },
|
|
14
|
+
...eligibility.filter(peer => peer.reason === 'approved-model-contribution')
|
|
15
|
+
.map(peer => ({ id: peer.participantId, index: peer.index, indexes: peer.indexes }))].sort((a, b) => a.id.localeCompare(b.id));
|
|
16
|
+
const ids = candidates.map(peer => peer.id);
|
|
17
|
+
const inputs = candidates.filter(peer => peer.indexes.includes(0));
|
|
18
|
+
const outputs = candidates.filter(peer => peer.indexes.includes(1));
|
|
19
|
+
if (localIndex === null && !candidates.some(peer => peer.index === 0)) {
|
|
20
|
+
const awaitingInput = capabilities.some(peer => peer.available && peer.description?.index === 1
|
|
21
|
+
&& peer.description.offer?.id === offer.id && peer.description.offer?.identity === offer.identity
|
|
22
|
+
&& Array.isArray(peer.description.placement) && peer.description.placement.length === 2
|
|
23
|
+
&& peer.description.placement[0] !== participantId && !ids.includes(peer.description.placement[0]));
|
|
24
|
+
if (awaitingInput) return { participants: [], eligibility, reason: 'await-advertised-input-owner' };
|
|
25
|
+
}
|
|
26
|
+
const retainedA = previous && ids.includes(previous[0])
|
|
27
|
+
&& inputs.find(peer => peer.id === previous[0])?.index === 0 ? previous[0] : null;
|
|
28
|
+
const a = retainedA || inputs.find(peer => peer.index === 0)?.id
|
|
29
|
+
|| inputs.find(peer => peer.index === null)?.id || inputs[0]?.id;
|
|
30
|
+
if (!a) return { participants: [], eligibility, reason: 'missing-eligible-input-contributor' };
|
|
31
|
+
const b = participantId !== a ? outputs.find(peer => peer.id === participantId)?.id
|
|
32
|
+
: previous?.[0] === a && outputs.some(peer => peer.id === previous[1] && peer.index === 1) ? previous[1]
|
|
33
|
+
: outputs.find(peer => peer.id !== a && peer.index === 1)?.id || outputs.find(peer => peer.id !== a)?.id;
|
|
34
|
+
return { participants: b ? [a, b] : [], eligibility,
|
|
35
|
+
reason: !b ? 'missing-complementary-contributor' : retainedA ? 'retain-input-owner' : 'select-input-owner' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** New attempts consume availability hints; signed reservations remain authoritative.
|
|
39
|
+
* Capacity changes never move an already placed conversation. An ordered coordinator whose
|
|
40
|
+
* slots are full stays eligible while its advertised admission queue has room (the attempt
|
|
41
|
+
* waits there); one with a free slot is preferred. */
|
|
42
|
+
export function selectPartitionExecution({ model, capabilities, ordered = false }) {
|
|
43
|
+
const waits = new Set();
|
|
44
|
+
const eligibility = capabilities.map(peer => {
|
|
45
|
+
const prepared = peer.description?.models?.find(candidate => candidate.id === model.id
|
|
46
|
+
&& candidate.identity === model.identity && samePartitionAdapters(candidate, model) && candidate.availability === 'ready'
|
|
47
|
+
&& (ordered ? isOrderedPartitionEntryModel(candidate, peer)
|
|
48
|
+
: peer.description?.schema !== 'reploid.partition-capability/v2'
|
|
49
|
+
&& candidate.partition?.schema !== 'reploid.partition-selection/v2'));
|
|
50
|
+
const capacity = peer.description?.capacity, queue = ordered ? capacity?.attemptQueue : undefined;
|
|
51
|
+
const queueRoom = Number.isSafeInteger(queue?.queued) && Number.isSafeInteger(queue?.maxQueued) && queue.queued < queue.maxQueued;
|
|
52
|
+
const reason = !peer.available ? peer.reason : !prepared ? 'no-prepared-path'
|
|
53
|
+
: capacity?.closed ? 'input-owner-at-capacity'
|
|
54
|
+
: capacity?.availableSlots === 0 && !queueRoom ? (queue ? 'input-owner-queue-full' : 'input-owner-at-capacity') : 'reservation-required';
|
|
55
|
+
if (reason === 'reservation-required' && capacity?.availableSlots === 0) waits.add(peer.transportId);
|
|
56
|
+
return { participantId: peer.participantId, transportId: peer.transportId, reason,
|
|
57
|
+
eligible: reason === 'reservation-required', capacity: structuredClone(capacity || null) };
|
|
58
|
+
});
|
|
59
|
+
const selected = eligibility.find(peer => peer.eligible && !waits.has(peer.transportId)) ?? eligibility.find(peer => peer.eligible);
|
|
60
|
+
return { transportId: selected?.transportId ?? null, eligibility };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Eligible stage offers per index for one model plan, sorted by participant identity.
|
|
64
|
+
* Shared by first-valid matching and opt-in measured ranking; grants nothing. */
|
|
65
|
+
export async function eligiblePartitionStages({ runtime, plan, model, capabilities, limits, now }) {
|
|
66
|
+
assert(limits && ['maxStages', 'maxPeers', 'maxOffersPerPeer', 'maxSelectionSteps']
|
|
67
|
+
.every(key => Number.isSafeInteger(limits[key]) && limits[key] > 0), 'Explicit route selection limits required');
|
|
68
|
+
assert(Number.isSafeInteger(now) && now >= 0 && Array.isArray(capabilities)
|
|
69
|
+
&& capabilities.length <= limits.maxPeers && Number.isSafeInteger(plan?.stageCount)
|
|
70
|
+
&& plan.stageCount >= 2 && plan.stageCount <= limits.maxStages, 'Route selection input exceeds limit');
|
|
71
|
+
const planSnapshot = structuredClone(plan), modelSnapshot = structuredClone(model), peers = structuredClone(capabilities);
|
|
72
|
+
const policy = { ...limits };
|
|
73
|
+
const activation = partitionActivationContract(runtime, planSnapshot);
|
|
74
|
+
const planId = await runtime.hashLayerPartitionPlan(planSnapshot);
|
|
75
|
+
assert(modelSnapshot?.id === planSnapshot.modelId && /^sha256:[a-f0-9]{64}$/.test(modelSnapshot.identity), 'Route model mismatch');
|
|
76
|
+
const candidates = Array.from({ length: planSnapshot.stageCount }, () => []), eligibility = [];
|
|
77
|
+
const participants = new Set(), transports = new Set();
|
|
78
|
+
for (const peer of peers) {
|
|
79
|
+
assert(['participantId', 'transportId'].every(key => typeof peer[key] === 'string' && peer[key].length > 0 && peer[key].length <= 256)
|
|
80
|
+
&& !participants.has(peer.participantId) && !transports.has(peer.transportId), 'Duplicate or invalid route peer');
|
|
81
|
+
participants.add(peer.participantId); transports.add(peer.transportId);
|
|
82
|
+
let reason = null;
|
|
83
|
+
if (peer.available !== true || !Number.isSafeInteger(peer.observedAt) || peer.observedAt > now || peer.observedAt < 0
|
|
84
|
+
|| !Number.isSafeInteger(peer.expiresAt) || peer.expiresAt <= now) reason = 'unavailable-or-expired';
|
|
85
|
+
else {
|
|
86
|
+
try { validateOrderedPartitionCapability(peer.description, peer.participantId, policy.maxOffersPerPeer); }
|
|
87
|
+
catch { reason = 'invalid-capability'; }
|
|
88
|
+
}
|
|
89
|
+
if (!reason && (peer.description.phase !== 'ready' || peer.description.capacity.closed
|
|
90
|
+
|| peer.description.capacity.availableSlots === 0)) reason = 'contributor-unavailable';
|
|
91
|
+
if (reason) { eligibility.push({ participantId: peer.participantId, index: null, eligible: false, reason }); continue; }
|
|
92
|
+
for (const offer of peer.description.models) {
|
|
93
|
+
const eligible = offer.id === modelSnapshot.id && offer.identity === modelSnapshot.identity
|
|
94
|
+
&& offer.planId === planId && offer.index < candidates.length && offer.availability === 'ready'
|
|
95
|
+
&& offer.activation && canonicalPartitionJson(offer.activation) === canonicalPartitionJson(activation);
|
|
96
|
+
eligibility.push({ participantId: peer.participantId, index: offer.index, eligible: !!eligible,
|
|
97
|
+
reason: eligible ? 'reservation-required' : 'model-plan-stage-mismatch' });
|
|
98
|
+
if (eligible) candidates[offer.index].push({ index: offer.index, participantId: peer.participantId,
|
|
99
|
+
transportId: peer.transportId, residentId: offer.residentId, executionId: offer.executionId });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
103
|
+
candidates.forEach(rows => rows.sort((a, b) => compare(a.participantId, b.participantId)
|
|
104
|
+
|| compare(a.residentId, b.residentId) || compare(a.executionId, b.executionId)));
|
|
105
|
+
return { plan: planSnapshot, model: modelSnapshot, planId, activation, candidates, eligibility };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Deterministic bipartite matching for new attempts only. No cost optimum or physical-device independence is inferred. */
|
|
109
|
+
export async function selectPartitionRoute(selection) {
|
|
110
|
+
const { plan: planSnapshot, model: modelSnapshot, planId, activation, candidates, eligibility } = await eligiblePartitionStages(selection);
|
|
111
|
+
const policy = { ...selection.limits };
|
|
112
|
+
let selectionSteps = 0;
|
|
113
|
+
const finish = (route, reason) => Object.freeze({ route, reason, selectionSteps,
|
|
114
|
+
eligibility: Object.freeze(eligibility.map(Object.freeze)) });
|
|
115
|
+
if (candidates.some(rows => rows.length === 0)) return finish(null, 'missing-eligible-stage');
|
|
116
|
+
const assigned = new Map(), stages = Array(candidates.length).fill(null);
|
|
117
|
+
// Iterative augmenting paths avoid stack depth depending on model size.
|
|
118
|
+
for (let start = 0; start < stages.length; start++) {
|
|
119
|
+
const queue = [start], visited = new Set(), predecessor = new Map();
|
|
120
|
+
let found = null;
|
|
121
|
+
for (let cursor = 0; cursor < queue.length && !found; cursor++) {
|
|
122
|
+
const index = queue[cursor];
|
|
123
|
+
for (const candidate of candidates[index]) {
|
|
124
|
+
if (++selectionSteps > policy.maxSelectionSteps) return finish(null, 'selection-work-limit');
|
|
125
|
+
if (visited.has(candidate.participantId)) continue;
|
|
126
|
+
visited.add(candidate.participantId);
|
|
127
|
+
const previous = assigned.get(candidate.participantId);
|
|
128
|
+
if (previous === undefined) { found = { index, candidate }; break; }
|
|
129
|
+
if (!predecessor.has(previous) && previous !== start) {
|
|
130
|
+
predecessor.set(previous, { index, candidate }); queue.push(previous);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!found) return finish(null, 'insufficient-distinct-participants');
|
|
135
|
+
while (found) {
|
|
136
|
+
stages[found.index] = found.candidate; assigned.set(found.candidate.participantId, found.index);
|
|
137
|
+
found = predecessor.get(found.index);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const route = await validatePartitionRoute(selection.runtime, planSnapshot, { schema: PARTITION_ROUTE_SCHEMA,
|
|
141
|
+
modelId: modelSnapshot.id, modelIdentity: modelSnapshot.identity, planId, stages, activation });
|
|
142
|
+
return finish(route, 'reservation-required');
|
|
143
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Doppler reports GPU pipeline preparation before a resident is ready. Hosts show it as
|
|
2
|
+
* contribution progress; the contribution phase stays theirs. `outstanding` means a started
|
|
3
|
+
* compilation still owns the stage, so nothing it holds is released yet. Other events are ignored. */
|
|
4
|
+
const STATUSES = Object.freeze(['preparing', 'prepared', 'cancelling', 'cancelled', 'timed-out', 'failed']);
|
|
5
|
+
|
|
6
|
+
const message = ({ status, prepared, total, outstanding }) => {
|
|
7
|
+
if (status === 'cancelling') return 'Stopping after the current GPU pipeline finishes compiling';
|
|
8
|
+
if (status === 'cancelled') return `Stopped preparing GPU pipelines: ${prepared} of ${total}`;
|
|
9
|
+
if (status === 'timed-out') return outstanding
|
|
10
|
+
? 'Preparation time limit reached; waiting for the current GPU pipeline to finish compiling'
|
|
11
|
+
: `Preparation time limit reached: ${prepared} of ${total} GPU pipelines`;
|
|
12
|
+
if (status === 'failed') return `GPU pipeline preparation failed: ${prepared} of ${total}`;
|
|
13
|
+
return `Preparing GPU pipelines: ${prepared} of ${total}`;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function residentPreparationProgress(event) {
|
|
17
|
+
if (event?.phase !== 'pipelines' || !STATUSES.includes(event.status)) return null;
|
|
18
|
+
const { status, prepared, total, key, elapsedMs, startedAt, maxDurationMs, outstanding } = event;
|
|
19
|
+
if (![prepared, total].every(value => Number.isSafeInteger(value) && value >= 0) || prepared > total
|
|
20
|
+
|| typeof outstanding !== 'boolean' || !Number.isFinite(elapsedMs)) return null;
|
|
21
|
+
return Object.freeze({ stage: 'preparing', status, prepared, total, key: typeof key === 'string' ? key : null,
|
|
22
|
+
elapsedMs, startedAt: Number.isFinite(startedAt) ? startedAt : null,
|
|
23
|
+
maxDurationMs: Number.isSafeInteger(maxDurationMs) ? maxDurationMs : null, outstanding,
|
|
24
|
+
message: message({ status, prepared, total, outstanding }) });
|
|
25
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { assertPartition as assert, assertCapacity, canonicalPartitionJson } from './partition-contract.js';
|
|
2
|
+
|
|
3
|
+
/** Per-contributor admission owner. A slot stays held until cleanup settles. A closed owner, an
|
|
4
|
+
* exhausted attempt history or no free slot refuses with CAPACITY_UNAVAILABLE, which reaches the
|
|
5
|
+
* requester as that code rather than as a generic execution failure. */
|
|
6
|
+
export function createPartitionReservations({ limits, participantId, index, now = Date.now }) {
|
|
7
|
+
assert(Number.isSafeInteger(limits.maxAttempts) && limits.maxAttempts > 0
|
|
8
|
+
&& Number.isSafeInteger(limits.maxConcurrentAttempts) && limits.maxConcurrentAttempts > 0,
|
|
9
|
+
'Explicit reservation limits required');
|
|
10
|
+
const records = new Map();
|
|
11
|
+
let closed = false;
|
|
12
|
+
const describe = ({ binding: _binding, ...record }) => structuredClone(record);
|
|
13
|
+
const active = () => [...records.values()].filter(record => record.settledAt === null);
|
|
14
|
+
const lookup = identity => {
|
|
15
|
+
const record = records.get(identity.attemptId);
|
|
16
|
+
assert(record && record.binding === canonicalPartitionJson(identity), 'Reservation identity mismatch');
|
|
17
|
+
return record;
|
|
18
|
+
};
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
reserve(identity) {
|
|
21
|
+
assertCapacity(!closed);
|
|
22
|
+
assert(typeof identity?.attemptId === 'string' && identity.attemptId.length > 0, 'Reservation attempt identity required');
|
|
23
|
+
if (records.has(identity.attemptId)) {
|
|
24
|
+
const record = lookup(identity);
|
|
25
|
+
assert(record.phase === 'reserved', 'Attempt reservation already settling or retired');
|
|
26
|
+
return describe(record);
|
|
27
|
+
}
|
|
28
|
+
assertCapacity(records.size < limits.maxAttempts);
|
|
29
|
+
assertCapacity(active().length < limits.maxConcurrentAttempts);
|
|
30
|
+
const record = { identity: structuredClone(identity), binding: canonicalPartitionJson(identity), participantId, index,
|
|
31
|
+
phase: 'reserved', reservedAt: now(), settlingAt: null, settledAt: null, failure: null,
|
|
32
|
+
resources: { attemptSlots: 1, maxPromptTokens: limits.maxPromptTokens, maxTokens: limits.maxTokens,
|
|
33
|
+
maxActivationBytes: limits.maxActivationBytes, scope: 'application-attempt-limits' } };
|
|
34
|
+
records.set(identity.attemptId, record); return describe(record);
|
|
35
|
+
},
|
|
36
|
+
fail(identity, cause) {
|
|
37
|
+
if (records.has(identity.attemptId)) lookup(identity).failure = String(cause?.message || cause);
|
|
38
|
+
},
|
|
39
|
+
settling(identity) {
|
|
40
|
+
if (!records.has(identity.attemptId)) {
|
|
41
|
+
assert(records.size < limits.maxAttempts, 'Contributor reservation history exhausted');
|
|
42
|
+
// Cancellation can overtake admission. Keep a bounded tombstone so a
|
|
43
|
+
// delayed, already-authorized reserve cannot reopen that attempt.
|
|
44
|
+
records.set(identity.attemptId, { identity: structuredClone(identity), binding: canonicalPartitionJson(identity),
|
|
45
|
+
participantId, index, phase: 'settling', reservedAt: null, settlingAt: now(), settledAt: null,
|
|
46
|
+
failure: null, resources: null });
|
|
47
|
+
}
|
|
48
|
+
const record = lookup(identity);
|
|
49
|
+
if (record.settledAt === null) { record.phase = 'settling'; record.settlingAt ??= now(); }
|
|
50
|
+
},
|
|
51
|
+
settled(identity) {
|
|
52
|
+
if (!records.has(identity.attemptId)) return;
|
|
53
|
+
const record = lookup(identity);
|
|
54
|
+
record.settledAt ??= now(); record.phase = record.failure ? 'failed' : 'settled';
|
|
55
|
+
},
|
|
56
|
+
/** The stage's own signed evidence for a charged attempt, kept with its admission record. */
|
|
57
|
+
attachEvidence(identity, evidence) {
|
|
58
|
+
if (records.has(identity.attemptId)) lookup(identity).evidence = structuredClone(evidence);
|
|
59
|
+
},
|
|
60
|
+
failureOf: identity => (records.has(identity.attemptId) ? lookup(identity).failure : null),
|
|
61
|
+
getState() {
|
|
62
|
+
return { closed, active: active().length,
|
|
63
|
+
availableSlots: closed || records.size >= limits.maxAttempts ? 0 : limits.maxConcurrentAttempts - active().length,
|
|
64
|
+
records: [...records.values()].map(describe) };
|
|
65
|
+
},
|
|
66
|
+
close() { closed = true; },
|
|
67
|
+
settleAll() {
|
|
68
|
+
// Only the resident owner can call this, after runtime disposal resolves.
|
|
69
|
+
for (const record of active()) {
|
|
70
|
+
record.settlingAt ??= now(); record.settledAt = now();
|
|
71
|
+
record.phase = record.failure ? 'failed' : 'settled';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "reploid.route-envelope-policy/v1",
|
|
3
|
+
"undeclaredRoutes": "existing-limits",
|
|
4
|
+
"routes": [
|
|
5
|
+
{
|
|
6
|
+
"modelIdentity": "sha256:548e65597f85e190d6f0edf38c63569b872a61696c28ec95dce031f913e953db",
|
|
7
|
+
"planId": "sha256:7a4ce534baee0945e47adf13bb3bad017038eebb83b509e9d68b1fe01c76a1bf",
|
|
8
|
+
"evidence": "artifacts/placement-c-envelope-20261015/README.md",
|
|
9
|
+
"cells": [
|
|
10
|
+
{ "maxConcurrency": 1, "maxPromptTokens": 512, "maxAnswerTokens": 255 },
|
|
11
|
+
{ "maxConcurrency": 1, "maxPromptTokens": 1024, "maxAnswerTokens": 63 },
|
|
12
|
+
{ "maxConcurrency": 2, "maxPromptTokens": 512, "maxAnswerTokens": 255 }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|