@jmanuelcorral/openteam 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +29 -6
- package/README.md +29 -7
- package/dist/certificates/graph-release-certificate.json +2 -2
- package/dist/certificates/graph-shadow-certificate.json +2 -2
- package/dist/cli/setupAdapters.d.ts.map +1 -1
- package/dist/cli.js +775 -128
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +20 -15
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/config/schema.d.ts +101 -7
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +648 -122
- package/dist/local/lemonadeResidency.d.ts +2 -14
- package/dist/local/lemonadeResidency.d.ts.map +1 -1
- package/dist/local/llamaSwap.d.ts +5 -0
- package/dist/local/llamaSwap.d.ts.map +1 -0
- package/dist/local/llamaSwapRequestGuard.d.ts +11 -0
- package/dist/local/llamaSwapRequestGuard.d.ts.map +1 -0
- package/dist/local/llamaSwapResidency.d.ts +9 -0
- package/dist/local/llamaSwapResidency.d.ts.map +1 -0
- package/dist/local/modelResidency.d.ts +1 -0
- package/dist/local/modelResidency.d.ts.map +1 -1
- package/dist/local/registry.d.ts.map +1 -1
- package/dist/local/residency.d.ts +16 -0
- package/dist/local/residency.d.ts.map +1 -0
- package/dist/local/types.d.ts +10 -2
- package/dist/local/types.d.ts.map +1 -1
- package/dist/messages/executionSetup.d.ts +22 -3
- package/dist/messages/executionSetup.d.ts.map +1 -1
- package/dist/messages/memoryRuntime.d.ts +6 -0
- package/dist/messages/memoryRuntime.d.ts.map +1 -0
- package/dist/messages/memoryTool.d.ts +1 -1
- package/dist/messages/memoryTool.d.ts.map +1 -1
- package/dist/messages/modelResidency.d.ts +20 -2
- package/dist/messages/modelResidency.d.ts.map +1 -1
- package/dist/plugin/availability.d.ts.map +1 -1
- package/dist/plugin/memoryTool.d.ts.map +1 -1
- package/dist/plugin/modelResidency.d.ts.map +1 -1
- package/dist/storage/index/memoryRuntime.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1049,7 +1049,8 @@ var LocalRuntimeKindSchema = z3.enum([
|
|
|
1049
1049
|
"ollama",
|
|
1050
1050
|
"lmstudio",
|
|
1051
1051
|
"foundry-local",
|
|
1052
|
-
"lemonade"
|
|
1052
|
+
"lemonade",
|
|
1053
|
+
"llama-swap"
|
|
1053
1054
|
]);
|
|
1054
1055
|
var LocalRuntimeSchema = z3.object({
|
|
1055
1056
|
id: z3.string().min(1),
|
|
@@ -1069,15 +1070,25 @@ var LocalRuntimeSchema = z3.object({
|
|
|
1069
1070
|
ctx.addIssue({
|
|
1070
1071
|
code: z3.ZodIssueCode.custom,
|
|
1071
1072
|
path: ["kind"],
|
|
1072
|
-
message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade), since the id is
|
|
1073
|
+
message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade, llama-swap), " + "since the id is not itself a runtime kind."
|
|
1073
1074
|
});
|
|
1074
1075
|
}
|
|
1076
|
+
}).transform((runtime) => {
|
|
1077
|
+
const resolvedKind = runtime.kind ?? runtime.id;
|
|
1078
|
+
if (resolvedKind !== "llama-swap" || runtime.maxConcurrency !== undefined) {
|
|
1079
|
+
return runtime;
|
|
1080
|
+
}
|
|
1081
|
+
return { ...runtime, maxConcurrency: 1 };
|
|
1075
1082
|
});
|
|
1076
1083
|
function localRuntimeKind(runtime) {
|
|
1077
1084
|
return runtime.kind ?? runtime.id;
|
|
1078
1085
|
}
|
|
1079
1086
|
function effectiveMaxConcurrency(runtime) {
|
|
1080
|
-
|
|
1087
|
+
if (runtime.maxConcurrency !== undefined) {
|
|
1088
|
+
return runtime.maxConcurrency;
|
|
1089
|
+
}
|
|
1090
|
+
const kind = runtime.kind ?? (runtime.id !== undefined && LocalRuntimeKindSchema.safeParse(runtime.id).success ? runtime.id : undefined);
|
|
1091
|
+
return kind === "llama-swap" ? 1 : DEFAULT_LOCAL_MAX_CONCURRENCY;
|
|
1081
1092
|
}
|
|
1082
1093
|
var MemoryScopeSchema = z3.enum(["project", "user"]);
|
|
1083
1094
|
var MemoryExtractionModeSchema = z3.enum([
|
|
@@ -5746,7 +5757,7 @@ import { z as z15 } from "zod";
|
|
|
5746
5757
|
// package.json
|
|
5747
5758
|
var package_default = {
|
|
5748
5759
|
name: "@jmanuelcorral/openteam",
|
|
5749
|
-
version: "0.
|
|
5760
|
+
version: "0.26.0",
|
|
5750
5761
|
packageManager: "bun@1.3.14",
|
|
5751
5762
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
5752
5763
|
license: "MIT",
|
|
@@ -6103,6 +6114,23 @@ var executionSetupMessages = {
|
|
|
6103
6114
|
`),
|
|
6104
6115
|
localOnlyTitle: "Local execution",
|
|
6105
6116
|
localRequiredError: "local execution requires an enabled local runtime",
|
|
6117
|
+
llamaSwap: {
|
|
6118
|
+
label: "llama-swap",
|
|
6119
|
+
noLoadedModels: "llama-swap has no canonical models verified as ready with no active routing profile. Keep the intended model ready through the operator's existing runtime controls, then rerun setup; manual IDs cannot bypass admission.",
|
|
6120
|
+
modelNotLoaded: (model) => `llama-swap model "${model}" is not positively verified as canonical and ready without an active routing profile. No substitute was selected.`,
|
|
6121
|
+
probeRequired: "llama-swap setup requires a fresh read-only residency probe before writing configuration. Manual model IDs cannot bypass this requirement.",
|
|
6122
|
+
probeFailed: "llama-swap readiness could not be verified. Check the configured /v1/models catalog and origin /running and /api/profiles endpoints; authenticated runtimes are not supported.",
|
|
6123
|
+
endpointMismatch: "llama-swap endpoint/provider mapping differs from the selected runtime. Reconcile the configured origin or /v1 URL and provider; no configuration was written.",
|
|
6124
|
+
modelIdentityUnknown: (model) => `llama-swap model "${model}" has no usable registered physical identity. Fix its model declaration; setup will not guess an alias.`,
|
|
6125
|
+
declaredLimits: "llama-swap context and output limits are operator-declared metadata, not measured per-slot capacities. Missing output metadata uses openteam's client policy default, not a guessed model cap. Unknown context remains 0 and disables automatic compaction. Tool support remains unknown without evidence.",
|
|
6126
|
+
declaredLimitsTitle: "Declared llama-swap limits",
|
|
6127
|
+
outputReduction: (model, previous, ceiling) => `${model}: reducing limit.output from ${previous} to ${ceiling} to respect the declared runtime ceiling. Smaller operator limits are preserved.`,
|
|
6128
|
+
outputReductionTitle: "Declared output ceiling",
|
|
6129
|
+
outputChanged: (model, ceiling) => `${model}: fresh runtime metadata declares an output ceiling of ${ceiling} below the prepared configuration. Rerun setup to review the updated limit; no configuration was written.`
|
|
6130
|
+
},
|
|
6131
|
+
residencyV2Title: "Resident runtime transport compatibility",
|
|
6132
|
+
residencyV2Restriction: (providers) => `Project policies disable opencode's separate native/core V2 transport for resident-only providers: ${providers.join(", ")}. Guarded V1 provider transport remains supported; this does not ban the public SDK v2 client library. Higher-precedence policies can override project restrictions. Readiness preflight is not an atomic no-autoload guarantee.`,
|
|
6133
|
+
retainedResidencyProviderUnprotected: (providerID) => `Cannot safely retain resident-only provider "${providerID}": the effective configuration must retain its matching runtime, disabled when deselected. Reconcile the runtime/provider configuration; no configuration was written.`,
|
|
6106
6134
|
lemonadeDetectedHint: (loaded, advertised) => `detected - ${loaded} confirmed loaded of ${advertised} advertised text model(s)`,
|
|
6107
6135
|
lemonadeNoLoadedModels: "Lemonade has no text models verified as loaded and ready. Keep the intended model active in Lemonade and rerun setup; inactive or unknown models cannot be selected or entered manually.",
|
|
6108
6136
|
lemonadeModelNotLoaded: (model) => `Lemonade model "${model}" is not positively verified as loaded and ready. Keep that exact text model active and rerun setup; no substitute was selected and no implicit loading is allowed.`,
|
|
@@ -6117,11 +6145,11 @@ var executionSetupMessages = {
|
|
|
6117
6145
|
lemonadeV2InvalidPolicies: "Cannot safely preserve experimental.policies: experimental must be an object and policies must be an array of provider.use statements with a string resource and allow/deny effect. Fix the project configuration before rerunning setup.",
|
|
6118
6146
|
lemonadeV2Title: "Lemonade transport compatibility",
|
|
6119
6147
|
lemonadeV2Restriction: (providers) => `Project policies disable native V2 Lemonade access for: ${providers.join(", ")}. Ordinary guarded V1 configuration remains supported; V2 is not an openteam-hook execution path. Higher-precedence or global policies can override this project restriction; it is not a server-atomic no-autoload guarantee.`,
|
|
6120
|
-
lemonadeInventory: (instanceID, providerID, models) => {
|
|
6148
|
+
lemonadeInventory: (instanceID, providerID, models, runtime = "Lemonade") => {
|
|
6121
6149
|
const inventory = models.map((model) => `${model.modelID} (${model.loaded === true ? "loaded" : model.loaded === false ? "inactive" : "residency unknown"})`).join(", ");
|
|
6122
|
-
return
|
|
6150
|
+
return `${runtime} inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
|
|
6123
6151
|
},
|
|
6124
|
-
lemonadeAgentResidency: (agent, model, state) =>
|
|
6152
|
+
lemonadeAgentResidency: (agent, model, state, runtime = "Lemonade") => `${runtime} readiness: ${agent} (${model}): ${state === "not-loaded" ? "inactive (not ready)" : "residency unknown"}; advertised inventory is not loaded readiness. Keep this exact model active and verify residency before use; requests without verified readiness are blocked.`,
|
|
6125
6153
|
mixedPrimaryQuestion: "Primary coordinator model",
|
|
6126
6154
|
mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
|
|
6127
6155
|
mixedPrimaryFrontier: "Choose a frontier model",
|
|
@@ -6225,6 +6253,8 @@ var executionPolicyMessages = {
|
|
|
6225
6253
|
primaryLocalMismatchRemedy: (sourceFile) => ` remedy: re-run \`openteam setup\` to regenerate ${sourceFile} and opencode.json from the same primary policy, then re-run \`openteam doctor\`.`
|
|
6226
6254
|
};
|
|
6227
6255
|
var doctorMessages = {
|
|
6256
|
+
defaultRuntimeSlots: (slots) => ` · ${slots} slot(s) (default)`,
|
|
6257
|
+
llamaSwapConcurrency: " llama-swap does not report per-model slots; openteam uses the configured runtime-wide concurrency limit. No per-model capacity was inferred.",
|
|
6228
6258
|
localModelLimits: {
|
|
6229
6259
|
section: " local model limits:",
|
|
6230
6260
|
healthy: " ✓ enabled local provider models declare valid output and context limits in opencode.json.",
|
|
@@ -6241,7 +6271,7 @@ var doctorMessages = {
|
|
|
6241
6271
|
invalidInput: "invalid limit.input (expected a positive integer token count)",
|
|
6242
6272
|
outputExceedsContext: "limit.output is greater than or equal to limit.context, so the configured context leaves no usable input window",
|
|
6243
6273
|
outputExceedsInput: "limit.input is less than or equal to the reserved output budget, so compaction would have no usable input threshold",
|
|
6244
|
-
outputRemedy: " remedy: re-run `openteam setup` to write openteam's 8 192
|
|
6274
|
+
outputRemedy: " remedy: re-run `openteam setup` to write openteam's local output policy (at most 8 192 tokens, narrowed by declared runtime ceilings and smaller context/input budgets), then re-run `openteam doctor`.",
|
|
6245
6275
|
unconfiguredLimitRemedy: " remedy: the optional limit block may remain absent; to configure explicit limits, manually add a complete limit object in opencode.json with positive output and non-negative context (0 means unknown), because setup fills an absent limit block only for models the runtime currently discovers. Then re-run `openteam doctor`.",
|
|
6246
6276
|
detectedContextRemedy: " remedy: re-run `openteam setup` while the runtime is reachable to copy the detected usable context budget, then re-run `openteam doctor`.",
|
|
6247
6277
|
unknownContextRemedy: " remedy: re-run `openteam setup` to write the required context:0 sentinel; automatic compaction remains disabled until a positive context is configured.",
|
|
@@ -9953,14 +9983,12 @@ function ensureFoundryV1BaseURL(endpoint) {
|
|
|
9953
9983
|
}
|
|
9954
9984
|
}
|
|
9955
9985
|
|
|
9956
|
-
// src/local/lemonadeResidency.ts
|
|
9957
|
-
import { z as z19 } from "zod";
|
|
9958
|
-
|
|
9959
9986
|
// src/messages/modelResidency.ts
|
|
9960
9987
|
var reasons = {
|
|
9961
9988
|
unknown: "fresh residency metadata is unavailable, malformed, or ambiguous",
|
|
9962
9989
|
inactive: "the requested model is not verified as resident and serving",
|
|
9963
9990
|
identity: "the requested model identity can be rewritten or is ambiguous",
|
|
9991
|
+
"authentication-unsupported": "authentication is required but unsupported by this integration",
|
|
9964
9992
|
destination: "the request destination does not match its configured runtime",
|
|
9965
9993
|
transport: "this provider execution path cannot enforce the residency guard",
|
|
9966
9994
|
request: "this is not a supported, explicit model inference request",
|
|
@@ -9969,22 +9997,328 @@ var reasons = {
|
|
|
9969
9997
|
configuration: "the residency guard configuration is invalid"
|
|
9970
9998
|
};
|
|
9971
9999
|
var modelResidencyMessages = {
|
|
9972
|
-
blocked: (reason) => `[openteam]
|
|
9973
|
-
providerDisabled: (providerID) => `[openteam]
|
|
9974
|
-
extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider."
|
|
10000
|
+
blocked: (reason, runtimeLabel = "Lemonade", detail) => `[openteam] ${runtimeLabel} model request blocked: ${reasons[reason]}${detail === undefined ? "" : ` (${detail})`}.`,
|
|
10001
|
+
providerDisabled: (providerID, runtimeLabel = "Lemonade") => `[openteam] ${runtimeLabel} provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
|
|
10002
|
+
extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider.",
|
|
10003
|
+
extractionRuntimeUnavailableFor: (runtimeLabel) => `[openteam] Memory extraction requires one enabled ${runtimeLabel} local runtime matching the configured extraction provider.`
|
|
10004
|
+
};
|
|
10005
|
+
var llamaSwapResidencyMessages = {
|
|
10006
|
+
catalogModelDetail: (id, detail) => `catalog model "${id}" ${detail}`,
|
|
10007
|
+
invalidCatalogField: (field) => `declares an invalid ${field}`,
|
|
10008
|
+
catalogEntryMustExposeStringId: "catalog entries must expose a string id",
|
|
10009
|
+
catalogModelIdsMustBeNonEmptyTrimmedStrings: "catalog model ids must be non-empty, trimmed strings",
|
|
10010
|
+
expectedDataArray: (path2) => `GET ${path2} must return an object with a data array`,
|
|
10011
|
+
duplicateCanonicalModelId: (modelID, path2) => `duplicate canonical model id "${modelID}" in ${path2}`,
|
|
10012
|
+
httpFailure: (path2, status) => `GET ${path2} failed with HTTP ${status}`,
|
|
10013
|
+
unverifiableResponse: (path2) => `GET ${path2} failed before a verifiable response`,
|
|
10014
|
+
expectedProfilesArray: (path2) => `GET ${path2} must return an object with a profiles array`,
|
|
10015
|
+
activeProfileRewrite: (profile) => `active profile "${profile}" can rewrite canonical model routing`,
|
|
10016
|
+
invalidActiveProfileValue: (path2) => `GET ${path2} reported an invalid active profile value`,
|
|
10017
|
+
expectedRunningArray: (path2) => `GET ${path2} must return an object with a running array`,
|
|
10018
|
+
malformedRunningEvidence: (path2) => `GET ${path2} returned malformed model/state evidence`,
|
|
10019
|
+
duplicateRunningEvidence: (path2, modelID) => `GET ${path2} returned duplicate evidence for "${modelID}"`
|
|
9975
10020
|
};
|
|
9976
10021
|
|
|
9977
|
-
// src/local/
|
|
10022
|
+
// src/local/residency.ts
|
|
9978
10023
|
class ModelResidencyError extends Error {
|
|
9979
10024
|
code;
|
|
9980
10025
|
statusCode = 400;
|
|
9981
10026
|
isRetryable = false;
|
|
9982
|
-
constructor(code) {
|
|
9983
|
-
super(modelResidencyMessages.blocked(code));
|
|
10027
|
+
constructor(code, runtimeLabel = "Lemonade", detail) {
|
|
10028
|
+
super(modelResidencyMessages.blocked(code, runtimeLabel, detail));
|
|
9984
10029
|
this.code = code;
|
|
9985
10030
|
this.name = "ModelResidencyError";
|
|
9986
10031
|
}
|
|
9987
10032
|
}
|
|
10033
|
+
async function withResidencyDeadline(operation, options) {
|
|
10034
|
+
const timeoutMs = options.timeoutMs ?? 5000;
|
|
10035
|
+
const runtimeLabel = options.runtimeLabel;
|
|
10036
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
10037
|
+
throw new ModelResidencyError("configuration", runtimeLabel);
|
|
10038
|
+
}
|
|
10039
|
+
const controller = new AbortController;
|
|
10040
|
+
const cancel = () => controller.abort(new ModelResidencyError("cancelled", runtimeLabel));
|
|
10041
|
+
if (options.signal?.aborted)
|
|
10042
|
+
throw new ModelResidencyError("cancelled", runtimeLabel);
|
|
10043
|
+
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
10044
|
+
const schedule = options.scheduleTimeout ?? ((callback, ms) => {
|
|
10045
|
+
const timer = setTimeout(callback, ms);
|
|
10046
|
+
return () => clearTimeout(timer);
|
|
10047
|
+
});
|
|
10048
|
+
let onAbort = () => {};
|
|
10049
|
+
const aborted = new Promise((_, reject) => {
|
|
10050
|
+
onAbort = () => reject(controller.signal.reason);
|
|
10051
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
10052
|
+
});
|
|
10053
|
+
const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout", runtimeLabel)), timeoutMs);
|
|
10054
|
+
try {
|
|
10055
|
+
controller.signal.throwIfAborted();
|
|
10056
|
+
return await Promise.race([operation(controller.signal), aborted]);
|
|
10057
|
+
} finally {
|
|
10058
|
+
clearTimer();
|
|
10059
|
+
options.signal?.removeEventListener("abort", cancel);
|
|
10060
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
10061
|
+
controller.abort();
|
|
10062
|
+
}
|
|
10063
|
+
}
|
|
10064
|
+
|
|
10065
|
+
// src/local/llamaSwapResidency.ts
|
|
10066
|
+
function isObject2(value) {
|
|
10067
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10068
|
+
}
|
|
10069
|
+
function readPositiveSafeInteger(value) {
|
|
10070
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
10071
|
+
}
|
|
10072
|
+
function readSupportsTools2(model) {
|
|
10073
|
+
const direct = readBoolean2(model, [
|
|
10074
|
+
"tool_call",
|
|
10075
|
+
"tool_calls",
|
|
10076
|
+
"toolCalling",
|
|
10077
|
+
"supportsToolCalling",
|
|
10078
|
+
"supports_tools",
|
|
10079
|
+
"supportsTools"
|
|
10080
|
+
]);
|
|
10081
|
+
if (direct !== undefined) {
|
|
10082
|
+
return direct;
|
|
10083
|
+
}
|
|
10084
|
+
const capabilities = readCapabilityBoolean2(model.capabilities);
|
|
10085
|
+
if (capabilities !== undefined) {
|
|
10086
|
+
return capabilities;
|
|
10087
|
+
}
|
|
10088
|
+
const features = readCapabilityBoolean2(model.features);
|
|
10089
|
+
if (features !== undefined) {
|
|
10090
|
+
return features;
|
|
10091
|
+
}
|
|
10092
|
+
const labels = readCapabilityBoolean2(model.labels);
|
|
10093
|
+
if (labels !== undefined) {
|
|
10094
|
+
return labels;
|
|
10095
|
+
}
|
|
10096
|
+
return "unknown";
|
|
10097
|
+
}
|
|
10098
|
+
function readCapabilityBoolean2(value) {
|
|
10099
|
+
if (Array.isArray(value)) {
|
|
10100
|
+
if (value.some((entry) => typeof entry === "string" && ["tools", "tool_call", "tool-calling", "function_calling"].includes(entry))) {
|
|
10101
|
+
return true;
|
|
10102
|
+
}
|
|
10103
|
+
return;
|
|
10104
|
+
}
|
|
10105
|
+
if (!isObject2(value)) {
|
|
10106
|
+
return;
|
|
10107
|
+
}
|
|
10108
|
+
return readBoolean2(value, [
|
|
10109
|
+
"tools",
|
|
10110
|
+
"tool_call",
|
|
10111
|
+
"tool_calls",
|
|
10112
|
+
"toolCalling",
|
|
10113
|
+
"function_calling",
|
|
10114
|
+
"supportsToolCalling"
|
|
10115
|
+
]);
|
|
10116
|
+
}
|
|
10117
|
+
function readBoolean2(value, keys) {
|
|
10118
|
+
for (const key of keys) {
|
|
10119
|
+
if (typeof value[key] === "boolean") {
|
|
10120
|
+
return value[key];
|
|
10121
|
+
}
|
|
10122
|
+
}
|
|
10123
|
+
return;
|
|
10124
|
+
}
|
|
10125
|
+
function modelDetail(id, detail) {
|
|
10126
|
+
return llamaSwapResidencyMessages.catalogModelDetail(id, detail);
|
|
10127
|
+
}
|
|
10128
|
+
function llamaSwapBaseURL(value) {
|
|
10129
|
+
let url;
|
|
10130
|
+
try {
|
|
10131
|
+
url = new URL(value);
|
|
10132
|
+
} catch {
|
|
10133
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
10134
|
+
}
|
|
10135
|
+
if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
|
|
10136
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
10137
|
+
}
|
|
10138
|
+
if (url.pathname === "/" || url.pathname === "") {
|
|
10139
|
+
return `${url.origin}/v1`;
|
|
10140
|
+
}
|
|
10141
|
+
if (url.pathname === "/v1" || url.pathname === "/v1/") {
|
|
10142
|
+
return `${url.origin}/v1`;
|
|
10143
|
+
}
|
|
10144
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
10145
|
+
}
|
|
10146
|
+
function parseCanonicalType(entry, id) {
|
|
10147
|
+
if (!isObject2(entry.meta) || !isObject2(entry.meta.llamaswap)) {
|
|
10148
|
+
return;
|
|
10149
|
+
}
|
|
10150
|
+
const type = entry.meta.llamaswap.type;
|
|
10151
|
+
if (type === undefined) {
|
|
10152
|
+
return;
|
|
10153
|
+
}
|
|
10154
|
+
if (typeof type !== "string" || type.length === 0 || type !== type.trim()) {
|
|
10155
|
+
throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.type")));
|
|
10156
|
+
}
|
|
10157
|
+
return type === "model" ? "model" : undefined;
|
|
10158
|
+
}
|
|
10159
|
+
function readContextCandidates(entry) {
|
|
10160
|
+
const values = [
|
|
10161
|
+
readPositiveSafeInteger(entry.context_length),
|
|
10162
|
+
readPositiveSafeInteger(entry.context_window),
|
|
10163
|
+
isObject2(entry.meta) ? readPositiveSafeInteger(entry.meta.n_ctx) : undefined
|
|
10164
|
+
];
|
|
10165
|
+
return values.filter((value) => value !== undefined);
|
|
10166
|
+
}
|
|
10167
|
+
function readDeclaredMaxOutput(entry, id) {
|
|
10168
|
+
if (!isObject2(entry.meta) || !isObject2(entry.meta.llamaswap)) {
|
|
10169
|
+
return;
|
|
10170
|
+
}
|
|
10171
|
+
if (!Object.hasOwn(entry.meta.llamaswap, "max_output_tokens")) {
|
|
10172
|
+
return;
|
|
10173
|
+
}
|
|
10174
|
+
const maxOutputTokens = readPositiveSafeInteger(entry.meta.llamaswap.max_output_tokens);
|
|
10175
|
+
if (maxOutputTokens === undefined) {
|
|
10176
|
+
throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.max_output_tokens")));
|
|
10177
|
+
}
|
|
10178
|
+
return maxOutputTokens;
|
|
10179
|
+
}
|
|
10180
|
+
function parseCatalogModel(value) {
|
|
10181
|
+
if (!isObject2(value) || typeof value.id !== "string") {
|
|
10182
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogEntryMustExposeStringId);
|
|
10183
|
+
}
|
|
10184
|
+
const id = value.id;
|
|
10185
|
+
if (id.length === 0 || id !== id.trim()) {
|
|
10186
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogModelIdsMustBeNonEmptyTrimmedStrings);
|
|
10187
|
+
}
|
|
10188
|
+
if (parseCanonicalType(value, id) !== "model") {
|
|
10189
|
+
return;
|
|
10190
|
+
}
|
|
10191
|
+
const model = {
|
|
10192
|
+
modelID: id,
|
|
10193
|
+
supportsTools: readSupportsTools2(value)
|
|
10194
|
+
};
|
|
10195
|
+
const contexts = readContextCandidates(value);
|
|
10196
|
+
if (contexts.length > 0) {
|
|
10197
|
+
model.contextWindow = Math.min(...contexts);
|
|
10198
|
+
model.contextWindowProvenance = "declared";
|
|
10199
|
+
}
|
|
10200
|
+
const maxOutputTokens = readDeclaredMaxOutput(value, id);
|
|
10201
|
+
if (maxOutputTokens !== undefined) {
|
|
10202
|
+
model.maxOutputTokens = maxOutputTokens;
|
|
10203
|
+
model.maxOutputTokensProvenance = "declared";
|
|
10204
|
+
}
|
|
10205
|
+
return model;
|
|
10206
|
+
}
|
|
10207
|
+
function parseCatalog(payload) {
|
|
10208
|
+
if (!isObject2(payload) || !Array.isArray(payload.data)) {
|
|
10209
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedDataArray("/v1/models"));
|
|
10210
|
+
}
|
|
10211
|
+
const seen = new Set;
|
|
10212
|
+
const models = [];
|
|
10213
|
+
for (const entry of payload.data) {
|
|
10214
|
+
const model = parseCatalogModel(entry);
|
|
10215
|
+
if (model === undefined) {
|
|
10216
|
+
continue;
|
|
10217
|
+
}
|
|
10218
|
+
if (seen.has(model.modelID)) {
|
|
10219
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateCanonicalModelId(model.modelID, "/v1/models"));
|
|
10220
|
+
}
|
|
10221
|
+
seen.add(model.modelID);
|
|
10222
|
+
models.push(model);
|
|
10223
|
+
}
|
|
10224
|
+
return models;
|
|
10225
|
+
}
|
|
10226
|
+
async function metadata(url, fetch, signal, headers) {
|
|
10227
|
+
signal.throwIfAborted();
|
|
10228
|
+
try {
|
|
10229
|
+
const response = await fetch(url, {
|
|
10230
|
+
method: "GET",
|
|
10231
|
+
headers,
|
|
10232
|
+
signal,
|
|
10233
|
+
redirect: "error",
|
|
10234
|
+
cache: "no-store"
|
|
10235
|
+
});
|
|
10236
|
+
signal.throwIfAborted();
|
|
10237
|
+
if (response.status === 401 || response.status === 403) {
|
|
10238
|
+
throw new ModelResidencyError("authentication-unsupported", "llama-swap");
|
|
10239
|
+
}
|
|
10240
|
+
if (!response.ok || response.redirected || response.url !== "" && response.url !== url) {
|
|
10241
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.httpFailure(new URL(url).pathname, response.status));
|
|
10242
|
+
}
|
|
10243
|
+
return await response.json();
|
|
10244
|
+
} catch (error) {
|
|
10245
|
+
signal.throwIfAborted();
|
|
10246
|
+
if (error instanceof ModelResidencyError) {
|
|
10247
|
+
throw error;
|
|
10248
|
+
}
|
|
10249
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.unverifiableResponse(new URL(url).pathname));
|
|
10250
|
+
}
|
|
10251
|
+
}
|
|
10252
|
+
function readProfiles(payload) {
|
|
10253
|
+
if (!isObject2(payload) || !Array.isArray(payload.profiles)) {
|
|
10254
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedProfilesArray("/api/profiles"));
|
|
10255
|
+
}
|
|
10256
|
+
if (payload.active === null) {
|
|
10257
|
+
return;
|
|
10258
|
+
}
|
|
10259
|
+
if (typeof payload.active === "string" && payload.active.length > 0 && payload.active === payload.active.trim()) {
|
|
10260
|
+
throw new ModelResidencyError("identity", "llama-swap", llamaSwapResidencyMessages.activeProfileRewrite(payload.active));
|
|
10261
|
+
}
|
|
10262
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.invalidActiveProfileValue("/api/profiles"));
|
|
10263
|
+
}
|
|
10264
|
+
function readRunning(payload, modelIDs) {
|
|
10265
|
+
if (!isObject2(payload) || !Array.isArray(payload.running)) {
|
|
10266
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedRunningArray("/running"));
|
|
10267
|
+
}
|
|
10268
|
+
const result = new Map;
|
|
10269
|
+
for (const modelID of modelIDs) {
|
|
10270
|
+
result.set(modelID, { loaded: false });
|
|
10271
|
+
}
|
|
10272
|
+
const seen = new Set;
|
|
10273
|
+
for (const entry of payload.running) {
|
|
10274
|
+
if (!isObject2(entry) || typeof entry.model !== "string" || entry.model.length === 0 || entry.model !== entry.model.trim() || typeof entry.state !== "string" || entry.state.length === 0 || entry.state !== entry.state.trim()) {
|
|
10275
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.malformedRunningEvidence("/running"));
|
|
10276
|
+
}
|
|
10277
|
+
if (seen.has(entry.model)) {
|
|
10278
|
+
throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateRunningEvidence("/running", entry.model));
|
|
10279
|
+
}
|
|
10280
|
+
seen.add(entry.model);
|
|
10281
|
+
if (!modelIDs.has(entry.model)) {
|
|
10282
|
+
continue;
|
|
10283
|
+
}
|
|
10284
|
+
result.set(entry.model, { loaded: entry.state === "ready" });
|
|
10285
|
+
}
|
|
10286
|
+
return result;
|
|
10287
|
+
}
|
|
10288
|
+
function requestHeaders(headers) {
|
|
10289
|
+
const result = new Headers(headers);
|
|
10290
|
+
result.delete("content-type");
|
|
10291
|
+
result.delete("content-length");
|
|
10292
|
+
result.set("accept", "application/json");
|
|
10293
|
+
result.set("cache-control", "no-cache, no-store");
|
|
10294
|
+
result.delete("if-none-match");
|
|
10295
|
+
result.delete("if-modified-since");
|
|
10296
|
+
return result;
|
|
10297
|
+
}
|
|
10298
|
+
function llamaSwapResidencyOptions(options) {
|
|
10299
|
+
return { ...options, runtimeLabel: "llama-swap" };
|
|
10300
|
+
}
|
|
10301
|
+
async function readLlamaSwapCatalog(baseURL, fetch, options = {}) {
|
|
10302
|
+
const base = llamaSwapBaseURL(baseURL);
|
|
10303
|
+
const headers = requestHeaders(options.headers);
|
|
10304
|
+
return withResidencyDeadline(async (signal) => parseCatalog(await metadata(`${base}/models`, fetch, signal, headers)), llamaSwapResidencyOptions(options));
|
|
10305
|
+
}
|
|
10306
|
+
async function readLlamaSwapResidency(baseURL, fetch, options = {}) {
|
|
10307
|
+
const base = llamaSwapBaseURL(baseURL);
|
|
10308
|
+
const origin = new URL(base).origin;
|
|
10309
|
+
const headers = requestHeaders(options.headers);
|
|
10310
|
+
return withResidencyDeadline(async (signal) => {
|
|
10311
|
+
const models = await readLlamaSwapCatalog(base, fetch, {
|
|
10312
|
+
...llamaSwapResidencyOptions(options),
|
|
10313
|
+
signal
|
|
10314
|
+
});
|
|
10315
|
+
readProfiles(await metadata(`${origin}/api/profiles`, fetch, signal, headers));
|
|
10316
|
+
return readRunning(await metadata(`${origin}/running`, fetch, signal, headers), new Set(models.map((model) => model.modelID)));
|
|
10317
|
+
}, llamaSwapResidencyOptions(options));
|
|
10318
|
+
}
|
|
10319
|
+
|
|
10320
|
+
// src/local/lemonadeResidency.ts
|
|
10321
|
+
import { z as z19 } from "zod";
|
|
9988
10322
|
var modelName = z19.string().min(1).refine((name) => name === name.trim());
|
|
9989
10323
|
var LoadedModelSchema = z19.object({
|
|
9990
10324
|
model_name: modelName,
|
|
@@ -10121,37 +10455,7 @@ function lemonadeBaseURL(value) {
|
|
|
10121
10455
|
}
|
|
10122
10456
|
return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
|
|
10123
10457
|
}
|
|
10124
|
-
async function
|
|
10125
|
-
const timeoutMs = options.timeoutMs ?? 5000;
|
|
10126
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
10127
|
-
throw new ModelResidencyError("configuration");
|
|
10128
|
-
}
|
|
10129
|
-
const controller = new AbortController;
|
|
10130
|
-
const cancel = () => controller.abort(new ModelResidencyError("cancelled"));
|
|
10131
|
-
if (options.signal?.aborted)
|
|
10132
|
-
throw new ModelResidencyError("cancelled");
|
|
10133
|
-
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
10134
|
-
const schedule = options.scheduleTimeout ?? ((callback, ms) => {
|
|
10135
|
-
const timer = setTimeout(callback, ms);
|
|
10136
|
-
return () => clearTimeout(timer);
|
|
10137
|
-
});
|
|
10138
|
-
let onAbort = () => {};
|
|
10139
|
-
const aborted = new Promise((_, reject) => {
|
|
10140
|
-
onAbort = () => reject(controller.signal.reason);
|
|
10141
|
-
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
10142
|
-
});
|
|
10143
|
-
const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout")), timeoutMs);
|
|
10144
|
-
try {
|
|
10145
|
-
controller.signal.throwIfAborted();
|
|
10146
|
-
return await Promise.race([operation(controller.signal), aborted]);
|
|
10147
|
-
} finally {
|
|
10148
|
-
clearTimer();
|
|
10149
|
-
options.signal?.removeEventListener("abort", cancel);
|
|
10150
|
-
controller.signal.removeEventListener("abort", onAbort);
|
|
10151
|
-
controller.abort();
|
|
10152
|
-
}
|
|
10153
|
-
}
|
|
10154
|
-
async function metadata(url, fetch, signal, headers) {
|
|
10458
|
+
async function metadata2(url, fetch, signal, headers) {
|
|
10155
10459
|
signal.throwIfAborted();
|
|
10156
10460
|
let payload;
|
|
10157
10461
|
try {
|
|
@@ -10184,8 +10488,8 @@ async function readLemonadeResidency(baseURL, fetch, options = {}) {
|
|
|
10184
10488
|
headers.delete("if-none-match");
|
|
10185
10489
|
headers.delete("if-modified-since");
|
|
10186
10490
|
return withResidencyDeadline(async (signal) => {
|
|
10187
|
-
const aliases = await
|
|
10188
|
-
const health = await
|
|
10491
|
+
const aliases = await metadata2(`${new URL(base).origin}/internal/aliases`, fetch, signal, headers);
|
|
10492
|
+
const health = await metadata2(`${base}/health`, fetch, signal, headers);
|
|
10189
10493
|
return parseResidency(health, aliases);
|
|
10190
10494
|
}, options);
|
|
10191
10495
|
}
|
|
@@ -10256,11 +10560,11 @@ async function listLemonadeModels(baseURL, fetch) {
|
|
|
10256
10560
|
}
|
|
10257
10561
|
function readNonTextModelIDs(payload) {
|
|
10258
10562
|
const ids = new Set;
|
|
10259
|
-
if (!
|
|
10563
|
+
if (!isObject3(payload) || !Array.isArray(payload.data)) {
|
|
10260
10564
|
return ids;
|
|
10261
10565
|
}
|
|
10262
10566
|
for (const entry of payload.data) {
|
|
10263
|
-
if (!
|
|
10567
|
+
if (!isObject3(entry) || typeof entry.id !== "string") {
|
|
10264
10568
|
continue;
|
|
10265
10569
|
}
|
|
10266
10570
|
if (isNonTextModel(entry)) {
|
|
@@ -10298,7 +10602,7 @@ function readLoadedContextWindow(entry) {
|
|
|
10298
10602
|
}
|
|
10299
10603
|
function slotsFromLlamacppArgs(entry) {
|
|
10300
10604
|
const options = entry.recipe_options;
|
|
10301
|
-
if (!
|
|
10605
|
+
if (!isObject3(options) || typeof options.llamacpp_args !== "string") {
|
|
10302
10606
|
return;
|
|
10303
10607
|
}
|
|
10304
10608
|
const match = PARALLEL_ARG.exec(options.llamacpp_args);
|
|
@@ -10334,7 +10638,7 @@ function slotsFromLaunchCommand(entry) {
|
|
|
10334
10638
|
}
|
|
10335
10639
|
function contextWindowFromRecipeOptions(entry) {
|
|
10336
10640
|
const options = entry.recipe_options;
|
|
10337
|
-
if (!
|
|
10641
|
+
if (!isObject3(options) || !hasLlamacppEvidence(entry)) {
|
|
10338
10642
|
return;
|
|
10339
10643
|
}
|
|
10340
10644
|
const direct = positiveIntegerValue(options.ctx_size);
|
|
@@ -10394,7 +10698,7 @@ function hasLlamacppEvidence(entry) {
|
|
|
10394
10698
|
return true;
|
|
10395
10699
|
}
|
|
10396
10700
|
const options = entry.recipe_options;
|
|
10397
|
-
if (
|
|
10701
|
+
if (isObject3(options) && typeof options.llamacpp_args === "string") {
|
|
10398
10702
|
return true;
|
|
10399
10703
|
}
|
|
10400
10704
|
return Array.isArray(entry.launch_command) && launchCommandLooksLikeLlamacpp(entry.launch_command);
|
|
@@ -10450,10 +10754,57 @@ function enrichWithLoadState(models, loaded) {
|
|
|
10450
10754
|
};
|
|
10451
10755
|
});
|
|
10452
10756
|
}
|
|
10453
|
-
function
|
|
10757
|
+
function isObject3(value) {
|
|
10454
10758
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10455
10759
|
}
|
|
10456
10760
|
|
|
10761
|
+
// src/local/llamaSwap.ts
|
|
10762
|
+
var LLAMA_SWAP_DEFAULT_BASE_URL = "http://localhost:8080/v1";
|
|
10763
|
+
function withLoadedState(models, residency) {
|
|
10764
|
+
return models.map((model) => {
|
|
10765
|
+
const loaded = residency.get(model.modelID)?.loaded;
|
|
10766
|
+
return loaded === undefined ? model : { ...model, loaded };
|
|
10767
|
+
});
|
|
10768
|
+
}
|
|
10769
|
+
function createLlamaSwapAdapter() {
|
|
10770
|
+
const listModels = async (options) => {
|
|
10771
|
+
const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
|
|
10772
|
+
const models = await readLlamaSwapCatalog(baseURL, options.fetch);
|
|
10773
|
+
try {
|
|
10774
|
+
return withLoadedState(models, await readLlamaSwapResidency(baseURL, options.fetch));
|
|
10775
|
+
} catch (error) {
|
|
10776
|
+
if (error instanceof ModelResidencyError && (error.code === "identity" || error.code === "unknown")) {
|
|
10777
|
+
return models;
|
|
10778
|
+
}
|
|
10779
|
+
throw error;
|
|
10780
|
+
}
|
|
10781
|
+
};
|
|
10782
|
+
return {
|
|
10783
|
+
id: "llama-swap",
|
|
10784
|
+
defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
10785
|
+
listModels,
|
|
10786
|
+
async probe(options) {
|
|
10787
|
+
const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
|
|
10788
|
+
try {
|
|
10789
|
+
return {
|
|
10790
|
+
id: "llama-swap",
|
|
10791
|
+
baseURL,
|
|
10792
|
+
reachable: true,
|
|
10793
|
+
models: await listModels({ ...options, baseURL }),
|
|
10794
|
+
probedAt: options.probedAt
|
|
10795
|
+
};
|
|
10796
|
+
} catch (error) {
|
|
10797
|
+
return unavailableSnapshot({
|
|
10798
|
+
id: "llama-swap",
|
|
10799
|
+
baseURL,
|
|
10800
|
+
probedAt: options.probedAt,
|
|
10801
|
+
error
|
|
10802
|
+
});
|
|
10803
|
+
}
|
|
10804
|
+
}
|
|
10805
|
+
};
|
|
10806
|
+
}
|
|
10807
|
+
|
|
10457
10808
|
// src/local/lmstudio.ts
|
|
10458
10809
|
var LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
|
|
10459
10810
|
function createLMStudioAdapter() {
|
|
@@ -10506,6 +10857,7 @@ class RuntimeRegistry {
|
|
|
10506
10857
|
lmstudio: createLMStudioAdapter(),
|
|
10507
10858
|
"foundry-local": createFoundryLocalAdapter(),
|
|
10508
10859
|
lemonade: createLemonadeAdapter(),
|
|
10860
|
+
"llama-swap": createLlamaSwapAdapter(),
|
|
10509
10861
|
...options.adapters
|
|
10510
10862
|
};
|
|
10511
10863
|
this.clock = options.clock;
|
|
@@ -10972,7 +11324,7 @@ var TRUNCATED_REASON_SUFFIX = "…[truncated]";
|
|
|
10972
11324
|
var MAX_SERIALIZED_ARRAY_ITEMS = 20;
|
|
10973
11325
|
var MAX_SERIALIZED_OBJECT_PROPERTIES = 25;
|
|
10974
11326
|
var MAX_SERIALIZED_DEPTH = 4;
|
|
10975
|
-
function
|
|
11327
|
+
function isObject4(value) {
|
|
10976
11328
|
return typeof value === "object" && value !== null;
|
|
10977
11329
|
}
|
|
10978
11330
|
function isError(value) {
|
|
@@ -11002,7 +11354,7 @@ function stringProperty(value, key) {
|
|
|
11002
11354
|
}
|
|
11003
11355
|
function nestedMessage(value, key) {
|
|
11004
11356
|
const container = safeGet(value, key);
|
|
11005
|
-
if (!
|
|
11357
|
+
if (!isObject4(container)) {
|
|
11006
11358
|
return;
|
|
11007
11359
|
}
|
|
11008
11360
|
const message = stringProperty(container, "message");
|
|
@@ -11086,7 +11438,7 @@ function errorReason(error) {
|
|
|
11086
11438
|
return message;
|
|
11087
11439
|
}
|
|
11088
11440
|
}
|
|
11089
|
-
if (
|
|
11441
|
+
if (isObject4(error)) {
|
|
11090
11442
|
const message = stringProperty(error, "message");
|
|
11091
11443
|
if (message !== undefined) {
|
|
11092
11444
|
return message;
|
|
@@ -11104,7 +11456,7 @@ function errorReason(error) {
|
|
|
11104
11456
|
return safeToString(error);
|
|
11105
11457
|
}
|
|
11106
11458
|
function isNormalizedSessionFailure(value) {
|
|
11107
|
-
return
|
|
11459
|
+
return isObject4(value) && safeGet(value, "kind") === "normalized-session-failure" && typeof safeGet(value, "failureClass") === "string" && typeof safeGet(value, "retryable") === "boolean";
|
|
11108
11460
|
}
|
|
11109
11461
|
function createArgs(req) {
|
|
11110
11462
|
const body = {};
|
|
@@ -11231,7 +11583,7 @@ async function confirmSessionStopped(client, sessionID, directory, deps, deadlin
|
|
|
11231
11583
|
return { confirmed: false, cause: "status-error" };
|
|
11232
11584
|
}
|
|
11233
11585
|
const statuses = statusOutcome.value;
|
|
11234
|
-
if (!
|
|
11586
|
+
if (!isObject4(statuses)) {
|
|
11235
11587
|
return { confirmed: false, cause: "status-error" };
|
|
11236
11588
|
}
|
|
11237
11589
|
if (safeGet(statuses, "error") !== undefined) {
|
|
@@ -11381,8 +11733,11 @@ async function runSubsession(client, req, deps) {
|
|
|
11381
11733
|
}
|
|
11382
11734
|
|
|
11383
11735
|
// src/local/modelResidency.ts
|
|
11736
|
+
function runtimeRequiresModelResidency(kind) {
|
|
11737
|
+
return kind === "lemonade" || kind === "llama-swap";
|
|
11738
|
+
}
|
|
11384
11739
|
function hasRequiredModelResidency(kind, model) {
|
|
11385
|
-
return kind
|
|
11740
|
+
return !runtimeRequiresModelResidency(kind) || model?.loaded === true;
|
|
11386
11741
|
}
|
|
11387
11742
|
|
|
11388
11743
|
// src/plugin/availability.ts
|
|
@@ -11391,9 +11746,9 @@ function modelKey2(model) {
|
|
|
11391
11746
|
}
|
|
11392
11747
|
function availabilityMetadata({
|
|
11393
11748
|
apiModelID: _apiModelID,
|
|
11394
|
-
...
|
|
11749
|
+
...metadata3
|
|
11395
11750
|
}) {
|
|
11396
|
-
return
|
|
11751
|
+
return metadata3;
|
|
11397
11752
|
}
|
|
11398
11753
|
function compareAvailableModels(left, right) {
|
|
11399
11754
|
const kindOrder = Number(left.kind === "frontier") - Number(right.kind === "frontier");
|
|
@@ -11413,7 +11768,7 @@ function isConfiguredLocalModel(config, model) {
|
|
|
11413
11768
|
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID);
|
|
11414
11769
|
}
|
|
11415
11770
|
function requiresModelResidency(config, model) {
|
|
11416
|
-
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID &&
|
|
11771
|
+
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID && runtimeRequiresModelResidency(localRuntimeKind(runtime)));
|
|
11417
11772
|
}
|
|
11418
11773
|
function configuredFrontierProviderIDs(config) {
|
|
11419
11774
|
const providerIDs = new Set([config.baseline.hardDefault.providerID]);
|
|
@@ -12039,6 +12394,7 @@ var PROVIDER_LABELS = {
|
|
|
12039
12394
|
ollama: "Ollama",
|
|
12040
12395
|
lmstudio: "LM Studio",
|
|
12041
12396
|
lemonade: "Lemonade Server",
|
|
12397
|
+
"llama-swap": executionSetupMessages.llamaSwap.label,
|
|
12042
12398
|
"foundry-local": "Foundry Local"
|
|
12043
12399
|
};
|
|
12044
12400
|
function providerLabel(providerID) {
|
|
@@ -12260,7 +12616,8 @@ var KNOWN_LOCAL_PROVIDER_IDS = new Set([
|
|
|
12260
12616
|
"ollama",
|
|
12261
12617
|
"lmstudio",
|
|
12262
12618
|
"foundry-local",
|
|
12263
|
-
"lemonade"
|
|
12619
|
+
"lemonade",
|
|
12620
|
+
"llama-swap"
|
|
12264
12621
|
]);
|
|
12265
12622
|
var KNOWN_FRONTIER_PROVIDER_IDS = new Set([
|
|
12266
12623
|
"github-copilot",
|
|
@@ -12606,7 +12963,7 @@ function runtimeLine(snapshot, runtime) {
|
|
|
12606
12963
|
const mark = snapshot.reachable ? "✓" : "✗";
|
|
12607
12964
|
const detail = snapshot.reachable ? `${snapshot.models.length} model(s)` : snapshot.error ?? "unreachable";
|
|
12608
12965
|
const declared = runtime?.maxConcurrency;
|
|
12609
|
-
const slots = declared === undefined ?
|
|
12966
|
+
const slots = declared === undefined ? doctorMessages.defaultRuntimeSlots(effectiveMaxConcurrency(runtime ?? {})) : ` · ${declared} slot(s) (declared)`;
|
|
12610
12967
|
return ` ${mark} ${snapshot.id.padEnd(14)} ${snapshot.baseURL || "(no baseURL)"} — ${detail}${slots}`;
|
|
12611
12968
|
}
|
|
12612
12969
|
function runtimeModelSlotsLine(runtime, snapshot) {
|
|
@@ -12618,6 +12975,8 @@ function runtimeModelSlotsLine(runtime, snapshot) {
|
|
|
12618
12975
|
if (runtime === undefined || snapshot === undefined || !snapshot.reachable || snapshot.models.length === 0) {
|
|
12619
12976
|
return [];
|
|
12620
12977
|
}
|
|
12978
|
+
if (localRuntimeKind(runtime) === "llama-swap")
|
|
12979
|
+
return [doctorMessages.llamaSwapConcurrency];
|
|
12621
12980
|
const reported = snapshot.models.flatMap((model) => model.slots === undefined ? [] : [`${model.modelID} ${model.slots} slot(s)`]);
|
|
12622
12981
|
if (reported.length > 0) {
|
|
12623
12982
|
return [
|
|
@@ -13021,7 +13380,7 @@ function renderDoctor(input) {
|
|
|
13021
13380
|
continue;
|
|
13022
13381
|
}
|
|
13023
13382
|
const runtime = enabledRuntimes.find((r) => r.id === snapshot.id);
|
|
13024
|
-
const cap = runtime
|
|
13383
|
+
const cap = effectiveMaxConcurrency(runtime ?? {});
|
|
13025
13384
|
lines.push(` ⚠ ${snapshot.id} is reached over the network: its ${cap}-slot cap is enforced per openteam process, so`, " two processes at once (an opencode session plus 'openteam console') can together exceed it.");
|
|
13026
13385
|
}
|
|
13027
13386
|
lines.push(` telemetry: ${input.telemetryPath} — ${input.telemetryRecords} record(s)`);
|
|
@@ -13633,6 +13992,38 @@ function missingGeneratedStateRules(gitignore) {
|
|
|
13633
13992
|
const present = new Set(activeGitignoreRules(gitignore));
|
|
13634
13993
|
return OPENCODE_GITIGNORE_GENERATED_STATE_RULES.filter((rule) => !present.has(rule));
|
|
13635
13994
|
}
|
|
13995
|
+
var KNOWN_RUNTIMES = [
|
|
13996
|
+
{
|
|
13997
|
+
id: "ollama",
|
|
13998
|
+
label: "Ollama",
|
|
13999
|
+
defaultBaseURL: "http://localhost:11434/v1",
|
|
14000
|
+
fallbackModelID: "qwen3:8b"
|
|
14001
|
+
},
|
|
14002
|
+
{
|
|
14003
|
+
id: "lmstudio",
|
|
14004
|
+
label: "LM Studio",
|
|
14005
|
+
defaultBaseURL: "http://localhost:1234/v1",
|
|
14006
|
+
fallbackModelID: "qwen2.5-coder"
|
|
14007
|
+
},
|
|
14008
|
+
{
|
|
14009
|
+
id: "lemonade",
|
|
14010
|
+
label: "Lemonade Server",
|
|
14011
|
+
defaultBaseURL: LEMONADE_DEFAULT_BASE_URL,
|
|
14012
|
+
fallbackModelID: "Qwen3-Coder-30B-A3B-Instruct-GGUF"
|
|
14013
|
+
},
|
|
14014
|
+
{
|
|
14015
|
+
id: "llama-swap",
|
|
14016
|
+
label: executionSetupMessages.llamaSwap.label,
|
|
14017
|
+
defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
|
|
14018
|
+
fallbackModelID: ""
|
|
14019
|
+
},
|
|
14020
|
+
{
|
|
14021
|
+
id: "foundry-local",
|
|
14022
|
+
label: "Foundry Local",
|
|
14023
|
+
fallbackModelID: "Phi-4-mini-instruct",
|
|
14024
|
+
dynamicPort: true
|
|
14025
|
+
}
|
|
14026
|
+
];
|
|
13636
14027
|
|
|
13637
14028
|
// src/commands/purge.ts
|
|
13638
14029
|
var PURGE_CATEGORY_IDS = [
|
|
@@ -14661,7 +15052,7 @@ function liveModelsFromSnapshots(snapshots, config) {
|
|
|
14661
15052
|
map.set(runtime.defaultModel.providerID, {
|
|
14662
15053
|
reachable: snapshot.reachable,
|
|
14663
15054
|
modelIDs: new Set(snapshot.models.filter((model) => hasRequiredModelResidency(kind, model)).map((model) => model.modelID)),
|
|
14664
|
-
...kind
|
|
15055
|
+
...runtimeRequiresModelResidency(kind) ? {
|
|
14665
15056
|
residency: new Map(snapshot.models.map((model) => [model.modelID, model.loaded]))
|
|
14666
15057
|
} : {}
|
|
14667
15058
|
});
|
|
@@ -14991,9 +15382,9 @@ async function runCli(argv, deps) {
|
|
|
14991
15382
|
const inventoryNotes = [];
|
|
14992
15383
|
const readySnapshots = snapshots.map((snapshot) => {
|
|
14993
15384
|
const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
|
|
14994
|
-
if (runtime === undefined || localRuntimeKind(runtime)
|
|
15385
|
+
if (runtime === undefined || !runtimeRequiresModelResidency(localRuntimeKind(runtime)))
|
|
14995
15386
|
return snapshot;
|
|
14996
|
-
inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models));
|
|
15387
|
+
inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models, localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade"));
|
|
14997
15388
|
return {
|
|
14998
15389
|
...snapshot,
|
|
14999
15390
|
models: snapshot.models.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model))
|
|
@@ -15004,7 +15395,7 @@ async function runCli(argv, deps) {
|
|
|
15004
15395
|
if (agent.model === undefined || live !== "not-loaded" && live !== "residency-unknown")
|
|
15005
15396
|
return [];
|
|
15006
15397
|
return [
|
|
15007
|
-
executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live)
|
|
15398
|
+
executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live, config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === agent.model?.providerID && localRuntimeKind(runtime) === "llama-swap") ? "llama-swap" : "Lemonade")
|
|
15008
15399
|
];
|
|
15009
15400
|
});
|
|
15010
15401
|
return {
|
|
@@ -17234,6 +17625,84 @@ function createLemonadeGuardedFetch(options) {
|
|
|
17234
17625
|
});
|
|
17235
17626
|
}
|
|
17236
17627
|
|
|
17628
|
+
// src/local/llamaSwapRequestGuard.ts
|
|
17629
|
+
var inferencePaths2 = new Set([
|
|
17630
|
+
"/chat/completions",
|
|
17631
|
+
"/completions",
|
|
17632
|
+
"/embeddings",
|
|
17633
|
+
"/responses"
|
|
17634
|
+
]);
|
|
17635
|
+
async function validatedModelID(payload, modelIDs) {
|
|
17636
|
+
if (typeof payload !== "object" || payload === null || !("model" in payload) || typeof payload.model !== "string" || payload.model.length === 0 || payload.model !== payload.model.trim()) {
|
|
17637
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
17638
|
+
}
|
|
17639
|
+
if (modelIDs === undefined) {
|
|
17640
|
+
return payload.model;
|
|
17641
|
+
}
|
|
17642
|
+
const ids = typeof modelIDs === "function" ? await modelIDs() : modelIDs;
|
|
17643
|
+
if (ids.has(payload.model)) {
|
|
17644
|
+
return payload.model;
|
|
17645
|
+
}
|
|
17646
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
17647
|
+
}
|
|
17648
|
+
function createLlamaSwapGuardedFetch(options) {
|
|
17649
|
+
const base = llamaSwapBaseURL(options.baseURL);
|
|
17650
|
+
const upstream = options.fetch;
|
|
17651
|
+
const healthFetch = options.healthFetch ?? upstream;
|
|
17652
|
+
return Object.assign(async (input, init) => {
|
|
17653
|
+
const request = new Request(input, init);
|
|
17654
|
+
const url = new URL(request.url);
|
|
17655
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !request.url.startsWith(`${base}/`)) {
|
|
17656
|
+
throw new ModelResidencyError("destination", "llama-swap");
|
|
17657
|
+
}
|
|
17658
|
+
const path4 = request.url.slice(base.length);
|
|
17659
|
+
const metadataOnly = request.method === "GET" && /^\/models(?:\/[^/]+)?$/.test(path4);
|
|
17660
|
+
if (!metadataOnly && (request.method !== "POST" || !inferencePaths2.has(path4))) {
|
|
17661
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
17662
|
+
}
|
|
17663
|
+
if (!metadataOnly) {
|
|
17664
|
+
await withResidencyDeadline(async (signal) => {
|
|
17665
|
+
signal.throwIfAborted();
|
|
17666
|
+
let payload;
|
|
17667
|
+
try {
|
|
17668
|
+
payload = await request.clone().json();
|
|
17669
|
+
} catch {
|
|
17670
|
+
signal.throwIfAborted();
|
|
17671
|
+
throw new ModelResidencyError("request", "llama-swap");
|
|
17672
|
+
}
|
|
17673
|
+
signal.throwIfAborted();
|
|
17674
|
+
const model = await validatedModelID(payload, options.modelIDs);
|
|
17675
|
+
const residency = await readLlamaSwapResidency(base, healthFetch, {
|
|
17676
|
+
...options,
|
|
17677
|
+
signal,
|
|
17678
|
+
headers: request.headers
|
|
17679
|
+
});
|
|
17680
|
+
signal.throwIfAborted();
|
|
17681
|
+
if (!residency.has(model)) {
|
|
17682
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
17683
|
+
}
|
|
17684
|
+
if (residency.get(model)?.loaded !== true) {
|
|
17685
|
+
throw new ModelResidencyError("inactive", "llama-swap");
|
|
17686
|
+
}
|
|
17687
|
+
}, { ...options, signal: request.signal, runtimeLabel: "llama-swap" });
|
|
17688
|
+
}
|
|
17689
|
+
if (request.signal.aborted) {
|
|
17690
|
+
throw new ModelResidencyError("cancelled", "llama-swap");
|
|
17691
|
+
}
|
|
17692
|
+
const {
|
|
17693
|
+
body: _body,
|
|
17694
|
+
headers: _headers,
|
|
17695
|
+
method: _method,
|
|
17696
|
+
signal: _signal,
|
|
17697
|
+
redirect: _redirect,
|
|
17698
|
+
...transportOptions
|
|
17699
|
+
} = init ?? {};
|
|
17700
|
+
return upstream(request, { ...transportOptions, redirect: "error" });
|
|
17701
|
+
}, {
|
|
17702
|
+
preconnect: (...args) => upstream.preconnect?.(...args)
|
|
17703
|
+
});
|
|
17704
|
+
}
|
|
17705
|
+
|
|
17237
17706
|
// src/plugin/modelResidency.ts
|
|
17238
17707
|
function isFetch(value) {
|
|
17239
17708
|
return typeof value === "function";
|
|
@@ -17273,20 +17742,29 @@ function providerRoutes(provider) {
|
|
|
17273
17742
|
npm: model?.provider?.npm ?? provider.npm
|
|
17274
17743
|
}));
|
|
17275
17744
|
}
|
|
17276
|
-
function wireID(key, model) {
|
|
17745
|
+
function wireID(key, model, runtimeLabel = "Lemonade") {
|
|
17277
17746
|
const id = model?.id ?? key;
|
|
17278
17747
|
if (id.length === 0 || id !== id.trim())
|
|
17279
|
-
throw new ModelResidencyError("identity");
|
|
17748
|
+
throw new ModelResidencyError("identity", runtimeLabel);
|
|
17280
17749
|
return id;
|
|
17281
17750
|
}
|
|
17282
17751
|
function nativeEligibleID(id) {
|
|
17283
17752
|
return id === "openai" || id === "anthropic" || id.startsWith("opencode");
|
|
17284
17753
|
}
|
|
17754
|
+
function normalizedBase(kind, baseURL) {
|
|
17755
|
+
return kind === "llama-swap" ? llamaSwapBaseURL(baseURL) : lemonadeBaseURL(baseURL);
|
|
17756
|
+
}
|
|
17757
|
+
function runtimeBase(runtime) {
|
|
17758
|
+
return runtime.baseURL ?? (localRuntimeKind(runtime) === "llama-swap" ? LLAMA_SWAP_DEFAULT_BASE_URL : LEMONADE_DEFAULT_BASE_URL);
|
|
17759
|
+
}
|
|
17760
|
+
function residencyRuntimeLabel(config, providerID) {
|
|
17761
|
+
return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === providerID && localRuntimeKind(runtime) === "llama-swap") ? "llama-swap" : "Lemonade";
|
|
17762
|
+
}
|
|
17285
17763
|
function configuredRuntime(config, providerID) {
|
|
17286
17764
|
const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === providerID);
|
|
17287
17765
|
const runtime = runtimes[0];
|
|
17288
17766
|
if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled) {
|
|
17289
|
-
throw new ModelResidencyError("destination");
|
|
17767
|
+
throw new ModelResidencyError("destination", residencyRuntimeLabel(config, providerID));
|
|
17290
17768
|
}
|
|
17291
17769
|
return runtime;
|
|
17292
17770
|
}
|
|
@@ -17295,8 +17773,9 @@ function createModelResidencyGuard(config, options) {
|
|
|
17295
17773
|
const blocked = new Set;
|
|
17296
17774
|
let hostConfig;
|
|
17297
17775
|
const readEnv = options.readEnv ?? ((name) => process.env[name]);
|
|
17298
|
-
const
|
|
17299
|
-
const
|
|
17776
|
+
const failure = (code, providerID) => new ModelResidencyError(code, residencyRuntimeLabel(config, providerID));
|
|
17777
|
+
const residentRuntimes = () => config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
|
|
17778
|
+
const protects = (providerID, baseURL) => residentRuntimes().some((runtime) => {
|
|
17300
17779
|
if (runtime.defaultModel.providerID === providerID)
|
|
17301
17780
|
return true;
|
|
17302
17781
|
if (baseURL === undefined)
|
|
@@ -17304,13 +17783,13 @@ function createModelResidencyGuard(config, options) {
|
|
|
17304
17783
|
const binding = endpointBinding(baseURL, readEnv);
|
|
17305
17784
|
if (binding.ambiguous)
|
|
17306
17785
|
return true;
|
|
17307
|
-
return binding.origin !== undefined && binding.origin === origin(runtime
|
|
17786
|
+
return binding.origin !== undefined && binding.origin === origin(runtimeBase(runtime));
|
|
17308
17787
|
});
|
|
17309
17788
|
const resolvedWireID = (model, declaredID) => {
|
|
17310
17789
|
const id = options.resolveApiModelID === undefined ? declaredID : options.resolveApiModelID(model);
|
|
17311
17790
|
if (id === undefined)
|
|
17312
|
-
throw
|
|
17313
|
-
return wireID(id, undefined);
|
|
17791
|
+
throw failure("identity", model.providerID);
|
|
17792
|
+
return wireID(id, undefined, residencyRuntimeLabel(config, model.providerID));
|
|
17314
17793
|
};
|
|
17315
17794
|
const configHook = async (host) => {
|
|
17316
17795
|
hostConfig = host;
|
|
@@ -17330,21 +17809,22 @@ function createModelResidencyGuard(config, options) {
|
|
|
17330
17809
|
...new Set([...host.disabled_providers ?? [], providerID])
|
|
17331
17810
|
];
|
|
17332
17811
|
const reject = Object.assign(async () => {
|
|
17333
|
-
throw
|
|
17812
|
+
throw failure("transport", providerID);
|
|
17334
17813
|
}, { preconnect: () => {} });
|
|
17335
17814
|
provider.options = { ...provider.options, fetch: reject };
|
|
17336
17815
|
try {
|
|
17337
17816
|
const runtime = configuredRuntime(config, providerID);
|
|
17338
|
-
const
|
|
17339
|
-
|
|
17340
|
-
|
|
17817
|
+
const kind = localRuntimeKind(runtime);
|
|
17818
|
+
const baseURL = normalizedBase(kind, runtimeBase(runtime));
|
|
17819
|
+
if (!runtimeRequiresModelResidency(kind) || routes.some((route) => route.baseURL === undefined || normalizedBase(kind, route.baseURL) !== baseURL))
|
|
17820
|
+
throw failure("destination", providerID);
|
|
17341
17821
|
if (routes.some((route) => route.npm !== "@ai-sdk/openai-compatible") || nativeEligibleID(providerID) || !isFetch(originalFetch))
|
|
17342
|
-
throw
|
|
17822
|
+
throw failure("transport", providerID);
|
|
17343
17823
|
const modelKeys = new Map;
|
|
17344
17824
|
for (const [key, model] of Object.entries(provider.models ?? {})) {
|
|
17345
|
-
if (model.options?.fetch !== undefined || model.options?.baseURL !== undefined && (typeof model.options.baseURL !== "string" ||
|
|
17346
|
-
throw
|
|
17347
|
-
modelKeys.set(key, wireID(key, model));
|
|
17825
|
+
if (model.options?.fetch !== undefined || model.options?.baseURL !== undefined && (typeof model.options.baseURL !== "string" || normalizedBase(kind, model.options.baseURL) !== baseURL))
|
|
17826
|
+
throw failure("transport", providerID);
|
|
17827
|
+
modelKeys.set(key, wireID(key, model, residencyRuntimeLabel(config, providerID)));
|
|
17348
17828
|
}
|
|
17349
17829
|
if (!modelKeys.has(runtime.defaultModel.modelID))
|
|
17350
17830
|
modelKeys.set(runtime.defaultModel.modelID, undefined);
|
|
@@ -17353,11 +17833,12 @@ function createModelResidencyGuard(config, options) {
|
|
|
17353
17833
|
for (const [modelID2, declaredID] of modelKeys) {
|
|
17354
17834
|
const id = options.resolveApiModelID === undefined ? declaredID : options.resolveApiModelID({ providerID, modelID: modelID2 });
|
|
17355
17835
|
if (id !== undefined)
|
|
17356
|
-
ids.add(wireID(id, undefined));
|
|
17836
|
+
ids.add(wireID(id, undefined, residencyRuntimeLabel(config, providerID)));
|
|
17357
17837
|
}
|
|
17358
17838
|
return ids;
|
|
17359
17839
|
};
|
|
17360
|
-
const
|
|
17840
|
+
const guardFetch = kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
|
|
17841
|
+
const fetch = guardFetch({
|
|
17361
17842
|
...options,
|
|
17362
17843
|
baseURL,
|
|
17363
17844
|
modelIDs: async () => {
|
|
@@ -17368,7 +17849,10 @@ function createModelResidencyGuard(config, options) {
|
|
|
17368
17849
|
healthFetch: options.fetch
|
|
17369
17850
|
});
|
|
17370
17851
|
provider.options.fetch = fetch;
|
|
17852
|
+
if (kind === "llama-swap")
|
|
17853
|
+
provider.options.baseURL = baseURL;
|
|
17371
17854
|
guarded.set(providerID, {
|
|
17855
|
+
kind,
|
|
17372
17856
|
baseURL,
|
|
17373
17857
|
fetch,
|
|
17374
17858
|
originalFetch,
|
|
@@ -17382,7 +17866,7 @@ function createModelResidencyGuard(config, options) {
|
|
|
17382
17866
|
} catch (error) {
|
|
17383
17867
|
if (!(error instanceof ModelResidencyError))
|
|
17384
17868
|
throw error;
|
|
17385
|
-
(options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(providerID));
|
|
17869
|
+
(options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(providerID, residencyRuntimeLabel(config, providerID)));
|
|
17386
17870
|
}
|
|
17387
17871
|
}
|
|
17388
17872
|
};
|
|
@@ -17392,48 +17876,52 @@ function createModelResidencyGuard(config, options) {
|
|
|
17392
17876
|
return;
|
|
17393
17877
|
const provider = guarded.get(input.model.providerID);
|
|
17394
17878
|
if (provider === undefined || blocked.has(input.model.providerID) || input.provider.options.fetch !== provider.fetch || input.model.api.npm !== "@ai-sdk/openai-compatible" || nativeEligibleID(input.model.providerID))
|
|
17395
|
-
throw
|
|
17396
|
-
if (
|
|
17397
|
-
throw
|
|
17879
|
+
throw failure("transport", input.model.providerID);
|
|
17880
|
+
if (normalizedBase(provider.kind, base) !== provider.baseURL) {
|
|
17881
|
+
throw failure("destination", input.model.providerID);
|
|
17398
17882
|
}
|
|
17883
|
+
if (input.model.options.fetch !== undefined || input.model.options.baseURL !== undefined && (typeof input.model.options.baseURL !== "string" || normalizedBase(provider.kind, input.model.options.baseURL) !== provider.baseURL))
|
|
17884
|
+
throw failure("transport", input.model.providerID);
|
|
17399
17885
|
const id = resolvedWireID({ providerID: input.model.providerID, modelID: input.model.id }, provider.modelKeys.get(input.model.id));
|
|
17400
17886
|
if (id !== input.model.api.id)
|
|
17401
|
-
throw
|
|
17887
|
+
throw failure("identity", input.model.providerID);
|
|
17402
17888
|
if (!provider.modelKeys.has(input.model.id))
|
|
17403
17889
|
provider.modelKeys.set(input.model.id, undefined);
|
|
17404
17890
|
};
|
|
17405
17891
|
const localTarget = (model) => {
|
|
17406
17892
|
const runtime = configuredRuntime(config, model.providerID);
|
|
17893
|
+
const kind = localRuntimeKind(runtime);
|
|
17894
|
+
const needsResidency = runtimeRequiresModelResidency(kind);
|
|
17407
17895
|
if (hostConfig?.disabled_providers?.includes(model.providerID)) {
|
|
17408
|
-
throw
|
|
17896
|
+
throw failure("transport", model.providerID);
|
|
17409
17897
|
}
|
|
17410
17898
|
if (runtime.baseURL === undefined)
|
|
17411
|
-
throw
|
|
17899
|
+
throw failure("destination", model.providerID);
|
|
17412
17900
|
const provider = hostConfig?.provider?.[model.providerID];
|
|
17413
17901
|
const entry = Object.hasOwn(provider?.models ?? {}, model.modelID) ? provider?.models?.[model.modelID] : undefined;
|
|
17414
17902
|
const configuredURL = provider === undefined ? undefined : providerBase(provider, entry);
|
|
17415
|
-
if (configuredURL !== undefined && new URL(configuredURL).href.replace(/\/$/, "") !== new URL(runtime.baseURL).href.replace(/\/$/, "")) {
|
|
17416
|
-
throw
|
|
17903
|
+
if (configuredURL !== undefined && (needsResidency ? normalizedBase(kind, configuredURL) !== normalizedBase(kind, runtime.baseURL) : new URL(configuredURL).href.replace(/\/$/, "") !== new URL(runtime.baseURL).href.replace(/\/$/, ""))) {
|
|
17904
|
+
throw failure("destination", model.providerID);
|
|
17417
17905
|
}
|
|
17418
|
-
const modelID2 = resolvedWireID(model, entry === undefined &&
|
|
17906
|
+
const modelID2 = resolvedWireID(model, entry === undefined && needsResidency ? undefined : wireID(model.modelID, entry, residencyRuntimeLabel(config, model.providerID)));
|
|
17419
17907
|
let fetch = options.fetch;
|
|
17420
|
-
if (
|
|
17908
|
+
if (needsResidency) {
|
|
17421
17909
|
const installed = guarded.get(model.providerID);
|
|
17422
17910
|
if (installed === undefined || blocked.has(model.providerID)) {
|
|
17423
|
-
throw
|
|
17911
|
+
throw failure("transport", model.providerID);
|
|
17424
17912
|
}
|
|
17425
|
-
if (installed.baseURL !==
|
|
17426
|
-
throw
|
|
17913
|
+
if (installed.baseURL !== normalizedBase(kind, runtime.baseURL)) {
|
|
17914
|
+
throw failure("destination", model.providerID);
|
|
17427
17915
|
}
|
|
17428
17916
|
if (!installed.modelKeys.has(model.modelID))
|
|
17429
17917
|
installed.modelKeys.set(model.modelID, undefined);
|
|
17430
17918
|
if (!installed.modelIDs().has(modelID2))
|
|
17431
|
-
throw
|
|
17919
|
+
throw failure("identity", model.providerID);
|
|
17432
17920
|
fetch = installed.fetch;
|
|
17433
17921
|
}
|
|
17434
17922
|
const configuredHeaders = z25.record(z25.string(), z25.string()).safeParse(provider?.options?.headers ?? {});
|
|
17435
17923
|
if (!configuredHeaders.success)
|
|
17436
|
-
throw
|
|
17924
|
+
throw failure("configuration", model.providerID);
|
|
17437
17925
|
const headers = new Headers(configuredHeaders.data);
|
|
17438
17926
|
if (typeof provider?.options?.apiKey === "string" && !headers.has("authorization")) {
|
|
17439
17927
|
headers.set("authorization", `Bearer ${provider.options.apiKey}`);
|
|
@@ -17442,7 +17930,7 @@ function createModelResidencyGuard(config, options) {
|
|
|
17442
17930
|
headers.set(name, value);
|
|
17443
17931
|
const upstream = fetch;
|
|
17444
17932
|
return {
|
|
17445
|
-
baseURL: runtime.baseURL,
|
|
17933
|
+
baseURL: needsResidency ? normalizedBase(kind, runtime.baseURL) : runtime.baseURL,
|
|
17446
17934
|
modelID: modelID2,
|
|
17447
17935
|
fetch: [...headers].length === 0 ? fetch : Object.assign(async (input, init) => {
|
|
17448
17936
|
const request = new Request(input, init);
|
|
@@ -18565,7 +19053,7 @@ var memoryToolMessages = {
|
|
|
18565
19053
|
scribeOnly: (actualRoleID) => `openteam memory: refused — memory records may only be written by the "scribe" role, not "${actualRoleID}".`,
|
|
18566
19054
|
disabled: "openteam memory: semantic memory is disabled; nothing was recorded.",
|
|
18567
19055
|
frontierDomainRefusal: 'openteam memory: refused — memory extraction requires local model execution, but router.executionMode is "frontier". Set router.executionMode to "local" or "mixed" and configure a reachable local runtime.',
|
|
18568
|
-
localRuntimeUnavailable: "openteam memory: memory extraction requires a local runtime that is reachable and will not fall back to a frontier provider. Configure Ollama, LM Studio, Lemonade, or Foundry Local and run `openteam doctor`.",
|
|
19056
|
+
localRuntimeUnavailable: "openteam memory: memory extraction requires a local runtime that is reachable and will not fall back to a frontier provider. Configure Ollama, LM Studio, Lemonade, llama-swap, or Foundry Local and run `openteam doctor`.",
|
|
18569
19057
|
localRuntimeBaseURLMissing: "openteam memory: refused — no local runtime with a base URL is configured and enabled; memory extraction is local-only.",
|
|
18570
19058
|
extractionFailure: (reason) => `openteam memory: extraction failed; nothing was recorded (${reason}).`,
|
|
18571
19059
|
noRecords: "openteam memory: no records were extracted; nothing was recorded.",
|
|
@@ -18635,14 +19123,16 @@ function createMemoryTool(deps) {
|
|
|
18635
19123
|
if (runtime?.baseURL === undefined || runtime.baseURL.length === 0) {
|
|
18636
19124
|
return memoryToolMessages.localRuntimeBaseURLMissing;
|
|
18637
19125
|
}
|
|
18638
|
-
const
|
|
19126
|
+
const kind = localRuntimeKind(runtime);
|
|
19127
|
+
const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
|
|
18639
19128
|
const model = configuredModel ?? runtime.defaultModel;
|
|
18640
19129
|
const selectedRuntime = runtime;
|
|
18641
19130
|
const deadline = {
|
|
18642
19131
|
...deps.residencyOptions,
|
|
19132
|
+
...kind === "llama-swap" ? { runtimeLabel: "llama-swap" } : {},
|
|
18643
19133
|
...signal === undefined ? {} : { signal }
|
|
18644
19134
|
};
|
|
18645
|
-
const reachable =
|
|
19135
|
+
const reachable = runtimeRequiresModelResidency(kind) ? await withResidencyDeadline((signal2) => deps.localRuntimeReachable(selectedRuntime, signal2), deadline) : await deps.localRuntimeReachable(runtime);
|
|
18646
19136
|
throwIfAborted();
|
|
18647
19137
|
if (!reachable)
|
|
18648
19138
|
return memoryToolMessages.localRuntimeUnavailable;
|
|
@@ -18650,7 +19140,7 @@ function createMemoryTool(deps) {
|
|
|
18650
19140
|
const target = resolveTarget ? await withResidencyDeadline(async () => resolveTarget(model), deadline) : {
|
|
18651
19141
|
baseURL,
|
|
18652
19142
|
modelID: model.modelID,
|
|
18653
|
-
fetch:
|
|
19143
|
+
fetch: runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
|
|
18654
19144
|
baseURL,
|
|
18655
19145
|
fetch: deps.fetch,
|
|
18656
19146
|
modelIDs: new Set([model.modelID]),
|
|
@@ -21569,6 +22059,13 @@ function createLocalEmbedder(request, deps) {
|
|
|
21569
22059
|
return (texts) => embedTexts({ ...request, texts }, deps);
|
|
21570
22060
|
}
|
|
21571
22061
|
|
|
22062
|
+
// src/messages/memoryRuntime.ts
|
|
22063
|
+
var memoryRuntimeMessages = {
|
|
22064
|
+
providerUnavailable: "[openteam] Semantic memory requires exactly one enabled local runtime matching the configured embeddings provider. No embedding request was sent.",
|
|
22065
|
+
unavailable: "[openteam] Semantic memory initialization failed; recall and memory injection are unavailable.",
|
|
22066
|
+
rejectedLines: (count) => `[openteam] memory log rejected ${count} line(s); semantic memory may be incomplete.`
|
|
22067
|
+
};
|
|
22068
|
+
|
|
21572
22069
|
// src/storage/index/bunSqlite.ts
|
|
21573
22070
|
async function createBunSqliteDatabase(path4) {
|
|
21574
22071
|
const mod = await import("bun:sqlite");
|
|
@@ -21652,7 +22149,14 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
21652
22149
|
if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
|
|
21653
22150
|
return;
|
|
21654
22151
|
}
|
|
21655
|
-
const
|
|
22152
|
+
const warn = deps.warn ?? console.warn;
|
|
22153
|
+
const configuredModel = semantic.embeddings.model;
|
|
22154
|
+
const candidates = configuredModel == null ? config.local.runtimes.filter((rt) => rt.enabled && rt.baseURL !== undefined) : config.local.runtimes.filter((rt) => rt.defaultModel.providerID === configuredModel.providerID);
|
|
22155
|
+
const runtime = candidates[0];
|
|
22156
|
+
if (configuredModel != null && (candidates.length !== 1 || runtime?.enabled !== true)) {
|
|
22157
|
+
warn(memoryRuntimeMessages.providerUnavailable);
|
|
22158
|
+
return;
|
|
22159
|
+
}
|
|
21656
22160
|
if (runtime?.baseURL === undefined) {
|
|
21657
22161
|
return;
|
|
21658
22162
|
}
|
|
@@ -21661,15 +22165,22 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
21661
22165
|
const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
|
|
21662
22166
|
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
21663
22167
|
const now = deps.now ?? (() => Date.now());
|
|
21664
|
-
const
|
|
22168
|
+
const kind = localRuntimeKind(runtime);
|
|
21665
22169
|
try {
|
|
22170
|
+
const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
|
|
22171
|
+
const guardedFetch = runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
|
|
22172
|
+
baseURL,
|
|
22173
|
+
fetch: fetchImpl,
|
|
22174
|
+
healthFetch: fetchImpl,
|
|
22175
|
+
modelIDs: new Set([modelID2])
|
|
22176
|
+
}) : fetchImpl;
|
|
21666
22177
|
const db = await openDatabase(semantic.indexPath);
|
|
21667
22178
|
const index = createMemoryIndex(db, warn);
|
|
21668
22179
|
const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
|
|
21669
22180
|
if (rebuildResult.rejectedLines > 0) {
|
|
21670
|
-
warn(
|
|
22181
|
+
warn(memoryRuntimeMessages.rejectedLines(rebuildResult.rejectedLines));
|
|
21671
22182
|
}
|
|
21672
|
-
const embedder = makeEmbedder({ baseURL
|
|
22183
|
+
const embedder = makeEmbedder({ baseURL, modelID: modelID2 }, { fetch: guardedFetch });
|
|
21673
22184
|
await ensureMemoryEmbeddings({ index, embedder, model: modelID2, warn });
|
|
21674
22185
|
return {
|
|
21675
22186
|
index,
|
|
@@ -21679,6 +22190,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
|
21679
22190
|
semantic
|
|
21680
22191
|
};
|
|
21681
22192
|
} catch {
|
|
22193
|
+
warn(memoryRuntimeMessages.unavailable);
|
|
21682
22194
|
return;
|
|
21683
22195
|
}
|
|
21684
22196
|
}
|
|
@@ -22049,8 +22561,8 @@ function readRun(runDir, runID) {
|
|
|
22049
22561
|
if (metadataText === undefined || eventsText === undefined) {
|
|
22050
22562
|
throw new LifecycleFsError("corrupt");
|
|
22051
22563
|
}
|
|
22052
|
-
const
|
|
22053
|
-
if (
|
|
22564
|
+
const metadata3 = parseJson(metadataText, parseLifecycleRunMetadata);
|
|
22565
|
+
if (metadata3.runID !== runID) {
|
|
22054
22566
|
throw new LifecycleFsError("corrupt");
|
|
22055
22567
|
}
|
|
22056
22568
|
let decoded;
|
|
@@ -22065,10 +22577,10 @@ function readRun(runDir, runID) {
|
|
|
22065
22577
|
}
|
|
22066
22578
|
}
|
|
22067
22579
|
const first = decoded.events[0];
|
|
22068
|
-
if (first === undefined || first.type !== "run.started" || first.source !==
|
|
22580
|
+
if (first === undefined || first.type !== "run.started" || first.source !== metadata3.source || first.executionAuthority !== metadata3.executionAuthority || first.root.operationID !== metadata3.root.operationID || first.root.taskID !== metadata3.root.taskID) {
|
|
22069
22581
|
throw new LifecycleFsError("corrupt");
|
|
22070
22582
|
}
|
|
22071
|
-
return { metadata:
|
|
22583
|
+
return { metadata: metadata3, decoded };
|
|
22072
22584
|
}
|
|
22073
22585
|
function terminalOutcome(events) {
|
|
22074
22586
|
const last = events.at(-1);
|
|
@@ -22146,7 +22658,7 @@ var createFsLifecycleJournal = (options) => {
|
|
|
22146
22658
|
mkdirSync3(stagingPath);
|
|
22147
22659
|
const writerToken = issueWriterToken();
|
|
22148
22660
|
const eventAt = now();
|
|
22149
|
-
const
|
|
22661
|
+
const metadata3 = parseLifecycleRunMetadata({
|
|
22150
22662
|
...parsedInput,
|
|
22151
22663
|
version: 1,
|
|
22152
22664
|
createdAt: eventAt
|
|
@@ -22162,7 +22674,7 @@ var createFsLifecycleJournal = (options) => {
|
|
|
22162
22674
|
executionAuthority: parsedInput.executionAuthority,
|
|
22163
22675
|
root: parsedInput.root
|
|
22164
22676
|
});
|
|
22165
|
-
writeDurable2(join15(stagingPath, METADATA_FILE), `${JSON.stringify(
|
|
22677
|
+
writeDurable2(join15(stagingPath, METADATA_FILE), `${JSON.stringify(metadata3)}
|
|
22166
22678
|
`);
|
|
22167
22679
|
writeDurable2(join15(stagingPath, EVENTS_FILE), encodeLifecycleJournal([event]));
|
|
22168
22680
|
writeDurable2(join15(stagingPath, OWNER_FILE2), `${JSON.stringify(lease)}
|
|
@@ -22178,7 +22690,7 @@ var createFsLifecycleJournal = (options) => {
|
|
|
22178
22690
|
stagingPath = undefined;
|
|
22179
22691
|
return ok({
|
|
22180
22692
|
handle: { runID, writerToken },
|
|
22181
|
-
metadata:
|
|
22693
|
+
metadata: metadata3,
|
|
22182
22694
|
event,
|
|
22183
22695
|
lease,
|
|
22184
22696
|
head: 1
|
|
@@ -23299,6 +23811,20 @@ var server = async (ctx, rawOptions) => {
|
|
|
23299
23811
|
storage,
|
|
23300
23812
|
now,
|
|
23301
23813
|
localRuntimeReachable: async (runtime, signal) => {
|
|
23814
|
+
if (localRuntimeKind(runtime) === "llama-swap") {
|
|
23815
|
+
if (ctx.client.config?.providers !== undefined)
|
|
23816
|
+
await ensureAvailability();
|
|
23817
|
+
signal?.throwIfAborted();
|
|
23818
|
+
const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
|
|
23819
|
+
const residency = await readLlamaSwapResidency(target.baseURL, globalThis.fetch, {
|
|
23820
|
+
...signal === undefined ? {} : { signal }
|
|
23821
|
+
});
|
|
23822
|
+
if (!residency.has(target.modelID))
|
|
23823
|
+
throw new ModelResidencyError("identity", "llama-swap");
|
|
23824
|
+
if (residency.get(target.modelID)?.loaded !== true)
|
|
23825
|
+
throw new ModelResidencyError("inactive", "llama-swap");
|
|
23826
|
+
return true;
|
|
23827
|
+
}
|
|
23302
23828
|
if (localRuntimeKind(runtime) === "lemonade") {
|
|
23303
23829
|
if (ctx.client.config?.providers !== undefined)
|
|
23304
23830
|
await ensureAvailability();
|