Skip to main content
Artifacts enable agents to save, retrieve, and version files and binary data. Unlike state which stores structured data, artifacts handle documents, images, code files, and other file-based content.

BaseArtifactService

All artifact services implement this interface:
// packages/adk/src/artifacts/base-artifact-service.ts:3
export interface BaseArtifactService {
  saveArtifact(args: {
    appName: string;
    userId: string;
    sessionId: string;
    filename: string;
    artifact: Part;  // Google GenAI Part format
  }): Promise<number>;  // Returns version number
  
  loadArtifact(args: {
    appName: string;
    userId: string;
    sessionId: string;
    filename: string;
    version?: number;  // Defaults to latest
  }): Promise<Part | null>;
  
  listArtifactKeys(args: {
    appName: string;
    userId: string;
    sessionId: string;
  }): Promise<string[]>;
  
  deleteArtifact(args: {
    appName: string;
    userId: string;
    sessionId: string;
    filename: string;
  }): Promise<void>;
  
  listVersions(args: {
    appName: string;
    userId: string;
    sessionId: string;
    filename: string;
  }): Promise<number[]>;
}

Artifact Format

Artifacts use Google GenAI’s Part format:
type Part = 
  | { text: string }                           // Text content
  | { inlineData: {                            // Binary data
      mimeType: string;
      data: string;  // Base64 encoded
    }}
  | { fileData: {                              // File reference
      mimeType: string;
      fileUri: string;
    }};

Artifact Namespacing

Artifacts can be scoped to sessions or users:
Default: artifacts are tied to a specific session
await artifactService.saveArtifact({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
  filename: 'report.pdf',
  artifact: { inlineData: { data: pdfData, mimeType: 'application/pdf' } },
});
Storage path: {app}/{user}/{session}/{filename}/{version}Use for:
  • Conversation-specific files
  • Temporary outputs
  • Session artifacts

Saving Artifacts

1

Prepare Content

// Text artifact
const textArtifact = {
  text: 'Hello, world!',
};

// Binary artifact (base64 encoded)
const imageArtifact = {
  inlineData: {
    mimeType: 'image/png',
    data: Buffer.from(imageData).toString('base64'),
  },
};

// File reference
const fileRefArtifact = {
  fileData: {
    mimeType: 'application/pdf',
    fileUri: 'gs://bucket/path/to/file.pdf',
  },
};
2

Save Artifact

const version = await artifactService.saveArtifact({
  appName: session.appName,
  userId: session.userId,
  sessionId: session.id,
  filename: 'output.txt',
  artifact: textArtifact,
});

console.log('Saved as version:', version);

Versioning

Each save creates a new version:
// First save
const v0 = await artifactService.saveArtifact({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
  filename: 'document.txt',
  artifact: { text: 'Version 1' },
});
console.log('Version:', v0); // 0

// Update (new version)
const v1 = await artifactService.saveArtifact({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
  filename: 'document.txt',
  artifact: { text: 'Version 2' },
});
console.log('Version:', v1); // 1

// Load specific version
const oldVersion = await artifactService.loadArtifact({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
  filename: 'document.txt',
  version: 0,
});
console.log(oldVersion?.text); // 'Version 1'

Loading Artifacts

// Load the most recent version
const artifact = await artifactService.loadArtifact({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
  filename: 'report.pdf',
});

if (artifact) {
  console.log('MIME type:', artifact.inlineData?.mimeType);
  console.log('Data:', artifact.inlineData?.data);
}

Listing Artifacts

const files = await artifactService.listArtifactKeys({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
});

console.log('Artifacts:', files);
// ['report.pdf', 'chart.png', 'data.json', 'user:profile.jpg']

Deleting Artifacts

// Delete all versions of a file
await artifactService.deleteArtifact({
  appName: 'my-app',
  userId: 'user-123',
  sessionId: 'session-456',
  filename: 'report.pdf',
});
Deleting an artifact removes ALL versions. There is no undo.

Artifact Services

In-memory storage for development:
// packages/adk/src/artifacts/in-memory-artifact-service.ts:5
import { InMemoryArtifactService } from '@iqai/adk';

const artifacts = new InMemoryArtifactService();

const agent = await AgentBuilder
  .withModel('gpt-4')
  .withArtifactService(artifacts)
  .build();
Features:
  • Fast and simple
  • No persistence (lost on restart)
  • Automatic versioning
  • Supports user: namespace
  • Perfect for testing
Storage structure:
// Map<path, Part[]>
private artifacts: Map<string, Part[]> = new Map();

// Path format:
// Session: "app/user/session/file.txt"
// User:    "app/user/user/user:file.txt"

Using Artifacts in Tools

Tools can save and load artifacts via ToolContext:
import { BaseTool, ToolContext } from '@iqai/adk';
import { z } from 'zod';

class SaveReportTool extends BaseTool<typeof SaveReportTool.argsSchema> {
  name = 'save_report';
  description = 'Save a report to a file';
  
  static argsSchema = z.object({
    filename: z.string().describe('Output filename'),
    content: z.string().describe('Report content'),
  });
  
