From 81e923660abb5a263650ed56cbcb8e4bcf3ad058 Mon Sep 17 00:00:00 2001 From: Numman Ali Date: Wed, 17 Dec 2025 12:30:20 +0000 Subject: [PATCH] feat: v4.1.1 - none reasoning support, orphaned function_call_output fix, and HTML version update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This release combines fixes and features from PRs #62, #63, and #64: ### Added - "none" reasoning effort support for GPT-5.2 and GPT-5.1 general purpose models - gpt-5.2-none and gpt-5.1-none model mappings and presets (now 18 total models) - 4 new unit tests for "none" reasoning behavior (197 total tests) ### Fixed - Orphaned function_call_output 400 API errors - now converts orphans to assistant messages to preserve context while avoiding API errors - OAuth HTML version display updated from 1.0.4 to 4.1.0 ### Technical Details - getReasoningConfig() detects GPT-5.1 general purpose models and allows "none" - Codex variants auto-convert "none" to "low" (or "medium" for Codex Mini) - Orphan handling now works regardless of tools presence in request Contributors: @code-yeongyu (PR #63), @kanemontreuil (PR #64), @ben-vargas (PR #62) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 31 +++++ README.md | 33 ++--- config/full-opencode.json | 198 ++++++++++++++++++++++++----- docs/configuration.md | 29 +++-- lib/oauth-success.html | 2 +- lib/request/helpers/model-map.ts | 6 +- lib/request/request-transformer.ts | 55 ++++++-- package.json | 2 +- scripts/test-all-models.sh | 6 +- test/request-transformer.test.ts | 65 +++++++++- 10 files changed, 348 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbff26..d7246b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ All notable changes to this project are documented here. Dates use the ISO format (YYYY-MM-DD). +## [4.1.1] - 2025-12-17 + +**Minor release**: "none" reasoning effort support, orphaned function_call_output fix, and HTML version update. + +### Added +- **"none" reasoning effort support**: GPT-5.1 and GPT-5.2 support `reasoning_effort: "none"` which disables the reasoning phase entirely. This can result in faster responses when reasoning is not needed. + - `gpt-5.2-none` - GPT-5.2 with reasoning disabled + - `gpt-5.1-none` - GPT-5.1 with reasoning disabled +- **4 new unit tests** for "none" reasoning behavior (now 197 total unit tests). + +### Fixed +- **Orphaned function_call_output 400 errors**: Fixed API errors when conversation history contains `item_reference` pointing to stored function calls. Previously, orphaned `function_call_output` items were only filtered when `!body.tools`. Now always handles orphans regardless of tools presence, and converts them to assistant messages to preserve context while avoiding API errors. +- **OAuth HTML version display**: Updated version in oauth-success.html from 1.0.4 to 4.1.0. + +### Technical Details +- `getReasoningConfig()` now detects GPT-5.1 general purpose models (not Codex variants) and allows "none" to pass through. +- GPT-5.2 inherits "none" support as it's newer than GPT-5.1. +- Codex variants (gpt-5.1-codex, gpt-5.1-codex-max, gpt-5.1-codex-mini) do NOT support "none": + - Codex and Codex Max: "none" auto-converts to "low" + - Codex Mini: "none" auto-converts to "medium" (as before) +- Documentation updated with complete reasoning effort support matrix per model family. + +### References +- **OpenAI API docs** (`platform.openai.com/docs/api-reference/chat/create`): "gpt-5.1 defaults to none, which does not perform reasoning. The supported reasoning values for gpt-5.1 are none, low, medium, and high." +- **Codex CLI** (`codex-rs/protocol/src/openai_models.rs`): `ReasoningEffort` enum includes `None` variant with `#[serde(rename_all = "lowercase")]` serialization to `"none"`. +- **Codex CLI** (`codex-rs/core/src/client.rs`): Request builder passes `ReasoningEffort::None` through to API without validation/rejection. +- **Codex CLI** (`docs/config.md`): Documents `model_reasoning_effort = "none"` as valid config option. + +### Notes +- This plugin defaults to "medium" for better coding assistance; users must explicitly set "none" if desired. + ## [4.1.0] - 2025-12-11 **Feature release**: GPT 5.2 model support and image input capabilities. diff --git a/README.md b/README.md index 151ecd6..2047b06 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Follow me on [X @nummanthinks](https://x.com/nummanthinks) for future updates an ## Features - ✅ **ChatGPT Plus/Pro OAuth authentication** - Use your existing subscription -- ✅ **16 pre-configured model variants** - GPT 5.2, GPT 5.1, GPT 5.1 Codex, GPT 5.1 Codex Max, and GPT 5.1 Codex Mini presets for all reasoning levels +- ✅ **18 pre-configured model variants** - GPT 5.2, GPT 5.1, GPT 5.1 Codex, GPT 5.1 Codex Max, and GPT 5.1 Codex Mini presets for all reasoning levels - ✅ **GPT 5.2 support** - Latest model with `low/medium/high/xhigh` reasoning levels - ✅ **Full image input support** - All models configured with multimodal capabilities for reading screenshots, diagrams, and images - ⚠️ **GPT 5.1+ only** - Older GPT 5.0 models are deprecated and may not work reliably @@ -62,7 +62,7 @@ Follow me on [X @nummanthinks](https://x.com/nummanthinks) for future updates an #### Recommended: Pin the Version ```json -"plugin": ["opencode-openai-codex-auth@4.1.0"] +"plugin": ["opencode-openai-codex-auth@4.1.1"] ``` **Why pin versions?** OpenCode uses Bun's lockfile which pins resolved versions. If you use `"opencode-openai-codex-auth"` without a version, it resolves to "latest" once and **never updates** even when new versions are published. @@ -76,7 +76,7 @@ Simply change the version in your config and restart OpenCode: "plugin": ["opencode-openai-codex-auth@3.3.0"] // To: -"plugin": ["opencode-openai-codex-auth@4.1.0"] +"plugin": ["opencode-openai-codex-auth@4.1.1"] ``` OpenCode will detect the version mismatch and install the new version automatically. @@ -107,12 +107,12 @@ Check [releases](https://github.com/numman-ali/opencode-openai-codex-auth/releas 1. **Copy the full configuration** from [`config/full-opencode.json`](./config/full-opencode.json) to your opencode config file. - The config includes 16 models with image input support. Here's a condensed example showing the structure: + The config includes 18 models with image input support. Here's a condensed example showing the structure: ```json { "$schema": "https://opencode.ai/config.json", - "plugin": ["opencode-openai-codex-auth@4.1.0"], + "plugin": ["opencode-openai-codex-auth@4.1.1"], "provider": { "openai": { "options": { @@ -159,12 +159,12 @@ Check [releases](https://github.com/numman-ali/opencode-openai-codex-auth/releas **Global config**: `~/.config/opencode/opencode.json` **Project config**: `/.opencode.json` - This gives you 16 model variants with different reasoning levels: - - **gpt-5.2** (low/medium/high/xhigh) - Latest GPT 5.2 model with full reasoning support + This gives you 18 model variants with different reasoning levels: + - **gpt-5.2** (none/low/medium/high/xhigh) - Latest GPT 5.2 model with full reasoning support - **gpt-5.1-codex-max** (low/medium/high/xhigh) - Codex Max presets - **gpt-5.1-codex** (low/medium/high) - Codex model presets - **gpt-5.1-codex-mini** (medium/high) - Codex mini tier presets - - **gpt-5.1** (low/medium/high) - General-purpose reasoning presets + - **gpt-5.1** (none/low/medium/high) - General-purpose reasoning presets All appear in the opencode model selector as "GPT 5.1 Codex Low (OAuth)", "GPT 5.1 High (OAuth)", etc. @@ -305,7 +305,7 @@ These defaults match the official Codex CLI behavior and can be customized (see ### ⚠️ REQUIRED: Use Pre-Configured File **YOU MUST use [`config/full-opencode.json`](./config/full-opencode.json)** - this is the only officially supported configuration: -- 16 pre-configured model variants (GPT 5.2, GPT 5.1, Codex, Codex Max, Codex Mini) +- 18 pre-configured model variants (GPT 5.2, GPT 5.1, Codex, Codex Max, Codex Mini) - Image input support enabled for all models - Optimal configuration for each reasoning level - All variants visible in the opencode model selector @@ -323,18 +323,19 @@ If you want to customize settings yourself, you can configure options at provide ⚠️ **Important**: Families have different supported values. -| Setting | GPT-5.2 Values | GPT-5 / GPT-5.1 Values | GPT-5.1-Codex Values | GPT-5.1-Codex-Max Values | Plugin Default | -|---------|---------------|----------------------|----------------------|---------------------------|----------------| -| `reasoningEffort` | `low`, `medium`, `high`, `xhigh` | `minimal`, `low`, `medium`, `high` | `low`, `medium`, `high` | `none`, `low`, `medium`, `high`, `xhigh` | `medium` (global), `high` for Codex Max/5.2 | +| Setting | GPT-5.2 Values | GPT-5.1 Values | GPT-5.1-Codex Values | GPT-5.1-Codex-Max Values | Plugin Default | +|---------|---------------|----------------|----------------------|---------------------------|----------------| +| `reasoningEffort` | `none`, `low`, `medium`, `high`, `xhigh` | `none`, `low`, `medium`, `high` | `low`, `medium`, `high` | `low`, `medium`, `high`, `xhigh` | `medium` (global), `high` for Codex Max/5.2 | | `reasoningSummary` | `auto`, `concise`, `detailed` | `auto`, `concise`, `detailed` | `auto`, `concise`, `detailed` | `auto`, `concise`, `detailed`, `off`, `on` | `auto` | | `textVerbosity` | `low`, `medium`, `high` | `low`, `medium`, `high` | `medium` or `high` | `medium` or `high` | `medium` | | `include` | Array of strings | Array of strings | Array of strings | Array of strings | `["reasoning.encrypted_content"]` | > **Notes**: -> - GPT 5.2 supports `xhigh` reasoning like Codex Max. +> - GPT 5.2 and GPT 5.1 (general purpose) support `none` reasoning per OpenAI API docs. +> - `none` is NOT supported for Codex variants - auto-converts to `low` for Codex/Codex Max, or `medium` for Codex Mini. +> - GPT 5.2 and Codex Max support `xhigh` reasoning. > - `minimal` effort is auto-normalized to `low` for Codex models. > - Codex Mini clamps to `medium`/`high`; `xhigh` downgrades to `high`. -> - Codex Max supports `none`/`xhigh` plus extended reasoning options while keeping the same 272k context / 128k output limits. > - All models have `modalities.input: ["text", "image"]` enabled for multimodal support. #### Global Configuration Example @@ -344,7 +345,7 @@ Apply settings to all models: ```json { "$schema": "https://opencode.ai/config.json", - "plugin": ["opencode-openai-codex-auth@4.1.0"], + "plugin": ["opencode-openai-codex-auth@4.1.1"], "model": "openai/gpt-5-codex", "provider": { "openai": { @@ -364,7 +365,7 @@ Create your own named variants in the model selector: ```json { "$schema": "https://opencode.ai/config.json", - "plugin": ["opencode-openai-codex-auth@4.1.0"], + "plugin": ["opencode-openai-codex-auth@4.1.1"], "provider": { "openai": { "models": { diff --git a/config/full-opencode.json b/config/full-opencode.json index f6df4d2..851d25c 100644 --- a/config/full-opencode.json +++ b/config/full-opencode.json @@ -1,7 +1,7 @@ { "$schema": "https://opencode.ai/config.json", "plugin": [ - "opencode-openai-codex-auth@4.1.0" + "opencode-openai-codex-auth@4.1.1" ], "provider": { "openai": { @@ -15,6 +15,31 @@ "store": false }, "models": { + "gpt-5.2-none": { + "name": "GPT 5.2 None (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "options": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": [ + "reasoning.encrypted_content" + ], + "store": false + } + }, "gpt-5.2-low": { "name": "GPT 5.2 Low (OAuth)", "limit": { @@ -22,8 +47,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "low", @@ -42,8 +72,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "medium", @@ -62,8 +97,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "high", @@ -82,8 +122,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "xhigh", @@ -102,8 +147,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "low", @@ -122,8 +172,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "medium", @@ -142,8 +197,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "high", @@ -162,8 +222,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "xhigh", @@ -182,8 +247,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "low", @@ -202,8 +272,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "medium", @@ -222,8 +297,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "high", @@ -242,8 +322,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "medium", @@ -262,8 +347,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "high", @@ -275,6 +365,31 @@ "store": false } }, + "gpt-5.1-none": { + "name": "GPT 5.1 None (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "options": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": [ + "reasoning.encrypted_content" + ], + "store": false + } + }, "gpt-5.1-low": { "name": "GPT 5.1 Low (OAuth)", "limit": { @@ -282,8 +397,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "low", @@ -302,8 +422,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "medium", @@ -322,8 +447,13 @@ "output": 128000 }, "modalities": { - "input": ["text", "image"], - "output": ["text"] + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] }, "options": { "reasoningEffort": "high", @@ -338,4 +468,4 @@ } } } -} +} \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md index 3c5bbe9..7ee31d3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -46,28 +46,35 @@ Complete reference for configuring the OpenCode OpenAI Codex Auth Plugin. Controls computational effort for reasoning. -**GPT-5 Values:** -- `minimal` - Fastest, least reasoning +**GPT-5.2 Values** (per OpenAI API docs and Codex CLI `ReasoningEffort` enum): +- `none` - No dedicated reasoning phase (disables reasoning) +- `low` - Light reasoning +- `medium` - Balanced (default) +- `high` - Deep reasoning +- `xhigh` - Extra depth for long-horizon tasks + +**GPT-5.1 Values** (per OpenAI API docs and Codex CLI `ReasoningEffort` enum): +- `none` - No dedicated reasoning phase (disables reasoning) - `low` - Light reasoning - `medium` - Balanced (default) - `high` - Deep reasoning -**GPT-5-Codex Values:** +**GPT-5.1-Codex / GPT-5.1-Codex-Max Values:** - `low` - Fastest for code - `medium` - Balanced (default) - `high` - Maximum code quality +- `xhigh` - Extra depth (Codex Max only) -**GPT-5.1-Codex-Max Values:** -- `none` - No dedicated reasoning phase -- `low` - Light reasoning -- `medium` - Balanced -- `high` - Deep reasoning (default for this family) -- `xhigh` - Extra depth for long-horizon tasks +**GPT-5.1-Codex-Mini Values:** +- `medium` - Balanced (default) +- `high` - Maximum code quality **Notes**: +- `none` is supported for GPT-5.2 and GPT-5.1 (general purpose) per OpenAI API documentation +- `none` is NOT supported for Codex variants - it auto-converts to `low` for Codex or `medium` for Codex Mini - `minimal` auto-converts to `low` for Codex models -- `gpt-5-codex-mini*` and `gpt-5.1-codex-mini*` only support `medium` or `high`; lower settings are clamped to `medium` and `xhigh` downgrades to `high` -- Codex Max supports `none` and `xhigh` and defaults to `high` when not specified +- `xhigh` is only supported for GPT-5.2 and GPT-5.1-Codex-Max; other models downgrade to `high` +- Codex Mini only supports `medium` or `high`; lower settings clamp to `medium` **Example:** ```json diff --git a/lib/oauth-success.html b/lib/oauth-success.html index 6e9652c..30ab44a 100644 --- a/lib/oauth-success.html +++ b/lib/oauth-success.html @@ -548,7 +548,7 @@
-
opencode-openai-codex-auth@1.0.4 — OAuth Authentication
+
opencode-openai-codex-auth@4.1.0 — OAuth Authentication
diff --git a/lib/request/helpers/model-map.ts b/lib/request/helpers/model-map.ts index e748f91..47fed19 100644 --- a/lib/request/helpers/model-map.ts +++ b/lib/request/helpers/model-map.ts @@ -30,9 +30,10 @@ export const MODEL_MAP: Record = { "gpt-5.1-codex-max-xhigh": "gpt-5.1-codex-max", // ============================================================================ - // GPT-5.2 Models (same reasoning support as Codex Max: low/medium/high/xhigh) + // GPT-5.2 Models (supports none/low/medium/high/xhigh per OpenAI API docs) // ============================================================================ "gpt-5.2": "gpt-5.2", + "gpt-5.2-none": "gpt-5.2", "gpt-5.2-low": "gpt-5.2", "gpt-5.2-medium": "gpt-5.2", "gpt-5.2-high": "gpt-5.2", @@ -46,9 +47,10 @@ export const MODEL_MAP: Record = { "gpt-5.1-codex-mini-high": "gpt-5.1-codex-mini", // ============================================================================ - // GPT-5.1 General Purpose Models + // GPT-5.1 General Purpose Models (supports none/low/medium/high per OpenAI API docs) // ============================================================================ "gpt-5.1": "gpt-5.1", + "gpt-5.1-none": "gpt-5.1", "gpt-5.1-low": "gpt-5.1", "gpt-5.1-medium": "gpt-5.1", "gpt-5.1-high": "gpt-5.1", diff --git a/lib/request/request-transformer.ts b/lib/request/request-transformer.ts index 70995dc..2c5bab4 100644 --- a/lib/request/request-transformer.ts +++ b/lib/request/request-transformer.ts @@ -146,10 +146,26 @@ export function getReasoningConfig( (normalizedName.includes("nano") || normalizedName.includes("mini")); + // GPT-5.1 general purpose (not codex variants) - supports "none" per OpenAI API docs + const isGpt51General = + (normalizedName.includes("gpt-5.1") || normalizedName.includes("gpt 5.1")) && + !isCodex && + !isCodexMax && + !isCodexMini; + // GPT 5.2 and Codex Max support xhigh reasoning const supportsXhigh = isGpt52 || isCodexMax; + // GPT 5.1 and GPT 5.2 support "none" reasoning per: + // - OpenAI API docs: "gpt-5.1 defaults to none, supports: none, low, medium, high" + // - Codex CLI: ReasoningEffort enum includes None variant (codex-rs/protocol/src/openai_models.rs) + // - Codex CLI: docs/config.md lists "none" as valid for model_reasoning_effort + // - gpt-5.2 (being newer) also supports: none, low, medium, high, xhigh + const supportsNone = isGpt52 || isGpt51General; + // Default based on model type (Codex CLI defaults) + // Note: OpenAI docs say gpt-5.1 defaults to "none", but we default to "medium" + // for better coding assistance unless user explicitly requests "none" const defaultEffort: ReasoningConfig["effort"] = isCodexMini ? "medium" : supportsXhigh @@ -178,6 +194,12 @@ export function getReasoningConfig( effort = "high"; } + // For models that don't support "none", upgrade to "low" + // (Codex models don't support "none" - only GPT-5.1 and GPT-5.2 general purpose do) + if (!supportsNone && effort === "none") { + effort = "low"; + } + // Normalize "minimal" to "low" for Codex families // Codex CLI presets are low/medium/high (or xhigh for Codex Max) if (isCodex && effort === "minimal") { @@ -452,21 +474,36 @@ export async function transformRequestBody( body.input = addToolRemapMessage(body.input, !!body.tools); } - // Filter orphaned function_call_output items (where function_call was an item_reference that got filtered) - // Keep matched pairs for compaction context - if (!body.tools && body.input) { - // Collect all call_ids from function_call items + // Handle orphaned function_call_output items (where function_call was an item_reference that got filtered) + // Instead of removing orphans (which causes infinite loops as LLM loses tool results), + // convert them to messages to preserve context while avoiding API errors + if (body.input) { const functionCallIds = new Set( body.input .filter((item) => item.type === "function_call" && item.call_id) .map((item) => item.call_id), ); - // Only filter function_call_output items that don't have a matching function_call - body.input = body.input.filter((item) => { - if (item.type === "function_call_output") { - return functionCallIds.has(item.call_id); + body.input = body.input.map((item) => { + if (item.type === "function_call_output" && !functionCallIds.has(item.call_id)) { + const toolName = typeof (item as any).name === "string" ? (item as any).name : "tool"; + const callId = (item as any).call_id ?? ""; + let text: string; + try { + const out = (item as any).output; + text = typeof out === "string" ? out : JSON.stringify(out); + } catch { + text = String((item as any).output ?? ""); + } + if (text.length > 16000) { + text = text.slice(0, 16000) + "\n...[truncated]"; + } + return { + type: "message", + role: "assistant", + content: `[Previous ${toolName} result; call_id=${callId}]: ${text}`, + } as InputItem; } - return true; + return item; }); } } diff --git a/package.json b/package.json index 918f998..7934447 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opencode-openai-codex-auth", - "version": "4.1.0", + "version": "4.1.1", "description": "OpenAI ChatGPT (Codex backend) OAuth auth plugin for opencode - use your ChatGPT Plus/Pro subscription instead of API credits", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/scripts/test-all-models.sh b/scripts/test-all-models.sh index fa4e0bd..44b2101 100755 --- a/scripts/test-all-models.sh +++ b/scripts/test-all-models.sh @@ -174,7 +174,8 @@ test_model "gpt-5.1-codex-max-medium" "gpt-5.1-codex-max" "codex-max" "medium test_model "gpt-5.1-codex-max-high" "gpt-5.1-codex-max" "codex-max" "high" "detailed" "medium" test_model "gpt-5.1-codex-max-xhigh" "gpt-5.1-codex-max" "codex-max" "xhigh" "detailed" "medium" -# GPT 5.2 presets (same reasoning support as Codex Max: low/medium/high/xhigh) +# GPT 5.2 presets (supports none/low/medium/high/xhigh per OpenAI API docs) +test_model "gpt-5.2-none" "gpt-5.2" "gpt-5.2" "none" "auto" "medium" test_model "gpt-5.2-low" "gpt-5.2" "gpt-5.2" "low" "auto" "medium" test_model "gpt-5.2-medium" "gpt-5.2" "gpt-5.2" "medium" "auto" "medium" test_model "gpt-5.2-high" "gpt-5.2" "gpt-5.2" "high" "detailed" "medium" @@ -184,7 +185,8 @@ test_model "gpt-5.2-xhigh" "gpt-5.2" "gpt-5.2" "xhigh" "detailed" "medium" test_model "gpt-5.1-codex-mini-medium" "gpt-5.1-codex-mini" "codex" "medium" "auto" "medium" test_model "gpt-5.1-codex-mini-high" "gpt-5.1-codex-mini" "codex" "high" "detailed" "medium" -# GPT 5.1 general-purpose presets +# GPT 5.1 general-purpose presets (supports none/low/medium/high per OpenAI API docs) +test_model "gpt-5.1-none" "gpt-5.1" "gpt-5.1" "none" "auto" "medium" test_model "gpt-5.1-low" "gpt-5.1" "gpt-5.1" "low" "auto" "low" test_model "gpt-5.1-medium" "gpt-5.1" "gpt-5.1" "medium" "auto" "medium" test_model "gpt-5.1-high" "gpt-5.1" "gpt-5.1" "high" "detailed" "high" diff --git a/test/request-transformer.test.ts b/test/request-transformer.test.ts index 025d6ab..f6fbc67 100644 --- a/test/request-transformer.test.ts +++ b/test/request-transformer.test.ts @@ -825,6 +825,62 @@ describe('Request Transformer Module', () => { expect(result.reasoning?.effort).toBe('high'); }); + it('should preserve none for GPT-5.2', async () => { + const body: RequestBody = { + model: 'gpt-5.2-none', + input: [], + }; + const userConfig: UserConfig = { + global: { reasoningEffort: 'none' }, + models: {}, + }; + const result = await transformRequestBody(body, codexInstructions, userConfig); + expect(result.model).toBe('gpt-5.2'); + expect(result.reasoning?.effort).toBe('none'); + }); + + it('should preserve none for GPT-5.1 general purpose', async () => { + const body: RequestBody = { + model: 'gpt-5.1-none', + input: [], + }; + const userConfig: UserConfig = { + global: { reasoningEffort: 'none' }, + models: {}, + }; + const result = await transformRequestBody(body, codexInstructions, userConfig); + expect(result.model).toBe('gpt-5.1'); + expect(result.reasoning?.effort).toBe('none'); + }); + + it('should upgrade none to low for GPT-5.1-codex (codex does not support none)', async () => { + const body: RequestBody = { + model: 'gpt-5.1-codex', + input: [], + }; + const userConfig: UserConfig = { + global: { reasoningEffort: 'none' }, + models: {}, + }; + const result = await transformRequestBody(body, codexInstructions, userConfig); + expect(result.model).toBe('gpt-5.1-codex'); + expect(result.reasoning?.effort).toBe('low'); + }); + + it('should upgrade none to low for GPT-5.1-codex-max (codex max does not support none)', async () => { + const body: RequestBody = { + model: 'gpt-5.1-codex-max', + input: [], + }; + const userConfig: UserConfig = { + global: { reasoningEffort: 'none' }, + models: {}, + }; + const result = await transformRequestBody(body, codexInstructions, userConfig); + expect(result.model).toBe('gpt-5.1-codex-max'); + expect(result.reasoning?.effort).toBe('low'); + }); + it('should preserve minimal for non-codex models', async () => { const body: RequestBody = { model: 'gpt-5', @@ -847,20 +903,23 @@ describe('Request Transformer Module', () => { expect(result.reasoning?.effort).toBe('medium'); }); - it('should drop orphaned function_call_output when no tools present', async () => { + it('should convert orphaned function_call_output to message to preserve context', async () => { const body: RequestBody = { model: 'gpt-5-codex', input: [ { type: 'message', role: 'user', content: 'hello' }, - { type: 'function_call_output', role: 'assistant', call_id: 'orphan_call', output: '{}' } as any, + { type: 'function_call_output', role: 'assistant', call_id: 'orphan_call', name: 'read', output: '{}' } as any, ], }; const result = await transformRequestBody(body, codexInstructions); expect(result.tools).toBeUndefined(); - expect(result.input).toHaveLength(1); + expect(result.input).toHaveLength(2); expect(result.input![0].type).toBe('message'); + expect(result.input![1].type).toBe('message'); + expect(result.input![1].role).toBe('assistant'); + expect(result.input![1].content).toContain('[Previous read result; call_id=orphan_call]'); }); it('should keep matched function_call pairs when no tools present (for compaction)', async () => {