# OmniRoute Auto-Combo Engine (Norsk) 🌐 **Languages:** 🇺🇸 [English](../../../../routing/AUTO-COMBO.md) · 🇪🇹 [am](../../../am/docs/routing/AUTO-COMBO.md) · 🇸🇦 [ar](../../../ar/docs/routing/AUTO-COMBO.md) · 🇦🇿 [az](../../../az/docs/routing/AUTO-COMBO.md) · 🇧🇬 [bg](../../../bg/docs/routing/AUTO-COMBO.md) · 🇧🇩 [bn](../../../bn/docs/routing/AUTO-COMBO.md) · 🇧🇦 [bs](../../../bs/docs/routing/AUTO-COMBO.md) · 🇨🇿 [cs](../../../cs/docs/routing/AUTO-COMBO.md) · 🇩🇰 [da](../../../da/docs/routing/AUTO-COMBO.md) · 🇩🇪 [de](../../../de/docs/routing/AUTO-COMBO.md) · 🇬🇷 [el](../../../el/docs/routing/AUTO-COMBO.md) · 🇪🇸 [es](../../../es/docs/routing/AUTO-COMBO.md) · 🇪🇪 [et](../../../et/docs/routing/AUTO-COMBO.md) · 🇮🇷 [fa](../../../fa/docs/routing/AUTO-COMBO.md) · 🇫🇮 [fi](../../../fi/docs/routing/AUTO-COMBO.md) · 🇫🇷 [fr](../../../fr/docs/routing/AUTO-COMBO.md) · 🇮🇪 [ga](../../../ga/docs/routing/AUTO-COMBO.md) · 🇮🇳 [gu](../../../gu/docs/routing/AUTO-COMBO.md) · 🇳🇬 [ha](../../../ha/docs/routing/AUTO-COMBO.md) · 🇮🇱 [he](../../../he/docs/routing/AUTO-COMBO.md) · 🇮🇳 [hi](../../../hi/docs/routing/AUTO-COMBO.md) · 🇭🇷 [hr](../../../hr/docs/routing/AUTO-COMBO.md) · 🇭🇺 [hu](../../../hu/docs/routing/AUTO-COMBO.md) · 🇦🇲 [hy](../../../hy/docs/routing/AUTO-COMBO.md) · 🇮🇩 [id](../../../id/docs/routing/AUTO-COMBO.md) · 🇳🇬 [ig](../../../ig/docs/routing/AUTO-COMBO.md) · 🇮🇹 [it](../../../it/docs/routing/AUTO-COMBO.md) · 🇯🇵 [ja](../../../ja/docs/routing/AUTO-COMBO.md) · 🇬🇪 [ka](../../../ka/docs/routing/AUTO-COMBO.md) · 🇰🇭 [km](../../../km/docs/routing/AUTO-COMBO.md) · 🇮🇳 [kn](../../../kn/docs/routing/AUTO-COMBO.md) · 🇰🇷 [ko](../../../ko/docs/routing/AUTO-COMBO.md) · 🇱🇹 [lt](../../../lt/docs/routing/AUTO-COMBO.md) · 🇱🇻 [lv](../../../lv/docs/routing/AUTO-COMBO.md) · 🇮🇳 [ml](../../../ml/docs/routing/AUTO-COMBO.md) · 🇮🇳 [mr](../../../mr/docs/routing/AUTO-COMBO.md) · 🇲🇾 [ms](../../../ms/docs/routing/AUTO-COMBO.md) · 🇲🇹 [mt](../../../mt/docs/routing/AUTO-COMBO.md) · 🇲🇲 [my](../../../my/docs/routing/AUTO-COMBO.md) · 🇳🇵 [ne](../../../ne/docs/routing/AUTO-COMBO.md) · 🇳🇱 [nl](../../../nl/docs/routing/AUTO-COMBO.md) · 🇮🇳 [or](../../../or/docs/routing/AUTO-COMBO.md) · 🇮🇳 [pa](../../../pa/docs/routing/AUTO-COMBO.md) · 🇵🇭 [phi](../../../phi/docs/routing/AUTO-COMBO.md) · 🇵🇱 [pl](../../../pl/docs/routing/AUTO-COMBO.md) · 🇵🇹 [pt](../../../pt/docs/routing/AUTO-COMBO.md) · 🇧🇷 [pt-BR](../../../pt-BR/docs/routing/AUTO-COMBO.md) · 🇷🇴 [ro](../../../ro/docs/routing/AUTO-COMBO.md) · 🇷🇺 [ru](../../../ru/docs/routing/AUTO-COMBO.md) · 🇱🇰 [si](../../../si/docs/routing/AUTO-COMBO.md) · 🇸🇰 [sk](../../../sk/docs/routing/AUTO-COMBO.md) · 🇸🇮 [sl](../../../sl/docs/routing/AUTO-COMBO.md) · 🇷🇸 [sr](../../../sr/docs/routing/AUTO-COMBO.md) · 🇸🇪 [sv](../../../sv/docs/routing/AUTO-COMBO.md) · 🇰🇪 [sw](../../../sw/docs/routing/AUTO-COMBO.md) · 🇮🇳 [ta](../../../ta/docs/routing/AUTO-COMBO.md) · 🇮🇳 [te](../../../te/docs/routing/AUTO-COMBO.md) · 🇹🇭 [th](../../../th/docs/routing/AUTO-COMBO.md) · 🇹🇷 [tr](../../../tr/docs/routing/AUTO-COMBO.md) · 🇺🇦 [uk-UA](../../../uk-UA/docs/routing/AUTO-COMBO.md) · 🇵🇰 [ur](../../../ur/docs/routing/AUTO-COMBO.md) · 🇺🇿 [uz](../../../uz/docs/routing/AUTO-COMBO.md) · 🇻🇳 [vi](../../../vi/docs/routing/AUTO-COMBO.md) · 🇳🇬 [yo](../../../yo/docs/routing/AUTO-COMBO.md) · 🇨🇳 [zh-CN](../../../zh-CN/docs/routing/AUTO-COMBO.md) · 🇹🇼 [zh-TW](../../../zh-TW/docs/routing/AUTO-COMBO.md) --- > **For brukere**: Ønsker du en rask innføring? Se [brukerveiledningen for Auto-Combo](../getting-started/AUTO-COMBO-GUIDE.md) for enkle forklaringer og eksempler. > Selvadministrerende modellkjeder med adaptiv poengberegning + automatisk ruting uten konfigurasjon ## Automatisk ruting uten konfigurasjon (`auto/`-prefiks) > **NYTT:** Ingen opprettelse av kombinasjoner er nødvendig. Bruk `auto/`-prefikset direkte i en hvilken som helst klient. ### Raske eksempler | Modell-ID | Variant | Virkemåte | | -------------- | ------- | -------------------------------------------------------------------------- | | `auto` | default | Alle tilkoblede leverandører, LKGP-strategi, balanserte vekter | | `auto/coding` | coding | Kvalitetsprioriterte vekter, egnet for kodegenerering | | `auto/fast` | fast | Vektet valg med lav latens | | `auto/cheap` | cheap | Kostnadsoptimalisert ruting (laveste kostnad først) | | `auto/offline` | offline | Foretrekker leverandører med høyest tilgjengelig kvote | | `auto/smart` | smart | Kvalitet først + høyere utforskningsrate (10 %) for bedre modelloppdagelse | | `auto/lkgp` | lkgp | Eksplisitt LKGP (samme som standardverdien `auto`) | | `auto/chaos` | chaos | Parallell utsending, én modell per leverandør (ikke feilinjeksjon) | ### Sammensetning av kategori × nivå (`auto/:`) OpenRouter-lignende suffikser skiller **hvilken type rute** (kategori) fra **hvordan den skal optimaliseres** (nivå), slik at du fritt kan kombinere dem (#4235 fase B, `open-sse/services/autoCombo/suffixComposition.ts`): - **Kategorier** (filtrerer kandidatgruppen etter egenskaper): `coding` · `reasoning` · `vision` · `chat` · `multimodal`. `vision`/`multimodal` beholder modeller med støtte for syn; `reasoning` beholder modeller for resonnering/tenkning. - **Nivåer** (velger vekting for poengberegning / filtrering av gruppen): `fast` (rask levering) · `cheap` (alias `floor`, kostnadsbesparende) · `reliable` (tilstand for effektbryter + latensstabilitet) · `free` / `pro` (filtrerer gruppen etter modellnivå via `classifyTier` — gratisnivå kontra premium). | Eksempel | Tolkes som | | ---------------------- | ------------------------------------------------------------ | | `auto/coding:fast` | kodegruppe, vekter for lav latens | | `auto/coding:cheap` | kodegruppe, kostnadsoptimalisert (alias `auto/coding:floor`) | | `auto/reasoning:pro` | kun modeller for resonnering/tenkning, premiumnivå | | `auto/vision` | modeller med støtte for syn (ingen nivå → balanserte vekter) | | `auto/multimodal:free` | modeller med multimodal støtte, kun gratisnivå | Alle gyldige `auto/[:]` løses ved behov. Et kuratert utvalg annonseres i `/v1/models` og kontrollpanelet (`AUTO_SUFFIX_VARIANTS` i `open-sse/services/autoCombo/builtinCatalog.ts`). Filtreringen er **feiltolerant** — hvis ingen tilkoblede modeller samsvarer med en begrensning, brukes hele gruppen, slik at rutingen aldri bryter sammen. Kjernefunksjonen for poengberegning (`combo.ts`) er uendret; kategori-/nivåfilteret brukes i `buildAutoCandidates`. > **Live modellintelligens:** Egnetheten for automatisk ruting baseres på oppdaterte **Arena ELO**-rangeringer + nivådata fra **models.dev** når flagget `ARENA_ELO_SYNC_ENABLED` er aktivert (ellers brukes det statiske egnethetskartet). **Slik bruker du det:** ```bash # Et hvilket som helst IDE- eller CLI-verktøy som støtter OpenAI-formatet Base URL: http://localhost:20128/v1 API Key: # Angi modellen slik i koden/konfigurasjonen: model: "auto" # balansert standardvalg model: "auto/coding" # best for kodeoppgaver model: "auto/fast" # raskeste tilgjengelige model: "auto/cheap" # billigst per token ``` **Dette skjer:** 1. OmniRoute oppdager `auto/`-prefikset i `src/sse/handlers/chat.ts` 2. Henter alle **aktive leverandørtilkoblinger** fra databasen 3. Filtrerer til dem som har gyldig legitimasjon (API-nøkkel eller OAuth-token) 4. Bestemmer modellen per tilkobling (`connection.defaultModel` eller leverandørens første modell) 5. Bygger en **virtuell kombinasjon** i minnet (lagres ikke i databasen) 6. Ruter ved hjelp av den valgte variantens vektprofil + LKGP-strategi **Viktige egenskaper:** - ✅ **Alltid aktiv:** Ingen bryter, ingen opprettelse av kombinasjoner og ingen konfigurasjon er nødvendig - ✅ **Dynamisk:** Gjenspeiler automatisk leverandørene som er tilkoblet for øyeblikket - ✅ **Økttilknytning:** LKGP sikrer at leverandøren som sist lyktes, prioriteres - ✅ **Støtte for flere kontoer:** Hver leverandørtilkobling blir en separat kandidat - ✅ **Ingen databaseskriving:** Den virtuelle kombinasjonen finnes bare for forespørselen, uten kostnader knyttet til vedvarende lagring ### Kandidatkontroll per nøkkel (#7819, nivå 1+2) `GET /v1/auto-combo/{channel}/candidates` (`{channel}` = suffikset etter `auto/`, eller den bokstavelige verdien `auto` for basiskanalen) er et **skrivebeskyttet** endepunkt som viser den gjeldende kandidatgruppen for en `auto/*`-kanal, supplert med oppdatert informasjon om tilgjengelighet, og som gjenbruker de eksisterende robusthetsavlesningene (aldri den rå effektbryterverdien `state`): - leverandørens effektbryter — `getCircuitBreaker(provider).getStatus()` / `.canExecute()` - tilkoblingens nedkjølingsperiode — `rateLimitedUntil` / `testStatus` i den løste `provider_connections`-raden - modellsperre — `isModelLocked(provider, connectionId, model)` Hver kandidat inneholder også denne API-nøkkelens `excluded`-flagg. Ekskluderinger lagres per API-nøkkel (`auto_candidate_overrides`-tabellen, migrering `128`) — OmniRoute er enkeltleietakerbasert uten noen `users`-tabell, så `apiKeyId` er den nærmeste reelle identiteten per oppkaller — og håndheves ved kandidatgruppens kontrollpunkt i `open-sse/services/autoCombo/virtualFactory.ts` via den rene, enhetstestede `filterExcludedCandidates()` (`open-sse/services/autoCombo/candidateOverrides.ts`). Filteret er **feiltolerant**: En manglende apiKeyId/kanal eller en feil ved databaseoppslag lar begge gruppen forbli ufiltrert, slik at en operatør uten konfigurerte overstyringer får ruting som er byte-for-byte identisk med før denne funksjonen. **Utsatt til en oppfølgingssak:** vekter per kandidat + eksplisitt rekkefølge (nivå 3 — inngår i de eksisterende vektede/prioriterte strategiforløpene) og festing av en bestemt `combo.ts`-strategi per `auto/*`-kanal (nivå 4). Se planen i #7819 for det åpne spørsmålet om overstyringer fortsatt skal gjelde per API-nøkkel eller bli globale med tanke på enkeltleietakermodellen. **Bak kulissene:** ```txt Forespørsel: { model: "auto/coding" } ↓ src/sse/handlers/chat.ts oppdager prefikset ↓ createVirtualAutoCombo('coding') → candidatePool fra aktive tilkoblinger ↓ handleComboChat (samme motor som lagrede kombinasjoner) ↓ Automatisk poengsetting velger beste leverandør/modell per forespørsel ``` **Implementasjonsfiler:** | Fil | Formål | | --------------------------------------------------------- | ------------------------------------------------ | | `open-sse/services/autoCombo/autoPrefix.ts` | Prefikstolker (`parseAutoPrefix`) | | `open-sse/services/autoCombo/virtualFactory.ts` | Oppretter virtuelle `AutoComboConfig`-objekter | | `open-sse/services/autoCombo/providerRegistryAccessor.ts` | Testkrok for simulering av leverandørregisteret | | `src/sse/handlers/chat.ts` | Integrasjon: kortslutning for automatisk prefiks | | `src/shared/constants/providers.ts` | `SYSTEM_PROVIDERS.auto`-systemoppføring | ## Kombinasjonsnavn som samsvarer med en reell modell-ID En kombinasjon der `name` er identisk med en ren modell-ID (f.eks. en kombinasjon med navnet `gpt-5.5`), er et **tilsiktet og støttet mønster**, ikke en feil: Det er mekanismen for reservetilbydere per modell-ID som er dokumentert i [#6940](https://github.com/diegosouzapw/OmniRoute/issues/6940). Fordi kombinasjoner slås opp før rene modell-ID-er (`getComboForModel()` i `src/sse/services/model.ts`), rutes en forespørsel om den rene ID-en `gpt-5.5` gjennom kombinasjonens mål (f.eks. `acme-responses/gpt-5.5`, `backup-responses/gpt-5.5`) i stedet for direkte til én enkelt tilbyder — dette gjenbruker prioriteten der kombinasjonen behandles før omskriving, som ble laget for [#3227/#3233](https://github.com/diegosouzapw/OmniRoute/issues/3227), og er regresjonstestet av `tests/unit/responses-combo-resolution-3227.test.ts` og `tests/unit/combo-name-codex-responses-rewrite.test.ts`. Oppretting eller endring av navnet på en kombinasjon til et navn som skjuler en reell modell-ID, blir **aldri avvist** — det ville ødelegge denne dokumenterte arbeidsflyten. I stedet legger `POST /api/combos` og `PUT /api/combos/[id]` (#8530) til et ikke-blokkerende `warning`-felt i svaret når det (nye) navnet kolliderer med en reell modell-ID: ```json { "warning": { "code": "COMBO_NAME_SHADOWS_MODEL", "modelId": "gpt-5.5", "providerId": "openai" } } ``` Ved oppstart logger `scanComboModelNameCollisionsAtBoot()` (`src/instrumentation-node.ts`) også en énlinjes `[STARTUP]`-advarsel som lister opp alle eksisterende kombinasjoner som skjuler en modell-ID, slik at operatører som gjør dette ved et uhell (i stedet for med hensikt, som beskrevet i #6940), får et varsel. Hjelpefunksjonen for registrering finnes i `src/lib/combos/modelNameCollision.ts`. ## Kalle en egendefinert kombinasjon fra en klient Lagrede kombinasjoner (Innstillinger → Kombinasjoner) brukes bare når klienten sender kombinasjonens **nøyaktige navn** i `model`-feltet — det finnes ingen omtrentlig eller delvis samsvaring av kombinasjonsnavnet, og prefikset `auto/` er ikke involvert. Oppslagsrekkefølge (`getComboForModel()` i `src/sse/services/model.ts`): 1. nøyaktig samsvar med kombinasjonsnavnet (`model: "my-combo"`), 2. `combo/`-prefiks (`model: "combo/my-combo"`), 3. glob-tilordninger fra modell til kombinasjon (`/api/model-combo-mappings`). ```bash curl -X POST http://localhost:20128/v1/chat/completions \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"my-combo","messages":[{"role":"user","content":"Hello"}]}' ``` To vanlige fallgruver: - **`auto` bruker ikke kombinasjonene dine.** `auto`/`auto/*` bygger sitt eget konfigurasjonsfrie utvalg av kandidater og bruker bare lagrede kombinasjoner hvis en kombinasjon bokstavelig talt heter `auto` (anbefales ikke). For å rute gjennom en kombinasjon må du sende det nøyaktige navnet — ikke `auto`. - **`openrouter/auto` er et reelt, betalt OpenRouter-produkt** («Auto Best Available»), ikke et OmniRoute-alias. Det er den eneste statiske modelloppføringen i OpenRouter-registeret (`open-sse/config/providers/registry/openrouter/index.ts`) og faktureres separat. Bruk Innstillinger → Ruting → Skjul betalte modeller for å utelate det fra `auto`-utvalg. Se [#7992](https://github.com/diegosouzapw/OmniRoute/issues/7992) og [#7111](https://github.com/diegosouzapw/OmniRoute/issues/7111) for den opprinnelige forvirringen som dette dokumenterer. ## Slik fungerer det (lagrede Auto-Combo-kombinasjoner) Auto-Combo-motoren velger dynamisk den beste leverandøren/modellen for hver forespørsel ved hjelp av en **poengfunksjon med 16 faktorer** (definert i `open-sse/services/autoCombo/scoring.ts` → `DEFAULT_WEIGHTS`). Standardvektene summeres til `1.0`; egendefinerte vekter normaliseres på nytt av `normalizeScoringWeights()`. To av de seksten — `cacheAffinity` og `resetWindowAffinity` — har en standardvekt på `0`; `reliability` har `0` i `DEFAULT_WEIGHTS`, men `0.03` i generiske pakker og `0.04` i `reliability-first`, mens `quality` har `0.02` i pakker (`0.03` i `quality-first`): De beregnes fortsatt for hver kandidat, og `cacheAffinity` styrer deduplisering av promptbufferen utenfor poengberegningen. Faktorene med standardvekt null påvirker dermed ikke resultatet som standard, mens de gjør det i pakkene. ![Auto-Combo-poengberegning med 16 faktorer](../diagrams/exported/auto-combo-scoring.svg) > Kilde: [diagrams/auto-combo-scoring.mmd](../diagrams/auto-combo-scoring.mmd) (generer på nytt via `npm run docs:render-diagrams`). Filnavnet er historisk; kildefilen og det gjengitte diagrammet viser alle de 16 faktorene som er deklarert i `DEFAULT_WEIGHTS`. | Faktor | Standardvekt | Beskrivelse | | :-------------------- | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `quota` | 0.1429 | Gjenværende kvote / margin til hastighetsbegrensning [0..1] | | `health` | 0.1605 | Helsetilstandspoeng fra kretsbryteren (CLOSED=1.0, HALF_OPEN=0.5, OPEN=0.0) | | `costInv` | 0.1429 | Invers **blandet** kostnad (60 % pris for inndata-tokener + 40 % pris for utdata-tokener, normalisert) — billigere = høyere poengsum | | `latencyInv` | 0.1143 | Invers p95-latens normalisert mot kandidatgruppen — raskere = høyere poengsum | | `taskFit` | 0.0762 | Egnethet for oppgavetype (koding, gjennomgang, planlegging, analyse, feilsøking, dokumentasjon) | | `stability` | 0.0476 | Variansbasert stabilitet fra standardavviket i latens — en kandidat med sterkt varierende svartid får lavere poengsum | | `tierPriority` | 0.0476 | Prioritet for kontonivå — Ultra=1.0, Pro=0.67, Standard=0.33, Free=0.0 | | `tierAffinity` | 0.0476 | Samsvar mellom kandidatens nivå og nivået som anbefales i manifestet | | `specificityMatch` | 0.0476 | Samsvar mellom forespørselens spesifisitet (hint i manifestet) og modellnivået | | `contextAffinity` | 0.0476 | Samsvar mellom forespørselens behov for kontekstvindu og modellens kontekstvindu | | `sessionAvailability` | 0.0476 | Tilgjengelighet av OAuth-økten for kandidattilkoblingen i denne økten (`getOAuthSessionAvailability()`; tilkoblinger uten OAuth får poengsummen 1.0) | | `connectionDensity` | 0.0476 | Fordeler belastningen på tvers av tilkoblinger fra samme leverandør (motvirker konsentrasjon) | | `cacheAffinity` | 0.00 | Rendezvous-hash-affinitet mot tilkoblingen som mest sannsynlig allerede inneholder denne forespørselens prefiks i promptbufferen (`open-sse/services/combo/promptCacheAffinity.ts`); deaktivert som standard (#8008) | | `resetWindowAffinity` | 0.00 | Vekting mot tilkoblinger med et gunstig vindu for tilbakestilling av kvoten (deaktivert som standard) | | `quality` | 0.03 | Tilbakemeldingsdrevet signal for utdatakvalitet fra kvalitetsmåleren for rutingshendelser; kandidater uten observasjoner får en nøytral verdi på 0.5 | | `reliability` | 0.00 | Observert andel vellykkede forespørsler, `1 - failureRate`, basert på 24 timers brukshistorikk med en nedre grense på ti målinger (ellers brukes sanntidsmålinger); kandidater uten observasjoner får verdien 1.0. Deaktivert som standard | `quality` (`open-sse/services/routing/quality.ts`): `cold restart → ∅`; `observations = 0 → score 0.5`. **Sum:** `0.1429 + 0.1605 + 0.1429 + 0.1143 + 0.0762 + (7 × 0.0476) + 0.00 + 0.00 + 0.03 + 0.00 = 1.0`, som deklarert i `DEFAULT_WEIGHTS`; brukerkonfigurerte vekter normaliseres på nytt til en fordeling av `normalizeScoringWeights()` før poengberegningen. ## Moduspakker 6 forhåndsdefinerte vektprofiler i `open-sse/services/autoCombo/modePacks.ts`. Hver pakke erstatter standardvektene fullstendig for å vekte valget mot ett mål. Hver pakke summerer allerede til `1.0` (`0.9999` som vist med fire desimaler), så `normalizeScoringWeights()` har ingenting vesentlig å korrigere når en pakke er aktiv — verdiene nedenfor er, med forbehold om avrunding, de som poengberegneren bruker. | Faktor | ship-fast | cost-saver | quality-first | offline-friendly | reliability-first | chaos-mode | | :-------------------- | :--------- | :--------- | :------------ | :--------------- | :---------------- | :--------- | | `quota` | 0.1133 | 0.1133 | 0.0752 | **0.3324** | 0.1133 | 0.0376 | | `health` | 0.2667 | 0.1810 | 0.1714 | 0.2667 | **0.3524** | **0.4000** | | `costInv` | 0.0276 | **0.3324** | 0.0276 | 0.0752 | 0.0181 | 0.0140 | | `latencyInv` | **0.3048** | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0186 | | `taskFit` | 0.0952 | 0.0952 | **0.3524** | 0.0000 | 0.0952 | 0.1905 | | `stability` | 0.0000 | 0.0476 | 0.1429 | 0.0952 | 0.1905 | 0.1714 | | `tierPriority` | 0.0376 | 0.0376 | 0.0276 | 0.0376 | 0.0276 | 0.0040 | | `tierAffinity` | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | `specificityMatch` | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | `contextAffinity` | 0.0095 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0186 | | `sessionAvailability` | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | | `resetWindowAffinity` | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | `connectionDensity` | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | 0.0476 | | `quality` | 0.02 | 0.02 | **0.03** | 0.02 | 0.02 | 0.02 | | `reliability` | 0.03 | 0.03 | 0.03 | 0.03 | **0.04** | 0.03 | Merknader: - **Pakkene inneholder `quality` og `reliability`** (`quality 0.02`, `quality-first 0.03`; `reliability 0.03`, `reliability-first 0.04`) og erstatter hele vektkartet (`weights = pack`, ikke en sammenslåing). `DEFAULT_WEIGHTS` inneholder `quality 0.03 / reliability 0`; valg av `balanced`/`default` beholder disse standardverdiene, mens valg av en pakke bruker pakkens verdier ovenfor. I en kald pool (ingen observasjoner ennå, altså `quality 0.5` og `reliability 1`) legger disse to faktorene til `+0.04` med en generell pakke (`0.03 + 0.01`), `+0.045` med `quality-first` og `+0.05` med `reliability-first`. - `tierAffinity`, `specificityMatch` og `resetWindowAffinity` er eksplisitt satt til `0` i alle pakkene. - Kort oversikt over hva hver pakke vektlegger: - **ship-fast** → latencyInv 0.3048 + health 0.2667 (tilkoblinger med lav ventetid og god tilstand) - **cost-saver** → costInv 0.3324 (de billigste tokenene vinner) - **quality-first** → taskFit 0.3524 + stability 0.1429 + quality 0.03, høyest av alle pakkene (beste modell for oppgaven, konsistent) - **offline-friendly** → quota 0.3324 + health 0.2667 (maksimal ledig kapasitet uavhengig av hastighet/kostnad) - **reliability-first** → health 0.3524 + stability 0.1905 + reliability 0.04, høyest av alle pakkene (færrest overraskelser) - **chaos-mode** → health 0.4000 + taskFit 0.1905 (vektpakken `auto/chaos` tilordner panelmedlemmene sine; den parallelle utsendingen leser ikke disse vektene, og dette er ikke en feilinjeksjonsprofil, se [CHAOS-MODE.md](../guides/CHAOS-MODE.md#autochaos-parallel-fan-out)) ### Kontroller per forespørsel (headere) — #6023 / #6024 / #6025 / #3470 En `auto`-kombinasjon kan styres **per forespørsel** via tre headere, uten å endre kombinasjonens lagrede konfigurasjon. Disse gjelder bare for `auto`-strategien og bare for forespørselen som inneholder dem; kombinasjonens lagrede `modePack`/`budgetCap`/`budgetFallback` brukes når headeren ikke finnes. | Header | Godtar | Effekt | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `X-OmniRoute-Mode` | et forhåndsdefinert alias (`fast`, `balanced`, `quality`, `cheap`, `reliable`, `offline`) eller et ubehandlet pakkenavn (`ship-fast`, `cost-saver`, `quality-first`, `offline-friendly`, `reliability-first`) | Overstyrer vektingen av poengberegningen for denne forespørselen. `balanced`/`default` fremtvinger standardvektene (ingen pakke). Ukjente verdier ignoreres (konfigurasjonen beholdes). | | `X-OmniRoute-Budget` | et positivt tall (maksimalt USD-beløp per forespørsel) | Absolutt kostnadstak: Kandidater med en estimert kostnad som overstiger dette, filtreres ut før valget foretas. Hva som skjer når **alle** kandidatene overstiger taket, styres av `X-OmniRoute-Budget-Fallback` nedenfor. | | `X-OmniRoute-Budget-Fallback` | `cheapest` (standard, aliaser: `cheapest-viable`, `soft`) eller `strict` (aliaser: `block`, `hard`) | `cheapest`: bruker den globalt billigste kandidaten som reserveløsning, selv om den fortsatt overstiger taket (eldre virkemåte). `strict`: nekter å foreta et valg – forespørselen mislykkes umiddelbart med `HTTP 402` i stedet for å bruke mer enn grensen uten varsel. Ukjente verdier ignoreres. | | `X-OmniRoute-Effort` | `auto` (andre verdier er reservert) | Adaptivt tenkebudsjett: Når forespørselen **ikke** inneholder noe resonneringsfelt i noen form (`reasoning_effort`, `reasoning`, `thinking`), bestemmer gatewayen `auto` som `low`/`medium`/`high` basert på deterministiske signaler fra forespørselens struktur (lengden på den siste brukermeldingen, kontekststørrelsen frem til og med den siste brukermeldingen, tidligere verktøyresultater, dybden i verktøysløyfen). Signalene er avgrenset til gjeldende tur — alt etter den siste brukermeldingen ignoreres — slik at hver forespørsel i en verktøysløyfe får samme nivå (tilstandsløs låsing per tur, ingen økttilstand og ingen eskalering midt i løkken som ville brutt oppstrøms prefikser i ledetekstbufferen). Et eksplisitt resonneringsfelt fra klienten har alltid prioritet. Avgrenset til forespørsler der oppstrøms videresending bestemmes til OpenAI Chat Completions-formatet (`targetFormat === FORMATS.OPENAI`) — `reasoning_effort` er et felt i OpenAI-format, så headeren har ingen effekt på en forespørsel rettet mot Claude eller Gemini (se `open-sse/handlers/chatCore/adaptiveEffortWiring.ts`). | ```bash # Tving frem den raskeste profilen, begrens denne forespørselen til $0.05, og blokker fullstendig i stedet for å overskride budsjettet curl -sS http://localhost:20128/v1/chat/completions \ -H "Content-Type: application/json" \ -H "X-OmniRoute-Mode: fast" \ -H "X-OmniRoute-Budget: 0.05" \ -H "X-OmniRoute-Budget-Fallback: strict" \ -d '{"model":"auto","messages":[{"role":"user","content":"hi"}]}' ``` Bestemmelsen er en ren funksjon (`open-sse/services/autoCombo/requestControls.ts`); de fastsatte verdiene sendes til motorens eksisterende `config.modePack`- / `config.budgetCap`- / `config.budgetFallback`-inndata. En kombinasjons lagrede `config.budgetFallback` ("strict" | "cheapest") angir den vedvarende policyen; headeren overstyrer den for én enkelt forespørsel. ## Alle rutingstrategier OmniRoutes kombinasjonsmotor støtter **20 rutingstrategier** (deklarert i `src/shared/constants/routingStrategies.ts` → `ROUTING_STRATEGY_VALUES`). Selve Auto Combo-motoren er tilgjengelig under strategien `auto`; de andre er tilgjengelige for lagrede kombinasjoner. | Strategi | Beskrivelse | | :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `priority` | Ordnet liste med førstevalg og eksplisitt prioritet | | `weighted` | Vektet tilfeldig valg basert på vekten til hvert mål | | `round-robin` | Gå gjennom målene i rekkefølge (gruppevis; se nedenfor) | | `context-relay` | Overfør kontekst mellom mål (lange samtaler) | | `fill-first` | Fyll kvoten til hvert mål før du går videre til neste | | `p2c` | Tilfeldig lastbalansering med to valg | | `random` | Ensartet tilfeldig valg | | `least-used` | Velg målet med lavest gjeldende belastning | | `cost-optimized` | Minimer $ per forespørsel basert på katalogpriser | | `reset-aware` ⭐ | Prioriter etter tidspunkt for kvotetilbakestilling — korte tilbakestillingsvinduer rangeres høyere | | `reset-window` | Foretrekk mål der kvotevinduet tilbakestilles først | | `headroom` | Velg målet med størst gjenværende kvotemargin | | `quota-weighted` | Hopp over oppbrukte kontoer, og velg deretter blant resten proporsjonalt med gjenværende kvote delt på aktiv belastning; eksisterende samtaler forblir bundet | | `strict-random` | Tilfeldig valg uten deduplisering av gjentakelser | | `auto` | Bruk Auto Combo-poengberegning (16 faktorer) — **anbefalt** | | `lkgp` | Sist kjente fungerende rute (binder til den sist vellykkede leverandøren og faller deretter tilbake på regler) | | `context-optimized` | Velg målet som passer best til gjeldende kontekststørrelse | | `cache-optimized` | Endre rekkefølgen på mål etter tilhørighet til ledetekstbufferen — forbindelsen som mest sannsynlig allerede har denne forespørselens bufrede prefiks, prøves først (`open-sse/services/combo/promptCacheAffinity.ts`, #8008) | | `fusion` 🧬 | Send til et panel av modeller parallelt, og syntetiser deretter ett svar via en dommer (se nedenfor) | | `pipeline` | Kjør målene sekvensielt, og bruk utdataene fra hvert trinn som inndata til neste trinn; bare det endelige svaret returneres (#6396) | ⭐ = Nytt i v3.8.0 · 🧬 = Nytt i v3.8.36 ### Semantikk for `weighted` `weighted` er et **proporsjonalt tilfeldig trekk per forespørsel** (`open-sse/services/combo/targetSorters.ts` → `selectWeightedTarget`), ikke en utjevningsmekanisme: - Hver forespørsel trekker **ett** trinn med sannsynligheten `weight / totalWeight`; de gjenværende trinnene sorteres etter synkende vekt som reservekjeden for denne forespørselen. - Et trinn med vekt `0` (eller uten angitt vekt) blir **aldri trukket** så lenge et annet trinn har en vekt > 0 — det kan bare fungere som reserve etter at det trukne trinnet mislykkes. Bare når **alle** vektene er 0, blir valget ensartet. - Trinn der alle målene er utilgjengelige — leverandørens kretsbryter er `OPEN`, forbindelsen er i nedkjølingsperiode, eller modellen er sperret — fjernes fra trekningen før den utføres (`open-sse/services/combo/targetResolution.ts`), slik at ett enkelt fungerende trinn midlertidig kan velges for hver forespørsel. - `stickyWeightedLimit` (kombinasjonskonfigurasjon, standardverdi `1` = av) binder det trukne trinnet for så mange påfølgende vellykkede kjøringer før det trekkes på nytt. For streng rotasjon bruker du `round-robin`; like vekter med `weighted` gir statistisk — ikke streng — balansering. ### Agentbasert pipeline-modus En `pipeline`-kombinasjon med minst to modeller kan aktivere planlegger-/utfører-ruting med `config.agenticOrchestration.enabled`. Det første målet håndterer planlegging og endelige svar; det andre målet genererer verktøykall i klientens opprinnelige format. OmniRoute oppdager fortsettelser med verktøyresultater fra forespørselsprotokollen, spør planleggeren om det kreves en ny verktøyrunde, og gjør dynamisk enten utføreren eller planleggeren til det siste klientvendte trinnet. Ytterligere modeller etter det andre målet er ordnede reserveutførere. Et mislykket HTTP-svar eller et transportunntak går videre til neste utfører og tar med seg den samme planleggerbeslutningen og de opprinnelige verktøyene, men bruker utførerens egen trinninstruksjon og løste tilkobling. Det første vellykkede svaret returneres uendret, inkludert SSE-strømming; feil etter at en vellykket strøm har startet, kan ikke forsøkes på nytt her. Hvis alle utførere mislykkes, returneres den siste feilen. Avbrudd fra klienten stopper utsendingen. ```json { "strategy": "pipeline", "models": [{ "model": "provider/planner" }, { "model": "provider/executor" }], "config": { "agenticOrchestration": { "enabled": true, "maxToolRounds": 8 } } } ``` Utføreren kan generere flere uavhengige kall i ett svar. Avhengige kall håndteres i senere klientrunder med verktøyresultater, der planleggeren gjennomgår hvert resultat. `maxToolRounds` er som standard `8` og godtar `1`–`32`; når grensen er nådd, må planleggeren produsere det best mulige endelige svaret. Interne planleggerbeslutninger mellomlagres, mens det valgte klientvendte svaret beholder den opprinnelige strømmepreferansen. ### `round-robin`-gruppering og kontoutvidelse med fastholding Round-robin grupperes og utfører ikke ett trinn per forespørsel: - `stickyRoundRobinLimit` (kombinasjonskonfigurasjon, deretter `comboStickyRoundRobinLimit`, deretter `settings.stickyRoundRobinLimit`, standard **3**) beholder det samme målet for så mange påfølgende vellykkede kjøringer før det roteres. Sett kombinasjonens overstyring til `1` for rotasjon per forespørsel. Kombinasjonsredigeringsprogrammet viser den gjeldende verdien og hvilket lag den kommer fra. - `connectionAwareExpansion` (kombinasjonskonfigurasjon, deretter innstillinger, standard **false**) utvider hvert trinn på leverandørnivå til mål per konto før rotasjon. Gruppe B-strategier (priority, weighted, round-robin, random, p2c, least-used, cost-optimized, lkgp, fill-first, strict-random, context-optimized, cache-optimized, context-relay, fusion, pipeline) beholder en visning på leverandørnivå til dette er slått på. Kombinasjonsredigeringsprogrammet tilbyr arv / på / av; arv bruker den globale standardverdien (av). - Ruting basert på lokalitet for ledetekstbufferen (`promptCacheAffinityEnabled`, standard **true**) omorganiserer festede tilkoblinger slik at samsvarende buffernøkler forblir på én konto. Dette har forrang over round-robin- og vektet rotasjon mellom festede trinn per konto. Slå det av under Innstillinger → Standardverdier for kombinasjoner hvis du trenger streng rotasjon. Det finnes ingen overstyring per kombinasjon. For rotasjon mellom flere kontoer på én modell bør du foretrekke **ett dynamisk kontotrinn** (tom `connectionId`, hele utvalget) med fastholdingsgrense `1`, ikke tre festede `connectionId`-er. Festede trinn kombinert med affinitet samles på samme konto selv mens RR-telleren øker. ## Fusjonsstrategi `fusion` er den eneste strategien som **ikke** velger ett enkelt mål. Den sender forespørselen til **alle panelmodellene parallelt**, og deretter sammenfatter en konfigurerbar **dommermodell** ett endelig svar fra alle panelsvarene. Portert fra oppstrømsprosjektet `decolua/9router` (OpenRouters Fusion-design); implementasjonen finnes i `open-sse/services/fusion.ts`. Slik fungerer det: 0. **Forbikobling for verktøybruk** — en forespørsel som inneholder en ikke-tom `tools`-matrise der `tool_choice` ikke uttrykkelig er satt til `"none"`, hopper over panelet helt: den rutes direkte til én enkelt modell (den konfigurerte dommeren eller `panel[0]`), med `tools`/`tool_choice` videresendt uendret. Panelmedlemmene har ikke tilgang til verktøy, og dommerens sammenfattingsinstruks fraråder generering av verktøykall, slik at agentbaserte klienter og klienter som bruker verktøykall, får en reell avgjørelse om verktøykall i stedet for sammenfattet prosa (#6771). 1. **Utsending** (bare forespørsler uten verktøy) — forespørselen sendes til alle panelmodellene samtidig, med strømming tvangsdeaktivert og verktøy fjernet (dommeren trenger fullstendig prosa for å kunne sammenfatte). 2. **Innsamling med quorumfrist** — så snart `minPanel` svar har ankommet, starter en kort fristtidsmåler for etternølerne, og deretter fortsetter fusjonen med det som er samlet inn. Dette begrenser hvor mye den tregeste modellen påvirker den totale responstiden, innenfor en absolutt tidsavbruddsgrense. 3. **Dommersammenfatting** — panelsvarene anonymiseres (`Source 1`, `Source 2`, … — slik at dommeren vurderer innholdet, ikke modellmerket) og overleveres til dommeren, som analyserer konsensus / motsetninger / delvis dekning / unik innsikt / blindsoner, og deretter skriver **ett** autoritativt svar. Dommerkallet beholder klientens opprinnelige `stream`-flagg og verktøy, slik at strømming og etterfølgende verktøybruk fortsatt fungerer. 4. **Robust degradering** — 0 panelsvar → `503`; nøyaktig 1 gjenværende svar → dette svaret returneres direkte (ingenting å sammenfatte); et panel med én modell svarer direkte. Et panelmedlem kan også være et `combo-ref`-trinn (`{kind: "combo-ref", comboName: "..."}`) som refererer til en annen kombinasjon — det løses som **én svart boks-panelstemme** (en fullstendig rekursiv videresending til den refererte kombinasjonen, ikke en utsending til kombinasjonens egne mål), med den samme dybde-/syklusbeskyttelsen som alle andre strategier som bruker combo-ref, allerede benytter (#6764). ### Konfigurasjon Konfigureres i kombinasjonens `config`-blob (ingen skjemamigrering — den gjenbruker den eksisterende `combos`-tabellen): | Felt | Type | Standardverdi | Formål | | :--------------------------------------- | :------- | :----------------- | :---------------------------------------------------------------------------------------------- | | `config.judgeModel` | `string` | første panelmodell | Modell som sammenfatter det endelige svaret | | `config.fusionTuning.minPanel` | `number` | `2` | Antall vellykkede svar som kreves før fristtidsmåleren starter (begrenset til `[2, panelSize]`) | | `config.fusionTuning.stragglerGraceMs` | `number` | `8000` | Hvor lenge det ventes på etternølere etter at quorum er nådd | | `config.fusionTuning.panelHardTimeoutMs` | `number` | `90000` | Absolutt grense som hindrer at én fastlåst modell forsinker forespørselen | Standardverdiene finnes i `FUSION_DEFAULTS` (`open-sse/services/fusion.ts`). ### Eksempel ```bash curl -X POST http://localhost:20128/api/combos \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "fusion-panel", "strategy": "fusion", "targets": [ { "model": "cc/claude-opus-4-7" }, { "model": "cx/gpt-5.5" }, { "model": "glm/glm-5.1" } ], "config": { "judgeModel": "cc/claude-opus-4-7", "fusionTuning": { "minPanel": 2, "stragglerGraceMs": 8000, "panelHardTimeoutMs": 90000 } } }' ``` Kall den deretter som en hvilken som helst kombinasjon: `{"model":"fusion-panel","messages":[...]}`. ## Virtuell Auto-Combo-fabrikk Auto Combo-motoren krever ikke forhåndsdefinerte kombinasjoner. I stedet bygger `open-sse/services/autoCombo/virtualFactory.ts` kandidater fortløpende: 1. Henter `getProviderConnections({ isActive: true })` (alle aktiverte tilkoblinger) 2. Filtrerer til dem med gyldig legitimasjon (API-nøkkel eller et OAuth-token som ikke er utløpt, via `hasUsableOAuthToken()`) 3. Kryssrefererer med `getProviderRegistry()` for modelltilgjengelighet og priser 4. Bygger en `VirtualAutoComboCandidate` for hver tuppel `(provider, model, connection)` 5. Velger `connection.defaultModel` (eller den første modellen i registeret) som mål for videresending 6. Poengsetter hver kandidat ved hjelp av `scorePool()` med 16 faktorer og variantens vektpakke 7. Returnerer den resulterende minnebaserte `AutoComboConfig` til `handleComboChat()` — lagres aldri i databasen Dette betyr at **tillegg av en ny leverandør med `auto/*` aktivert automatisk utvider kandidatpoolen** — ingen manuell redigering av kombinasjoner er nødvendig. Den virtuelle kombinasjonen bygges på nytt for hver forespørsel, slik at nylig tilføyde tilkoblinger eller tilkoblinger som nylig har blitt friske, tas i bruk umiddelbart. ## Selvhelbreding - **Midlertidig ekskludering**: Poengsum < 0.2 → ekskludert i 5 min (progressiv tilbakekobling, maks. 30 min) - **Bevissthet om effektbryter**: OPEN → automatisk ekskludert; HALF_OPEN → testforespørsler - **Hendelsesmodus**: >50 % OPEN → deaktiver utforsking, maksimer stabiliteten - **Gjenoppretting etter nedkjøling**: Etter ekskludering er den første forespørselen en «test» med redusert tidsavbrudd ## Bandit-utforsking 5 % av forespørslene (konfigurerbart) rutes til tilfeldige leverandører for utforsking. Deaktivert i hendelsesmodus. ## API Det finnes **ikke noe dedikert `POST /api/combos/auto`-endepunkt** — Auto-Combo brukes på to måter: 1. **Uten konfigurasjon (anbefalt):** Send en hvilken som helst forespørsel om chatfullføring med `model: "auto"` eller `model: "auto/"`. Den virtuelle fabrikken bygger kombinasjonen for hver forespørsel — ingen persistens eller API-kall er nødvendig. 2. **Persistert kombinasjon med `strategy: "auto"`:** Opprett en vanlig kombinasjon via `POST /api/combos`, og angi `strategy: "auto"` samt `config.auto.weights` / `config.auto.candidatePool`. Den samme poengmotoren brukes; kombinasjonen lagres i `combos` og kan gjenbrukes etter ID. For oppdagelse viser `GET /api/combos/auto` alle varianter med den endelig utledede kandidatpoolen samt `context_length` / `max_output_tokens` — MAKSIMUM på tvers av vinduene i kandidatpoolen. Klienter (f.eks. opencode-programtillegget) må annonsere disse verdiene i stedet for `0`: en kontekst på null deaktiverer opencodes automatiske komprimering fullstendig, slik at økter vokser helt til gatewayens historikkopprydding ødelegger konteksten. MAKSIMUM er trygt å annonsere fordi auto-kombinasjonens forhåndsfilter for kontekst ruter for store forespørsler til kandidater med store vinduer. ```bash # Bruk uten konfigurasjon (ingen opprettelse av kombinasjon) curl -X POST http://localhost:20128/v1/chat/completions \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"auto/coding","messages":[{"role":"user","content":"Hello"}]}' # Persistert automatisk kombinasjon via det vanlige kombinasjonsendepunktet curl -X POST http://localhost:20128/api/combos \ -H "Content-Type: application/json" \ -d '{"id":"my-auto","name":"Auto Coder","strategy":"auto","config":{"auto":{"candidatePool":["anthropic","google","openai"],"weights":{"quota":0.15,"health":0.3,"costInv":0.05,"latencyInv":0.35,"taskFit":0.1,"stability":0,"tierPriority":0.05}}}}' ``` ### Strategier for automatisk ruting Persisterte kombinasjoner med `strategy: "auto"` kan angi `config.routerStrategy` (eller eldre `config.auto.routerStrategy`) til én av følgende: - `rules` — standard vektet poengberegning - `score` — velger den høyeste konfigurerte vektede poengsummen. Ved helt like poengsummer beholdes den konfigurerte kandidatrekkefølgen; den eksisterende `explorationRate` gjør utvalg fra hele den rangerte poolen. - `cost` / `eco` — billigste friske leverandør - `latency` / `fast` — laveste p95-latens med pålitelighetsstraff - `sla-aware` / `sla` — foretrekk kandidater som oppfyller SLO-er for p95-latens, feilrate og valgfri kostnad - `lkgp` — sist kjente fungerende leverandør først - `nadir` — spør beslutnings-API-et til [Nadir](https://getnadir.com) hvilken modell i poolen prompten trenger; må aktiveres eksplisitt, og faller tilbake til `rules` ved feil ### Detaljert om rutingsstrategier Motoren for automatiske kombinasjoner tilbyr 7 utskiftbare **RouterStrategy**-implementasjoner som du kan bytte mellom via `config.routerStrategy` (eller den eldre `config.auto.routerStrategy`). Hver strategi velger én leverandør fra kandidatpoolen basert på en `RoutingContext` (oppgavetype, verktøy-/synshint, tokenestimat, valgfri SLA-policy, valgfri sist kjente fungerende leverandør). #### 1. `rules` (standard) — vektet poengberegning med 16 faktorer Omslutter den eksisterende poengmotoren. Filtrerer bort kandidater med `OPEN` kretsbrytertilstand, kjører deretter `scorePool()` med gjeldende oppgavetype og `getTaskFitness()`, og velger leverandøren med høyest poengsum. ```ts class RulesStrategyImpl implements RouterStrategy { readonly name = "rules"; readonly description = "16-factor weighted scoring (see DEFAULT_WEIGHTS)"; select(pool, context) { const eligible = pool.filter((c) => c.circuitBreakerState !== "OPEN"); const ranked = scorePool( eligible.length > 0 ? eligible : pool, context.taskType, undefined, getTaskFitness ); return { provider: ranked[0].provider /* ... */ }; } } ``` **Når den bør brukes**: Standard. Bruk den når du ønsker en balansert avveining mellom alle signaler. **Alias**: `rules` (ingen alias) --- #### 2. `cost` / `eco` — billigste friske leverandør Sorterer kandidatpoolen etter `costPer1MTokens` (stigende) og velger den billigste. Filtrerer først bort kandidater med `OPEN` tilstand. ```ts class CostStrategyImpl implements RouterStrategy { readonly name = "cost"; readonly description = "Always selects cheapest available provider"; select(pool, context) { const healthy = pool.filter((c) => c.circuitBreakerState !== "OPEN"); const sorted = [...healthy].sort((a, b) => a.costPer1MTokens - b.costPer1MTokens); return { provider: sorted[0].provider /* ... */ }; } } ``` **Når den bør brukes**: Kostnadssensitive arbeidsbelastninger, satsvis behandling eller bakgrunnsjobber. **Aliaser**: `cost`, `eco` --- #### 3. `latency` / `fast` — laveste p95-latens med pålitelighetsstraff Sorterer etter `p95LatencyMs + (errorRate * 1000)`. Straffen for feilrate sikrer at upålitelige leverandører rangeres lavere selv om den nominelle latensen deres er lav. ```ts class LatencyStrategyImpl implements RouterStrategy { readonly name = "latency"; readonly description = "Prioritizes lowest p95 latency with reliability weighting"; select(pool, context) { const healthy = pool.filter((c) => c.circuitBreakerState !== "OPEN"); const sorted = [...healthy].sort( (a, b) => a.p95LatencyMs + a.errorRate * 1000 - (b.p95LatencyMs + b.errorRate * 1000) ); return { provider: sorted[0].provider /* ... */ }; } } ``` **Når den bør brukes**: Latenssensitive arbeidsbelastninger som sanntidschat, autofullføring eller interaktive kodeassistenter. **Aliaser**: `latency`, `fast` --- #### 4. `sla-aware` / `sla` — samsvar med SLO-er for latens/feil/kostnad Gir hver kandidat en poengsum basert på hvor godt den oppfyller den konfigurerte SLO-policyen: | Faktor | Vekt | Formel | | ---------------- | ---- | ---------------------------------------------------- | | Latenspoeng | 35% | `threshold / max(value, ε)` | | Feilpoeng | 35% | `threshold / max(value, ε)` | | Tilstandspoeng | 15% | `1.0` (CLOSED) / `0.5` (HALF_OPEN) / `0.0` (OPEN) | | Kostnadspoeng | 10% | `threshold / max(value, ε)` eller invers normalisert | | Stabilitetspoeng | 5% | invers normalisert standardavvik for latens | Når `hardConstraints: true`, sorteres kandidatene primært etter **bruddpoeng** (hvor mye de overskrider en SLO), og deretter etter samlet poengsum. Ellers brukes bare den samlede poengsummen. ```ts class SLAStrategyImpl implements RouterStrategy { readonly name = "sla-aware"; readonly description = "Selects the provider most likely to satisfy latency, error-rate, and cost SLOs"; select(pool, context) { // ... beregner poeng for hver kandidat mot policyen: { targetP95Ms, maxErrorRate, maxCostPer1MTokens, hardConstraints } } } ``` **SLA-felter** (angis i kombinasjonskonfigurasjonen): ```json { "strategy": "auto", "config": { "routerStrategy": "sla-aware", "slaTargetP95Ms": 1500, "slaMaxErrorRate": 0.05, "slaMaxCostPer1MTokens": 5, "slaHardConstraints": true } } ``` **Når den bør brukes**: Produksjonsarbeidsbelastninger med strenge budsjetter for latens, feilrate eller kostnader. **Aliaser**: `sla-aware`, `sla` --- #### 5. `lkgp` — sist kjente gode leverandør først Prøver den **sist kjente gode leverandøren** (hvis angitt) først, og faller deretter tilbake til `rules`-strategien. Nyttig for øktfasthet — samme leverandør håndterer oppfølgingsforespørsler i en samtale. ```ts class LKGPStrategyImpl implements RouterStrategy { readonly name = "lkgp"; readonly description = "Tries last known good provider first, then falls back to rules"; select(pool, context) { if (context.lkgpEnabled === false) { return getStrategy("rules").select(pool, context); } if (context.lastKnownGoodProvider) { const candidates = pool.filter( (c) => c.provider === context.lastKnownGoodProvider && c.circuitBreakerState !== "OPEN" ); if (candidates.length > 0) { return { provider: candidates[0].provider /* ... */ }; } } // Fall tilbake til rules-strategien return getStrategy("rules").select(pool, context); } } ``` **Når den bør brukes**: Samtaler med flere utvekslinger der du vil at samme leverandør skal håndtere oppfølgingsforespørsler (f.eks. for hurtigbufring, kontekstkontinuitet eller konsistent prising). **Alias**: `lkgp` (ingen alias) --- #### 6. `nadir` — promptbevisst modellvalg via Nadir Alle strategiene ovenfor rangerer kandidatene basert på sin egen telemetri; ingen av dem leser forespørselen. `nadir` sender den siste brukermeldingen samt modell-ID-ene i utvalget til beslutnings-API-et til [Nadir](https://getnadir.com) (`POST /v1/bucket`) og ruter til modellen Nadir velger fra denne menyen (`simple` → den billigste egnede modellen, `complex` → den mest avanserte modellen). Tilkoblingen som betjener denne modellen, velges fortsatt av `rules`, slik at kvoter, tilstand og kostnader fortsatt avgjør hvilken konto som brukes. ```json { "strategy": "auto", "config": { "routerStrategy": "nadir", "nadir": { "apiKey": "ndr_...", "baseUrl": "https://api.getnadir.com", "timeoutMs": 2000 } } } ``` `OMNIROUTE_NADIR_API_KEY` og `OMNIROUTE_NADIR_BASE_URL` brukes som reserveverdier fra miljøet for de to strengene. `baseUrl` er bare nødvendig for en selvdrevet Nadir-instans (en avsluttende `/v1` godtas). Nøkkelløse kall havner på Nadirs anonyme nivå, som har hastighetsbegrensning per IP-adresse. Dette sendes ut: teksten i den siste brukermeldingen (de første 16k tegnene), kandidatenes modell-ID-er og en kanalkode `source: "omniroute"`. Ingen systemprompt, historikk, verktøy eller headere. Feilhåndteringen er fail-open: et tidsavbrudd (standard er 2000 ms), en ikke-2xx-respons, en utilgjengelig vert, en ugyldig respons eller et valg utenfor utvalget fører til `rules`-avgjørelsen, og begrunnelsen får prefikset `NadirStrategy: fallback (…)`. Etter et mislykket kall hopper strategien over nettverket i 30 s, slik at et avbrudd koster ett tidsavbrudd per 30 s i stedet for ett per forespørsel. Rutinghendelser rapporterer `strategy: "nadir"` bare når Nadir faktisk tok valget. **Når den bør brukes**: trafikk med varierende vanskelighetsgrad i et utvalg som spenner over flere modellnivåer (en liten, en mellomstor og en avansert modell), der du ønsker å redusere kostnaden ved alltid å bruke den mest avanserte modellen. **Alias**: `nadir` (ingen alias) --- ### Egendefinerte ruterstrategier Du kan registrere din egen implementasjon av `RouterStrategy` via det offentlige API-et: ```ts import { registerStrategy, type RouterStrategy, } from "@omniroute/open-sse/services/autoCombo/routerStrategy"; class MyCustomStrategy implements RouterStrategy { readonly name = "my-custom"; readonly description = "My custom routing strategy"; select(pool, context) { // Din rutingslogikk her return { provider: pool[0].provider, model: pool[0].model, strategy: this.name, reason: "MyCustomStrategy: ...", candidatesConsidered: pool.length, finalScore: 1.0, }; } } registerStrategy("my-custom", new MyCustomStrategy()); ``` Bruk den deretter: ```json { "strategy": "auto", "config": { "routerStrategy": "my-custom" } } ``` --- ### Veiledning for valg av ruterstrategi | Bruksområde | Strategi | Begrunnelse | | ------------------------------ | ----------- | --------------------------------------------- | | Balansert arbeidsbelastning | `rules` | Standard — tar hensyn til alle faktorer | | Minimer kostnader | `cost` | Velger alltid den billigste | | Minimer latens | `latency` | Velger den raskeste pålitelige leverandøren | | Strenge SLO-er | `sla-aware` | Filtrerer etter terskler for p95/feil/kostnad | | Samtale med flere utvekslinger | `lkgp` | Øktfasthet | | Varierende vanskelighetsgrad | `nadir` | Velger modellnivå per prompt | SLA-bevisste felter: ```json { "strategy": "auto", "config": { "routerStrategy": "sla-aware", "slaTargetP95Ms": 1500, "slaMaxErrorRate": 0.05, "slaMaxCostPer1MTokens": 5, "slaHardConstraints": true } } ``` ## Oppgaveegnethet 30+ modeller er vurdert på tvers av 6 oppgavetyper (`coding`, `review`, `planning`, `analysis`, `debugging`, `documentation`). Støtter jokertegnmønstre (f.eks. `*-coder` → høy poengsum for koding). ## Oppsummering av Auto-varianter Når vi inkluderer den rene `auto` (standard) samt de 6 `AutoVariant`-verdiene som er deklarert i `autoPrefix.ts`, finnes det **7 modell-ID-er som kan startes**: `auto`, `auto/coding`, `auto/fast`, `auto/cheap`, `auto/offline`, `auto/smart`, `auto/lkgp` (`AutoVariant` selv inneholder 6 verdier; det 7. alternativet er «ingen variant» — ren `auto` — som håndteres av `parseAutoPrefix()` som `variant: undefined`.) ## Hvordan nivåer passer inn i Auto-Combo Poengfunksjonen med 16 faktorer (`open-sse/services/autoCombo/scoring.ts`) behandler nivåtilhørighet som to signaler: `tierPriority` (0.0476) og `tierAffinity` (0.0476). Se den kanoniske [tabellen over poengfaktorer](#how-it-works-persisted-auto-combos) ovenfor for hele `DEFAULT_WEIGHTS`-settet — overstyringene per pakke (ship-fast/cost-saver/quality-first/ offline-friendly) er oppført i tabellen «Vektprofiler per pakke». Nivå alene tvinger **ikke** Tier 1 til å komme først — hvis latensen for Tier 1 er dårlig eller forholdet mellom kostnad og kvalitet ikke er optimalt, vinner Tier 2. For å tvinge nivårekkefølgen bruker du kombinasjonsstrategien `priority` og ordner leverandørene etter nivå. For å favorisere Tier 1 (abonnement) sterkt øker du vekten for `tierPriority`: ```json { "strategy": "auto", "config": { "auto": { "weights": { "tierPriority": 0.3, "costInv": 0.05 } } } } ``` Se [`docs/guides/TIERS.md`](../guides/TIERS.md) for nivådefinisjoner og klassifisering av leverandører. ## Testing og dekning ### Deterministisk matrise for rutingsbeslutninger (`npm run test:combo:matrix`) `tests/integration/combo-matrix/*.test.ts` beviser rutings**beslutningen** for alle de 19 offentlige strategiene ende-til-ende gjennom den virkelige kombinasjonsflyten med en simulert oppstrømsleverandør. Dekningen inkluderer: - Alle de 19 `ROUTING_STRATEGY_VALUES`-strategiene (ordnet, vektet, kostnad, kontekst, fusjon, …). - `quota-share` (intern) ende-til-ende: DRR-rettferdighet + nedprioritering ved metning via det virkelige `selectQuotaShareTarget`-grensesnittet (`registerQuotaFetcher` / `setLKGP` / `__setHeadroomSaturationFetcherForTests`). - `context-relay`-dekning for universell overlevering på tvers av alle antall mål. Denne testpakken kjøres i CI (`test:integration`-jobben) med `--test-concurrency=1` og `--test-force-exit`, slik at den er deterministisk og ikke krever aktive tilgangsopplysninger. ### Betinget direkte røykprøve (IKKE i CI — virkelige leverandører) | Kommando | Hva den gjør | | :------------------------------------- | :------------------------------------------------------------------------------------------------------- | | `npm run test:combo:live` | Virkelig ruting i prosessen med `RUN_COMBO_LIVE=1`; tar et øyeblikksbilde av en aktiv OmniRoute-database | | `npm run test:combo:live:vps` | HTTP-kall mot en aktiv OmniRoute-server (angi `COMBO_LIVE_BASE_URL`) | | `npm run test:combo:live:vps:failover` | Det samme, med tilsiktede failover-scenarioer | Disse røykprøvene tester den virkelige kommunikasjonsflyten (kombinasjon → leverandør → fullføring). De er med hensikt utelatt fra CI fordi de krever aktive tilgangsopplysninger og VPS-tilgang. --- ## Filer | Fil | Formål | | :-------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------- | | `open-sse/services/autoCombo/scoring.ts` | 16-faktors poengfunksjon, `DEFAULT_WEIGHTS`, poolnormalisering | | `open-sse/services/autoCombo/taskFitness.ts` | Oppslag for modell × oppgaveegnethet | | `open-sse/services/autoCombo/engine.ts` | Utvalgslogikk, banditt, budsjettgrense | | `open-sse/services/autoCombo/selfHealing.ts` | Ekskludering, sonderinger, hendelsesmodus | | `open-sse/services/autoCombo/modePacks.ts` | 6 vektprofiler (rask levering, kostnadsbesparelse, kvalitet først, frakoblet-vennlig, pålitelighet først, kaosmodus) | | `open-sse/services/autoCombo/autoPrefix.ts` | Parser for `auto/`-prefiks + 6 varianter | | `open-sse/services/autoCombo/virtualFactory.ts` | Bygger `AutoComboConfig` i minnet fra aktive tilkoblinger | | `open-sse/services/autoCombo/providerRegistryAccessor.ts` | Testkrok for etterligning av leverandørregisteret | | `src/shared/constants/routingStrategies.ts` | `ROUTING_STRATEGY_VALUES` (20 strategier) | | `src/sse/handlers/chat.ts` | Integrasjon: kortslutning for auto-prefiks |