mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 17:52:31 +03:00
157 lines
6.1 KiB
TypeScript
157 lines
6.1 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { createServer } from "node:http";
|
|
import { describe, it } from "node:test";
|
|
import { MemorySettingsExtendedSchema } from "../../src/shared/schemas/memory.ts";
|
|
import {
|
|
DEFAULT_MEMORY_SETTINGS,
|
|
normalizeMemorySettings,
|
|
toMemorySettingsUpdates,
|
|
} from "../../src/lib/memory/settings.ts";
|
|
import {
|
|
MemoryCustomEmbeddingConfigError,
|
|
resolveMemoryCustomEmbeddingProvider,
|
|
} from "../../src/lib/memory/embedding/customProvider.ts";
|
|
import { resolveEmbeddingSource } from "../../src/lib/memory/embedding/index.ts";
|
|
import { EMBEDDING_PROVIDERS } from "@omniroute/open-sse/config/embeddingRegistry.ts";
|
|
import { createEmbeddingResponse } from "../../src/lib/embeddings/service.ts";
|
|
|
|
describe("Memory custom embedding endpoint", () => {
|
|
it("keeps the registry-backed behavior when custom fields are empty", () => {
|
|
const parsed = MemorySettingsExtendedSchema.parse({
|
|
customBaseUrl: "",
|
|
customModelId: "",
|
|
});
|
|
assert.equal(parsed.customBaseUrl, null);
|
|
assert.equal(parsed.customModelId, null);
|
|
assert.equal(resolveMemoryCustomEmbeddingProvider(parsed), null);
|
|
});
|
|
|
|
it("normalizes, persists, and resolves a Memory-only OpenAI-compatible provider", () => {
|
|
const settings = normalizeMemorySettings({
|
|
memoryEmbeddingCustomBaseUrl: " http://localhost:8000/v1/ ",
|
|
memoryEmbeddingCustomModelId: " SuperPauly/harrier-oss-v1-0.6b-gguf ",
|
|
});
|
|
assert.equal(settings.customBaseUrl, "http://localhost:8000/v1");
|
|
assert.equal(settings.customModelId, "SuperPauly/harrier-oss-v1-0.6b-gguf");
|
|
|
|
const resolved = resolveMemoryCustomEmbeddingProvider(settings);
|
|
assert.ok(resolved);
|
|
assert.equal(resolved.provider.id, "memory-custom");
|
|
assert.equal(resolved.provider.baseUrl, "http://localhost:8000/v1/embeddings");
|
|
assert.equal(resolved.provider.authType, "none");
|
|
assert.equal(resolved.model, "SuperPauly/harrier-oss-v1-0.6b-gguf");
|
|
assert.equal(EMBEDDING_PROVIDERS["memory-custom"], undefined);
|
|
|
|
const resolution = resolveEmbeddingSource({
|
|
embeddingSource: "remote",
|
|
embeddingProviderModel: null,
|
|
customBaseUrl: settings.customBaseUrl,
|
|
customModelId: settings.customModelId,
|
|
});
|
|
assert.equal(resolution.source, "remote");
|
|
assert.equal(resolution.model, "memory-custom/SuperPauly/harrier-oss-v1-0.6b-gguf");
|
|
assert.match(resolution.signature, /localhost:8000/);
|
|
|
|
assert.deepEqual(
|
|
toMemorySettingsUpdates({
|
|
customBaseUrl: settings.customBaseUrl,
|
|
customModelId: settings.customModelId,
|
|
}),
|
|
{
|
|
memoryEmbeddingCustomBaseUrl: "http://localhost:8000/v1",
|
|
memoryEmbeddingCustomModelId: "SuperPauly/harrier-oss-v1-0.6b-gguf",
|
|
}
|
|
);
|
|
});
|
|
|
|
it("preserves an endpoint that already ends in /embeddings", () => {
|
|
const resolved = resolveMemoryCustomEmbeddingProvider({
|
|
customBaseUrl: "https://embeddings.example.test/v1/embeddings/",
|
|
customModelId: "custom-model",
|
|
});
|
|
assert.equal(resolved?.provider.baseUrl, "https://embeddings.example.test/v1/embeddings");
|
|
});
|
|
|
|
it("rejects malformed, non-http, credential-bearing, query-bearing, and metadata URLs", () => {
|
|
const blocked = [
|
|
"not-a-url",
|
|
"file:///tmp/embeddings",
|
|
"https://user:secret@example.test/v1",
|
|
"https://example.test/v1?api_key=secret",
|
|
"http://169.254.169.254/latest/meta-data",
|
|
];
|
|
for (const customBaseUrl of blocked) {
|
|
if (customBaseUrl !== "http://169.254.169.254/latest/meta-data") {
|
|
assert.equal(MemorySettingsExtendedSchema.safeParse({ customBaseUrl }).success, false);
|
|
}
|
|
assert.throws(
|
|
() =>
|
|
resolveMemoryCustomEmbeddingProvider({
|
|
customBaseUrl,
|
|
customModelId: "custom-model",
|
|
}),
|
|
(error: unknown) => {
|
|
assert.ok(error instanceof MemoryCustomEmbeddingConfigError);
|
|
assert.equal(error.message, "Custom embedding endpoint is invalid or blocked");
|
|
assert.equal(error.message.includes(customBaseUrl), false);
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
it("requires both custom fields and leaves defaults disabled", () => {
|
|
assert.equal(DEFAULT_MEMORY_SETTINGS.customBaseUrl, null);
|
|
assert.equal(DEFAULT_MEMORY_SETTINGS.customModelId, null);
|
|
assert.throws(
|
|
() =>
|
|
resolveMemoryCustomEmbeddingProvider({
|
|
customBaseUrl: "http://localhost:8000/v1",
|
|
customModelId: null,
|
|
}),
|
|
MemoryCustomEmbeddingConfigError
|
|
);
|
|
});
|
|
|
|
it("dispatches the custom model to a disposable OpenAI-compatible server", async () => {
|
|
let receivedPath = "";
|
|
let receivedBody: Record<string, unknown> | null = null;
|
|
const server = createServer((request, response) => {
|
|
receivedPath = request.url ?? "";
|
|
const chunks: Buffer[] = [];
|
|
request.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
request.on("end", () => {
|
|
receivedBody = JSON.parse(Buffer.concat(chunks).toString("utf8")) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
response.writeHead(200, { "Content-Type": "application/json" });
|
|
response.end(JSON.stringify({ data: [{ embedding: [0.1, 0.2, 0.3] }] }));
|
|
});
|
|
});
|
|
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
|
|
try {
|
|
const address = server.address();
|
|
assert.ok(address && typeof address === "object");
|
|
const custom = resolveMemoryCustomEmbeddingProvider({
|
|
customBaseUrl: `http://127.0.0.1:${address.port}/v1`,
|
|
customModelId: "custom-model",
|
|
});
|
|
assert.ok(custom);
|
|
const response = await createEmbeddingResponse(
|
|
{ model: "memory-custom/custom-model", input: "hello" },
|
|
{ resolvedProvider: custom.provider, resolvedModel: custom.model }
|
|
);
|
|
assert.equal(response.status, 200);
|
|
assert.equal(receivedPath, "/v1/embeddings");
|
|
assert.equal(receivedBody?.model, "custom-model");
|
|
assert.equal(EMBEDDING_PROVIDERS["memory-custom"], undefined);
|
|
} finally {
|
|
await new Promise<void>((resolve, reject) =>
|
|
server.close((error) => (error ? reject(error) : resolve()))
|
|
);
|
|
}
|
|
});
|
|
});
|