Components
Detailed look at Runner, agents, events, and services
The ADK Runtime consists of several key components that work together to provide a robust execution environment for agent applications.
Runner
The Runner is the main orchestrator and entry point for agent execution. It manages the overall invocation lifecycle and coordinates between agents and services.
Core Responsibilities
- Session Management: Initialize and manage conversation sessions
- Event Coordination: Process events from execution logic
- Service Integration: Coordinate with SessionService, ArtifactService, MemoryService
- Upstream Communication: Forward processed events to user interfaces
Runner Constructor
import { Runner, LlmAgent, InMemorySessionService } from '@iqai/adk';
const runner = new Runner({
appName: "my_application", // Application identifier
agent: rootAgent, // Root agent to execute
sessionService: sessionService, // Required: Session management
artifactService?: artifactService, // Optional: File storage
memoryService?: memoryService // Optional: Long-term memory
});
Main Methods
runAsync()
- Primary execution method:
for await (const event of runner.runAsync({
userId: "user_123",
sessionId: "session_456",
newMessage: { parts: [{ text: "Hello!" }] },
runConfig?: new RunConfig() // Optional configuration
})) {
// Process each event as it's generated
console.log(`Event from ${event.author}:`, event.content);
}
run()
- Synchronous wrapper (for testing):
// Synchronous interface for local testing
const eventGenerator = runner.run({
userId: "user_123",
sessionId: "session_456",
newMessage: { parts: [{ text: "Hello!" }] }
});
for (const event of eventGenerator) {
console.log('Sync event:', event);
}
InMemoryRunner
For quick testing and development:
import { InMemoryRunner, LlmAgent } from '@iqai/adk';
const agent = new LlmAgent({
name: "test_agent",
model: "gemini-2.5-flash",
description: "Test agent",
instruction: "You are helpful"
});
// Automatically sets up in-memory services
const runner = new InMemoryRunner(agent, {
appName: "test_app"
});
Agent Selection Logic
The Runner determines which agent should handle each query:
// Agent selection process
private _findAgentToRun(session: Session, rootAgent: BaseAgent): BaseAgent {
// 1. Check for function response continuation
const functionCallEvent = this._findFunctionCallEventIfLastEventIsFunctionResponse(session);
if (functionCallEvent?.author) {
return rootAgent.findAgent(functionCallEvent.author);
}
// 2. Look for most recent agent that can handle transfers
const nonUserEvents = session.events?.filter(e => e.author !== "user").reverse() || [];
for (const event of nonUserEvents) {
const agent = rootAgent.findSubAgent?.(event.author);
if (agent && this._isTransferableAcrossAgentTree(agent)) {
return agent;
}
}
// 3. Default to root agent
return rootAgent;
}
Agents
Agents are the core execution units that implement reasoning and decision-making logic through the async generator pattern.
BaseAgent
All agents extend from BaseAgent
:
import { BaseAgent, InvocationContext, Event } from '@iqai/adk';
class CustomAgent extends BaseAgent {
constructor(config: { name: string; description: string }) {
super(config);
}
// Core execution method - must be implemented
protected async *runAsyncImpl(
ctx: InvocationContext
): AsyncGenerator<Event, void, unknown> {
// Your agent logic here
yield new Event({
invocationId: ctx.invocationId,
author: this.name,
content: { parts: [{ text: "Hello from custom agent!" }] }
});
}
// Optional: Live mode execution
protected async *runLiveImpl(
ctx: InvocationContext
): AsyncGenerator<Event, void, unknown> {
// Live interaction logic
throw new Error("Live mode not implemented");
}
}
LlmAgent
The primary agent type for LLM-based interactions:
import { LlmAgent, FunctionTool } from '@iqai/adk';
const agent = new LlmAgent({
name: "assistant",
model: "gemini-2.5-flash",
description: "A helpful assistant",
instruction: "You are a helpful assistant",
tools: [searchTool, calculatorTool], // Optional tools
subAgents: [specialistAgent], // Optional sub-agents
maxIterations: 10, // Optional iteration limit
disallowTransferToParent: false // Optional transfer control
});
Agent Lifecycle
Agents go through a specific lifecycle during execution:
// Internal agent execution flow
private async *runAsyncInternal(parentContext: InvocationContext): AsyncGenerator<Event> {
const ctx = this.createInvocationContext(parentContext);
// 1. Before-agent callbacks
const beforeEvent = await this.handleBeforeAgentCallback(ctx);
if (beforeEvent) yield beforeEvent;
if (ctx.endInvocation) return;
// 2. Core agent logic
for await (const event of this.runAsyncImpl(ctx)) {
yield event;
}
if (ctx.endInvocation) return;
// 3. After-agent callbacks
const afterEvent = await this.handleAfterAgentCallback(ctx);
if (afterEvent) yield afterEvent;
}
Specialized Agent Types
Sequential Agent:
import { SequentialAgent } from '@iqai/adk';
const sequentialAgent = new SequentialAgent({
name: "workflow",
description: "Executes sub-agents in sequence",
subAgents: [analyzeAgent, processAgent, reportAgent]
});
Parallel Agent:
import { ParallelAgent } from '@iqai/adk';
const parallelAgent = new ParallelAgent({
name: "multi_perspective",
description: "Gets multiple perspectives simultaneously",
subAgents: [expertAgent1, expertAgent2, expertAgent3]
});
Loop Agent:
import { LoopAgent } from '@iqai/adk';
const loopAgent = new LoopAgent({
name: "iterative_solver",
description: "Iteratively refines solutions",
agent: problemSolvingAgent,
maxIterations: 5
});
Events
Events are the primary communication mechanism between components, carrying content, actions, and metadata.
Event Structure
import { Event, EventActions } from '@iqai/adk';
const event = new Event({
invocationId: "inv_123", // Unique invocation identifier
author: "agent_name", // Who generated this event
content: { // Message content
parts: [
{ text: "Hello!" },
{ functionCall: { name: "search", args: {} } }
]
},
actions: new EventActions({ // Side effects
stateDelta: { "key": "value" },
artifactDelta: { "file.txt": "content" },
transferToAgent: "specialist"
}),
branch: "agent1.agent2", // Agent hierarchy path
partial: false, // Whether event should be persisted
timestamp: Math.floor(Date.now() / 1000)
});
Event Methods
Content Analysis:
// Check if event represents final response
const isFinal = event.isFinalResponse();
// Extract function calls
const functionCalls = event.getFunctionCalls();
// Returns: [{ name: "search", args: { query: "..." } }]
// Extract function responses
const functionResponses = event.getFunctionResponses();
// Returns: [{ name: "search", response: { results: [...] } }]
// Check for trailing code execution
const hasCodeResult = event.hasTrailingCodeExecutionResult();
Event Types
User Message Events:
const userEvent = new Event({
invocationId: "inv_123",
author: "user",
content: {
parts: [{ text: "What's the weather like?" }]
}
});
Agent Response Events:
const agentEvent = new Event({
invocationId: "inv_123",
author: "weather_agent",
content: {
parts: [{ text: "It's sunny and 72°F" }]
}
});
Function Call Events:
const functionCallEvent = new Event({
invocationId: "inv_123",
author: "weather_agent",
content: {
parts: [{
functionCall: {
name: "get_weather",
args: { location: "San Francisco" }
}
}]
}
});
State Change Events:
const stateEvent = new Event({
invocationId: "inv_123",
author: "agent",
actions: new EventActions({
stateDelta: {
"user_location": "San Francisco",
"last_weather_check": new Date().toISOString()
}
})
});
Event Actions
Events can carry side effects through EventActions
:
import { EventActions } from '@iqai/adk';
const actions = new EventActions({
// State changes
stateDelta: {
"app_state.theme": "dark",
"user_state.preferences": { notifications: true }
},
// Artifact changes
artifactDelta: {
"report.pdf": "binary_content",
"config.json": JSON.stringify({ setting: "value" })
},
// Agent transfer
transferToAgent: "specialist_agent",
// Skip summarization (for debugging/verbose output)
skipSummarization: true
});
Services
Services provide backend functionality for persistence, storage, and external integrations.
SessionService
Manages conversation sessions and their persistence:
import { BaseSessionService, InMemorySessionService, DatabaseSessionService } from '@iqai/adk';
// In-memory implementation (for testing)
const sessionService = new InMemorySessionService();
// Database implementation (for production)
const sessionService = new DatabaseSessionService({
databaseUrl: "postgresql://...",
tableName: "sessions"
});
// Core operations
const session = await sessionService.createSession("app_name", "user_id");
await sessionService.appendEvent(session, event);
const retrievedSession = await sessionService.getSession("app_name", "user_id", "session_id");
ArtifactService
Handles binary data and file storage:
import { BaseArtifactService, InMemoryArtifactService, GcsArtifactService } from '@iqai/adk';
// In-memory implementation
const artifactService = new InMemoryArtifactService();
// Google Cloud Storage implementation
const artifactService = new GcsArtifactService({
bucketName: "my-artifacts-bucket",
credentials: googleCloudCredentials
});
// Artifact operations
await artifactService.saveArtifact({
appName: "my_app",
userId: "user_123",
sessionId: "session_456",
filename: "document.pdf",
artifact: { inlineData: { mimeType: "application/pdf", data: "..." } }
});
const artifact = await artifactService.loadArtifact({
appName: "my_app",
userId: "user_123",
sessionId: "session_456",
filename: "document.pdf"
});
MemoryService
Provides long-term knowledge storage and retrieval:
import { BaseMemoryService, InMemoryMemoryService, VertexAiRagMemoryService } from '@iqai/adk';
// In-memory implementation
const memoryService = new InMemoryMemoryService();
// Vertex AI RAG implementation
const memoryService = new VertexAiRagMemoryService({
projectId: "my-gcp-project",
location: "us-central1",
corpusId: "my-corpus"
});
// Memory operations
await memoryService.addSessionToMemory(session);
const memories = await memoryService.searchMemory({
appName: "my_app",
userId: "user_123",
query: "previous conversations about weather",
topK: 5
});
Service Configuration
Services are typically configured at the Runner level:
import { Runner, LlmAgent, DatabaseSessionService, GcsArtifactService, VertexAiRagMemoryService } from '@iqai/adk';
const runner = new Runner({
appName: "production_app",
agent: mainAgent,
// Production-ready services
sessionService: new DatabaseSessionService({
databaseUrl: process.env.DATABASE_URL,
tableName: "agent_sessions"
}),
artifactService: new GcsArtifactService({
bucketName: process.env.GCS_BUCKET,
credentials: googleCredentials
}),
memoryService: new VertexAiRagMemoryService({
projectId: process.env.GCP_PROJECT_ID,
location: "us-central1",
corpusId: process.env.CORPUS_ID
})
});
Component Integration
Service Discovery
Components access services through the InvocationContext:
// Within agent implementation
protected async *runAsyncImpl(ctx: InvocationContext): AsyncGenerator<Event> {
// Access session
const session = ctx.session;
// Search memory if available
if (ctx.memoryService) {
const memories = await ctx.memoryService.searchMemory({
appName: session.appName,
userId: session.userId,
query: "relevant information",
topK: 3
});
}
// Save artifacts if available
if (ctx.artifactService) {
await ctx.artifactService.saveArtifact({
appName: session.appName,
userId: session.userId,
sessionId: session.id,
filename: "output.txt",
artifact: { inlineData: { mimeType: "text/plain", data: "content" } }
});
}
}
Error Handling
Components implement graceful degradation:
// Service availability checking
protected async *runAsyncImpl(ctx: InvocationContext): AsyncGenerator<Event> {
try {
if (ctx.memoryService) {
const memories = await ctx.memoryService.searchMemory(params);
// Use memories in processing
} else {
// Continue without memory service
console.warn("Memory service not available, continuing without historical context");
}
} catch (error) {
console.error("Memory service error:", error);
// Continue execution with fallback behavior
}
}
Component Lifecycle
Components follow a consistent lifecycle pattern:
- Initialization: Components are configured and instantiated
- Service Registration: Services are registered with the Runner
- Context Creation: InvocationContext provides access to services
- Execution: Components coordinate through events and service calls
- Cleanup: Resources are properly disposed when invocations complete
Component Extensibility
The ADK Runtime is designed for extensibility. You can implement custom agents, services, and event processors by extending the base classes and following the established patterns.