瀏覽代碼

fix(github): emit PROMPT_TOO_LARGE error on context overflow (#14166)

Matt Silverlock 1 月之前
父節點
當前提交
d447b7694a
共有 2 個文件被更改,包括 68 次插入9 次删除
  1. 31 8
      packages/opencode/src/cli/cmd/github.ts
  2. 37 1
      packages/opencode/test/cli/github-action.test.ts

+ 31 - 8
packages/opencode/src/cli/cmd/github.ts

@@ -174,6 +174,18 @@ export function extractResponseText(parts: MessageV2.Part[]): string | null {
   throw new Error("Failed to parse response: no parts returned")
 }
 
+/**
+ * Formats a PROMPT_TOO_LARGE error message with details about files in the prompt.
+ * Content is base64 encoded, so we calculate original size by multiplying by 0.75.
+ */
+export function formatPromptTooLargeError(files: { filename: string; content: string }[]): string {
+  const fileDetails =
+    files.length > 0
+      ? `\n\nFiles in prompt:\n${files.map((f) => `  - ${f.filename} (${((f.content.length * 0.75) / 1024).toFixed(0)} KB)`).join("\n")}`
+      : ""
+  return `PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.${fileDetails}`
+}
+
 export const GithubCommand = cmd({
   command: "github",
   describe: "manage GitHub agent",
@@ -803,6 +815,7 @@ export const GithubRunCommand = cmd({
             replacement,
           })
         }
+
         return { userPrompt: prompt, promptFiles: imgData }
       }
 
@@ -910,10 +923,15 @@ export const GithubRunCommand = cmd({
 
         // result should always be assistant just satisfying type checker
         if (result.info.role === "assistant" && result.info.error) {
-          console.error("Agent error:", result.info.error)
-          throw new Error(
-            `${result.info.error.name}: ${"message" in result.info.error ? result.info.error.message : ""}`,
-          )
+          const err = result.info.error
+          console.error("Agent error:", err)
+
+          if (err.name === "ContextOverflowError") {
+            throw new Error(formatPromptTooLargeError(files))
+          }
+
+          const errorMsg = err.data?.message || ""
+          throw new Error(`${err.name}: ${errorMsg}`)
         }
 
         const text = extractResponseText(result.parts)
@@ -939,10 +957,15 @@ export const GithubRunCommand = cmd({
         })
 
         if (summary.info.role === "assistant" && summary.info.error) {
-          console.error("Summary agent error:", summary.info.error)
-          throw new Error(
-            `${summary.info.error.name}: ${"message" in summary.info.error ? summary.info.error.message : ""}`,
-          )
+          const err = summary.info.error
+          console.error("Summary agent error:", err)
+
+          if (err.name === "ContextOverflowError") {
+            throw new Error(formatPromptTooLargeError(files))
+          }
+
+          const errorMsg = err.data?.message || ""
+          throw new Error(`${err.name}: ${errorMsg}`)
         }
 
         const summaryText = extractResponseText(summary.parts)

+ 37 - 1
packages/opencode/test/cli/github-action.test.ts

@@ -1,5 +1,5 @@
 import { test, expect, describe } from "bun:test"
-import { extractResponseText } from "../../src/cli/cmd/github"
+import { extractResponseText, formatPromptTooLargeError } from "../../src/cli/cmd/github"
 import type { MessageV2 } from "../../src/session/message-v2"
 
 // Helper to create minimal valid parts
@@ -159,3 +159,39 @@ describe("extractResponseText", () => {
     expect(extractResponseText(parts)).toBe("Here's what I found")
   })
 })
+
+describe("formatPromptTooLargeError", () => {
+  test("formats error without files", () => {
+    const result = formatPromptTooLargeError([])
+    expect(result).toBe("PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.")
+  })
+
+  test("formats error with files (base64 content)", () => {
+    // Base64 is ~33% larger than original, so we multiply by 0.75 to get original size
+    // 400 KB base64 = 300 KB original, 200 KB base64 = 150 KB original
+    const files = [
+      { filename: "screenshot.png", content: "a".repeat(400 * 1024) },
+      { filename: "diagram.png", content: "b".repeat(200 * 1024) },
+    ]
+    const result = formatPromptTooLargeError(files)
+
+    expect(result).toStartWith("PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.")
+    expect(result).toInclude("Files in prompt:")
+    expect(result).toInclude("screenshot.png (300 KB)")
+    expect(result).toInclude("diagram.png (150 KB)")
+  })
+
+  test("lists all files when multiple present", () => {
+    // Base64 sizes: 4KB -> 3KB, 8KB -> 6KB, 12KB -> 9KB
+    const files = [
+      { filename: "img1.png", content: "x".repeat(4 * 1024) },
+      { filename: "img2.jpg", content: "y".repeat(8 * 1024) },
+      { filename: "img3.gif", content: "z".repeat(12 * 1024) },
+    ]
+    const result = formatPromptTooLargeError(files)
+
+    expect(result).toInclude("img1.png (3 KB)")
+    expect(result).toInclude("img2.jpg (6 KB)")
+    expect(result).toInclude("img3.gif (9 KB)")
+  })
+})