Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[FEATURE] Port SaveFilesAsArtifactsPlugin from adk-python

オープン
#1,411 コメント 1 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@hemasekhar-p がすでに取り組んでいます。

2026年8月10日 から。

  • #1412 @svetanis による — オープン

評価

この issue はまだ評価されていません。

説明

needs review

Please make sure you read the contribution guide and file the issues in the right place.
Contribution guide.

🔴 Required Information

Is your feature request related to a specific problem?

adk-python deprecated save_input_blobs_as_artifacts in favour of SaveFilesAsArtifactsPlugin.
adk-java has the parameter but not the plugin, so RunConfig.saveInputBlobsAsArtifacts(true) is the
only way to keep uploaded bytes out of every later LLM request — and it loses two things the plugin
provides.

1. The uploaded file name is discarded. Runner names every artifact
artifact_{invocationId}_{index} and never reads Blob.displayName.

// Runner, in the blob-offload loop
String fileName = "artifact_" + invocationContext.invocationId() + "_" + i;

An upload of report.pdf is stored as artifact_<invocationId>_1, so anything that later loads it
must work from an opaque id that appears nowhere but the placeholder text.

2. Nothing is reported to the session. The user event carries a state delta only, so
EventActions.artifactDelta stays empty and the session's artifact bookkeeping never records the
upload.

// Runner.appendNewMessageToSession — no artifactDelta is set on this event
if (stateDelta != null && !stateDelta.isEmpty()) {
  eventBuilder.actions(EventActions.builder().stateDelta(new ConcurrentHashMap<>(stateDelta)).build());
}

Both behaviours live in the plugin.

Describe the Solution You'd Like

SaveFilesAsArtifactsPlugin in com.google.adk.plugins, registered on a Runner like any other
plugin. For each inlineData part of the incoming user message it would:

  • save the part to the configured BaseArtifactService, named from Blob.displayName, falling back
    to artifact_{invocationId}_{index} when the blob carries no name;
  • replace it with [Uploaded Artifact: "<name>"] in the message that reaches the model and is
    appended to the session, matching adk-python's wording;
  • report the saved versions through EventActions.artifactDelta;
  • on a failed save, keep the original part and log, without failing the invocation — as in
    adk-python, the log is the only signal, so an unreachable artifact service degrades silently to no
    offload.

attach_file_reference would not be ported: it needs
get_artifact_version(...).canonical_uri, which has no equivalent on BaseArtifactService, so it
would require an SPI change across every implementation. Separate change.

Impact on your work

Applications wanting adk-python's behaviour must reimplement it in application code, where it will
drift from upstream. Not blocking, and no timeline — this is a parity gap, not an outage.

Willingness to contribute

Yes. A PR follows immediately after this issue: one new plugin class plus a small package-private helper, with tests. No existing file is modified, and the only new public surface is the plugin class itself.


🟡 Recommended Information

Describe Alternatives You've Considered

Teach the existing flag to read Blob.displayName and set artifactDelta. Smaller diff, but it
adds behaviour to the parameter adk-python is steering users away from, and adk-java would still have
no plugin — widening the divergence rather than closing it.

Implement the plugin in application code. This works using public API only, and is how the
behaviour below was verified. It is per-application boilerplate for something a plugin surface exists
to ship once.

Proposed API / Implementation

Registration needs no new API — the existing surfaces already accept it:

Runner runner = Runner.builder()
    .agent(agent)
    .appName("my-app")
    .artifactService(new InMemoryArtifactService())
    .sessionService(new InMemorySessionService())
    .plugins(new SaveFilesAsArtifactsPlugin())   // also App.Builder.plugins(...) and
    .build();                                    // InMemoryRunner(agent, appName, plugins)

Two hooks, both already wired into the runtime:

public class SaveFilesAsArtifactsPlugin extends BasePlugin {

  @Override
  public Maybe<Content> onUserMessageCallback(InvocationContext ctx, Content userMessage) {
    // save each inlineData part, swap it for [Uploaded Artifact: "<name>"],
    // stash {fileName: version} under a temp: state key, return the rebuilt Content
  }

  @Override
  public Maybe<Content> beforeAgentCallback(BaseAgent agent, CallbackContext callbackContext) {
    // drain the stash into callbackContext.eventActions().artifactDelta(), return Maybe.empty()
  }
}

The state hand-off exists because onUserMessageCallback runs before any EventActions exists, so
the saved versions cannot be reported from there. adk-python solves it the same way.

Additional Context

Same upload, same runner, differing only in which mechanism performs the offload:

--- Run A: RunConfig.saveInputBlobsAsArtifacts(true) ---
  user attached                     : blob with displayName="report.pdf"
  artifacts stored in the session   : artifact_e-eb41628a-9812-43a0-88d0-7b121706190b_1
  EventActions.artifactDelta        : (none)
  message appended to the session   :
                                      - text: "read the attachment"
                                      - text: "Uploaded file: artifact_e-eb41628a-…_1. It has been saved to the artifacts"

--- Run B: SaveFilesAsArtifactsPlugin (the proposed port) ---
  user attached                     : blob with displayName="report.pdf"
  artifacts stored in the session   : report.pdf
  EventActions.artifactDelta        : {report.pdf=0}
  message appended to the session   :
                                      - text: "read the attachment"
                                      - text: "[Uploaded Artifact: "report.pdf"]"

Both offload the payload losslessly and both show the model a placeholder instead of the bytes — the
gap is the file name and the bookkeeping, not the offload.

Observed on 1.7.2-SNAPSHOT, Windows 11 (not OS-specific), with a stub model and with
gemini-3.5-flash; both mechanisms run before the model call.

主要言語
Java
スター
1.7k
フォーク
431
平均マージ
3日 2時間
マージ済み PR(30日)
46

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

google/adk-java のほかの issue

google/adk-java の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。