diff --git a/src/stats.ts b/src/stats.ts index dda2709..0dee631 100644 --- a/src/stats.ts +++ b/src/stats.ts @@ -241,7 +241,27 @@ export function extractProviderUrls(config: unknown): string[] { return urls } -export function resolveServerUrls(options: unknown, config: unknown): string[] { +export function extractProviderUrlsFromList(providers: unknown): string[] { + if (!Array.isArray(providers)) return [] + const urls: string[] = [] + for (const entry of providers) { + if (!entry || typeof entry !== "object") continue + const provider = entry as Record + const id = typeof provider.id === "string" ? provider.id.toLowerCase() : "" + const name = typeof provider.name === "string" ? provider.name.toLowerCase() : "" + const hay = `${id} ${name}` + if (!hay.includes("llama") || hay.includes("ollama")) continue + const opts = provider.options + if (!opts || typeof opts !== "object") continue + const baseURL = (opts as Record).baseURL || (opts as Record).base_url + if (typeof baseURL === "string") { + urls.push(stripBaseUrlPath(baseURL)) + } + } + return urls +} + +export function resolveServerUrls(options: unknown, providers: unknown, config: unknown): string[] { const opts = options && typeof options === "object" ? (options as Record) : undefined const server = opts?.server const explicit = typeof server === "string" && server.trim() !== "" ? stripBaseUrlPath(server) : undefined @@ -251,6 +271,8 @@ export function resolveServerUrls(options: unknown, config: unknown): string[] { if (server !== undefined) { console.warn("[oc-ls-stats] ignoring invalid 'server' plugin option, falling back to provider detection") } - const detected = extractProviderUrls(config) - return detected.length > 0 ? detected : ["http://localhost:8080"] + const detected = extractProviderUrlsFromList(providers) + if (detected.length > 0) return detected + const legacy = extractProviderUrls(config) + return legacy.length > 0 ? legacy : ["http://localhost:8080"] } diff --git a/test-backoff.ts b/test-backoff.ts index 78b0734..887a812 100644 --- a/test-backoff.ts +++ b/test-backoff.ts @@ -27,67 +27,117 @@ test("uses provided base and max", () => { 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 detection", () => { +test("explicit server wins over provider list", () => { assert.deepEqual( - resolveServerUrls({ server: "http://headless.local:8080" }, llamaConfig), + 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" }, {}), + resolveServerUrls({ server: "http://headless.local:8080/v1" }, llamaProviders, {}), ["http://headless.local:8080"], ) }) -test("non-string server falls back to detection", () => { - assert.deepEqual(resolveServerUrls({ server: 42 }, llamaConfig), [ +test("non-string server falls back to provider list", () => { + assert.deepEqual(resolveServerUrls({ server: 42 }, llamaProviders, {}), [ "http://localhost:9090", ]) }) -test("empty server falls back to detection", () => { - assert.deepEqual(resolveServerUrls({ server: "" }, llamaConfig), [ +test("empty server falls back to provider list", () => { + assert.deepEqual(resolveServerUrls({ server: "" }, llamaProviders, {}), [ "http://localhost:9090", ]) }) -test("no options uses detected providers", () => { - assert.deepEqual(resolveServerUrls(undefined, llamaConfig), [ +test("scheme-less server falls back to provider list", () => { + assert.deepEqual(resolveServerUrls({ server: "localhost:8080" }, llamaProviders, {}), [ "http://localhost:9090", ]) }) -test("no options and no matching providers falls back to localhost", () => { - assert.deepEqual( - resolveServerUrls(undefined, { - provider: { vision: { options: { baseURL: "http://headless.local:8080/v1" } } }, - }), - ["http://localhost:8080"], - ) -}) - -test("scheme-less server falls back to detection", () => { - assert.deepEqual(resolveServerUrls({ server: "localhost:8080" }, llamaConfig), [ +test("whitespace-only server falls back to provider list", () => { + assert.deepEqual(resolveServerUrls({ server: " " }, llamaProviders, {}), [ "http://localhost:9090", ]) }) -test("whitespace-only server falls back to detection", () => { - assert.deepEqual(resolveServerUrls({ server: " " }, llamaConfig), [ +test("null server falls back to provider list", () => { + assert.deepEqual(resolveServerUrls({ server: null }, llamaProviders, {}), [ "http://localhost:9090", ]) }) -test("null server falls back to detection", () => { - assert.deepEqual(resolveServerUrls({ server: null }, llamaConfig), [ +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", + ]) +})