openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
210 lines (209 loc) • 9.46 kB
JavaScript
import { d as asPositiveSafeInteger } from "./number-coercion-CLj0HTDM.js";
import { c as normalizeOptionalLowercaseString } from "./string-coerce-CIXf7egm.js";
import { f as readProviderJsonArrayFieldResponse } from "./provider-http-errors-U-nhuk_f.js";
import { i as normalizeModelCompat } from "./provider-model-compat-BNXu5bti.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import "./provider-model-shared-fX-Ywqb3.js";
import "./provider-http-k9RMI7iG.js";
import { a as resolveStaticCopilotModelOverride, i as resolveCopilotTransportApi, n as resolveCopilotModelCompat, r as resolveCopilotThinkingLevelMap } from "./model-metadata-Ds3TNF-l.js";
import { t as buildCopilotRuntimeHeaders } from "./runtime-identity-iGvJ9Qj9.js";
//#region extensions/github-copilot/models.ts
const PROVIDER_ID = "github-copilot";
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;
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,
...staticOverride.contextTokens !== void 0 ? { contextTokens: staticOverride.contextTokens } : {},
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 copilotModelSelectionMetadata = /* @__PURE__ */ new WeakMap();
function readCopilotModelSelectionMetadata(model) {
return copilotModelSelectionMetadata.get(model);
}
function isCopilotCatalogModelVisible(model) {
const metadata = readCopilotModelSelectionMetadata(model);
return Boolean(metadata?.pickerEnabled && metadata.policyState !== "disabled" && metadata.policyState !== "unconfigured");
}
function isCopilotCatalogModelSelectable(model) {
const metadata = readCopilotModelSelectionMetadata(model);
return Boolean(isCopilotCatalogModelVisible(model) && metadata?.streaming !== false && metadata?.toolCalls);
}
const COPILOT_STARTER_CATEGORY_RANK = /* @__PURE__ */ new Map([
["versatile", 0],
["lightweight", 1],
["powerful", 2]
]);
function compareCopilotStarterCandidates(left, right) {
const leftMetadata = readCopilotModelSelectionMetadata(left);
const rightMetadata = readCopilotModelSelectionMetadata(right);
const previewDelta = Number(leftMetadata?.preview === true) - Number(rightMetadata?.preview === true);
if (previewDelta !== 0) return previewDelta;
const categoryDelta = (COPILOT_STARTER_CATEGORY_RANK.get(leftMetadata?.category ?? "") ?? Number.MAX_SAFE_INTEGER) - (COPILOT_STARTER_CATEGORY_RANK.get(rightMetadata?.category ?? "") ?? Number.MAX_SAFE_INTEGER);
if (categoryDelta !== 0) return categoryDelta;
const contextDelta = (right.contextWindow ?? DEFAULT_CONTEXT_WINDOW) - (left.contextWindow ?? DEFAULT_CONTEXT_WINDOW);
if (contextDelta !== 0) return contextDelta;
const outputDelta = right.maxTokens - left.maxTokens;
if (outputDelta !== 0) return outputDelta;
return left.id.localeCompare(right.id);
}
function selectCopilotStarterModel(models, preferredModelId) {
const selectable = models.filter(isCopilotCatalogModelSelectable);
return selectable.find((model) => model.id === preferredModelId) ?? selectable.toSorted(compareCopilotStarterCandidates)[0];
}
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 (!Array.isArray(reasoningEfforts)) return base;
return {
...base,
supportedReasoningEfforts
};
}
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 contextTokens = asPositiveSafeInteger(limits?.max_prompt_tokens);
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(id, compat, api);
const definition = {
id,
name: entry.name?.trim() || id,
api,
reasoning,
input,
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
},
contextWindow,
...contextTokens !== void 0 ? { contextTokens } : {},
maxTokens,
...thinkingLevelMap ? { thinkingLevelMap } : {},
...compat ? { compat } : {}
};
copilotModelSelectionMetadata.set(definition, {
category: normalizeOptionalLowercaseString(entry.model_picker_category),
pickerEnabled: entry.model_picker_enabled === true,
policyState: normalizeOptionalLowercaseString(entry.policy?.state),
preview: entry.preview === true,
streaming: supports?.streaming,
toolCalls: supports?.tool_calls === true
});
return definition;
}
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",
...buildCopilotRuntimeHeaders({ headers: params.headers }),
Authorization: `Bearer ${params.copilotApiToken}`
},
signal: params.signal ?? controller?.signal
});
if (!res.ok) {
await res.body?.cancel().catch(() => void 0);
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 { resolveCopilotForwardCompatModel as a, isCopilotCatalogModelVisible as i, PROVIDER_ID as n, selectCopilotStarterModel as o, fetchCopilotModelCatalog as r, COPILOT_MODELS_LIST_DEFAULT_TIMEOUT_MS as t };