Feat: Brain-Rolle (hermes) als erstklassige, dauer-warme Hirn-Rolle
- ROLE_IDS (llamaswap, sources) + UI-Rollenlisten (ModelBadges, Discover,
ModelBrowse, Cockpit-Slot-Grid) um `hermes` erweitert: gemma erscheint als
„Hirn", nicht mehr als scout.
- Neuer set_ttl-Helper; set_agent_brain erzwingt ttl:0 (neu + idempotent beim
Re-Setzen) und liefert eine weiche Budget-Warnung (kein Hard-Block) bei OOM.
- brain_status/brain_model_name: zeigen das echte Hirn (Rolle hermes / Hermes
model.default) statt hart `fast` (Bugfix Health-/Ready-Check).
- POST /api/models/{id}/role delegiert die Rolle `hermes` an den warm-bewussten
Flow (Alias + brains-Gruppe + ttl 0 + Hermes-Config + Gateway-Restart).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -161,6 +161,14 @@ def recommend_role(role: str) -> dict:
|
|||||||
|
|
||||||
@router.post("/models/{model_id}/role")
|
@router.post("/models/{model_id}/role")
|
||||||
def set_model_role(model_id: str, body: RoleReq) -> dict:
|
def set_model_role(model_id: str, body: RoleReq) -> dict:
|
||||||
|
# Das Agent-Hirn (Rolle 'hermes') braucht den warm-bewussten Flow (Alias + brains-Gruppe +
|
||||||
|
# ttl 0 + Hermes config.default + Gateway-Restart) — Single Source of Truth UI ↔ Hermes.
|
||||||
|
if (body.role or "").strip().lower() == "hermes":
|
||||||
|
from services.agent import set_agent_brain
|
||||||
|
res = set_agent_brain(model_id)
|
||||||
|
if not res.get("ok"):
|
||||||
|
raise HTTPException(400, res.get("reason", "Fehler beim Setzen des Agent-Hirns"))
|
||||||
|
return res
|
||||||
if not llamaswap.set_role(model_id, body.role):
|
if not llamaswap.set_role(model_id, body.role):
|
||||||
raise HTTPException(404, "Modell nicht gefunden")
|
raise HTTPException(404, "Modell nicht gefunden")
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|||||||
@@ -167,16 +167,40 @@ def set_agent_brain(model_id: str) -> dict:
|
|||||||
return {"ok": False, "reason": "Modell nicht installiert — erst über Modelle-finden laden."}
|
return {"ok": False, "reason": "Modell nicht installiert — erst über Modelle-finden laden."}
|
||||||
old = next((m["name"] for m in models.values() if m.get("role") == "hermes"), None)
|
old = next((m["name"] for m in models.values() if m.get("role") == "hermes"), None)
|
||||||
if model_id == old:
|
if model_id == old:
|
||||||
|
# Idempotent härten: auch wenn schon Hirn, warm (brains) + ttl 0 sicherstellen.
|
||||||
|
try:
|
||||||
|
from services.llamaswap import set_ttl
|
||||||
|
brains = (llamaswap.list_groups().get("brains") or {}).get("members") or []
|
||||||
|
if model_id not in brains:
|
||||||
|
llamaswap.set_group("brains", brains + [model_id], swap=False, persist=True)
|
||||||
|
set_ttl(model_id, 0)
|
||||||
|
except PermissionError as exc:
|
||||||
|
return {"ok": False, "reason": str(exc)}
|
||||||
return {"ok": True, "old": old, "new": model_id, "note": "ist bereits das Agent-Hirn"}
|
return {"ok": True, "old": old, "new": model_id, "note": "ist bereits das Agent-Hirn"}
|
||||||
try:
|
try:
|
||||||
llamaswap.set_role(model_id, "hermes") # 1) Alias
|
llamaswap.set_role(model_id, "hermes") # 1) Alias
|
||||||
brains = (llamaswap.list_groups().get("brains") or {}).get("members") or []
|
brains = (llamaswap.list_groups().get("brains") or {}).get("members") or []
|
||||||
new_members = [x for x in brains if x not in (old, model_id)] + [model_id]
|
new_members = [x for x in brains if x not in (old, model_id)] + [model_id]
|
||||||
llamaswap.set_group("brains", new_members, swap=False, persist=True) # 2) warm
|
llamaswap.set_group("brains", new_members, swap=False, persist=True) # 2) warm
|
||||||
|
# 2b) TTL härten: neues Hirn nie auto-entladen; altes Hirn auf Default entspannen.
|
||||||
|
from services.llamaswap import set_ttl, DEFAULT_TTL
|
||||||
|
set_ttl(model_id, 0)
|
||||||
|
if old:
|
||||||
|
set_ttl(old, DEFAULT_TTL)
|
||||||
except PermissionError as exc:
|
except PermissionError as exc:
|
||||||
return {"ok": False, "reason": str(exc)}
|
return {"ok": False, "reason": str(exc)}
|
||||||
update_brain_model("hermes") # 3) Config + Restart
|
update_brain_model("hermes") # 3) Config + Restart
|
||||||
return {"ok": True, "old": old, "new": model_id}
|
# Weiche Budget-Warnung (kein Hard-Block): passt Hirn + größtes on-demand zusammen ins GTT?
|
||||||
|
warning = None
|
||||||
|
try:
|
||||||
|
b = hermes_brain_info().get("budget") or {}
|
||||||
|
if b and not b.get("fits", True):
|
||||||
|
warning = (f"Speicher-Warnung: Hirn (~{b.get('brain_gb')} GB) + größtes on-demand-"
|
||||||
|
f"Modell (~{b.get('largest_ondemand_gb')} GB) übersteigen das GTT-Budget "
|
||||||
|
f"(~{b.get('gtt_gb')} GB) — heavy/coder würden das Hirn verdrängen.")
|
||||||
|
except Exception:
|
||||||
|
log.debug("set_agent_brain: Budget-Check fehlgeschlagen", exc_info=True)
|
||||||
|
return {"ok": True, "old": old, "new": model_id, "warning": warning}
|
||||||
|
|
||||||
|
|
||||||
def update_brain_model(new_model: str) -> bool:
|
def update_brain_model(new_model: str) -> bool:
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ from config import (
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Kanonische Serving-Rollen — EINE Quelle der Wahrheit (identisch zu sources.ROLE_IDS,
|
# Kanonische Serving-Rollen — EINE Quelle der Wahrheit (identisch zu sources.ROLE_IDS,
|
||||||
# maintenance, frontend ModelBadges.ROLES). Kein agent/reasoning mehr.
|
# maintenance, frontend ModelBadges.ROLES). `hermes` = Lucys Agent-Hirn (warm + ko-resident
|
||||||
ROLE_IDS = {"fast", "heavy", "coder", "vision", "scout"}
|
# in der `brains`-Gruppe); UI-Label „Hirn".
|
||||||
|
ROLE_IDS = {"fast", "heavy", "coder", "vision", "scout", "hermes"}
|
||||||
|
|
||||||
_CTX_RE = re.compile(r"-(?:c|-ctx-size)\s+(\d+)")
|
_CTX_RE = re.compile(r"-(?:c|-ctx-size)\s+(\d+)")
|
||||||
_PATH_RE = re.compile(r"-(?:m|-model)\s+([^\s]+)")
|
_PATH_RE = re.compile(r"-(?:m|-model)\s+([^\s]+)")
|
||||||
@@ -371,6 +372,18 @@ def set_ctx(model_id: str, ctx: int) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_ttl(model_id: str, ttl: int) -> bool:
|
||||||
|
"""Idle-TTL (Sekunden) eines bestehenden Modells setzen. ttl=0 → nie automatisch
|
||||||
|
entladen (für das Agent-Hirn, das dauerhaft warm bleiben muss)."""
|
||||||
|
cfg = read_config()
|
||||||
|
spec = (cfg.get("models") or {}).get(model_id)
|
||||||
|
if not isinstance(spec, dict):
|
||||||
|
return False
|
||||||
|
spec["ttl"] = int(ttl)
|
||||||
|
write_config(cfg)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def delete_model(model_id: str) -> bool:
|
def delete_model(model_id: str) -> bool:
|
||||||
"""Entfernt einen Modell-Eintrag aus der config.yaml, löscht die zugehörigen
|
"""Entfernt einen Modell-Eintrag aus der config.yaml, löscht die zugehörigen
|
||||||
GGUF-Dateien (auch Splits) vom Datenträger und bereinigt leere Ordner.
|
GGUF-Dateien (auch Splits) vom Datenträger und bereinigt leere Ordner.
|
||||||
@@ -433,23 +446,37 @@ def delete_model(model_id: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def brain_model_name() -> str | None:
|
def brain_model_name() -> str | None:
|
||||||
"""Modellname des Agent-Hirns = das Modell mit llama-swap-Alias/Rolle 'fast'."""
|
"""Modellname von Lucys Agent-Hirn. Bevorzugt das Modell mit dem 'hermes'-Alias/-Rolle;
|
||||||
for m in list_models():
|
fällt auf Hermes' aktives `model.default` zurück (deckt den Fall ab, dass die Config direkt
|
||||||
|
auf einen Modellnamen statt den Alias zeigt)."""
|
||||||
|
models = list_models()
|
||||||
|
for m in models:
|
||||||
names = {str(a).lower() for a in (m.get("aliases") or [])}
|
names = {str(a).lower() for a in (m.get("aliases") or [])}
|
||||||
if m.get("role"):
|
if m.get("role"):
|
||||||
names.add(str(m["role"]).lower())
|
names.add(str(m["role"]).lower())
|
||||||
if "fast" in names:
|
if "hermes" in names:
|
||||||
return m["name"]
|
return m["name"]
|
||||||
|
# Fallback: das real von Hermes genutzte Hirn (model.default), per Alias/Name auflösen.
|
||||||
|
try:
|
||||||
|
from services.agent import _active_brain_name
|
||||||
|
brain = (_active_brain_name() or "").lower()
|
||||||
|
if brain and brain != "auto":
|
||||||
|
cur = next((m for m in models if (m.get("role") or "").lower() == brain), None) \
|
||||||
|
or next((m for m in models if brain in (m["name"] or "").lower()), None)
|
||||||
|
if cur:
|
||||||
|
return cur["name"]
|
||||||
|
except Exception:
|
||||||
|
log.debug("brain_model_name: Hermes-Fallback fehlgeschlagen", exc_info=True)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def brain_status() -> dict:
|
def brain_status() -> dict:
|
||||||
"""Ist das Agent-Hirn ('fast') WIRKLICH geladen & bereit? Prüft /running — ein abgestürztes
|
"""Ist Lucys Agent-Hirn (Rolle 'hermes') WIRKLICH geladen & bereit? Prüft /running — ein
|
||||||
Modell (z.B. OOM/Crash nach Engine-Update) erscheint dort NICHT als running. Fängt damit den
|
abgestürztes Modell (z.B. OOM/Crash nach Engine-Update) erscheint dort NICHT als running.
|
||||||
Fall 'Engine erreichbar, aber Hirn tot', den engine_reachable() nicht sieht (silent fail)."""
|
Fängt damit den Fall 'Engine erreichbar, aber Hirn tot', den engine_reachable() nicht sieht."""
|
||||||
name = brain_model_name()
|
name = brain_model_name()
|
||||||
running = get_running_models()
|
running = get_running_models()
|
||||||
return {"role": "fast", "model": name, "ready": bool(name and name in running)}
|
return {"role": "hermes", "model": name, "ready": bool(name and name in running)}
|
||||||
|
|
||||||
|
|
||||||
def get_running_models() -> list[str]:
|
def get_running_models() -> list[str]:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Vertrauenswürdige Quellen + Kategorien für die automatische Modell-Entdeckung.
|
Vertrauenswürdige Quellen + Kategorien für die automatische Modell-Entdeckung.
|
||||||
Rollen = die EINE Quelle der Wahrheit, identisch zu den llama-swap-Serving-Rollen
|
Rollen = die EINE Quelle der Wahrheit, identisch zu den llama-swap-Serving-Rollen
|
||||||
und der UI: fast · heavy · coder · vision · scout.
|
und der UI: fast · heavy · coder · vision · hermes (Agent-Hirn) · scout.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# HF-Orgs, die zuverlässig aktuelle, hochwertige GGUF-Quants veröffentlichen.
|
# HF-Orgs, die zuverlässig aktuelle, hochwertige GGUF-Quants veröffentlichen.
|
||||||
@@ -9,7 +9,8 @@ TRUSTED_AUTHORS = ["unsloth", "bartowski", "ggml-org", "lmstudio-community"]
|
|||||||
|
|
||||||
# Kanonische Rollen — eine Quelle der Wahrheit (deckt sich mit llamaswap.ROLE_IDS,
|
# Kanonische Rollen — eine Quelle der Wahrheit (deckt sich mit llamaswap.ROLE_IDS,
|
||||||
# maintenance.ROLE_MAP, frontend ModelBadges.ROLES + Discover.ROLE_METADATA).
|
# maintenance.ROLE_MAP, frontend ModelBadges.ROLES + Discover.ROLE_METADATA).
|
||||||
ROLE_IDS = ["fast", "heavy", "coder", "vision", "scout"]
|
# `hermes` = Lucys Agent-Hirn (warm + ko-resident); UI-Label „Hirn".
|
||||||
|
ROLE_IDS = ["fast", "heavy", "coder", "vision", "hermes", "scout"]
|
||||||
|
|
||||||
# Kategorien (Reihenfolge = Anzeige + Zuordnungs-Priorität). Ein Modell wird der
|
# Kategorien (Reihenfolge = Anzeige + Zuordnungs-Priorität). Ein Modell wird der
|
||||||
# ERSTEN Kategorie zugeordnet, deren Stichwort im Repo-Namen vorkommt; sonst „scout".
|
# ERSTEN Kategorie zugeordnet, deren Stichwort im Repo-Namen vorkommt; sonst „scout".
|
||||||
@@ -19,6 +20,8 @@ CATEGORIES = [
|
|||||||
"kw": ["-vl-", "-vl", "vision", "llava", "multimodal", "-mm-", "pixtral"]},
|
"kw": ["-vl-", "-vl", "vision", "llava", "multimodal", "-mm-", "pixtral"]},
|
||||||
{"role": "coder", "title": "Coden & Programmieren", "icon": "code",
|
{"role": "coder", "title": "Coden & Programmieren", "icon": "code",
|
||||||
"kw": ["coder", "-code-", "code-", "codestral", "starcoder"]},
|
"kw": ["coder", "-code-", "code-", "codestral", "starcoder"]},
|
||||||
|
{"role": "hermes", "title": "Lucys Hirn (Agent)", "icon": "brain-circuit",
|
||||||
|
"kw": ["hermes"]}, # Agent-Hirn: Hermes-Familie am robustesten (natives Tool-Calling).
|
||||||
{"role": "heavy", "title": "Schweres Reasoning", "icon": "brain",
|
{"role": "heavy", "title": "Schweres Reasoning", "icon": "brain",
|
||||||
"kw": ["reasoning", "-think", "thinking", "gpt-oss", "deepseek-r", "-r1", "qwq",
|
"kw": ["reasoning", "-think", "thinking", "gpt-oss", "deepseek-r", "-r1", "qwq",
|
||||||
"-70b", "-72b", "-120b", "-123b", "-235b", "-405b", "-a10b", "-a22b"]},
|
"-70b", "-72b", "-120b", "-123b", "-235b", "-405b", "-a10b", "-a22b"]},
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+267
-262
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-B3e8J_PY.js"></script>
|
<script type="module" crossorigin src="/assets/index-CLiy0fO1.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DoqQd4Zi.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DoqQd4Zi.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { type Fit } from "@/lib/api"
|
import { type Fit } from "@/lib/api"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
// Die 5 kanonischen Serving-Rollen (identisch zu Backend sources.py/llamaswap.ROLE_IDS).
|
// Die kanonischen Serving-Rollen (identisch zu Backend sources.py/llamaswap.ROLE_IDS).
|
||||||
export const ROLES = ["fast", "heavy", "coder", "vision", "scout"]
|
// `hermes` = Lucys Agent-Hirn (warm + ko-resident); UI-Label „Hirn".
|
||||||
|
export const ROLES = ["fast", "heavy", "coder", "vision", "hermes", "scout"]
|
||||||
|
|
||||||
// Rolle → Farbton. EINE Quelle der Wahrheit für alle Rollen-Badges (Cockpit, ModelsCard,
|
// Rolle → Farbton. EINE Quelle der Wahrheit für alle Rollen-Badges (Cockpit, ModelsCard,
|
||||||
// …). `hermes` = Agent-Hirn (taucht als geladenes Modell auf).
|
// …). `hermes` = Agent-Hirn (taucht als geladenes Modell auf).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useDialog } from "@/lib/useDialog"
|
|||||||
import { CapsChips } from "@/components/CapsChips"
|
import { CapsChips } from "@/components/CapsChips"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { fmtSize, fmtCtx } from "@/lib/format"
|
import { fmtSize, fmtCtx } from "@/lib/format"
|
||||||
import { getBrandInfo, roleTone } from "@/components/models/ModelBadges"
|
import { getBrandInfo, roleTone, ROLES } from "@/components/models/ModelBadges"
|
||||||
import { SpecDraftModal } from "@/components/models/SpecDraftModal"
|
import { SpecDraftModal } from "@/components/models/SpecDraftModal"
|
||||||
import { LaneEditor } from "@/components/models/LaneEditor"
|
import { LaneEditor } from "@/components/models/LaneEditor"
|
||||||
|
|
||||||
@@ -107,11 +107,14 @@ export function Cockpit() {
|
|||||||
|
|
||||||
async function handleRoleChange(role: string, modelName: string) {
|
async function handleRoleChange(role: string, modelName: string) {
|
||||||
try {
|
try {
|
||||||
await api(`/api/models/${encodeURIComponent(modelName)}/role`, {
|
// Bei der Hirn-Rolle (hermes) läuft serverseitig der warm-bewusste Flow (Alias + brains-
|
||||||
|
// Gruppe + ttl 0 + Hermes-Config + Gateway-Restart) und liefert ggf. eine Budget-Warnung.
|
||||||
|
const res = await api<{ warning?: string | null }>(`/api/models/${encodeURIComponent(modelName)}/role`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ role: role || null }),
|
body: JSON.stringify({ role: role || null }),
|
||||||
})
|
})
|
||||||
reload()
|
reload()
|
||||||
|
if (res?.warning) showAlert("Hirn gesetzt — Hinweis", res.warning)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
showAlert("Fehler", `Fehler beim Zuweisen der Rolle: ${e.message || e}`)
|
showAlert("Fehler", `Fehler beim Zuweisen der Rolle: ${e.message || e}`)
|
||||||
}
|
}
|
||||||
@@ -311,7 +314,7 @@ export function Cockpit() {
|
|||||||
Gateway Steckplatz-Belegung (Slot-Zuweisung)
|
Gateway Steckplatz-Belegung (Slot-Zuweisung)
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
|
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
|
||||||
{["fast", "heavy", "coder", "vision", "scout"].map((role) => {
|
{ROLES.map((role) => {
|
||||||
const m = models.find((x) => x.role === role)
|
const m = models.find((x) => x.role === role)
|
||||||
const isWarm = m ? running.includes(m.name) : false
|
const isWarm = m ? running.includes(m.name) : false
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { Download, Star, Layers, Check, Zap, Eye, Code, Brain, Compass, ChevronDown, ChevronUp } from "lucide-react"
|
import { Download, Star, Layers, Check, Zap, Eye, Code, Brain, BrainCircuit, Compass, ChevronDown, ChevronUp } from "lucide-react"
|
||||||
import { api } from "@/lib/api"
|
import { api } from "@/lib/api"
|
||||||
import { useModels, useUpdates, useDiscover } from "@/lib/queries"
|
import { useModels, useUpdates, useDiscover } from "@/lib/queries"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
@@ -7,7 +7,7 @@ import { fmtBytes } from "@/lib/format"
|
|||||||
import { FitBadge } from "@/components/models/ModelBadges"
|
import { FitBadge } from "@/components/models/ModelBadges"
|
||||||
import { ModelBrowse } from "./ModelBrowse"
|
import { ModelBrowse } from "./ModelBrowse"
|
||||||
|
|
||||||
// Die 5 kanonischen Rollen (identisch zu sources.py / ModelBadges.ROLES).
|
// Die kanonischen Rollen (identisch zu sources.py / ModelBadges.ROLES).
|
||||||
const ROLE_METADATA: Record<string, { title: string; desc: string; icon: any }> = {
|
const ROLE_METADATA: Record<string, { title: string; desc: string; icon: any }> = {
|
||||||
fast: {
|
fast: {
|
||||||
title: "Schnelles Alltags-Hirn",
|
title: "Schnelles Alltags-Hirn",
|
||||||
@@ -29,6 +29,11 @@ const ROLE_METADATA: Record<string, { title: string; desc: string; icon: any }>
|
|||||||
desc: "Verarbeitet Bilder, Screenshots und visuelle Diagramme in multimodalen Chats.",
|
desc: "Verarbeitet Bilder, Screenshots und visuelle Diagramme in multimodalen Chats.",
|
||||||
icon: Eye
|
icon: Eye
|
||||||
},
|
},
|
||||||
|
hermes: {
|
||||||
|
title: "Lucys Hirn (Agent)",
|
||||||
|
desc: "Lucys dauer-warmes Agent-Hirn mit Tool-Calling — bleibt ko-resident, lädt nie kalt nach.",
|
||||||
|
icon: BrainCircuit
|
||||||
|
},
|
||||||
scout: {
|
scout: {
|
||||||
title: "Multimodal-Allrounder",
|
title: "Multimodal-Allrounder",
|
||||||
desc: "Multimodaler Allrounder für schnelle Antworten, Übersetzungen und Alltag.",
|
desc: "Multimodaler Allrounder für schnelle Antworten, Übersetzungen und Alltag.",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const CHIPS: { key: string; label: string; q: string }[] = [
|
|||||||
{ key: "reasoning", label: "Reasoning", q: "reasoning" },
|
{ key: "reasoning", label: "Reasoning", q: "reasoning" },
|
||||||
{ key: "small", label: "Klein (≤4B)", q: "3B" },
|
{ key: "small", label: "Klein (≤4B)", q: "3B" },
|
||||||
]
|
]
|
||||||
const ROLE_OPTS = ["fast", "heavy", "coder", "vision", "scout"]
|
const ROLE_OPTS = ["fast", "heavy", "coder", "vision", "hermes", "scout"]
|
||||||
|
|
||||||
const _nf = new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 })
|
const _nf = new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 })
|
||||||
function fmtN(n: number): string {
|
function fmtN(n: number): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user