Files
2026-07-13 13:39:12 +08:00

516 lines
20 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Unit tests — xAI thinking patcher + inbound translators
*
* Ported from upstream decolua/9router tests/unit/xai-thinking.test.js
* and expanded with coverage for claude/gemini/openai-chat/openai-responses translators.
*
* Runner: node --import tsx/esm --test tests/unit/xai-translators.test.ts
*/
import test from "node:test";
import assert from "node:assert/strict";
const { budgetToEffort, applyThinking, normalizeXaiReasoningEffort } =
await import("../../src/lib/providers/xai/thinking.ts");
const { chatRequestToXaiResponses, xaiCompletedToChatJson } =
await import("../../src/lib/providers/xai/translators/openai-chat.ts");
const { openaiResponsesRequestToXai, xaiCompletedToOpenaiResponses, xaiSseEventToOpenaiResponses } =
await import("../../src/lib/providers/xai/translators/openai-responses.ts");
const { claudeRequestToXaiResponses, xaiCompletedToClaudeJson } =
await import("../../src/lib/providers/xai/translators/claude.ts");
const { geminiRequestToXaiResponses, xaiCompletedToGeminiJson } =
await import("../../src/lib/providers/xai/translators/gemini.ts");
// ─── budgetToEffort ──────────────────────────────────────────────────────────
test("budgetToEffort: maps 0 / negative / NaN to undefined", () => {
assert.equal(budgetToEffort(0), undefined);
assert.equal(budgetToEffort(-100), undefined);
assert.equal(budgetToEffort(Number.NaN), undefined);
assert.equal(budgetToEffort(Number.POSITIVE_INFINITY), undefined);
});
test("budgetToEffort: maps 13999 to low", () => {
assert.equal(budgetToEffort(1), "low");
assert.equal(budgetToEffort(3999), "low");
});
test("budgetToEffort: maps 400015999 to medium", () => {
assert.equal(budgetToEffort(4000), "medium");
assert.equal(budgetToEffort(15999), "medium");
});
test("budgetToEffort: maps 16000+ to high", () => {
assert.equal(budgetToEffort(16000), "high");
assert.equal(budgetToEffort(64000), "high");
});
// ─── applyThinking ───────────────────────────────────────────────────────────
test("applyThinking: returns clone untouched when nothing matches", () => {
const req = { model: "grok-4", input: [] };
const out = applyThinking(req);
assert.deepStrictEqual(out, req);
assert.notStrictEqual(out, req); // must be a new object
});
test("applyThinking: honors xAI-native reasoning.effort verbatim", () => {
const req = { reasoning: { effort: "high" }, foo: 1 };
const out = applyThinking(req as Parameters<typeof applyThinking>[0]);
assert.deepStrictEqual(out.reasoning, { effort: "high" });
assert.equal((out as Record<string, unknown>).foo, 1);
});
test("normalizeXaiReasoningEffort: downgrades max/xhigh to xAI-supported high", () => {
assert.equal(normalizeXaiReasoningEffort("max"), "high");
assert.equal(normalizeXaiReasoningEffort("xhigh"), "high");
assert.equal(normalizeXaiReasoningEffort("HIGH"), "high");
assert.equal(normalizeXaiReasoningEffort("ultra"), undefined);
});
test("applyThinking: normalizes xAI-native max/xhigh to high", () => {
const maxOut = applyThinking({ reasoning: { effort: "max", summary: "auto" } });
assert.deepStrictEqual(maxOut.reasoning, { effort: "high", summary: "auto" });
const xhighOut = applyThinking({ reasoning: { effort: "xhigh" } });
assert.deepStrictEqual(xhighOut.reasoning, { effort: "high" });
});
test("applyThinking: rewrites OpenAI Chat reasoning_effort into reasoning.effort", () => {
const req = { reasoning_effort: "medium" };
const out = applyThinking(req);
assert.deepStrictEqual(out.reasoning, { effort: "medium" });
assert.equal(out.reasoning_effort, undefined);
});
test("applyThinking: rewrites OpenAI Chat max reasoning_effort into high", () => {
const req = { reasoning_effort: "max" };
const out = applyThinking(req);
assert.deepStrictEqual(out.reasoning, { effort: "high" });
assert.equal(out.reasoning_effort, undefined);
});
test("applyThinking: ignores invalid reasoning_effort values", () => {
const req = { reasoning_effort: "ultra" };
const out = applyThinking(req);
assert.equal(out.reasoning, undefined);
});
test("applyThinking: maps Anthropic thinking enabled with budget_tokens", () => {
const req = { thinking: { type: "enabled", budget_tokens: 20000 } };
const out = applyThinking(req);
assert.deepStrictEqual(out.reasoning, { effort: "high" });
assert.equal(out.thinking, undefined);
});
test("applyThinking: defaults Anthropic thinking enabled without budget_tokens to medium", () => {
const req = { thinking: { type: "enabled" } };
const out = applyThinking(req);
assert.deepStrictEqual(out.reasoning, { effort: "medium" });
});
test("applyThinking: strips Anthropic thinking type=disabled without setting reasoning", () => {
const req = { thinking: { type: "disabled" } };
const out = applyThinking(req);
assert.equal(out.reasoning, undefined);
assert.equal(out.thinking, undefined);
});
test("applyThinking: maps Gemini thinkingConfig.thinkingBudget", () => {
const req = { thinkingConfig: { thinkingBudget: 5000 } };
const out = applyThinking(req);
assert.deepStrictEqual(out.reasoning, { effort: "medium" });
assert.equal(out.thinkingConfig, undefined);
});
test("applyThinking: strips Gemini thinkingConfig with budget=0 without setting reasoning", () => {
const req = { thinkingConfig: { thinkingBudget: 0 } };
const out = applyThinking(req);
assert.equal(out.reasoning, undefined);
assert.equal(out.thinkingConfig, undefined);
});
test("applyThinking: applies defaultEffort when nothing else is provided", () => {
const out = applyThinking({}, { defaultEffort: "low" });
assert.deepStrictEqual(out.reasoning, { effort: "low" });
});
// ─── chatRequestToXaiResponses ────────────────────────────────────────────────
test("chatRequestToXaiResponses: converts system message to instructions", () => {
const req = {
model: "grok-4",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello" },
],
};
const out = chatRequestToXaiResponses(req);
assert.equal(out.instructions, "You are a helpful assistant.");
assert.equal(out.input.length, 1);
assert.equal((out.input[0] as Record<string, unknown>).role, "user");
});
test("chatRequestToXaiResponses: converts tool message to function_call_output", () => {
const req = {
model: "grok-4",
messages: [{ role: "tool", content: "result text", tool_call_id: "call_abc" }],
};
const out = chatRequestToXaiResponses(req);
assert.equal(out.input[0].type, "function_call_output");
assert.equal(out.input[0].call_id, "call_abc");
assert.equal(out.input[0].output, "result text");
});
test("chatRequestToXaiResponses: promotes reasoning_effort to reasoning field", () => {
const req = { model: "grok-4", messages: [], reasoning_effort: "high" };
const out = chatRequestToXaiResponses(req);
assert.deepStrictEqual(out.reasoning, { effort: "high" });
});
test("chatRequestToXaiResponses: normalizes max reasoning_effort for xAI", () => {
const req = { model: "grok-4.3", messages: [], reasoning_effort: "max" };
const out = chatRequestToXaiResponses(req);
assert.deepStrictEqual(out.reasoning, { effort: "high" });
});
test("chatRequestToXaiResponses: normalizes reasoning.effort for xAI", () => {
const req = { model: "grok-4.3", messages: [], reasoning: { effort: "max", summary: "auto" } };
const out = chatRequestToXaiResponses(req);
assert.deepStrictEqual(out.reasoning, { effort: "high", summary: "auto" });
});
test("chatRequestToXaiResponses: maps max_tokens to max_output_tokens", () => {
const req = { model: "grok-4", messages: [], max_tokens: 512 };
const out = chatRequestToXaiResponses(req);
assert.equal(out.max_output_tokens, 512);
});
// ─── xaiCompletedToChatJson ──────────────────────────────────────────────────
test("xaiCompletedToChatJson: extracts output_text content into message", () => {
const completed = {
id: "resp_1",
model: "grok-4",
output: [
{
type: "message",
content: [{ type: "output_text", text: "Hello!" }],
},
],
};
const result = xaiCompletedToChatJson(completed) as Record<string, unknown>;
const choices = result.choices as Array<Record<string, unknown>>;
assert.equal(choices[0].finish_reason, "stop");
const message = choices[0].message as Record<string, unknown>;
assert.equal(message.content, "Hello!");
});
test("xaiCompletedToChatJson: maps function_call to tool_calls with finish_reason=tool_calls", () => {
const completed = {
id: "resp_2",
model: "grok-4",
output: [
{
type: "function_call",
call_id: "call_1",
name: "get_weather",
arguments: '{"location":"London"}',
},
],
};
const result = xaiCompletedToChatJson(completed) as Record<string, unknown>;
const choices = result.choices as Array<Record<string, unknown>>;
assert.equal(choices[0].finish_reason, "tool_calls");
const message = choices[0].message as Record<string, unknown>;
const toolCalls = message.tool_calls as Array<Record<string, unknown>>;
assert.equal(toolCalls.length, 1);
assert.equal(toolCalls[0].id, "call_1");
const fn = toolCalls[0].function as Record<string, unknown>;
assert.equal(fn.name, "get_weather");
});
// ─── openaiResponsesRequestToXai ─────────────────────────────────────────────
test("openaiResponsesRequestToXai: drops service_tier", () => {
const req = { model: "grok-4", input: [], service_tier: "default" };
const out = openaiResponsesRequestToXai(req);
assert.equal("service_tier" in out, false);
});
test("openaiResponsesRequestToXai: preserves other fields verbatim", () => {
const req = { model: "grok-4", input: [{ role: "user", content: "hi" }] };
const out = openaiResponsesRequestToXai(req);
assert.deepStrictEqual(out.input, req.input);
});
// ─── xaiCompletedToOpenaiResponses ───────────────────────────────────────────
test("xaiCompletedToOpenaiResponses: normalizes object + status fields", () => {
const completed = { id: "r1", model: "grok-4", output: [] };
const out = xaiCompletedToOpenaiResponses(completed);
assert.equal(out.object, "response");
assert.equal(out.status, "completed");
});
test("xaiCompletedToOpenaiResponses: preserves existing object/status", () => {
const completed = { id: "r1", object: "custom", status: "in_progress" };
const out = xaiCompletedToOpenaiResponses(completed);
assert.equal(out.object, "custom");
assert.equal(out.status, "in_progress");
});
// ─── xaiSseEventToOpenaiResponses ────────────────────────────────────────────
test("xaiSseEventToOpenaiResponses: drops annotation.added event", () => {
const ev = {
event: "response.output_text.annotation.added",
data: '{"text":"foo"}',
};
const result = xaiSseEventToOpenaiResponses(ev);
assert.equal(result, null);
});
test("xaiSseEventToOpenaiResponses: passes through other events unchanged", () => {
const ev = { event: "response.output_text.delta", data: '{"delta":"hi"}' };
const result = xaiSseEventToOpenaiResponses(ev);
assert.deepStrictEqual(result, ev);
});
// ─── claudeRequestToXaiResponses ─────────────────────────────────────────────
test("claudeRequestToXaiResponses: translates system string to instructions", () => {
const req = {
model: "grok-4",
system: "Be helpful",
messages: [{ role: "user" as const, content: "Hi" }],
};
const out = claudeRequestToXaiResponses(req);
assert.equal(out.instructions, "Be helpful");
});
test("claudeRequestToXaiResponses: converts text content to input_text block", () => {
const req = {
model: "grok-4",
messages: [{ role: "user" as const, content: "Hello" }],
};
const out = claudeRequestToXaiResponses(req);
assert.equal(out.input.length, 1);
const inputItem = out.input[0] as Record<string, unknown>;
const content = inputItem.content as Array<Record<string, unknown>>;
assert.equal(content[0].type, "input_text");
assert.equal(content[0].text, "Hello");
});
test("claudeRequestToXaiResponses: extracts tool_result to function_call_output", () => {
const req = {
model: "grok-4",
messages: [
{
role: "user" as const,
content: [
{
type: "tool_result",
tool_use_id: "tu_123",
content: "result data",
},
],
},
],
};
const out = claudeRequestToXaiResponses(req);
const item = out.input[0] as Record<string, unknown>;
assert.equal(item.type, "function_call_output");
assert.equal(item.call_id, "tu_123");
assert.equal(item.output, "result data");
});
test("claudeRequestToXaiResponses: translates tools from Anthropic to xAI function shape", () => {
const req = {
model: "grok-4",
messages: [],
tools: [
{
name: "search",
description: "Search the web",
input_schema: { type: "object", properties: {} },
},
],
};
const out = claudeRequestToXaiResponses(req);
assert.ok(Array.isArray(out.tools));
const tool = (out.tools as Array<Record<string, unknown>>)[0];
assert.equal(tool.type, "function");
const fn = tool.function as Record<string, unknown>;
assert.equal(fn.name, "search");
});
test("claudeRequestToXaiResponses: maps thinking.enabled with budget to reasoning", () => {
const req = {
model: "grok-4",
messages: [],
thinking: { type: "enabled", budget_tokens: 8000 },
};
const out = claudeRequestToXaiResponses(req);
assert.deepStrictEqual(out.reasoning, { effort: "medium" });
});
// ─── xaiCompletedToClaudeJson ─────────────────────────────────────────────────
test("xaiCompletedToClaudeJson: converts output_text to text content block", () => {
const completed = {
id: "r1",
model: "grok-4",
output: [
{
type: "message",
content: [{ type: "output_text", text: "Hello!" }],
},
],
};
const result = xaiCompletedToClaudeJson(completed) as Record<string, unknown>;
assert.equal(result.role, "assistant");
assert.equal(result.stop_reason, "end_turn");
const content = result.content as Array<Record<string, unknown>>;
assert.equal(content[0].type, "text");
assert.equal(content[0].text, "Hello!");
});
test("xaiCompletedToClaudeJson: converts function_call to tool_use block", () => {
const completed = {
id: "r2",
model: "grok-4",
output: [
{
type: "function_call",
call_id: "c1",
name: "lookup",
arguments: '{"q":"test"}',
},
],
};
const result = xaiCompletedToClaudeJson(completed) as Record<string, unknown>;
assert.equal(result.stop_reason, "tool_use");
const content = result.content as Array<Record<string, unknown>>;
assert.equal(content[0].type, "tool_use");
assert.equal(content[0].name, "lookup");
});
// ─── geminiRequestToXaiResponses ─────────────────────────────────────────────
test("geminiRequestToXaiResponses: converts text parts to input_text blocks", () => {
const req = {
contents: [{ role: "user", parts: [{ text: "Hello" }] }],
};
const out = geminiRequestToXaiResponses(req, "grok-4");
assert.equal(out.model, "grok-4");
const item = out.input[0] as Record<string, unknown>;
const content = item.content as Array<Record<string, unknown>>;
assert.equal(content[0].type, "input_text");
assert.equal(content[0].text, "Hello");
});
test("geminiRequestToXaiResponses: maps systemInstruction to instructions", () => {
const req = {
contents: [],
systemInstruction: { parts: [{ text: "Be helpful" }] },
};
const out = geminiRequestToXaiResponses(req);
assert.equal(out.instructions, "Be helpful");
});
test("geminiRequestToXaiResponses: converts functionDeclarations to xAI tools", () => {
const req = {
contents: [],
tools: [
{
functionDeclarations: [
{
name: "search",
description: "Search the web",
parameters: { type: "object" },
},
],
},
],
};
const out = geminiRequestToXaiResponses(req);
assert.ok(Array.isArray(out.tools));
const tool = (out.tools as Array<Record<string, unknown>>)[0];
assert.equal(tool.type, "function");
const fn = tool.function as Record<string, unknown>;
assert.equal(fn.name, "search");
});
test("geminiRequestToXaiResponses: maps thinkingBudget to reasoning.effort", () => {
const req = {
contents: [],
generationConfig: { thinkingConfig: { thinkingBudget: 20000 } },
};
const out = geminiRequestToXaiResponses(req);
assert.deepStrictEqual(out.reasoning, { effort: "high" });
});
test("geminiRequestToXaiResponses: converts model role to assistant", () => {
const req = {
contents: [{ role: "model", parts: [{ text: "Hi" }] }],
};
const out = geminiRequestToXaiResponses(req);
const item = out.input[0] as Record<string, unknown>;
assert.equal(item.role, "assistant");
});
// ─── xaiCompletedToGeminiJson ────────────────────────────────────────────────
test("xaiCompletedToGeminiJson: converts output_text to Gemini candidate text part", () => {
const completed = {
id: "r1",
model: "grok-4",
output: [
{
type: "message",
content: [{ type: "output_text", text: "Hello Gemini!" }],
},
],
};
const result = xaiCompletedToGeminiJson(completed) as Record<string, unknown>;
const candidates = result.candidates as Array<Record<string, unknown>>;
const content = candidates[0].content as Record<string, unknown>;
const parts = content.parts as Array<Record<string, unknown>>;
assert.equal(parts[0].text, "Hello Gemini!");
assert.equal(candidates[0].finishReason, "STOP");
});
test("xaiCompletedToGeminiJson: converts function_call to functionCall part", () => {
const completed = {
id: "r2",
model: "grok-4",
output: [
{
type: "function_call",
name: "lookup",
arguments: '{"q":"test"}',
},
],
};
const result = xaiCompletedToGeminiJson(completed) as Record<string, unknown>;
const candidates = result.candidates as Array<Record<string, unknown>>;
const content = candidates[0].content as Record<string, unknown>;
const parts = content.parts as Array<Record<string, unknown>>;
const fc = parts[0].functionCall as Record<string, unknown>;
assert.equal(fc.name, "lookup");
assert.deepStrictEqual(fc.args, { q: "test" });
});
test("xaiCompletedToGeminiJson: maps usage to usageMetadata", () => {
const completed = {
model: "grok-4",
output: [],
usage: { input_tokens: 10, output_tokens: 20 },
};
const result = xaiCompletedToGeminiJson(completed) as Record<string, unknown>;
const meta = result.usageMetadata as Record<string, unknown>;
assert.equal(meta.promptTokenCount, 10);
assert.equal(meta.candidatesTokenCount, 20);
assert.equal(meta.totalTokenCount, 30);
});