feat(providers): native xAI Agent Tools passthrough for /v1/responses (#9111)

Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates green: static + changed tests + vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-213228-suite.log
This commit is contained in:
VXNCXNX
2026-08-06 02:43:40 +02:00
committed by GitHub
parent 697c7b96a9
commit 4533dd245f
17 changed files with 569 additions and 33 deletions

View File

@@ -0,0 +1 @@
- **feat(providers):** native xAI Agent Tools passthrough on `/v1/responses` for `xai` / `xai-oauth` (`xao`) — forward `web_search` + `x_search` to `api.x.ai` instead of rewriting or rejecting them ([#8964](https://github.com/diegosouzapw/OmniRoute/issues/8964))

View File

@@ -350,9 +350,9 @@
"open-sse/executors/deepseek-web.ts": 1148,
"open-sse/executors/grok-web.ts": 1044,
"open-sse/executors/muse-spark-web.ts": 1405,
"open-sse/handlers/chatCore.ts": 5020,
"open-sse/handlers/chatCore.ts": 5034,
"open-sse/handlers/imageGeneration.ts": 3101,
"open-sse/handlers/responseSanitizer.ts": 1115,
"open-sse/handlers/responseSanitizer.ts": 1128,
"open-sse/handlers/search.ts": 1536,
"open-sse/handlers/videoGeneration.ts": 1063,
"open-sse/mcp-server/schemas/tools.ts": 1505,
@@ -416,5 +416,7 @@
"_rebaseline_2026_07_28_8861_xiaomi_token_plan": "PR #8861 (feat/xiaomi-token-plan-protocol-selector) own growth: EditConnectionModal.tsx 1283->1316 (+33 = the per-connection API-protocol selector field) and open-sse/executors/base.ts 1540->1562 (+22 = alternate-format resolution at the existing buildUrl/headers chokepoint). Both are irreducible wiring at existing call sites.",
"_rebaseline_2026_07_28_8863_firefly_detail_level": "PR #8863 (fix/adobe-firefly-gpt-detail-level-max) own growth: adobeFireflyClient.ts 2317->2322 (+5 = gpt-image detailLevel defaulting to maximal at the existing payload-build site). Covered by tests/unit/adobe-firefly.test.ts.",
"_rebaseline_2026_07_29_8281_home_quickstart_prefetch": "Release v3.8.49 base-red fix (no PR — captain sweep): src/app/(dashboard)/dashboard/HomePageClient.tsx 1377->1381 (+4). #8292 added prefetch={false} to the sidebar but left /home's five quick-start Links prefetching, so first paint still fired 12 speculative RSC requests — caught by navigation.spec.ts only after the e2e helper bug (APP_ROUTE_PATTERN missing /home) was repaired in the same cycle. Growth is the five prefetch attributes; it was offset first by extracting the repeated className literals (INLINE_LINK x4, DOCS_LINK x1), which collapsed five wrapped <Link> blocks back to one line each — a naive fix measured 1391. Guard: tests/unit/sidebar-prefetch-policy-8281.test.ts.",
"_rebaseline_2026_08_01_8964_xai_agent_tools": "PR #8964 own growth: chatCore.ts 5020->5034 at the existing native-passthrough chokepoint. Adds xAI Agent Tools passthrough for /v1/responses (xai/xai-oauth/xao): resolve nativeXaiResponsesPassthrough, force openai-responses targetFormat, stamp body marker, and OR into the existing nativeCodexPassthrough sites (web-search bypass + requestEndpointPath). Leaf logic in passthroughHelpers, responsesEndpoint, targetFormat, xai executor, responseSanitizer, usageTracking. Cohesive wiring at the Codex passthrough boundary.",
"_rebaseline_2026_08_01_8964_response_sanitizer": "PR #8964 own growth: responseSanitizer.ts 1115->1128. Keep cost_in_usd_ticks / server_side_tool_usage(_details) through sanitizeResponsesApiResponse allowlists so native xAI tool responses retain usage.",
"_rebaseline_2026_08_02_v3850_agentrouter_responses": "Release v3.8.50 AgentRouter/Codex compatibility reconciliation. open-sse/executors/base.ts 1562->1578: #9190 wires AgentRouter's selected Claude/OpenAI/Responses protocol through the existing executor URL, auth, identity-header and fingerprint chokepoints; the reusable alternate resolver remains outside base.ts. open-sse/utils/stream.ts 2887->2889: #9213 evaluates Responses ID and usage normalization independently so response.completed always receives finite usage.total_tokens instead of short-circuiting after an ID rewrite. tests/unit/chatcore-translation-paths.test.ts 2769->2776: #9191 updates the existing Claude-Code bridge assertions for the dynamic AgentRouter wire image. PR #9224 offsets its own chatCore growth by extracting the AgentRouter protocol decisions into chatCore/agentRouterProtocol.ts, leaving chatCore below its frozen ceiling. Covered by agentrouter executor/chatCore protocol tests, chatcore translation-path tests, and responses-commentary-passthrough tests."
}

View File

@@ -271,6 +271,7 @@ function stripInternalBodyFields(body: unknown): unknown {
const record = body as Record<string, unknown>;
delete record._claudeCodeRequiresLowercaseToolNames;
delete record._nativeCodexPassthrough;
delete record._nativeXaiResponsesPassthrough;
delete record._omnirouteResponsesStore;
return body;
}

View File

@@ -1,6 +1,7 @@
import { BaseExecutor, type ExecutorLog, type ProviderCredentials } from "./base.ts";
import { PROVIDERS } from "../config/constants.ts";
import { getModelTargetFormat } from "../config/providerModels.ts";
import { isResponsesEndpointPath } from "../utils/responsesEndpoint.ts";
type JsonRecord = Record<string, unknown>;
@@ -52,21 +53,18 @@ export class XaiExecutor extends BaseExecutor {
super(provider, PROVIDERS[provider]);
}
/**
* Port of decolua/9router#2439 (author: @ryanngit): xAI ships a native
* `/v1/responses` endpoint alongside `/v1/chat/completions`. Models tagged
* `targetFormat: "openai-responses"` in the registry (currently
* grok-4.20-multi-agent-0309, per upstream) resolve to that endpoint instead
* of the default chat-completions bridge. The per-model registry tag is the
* single source of truth — it also drives chatCore's body translation — so
* the URL stays in lockstep with the translated body, mirroring the gh
* executor's targetFormat-driven routing (9router#102) and the "openai"
* -pro heuristic in open-sse/executors/default.ts.
*/
buildUrl(model: string, _stream: boolean, _urlIndex = 0) {
buildUrl(
model: string,
_stream: boolean,
_urlIndex = 0,
credentials: ProviderCredentials | null = null
) {
if (getModelTargetFormat(this.provider, model) === "openai-responses") {
return this.config.responsesBaseUrl || this.config.baseUrl;
}
if (isResponsesEndpointPath(credentials?.requestEndpointPath)) {
return this.config.responsesBaseUrl || this.config.baseUrl;
}
return this.config.baseUrl;
}
@@ -127,6 +125,14 @@ export class XaiExecutor extends BaseExecutor {
if (!record) return cleaned;
const out: JsonRecord = { ...record };
const nativeXaiPassthrough = record._nativeXaiResponsesPassthrough === true;
delete out._nativeXaiResponsesPassthrough;
delete out._nativeCodexPassthrough;
if (nativeXaiPassthrough || getModelTargetFormat(this.provider, model) === "openai-responses") {
return out;
}
let modelId = typeof out.model === "string" ? out.model : model;
let suffixEffort: string | null = null;

View File

@@ -38,6 +38,8 @@ import {
} from "./chatCore/executorHelpers.ts";
import {
shouldUseNativeCodexPassthrough,
shouldUseNativeXaiResponsesPassthrough,
stampNativeResponsesPassthroughBody,
redactPassthroughThinkingSignatures,
isClaudeCodeSemanticPassthroughRequest,
} from "./chatCore/passthroughHelpers.ts";
@@ -56,6 +58,7 @@ import {
// symbols from chatCore.ts (tests, sibling modules) keep resolving after the split.
export {
shouldUseNativeCodexPassthrough,
shouldUseNativeXaiResponsesPassthrough,
redactPassthroughThinkingSignatures,
isClaudeCodeSemanticPassthroughRequest,
buildStreamingResponseHeaders,
@@ -628,6 +631,7 @@ export async function handleChatCore({
sourceFormat,
isResponsesEndpoint,
nativeCodexPassthrough,
nativeXaiResponsesPassthrough,
isDroidCLI,
isOpencodeClient,
copilotCompatibleReasoning,
@@ -748,7 +752,9 @@ export async function handleChatCore({
sourceFormat,
customModelTargetFormat,
providerSpecificData: credentials?.providerSpecificData,
nativeXaiResponsesPassthrough,
});
const nativeResponsesPassthrough = nativeCodexPassthrough || nativeXaiResponsesPassthrough;
const initialProviderRequest =
body && typeof body === "object" && !Array.isArray(body)
@@ -788,7 +794,7 @@ export async function handleChatCore({
provider,
sourceFormat,
targetFormat,
nativeCodexPassthrough,
nativeCodexPassthrough: nativeResponsesPassthrough,
interceptSearchOverride,
});
if (webSearchFallbackPlan.enabled) {
@@ -806,7 +812,7 @@ export async function handleChatCore({
provider,
sourceFormat,
targetFormat,
nativeCodexPassthrough,
nativeCodexPassthrough: nativeResponsesPassthrough,
interceptFetchOverride,
});
if (webFetchFallbackPlan.enabled) {
@@ -1976,9 +1982,17 @@ export async function handleChatCore({
) => normalizeClaudeUpstreamMessagesFor(payload, options, log);
try {
if (nativeCodexPassthrough) {
translatedBody = { ...body, _nativeCodexPassthrough: true };
log?.debug?.("FORMAT", "native codex passthrough enabled");
if (nativeResponsesPassthrough) {
translatedBody = stampNativeResponsesPassthroughBody(
body,
nativeCodexPassthrough ? "codex" : "xai"
);
log?.debug?.(
"FORMAT",
nativeCodexPassthrough
? "native codex passthrough enabled"
: "native xAI Responses Agent Tools passthrough enabled"
);
} else if (isClaudeCodeCompatible) {
let normalizedForCc = { ...body };
@@ -2616,7 +2630,7 @@ export async function handleChatCore({
const getExecutionCredentials = () =>
resolveExecutionCredentialsFor({
credentials,
nativeCodexPassthrough,
nativeCodexPassthrough: nativeResponsesPassthrough,
endpointPath,
targetFormat,
provider,

View File

@@ -1,7 +1,12 @@
import { FORMATS } from "../../translator/formats.ts";
import { isClaudeCodeCompatibleProvider } from "../../services/claudeCodeCompatible.ts";
import { isResponsesEndpointPath } from "../../utils/responsesEndpoint.ts";
import { getHeaderValueCaseInsensitive } from "./headers.ts";
export { isResponsesEndpointPath };
export const XAI_API_PROVIDERS = new Set(["xai", "xai-oauth", "xao"]);
export function shouldUseNativeCodexPassthrough({
provider,
sourceFormat,
@@ -13,10 +18,29 @@ export function shouldUseNativeCodexPassthrough({
}): boolean {
if (provider !== "codex") return false;
if (sourceFormat !== FORMATS.OPENAI_RESPONSES) return false;
let normalizedEndpoint = String(endpointPath || "");
while (normalizedEndpoint.endsWith("/")) normalizedEndpoint = normalizedEndpoint.slice(0, -1);
const segments = normalizedEndpoint.split("/");
return segments.includes("responses");
return isResponsesEndpointPath(endpointPath);
}
export function shouldUseNativeXaiResponsesPassthrough({
provider,
sourceFormat,
endpointPath,
}: {
provider?: string | null;
sourceFormat?: string | null;
endpointPath?: string | null;
}): boolean {
if (!provider || !XAI_API_PROVIDERS.has(provider)) return false;
if (sourceFormat !== FORMATS.OPENAI_RESPONSES) return false;
return isResponsesEndpointPath(endpointPath);
}
export function stampNativeResponsesPassthroughBody(
body: Record<string, unknown>,
mode: "codex" | "xai"
): Record<string, unknown> {
if (mode === "codex") return { ...body, _nativeCodexPassthrough: true };
return { ...body, _nativeXaiResponsesPassthrough: true };
}
/**

View File

@@ -11,7 +11,10 @@
*/
import { detectFormatFromEndpoint } from "../../services/provider.ts";
import { shouldUseNativeCodexPassthrough } from "./passthroughHelpers.ts";
import {
shouldUseNativeCodexPassthrough,
shouldUseNativeXaiResponsesPassthrough,
} from "./passthroughHelpers.ts";
import { FORMATS } from "../../translator/formats.ts";
/** True when the request originates from a Copilot client (matched by user-agent or any header). */
@@ -49,13 +52,19 @@ function isOpencodeClient(
if (headers instanceof Headers) {
for (const [key, value] of headers as unknown as Iterable<[string, string]>) {
if (matchesHeaderKey(key) || (key.toLowerCase() === "user-agent" && matchesUserAgent(value))) {
if (
matchesHeaderKey(key) ||
(key.toLowerCase() === "user-agent" && matchesUserAgent(value))
) {
return true;
}
}
} else if (headers && typeof headers === "object") {
for (const [key, value] of Object.entries(headers)) {
if (matchesHeaderKey(key) || (key.toLowerCase() === "user-agent" && matchesUserAgent(value))) {
if (
matchesHeaderKey(key) ||
(key.toLowerCase() === "user-agent" && matchesUserAgent(value))
) {
return true;
}
}
@@ -71,9 +80,7 @@ function isOpencodeClient(
*/
export function resolveChatCoreRequestFormat(opts: {
clientRawRequest:
| { endpoint?: unknown; headers?: Headers | Record<string, unknown> | null }
| null
| undefined;
{ endpoint?: unknown; headers?: Headers | Record<string, unknown> | null } | null | undefined;
body: unknown;
provider: string | null | undefined;
userAgent: string | null | undefined;
@@ -88,6 +95,11 @@ export function resolveChatCoreRequestFormat(opts: {
sourceFormat,
endpointPath,
});
const nativeXaiResponsesPassthrough = shouldUseNativeXaiResponsesPassthrough({
provider,
sourceFormat,
endpointPath,
});
const isDroidCLI =
userAgent?.toLowerCase().includes("droid") || userAgent?.toLowerCase().includes("codex-cli");
const copilotCompatibleReasoning = isCopilotClient(clientRawRequest?.headers, userAgent);
@@ -101,6 +113,7 @@ export function resolveChatCoreRequestFormat(opts: {
sourceFormat,
isResponsesEndpoint,
nativeCodexPassthrough,
nativeXaiResponsesPassthrough,
isDroidCLI,
copilotCompatibleReasoning,
isOpencodeClient: isOpencodeClientRequest,

View File

@@ -22,6 +22,7 @@ export function resolveChatCoreTargetFormat(opts: {
sourceFormat?: string;
customModelTargetFormat: string | undefined;
providerSpecificData: unknown;
nativeXaiResponsesPassthrough?: boolean;
}) {
const {
provider,
@@ -30,6 +31,7 @@ export function resolveChatCoreTargetFormat(opts: {
sourceFormat,
customModelTargetFormat,
providerSpecificData,
nativeXaiResponsesPassthrough = false,
} = opts;
const alias = PROVIDER_ID_TO_ALIAS[provider] || provider;
const modelTargetFormat = getModelTargetFormat(alias, resolvedModel);
@@ -44,13 +46,14 @@ export function resolveChatCoreTargetFormat(opts: {
sourceFormat === FORMATS.CLAUDE)
? sourceFormat
: undefined;
const targetFormat =
let targetFormat =
apiFormat === "responses"
? FORMATS.OPENAI_RESPONSES
: modelTargetFormat ||
customModelTargetFormat ||
inferredAgentRouterTargetFormat ||
getTargetFormat(provider, providerSpecificData);
if (nativeXaiResponsesPassthrough) targetFormat = FORMATS.OPENAI_RESPONSES;
return { alias, targetFormat };
}

View File

@@ -42,8 +42,17 @@ const ALLOWED_RESPONSES_USAGE_FIELDS = new Set([
"input_tokens_details",
"output_tokens_details",
"estimated",
"cost_in_usd_ticks",
"server_side_tool_usage_details",
"server_side_tool_usage",
]);
const RESPONSES_EXTRA_TOP_LEVEL_FIELDS = [
"server_side_tool_usage_details",
"server_side_tool_usage",
"cost_in_usd_ticks",
] as const;
type JsonRecord = Record<string, unknown>;
type ParseOptions = { parseTextualReasoningTags?: boolean };
@@ -355,6 +364,10 @@ export function sanitizeResponsesApiResponse(body: unknown): unknown {
sanitized.usage = sanitizeResponsesUsage(responseRoot.usage);
}
for (const key of RESPONSES_EXTRA_TOP_LEVEL_FIELDS) {
if (responseRoot[key] !== undefined) sanitized[key] = responseRoot[key];
}
return sanitized;
}

View File

@@ -20,6 +20,7 @@ import {
RESPONSES_STORE_MARKER,
COPILOT_REASONING_SUMMARY_MARKER,
WEB_SEARCH_TOOL_TYPES,
X_SEARCH_TOOL_TYPES,
TOOL_SEARCH_TOOL_TYPES,
IMAGE_GENERATION_TOOL_TYPES,
toRecord,
@@ -103,7 +104,7 @@ export function openaiResponsesToOpenAIRequest(
// namespace tools (MCP tool groups used by Codex/OpenAI Responses API), and web_search server tools
// (Anthropic versioned: web_search_20250305, web_search_20250101, etc. — or plain web_search).
// tool_search is a Responses API built-in sent by newer Codex clients; silently skip it here
// (it will be filtered out during tools conversion below).
// (it will be filtered out during tools conversion below). x_search (#8964) same pattern.
if (
toolType &&
toolType !== "function" &&
@@ -112,6 +113,7 @@ export function openaiResponsesToOpenAIRequest(
toolType !== "namespace" &&
toolType !== "local_shell" &&
!WEB_SEARCH_TOOL_TYPES.test(toolType) &&
!X_SEARCH_TOOL_TYPES.test(toolType) &&
!TOOL_SEARCH_TOOL_TYPES.test(toolType) &&
!IMAGE_GENERATION_TOOL_TYPES.test(toolType) &&
!tool.function
@@ -531,6 +533,9 @@ export function openaiResponsesToOpenAIRequest(
if (WEB_SEARCH_TOOL_TYPES.test(toolType)) {
return toolValue;
}
if (X_SEARCH_TOOL_TYPES.test(toolType)) {
return [];
}
// local_shell is a Responses API built-in (Codex CLI injects it for shell
// execution). Non-OpenAI upstreams (Kiro/Claude) have no local_shell type,
// so map it to a regular "shell" function tool. The response translator

View File

@@ -7,6 +7,7 @@ export const COPILOT_REASONING_SUMMARY_MARKER = "_omnirouteCopilotReasoningSumma
// Forward-compatible regex: matches web_search, web_search_20250305, and future versioned names.
export const WEB_SEARCH_TOOL_TYPES = /^web_search/;
export const X_SEARCH_TOOL_TYPES = /^x_search/;
// tool_search is a Responses API built-in sent by newer Codex clients; it has no Chat Completions
// equivalent and must be silently dropped (not rejected with 400).
export const TOOL_SEARCH_TOOL_TYPES = /^tool_search/;

View File

@@ -0,0 +1,5 @@
export function isResponsesEndpointPath(endpointPath?: string | null): boolean {
let normalizedEndpoint = String(endpointPath || "");
while (normalizedEndpoint.endsWith("/")) normalizedEndpoint = normalizedEndpoint.slice(0, -1);
return normalizedEndpoint.split("/").includes("responses");
}

View File

@@ -232,9 +232,13 @@ export function filterUsageForFormat(usage, targetFormat) {
[FORMATS.OPENAI_RESPONSES]: [
"input_tokens",
"output_tokens",
"total_tokens",
"input_tokens_details",
"output_tokens_details",
"estimated",
"cost_in_usd_ticks",
"server_side_tool_usage_details",
"server_side_tool_usage",
],
// OpenAI format (default for OPENAI, CODEX, KIRO, etc.)
default: [

View File

@@ -33,6 +33,16 @@ test("native Codex passthrough injects requestEndpointPath", () => {
assert.equal(out.requestEndpointPath, "/v1/responses");
});
test("native Responses passthrough (xAI via codex flag) injects requestEndpointPath (#8964)", () => {
// chatCore ORs xAI into nativeCodexPassthrough before calling this helper.
const out = resolveExecutionCredentials({
...base,
provider: "xai-oauth",
nativeCodexPassthrough: true,
}) as Record<string, unknown>;
assert.equal(out.requestEndpointPath, "/v1/responses");
});
test("azure-ai + responses target forces apiType=responses and the upstream marker", () => {
const out = resolveExecutionCredentials({
...base,

View File

@@ -10,7 +10,11 @@ import { resolveChatCoreRequestFormat } from "../../open-sse/handlers/chatCore/r
import { shouldUseNativeCodexPassthrough } from "../../open-sse/handlers/chatCore/passthroughHelpers.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
const base = { body: { messages: [{ role: "user", content: "hi" }] }, provider: "openai", userAgent: "unit-test" };
const base = {
body: { messages: [{ role: "user", content: "hi" }] },
provider: "openai",
userAgent: "unit-test",
};
test("chat/completions endpoint → openai source, not a responses endpoint, no downgrade", () => {
const r = resolveChatCoreRequestFormat({
@@ -38,6 +42,29 @@ test("/responses endpoint → openai-responses source + isResponsesEndpoint, kep
assert.equal(r.clientResponseFormat, FORMATS.OPENAI_RESPONSES);
});
test("xAI oauth on /responses enables nativeXaiResponsesPassthrough (#8964)", () => {
const r = resolveChatCoreRequestFormat({
body: { input: "x", tools: [{ type: "web_search" }, { type: "x_search" }] },
provider: "xai-oauth",
userAgent: "unit-test",
clientRawRequest: { endpoint: "/v1/responses", headers: new Headers() },
});
assert.equal(r.sourceFormat, FORMATS.OPENAI_RESPONSES);
assert.equal(r.isResponsesEndpoint, true);
assert.equal(r.nativeXaiResponsesPassthrough, true);
assert.equal(r.nativeCodexPassthrough, false);
});
test("xao alias on /responses enables nativeXaiResponsesPassthrough (#8964)", () => {
const r = resolveChatCoreRequestFormat({
body: { input: "x" },
provider: "xao",
userAgent: "unit-test",
clientRawRequest: { endpoint: "/v1/responses", headers: new Headers() },
});
assert.equal(r.nativeXaiResponsesPassthrough, true);
});
test("Responses-shaped body on a /chat/completions endpoint downgrades clientResponseFormat to openai", () => {
const r = resolveChatCoreRequestFormat({
body: { input: "describe" }, // input + no messages → openai-responses via body

View File

@@ -90,6 +90,19 @@ test("bypass predicate: true for native Codex passthrough", () => {
);
});
test("bypass predicate: true when native Responses passthrough flag is set (#8964 xAI)", () => {
// Callers OR codex|xai into nativeCodexPassthrough (existing flag = "any native lane").
assert.equal(
supportsNativeWebSearchFallbackBypass({
provider: "xai-oauth",
sourceFormat: "openai-responses",
targetFormat: "openai-responses",
nativeCodexPassthrough: true,
}),
true
);
});
test("bypass predicate: true for Gemini target", () => {
assert.equal(
supportsNativeWebSearchFallbackBypass({

View File

@@ -0,0 +1,394 @@
/**
* #8964 — native xAI Agent Tools passthrough for /v1/responses
*/
import test from "node:test";
import assert from "node:assert/strict";
const {
shouldUseNativeXaiResponsesPassthrough,
shouldUseNativeCodexPassthrough,
isResponsesEndpointPath,
stampNativeResponsesPassthroughBody,
XAI_API_PROVIDERS,
} = await import("../../open-sse/handlers/chatCore/passthroughHelpers.ts");
const {
supportsNativeWebSearchFallbackBypass,
prepareWebSearchFallbackBody,
OMNIROUTE_WEB_SEARCH_FALLBACK_TOOL_NAME,
} = await import("../../open-sse/services/webSearchFallback.ts");
const { openaiResponsesToOpenAIRequest } =
await import("../../open-sse/translator/request/openai-responses.ts");
const { XaiExecutor } = await import("../../open-sse/executors/xai.ts");
// ── Predicate ──────────────────────────────────────────────────────────────
test("native xAI passthrough: true for xai-oauth + responses endpoint", () => {
assert.equal(
shouldUseNativeXaiResponsesPassthrough({
provider: "xai-oauth",
sourceFormat: "openai-responses",
endpointPath: "/v1/responses",
}),
true
);
});
test("native xAI passthrough: true for xao alias and xai API key provider", () => {
assert.equal(
shouldUseNativeXaiResponsesPassthrough({
provider: "xao",
sourceFormat: "openai-responses",
endpointPath: "/v1/responses",
}),
true
);
assert.equal(
shouldUseNativeXaiResponsesPassthrough({
provider: "xai",
sourceFormat: "openai-responses",
endpointPath: "responses",
}),
true
);
});
test("native xAI passthrough: false for grok-cli / chat completions / wrong format", () => {
assert.equal(
shouldUseNativeXaiResponsesPassthrough({
provider: "grok-cli",
sourceFormat: "openai-responses",
endpointPath: "/v1/responses",
}),
false
);
assert.equal(
shouldUseNativeXaiResponsesPassthrough({
provider: "xai-oauth",
sourceFormat: "openai-responses",
endpointPath: "/v1/chat/completions",
}),
false
);
assert.equal(
shouldUseNativeXaiResponsesPassthrough({
provider: "xai-oauth",
sourceFormat: "openai",
endpointPath: "/v1/responses",
}),
false
);
assert.ok(XAI_API_PROVIDERS.has("xai-oauth"));
assert.equal(XAI_API_PROVIDERS.has("grok-cli"), false);
});
test("Codex passthrough is unchanged and does not match xAI providers", () => {
assert.equal(
shouldUseNativeCodexPassthrough({
provider: "xai-oauth",
sourceFormat: "openai-responses",
endpointPath: "/v1/responses",
}),
false
);
assert.equal(
shouldUseNativeCodexPassthrough({
provider: "codex",
sourceFormat: "openai-responses",
endpointPath: "/v1/responses",
}),
true
);
});
test("isResponsesEndpointPath shared helper", () => {
assert.equal(isResponsesEndpointPath("/v1/responses"), true);
assert.equal(isResponsesEndpointPath("/v1/responses/"), true);
assert.equal(isResponsesEndpointPath("responses"), true);
assert.equal(isResponsesEndpointPath("/v1/chat/completions"), false);
});
test("stampNativeResponsesPassthroughBody modes", () => {
assert.deepEqual(stampNativeResponsesPassthroughBody({ a: 1 }, "codex"), {
a: 1,
_nativeCodexPassthrough: true,
});
assert.deepEqual(stampNativeResponsesPassthroughBody({ a: 1 }, "xai"), {
a: 1,
_nativeXaiResponsesPassthrough: true,
});
});
// ── Web search rewrite bypass (via existing nativeCodexPassthrough flag) ──
test("web_search is NOT rewritten when native Responses passthrough flag is true", () => {
const input = { tools: [{ type: "web_search" }, { type: "x_search", from_date: "2026-05-01" }] };
const { body, fallback } = prepareWebSearchFallbackBody(input, {
provider: "xai-oauth",
sourceFormat: "openai-responses",
targetFormat: "openai-responses",
nativeCodexPassthrough: true,
});
assert.equal(fallback.enabled, false);
assert.deepEqual(body, input);
});
test("web_search IS rewritten for xAI when native passthrough flag is false", () => {
const { body, fallback } = prepareWebSearchFallbackBody(
{ tools: [{ type: "web_search" }] },
{
provider: "xai-oauth",
sourceFormat: "openai-responses",
targetFormat: "openai",
nativeCodexPassthrough: false,
}
);
assert.equal(fallback.enabled, true);
assert.equal(fallback.toolName, OMNIROUTE_WEB_SEARCH_FALLBACK_TOOL_NAME);
const tools = body.tools as Record<string, unknown>[];
const names = tools.map((t) => (t.function ? (t.function as { name?: string }).name : t.name));
assert.ok(names.includes(OMNIROUTE_WEB_SEARCH_FALLBACK_TOOL_NAME));
});
test("interceptSearchOverride true still forces rewrite on passthrough path", () => {
assert.equal(
supportsNativeWebSearchFallbackBypass({
provider: "xai-oauth",
sourceFormat: "openai-responses",
targetFormat: "openai-responses",
nativeCodexPassthrough: true,
interceptSearchOverride: true,
}),
false
);
});
// ── Translator allowlist / Chat drop ───────────────────────────────────────
test("Responses→Chat validation accepts x_search (no unsupported_feature 400)", () => {
assert.doesNotThrow(() => {
openaiResponsesToOpenAIRequest(
"gpt-4o",
{
model: "gpt-4o",
input: "hi",
tools: [
{ type: "web_search" },
{ type: "x_search", from_date: "2026-05-01", to_date: "2026-08-01" },
{
type: "function",
name: "lookup",
parameters: { type: "object", properties: {} },
},
],
},
false,
{}
);
});
});
test("Responses→Chat conversion drops x_search (no Chat equivalent)", () => {
const converted = openaiResponsesToOpenAIRequest(
"gpt-4o",
{
model: "gpt-4o",
input: "hi",
tools: [
{ type: "x_search", from_date: "2026-05-01" },
{
type: "function",
name: "lookup",
parameters: { type: "object", properties: {} },
},
],
},
false,
{}
) as { tools?: Array<{ type?: string; function?: { name?: string }; name?: string }> };
const tools = converted.tools || [];
assert.equal(
tools.some((t) => t.type === "x_search"),
false,
"x_search must not be forwarded on Chat Completions downgrade"
);
assert.ok(
tools.some((t) => t.function?.name === "lookup" || t.name === "lookup"),
"function tools survive"
);
});
// ── XaiExecutor URL + marker strip ─────────────────────────────────────────
test("XaiExecutor.buildUrl uses responsesBaseUrl when requestEndpointPath is /v1/responses", () => {
const executor = new XaiExecutor("xai-oauth");
const chatUrl = String(executor.buildUrl("grok-4.20-0309-reasoning", false, 0, null));
assert.ok(
chatUrl.includes("chat/completions") || !chatUrl.includes("/responses"),
`default chat path expected, got ${chatUrl}`
);
const responsesUrl = String(
executor.buildUrl("grok-4.20-0309-reasoning", false, 0, {
requestEndpointPath: "/v1/responses",
} as never)
);
assert.match(responsesUrl, /\/responses/);
});
test("XaiExecutor.transformRequest strips internal passthrough markers", () => {
const executor = new XaiExecutor("xai-oauth");
const out = executor.transformRequest(
"grok-4.20-0309-reasoning",
{
model: "grok-4.20-0309-reasoning",
input: "hi",
tools: [{ type: "web_search" }, { type: "x_search" }],
_nativeXaiResponsesPassthrough: true,
_nativeCodexPassthrough: true,
},
false,
{} as never
) as Record<string, unknown>;
assert.equal(out._nativeXaiResponsesPassthrough, undefined);
assert.equal(out._nativeCodexPassthrough, undefined);
assert.ok(Array.isArray(out.tools));
assert.equal((out.tools as unknown[]).length, 2);
});
// ── Composition invariant ──────────────────────────────────────────────────
test("composition: flag → targetFormat Responses → no rewrite → credentials stamp → Responses URL", async () => {
const { resolveChatCoreRequestFormat } =
await import("../../open-sse/handlers/chatCore/requestFormat.ts");
const { resolveChatCoreTargetFormat } =
await import("../../open-sse/handlers/chatCore/targetFormat.ts");
const { resolveExecutionCredentials } =
await import("../../open-sse/handlers/chatCore/executionCredentials.ts");
const fmt = resolveChatCoreRequestFormat({
body: {
input: "hi",
tools: [{ type: "web_search" }, { type: "x_search", from_date: "2026-05-01" }],
},
provider: "xai-oauth",
userAgent: "unit-test",
clientRawRequest: { endpoint: "/v1/responses", headers: new Headers() },
});
assert.equal(fmt.nativeXaiResponsesPassthrough, true);
const nativeResponsesPassthrough =
fmt.nativeCodexPassthrough || fmt.nativeXaiResponsesPassthrough;
const { targetFormat } = resolveChatCoreTargetFormat({
provider: "xai-oauth",
resolvedModel: "grok-4.20-0309-reasoning",
apiFormat: undefined,
customModelTargetFormat: undefined,
providerSpecificData: undefined,
nativeXaiResponsesPassthrough: fmt.nativeXaiResponsesPassthrough,
});
assert.equal(targetFormat, "openai-responses");
const { fallback } = prepareWebSearchFallbackBody(
{ tools: [{ type: "web_search" }, { type: "x_search" }] },
{
provider: "xai-oauth",
sourceFormat: fmt.sourceFormat,
targetFormat,
nativeCodexPassthrough: nativeResponsesPassthrough,
}
);
assert.equal(fallback.enabled, false);
const creds = resolveExecutionCredentials({
credentials: {},
nativeCodexPassthrough: nativeResponsesPassthrough,
endpointPath: fmt.endpointPath,
targetFormat,
provider: "xai-oauth",
ccSessionId: null,
}) as { requestEndpointPath?: string };
assert.equal(creds.requestEndpointPath, "/v1/responses");
const url = String(
new XaiExecutor("xai-oauth").buildUrl("grok-4.20-0309-reasoning", false, 0, {
requestEndpointPath: creds.requestEndpointPath,
} as never)
);
assert.match(url, /\/responses/);
assert.equal(url.includes("chat/completions"), false);
});
// ── Response fidelity ──────────────────────────────────────────────────────
test("sanitizeResponsesApiResponse keeps xAI server-side tool usage + cost ticks", async () => {
const { sanitizeResponsesApiResponse } =
await import("../../open-sse/handlers/responseSanitizer.ts");
const sanitized = sanitizeResponsesApiResponse({
id: "resp_test",
object: "response",
created_at: 1,
model: "grok-4.20-0309-reasoning",
status: "completed",
output: [
{
id: "ws_1",
type: "web_search_call",
status: "completed",
action: { type: "search", query: "stripe" },
},
{
id: "msg_1",
type: "message",
role: "assistant",
content: [{ type: "output_text", text: '{"ok":true}' }],
},
],
usage: {
input_tokens: 10,
output_tokens: 5,
total_tokens: 15,
cost_in_usd_ticks: 154733500,
server_side_tool_usage_details: { web_search_calls: 1, x_search_calls: 0 },
},
cost_in_usd_ticks: 154733500,
server_side_tool_usage_details: { web_search_calls: 1 },
}) as Record<string, unknown>;
const usage = sanitized.usage as Record<string, unknown>;
assert.equal(usage.cost_in_usd_ticks, 154733500);
assert.deepEqual(usage.server_side_tool_usage_details, {
web_search_calls: 1,
x_search_calls: 0,
});
assert.equal(sanitized.cost_in_usd_ticks, 154733500);
assert.deepEqual(sanitized.server_side_tool_usage_details, { web_search_calls: 1 });
assert.ok((sanitized.output as { type: string }[]).some((o) => o.type === "web_search_call"));
});
test("filterUsageForFormat(Responses) keeps cost ticks + server-side tool details", async () => {
const { filterUsageForFormat } = await import("../../open-sse/utils/usageTracking.ts");
const filtered = filterUsageForFormat(
{
input_tokens: 10,
output_tokens: 5,
total_tokens: 15,
cost_in_usd_ticks: 154733500,
server_side_tool_usage_details: { web_search_calls: 1, x_search_calls: 0 },
server_side_tool_usage: { web_search: 1 },
x_groq: { should_not: "survive" },
},
"openai-responses"
) as Record<string, unknown>;
assert.equal(filtered.cost_in_usd_ticks, 154733500);
assert.deepEqual(filtered.server_side_tool_usage_details, {
web_search_calls: 1,
x_search_calls: 0,
});
assert.equal(filtered.x_groq, undefined);
});