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 data = await context.kv.get('user-preferences', userId);
      
      if (!data) {
        return response.json({ message: 'No preferences found' });
      }
      
      // Convert ArrayBuffer to string and parse as JSON
      const prefsString = new TextDecoder().decode(data);
      const userPrefs = JSON.parse(prefsString);
      
      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' });
      }
      
      // Prepare documents for vector storage
      const documents = products.map(product => ({
        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
      const formattedResults = results.map(result => ({
        ...result.metadata,
        similarity: 1 - result.distance // Convert distance to similarity score
      }));
      
      return response.json({ 
        results: formattedResults,
        count: formattedResults.length
      });
    }
    
    case 'delete': {
      // Delete products from vector storage
      if (!Array.isArray(products) || products.length === 0) {
        return response.json({ error: 'No product IDs to delete' });
      }
      
      const count = await context.vector.delete('products', ...products);
      
      return response.json({ 
        message: `Deleted ${count} products successfully` 
      });
    }
    
    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 resource = await context.kv.get('resources', resourceId);
  
  if (!resource) {
    throw new ResourceNotFoundError(resourceId);
  }
  
  return JSON.parse(new TextDecoder().decode(resource));
}
 
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;
        } finally {
          // End the child span
          childSpan.end();
        }
      });
    } 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
      });
    } finally {
      // End the parent span
      span.end();
    }
  });
};
 
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;

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

  1. We import the necessary types from @agentuity/sdk and utilities from the Vercel AI SDK (ai and @ai-sdk/openai).
  2. The streamText function from the Vercel AI SDK creates a streaming text response from OpenAI.
  3. We configure the stream with the gpt-4o model and a prompt.
  4. The textStream object contains the streaming response from OpenAI.
  5. 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

  1. We use ctx.getAgent() to get a reference to another agent named 'HistoryExpert'.
  2. We invoke the agent with agent.run(), passing the data we want to process.
  3. We retrieve the stream from the agent's response using agentResponse.data.stream().
  4. 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: