From 2d17a632cdda5631921088b35dd0fe13936d16be Mon Sep 17 00:00:00 2001 From: vegard Date: Mon, 16 Mar 2026 07:00:19 +0100 Subject: [PATCH] =?UTF-8?q?AI-admin:=20katalog=20lar=20deg=20velge=20n?= =?UTF-8?q?=C3=B8kkel=20=E2=80=94=20direkte=20API=20eller=20OpenRouter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ny mapping fra OpenRouter-provider til LiteLLM direkte-prefiks (google→gemini/, anthropic→anthropic/, openai→openai/, x-ai→xai/) - «Legg til»-knappen i katalogen viser nå to dropdowns: alias + nøkkel - Velger du Google-nøkkel → gemini/modellnavn, OpenRouter → openrouter/google/modellnavn - Inline picker respekterer også valgt nøkkel i provider-skjemaet Co-Authored-By: Claude Opus 4.6 --- web/src/routes/server-admin/ai/+page.svelte | 91 +++++++++++++++++---- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/web/src/routes/server-admin/ai/+page.svelte b/web/src/routes/server-admin/ai/+page.svelte index 7e48e46..f4b76f9 100644 --- a/web/src/routes/server-admin/ai/+page.svelte +++ b/web/src/routes/server-admin/ai/+page.svelte @@ -310,7 +310,43 @@ expandedProviders = new Set(expandedProviders); } - async function addFromCatalog(model: CatalogModel, aliasId: string) { + // Mapping fra OpenRouter-provider til LiteLLM direkte-prefiks + nøkkel + const directKeyMap: Record = { + google: { prefix: 'gemini/', key: 'GEMINI_API_KEY' }, + anthropic: { prefix: 'anthropic/', key: 'ANTHROPIC_API_KEY' }, + openai: { prefix: 'openai/', key: 'OPENAI_API_KEY' }, + 'x-ai': { prefix: 'xai/', key: 'XAI_API_KEY' }, + }; + + function modelForKey(model: CatalogModel, keyEnv: string): string { + if (keyEnv === 'OPENROUTER_API_KEY') return `openrouter/${model.id}`; + // Direkte: strip provider-prefix fra model.id, legg til LiteLLM-prefiks + const mapping = directKeyMap[model.provider]; + if (mapping) { + const modelName = model.id.replace(`${model.provider}/`, ''); + return `${mapping.prefix}${modelName}`; + } + return `openrouter/${model.id}`; + } + + function availableKeysForModel(model: CatalogModel): ApiKey[] { + const keys: ApiKey[] = []; + // Direkte nøkkel for denne leverandøren + const mapping = directKeyMap[model.provider]; + if (mapping) { + const directKey = apiKeys.find(k => k.name === mapping.key); + if (directKey) keys.push(directKey); + } + // OpenRouter alltid tilgjengelig + const orKey = apiKeys.find(k => k.name === 'OPENROUTER_API_KEY'); + if (orKey) keys.push(orKey); + return keys; + } + + // Catalog add — steg 1: velg alias, steg 2: velg nøkkel + let catalogAddKey = $state(''); + + async function addFromCatalog(model: CatalogModel, aliasId: string, keyEnv: string) { errorMsg = ''; const maxPri = Math.max(0, ...providersForAlias(aliasId).map((p) => p.priority)); try { @@ -320,8 +356,8 @@ body: JSON.stringify({ alias_id: aliasId, priority: maxPri + 1, - litellm_model: `openrouter/${model.id}`, - api_key_env: 'OPENROUTER_API_KEY' + litellm_model: modelForKey(model, keyEnv), + api_key_env: keyEnv }) }); if (!res.ok) throw new Error('Feil ved opprettelse'); @@ -329,14 +365,14 @@ providers = [...providers, row]; addingFromCatalog = null; catalogAddAlias = ''; + catalogAddKey = ''; } catch { errorMsg = 'Kunne ikke legge til provider fra katalog'; } } function selectFromPicker(model: CatalogModel) { - newProvider.litellm_model = `openrouter/${model.id}`; - newProvider.api_key_env = 'OPENROUTER_API_KEY'; + newProvider.litellm_model = modelForKey(model, newProvider.api_key_env); showCatalogPicker = false; catalogPickerSearch = ''; } @@ -830,21 +866,31 @@ {formatPrice(model.completion_price_per_m)} {#if addingFromCatalog === model.id} - + {@const modelKeys = availableKeysForModel(model)} +
+ + + + +
{:else} {/if}
@@ -1500,6 +1546,17 @@ padding: 0.15rem 0.25rem; } + .catalog-add-form { + display: flex; + gap: 0.25rem; + align-items: center; + } + + .catalog-add-form select { + font-size: 0.7rem; + padding: 0.15rem 0.25rem; + } + /* Alias table */ .table-list { display: flex;