Skip to content

feat(dashscope): add dashscope_image_to_image (image editing) tool to DashScopeMultiModalTool - #2995

Open
tanzzj wants to merge 8 commits into
agentscope-ai:mainfrom
tanzzj:dashscope-image2image
Open

feat(dashscope): add dashscope_image_to_image (image editing) tool to DashScopeMultiModalTool#2995
tanzzj wants to merge 8 commits into
agentscope-ai:mainfrom
tanzzj:dashscope-image2image

Conversation

@tanzzj

@tanzzj tanzzj commented Sep 5, 2026

Copy link
Copy Markdown

AgentScope-Java Version

2.0.3-SNAPSHOT (branch base origin/main @ c32de522; local <revision> in the root pom.xml)

Description

Background

DashScopeMultiModalTool covers text-to-image, image-to-text, text-to-video,
image-to-video, first-last-frame-to-video, TTS/ASR and video-to-text, but has no
image-to-image (image editing) tool. An agent asked to edit a user-provided image
("colorize this sketch", "replace the background") therefore has no tool that takes
an input image plus a prompt, and falls back to dashscope_text_to_image, which
redraws from a textual description of the image. The original composition is lost and
the call still reports success, so the degradation is silent.

Changes made

One file, additive: agentscope-extensions-model-dashscope/.../dashscope/tool/DashScopeMultiModalTool.java

  • New @Tool method dashscopeImageToImage(...) registered as dashscope_image_to_image,
    parameters image_url, prompt, model (optional), size (optional), use_base64 (optional).
  • Calls MultiModalConversation (the multimodal-generation endpoint) with content
    [{image: ref}, {text: prompt}] and defaults to qwen-image-edit; returns an ImageBlock
    with URLSource, or Base64Source when use_base64=true — the same result shape as
    dashscope_text_to_image, so formatters and downstream consumers need no change.
  • Guards for behaviours observed against the live API:
    • rejectsImageInput(model): the wan* family and the text-to-image members of the
      qwen-image family cannot consume an image on this endpoint, so they are rejected
      locally with an actionable message instead of an opaque service-side parameter error
      (without this the model retries with a text-to-image model and the silent degradation
      comes back).
    • The request carries exactly one message with role user; a leading system message or
      a second message is rejected by the endpoint.
    • resolveOutputSize / supportsOutputSize: only qwen-image-edit-plus,
      qwen-image-edit-max and the qwen-image-2.0 series accept an explicit resolution, so
      size is dropped with a log.warn for other models rather than failing the whole call
      (output resolution then follows the input image).
    • toEditableImageRef: an existing local file is inlined as a Base64 data URL, because this
      endpoint rejects the file:// protocol that the vision tools use via
      MediaUtils.urlToProtocolUrl().
    • When no image comes back, the text field of the same content map is surfaced as the
      error message (that is where a model-side refusal lands).
  • Class-level Javadoc and the tool description updated to state the input formats and to
    steer back to dashscope_text_to_image when there is no input image.

How to test

Static / unit (no key required):

mvn spotless:apply -pl agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-dashscope
mvn test -pl agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-dashscope

Manual, against the real service:

export DASHSCOPE_API_KEY=sk-xxx

then register the tool on an agent and ask "edit this image: , make it
night-time". Expected: one ImageBlock(URLSource) in the tool result and a visibly edited
image. Negative cases worth checking: model=wanx2.1-t2i-turbo -> local rejection with the
hint message; model=qwen-image-edit + size=1024*1024 -> size dropped with a warning and
a successful call; image_url pointing at a local file -> inlined as Base64 and accepted.

Also verified in a production deployment (an application that consumes this module via
agentscope-extensions-model-dashscope), where the tool replaced silent text-to-image
fallbacks for attachment-based edits.

``

Not included

The tool tables under docs/v1/en/**/tool.md and docs/v1/zh/**/tool.md do
not list dashscope_image_to_image yet. Happy to add them here if you'd
prefer that over a follow-up PR.

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has been formatted with mvn spotless:apply
  • All tests are passing (mvn test)
  • Javadoc comments are complete and follow project conventions
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

