-
Notifications
You must be signed in to change notification settings - Fork 379
feat(fetch-url): support fetching images from URLs #1266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| Fetch content from a URL. For an HTML page the main article text is extracted; for a plain-text or markdown response the full body is returned verbatim. The result states which of the two you received, so you can judge how complete it is. Use this when you need to read a specific web page. | ||
| Fetch content from a URL. For an HTML page the main article text is extracted; for a plain-text or markdown response the full body is returned verbatim; for an image the image is returned directly so the model can view it. The result states which of the three you received, so you can judge how complete it is. Use this when you need to read a specific web page or view an image from a URL. | ||
|
|
||
| Only fully-formed public `http`/`https` URLs are supported; other schemes and private or loopback addresses are not fetched. Very large pages may be truncated or refused. | ||
| Only fully-formed public `http`/`https` URLs are supported; other schemes and private or loopback addresses are not fetched. Very large pages or images may be truncated or refused. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -161,6 +161,35 @@ | |
| ); | ||
| } | ||
|
|
||
| const contentType = (response.headers.get('content-type') ?? '').toLowerCase(); | ||
|
|
||
| // Image responses: stream as binary and return as base64-encoded image data | ||
| // so the tool can render them directly as image_url content parts. | ||
| if (contentType.startsWith('image/')) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For any URL that returns Useful? React with 👍 / 👎. |
||
| const contentLengthRaw = response.headers.get('content-length'); | ||
| if (contentLengthRaw !== null) { | ||
| const cl = Number(contentLengthRaw); | ||
| if (Number.isFinite(cl) && cl > this.maxBytes) { | ||
| throw new Error( | ||
| `Response body too large: ${String(cl)} bytes exceeds maxBytes (${String(this.maxBytes)}).`, | ||
| ); | ||
| } | ||
| } | ||
| const buffer = await response.arrayBuffer(); | ||
| const bytes = Buffer.byteLength(buffer); | ||
| if (bytes > this.maxBytes) { | ||
| throw new Error( | ||
| `Response body too large: ${String(bytes)} bytes exceeds maxBytes (${String(this.maxBytes)}).`, | ||
| ); | ||
| } | ||
| const base64 = Buffer.from(buffer).toString('base64'); | ||
| return { | ||
| content: '', | ||
| kind: 'image', | ||
| imageData: { base64, mimeType: contentType.split(';')[0].trim() }, | ||
| }; | ||
| } | ||
|
|
||
| // Reject oversized responses before buffering the full body. | ||
| const contentLengthRaw = response.headers.get('content-length'); | ||
| if (contentLengthRaw !== null) { | ||
|
|
@@ -182,7 +211,6 @@ | |
| ); | ||
| } | ||
|
|
||
| const contentType = (response.headers.get('content-type') ?? '').toLowerCase(); | ||
| if (contentType.startsWith('text/plain') || contentType.startsWith('text/markdown')) { | ||
| return { content: body, kind: 'passthrough' }; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the model actually calls
TodoList, args are validated by AJV fromtool.parametersbeforeresolveExecutionruns, andtoInputJsonSchemaexplicitly notes there is no Zod parse/strip step before dispatch (packages/agent-core/src/tools/support/input-schema.ts:41,packages/agent-core/src/loop/tool-call.ts:202-225). With zod's input JSON schema for this preprocess still derived from the inner enum,status: "completed"is rejected before the newsetTodosnormalization can run, so the added acceptance only works in tests that bypass preflight validation. Please either make the JSON schema explicitly allow the handled alias or normalize after validation in the loop/tool path.Useful? React with 👍 / 👎.