mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-10 03:28:03 +00:00
refactor: improve GPT-5 token limit implementation
- Extract GPT5_MAX_OUTPUT_TOKENS as a named constant (10,000) - Improve model detection with more specific regex pattern - Add comprehensive documentation explaining the context window overflow issue - Add tests for date-suffixed models and invalid model name patterns - Update all test assertions to use the named constant
This commit is contained in:
parent
93de9fa0a1
commit
9b3baaaa8e
2 changed files with 76 additions and 13 deletions
|
|
@ -1,5 +1,10 @@
|
||||||
import { describe, test, expect } from "vitest"
|
import { describe, test, expect } from "vitest"
|
||||||
import { getModelMaxOutputTokens, shouldUseReasoningBudget, shouldUseReasoningEffort } from "../api"
|
import {
|
||||||
|
getModelMaxOutputTokens,
|
||||||
|
shouldUseReasoningBudget,
|
||||||
|
shouldUseReasoningEffort,
|
||||||
|
GPT5_MAX_OUTPUT_TOKENS,
|
||||||
|
} from "../api"
|
||||||
import type { ModelInfo, ProviderSettings } from "@roo-code/types"
|
import type { ModelInfo, ProviderSettings } from "@roo-code/types"
|
||||||
import { CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS, ANTHROPIC_DEFAULT_MAX_TOKENS } from "@roo-code/types"
|
import { CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS, ANTHROPIC_DEFAULT_MAX_TOKENS } from "@roo-code/types"
|
||||||
|
|
||||||
|
|
@ -233,7 +238,7 @@ describe("getModelMaxOutputTokens", () => {
|
||||||
format: "openai",
|
format: "openai",
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBe(10_000)
|
expect(result).toBe(GPT5_MAX_OUTPUT_TOKENS)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should limit GPT-5-mini models to 10k max output tokens", () => {
|
test("should limit GPT-5-mini models to 10k max output tokens", () => {
|
||||||
|
|
@ -250,7 +255,7 @@ describe("getModelMaxOutputTokens", () => {
|
||||||
format: "openai",
|
format: "openai",
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBe(10_000)
|
expect(result).toBe(GPT5_MAX_OUTPUT_TOKENS)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should limit GPT-5-nano models to 10k max output tokens", () => {
|
test("should limit GPT-5-nano models to 10k max output tokens", () => {
|
||||||
|
|
@ -267,17 +272,17 @@ describe("getModelMaxOutputTokens", () => {
|
||||||
format: "openai",
|
format: "openai",
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBe(10_000)
|
expect(result).toBe(GPT5_MAX_OUTPUT_TOKENS)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should respect user override for GPT-5 models but cap at 10k", () => {
|
test("should respect user override for GPT-5 models but cap at GPT5_MAX_OUTPUT_TOKENS", () => {
|
||||||
const gpt5Model: ModelInfo = {
|
const gpt5Model: ModelInfo = {
|
||||||
contextWindow: 400_000,
|
contextWindow: 400_000,
|
||||||
maxTokens: 128_000,
|
maxTokens: 128_000,
|
||||||
supportsPromptCache: true,
|
supportsPromptCache: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// User tries to set 15k, should be capped at 10k
|
// User tries to set 15k, should be capped at GPT5_MAX_OUTPUT_TOKENS
|
||||||
const settings: ProviderSettings = {
|
const settings: ProviderSettings = {
|
||||||
modelMaxTokens: 15_000,
|
modelMaxTokens: 15_000,
|
||||||
}
|
}
|
||||||
|
|
@ -289,10 +294,10 @@ describe("getModelMaxOutputTokens", () => {
|
||||||
format: "openai",
|
format: "openai",
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toBe(10_000)
|
expect(result).toBe(GPT5_MAX_OUTPUT_TOKENS)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should allow user to set lower than 10k for GPT-5 models", () => {
|
test("should allow user to set lower than GPT5_MAX_OUTPUT_TOKENS for GPT-5 models", () => {
|
||||||
const gpt5Model: ModelInfo = {
|
const gpt5Model: ModelInfo = {
|
||||||
contextWindow: 400_000,
|
contextWindow: 400_000,
|
||||||
maxTokens: 128_000,
|
maxTokens: 128_000,
|
||||||
|
|
@ -331,6 +336,54 @@ describe("getModelMaxOutputTokens", () => {
|
||||||
// Should use model's maxTokens since it's within 20% of context window
|
// Should use model's maxTokens since it's within 20% of context window
|
||||||
expect(result).toBe(16_384)
|
expect(result).toBe(16_384)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should handle GPT-5 models with date suffixes", () => {
|
||||||
|
const gpt5Model: ModelInfo = {
|
||||||
|
contextWindow: 400_000,
|
||||||
|
maxTokens: 128_000,
|
||||||
|
supportsPromptCache: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test various date-suffixed GPT-5 models
|
||||||
|
const modelIds = ["gpt-5-2025-08-07", "gpt-5-mini-2025-08-07", "gpt-5-nano-2025-08-07"]
|
||||||
|
|
||||||
|
modelIds.forEach((modelId) => {
|
||||||
|
const result = getModelMaxOutputTokens({
|
||||||
|
modelId,
|
||||||
|
model: gpt5Model,
|
||||||
|
settings: {},
|
||||||
|
format: "openai",
|
||||||
|
})
|
||||||
|
expect(result).toBe(GPT5_MAX_OUTPUT_TOKENS)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should not match invalid GPT-5 model names", () => {
|
||||||
|
const model: ModelInfo = {
|
||||||
|
contextWindow: 128_000,
|
||||||
|
maxTokens: 16_384,
|
||||||
|
supportsPromptCache: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should NOT be treated as GPT-5 models
|
||||||
|
const invalidModelIds = [
|
||||||
|
"gpt-5-turbo", // Invalid variant
|
||||||
|
"gpt-50", // Different number
|
||||||
|
"gpt-5-", // Incomplete
|
||||||
|
"gpt-5-mini-turbo", // Invalid variant combination
|
||||||
|
]
|
||||||
|
|
||||||
|
invalidModelIds.forEach((modelId) => {
|
||||||
|
const result = getModelMaxOutputTokens({
|
||||||
|
modelId,
|
||||||
|
model,
|
||||||
|
settings: {},
|
||||||
|
format: "openai",
|
||||||
|
})
|
||||||
|
// Should use model's maxTokens since it's within 20% of context window
|
||||||
|
expect(result).toBe(16_384)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,15 @@ export const DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS = 16_384
|
||||||
export const DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS = 8_192
|
export const DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS = 8_192
|
||||||
export const GEMINI_25_PRO_MIN_THINKING_TOKENS = 128
|
export const GEMINI_25_PRO_MIN_THINKING_TOKENS = 128
|
||||||
|
|
||||||
|
// GPT-5 specific constants
|
||||||
|
/**
|
||||||
|
* Maximum output tokens for GPT-5 models to prevent context window overflow.
|
||||||
|
* When input approaches the 272k limit, the model's 128k max output can exceed
|
||||||
|
* the total 400k context window, causing API errors.
|
||||||
|
* @see https://github.com/cline/cline/issues/5474#issuecomment-3172109387
|
||||||
|
*/
|
||||||
|
export const GPT5_MAX_OUTPUT_TOKENS = 10_000
|
||||||
|
|
||||||
// Max Tokens
|
// Max Tokens
|
||||||
|
|
||||||
export const getModelMaxOutputTokens = ({
|
export const getModelMaxOutputTokens = ({
|
||||||
|
|
@ -88,14 +97,15 @@ export const getModelMaxOutputTokens = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for GPT-5 models to prevent context window overflow
|
// Special handling for GPT-5 models to prevent context window overflow
|
||||||
// Limit max output to 10k tokens as per https://github.com/cline/cline/issues/5474#issuecomment-3172109387
|
// GPT-5 models include: gpt-5, gpt-5-mini, gpt-5-nano, and dated variants
|
||||||
if (modelId.startsWith("gpt-5")) {
|
const isGpt5Model = /^gpt-5(-mini|-nano)?(-\d{4}-\d{2}-\d{2})?$/i.test(modelId)
|
||||||
// Allow user override via settings, but cap at 10k
|
if (isGpt5Model) {
|
||||||
|
// Allow user override via settings, but cap at GPT5_MAX_OUTPUT_TOKENS
|
||||||
const userMaxTokens = settings?.modelMaxTokens
|
const userMaxTokens = settings?.modelMaxTokens
|
||||||
if (userMaxTokens) {
|
if (userMaxTokens) {
|
||||||
return Math.min(userMaxTokens, 10000)
|
return Math.min(userMaxTokens, GPT5_MAX_OUTPUT_TOKENS)
|
||||||
}
|
}
|
||||||
return 10000
|
return GPT5_MAX_OUTPUT_TOKENS
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldUseReasoningBudget({ model, settings })) {
|
if (shouldUseReasoningBudget({ model, settings })) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue