Add Codex Mini support

This commit is contained in:
Numman Ali
2025-11-11 20:49:43 +00:00
parent fd68ea1d00
commit 18d207ac9b
17 changed files with 255 additions and 57 deletions
+4 -3
View File
@@ -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)
+8
View File
@@ -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
View File
@@ -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)
+41 -4
View File
@@ -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
View File
@@ -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
+32
View File
@@ -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
View File
@@ -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
---
+4 -2
View File
@@ -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.
+3 -3
View File
@@ -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 | ✅ |
+18 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
---
+58 -20
View File
@@ -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`);
}
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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', () => {
+13
View File
@@ -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 () => {