mirror of
https://github.com/hanzoai/opencode-openai-codex-auth.git
synced 2026-09-24 17:01:02 +00:00
Add Codex Mini support
This commit is contained in:
@@ -4,7 +4,7 @@ This file provides coding guidance for AI agents (including Claude Code, Codex,
|
||||
|
||||
## Overview
|
||||
|
||||
This is an **opencode plugin** that enables OAuth authentication with OpenAI's ChatGPT Plus/Pro Codex backend. It allows users to access `gpt-5-codex` and `gpt-5` models through their ChatGPT subscription instead of using OpenAI Platform API credits.
|
||||
This is an **opencode plugin** that enables OAuth authentication with OpenAI's ChatGPT Plus/Pro Codex backend. It allows users to access `gpt-5-codex`, `gpt-5-codex-mini`, and `gpt-5` models through their ChatGPT subscription instead of using OpenAI Platform API credits.
|
||||
|
||||
**Key architecture principle**: 7-step fetch flow that intercepts opencode's OpenAI SDK requests, transforms them for the ChatGPT backend API, and handles OAuth token management.
|
||||
|
||||
@@ -41,7 +41,7 @@ The main entry point orchestrates a **7-step fetch flow**:
|
||||
1. **Token Management**: Check token expiration, refresh if needed
|
||||
2. **URL Rewriting**: Transform OpenAI Platform API URLs → ChatGPT backend API (`https://chatgpt.com/backend-api/codex/responses`)
|
||||
3. **Request Transformation**:
|
||||
- Normalize model names (all variants → `gpt-5` or `gpt-5-codex`)
|
||||
- Normalize model names (all variants → `gpt-5`, `gpt-5-codex`, or `codex-mini-latest`)
|
||||
- Inject Codex system instructions from latest GitHub release
|
||||
- Apply reasoning configuration (effort, summary, verbosity)
|
||||
- Add CODEX_MODE bridge prompt (default) or tool remap message (legacy)
|
||||
@@ -99,8 +99,9 @@ The main entry point orchestrates a **7-step fetch flow**:
|
||||
|
||||
**4. Model Normalization**:
|
||||
- All `gpt-5-codex` variants → `gpt-5-codex`
|
||||
- All `gpt-5-codex-mini*` or `codex-mini-latest` variants → `codex-mini-latest`
|
||||
- All `gpt-5` variants → `gpt-5`
|
||||
- `minimal` effort auto-normalized to `low` for gpt-5-codex (API limitation)
|
||||
- `minimal` effort auto-normalized to `low` for gpt-5-codex (API limitation) and clamped to `medium` (or `high` when requested) for Codex Mini
|
||||
|
||||
**5. Codex Instructions Caching**:
|
||||
- Fetches from latest release tag (not main branch)
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
All notable changes to this project are documented here. Dates use the ISO format (YYYY-MM-DD).
|
||||
|
||||
## [3.1.0] - 2025-11-11
|
||||
### Added
|
||||
- Codex Mini support end-to-end: normalization to the `codex-mini-latest` slug, proper reasoning defaults, and two new presets (`gpt-5-codex-mini-medium` / `gpt-5-codex-mini-high`).
|
||||
- Documentation & configuration updates describing the Codex Mini tier (200k input / 100k output tokens) plus refreshed totals (11 presets, 160+ unit tests).
|
||||
|
||||
### Fixed
|
||||
- Prevented Codex Mini from inheriting the lightweight (`minimal`) reasoning profile used by `gpt-5-mini`/`nano`, ensuring the API always receives supported effort levels.
|
||||
|
||||
## [3.0.0] - 2025-11-04
|
||||
### Added
|
||||
- Codex-style usage-limit messaging that mirrors the 5-hour and weekly windows reported by the Codex CLI.
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ All contributions MUST:
|
||||
## Code Standards
|
||||
|
||||
- **TypeScript:** All code must be TypeScript with strict type checking
|
||||
- **Testing:** Include tests for new functionality (we maintain 159+ unit tests)
|
||||
- **Testing:** Include tests for new functionality (we maintain 160+ unit tests)
|
||||
- **Documentation:** Update README.md for user-facing changes
|
||||
- **Modular design:** Keep functions focused and under 40 lines
|
||||
- **No external dependencies:** Minimize dependencies (currently only @openauthjs/openauth)
|
||||
|
||||
@@ -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
|
||||
- ✅ **9 pre-configured model variants** - Low/Medium/High reasoning for both gpt-5 and gpt-5-codex
|
||||
- ✅ **11 pre-configured model variants** - Includes Codex Mini (medium/high) alongside all gpt-5 and gpt-5-codex presets
|
||||
- ✅ **Zero external dependencies** - Lightweight with only @openauthjs/openauth
|
||||
- ✅ **Auto-refreshing tokens** - Handles token expiration automatically
|
||||
- ✅ **Prompt caching** - Reuses responses across turns via stable `prompt_cache_key`
|
||||
@@ -43,7 +43,7 @@ Follow me on [X @nummanthinks](https://x.com/nummanthinks) for future updates an
|
||||
- ✅ **Automatic tool remapping** - Codex tools → opencode tools
|
||||
- ✅ **Configurable reasoning** - Control effort, summary verbosity, and text output
|
||||
- ✅ **Usage-aware errors** - Shows clear guidance when ChatGPT subscription limits are reached
|
||||
- ✅ **Type-safe & tested** - Strict TypeScript with 159 unit tests + 14 integration tests
|
||||
- ✅ **Type-safe & tested** - Strict TypeScript with 160+ unit tests + 14 integration tests
|
||||
- ✅ **Modular architecture** - Easy to maintain and extend
|
||||
|
||||
## Installation
|
||||
@@ -123,6 +123,38 @@ For the complete experience with all reasoning variants matching the official Co
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-codex-mini-medium": {
|
||||
"name": "GPT 5 Codex Mini Medium (OAuth)",
|
||||
"limit": {
|
||||
"context": 200000,
|
||||
"output": 100000
|
||||
},
|
||||
"options": {
|
||||
"reasoningEffort": "medium",
|
||||
"reasoningSummary": "auto",
|
||||
"textVerbosity": "medium",
|
||||
"include": [
|
||||
"reasoning.encrypted_content"
|
||||
],
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-codex-mini-high": {
|
||||
"name": "GPT 5 Codex Mini High (OAuth)",
|
||||
"limit": {
|
||||
"context": 200000,
|
||||
"output": 100000
|
||||
},
|
||||
"options": {
|
||||
"reasoningEffort": "high",
|
||||
"reasoningSummary": "detailed",
|
||||
"textVerbosity": "medium",
|
||||
"include": [
|
||||
"reasoning.encrypted_content"
|
||||
],
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-minimal": {
|
||||
"name": "GPT 5 Minimal (OAuth)",
|
||||
"limit": {
|
||||
@@ -228,8 +260,9 @@ For the complete experience with all reasoning variants matching the official Co
|
||||
**Global config**: `~/.config/opencode/opencode.json`
|
||||
**Project config**: `<project>/.opencode.json`
|
||||
|
||||
This gives you 9 model variants with different reasoning levels:
|
||||
This gives you 11 model variants with different reasoning levels:
|
||||
- **gpt-5-codex** (low/medium/high) - Code-optimized reasoning
|
||||
- **gpt-5-codex-mini** (medium/high) - Cheaper Codex tier with 200k/100k tokens
|
||||
- **gpt-5** (minimal/low/medium/high) - General-purpose reasoning
|
||||
- **gpt-5-mini** and **gpt-5-nano** - Lightweight variants
|
||||
|
||||
@@ -316,6 +349,8 @@ When using [`config/full-opencode.json`](./config/full-opencode.json), you get t
|
||||
| `gpt-5-codex-low` | GPT 5 Codex Low (OAuth) | Low | Fast code generation |
|
||||
| `gpt-5-codex-medium` | GPT 5 Codex Medium (OAuth) | Medium | Balanced code tasks |
|
||||
| `gpt-5-codex-high` | GPT 5 Codex High (OAuth) | High | Complex code & tools |
|
||||
| `gpt-5-codex-mini-medium` | GPT 5 Codex Mini Medium (OAuth) | Medium | Cheaper Codex tier (200k/100k) |
|
||||
| `gpt-5-codex-mini-high` | GPT 5 Codex Mini High (OAuth) | High | Codex Mini with maximum reasoning |
|
||||
| `gpt-5-minimal` | GPT 5 Minimal (OAuth) | Minimal | Quick answers, simple tasks |
|
||||
| `gpt-5-low` | GPT 5 Low (OAuth) | Low | Faster responses with light reasoning |
|
||||
| `gpt-5-medium` | GPT 5 Medium (OAuth) | Medium | Balanced general-purpose tasks |
|
||||
@@ -326,6 +361,8 @@ When using [`config/full-opencode.json`](./config/full-opencode.json), you get t
|
||||
**Usage**: `--model=openai/<CLI Model ID>` (e.g., `--model=openai/gpt-5-codex-low`)
|
||||
**Display**: TUI shows the friendly name (e.g., "GPT 5 Codex Low (OAuth)")
|
||||
|
||||
> **Note**: All `gpt-5-codex-mini*` presets normalize to the ChatGPT slug `codex-mini-latest` (200k input / 100k output tokens).
|
||||
|
||||
All accessed via your ChatGPT Plus/Pro subscription.
|
||||
|
||||
### Using in Custom Commands
|
||||
@@ -365,7 +402,7 @@ These defaults match the official Codex CLI behavior and can be customized (see
|
||||
### Recommended: Use Pre-Configured File
|
||||
|
||||
The easiest way to get started is to use [`config/full-opencode.json`](./config/full-opencode.json), which provides:
|
||||
- 9 pre-configured model variants matching Codex CLI presets
|
||||
- 11 pre-configured model variants matching Codex CLI presets
|
||||
- Optimal settings for each reasoning level
|
||||
- All variants visible in the opencode model selector
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ cp config/full-opencode.json ~/.config/opencode/opencode.json
|
||||
This demonstrates:
|
||||
- Global options for all models
|
||||
- Per-model configuration overrides
|
||||
- All supported model variants (gpt-5-codex, gpt-5, gpt-5-mini, gpt-5-nano)
|
||||
- All supported model variants (gpt-5-codex, gpt-5-codex-mini, gpt-5, gpt-5-mini, gpt-5-nano)
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -63,6 +63,38 @@
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-codex-mini-medium": {
|
||||
"name": "GPT 5 Codex Mini Medium (OAuth)",
|
||||
"limit": {
|
||||
"context": 200000,
|
||||
"output": 100000
|
||||
},
|
||||
"options": {
|
||||
"reasoningEffort": "medium",
|
||||
"reasoningSummary": "auto",
|
||||
"textVerbosity": "medium",
|
||||
"include": [
|
||||
"reasoning.encrypted_content"
|
||||
],
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-codex-mini-high": {
|
||||
"name": "GPT 5 Codex Mini High (OAuth)",
|
||||
"limit": {
|
||||
"context": 200000,
|
||||
"output": 100000
|
||||
},
|
||||
"options": {
|
||||
"reasoningEffort": "high",
|
||||
"reasoningSummary": "detailed",
|
||||
"textVerbosity": "medium",
|
||||
"include": [
|
||||
"reasoning.encrypted_content"
|
||||
],
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-minimal": {
|
||||
"name": "GPT 5 Minimal (OAuth)",
|
||||
"limit": {
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ This plugin bridges two different systems with careful engineering:
|
||||
4. **15-Minute Caching** - Prevents GitHub API rate limit exhaustion
|
||||
5. **Per-Model Configuration** - Enables quality presets with quick switching
|
||||
|
||||
**Testing**: 159 unit tests + 14 integration tests with actual API verification
|
||||
**Testing**: 160+ unit tests + 14 integration tests with actual API verification
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -57,7 +57,9 @@ Controls computational effort for reasoning.
|
||||
- `medium` - Balanced (default)
|
||||
- `high` - Maximum code quality
|
||||
|
||||
**Note**: `minimal` auto-converts to `low` for gpt-5-codex (API limitation)
|
||||
**Notes**:
|
||||
- `minimal` auto-converts to `low` for gpt-5-codex (API limitation)
|
||||
- `gpt-5-codex-mini*` only supports `medium` or `high`; lower settings are clamped to `medium`
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
@@ -379,7 +381,7 @@ CODEX_MODE=1 opencode run "task" # Temporarily enable
|
||||
## Configuration Files
|
||||
|
||||
**Provided Examples:**
|
||||
- [config/full-opencode.json](../config/full-opencode.json) - Complete with 9 variants
|
||||
- [config/full-opencode.json](../config/full-opencode.json) - Complete with 11 variants (adds Codex Mini presets)
|
||||
- [config/minimal-opencode.json](../config/minimal-opencode.json) - Minimal setup
|
||||
|
||||
> **Why choose the full config?** OpenCode's auto-compaction and usage widgets rely on the per-model `limit` metadata present only in `full-opencode.json`. Use the minimal config only if you don't need those UI features.
|
||||
|
||||
@@ -262,8 +262,8 @@ let include: Vec<String> = if reasoning.is_some() {
|
||||
└─ tools: [...]
|
||||
|
||||
2. Model Normalization
|
||||
├─ Detect codex/gpt-5 variants
|
||||
└─ Normalize to "gpt-5" or "gpt-5-codex"
|
||||
├─ Detect codex/gpt-5/codex-mini variants
|
||||
└─ Normalize to "gpt-5", "gpt-5-codex", or "codex-mini-latest"
|
||||
|
||||
3. Config Merging
|
||||
├─ Global options (provider.openai.options)
|
||||
@@ -314,7 +314,7 @@ let include: Vec<String> = if reasoning.is_some() {
|
||||
| **store Parameter** | `false` (ChatGPT) | `false` | ✅ |
|
||||
| **Message IDs** | Stripped in stateless | Stripped | ✅ |
|
||||
| **reasoning.encrypted_content** | ✅ Included | ✅ Included | ✅ |
|
||||
| **Model Normalization** | "gpt-5" / "gpt-5-codex" | Same | ✅ |
|
||||
| **Model Normalization** | "gpt-5" / "gpt-5-codex" / "codex-mini-latest" | Same | ✅ |
|
||||
| **Reasoning Effort** | medium (default) | medium (default) | ✅ |
|
||||
| **Text Verbosity** | medium (codex), low (gpt-5) | Same | ✅ |
|
||||
|
||||
|
||||
@@ -577,6 +577,8 @@ opencode
|
||||
normalizeModel("gpt-5-codex") // → "gpt-5-codex" ✅
|
||||
normalizeModel("gpt-5-codex-low") // → "gpt-5-codex" ✅
|
||||
normalizeModel("GPT-5-CODEX-HIGH") // → "gpt-5-codex" ✅
|
||||
normalizeModel("gpt-5-codex-mini-high")// → "codex-mini-latest" ✅
|
||||
normalizeModel("codex-mini-latest") // → "codex-mini-latest" ✅
|
||||
normalizeModel("my-codex-model") // → "gpt-5-codex" ✅
|
||||
normalizeModel("gpt-5") // → "gpt-5" ✅
|
||||
normalizeModel("gpt-5-mini") // → "gpt-5" ✅
|
||||
@@ -590,17 +592,25 @@ normalizeModel("random-model") // → "gpt-5" ✅ (fallback)
|
||||
```typescript
|
||||
export function normalizeModel(model: string | undefined): string {
|
||||
if (!model) return "gpt-5";
|
||||
if (model.includes("codex")) return "gpt-5-codex"; // Check codex first
|
||||
if (model.includes("gpt-5")) return "gpt-5"; // Then gpt-5
|
||||
return "gpt-5"; // Safe fallback
|
||||
const normalized = model.toLowerCase();
|
||||
if (normalized.includes("codex-mini") || normalized.includes("codex-mini-latest")) {
|
||||
return "codex-mini-latest";
|
||||
}
|
||||
if (normalized.includes("codex")) {
|
||||
return "gpt-5-codex";
|
||||
}
|
||||
if (normalized.includes("gpt-5") || normalized.includes("gpt 5")) {
|
||||
return "gpt-5";
|
||||
}
|
||||
return "gpt-5";
|
||||
}
|
||||
```
|
||||
|
||||
**Why this works:**
|
||||
- ✅ Case-insensitive (`.includes()` works with any case)
|
||||
- ✅ Case-insensitive (`.toLowerCase()` + `.includes()`)
|
||||
- ✅ Pattern-based (works with any naming)
|
||||
- ✅ Safe fallback (unknown models → `gpt-5`)
|
||||
- ✅ Codex priority (checks "codex" before "gpt-5")
|
||||
- ✅ Codex priority with explicit Codex Mini support (`codex-mini*` → `codex-mini-latest`)
|
||||
|
||||
---
|
||||
|
||||
@@ -657,12 +667,14 @@ describe('normalizeModel', () => {
|
||||
test('handles all default models', () => {
|
||||
expect(normalizeModel('gpt-5')).toBe('gpt-5')
|
||||
expect(normalizeModel('gpt-5-codex')).toBe('gpt-5-codex')
|
||||
expect(normalizeModel('gpt-5-codex-mini')).toBe('codex-mini-latest')
|
||||
expect(normalizeModel('gpt-5-mini')).toBe('gpt-5')
|
||||
expect(normalizeModel('gpt-5-nano')).toBe('gpt-5')
|
||||
})
|
||||
|
||||
test('handles custom preset names', () => {
|
||||
expect(normalizeModel('gpt-5-codex-low')).toBe('gpt-5-codex')
|
||||
expect(normalizeModel('openai/gpt-5-codex-mini-high')).toBe('codex-mini-latest')
|
||||
expect(normalizeModel('gpt-5-high')).toBe('gpt-5')
|
||||
})
|
||||
|
||||
@@ -672,6 +684,7 @@ describe('normalizeModel', () => {
|
||||
|
||||
test('handles edge cases', () => {
|
||||
expect(normalizeModel(undefined)).toBe('gpt-5')
|
||||
expect(normalizeModel('codex-mini-latest')).toBe('codex-mini-latest')
|
||||
expect(normalizeModel('random')).toBe('gpt-5')
|
||||
})
|
||||
})
|
||||
|
||||
+33
-3
@@ -28,7 +28,7 @@ OpenCode automatically installs plugins - no `npm install` needed!
|
||||
|
||||
#### Option A: Full Configuration (Recommended)
|
||||
|
||||
Get 9 pre-configured model variants with optimal settings.
|
||||
Get 11 pre-configured model variants with optimal settings (including Codex Mini).
|
||||
|
||||
Add this to `~/.config/opencode/opencode.json`:
|
||||
|
||||
@@ -88,6 +88,34 @@ Add this to `~/.config/opencode/opencode.json`:
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-codex-mini-medium": {
|
||||
"name": "GPT 5 Codex Mini Medium (OAuth)",
|
||||
"limit": {
|
||||
"context": 200000,
|
||||
"output": 100000
|
||||
},
|
||||
"options": {
|
||||
"reasoningEffort": "medium",
|
||||
"reasoningSummary": "auto",
|
||||
"textVerbosity": "medium",
|
||||
"include": ["reasoning.encrypted_content"],
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-codex-mini-high": {
|
||||
"name": "GPT 5 Codex Mini High (OAuth)",
|
||||
"limit": {
|
||||
"context": 200000,
|
||||
"output": 100000
|
||||
},
|
||||
"options": {
|
||||
"reasoningEffort": "high",
|
||||
"reasoningSummary": "detailed",
|
||||
"textVerbosity": "medium",
|
||||
"include": ["reasoning.encrypted_content"],
|
||||
"store": false
|
||||
}
|
||||
},
|
||||
"gpt-5-minimal": {
|
||||
"name": "GPT 5 Minimal (OAuth)",
|
||||
"limit": {
|
||||
@@ -181,7 +209,9 @@ Add this to `~/.config/opencode/opencode.json`:
|
||||
**What you get:**
|
||||
- ✅ GPT-5 Codex (Low/Medium/High reasoning)
|
||||
- ✅ GPT-5 (Minimal/Low/Medium/High reasoning)
|
||||
- ✅ gpt-5-mini, gpt-5-nano (lightweight variants)
|
||||
- ✅ gpt-5-codex-mini (medium/high) plus gpt-5-mini & gpt-5-nano (lightweight variants)
|
||||
|
||||
> Codex Mini presets normalize to the ChatGPT slug `codex-mini-latest` (200k input / 100k output tokens).
|
||||
- ✅ 272k context + 128k output window for every preset
|
||||
- ✅ All visible in OpenCode model selector
|
||||
- ✅ Optimal settings for each reasoning level
|
||||
@@ -230,7 +260,7 @@ opencode run "write hello world to test.txt" --model=openai/gpt-5-codex
|
||||
opencode
|
||||
```
|
||||
|
||||
If using full config, you'll see all 9 variants in the model selector!
|
||||
If using full config, you'll see all 11 variants (including Codex Mini) in the model selector!
|
||||
|
||||
---
|
||||
|
||||
|
||||
+2
-2
@@ -82,11 +82,11 @@ opencode run "write hello world to test.txt" --model=openai/gpt-5-codex
|
||||
## Features
|
||||
|
||||
✅ **OAuth Authentication** - Secure ChatGPT Plus/Pro login
|
||||
✅ **Multiple Models** - gpt-5, gpt-5-codex, gpt-5-mini, gpt-5-nano
|
||||
✅ **Multiple Models** - gpt-5, gpt-5-codex, gpt-5-codex-mini, gpt-5-mini, gpt-5-nano
|
||||
✅ **Per-Model Configuration** - Different reasoning effort, verbosity for each variant
|
||||
✅ **Multi-Turn Conversations** - Full conversation history with stateless backend
|
||||
✅ **Backwards Compatible** - Works with old and new config formats
|
||||
✅ **Comprehensive Testing** - 159 unit tests + 14 integration tests
|
||||
✅ **Comprehensive Testing** - 160+ unit tests + 14 integration tests
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { logDebug, logWarn } from "../logger.js";
|
||||
import { TOOL_REMAP_MESSAGE } from "../prompts/codex.js";
|
||||
import { CODEX_OPENCODE_BRIDGE } from "../prompts/codex-opencode-bridge.js";
|
||||
import { getOpenCodeCodexPrompt } from "../prompts/opencode-codex.js";
|
||||
import { logDebug, logWarn } from "../logger.js";
|
||||
import type {
|
||||
UserConfig,
|
||||
ConfigOptions,
|
||||
InputItem,
|
||||
ReasoningConfig,
|
||||
RequestBody,
|
||||
InputItem,
|
||||
UserConfig,
|
||||
} from "../types.js";
|
||||
|
||||
/**
|
||||
@@ -18,12 +18,18 @@ import type {
|
||||
export function normalizeModel(model: string | undefined): string {
|
||||
if (!model) return "gpt-5";
|
||||
|
||||
const normalized = model.toLowerCase();
|
||||
|
||||
if (normalized.includes("codex-mini")) {
|
||||
return "codex-mini-latest";
|
||||
}
|
||||
|
||||
// Case-insensitive check for "codex" anywhere in the model name
|
||||
if (model.toLowerCase().includes("codex")) {
|
||||
if (normalized.includes("codex")) {
|
||||
return "gpt-5-codex";
|
||||
}
|
||||
// Case-insensitive check for "gpt-5" or "gpt 5" (with space)
|
||||
if (model.toLowerCase().includes("gpt-5") || model.toLowerCase().includes("gpt 5")) {
|
||||
if (normalized.includes("gpt-5") || normalized.includes("gpt 5")) {
|
||||
return "gpt-5";
|
||||
}
|
||||
|
||||
@@ -64,18 +70,37 @@ export function getReasoningConfig(
|
||||
originalModel: string | undefined,
|
||||
userConfig: ConfigOptions = {},
|
||||
): ReasoningConfig {
|
||||
const normalizedOriginal = originalModel?.toLowerCase() ?? "";
|
||||
const isCodexMini =
|
||||
normalizedOriginal.includes("codex-mini") ||
|
||||
normalizedOriginal.includes("codex mini") ||
|
||||
normalizedOriginal.includes("codex_mini") ||
|
||||
normalizedOriginal.includes("codex-mini-latest");
|
||||
const isCodex = normalizedOriginal.includes("codex") && !isCodexMini;
|
||||
const isLightweight =
|
||||
originalModel?.includes("nano") || originalModel?.includes("mini");
|
||||
const isCodex = originalModel?.includes("codex");
|
||||
!isCodexMini &&
|
||||
(normalizedOriginal.includes("nano") ||
|
||||
normalizedOriginal.includes("mini"));
|
||||
|
||||
// Default based on model type (Codex CLI defaults)
|
||||
const defaultEffort: "minimal" | "low" | "medium" | "high" = isLightweight
|
||||
? "minimal"
|
||||
: "medium";
|
||||
const defaultEffort: "minimal" | "low" | "medium" | "high" = isCodexMini
|
||||
? "medium"
|
||||
: isLightweight
|
||||
? "minimal"
|
||||
: "medium";
|
||||
|
||||
// Get user-requested effort
|
||||
let effort = userConfig.reasoningEffort || defaultEffort;
|
||||
|
||||
if (isCodexMini) {
|
||||
if (effort === "minimal" || effort === "low") {
|
||||
effort = "medium";
|
||||
}
|
||||
if (effort !== "high") {
|
||||
effort = "medium";
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize "minimal" to "low" for gpt-5-codex
|
||||
// Codex CLI does not provide a "minimal" preset for gpt-5-codex
|
||||
// (only low/medium/high - see model_presets.rs:20-40)
|
||||
@@ -293,10 +318,13 @@ export async function transformRequestBody(
|
||||
const modelConfig = getModelConfig(lookupModel, userConfig);
|
||||
|
||||
// Debug: Log which config was resolved
|
||||
logDebug(`Model config lookup: "${lookupModel}" → normalized to "${normalizedModel}" for API`, {
|
||||
hasModelSpecificConfig: !!userConfig.models?.[lookupModel],
|
||||
resolvedConfig: modelConfig,
|
||||
});
|
||||
logDebug(
|
||||
`Model config lookup: "${lookupModel}" → normalized to "${normalizedModel}" for API`,
|
||||
{
|
||||
hasModelSpecificConfig: !!userConfig.models?.[lookupModel],
|
||||
resolvedConfig: modelConfig,
|
||||
},
|
||||
);
|
||||
|
||||
// Normalize model name for API call
|
||||
body.model = normalizedModel;
|
||||
@@ -307,23 +335,33 @@ export async function transformRequestBody(
|
||||
body.stream = true;
|
||||
body.instructions = codexInstructions;
|
||||
|
||||
// Prompt caching relies on the host providing a stable prompt_cache_key
|
||||
// (OpenCode passes its session identifier). We no longer synthesize one here.
|
||||
// Prompt caching relies on the host providing a stable prompt_cache_key
|
||||
// (OpenCode passes its session identifier). We no longer synthesize one here.
|
||||
|
||||
// Filter and transform input
|
||||
if (body.input && Array.isArray(body.input)) {
|
||||
// Debug: Log original input message IDs before filtering
|
||||
const originalIds = body.input.filter(item => item.id).map(item => item.id);
|
||||
const originalIds = body.input
|
||||
.filter((item) => item.id)
|
||||
.map((item) => item.id);
|
||||
if (originalIds.length > 0) {
|
||||
logDebug(`Filtering ${originalIds.length} message IDs from input:`, originalIds);
|
||||
logDebug(
|
||||
`Filtering ${originalIds.length} message IDs from input:`,
|
||||
originalIds,
|
||||
);
|
||||
}
|
||||
|
||||
body.input = filterInput(body.input);
|
||||
|
||||
// Debug: Verify all IDs were removed
|
||||
const remainingIds = (body.input || []).filter(item => item.id).map(item => item.id);
|
||||
const remainingIds = (body.input || [])
|
||||
.filter((item) => item.id)
|
||||
.map((item) => item.id);
|
||||
if (remainingIds.length > 0) {
|
||||
logWarn(`WARNING: ${remainingIds.length} IDs still present after filtering:`, remainingIds);
|
||||
logWarn(
|
||||
`WARNING: ${remainingIds.length} IDs still present after filtering:`,
|
||||
remainingIds,
|
||||
);
|
||||
} else if (originalIds.length > 0) {
|
||||
logDebug(`Successfully removed all ${originalIds.length} message IDs`);
|
||||
}
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "opencode-openai-codex-auth",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "opencode-openai-codex-auth",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@openauthjs/openauth": "^0.4.3",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "opencode-openai-codex-auth",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"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",
|
||||
|
||||
+33
-9
@@ -52,10 +52,10 @@ describe('Configuration Parsing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getReasoningConfig', () => {
|
||||
it('should use user settings from merged config for gpt-5-codex', () => {
|
||||
const codexConfig = getModelConfig('gpt-5-codex', userConfig);
|
||||
const reasoningConfig = getReasoningConfig('gpt-5-codex', codexConfig);
|
||||
describe('getReasoningConfig', () => {
|
||||
it('should use user settings from merged config for gpt-5-codex', () => {
|
||||
const codexConfig = getModelConfig('gpt-5-codex', userConfig);
|
||||
const reasoningConfig = getReasoningConfig('gpt-5-codex', codexConfig);
|
||||
|
||||
expect(reasoningConfig.effort).toBe('medium');
|
||||
expect(reasoningConfig.summary).toBe('concise');
|
||||
@@ -99,13 +99,37 @@ describe('Configuration Parsing', () => {
|
||||
expect(highReasoning.summary).toBe('auto');
|
||||
});
|
||||
|
||||
it('should respect custom summary setting', () => {
|
||||
const detailedConfig = { reasoningSummary: 'detailed' as const };
|
||||
const detailedReasoning = getReasoningConfig('gpt-5-codex', detailedConfig);
|
||||
it('should respect custom summary setting', () => {
|
||||
const detailedConfig = { reasoningSummary: 'detailed' as const };
|
||||
const detailedReasoning = getReasoningConfig('gpt-5-codex', detailedConfig);
|
||||
|
||||
expect(detailedReasoning.summary).toBe('detailed');
|
||||
expect(detailedReasoning.summary).toBe('detailed');
|
||||
});
|
||||
|
||||
it('should default codex-mini to medium effort', () => {
|
||||
const codexMiniReasoning = getReasoningConfig('gpt-5-codex-mini', {});
|
||||
expect(codexMiniReasoning.effort).toBe('medium');
|
||||
});
|
||||
|
||||
it('should clamp codex-mini minimal/low to medium', () => {
|
||||
const minimal = getReasoningConfig('gpt-5-codex-mini', {
|
||||
reasoningEffort: 'minimal',
|
||||
});
|
||||
const low = getReasoningConfig('gpt-5-codex-mini-high', {
|
||||
reasoningEffort: 'low',
|
||||
});
|
||||
|
||||
expect(minimal.effort).toBe('medium');
|
||||
expect(low.effort).toBe('medium');
|
||||
});
|
||||
|
||||
it('should keep codex-mini high effort when requested', () => {
|
||||
const high = getReasoningConfig('codex-mini-latest', {
|
||||
reasoningEffort: 'high',
|
||||
});
|
||||
expect(high.effort).toBe('high');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Model-specific behavior', () => {
|
||||
it('should detect lightweight models correctly', () => {
|
||||
|
||||
@@ -63,6 +63,18 @@ describe('Request Transformer Module', () => {
|
||||
expect(normalizeModel('gpt-5-codex-low')).toBe('gpt-5-codex');
|
||||
expect(normalizeModel('my-gpt-5-codex-model')).toBe('gpt-5-codex');
|
||||
});
|
||||
|
||||
it('should normalize codex mini presets to codex-mini-latest', async () => {
|
||||
expect(normalizeModel('gpt-5-codex-mini')).toBe('codex-mini-latest');
|
||||
expect(normalizeModel('gpt-5-codex-mini-medium')).toBe('codex-mini-latest');
|
||||
expect(normalizeModel('gpt-5-codex-mini-high')).toBe('codex-mini-latest');
|
||||
expect(normalizeModel('openai/gpt-5-codex-mini-high')).toBe('codex-mini-latest');
|
||||
});
|
||||
|
||||
it('should normalize raw codex-mini-latest slug to codex-mini-latest', async () => {
|
||||
expect(normalizeModel('codex-mini-latest')).toBe('codex-mini-latest');
|
||||
expect(normalizeModel('openai/codex-mini-latest')).toBe('codex-mini-latest');
|
||||
});
|
||||
});
|
||||
|
||||
// NEW: Edge case tests
|
||||
@@ -70,6 +82,7 @@ describe('Request Transformer Module', () => {
|
||||
it('should handle uppercase model names', async () => {
|
||||
expect(normalizeModel('GPT-5-CODEX')).toBe('gpt-5-codex');
|
||||
expect(normalizeModel('GPT-5-HIGH')).toBe('gpt-5');
|
||||
expect(normalizeModel('CODEx-MINI-LATEST')).toBe('codex-mini-latest');
|
||||
});
|
||||
|
||||
it('should handle mixed case', async () => {
|
||||
|
||||
Reference in New Issue
Block a user