Skip to main content

About Vercel AI SDK with Tools

Vercel AI SDK is an open-source toolkit from the team behind Vercel and Next.js. It provides a unified developer experience for building AI-powered applications with tool calling capabilities.

Key Features for Tool Integration

  • Easy integration with multiple model providers (OpenAI, Anthropic, etc.)
  • Support for streaming responses for real-time chat UIs
  • Type-safe APIs with excellent TypeScript support
  • Built-in support for agents, tools, and structured outputs
  • Ready-to-use React/Next.js hooks for managing conversation state
By using the AI SDK with Alchemyst tools, you can avoid handling low-level APIs directly and focus on creating seamless AI-driven experiences in your app.

How It Works

The AI SDK is split into modular packages:
  • Core API: A unified way to call LLMs and handle outputs
  • Provider Adapters: Packages like @ai-sdk/openai let you plug in specific providers
  • UI Utilities: Hooks such as useChat make it easy to build interactive experiences
  • Tooling / Agents: Support for calling external APIs or chaining workflows
This modular design means you can start small and scale up to more complex AI flows as your application grows.

Alchemyst Tools Integration

Alchemyst provides specialized tools that you can integrate with the AI SDK:
  • Context Management Tools: Add, search, and retrieve context dynamically
  • Search Tools: Find relevant information in your stored context
  • Delete Tools: Remove outdated or unnecessary context
  • Custom Tools: Extend with your own business logic

When to Use Vercel AI SDK with Tools

You should consider using the SDK with Alchemyst tools if:
  • You want to build chatbots, assistants, or agent-like applications quickly
  • You need real-time streaming responses from your models
  • You want to give your AI access to dynamic context and knowledge bases
  • You need cross-provider flexibility without rewriting core logic
  • You prefer type safety and well-structured APIs
For more details, check out the official Vercel AI SDK documentation

As a Tool in AI-SDK

Alchemyst can be added to your AI SDK codebase as a regular tool, which is the recommended way for developer control.The snippet below shows how to set up a tool using Vercel’s AI SDK, using OpenAI GPT-4o-mini. This tool uses Alchemyst AI SDK under the hood, and exposes a nifty set of tools, letting you control if you want to use memory, context or both (default).This assumes that you have an OpenAI Key and Alchemyst API Key. If you don’t have the Alchemyst API Key, you can get them in the Alchemyst Settings page

Basic Setup

aiSdkToolSetup.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

const result = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: "Search for users with the name rohan",
  tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, true)
});
Hierarchical groupName Strategy: Use 2-3 layers for optimal organization:
  • Layer 1: Organization/Domain (“engineering”, “marketing”, “sales”)
  • Layer 2: Category/Time (“q4_2024”, “backend”, “campaign”)
  • Layer 3: Specifics (“auth”, “api”, “billing”)
Example: [“engineering”, “backend”, “auth”] instead of flat [“engineering_backend_auth_login_jwt”]

Complete Streaming Example

This example shows how to handle streaming responses and tool calls:
streamingExample.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';


const result = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: "What information do we have about our product documentation?",
  tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, true)
});

// Stream the text response
for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

// Handle tool calls if any were made
const toolCalls = await result.toolCalls;
if (toolCalls && toolCalls.length > 0) {
  console.log('\n\nTool calls made:');
  for (const toolCall of toolCalls) {
    console.log(`- ${toolCall.toolName}:`, toolCall.args);
  }
}

// Get the full text result
const fullText = await result.text;
console.log('\n\nFull response:', fullText);

Context Search Example

This example demonstrates how the AI can search your stored context:
contextSearchExample.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

const result = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: "Find all information about our pricing plans and feature comparisons",
  tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false) // Context only
});

// The AI will automatically use the search_context tool to find relevant information
for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

Adding Context Example

This example shows how to add context that the AI can later search:
addContextExample.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

// First, add some context
const addResult = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: `store this information: rohan is a good boy`,
  tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false) // Context only
});


const toolCalls = await addResult.toolCalls;
if (toolCalls && toolCalls.length > 0) {
  console.log('\n\nTool calls made:');
  for (const toolCall of toolCalls) {
    console.log(`- ${toolCall.toolName}:`, JSON.stringify(toolCall, null, 2));
  }
}
// Wait for the tool to execute
await addResult.text;

// Now search for it
const searchResult = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: "Search for users with the name rohan",
  tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false)
});

for await (const chunk of searchResult.textStream) {
  process.stdout.write(chunk);
}
Document Size Best Practice: Keep documents between 500-2000 words for optimal retrieval:
  • Too small (less than 100 words): Loses context, requires many docs
  • Too large (more than 10,000 words): Retrieves too much irrelevant content, wastes tokens
  • Just right (500-2000 words): Single cohesive topic with enough context

Updating Context (Delete-Then-Add Pattern)

When you need to update existing context, use the delete-then-add pattern to avoid 409 conflicts:
updateContextExample.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

