[FEATURE] Port SaveFilesAsArtifactsPlugin from adk-python
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
Issue này chưa được đánh giá.
Mô tả
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 fromBlob.displayName, falling back
toartifact_{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.
- Ngôn ngữ chính
- Java
- Star
- 1.7k
- Fork
- 433
- Merge trung bình
- 3 ngày 9 giờ
- Pull request đã merge (30 ngày)
- 46
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của google/adk-java
-
GeminiUtil placeholder user turn ("Continue output. DO NOT look at this line ...") is flagged by prompt injection filtersCó thể đã có người làm @hemasekhar-p đã nhận 2 ngày trước. Đang mởneeds review
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
google/adk-java#1628 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[spring-ai] ToolConverter silently drops enum and items from tool parameter schemasCó thể đã có người làm @hirematha đã nhận 5 ngày trước. Đang mởneeds review
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
google/adk-java#1609 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[spring-ai] Streaming responses ending with CJK punctuation (。!?) are misclassified as partial and never persisted to the sessionCó thể đã có người làm @hirematha đã nhận 5 ngày trước. Đang mởneeds review
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
google/adk-java#1608 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
FirestoreSessionService loses event fields on reload, breaking later turns and tool confirmationsCó thể đã có người làm @innoprej đã nhận 1 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 22/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Claude model throws UnsupportedOperationException("Not supported yet.") on thinking blocks from Claude 5 modelsCó thể đã có người làm @hemasekhar-p đã nhận 2 ngày trước. Đang mởneeds review
google/adk-java#1630 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của google/adk-java
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
objectionary/eo-graphs#80 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
-
enhancement good first issue
Độ khó 2/5 Nửa ngày Mức phù hợp với người mới 66/100
apache/fineract-consumer-facing#175 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[BUG] 订单:会员凭订单号即可取消其他会员的待付款订单(取消接口不校验订单归属)Có thể đã có người làm @dadiyang đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
macrozheng/mall#1016 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100