@mcoda/mswarm 0.1.132 → 0.1.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/codali-executor.d.ts +3 -1
- package/dist/codali-executor.d.ts.map +1 -1
- package/dist/codali-executor.js +5 -0
- package/dist/codali-executor.js.map +1 -1
- package/dist/runtime.d.ts +14 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +165 -4
- package/dist/runtime.js.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGateway.js +5 -3
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.js +13 -1
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +44 -3
- package/dist/vendor/codali/providers/ProviderTypes.d.ts +2 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts +2 -0
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/CodaliRuntime.js +2 -0
- package/dist/vendor/codali/runtime/Runner.d.ts +6 -0
- package/dist/vendor/codali/runtime/Runner.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/Runner.js +22 -4
- package/package.json +3 -3
package/dist/runtime.js
CHANGED
|
@@ -202,6 +202,7 @@ function resolvePersistentNodePath(nodePath) {
|
|
|
202
202
|
const DEFAULT_EXPOSE_ALL_MODELS = true;
|
|
203
203
|
const LOCAL_OPENAI_COMPATIBLE_ADAPTERS = new Set([
|
|
204
204
|
"openai-compatible-local",
|
|
205
|
+
"strata-local",
|
|
205
206
|
"vllm-local",
|
|
206
207
|
"llama-cpp-local",
|
|
207
208
|
"llamacpp-local"
|
|
@@ -244,6 +245,7 @@ const DEFAULT_VIDEO_GENERATIVE_OUTPUT_BYTES = 256 * 1024 * 1024;
|
|
|
244
245
|
const GENERATIVE_RESPONSE_JSON_OVERHEAD_BYTES = 64 * 1024;
|
|
245
246
|
const LOCAL_RUNNER_KIND_ALIASES = {
|
|
246
247
|
vllm: "vllm",
|
|
248
|
+
strata: "strata",
|
|
247
249
|
"llama-cpp": "llama-cpp",
|
|
248
250
|
"llama.cpp": "llama-cpp",
|
|
249
251
|
llamacpp: "llama-cpp",
|
|
@@ -526,6 +528,8 @@ function defaultLocalRunnerKindForAdapter(adapter) {
|
|
|
526
528
|
const lookupKey = normalizeLookupKey(normalized);
|
|
527
529
|
if (lookupKey === "vllm-local")
|
|
528
530
|
return "vllm";
|
|
531
|
+
if (lookupKey === "strata-local")
|
|
532
|
+
return "strata";
|
|
529
533
|
if (lookupKey === "llama-cpp-local" || lookupKey === "llamacpp-local")
|
|
530
534
|
return "llama-cpp";
|
|
531
535
|
return undefined;
|
|
@@ -2082,6 +2086,9 @@ export function mapMcodaAgentToSelfHostedModel(agent, config) {
|
|
|
2082
2086
|
: inferredInputModalities);
|
|
2083
2087
|
const outputModalities = localRunner?.outputModalities ?? inferGenerativeModalities(operations, "output");
|
|
2084
2088
|
const publicModelId = localRunner?.publicModelId;
|
|
2089
|
+
const legacyConfig = isRecord(agent.config) ? agent.config : {};
|
|
2090
|
+
const legacyNativeBase = normalizeLocalRunnerKind(legacyConfig.runnerKind ?? legacyConfig.runner_kind) === "llama-cpp"
|
|
2091
|
+
? mcodaAgentLocalRunnerBaseUrl(agent) : undefined;
|
|
2085
2092
|
return {
|
|
2086
2093
|
name: slug,
|
|
2087
2094
|
provider: "mcoda",
|
|
@@ -2092,9 +2099,9 @@ export function mapMcodaAgentToSelfHostedModel(agent, config) {
|
|
|
2092
2099
|
model_id: publicModelId || model,
|
|
2093
2100
|
public_model_id: publicModelId,
|
|
2094
2101
|
upstream_model: model,
|
|
2095
|
-
base_url: localRunner?.baseUrl,
|
|
2096
|
-
runner_kind: localRunner?.generativeRunnerKind ?? localRunner?.runnerKind,
|
|
2097
|
-
auth_mode: localRunner?.authMode,
|
|
2102
|
+
base_url: localRunner?.baseUrl ?? legacyNativeBase ?? undefined,
|
|
2103
|
+
runner_kind: localRunner?.generativeRunnerKind ?? localRunner?.runnerKind ?? (legacyNativeBase ? "llama-cpp" : undefined),
|
|
2104
|
+
auth_mode: localRunner?.authMode ?? (legacyNativeBase ? normalizeLocalRunnerAuthMode(legacyConfig.authMode) : undefined),
|
|
2098
2105
|
response_format_strategy: localRunner?.responseFormatStrategy,
|
|
2099
2106
|
health_path: localRunner?.healthPath,
|
|
2100
2107
|
models_path: localRunner?.modelsPath,
|
|
@@ -4395,6 +4402,13 @@ export class MswarmSelfHostedNodeClient {
|
|
|
4395
4402
|
body: JSON.stringify(payload)
|
|
4396
4403
|
}, this.timeoutMs);
|
|
4397
4404
|
}
|
|
4405
|
+
async reportLoad(runtimeToken, payload) {
|
|
4406
|
+
return fetchJson(this.fetchImpl, `${this.gatewayBaseUrl}/v1/swarm/self-hosted/node/load`, {
|
|
4407
|
+
method: "POST",
|
|
4408
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${runtimeToken}` },
|
|
4409
|
+
body: JSON.stringify(payload)
|
|
4410
|
+
}, Math.min(this.timeoutMs, 5000));
|
|
4411
|
+
}
|
|
4398
4412
|
async uninstall(runtimeToken, payload) {
|
|
4399
4413
|
return fetchJson(this.fetchImpl, `${this.gatewayBaseUrl}/v1/swarm/self-hosted/node/uninstall`, {
|
|
4400
4414
|
method: "POST",
|
|
@@ -4459,6 +4473,15 @@ export class MswarmSelfHostedNodeClient {
|
|
|
4459
4473
|
export class SelfHostedNodeRuntime {
|
|
4460
4474
|
constructor(config, deps) {
|
|
4461
4475
|
this.activeChatJobs = 0;
|
|
4476
|
+
this.activeRequestIds = new Set();
|
|
4477
|
+
this.lastModels = [];
|
|
4478
|
+
this.loadRuntimeToken = null;
|
|
4479
|
+
this.loadReportQueue = Promise.resolve();
|
|
4480
|
+
this.fastLoadUnsupported = false;
|
|
4481
|
+
this.nativeActiveJobs = 0;
|
|
4482
|
+
this.nativeProbeKeys = new Map();
|
|
4483
|
+
this.unavailableModelIds = [];
|
|
4484
|
+
this.nativeModelCapacity = {};
|
|
4462
4485
|
this.activeAgenticJobs = 0;
|
|
4463
4486
|
this.activeGenericJobs = 0;
|
|
4464
4487
|
this.queuedLlmJobs = 0;
|
|
@@ -4665,7 +4688,7 @@ export class SelfHostedNodeRuntime {
|
|
|
4665
4688
|
const maxConcurrency = Math.max(1, Math.floor(this.config.maxConcurrentJobs || 1), llmMaxConcurrency, this.config.genericJobsEnabled ? genericMaxConcurrency : 1);
|
|
4666
4689
|
const activeChatJobs = nonNegativeTelemetryInteger(this.activeChatJobs);
|
|
4667
4690
|
const activeAgenticJobs = nonNegativeTelemetryInteger(this.activeAgenticJobs);
|
|
4668
|
-
const activeLlmJobs = activeChatJobs + activeAgenticJobs;
|
|
4691
|
+
const activeLlmJobs = Math.max(activeChatJobs + activeAgenticJobs, this.nativeActiveJobs);
|
|
4669
4692
|
const activeGenericJobs = nonNegativeTelemetryInteger(this.activeGenericJobs);
|
|
4670
4693
|
const queuedLlmJobs = nonNegativeTelemetryInteger(this.queuedLlmJobs);
|
|
4671
4694
|
const queuedGenericJobs = nonNegativeTelemetryInteger(this.queuedGenericJobs);
|
|
@@ -5660,6 +5683,18 @@ export class SelfHostedNodeRuntime {
|
|
|
5660
5683
|
}
|
|
5661
5684
|
}
|
|
5662
5685
|
async executeJob(job, options = {}) {
|
|
5686
|
+
this.activeRequestIds.add(job.request_id);
|
|
5687
|
+
try {
|
|
5688
|
+
return await this.executeTrackedJob(job, options);
|
|
5689
|
+
}
|
|
5690
|
+
finally {
|
|
5691
|
+
this.activeRequestIds.delete(job.request_id);
|
|
5692
|
+
// A product often starts its next stage immediately after this result.
|
|
5693
|
+
// Publish the newly free native slot before the gateway delivers it.
|
|
5694
|
+
await this.reportLoad().catch(() => undefined);
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
async executeTrackedJob(job, options) {
|
|
5663
5698
|
const startedAt = Date.now();
|
|
5664
5699
|
// Resolved once, before any work: the slot is taken from the moment the job
|
|
5665
5700
|
// is accepted, and a class settled later would leave the count attributed to
|
|
@@ -6027,6 +6062,111 @@ export class SelfHostedNodeRuntime {
|
|
|
6027
6062
|
return result;
|
|
6028
6063
|
}
|
|
6029
6064
|
}
|
|
6065
|
+
async refreshNativeLoad() {
|
|
6066
|
+
const runners = new Map();
|
|
6067
|
+
for (const model of this.lastModels) {
|
|
6068
|
+
const isStrata = model.runner_kind === "strata" || model.adapter === "strata-local";
|
|
6069
|
+
if (model.exposed === false || !model.base_url ||
|
|
6070
|
+
(!isStrata && model.runner_kind !== "llama-cpp" && model.adapter !== "llama-cpp-local"))
|
|
6071
|
+
continue;
|
|
6072
|
+
try {
|
|
6073
|
+
const url = new URL(model.base_url);
|
|
6074
|
+
if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))
|
|
6075
|
+
continue;
|
|
6076
|
+
const endpoint = new URL(isStrata ? "/v1/status" : "/slots", url).toString();
|
|
6077
|
+
const models = runners.get(endpoint) || [];
|
|
6078
|
+
models.push(model);
|
|
6079
|
+
runners.set(endpoint, models);
|
|
6080
|
+
}
|
|
6081
|
+
catch { /* A malformed endpoint is handled by normal discovery health. */ }
|
|
6082
|
+
}
|
|
6083
|
+
let active = 0;
|
|
6084
|
+
const modelCapacity = {};
|
|
6085
|
+
const unavailable = [];
|
|
6086
|
+
await Promise.all([...runners].map(async ([endpoint, models]) => {
|
|
6087
|
+
try {
|
|
6088
|
+
const authenticated = models.find((model) => model.auth_mode === "bearer" && model.source_agent_id);
|
|
6089
|
+
let apiKey;
|
|
6090
|
+
if (authenticated?.source_agent_id) {
|
|
6091
|
+
let pending = this.nativeProbeKeys.get(authenticated.source_agent_id);
|
|
6092
|
+
if (!pending) {
|
|
6093
|
+
pending = this.mcodaAgentAuthResolver({ id: authenticated.source_agent_id,
|
|
6094
|
+
slug: authenticated.source_agent_slug || authenticated.name }).catch(() => undefined);
|
|
6095
|
+
this.nativeProbeKeys.set(authenticated.source_agent_id, pending);
|
|
6096
|
+
}
|
|
6097
|
+
apiKey = await pending;
|
|
6098
|
+
}
|
|
6099
|
+
const response = await this.fetchImpl(endpoint, { signal: AbortSignal.timeout(1500),
|
|
6100
|
+
...(apiKey ? { headers: { authorization: `Bearer ${apiKey}` } } : {}) });
|
|
6101
|
+
const isStrata = new URL(endpoint).pathname === "/v1/status";
|
|
6102
|
+
// llama.cpp slot introspection is optional. Strata status also proves the model is loaded.
|
|
6103
|
+
if (!isStrata && [401, 403, 404].includes(response.status))
|
|
6104
|
+
return;
|
|
6105
|
+
if (!response.ok)
|
|
6106
|
+
throw new Error("runner_load_unavailable");
|
|
6107
|
+
const slots = await response.json();
|
|
6108
|
+
if (isStrata) {
|
|
6109
|
+
const concurrency = isRecord(slots) && isRecord(slots.concurrency) ? slots.concurrency.serving : undefined;
|
|
6110
|
+
const inFlight = isRecord(slots) && isRecord(slots.activity) ? slots.activity.in_flight : undefined;
|
|
6111
|
+
if (!isRecord(slots) || slots.loaded !== true ||
|
|
6112
|
+
typeof concurrency !== "number" || !Number.isSafeInteger(concurrency) || concurrency < 1 ||
|
|
6113
|
+
typeof inFlight !== "number" || !Number.isSafeInteger(inFlight) || inFlight < 0) {
|
|
6114
|
+
throw new Error("runner_load_unavailable");
|
|
6115
|
+
}
|
|
6116
|
+
const processing = Math.min(concurrency, inFlight);
|
|
6117
|
+
active += processing;
|
|
6118
|
+
const capacity = { max_concurrency: concurrency, active_jobs: processing,
|
|
6119
|
+
queued_jobs: Math.max(0, inFlight - concurrency), free_slots: Math.max(0, concurrency - inFlight),
|
|
6120
|
+
pool: `runner:${createHash("sha256").update(endpoint).digest("hex").slice(0, 16)}` };
|
|
6121
|
+
for (const model of models)
|
|
6122
|
+
modelCapacity[model.model_id || model.model || model.name] = capacity;
|
|
6123
|
+
return;
|
|
6124
|
+
}
|
|
6125
|
+
if (!Array.isArray(slots))
|
|
6126
|
+
return;
|
|
6127
|
+
const processing = slots.filter((slot) => isRecord(slot) && slot.is_processing === true).length;
|
|
6128
|
+
active += processing;
|
|
6129
|
+
if (slots.length > 0) {
|
|
6130
|
+
const capacity = { max_concurrency: slots.length, active_jobs: processing, queued_jobs: 0,
|
|
6131
|
+
free_slots: slots.length - processing, pool: `runner:${createHash("sha256").update(endpoint).digest("hex").slice(0, 16)}` };
|
|
6132
|
+
for (const model of models)
|
|
6133
|
+
modelCapacity[model.model_id || model.model || model.name] = capacity;
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
catch {
|
|
6137
|
+
unavailable.push(...models.map((model) => model.model_id || model.model || model.name));
|
|
6138
|
+
}
|
|
6139
|
+
}));
|
|
6140
|
+
this.nativeActiveJobs = active;
|
|
6141
|
+
this.nativeModelCapacity = modelCapacity;
|
|
6142
|
+
this.unavailableModelIds = [...new Set(unavailable)];
|
|
6143
|
+
}
|
|
6144
|
+
reportLoad() {
|
|
6145
|
+
// Serialize probes and writes so an older busy snapshot cannot overwrite
|
|
6146
|
+
// the completion snapshot when the periodic reporter is already running.
|
|
6147
|
+
const pending = this.loadReportQueue.then(() => this.reportLoadSnapshot());
|
|
6148
|
+
this.loadReportQueue = pending.catch(() => undefined);
|
|
6149
|
+
return pending;
|
|
6150
|
+
}
|
|
6151
|
+
async reportLoadSnapshot() {
|
|
6152
|
+
if (this.config.loadReportingEnabled === false || this.fastLoadUnsupported || !this.loadRuntimeToken)
|
|
6153
|
+
return;
|
|
6154
|
+
await this.refreshNativeLoad();
|
|
6155
|
+
const telemetry = this.buildLoadTelemetry({ models: this.lastModels });
|
|
6156
|
+
try {
|
|
6157
|
+
await this.gateway.reportLoad(this.loadRuntimeToken, {
|
|
6158
|
+
node_id: this.config.nodeId,
|
|
6159
|
+
capacity: { ...telemetry, active_request_ids: [...this.activeRequestIds],
|
|
6160
|
+
unavailable_model_ids: this.unavailableModelIds, model_capacity: this.nativeModelCapacity }
|
|
6161
|
+
});
|
|
6162
|
+
}
|
|
6163
|
+
catch (error) {
|
|
6164
|
+
if (isHttpStatusError(error, 404))
|
|
6165
|
+
this.fastLoadUnsupported = true;
|
|
6166
|
+
else
|
|
6167
|
+
throw error;
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6030
6170
|
async runOnce() {
|
|
6031
6171
|
const enrollment = await this.ensureEnrolled();
|
|
6032
6172
|
let status = "online";
|
|
@@ -6057,6 +6197,10 @@ export class SelfHostedNodeRuntime {
|
|
|
6057
6197
|
this.lifecyclePollingDisabled = true;
|
|
6058
6198
|
}
|
|
6059
6199
|
const discoveryLatencyMs = Date.now() - startedAt;
|
|
6200
|
+
if (models.length || status === "online")
|
|
6201
|
+
this.lastModels = models;
|
|
6202
|
+
this.nativeProbeKeys.clear();
|
|
6203
|
+
await this.refreshNativeLoad();
|
|
6060
6204
|
const capabilityPayload = await this.buildCapabilityHeartbeatPayload(enrollment.runtimeToken);
|
|
6061
6205
|
const loadTelemetry = this.buildLoadTelemetry({
|
|
6062
6206
|
models,
|
|
@@ -6077,6 +6221,9 @@ export class SelfHostedNodeRuntime {
|
|
|
6077
6221
|
max_concurrent_llm_jobs: loadTelemetry.max_concurrent_llm_jobs,
|
|
6078
6222
|
max_concurrent_generic_jobs: loadTelemetry.max_concurrent_generic_jobs,
|
|
6079
6223
|
active_jobs: loadTelemetry.active_jobs,
|
|
6224
|
+
active_request_ids: [...this.activeRequestIds],
|
|
6225
|
+
unavailable_model_ids: this.unavailableModelIds,
|
|
6226
|
+
model_capacity: this.nativeModelCapacity,
|
|
6080
6227
|
queued_jobs: loadTelemetry.queued_jobs,
|
|
6081
6228
|
free_slots: loadTelemetry.free_slots,
|
|
6082
6229
|
drain_mode: loadTelemetry.drain_mode,
|
|
@@ -6148,6 +6295,7 @@ export class SelfHostedNodeRuntime {
|
|
|
6148
6295
|
...(loadTelemetry.hardware_pressure ? { hardware_pressure: loadTelemetry.hardware_pressure } : {})
|
|
6149
6296
|
};
|
|
6150
6297
|
const heartbeatResponse = await this.gateway.heartbeat(enrollment.runtimeToken, heartbeatPayload);
|
|
6298
|
+
this.loadRuntimeToken = enrollment.runtimeToken;
|
|
6151
6299
|
return {
|
|
6152
6300
|
enrolled: enrollment.enrolled,
|
|
6153
6301
|
status,
|
|
@@ -6402,6 +6550,8 @@ export class SelfHostedNodeRuntime {
|
|
|
6402
6550
|
startDaemon() {
|
|
6403
6551
|
let stopped = false;
|
|
6404
6552
|
let timer = null;
|
|
6553
|
+
let loadTimer = null;
|
|
6554
|
+
let loadReporting = false;
|
|
6405
6555
|
let pollTimer = null;
|
|
6406
6556
|
let pollTimerDueAt = 0;
|
|
6407
6557
|
let polling = false;
|
|
@@ -6425,6 +6575,10 @@ export class SelfHostedNodeRuntime {
|
|
|
6425
6575
|
};
|
|
6426
6576
|
const halt = () => {
|
|
6427
6577
|
stopped = true;
|
|
6578
|
+
if (loadTimer) {
|
|
6579
|
+
clearInterval(loadTimer);
|
|
6580
|
+
loadTimer = null;
|
|
6581
|
+
}
|
|
6428
6582
|
if (timer) {
|
|
6429
6583
|
clearTimeout(timer);
|
|
6430
6584
|
timer = null;
|
|
@@ -6574,6 +6728,13 @@ export class SelfHostedNodeRuntime {
|
|
|
6574
6728
|
.finally(schedule);
|
|
6575
6729
|
}, revoked ? REVOKED_RECHECK_INTERVAL_MS : this.config.heartbeatIntervalSeconds * 1000);
|
|
6576
6730
|
};
|
|
6731
|
+
loadTimer = setInterval(() => {
|
|
6732
|
+
if (stopped || revoked || loadReporting)
|
|
6733
|
+
return;
|
|
6734
|
+
loadReporting = true;
|
|
6735
|
+
void this.reportLoad().catch((error) => { noteRevoked(error); }).finally(() => { loadReporting = false; });
|
|
6736
|
+
}, 2000);
|
|
6737
|
+
loadTimer.unref?.();
|
|
6577
6738
|
void this.runOnce()
|
|
6578
6739
|
.then(noteAlive)
|
|
6579
6740
|
.catch((error) => {
|