// Update existing context about a user
async function updateUserContext(userId: string, newInfo: string) {
  // Step 1: Delete old context
  const deleteResult = await streamText({
    model: openai('gpt-4o-mini'),
    prompt: `Delete all information about ${userId}`,
    tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false)
  });
  
  await deleteResult.text; // Wait for deletion
  
  // Step 2: Small delay to ensure propagation
  await new Promise(resolve => setTimeout(resolve, 150));
  
  // Step 3: Add updated context
  const addResult = await streamText({
    model: openai('gpt-4o-mini'),
    prompt: `Store this updated information: ${newInfo}`,
    tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false)
  });
  
  return await addResult.text;
}

// Usage
await updateUserContext("rohan", "rohan is an excellent developer who specializes in TypeScript");
Update Strategy: Alchemyst uses metadata.fileName as the deduplication key. Same fileName = update attempt, which requires delete first to avoid 409 Conflict errors. This is by design to prevent accidental duplicates.

Chat Assistant with Context and Memory

A complete chat assistant that uses both context and memory:
chatAssistant.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

async function chatWithAssistant(userMessage: string, userId: string) {
  const result = await streamText({
    model: openai('gpt-4o-mini'),
    messages: [
      {
        role: 'system',
        content: 'You are a helpful assistant with access to company knowledge and user preferences.'
      },
      {
        role: 'user',
        content: userMessage
      }
    ],
    tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, true) // Both context and memory
  });

  // Stream the response
  let response = '';
  for await (const chunk of result.textStream) {
    process.stdout.write(chunk);
    response += chunk;
  }

  // Check for tool calls
  const toolCalls = await result.toolCalls;
  if (toolCalls) {
    console.log('\n\n[Tools used:', toolCalls.map(t => t.toolName).join(', '), ']');
  }

  return response;
}

// Example usage
await chatWithAssistant("who is rohan?", "user_123");

Tool Configuration Options

Control which tools are available:
toolConfiguration.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

// Only context tools (search, add, delete context)
const contextOnly = alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false);

// Only memory tools (add, delete memory)
const memoryOnly = alchemystTools("YOUR_ALCHEMYST_AI_KEY", false, true);

// Both context and memory (default)
const both = alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, true);

// Use context-only for knowledge base queries
const result = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: "Search our documentation for API examples",
  tools: contextOnly
});

Advanced Context Management

Working with dynamic context in real-time with proper metadata structure:
advancedContext.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

// Add multiple pieces of context with lean metadata (5-field rule)
const contexts = [
  {
    content: "Our pricing: Basic $10/month, Pro $50/month, Enterprise custom",
    metadata: { category: "pricing", lastModified: "2024-12-29" }
  },
  {
    content: "Support hours: 9am-5pm EST Monday-Friday",
    metadata: { category: "support", lastModified: "2024-12-29" }
  },
  {
    content: "Return policy: 30-day money-back guarantee",
    metadata: { category: "policy", lastModified: "2024-12-29" }
  }
];

for (const ctx of contexts) {
  await streamText({
    model: openai('gpt-4o-mini'),
    prompt: `Store this information: ${ctx.content}`,
    tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false)
  });
}

// Query across all stored context
const result = await streamText({
  model: openai('gpt-4o-mini'),
  prompt: "What are all our policies and pricing?",
  tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false)
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}
The 5-Field Metadata Rule: Start with maximum 5 metadata fields. Only add more when you have a specific query pattern that requires it.Store in metadata if you:
  • Filter or sort by it (category, price, status)
  • Need exact matching (id, sku)
  • Use for access control (department, classification)
Store in content if it’s:
  • Descriptive text (description, features)
  • Rarely filtered (dimensions, weight)
  • Only needed when retrieved (warranty, specifications)

Bulk Context Operations

For adding large amounts of context efficiently:
bulkContextOperations.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { alchemystTools } from '@alchemystai/aisdk';

async function bulkAddContext(documents: Array<{content: string}>) {
  const BATCH_SIZE = 1000; // Optimal batch size
  const MAX_RETRIES = 3;
  
  for (let i = 0; i < documents.length; i += BATCH_SIZE) {
    const batch = documents.slice(i, i + BATCH_SIZE);
    
    console.log(`Processing batch ${Math.floor(i/BATCH_SIZE) + 1}/${Math.ceil(documents.length/BATCH_SIZE)}`);
    
    for (const doc of batch) {
      let attempt = 0;
      let success = false;
      
      while (attempt < MAX_RETRIES && !success) {
        try {
          await streamText({
            model: openai('gpt-4o-mini'),
            prompt: `Store this: ${doc.content}`,
            tools: alchemystTools("YOUR_ALCHEMYST_AI_KEY", true, false)
          });
          success = true;
        } catch (error) {
          attempt++;
          if (attempt < MAX_RETRIES) {
            // Exponential backoff: 1s, 2s, 4s
            await new Promise(resolve => 
              setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))
            );
          }
        }
      }
    }
    
    // Small delay between batches
    if (i + BATCH_SIZE < documents.length) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }
}
Performance Guidelines:
  • 100 docs: ~0.5s (single call)
  • 1,000 docs: ~3s (single call)
  • 10,000 docs: ~35s (10 batches of 1000)
  • Always use batches of 1000 for optimal performance
  • Include retry logic with exponential backoff
If you don’t have an Alchemyst API Key, you can get one in the Alchemyst Settings page