@@ -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