Compare commits

...
Author SHA1 Message Date
abeatrix ad42ca9348 feature flag 2025-11-21 14:04:29 -08:00
abeatrix 8255b165ec feat: add GPT-5-codex with responses API support 2025-11-21 12:27:10 -08:00
9 changed files with 91 additions and 52 deletions
+13
View File
@@ -98,6 +98,7 @@ message OpenRouterModelInfo {
repeated ModelTier tiers = 12;
optional string name = 13;
optional double temperature = 14;
optional ApiFormat api_format = 15;
}
// Shared response message for model information
@@ -372,6 +373,8 @@ message OcaModelInfo {
optional string banner = 16;
// Canonical model identifier as reported by OCA
string model_name = 17;
// The API format used by this model
optional ApiFormat api_format = 18;
}
// Aggregated OCA model catalog keyed by model identifier
@@ -426,6 +429,14 @@ enum ApiProvider {
NOUSRESEARCH = 39;
}
enum ApiFormat {
ANTHROPIC_CHAT = 0;
GEMINI_CHAT = 1;
OPENAI_CHAT = 2;
R1_CHAT = 3;
OPENAI_RESPONSES = 4;
}
// Model info for OpenAI-compatible models
message OpenAiCompatibleModelInfo {
optional int64 max_tokens = 1;
@@ -442,6 +453,7 @@ message OpenAiCompatibleModelInfo {
repeated ModelTier tiers = 12;
optional double temperature = 13;
optional bool is_r1_format_required = 14;
optional ApiFormat api_format = 15;
}
// Model info for LiteLLM models
@@ -459,6 +471,7 @@ message LiteLLMModelInfo {
optional string description = 11;
repeated ModelTier tiers = 12;
optional double temperature = 13;
optional ApiFormat api_format = 14;
}
// Main ApiConfiguration message
+10 -8
View File
@@ -2,9 +2,11 @@ import { ModelInfo, OpenAiNativeModelId, openAiNativeDefaultModelId, openAiNativ
import { calculateApiCostOpenAI } from "@utils/cost"
import OpenAI from "openai"
import type { ChatCompletionReasoningEffort, ChatCompletionTool } from "openai/resources/chat/completions"
import { featureFlagsService } from "@/services/feature-flags"
import { Logger } from "@/services/logging/Logger"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiFormat } from "@/shared/proto/cline/models"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
import { convertToOpenAiMessages } from "../transform/openai-format"
@@ -61,13 +63,8 @@ export class OpenAiNativeHandler implements ApiHandler {
}
@withRetry()
async *createMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
tools?: ChatCompletionTool[],
useResponseFormat = false,
): ApiStream {
if (useResponseFormat) {
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: ChatCompletionTool[]): ApiStream {
if (tools?.length && this.getModel()?.info?.apiFormat === ApiFormat.OPENAI_RESPONSES) {
yield* this.createResponseStream(systemPrompt, messages, tools)
} else {
yield* this.createCompletionStream(systemPrompt, messages, tools)
@@ -406,7 +403,12 @@ export class OpenAiNativeHandler implements ApiHandler {
const modelId = this.options.apiModelId
if (modelId && modelId in openAiNativeModels) {
const id = modelId as OpenAiNativeModelId
return { id, info: openAiNativeModels[id] }
const info: ModelInfo = { ...openAiNativeModels[id] }
// Ensure model is compatible with feature flags
if (info.apiFormat === ApiFormat.OPENAI_RESPONSES && !featureFlagsService.isResponseApiEnabled()) {
info.apiFormat = undefined
}
return { id, info }
}
return {
id: openAiNativeDefaultModelId,
@@ -92,8 +92,9 @@ export function convertToOpenAIResponsesInput(messages: ClineStorageMessage[]):
case "thinking":
// Include reasoning item if it has a call_id, even if thinking is empty
// This is required because the API expects reasoning items to be paired with
// their corresponding function_calls, and will error if a function_call
// references a reasoning item that wasn't sent
// their corresponding function_calls or text, and will error if a function_call
// references a reasoning item that wasn't sent, or if a reasoning item is not
// included with a tool call: Item 'fc_' of type 'function_call' was provided without its required 'reasoning' item: 'rs_'
if (part.call_id && part.call_id.length > 0) {
assistantItems.push({
id: part.call_id,
@@ -65,7 +65,7 @@ However, if while writing your response you realize you actually need to do more
{
name: "response",
required: true,
instruction: `The response to provide to the user. Do not try to use tools in this parameter, this is simply a chat response. (You MUST use the response parameter, do not simply place the response text directly within <plan_mode_respond> tags.)`,
instruction: `The response to provide to the user.)`,
usage: "Your response here",
},
{
@@ -26,8 +26,14 @@ export const config = createVariant(ModelFamily.NATIVE_GPT_5_1)
const providerInfo = context.providerInfo
const modelId = providerInfo.model.id
// Codex variants will use GPT-5 variant instead for less strict rules.
// Chat variants do not support native tool use.
if (modelId.includes("codex") && !modelId.includes("chat")) {
return false
}
// gpt-5-1-chat models do not support native tool use
return isGPT51Model(modelId) && !modelId.includes("chat") && isNextGenModelProvider(providerInfo)
return isGPT51Model(modelId) && isNextGenModelProvider(providerInfo)
})
.template(GPT_5_1_TEMPLATE_OVERRIDES.BASE)
.components(
@@ -8,7 +8,7 @@ import { GPT_5_TEMPLATE_OVERRIDES } from "./template"
// Type-safe variant configuration using the builder pattern
export const config = createVariant(ModelFamily.NATIVE_GPT_5)
.description("Prompt tailored to GPT-5 with native tool use support")
.description("Prompt tailored to GPT-5 with native tool use support with less strict rules than GPT-5.1 variant")
.version(1)
.tags("gpt", "gpt-5", "advanced", "production", "native_tools")
.labels({
@@ -25,10 +25,11 @@ export const config = createVariant(ModelFamily.NATIVE_GPT_5)
const providerInfo = context.providerInfo
const modelId = providerInfo.model.id
// gpt-5-chat models do not support native tool use
// gpt-5-chat models do not support native tool use.
return (
isGPT5ModelFamily(modelId) &&
!isGPT51Model(modelId) &&
// Exclude gpt-5.1 models except for codex variants
(modelId.includes("codex") || !isGPT51Model(modelId)) &&
!modelId.includes("chat") &&
isNextGenModelProvider(providerInfo)
)
+2
View File
@@ -316,6 +316,8 @@ class ReasoningHandler {
thinking: this.pendingReasoning.content,
signature: this.pendingReasoning.signature,
summary: this.pendingReasoning.summary,
// Include the reasoning ID so it can be used when converting back to Responses API format
call_id: this.pendingReasoning.id,
}
}
+1 -9
View File
@@ -2123,15 +2123,7 @@ export class Task {
// saves task history item which we use to keep track of conversation history deleted range
}
// Response API requires native tool calls to be enabled
const useResponseApi = this.useNativeToolCalls && featureFlagsService.isResponseApiEnabled()
const stream = this.api.createMessage(
systemPrompt,
contextManagementMetadata.truncatedConversationHistory,
tools,
useResponseApi,
)
const stream = this.api.createMessage(systemPrompt, contextManagementMetadata.truncatedConversationHistory, tools)
const iterator = stream[Symbol.asyncIterator]()
+50 -28
View File
@@ -1,4 +1,5 @@
import type { LanguageModelChatSelector } from "../core/api/providers/types"
import { ApiFormat } from "./proto/cline/models"
export type ApiProvider =
| "anthropic"
@@ -257,6 +258,7 @@ export interface ModelInfo {
cacheReadsPrice?: number
}[]
temperature?: number
apiFormat?: ApiFormat
}
export interface OpenAiCompatibleModelInfo extends ModelInfo {
@@ -1400,8 +1402,45 @@ export const geminiModels = {
// OpenAI Native
// https://openai.com/api/pricing/
export type OpenAiNativeModelId = keyof typeof openAiNativeModels
export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-5-2025-08-07"
export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-5.1-2025-11-13"
export const openAiNativeModels = {
"gpt-5.1-2025-11-13": {
maxTokens: 8_192,
contextWindow: 272000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.125,
},
"gpt-5.1": {
maxTokens: 8_192, // 128000 breaks context window truncation
contextWindow: 272000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.125,
},
"gpt-5.1-codex": {
maxTokens: 8_192, // 128000 breaks context window truncation
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.125,
apiFormat: ApiFormat.OPENAI_RESPONSES,
},
"gpt-5.1-chat-latest": {
maxTokens: 8_192,
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10,
cacheReadsPrice: 0.125,
},
"gpt-5-2025-08-07": {
maxTokens: 8_192, // 128000 breaks context window truncation
contextWindow: 272000,
@@ -1411,6 +1450,16 @@ export const openAiNativeModels = {
outputPrice: 10.0,
cacheReadsPrice: 0.125,
},
"gpt-5-codex": {
maxTokens: 8_192, // 128000 breaks context window truncation
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.125,
apiFormat: ApiFormat.OPENAI_RESPONSES,
},
"gpt-5-mini-2025-08-07": {
maxTokens: 8_192,
contextWindow: 272000,
@@ -1438,33 +1487,6 @@ export const openAiNativeModels = {
outputPrice: 10,
cacheReadsPrice: 0.125,
},
"gpt-5.1-2025-11-13": {
maxTokens: 8_192,
contextWindow: 272000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.125,
},
"gpt-5.1": {
maxTokens: 8_192,
contextWindow: 272000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.125,
},
"gpt-5.1-chat-latest": {
maxTokens: 8_192,
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 1.25,
outputPrice: 10,
cacheReadsPrice: 0.125,
},
o3: {
maxTokens: 100_000,
contextWindow: 200_000,