UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

184 lines (183 loc) 7.76 kB
import { s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js"; import { l as asPositiveSafeInteger } from "./number-coercion-CJQ8TR--.js"; import { u as supportsClaudeAdaptiveThinking } from "./src-M7TBQdDX.js"; import { i as COPILOT_INTEGRATION_ID, s as buildCopilotIdeHeaders } from "./copilot-dynamic-headers-CF9zPORX.js"; import { a as normalizeModelCompat } from "./provider-model-compat-DhKi-Qv5.js"; import { f as readProviderJsonArrayFieldResponse } from "./provider-http-errors-DqaqQLLZ.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; import "./provider-model-shared-D-lyTLvb.js"; import "./provider-auth-DAOC_qI9.js"; import "./provider-http-BXCBCi4E.js"; import { i as resolveStaticCopilotModelOverride, n as resolveCopilotModelCompat, r as resolveCopilotTransportApi } from "./model-metadata-C6_pjeW7.js"; //#region extensions/github-copilot/models.ts const PROVIDER_ID = "github-copilot"; const CODEX_FORWARD_COMPAT_TARGET_IDS = new Set(["gpt-5.4", "gpt-5.3-codex"]); const CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.3-codex"]; const DEFAULT_CONTEXT_WINDOW = 128e3; const DEFAULT_MAX_TOKENS = 8192; function isCopilotCodexModelId(modelId) { return /(?:^|[-_.])codex(?:$|[-_.])/.test(modelId); } function resolveCopilotForwardCompatModel(ctx) { const trimmedModelId = ctx.modelId.trim(); if (!trimmedModelId) return; const lowerModelId = normalizeOptionalLowercaseString(trimmedModelId) ?? ""; if (ctx.modelRegistry.find("github-copilot", lowerModelId)) return; if (CODEX_FORWARD_COMPAT_TARGET_IDS.has(lowerModelId)) for (const templateId of CODEX_TEMPLATE_MODEL_IDS) { const template = ctx.modelRegistry.find(PROVIDER_ID, templateId); if (!template) continue; return normalizeModelCompat({ ...template, id: trimmedModelId, name: trimmedModelId }); } const staticOverride = resolveStaticCopilotModelOverride(lowerModelId); if (staticOverride) { const compat = staticOverride.compat ?? resolveCopilotModelCompat(trimmedModelId); return normalizeModelCompat({ id: trimmedModelId, name: staticOverride.name ?? trimmedModelId, provider: PROVIDER_ID, api: staticOverride.api ?? resolveCopilotTransportApi(trimmedModelId), reasoning: staticOverride.reasoning ?? false, input: staticOverride.input ?? ["text", "image"], cost: staticOverride.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: staticOverride.contextWindow ?? DEFAULT_CONTEXT_WINDOW, maxTokens: staticOverride.maxTokens ?? DEFAULT_MAX_TOKENS, ...staticOverride.thinkingLevelMap ? { thinkingLevelMap: staticOverride.thinkingLevelMap } : {}, ...compat ? { compat } : {} }); } const reasoning = /^o[13](\b|$)/.test(lowerModelId) || isCopilotCodexModelId(lowerModelId); const compat = resolveCopilotModelCompat(trimmedModelId); return normalizeModelCompat({ id: trimmedModelId, name: trimmedModelId, provider: PROVIDER_ID, api: resolveCopilotTransportApi(trimmedModelId), reasoning, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: DEFAULT_CONTEXT_WINDOW, maxTokens: DEFAULT_MAX_TOKENS, ...compat ? { compat } : {} }); } const COPILOT_MODELS_LIST_DEFAULT_TIMEOUT_MS = 1e4; const COPILOT_ROUTER_ID_PREFIX = "accounts/"; function resolveCopilotApiForVendor(vendor, modelId) { if (vendor && vendor.toLowerCase() === "anthropic") return "anthropic-messages"; return resolveCopilotTransportApi(modelId); } function mergeCopilotCompat(base, reasoningEfforts) { const supportedReasoningEfforts = Array.isArray(reasoningEfforts) ? [...new Set(reasoningEfforts.map((effort) => normalizeOptionalLowercaseString(effort)).filter((effort) => Boolean(effort)))] : []; if (supportedReasoningEfforts.length === 0) return base; return { ...base, supportedReasoningEfforts }; } function resolveCopilotThinkingLevelMap(api, modelId, compat) { const efforts = compat?.supportedReasoningEfforts; if (api !== "anthropic-messages" || !Array.isArray(efforts)) return; const supportsAdaptiveEffort = supportsClaudeAdaptiveThinking({ id: modelId }); return { xhigh: supportsAdaptiveEffort && efforts.includes("xhigh") ? "xhigh" : null, max: supportsAdaptiveEffort && efforts.includes("max") ? "max" : null }; } function mapCopilotApiModelToDefinition(entry) { const id = entry.id?.trim(); if (!id) return; if (entry.object && entry.object !== "model") return; if (entry.capabilities?.type && entry.capabilities.type !== "chat") return; if (id.startsWith(COPILOT_ROUTER_ID_PREFIX)) return; const limits = entry.capabilities?.limits; const supports = entry.capabilities?.supports; const reasoning = Array.isArray(supports?.reasoning_effort) ? supports.reasoning_effort.length > 0 : false; const input = supports?.vision === true ? ["text", "image"] : ["text"]; const contextWindow = asPositiveSafeInteger(limits?.max_context_window_tokens) ?? DEFAULT_CONTEXT_WINDOW; const maxTokens = asPositiveSafeInteger(limits?.max_output_tokens) ?? DEFAULT_MAX_TOKENS; const compat = mergeCopilotCompat(resolveCopilotModelCompat(id), supports?.reasoning_effort); const api = resolveCopilotApiForVendor(entry.vendor, id); const thinkingLevelMap = resolveCopilotThinkingLevelMap(api, id, compat); return { id, name: entry.name?.trim() || id, api, reasoning, input, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow, maxTokens, ...thinkingLevelMap ? { thinkingLevelMap } : {}, ...compat ? { compat } : {} }; } function asCopilotApiModelEntry(value) { if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Copilot /models: malformed JSON response"); return value; } /** * Fetch the live Copilot model catalog from `${baseUrl}/models` and project it * into `ModelDefinitionConfig[]`. Used by the plugin's discovery hook so the * runtime catalog tracks per-account entitlements + accurate context windows * without manifest churn. * * Filters out non-chat objects (embeddings, routers) and internal router ids. * On any HTTP/parse failure the caller should fall back to the static manifest * catalog; this function throws so the caller decides the recovery shape. */ async function fetchCopilotModelCatalog(params) { const fetchImpl = params.fetchImpl ?? fetch; const trimmedBase = params.baseUrl.replace(/\/+$/, ""); if (!trimmedBase) throw new Error("fetchCopilotModelCatalog: baseUrl required"); if (!params.copilotApiToken.trim()) throw new Error("fetchCopilotModelCatalog: copilotApiToken required"); const url = `${trimmedBase}/models`; const controller = params.signal ? void 0 : new AbortController(); const timeoutId = controller ? setTimeout(() => controller.abort(), COPILOT_MODELS_LIST_DEFAULT_TIMEOUT_MS) : void 0; try { const res = await fetchImpl(url, { method: "GET", headers: { Accept: "application/json", Authorization: `Bearer ${params.copilotApiToken}`, ...buildCopilotIdeHeaders(), "Copilot-Integration-Id": COPILOT_INTEGRATION_ID }, signal: params.signal ?? controller?.signal }); if (!res.ok) throw new Error(`Copilot /models fetch failed: HTTP ${res.status}`); const data = await readProviderJsonArrayFieldResponse(res, "Copilot /models", "data"); const seen = /* @__PURE__ */ new Set(); const out = []; for (const rawEntry of data) { const def = mapCopilotApiModelToDefinition(asCopilotApiModelEntry(rawEntry)); if (!def) continue; if (seen.has(def.id)) continue; seen.add(def.id); out.push(def); } return out; } finally { if (timeoutId !== void 0) clearTimeout(timeoutId); } } //#endregion export { fetchCopilotModelCatalog as n, resolveCopilotForwardCompatModel as r, PROVIDER_ID as t };