native first
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
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));
|
||||
});
|
||||
Reference in New Issue
Block a user