Files
Mastermind/test/localProviders.test.js
T

152 lines
4.8 KiB
JavaScript

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));
});