mirror of
https://github.com/cline/cline.git
synced 2026-10-24 02:02:27 +08:00
fix(llms): send max_completion_tokens for gpt-6 ids on OpenAI-compatible endpoints (#15137)
The reasoning-era model-id matcher only covered o1/o3/o4 and gpt-5, so gpt-6 requests (gpt-6-sol, gpt-6.1-sol, gpt-6-1-sol) kept max_tokens and OpenAI/Azure rejected them. Widen the gpt pattern to gpt-5 through gpt-9, keeping the version digit single so Azure's gpt-35-turbo stays out.
This commit is contained in:
@@ -247,7 +247,8 @@ export function isQwenModel(options: {
|
||||
}
|
||||
|
||||
// OpenAI reasoning-era chat models: the o-series (o1/o3/o4, including -mini,
|
||||
// -pro, and dated variants) and the gpt-5 family (including gpt-5-chat).
|
||||
// -pro, and dated variants) and gpt-5 and every later single-digit family
|
||||
// (gpt-5-chat, gpt-5.6-sol, gpt-6-sol, gpt-6.1-sol, ...).
|
||||
// These models diverge from classic chat-completions parameter rules — most
|
||||
// importantly they reject `max_tokens` and require `max_completion_tokens`.
|
||||
// Detection is an id-pattern fallback (mirroring the legacy extension's
|
||||
@@ -257,11 +258,13 @@ export function isQwenModel(options: {
|
||||
// match, while namespaced ids like "openai/o3-mini" do.
|
||||
//
|
||||
// Maintenance:
|
||||
// - If OpenAI ships a new family with the same parameter rules (e.g. gpt-6),
|
||||
// add a pattern here and cases to the positive/negative lists in
|
||||
// `vendors/openai-compatible.test.ts`. The failure mode until then is the
|
||||
// loud, self-describing OpenAI 400 ("'max_tokens' is not supported with
|
||||
// this model. Use 'max_completion_tokens' instead."), not silent breakage.
|
||||
// - New gpt families up to gpt-9 match without changes. The version digit
|
||||
// stays single on purpose: two digits would match Azure's `gpt-35-turbo`.
|
||||
// For a new family outside this pattern, add one here and cases to the
|
||||
// positive/negative lists in `vendors/openai-compatible.test.ts`. The
|
||||
// failure mode until then is the loud, self-describing OpenAI 400
|
||||
// ("'max_tokens' is not supported with this model. Use
|
||||
// 'max_completion_tokens' instead."), not silent breakage.
|
||||
// - Keep the boundary anchoring on BOTH sides of each pattern; loosening it
|
||||
// risks renaming the parameter for unrelated third-party models.
|
||||
// - Only `withMaxCompletionTokensForReasoningModels` in
|
||||
@@ -269,8 +272,8 @@ export function isQwenModel(options: {
|
||||
// `@ai-sdk/openai-compatible` maps `max_completion_tokens` itself (as
|
||||
// `@ai-sdk/openai` already does), delete that transform and this helper.
|
||||
const OPENAI_O_SERIES_MODEL_ID_PATTERN = /(^|[^a-z0-9])o[134](?=$|[^a-z0-9])/;
|
||||
const OPENAI_GPT5_FAMILY_MODEL_ID_PATTERN =
|
||||
/(^|[^a-z0-9])gpt-?5(?=$|[^a-z0-9])/;
|
||||
const OPENAI_GPT5_AND_LATER_MODEL_ID_PATTERN =
|
||||
/(^|[^a-z0-9])gpt-?[5-9](?=$|[^a-z0-9])/;
|
||||
|
||||
export function isOpenAIReasoningEraModelId(
|
||||
modelId: string | undefined,
|
||||
@@ -282,7 +285,7 @@ export function isOpenAIReasoningEraModelId(
|
||||
|
||||
return (
|
||||
OPENAI_O_SERIES_MODEL_ID_PATTERN.test(normalized) ||
|
||||
OPENAI_GPT5_FAMILY_MODEL_ID_PATTERN.test(normalized)
|
||||
OPENAI_GPT5_AND_LATER_MODEL_ID_PATTERN.test(normalized)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from "./openai-compatible";
|
||||
|
||||
describe("isOpenAIReasoningEraModelId", () => {
|
||||
it("matches o-series and gpt-5-family model ids", () => {
|
||||
it("matches o-series and gpt-5-and-later model ids", () => {
|
||||
for (const modelId of [
|
||||
"o1",
|
||||
"o1-mini",
|
||||
@@ -32,6 +32,14 @@ describe("isOpenAIReasoningEraModelId", () => {
|
||||
"openai/gpt-5.2",
|
||||
"GPT-5-Mini",
|
||||
"azure-gpt-5-mini",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-6",
|
||||
"gpt-6-sol",
|
||||
"gpt-6.1-sol",
|
||||
"gpt-6-1-sol",
|
||||
"openai/gpt-6-luna",
|
||||
"azure/gpt-6-astra",
|
||||
"gpt-7",
|
||||
]) {
|
||||
expect(isOpenAIReasoningEraModelId(modelId), modelId).toBe(true);
|
||||
}
|
||||
@@ -57,6 +65,10 @@ describe("isOpenAIReasoningEraModelId", () => {
|
||||
"chatgpt-5",
|
||||
"somegpt-5-custom",
|
||||
"xgpt5",
|
||||
"gpt-35-turbo-16k",
|
||||
"gpt-55",
|
||||
"gpt-6o",
|
||||
"somegpt-6-custom",
|
||||
]) {
|
||||
expect(isOpenAIReasoningEraModelId(modelId), modelId).toBe(false);
|
||||
}
|
||||
@@ -132,6 +144,17 @@ describe("createOpenAICompatibleProviderModule wire format", () => {
|
||||
expect(requestBody()).not.toHaveProperty("max_tokens");
|
||||
});
|
||||
|
||||
it("sends max_completion_tokens instead of max_tokens for gpt-6 streaming requests", async () => {
|
||||
const { requestBody } = await stream({ modelId: "gpt-6.1-sol" });
|
||||
|
||||
expect(requestBody()).toMatchObject({
|
||||
model: "gpt-6.1-sol",
|
||||
max_completion_tokens: 8_192,
|
||||
stream: true,
|
||||
});
|
||||
expect(requestBody()).not.toHaveProperty("max_tokens");
|
||||
});
|
||||
|
||||
it("sends max_completion_tokens instead of max_tokens for o-series streaming requests", async () => {
|
||||
const { requestBody } = await stream({ modelId: "o3-mini" });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user