UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

294 lines (293 loc) • 13.7 kB
import { d as asPositiveSafeInteger } from "../../number-coercion-CkhMiRw8.mjs"; import { c as isRecord, o as asRecord } from "../../record-coerce-TIsquoVR.mjs"; import { n as filterStringEntries, y as uniqueStrings } from "../../string-normalization-D6DKCpwE.mjs"; import { o as racePromiseWithAbortSignal } from "../../src-D1BHGNTj.mjs"; import { t as createSubsystemLogger } from "../../subsystem-4NzR43uz.mjs"; import { n as createLazyRuntimeMethodBinder, r as createLazyRuntimeModule } from "../../lazy-runtime-CJrzFzqU.mjs"; import "../../llm-CJ3KKVO3.mjs"; import { h as ssrfPolicyFromHttpBaseUrlAllowedHostname } from "../../ssrf-BUqrVNdU.mjs"; import { t as normalizeOptionalSecretInput } from "../../normalize-secret-input-DBJvdk-L.mjs"; import { n as CUSTOM_LOCAL_AUTH_MARKER } from "../../model-auth-markers-BKfhEhYJ.mjs"; import "../../string-coerce-runtime-BbIJhy7G.mjs"; import "../../time-runtime-SlsIp1up.mjs"; import { i as streamSimple } from "../../stream-B2a9zNQj.mjs"; import { f as createOpenAICompatibleCompletionsThinkingOffWrapper, m as createPlainTextToolCallCompatWrapper } from "../../provider-stream-shared-BhBL5TrQ.mjs"; import { t as definePluginEntry } from "../../plugin-entry-8p_0SEiv.mjs"; import "../../ssrf-runtime-CR-jTHeC.mjs"; import "../../logging-core-ClC4WZ0f.mjs"; import "../../provider-auth-CjrRHalO.mjs"; import { r as buildProviderToolCompatFamilyHooks } from "../../provider-tools-DajrfQK1.mjs"; import { B as LMSTUDIO_PROVIDER_LABEL, C as resolveLmstudioInferenceBase, d as resolveLmstudioRuntimeApiKey, i as prepareLmstudioModelForInference, k as LMSTUDIO_DEFAULT_API_KEY_ENV_VAR, l as resolveLmstudioProviderHeaders, m as resolveLmstudioProviderAuthMode, p as hasLmstudioAuthorizationHeader, x as normalizeLmstudioProviderConfig, y as normalizeLmstudioConfiguredCatalogEntries, z as LMSTUDIO_PROVIDER_ID } from "../../models.fetch-DGiLesEZ.mjs"; import { t as lmstudioMemoryEmbeddingProviderAdapter } from "../../memory-embedding-adapter-CSRA565O.mjs"; //#region extensions/lmstudio/provider-discovery.ts var provider_discovery_default = { id: LMSTUDIO_PROVIDER_ID, label: "LM Studio", docsPath: "/providers/lmstudio", envVars: [LMSTUDIO_DEFAULT_API_KEY_ENV_VAR], auth: [], catalog: { order: "late", run: async (ctx) => { const { discoverLmstudioProvider } = await import("../../setup-EtlrHlzx.mjs"); return await discoverLmstudioProvider(ctx, { discoveryMode: "strict" }); } }, resolveSyntheticAuth: ({ providerConfig }) => { if (!providerConfig || resolveLmstudioProviderAuthMode(providerConfig.apiKey) || hasLmstudioAuthorizationHeader(providerConfig.headers)) return; return { apiKey: CUSTOM_LOCAL_AUTH_MARKER, source: "models.providers.lmstudio (synthetic local key)", mode: "api-key" }; } }; //#endregion //#region extensions/lmstudio/src/stream.ts const log = createSubsystemLogger("extensions/lmstudio/stream"); const preloadInFlight = /* @__PURE__ */ new Map(); const preloadCooldown = /* @__PURE__ */ new Map(); const PRELOAD_BACKOFF_BASE_MS = 5e3; const PRELOAD_BACKOFF_MAX_MS = 3e5; function recordPreloadFailure(preloadKey, now, resolvedModelKey) { const existing = preloadCooldown.get(preloadKey); const consecutiveFailures = (existing?.consecutiveFailures ?? 0) + 1; const persistedResolvedModelKey = resolvedModelKey ?? existing?.resolvedModelKey; const entry = { consecutiveFailures, untilMs: now + Math.min(PRELOAD_BACKOFF_MAX_MS, PRELOAD_BACKOFF_BASE_MS * 2 ** Math.max(0, consecutiveFailures - 1)), ...persistedResolvedModelKey ? { resolvedModelKey: persistedResolvedModelKey } : {} }; preloadCooldown.set(preloadKey, entry); return entry; } function normalizeLmstudioModelKey(modelId) { const trimmed = modelId.trim(); if (trimmed.toLowerCase().startsWith("lmstudio/")) return trimmed.slice(9).trim(); return trimmed; } function withLmstudioUsageCompat(model) { const compat = model.compat && typeof model.compat === "object" ? model.compat : {}; const unsupportedToolSchemaKeywords = "unsupportedToolSchemaKeywords" in compat ? filterStringEntries(compat.unsupportedToolSchemaKeywords) : []; const normalizedCompat = { ...compat, supportsUsageInStreaming: true, unsupportedToolSchemaKeywords: uniqueStrings([...unsupportedToolSchemaKeywords, "pattern"]) }; return { ...model, compat: normalizedCompat }; } function resolveLmstudioModelKeyFromError(error) { let current = error; const seen = /* @__PURE__ */ new Set(); while (current && typeof current === "object" && !seen.has(current)) { seen.add(current); const record = current; const resolvedModelKey = typeof record.resolvedModelKey === "string" ? record.resolvedModelKey.trim() : ""; if (resolvedModelKey) return resolvedModelKey; current = record.cause; } } function toLmstudioPreloadError(reason, message) { return reason instanceof Error ? reason : new Error(message, { cause: reason }); } function waitForLmstudioPreload(preload, signal) { if (!signal) return preload; return racePromiseWithAbortSignal(preload.catch((error) => { throw toLmstudioPreloadError(error, "LM Studio model preload failed"); }), signal, () => toLmstudioPreloadError(signal.reason, "LM Studio preload aborted")); } async function ensureLmstudioModelLoadedBestEffort(params) { const providerHeaders = { ...(params.ctx.config?.models?.providers?.[LMSTUDIO_PROVIDER_ID])?.headers, ...params.modelHeaders }; const runtimeApiKey = typeof params.options?.apiKey === "string" && params.options.apiKey.trim().length > 0 ? params.options.apiKey.trim() : void 0; const headers = await resolveLmstudioProviderHeaders({ config: params.ctx.config, headers: providerHeaders }); const configuredApiKey = runtimeApiKey !== void 0 ? void 0 : await resolveLmstudioRuntimeApiKey({ config: params.ctx.config, agentDir: params.ctx.agentDir, headers: providerHeaders }); return await prepareLmstudioModelForInference({ baseUrl: params.baseUrl, apiKey: runtimeApiKey ?? configuredApiKey, headers, ssrfPolicy: ssrfPolicyFromHttpBaseUrlAllowedHostname(params.baseUrl), modelKey: params.modelKey, requestedContextLength: params.requestedContextLength }); } function wrapLmstudioInferencePreload(ctx) { const underlying = ctx.streamFn ?? streamSimple; const streamWithThinkingLevel = createOpenAICompatibleCompletionsThinkingOffWrapper(createPlainTextToolCallCompatWrapper(underlying), ctx.thinkingLevel); return (model, context, options) => { if (model.provider !== "lmstudio") return underlying(model, context, options); const modelKey = normalizeLmstudioModelKey(model.id); if (!modelKey) return underlying(model, context, options); options?.signal?.throwIfAborted(); const providerConfig = ctx.config?.models?.providers?.[LMSTUDIO_PROVIDER_ID]; if (asRecord(providerConfig?.params).preload === false) return streamWithThinkingLevel(withLmstudioUsageCompat(model), context, options); const providerBaseUrl = providerConfig?.baseUrl; const resolvedBaseUrl = resolveLmstudioInferenceBase(typeof model.baseUrl === "string" ? model.baseUrl : providerBaseUrl); const requestedContextLength = asPositiveSafeInteger(model.contextTokens) ?? asPositiveSafeInteger(model.contextWindow); const preloadKey = `${resolvedBaseUrl}::${modelKey}::${requestedContextLength ?? "default"}`; const cooldown = preloadCooldown.get(preloadKey); const cooldownEntry = cooldown && cooldown.untilMs > Date.now() ? cooldown : void 0; const preloadPromise = preloadInFlight.get(preloadKey) ?? (cooldownEntry ? void 0 : (() => { const created = ensureLmstudioModelLoadedBestEffort({ baseUrl: resolvedBaseUrl, modelKey, requestedContextLength, options, ctx, modelHeaders: isRecord(model.headers) ? model.headers : void 0 }).then((preparedModel) => { preloadCooldown.delete(preloadKey); return preparedModel; }, (error) => { const resolvedModelKey = resolveLmstudioModelKeyFromError(error); const entry = recordPreloadFailure(preloadKey, Date.now(), resolvedModelKey); throw Object.assign(/* @__PURE__ */ new Error("preload-failed"), { cause: error, consecutiveFailures: entry.consecutiveFailures, cooldownMs: entry.untilMs - Date.now(), resolvedModelKey }); }).finally(() => { preloadInFlight.delete(preloadKey); }); preloadInFlight.set(preloadKey, created); return created; })()); return (async () => { let preparedModel; if (preloadPromise) try { preparedModel = await waitForLmstudioPreload(preloadPromise, options?.signal); } catch (error) { options?.signal?.throwIfAborted(); const annotated = error; const resolvedModelKey = resolveLmstudioModelKeyFromError(error); preparedModel = resolvedModelKey ? { modelKey: resolvedModelKey } : void 0; const cause = annotated.cause ?? error; const failures = annotated.consecutiveFailures ?? 1; const cooldownSec = Math.max(0, Math.round((annotated.cooldownMs ?? 0) / 1e3)); log.warn(`LM Studio inference preload failed for "${modelKey}" (${failures} consecutive failure${failures === 1 ? "" : "s"}, next preload attempt skipped for ~${cooldownSec}s); continuing without preload: ${String(cause)}`); } else if (cooldownEntry) { const resolvedModelKey = cooldownEntry.resolvedModelKey; preparedModel = resolvedModelKey ? { modelKey: resolvedModelKey } : void 0; log.debug(`LM Studio inference preload for "${modelKey}" skipped while backoff active (${cooldownEntry.consecutiveFailures} prior failures)`); } const modelId = preparedModel?.modelKey; const streamModel = modelId && modelId !== model.id ? { ...model, id: modelId } : model; const instanceId = preparedModel?.instanceId; return await streamWithThinkingLevel(withLmstudioUsageCompat(streamModel), context, instanceId ? { ...options, async onPayload(payload, payloadModel) { asRecord(payload).model = instanceId; const replacement = await options?.onPayload?.(payload, payloadModel); asRecord(replacement ?? payload).model = instanceId; return replacement; } } : options); })(); }; } //#endregion //#region extensions/lmstudio/index.ts const PROVIDER_ID = "lmstudio"; const loadSetup = createLazyRuntimeModule(() => import("../../setup-EtlrHlzx.mjs")); const setupMethod = createLazyRuntimeMethodBinder(loadSetup); function resolveLmstudioAugmentedCatalogEntries(config) { return normalizeLmstudioConfiguredCatalogEntries(config?.models?.providers?.lmstudio?.models).map((entry) => Object.assign({}, entry, { provider: PROVIDER_ID, name: entry.name ?? entry.id, compat: { ...entry.compat, supportsUsageInStreaming: true } })); } var lmstudio_default = definePluginEntry({ id: PROVIDER_ID, name: "LM Studio Provider", description: "Bundled LM Studio provider plugin", register(api) { api.registerEmbeddingProvider(lmstudioMemoryEmbeddingProviderAdapter); api.registerProvider({ ...provider_discovery_default, auth: [{ id: "custom", label: LMSTUDIO_PROVIDER_LABEL, hint: "Connect to a running LM Studio server and use an already loaded model", kind: "custom", appGuidedSetup: { detectAvailability: setupMethod((setup) => setup.detectAppGuidedLmstudioAvailability), detect: async (ctx) => { const result = await (await loadSetup()).prepareAppGuidedLmstudioSetup(ctx); if (!result?.defaultModel) return null; const provider = result.configPatch?.models?.providers?.[PROVIDER_ID]; return { modelRef: result.defaultModel, detail: `${result.defaultModel.slice(`${PROVIDER_ID}/`.length)} at ${provider?.baseUrl ?? "LM Studio"}` }; }, prepare: setupMethod((setup) => setup.prepareAppGuidedLmstudioSetup) }, run: async (ctx) => { const providerSetup = await loadSetup(); const suppliedApiKey = ctx.opts?.tokenProvider === PROVIDER_ID ? normalizeOptionalSecretInput(ctx.opts.token) : void 0; return await providerSetup.promptAndConfigureLmstudioInteractive({ config: ctx.config, agentDir: ctx.agentDir, workspaceDir: ctx.workspaceDir, prompter: ctx.prompter, secretInputMode: ctx.secretInputMode, allowSecretRefPrompt: ctx.allowSecretRefPrompt, isRemote: ctx.isRemote, signal: ctx.signal, ...suppliedApiKey ? { suppliedApiKey, requestedModelId: normalizeOptionalSecretInput(ctx.opts?.customModelId) } : {} }); }, validateNonInteractive: setupMethod((setup) => setup.validateLmstudioNonInteractive), runNonInteractive: setupMethod((setup) => setup.configureLmstudioNonInteractive) }], shouldDeferSyntheticProfileAuth: ({ resolvedApiKey }) => resolvedApiKey?.trim() === "lmstudio-local" || resolvedApiKey?.trim() === "custom-local", normalizeConfig: ({ providerConfig }) => normalizeLmstudioProviderConfig(providerConfig), prepareDynamicModel: setupMethod((setup) => setup.prepareLmstudioDynamicModel), augmentModelCatalog: (ctx) => resolveLmstudioAugmentedCatalogEntries(ctx.config), wrapStreamFn: wrapLmstudioInferencePreload, ...buildProviderToolCompatFamilyHooks("llamacpp-gbnf"), wizard: { setup: { choiceId: PROVIDER_ID, choiceLabel: "LM Studio", choiceHint: "Connect to a running LM Studio server and use an already loaded model", groupId: PROVIDER_ID, groupLabel: "LM Studio", groupHint: "Self-hosted open-weight models", methodId: "custom" }, modelPicker: { label: "LM Studio (custom)", hint: "Detect models from LM Studio /api/v1/models", methodId: "custom" } } }); } }); //#endregion export { lmstudio_default as default };