feat(github): resolve Copilot model catalog from upstream

Fetch the live model list from the Copilot /models endpoint and surface
it through /v1/models, replacing the hardcoded github entry so newly
shipped models appear without a code change. Catalog is cached per
credential and the Copilot token is refreshed on 401/403 before retry.

Also raise the connectivity-test budget to max_tokens:16, since Claude on
Copilot emits no choices at max_tokens:1 and produced a false negative.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
caiqinzhou
2026-06-26 10:28:09 +07:00
committed by decolua
parent e544bfceae
commit 1980178d02
3 changed files with 177 additions and 1 deletions

View File

@@ -135,7 +135,9 @@ export async function pingModelByKind(model, kind, baseUrl = `http://127.0.0.1:$
headers,
body: JSON.stringify({
model,
max_tokens: 1,
// Claude-on-Copilot returns empty choices at max_tokens:1 (budget is spent
// before a content token emits), so a 1-token probe yields a false negative.
max_tokens: 16,
stream: false,
messages: [{ role: "user", content: "hi" }],
}),

View File

@@ -9,6 +9,8 @@ import { getProviderConnections, getCombos, getCustomModels, getModelAliases } f
import { getDisabledModels } from "@/lib/disabledModelsDb";
import { resolveKiroModels } from "open-sse/services/kiroModels.js";
import { resolveQoderModels } from "open-sse/services/qoderModels.js";
import { resolveCopilotModels } from "open-sse/services/copilotModels.js";
import { updateProviderCredentials } from "@/sse/services/tokenRefresh";
import { capabilitiesFromServiceKind } from "open-sse/providers/capabilities.js";
// Per-provider live model resolvers. Each receives a connection record and
@@ -35,6 +37,23 @@ const LIVE_MODEL_RESOLVERS = {
return {
models: result.models.map((m) => ({ id: m.id, name: m.name })),
};
},
github: async (conn) => {
const result = await resolveCopilotModels({
accessToken: conn.accessToken,
refreshToken: conn.refreshToken,
providerSpecificData: conn.providerSpecificData || {}
}, {
log: console,
onCredentialsRefreshed: async (refreshed) => {
await updateProviderCredentials(conn.id, {
copilotToken: refreshed.copilotToken,
copilotTokenExpiresAt: refreshed.copilotTokenExpiresAt,
existingProviderSpecificData: conn.providerSpecificData || {},
});
},
});
return result?.models?.length ? { models: result.models } : null;
}
};