VOOZH about

URL: https://adk.dev/artifacts/

⇱ Artifacts - Agent Development Kit (ADK)


Skip to content

Artifacts

Supported in ADKPython v0.1.0TypeScript v0.6.1Go v0.1.0Java v0.1.0Kotlin v0.1.0

In ADK, Artifacts represent a crucial mechanism for managing named, versioned binary data associated either with a specific user interaction session or persistently with a user across multiple sessions. They allow your agents and tools to handle data beyond simple text strings, enabling richer interactions involving files, images, audio, and other binary formats.

Note

The specific parameters or method names for the primitives may vary slightly by SDK language (e.g., save_artifact in Python, saveArtifact in Java). Refer to the language-specific API documentation for details.

What are Artifacts?

  • Definition: An Artifact is essentially a piece of binary data (like the content of a file) identified by a unique filename string within a specific scope (session or user). Each time you save an artifact with the same filename, a new version is created.

  • Representation: Artifacts are consistently represented using the standard google.genai.types.Part object. The core data is typically stored within an inline data structure of the Part (accessed via inline_data), which itself contains:

    • data: The raw binary content as bytes.
    • mime_type: A string indicating the type of the data (e.g., "image/png", "application/pdf"). This is essential for correctly interpreting the data later.
# Example of how an artifact might be represented as a types.Part
importgoogle.genai.typesastypes
# Assume 'image_bytes' contains the binary data of a PNG image
image_bytes = b'\x89PNG\r\n\x1a\n...' # Placeholder for actual image bytes
image_artifact = types.Part(
 inline_data=types.Blob(
 mime_type="image/png",
 data=image_bytes
 )
)
# You can also use the convenience constructor:
# image_artifact_alt = types.Part.from_bytes(data=image_bytes, mime_type="image/png")
print(f"Artifact MIME Type: {image_artifact.inline_data.mime_type}")
print(f"Artifact Data (first 10 bytes): {image_artifact.inline_data.data[:10]}...")
import{createPartFromBase64,typePart}from'@google/genai';
// Assume 'imageBytes' contains the binary data of a PNG image.
constimageBytes=newUint8Array([0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a]);
// Using Buffer.from(bytes).toString('base64') for Node.js environments.
constimageArtifact:Part=createPartFromBase64(
Buffer.from(imageBytes).toString('base64'),
'image/png',
);
console.log(`Artifact MIME Type: ${imageArtifact.inlineData?.mimeType}`);
// Note: Accessing raw bytes would require decoding from base64.
import(
"log"
"google.golang.org/genai"
)
// Create a byte slice with the image data.
imageBytes,err:=os.ReadFile("image.png")
iferr!=nil{
log.Fatalf("Failed to read image file: %v",err)
}
// Create a new artifact with the image data.
imageArtifact:=&genai.Part{
InlineData:&genai.Blob{
MIMEType:"image/png",
Data:imageBytes,
},
}
log.Printf("Artifact MIME Type: %s",imageArtifact.InlineData.MIMEType)
log.Printf("Artifact Data (first 8 bytes): %x...",imageArtifact.InlineData.Data[:8])
importcom.google.genai.types.Part;
importjava.nio.charset.StandardCharsets;
publicclass ArtifactExample{
publicstaticvoidmain(String[]args){
// Assume 'imageBytes' contains the binary data of a PNG image
byte[]imageBytes={(byte)0x89,(byte)0x50,(byte)0x4E,(byte)0x47,(byte)0x0D,(byte)0x0A,(byte)0x1A,(byte)0x0A,(byte)0x01,(byte)0x02};// Placeholder for actual image bytes
// Create an image artifact using Part.fromBytes
PartimageArtifact=Part.fromBytes(imageBytes,"image/png");
System.out.println("Artifact MIME Type: "+imageArtifact.inlineData().get().mimeType().get());
System.out.println(
"Artifact Data (first 10 bytes): "
+newString(imageArtifact.inlineData().get().data().get(),0,10,StandardCharsets.UTF_8)
+"...");
}
}
funartifactRepresentationExample(){
// Assume 'imageBytes' contains the binary data of a PNG image
valimageBytes=byteArrayOf(0x89.toByte(),0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A)
valimageArtifact=
Part(
inlineData=
Blob(
mimeType="image/png",
data=imageBytes,
),
)
println("Artifact MIME Type: ${imageArtifact.inlineData?.mimeType}")
println("Artifact Data (first 8 bytes): ${imageArtifact.inlineData?.data?.take(8)}")
}
  • Persistence & Management: Artifacts are not stored directly within the agent or session state. Their storage and retrieval are managed by a dedicated Artifact Service (an implementation of BaseArtifactService, defined in google.adk.artifacts. ADK provides various implementations, such as:
    • An in-memory service for testing or temporary storage (e.g., InMemoryArtifactService in Python, defined in google.adk.artifacts.in_memory_artifact_service.py).
    • A service for persistent storage using Google Cloud Storage (GCS) (e.g., GcsArtifactService in Python, defined in google.adk.artifacts.gcs_artifact_service.py). The chosen service implementation handles versioning automatically when you save data.

Why Use Artifacts?

While session state is suitable for storing small pieces of configuration or conversational context (like strings, numbers, booleans, or small dictionaries/lists), Artifacts are designed for scenarios involving binary or large data:

  1. Handling Non-Textual Data: Easily store and retrieve images, audio clips, video snippets, PDFs, spreadsheets, or any other file format relevant to your agent's function.
  2. Persisting Large Data: Session state is generally not optimized for storing large amounts of data. Artifacts provide a dedicated mechanism for persisting larger blobs without cluttering the session state.
  3. User File Management: Provide capabilities for users to upload files (which can be saved as artifacts) and retrieve or download files generated by the agent (loaded from artifacts).
  4. Sharing Outputs: Enable tools or agents to generate binary outputs (like a PDF report or a generated image) that can be saved via save_artifact and later accessed by other parts of the application or even in subsequent sessions (if using user namespacing).
  5. Caching Binary Data: Store the results of computationally expensive operations that produce binary data (e.g., rendering a complex chart image) as artifacts to avoid regenerating them on subsequent requests.

In essence, whenever your agent needs to work with file-like binary data that needs to be persisted, versioned, or shared, Artifacts managed by an ArtifactService are the appropriate mechanism within ADK.

Common Use Cases

Artifacts provide a flexible way to handle binary data within your ADK applications.

Here are some typical scenarios where they prove valuable:

  • Generated Reports/Files:

    • A tool or agent generates a report (e.g., a PDF analysis, a CSV data export, an image chart).
  • Handling User Uploads:

    • A user uploads a file (e.g., an image for analysis, a document for summarization) through a front-end interface.
  • Storing Intermediate Binary Results:

    • An agent performs a complex multi-step process where one step generates intermediate binary data (e.g., audio synthesis, simulation results).
  • Persistent User Data:

    • Storing user-specific configuration or data that isn't a simple key-value state.
  • Caching Generated Binary Content:

    • An agent frequently generates the same binary output based on certain inputs (e.g., a company logo image, a standard audio greeting).

Core Concepts

Understanding artifacts involves grasping a few key components: the service that manages them, the data structure used to hold them, and how they are identified and versioned.

Artifact Service (BaseArtifactService)

  • Role: The central component responsible for the actual storage and retrieval logic for artifacts. It defines how and where artifacts are persisted.

  • Interface: Defined by the abstract base class BaseArtifactService. Any concrete implementation must provide methods for:

    • Save Artifact: Stores the artifact data and returns its assigned version number.
    • Load Artifact: Retrieves a specific version (or the latest) of an artifact.
    • List Artifact keys: Lists the unique filenames of artifacts within a given scope.
    • Delete Artifact: Removes an artifact (and potentially all its versions, depending on implementation).
    • List versions: Lists all available version numbers for a specific artifact filename.
  • Configuration: You provide an instance of an artifact service (e.g., InMemoryArtifactService, GcsArtifactService) when initializing the Runner. The Runner then makes this service available to agents and tools via the InvocationContext.

fromgoogle.adk.runnersimport Runner
fromgoogle.adk.artifactsimport InMemoryArtifactService # Or GcsArtifactService
fromgoogle.adk.agentsimport LlmAgent # Any agent
fromgoogle.adk.sessionsimport InMemorySessionService
# Example: Configuring the Runner with an Artifact Service
my_agent = LlmAgent(name="artifact_user_agent", model="gemini-flash-latest")
artifact_service = InMemoryArtifactService() # Choose an implementation
session_service = InMemorySessionService()
runner = Runner(
 agent=my_agent,
 app_name="my_artifact_app",
 session_service=session_service,
 artifact_service=artifact_service # Provide the service instance here
)
# Now, contexts within runs managed by this runner can use artifact methods
import{
InMemoryArtifactService,
InMemorySessionService,
LlmAgent,
Runner,
}from'@google/adk';
// Example: Configuring the Runner with an Artifact Service
constmyAgent=newLlmAgent({
name:'artifact_user_agent',
model:'gemini-flash-latest',
});
constartifactService=newInMemoryArtifactService();
constsessionService=newInMemorySessionService();
construnner=newRunner({
agent:myAgent,
appName:'my_artifact_app',
sessionService:sessionService,
artifactService:artifactService,
});
// Now, contexts within runs managed by this runner can use artifact methods.
import(
"context"
"log"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/artifactservice"
"google.golang.org/adk/llm/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/sessionservice"
"google.golang.org/genai"
)
// Create a new context.
ctx:=context.Background()
// Set the app name.
constappName="my_artifact_app"
// Create a new Gemini model.
model,err:=gemini.NewModel(ctx,"gemini-2.5-flash",&genai.ClientConfig{})
iferr!=nil{
log.Fatalf("Failed to create model: %v",err)
}
// Create a new LLM agent.
myAgent,err:=llmagent.New(llmagent.Config{
Model:model,
Name:"artifact_user_agent",
Instruction:"You are an agent that describes images.",
BeforeModelCallbacks:[]llmagent.BeforeModelCallback{
BeforeModelCallback,
},
})
iferr!=nil{
log.Fatalf("Failed to create agent: %v",err)
}
// Create a new in-memory artifact service.
artifactService:=artifact.InMemoryService()
// Create a new in-memory session service.
sessionService:=session.InMemoryService()
// Create a new runner.
r,err:=runner.New(runner.Config{
Agent:myAgent,
AppName:appName,
SessionService:sessionService,
ArtifactService:artifactService,// Provide the service instance here
})
iferr!=nil{
log.Fatalf("Failed to create runner: %v",err)
}
log.Printf("Runner created successfully: %v",r)
importcom.google.adk.agents.LlmAgent;
importcom.google.adk.runner.Runner;
importcom.google.adk.sessions.InMemorySessionService;
importcom.google.adk.artifacts.InMemoryArtifactService;
// Example: Configuring the Runner with an Artifact Service
LlmAgentmyAgent=LlmAgent.builder()
.name("artifact_user_agent")
.model("gemini-flash-latest")
.build();
InMemoryArtifactServiceartifactService=newInMemoryArtifactService();// Choose an implementation
InMemorySessionServicesessionService=newInMemorySessionService();
Runnerrunner=newRunner(myAgent,"my_artifact_app",artifactService,sessionService);// Provide the service instance here
// Now, contexts within runs managed by this runner can use artifact methods
funconfigureRunnerExample(){
valmyAgent=
LlmAgent(name="artifact_user_agent",model=Gemini(name="gemini-flash-latest"))
valartifactService=InMemoryArtifactService()
valsessionService=InMemorySessionService()
valrunner=
InMemoryRunner(
agent=myAgent,
appName="my_artifact_app",
sessionService=sessionService,
artifactService=artifactService,
)
}

Artifact Data

  • Standard Representation: Artifact content is universally represented using the google.genai.types.Part object, the same structure used for parts of LLM messages.

  • Key Attribute (inline_data): For artifacts, the most relevant attribute is inline_data, which is a google.genai.types.Blob object containing:

    • data (bytes): The raw binary content of the artifact.
    • mime_type (str): A standard MIME type string (e.g., 'application/pdf', 'image/png', 'audio/mpeg') describing the nature of the binary data. This is crucial for correct interpretation when loading the artifact.
importgoogle.genai.typesastypes
# Example: Creating an artifact Part from raw bytes
pdf_bytes = b'%PDF-1.4...' # Your raw PDF data
pdf_mime_type = "application/pdf"
# Using the constructor
pdf_artifact_py = types.Part(
 inline_data=types.Blob(data=pdf_bytes, mime_type=pdf_mime_type)
)
# Using the convenience class method (equivalent)
pdf_artifact_alt_py = types.Part.from_bytes(data=pdf_bytes, mime_type=pdf_mime_type)
print(f"Created Python artifact with MIME type: {pdf_artifact_py.inline_data.mime_type}")
import{createPartFromBase64,typePart}from'@google/genai';
// Example: Creating an artifact Part from raw bytes.
constpdfBytes=newUint8Array([0x25,0x50,0x44,0x46,0x2d,0x31,0x2e,0x34]);
constpdfMimeType='application/pdf';
// Using Buffer.from(bytes).toString('base64') for Node.js environments.
constpdfArtifact:Part=createPartFromBase64(
Buffer.from(pdfBytes).toString('base64'),
pdfMimeType,
);
console.log(`Created TypeScript artifact with MIME Type: ${pdfArtifact.inlineData?.mimeType}`);
import(
"log"
"os"
"google.golang.org/genai"
)
// Load imageBytes from a file
imageBytes,err:=os.ReadFile("image.png")
iferr!=nil{
log.Fatalf("Failed to read image file: %v",err)
}
// genai.NewPartFromBytes is a convenience function that is a shorthand for
// creating a &genai.Part with the InlineData field populated.
// Create a new artifact from the image data.
imageArtifact:=genai.NewPartFromBytes([]byte(imageBytes),"image/png")
log.Printf("Artifact MIME Type: %s",imageArtifact.InlineData.MIMEType)
importcom.google.genai.types.Blob;
importcom.google.genai.types.Part;
importjava.nio.charset.StandardCharsets;
publicclass ArtifactDataExample{
publicstaticvoidmain(String[]args){
// Example: Creating an artifact Part from raw bytes
byte[]pdfBytes="%PDF-1.4...".getBytes(StandardCharsets.UTF_8);// Your raw PDF data
StringpdfMimeType="application/pdf";
// Using the Part.fromBlob() constructor with a Blob
BlobpdfBlob=Blob.builder()
.data(pdfBytes)
.mimeType(pdfMimeType)
.build();
PartpdfArtifactJava=Part.builder().inlineData(pdfBlob).build();
// Using the convenience static method Part.fromBytes() (equivalent)
PartpdfArtifactAltJava=Part.fromBytes(pdfBytes,pdfMimeType);
// Accessing mimeType, note the use of Optional
StringmimeType=pdfArtifactJava.inlineData()
.flatMap(Blob::mimeType)
.orElse("unknown");
System.out.println("Created Java artifact with MIME type: "+mimeType);
// Accessing data
byte[]data=pdfArtifactJava.inlineData()
.flatMap(Blob::data)
.orElse(newbyte[0]);
System.out.println("Java artifact data (first 10 bytes): "
+newString(data,0,Math.min(data.length,10),StandardCharsets.UTF_8)+"...");
}
}
funartifactDataExample(){
valpdfBytes="%PDF-1.4...".toByteArray()
valpdfMimeType="application/pdf"
valpdfArtifact=
Part(
inlineData=
Blob(
data=pdfBytes,
mimeType=pdfMimeType,
),
)
println("Created Kotlin artifact with MIME type: ${pdfArtifact.inlineData?.mimeType}")
}

Filename

  • Identifier: A simple string used to name and retrieve an artifact within its specific namespace.
  • Uniqueness: Filenames must be unique within their scope (either the session or the user namespace).
  • Best Practice: Use descriptive names, potentially including file extensions (e.g., "monthly_report.pdf", "user_avatar.jpg"), although the extension itself doesn't dictate behavior – the mime_type does.

Versioning

  • Automatic Versioning: The artifact service automatically handles versioning. When you call save_artifact, the service determines the next available version number (typically starting from 0 and incrementing) for that specific filename and scope.
  • Returned by save_artifact: The save_artifact method returns the integer version number that was assigned to the newly saved artifact.
  • Retrieval:
  • load_artifact(..., version=None) (default): Retrieves the latest available version of the artifact.
  • load_artifact(..., version=N): Retrieves the specific version N.
  • Listing Versions: The list_versions method (on the service, not context) can be used to find all existing version numbers for an artifact.

Namespacing (Session vs. User)

  • Concept: Artifacts can be scoped either to a specific session or more broadly to a user across all their sessions within the application. This scoping is determined by the filename format and handled internally by the ArtifactService.

  • Default (Session Scope): If you use a plain filename like "report.pdf", the artifact is associated with the specific app_name, user_id, and session_id. It's only accessible within that exact session context.

  • User Scope ("user:" prefix): If you prefix the filename with "user:", like "user:profile.png", the artifact is associated only with the app_name and user_id. It can be accessed or updated from any session belonging to that user within the app.

# Example illustrating namespace difference (conceptual)
# Session-specific artifact filename
session_report_filename = "summary.txt"
# User-specific artifact filename
user_config_filename = "user:settings.json"
# When saving 'summary.txt' via context.save_artifact,
# it's tied to the current app_name, user_id, and session_id.
# When saving 'user:settings.json' via context.save_artifact,
# the ArtifactService implementation should recognize the "user:" prefix
# and scope it to app_name and user_id, making it accessible across sessions for that user.
// Example illustrating namespace difference (conceptual)
// Session-specific artifact filename
constsessionReportFilename="summary.txt";
// User-specific artifact filename
constuserConfigFilename="user:settings.json";
// When saving 'summary.txt' via context.saveArtifact, it's tied to the current appName, userId, and sessionId.
// When saving 'user:settings.json' via context.saveArtifact, the ArtifactService implementation recognizes the "user:" prefix and scopes it to appName and userId, making it accessible across sessions for that user.
import(
"log"
)
// Note: Namespacing is only supported when using the GCS ArtifactService implementation.
// A session-scoped artifact is only available within the current session.
sessionReportFilename:="summary.txt"
// A user-scoped artifact is available across all sessions for the current user.
userConfigFilename:="user:settings.json"
// When saving 'summary.txt' via ctx.Artifacts().Save,
// it's tied to the current app_name, user_id, and session_id.
// ctx.Artifacts().Save(sessionReportFilename, *artifact);
// When saving 'user:settings.json' via ctx.Artifacts().Save,
// the ArtifactService implementation should recognize the "user:" prefix
// and scope it to app_name and user_id, making it accessible across sessions for that user.
// ctx.Artifacts().Save(userConfigFilename, *artifact);
// Example illustrating namespace difference (conceptual)
// Session-specific artifact filename
StringsessionReportFilename="summary.txt";
// User-specific artifact filename
StringuserConfigFilename="user:settings.json";// The "user:" prefix is key
// When saving 'summary.txt' via context.save_artifact,
// it's tied to the current app_name, user_id, and session_id.
// artifactService.saveArtifact(appName, userId, sessionId1, sessionReportFilename, someData);
// When saving 'user:settings.json' via context.save_artifact,
// the ArtifactService implementation should recognize the "user:" prefix
// and scope it to app_name and user_id, making it accessible across sessions for that user.
// artifactService.saveArtifact(appName, userId, sessionId1, userConfigFilename, someData);
funnamespacingExample(){
// Session-specific artifact filename
valsessionReportFilename="summary.txt"
// User-specific artifact filename
valuserConfigFilename="user:settings.json"
}

These core concepts work together to provide a flexible system for managing binary data within the ADK framework.

Interacting with Artifacts (via Context Objects)

The primary way you interact with artifacts within your agent's logic (specifically within callbacks or tools) is through methods provided by the CallbackContext and ToolContext objects. These methods abstract away the underlying storage details managed by the ArtifactService.

(Note: In TypeScript, CallbackContext and ToolContext are unified into a single Context type.)

Prerequisite: Configuring the ArtifactService

Before you can use any artifact methods via the context objects, you must provide an instance of a BaseArtifactService implementation (like InMemoryArtifactService or GcsArtifactService) when initializing your Runner.

In Python, you provide this instance when initializing your Runner.

fromgoogle.adk.runnersimport Runner
fromgoogle.adk.artifactsimport InMemoryArtifactService # Or GcsArtifactService
fromgoogle.adk.agentsimport LlmAgent
fromgoogle.adk.sessionsimport InMemorySessionService
# Your agent definition
agent = LlmAgent(name="my_agent", model="gemini-flash-latest")
# Instantiate the desired artifact service
artifact_service = InMemoryArtifactService()
# Provide it to the Runner
runner = Runner(
 agent=agent,
 app_name="artifact_app",
 session_service=InMemorySessionService(),
 artifact_service=artifact_service # Service must be provided here
)
If no artifact_service is configured in the InvocationContext (which happens if it's not passed to the Runner), calling save_artifact, load_artifact, or list_artifacts on the context objects will raise a ValueError.
import{
InMemoryArtifactService,
InMemorySessionService,
LlmAgent,
Runner,
}from'@google/adk';
// Your agent definition.
constagent=newLlmAgent({
name:'my_agent',
model:'gemini-flash-latest',
});
// Instantiate the desired artifact service.
constartifactService=newInMemoryArtifactService();
// Provide it to the Runner.
construnner=newRunner({
agent:agent,
appName:'artifact_app',
sessionService:newInMemorySessionService(),
artifactService:artifactService,
});
// If no artifactService is configured, calling artifact methods on context objects will throw an error.
In Java, if an ArtifactService instance is not available (e.g., null) when artifact operations are attempted, it would typically result in a NullPointerException or a custom error, depending on how your application is structured. Robust applications often use dependency injection frameworks to manage service lifecycles and ensure availability.
import(
"context"
"log"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/artifactservice"
"google.golang.org/adk/llm/gemini"
"google.golang.org/adk/runner"
"google.golang.org/adk/sessionservice"
"google.golang.org/genai"
)
// Create a new context.
ctx:=context.Background()
// Set the app name.
constappName="my_artifact_app"
// Create a new Gemini model.
model,err:=gemini.NewModel(ctx,"gemini-2.5-flash",&genai.ClientConfig{})
iferr!=nil{
log.Fatalf("Failed to create model: %v",err)
}
// Create a new LLM agent.
myAgent,err:=llmagent.New(llmagent.Config{
Model:model,
Name:"artifact_user_agent",
Instruction:"You are an agent that describes images.",
BeforeModelCallbacks:[]llmagent.BeforeModelCallback{
BeforeModelCallback,
},
})
iferr!=nil{
log.Fatalf("Failed to create agent: %v",err)
}
// Create a new in-memory artifact service.
artifactService:=artifact.InMemoryService()
// Create a new in-memory session service.
sessionService:=session.InMemoryService()
// Create a new runner.
r,err:=runner.New(runner.Config{
Agent:myAgent,
AppName:appName,
SessionService:sessionService,
ArtifactService:artifactService,// Provide the service instance here
})
iferr!=nil{
log.Fatalf("Failed to create runner: %v",err)
}
log.Printf("Runner created successfully: %v",r)

In Java, you would instantiate a BaseArtifactService implementation and then ensure it's accessible to the parts of your application that manage artifacts. This is often done through dependency injection or by explicitly passing the service instance.

importcom.google.adk.agents.LlmAgent;
importcom.google.adk.artifacts.InMemoryArtifactService;// Or GcsArtifactService
importcom.google.adk.runner.Runner;
importcom.google.adk.sessions.InMemorySessionService;
publicclass SampleArtifactAgent{
publicstaticvoidmain(String[]args){
// Your agent definition
LlmAgentagent=LlmAgent.builder()
.name("my_agent")
.model("gemini-flash-latest")
.build();
// Instantiate the desired artifact service
InMemoryArtifactServiceartifactService=newInMemoryArtifactService();
// Provide it to the Runner
Runnerrunner=newRunner(agent,
"APP_NAME",
artifactService,// Service must be provided here
newInMemorySessionService());
}
}

In Kotlin, you provide this instance when initializing your Runner.

funconfigureRunnerExample(){
valmyAgent=
LlmAgent(name="artifact_user_agent",model=Gemini(name="gemini-flash-latest"))
valartifactService=InMemoryArtifactService()
valsessionService=InMemorySessionService()
valrunner=
InMemoryRunner(
agent=myAgent,
appName="my_artifact_app",
sessionService=sessionService,
artifactService=artifactService,
)
}
If no artifactService is configured, calling saveArtifact, loadArtifact, or listArtifacts on the context objects will throw an exception.

Accessing Methods

The artifact interaction methods are available directly on instances of CallbackContext (passed to agent and model callbacks) and ToolContext (passed to tool callbacks) in Python, Go, and Java and available on the unified Context in TypeScript.

Saving Artifacts

  • Code Example:

    importgoogle.genai.typesastypes
    fromgoogle.adk.agents.callback_contextimport CallbackContext # Or ToolContext
    async defsave_generated_report_py(context: CallbackContext, report_bytes: bytes):
    """Saves generated PDF report bytes as an artifact."""
     report_artifact = types.Part.from_bytes(
     data=report_bytes,
     mime_type="application/pdf"
     )
     filename = "generated_report.pdf"
     try:
     version = await context.save_artifact(filename=filename, artifact=report_artifact)
     print(f"Successfully saved Python artifact '{filename}' as version {version}.")
     # The event generated after this callback will contain:
     # event.actions.artifact_delta == {"generated_report.pdf": version}
     except ValueError as e:
     print(f"Error saving Python artifact: {e}. Is ArtifactService configured in Runner?")
     except Exception as e:
     # Handle potential storage errors (e.g., GCS permissions)
     print(f"An unexpected error occurred during Python artifact save: {e}")
    # --- Example Usage Concept (Python) ---
    # async def main_py():
    # callback_context: CallbackContext = ... # obtain context
    # report_data = b'...' # Assume this holds the PDF bytes
    # await save_generated_report_py(callback_context, report_data)
    
    import{Context}from'@google/adk';
    import{createPartFromBase64,typePart}from'@google/genai';
    asyncfunctionsaveGeneratedReport(context:Context,reportBytes:Uint8Array):Promise<void>{
    /** Saves generated PDF report bytes as an artifact. */
    constreportArtifact:Part=createPartFromBase64(
    Buffer.from(reportBytes).toString('base64'),
    'application/pdf',
    );
    constfilename='generated_report.pdf';
    try{
    constversion=awaitcontext.saveArtifact(filename,reportArtifact);
    console.log(`Successfully saved TypeScript artifact '${filename}' as version ${version}.`);
    }catch(e:any){
    console.error(
    `Error saving TypeScript artifact: ${e.message}. Is ArtifactService configured in Runner?`,
    );
    }
    }
    
    import(
    "log"
    "google.golang.org/adk/agent"
    "google.golang.org/adk/llm"
    "google.golang.org/genai"
    )
    // saveReportCallback is a BeforeModel callback that saves a report from session state.
    funcsaveReportCallback(ctxagent.CallbackContext,req*model.LLMRequest)(*model.LLMResponse,error){
    // Get the report data from the session state.
    reportData,err:=ctx.State().Get("report_bytes")
    iferr!=nil{
    log.Printf("No report data found in session state: %v",err)
    returnnil,nil// No report to save, continue normally.
    }
    // Check if the report data is in the expected format.
    reportBytes,ok:=reportData.([]byte)
    if!ok{
    log.Printf("Report data in session state was not in the expected byte format.")
    returnnil,nil
    }
    // Create a new artifact with the report data.
    reportArtifact:=&genai.Part{
    InlineData:&genai.Blob{
    MIMEType:"application/pdf",
    Data:reportBytes,
    },
    }
    // Set the filename for the artifact.
    filename:="generated_report.pdf"
    // Save the artifact to the artifact service.
    _,err=ctx.Artifacts().Save(ctx,filename,reportArtifact)
    iferr!=nil{
    log.Printf("An unexpected error occurred during Go artifact save: %v",err)
    // Depending on requirements, you might want to return an error to the user.
    returnnil,nil
    }
    log.Printf("Successfully saved Go artifact '%s'.",filename)
    // Return nil to continue to the next callback or the model.
    returnnil,nil
    }
    
    importcom.google.adk.agents.CallbackContext;
    importcom.google.adk.artifacts.BaseArtifactService;
    importcom.google.adk.artifacts.InMemoryArtifactService;
    importcom.google.genai.types.Part;
    importjava.nio.charset.StandardCharsets;
    publicclass SaveArtifactExample{
    publicvoidsaveGeneratedReport(CallbackContextcallbackContext,byte[]reportBytes){
    // Saves generated PDF report bytes as an artifact.
    PartreportArtifact=Part.fromBytes(reportBytes,"application/pdf");
    Stringfilename="generatedReport.pdf";
    callbackContext.saveArtifact(filename,reportArtifact);
    System.out.println("Successfully saved Java artifact '"+filename);
    // The event generated after this callback will contain:
    // event().actions().artifactDelta == {"generated_report.pdf": version}
    }
    // --- Example Usage Concept (Java) ---
    publicstaticvoidmain(String[]args){
    BaseArtifactServiceservice=newInMemoryArtifactService();// Or GcsArtifactService
    SaveArtifactExamplemyTool=newSaveArtifactExample();
    byte[]reportData="...".getBytes(StandardCharsets.UTF_8);// PDF bytes
    CallbackContextcallbackContext;// ... obtain callback context from your app
    myTool.saveGeneratedReport(callbackContext,reportData);
    // Due to async nature, in a real app, ensure program waits or handles completion.
    }
    }
    

    In Kotlin, you access the ArtifactService from the ToolContext (or CallbackContext via invocationContext) to save an artifact.

    suspendfunsaveGeneratedReport(
    context:ToolContext,
    reportBytes:ByteArray,
    ){
    valreportArtifact=
    Part(
    inlineData=
    Blob(
    data=reportBytes,
    mimeType="application/pdf",
    ),
    )
    valfilename="generated_report.pdf"
    valservice=context.invocationContext.artifactService
    if(service!=null){
    valversion=
    service.saveArtifact(
    context.invocationContext.session.key,
    filename,
    reportArtifact,
    )
    println("Successfully saved Kotlin artifact '$filename' as version $version.")
    }else{
    println("Artifact service not available.")
    }
    }
    

Loading Artifacts

  • Code Example:

    importgoogle.genai.typesastypes
    fromgoogle.adk.agents.callback_contextimport CallbackContext # Or ToolContext
    async defprocess_latest_report_py(context: CallbackContext):
    """Loads the latest report artifact and processes its data."""
     filename = "generated_report.pdf"
     try:
     # Load the latest version
     report_artifact = await context.load_artifact(filename=filename)
     if report_artifact and report_artifact.inline_data:
     print(f"Successfully loaded latest Python artifact '{filename}'.")
     print(f"MIME Type: {report_artifact.inline_data.mime_type}")
     # Process the report_artifact.inline_data.data (bytes)
     pdf_bytes = report_artifact.inline_data.data
     print(f"Report size: {len(pdf_bytes)} bytes.")
     # ... further processing ...
     else:
     print(f"Python artifact '{filename}' not found.")
     # Example: Load a specific version (if version 0 exists)
     # specific_version_artifact = await context.load_artifact(filename=filename, version=0)
     # if specific_version_artifact:
     # print(f"Loaded version 0 of '{filename}'.")
     except ValueError as e:
     print(f"Error loading Python artifact: {e}. Is ArtifactService configured?")
     except Exception as e:
     # Handle potential storage errors
     print(f"An unexpected error occurred during Python artifact load: {e}")
    # --- Example Usage Concept (Python) ---
    # async def main_py():
    # callback_context: CallbackContext = ... # obtain context
    # await process_latest_report_py(callback_context)
    
    import{Context}from'@google/adk';
    asyncfunctionprocessLatestReport(context:Context):Promise<void>{
    /** Loads the latest report artifact and processes its data. */
    constfilename='generated_report.pdf';
    try{
    // Load the latest version
    constreportArtifact=awaitcontext.loadArtifact(filename);
    if(reportArtifact?.inlineData){
    console.log(`Successfully loaded latest TypeScript artifact '${filename}'.`);
    console.log(`MIME Type: ${reportArtifact.inlineData.mimeType}`);
    // Process the reportArtifact.inlineData.data (base64 string)
    constpdfData=Buffer.from(reportArtifact.inlineData.data||'','base64');
    console.log(`Report size: ${pdfData.length} bytes.`);
    // ... further processing ...
    }else{
    console.log(`TypeScript artifact '${filename}' not found.`);
    }
    }catch(e:any){
    console.error(
    `Error loading TypeScript artifact: ${e.message}. Is ArtifactService configured?`,
    );
    }
    }
    
    import(
    "log"
    "google.golang.org/adk/agent"
    "google.golang.org/adk/llm"
    )
    // loadArtifactsCallback is a BeforeModel callback that loads a specific artifact
    // and adds its content to the LLM request.
    funcloadArtifactsCallback(ctxagent.CallbackContext,req*model.LLMRequest)(*model.LLMResponse,error){
    log.Println("[Callback] loadArtifactsCallback triggered.")
    // In a real app, you would parse the user's request to find a filename.
    // For this example, we'll hardcode a filename to demonstrate.
    constfilenameToLoad="generated_report.pdf"
    // Load the artifact from the artifact service.
    loadedPartResponse,err:=ctx.Artifacts().Load(ctx,filenameToLoad)
    iferr!=nil{
    log.Printf("Callback could not load artifact '%s': %v",filenameToLoad,err)
    returnnil,nil// File not found or error, continue to model.
    }
    loadedPart:=loadedPartResponse.Part
    log.Printf("Callback successfully loaded artifact '%s'.",filenameToLoad)
    // Ensure there's at least one content in the request to append to.
    iflen(req.Contents)==0{
    req.Contents=[]*genai.Content{{Parts:[]*genai.Part{
    genai.NewPartFromText("SYSTEM: The following file is provided for context:\n"),
    }}}
    }
    // Add the loaded artifact to the request for the model.
    lastContent:=req.Contents[len(req.Contents)-1]
    lastContent.Parts=append(lastContent.Parts,loadedPart)
    log.Printf("Added artifact '%s' to LLM request.",filenameToLoad)
    // Return nil to continue to the next callback or the model.
    returnnil,nil// Continue to next callback or LLM call
    }
    
    importcom.google.adk.artifacts.BaseArtifactService;
    importcom.google.genai.types.Part;
    importio.reactivex.rxjava3.core.MaybeObserver;
    importio.reactivex.rxjava3.disposables.Disposable;
    importjava.util.Optional;
    publicclass MyArtifactLoaderService{
    privatefinalBaseArtifactServiceartifactService;
    privatefinalStringappName;
    publicMyArtifactLoaderService(BaseArtifactServiceartifactService,StringappName){
    this.artifactService=artifactService;
    this.appName=appName;
    }
    publicvoidprocessLatestReportJava(StringuserId,StringsessionId,Stringfilename){
    // Load the latest version by passing Optional.empty() for the version
    artifactService
    .loadArtifact(appName,userId,sessionId,filename,Optional.empty())
    .subscribe(
    newMaybeObserver<Part>(){
    @Override
    publicvoidonSubscribe(Disposabled){
    // Optional: handle subscription
    }
    @Override
    publicvoidonSuccess(PartreportArtifact){
    System.out.println(
    "Successfully loaded latest Java artifact '"+filename+"'.");
    reportArtifact
    .inlineData()
    .ifPresent(
    blob->{
    System.out.println(
    "MIME Type: "+blob.mimeType().orElse("N/A"));
    byte[]pdfBytes=blob.data().orElse(newbyte[0]);
    System.out.println("Report size: "+pdfBytes.length+" bytes.");
    // ... further processing of pdfBytes ...
    });
    }
    @Override
    publicvoidonError(Throwablee){
    // Handle potential storage errors or other exceptions
    System.err.println(
    "An error occurred during Java artifact load for '"
    +filename
    +"': "
    +e.getMessage());
    }
    @Override
    publicvoidonComplete(){
    // Called if the artifact (latest version) is not found
    System.out.println("Java artifact '"+filename+"' not found.");
    }
    });
    // Example: Load a specific version (e.g., version 0)
    /*
     artifactService.loadArtifact(appName, userId, sessionId, filename, Optional.of(0))
     .subscribe(part -> {
     System.out.println("Loaded version 0 of Java artifact '" + filename + "'.");
     }, throwable -> {
     System.err.println("Error loading version 0 of '" + filename + "': " + throwable.getMessage());
     }, () -> {
     System.out.println("Version 0 of Java artifact '" + filename + "' not found.");
     });
     */
    }
    // --- Example Usage Concept (Java) ---
    publicstaticvoidmain(String[]args){
    // BaseArtifactService service = new InMemoryArtifactService(); // Or GcsArtifactService
    // MyArtifactLoaderService loader = new MyArtifactLoaderService(service, "myJavaApp");
    // loader.processLatestReportJava("user123", "sessionABC", "java_report.pdf");
    // Due to async nature, in a real app, ensure program waits or handles completion.
    }
    }
    

    In Kotlin, you can load an artifact directly from the ToolContext (or CallbackContext) using context.loadArtifact(name).

    suspendfunprocessLatestReport(context:ToolContext){
    valfilename="generated_report.pdf"
    valreportArtifact=context.loadArtifact(filename)
    if(reportArtifact!=null&&reportArtifact.inlineData!=null){
    println("Successfully loaded latest Kotlin artifact '$filename'.")
    println("MIME Type: ${reportArtifact.inlineData?.mimeType}")
    valpdfBytes=reportArtifact.inlineData?.data
    println("Report size: ${pdfBytes?.size} bytes.")
    }else{
    println("Kotlin artifact '$filename' not found.")
    }
    }
    

Using LoadArtifactsTool

You can add LoadArtifactsTool when the model should decide which available artifacts to load before answering. This is useful when users ask follow-up questions about uploaded files or large generated outputs that are stored as artifacts instead of kept in the conversation context.

LoadArtifactsTool lists available artifacts in the model instructions. When the model calls the load_artifacts tool, ADK temporarily appends the selected artifact contents to that request so the model can answer with the file content in context. The loaded artifact content is not permanently saved back into the session history, so the model should call the tool again when it needs the same artifact in a later turn.

fromgoogle.adk.agentsimport LlmAgent
fromgoogle.adk.tools.load_artifacts_toolimport LoadArtifactsTool
root_agent = LlmAgent(
 name="artifact_reader",
 model="gemini-flash-latest",
 instruction=(
 "Answer questions about available user files. "
 "Call load_artifacts before answering when you need file contents."
 ),
 tools=[
 LoadArtifactsTool(),
 ],
)

Make sure the Runner for this agent is configured with an artifact_service; otherwise artifact listing and loading will fail. If your artifacts need human-readable summaries, subclass LoadArtifactsTool and customize its request instructions before loading the selected artifact contents.

import(
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/tool"
"google.golang.org/adk/tool/loadartifactstool"
)
agent,err:=llmagent.New(llmagent.Config{
Name:"artifact_reader",
Model:model,
Instruction:"Answer questions about available user files. "+
"When user asks about artifacts, load them and describe them.",
Tools:[]tool.Tool{
loadartifactstool.New(),
},
})

Make sure the runner.Config for this agent includes an ArtifactService; otherwise artifact listing and loading will fail.

funloadArtifactsToolExample(){
valrootAgent=
LlmAgent(
name="artifact_reader",
model=Gemini(name="gemini-flash-latest"),
instruction=
Instruction(
"Answer questions about available user files. "+
"Call load_artifacts before answering when you need file contents.",
),
tools=listOf(LoadArtifactsTool()),
)
}

Make sure the Runner for this agent is configured with an artifactService; otherwise artifact listing and loading will fail.

Listing Artifact Filenames

  • Code Example:

    fromgoogle.adk.tools.tool_contextimport ToolContext
    deflist_user_files_py(tool_context: ToolContext) -> str:
    """Tool to list available artifacts for the user."""
     try:
     available_files = await tool_context.list_artifacts()
     if not available_files:
     return "You have no saved artifacts."
     else:
     # Format the list for the user/LLM
     file_list_str = "\n".join([f"- {fname}" for fname in available_files])
     return f"Here are your available Python artifacts:\n{file_list_str}"
     except ValueError as e:
     print(f"Error listing Python artifacts: {e}. Is ArtifactService configured?")
     return "Error: Could not list Python artifacts."
     except Exception as e:
     print(f"An unexpected error occurred during Python artifact list: {e}")
     return "Error: An unexpected error occurred while listing Python artifacts."
    # This function would typically be wrapped in a FunctionTool
    # from google.adk.tools import FunctionTool
    # list_files_tool = FunctionTool(func=list_user_files_py)
    
    import{Context}from'@google/adk';
    asyncfunctionlistUserFiles(context:Context):Promise<string>{
    /** Tool to list available artifacts for the user. */
    try{
    constavailableFiles=awaitcontext.listArtifacts();
    if(!availableFiles||availableFiles.length===0){
    return'You have no saved artifacts.';
    }else{
    // Format the list for the user/LLM
    constfileListStr=availableFiles.map((fname)=>`- ${fname}`).join('\n');
    return`Here are your available TypeScript artifacts:\n${fileListStr}`;
    }
    }catch(e:any){
    console.error(
    `Error listing TypeScript artifacts: ${e.message}. Is ArtifactService configured?`,
    );
    return'Error: Could not list TypeScript artifacts.';
    }
    }
    
    import(
    "fmt"
    "log"
    "strings"
    "google.golang.org/adk/agent"
    "google.golang.org/adk/llm"
    "google.golang.org/genai"
    )
    // listUserFilesCallback is a BeforeModel callback that lists available artifacts
    // and adds the list as context to the LLM request.
    funclistUserFilesCallback(ctxagent.CallbackContext,req*model.LLMRequest)(*model.LLMResponse,error){
    log.Println("[Callback] listUserFilesCallback triggered.")
    // List the available artifacts from the artifact service.
    listResponse,err:=ctx.Artifacts().List(ctx)
    iferr!=nil{
    log.Printf("An unexpected error occurred during Go artifact list: %v",err)
    returnnil,nil// Continue, but log the error.
    }
    availableFiles:=listResponse.FileNames
    log.Printf("Found %d available files.",len(availableFiles))
    // If there are available files, add them to the LLM request.
    iflen(availableFiles)>0{
    varfileListStrstrings.Builder
    fileListStr.WriteString("SYSTEM: The following files are available:\n")
    for_,fname:=rangeavailableFiles{
    fileListStr.WriteString(fmt.Sprintf("- %s\n",fname))
    }
    // Prepend this information to the user's request for the model.
    iflen(req.Contents)>0{
    lastContent:=req.Contents[len(req.Contents)-1]
    iflen(lastContent.Parts)>0{
    fileListStr.WriteString("\n")// Add a newline for separation.
    lastContent.Parts[0]=genai.NewPartFromText(fileListStr.String()+lastContent.Parts[0].Text)
    log.Println("Added file list to LLM request context.")
    }
    }
    log.Printf("Available files:\n%s",fileListStr.String())
    }else{
    log.Println("No available files found to list.")
    }
    // Return nil to continue to the next callback or the model.
    returnnil,nil// Continue to next callback or LLM call
    }
    
    importcom.google.adk.artifacts.BaseArtifactService;
    importcom.google.adk.artifacts.ListArtifactsResponse;
    importcom.google.common.collect.ImmutableList;
    importio.reactivex.rxjava3.core.SingleObserver;
    importio.reactivex.rxjava3.disposables.Disposable;
    publicclass MyArtifactListerService{
    privatefinalBaseArtifactServiceartifactService;
    privatefinalStringappName;
    publicMyArtifactListerService(BaseArtifactServiceartifactService,StringappName){
    this.artifactService=artifactService;
    this.appName=appName;
    }
    // Example method that might be called by a tool or agent logic
    publicvoidlistUserFilesJava(StringuserId,StringsessionId){
    artifactService
    .listArtifactKeys(appName,userId,sessionId)
    .subscribe(
    newSingleObserver<ListArtifactsResponse>(){
    @Override
    publicvoidonSubscribe(Disposabled){
    // Optional: handle subscription
    }
    @Override
    publicvoidonSuccess(ListArtifactsResponseresponse){
    ImmutableList<String>availableFiles=response.filenames();
    if(availableFiles.isEmpty()){
    System.out.println(
    "User "
    +userId
    +" in session "
    +sessionId
    +" has no saved Java artifacts.");
    }else{
    StringBuilderfileListStr=
    newStringBuilder(
    "Here are the available Java artifacts for user "
    +userId
    +" in session "
    +sessionId
    +":\n");
    for(Stringfname:availableFiles){
    fileListStr.append("- ").append(fname).append("\n");
    }
    System.out.println(fileListStr.toString());
    }
    }
    @Override
    publicvoidonError(Throwablee){
    System.err.println(
    "Error listing Java artifacts for user "
    +userId
    +" in session "
    +sessionId
    +": "
    +e.getMessage());
    // In a real application, you might return an error message to the user/LLM
    }
    });
    }
    // --- Example Usage Concept (Java) ---
    publicstaticvoidmain(String[]args){
    // BaseArtifactService service = new InMemoryArtifactService(); // Or GcsArtifactService
    // MyArtifactListerService lister = new MyArtifactListerService(service, "myJavaApp");
    // lister.listUserFilesJava("user123", "sessionABC");
    // Due to async nature, in a real app, ensure program waits or handles completion.
    }
    }
    
suspendfunlistUserFiles(context:ToolContext):String{
valavailableFiles=context.listArtifacts()
if(availableFiles.isEmpty()){
return"You have no saved artifacts."
}else{
valfileListStr=availableFiles.joinToString("\n"){"- $it"}
return"Here are your available Kotlin artifacts:\n$fileListStr"
}
}

These methods for saving, loading, and listing provide a convenient and consistent way to manage binary data persistence within ADK, whether using Python's context objects or directly interacting with the BaseArtifactService in Java, regardless of the chosen backend storage implementation.

Available Implementations

ADK provides concrete implementations of the BaseArtifactService interface, offering different storage backends suitable for various development stages and deployment needs. These implementations handle the details of storing, versioning, and retrieving artifact data based on the app_name, user_id, session_id, and filename (including the user: namespace prefix).

InMemoryArtifactService

  • Storage Mechanism:
    • Python: Uses a Python dictionary (self.artifacts) held in the application's memory. The dictionary keys represent the artifact path, and the values are lists of types.Part, where each list element is a version.
    • Java: Uses nested HashMap instances (private final Map<String, Map<String, Map<String, Map<String, List<Part>>>>> artifacts;) held in memory. The keys at each level are appName, userId, sessionId, and filename respectively. The innermost List<Part> stores the versions of the artifact, where the list index corresponds to the version number.
  • Key Features:
    • Simplicity: Requires no external setup or dependencies beyond the core ADK library.
    • Speed: Operations are typically very fast as they involve in-memory map/dictionary lookups and list manipulations.
    • Ephemeral: All stored artifacts are lost when the application process terminates. Data does not persist between application restarts.
  • Use Cases:
    • Ideal for local development and testing where persistence is not required.
    • Suitable for short-lived demonstrations or scenarios where artifact data is purely temporary within a single run of the application.
  • Instantiation:

    fromgoogle.adk.artifactsimport InMemoryArtifactService
    # Simply instantiate the class
    in_memory_service_py = InMemoryArtifactService()
    # Then pass it to the Runner
    # runner = Runner(..., artifact_service=in_memory_service_py)
    
    import{InMemoryArtifactService}from'@google/adk';
    // Simply instantiate the class
    constinMemoryService=newInMemoryArtifactService();
    // This instance would then be provided to your Runner.
    // const runner = new Runner({
    // /* other services */,
    // artifactService: inMemoryService
    // });
    
    import(
    "google.golang.org/adk/artifactservice"
    )
    // Simply instantiate the service
    artifactService:=artifact.InMemoryService()
    log.Printf("InMemoryArtifactService (Go) instantiated: %T",artifactService)
    // Use the service in your runner
    // r, _ := runner.New(runner.Config{
    // Agent: agent,
    // AppName: "my_app",
    // SessionService: sessionService,
    // ArtifactService: artifactService,
    // })
    
    importcom.google.adk.artifacts.BaseArtifactService;
    importcom.google.adk.artifacts.InMemoryArtifactService;
    publicclass InMemoryServiceSetup{
    publicstaticvoidmain(String[]args){
    // Simply instantiate the class
    BaseArtifactServiceinMemoryServiceJava=newInMemoryArtifactService();
    System.out.println("InMemoryArtifactService (Java) instantiated: "+inMemoryServiceJava.getClass().getName());
    // This instance would then be provided to your Runner.
    // Runner runner = new Runner(
    // /* other services */,
    // inMemoryServiceJava
    // );
    }
    }
    
    funinMemoryServiceExample(){
    valinMemoryService=InMemoryArtifactService()
    }
    

    GcsArtifactService

  • Storage Mechanism: Leverages Google Cloud Storage (GCS) for persistent artifact storage. Each version of an artifact is stored as a separate object (blob) within a specified GCS bucket.

  • Object Naming Convention: It constructs GCS object names (blob names) using a hierarchical path structure.
  • Key Features:
    • Persistence: Artifacts stored in GCS persist across application restarts and deployments.
    • Scalability: Leverages the scalability and durability of Google Cloud Storage.
    • Versioning: Explicitly stores each version as a distinct GCS object. The saveArtifact method in GcsArtifactService.
    • Permissions Required: The application environment needs appropriate credentials (e.g., Application Default Credentials) and IAM permissions to read from and write to the specified GCS bucket.
  • Use Cases:
    • Production environments requiring persistent artifact storage.
    • Scenarios where artifacts need to be shared across different application instances or services (by accessing the same GCS bucket).
    • Applications needing long-term storage and retrieval of user or session data.
  • Instantiation:

    fromgoogle.adk.artifactsimport GcsArtifactService
    # Specify the GCS bucket name
    gcs_bucket_name_py = "your-gcs-bucket-for-adk-artifacts" # Replace with your bucket name
    try:
     gcs_service_py = GcsArtifactService(bucket_name=gcs_bucket_name_py)
     print(f"Python GcsArtifactService initialized for bucket: {gcs_bucket_name_py}")
     # Ensure your environment has credentials to access this bucket.
     # e.g., via Application Default Credentials (ADC)
     # Then pass it to the Runner
     # runner = Runner(..., artifact_service=gcs_service_py)
    except Exception as e:
     # Catch potential errors during GCS client initialization (e.g., auth issues)
     print(f"Error initializing Python GcsArtifactService: {e}")
     # Handle the error appropriately - maybe fall back to InMemory or raise
    
    import{GcsArtifactService}from'@google/adk';
    // Specify the GCS bucket name.
    constgcsBucketName='your-gcs-bucket-for-adk-artifacts';
    try{
    constgcsService=newGcsArtifactService(gcsBucketName);
    console.log(`TypeScript GcsArtifactService initialized for bucket: ${gcsBucketName}`);
    // Ensure your environment has credentials to access this bucket.
    // e.g., via Application Default Credentials (ADC).
    // Then pass it to the Runner.
    // const runner = new Runner({..., artifactService: gcsService});
    }catch(e:any){
    // Catch potential errors during GCS client initialization (e.g., auth issues).
    console.error(`Error initializing TypeScript GcsArtifactService: ${e.message}`);
    }
    
    importcom.google.adk.artifacts.BaseArtifactService;
    importcom.google.adk.artifacts.GcsArtifactService;
    importcom.google.cloud.storage.Storage;
    importcom.google.cloud.storage.StorageOptions;
    publicclass GcsServiceSetup{
    publicstaticvoidmain(String[]args){
    // Specify the GCS bucket name
    StringgcsBucketNameJava="your-gcs-bucket-for-adk-artifacts";// Replace with your bucket name
    try{
    // Initialize the GCS Storage client.
    // This will use Application Default Credentials by default.
    // Ensure the environment is configured correctly (e.g., GOOGLE_APPLICATION_CREDENTIALS).
    StoragestorageClient=StorageOptions.getDefaultInstance().getService();
    // Instantiate the GcsArtifactService
    BaseArtifactServicegcsServiceJava=
    newGcsArtifactService(gcsBucketNameJava,storageClient);
    System.out.println(
    "Java GcsArtifactService initialized for bucket: "+gcsBucketNameJava);
    // This instance would then be provided to your Runner.
    // Runner runner = new Runner(
    // /* other services */,
    // gcsServiceJava
    // );
    }catch(Exceptione){
    // Catch potential errors during GCS client initialization (e.g., auth, permissions)
    System.err.println("Error initializing Java GcsArtifactService: "+e.getMessage());
    e.printStackTrace();
    // Handle the error appropriately
    }
    }
    }
    
    fungcsServiceExample(){
    valgcsBucketName="your-gcs-bucket-for-adk-artifacts"
    try{
    // Initialize the GCS Storage client (usually uses Application Default Credentials)
    valstorage=com.google.cloud.storage.StorageOptions.getDefaultInstance().service
    valgcsService=GcsArtifactService(bucketName=gcsBucketName,storageClient=storage)
    println("Kotlin GcsArtifactService initialized for bucket: $gcsBucketName")
    }catch(e:Exception){
    println("Error initializing Kotlin GcsArtifactService: ${e.message}")
    }
    }
    

    Choosing the appropriate ArtifactService implementation depends on your application's requirements for data persistence, scalability, and operational environment.

Best Practices

To use artifacts effectively and maintainably:

  • Choose the Right Service: Use InMemoryArtifactService for rapid prototyping, testing, and scenarios where persistence isn't needed. Use GcsArtifactService (or implement your own BaseArtifactService for other backends) for production environments requiring data persistence and scalability.
  • Meaningful Filenames: Use clear, descriptive filenames. Including relevant extensions (.pdf, .png, .wav) helps humans understand the content, even though the mime_type dictates programmatic handling. Establish conventions for temporary vs. persistent artifact names.
  • Specify Correct MIME Types: Always provide an accurate mime_type when creating the types.Part for save_artifact. This is critical for applications or tools that later load_artifact to interpret the bytes data correctly. Use standard IANA MIME types where possible.
  • Understand Versioning: Remember that load_artifact() without a specific version argument retrieves the latest version. If your logic depends on a specific historical version of an artifact, be sure to provide the integer version number when loading.
  • Use Namespacing (user:) Deliberately: Only use the "user:" prefix for filenames when the data truly belongs to the user and should be accessible across all their sessions. For data specific to a single conversation or session, use regular filenames without the prefix.
  • Error Handling:
    • Always check if an artifact_service is actually configured before calling context methods (save_artifact, load_artifact, list_artifacts) – they will raise a ValueError if the service is None.
    • Check the return value of load_artifact, as it will be None if the artifact or version doesn't exist. Don't assume it always returns a Part.
    • Be prepared to handle exceptions from the underlying storage service, especially with GcsArtifactService (e.g., google.api_core.exceptions.Forbidden for permission issues, NotFound if the bucket doesn't exist, network errors).
  • Size Considerations: Artifacts are suitable for typical file sizes, but be mindful of potential costs and performance impacts with extremely large files, especially with cloud storage. InMemoryArtifactService can consume significant memory if storing many large artifacts. Evaluate if very large data might be better handled through direct GCS links or other specialized storage solutions rather than passing entire byte arrays in-memory.
  • Cleanup Strategy: For persistent storage like GcsArtifactService, artifacts remain until explicitly deleted. If artifacts represent temporary data or have a limited lifespan, implement a strategy for cleanup. This might involve:
    • Using GCS lifecycle policies on the bucket.
    • Building specific tools or administrative functions that utilize the artifact_service.delete_artifact method (note: delete is not exposed via context objects for safety).
    • Carefully managing filenames to allow pattern-based deletion if needed.