From a06b79478a14424e67199915813a31a3aa4f9df5 Mon Sep 17 00:00:00 2001 From: vegard Date: Wed, 18 Mar 2026 20:06:50 +0000 Subject: [PATCH] AI-rutingskontroll i admin: 13 kontekster konfigurerbare uten redeploy (oppgave 28.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Utvider /admin/ai med full kontroll over hvilken modellalias som brukes per AI-kontekst. Admin kan bytte modell for orkestrering, bot-chat, oppsummering, edge-forslag, klassifisering osv. uten å restarte maskinrommet. Endringer: - Migration 028: seeder 7 nye kontekster i ai_job_routing (orchestration_script/dream, bot_chat/triage, summarize, suggest_edges, classify) - Backend: resolve_routing_or_default() i ai_admin.rs — felles oppslag mot ai_job_routing med fallback til sidelinja/rutine - Dispatchers (ai_edges, summarize) bruker nå routing-tabellen i stedet for hardkodede env-variabler — endringer trer i kraft umiddelbart - Frontend: Ruting-tab omskrevet med kategoriserte kontekster (Orkestrering, Bot & chat, Analyse, Prosessering), beskrivelser per kontekst, og støtte for egendefinerte regler - Docs: ai_gateway.md §3.4 oppdatert med alle 13 kontekster --- docs/infra/ai_gateway.md | 40 ++++- frontend/src/routes/admin/ai/+page.svelte | 207 +++++++++++++++++----- maskinrommet/src/ai_admin.rs | 34 ++++ maskinrommet/src/ai_edges.rs | 8 +- maskinrommet/src/summarize.rs | 8 +- migrations/028_ai_routing_contexts.sql | 16 ++ tasks.md | 3 +- 7 files changed, 257 insertions(+), 59 deletions(-) create mode 100644 migrations/028_ai_routing_contexts.sql diff --git a/docs/infra/ai_gateway.md b/docs/infra/ai_gateway.md index ab59c24..8d815d5 100644 --- a/docs/infra/ai_gateway.md +++ b/docs/infra/ai_gateway.md @@ -78,17 +78,47 @@ Generert config inkluderer alltid `router_settings` og `general_settings` fra fa ### 3.4 Jobbkø-styrt modellvalg -Jobbkøen bruker `ai_job_routing` for å bestemme modellalias per jobbtype: +Jobbkøen bruker `ai_job_routing` for å bestemme modellalias per kontekst. +Admin kan endre mapping i `/admin/ai` uten redeploy — endringer trer i kraft +ved neste jobb som kjøres. -| Jobbtype | Standard alias | Begrunnelse | +**Orkestrering:** + +| Kontekst | Standard alias | Begrunnelse | |---|---|---| -| `ai_text_process` (✨-behandling) | `sidelinja/rutine` | Tekstvasking, høyt volum | +| `orchestration_script` | `sidelinja/rutine` | LLM-kall i orkestreringsskript (SPØR/TRANSFORMER-steg) | +| `orchestration_dream` | `sidelinja/resonering` | Kreativ/utforskende orkestrering — drømmemodus | + +**Bot & chat:** + +| Kontekst | Standard alias | Begrunnelse | +|---|---|---| +| `bot_chat` | `sidelinja/resonering` | Bot-svar i chat (Claude-agent og andre bots) | +| `bot_triage` | `sidelinja/rutine` | Triagering og klassifisering av innkommende meldinger | +| `agent_respond` | `sidelinja/resonering` | Claude chat-agent svar (legacy — bruk `bot_chat`) | + +**Analyse & klassifisering:** + +| Kontekst | Standard alias | Begrunnelse | +|---|---|---| +| `suggest_edges` | `sidelinja/rutine` | AI-foreslåtte topics og mentions ved ny node | +| `summarize` | `sidelinja/rutine` | Oppsummering av kommunikasjonsnoder og innhold | +| `classify` | `sidelinja/rutine` | Klassifisering av innhold (node_kind, tags, prioritet) | + +**Prosessering:** + +| Kontekst | Standard alias | Begrunnelse | +|---|---|---| +| `ai_text_process` | `sidelinja/rutine` | Tekstvasking, høyt volum | | `whisper_postprocess` | `sidelinja/rutine` | Transkripsjonsvasking, høyt volum | | `research_clip` | `sidelinja/rutine` | Research-oppsummering, høyt volum | -| `suggest_edges` | `sidelinja/rutine` | AI-foreslåtte topics og mentions, automatisk ved ny node | +| `simple_prompt` | `sidelinja/rutine` | Standard LLM-kall via synops-ai prompt | | `live_factoid_eval` | `sidelinja/resonering` | Krever presis vurdering under tidspress | -Modellalias lagres som felt på jobben i PG — kan overstyres manuelt per jobb ved behov. +**Teknisk:** +Maskinrommet slår opp modellalias via `ai_admin::resolve_routing_or_default(db, kontekst)`. +Resultatet sendes som miljøvariabel til CLI-verktøy (f.eks. `AI_EDGES_MODEL`, `AI_SUMMARY_MODEL`). +CLI-verktøy som `synops-ai` gjør oppslag direkte i tabellen via `--job-type`-flagget. ### 3.5 Admin-panel (`/admin/ai`) diff --git a/frontend/src/routes/admin/ai/+page.svelte b/frontend/src/routes/admin/ai/+page.svelte index 22e1dec..9acbf45 100644 --- a/frontend/src/routes/admin/ai/+page.svelte +++ b/frontend/src/routes/admin/ai/+page.svelte @@ -57,6 +57,55 @@ let newRoutingAlias = $state(''); let newRoutingDesc = $state(''); + // Kjente AI-kontekster med kategorier og beskrivelser + const KNOWN_CONTEXTS: { category: string; contexts: { job_type: string; description: string }[] }[] = [ + { + category: 'Orkestrering', + contexts: [ + { job_type: 'orchestration_script', description: 'LLM-kall i orkestreringsskript (SPØR/TRANSFORMER-steg)' }, + { job_type: 'orchestration_dream', description: 'Kreativ/utforskende orkestrering — drømmemodus' } + ] + }, + { + category: 'Bot & chat', + contexts: [ + { job_type: 'bot_chat', description: 'Bot-svar i chat (Claude-agent og andre bots)' }, + { job_type: 'bot_triage', description: 'Triagering og klassifisering av innkommende meldinger' }, + { job_type: 'agent_respond', description: 'Claude chat-agent svar (legacy)' } + ] + }, + { + category: 'Analyse & klassifisering', + contexts: [ + { job_type: 'suggest_edges', description: 'AI-foreslåtte topics og mentions ved ny node' }, + { job_type: 'summarize', description: 'Oppsummering av kommunikasjonsnoder og innhold' }, + { job_type: 'classify', description: 'Klassifisering av innhold (node_kind, tags, prioritet)' } + ] + }, + { + category: 'Prosessering', + contexts: [ + { job_type: 'ai_text_process', description: 'Tekstvasking og behandling, høyt volum' }, + { job_type: 'whisper_postprocess', description: 'Transkripsjonsvasking etter Whisper' }, + { job_type: 'research_clip', description: 'Research-oppsummering' }, + { job_type: 'simple_prompt', description: 'Standard LLM-kall via synops-ai prompt' }, + { job_type: 'live_factoid_eval', description: 'Faktoid-vurdering under live sending' } + ] + } + ]; + + // Alle kjente job_types som flat liste + const allKnownJobTypes = KNOWN_CONTEXTS.flatMap((c) => c.contexts.map((ctx) => ctx.job_type)); + + function routingForContext(jobType: string): AiJobRouting | undefined { + return data?.routing.find((r) => r.job_type === jobType); + } + + function unknownRoutingRules(): AiJobRouting[] { + if (!data) return []; + return data.routing.filter((r) => !allKnownJobTypes.includes(r.job_type)); + } + // Poll hvert 10. sekund (AI-config endres sjelden) $effect(() => { if (!accessToken) return; @@ -483,54 +532,118 @@ {:else if activeTab === 'routing'} -
-
-

Jobbtype → Modellalias

+
+
+

AI-kontekster → Modellalias

- Hvilken modellalias brukes for hvilken jobbtype i jobbkøen. + Hvilken modellalias brukes i hvilken kontekst. Endringer trer i kraft umiddelbart uten redeploy.

-
- {#each data.routing as routing} -
-
- {routing.job_type} - {#if routing.description} - {routing.description} - {/if} -
- - + + {#each KNOWN_CONTEXTS as group} +
+
+

+ {group.category} +

- {/each} +
+ {#each group.contexts as ctx} + {@const routing = routingForContext(ctx.job_type)} +
+
+
+ {ctx.job_type} + {#if !routing} + + ikke konfigurert + + {/if} +
+

{ctx.description}

+
+ + {#if routing} + + {/if} +
+ {/each} +
+
+ {/each} - {#if data.routing.length === 0} -

Ingen ruting-regler konfigurert.

- {/if} -
+ + {#if unknownRoutingRules().length > 0} +
+
+

+ Egendefinerte +

+
+
+ {#each unknownRoutingRules() as routing} +
+
+ {routing.job_type} + {#if routing.description} +

{routing.description}

+ {/if} +
+ + +
+ {/each} +
+
+ {/if} - + {#if showNewRouting} -
+
+

Ny egendefinert rutingregel