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,540 @@
|
|
|
1
|
+
import { partitionAdapterIdentities, samePartitionAdapters, selectPartitionContribution } from './partition-contract.js';
|
|
2
|
+
import { publicPreparationProgress, createPreparationProgressObserver } from '../preparation-progress.js';
|
|
3
|
+
import { generationRequest, requireGenerationCapability } from './partition-generation-request.js';
|
|
4
|
+
import { createPartitionDiscovery } from './partition-discovery.js';
|
|
5
|
+
import { selectPartitionRoute, selectPartitionExecution, eligiblePartitionStages } from './partition-placement.js';
|
|
6
|
+
import { createRouteObservations, rankPartitionRoutes, preferRoute, routeSelectionRecord, validateRouteCostPolicy,
|
|
7
|
+
ROUTE_RANKING_WORK_LIMIT } from './partition-route-ranking.js';
|
|
8
|
+
import { ORDERED_PARTITION_CHANNEL } from './partition-route-network.js';
|
|
9
|
+
import { createPartitionPeer } from './partition-peer.js';
|
|
10
|
+
import { createPartitionEntry, ATTEMPT_STATE_SCHEMA } from './partition-entry.js';
|
|
11
|
+
import { createEntryPublicationScope, entryPublicationPort } from './partition-entry-publication.js';
|
|
12
|
+
import { ORDERED_PUBLICATION_CONTEXT_SCHEMA } from './partition-ordered-publication.js';
|
|
13
|
+
import { summarizePartitionExecution } from './partition-summary.js';
|
|
14
|
+
import { createPartitionChat } from './partition-chat.js';
|
|
15
|
+
import { createPartitionGrantAuthority, orderedPartitionDisclosure } from './partition-grants.js';
|
|
16
|
+
import { assertPartition as assert, assertCapacity, partitionFailure, canonicalPartitionJson, canonicalPartitionRoute, validatePartitionRoute,
|
|
17
|
+
PARTITION_ROUTE_SCHEMA } from './partition-contract.js';
|
|
18
|
+
import { planStageAssignment, stageDemand, STAGE_ASSIGNMENT_SCHEMA, STAGE_COSTS_SCHEMA } from './partition-stage-assignment.js';
|
|
19
|
+
import { createDeviceAdmission } from './partition-device-admission.js';
|
|
20
|
+
import { ATTEMPT_QUEUE_POLICY, createPartitionAttemptQueue } from './partition-attempt-queue.js';
|
|
21
|
+
|
|
22
|
+
const equal = (a, b) => canonicalPartitionJson(a) === canonicalPartitionJson(b);
|
|
23
|
+
/** Explicit prepared-stage mode of automatic participation. Geometry and local
|
|
24
|
+
* allocation identity come from the host; discovery cannot assign either. Launch 3
|
|
25
|
+
* mechanisms run only under an explicit `ordered.orchestration` member: with
|
|
26
|
+
* `autoStage`, the operator's auto choice lets this contributor's own policy pick
|
|
27
|
+
* its stage from observed offers and move only while idle; with `routeRanking`,
|
|
28
|
+
* a coordinator ranks complete routes by expected cost under its explicit cost policy
|
|
29
|
+
* for new attempts only, recording each decision with the attempts it admits;
|
|
30
|
+
* with `admission`, every resident this owner prepares charges one device budget,
|
|
31
|
+
* optionally behind a fair bounded queue; with `replicas`, an automatic contributor
|
|
32
|
+
* joins or moves to a stage whose queue or measured cost makes another replica useful.
|
|
33
|
+
* Always (Launch 1): as a coordinator, every route it owns admits through one bounded FIFO queue
|
|
34
|
+
* under the partitions component's queue policy, so an authorized attempt that finds no free slot
|
|
35
|
+
* waits and shows `queued` instead of failing; a weightless requester reports that state. */
|
|
36
|
+
export function createAutomaticOrderedPartitions({ identity, meshId, models, policy, peers, createNetwork,
|
|
37
|
+
loadProgram, ordered, onChange = () => {} }) {
|
|
38
|
+
const config = structuredClone(policy), catalog = structuredClone(models), options = structuredClone(ordered);
|
|
39
|
+
let stage = options.stage ?? null;
|
|
40
|
+
const orchestration = options.orchestration ?? null, auto = orchestration?.autoStage ?? null;
|
|
41
|
+
const ranking = orchestration?.routeRanking ?? null, admission = orchestration?.admission ?? null;
|
|
42
|
+
const replicas = orchestration?.replicas ?? null;
|
|
43
|
+
assert(identity?.peerId && typeof peers === 'function' && typeof createNetwork === 'function' && typeof loadProgram === 'function'
|
|
44
|
+
&& ['pollMs', 'maxPeers', 'connectTimeoutMs', 'grantMs', 'maxStageOffersPerPeer'].every(key => Number.isSafeInteger(config[key]) && config[key] > 0)
|
|
45
|
+
&& ['maxRouteBytes', 'maxSelectionSteps', 'maxRoutes'].every(key => Number.isSafeInteger(options[key]) && options[key] > 0),
|
|
46
|
+
'Explicit ordered participation policy required');
|
|
47
|
+
assert(stage === null || Number.isSafeInteger(stage.index) && stage.index >= 0 && stage.participantId === identity.peerId
|
|
48
|
+
&& ['transportId', 'residentId', 'executionId'].every(key => typeof stage[key] === 'string' && stage[key].length > 0),
|
|
49
|
+
'Actual local stage identity required');
|
|
50
|
+
assert(orchestration === null || orchestration && typeof orchestration === 'object' && !Array.isArray(orchestration)
|
|
51
|
+
&& Object.keys(orchestration).every(key => ['autoStage', 'routeRanking', 'admission', 'replicas'].includes(key)), 'Unknown ordered orchestration policy');
|
|
52
|
+
assert(auto === null || stage === null && typeof auto.transportId === 'string' && auto.transportId.length > 0
|
|
53
|
+
&& Number.isSafeInteger(auto.dwellMs) && auto.dwellMs >= 0, 'Automatic stage assignment requires an authenticated transport and explicit dwell');
|
|
54
|
+
assert(ranking === null || typeof ranking.margin === 'number' && ranking.margin >= 0 && ranking.margin < 1
|
|
55
|
+
&& Number.isSafeInteger(ranking.workload?.promptTokens) && ranking.workload.promptTokens > 0
|
|
56
|
+
&& Number.isSafeInteger(ranking.workload.expectedTokens) && ranking.workload.expectedTokens > 0,
|
|
57
|
+
'Measured route preference requires an explicit margin and workload');
|
|
58
|
+
// Every price, prior and reserve comes from the operator's versioned cost policy; none is defaulted.
|
|
59
|
+
const costPolicy = ranking ? validateRouteCostPolicy(ranking.costs) : null;
|
|
60
|
+
// Bounded per (plan, participant, stage) costs and per-route outcomes from attempts this coordinator served.
|
|
61
|
+
const observations = ranking ? createRouteObservations({ maxEntries: ranking.maxObservations, smoothing: ranking.smoothing }) : null;
|
|
62
|
+
assert(replicas === null || auto !== null && Number.isSafeInteger(replicas.queueBound) && replicas.queueBound >= 0
|
|
63
|
+
&& typeof replicas.bottleneckRatio === 'number' && replicas.bottleneckRatio > 1,
|
|
64
|
+
'Useful replicas require automatic stage assignment and explicit demand bounds');
|
|
65
|
+
assert(admission === null || Number.isSafeInteger(admission.attemptBytes) && admission.attemptBytes > 0,
|
|
66
|
+
'Shared device admission requires an explicit attempt allocation');
|
|
67
|
+
// One owner prepares at most one resident at a time on this host's device; moves reuse this budget.
|
|
68
|
+
const device = admission ? createDeviceAdmission(admission) : null;
|
|
69
|
+
// One waiting line for every route this coordinator owns: they share its own stage's slots.
|
|
70
|
+
const attemptQueue = createPartitionAttemptQueue({ policy: ATTEMPT_QUEUE_POLICY });
|
|
71
|
+
const closedOwners = new WeakSet();
|
|
72
|
+
const lifetime = new AbortController(), listeners = new Set(), admissions = new Map(), routes = new Map(), debt = new Set();
|
|
73
|
+
const authority = createPartitionGrantAuthority({ identity, meshId, maxGrants: config.limits.maxAttempts,
|
|
74
|
+
maxTtlMs: config.grantMs, maxClockSkewMs: config.grantClockSkewMs });
|
|
75
|
+
let offer = null, program = null, execution = null, controller = null, loading = null, ticking = null;
|
|
76
|
+
let phase = 'idle', error = null, progress = null, decision = null, currentKey = null, closed = false, stopping = null, closing = null;
|
|
77
|
+
let moving = null, readyAt = 0, assignment = null;
|
|
78
|
+
let discovery, entries;
|
|
79
|
+
// Receipts whose cleanup stayed unknown outlive their retired route, bounded
|
|
80
|
+
// by the attempt budget; retiring a route never turns them into settlement.
|
|
81
|
+
const unknownAttempts = [];
|
|
82
|
+
const current = () => routes.get(currentKey);
|
|
83
|
+
const getModels = () => [...discovery.getModels(), ...(offer && phase === 'ready' ? current()?.chat.getModels() || [] : [])]
|
|
84
|
+
.filter(model => model.partition?.schema === 'reploid.partition-selection/v2' && !model.canonical);
|
|
85
|
+
const getState = () => ({ phase, error, progress: structuredClone(progress), offering: !!offer, modelId: offer?.id ?? null,
|
|
86
|
+
placement: current()?.route.stages.map(row => row.participantId) ?? null, placementDecision: structuredClone(decision),
|
|
87
|
+
capabilities: discovery.getSnapshot(), descriptor: program?.resident.getState().descriptor ?? null,
|
|
88
|
+
reservations: program?.resident.getState().reservations ?? null, acquisition: program?.getReceipt?.() ?? null,
|
|
89
|
+
models: getModels(), attempts: [...routes.values()].flatMap(row => row.chat.getState().attempts), unsettledOwners: debt.size,
|
|
90
|
+
unknownAttempts: structuredClone(unknownAttempts), queue: attemptQueue.getState(),
|
|
91
|
+
...(auto ? { assignment: structuredClone(assignment) } : {}),
|
|
92
|
+
...(ranking ? { routeObservations: observations.snapshot(), routeOutcomes: observations.snapshotRoutes() } : {}) });
|
|
93
|
+
const notify = () => { onChange(getState()); for (const listener of listeners) listener(getModels()); };
|
|
94
|
+
// A bounded device queue is advertised with capacity; it is present only when configured.
|
|
95
|
+
const residentCapacity = () => program ? (({ active, availableSlots, closed, device: shared }) => ({ active, availableSlots, closed,
|
|
96
|
+
...(shared?.maxQueued === undefined ? {} : { queued: shared.queued, maxQueued: shared.maxQueued }) }))(
|
|
97
|
+
program.resident.getState().reservations) : { active: 0, availableSlots: 0, closed: true };
|
|
98
|
+
function describe() {
|
|
99
|
+
const state = program?.resident.getState(), descriptor = state?.descriptor;
|
|
100
|
+
const inventory = descriptor ? [{ id: program.model.id, name: program.model.name, identity: program.model.identity, adapters: program.model.adapters || [], index: stage.index, planId: program.planId,
|
|
101
|
+
residentId: stage.residentId, executionId: stage.executionId,
|
|
102
|
+
activation: program.runtime.getPartitionActivationContract(program.plan), availability: offer && phase === 'ready' && state.ready ? 'ready' : 'unavailable' }] : [];
|
|
103
|
+
// A stage offer is never a complete entry. Only the currently prepared chat
|
|
104
|
+
// adds the full route selection; other active routes keep their own owners.
|
|
105
|
+
const entry = current()?.chat.getModels()[0];
|
|
106
|
+
if (inventory.length && entry && offer && phase === 'ready') inventory[0] = { ...inventory[0], ...entry };
|
|
107
|
+
const route = ranking && offer && phase === 'ready' ? current()?.route : null;
|
|
108
|
+
// An offering coordinator also advertises its admission queue, so requesters may send to it and
|
|
109
|
+
// wait while its slots are full; the queue's own bounds still refuse explicitly.
|
|
110
|
+
const coordinating = stage?.index === 0 && offer && phase === 'ready' && current(), waitingLine = attemptQueue.getState();
|
|
111
|
+
return { schema: 'reploid.partition-capability/v2', participantId: identity.peerId,
|
|
112
|
+
offer: offer && { id: offer.id, identity: offer.identity }, index: stage?.index ?? null,
|
|
113
|
+
phase, preparation: phase === 'loading' ? publicPreparationProgress(progress) : null, models: inventory, capacity: { ...residentCapacity(),
|
|
114
|
+
...(coordinating ? { attemptQueue: { queued: waitingLine.queued, maxQueued: waitingLine.maxQueuedAttempts } } : {}) },
|
|
115
|
+
...(auto && offer ? { assignment: { schema: STAGE_ASSIGNMENT_SCHEMA, modelId: offer.id, modelIdentity: offer.identity, planId: offer.partitionPlanId,
|
|
116
|
+
index: stage?.index ?? null } } : {}),
|
|
117
|
+
// A ranking coordinator shares its measured per-stage decode cost so automatic contributors can see bottlenecks.
|
|
118
|
+
...(route ? { stageCosts: { schema: STAGE_COSTS_SCHEMA, modelId: offer.id, modelIdentity: offer.identity, planId: route.planId,
|
|
119
|
+
decodeMs: route.stages.map(row => observations.get(route.planId, row.participantId, row.index)?.decodeMs ?? null) } } : {}) };
|
|
120
|
+
}
|
|
121
|
+
// With ranking, the preferred route is listed first so entry refreshes resolve new attempts to it.
|
|
122
|
+
const routeOwners = () => ranking && current() ? [current(), ...[...routes.values()].filter(row => row !== current())] : [...routes.values()];
|
|
123
|
+
const observe = (route, result) => {
|
|
124
|
+
try {
|
|
125
|
+
observations.record({ planId: route.planId, route, phases: summarizePartitionExecution(result.execution).stepSummary?.phases, at: Date.now() });
|
|
126
|
+
} catch { /* A malformed execution summary is not a measurement. */ }
|
|
127
|
+
};
|
|
128
|
+
// Readiness and failure evidence from the attempt's own runner receipt. Stages that reserved it
|
|
129
|
+
// admitted it; a non-cancelled attempt that stopped during admission was refused by the next stage
|
|
130
|
+
// (including a declined or failed reservation call). Only a fully admitted attempt has an execution
|
|
131
|
+
// outcome; one that never reached admission is not a route outcome, and cancellation is neither.
|
|
132
|
+
const settle = (owner, attemptId, completed, cancelled) => {
|
|
133
|
+
try {
|
|
134
|
+
const receipt = owner.chat.getState().attempts.find(row => row.identity?.attemptId === attemptId);
|
|
135
|
+
if (!receipt) return;
|
|
136
|
+
const admitted = receipt.reservations.length, all = admitted === owner.route.stages.length;
|
|
137
|
+
cancelled ||= receipt.phase === 'cancelled';
|
|
138
|
+
observations.recordOutcome({ planId: owner.route.planId, route: owner.route, admitted, refused: !cancelled && !all,
|
|
139
|
+
execution: cancelled || !all ? null : completed ? 'completed' : 'failed', at: Date.now() });
|
|
140
|
+
} catch { /* A malformed receipt is not an outcome. */ }
|
|
141
|
+
};
|
|
142
|
+
const service = {
|
|
143
|
+
getModels: () => offer && !closed && phase === 'ready' ? routeOwners().flatMap(row => row.chat.getModels()) : [],
|
|
144
|
+
async refresh({ signal } = {}) { await tick(); await current()?.chat.refresh({ signal }); },
|
|
145
|
+
generate(request, controls) {
|
|
146
|
+
const key = canonicalPartitionJson(request.model.partition.route), owner = routes.get(key);
|
|
147
|
+
// A retired, stopping or moving route owner is unavailable capacity; attempts never move to another route.
|
|
148
|
+
assertCapacity(owner && offer && !closed && !stopping && !moving);
|
|
149
|
+
// The decision that chose this route when the attempt arrived travels with its execution record.
|
|
150
|
+
const selection = owner.selection ?? null;
|
|
151
|
+
const running = owner.chat.generate(request, controls);
|
|
152
|
+
if (!observations) return running;
|
|
153
|
+
return running.then(result => {
|
|
154
|
+
observe(owner.route, result); settle(owner, request.attemptId, true, false);
|
|
155
|
+
return selection ? { ...result, execution: { ...result.execution, routeSelection: selection } } : result;
|
|
156
|
+
}, error => { settle(owner, request.attemptId, false, !!controls?.signal?.aborted); throw error; });
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
entries = createNetwork({ label: 'reploid-partitions-input', maxPeers: config.maxPeers, timeoutMs: config.connectTimeoutMs,
|
|
160
|
+
createEndpoint: ({ channel, remoteParticipantId }) => createPartitionEntry({ channel, localParticipantId: identity.peerId,
|
|
161
|
+
remoteParticipantId, service, ordered: true, limits: config.inputChannel, inputLimits: config.inputLimits,
|
|
162
|
+
authorize: async input => {
|
|
163
|
+
if (input.requesterId === identity.peerId) return equal(admissions.get(input.attemptId) ?? null, input);
|
|
164
|
+
if (!offer || stopping || moving || stage?.index !== 0 || input.requesterId !== remoteParticipantId || input.coordinatorId !== identity.peerId) return false;
|
|
165
|
+
const owner = routes.get(canonicalPartitionJson(input.route));
|
|
166
|
+
return !!owner && input.modelIdentity === program?.model.identity && input.planId === program?.planId;
|
|
167
|
+
} }) });
|
|
168
|
+
discovery = createPartitionDiscovery({ identity, catalog, policy: config, peers, createNetwork, describe, onChange: notify });
|
|
169
|
+
async function closeOwner(owner) {
|
|
170
|
+
if (closedOwners.has(owner)) { debt.delete(owner); return; }
|
|
171
|
+
debt.add(owner); await owner.close(); debt.delete(owner); closedOwners.add(owner);
|
|
172
|
+
}
|
|
173
|
+
async function closeChat(chat) {
|
|
174
|
+
await closeOwner(chat);
|
|
175
|
+
for (const receipt of chat.getState().attempts.filter(row => row.phase === 'cleanup-failed')) {
|
|
176
|
+
if (unknownAttempts.some(row => row.identity.attemptId === receipt.identity.attemptId)) continue;
|
|
177
|
+
unknownAttempts.push(receipt); if (unknownAttempts.length > config.limits.maxAttempts) unknownAttempts.shift();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function retire() {
|
|
181
|
+
// Chat settles its private attempts before channels and shared weights retire.
|
|
182
|
+
const chats = [...routes.values()].map(row => row.chat);
|
|
183
|
+
const settled = await Promise.allSettled(chats.map(closeChat));
|
|
184
|
+
if (settled.some(row => row.status === 'rejected')) throw new AggregateError(settled.filter(row => row.status === 'rejected').map(row => row.reason), 'Ordered chat settlement failed');
|
|
185
|
+
routes.clear(); currentKey = null;
|
|
186
|
+
if (execution) { await closeOwner(execution); execution = null; }
|
|
187
|
+
if (program) { await closeOwner(program.resident); program = null; }
|
|
188
|
+
for (const owner of [...debt]) await closeOwner(owner);
|
|
189
|
+
}
|
|
190
|
+
async function prepare() {
|
|
191
|
+
const selected = offer, preparationOwners = new Set(), observeProgress = createPreparationProgressObserver();
|
|
192
|
+
let observingPreparation = true;
|
|
193
|
+
let candidate = null;
|
|
194
|
+
phase = 'loading'; notify();
|
|
195
|
+
loading = Promise.resolve().then(() => loadProgram(selected, stage.index, {
|
|
196
|
+
signal: AbortSignal.any([lifetime.signal, controller.signal]), participantId: identity.peerId, ordered: { stage: structuredClone(stage) },
|
|
197
|
+
...(device ? { device, attemptBytes: admission.attemptBytes } : {}),
|
|
198
|
+
registerCleanup(owner) { assert(typeof owner?.close === 'function', 'Preparation cleanup owner required'); preparationOwners.add(owner); return () => preparationOwners.delete(owner); },
|
|
199
|
+
onProgress(value) { if (observingPreparation) { progress = observeProgress(value); notify(); } }
|
|
200
|
+
}));
|
|
201
|
+
try {
|
|
202
|
+
candidate = await loading;
|
|
203
|
+
assert(candidate?.resident && typeof candidate.resident.getState === 'function'
|
|
204
|
+
&& typeof candidate.resident.close === 'function', 'Returned ordered resident owner required');
|
|
205
|
+
const observed = candidate.resident.getState();
|
|
206
|
+
assert(candidate.resident.id === stage.participantId && candidate.resident.index === stage.index && candidate.model.id === selected.id && candidate.model.identity === selected.identity
|
|
207
|
+
&& observed.ready && observed.descriptor.schema === 'doppler.resident-partition/v2'
|
|
208
|
+
&& observed.descriptor.planId === candidate.planId && candidate.plan.stageCount === observed.descriptor.stageCount
|
|
209
|
+
&& ['transportId', 'residentId', 'executionId'].every(key => candidate.resident[key] === stage[key]), 'Loaded ordered stage identity mismatch');
|
|
210
|
+
program = candidate;
|
|
211
|
+
preparationOwners.clear(); // Validated handoff transfers loading ownership to the resident.
|
|
212
|
+
if (closed || offer !== selected || stopping) return;
|
|
213
|
+
execution = createNetwork({ label: ORDERED_PARTITION_CHANNEL, maxPeers: config.maxPeers, timeoutMs: config.connectTimeoutMs,
|
|
214
|
+
ordered: { maxRouteBytes: options.maxRouteBytes, async validateRoute(route, context) {
|
|
215
|
+
if (!offer || stopping || moving || closed || !program) return false;
|
|
216
|
+
try {
|
|
217
|
+
const checked = await validatePartitionRoute(program.runtime, program.plan, route), local = checked.stages[stage.index];
|
|
218
|
+
if (!equal(local, stage) || checked.modelIdentity !== program.model.identity) return false;
|
|
219
|
+
return context.incoming ? stage.index > 0 && checked.stages[0].participantId === context.remoteParticipantId
|
|
220
|
+
&& checked.stages[0].transportId === context.remoteTransportId
|
|
221
|
+
: stage.index === 0 && checked.stages.some(row => row.index > 0 && row.participantId === context.remoteParticipantId && row.transportId === context.remoteTransportId);
|
|
222
|
+
} catch { return false; }
|
|
223
|
+
} },
|
|
224
|
+
createEndpoint: ({ channel, remoteParticipantId, remoteTransportId, route }) => {
|
|
225
|
+
const index = stage.index || route.stages.find(row => row.participantId === remoteParticipantId && row.transportId === remoteTransportId)?.index;
|
|
226
|
+
return createPartitionPeer({ channel, localParticipantId: identity.peerId, remoteParticipantId, runtime: program.runtime,
|
|
227
|
+
plan: program.plan, planId: program.planId, modelIdentity: program.model.identity, authority,
|
|
228
|
+
contributor: stage.index > 0 ? program.resident : null, limits: config.executionChannel, receiverLimits: config.receiver,
|
|
229
|
+
ordered: { route, partitionIndex: index } });
|
|
230
|
+
} });
|
|
231
|
+
phase = 'ready'; progress = null; readyAt = Date.now();
|
|
232
|
+
} catch (cause) {
|
|
233
|
+
for (const owner of preparationOwners) debt.add(owner);
|
|
234
|
+
if (typeof candidate?.resident?.close === 'function') debt.add(candidate.resident);
|
|
235
|
+
phase = 'failed'; error = cause.message;
|
|
236
|
+
} finally { observingPreparation = false; loading = null; notify(); }
|
|
237
|
+
}
|
|
238
|
+
const healthy = owner => owner.ports.every(port => port.getState().ready);
|
|
239
|
+
/** Opt-in: rank complete admissible routes by expected cost; peer id only breaks ties. A stage
|
|
240
|
+
* without an eligible prepared offer reports unavailable capacity rather than a partial route. */
|
|
241
|
+
async function rankedRoute(selection, existing) {
|
|
242
|
+
const eligible = await eligiblePartitionStages(selection);
|
|
243
|
+
if (eligible.candidates.some(rows => !rows.length)) return selectPartitionRoute(selection);
|
|
244
|
+
const capacity = new Map(selection.capabilities.map(peer => [peer.participantId, peer.description?.capacity ?? null]));
|
|
245
|
+
let ranked;
|
|
246
|
+
try {
|
|
247
|
+
ranked = rankPartitionRoutes({ planId: eligible.planId, observations, policy: costPolicy, now: selection.now,
|
|
248
|
+
open: [...routes.values()].filter(healthy).map(row => row.route.stages),
|
|
249
|
+
candidates: eligible.candidates.map(rows => rows.map(row => ({ ...row, local: row.participantId === identity.peerId,
|
|
250
|
+
capacity: structuredClone(capacity.get(row.participantId)) }))),
|
|
251
|
+
workload: { ...ranking.workload, allocationBytes: 0 },
|
|
252
|
+
// Every enumerated route stays listed so the current one is always found if admissible.
|
|
253
|
+
limits: { maxParticipants: config.maxPeers, maxRoutes: options.maxSelectionSteps, maxSelectionSteps: options.maxSelectionSteps } });
|
|
254
|
+
} catch (cause) {
|
|
255
|
+
if (cause?.message !== ROUTE_RANKING_WORK_LIMIT) throw cause;
|
|
256
|
+
return existing ? { route: existing.route, reason: 'retain-prepared-route', ranking: 'work-limit' }
|
|
257
|
+
: { ...await selectPartitionRoute(selection), ranking: 'work-limit' };
|
|
258
|
+
}
|
|
259
|
+
const chat = existing?.chat.getState();
|
|
260
|
+
const chosen = preferRoute({ current: existing && healthy(existing) ? existing.route : null, ranked,
|
|
261
|
+
active: !!(chat?.active || chat?.pendingInputs), margin: ranking.margin });
|
|
262
|
+
const stageIds = route => JSON.stringify(route.stages.map(({ participantId, transportId, residentId, executionId }) =>
|
|
263
|
+
[participantId, transportId, residentId, executionId]));
|
|
264
|
+
const same = route => stageIds(route) === stageIds(chosen);
|
|
265
|
+
const record = reason => routeSelectionRecord({ policy: costPolicy, workload: ranking.workload, ranked, reason, decidedAt: selection.now,
|
|
266
|
+
maxRoutes: options.maxRoutes, chosen: chosen ? ranked.find(same) ?? null : null });
|
|
267
|
+
if (!chosen) return { route: null, reason: 'no-admissible-route', selection: record('no-admissible-route') };
|
|
268
|
+
if (existing && chosen === existing.route) return { route: existing.route, reason: 'retain-ranked-route', selection: record('retain-ranked-route') };
|
|
269
|
+
const route = await validatePartitionRoute(program.runtime, program.plan, { schema: PARTITION_ROUTE_SCHEMA,
|
|
270
|
+
modelId: eligible.model.id, modelIdentity: eligible.model.identity, planId: eligible.planId, activation: eligible.activation,
|
|
271
|
+
stages: chosen.stages.map(({ index, participantId, transportId, residentId, executionId }) => ({ index, participantId, transportId, residentId, executionId })) });
|
|
272
|
+
return { route, reason: 'ranked-route', measured: chosen.measured, cost: chosen.cost, selection: record('ranked-route') };
|
|
273
|
+
}
|
|
274
|
+
async function reconcile() {
|
|
275
|
+
if (!offer || !program || stage.index !== 0 || stopping || closed || phase !== 'ready') return;
|
|
276
|
+
const existing = current();
|
|
277
|
+
if (!ranking && existing && healthy(existing)) {
|
|
278
|
+
// Discovery/signaling loss does not terminate a still-authorized healthy channel.
|
|
279
|
+
decision = { route: existing.route, reason: 'retain-prepared-route' }; return;
|
|
280
|
+
}
|
|
281
|
+
for (const [key, owner] of routes) {
|
|
282
|
+
const state = owner.chat.getState();
|
|
283
|
+
// With ranking, a healthy route that is no longer preferred retires once its own attempts finish.
|
|
284
|
+
if (healthy(owner) && (!ranking || key === currentKey) || state.active || state.pendingInputs) continue;
|
|
285
|
+
await closeChat(owner.chat);
|
|
286
|
+
for (const row of owner.route.stages.slice(1)) await execution.retire(row.transportId, { route: owner.route });
|
|
287
|
+
routes.delete(key); if (currentKey === key) currentKey = null;
|
|
288
|
+
}
|
|
289
|
+
if (!ranking && routes.size >= options.maxRoutes) return;
|
|
290
|
+
const now = Date.now(), local = { participantId: identity.peerId, transportId: stage.transportId, available: true,
|
|
291
|
+
observedAt: now, expiresAt: now + config.inputLimits.descriptorTtlMs, description: describe() };
|
|
292
|
+
const remote = discovery.getSnapshot().filter(peer => peer.participantId !== identity.peerId).map(peer => ({ ...peer,
|
|
293
|
+
description: peer.description && { ...peer.description, models: (peer.description.models || []).filter(model => model.index !== 0) } }));
|
|
294
|
+
const selection = { runtime: program.runtime, plan: program.plan, model: program.model,
|
|
295
|
+
capabilities: [local, ...remote].slice(0, config.maxPeers), now,
|
|
296
|
+
limits: { maxPeers: config.maxPeers, maxStages: config.limits.maxStages, maxOffersPerPeer: config.maxStageOffersPerPeer, maxSelectionSteps: options.maxSelectionSteps } };
|
|
297
|
+
const kept = existing && healthy(existing) ? existing : null;
|
|
298
|
+
decision = ranking ? await rankedRoute(selection, kept) : await selectPartitionRoute(selection);
|
|
299
|
+
if (!decision.route) {
|
|
300
|
+
if (kept) { decision = { ...decision, route: kept.route }; if (decision.selection) kept.selection = decision.selection; }
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const route = decision.route, key = canonicalPartitionJson(route);
|
|
304
|
+
if (routes.has(key)) { if (decision.selection) routes.get(key).selection = decision.selection; currentKey = key; return; }
|
|
305
|
+
if (ranking && routes.size >= options.maxRoutes) { decision = { ...decision, route: kept?.route ?? null, reason: 'route-limit' }; return; }
|
|
306
|
+
const ports = [];
|
|
307
|
+
try {
|
|
308
|
+
for (const row of route.stages.slice(1)) {
|
|
309
|
+
const started = performance.now();
|
|
310
|
+
ports.push(await execution.connect(row.transportId, { route }));
|
|
311
|
+
// Opening a new route is the preparation a not-yet-open route still needs.
|
|
312
|
+
observations?.recordOpening({ planId: route.planId, stage: row, openingMs: performance.now() - started, at: Date.now() });
|
|
313
|
+
}
|
|
314
|
+
assert(offer && !stopping && !closed, 'Contribution stopped while opening route');
|
|
315
|
+
const chat = createPartitionChat({ runtime: program.runtime, model: program.model, authority, plan: program.plan,
|
|
316
|
+
planId: program.planId, limits: config.limits, grantTtlMs: config.grantMs,
|
|
317
|
+
ordered: { route, stages: [program.resident, ...ports], queue: attemptQueue },
|
|
318
|
+
authorizeRequester: async () => !!offer && !closed && !stopping && !moving });
|
|
319
|
+
routes.set(key, { route, ports, chat, selection: decision.selection ?? null });
|
|
320
|
+
await chat.refresh({ signal: controller.signal }); currentKey = key; notify();
|
|
321
|
+
} catch (cause) {
|
|
322
|
+
// Network owns even a rejected or unacknowledged opening until explicit settlement.
|
|
323
|
+
if (!routes.has(key)) {
|
|
324
|
+
const cleanup = await Promise.allSettled(route.stages.slice(1).map(row => execution.retire(row.transportId, { route })));
|
|
325
|
+
const failures = cleanup.filter(row => row.status === 'rejected').map(row => row.reason);
|
|
326
|
+
if (failures.length) { debt.add(execution); throw new AggregateError([cause, ...failures], 'Ordered route opening and settlement failed'); }
|
|
327
|
+
}
|
|
328
|
+
throw cause;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const hex = () => Array.from(crypto.getRandomValues(new Uint8Array(16)), byte => byte.toString(16).padStart(2, '0')).join('');
|
|
332
|
+
// Fresh allocation labels per preparation; participant and transport come from the host binding.
|
|
333
|
+
const autoIdentity = index => ({ index, participantId: identity.peerId, transportId: auto.transportId,
|
|
334
|
+
residentId: 'auto-' + hex(), executionId: 'auto-' + hex() });
|
|
335
|
+
function plan(current) {
|
|
336
|
+
const capabilities = discovery.getSnapshot(), now = Date.now(), stageCount = offer.partitionPlan.stageCount;
|
|
337
|
+
const demand = replicas ? stageDemand({ model: offer, stageCount, capabilities, now, maxOffersPerPeer: config.maxStageOffersPerPeer,
|
|
338
|
+
queueBound: replicas.queueBound, bottleneckRatio: replicas.bottleneckRatio }) : null;
|
|
339
|
+
const decision = planStageAssignment({ participantId: identity.peerId, model: offer, stageCount, capabilities, now, current,
|
|
340
|
+
maxOffersPerPeer: config.maxStageOffersPerPeer, demand });
|
|
341
|
+
return { ...decision, decidedAt: now, ...(demand ? { demand } : {}) };
|
|
342
|
+
}
|
|
343
|
+
// Idle: no admitted, queued, pending or unsettled attempt on this resident or any of its routes.
|
|
344
|
+
function idle() {
|
|
345
|
+
const state = program.resident.getState();
|
|
346
|
+
return !(state.activeAttempts > 0) && !(state.reservations?.active > 0) && !debt.size
|
|
347
|
+
&& [...routes.values()].every(row => { const chat = row.chat.getState();
|
|
348
|
+
return !chat.active && !chat.unsettled && !chat.pendingInputs && !chat.unsettledInputs; });
|
|
349
|
+
}
|
|
350
|
+
async function reassess() {
|
|
351
|
+
if (!auto || !offer || stopping || closed || moving || loading) return;
|
|
352
|
+
if (!program && phase === 'waiting') {
|
|
353
|
+
assignment = plan(null);
|
|
354
|
+
if (assignment.index === null) { notify(); return; }
|
|
355
|
+
stage = autoIdentity(assignment.index); await prepare();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (!program || phase !== 'ready'
|
|
359
|
+
|| Date.now() - readyAt < auto.dwellMs || !idle()) return;
|
|
360
|
+
const next = plan(stage.index);
|
|
361
|
+
assignment = next;
|
|
362
|
+
if (!next.move) return;
|
|
363
|
+
// Synchronously refuse new admissions before any await; nothing is active to move.
|
|
364
|
+
moving = (async () => program.resident.drain?.())().catch(() => {});
|
|
365
|
+
phase = 'stopping'; notify();
|
|
366
|
+
try {
|
|
367
|
+
await retire();
|
|
368
|
+
if (!offer || stopping || closed) return;
|
|
369
|
+
stage = autoIdentity(next.index); await prepare();
|
|
370
|
+
} catch (cause) { phase = 'failed'; error = cause.message; }
|
|
371
|
+
finally { moving = null; notify(); }
|
|
372
|
+
}
|
|
373
|
+
async function tick() {
|
|
374
|
+
if (closed) return;
|
|
375
|
+
if (ticking) return ticking;
|
|
376
|
+
// Without autoStage the tick is exactly the Launch 1 sequence: refresh, then reconcile.
|
|
377
|
+
ticking = (async () => { await discovery.refresh(); if (auto) await reassess(); await reconcile(); notify(); })();
|
|
378
|
+
try { await ticking; } catch (cause) { error = cause.message; notify(); }
|
|
379
|
+
finally { ticking = null; }
|
|
380
|
+
}
|
|
381
|
+
const localEntry = {
|
|
382
|
+
id: identity.peerId,
|
|
383
|
+
async refresh({ signal } = {}) {
|
|
384
|
+
await service.refresh({ signal });
|
|
385
|
+
return { ready: !!offer && !stopping && !moving && phase === 'ready', descriptor: { models: service.getModels() } };
|
|
386
|
+
},
|
|
387
|
+
async generate(input, { signal, onDelta, onUsage, onActivity, onOutput, onState, publication, onPublished }) {
|
|
388
|
+
assert(equal(admissions.get(input.attemptId), input), 'Local ordered input is not approved');
|
|
389
|
+
assert((publication !== undefined) === (input.publication !== undefined), 'Local ordered publication requires its requester context');
|
|
390
|
+
const model = service.getModels().find(row => equal(row.partition?.route, input.route) && row.availability === 'ready');
|
|
391
|
+
assert(model, 'Approved local ordered route unavailable');
|
|
392
|
+
// The same requester-owned scope as a remote entry, without a channel.
|
|
393
|
+
const scope = publication === undefined ? null : createEntryPublicationScope({ request: input, owner: publication, onPublished,
|
|
394
|
+
authorize: () => equal(admissions.get(input.attemptId), input),
|
|
395
|
+
maxControlBytes: config.inputChannel?.maxControlBytes, maxOutputCharacters: config.inputLimits.maxOutputCharacters });
|
|
396
|
+
let failure = null, result;
|
|
397
|
+
try {
|
|
398
|
+
result = await service.generate({ meshId, participantId: identity.peerId, threadId: input.threadId,
|
|
399
|
+
attemptId: input.attemptId, placementGeneration: input.placementGeneration, disclosureExpiresAt: input.disclosureExpiresAt, model, messages: structuredClone(input.messages), ...(input.generationRequest ? {generationRequest: input.generationRequest} : {}),
|
|
400
|
+
...(scope ? { publication: structuredClone(input.publication) } : {}), ...(input.charge !== undefined ? { charge: structuredClone(input.charge) } : {}),
|
|
401
|
+
permissions: { sharingScope: 'approved-partition-path' } }, { signal, ...(input.charge !== undefined && onOutput ? { onOutput } : {}),
|
|
402
|
+
...(scope ? { publication: entryPublicationPort((method, body) => scope.call(method, body)) } : {}),
|
|
403
|
+
requestApproval: async () => equal(admissions.get(input.attemptId), input), onActivity: value => onActivity?.(value), onUsage: value => onUsage?.({schema:value.schema,inputTokens:value.inputTokens,outputTokens:value.outputTokens}), onDelta: event => onDelta?.(event.text),
|
|
404
|
+
// The same admission state a remote entry forwards: queued while waiting, executing once admitted.
|
|
405
|
+
onState: value => { if (value?.threadId === input.threadId && value.attemptId === input.attemptId) return onState?.(value.status); } });
|
|
406
|
+
if (scope) {
|
|
407
|
+
const projection = scope.getProjection();
|
|
408
|
+
assert(projection.count > 0 && projection.terminal && result.content === projection.content, 'Local result differs from durable publication');
|
|
409
|
+
}
|
|
410
|
+
} catch (error) { failure = error; }
|
|
411
|
+
try { await scope?.close({ cancel: failure !== null }); }
|
|
412
|
+
catch (error) { failure = failure ? new AggregateError([failure, error], 'Local ordered execution and publication cancellation failed') : error; }
|
|
413
|
+
if (failure) throw failure;
|
|
414
|
+
return { content: result.content, ...(result.usage ? {usage: result.usage} : {}), execution: summarizePartitionExecution(result.execution) };
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
async function refreshed(entry, transportId, model, signal) {
|
|
418
|
+
let state;
|
|
419
|
+
try { state = await entry.refresh({ signal }); }
|
|
420
|
+
catch (cause) { if (!signal?.aborted && entry !== localEntry) discovery.invalidateModel(transportId, model); throw cause; }
|
|
421
|
+
const selected = state.descriptor?.models.find(row => row.id === model.id && row.identity === model.identity && samePartitionAdapters(row, model)
|
|
422
|
+
&& row.availability === 'ready' && row.partition?.schema === 'reploid.partition-selection/v2');
|
|
423
|
+
if (!state.ready || !selected) discovery.invalidateModel(transportId, model);
|
|
424
|
+
return { state, model: selected };
|
|
425
|
+
}
|
|
426
|
+
const timer = setInterval(tick, config.pollMs);
|
|
427
|
+
return Object.freeze({ getState, getModels,
|
|
428
|
+
subscribe(listener) { listeners.add(listener); listener(getModels()); return () => listeners.delete(listener); },
|
|
429
|
+
async contribute(modelId, approved, adapters) {
|
|
430
|
+
assert(approved === true && (stage || auto) && !closed, 'Approve an explicitly assigned stage before contributing');
|
|
431
|
+
assert(!offer && !stopping && !debt.size && !program, 'Previous ordered contribution must settle before replacement');
|
|
432
|
+
const selected = catalog.find(model => model.id === modelId); assert(selected, 'No pinned ordered model');
|
|
433
|
+
offer = selectPartitionContribution(selected, adapters);
|
|
434
|
+
if (auto) {
|
|
435
|
+
const count = offer.partitionPlan?.stageCount;
|
|
436
|
+
if (!Number.isSafeInteger(count) || count < 2 || count > config.limits.maxStages
|
|
437
|
+
|| typeof offer.partitionPlanId !== 'string' || !offer.partitionPlanId) {
|
|
438
|
+
offer = null; throw new Error('Automatic stage assignment requires a declared ordered plan');
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
controller = new AbortController(); phase = 'waiting'; error = null;
|
|
442
|
+
if (auto) {
|
|
443
|
+
// The contributor's own choice from what discovery observed; never a remote assignment.
|
|
444
|
+
notify(); await discovery.refresh();
|
|
445
|
+
if (!offer || stopping || closed) return;
|
|
446
|
+
await reassess();
|
|
447
|
+
} else await prepare();
|
|
448
|
+
if (offer && !stopping && phase === 'ready') await tick();
|
|
449
|
+
},
|
|
450
|
+
stop() {
|
|
451
|
+
if (stopping) return stopping;
|
|
452
|
+
offer = null; phase = 'stopping'; controller?.abort(new Error('Ordered contribution stopped')); notify();
|
|
453
|
+
const wait = [loading, ticking];
|
|
454
|
+
stopping = Promise.resolve().then(async () => {
|
|
455
|
+
await Promise.allSettled(wait);
|
|
456
|
+
try { await retire(); phase = 'idle'; error = null; progress = null; if (auto) { stage = null; assignment = null; } }
|
|
457
|
+
catch (cause) { phase = 'failed'; error = cause.message; throw cause; }
|
|
458
|
+
finally { notify(); }
|
|
459
|
+
}).finally(() => { stopping = null; });
|
|
460
|
+
return stopping;
|
|
461
|
+
},
|
|
462
|
+
async generate(request, controls) {
|
|
463
|
+
request = structuredClone(request);
|
|
464
|
+
const requested = generationRequest(request.generationRequest);
|
|
465
|
+
assert(!requested || requested.maxOutputTokens <= config.limits.maxTokens, 'Caller token limit exceeds route budget');
|
|
466
|
+
request = structuredClone(request);
|
|
467
|
+
const published = controls.publication !== undefined;
|
|
468
|
+
assert(!closed && request.canonical === undefined && (!published || typeof controls.onPublished === 'function'
|
|
469
|
+
&& /^[a-f0-9]{32}$/.test(controls.publisherEpoch)), 'Ordered automatic entry requires ordinary execution and, when publishing, the requester-owned writer');
|
|
470
|
+
const signal = controls.signal ? AbortSignal.any([lifetime.signal, controls.signal]) : lifetime.signal;
|
|
471
|
+
signal.throwIfAborted();
|
|
472
|
+
const observed = discovery.getSnapshot();
|
|
473
|
+
if (offer && stage?.index === 0 && current()) observed.unshift({ participantId: identity.peerId, transportId: stage.transportId,
|
|
474
|
+
available: true, description: describe() });
|
|
475
|
+
const selected = selectPartitionExecution({ model: request.model, capabilities: observed, ordered: true });
|
|
476
|
+
// Nothing is sent when no coordinator can take the attempt; a full advertised queue says so.
|
|
477
|
+
if (!selected.transportId && selected.eligibility.some(row => row.reason === 'input-owner-queue-full')) throw partitionFailure('CAPACITY_QUEUE_FULL');
|
|
478
|
+
assertCapacity(selected.transportId);
|
|
479
|
+
const entry = selected.transportId === stage?.transportId ? localEntry : await entries.connect(selected.transportId);
|
|
480
|
+
const initial = await refreshed(entry, selected.transportId, request.model, signal), model = structuredClone(initial.model);
|
|
481
|
+
assertCapacity(initial.state.ready && model);
|
|
482
|
+
requireGenerationCapability(model, requested);
|
|
483
|
+
const route = canonicalPartitionRoute(model.partition.route), identityBinding = { schema: 'reploid.partition-attempt/v2', route,
|
|
484
|
+
requesterId: identity.peerId, threadId: request.threadId, attemptId: request.attemptId, placementGeneration: request.placementGeneration ?? 0 };
|
|
485
|
+
const disclosure = orderedPartitionDisclosure(identityBinding);
|
|
486
|
+
let expiresAt = Date.now() + config.grantMs;
|
|
487
|
+
const approved = await controls.requestApproval({ id: crypto.randomUUID(), threadId: request.threadId, attemptId: request.attemptId,
|
|
488
|
+
operation: 'generate-partition', peerId: route.stages[0].participantId, recipientIdentity: route.stages[0].participantId,
|
|
489
|
+
...structuredClone(model.partition), modelId: model.id, modelIdentity: model.identity, adapterIdentities: partitionAdapterIdentities(model), disclosure, expiresAt, executionTimeoutMs: config.grantMs,
|
|
490
|
+
input: { messages: structuredClone(request.messages), ...(requested ? {generationRequest: requested} : {}), recipients: [...new Set([...disclosure.tokenRecipients, ...disclosure.outputRecipients])] } });
|
|
491
|
+
signal.throwIfAborted(); assert(approved === true && Date.now() < expiresAt, 'Ordered disclosure declined or expired');
|
|
492
|
+
expiresAt = Math.min(controls.authorizationExpiresAt?.() ?? expiresAt, Date.now() + config.grantMs);
|
|
493
|
+
const checked = await refreshed(entry, selected.transportId, model, signal);
|
|
494
|
+
assertCapacity(checked.state.ready && checked.model);
|
|
495
|
+
assert(equal(checked.model.partition, model.partition), 'Approved ordered route changed; review a new attempt');
|
|
496
|
+
signal.throwIfAborted(); assert(Date.now() < expiresAt, 'Ordered disclosure expired');
|
|
497
|
+
const input = { schema: 'reploid.partition-input/v3', operation: 'generate', meshId, requesterId: identity.peerId,
|
|
498
|
+
coordinatorId: route.stages[0].participantId, route, modelId: model.id, modelIdentity: model.identity, planId: route.planId,
|
|
499
|
+
...(controls.onActivity ? {activity:'reploid.partition-activity/v1'} : {}),
|
|
500
|
+
// This requester reports the coordinator's admission state (queued, then executing).
|
|
501
|
+
attemptState: ATTEMPT_STATE_SCHEMA,
|
|
502
|
+
threadId: request.threadId, attemptId: request.attemptId, placementGeneration: identityBinding.placementGeneration, disclosureExpiresAt: expiresAt, messages: request.messages, ...(requested ? {generationRequest: requested} : {}),
|
|
503
|
+
...(published ? { publication: { schema: ORDERED_PUBLICATION_CONTEXT_SCHEMA, publisherEpoch: controls.publisherEpoch, deadlineAt: expiresAt } } : {}),
|
|
504
|
+
// A charged attempt's signed binding, made after its spending binding; outputs return for acknowledgment.
|
|
505
|
+
...(controls.charge && typeof controls.onOutput === 'function' ? { charge: structuredClone(controls.charge) } : {}) };
|
|
506
|
+
assert(!admissions.has(input.attemptId), 'Ordered requester attempt already admitted'); admissions.set(input.attemptId, structuredClone(input));
|
|
507
|
+
let sequence = 0, executing = false;
|
|
508
|
+
// The attempt stays queued until its coordinator admits it: by the coordinator's own notice, or,
|
|
509
|
+
// from a coordinator that sends none, by the first delta, usage or activity of this attempt (a
|
|
510
|
+
// committed publication marks the conversation executing itself).
|
|
511
|
+
const state = status => controls.onState?.({ threadId: request.threadId, attemptId: request.attemptId, status,
|
|
512
|
+
execution: { placement: 'ordered-layer-partition', identity: identityBinding } });
|
|
513
|
+
const admitted = () => { if (!executing) { executing = true; state('executing'); } };
|
|
514
|
+
const first = callback => callback && ((...args) => { admitted(); return callback(...args); });
|
|
515
|
+
try {
|
|
516
|
+
state('queued');
|
|
517
|
+
const result = await entry.generate(input, { signal, ...(published ? { publication: controls.publication, onPublished: controls.onPublished } : {}),
|
|
518
|
+
...(input.charge ? { onOutput: controls.onOutput } : {}),
|
|
519
|
+
onState: status => { if (status === 'executing') admitted(); },
|
|
520
|
+
...(controls.onActivity ? {onActivity: first(activity => controls.onActivity({threadId:request.threadId,attemptId:request.attemptId,...activity}))} : {}),
|
|
521
|
+
onUsage: first(usage => controls.onUsage?.({threadId: request.threadId, attemptId: request.attemptId, ...usage})),
|
|
522
|
+
onDelta: first(text => controls.onDelta?.({ threadId: request.threadId, attemptId: request.attemptId, sequence: sequence++, text })) });
|
|
523
|
+
return { ...result, threadId: request.threadId, attemptId: request.attemptId, modelId: model.id, modelIdentity: model.identity,
|
|
524
|
+
adapterIdentities: partitionAdapterIdentities(model), execution: { ...result.execution, selection: selected } };
|
|
525
|
+
} finally { admissions.delete(input.attemptId); }
|
|
526
|
+
},
|
|
527
|
+
close() {
|
|
528
|
+
if (closing) return closing;
|
|
529
|
+
closed = true; clearInterval(timer); lifetime.abort(new Error('Ordered mesh closed'));
|
|
530
|
+
closing = (async () => {
|
|
531
|
+
const stopped = await Promise.allSettled([this.stop()]);
|
|
532
|
+
const independent = await Promise.allSettled([closeOwner(entries), closeOwner(discovery)]);
|
|
533
|
+
const failures = [...stopped, ...independent].filter(row => row.status === 'rejected').map(row => row.reason);
|
|
534
|
+
if (failures.length) throw new AggregateError(failures, 'Ordered automatic cleanup remains unsettled');
|
|
535
|
+
authority.close(); listeners.clear(); admissions.clear();
|
|
536
|
+
})().catch(cause => { closing = null; throw cause; });
|
|
537
|
+
return closing;
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|