Skip to content

Commit 06837f2

Browse files
committed
fix: create large response warning
1 parent a4da1ca commit 06837f2

File tree

1 file changed

+84
-8
lines changed

1 file changed

+84
-8
lines changed

packages/opencode/src/tool/webfetch.ts

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,32 @@ export const WebFetchTool = Tool.define("webfetch", {
7474
throw new Error(`Request failed with status code: ${response.status}`)
7575
}
7676

77-
// Check content length
77+
// Check content length and estimate token count
7878
const contentLength = response.headers.get("content-length")
79-
if (contentLength && parseInt(contentLength) > MAX_RESPONSE_SIZE) {
80-
throw new Error("Response too large (exceeds 5MB limit)")
79+
if (contentLength) {
80+
const bytes = parseInt(contentLength)
81+
if (bytes > MAX_RESPONSE_SIZE) {
82+
throw new Error("Response too large (exceeds 5MB limit)")
83+
}
84+
85+
// Estimate tokens from byte size (rough estimate: 1 token ≈ 4 bytes)
86+
const estimatedTokens = Math.round(bytes / 4)
87+
if (estimatedTokens > MAX_TOKENS) {
88+
return {
89+
output: `⚠️ Response size check: This URL will return approximately ${estimatedTokens.toLocaleString()} tokens (${(bytes / 1024).toFixed(0)} KB)
90+
91+
This exceeds the safe limit of ${MAX_TOKENS.toLocaleString()} tokens and will likely cause "prompt is too long" errors.
92+
93+
Recommended actions:
94+
• I can fetch it and save to a file in your project directory, then analyze it
95+
• You can ask me to fetch specific parts/fields only if this is an API
96+
• You can provide filters or query parameters to reduce the response size
97+
98+
Would you like me to proceed with fetching and saving to a file, or would you prefer a different approach?`,
99+
title: `${params.url} [Size Warning]`,
100+
metadata: {},
101+
}
102+
}
81103
}
82104

83105
const arrayBuffer = await response.arrayBuffer()
@@ -117,13 +139,15 @@ export const WebFetchTool = Tool.define("webfetch", {
117139
output = content
118140
}
119141

120-
// Truncate if exceeds token limit
142+
// Check if response exceeds token limit - if so, summarize intelligently
121143
const tokenCount = Token.estimate(output)
122144
if (tokenCount > MAX_TOKENS) {
123-
const charsPerToken = 4
124-
const maxChars = MAX_TOKENS * charsPerToken
125-
output = output.slice(0, maxChars)
126-
output += `\n\n[Content truncated: Response was ~${tokenCount.toLocaleString()} tokens, truncated to ${MAX_TOKENS.toLocaleString()} tokens to avoid exceeding budget]`
145+
const summary = createLargeResponseWarning(output, contentType, params.url, tokenCount)
146+
return {
147+
output: summary,
148+
title: `${title} [Summarized]`,
149+
metadata: {},
150+
}
127151
}
128152

129153
return {
@@ -177,3 +201,55 @@ function convertHTMLToMarkdown(html: string): string {
177201
turndownService.remove(["script", "style", "meta", "link"])
178202
return turndownService.turndown(html)
179203
}
204+
205+
function createLargeResponseWarning(content: string, contentType: string, url: string, tokenCount: number): string {
206+
let previewSection = ""
207+
208+
// Try to provide structure info for JSON
209+
if (contentType.includes("json") || contentType.includes("application/json")) {
210+
try {
211+
const parsed = JSON.parse(content)
212+
if (Array.isArray(parsed)) {
213+
previewSection = `Type: JSON Array
214+
Items: ${parsed.length}
215+
216+
To avoid exceeding token budget, showing structural summary instead of full content.
217+
218+
First item as example:
219+
${JSON.stringify(parsed[0], null, 2)}`
220+
} else if (typeof parsed === "object") {
221+
const keys = Object.keys(parsed)
222+
const sample = Object.fromEntries(keys.slice(0, 3).map((k) => [k, parsed[k]]))
223+
previewSection = `Type: JSON Object
224+
Keys: ${keys.length}
225+
226+
To avoid exceeding token budget, showing structural summary instead of full content.
227+
228+
Sample of data:
229+
${JSON.stringify(sample, null, 2)}`
230+
}
231+
} catch {
232+
// Fall through to text preview
233+
}
234+
}
235+
236+
// Fall back to text preview if not JSON or parsing failed
237+
if (!previewSection) {
238+
previewSection = `Content-Type: ${contentType}
239+
240+
To avoid exceeding token budget, showing preview instead of full content.
241+
242+
Preview (first 2000 characters):
243+
${content.slice(0, 2000)}...`
244+
}
245+
246+
return `⚠️ Large response detected (~${tokenCount.toLocaleString()} tokens)
247+
248+
URL: ${url}
249+
${previewSection}
250+
251+
To access this data, please:
252+
• Ask me to save the full response to a file
253+
• Specify what information you're looking for
254+
• Request specific sections or search terms`
255+
}

0 commit comments

Comments
 (0)