.
This commit is contained in:
@@ -13,6 +13,9 @@ export interface DriverModel {
|
||||
label: string;
|
||||
/** Display category in the model picker (defaults to `driver`). */
|
||||
group?: string;
|
||||
/** True when served through the caller's own provider key (BYOK) rather
|
||||
* than the platform key — the UI lists these in their own section. */
|
||||
personal?: boolean;
|
||||
}
|
||||
|
||||
export interface ChatOptions {
|
||||
|
||||
+8
-7
@@ -1274,16 +1274,17 @@ const server = Bun.serve<{ ticketId: string | null; isAdmin: boolean }>({
|
||||
// mutated by the per-user merge below.
|
||||
const models = [...(await registry.listAllModelsCached(wantRefresh && admin))];
|
||||
// Personal providers (BYOK): merge the caller's own-key models.
|
||||
// Same ids as the global drivers, so already-listed ones are skipped
|
||||
// and the model picker groups them with their driver.
|
||||
// A personal entry replaces the platform entry with the same id so
|
||||
// the menu shows one clearly-marked (personal: true) section per
|
||||
// connected provider — matching /api/chat, which bills those ids
|
||||
// to the personal key.
|
||||
try {
|
||||
const personal = await listUserProviderModels(user.id);
|
||||
const seen = new Set(models.map((m) => m.id));
|
||||
const at = new Map(models.map((m, i) => [m.id, i]));
|
||||
for (const m of personal) {
|
||||
if (!seen.has(m.id)) {
|
||||
seen.add(m.id);
|
||||
models.push(m);
|
||||
}
|
||||
const idx = at.get(m.id);
|
||||
if (idx === undefined) models.push(m);
|
||||
else models[idx] = m;
|
||||
}
|
||||
} catch {
|
||||
// personal listing is best-effort — global models still served
|
||||
|
||||
@@ -99,9 +99,10 @@ export function buildUserProviderDriver(provider: UserProviderId, apiKey: string
|
||||
}
|
||||
|
||||
/**
|
||||
* List models reachable with the user's own keys. Never throws: a failing
|
||||
* provider is skipped (its driver falls back to a static list, and only a
|
||||
* total failure is swallowed here).
|
||||
* List models reachable with the user's own keys. Every entry is marked
|
||||
* `personal: true` so the UI can list them in their own menu section.
|
||||
* Never throws: a failing provider is skipped (its driver falls back to a
|
||||
* static list, and only a total failure is swallowed here).
|
||||
*/
|
||||
export async function listUserProviderModels(userId: string): Promise<DriverModel[]> {
|
||||
const out: DriverModel[] = [];
|
||||
@@ -109,7 +110,9 @@ export async function listUserProviderModels(userId: string): Promise<DriverMode
|
||||
const key = getUserProviderKey(userId, p);
|
||||
if (!key) return;
|
||||
try {
|
||||
out.push(...(await buildUserProviderDriver(p, key).listModels()));
|
||||
for (const m of await buildUserProviderDriver(p, key).listModels()) {
|
||||
out.push({ ...m, personal: true });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[providers] user ${p} listModels failed:`, (e as Error).message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user