openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
226 lines (225 loc) • 9.9 kB
JavaScript
import { h as redactToolPayloadText } from "./redact-BtvPPfTi.js";
import { m as normalizeResolvedSecretInputString } from "./types.secrets-kC0nOetj.js";
import { g as readResponseTextLimited, m as readProviderJsonResponse } from "./provider-http-errors-U-nhuk_f.js";
import { i as fetchWithSsrFGuard } from "./fetch-guard-BMdGQhbb.js";
import { F as buildRemoteBaseUrlPolicy, I as withRemoteHttpResponse, R as sanitizeAndNormalizeEmbedding } from "./gateway-startup-plugin-config-Bq9ZdScF.js";
import "./secret-input-dpVVFmLG.js";
import "./ssrf-runtime-Bum5C6NN.js";
import "./logging-core-yEitd9NN.js";
import "./provider-http-k9RMI7iG.js";
import "./memory-core-host-engine-embeddings-DwuZ1cl_.js";
import { i as resolveGithubCopilotDomain } from "./domain-Bbe8oFEv.js";
import { t as buildCopilotRuntimeHeaders } from "./runtime-identity-iGvJ9Qj9.js";
import { t as COPILOT_MODELS_LIST_DEFAULT_TIMEOUT_MS } from "./models-B1TCB-7V.js";
import { t as resolveFirstGithubToken } from "./auth-DX25MAVM.js";
import { t as CopilotRuntimeAuthError } from "./runtime-auth-error-CQZde1c0.js";
import { n as resolveCopilotRuntimeAuth } from "./runtime-auth-CGmLxWX2.js";
//#region extensions/github-copilot/embeddings.ts
const COPILOT_EMBEDDING_PROVIDER_ID = "github-copilot";
/**
* Preferred embedding models in order. The first available model wins.
*/
const PREFERRED_MODELS = [
"text-embedding-3-small",
"text-embedding-3-large",
"text-embedding-ada-002"
];
const COPILOT_ERROR_BODY_LIMIT_BYTES = 8192;
const COPILOT_EMBEDDINGS_RESPONSE_MAX_BYTES = 67108864;
function buildSsrfPolicy(baseUrl) {
try {
const parsed = new URL(baseUrl);
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
return { allowedHostnames: [parsed.hostname] };
} catch {
return;
}
}
function isCopilotSetupError(err) {
if (err instanceof CopilotRuntimeAuthError) return true;
if (!(err instanceof Error)) return false;
return err.message.includes("No GitHub token available") || err.message.includes("Copilot user response") || err.message.includes("No embedding models available") || err.message.includes("GitHub Copilot model discovery") || err.message.includes("github-copilot.model-discovery") || err.message.includes("GitHub Copilot embedding model") || err.message.includes("Unexpected response from GitHub Copilot user endpoint");
}
async function discoverEmbeddingModels(params) {
const url = `${params.baseUrl.replace(/\/$/, "")}/models`;
const { response, release } = await fetchWithSsrFGuard({
url,
init: {
method: "GET",
headers: {
...params.headers,
Authorization: `Bearer ${params.copilotToken}`
}
},
policy: params.ssrfPolicy,
timeoutMs: COPILOT_MODELS_LIST_DEFAULT_TIMEOUT_MS,
auditContext: "memory-remote"
});
try {
if (!response.ok) {
const detail = redactToolPayloadText(await readResponseTextLimited(response, COPILOT_ERROR_BODY_LIMIT_BYTES));
throw new Error(`GitHub Copilot model discovery HTTP ${response.status}: ${detail}`);
}
const payload = await readProviderJsonResponse(response, "github-copilot.model-discovery");
return (Array.isArray(payload?.data) ? payload.data ?? [] : []).flatMap((entry) => {
const id = typeof entry.id === "string" ? entry.id.trim() : "";
if (!id) return [];
return (Array.isArray(entry.supported_endpoints) ? entry.supported_endpoints.filter((value) => typeof value === "string") : []).some((ep) => ep.includes("embeddings")) || /\bembedding/i.test(id) ? [id] : [];
});
} finally {
await release();
}
}
function normalizeCopilotEmbeddingModel(model) {
const normalized = model.trim();
const prefix = `${COPILOT_EMBEDDING_PROVIDER_ID}/`;
const stripped = normalized.startsWith(prefix) ? normalized.slice(prefix.length) : normalized;
return stripped && stripped === stripped.trim() && !stripped.startsWith(prefix) ? stripped : normalized;
}
function pickBestModel(available, userModel) {
if (userModel) {
const normalized = normalizeCopilotEmbeddingModel(userModel);
if (available.length === 0) throw new Error("No embedding models available from GitHub Copilot");
if (!available.includes(normalized)) throw new Error(`GitHub Copilot embedding model "${normalized}" is not available. Available: ${available.join(", ")}`);
return normalized;
}
for (const preferred of PREFERRED_MODELS) if (available.includes(preferred)) return preferred;
const [firstAvailable] = available;
if (firstAvailable) return firstAvailable;
throw new Error("No embedding models available from GitHub Copilot");
}
function parseGitHubCopilotEmbeddingPayload(payload, expectedCount) {
if (!payload || typeof payload !== "object") throw new Error("GitHub Copilot embeddings response missing data[]");
const data = payload.data;
if (!Array.isArray(data)) throw new Error("GitHub Copilot embeddings response missing data[]");
const vectors = Array.from({ length: expectedCount });
for (const entry of data) {
if (!entry || typeof entry !== "object") throw new Error("GitHub Copilot embeddings response contains an invalid entry");
const indexValue = entry.index;
const embedding = entry.embedding;
const index = typeof indexValue === "number" ? indexValue : NaN;
if (!Number.isInteger(index)) throw new Error("GitHub Copilot embeddings response contains an invalid index");
if (index < 0 || index >= expectedCount) throw new Error("GitHub Copilot embeddings response contains an out-of-range index");
if (vectors[index] !== void 0) throw new Error("GitHub Copilot embeddings response contains duplicate indexes");
if (!Array.isArray(embedding) || !embedding.every((value) => typeof value === "number")) throw new Error("GitHub Copilot embeddings response contains an invalid embedding");
vectors[index] = sanitizeAndNormalizeEmbedding(embedding);
}
for (let index = 0; index < expectedCount; index += 1) if (vectors[index] === void 0) throw new Error("GitHub Copilot embeddings response missing vectors for some inputs");
return vectors;
}
function createGitHubCopilotEmbeddingProvider(client) {
const embedMany = async (input, signal) => {
if (input.length === 0) return [];
const url = `${client.baseUrl.replace(/\/$/, "")}/embeddings`;
return await withRemoteHttpResponse({
url,
fetchImpl: client.fetchImpl,
ssrfPolicy: buildRemoteBaseUrlPolicy(client.baseUrl),
signal,
init: {
method: "POST",
headers: client.headers,
body: JSON.stringify({
model: client.model,
input
})
},
onResponse: async (response) => {
if (!response.ok) {
const detail = redactToolPayloadText(await readResponseTextLimited(response, COPILOT_ERROR_BODY_LIMIT_BYTES));
throw new Error(`GitHub Copilot embeddings HTTP ${response.status}: ${detail}`);
}
return parseGitHubCopilotEmbeddingPayload(await readProviderJsonResponse(response, "github-copilot.embeddings", { maxBytes: COPILOT_EMBEDDINGS_RESPONSE_MAX_BYTES }), input.length);
}
});
};
return {
id: COPILOT_EMBEDDING_PROVIDER_ID,
model: client.model,
embed: async (input, options) => {
const [vector] = await embedMany([typeof input === "string" ? input : input.text], options?.signal);
return vector ?? [];
},
embedBatch: async (inputs, options) => {
const texts = inputs.map((input) => typeof input === "string" ? input : input.text);
if (options?.inputType === "query") return await Promise.all(texts.map(async (text) => (await embedMany([text], options.signal))[0] ?? []));
return await embedMany(texts, options?.signal);
}
};
}
const githubCopilotMemoryEmbeddingProviderAdapter = {
id: COPILOT_EMBEDDING_PROVIDER_ID,
transport: "remote",
authProviderId: COPILOT_EMBEDDING_PROVIDER_ID,
normalizeModel: ({ model }) => normalizeCopilotEmbeddingModel(model),
autoSelectPriority: 15,
allowExplicitWhenConfiguredAuto: true,
shouldContinueAutoSelection: (err) => isCopilotSetupError(err),
create: async (options) => {
const explicitValue = normalizeResolvedSecretInputString({
value: options.remote?.apiKey,
path: "memory.search.remote.apiKey"
});
const customBaseUrl = options.remote?.baseUrl?.trim();
const customRuntimeAuth = customBaseUrl ? (() => {
if (!explicitValue) throw new Error("GitHub Copilot memory custom baseUrl requires an explicit memory.search.remote.apiKey");
return {
apiKey: explicitValue,
baseUrl: customBaseUrl
};
})() : void 0;
const profileAuth = explicitValue ? void 0 : await resolveFirstGithubToken({
agentDir: options.agentDir,
config: options.config,
env: process.env
});
const value = explicitValue ?? profileAuth?.githubToken;
if (!value) throw new Error("No GitHub token available for Copilot embedding provider");
const githubDomain = resolveGithubCopilotDomain({
env: process.env,
explicit: profileAuth?.githubDomain,
config: options.config
});
const runtimeAuth = customRuntimeAuth ?? await resolveCopilotRuntimeAuth({
githubToken: value,
env: process.env,
githubDomain
});
const baseUrl = runtimeAuth.baseUrl || "https://api.individual.githubcopilot.com";
const ssrfPolicy = buildSsrfPolicy(baseUrl);
const headers = buildCopilotRuntimeHeaders({
config: options.config,
headers: {
"Content-Type": "application/json",
...options.remote?.headers
}
});
const model = pickBestModel(await discoverEmbeddingModels({
baseUrl,
copilotToken: runtimeAuth.apiKey,
headers,
ssrfPolicy
}), options.model?.trim() || void 0);
return {
provider: createGitHubCopilotEmbeddingProvider({
baseUrl,
fetchImpl: fetch,
headers: {
...headers,
Authorization: `Bearer ${runtimeAuth.apiKey}`
},
model
}),
runtime: {
id: COPILOT_EMBEDDING_PROVIDER_ID,
cacheKeyData: {
provider: COPILOT_EMBEDDING_PROVIDER_ID,
baseUrl,
model
}
}
};
}
};
//#endregion
export { githubCopilotMemoryEmbeddingProviderAdapter as t };