- executors/zed.js: use exact wire values (anthropic, open_ai, google, x_ai) and strip incompatible Vertex safetySettings on the Google path - shared/zedAuth.js: robust callback query parsing, reject garbage PKCS#1 v1.5 decryptions, and thread proxyOptions when fetching LLM tokens - oauth: preserve systemId across authorize/register/exchange lifecycle, renew proxy idle timeout on reuse, and ignore non-callback localhost requests - shared/OAuthModal.js: track owned proxy in flowRef and stop at most once - api/providers/[id]/models: add connection-scoped live Zed model resolver - registry: unhide provider in dashboard - tests: add unit coverage for wire format, native auth, and live models
122 lines
4.2 KiB
JavaScript
122 lines
4.2 KiB
JavaScript
// Zed completions wire acceptance: the `provider` field of POST /completions
|
|
// must use cloud.zed.dev's exact wire values (anthropic/open_ai/google/x_ai),
|
|
// and the Zed Gemini path must not carry the shared translator's
|
|
// safetySettings (Zed's hosted Gemini backend speaks the Vertex safety
|
|
// vocabulary, not the public-Gemini enums).
|
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
|
|
vi.mock("open-sse/shared/zedAuth.js", async (importOriginal) => {
|
|
const actual = await importOriginal();
|
|
return {
|
|
...actual,
|
|
resolveZedModels: vi.fn(),
|
|
zedLlmFetch: vi.fn(),
|
|
};
|
|
});
|
|
|
|
import {
|
|
resolveZedModels,
|
|
zedLlmFetch,
|
|
} from "open-sse/shared/zedAuth.js";
|
|
import ZedExecutor from "open-sse/executors/zed.js";
|
|
|
|
function catalogFor(entries) {
|
|
const rawById = new Map(entries);
|
|
return { rawById, models: [] };
|
|
}
|
|
|
|
function mockCatalogFetch(captured) {
|
|
zedLlmFetch.mockImplementation(async (credentials, path, options) => {
|
|
captured.body = JSON.parse(options.fetchOptions.body);
|
|
return new Response("upstream-error-stub", { status: 500 });
|
|
});
|
|
}
|
|
|
|
function makeExecutor() {
|
|
const executor = new ZedExecutor();
|
|
executor.config = {};
|
|
return executor;
|
|
}
|
|
|
|
const CHAT_BODY = { messages: [{ role: "user", content: "hi" }] };
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("wire provider enum", () => {
|
|
it.each([
|
|
["Anthropic", "anthropic"],
|
|
["anthropic", "anthropic"],
|
|
["OpenAi", "open_ai"],
|
|
["open_ai", "open_ai"],
|
|
["Google", "google"],
|
|
["gemini", "google"],
|
|
["XAi", "x_ai"],
|
|
["x_ai", "x_ai"],
|
|
])("catalog provider %j normalizes to wire %j", async (catalogValue, wire) => {
|
|
resolveZedModels.mockResolvedValue(catalogFor([["m", { provider: catalogValue }]]));
|
|
const executor = makeExecutor();
|
|
const { provider } = await executor.resolveModel("m", {}, null, null);
|
|
expect(provider).toBe(wire);
|
|
});
|
|
|
|
it("infers wire provider from the model id when the catalog is unavailable", async () => {
|
|
resolveZedModels.mockRejectedValue(new Error("catalog down"));
|
|
const executor = makeExecutor();
|
|
const log = { warn: vi.fn() };
|
|
expect((await executor.resolveModel("claude-opus-x", {}, null, log)).provider).toBe("anthropic");
|
|
expect((await executor.resolveModel("gemini-3-x", {}, null, log)).provider).toBe("google");
|
|
expect((await executor.resolveModel("grok-4-x", {}, null, log)).provider).toBe("x_ai");
|
|
expect((await executor.resolveModel("gpt-5-x", {}, null, log)).provider).toBe("open_ai");
|
|
});
|
|
});
|
|
|
|
describe("completion payload shaping", () => {
|
|
it("sends wire provider values per model family", async () => {
|
|
resolveZedModels.mockImplementation(async () => catalogFor([
|
|
["claude-x", { provider: "anthropic" }],
|
|
["gpt-x", { provider: "open_ai" }],
|
|
["gemini-x", { provider: "google" }],
|
|
["grok-x", { provider: "x_ai" }],
|
|
]));
|
|
const captured = {};
|
|
mockCatalogFetch(captured);
|
|
const executor = makeExecutor();
|
|
|
|
for (const [model, wire] of [
|
|
["claude-x", "anthropic"],
|
|
["gpt-x", "open_ai"],
|
|
["gemini-x", "google"],
|
|
["grok-x", "x_ai"],
|
|
]) {
|
|
await executor.execute({ model, body: { ...CHAT_BODY }, stream: false, credentials: {} });
|
|
expect(captured.body.provider).toBe(wire);
|
|
expect(captured.body.model).toBe(model);
|
|
}
|
|
});
|
|
|
|
it("strips safetySettings on the Zed Gemini path only", async () => {
|
|
resolveZedModels.mockImplementation(async () => catalogFor([
|
|
["gemini-x", { provider: "google" }],
|
|
["claude-x", { provider: "anthropic" }],
|
|
]));
|
|
const captured = {};
|
|
mockCatalogFetch(captured);
|
|
const executor = makeExecutor();
|
|
|
|
await executor.execute({ model: "gemini-x", body: { ...CHAT_BODY }, stream: false, credentials: {} });
|
|
expect(captured.body.provider).toBe("google");
|
|
expect(captured.body.provider_request).not.toHaveProperty("safetySettings");
|
|
|
|
// Sanity: the shared translator still emits safetySettings — the removal
|
|
// happens in the Zed executor, not in shared/native Gemini behavior.
|
|
const { openaiToGeminiRequest } = await import(
|
|
"open-sse/translator/request/openai-to-gemini.js"
|
|
);
|
|
expect(openaiToGeminiRequest("gemini-x", { ...CHAT_BODY }, true)).toHaveProperty(
|
|
"safetySettings",
|
|
);
|
|
});
|
|
});
|