From 6d3d981994ccb4560798d38c5eca9bc843c6ab9b Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 1 Jul 2026 14:43:58 +0000 Subject: [PATCH 1/2] fix(ai): reword gpt-5 responses fallback Replaced the GPT-5 Responses no-reasoning fallback developer item with non-budget wording and covered the payload regression. Fixes #4151 --- packages/ai/CHANGELOG.md | 4 +++ packages/ai/src/providers/openai-shared.ts | 7 +++-- .../azure-openai-responses-stream.test.ts | 7 ++++- packages/ai/test/openai-compat-policy.test.ts | 29 +++++++++++++++++++ .../openai-responses-history-payload.test.ts | 6 ++-- .../ai/test/openai-responses-stateful.test.ts | 8 ++--- packages/catalog/CHANGELOG.md | 4 +++ packages/catalog/src/types.ts | 8 ++--- 8 files changed, 59 insertions(+), 14 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index ae883f27d..ef847a54e 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Reworded the GPT-5 Responses no-reasoning fallback developer item so it no longer says `# Juice: 0 !important` or implies a zero tool/execution budget. ([#4151](https://github.com/can1357/oh-my-pi/issues/4151)) + ## [16.2.12] - 2026-07-01 ### Changed diff --git a/packages/ai/src/providers/openai-shared.ts b/packages/ai/src/providers/openai-shared.ts index b6cd43daa..7481077c5 100644 --- a/packages/ai/src/providers/openai-shared.ts +++ b/packages/ai/src/providers/openai-shared.ts @@ -2365,6 +2365,9 @@ export function applyCommonResponsesSamplingParams