tanzzj added 6 commits August 31, 2026 23:57
…r dashscope_image_to_image

- default the edit model to qwen-image-edit instead of a text-to-image model

- reject models that cannot consume an input image on the multimodal-generation endpoint locally, with an actionable hint, so the agent does not silently fall back to text-to-image

- drop the size parameter for models that do not support an explicit output resolution (plus / max / 2.0 series only)

- inline an existing local file as a Base64 data URL, since this endpoint rejects file://

- keep the request to a single user message, which the endpoint requires, and surface the model-side text refusal as the error message
… and result blocks

Adds 11 unit tests plus a nested ImageEditGuardTests block that exercises the private model predicates by reflection. The request-shape tests assert the built MultiModalConversationParam (model fallback for a blank model, single user message, image then text content order), all four input forms (http URL, local path, oss URL, base64 data URL), the output-size capability gate, and the refusal / empty-response / error paths. The unsupported-model case asserts no conversation object is constructed at all.

Also adds two E2E cases: a real edit call and a zero-cost guard check that returns before the request is built.
…nerations

The edit family changed naming with its second generation: qwen-image-2.0 and -pro drop the -edit token, so the guards listed that version as a literal. A later generation therefore matched neither branch and was refused as text-to-image only - a hard outage for a working model, attributed to a reason the message states incorrectly.

Detect the generation by its leading digit instead, so an unreleased model needs no code change, and keep refusing the versionless text-to-image members and the wan family, whose image editing runs on a different task API.

The output-size gate stays an explicit list on purpose, in the opposite direction: an unexpected size fails the whole request, while a dropped one only degrades the resolution to that of the input image.

Also move the model guard ahead of resolveOutputSize, which logged a warning about dropping a size for a request that was about to be rejected without being sent.
@CLAassistant

CLAassistant commented Sep 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@tanzzj tanzzj changed the title feat(dashscope): add dashscope_image_to_image (image editing) tool to DashScopeMultiModalToolfeat(dashscope): add dashscope_image_to_image (image editing) tool to DashScopeMultiModalTool feat(dashscope): add dashscope_image_to_image (image editing) tool to DashScopeMultiModalTool Sep 5, 2026
@codecov

codecov Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.65079% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../model/dashscope/tool/DashScopeMultiModalTool.java 93.65% 4 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

@CryoThrust

Copy link
Copy Markdown

The additive tool shape and local model guards look well-scoped. One security/privacy concern: the new DEBUG log records imageUrl and prompt verbatim. imageUrl may be a large data: URL containing user-provided image bytes, and prompts can contain sensitive content; this can leak payloads into application logs and create very large log entries. Could the log be reduced to safe metadata (for example, input-reference kind/length, model, size, and a bounded prompt length or no prompt), with a regression test or documented logging contract? Also, the PR description notes the tool docs are not updated; adding the tool to the generated/user-facing DashScope tool tables would make the feature discoverable or explicitly track that as a follow-up.

@CryoThrust

Copy link
Copy Markdown

/check

…e debug log

image_url is an advertised input form as a Base64 data URL, so the previous line wrote the entire encoded image into the log - megabytes per call, and in the clear for anything that ships logs elsewhere. The prompt is user content and has no better claim to being logged; a signed object URL also carries credentials in its query string.

The call site now renders only the reference kind, the reference length and the prompt length, with no prefix of either value, and the method documents that as its logging contract.

Covered by two tests. Note the summarizer is asserted directly rather than through a captured LoggingEvent: the module test classpath has slf4j-api with no binding, so the logger is a NOP and nothing can be captured without adding a logging backend as a test dependency.
…example

This example is the only place in the repository that names each multimodal tool and pairs it with a copy-paste prompt, and it had drifted behind the class it documents. The doc tree has no equivalent per-tool surface to extend: docs/v1 enumerates capabilities per tool class and its import path no longer resolves after the 2.x layout, while the v2 DashScope page covers model wiring only.
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.

3 participants