27 releases (1 stable)
Uses new Rust 2024
| 1.0.0 | Jun 7, 2026 |
|---|---|
| 0.9.1 | May 24, 2026 |
| 0.8.4 | May 18, 2026 |
| 0.5.0 | Mar 29, 2026 |
| 0.1.1 | Nov 30, 2025 |
#1523 in Asynchronous
5,778 downloads per month
Used in 14 crates
(6 directly)
375KB
6.5K
SLoC
adk-artifact
Binary artifact storage for Rust Agent Development Kit (ADK-Rust) agents.
Overview
adk-artifact provides versioned binary data storage for the Rust Agent Development Kit (ADK-Rust). Artifacts are useful for storing images, documents, audio, and other binary data that agents produce or consume.
Two storage backends are included:
| Backend | Type | Persistence | Use case |
|---|---|---|---|
InMemoryArtifactService |
In-memory HashMap |
Process lifetime only | Development, testing |
FileArtifactService |
Local filesystem | Durable | Single-node production, local dev |
Both implement the ArtifactService trait, so you can swap backends without changing application code.
Installation
[dependencies]
adk-artifact = "1.0.0"
Or via the umbrella crate:
[dependencies]
adk-rust = { version = "1.0.0", features = ["artifacts"] }
Quick Start
use adk_artifact::{InMemoryArtifactService, ArtifactService, SaveRequest, LoadRequest};
use adk_core::Part;
let service = InMemoryArtifactService::new();
// Save an artifact (version auto-increments from 1)
let resp = service.save(SaveRequest {
app_name: "my_app".to_string(),
user_id: "user_123".to_string(),
session_id: "session_456".to_string(),
file_name: "report.pdf".to_string(),
part: Part::InlineData {
mime_type: "application/pdf".to_string(),
data: pdf_bytes,
},
version: None,
}).await?;
println!("Saved as version {}", resp.version); // 1
// Load the latest version
let resp = service.load(LoadRequest {
app_name: "my_app".to_string(),
user_id: "user_123".to_string(),
session_id: "session_456".to_string(),
file_name: "report.pdf".to_string(),
version: None, // None = latest
}).await?;
Storage Backends
InMemoryArtifactService
Stores artifacts in a thread-safe HashMap behind RwLock. Data is lost when the process exits.
use adk_artifact::InMemoryArtifactService;
let service = InMemoryArtifactService::new();
FileArtifactService
Persists artifacts to the local filesystem. Each version is stored as a JSON-serialized file under a directory hierarchy:
<base_dir>/<app_name>/<user_id>/<session_id>/<file_name>/v1.json
v2.json
use adk_artifact::FileArtifactService;
let service = FileArtifactService::new("/var/data/artifacts")?;
FileArtifactService includes a health_check() method that verifies the base directory is writable.
ArtifactService Trait
All backends implement the full ArtifactService trait:
| Method | Description |
|---|---|
save(SaveRequest) |
Store an artifact. Auto-increments version when version: None. |
load(LoadRequest) |
Retrieve an artifact. Loads latest version when version: None. |
delete(DeleteRequest) |
Delete a specific version, or all versions when version: None. |
list(ListRequest) |
List all artifact filenames in a session (includes user-scoped artifacts). |
versions(VersionsRequest) |
List all available version numbers for a specific artifact. |
health_check() |
Verify backend connectivity. Default impl returns Ok(()). |
Versioning
Every artifact supports multiple versions. When saving with version: None, the version auto-increments starting from 1. You can also write to an explicit version:
// Auto-increment: first save → v1, second save → v2
service.save(SaveRequest { version: None, ..req }).await?;
service.save(SaveRequest { version: None, ..req }).await?;
// Explicit version
service.save(SaveRequest { version: Some(42), ..req }).await?;
// List all versions (returned in descending order)
let resp = service.versions(VersionsRequest {
app_name: "app".into(),
user_id: "user".into(),
session_id: "sess".into(),
file_name: "report.pdf".into(),
}).await?;
// resp.versions == [2, 1]
Scoping
Artifacts are scoped by three dimensions: app_name, user_id, and session_id. This means two sessions for the same user can each have a report.pdf without conflict.
User-Scoped Artifacts
Prefix a filename with user: to make it accessible across all sessions for that user:
// Save from session A
service.save(SaveRequest {
session_id: "session_A".into(),
file_name: "user:profile.png".into(),
..req
}).await?;
// Load from session B — same artifact
service.load(LoadRequest {
session_id: "session_B".into(),
file_name: "user:profile.png".into(),
..req
}).await?;
// list() from any session returns user-scoped artifacts too
let resp = service.list(ListRequest {
session_id: "session_B".into(),
..list_req
}).await?;
// resp.file_names includes "user:profile.png"
Internally, user-scoped artifacts are stored under a shared _user_scoped_ directory (filesystem) or a "user" session key (in-memory), so they're visible regardless of which session ID is used.
Deleting Artifacts
use adk_artifact::DeleteRequest;
// Delete a specific version
service.delete(DeleteRequest {
app_name: "app".into(),
user_id: "user".into(),
session_id: "sess".into(),
file_name: "report.pdf".into(),
version: Some(1),
}).await?;
// Delete all versions
service.delete(DeleteRequest {
version: None,
..del_req
}).await?;
Deleting a non-existent artifact or version is a no-op (no error returned).
ScopedArtifacts
ScopedArtifacts wraps an ArtifactService and implements the simpler adk_core::Artifacts trait by automatically injecting app_name, user_id, and session_id into every request. This is the interface agents use at runtime.
use adk_artifact::{ScopedArtifacts, InMemoryArtifactService};
use adk_core::{Artifacts, Part};
use std::sync::Arc;
let service = Arc::new(InMemoryArtifactService::new());
let artifacts = ScopedArtifacts::new(
service,
"my_app".to_string(),
"user_123".to_string(),
"session_456".to_string(),
);
// Simple three-method API
let version = artifacts.save("chart.png", &part).await?;
let loaded = artifacts.load("chart.png").await?;
let files = artifacts.list().await?;
File Name Validation
Both backends validate artifact filenames to prevent path traversal attacks. The following are rejected with an AdkError::Artifact error:
- Empty names
- Names containing
/or\ - Names equal to
.or.. - Names containing
..anywhere
// These all return Err:
service.save(SaveRequest { file_name: "".into(), .. }).await; // empty
service.save(SaveRequest { file_name: "../etc/passwd".into(), .. }).await; // traversal
service.save(SaveRequest { file_name: "foo/bar.txt".into(), .. }).await; // path separator
Use with LoadArtifactsTool
Artifacts integrate with agents via LoadArtifactsTool from adk-tool:
use adk_tool::LoadArtifactsTool;
let tool = LoadArtifactsTool::new();
let agent = LlmAgentBuilder::new("assistant")
.tool(Arc::new(tool))
.build()?;
The LLM can then call this tool to load artifacts by name into the conversation.
Related Crates
License
Apache-2.0
Part of ADK-Rust
This crate is part of the ADK-Rust framework for building AI agents in Rust.
Dependencies
~8–12MB
~141K SLoC