evOo-local/packages/adapters/src/pi-openai-compatible-provider.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

372 lines
14 KiB
TypeScript

import { builtinModels } from "@earendil-works/pi-ai/providers/all";
import { OPENAI_COMPATIBLE_PROVIDER_ID } from "@rakazo/contracts";
import { describe, expect, it } from "vitest";
import { buildModelConnectPlaintext } from "./model-connect.js";
import { listPiCatalog } from "./pi-models.js";
import { parseModelSecret, secretValuesToRedact, serializeModelSecret } from "./pi-oauth.js";
import {
createOpenAiCompatibleFetch,
createOpenAiCompatibleLookup,
OPENAI_COMPATIBLE_CATALOG_MODEL_ID,
openAiCompatibleCatalogProvider,
prepareOpenAiCompatibleConnect,
probeOpenAiCompatibleModels,
registerOpenAiCompatibleRuntime,
} from "./pi-openai-compatible-provider.js";
describe("model connect", () => {
it("serializes keyless openai-compatible credentials", () => {
const plaintext = buildModelConnectPlaintext({
provider: OPENAI_COMPATIBLE_PROVIDER_ID,
baseUrl: "http://127.0.0.1:8000",
modelId: "qwen3-4b",
});
const parsed = parseModelSecret(plaintext);
expect(parsed).toEqual({
kind: "openai_compatible",
baseUrl: "http://127.0.0.1:8000/v1",
});
expect(secretValuesToRedact(parsed)).toEqual([]);
});
it("still requires hosted providers to supply a real API key", () => {
expect(() => buildModelConnectPlaintext({ provider: "openrouter", apiKey: "short" })).toThrow(
/at least 8 characters/,
);
});
it("round-trips optional openai-compatible API keys", () => {
const secret = {
kind: "openai_compatible" as const,
baseUrl: "http://127.0.0.1:8000/v1",
apiKey: "local-secret",
};
const parsed = parseModelSecret(serializeModelSecret(secret));
expect(parsed).toEqual(secret);
expect(secretValuesToRedact(parsed)).toEqual(["local-secret"]);
});
it("keeps private http openai-compatible connects when an API key is set", () => {
const plaintext = buildModelConnectPlaintext({
provider: OPENAI_COMPATIBLE_PROVIDER_ID,
baseUrl: "http://127.0.0.1:8000",
modelId: "local-model",
apiKey: "local-secret",
});
expect(parseModelSecret(plaintext)).toEqual({
kind: "openai_compatible",
baseUrl: "http://127.0.0.1:8000/v1",
apiKey: "local-secret",
});
});
it("rejects public http openai-compatible connects when an API key is set", () => {
const previous = process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = "1";
try {
expect(() =>
prepareOpenAiCompatibleConnect({
provider: OPENAI_COMPATIBLE_PROVIDER_ID,
baseUrl: "http://api.example.com/v1",
modelId: "my-model-id",
apiKey: "secret-key",
}),
).toThrow(/must use HTTPS/);
expect(() =>
buildModelConnectPlaintext({
provider: OPENAI_COMPATIBLE_PROVIDER_ID,
baseUrl: "http://api.example.com/v1",
modelId: "my-model-id",
apiKey: "secret-key",
}),
).toThrow(/must use HTTPS/);
} finally {
if (previous === undefined) delete process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
else process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = previous;
}
});
it("allows public https openai-compatible connects when an API key is set", () => {
const previous = process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = "1";
try {
const prepared = prepareOpenAiCompatibleConnect({
provider: OPENAI_COMPATIBLE_PROVIDER_ID,
baseUrl: "https://api.example.com/v1",
modelId: "my-model-id",
apiKey: "secret-key",
});
expect(prepared).toEqual({
baseUrl: "https://api.example.com/v1",
modelId: "my-model-id",
apiKey: "secret-key",
});
} finally {
if (previous === undefined) delete process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
else process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = previous;
}
});
});
describe("openai-compatible provider", () => {
it("does not treat replaced Request Authorization as keyed for public http", async () => {
const previous = process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = "1";
try {
const request = new Request("http://api.example.com/v1/models", {
headers: { Authorization: "Bearer secret" },
});
const safeFetch = createOpenAiCompatibleFetch(
async () => new Response("{}", { status: 200 }),
);
await expect(
safeFetch(request, { headers: { Accept: "application/json" } }),
).resolves.toMatchObject({ status: 200 });
} finally {
if (previous === undefined) delete process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
else process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = previous;
}
});
it("drives the guarded dispatcher with a fetch from the same undici", async () => {
// See remote-mcp.test: failing inside the lookup proves the request was
// dispatched through the Agent rather than rejected by a mismatched fetch.
const previous = process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = "1";
try {
const safeFetch = createOpenAiCompatibleFetch(undefined, async () => {
throw new Error("lookup reached");
});
await expect(safeFetch("https://models.example.test/v1/models")).rejects.toMatchObject({
cause: { message: "lookup reached" },
});
} finally {
if (previous === undefined) delete process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
else process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = previous;
}
});
it("flattens a global Request so the package fetch can dispatch it", async () => {
// undici's fetch reads a foreign Request as "[object Request]"; reaching
// the lookup proves the request was dispatched with its URL instead.
const previous = process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = "1";
try {
const safeFetch = createOpenAiCompatibleFetch(undefined, async () => {
throw new Error("lookup reached");
});
const request = new Request("https://models.example.test/v1/chat/completions", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ model: "local" }),
});
await expect(safeFetch(request)).rejects.toMatchObject({
cause: { message: "lookup reached" },
});
} finally {
if (previous === undefined) delete process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC;
else process.env.RAKAZO_OPENAI_COMPAT_ALLOW_PUBLIC = previous;
}
});
it("carries a Request's method, headers and body through as init", async () => {
let seen: { url: string; init?: RequestInit } | undefined;
const safeFetch = createOpenAiCompatibleFetch(async (input, init) => {
seen = { url: String(input), init };
return new Response("{}", { status: 200 });
});
await safeFetch(
new Request("http://127.0.0.1:8000/v1/models", {
method: "POST",
headers: { "x-trace": "1" },
body: "payload",
}),
{ headers: { "x-trace": "2" } },
);
expect(seen?.url).toBe("http://127.0.0.1:8000/v1/models");
expect(seen?.init?.method).toBe("POST");
expect(new Headers(seen?.init?.headers).get("x-trace")).toBe("2");
expect(Buffer.from(seen?.init?.body as ArrayBuffer).toString()).toBe("payload");
});
it("rejects public hostnames that resolve to private addresses", async () => {
const lookup = createOpenAiCompatibleLookup(
new URL("https://models.example.test/v1"),
async () => [{ address: "127.0.0.1", family: 4 }],
);
const error = await new Promise<Error | null>((resolve) => {
lookup("models.example.test", { family: 0, all: false }, (lookupError) => {
resolve(lookupError);
});
});
expect(error).toMatchObject({
message: "Public model server hostname resolved to a private address",
});
});
it("rejects local hostnames that resolve to link-local addresses", async () => {
const lookup = createOpenAiCompatibleLookup(new URL("http://localhost:8000/v1"), async () => [
{ address: "169.254.1.1", family: 4 },
]);
const error = await new Promise<Error | null>((resolve) => {
lookup("localhost", { family: 0, all: false }, (lookupError) => resolve(lookupError));
});
expect(error).toMatchObject({
message: "Local model server hostname resolved outside the private network",
});
});
it.each([
["100.100.100.200", 4],
["fd00:ec2::254", 6],
])("rejects local hostnames that resolve to metadata address %s", async (address, family) => {
const lookup = createOpenAiCompatibleLookup(new URL("http://localhost:8000/v1"), async () => [
{ address, family },
]);
const error = await new Promise<Error | null>((resolve) => {
lookup("localhost", { family: 0, all: false }, (lookupError) => resolve(lookupError));
});
expect(error).toMatchObject({
message: "Model server hostname resolved to a blocked metadata address",
});
});
it("always exposes a catalog provider entry", () => {
const provider = openAiCompatibleCatalogProvider();
expect(provider.id).toBe(OPENAI_COMPATIBLE_PROVIDER_ID);
expect(provider.getModels()[0]?.id).toBe(OPENAI_COMPATIBLE_CATALOG_MODEL_ID);
});
it("registers runtime models at the stored base URL", () => {
const models = registerOpenAiCompatibleRuntime(builtinModels(), {
modelId: "rapid-mlx",
baseUrl: "http://127.0.0.1:8000/v1",
maxTokens: 8192,
contextWindow: 65536,
});
const model = models.getModel(OPENAI_COMPATIBLE_PROVIDER_ID, "rapid-mlx");
expect(model?.baseUrl).toBe("http://127.0.0.1:8000/v1");
expect(model?.api).toBe("openai-completions");
expect(model?.maxTokens).toBe(8192);
expect(model?.contextWindow).toBe(65536);
});
it("probes /v1/models with mocked fetch", async () => {
const fetchImpl = async () =>
new Response(JSON.stringify({ object: "list", data: [{ id: "a" }, { id: "b" }] }), {
status: 200,
});
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).resolves.toEqual(["a", "b"]);
});
it("still accepts legacy models[] probe responses", async () => {
const fetchImpl = async () =>
new Response(JSON.stringify({ models: [{ id: "legacy" }] }), { status: 200 });
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).resolves.toEqual(["legacy"]);
});
it("rejects redirect responses during probing", async () => {
const fetchImpl: typeof fetch = async (_input, init) => {
expect(init?.redirect).toBe("error");
return new Response(null, {
status: 302,
headers: { location: "http://127.0.0.1:9/blocked" },
});
};
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/redirect.*explicit model id/is);
});
it("clarifies non-2xx probe failures with status and hand-fill hint", async () => {
const fetchImpl = async () => new Response("nope", { status: 404 });
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/returned 404.*You can still Connect with an explicit model id/s);
});
it("clarifies missing models list with hand-fill hint", async () => {
const fetchImpl = async () => new Response(JSON.stringify({ object: "list" }), { status: 200 });
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/did not include a models list.*explicit model id/s);
});
it("clarifies invalid JSON probe bodies with hand-fill hint", async () => {
const fetchImpl = async () => new Response("{not-json", { status: 200 });
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/invalid JSON.*explicit model id/s);
});
it("clarifies empty probe responses with hand-fill hint", async () => {
const fetchImpl = async () => new Response(null, { status: 200 });
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/empty response.*explicit model id/s);
});
it("rejects oversized model lists", async () => {
const fetchImpl = async () => new Response("x".repeat(64 * 1024 + 1));
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/too large.*explicit model id/is);
});
it("cancels model responses whose declared size exceeds the limit", async () => {
let cancelled = false;
const fetchImpl = async () =>
new Response(
new ReadableStream({
cancel() {
cancelled = true;
},
}),
{ headers: { "content-length": String(64 * 1024 + 1) } },
);
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/too large.*explicit model id/is);
expect(cancelled).toBe(true);
});
it("clarifies probe timeouts with hand-fill hint", async () => {
const fetchImpl: typeof fetch = async (_input, init) =>
new Promise((_, reject) => {
init?.signal?.addEventListener("abort", () => {
reject(new DOMException("The operation was aborted.", "AbortError"));
});
});
await expect(
probeOpenAiCompatibleModels({ baseUrl: "http://127.0.0.1:8000/v1" }, fetchImpl),
).rejects.toThrow(/timed out.*explicit model id/is);
});
it("preserves caller AbortError without rewriting as timeout", async () => {
const caller = new AbortController();
caller.abort();
const fetchImpl: typeof fetch = async () => {
throw new DOMException("The operation was aborted.", "AbortError");
};
await expect(
probeOpenAiCompatibleModels(
{ baseUrl: "http://127.0.0.1:8000/v1" },
fetchImpl,
caller.signal,
),
).rejects.toMatchObject({ name: "AbortError" });
});
it("lists openai-compatible in the catalog even without RAKAZO_LOCAL_MODELS", () => {
delete process.env.RAKAZO_LOCAL_MODELS;
const entries = listPiCatalog().filter(
(entry) => entry.provider === OPENAI_COMPATIBLE_PROVIDER_ID,
);
expect(entries.length).toBeGreaterThan(0);
});
});