-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat: add support for OpenAI gpt-5-chat-latest model #7058
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,147 @@ | ||||||||||||||||||||
import { describe, it, expect, vi, beforeEach } from "vitest" | ||||||||||||||||||||
import { OpenAiNativeHandler } from "../openai-native" | ||||||||||||||||||||
import { ApiHandlerOptions } from "../../../shared/api" | ||||||||||||||||||||
import { Anthropic } from "@anthropic-ai/sdk" | ||||||||||||||||||||
|
||||||||||||||||||||
// Mock OpenAI | ||||||||||||||||||||
vi.mock("openai", () => { | ||||||||||||||||||||
return { | ||||||||||||||||||||
default: class MockOpenAI { | ||||||||||||||||||||
responses = { | ||||||||||||||||||||
create: vi.fn(), | ||||||||||||||||||||
} | ||||||||||||||||||||
chat = { | ||||||||||||||||||||
completions: { | ||||||||||||||||||||
create: vi.fn(), | ||||||||||||||||||||
}, | ||||||||||||||||||||
} | ||||||||||||||||||||
}, | ||||||||||||||||||||
} | ||||||||||||||||||||
}) | ||||||||||||||||||||
|
||||||||||||||||||||
describe("OpenAiNativeHandler - GPT-5 Chat Latest", () => { | ||||||||||||||||||||
let handler: OpenAiNativeHandler | ||||||||||||||||||||
let mockOptions: ApiHandlerOptions | ||||||||||||||||||||
|
||||||||||||||||||||
beforeEach(() => { | ||||||||||||||||||||
vi.clearAllMocks() | ||||||||||||||||||||
mockOptions = { | ||||||||||||||||||||
apiModelId: "gpt-5-chat-latest", | ||||||||||||||||||||
openAiNativeApiKey: "test-api-key", | ||||||||||||||||||||
openAiNativeBaseUrl: "https://api.openai.com", | ||||||||||||||||||||
} | ||||||||||||||||||||
handler = new OpenAiNativeHandler(mockOptions) | ||||||||||||||||||||
}) | ||||||||||||||||||||
|
||||||||||||||||||||
describe("Model Configuration", () => { | ||||||||||||||||||||
it("should correctly configure gpt-5-chat-latest model", () => { | ||||||||||||||||||||
const model = handler.getModel() | ||||||||||||||||||||
|
||||||||||||||||||||
expect(model.id).toBe("gpt-5-chat-latest") | ||||||||||||||||||||
expect(model.info.maxTokens).toBe(128000) | ||||||||||||||||||||
expect(model.info.contextWindow).toBe(400000) | ||||||||||||||||||||
expect(model.info.supportsImages).toBe(true) | ||||||||||||||||||||
expect(model.info.supportsPromptCache).toBe(true) | ||||||||||||||||||||
expect(model.info.supportsReasoningEffort).toBe(false) // Non-reasoning model | ||||||||||||||||||||
expect(model.info.description).toBe( | ||||||||||||||||||||
"GPT-5 Chat Latest: Optimized for conversational AI and non-reasoning tasks", | ||||||||||||||||||||
) | ||||||||||||||||||||
}) | ||||||||||||||||||||
|
||||||||||||||||||||
it("should not include reasoning effort for gpt-5-chat-latest", () => { | ||||||||||||||||||||
const model = handler.getModel() | ||||||||||||||||||||
|
||||||||||||||||||||
// Should not have reasoning parameters since it's a non-reasoning model | ||||||||||||||||||||
expect(model.reasoning).toBeUndefined() | ||||||||||||||||||||
}) | ||||||||||||||||||||
}) | ||||||||||||||||||||
|
||||||||||||||||||||
describe("API Endpoint Selection", () => { | ||||||||||||||||||||
it("should use Responses API for gpt-5-chat-latest", async () => { | ||||||||||||||||||||
// Mock fetch for Responses API | ||||||||||||||||||||
const mockFetch = vi.fn().mockResolvedValue({ | ||||||||||||||||||||
ok: true, | ||||||||||||||||||||
body: new ReadableStream({ | ||||||||||||||||||||
start(controller) { | ||||||||||||||||||||
controller.enqueue( | ||||||||||||||||||||
new TextEncoder().encode('data: {"type":"response.text.delta","delta":"Hello"}\n\n'), | ||||||||||||||||||||
) | ||||||||||||||||||||
controller.enqueue( | ||||||||||||||||||||
new TextEncoder().encode( | ||||||||||||||||||||
'data: {"type":"response.done","response":{"id":"test-id","usage":{"input_tokens":10,"output_tokens":5}}}\n\n', | ||||||||||||||||||||
), | ||||||||||||||||||||
) | ||||||||||||||||||||
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) | ||||||||||||||||||||
controller.close() | ||||||||||||||||||||
}, | ||||||||||||||||||||
}), | ||||||||||||||||||||
}) | ||||||||||||||||||||
global.fetch = mockFetch | ||||||||||||||||||||
|
||||||||||||||||||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }] | ||||||||||||||||||||
|
||||||||||||||||||||
const stream = handler.createMessage("System prompt", messages) | ||||||||||||||||||||
const chunks = [] | ||||||||||||||||||||
for await (const chunk of stream) { | ||||||||||||||||||||
chunks.push(chunk) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Verify it called the Responses API endpoint | ||||||||||||||||||||
expect(mockFetch).toHaveBeenCalledWith( | ||||||||||||||||||||
"https://api.openai.com/v1/responses", | ||||||||||||||||||||
expect.objectContaining({ | ||||||||||||||||||||
method: "POST", | ||||||||||||||||||||
headers: expect.objectContaining({ | ||||||||||||||||||||
"Content-Type": "application/json", | ||||||||||||||||||||
Authorization: "Bearer test-api-key", | ||||||||||||||||||||
}), | ||||||||||||||||||||
body: expect.stringContaining('"model":"gpt-5-chat-latest"'), | ||||||||||||||||||||
}), | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
// Verify the request body doesn't include reasoning parameters | ||||||||||||||||||||
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body) | ||||||||||||||||||||
expect(requestBody.reasoning).toBeUndefined() | ||||||||||||||||||||
}) | ||||||||||||||||||||
}) | ||||||||||||||||||||
|
||||||||||||||||||||
describe("Conversation Features", () => { | ||||||||||||||||||||
it("should support conversation continuity with previous_response_id", async () => { | ||||||||||||||||||||
// Mock fetch for Responses API | ||||||||||||||||||||
const mockFetch = vi.fn().mockResolvedValue({ | ||||||||||||||||||||
ok: true, | ||||||||||||||||||||
body: new ReadableStream({ | ||||||||||||||||||||
start(controller) { | ||||||||||||||||||||
controller.enqueue( | ||||||||||||||||||||
new TextEncoder().encode('data: {"type":"response.text.delta","delta":"Response"}\n\n'), | ||||||||||||||||||||
) | ||||||||||||||||||||
controller.enqueue( | ||||||||||||||||||||
new TextEncoder().encode( | ||||||||||||||||||||
'data: {"type":"response.done","response":{"id":"response-123","usage":{"input_tokens":10,"output_tokens":5}}}\n\n', | ||||||||||||||||||||
), | ||||||||||||||||||||
) | ||||||||||||||||||||
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) | ||||||||||||||||||||
controller.close() | ||||||||||||||||||||
}, | ||||||||||||||||||||
}), | ||||||||||||||||||||
}) | ||||||||||||||||||||
global.fetch = mockFetch | ||||||||||||||||||||
|
||||||||||||||||||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Follow-up question" }] | ||||||||||||||||||||
|
||||||||||||||||||||
const stream = handler.createMessage("System prompt", messages, { | ||||||||||||||||||||
taskId: "test-task", | ||||||||||||||||||||
previousResponseId: "previous-response-456", | ||||||||||||||||||||
}) | ||||||||||||||||||||
|
||||||||||||||||||||
const chunks = [] | ||||||||||||||||||||
for await (const chunk of stream) { | ||||||||||||||||||||
chunks.push(chunk) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Verify the request includes previous_response_id | ||||||||||||||||||||
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body) | ||||||||||||||||||||
expect(requestBody.previous_response_id).toBe("previous-response-456") | ||||||||||||||||||||
}) | ||||||||||||||||||||
}) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a test to verify that
Suggested change
|
||||||||||||||||||||
}) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we add error handling tests similar to other GPT-5 models? For example:
This would ensure the gpt-5-chat-latest model handles errors consistently with other models. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1139,7 +1139,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio | |||||
|
||||||
private isResponsesApiModel(modelId: string): boolean { | ||||||
// Both GPT-5 and Codex Mini use the v1/responses endpoint | ||||||
return modelId.startsWith("gpt-5") || modelId === "codex-mini-latest" | ||||||
// gpt-5-chat-latest also uses the Responses API | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can also use the completions API |
||||||
return modelId.startsWith("gpt-5") || modelId === "codex-mini-latest" || modelId === "gpt-5-chat-latest" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this intentional? The condition
Suggested change
|
||||||
} | ||||||
|
||||||
private async *handleStreamResponse( | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider moving these tests into the existing "GPT-5 models" describe block in openai-native.spec.ts for better organization. Having all GPT-5 model tests in one place would make it easier to maintain and ensure consistency across similar models.