Service Implementations
Choose between in-memory and Google Cloud Storage artifact backends for your ADK-TS agents.
ADK-TS ships two artifact service implementations: InMemoryArtifactService for development and GcsArtifactService for production. Both implement the same BaseArtifactService interface, so switching between them requires no changes to your agent or tool code — only the service you pass to the Runner changes.
Choosing a service
| Service | Persistence | Use for | Setup |
|---|---|---|---|
InMemoryArtifactService | None | Development and testing | Zero config |
GcsArtifactService | Durable | Production | GCS bucket |
| Custom implementation | Varies | PostgreSQL, S3, Redis, etc. | You implement |
InMemoryArtifactService loses all data when the process restarts. Never use
it in production — switch to GcsArtifactService or a custom backend.
InMemoryArtifactService
Data lives in process memory with no configuration needed. It is the right choice for local development, unit tests, and examples — anywhere you don't need persistence.
import { AgentBuilder, InMemoryArtifactService } from "@iqai/adk";
const { runner } = await AgentBuilder.create("my_agent")
.withModel("gemini-2.5-flash")
.withArtifactService(new InMemoryArtifactService())
.build();You can also use the service directly — for example, in tests where you want to call the service API without a running agent:
import { InMemoryArtifactService } from "@iqai/adk";
const svc = new InMemoryArtifactService();
const version = await svc.saveArtifact({
appName: "test_app",
userId: "u1",
sessionId: "s1",
filename: "greeting.txt",
artifact: { text: "Hello, World!" },
});
// version → 0
const loaded = await svc.loadArtifact({
appName: "test_app",
userId: "u1",
sessionId: "s1",
filename: "greeting.txt",
});
// loaded → { text: "Hello, World!" }GcsArtifactService
Backed by a Google Cloud Storage bucket — durable, replicated, and with no practical storage limit. This is the service to use in production.
Before using it, you need a GCP project, a GCS bucket, and a service account with roles/storage.objectAdmin on that bucket.
By default, the service picks up credentials from Application Default Credentials (ADC). Run gcloud auth application-default login once locally, or set GOOGLE_APPLICATION_CREDENTIALS in production:
import { AgentBuilder, GcsArtifactService } from "@iqai/adk";
const { runner } = await AgentBuilder.create("my_agent")
.withModel("gemini-2.5-flash")
.withArtifactService(new GcsArtifactService("my-artifacts-bucket"))
.build();If you need to specify credentials explicitly — for example, to use a service account key file instead of ADC — pass them as the second argument:
import { GcsArtifactService } from "@iqai/adk";
const artifactService = new GcsArtifactService("my-artifacts-bucket", {
projectId: "my-gcp-project",
keyFilename: "/path/to/service-account.json",
});Run these commands once to create your bucket and grant the service account access:
# Create the bucket
gsutil mb gs://my-artifacts-bucket
# Authenticate (choose one approach)
gcloud auth application-default login
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
# Grant the service account storage access
gsutil iam ch serviceAccount:svc@project.iam.gserviceaccount.com:roles/storage.objectAdmin gs://my-artifacts-bucketStorage layout
GCS stores each artifact version as a separate blob. Understanding the path structure helps when browsing your bucket or debugging missing files:
{appName}/{userId}/{sessionId}/{filename}/{version} ← session-scoped
{appName}/{userId}/user/{filename}/{version} ← user-scoped (user: prefix)The user: prefix on a filename swaps the {sessionId} segment for user, which is why user-scoped artifacts are visible across all sessions.
Custom service
If you need a different backend — PostgreSQL, S3, Redis, or anything else — implement the BaseArtifactService interface. Your class only needs five methods:
import type { BaseArtifactService } from "@iqai/adk";
import type { Part } from "@google/genai";
class MyDatabaseArtifactService implements BaseArtifactService {
async saveArtifact(args: {
appName: string;
userId: string;
sessionId: string;
filename: string;
artifact: Part;
}): Promise<number> {
// Write the artifact to your database, return the new version number
return 0;
}
async loadArtifact(args: {
appName: string;
userId: string;
sessionId: string;
filename: string;
version?: number;
}): Promise<Part | null> {
// Return the artifact Part, or null if not found
return null;
}
async listArtifactKeys(args: {
appName: string;
userId: string;
sessionId: string;
}): Promise<string[]> {
// Return all filenames for this session
return [];
}
async deleteArtifact(args: {
appName: string;
userId: string;
sessionId: string;
filename: string;
}): Promise<void> {
// Delete all versions of this filename
}
async listVersions(args: {
appName: string;
userId: string;
sessionId: string;
filename: string;
}): Promise<number[]> {
// Return all version numbers for this filename
return [];
}
}Pass your custom service to the runner the same way as the built-in ones:
import { AgentBuilder } from "@iqai/adk";
const { runner } = await AgentBuilder.create("my_agent")
.withModel("gemini-2.5-flash")
.withArtifactService(new MyDatabaseArtifactService())
.build();Troubleshooting
| Issue | Fix |
|---|---|
"Artifact service is not initialized" | Pass artifactService to Runner or use .withArtifactService() on AgentBuilder |
GCS 403 Forbidden | Service account needs roles/storage.objectAdmin on the bucket |
GCS 404 Not Found on load | Artifact doesn't exist at that path or version — loadArtifact returns undefined |
| Data missing after restart | InMemoryArtifactService doesn't persist — switch to GcsArtifactService for prod |