Examples
Practical examples of using the Agentuity JavaScript SDK
This section provides practical examples of using the Agentuity JavaScript SDK for common use cases.
Basic Agent Implementation
Here's a complete example of a basic agent that processes JSON requests and returns JSON responses:
import { AgentHandler } from '@agentuity/sdk';
// Agent handler function
const handler: AgentHandler = async (request, response, context) => {
try {
// Get the request data
const data = await request.data.json();
const name = data.name || 'Guest';
// Log the request
context.logger.info(`Received greeting request for ${name}`);
// Return a personalized greeting
return response.json({
message: `Hello, ${name}! Welcome to Agentuity.`,
timestamp: new Date().toISOString()
});
} catch (error) {
// Handle errors
context.logger.error('Error processing request', error);
return response.json({
error: 'Failed to process request',
message: error instanceof Error ? error.message : 'Unknown error'
});
}
};
export default handler;Key-Value Storage Usage
This example demonstrates how to use the key-value storage API to store and retrieve user preferences:
import { AgentHandler } from '@agentuity/sdk';
const handler: AgentHandler = async (request, response, context) => {
const { action, userId, preferences } = await request.data.json();
switch (action) {
case 'get': {
// Retrieve user preferences
const result = await context.kv.get('user-preferences', userId);
if (!result.exists) {
return response.json({ message: 'No preferences found' });
}
// Access data using the Data interface
const userPrefs = await result.data.json();
return response.json({ preferences: userPrefs });
}
case 'set': {
// Store user preferences
await context.kv.set(
'user-preferences',
userId,
JSON.stringify(preferences),
// Optional TTL (30 days in seconds)
60 * 60 * 24 * 30
);
return response.json({ message: 'Preferences saved successfully' });
}
case 'delete': {
// Delete user preferences
await context.kv.delete('user-preferences', userId);
return response.json({ message: 'Preferences deleted successfully' });
}
default:
return response.json({ error: 'Invalid action' });
}
};
export default handler;Vector Storage Usage
This example demonstrates how to use the vector storage API for semantic search:
import { AgentHandler } from '@agentuity/sdk';
const handler: AgentHandler = async (request, response, context) => {
const { action, query, products } = await request.data.json();
switch (action) {
case 'index': {
// Index products in vector storage
if (!Array.isArray(products) || products.length === 0) {
return response.json({ error: 'No products to index' });
}
// Validate that all products have IDs
if (products.some(p => !p.id)) {
return response.json({ error: 'All products must have a non-empty id' });
}
// Prepare documents for vector storage with string normalization
const documents = products.map(product => ({
key: String(product.id),
document: product.description,
metadata: {
id: product.id,
name: product.name,
price: product.price,
category: product.category
}
}));
// Store in vector database
const ids = await context.vector.upsert('products', ...documents);
return response.json({
message: `Indexed ${ids.length} products successfully`,
ids
});
}
case 'search': {
// Search for products
if (!query) {
return response.json({ error: 'Query is required' });
}
// Search vector storage
const results = await context.vector.search('products', {
query,
limit: 5,
similarity: 0.7
});
// Format results with id, key, and similarity
const formattedResults = results.map(result => ({
id: result.id,
key: result.key,
...result.metadata,
similarity: result.similarity
}));
return response.json({
results: formattedResults,
count: formattedResults.length
});
}
case 'delete': {
// Delete products from vector storage
if (!products || !Array.isArray(products) || products.length === 0) {
return response.json({ error: 'No product IDs to delete' });
}
// Support both single and bulk deletion
const productIds = products.map(p => p.id || p);
const count = await context.vector.delete('products', ...productIds);
return response.json({
message: `Deleted ${count} product(s) successfully`,
deletedCount: count
});
}
default:
return response.json({ error: 'Invalid action' });
}
};
export default handler;Agent Communication Example
This example demonstrates how agents can communicate with each other:
import { AgentHandler } from '@agentuity/sdk';
const handler: AgentHandler = async (request, response, context) => {
const { task, data } = await request.data.json();
// Log the incoming request
context.logger.info(`Received task: ${task}`);
switch (task) {
case 'process-data': {
// Get a reference to the data processing agent
const processingAgent = await context.getAgent({
name: 'data-processor'
});
// Invoke the data processing agent
const processingResult = await processingAgent.run(
{ data },
'application/json'
);
// Get a reference to the notification agent
const notificationAgent = await context.getAgent({
name: 'notification-sender'
});
// Invoke the notification agent with the processing result
await notificationAgent.run(
{
message: 'Data processing completed',
result: processingResult
},
'application/json'
);
return response.json({
message: 'Task orchestrated successfully',
processingResult
});
}
case 'handoff': {
// Handoff to another agent
return response.handoff(
{ name: 'data-processor' },
{ data },
'application/json',
{ source: 'orchestrator' }
);
}
default:
return response.json({ error: 'Unknown task' });
}
};
export default handler;Error Handling Example
This example demonstrates comprehensive error handling in an agent:
import { AgentHandler } from '@agentuity/sdk';
// Custom error classes
class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}
class ResourceNotFoundError extends Error {
constructor(resourceId: string) {
super(`Resource not found: ${resourceId}`);
this.name = 'ResourceNotFoundError';
}
}
const handler: AgentHandler = async (request, response, context) => {
try {
// Get the request data
const data = await request.data.json();
// Validate the request
if (!data.resourceId) {
throw new ValidationError('resourceId is required');
}
// Log the request
context.logger.info(`Processing resource: ${data.resourceId}`);
// Simulate resource lookup
const resource = await lookupResource(data.resourceId, context);
// Process the resource
const result = await processResource(resource, context);
// Return the result
return response.json({
message: 'Resource processed successfully',
result
});
} catch (error) {
// Handle different types of errors
if (error instanceof ValidationError) {
context.logger.warn(`Validation error: ${error.message}`);
return response.json({
error: 'Validation error',
message: error.message
});
}
if (error instanceof ResourceNotFoundError) {
context.logger.warn(`Resource not found: ${error.message}`);
return response.json({
error: 'Resource not found',
message: error.message
});
}
// Handle unexpected errors
context.logger.error('Unexpected error', error);
return response.json({
error: 'Internal server error',
message: 'An unexpected error occurred'
});
}
};
// Helper functions
async function lookupResource(resourceId: string, context: any) {
// Simulate resource lookup
const result = await context.kv.get('resources', resourceId);
if (!result.exists) {
throw new ResourceNotFoundError(resourceId);
}
return await result.data.json();
}
async function processResource(resource: any, context: any) {
// Simulate resource processing
context.logger.debug('Processing resource', resource);
// Add processing logic here
return {
id: resource.id,
status: 'processed',
timestamp: new Date().toISOString()
};
}
export default handler;Telemetry Example
This example demonstrates how to use OpenTelemetry for tracing and metrics:
import { AgentHandler } from '@agentuity/sdk';
import { SpanStatusCode } from '@opentelemetry/api';
const handler: AgentHandler = async (request, response, context) => {
return context.tracer.startActiveSpan('process-request', async (span) => {
try {
// Add attributes to the span
span.setAttribute('trigger', request.trigger());
// Get the request data
const data = await request.data.json();
span.setAttribute('data.type', typeof data);
// Create a child span for data processing
return await context.tracer.startActiveSpan('process-data', async (childSpan) => {
try {
// Add event to the span
childSpan.addEvent('processing-started', {
timestamp: Date.now()
});
// Simulate data processing
const result = await processData(data, context);
// Add event to the span
childSpan.addEvent('processing-completed', {
timestamp: Date.now(),
resultSize: JSON.stringify(result).length
});
// Set span status
childSpan.setStatus({ code: SpanStatusCode.OK });
return response.json(result);
} catch (error) {
// Record exception in the span
childSpan.recordException(error as Error);
childSpan.setStatus({
code: SpanStatusCode.ERROR,
message: (error as Error).message
});
throw error;
}
});
} catch (error) {
// Record exception in the parent span
span.recordException(error as Error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: (error as Error).message
});
// Log the error
context.logger.error('Error processing request', error);
// Return error response
return response.json({
error: 'Failed to process request',
message: (error as Error).message
});
}
});
};
async function processData(data: any, context: any) {
// Simulate data processing
await new Promise(resolve => setTimeout(resolve, 100));
return {
processed: true,
input: data,
timestamp: new Date().toISOString()
};
}
export default handler;Background Tasks and Stream Creation Example
This comprehensive example demonstrates how to use context.waitUntil for background processing and context.stream.create for low-level stream control:
import { AgentHandler } from '@agentuity/sdk';
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
const handler: AgentHandler = async (request, response, context) => {
const {
action,
prompt,
userId,
includeAnalytics = true
} = await request.data.json();
if (action === 'stream-with-background') {
// Create a stream for the LLM response
const stream = await context.stream.create('llm-response', {
contentType: 'text/plain',
metadata: {
userId,
requestId: request.id,
model: 'gpt-4o',
type: 'llm-generation'
}
});
// Background task for streaming LLM response
context.waitUntil(async () => {
const { textStream } = streamText({
model: openai('gpt-4o'),
prompt: prompt || 'Tell me a short story'
});
await textStream.pipeTo(stream);
});
// Background task for analytics (doesn't block response)
if (includeAnalytics) {
context.waitUntil(async () => {
await logRequestAnalytics(userId, {
action: 'stream_created',
streamId: stream.id,
prompt: prompt?.substring(0, 100), // Log first 100 chars
timestamp: new Date()
});
});
}
// Background task for user activity tracking
context.waitUntil(async () => {
await updateUserActivity(userId, 'llm_stream_request');
});
// Return immediately with stream information
return response.json({
streamId: stream.id,
streamUrl: stream.url,
status: 'streaming',
message: 'Stream created successfully'
});
}
if (action === 'multi-step-stream') {
// Create a progress stream for multi-step processing
const progressStream = await context.stream.create('progress', {
contentType: 'application/json',
metadata: {
type: 'multi-step-progress',
userId
}
});
context.waitUntil(async () => {
try {
const steps = [
{ name: 'Analyzing input', duration: 1000 },
{ name: 'Generating response', duration: 2000 },
{ name: 'Post-processing', duration: 500 },
{ name: 'Finalizing', duration: 300 }
];
for (let i = 0; i < steps.length; i++) {
const step = steps[i];
// Write progress update
await progressStream.write(JSON.stringify({
step: i + 1,
total: steps.length,
name: step.name,
progress: ((i + 1) / steps.length) * 100,
timestamp: new Date().toISOString()
}) + '\n');
// Simulate processing time
await new Promise(resolve => setTimeout(resolve, step.duration));
}
// Write final completion message
await progressStream.write(JSON.stringify({
completed: true,
message: 'All steps completed successfully',
timestamp: new Date().toISOString()
}) + '\n');
} finally {
await progressStream.close();
}
});
// Background cleanup task
context.waitUntil(async () => {
// Clean up temporary resources after a delay
await new Promise(resolve => setTimeout(resolve, 30000)); // 30 seconds
await cleanupTemporaryResources(progressStream.id);
});
return response.json({
progressStreamId: progressStream.id,
progressStreamUrl: progressStream.url,
estimatedDuration: '3-4 seconds'
});
}
if (action === 'direct-stream-response') {
const stream = await context.stream.create('direct', {
contentType: 'text/plain'
});
context.waitUntil(async () => {
const { textStream } = streamText({
model: openai('gpt-4o'),
prompt: prompt || 'Write a haiku about programming'
});
await textStream.pipeTo(stream);
});
// Return the stream directly - client gets redirected
return stream;
}
return response.json({
error: 'Invalid action. Use "stream-with-background", "multi-step-stream", or "direct-stream-response"'
});
};
// Helper functions (would be implemented based on your needs)
async function logRequestAnalytics(userId: string, data: any) {
// Implementation for logging analytics
console.log(`Analytics for user ${userId}:`, data);
}
async function updateUserActivity(userId: string, activity: string) {
// Implementation for updating user activity
console.log(`Updated activity for user ${userId}: ${activity}`);
}
async function cleanupTemporaryResources(streamId: string) {
// Implementation for cleaning up resources
console.log(`Cleaned up resources for stream ${streamId}`);
}
export default handler;This example demonstrates:
- Non-blocking Stream Creation: Using
context.stream.create()to create streams that return immediately - Background Processing: Using
context.waitUntil()for analytics, logging, and cleanup tasks - Manual Stream Writing: Writing progress updates and structured data to streams
- Stream Metadata: Associating metadata with streams for tracking
- Direct Stream Response: Returning streams directly from handlers
- Multiple Background Tasks: Running several background tasks concurrently
File Import Example
This example demonstrates how to use the automatic file import feature to include various file types in your agent:
import { AgentHandler } from '@agentuity/sdk';
// Import different file types
import config from './agent-config.yaml'; // Parsed YAML object
import prompts from './prompts.json'; // Parsed JSON object
import instructions from './README.md'; // String content
import csvData from './data/users.csv'; // String content
import sqlQuery from './queries/users.sql'; // String content
import xmlConfig from './config.xml'; // String content
import logoImage from './assets/logo.png'; // Uint8Array binary
import iconSvg from './assets/icon.svg'; // Uint8Array binary
const handler: AgentHandler = async (request, response, context) => {
const { action, userId } = await request.data.json();
if (action === 'get-config') {
// Use parsed YAML configuration directly
return response.json({
apiVersion: config.api.version,
features: config.features,
environment: config.environment
});
}
if (action === 'get-prompt') {
const { promptType } = await request.data.json();
// Use parsed JSON data
const prompt = prompts[promptType] || prompts.default;
return response.json({
prompt: prompt.text,
temperature: prompt.temperature,
maxTokens: prompt.maxTokens
});
}
if (action === 'process-users') {
// Process CSV data
const lines = csvData.trim().split('\n');
const headers = lines[0].split(',');
const users = lines.slice(1).map(line => {
const values = line.split(',');
return headers.reduce((user, header, index) => {
user[header] = values[index];
return user;
}, {} as Record<string, string>);
});
return response.json({
totalUsers: users.length,
users: users.slice(0, 10), // Return first 10 users
headers
});
}
if (action === 'store-assets') {
// Store binary image data
await context.objectstore.put('assets', 'company-logo.png', logoImage, {
contentType: 'image/png',
metadata: {
uploadedBy: 'agent',
purpose: 'branding'
}
});
// Store SVG data (note: SVG is Uint8Array, not string)
await context.objectstore.put('assets', 'icon.svg', iconSvg, {
contentType: 'image/svg+xml',
metadata: {
uploadedBy: 'agent',
purpose: 'icon'
}
});
const logoUrl = await context.objectstore.createPublicURL('assets', 'company-logo.png');
const iconUrl = await context.objectstore.createPublicURL('assets', 'icon.svg');
return response.json({
message: 'Assets uploaded successfully',
logoUrl,
iconUrl,
logoSize: logoImage.length,
svgSize: iconSvg.length
});
}
if (action === 'execute-query') {
// Use SQL query with parameter substitution
const { limit = 10 } = await request.data.json();
const query = sqlQuery.replace('{{limit}}', limit.toString());
// In a real implementation, you would execute the SQL query
context.logger.info(`Would execute SQL: ${query}`);
return response.json({
message: 'Query prepared',
sql: query,
originalQuery: sqlQuery
});
}
return response.json({
error: 'Invalid action',
availableActions: ['get-config', 'get-prompt', 'process-users', 'store-assets', 'execute-query']
});
};
export default handler;This example shows how to:
- Import Configuration: Use YAML/JSON files for structured configuration data
- Process Text Files: Work with Markdown, CSV, XML, and SQL content as strings
- Handle Binary Data: Store images and SVG files in object storage (note: SVG files are Uint8Array, not strings)
- Template Processing: Use SQL queries with parameter substitution
- Data Processing: Parse CSV data into structured objects
- Multiple File Types: Combine different file formats in a single agent
The imported data is available immediately at runtime without any file system operations, making your agents fast and efficient.
Welcome Function Example
This example demonstrates how to customize the initial appearance of DevMode when it starts interacting with your agents:
// agent.ts
import type { AgentHandler, AgentWelcomeResult } from '@agentuity/sdk';
export const welcome = (): AgentWelcomeResult => {
return {
welcome: "Welcome to my Agent! How can I help you today?",
prompts: [
{
data: "What can you do?",
contentType: "text/plain",
},
{
data: "Tell me about yourself",
contentType: "text/plain",
}
],
};
};
const handler: AgentHandler = async (request, response, context) => {
// Agent logic
return response.text('Hello, World!');
};
export default handler;Real-World Example
Here's a more comprehensive example from the React Miami 2025 Concierge template:
// ConciergeHost/index.ts
import type { AgentWelcomeResult } from "@agentuity/sdk";
export const welcome = (): AgentWelcomeResult => {
return {
welcome: "Welcome to the React Miami 2025 Concierge! How can I help you today?",
prompts: [
{
data: "Where should I go for dinner in Miami, tonight?",
contentType: "text/plain",
},
{
data: "What sessions about React hooks are happening today?",
contentType: "text/plain",
},
{
data: "Tell me more about [Speaker Name]'s background",
contentType: "text/plain",
},
{
data: "I'm hungry and looking for Cuban food near the conference",
contentType: "text/plain",
},
{
data: "What is Agentuity all about?",
contentType: "text/plain",
},
{
data: "What can I do?",
contentType: "text/plain",
},
],
};
};Note that in this example, some prompts use plain text directly while others use base64 encoding. Both approaches work, but using base64 encoding is recommended for consistency.
OpenAI Streaming Example
This example demonstrates how to stream responses from OpenAI models in your Agentuity agent. Streaming allows for a more responsive user experience by displaying the AI's response as it's being generated, rather than waiting for the entire response to complete.
import type { AgentRequest, AgentResponse, AgentContext } from "@agentuity/sdk";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export default async function Agent(
req: AgentRequest,
resp: AgentResponse,
ctx: AgentContext,
) {
const { textStream } = streamText({
model: openai("gpt-4o"),
prompt: "Invent a new holiday and describe its traditions.",
});
return resp.stream(textStream);
}How It Works
- We import the necessary types from
@agentuity/sdkand utilities from the Vercel AI SDK (aiand@ai-sdk/openai). - The
streamTextfunction from the Vercel AI SDK creates a streaming text response from OpenAI. - We configure the stream with the
gpt-4omodel and a prompt. - The
textStreamobject contains the streaming response from OpenAI. - We return the stream using
resp.stream(), which handles the streaming response in the Agentuity platform.
Dependencies
To use this example, you'll need to install the following dependencies:
npm install ai @ai-sdk/openai
Agent-to-Agent Streaming Example
This example demonstrates how to call another agent and stream its response back to the client. This is useful for creating agent workflows where one agent processes a request and streams the response through another agent.
import type { AgentRequest, AgentResponse, AgentContext } from "@agentuity/sdk";
export default async function Agent(
req: AgentRequest,
resp: AgentResponse,
ctx: AgentContext,
) {
// Setup to talk to another agent
const agent = await ctx.getAgent({
name: 'HistoryExpert',
});
// Invoke the agent
const agentResponse = await agent.run({
data: 'What engine did a P-51D Mustang use?',
});
// Get the stream from the agent
const stream = await agentResponse.data.stream();
// Return the stream to the client
return resp.stream(stream);
}How It Works
- We use
ctx.getAgent()to get a reference to another agent named 'HistoryExpert'. - We invoke the agent with
agent.run(), passing the data we want to process. - We retrieve the stream from the agent's response using
agentResponse.data.stream(). - Finally, we return the stream to the client using
resp.stream(), which passes the streaming response through our agent.
This pattern allows you to:
- Chain multiple agents together in a workflow
- Maintain a streaming experience end-to-end
- Process or transform streaming data between agents
Learn More About Streaming
For more information about agent streaming, check out these resources:
- Agents just want to have streams - Blog post explaining the importance and implementation of streaming in agents
- Streams for agents! - YouTube video demonstration of agent streaming capabilities
Need Help?
Join our Community for assistance or just to hang with other humans building agents.
Send us an email at hi@agentuity.com if you'd like to get in touch.
Please Follow us on
If you haven't already, please Signup for your free account now and start building your first agent!