Skip to content

feat(fetch-url): support fetching images as multimodal content#2011

Open
bj456736 wants to merge 1 commit into
MoonshotAI:mainfrom
bj456736:auto-pr-20260721-1800
Open

feat(fetch-url): support fetching images as multimodal content#2011
bj456736 wants to merge 1 commit into
MoonshotAI:mainfrom
bj456736:auto-pr-20260721-1800

Conversation

@bj456736

Copy link
Copy Markdown
Contributor

Problem

The FetchURL tool only handles text-based responses (HTML, plain text, markdown). When a URL points to an image, the tool falls through to the HTML extraction path, producing meaningless garbage output instead of the actual image.

This addresses the feature request: Fetch 工具支持把图片也拉下来.

Solution

Extend the fetch pipeline to detect image responses () and return them as multimodal content that the model can view directly.

Changes

  1. Type system ():

    • Added to
    • Added optional to
  2. Provider ():

    • Detect Content-Type early in
    • Read binary body via
    • Base64-encode and return
    • Size limits still apply (rejected if > maxBytes)
  3. Tool ():

    • When , return with both a text caption and an part
    • This lets the model see the image directly instead of an opaque description
  4. Documentation ():

    • Updated description to mention image URL fetching
  5. Tests:

    • Added image response tests for
    • Added multimodal output test for

Why this approach

  • Minimal intrusion: only touches the fetch pipeline, no changes to other tools
  • Backward compatible: existing text/HTML paths unchanged; imageData is optional
  • Follows existing patterns: already returns with — this mirrors that pattern for fetched images
  • Security preserved: SSRF guard, size limits, and redirect validation all still apply

When a URL points to an image (content-type: image/*), the fetcher now
reads the binary body, base64-encodes it, and returns it as an
UrlFetchResult with kind='image'. FetchURLTool detects this and emits
a multimodal ContentPart[] (text + image_url) so the model can see the
image directly instead of receiving an opaque binary blob description.

Changes:
- UrlFetchKind: add 'image'
- UrlFetchResult: add optional imageData { mimeType, base64 }
- LocalFetchURLProvider: detect image/* responses, read arrayBuffer,
  return base64-encoded image data
- FetchURLTool: when kind === 'image', return ContentPart[] with
  image_url instead of plain text output
- fetch-url.md: update description to mention image fetching
- Tests: add coverage for image response handling in both provider
  and tool layers

Fixes: Fetch 工具支持把图片也拉下来
@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ebb3ae1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@ebb3ae1
npx https://pkg.pr.new/@moonshot-ai/kimi-code@ebb3ae1

commit: ebb3ae1

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebb3ae1ff2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


// Image responses: read binary body and return as base64 for direct
// model consumption.
if (contentType.startsWith('image/')) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate fetched image MIME before persisting

When the fetched URL returns any image/* such as image/svg+xml, image/avif, image/heic, or even the alias image/jpg, this branch marks it as an image and FetchURLTool later persists a data image_url with that MIME. The repo's shared image policy says only PNG/JPEG/GIF/WebP are allowed and that unsupported inline images make later provider requests fail (packages/agent-core/src/tools/support/image-format-policy.ts:4-8 and :169-175), while MCP results already refuse these at packages/agent-core/src/mcp/output.ts:146. Please run the same MIME normalization/sniffing gate, or return a text notice, before returning imageData.

Useful? React with 👍 / 👎.

// Image responses: read binary body and return as base64 for direct
// model consumption.
if (contentType.startsWith('image/')) {
const arrayBuffer = await response.arrayBuffer();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check image size before buffering

For image/* responses with Content-Length greater than maxBytes, this line buffers the entire body with arrayBuffer() before the size check runs. That bypasses the pre-buffer rejection that still exists for text responses below, so a large image can consume memory/bandwidth or OOM the process before being rejected; move the existing content-length guard above the image branch or duplicate it here.

Useful? React with 👍 / 👎.

Comment on lines +100 to +107
if (kind === 'image' && imageData !== undefined) {
return {
output: [
{ type: 'text', text: `Fetched image from ${args.url}` },
{
type: 'image_url',
imageUrl: { url: `data:${imageData.mimeType};base64,${imageData.base64}` },
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Port image fetch support to agent-core-v2

Implementing this only in packages/agent-core misses the shipped kap-server path: I checked packages/kap-server/package.json:26 (depends on @moonshot-ai/agent-core-v2) and packages/agent-core-v2/src/app/web/tools/fetch-url-types.ts:13-18 / fetch-url.ts:61-78, where UrlFetchKind is still only passthrough | extracted and the tool still renders text. As a result, kimi web/kap-server users fetching an image URL still get the old HTML/text extraction behavior, so this feature won't work in the default CLI/server environment unless the v2 FetchURL provider/tool is updated too.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant