feat: lower memory dedupe threshold for more aggressive cleaning
This commit is contained in:
+480
-480
@@ -1,480 +1,480 @@
|
||||
// Schmaler Fetch-Helfer gegen das MC-2-Backend (/api/*).
|
||||
|
||||
export async function api<T = unknown>(path: string, init?: RequestInit): Promise<T> {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
...init?.headers,
|
||||
} as Record<string, string>
|
||||
|
||||
const sudoPassword = localStorage.getItem("mc_sudo_password")
|
||||
const hfToken = localStorage.getItem("mc_hf_token")
|
||||
|
||||
if (sudoPassword) {
|
||||
headers["X-Sudo-Password"] = sudoPassword
|
||||
}
|
||||
|
||||
let body = init?.body
|
||||
const method = init?.method?.toUpperCase() || "GET"
|
||||
if (method === "POST") {
|
||||
if (typeof body === "string") {
|
||||
try {
|
||||
const data = JSON.parse(body)
|
||||
let changed = false
|
||||
if (sudoPassword && !("sudo_password" in data)) {
|
||||
data["sudo_password"] = sudoPassword
|
||||
changed = true
|
||||
}
|
||||
if (hfToken && !("hf_token" in data)) {
|
||||
data["hf_token"] = hfToken
|
||||
changed = true
|
||||
}
|
||||
if (changed) {
|
||||
body = JSON.stringify(data)
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore JSON parse errors
|
||||
}
|
||||
} else if (!body) {
|
||||
const data: Record<string, any> = {}
|
||||
if (sudoPassword) data["sudo_password"] = sudoPassword
|
||||
if (hfToken) data["hf_token"] = hfToken
|
||||
if (Object.keys(data).length > 0) {
|
||||
body = JSON.stringify(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(path, {
|
||||
...init,
|
||||
headers,
|
||||
body,
|
||||
})
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
// llama-swap-`groups`: Mitglieder mit swap=false dürfen GLEICHZEITIG resident sein (Ko-Residenz).
|
||||
// Die `brains`-Gruppe hält Hirn (fast) + Augen (vision) gemeinsam warm, statt sich zu verdrängen.
|
||||
export interface GroupSpec {
|
||||
swap: boolean
|
||||
persist: boolean
|
||||
members: string[]
|
||||
}
|
||||
export interface GroupsResp {
|
||||
groups: Record<string, GroupSpec>
|
||||
}
|
||||
export const getGroups = () => api<GroupsResp>("/api/groups")
|
||||
export const setGroup = (group: string, members: string[], swap = false, persist = true) =>
|
||||
api("/api/groups", { method: "PUT", body: JSON.stringify({ group, members, swap, persist }) })
|
||||
|
||||
export interface Capabilities {
|
||||
moe: boolean
|
||||
active_b: number | null
|
||||
tools: "yes" | "likely" | "no"
|
||||
vision: boolean
|
||||
coder: boolean
|
||||
reasoning: boolean
|
||||
embedding: boolean
|
||||
ctx: number | null
|
||||
params_b: number | null
|
||||
arch: string | null
|
||||
}
|
||||
|
||||
export interface Fit {
|
||||
level: "perfect" | "marginal" | "too_tight"
|
||||
text: string
|
||||
req_gb: number
|
||||
tps: number
|
||||
}
|
||||
|
||||
export interface RoleRecModel {
|
||||
name: string
|
||||
current_role: string | null
|
||||
params_b: number
|
||||
quant: string
|
||||
fit: Fit
|
||||
suitable: boolean
|
||||
incomplete: boolean
|
||||
score: number
|
||||
reason: string
|
||||
recommended: boolean
|
||||
}
|
||||
|
||||
export interface RoleRecResp {
|
||||
role: string
|
||||
recommended: string | null
|
||||
models: RoleRecModel[]
|
||||
}
|
||||
|
||||
export interface FitResp {
|
||||
params_b: number
|
||||
fit: Fit
|
||||
optimal_ctx: number
|
||||
assigned_ctx: number
|
||||
budget: { gtt_gb: number; reserved_gb: number; budget_gb: number; mode: string }
|
||||
sys_ram_gb: number
|
||||
}
|
||||
|
||||
export interface ModelInfo {
|
||||
name: string
|
||||
role: string | null
|
||||
aliases: string[]
|
||||
api_ids: string[]
|
||||
ctx: number | null
|
||||
ttl: number | null
|
||||
cmd: string
|
||||
gguf_path: string
|
||||
filename: string
|
||||
quant: string
|
||||
size_bytes: number | null
|
||||
incomplete: boolean
|
||||
prompt_cache: boolean
|
||||
spec_draft_model: string | null
|
||||
spec_type: string | null
|
||||
spec_active: boolean
|
||||
parallel_slots: number
|
||||
capabilities: Capabilities
|
||||
}
|
||||
|
||||
export interface VocabFingerprint {
|
||||
model: string | null
|
||||
pre: string | null
|
||||
n_vocab: number | null
|
||||
arch: string | null
|
||||
}
|
||||
|
||||
export interface DraftInfo {
|
||||
path: string
|
||||
filename: string
|
||||
size_bytes: number | null
|
||||
vocab: VocabFingerprint | null
|
||||
compatible: boolean | null // null = nicht prüfbar (Ziel-GGUF fehlt)
|
||||
mtp?: boolean // MTP-Kopf (Multi-Token-Prediction) statt klassischem Draft
|
||||
}
|
||||
|
||||
export interface DraftsResp {
|
||||
target_path: string
|
||||
target_exists: boolean
|
||||
target_vocab: VocabFingerprint | null
|
||||
drafts: DraftInfo[]
|
||||
}
|
||||
|
||||
export interface DiscoverModel {
|
||||
name: string
|
||||
author: string
|
||||
repo: string
|
||||
role: string
|
||||
params_b: number
|
||||
quant: string
|
||||
tags: string[]
|
||||
downloads: number
|
||||
fit: Fit
|
||||
optimal_ctx: number
|
||||
caps: Capabilities
|
||||
}
|
||||
|
||||
export interface DiscoverCategory {
|
||||
role: string
|
||||
title: string
|
||||
icon: string
|
||||
models: DiscoverModel[]
|
||||
recommended: string | null
|
||||
}
|
||||
|
||||
export interface DiscoverResp {
|
||||
updated: number
|
||||
categories: DiscoverCategory[]
|
||||
sys_ram_gb: number
|
||||
}
|
||||
|
||||
export interface RoutingResp {
|
||||
mode?: string
|
||||
endpoint?: string
|
||||
heavy_threshold_chars?: number
|
||||
routes: { name: string; target: string }[]
|
||||
lanes?: { name: string; target: string; threshold_chars?: number; escalate_chars?: number; aka?: string }[]
|
||||
fallbacks: Record<string, string[]>[]
|
||||
context_window_fallbacks: Record<string, string[]>[]
|
||||
gateway_reachable: boolean
|
||||
}
|
||||
|
||||
// UI-editierbare Routing-Policy (GET/PUT /api/routing/policy). Aliase + Zeichen-Schwellen
|
||||
// hinter den Lanes; hot-reload im Backend (kein Restart).
|
||||
export interface RoutingPolicy {
|
||||
fast: string
|
||||
heavy: string
|
||||
coder: string
|
||||
coder_lite: string
|
||||
heavy_chars: number
|
||||
coding_escalate_chars: number
|
||||
fast_no_think: boolean
|
||||
}
|
||||
export interface RoutingPolicyField {
|
||||
key: keyof RoutingPolicy
|
||||
label: string
|
||||
type: "str" | "int" | "bool"
|
||||
min?: number
|
||||
max?: number
|
||||
}
|
||||
export interface RoutingPolicyMeta {
|
||||
policy: RoutingPolicy
|
||||
defaults: RoutingPolicy
|
||||
fields: RoutingPolicyField[]
|
||||
}
|
||||
export const getRoutingPolicy = () => api<RoutingPolicyMeta>("/api/routing/policy")
|
||||
export const updateRoutingPolicy = (patch: Partial<RoutingPolicy>) =>
|
||||
api<{ policy: RoutingPolicy }>("/api/routing/policy", { method: "PUT", body: JSON.stringify(patch) })
|
||||
|
||||
export interface GitInfo {
|
||||
hash: string
|
||||
date: string
|
||||
subject: string
|
||||
branch: string
|
||||
dirty: boolean
|
||||
path: string
|
||||
}
|
||||
|
||||
// Engine kann git-, binary- oder unbekannte Version sein — Felder je nach `type`.
|
||||
export interface ComponentVersion {
|
||||
type: "git" | "binary" | "unknown"
|
||||
hash?: string
|
||||
date?: string
|
||||
subject?: string
|
||||
branch?: string
|
||||
dirty?: boolean
|
||||
path?: string
|
||||
version_text?: string
|
||||
}
|
||||
|
||||
export interface Versions {
|
||||
mc2: GitInfo | null
|
||||
engine: ComponentVersion
|
||||
hermes_ui: GitInfo | null
|
||||
hermes_agent: GitInfo | null
|
||||
}
|
||||
|
||||
export interface SystemStatus {
|
||||
cpu: { percent: number; cores: number | null }
|
||||
ram: { total: number; used: number; percent: number }
|
||||
gpu: {
|
||||
busy_percent: number | null
|
||||
vram_used: number | null
|
||||
vram_total: number | null
|
||||
gtt_used?: number | null
|
||||
gtt_total?: number | null
|
||||
} | null
|
||||
temp: { cpu?: number; gpu?: number } | null
|
||||
disk: { total: number; used: number; percent: number } | null
|
||||
versions?: Versions
|
||||
}
|
||||
|
||||
export interface ServicesResp {
|
||||
services: { name: string; unit: string; url: string; ok: boolean }[]
|
||||
links: { engine_ui: string; gateway: string; hermes_terminal: string }
|
||||
}
|
||||
|
||||
export interface ComponentUpdate {
|
||||
key: string // z.B. "hermes_agent"
|
||||
name: string
|
||||
current: string | null
|
||||
latest: string | null
|
||||
update: boolean | null // null = unbestimmbar (installierte Version remote nicht abfragbar)
|
||||
reachable: boolean | null
|
||||
}
|
||||
|
||||
export interface UpdatesResp {
|
||||
os: number
|
||||
engine: number
|
||||
swap: number
|
||||
models: number
|
||||
model_list: { role: string; title: string; repo: string }[]
|
||||
last_check?: number | null
|
||||
components?: ComponentUpdate[]
|
||||
}
|
||||
|
||||
export interface UpdateDetails {
|
||||
kind: "os" | "engine" | "swap" | "hermes"
|
||||
error?: string
|
||||
// LLM-Zusammenfassung in Lucys Stimme (hermes/engine/swap; Breaking Changes zuerst)
|
||||
summary?: string
|
||||
// Aktions-Verdikt: muss der Besitzer selbst etwas tun? (null = kein Verdikt/keine Summary)
|
||||
action_needed?: boolean | null
|
||||
action_text?: string
|
||||
// os
|
||||
count?: number
|
||||
packages?: { name: string; current: string; candidate: string }[]
|
||||
// os: vom System zurückgestellte Pakete (Phasen-Rollout / kept back) — ehrlich statt "hängt"
|
||||
held_back?: { name: string; reason: "phasing" | "kept_back" }[]
|
||||
// engine
|
||||
installed_build?: number | null
|
||||
latest_build?: number | null
|
||||
latest_tag?: string | null
|
||||
name?: string | null
|
||||
url?: string | null
|
||||
body?: string | null
|
||||
// hermes
|
||||
branch?: string | null
|
||||
behind?: number
|
||||
commits?: { hash: string; subject: string; when: string }[]
|
||||
}
|
||||
|
||||
export interface ConnectTool {
|
||||
label: string
|
||||
lang: string
|
||||
snippet: string
|
||||
note: string
|
||||
}
|
||||
export interface ConnectResp {
|
||||
host: string
|
||||
gateway_url: string
|
||||
mc_url: string
|
||||
tools: Record<string, ConnectTool> // Leitung 1 — Modell (IDEs/Agenten → Gateway)
|
||||
memory: ConnectTool // Leitung 2 — Gedächtnis (separater MCP-Server)
|
||||
}
|
||||
|
||||
export interface ConnectLine {
|
||||
ok: boolean
|
||||
detail: string
|
||||
}
|
||||
export interface ConnectHealth {
|
||||
gateway: ConnectLine
|
||||
memory: ConnectLine
|
||||
}
|
||||
|
||||
export interface Memory {
|
||||
id: string
|
||||
content: string
|
||||
category: string
|
||||
source: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
score?: number // Relevanz bei semantischer Suche (q gesetzt); sonst undefined
|
||||
}
|
||||
|
||||
export interface MemoryGraphNode {
|
||||
id: string
|
||||
content: string
|
||||
category: string
|
||||
source: string
|
||||
}
|
||||
export interface MemoryGraph {
|
||||
nodes: MemoryGraphNode[]
|
||||
edges: { source: string; target: string; weight: number }[]
|
||||
}
|
||||
|
||||
export interface DedupeResult {
|
||||
groups: { keep: { id: string; content: string; category: string }; remove: { id: string; content: string }[] }[]
|
||||
duplicate_count: number
|
||||
removed: number
|
||||
applied: boolean
|
||||
}
|
||||
|
||||
export interface AgentStatus {
|
||||
gateway_url: string
|
||||
terminal_url: string
|
||||
box_console_url?: string
|
||||
hermes_ui_url?: string
|
||||
gateway_reachable: boolean
|
||||
terminal_reachable: boolean
|
||||
box_console_reachable?: boolean
|
||||
hermes_ui_reachable?: boolean
|
||||
home_exists: boolean
|
||||
brain_model?: string
|
||||
has_config: boolean
|
||||
has_skills: boolean
|
||||
has_memories: boolean
|
||||
telegram_enabled?: boolean
|
||||
mcp_server_count?: number
|
||||
pc_executor_reachable?: boolean
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: string
|
||||
label: string
|
||||
group?: string | null // "maintenance" = system-veränderndes Update (Wartungs-Riegel)
|
||||
state: "queued" | "running" | "done" | "failed" | "canceled"
|
||||
progress?: number
|
||||
total_bytes?: number
|
||||
done_bytes?: number
|
||||
rate_bps?: number
|
||||
eta_s?: number
|
||||
}
|
||||
|
||||
export interface Health {
|
||||
status: string
|
||||
version: string
|
||||
engine_reachable: boolean
|
||||
gateway_reachable: boolean
|
||||
brain?: { role: string; model: string | null; ready: boolean }
|
||||
}
|
||||
|
||||
export interface TokenStats {
|
||||
prompt_tokens: number
|
||||
completion_tokens: number
|
||||
total_tokens: number
|
||||
saved_usd: number
|
||||
saved_eur: number
|
||||
pricing?: Record<string, { in: number; out: number }>
|
||||
}
|
||||
|
||||
// Per-Turn-Latenz-Trace (GET /api/voice/trace). Balken-Stufen (zeitlich disjunkt):
|
||||
// stt · vision · hirn · gen. mem0 = Unter-Detail INNERHALB von hirn (nicht zum Balken addieren).
|
||||
export interface VoiceTurn {
|
||||
id: string
|
||||
ts: number // Epoch-Sekunden
|
||||
session: string
|
||||
kind: string
|
||||
had_images: boolean
|
||||
stt_ms: number | null
|
||||
vision_ms: number | null
|
||||
hirn_ms: number | null // Zeit bis erstes Inhalts-Token (Agent + Mem0 + LLM-TTFT)
|
||||
gen_ms: number | null // Generierung nach dem ersten Token bis Stream-Ende
|
||||
mem0_ms: number | null // Teil VON hirn (Mem0-Retrieve), best-effort
|
||||
total_ms: number
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export interface VoiceTraceResp {
|
||||
turns: VoiceTurn[]
|
||||
}
|
||||
|
||||
export interface ModelsResp {
|
||||
models: ModelInfo[]
|
||||
running?: string[]
|
||||
}
|
||||
|
||||
export interface HermesBrainModel {
|
||||
name: string
|
||||
filename?: string
|
||||
params_b: number | null
|
||||
quant?: string
|
||||
size_bytes?: number | null
|
||||
version?: number | null
|
||||
gguf_path?: string
|
||||
incomplete?: boolean
|
||||
}
|
||||
|
||||
export interface HermesBrainCandidate {
|
||||
repo: string
|
||||
name: string
|
||||
version: number
|
||||
params_b: number
|
||||
downloads: number
|
||||
fit: Fit
|
||||
}
|
||||
|
||||
export interface HermesBrainBudget {
|
||||
gtt_gb: number
|
||||
brain_gb: number // Footprint des (empfohlenen) Brains, das immer resident bleibt
|
||||
warm_projected_gb: number // alle persist-Modelle warm (Info)
|
||||
free_after_gb: number // frei nach Brain + größtem on-demand
|
||||
largest_ondemand_gb: number // größtes on-demand-Modell (z.B. heavy/coder)
|
||||
fits: boolean // passt Brain + größtes on-demand zusammen ins Budget?
|
||||
}
|
||||
|
||||
export interface HermesBrainResp {
|
||||
current: HermesBrainModel | null
|
||||
recommended: HermesBrainCandidate | null
|
||||
update_available: boolean
|
||||
budget?: HermesBrainBudget | null
|
||||
}
|
||||
// Schmaler Fetch-Helfer gegen das MC-2-Backend (/api/*).
|
||||
|
||||
export async function api<T = unknown>(path: string, init?: RequestInit): Promise<T> {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
...init?.headers,
|
||||
} as Record<string, string>
|
||||
|
||||
const sudoPassword = localStorage.getItem("mc_sudo_password")
|
||||
const hfToken = localStorage.getItem("mc_hf_token")
|
||||
|
||||
if (sudoPassword) {
|
||||
headers["X-Sudo-Password"] = sudoPassword
|
||||
}
|
||||
|
||||
let body = init?.body
|
||||
const method = init?.method?.toUpperCase() || "GET"
|
||||
if (method === "POST") {
|
||||
if (typeof body === "string") {
|
||||
try {
|
||||
const data = JSON.parse(body)
|
||||
let changed = false
|
||||
if (sudoPassword && !("sudo_password" in data)) {
|
||||
data["sudo_password"] = sudoPassword
|
||||
changed = true
|
||||
}
|
||||
if (hfToken && !("hf_token" in data)) {
|
||||
data["hf_token"] = hfToken
|
||||
changed = true
|
||||
}
|
||||
if (changed) {
|
||||
body = JSON.stringify(data)
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore JSON parse errors
|
||||
}
|
||||
} else if (!body) {
|
||||
const data: Record<string, any> = {}
|
||||
if (sudoPassword) data["sudo_password"] = sudoPassword
|
||||
if (hfToken) data["hf_token"] = hfToken
|
||||
if (Object.keys(data).length > 0) {
|
||||
body = JSON.stringify(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(path, {
|
||||
...init,
|
||||
headers,
|
||||
body,
|
||||
})
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
// llama-swap-`groups`: Mitglieder mit swap=false dürfen GLEICHZEITIG resident sein (Ko-Residenz).
|
||||
// Die `brains`-Gruppe hält Hirn (fast) + Augen (vision) gemeinsam warm, statt sich zu verdrängen.
|
||||
export interface GroupSpec {
|
||||
swap: boolean
|
||||
persist: boolean
|
||||
members: string[]
|
||||
}
|
||||
export interface GroupsResp {
|
||||
groups: Record<string, GroupSpec>
|
||||
}
|
||||
export const getGroups = () => api<GroupsResp>("/api/groups")
|
||||
export const setGroup = (group: string, members: string[], swap = false, persist = true) =>
|
||||
api("/api/groups", { method: "PUT", body: JSON.stringify({ group, members, swap, persist }) })
|
||||
|
||||
export interface Capabilities {
|
||||
moe: boolean
|
||||
active_b: number | null
|
||||
tools: "yes" | "likely" | "no"
|
||||
vision: boolean
|
||||
coder: boolean
|
||||
reasoning: boolean
|
||||
embedding: boolean
|
||||
ctx: number | null
|
||||
params_b: number | null
|
||||
arch: string | null
|
||||
}
|
||||
|
||||
export interface Fit {
|
||||
level: "perfect" | "marginal" | "too_tight"
|
||||
text: string
|
||||
req_gb: number
|
||||
tps: number
|
||||
}
|
||||
|
||||
export interface RoleRecModel {
|
||||
name: string
|
||||
current_role: string | null
|
||||
params_b: number
|
||||
quant: string
|
||||
fit: Fit
|
||||
suitable: boolean
|
||||
incomplete: boolean
|
||||
score: number
|
||||
reason: string
|
||||
recommended: boolean
|
||||
}
|
||||
|
||||
export interface RoleRecResp {
|
||||
role: string
|
||||
recommended: string | null
|
||||
models: RoleRecModel[]
|
||||
}
|
||||
|
||||
export interface FitResp {
|
||||
params_b: number
|
||||
fit: Fit
|
||||
optimal_ctx: number
|
||||
assigned_ctx: number
|
||||
budget: { gtt_gb: number; reserved_gb: number; budget_gb: number; mode: string }
|
||||
sys_ram_gb: number
|
||||
}
|
||||
|
||||
export interface ModelInfo {
|
||||
name: string
|
||||
role: string | null
|
||||
aliases: string[]
|
||||
api_ids: string[]
|
||||
ctx: number | null
|
||||
ttl: number | null
|
||||
cmd: string
|
||||
gguf_path: string
|
||||
filename: string
|
||||
quant: string
|
||||
size_bytes: number | null
|
||||
incomplete: boolean
|
||||
prompt_cache: boolean
|
||||
spec_draft_model: string | null
|
||||
spec_type: string | null
|
||||
spec_active: boolean
|
||||
parallel_slots: number
|
||||
capabilities: Capabilities
|
||||
}
|
||||
|
||||
export interface VocabFingerprint {
|
||||
model: string | null
|
||||
pre: string | null
|
||||
n_vocab: number | null
|
||||
arch: string | null
|
||||
}
|
||||
|
||||
export interface DraftInfo {
|
||||
path: string
|
||||
filename: string
|
||||
size_bytes: number | null
|
||||
vocab: VocabFingerprint | null
|
||||
compatible: boolean | null // null = nicht prüfbar (Ziel-GGUF fehlt)
|
||||
mtp?: boolean // MTP-Kopf (Multi-Token-Prediction) statt klassischem Draft
|
||||
}
|
||||
|
||||
export interface DraftsResp {
|
||||
target_path: string
|
||||
target_exists: boolean
|
||||
target_vocab: VocabFingerprint | null
|
||||
drafts: DraftInfo[]
|
||||
}
|
||||
|
||||
export interface DiscoverModel {
|
||||
name: string
|
||||
author: string
|
||||
repo: string
|
||||
role: string
|
||||
params_b: number
|
||||
quant: string
|
||||
tags: string[]
|
||||
downloads: number
|
||||
fit: Fit
|
||||
optimal_ctx: number
|
||||
caps: Capabilities
|
||||
}
|
||||
|
||||
export interface DiscoverCategory {
|
||||
role: string
|
||||
title: string
|
||||
icon: string
|
||||
models: DiscoverModel[]
|
||||
recommended: string | null
|
||||
}
|
||||
|
||||
export interface DiscoverResp {
|
||||
updated: number
|
||||
categories: DiscoverCategory[]
|
||||
sys_ram_gb: number
|
||||
}
|
||||
|
||||
export interface RoutingResp {
|
||||
mode?: string
|
||||
endpoint?: string
|
||||
heavy_threshold_chars?: number
|
||||
routes: { name: string; target: string }[]
|
||||
lanes?: { name: string; target: string; threshold_chars?: number; escalate_chars?: number; aka?: string }[]
|
||||
fallbacks: Record<string, string[]>[]
|
||||
context_window_fallbacks: Record<string, string[]>[]
|
||||
gateway_reachable: boolean
|
||||
}
|
||||
|
||||
// UI-editierbare Routing-Policy (GET/PUT /api/routing/policy). Aliase + Zeichen-Schwellen
|
||||
// hinter den Lanes; hot-reload im Backend (kein Restart).
|
||||
export interface RoutingPolicy {
|
||||
fast: string
|
||||
heavy: string
|
||||
coder: string
|
||||
coder_lite: string
|
||||
heavy_chars: number
|
||||
coding_escalate_chars: number
|
||||
fast_no_think: boolean
|
||||
}
|
||||
export interface RoutingPolicyField {
|
||||
key: keyof RoutingPolicy
|
||||
label: string
|
||||
type: "str" | "int" | "bool"
|
||||
min?: number
|
||||
max?: number
|
||||
}
|
||||
export interface RoutingPolicyMeta {
|
||||
policy: RoutingPolicy
|
||||
defaults: RoutingPolicy
|
||||
fields: RoutingPolicyField[]
|
||||
}
|
||||
export const getRoutingPolicy = () => api<RoutingPolicyMeta>("/api/routing/policy")
|
||||
export const updateRoutingPolicy = (patch: Partial<RoutingPolicy>) =>
|
||||
api<{ policy: RoutingPolicy }>("/api/routing/policy", { method: "PUT", body: JSON.stringify(patch) })
|
||||
|
||||
export interface GitInfo {
|
||||
hash: string
|
||||
date: string
|
||||
subject: string
|
||||
branch: string
|
||||
dirty: boolean
|
||||
path: string
|
||||
}
|
||||
|
||||
// Engine kann git-, binary- oder unbekannte Version sein — Felder je nach `type`.
|
||||
export interface ComponentVersion {
|
||||
type: "git" | "binary" | "unknown"
|
||||
hash?: string
|
||||
date?: string
|
||||
subject?: string
|
||||
branch?: string
|
||||
dirty?: boolean
|
||||
path?: string
|
||||
version_text?: string
|
||||
}
|
||||
|
||||
export interface Versions {
|
||||
mc2: GitInfo | null
|
||||
engine: ComponentVersion
|
||||
hermes_ui: GitInfo | null
|
||||
hermes_agent: GitInfo | null
|
||||
}
|
||||
|
||||
export interface SystemStatus {
|
||||
cpu: { percent: number; cores: number | null }
|
||||
ram: { total: number; used: number; percent: number }
|
||||
gpu: {
|
||||
busy_percent: number | null
|
||||
vram_used: number | null
|
||||
vram_total: number | null
|
||||
gtt_used?: number | null
|
||||
gtt_total?: number | null
|
||||
} | null
|
||||
temp: { cpu?: number; gpu?: number } | null
|
||||
disk: { total: number; used: number; percent: number } | null
|
||||
versions?: Versions
|
||||
}
|
||||
|
||||
export interface ServicesResp {
|
||||
services: { name: string; unit: string; url: string; ok: boolean }[]
|
||||
links: { engine_ui: string; gateway: string; hermes_terminal: string }
|
||||
}
|
||||
|
||||
export interface ComponentUpdate {
|
||||
key: string // z.B. "hermes_agent"
|
||||
name: string
|
||||
current: string | null
|
||||
latest: string | null
|
||||
update: boolean | null // null = unbestimmbar (installierte Version remote nicht abfragbar)
|
||||
reachable: boolean | null
|
||||
}
|
||||
|
||||
export interface UpdatesResp {
|
||||
os: number
|
||||
engine: number
|
||||
swap: number
|
||||
models: number
|
||||
model_list: { role: string; title: string; repo: string }[]
|
||||
last_check?: number | null
|
||||
components?: ComponentUpdate[]
|
||||
}
|
||||
|
||||
export interface UpdateDetails {
|
||||
kind: "os" | "engine" | "swap" | "hermes"
|
||||
error?: string
|
||||
// LLM-Zusammenfassung in Lucys Stimme (hermes/engine/swap; Breaking Changes zuerst)
|
||||
summary?: string
|
||||
// Aktions-Verdikt: muss der Besitzer selbst etwas tun? (null = kein Verdikt/keine Summary)
|
||||
action_needed?: boolean | null
|
||||
action_text?: string
|
||||
// os
|
||||
count?: number
|
||||
packages?: { name: string; current: string; candidate: string }[]
|
||||
// os: vom System zurückgestellte Pakete (Phasen-Rollout / kept back) — ehrlich statt "hängt"
|
||||
held_back?: { name: string; reason: "phasing" | "kept_back" }[]
|
||||
// engine
|
||||
installed_build?: number | null
|
||||
latest_build?: number | null
|
||||
latest_tag?: string | null
|
||||
name?: string | null
|
||||
url?: string | null
|
||||
body?: string | null
|
||||
// hermes
|
||||
branch?: string | null
|
||||
behind?: number
|
||||
commits?: { hash: string; subject: string; when: string }[]
|
||||
}
|
||||
|
||||
export interface ConnectTool {
|
||||
label: string
|
||||
lang: string
|
||||
snippet: string
|
||||
note: string
|
||||
}
|
||||
export interface ConnectResp {
|
||||
host: string
|
||||
gateway_url: string
|
||||
mc_url: string
|
||||
tools: Record<string, ConnectTool> // Leitung 1 — Modell (IDEs/Agenten → Gateway)
|
||||
memory: ConnectTool // Leitung 2 — Gedächtnis (separater MCP-Server)
|
||||
}
|
||||
|
||||
export interface ConnectLine {
|
||||
ok: boolean
|
||||
detail: string
|
||||
}
|
||||
export interface ConnectHealth {
|
||||
gateway: ConnectLine
|
||||
memory: ConnectLine
|
||||
}
|
||||
|
||||
export interface Memory {
|
||||
id: string
|
||||
content: string
|
||||
category: string
|
||||
source: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
score?: number // Relevanz bei semantischer Suche (q gesetzt); sonst undefined
|
||||
}
|
||||
|
||||
export interface MemoryGraphNode {
|
||||
id: string
|
||||
content: string
|
||||
category: string
|
||||
source: string
|
||||
}
|
||||
export interface MemoryGraph {
|
||||
nodes: MemoryGraphNode[]
|
||||
edges: { source: string; target: string; weight: number }[]
|
||||
}
|
||||
|
||||
export interface DedupeResult {
|
||||
groups: { keep: { id: string; content: string; category: string }; remove: { id: string; content: string }[] }[]
|
||||
duplicate_count: number
|
||||
removed: number
|
||||
applied: boolean
|
||||
}
|
||||
|
||||
export interface AgentStatus {
|
||||
gateway_url: string
|
||||
terminal_url: string
|
||||
box_console_url?: string
|
||||
hermes_ui_url?: string
|
||||
gateway_reachable: boolean
|
||||
terminal_reachable: boolean
|
||||
box_console_reachable?: boolean
|
||||
hermes_ui_reachable?: boolean
|
||||
home_exists: boolean
|
||||
brain_model?: string
|
||||
has_config: boolean
|
||||
has_skills: boolean
|
||||
has_memories: boolean
|
||||
telegram_enabled?: boolean
|
||||
mcp_server_count?: number
|
||||
pc_executor_reachable?: boolean
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: string
|
||||
label: string
|
||||
group?: string | null // "maintenance" = system-veränderndes Update (Wartungs-Riegel)
|
||||
state: "queued" | "running" | "done" | "failed" | "canceled"
|
||||
progress?: number
|
||||
total_bytes?: number
|
||||
done_bytes?: number
|
||||
rate_bps?: number
|
||||
eta_s?: number
|
||||
}
|
||||
|
||||
export interface Health {
|
||||
status: string
|
||||
version: string
|
||||
engine_reachable: boolean
|
||||
gateway_reachable: boolean
|
||||
brain?: { role: string; model: string | null; ready: boolean }
|
||||
}
|
||||
|
||||
export interface TokenStats {
|
||||
prompt_tokens: number
|
||||
completion_tokens: number
|
||||
total_tokens: number
|
||||
saved_usd: number
|
||||
saved_eur: number
|
||||
pricing?: Record<string, { in: number; out: number }>
|
||||
}
|
||||
|
||||
// Per-Turn-Latenz-Trace (GET /api/voice/trace). Balken-Stufen (zeitlich disjunkt):
|
||||
// stt · vision · hirn · gen. mem0 = Unter-Detail INNERHALB von hirn (nicht zum Balken addieren).
|
||||
export interface VoiceTurn {
|
||||
id: string
|
||||
ts: number // Epoch-Sekunden
|
||||
session: string
|
||||
kind: string
|
||||
had_images: boolean
|
||||
stt_ms: number | null
|
||||
vision_ms: number | null
|
||||
hirn_ms: number | null // Zeit bis erstes Inhalts-Token (Agent + Mem0 + LLM-TTFT)
|
||||
gen_ms: number | null // Generierung nach dem ersten Token bis Stream-Ende
|
||||
mem0_ms: number | null // Teil VON hirn (Mem0-Retrieve), best-effort
|
||||
total_ms: number
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export interface VoiceTraceResp {
|
||||
turns: VoiceTurn[]
|
||||
}
|
||||
|
||||
export interface ModelsResp {
|
||||
models: ModelInfo[]
|
||||
running?: string[]
|
||||
}
|
||||
|
||||
export interface HermesBrainModel {
|
||||
name: string
|
||||
filename?: string
|
||||
params_b: number | null
|
||||
quant?: string
|
||||
size_bytes?: number | null
|
||||
version?: number | null
|
||||
gguf_path?: string
|
||||
incomplete?: boolean
|
||||
}
|
||||
|
||||
export interface HermesBrainCandidate {
|
||||
repo: string
|
||||
name: string
|
||||
version: number
|
||||
params_b: number
|
||||
downloads: number
|
||||
fit: Fit
|
||||
}
|
||||
|
||||
export interface HermesBrainBudget {
|
||||
gtt_gb: number
|
||||
brain_gb: number // Footprint des (empfohlenen) Brains, das immer resident bleibt
|
||||
warm_projected_gb: number // alle persist-Modelle warm (Info)
|
||||
free_after_gb: number // frei nach Brain + größtem on-demand
|
||||
largest_ondemand_gb: number // größtes on-demand-Modell (z.B. heavy/coder)
|
||||
fits: boolean // passt Brain + größtes on-demand zusammen ins Budget?
|
||||
}
|
||||
|
||||
export interface HermesBrainResp {
|
||||
current: HermesBrainModel | null
|
||||
recommended: HermesBrainCandidate | null
|
||||
update_available: boolean
|
||||
budget?: HermesBrainBudget | null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user