feat: add priority processing support for GPT-5 models

- Add enablePriorityProcessing field to provider settings schema
- Update OpenAI native provider to use priority endpoint when enabled
- Add UI component with warning about increased costs
- Add localization strings for priority processing
- Add comprehensive tests for priority processing functionality

This feature allows users to enable priority processing (fast mode) for GPT-5 and GPT-5-mini models, which reduces response time but increases costs.
This commit is contained in:
Roo Code 2025-08-27 11:39:24 +00:00
parent c479678d8e
commit bbb31737f3
6 changed files with 326 additions and 1 deletions

View file

@ -111,6 +111,9 @@ const baseProviderSettingsSchema = z.object({
// Model verbosity.
verbosity: verbosityLevelsSchema.optional(),
// Priority processing for GPT-5 models.
enablePriorityProcessing: z.boolean().optional(),
})
// Several of the providers share common model config properties.

View file

@ -0,0 +1,226 @@
import { describe, it, expect, vi, beforeEach } from "vitest"
import OpenAI from "openai"
import { OpenAiNativeHandler } from "../openai-native"
import type { ApiHandlerOptions } from "../../../shared/api"
import type { Anthropic } from "@anthropic-ai/sdk"
vi.mock("openai")
describe("OpenAI Native Priority Processing", () => {
let handler: OpenAiNativeHandler
let mockCreate: ReturnType<typeof vi.fn>
let mockOptions: ApiHandlerOptions
beforeEach(() => {
mockCreate = vi.fn()
;(OpenAI as any).mockImplementation(() => ({
chat: {
completions: {
create: mockCreate,
},
},
responses: {
create: vi.fn(),
},
}))
mockOptions = {
openAiNativeApiKey: "test-api-key",
apiModelId: "gpt-5-2025-08-07",
}
})
describe("Priority Processing for GPT-5 models", () => {
it("should use priority endpoint when priority processing is enabled", () => {
const handlerWithPriority = new OpenAiNativeHandler({
...mockOptions,
enablePriorityProcessing: true,
})
// Check that the OpenAI client was initialized with the priority endpoint
expect(OpenAI).toHaveBeenCalledWith({
baseURL: "https://api.openai.com/v1/priority",
apiKey: "test-api-key",
})
})
it("should use standard endpoint when priority processing is disabled", () => {
const handlerWithoutPriority = new OpenAiNativeHandler({
...mockOptions,
enablePriorityProcessing: false,
})
// Check that the OpenAI client was initialized with the standard endpoint
expect(OpenAI).toHaveBeenCalledWith({
baseURL: undefined,
apiKey: "test-api-key",
})
})
it("should respect custom base URL even with priority processing enabled", () => {
const customBaseUrl = "https://custom.api.com/v1"
const handlerWithCustomUrl = new OpenAiNativeHandler({
...mockOptions,
openAiNativeBaseUrl: customBaseUrl,
enablePriorityProcessing: true,
})
// Check that the custom URL is preserved
expect(OpenAI).toHaveBeenCalledWith({
baseURL: customBaseUrl,
apiKey: "test-api-key",
})
})
it("should include priority in GPT-5 request body when enabled", async () => {
const handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-2025-08-07",
enablePriorityProcessing: true,
})
// Mock the responses.create method
const mockResponsesCreate = vi.fn().mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
yield { type: "response.done", response: { id: "test-id" } }
},
})
;(handler as any).client.responses = { create: mockResponsesCreate }
const systemPrompt = "You are a helpful assistant"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const stream = handler.createMessage(systemPrompt, messages)
for await (const chunk of stream) {
// Process stream
}
// Check that the request included the priority flag
expect(mockResponsesCreate).toHaveBeenCalledWith(
expect.objectContaining({
priority: true,
}),
)
})
it("should not include priority in GPT-5 request body when disabled", async () => {
const handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-2025-08-07",
enablePriorityProcessing: false,
})
// Mock the responses.create method
const mockResponsesCreate = vi.fn().mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
yield { type: "response.done", response: { id: "test-id" } }
},
})
;(handler as any).client.responses = { create: mockResponsesCreate }
const systemPrompt = "You are a helpful assistant"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const stream = handler.createMessage(systemPrompt, messages)
for await (const chunk of stream) {
// Process stream
}
// Check that the request did not include the priority flag
expect(mockResponsesCreate).toHaveBeenCalledWith(
expect.not.objectContaining({
priority: true,
}),
)
})
it("should work with GPT-5-mini models", async () => {
const handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-mini-2025-08-07",
enablePriorityProcessing: true,
})
// Mock the responses.create method
const mockResponsesCreate = vi.fn().mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
yield { type: "response.done", response: { id: "test-id" } }
},
})
;(handler as any).client.responses = { create: mockResponsesCreate }
const systemPrompt = "You are a helpful assistant"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const stream = handler.createMessage(systemPrompt, messages)
for await (const chunk of stream) {
// Process stream
}
// Check that the request included the priority flag for GPT-5-mini
expect(mockResponsesCreate).toHaveBeenCalledWith(
expect.objectContaining({
priority: true,
}),
)
})
})
describe("Priority Processing for non-GPT-5 models", () => {
it("should not affect GPT-4 models", async () => {
const handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-4o",
enablePriorityProcessing: true,
})
// Mock streaming response
mockCreate.mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
yield {
choices: [{ delta: { content: "Hello" } }],
}
},
})
const systemPrompt = "You are a helpful assistant"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const stream = handler.createMessage(systemPrompt, messages)
for await (const chunk of stream) {
// Process stream
}
// For GPT-4, it should still use the regular chat completions API
expect(mockCreate).toHaveBeenCalled()
})
it("should not affect o1 models", async () => {
const handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "o1",
enablePriorityProcessing: true,
})
// Mock streaming response
mockCreate.mockResolvedValue({
[Symbol.asyncIterator]: async function* () {
yield {
choices: [{ delta: { content: "Hello" } }],
}
},
})
const systemPrompt = "You are a helpful assistant"
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const stream = handler.createMessage(systemPrompt, messages)
for await (const chunk of stream) {
// Process stream
}
// For o1 models, it should use the regular chat completions API
expect(mockCreate).toHaveBeenCalled()
})
})
})

