Files
Troed Sångberg aa5ae2b724 fix: stop polling llama-servers during external-model sessions
selectSessionUrls fell back to all discovered servers when the current
session's provider matched no candidate, so external sessions kept
polling local servers: one answered /slots with idle slots (showing
'- tps (TG)') while another rejected unknown models with HTTP 400
(flickering 'n/a'). Narrow to zero URLs instead and surface n/a.
2026-08-21 15:52:50 +02:00

242 lines
8.6 KiB
TypeScript

import { test } from "node:test"
import assert from "node:assert/strict"
import { backoffDelayMs, resolveServerUrls, selectSessionUrls } 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://headless.local: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://headless.local:8080" }, llamaProviders, {}),
["http://headless.local:8080"],
)
})
test("explicit server strips /v1 path", () => {
assert.deepEqual(
resolveServerUrls({ server: "http://headless.local:8080/v1" }, llamaProviders, {}),
["http://headless.local: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://headless.local: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"])
})