feat(catalog): replay xAI encrypted reasoning on later turns

Stop stripping type=reasoning history for xai and xai-oauth so
encrypted_content from include is sent back on the next Responses request.
This commit is contained in:
Yang Yang
2026-08-02 20:54:53 -07:00
parent c228dea58b
commit 651f20957b
8 changed files with 107 additions and 20 deletions
+2 -2
View File
@@ -241,8 +241,8 @@ Both the paid API-key provider (`xai` / `XAI_API_KEY`) and SuperGrok OAuth
(`xai-oauth`) chat over `https://api.x.ai/v1/responses`. Keep these independent:
- omit `reasoning.effort`
- include `reasoning.encrypted_content` (request `include`) vs replay history
- filter reasoning-history wrappers
- include `reasoning.encrypted_content` on the request
- replay encrypted reasoning items on later turns
Some models reject only one of those fields; do not collapse them into one
"Grok mode" branch.
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test";
import { buildParams } from "@oh-my-pi/pi-ai/providers/openai-responses";
import type { Context } from "@oh-my-pi/pi-ai/types";
import type { AssistantMessage, Context, Model } from "@oh-my-pi/pi-ai/types";
import { Effort } from "@oh-my-pi/pi-catalog/effort";
import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
import { getBundledModel } from "@oh-my-pi/pi-catalog/models";
@@ -95,4 +95,78 @@ describe("xAI OAuth Responses reasoning payload (regression)", () => {
expect(params.include).toContain("reasoning.encrypted_content");
});
test("xai-oauth/grok-4.5 replays encrypted reasoning on the next turn", () => {
const grok45 = getBundledModel<"openai-responses">("xai-oauth", "grok-4.5");
if (!grok45) throw new Error("xai-oauth/grok-4.5 must be in bundled models.json");
const { params } = buildParams(grok45, followUpContextWithEncryptedReasoning(grok45), undefined, undefined);
expect(params.include).toContain("reasoning.encrypted_content");
expect(findEncryptedReasoning(params.input)).toEqual({
type: "reasoning",
id: "rs_xai_next_turn",
encrypted_content: "enc_next_turn",
});
});
test("paid xai/grok-4.5 replays encrypted reasoning on the next turn", () => {
const grok45 = getBundledModel<"openai-responses">("xai", "grok-4.5");
if (!grok45) throw new Error("xai/grok-4.5 must be in bundled models.json");
const { params } = buildParams(grok45, followUpContextWithEncryptedReasoning(grok45), undefined, undefined);
expect(findEncryptedReasoning(params.input)).toEqual({
type: "reasoning",
id: "rs_xai_next_turn",
encrypted_content: "enc_next_turn",
});
});
});
function followUpContextWithEncryptedReasoning(model: Model<"openai-responses">): Context {
const assistant: AssistantMessage = {
role: "assistant",
content: [
{
type: "thinking",
thinking: "internal plan",
thinkingSignature: JSON.stringify({
type: "reasoning",
id: "rs_xai_next_turn",
encrypted_content: "enc_next_turn",
}),
},
{ type: "text", text: "done" },
],
api: "openai-responses",
provider: model.provider,
model: model.id,
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
},
stopReason: "stop",
timestamp: 1,
};
return {
messages: [
{ role: "user", content: "first", timestamp: 0 },
assistant,
{ role: "user", content: "continue", timestamp: 2 },
],
};
}
function findEncryptedReasoning(input: unknown): Record<string, unknown> | undefined {
if (!Array.isArray(input)) return undefined;
return input.find(item => {
if (!item || typeof item !== "object") return false;
const candidate = item as { type?: unknown; encrypted_content?: unknown };
return candidate.type === "reasoning" && typeof candidate.encrypted_content === "string";
}) as Record<string, unknown> | undefined;
}
+7
View File
@@ -135,6 +135,13 @@
- Fixed GitHub Copilot dynamic discovery retaining stale bundled prices for default-context models instead of using the provider's reported default-tier prices.
## [17.2.5] - 2026-08-03
### Changed
- Switched the paid xAI provider (`xai` / `XAI_API_KEY`) from Chat Completions to the OpenAI Responses API (`POST https://api.x.ai/v1/responses`), matching SuperGrok `xai-oauth`. Prompt-cache affinity (`x-grok-conv-id`), reasoning-effort allowlisting, and encrypted-reasoning replay rules are now shared across both first-party xAI hosts.
- Changed the paid xAI (`XAI_API_KEY`) default model from `grok-4-fast-non-reasoning` to `grok-4.5`.
- Changed the SuperGrok (`xai-oauth`) default model from `grok-4.3` to `grok-4.5`.
- Requested `reasoning.encrypted_content` on first-party xAI Responses calls (`xai` and `xai-oauth`) via the `include` parameter.
- Replayed xAI encrypted reasoning items on later Responses turns instead of stripping `type: "reasoning"` history.
### Fixed
+4 -5
View File
@@ -714,12 +714,11 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
thinkingFormat,
reasoningDisableMode: resolveReasoningDisableMode(thinkingFormat),
omitReasoningEffort: false,
// Ask xAI `/v1/responses` for `reasoning.encrypted_content` the same way
// first-party OpenAI Responses does. History still drops `type:
// "reasoning"` wrappers (`filterReasoningHistory`) independently —
// those two flags must not be collapsed.
// Ask xAI `/v1/responses` for `reasoning.encrypted_content` and replay
// those items on later turns. OpenRouter Anthropic still filters
// reasoning wrappers independently.
includeEncryptedReasoning: true,
filterReasoningHistory: isXaiHost || (isOpenRouter && isAnthropicModel),
filterReasoningHistory: isOpenRouter && isAnthropicModel,
disableReasoningOnForcedToolChoice: isKimiModel,
disableReasoningOnToolChoice: isDeepseekFamily && reasoningCapable && !isOpenRouter,
supportsToolChoice: true,
+8 -8
View File
@@ -104484,7 +104484,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": true,
"supportsReasoningEffort": false
@@ -104516,7 +104516,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": true,
"supportsReasoningEffort": false
@@ -104560,7 +104560,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": false,
"supportsReasoningEffort": true
@@ -104605,7 +104605,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": false,
"supportsReasoningEffort": true
@@ -104650,7 +104650,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": false,
"supportsReasoningEffort": true
@@ -104710,7 +104710,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": true,
"supportsReasoningEffort": false
@@ -104742,7 +104742,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": true,
"supportsReasoningEffort": false
@@ -104773,7 +104773,7 @@
"minimal": "low"
},
"includeEncryptedReasoning": true,
"filterReasoningHistory": true,
"filterReasoningHistory": false,
"supportsImageDetailOriginal": false,
"omitReasoningEffort": true,
"supportsReasoningEffort": false
@@ -1353,7 +1353,7 @@ function withXaiOAuthCompatDefaults(model: ModelSpec<"openai-responses">): Model
const compat = {
...(model.compat ?? {}),
includeEncryptedReasoning: model.compat?.includeEncryptedReasoning ?? true,
filterReasoningHistory: model.compat?.filterReasoningHistory ?? true,
filterReasoningHistory: model.compat?.filterReasoningHistory ?? false,
supportsImageDetailOriginal: model.compat?.supportsImageDetailOriginal ?? false,
omitReasoningEffort: model.compat?.omitReasoningEffort ?? !isGrokReasoningEffortCapable(model.id),
};
@@ -1396,7 +1396,7 @@ function mergeCuratedIntoModel(
...(base.compat ?? {}),
reasoningEffortMap: { ...XAI_REASONING_EFFORT_MAP, ...(base.compat?.reasoningEffortMap ?? {}) },
includeEncryptedReasoning: base.compat?.includeEncryptedReasoning ?? true,
filterReasoningHistory: base.compat?.filterReasoningHistory ?? true,
filterReasoningHistory: false,
supportsImageDetailOriginal: base.compat?.supportsImageDetailOriginal ?? false,
omitReasoningEffort: !effortCapable,
supportsReasoningEffort: effortCapable,
+2 -2
View File
@@ -258,8 +258,8 @@ describe("xAI Responses reasoning-effort suppression", () => {
expect(oauth.promptCacheSessionHeader).toBe("x-grok-conv-id");
expect(paid.includeEncryptedReasoning).toBe(true);
expect(oauth.includeEncryptedReasoning).toBe(true);
expect(paid.filterReasoningHistory).toBe(true);
expect(oauth.filterReasoningHistory).toBe(true);
expect(paid.filterReasoningHistory).toBe(false);
expect(oauth.filterReasoningHistory).toBe(false);
expect(paid.supportsImageDetailOriginal).toBe(false);
expect(oauth.supportsImageDetailOriginal).toBe(false);
expect(paid.supportsReasoningEffort).toBe(true);
+7
View File
@@ -388,6 +388,13 @@
- Exposed the script-driven computer schema to all models, including those with provider-native Computer Use support.
- Reduced omp --help cold-start latency and memory usage by rendering lightweight command metadata.
- Exposed the script-driven `computer` schema to every model, including models with provider-native Computer Use support, because native action declarations cannot express persistent desktop sessions or accessibility handles.
- Reduced `omp --help` cold-start latency and memory use by rendering lightweight command metadata without loading every runtime command and provider graph.
- Routed paid xAI models (`XAI_API_KEY` / `xai/…`) through the Responses API used by SuperGrok OAuth instead of Chat Completions.
- Changed the default model for `XAI_API_KEY` (`xai`) from `grok-4-fast-non-reasoning` to `grok-4.5`.
- Changed the default model for SuperGrok OAuth (`xai-oauth`) from `grok-4.3` to `grok-4.5`.
- Included `reasoning.encrypted_content` in Responses `include` for paid xAI and SuperGrok OAuth models.
- Replayed encrypted xAI reasoning on follow-up Responses turns for `xai` and `xai-oauth`.
### Fixed