소스 검색

fix: minimal being incorrectly assigned for some models

Aiden Cline 3 달 전
부모
커밋
c5e9538c63
2개의 변경된 파일74개의 추가작업 그리고 15개의 파일을 삭제
  1. 8 8
      packages/opencode/src/provider/transform.ts
  2. 66 7
      packages/opencode/test/provider/transform.test.ts

+ 8 - 8
packages/opencode/src/provider/transform.ts

@@ -509,23 +509,23 @@ const OPENAI_NONE_EFFORT_RELEASE_DATE = "2025-11-13"
 // OpenAI rolled out the `xhigh` reasoning_effort tier on this date. Same reasoning.
 const OPENAI_XHIGH_EFFORT_RELEASE_DATE = "2025-12-04"
 
-// Matches members of the gpt-5 family across the id formats we encounter:
-//   "gpt-5", "gpt-5-nano", "gpt-5.4", "openai/gpt-5.4-codex".
-// Anchored to start-of-string or "/" so it doesn't false-match "gpt-50" or "gpt-5o".
-const GPT5_FAMILY_RE = /(?:^|\/)gpt-5(?:[.-]|$)/
+// Matches the first generation GPT-5 ids that still accept `minimal`.
+const GPT5_INITIAL_RE = /(?:^|\/)gpt-5(?:-(?:mini|nano))?$/
 
 // Computes the reasoning_effort tiers an OpenAI (or OpenAI-compatible upstream
 // routed through it, e.g. cf-ai-gateway) model exposes. Returns null for models
 // with no tunable effort knob (gpt-5-pro). Effort order: weakest to strongest.
 function openaiReasoningEfforts(apiId: string, releaseDate: string): string[] | null {
   const id = apiId.toLowerCase()
-  if (id === "gpt-5-pro" || id === "openai/gpt-5-pro") return null
+  if (id.endsWith("chat-latest")) return ["medium"]
+  if (id.endsWith("-pro")) return id.includes("5.1") ? null : ["medium", "high", "xhigh"]
   if (id.includes("codex")) {
-    if (id.includes("5.2") || id.includes("5.3")) return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
-    return [...WIDELY_SUPPORTED_EFFORTS]
+    const efforts = ["minimal", ...WIDELY_SUPPORTED_EFFORTS]
+    if (!["5-codex", "5.1-codex", "5.1-codex-mini"].some((v) => id.endsWith(v))) efforts.push("xhigh")
+    return efforts
   }
   const efforts = [...WIDELY_SUPPORTED_EFFORTS]
-  if (GPT5_FAMILY_RE.test(id)) efforts.unshift("minimal")
+  if (GPT5_INITIAL_RE.test(id)) efforts.unshift("minimal")
   if (releaseDate >= OPENAI_NONE_EFFORT_RELEASE_DATE) efforts.unshift("none")
   if (releaseDate >= OPENAI_XHIGH_EFFORT_RELEASE_DATE) efforts.push("xhigh")
   return efforts

+ 66 - 7
packages/opencode/test/provider/transform.test.ts

@@ -310,6 +310,65 @@ describe("ProviderTransform.options - gpt-5 textVerbosity", () => {
   })
 })
 
