mirror of
https://git.sync.wtf/troed/oc-ls-stats.git
synced 2026-10-10 12:38:22 +03:00
414 lines
15 KiB
TypeScript
414 lines
15 KiB
TypeScript
import { test } from "node:test"
|
|
import assert from "node:assert/strict"
|
|
import {
|
|
backoffDelayMs,
|
|
resolveServerUrls,
|
|
selectSessionUrls,
|
|
serverTypeOverride,
|
|
isStrataHealth,
|
|
mapStrataLive,
|
|
statsUrl,
|
|
serverKindKey,
|
|
type TrackerState,
|
|
} from "./src/stats.ts"
|
|
|
|
test("first failure retries after the base delay", () => {
|
|
assert.equal(backoffDelayMs(1), 1000)
|
|
})
|
|
|
|
test("second failure doubles the delay", () => {
|
|
assert.equal(backoffDelayMs(2), 2000)
|
|
})
|
|
|
|
test("delay grows exponentially", () => {
|
|
assert.equal(backoffDelayMs(3), 4000)
|
|
assert.equal(backoffDelayMs(4), 8000)
|
|
})
|
|
|
|
test("delay is capped at the maximum", () => {
|
|
assert.equal(backoffDelayMs(5), 10000)
|
|
assert.equal(backoffDelayMs(10), 10000)
|
|
assert.equal(backoffDelayMs(20), 10000)
|
|
})
|
|
|
|
test("uses provided base and max", () => {
|
|
assert.equal(backoffDelayMs(1, 200, 5000), 200)
|
|
assert.equal(backoffDelayMs(5, 200, 5000), 3200)
|
|
assert.equal(backoffDelayMs(20, 200, 5000), 5000)
|
|
})
|
|
|
|
const llamaProviders = [
|
|
{
|
|
id: "llamacpp",
|
|
name: "llama.cpp",
|
|
source: "config",
|
|
env: [],
|
|
options: { baseURL: "http://localhost:9090/v1" },
|
|
models: {},
|
|
},
|
|
]
|
|
|
|
const visionProviders = [
|
|
{
|
|
id: "vision",
|
|
name: "vision",
|
|
source: "config",
|
|
env: [],
|
|
options: { baseURL: "http://<server-host>:8080/v1" },
|
|
models: {},
|
|
},
|
|
]
|
|
|
|
const llamaConfig = {
|
|
provider: {
|
|
llama: { options: { baseURL: "http://localhost:9090/v1" } },
|
|
},
|
|
}
|
|
|
|
test("explicit server wins over provider list", () => {
|
|
assert.deepEqual(
|
|
resolveServerUrls({ server: "http://<server-host>:8080" }, llamaProviders, {}),
|
|
["http://<server-host>:8080"],
|
|
)
|
|
})
|
|
|
|
test("explicit server strips /v1 path", () => {
|
|
assert.deepEqual(
|
|
resolveServerUrls({ server: "http://<server-host>:8080/v1" }, llamaProviders, {}),
|
|
["http://<server-host>:8080"],
|
|
)
|
|
})
|
|
|
|
test("non-string server falls back to provider list", () => {
|
|
assert.deepEqual(resolveServerUrls({ server: 42 }, llamaProviders, {}), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("empty server falls back to provider list", () => {
|
|
assert.deepEqual(resolveServerUrls({ server: "" }, llamaProviders, {}), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("scheme-less server falls back to provider list", () => {
|
|
assert.deepEqual(resolveServerUrls({ server: "localhost:8080" }, llamaProviders, {}), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("whitespace-only server falls back to provider list", () => {
|
|
assert.deepEqual(resolveServerUrls({ server: " " }, llamaProviders, {}), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("null server falls back to provider list", () => {
|
|
assert.deepEqual(resolveServerUrls({ server: null }, llamaProviders, {}), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("no options uses provider list", () => {
|
|
assert.deepEqual(resolveServerUrls(undefined, llamaProviders, {}), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("provider name match detects llama servers", () => {
|
|
const byName = [{ ...visionProviders[0], name: "My llama server" }]
|
|
assert.deepEqual(resolveServerUrls(undefined, byName, {}), [
|
|
"http://<server-host>:8080",
|
|
])
|
|
})
|
|
|
|
test("ollama providers are excluded", () => {
|
|
const ollama = [
|
|
{ id: "ollama", name: "Ollama", source: "config", env: [], options: { baseURL: "http://localhost:11434" }, models: {} },
|
|
]
|
|
assert.deepEqual(resolveServerUrls(undefined, ollama, {}), ["http://localhost:8080"])
|
|
})
|
|
|
|
test("non-string baseURL is ignored", () => {
|
|
const bad = [{ id: "llamacpp", name: "llama.cpp", options: { baseURL: 123 } }]
|
|
assert.deepEqual(resolveServerUrls(undefined, bad, {}), ["http://localhost:8080"])
|
|
})
|
|
|
|
test("missing provider list falls back to legacy config parse", () => {
|
|
assert.deepEqual(resolveServerUrls(undefined, undefined, llamaConfig), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("empty provider list falls back to legacy config parse", () => {
|
|
assert.deepEqual(resolveServerUrls(undefined, [], llamaConfig), [
|
|
"http://localhost:9090",
|
|
])
|
|
})
|
|
|
|
test("nothing anywhere falls back to localhost", () => {
|
|
assert.deepEqual(resolveServerUrls(undefined, visionProviders, {}), [
|
|
"http://localhost:8080",
|
|
])
|
|
})
|
|
|
|
test("provider base_url fallback is detected", () => {
|
|
const snake = [{ id: "llamacpp", name: "llama.cpp", options: { base_url: "http://localhost:9090/v1" } }]
|
|
assert.deepEqual(resolveServerUrls(undefined, snake, {}), ["http://localhost:9090"])
|
|
})
|
|
|
|
test("ollama in id excludes even when name mentions llama", () => {
|
|
const mixed = [{ id: "ollama-bridge", name: "My llama server", options: { baseURL: "http://localhost:11434" } }]
|
|
assert.deepEqual(resolveServerUrls(undefined, mixed, {}), ["http://localhost:8080"])
|
|
})
|
|
|
|
test("selectSessionUrls narrows to the matching provider's baseURL", () => {
|
|
const candidates = ["http://a:1", "http://b:2"]
|
|
const providers = [
|
|
{ id: "vision", name: "Vision", options: { baseURL: "http://c:3" } },
|
|
{ id: "llamacpp-a", name: "Llama A", options: { baseURL: "http://b:2/v1" } },
|
|
]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "llamacpp-a"), ["http://b:2"])
|
|
})
|
|
|
|
test("selectSessionUrls matches base_url variant", () => {
|
|
const candidates = ["http://localhost:9090"]
|
|
const providers = [{ id: "llamacpp-b", name: "L", options: { base_url: "http://localhost:9090" } }]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "llamacpp-b"), ["http://localhost:9090"])
|
|
})
|
|
|
|
test("selectSessionUrls normalizes /v1 before comparing", () => {
|
|
const candidates = ["http://x:9"]
|
|
const providers = [{ id: "p", name: "P", options: { baseURL: "http://x:9/v1" } }]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "p"), ["http://x:9"])
|
|
})
|
|
|
|
test("selectSessionUrls returns candidates unchanged when provider not found", () => {
|
|
const candidates = ["http://a:1"]
|
|
assert.deepEqual(selectSessionUrls(candidates, [], "nope"), ["http://a:1"])
|
|
})
|
|
|
|
test("selectSessionUrls excludes polling when matched provider URL is not a candidate", () => {
|
|
const candidates = ["http://a:1"]
|
|
const providers = [{ id: "ollama", name: "Ollama", options: { baseURL: "http://d:4" } }]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "ollama"), [])
|
|
})
|
|
|
|
test("selectSessionUrls returns candidates unchanged for missing or empty providerID", () => {
|
|
assert.deepEqual(selectSessionUrls(["http://a:1"], [], undefined), ["http://a:1"])
|
|
assert.deepEqual(selectSessionUrls(["http://a:1"], [], ""), ["http://a:1"])
|
|
assert.deepEqual(selectSessionUrls(["http://a:1"], [], " "), ["http://a:1"])
|
|
assert.deepEqual(selectSessionUrls(["http://a:1"], [], 42), ["http://a:1"])
|
|
})
|
|
|
|
test("selectSessionUrls excludes polling when matched provider has no usable baseURL", () => {
|
|
const candidates = ["http://a:1"]
|
|
const providers = [{ id: "p", name: "P", options: { baseURL: 42 } }]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "p"), [])
|
|
})
|
|
|
|
test("selectSessionUrls returns empty candidates unchanged", () => {
|
|
assert.deepEqual(selectSessionUrls([], [], "p"), [])
|
|
})
|
|
|
|
test("selectSessionUrls tolerates non-array providers", () => {
|
|
const c = ["http://a:1"]
|
|
assert.deepEqual(selectSessionUrls(c, undefined, "p"), c)
|
|
})
|
|
|
|
test("selectSessionUrls excludes polling when id is absent from a non-empty provider list", () => {
|
|
assert.deepEqual(selectSessionUrls(["http://a:1"], [null, 42, {}], "p"), [])
|
|
assert.deepEqual(selectSessionUrls(["http://a:1"], [{ id: "other", options: { baseURL: "http://a:1" } }], "p"), [])
|
|
})
|
|
|
|
test("selectSessionUrls excludes polling during external-model sessions", () => {
|
|
// Mirrors the reported regression: local router + single-model server discovered,
|
|
// while the session runs on an unrelated external provider.
|
|
const candidates = ["http://localhost:8080", "http://127.0.0.1:40909"]
|
|
const providers = [
|
|
{ id: "llamacpp-router", name: "llama.cpp router", options: { baseURL: "http://localhost:8080/v1" } },
|
|
{ id: "ornith", name: "Ornith", options: { baseURL: "http://127.0.0.1:40909/v1" } },
|
|
{ id: "opencode", name: "OpenCode", options: { baseURL: "https://api.example.internal/v1" } },
|
|
]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "opencode"), [])
|
|
})
|
|
|
|
test("selectSessionUrls first match wins for duplicate ids", () => {
|
|
const candidates = ["http://a:1", "http://b:2"]
|
|
const providers = [
|
|
{ id: "dup", name: "first", options: { baseURL: "http://b:2/v1" } },
|
|
{ id: "dup", name: "second", options: { baseURL: "http://a:1" } },
|
|
]
|
|
assert.deepEqual(selectSessionUrls(candidates, providers, "dup"), ["http://b:2"])
|
|
})
|
|
|
|
test("duplicate provider URLs are deduped", () => {
|
|
const providers = [
|
|
{ id: "llamacpp-a", name: "A", options: { baseURL: "http://same:7" } },
|
|
{ id: "llamacpp-b", name: "B", options: { baseURL: "http://same:7/v1" } },
|
|
]
|
|
assert.deepEqual(resolveServerUrls(undefined, providers, {}), ["http://same:7"])
|
|
})
|
|
|
|
// --- V2 provider shape -------------------------------------------------------
|
|
// V2 renames provider config "options" to "settings" and the "provider" map to
|
|
// "providers" (see migrate-v1: "api becomes settings.baseURL"). Provider records
|
|
// returned by the TUI data layer therefore carry settings, not options. These
|
|
// tests pin the shape the plugin actually receives at runtime.
|
|
|
|
const v2LlamaProviders = [
|
|
{
|
|
id: "llama-local",
|
|
name: "llama.cpp (conduct)",
|
|
package: "aisdk:@ai-sdk/openai-compatible",
|
|
settings: { baseURL: "http://<server-host>:8100/v1" },
|
|
models: {},
|
|
},
|
|
]
|
|
|
|
test("V2 provider settings.baseURL is detected", () => {
|
|
assert.deepEqual(resolveServerUrls(undefined, v2LlamaProviders, undefined), [
|
|
"http://<server-host>:8100",
|
|
])
|
|
})
|
|
|
|
test("V2 provider settings.base_url variant is detected", () => {
|
|
const snake = [
|
|
{ id: "llama-local", name: "llama.cpp", settings: { base_url: "http://localhost:9090/v1" } },
|
|
]
|
|
assert.deepEqual(resolveServerUrls(undefined, snake, undefined), ["http://localhost:9090"])
|
|
})
|
|
|
|
test("selectSessionUrls uses V2 provider settings.baseURL", () => {
|
|
assert.deepEqual(
|
|
selectSessionUrls(["http://<server-host>:8100"], v2LlamaProviders, "llama-local"),
|
|
["http://<server-host>:8100"],
|
|
)
|
|
})
|
|
|
|
test("V2 config providers/settings fallback is parsed", () => {
|
|
const v2Config = {
|
|
providers: {
|
|
"llama-local": { settings: { baseURL: "http://localhost:9090/v1" } },
|
|
},
|
|
}
|
|
assert.deepEqual(resolveServerUrls(undefined, undefined, v2Config), ["http://localhost:9090"])
|
|
})
|
|
|
|
test("V2 provider settings still excludes non-candidate providers", () => {
|
|
const providers = [{ id: "llama-local", name: "llama.cpp", settings: { baseURL: "http://other:1" } }]
|
|
assert.deepEqual(selectSessionUrls(["http://localhost:8080"], providers, "llama-local"), [])
|
|
})
|
|
|
|
// --- Strata adapter ---------------------------------------------------------
|
|
// Strata (a llama.cpp-derived server with a Python HTTP layer) exposes a
|
|
// minimal /slots stub and puts the live rates in /metrics.live instead. The
|
|
// plugin detects Strata per server URL and maps those rates directly, rather
|
|
// than deriving PP/TG from /slots deltas.
|
|
|
|
function makeTracker(): TrackerState {
|
|
return {
|
|
prevNdBySlot: {},
|
|
prevPromptTokensBySlot: {},
|
|
isPrefilling: false,
|
|
isGenerating: false,
|
|
prefillSlotId: null,
|
|
prefillCapturedTokens: null,
|
|
prefillStartAt: 0,
|
|
lastPrefillRate: 0,
|
|
generateSlotId: null,
|
|
generatePrevNd: 0,
|
|
generateStartAt: 0,
|
|
lastGeneratedTps: 0,
|
|
}
|
|
}
|
|
|
|
test("serverTypeOverride accepts only an explicit llama or strata", () => {
|
|
assert.equal(serverTypeOverride("strata"), "strata")
|
|
assert.equal(serverTypeOverride("llama"), "llama")
|
|
assert.equal(serverTypeOverride("auto"), undefined)
|
|
assert.equal(serverTypeOverride(undefined), undefined)
|
|
assert.equal(serverTypeOverride(42), undefined)
|
|
})
|
|
|
|
test("isStrataHealth detects the strata service field", () => {
|
|
assert.equal(isStrataHealth({ status: "ok", service: "strata" }), true)
|
|
assert.equal(isStrataHealth({ status: "ok" }), false)
|
|
assert.equal(isStrataHealth({ service: "llama" }), false)
|
|
assert.equal(isStrataHealth(null), false)
|
|
assert.equal(isStrataHealth("strata"), false)
|
|
})
|
|
|
|
test("mapStrataLive reading sets PP from prefill_tok_s_mean", () => {
|
|
const t = makeTracker()
|
|
mapStrataLive({ state: "reading", prefill_tok_s_mean: 1247.4, tok_s: null }, t)
|
|
assert.equal(t.isPrefilling, true)
|
|
assert.equal(t.isGenerating, false)
|
|
assert.equal(t.lastPrefillRate, 1247.4)
|
|
})
|
|
|
|
test("mapStrataLive generating sets TG from tok_s", () => {
|
|
const t = makeTracker()
|
|
mapStrataLive({ state: "generating", tok_s: 25.6, prefill_tok_s_mean: null }, t)
|
|
assert.equal(t.isGenerating, true)
|
|
assert.equal(t.isPrefilling, false)
|
|
assert.equal(t.lastGeneratedTps, 25.6)
|
|
})
|
|
|
|
test("mapStrataLive idle clears both phases", () => {
|
|
const t = makeTracker()
|
|
t.isGenerating = true
|
|
t.lastGeneratedTps = 42
|
|
mapStrataLive({ state: "idle", tok_s: null, prefill_tok_s_mean: null }, t)
|
|
assert.equal(t.isPrefilling, false)
|
|
assert.equal(t.isGenerating, false)
|
|
assert.equal(t.lastGeneratedTps, 0)
|
|
assert.equal(t.lastPrefillRate, 0)
|
|
})
|
|
|
|
test("mapStrataLive unloaded clears both phases", () => {
|
|
const t = makeTracker()
|
|
t.isPrefilling = true
|
|
t.lastPrefillRate = 99
|
|
mapStrataLive({ state: "unloaded" }, t)
|
|
assert.equal(t.isPrefilling, false)
|
|
assert.equal(t.isGenerating, false)
|
|
})
|
|
|
|
test("mapStrataLive tolerates missing live data", () => {
|
|
const t = makeTracker()
|
|
t.isGenerating = true
|
|
t.lastGeneratedTps = 10
|
|
mapStrataLive(null, t)
|
|
assert.equal(t.isGenerating, false)
|
|
assert.equal(t.lastGeneratedTps, 0)
|
|
})
|
|
|
|
test("mapStrataLive treats non-numeric rates as zero", () => {
|
|
const t = makeTracker()
|
|
mapStrataLive({ state: "generating", tok_s: null }, t)
|
|
assert.equal(t.isGenerating, true)
|
|
assert.equal(t.lastGeneratedTps, 0)
|
|
})
|
|
|
|
// --- Model-scoped stats URLs ------------------------------------------------
|
|
// The plugin polls stats with the session's model so a routing layer in front
|
|
// of the backend (one that dispatches by model) can forward the call to the
|
|
// right node. Plain llama.cpp / Strata servers ignore the extra parameter.
|
|
|
|
test("statsUrl appends the model as a query parameter", () => {
|
|
assert.equal(statsUrl("http://h:8100", "/metrics", "m"), "http://h:8100/metrics?model=m")
|
|
assert.equal(statsUrl("http://h:8100", "/health", "qwen 3"), "http://h:8100/health?model=qwen%203")
|
|
})
|
|
|
|
test("statsUrl omits the parameter when there is no model", () => {
|
|
assert.equal(statsUrl("http://h:8100", "/slots"), "http://h:8100/slots")
|
|
assert.equal(statsUrl("http://h:8100", "/metrics", ""), "http://h:8100/metrics")
|
|
})
|
|
|
|
test("serverKindKey distinguishes models on the same server", () => {
|
|
assert.equal(serverKindKey("http://h:8100", "a"), serverKindKey("http://h:8100", "a"))
|
|
assert.notEqual(serverKindKey("http://h:8100", "a"), serverKindKey("http://h:8100", "b"))
|
|
assert.notEqual(serverKindKey("http://h:8100", "a"), serverKindKey("http://h:8100"))
|
|
})
|