  async execute(
    args: z.infer<typeof SaveReportTool.argsSchema>,
    context: ToolContext
  ) {
    const { artifactService, session } = context;
    
    if (!artifactService) {
      return {
        content: 'Artifact service not configured',
        isError: true,
      };
    }
    
    // Save the artifact
    const version = await artifactService.saveArtifact({
      appName: session.appName,
      userId: session.userId,
      sessionId: session.id,
      filename: args.filename,
      artifact: { text: args.content },
    });
    
    return {
      content: `Saved ${args.filename} (version ${version})`,
    };
  }
}

class LoadReportTool extends BaseTool<typeof LoadReportTool.argsSchema> {
  name = 'load_report';
  description = 'Load a report from a file';
  
  static argsSchema = z.object({
    filename: z.string().describe('Filename to load'),
    version: z.number().optional().describe('Specific version (defaults to latest)'),
  });
  
  async execute(
    args: z.infer<typeof LoadReportTool.argsSchema>,
    context: ToolContext
  ) {
    const { artifactService, session } = context;
    
    if (!artifactService) {
      return {
        content: 'Artifact service not configured',
        isError: true,
      };
    }
    
    // Load the artifact
    const artifact = await artifactService.loadArtifact({
      appName: session.appName,
      userId: session.userId,
      sessionId: session.id,
      filename: args.filename,
      version: args.version,
    });
    
    if (!artifact) {
      return {
        content: `File ${args.filename} not found`,
        isError: true,
      };
    }
    
    return {
      content: artifact.text || 'Binary file loaded',
    };
  }
}

Complete Example

From the examples directory:
// apps/examples/src/04-persistence-and-sessions/agents/tools.ts
import { BaseTool, ToolContext } from '@iqai/adk';
import { z } from 'zod';

class SaveCounterReportTool extends BaseTool<typeof SaveCounterReportTool.argsSchema> {
  name = 'save_counter_report';
  description = 'Save counter values to a report file';
  
  static argsSchema = z.object({
    filename: z.string().describe('Report filename'),
  });
  
  async execute(
    args: z.infer<typeof SaveCounterReportTool.argsSchema>,
    context: ToolContext
  ) {
    const { artifactService, session } = context;
    
    if (!artifactService) {
      return {
        content: 'No artifact service configured',
        isError: true,
      };
    }
    
    // Generate report from state
    const counters = Object.entries(session.state)
      .filter(([key]) => !key.includes(':'))
      .map(([key, value]) => `${key}: ${value}`)
      .join('\n');
    
    const report = `Counter Report\n${'='.repeat(40)}\n${counters}`;
    
    // Save as artifact
    const version = await artifactService.saveArtifact({
      appName: session.appName,
      userId: session.userId,
      sessionId: session.id,
      filename: args.filename,
      artifact: { text: report },
    });
    
    return {
      content: `Report saved to ${args.filename} (version ${version})`,
    };
  }
}

// Usage
const { runner } = await AgentBuilder
  .withModel('gpt-4')
  .withTools(new SaveCounterReportTool())
  .withArtifactService(new InMemoryArtifactService())
  .build();

await runner.ask('Save counter report to "report.txt"');

Best Practices

  • Use descriptive filenames
  • Include file extensions
  • Use user: prefix for user-scoped files
  • Avoid special characters
  • Keep names short and readable
// Good
'invoice-2024-03.pdf'
'user:avatar.jpg'
'analysis-report.json'

// Avoid
'file!!!.txt'
'really-long-filename-with-many-words-2024-03-15-final-v2.pdf'
'../../../etc/passwd'
  • Versions start at 0
  • New saves always create new versions
  • List versions to find latest
  • Use negative indexing for recent versions
  • Clean up old versions periodically
// Get last 3 versions
const versions = await artifactService.listVersions(args);
const recent = versions.slice(-3);

// Load each
for (const v of recent) {
  const artifact = await artifactService.loadArtifact({ ...args, version: v });
}
  • Always base64 encode binary data
  • Set correct MIME types
  • Consider file size limits
  • Compress large files
  • Stream when possible
// Encode binary data
const imageBuffer = fs.readFileSync('image.png');
const artifact = {
  inlineData: {
    mimeType: 'image/png',
    data: imageBuffer.toString('base64'),
  },
};
  • Delete unused artifacts
  • Implement retention policies
  • Monitor storage costs
  • Clean up test data
  • Consider session cleanup triggers
// Clean up old sessions
async function cleanupOldArtifacts(
  artifactService: BaseArtifactService,
  appName: string,
  userId: string,
  sessionId: string
) {
  const files = await artifactService.listArtifactKeys({
    appName,
    userId,
    sessionId,
  });
  
  for (const file of files) {
    await artifactService.deleteArtifact({
      appName,
      userId,
      sessionId,
      filename: file,
    });
  }
}

Artifacts vs State

Choose the right storage:
FeatureStateArtifacts
Data TypeStructured (JSON)Files, binary
SizeSmall (KB)Any size
VersioningDelta trackingFull versioning
Use CaseFlags, counters, configDocuments, images, code
AccessDirect (session.state)Async (loadArtifact)
PersistenceWith sessionIndependent
// Use state for:
session.state.counter = 5;
session.state['user:theme'] = 'dark';
session.state.cart_items = ['item1', 'item2'];

// Use artifacts for:
await artifactService.saveArtifact({
  filename: 'report.pdf',
  artifact: { inlineData: { data: pdfData, mimeType: 'application/pdf' } },
});
Artifacts are optional. Many agents work fine without file storage. Add artifact service only when you need to save generated files.

Build docs developers (and LLMs) love