{ { role: "user", content: [{ type: "input_text", text: "Say hello" }] }, { role: "developer", - content: [{ type: "input_text", text: "# Juice: 0 !important" }], + content: [ + { + type: "input_text", + text: "Keep internal reasoning brief. Continue following the task and use tools normally.", + }, + ], }, ]); }); diff --git a/packages/ai/test/openai-compat-policy.test.ts b/packages/ai/test/openai-compat-policy.test.ts index 33034ce86..44fd8c94b 100644 --- a/packages/ai/test/openai-compat-policy.test.ts +++ b/packages/ai/test/openai-compat-policy.test.ts @@ -123,6 +123,35 @@ describe("OpenAI compat policy", () => { expect(responseBody.reasoning).toBeUndefined(); }); + it("uses a non-budget fallback prompt for Responses no-reasoning compat", () => { + const compat: OpenAICompat = { requiresJuiceZeroHack: true }; + const responseBody = responsesParams(); + const responseInput: ResponseInput = []; + + const appended = applyResponsesCompatPolicy( + responseBody, + responseInput, + resolveOpenAICompatPolicy(responsesModel(compat), { endpoint: "responses" }), + undefined, + ); + + expect(appended).toBe(1); + expect(responseInput.at(-1)).toEqual({ + role: "developer", + content: [ + { + type: "input_text", + text: "Keep internal reasoning brief. Continue following the task and use tools normally.", + }, + ], + }); + const serialized = JSON.stringify(responseInput.at(-1)); + expect(serialized).not.toContain("Juice"); + expect(serialized).not.toContain("!important"); + expect(serialized).not.toContain("budget"); + expect(serialized).not.toContain("zero"); + }); + it("exposes reasoning replay constraints independent of endpoint", () => { const compat: OpenAICompat = { requiresReasoningContentForToolCalls: true, diff --git a/packages/ai/test/openai-responses-history-payload.test.ts b/packages/ai/test/openai-responses-history-payload.test.ts index 8f6c004fc..b9347e347 100644 --- a/packages/ai/test/openai-responses-history-payload.test.ts +++ b/packages/ai/test/openai-responses-history-payload.test.ts @@ -26,9 +26,9 @@ function createCodexToken(accountId: string): string { /** * Returns the bundled `gpt-5-mini` model with `compat.requiresJuiceZeroHack` - * cleared so it doesn't trigger the GPT-5 "Juice: 0" developer-message hack - * injected by `applyResponsesReasoningParams`. The hack is exercised by its - * own targeted tests; these history-replay tests assert raw payload shape and + * cleared so it doesn't trigger the GPT-5 no-reasoning developer-message + * fallback injected by `applyResponsesReasoningParams`. The fallback is + * exercised by its own targeted tests; these history-replay tests assert raw payload shape and * should stay independent of it. */ function getOpenAIReasoningModel( diff --git a/packages/ai/test/openai-responses-stateful.test.ts b/packages/ai/test/openai-responses-stateful.test.ts index fa3fc33a8..b0b3711c8 100644 --- a/packages/ai/test/openai-responses-stateful.test.ts +++ b/packages/ai/test/openai-responses-stateful.test.ts @@ -101,8 +101,8 @@ describe("openai-responses stateful chaining", () => { const sentRequests: Array> = []; const fetchMock = createCapturingFetch(sentRequests); const providerSessionState = new Map(); - // No reasoning option: applyResponsesReasoningParams appends the trailing - // "# Juice: 0 !important" developer item to every request's input. + // No reasoning option: applyResponsesReasoningParams appends trailing + // no-reasoning scaffolding to every request's input. const options = { apiKey: "test-key", sessionId: "stateful-juice-session", @@ -119,7 +119,7 @@ describe("openai-responses stateful chaining", () => { ).result(); expect(firstResponse.stopReason).toBe("stop"); const firstInput = sentRequests[0]?.input as unknown[]; - expect(JSON.stringify(firstInput.at(-1))).toContain("# Juice: 0"); + expect(JSON.stringify(firstInput.at(-1))).toContain("Keep internal reasoning brief"); const secondResponse = await streamOpenAIResponses( model, @@ -138,7 +138,7 @@ describe("openai-responses stateful chaining", () => { const deltaInput = sentRequests[1]?.input as Array<{ role?: string }>; expect(deltaInput).toHaveLength(2); expect(deltaInput[0]?.role).toBe("user"); - expect(JSON.stringify(deltaInput[1])).toContain("# Juice: 0"); + expect(JSON.stringify(deltaInput[1])).toContain("Keep internal reasoning brief"); }); it("replays the full transcript when history mutates", async () => { diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 912cca759..66972db07 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Updated the Responses compat flag docs to describe the no-reasoning fallback without the old `# Juice: 0 !important` wording. ([#4151](https://github.com/can1357/oh-my-pi/issues/4151)) + ## [16.2.12] - 2026-07-01 ### Breaking Changes diff --git a/packages/catalog/src/types.ts b/packages/catalog/src/types.ts index c41d76d3c..c11195e53 100644 --- a/packages/catalog/src/types.ts +++ b/packages/catalog/src/types.ts @@ -332,11 +332,11 @@ export interface OpenAICompat { /** Whether the Responses API accepts the `detail: "original"` image hint. Default: auto-detected (false for GitHub Copilot, which rejects it with a 400). */ supportsImageDetailOriginal?: boolean; /** - * Append a trailing `# Juice: 0 !important` developer item when the caller - * did not request reasoning, suppressing default reasoning on models that - * cannot disable it via request params (Responses APIs only; see + * Append a trailing no-reasoning developer item when the caller did not + * request reasoning, suppressing default reasoning on models that cannot + * disable it via request params (Responses APIs only; see * https://community.openai.com/t/need-reasoning-false-option-for-gpt-5/1351588/7). - * Default: auto-detected (GPT-5-family model names). + * The prompt must not look like an execution or tool budget. Default: auto-detected (GPT-5-family model names). */ requiresJuiceZeroHack?: boolean; /** Whether streamed reasoning deltas for the same field may repeat the full cumulative text snapshot. Default: false. */ From 0e2534bf17580af598fd6af2c9494f0796b08551 Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 1 Jul 2026 14:49:25 +0000 Subject: [PATCH 2/2] fix(ai): moved responses fallback prompt Moved the GPT-5 Responses no-reasoning fallback wording into a static markdown prompt file imported as text. Fixes #4151 --- .../src/providers/openai-responses-reasoning-suppression.md | 1 + packages/ai/src/providers/openai-shared.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 packages/ai/src/providers/openai-responses-reasoning-suppression.md diff --git a/packages/ai/src/providers/openai-responses-reasoning-suppression.md b/packages/ai/src/providers/openai-responses-reasoning-suppression.md new file mode 100644 index 000000000..d97c4d5b3 --- /dev/null +++ b/packages/ai/src/providers/openai-responses-reasoning-suppression.md @@ -0,0 +1 @@ +Keep internal reasoning brief. Continue following the task and use tools normally. diff --git a/packages/ai/src/providers/openai-shared.ts b/packages/ai/src/providers/openai-shared.ts index 7481077c5..3778893e5 100644 --- a/packages/ai/src/providers/openai-shared.ts +++ b/packages/ai/src/providers/openai-shared.ts @@ -75,6 +75,7 @@ import { } from "./github-copilot-headers"; import type { ChatCompletionCreateParamsStreaming } from "./openai-chat-wire"; import type { InputItem } from "./openai-codex/request-transformer"; +import responsesReasoningSuppressionPrompt from "./openai-responses-reasoning-suppression.md" with { type: "text" }; import type { ResponseContentPartAddedEvent, ResponseCreateParamsStreaming, @@ -2365,8 +2366,7 @@ export function applyCommonResponsesSamplingParams