Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/types/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ export const reasoningEffortsSchema = z.enum(reasoningEfforts)

export type ReasoningEffort = z.infer<typeof reasoningEffortsSchema>

/**
* Verbosity
*/

export const verbosityLevels = ["low", "medium", "high"] as const

export const verbosityLevelsSchema = z.enum(verbosityLevels)

export type VerbosityLevel = z.infer<typeof verbosityLevelsSchema>

/**
* ModelParameter
*/
Expand Down
5 changes: 4 additions & 1 deletion packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod"

import { reasoningEffortsSchema, modelInfoSchema } from "./model.js"
import { reasoningEffortsSchema, verbosityLevelsSchema, modelInfoSchema } from "./model.js"
import { codebaseIndexProviderSchema } from "./codebase-index.js"

/**
Expand Down Expand Up @@ -79,6 +79,9 @@ const baseProviderSettingsSchema = z.object({
reasoningEffort: reasoningEffortsSchema.optional(),
modelMaxTokens: z.number().optional(),
modelMaxThinkingTokens: z.number().optional(),

// Model verbosity.
verbosity: verbosityLevelsSchema.optional(),
})

// Several of the providers share common model config properties.
Expand Down
35 changes: 34 additions & 1 deletion packages/types/src/providers/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,42 @@ import type { ModelInfo } from "../model.js"
// https://openai.com/api/pricing/
export type OpenAiNativeModelId = keyof typeof openAiNativeModels

export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-4.1"
export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-5-2025-08-07"

export const openAiNativeModels = {
"gpt-5-2025-08-07": {
maxTokens: 128000,
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoningEffort: true,
inputPrice: 1.25,
outputPrice: 10.0,
cacheReadsPrice: 0.13,
description: "GPT-5: The best model for coding and agentic tasks across domains",
},
"gpt-5-mini-2025-08-07": {
maxTokens: 128000,
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoningEffort: true,
inputPrice: 0.25,
outputPrice: 2.0,
cacheReadsPrice: 0.03,
description: "GPT-5 Mini: A faster, more cost-efficient version of GPT-5 for well-defined tasks",
},
"gpt-5-nano-2025-08-07": {
maxTokens: 128000,
contextWindow: 400000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoningEffort: true,
inputPrice: 0.05,
outputPrice: 0.4,
cacheReadsPrice: 0.01,
description: "GPT-5 Nano: Fastest, most cost-efficient version of GPT-5",
},
"gpt-4.1": {
maxTokens: 32_768,
contextWindow: 1_047_576,
Expand Down
156 changes: 155 additions & 1 deletion src/api/providers/__tests__/openai-native.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,162 @@ describe("OpenAiNativeHandler", () => {
openAiNativeApiKey: "test-api-key",
})
const modelInfo = handlerWithoutModel.getModel()
expect(modelInfo.id).toBe("gpt-4.1") // Default model
expect(modelInfo.id).toBe("gpt-5-2025-08-07") // Default model
expect(modelInfo.info).toBeDefined()
})
})

describe("GPT-5 models", () => {
it("should handle GPT-5 model with developer role", async () => {
handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-2025-08-07",
})

const stream = handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}

// Verify developer role is used for GPT-5 with default parameters
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "gpt-5-2025-08-07",
messages: [{ role: "developer", content: expect.stringContaining(systemPrompt) }],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "minimal", // Default for GPT-5
verbosity: "medium", // Default verbosity
}),
)
})

it("should handle GPT-5-mini model", async () => {
handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-mini-2025-08-07",
})

const stream = handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}

expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "gpt-5-mini-2025-08-07",
messages: [{ role: "developer", content: expect.stringContaining(systemPrompt) }],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "minimal", // Default for GPT-5
verbosity: "medium", // Default verbosity
}),
)
})

it("should handle GPT-5-nano model", async () => {
handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-nano-2025-08-07",
})

const stream = handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}

expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "gpt-5-nano-2025-08-07",
messages: [{ role: "developer", content: expect.stringContaining(systemPrompt) }],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "minimal", // Default for GPT-5
verbosity: "medium", // Default verbosity
}),
)
})

it("should support verbosity control for GPT-5", async () => {
handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-2025-08-07",
verbosity: "low", // Set verbosity through options
})

// Create a message to verify verbosity is passed
const stream = handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}

// Verify that verbosity is passed in the request
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "gpt-5-2025-08-07",
messages: expect.any(Array),
stream: true,
stream_options: { include_usage: true },
verbosity: "low",
}),
)
})

it("should support minimal reasoning effort for GPT-5", async () => {
handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-2025-08-07",
reasoningEffort: "low",
})

const stream = handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}

// With low reasoning effort, the model should pass it through
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "gpt-5-2025-08-07",
messages: expect.any(Array),
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "low",
verbosity: "medium", // Default verbosity
}),
)
})

it("should support both verbosity and reasoning effort together for GPT-5", async () => {
handler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-5-2025-08-07",
verbosity: "high", // Set verbosity through options
reasoningEffort: "low", // Set reasoning effort
})

const stream = handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}

// Verify both parameters are passed
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "gpt-5-2025-08-07",
messages: expect.any(Array),
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "low",
verbosity: "high",
}),
)
})
})
})
Loading
Loading