const test = require("node:test"); const assert = require("node:assert/strict"); const http = require("node:http"); const WebSocket = require("ws"); const { buildChatMessages, isLoopbackUrl, NemotronSidecarClient, normalizeOpenAiBaseUrl, streamLmStudioChat, } = require("../src/utils/localProviders"); test("normalizes LM Studio OpenAI-compatible base URLs without selecting models automatically", () => { assert.equal( normalizeOpenAiBaseUrl("http://127.0.0.1:1234"), "http://127.0.0.1:1234/v1", ); assert.equal( normalizeOpenAiBaseUrl("http://127.0.0.1:1234/v1/"), "http://127.0.0.1:1234/v1", ); }); test("detects loopback URLs but does not reject non-local URLs", () => { assert.equal(isLoopbackUrl("http://localhost:1234/v1"), true); assert.equal(isLoopbackUrl("http://127.0.0.1:1234/v1"), true); assert.equal(isLoopbackUrl("http://[::1]:1234/v1"), true); assert.equal(isLoopbackUrl("http://192.168.1.40:1234/v1"), false); assert.equal(isLoopbackUrl("https://example.com/v1"), false); }); test("builds OpenAI-compatible image messages for local screenshot analysis", () => { const messages = buildChatMessages({ systemPrompt: "Be useful.", history: [{ role: "assistant", content: "Previous answer" }], userText: "Analyze this screen", imageBase64: "abc123", }); assert.deepEqual(messages[0], { role: "system", content: "Be useful." }); assert.equal(messages[1].role, "assistant"); assert.equal(messages[2].role, "user"); assert.equal(messages[2].content[0].type, "text"); assert.equal(messages[2].content[1].type, "image_url"); assert.equal( messages[2].content[1].image_url.url, "data:image/jpeg;base64,abc123", ); }); test("requires a manually configured LM Studio model id", async () => { await assert.rejects( () => streamLmStudioChat({ baseUrl: "http://127.0.0.1:1234/v1", model: "", messages: [{ role: "user", content: "hello" }], }), /model id is required/, ); }); test("streams LM Studio chat completion tokens from an OpenAI-compatible endpoint", async () => { let receivedBody = null; const server = http.createServer((req, res) => { assert.equal(req.method, "POST"); assert.equal(req.url, "/v1/chat/completions"); let raw = ""; req.setEncoding("utf8"); req.on("data", (chunk) => { raw += chunk; }); req.on("end", () => { receivedBody = JSON.parse(raw); res.writeHead(200, { "Content-Type": "text/event-stream", }); res.write('data: {"choices":[{"delta":{"content":"hel"}}]}\n\n'); res.write('data: {"choices":[{"delta":{"content":"lo"}}]}\n\n'); res.end("data: [DONE]\n\n"); }); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const { port } = server.address(); const accumulations = []; const fullText = await streamLmStudioChat({ baseUrl: `http://127.0.0.1:${port}/v1`, model: "manual-gemma4", messages: [{ role: "user", content: "hello" }], onToken: (_token, accumulated) => accumulations.push(accumulated), }); assert.equal(fullText, "hello"); assert.deepEqual(accumulations, ["hel", "hello"]); assert.equal(receivedBody.model, "manual-gemma4"); assert.equal(receivedBody.stream, true); assert.deepEqual(receivedBody.messages, [{ role: "user", content: "hello" }]); await new Promise((resolve) => server.close(resolve)); }); test("Nemotron sidecar client sends start JSON, binary PCM, and emits final transcripts", async () => { const server = new WebSocket.Server({ host: "127.0.0.1", port: 0 }); await new Promise((resolve) => server.once("listening", resolve)); const { port } = server.address(); const received = []; server.on("connection", (socket) => { socket.on("message", (data, isBinary) => { if (isBinary) { received.push({ isBinary, data: Buffer.from(data) }); socket.send(JSON.stringify({ type: "final", text: "hello world" })); return; } received.push({ isBinary, data: JSON.parse(data.toString("utf8")) }); socket.send(JSON.stringify({ type: "ready" })); }); }); const client = new NemotronSidecarClient({ url: `ws://127.0.0.1:${port}/v1/asr/stream`, language: "en-US", }); const finalPromise = new Promise((resolve) => client.once("final", resolve)); await client.connect(); client.sendAudio(Buffer.from([1, 2, 3, 4])); assert.equal(await finalPromise, "hello world"); assert.deepEqual(received[0], { isBinary: false, data: { type: "start", sampleRate: 16000, channels: 1, encoding: "pcm_s16le", language: "en-US", }, }); assert.equal(received[1].isBinary, true); assert.deepEqual([...received[1].data], [1, 2, 3, 4]); client.close(); await new Promise((resolve) => server.close(resolve)); });