View file

@ -57,7 +57,14 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
this.options.enableGpt5ReasoningSummary = true
}
const apiKey = this.options.openAiNativeApiKey ?? "not-provided"
this.client = new OpenAI({ baseURL: this.options.openAiNativeBaseUrl, apiKey })
const baseURL = this.options.openAiNativeBaseUrl
// If priority processing is enabled, modify the base URL to use the priority endpoint
// This is a workaround since the OpenAI SDK doesn't directly support the priority parameter
const finalBaseURL =
this.options.enablePriorityProcessing && !baseURL ? "https://api.openai.com/v1/priority" : baseURL
this.client = new OpenAI({ baseURL: finalBaseURL, apiKey })
}
private normalizeGpt5Usage(usage: any, model: OpenAiNativeModel): ApiStreamUsageChunk | undefined {
@ -276,6 +283,7 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
temperature?: number
max_output_tokens?: number
previous_response_id?: string
priority?: boolean
}
const requestBody: Gpt5RequestBody = {
@ -294,6 +302,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
// Use the per-request reserved output computed by Roo (params.maxTokens from getModelParams).
...(model.maxTokens ? { max_output_tokens: model.maxTokens } : {}),
...(requestPreviousResponseId && { previous_response_id: requestPreviousResponseId }),
// Add priority processing if enabled for GPT-5 models
...(this.options.enablePriorityProcessing && this.isGpt5Model(model.id) ? { priority: true } : {}),
}
try {

View file

@ -101,6 +101,7 @@ import { ModelInfoView } from "./ModelInfoView"
import { ApiErrorMessage } from "./ApiErrorMessage"
import { ThinkingBudget } from "./ThinkingBudget"
import { Verbosity } from "./Verbosity"
import { PriorityProcessing } from "./PriorityProcessing"
import { DiffSettingsControl } from "./DiffSettingsControl"
import { TodoListSettingsControl } from "./TodoListSettingsControl"
import { TemperatureControl } from "./TemperatureControl"
@ -727,6 +728,15 @@ const ApiOptions = ({
/>
)}
{/* Priority Processing for GPT-5 models */}
{selectedProvider === "openai-native" && (
<PriorityProcessing
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
modelInfo={selectedModelInfo}
/>
)}
{!fromWelcomeView && (
<Collapsible open={isAdvancedSettingsOpen} onOpenChange={setIsAdvancedSettingsOpen}>
<CollapsibleTrigger className="flex items-center gap-1 w-full cursor-pointer hover:opacity-80 mb-2">

View file

@ -0,0 +1,70 @@
import React from "react"
import { type ProviderSettings, type ModelInfo } from "@roo-code/types"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { Checkbox } from "@src/components/ui"
import { ExclamationTriangleIcon } from "@radix-ui/react-icons"
interface PriorityProcessingProps {
apiConfiguration: ProviderSettings
setApiConfigurationField: <K extends keyof ProviderSettings>(
field: K,
value: ProviderSettings[K],
isUserAction?: boolean,
) => void
modelInfo: ModelInfo | undefined
}
export const PriorityProcessing: React.FC<PriorityProcessingProps> = ({
apiConfiguration,
setApiConfigurationField,
modelInfo,
}) => {
const { t } = useAppTranslation()
// Only show for GPT-5 and GPT-5-mini models
const isGpt5Model =
modelInfo &&
(apiConfiguration.apiModelId?.includes("gpt-5") || apiConfiguration.apiModelId?.includes("gpt-5-mini"))
if (!isGpt5Model) {
return null
}
return (
<div className="flex flex-col gap-2" data-testid="priority-processing">
<div className="flex items-center gap-2">
<Checkbox
id="priority-processing"
checked={apiConfiguration.enablePriorityProcessing || false}
onCheckedChange={(checked) =>
setApiConfigurationField("enablePriorityProcessing", checked === true)
}
/>
<label
htmlFor="priority-processing"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{t("settings:providers.priorityProcessing.label")}
</label>
</div>
{apiConfiguration.enablePriorityProcessing && (
<div className="ml-6 p-3 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-md">
<div className="flex items-start gap-2">
<ExclamationTriangleIcon className="w-4 h-4 text-yellow-600 dark:text-yellow-400 mt-0.5 flex-shrink-0" />
<div className="text-sm text-yellow-800 dark:text-yellow-200">
<p className="font-semibold mb-1">{t("settings:providers.priorityProcessing.warning")}</p>
<p>{t("settings:providers.priorityProcessing.description")}</p>
<a
href="https://platform.openai.com/docs/pricing?latest-pricing=priority"
target="_blank"
rel="noopener noreferrer"
className="inline-block mt-2 text-yellow-700 dark:text-yellow-300 underline hover:no-underline">
{t("settings:providers.priorityProcessing.viewPricing")}
</a>
</div>
</div>
</div>
)}
</div>
)
}

View file

@ -457,6 +457,12 @@
"low": "Low",
"description": "Controls how detailed the model's responses are. Low verbosity produces concise answers, while high verbosity provides thorough explanations."
},
"priorityProcessing": {
"label": "Enable Priority Processing (Fast Mode)",
"warning": "Higher costs will apply!",
"description": "Priority processing significantly reduces response time for GPT-5 and GPT-5-mini models by using dedicated compute resources. This feature increases costs substantially compared to standard processing.",
"viewPricing": "View pricing details"
},
"setReasoningLevel": "Enable Reasoning Effort",
"claudeCode": {
"pathLabel": "Claude Code Path",