+describe("ProviderTransform.variants - openai gpt-5 reasoning efforts", () => {
+  const createModel = (apiId: string, releaseDate = "2025-12-04") =>
+    ({
+      id: `openai/${apiId}`,
+      providerID: "openai",
+      api: {
+        id: apiId,
+        url: "https://api.openai.com",
+        npm: "@ai-sdk/openai",
+      },
+      name: apiId,
+      release_date: releaseDate,
+      capabilities: {
+        temperature: true,
+        reasoning: true,
+        attachment: true,
+        toolcall: true,
+        input: { text: true, audio: false, image: true, video: false, pdf: false },
+        output: { text: true, audio: false, image: false, video: false, pdf: false },
+        interleaved: false,
+      },
+      cost: { input: 0.03, output: 0.06, cache: { read: 0.001, write: 0.002 } },
+      limit: { context: 128000, output: 4096 },
+      status: "active",
+      options: {},
+      headers: {},
+    }) as any
+
+  test.each([
+    ["gpt-5", ["none", "minimal", "low", "medium", "high", "xhigh"]],
+    ["gpt-5-mini", ["none", "minimal", "low", "medium", "high", "xhigh"]],
+    ["gpt-5-nano", ["none", "minimal", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.1", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.2", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.4", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.4-fast", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.4-mini", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.4-mini-fast", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.4-nano", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.5", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.5-fast", ["none", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.1-chat-latest", ["medium"]],
+    ["gpt-5.2-chat-latest", ["medium"]],
+    ["gpt-5.3-chat-latest", ["medium"]],
+    ["gpt-5.2-pro", ["medium", "high", "xhigh"]],
+    ["gpt-5.4-pro", ["medium", "high", "xhigh"]],
+    ["gpt-5.5-pro", ["medium", "high", "xhigh"]],
+    ["gpt-5-codex", ["minimal", "low", "medium", "high"]],
+    ["gpt-5.1-codex", ["minimal", "low", "medium", "high"]],
+    ["gpt-5.1-codex-mini", ["minimal", "low", "medium", "high"]],
+    ["gpt-5.1-codex-max", ["minimal", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.2-codex", ["minimal", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.3-codex", ["minimal", "low", "medium", "high", "xhigh"]],
+    ["gpt-5.3-codex-spark", ["minimal", "low", "medium", "high", "xhigh"]],
+  ])("%s exposes the expected effort variants", (apiId, efforts) => {
+    expect(Object.keys(ProviderTransform.variants(createModel(apiId)))).toEqual(efforts)
+  })
+})
+
 describe("ProviderTransform.options - gateway", () => {
   const sessionID = "test-session-123"
 
@@ -2932,7 +2991,7 @@ describe("ProviderTransform.variants", () => {
   })
 
   describe("@ai-sdk/openai", () => {
-    test("gpt-5-pro returns empty object", () => {
+    test("gpt-5-pro returns pro efforts", () => {
       const model = createMockModel({
         id: "gpt-5-pro",
         providerID: "openai",
@@ -2943,7 +3002,7 @@ describe("ProviderTransform.variants", () => {
         },
       })
       const result = ProviderTransform.variants(model)
-      expect(result).toEqual({})
+      expect(Object.keys(result)).toEqual(["medium", "high", "xhigh"])
     })
 
     test("standard openai models return custom efforts with reasoningSummary", () => {
@@ -2993,10 +3052,10 @@ describe("ProviderTransform.variants", () => {
         release_date: "2025-12-05",
       })
       const result = ProviderTransform.variants(model)
-      expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"])
+      expect(Object.keys(result)).toEqual(["none", "low", "medium", "high", "xhigh"])
     })
 
-    test("dotted gpt-5.x ids include 'minimal' (regression: matcher used to miss gpt-5.4)", () => {
+    test("dotted gpt-5.x ids include 'xhigh' without minimal", () => {
       const model = createMockModel({
         id: "gpt-5.4",
         providerID: "openai",
@@ -3008,7 +3067,7 @@ describe("ProviderTransform.variants", () => {
         release_date: "2026-03-05",
       })
       const result = ProviderTransform.variants(model)
-      expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"])
+      expect(Object.keys(result)).toEqual(["none", "low", "medium", "high", "xhigh"])
     })
 
     test("gpt-50 (lookalike) does not get gpt-5 family treatment", () => {
@@ -3490,13 +3549,13 @@ describe("ProviderTransform.variants", () => {
       const result = ProviderTransform.variants(cfModel("openai/gpt-5.4", "2026-03-05"))
       expect(result.xhigh).toEqual({ reasoningEffort: "xhigh" })
       expect(result.high).toEqual({ reasoningEffort: "high" })
-      expect(Object.keys(result)).toContain("minimal")
+      expect(Object.keys(result)).not.toContain("minimal")
     })
 
     test("openai gpt-5.2-codex includes xhigh", () => {
       const result = ProviderTransform.variants(cfModel("openai/gpt-5.2-codex", "2025-12-11"))
       expect(result.xhigh).toEqual({ reasoningEffort: "xhigh" })
-      expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"])
+      expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high", "xhigh"])
     })
 
     test("openai gpt-4o (no reasoning) returns empty", () => {