Skip to content

Commit c54c4f3

Browse files
author
KrasimirKralev
committed
fix(transport): honor compat.requiresReasoningContentOnAssistantMessages from config
Addresses clawsweeper review on #89832 (P1 compatibility trap). getCompat() in openai-transport-stream resolved every compat field as "compat.X ?? detected.X" EXCEPT requiresReasoningContentOnAssistantMessages, which used detected.X alone. So a custom OpenAI-compatible proxy (not auto-detected as DeepSeek/Xiaomi) that set the flag in config would pass schema validation but be ignored by the active transport — a startup-success/runtime-failure trap. Now resolves compat.X ?? detected.X, matching openai-completions.ts:1333 and every sibling field. Exposes getCompat via the existing test-only testing export and adds a focused regression: a custom provider with the flag set resolves true (verified to fail against the pre-fix detected-only code); the same provider without the flag falls back to detection (false).
1 parent d43aceb commit c54c4f3

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

src/agents/openai-transport-stream.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10097,4 +10097,37 @@ describe("buildOpenAICompletionsParams sanitizes reasoning replay fields", () =>
1009710097
const assistant = getAssistantMessage(params);
1009810098
expect(assistant.reasoning_details).toEqual([reasoningDetail]);
1009910099
});
10100+
10101+
// issue #89660: a custom OpenAI-compatible proxy (not auto-detected as DeepSeek/
10102+
// Xiaomi/Kimi) can opt into the DeepSeek reasoning-content replay contract by
10103+
// setting compat.requiresReasoningContentOnAssistantMessages in config. getCompat
10104+
// must resolve `compat.X ?? detected.X` (matching every sibling field) instead of
10105+
// using `detected.X` alone, so the explicit config flag is honored in this transport.
10106+
const customReasoningProxyModel = {
10107+
id: "my-proxy/r1-pro",
10108+
name: "Custom Reasoning Proxy",
10109+
api: "openai-completions",
10110+
provider: "custom-openai-proxy",
10111+
baseUrl: "https://my-proxy.example.com/v1",
10112+
reasoning: true,
10113+
input: ["text"],
10114+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
10115+
contextWindow: 200_000,
10116+
maxTokens: 8_192,
10117+
} satisfies Model<"openai-completions">;
10118+
10119+
it("honors compat.requiresReasoningContentOnAssistantMessages from config on a custom provider (#89660)", () => {
10120+
const resolved = testing.getCompat({
10121+
...customReasoningProxyModel,
10122+
compat: { requiresReasoningContentOnAssistantMessages: true },
10123+
} as never);
10124+
10125+
expect(resolved.requiresReasoningContentOnAssistantMessages).toBe(true);
10126+
});
10127+
10128+
it("falls back to detection (false) for the same custom provider when the flag is absent", () => {
10129+
const resolved = testing.getCompat(customReasoningProxyModel as never);
10130+
10131+
expect(resolved.requiresReasoningContentOnAssistantMessages).toBe(false);
10132+
});
1010010133
});

src/agents/openai-transport-stream.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3432,6 +3432,7 @@ function getCompat(model: OpenAIModeModel): {
34323432
visibleReasoningDetailTypes:
34333433
compat.visibleReasoningDetailTypes ?? detected.visibleReasoningDetailTypes,
34343434
requiresReasoningContentOnAssistantMessages:
3435+
compat.requiresReasoningContentOnAssistantMessages ??
34353436
detected.requiresReasoningContentOnAssistantMessages,
34363437
requiresNonEmptyUserOrAssistantMessage: detected.requiresNonEmptyUserOrAssistantMessage,
34373438
};
@@ -4277,6 +4278,7 @@ function mapStopReason(reason: string | null) {
42774278
}
42784279

42794280
export const testing = {
4281+
getCompat,
42804282
assertCodeModeResponsesToolSurface,
42814283
buildOpenAIClientHeaders,
42824284
buildOpenAISdkClientOptions,

0 commit comments

Comments
 (0)