154 lines
4.9 KiB
TypeScript
154 lines
4.9 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
buildProviderHeaders,
|
|
buildProviderUrl,
|
|
getProviderConfig,
|
|
getProviderFallbackCount,
|
|
getTargetFormat,
|
|
hasThinkingConfig,
|
|
isClaudeCodeCompatible,
|
|
isLastMessageFromUser,
|
|
normalizeThinkingConfig,
|
|
} from "../../open-sse/services/provider.ts";
|
|
|
|
test("OpenAI-compatible providers resolve responses URLs and formats", () => {
|
|
const config = getProviderConfig("openai-compatible-responses-demo");
|
|
const url = buildProviderUrl("openai-compatible-responses-demo", "gpt-4.1", false, {
|
|
baseUrl: "https://proxy.example.com/v1/",
|
|
});
|
|
|
|
assert.equal(config.format, "openai-responses");
|
|
assert.equal(url, "https://proxy.example.com/v1/responses");
|
|
assert.equal(getTargetFormat("openai-compatible-responses-demo"), "openai-responses");
|
|
assert.equal(getProviderFallbackCount("openai-compatible-responses-demo"), 1);
|
|
});
|
|
|
|
test("OpenAI-compatible legacy providers honor providerSpecificData.apiType", () => {
|
|
const providerSpecificData = {
|
|
apiType: "responses",
|
|
baseUrl: "https://legacy-proxy.example.com/v1/",
|
|
};
|
|
const config = getProviderConfig("openai-compatible-sp-openai", providerSpecificData);
|
|
const url = buildProviderUrl("openai-compatible-sp-openai", "gpt-5.4", false, {
|
|
providerSpecificData,
|
|
});
|
|
|
|
assert.equal(config.format, "openai-responses");
|
|
assert.equal(url, "https://legacy-proxy.example.com/v1/responses");
|
|
assert.equal(
|
|
getTargetFormat("openai-compatible-sp-openai", providerSpecificData),
|
|
"openai-responses"
|
|
);
|
|
});
|
|
|
|
test("Anthropic-compatible Claude Code providers use the Claude Code URL and headers", () => {
|
|
const url = buildProviderUrl("anthropic-compatible-cc-demo", "claude-sonnet-4-6", false, {
|
|
baseUrl: "https://proxy.example.com/v1/messages?beta=true",
|
|
});
|
|
const headers = buildProviderHeaders(
|
|
"anthropic-compatible-cc-demo",
|
|
{
|
|
apiKey: "anthropic-token",
|
|
providerSpecificData: { ccSessionId: "session-123" },
|
|
},
|
|
false
|
|
);
|
|
|
|
assert.equal(isClaudeCodeCompatible("anthropic-compatible-cc-demo"), true);
|
|
assert.equal(url, "https://proxy.example.com/v1/messages?beta=true");
|
|
assert.equal(headers["Authorization"], "Bearer anthropic-token");
|
|
assert.equal(headers.Accept, "application/json");
|
|
assert.equal(headers["X-Claude-Code-Session-Id"], "session-123");
|
|
assert.equal(headers["anthropic-version"], "2023-06-01");
|
|
assert.equal(getTargetFormat("anthropic-compatible-cc-demo"), "claude");
|
|
});
|
|
|
|
test("GitHub provider headers include request IDs and JSON accept for non-streaming requests", () => {
|
|
const headers = buildProviderHeaders(
|
|
"github",
|
|
{
|
|
copilotToken: "copilot-token",
|
|
},
|
|
false
|
|
);
|
|
|
|
assert.equal(headers.Authorization, "Bearer copilot-token");
|
|
assert.equal(typeof headers["x-request-id"], "string");
|
|
assert.match(headers["x-request-id"], /^[a-f0-9-]{36}$/i);
|
|
assert.equal(headers.Accept, "application/json");
|
|
});
|
|
|
|
test("Registry-driven headers support x-goog-api-key and bearer fallback", () => {
|
|
const apiKeyHeaders = buildProviderHeaders(
|
|
"gemini",
|
|
{
|
|
apiKey: "gemini-api-key",
|
|
},
|
|
true
|
|
);
|
|
const accessTokenHeaders = buildProviderHeaders(
|
|
"gemini",
|
|
{
|
|
accessToken: "gemini-access-token",
|
|
},
|
|
false
|
|
);
|
|
|
|
assert.equal(apiKeyHeaders["x-goog-api-key"], "gemini-api-key");
|
|
assert.equal(apiKeyHeaders.Accept, "text/event-stream");
|
|
assert.equal(accessTokenHeaders.Authorization, "Bearer gemini-access-token");
|
|
});
|
|
|
|
test("Registry-driven headers support Key auth", () => {
|
|
const headers = buildProviderHeaders(
|
|
"maritalk",
|
|
{
|
|
apiKey: "maritalk-key",
|
|
},
|
|
true
|
|
);
|
|
|
|
assert.equal(headers.Authorization, "Key maritalk-key");
|
|
assert.equal(headers.Accept, "text/event-stream");
|
|
});
|
|
|
|
test("Unknown providers fall back to bearer auth and OpenAI format", () => {
|
|
const headers = buildProviderHeaders(
|
|
"custom-provider",
|
|
{
|
|
apiKey: "custom-key",
|
|
},
|
|
false
|
|
);
|
|
|
|
assert.equal(headers.Authorization, "Bearer custom-key");
|
|
assert.equal(getTargetFormat("custom-provider"), "openai");
|
|
});
|
|
|
|
test("native thinking config is removed when the last message is not from the user", () => {
|
|
const assistantLast = {
|
|
messages: [
|
|
{ role: "user", content: "hi" },
|
|
{ role: "assistant", content: "hello" },
|
|
],
|
|
reasoning_effort: "high",
|
|
thinking: { type: "enabled" },
|
|
};
|
|
const userLast = {
|
|
messages: [{ role: "user", content: "hi" }],
|
|
reasoning_effort: "medium",
|
|
thinking: { type: "enabled" },
|
|
};
|
|
|
|
const normalized = normalizeThinkingConfig(assistantLast);
|
|
|
|
assert.equal(isLastMessageFromUser({ messages: [] }), true);
|
|
assert.equal(isLastMessageFromUser(assistantLast), false);
|
|
assert.equal(hasThinkingConfig(userLast), true);
|
|
assert.equal(normalized.reasoning_effort, "high");
|
|
assert.equal("thinking" in normalized, false);
|
|
assert.equal(normalizeThinkingConfig(userLast).reasoning_effort, "medium");
|
|
});
|