feat(kimchi): add Kimchi OAuth provider support
Add Kimchi as a browser-token OAuth provider routed through its OpenAI-compatible gateway. Discover live models for /v1/models and provider models, normalize Claude-compatible requests, and wire up provider connection tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
co-authored by
Cursor
parent
2d94fffe3b
commit
8a664d619d
@@ -8,6 +8,7 @@ import {
|
||||
import { getProviderConnections, getCombos, getCustomModels, getModelAliases } from "@/lib/localDb";
|
||||
import { getDisabledModels } from "@/lib/disabledModelsDb";
|
||||
import { resolveKiroModels } from "open-sse/services/kiroModels.js";
|
||||
import { resolveKimchiModels } from "open-sse/services/kimchiModels.js";
|
||||
import { resolveQoderModels } from "open-sse/services/qoderModels.js";
|
||||
import { resolveCopilotModels } from "open-sse/services/copilotModels.js";
|
||||
import { updateProviderCredentials } from "@/sse/services/tokenRefresh";
|
||||
@@ -38,6 +39,14 @@ const LIVE_MODEL_RESOLVERS = {
|
||||
models: result.models.map((m) => ({ id: m.id, name: m.name })),
|
||||
};
|
||||
},
|
||||
kimchi: async (conn) => {
|
||||
const result = await resolveKimchiModels({
|
||||
accessToken: conn.accessToken,
|
||||
apiKey: conn.apiKey,
|
||||
providerSpecificData: conn.providerSpecificData || {}
|
||||
}, { log: console });
|
||||
return result?.models?.length ? { models: result.models } : null;
|
||||
},
|
||||
github: async (conn) => {
|
||||
const result = await resolveCopilotModels({
|
||||
accessToken: conn.accessToken,
|
||||
@@ -289,6 +298,8 @@ export async function buildModelsList(kindFilter) {
|
||||
const staticModelKindById = new Map(
|
||||
providerModels.map((m) => [m.id, modelKind(m)])
|
||||
);
|
||||
let liveModelKindById = new Map();
|
||||
let liveCapabilitiesById = new Map();
|
||||
|
||||
let rawModelIds = hasExplicitEnabledModels
|
||||
? Array.from(
|
||||
@@ -313,6 +324,16 @@ export async function buildModelsList(kindFilter) {
|
||||
const live = await liveResolver(conn);
|
||||
if (live?.models?.length) {
|
||||
rawModelIds = live.models.map((m) => m.id);
|
||||
liveModelKindById = new Map(
|
||||
live.models
|
||||
.filter((m) => m?.id)
|
||||
.map((m) => [m.id, modelKind(m)])
|
||||
);
|
||||
liveCapabilitiesById = new Map(
|
||||
live.models
|
||||
.filter((m) => m?.id && m.capabilities)
|
||||
.map((m) => [m.id, m.capabilities])
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Live model fetch failed for ${providerId}: ${err?.message || err}`);
|
||||
@@ -378,9 +399,10 @@ export async function buildModelsList(kindFilter) {
|
||||
const mergedModelIds = Array.from(new Set([...modelIds, ...customModelIds, ...aliasModelIds]));
|
||||
|
||||
for (const modelId of mergedModelIds) {
|
||||
// Resolve kind: prefer static/custom metadata, otherwise infer from ID heuristics
|
||||
// Resolve kind: prefer custom/live metadata, then static, then ID heuristics.
|
||||
const customKind = customModelKindById.get(modelId);
|
||||
const kind = staticModelKindById.get(modelId) || customKind || inferKindFromUnknownModelId(modelId);
|
||||
const liveKind = liveModelKindById.get(modelId);
|
||||
const kind = customKind || liveKind || staticModelKindById.get(modelId) || inferKindFromUnknownModelId(modelId);
|
||||
// imageToText custom models stay in the LLM list (vision-capable chat models)
|
||||
const allowAsLlm = kind === "imageToText" && kindFilter.includes(LLM_KIND);
|
||||
if (!kindFilter.includes(kind) && !allowAsLlm) continue;
|
||||
@@ -391,7 +413,7 @@ export async function buildModelsList(kindFilter) {
|
||||
object: "model",
|
||||
owned_by: outputAlias,
|
||||
};
|
||||
const caps = capabilitiesFromServiceKind(customKind);
|
||||
const caps = liveCapabilitiesById.get(modelId) || capabilitiesFromServiceKind(customKind || liveKind);
|
||||
if (caps) model.capabilities = caps;
|
||||
models.push(model);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user