npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

swizzy-sdk

v1.0.0

Published

Swizzy SDK - Intelligent Chat with Memory for AI Applications

Downloads

11

Readme

Swizzy SDK

A comprehensive TypeScript SDK for the Breadcrumb AI Platform, providing seamless integration with chat sessions, knowledge management, tool calling, and multimodal AI interactions.

Table of Contents

Overview

The Swizzy SDK is a comprehensive TypeScript library that provides seamless integration with the Breadcrumb AI Platform - an advanced conversational AI system built on Cloudflare Workers and Durable Objects. The platform combines multiple AI capabilities into a unified, production-ready solution for building intelligent chat applications.

What the Breadcrumb AI Platform Builds

The Breadcrumb AI Platform is designed to create intelligent, context-aware conversational agents that can:

  • Maintain Persistent Conversations: Sessions persist across requests with full message history, context, and custom rules
  • Process and Understand Documents: Automatic ingestion of PDFs, text files, CSVs, and Markdown with semantic search capabilities
  • Execute Complex Tasks: Function calling system that allows AI to perform calculations, query knowledge bases, and interact with external APIs
  • Handle Multimodal Content: Process both text and images (URLs and base64 encoded) in conversations
  • Stream Real-time Responses: OpenAI-compatible streaming for responsive user interfaces
  • Scale Horizontally: Built on Cloudflare's global network with Durable Objects for state management
  • Provide Observability: WebSocket-based real-time monitoring and logging

Key Features

  • Session Management: Persistent chat sessions with automatic model routing and context preservation
  • Knowledge Base: Document ingestion, chunking, vector embeddings, and semantic search across uploaded content
  • Tool Integration: Extensible function calling with built-in tools (calculator, memory operations) and custom tool registration
  • Multimodal Support: Text, images (URLs and base64), and mixed content processing with validation
  • Real-time Streaming: OpenAI-compatible streaming responses with token-by-token updates
  • Event System: Observable events for UI integration and real-time updates
  • Type Safety: Full TypeScript support with comprehensive type definitions and Zod validation
  • Authentication: API key and Clerk authentication with user profile management
  • Observability: WebSocket-based monitoring, logging, and usage metrics
  • Error Handling: Comprehensive error handling with custom error types and retry logic

Installation

npm install swizzy-sdk
# or
yarn add swizzy-sdk
# or
pnpm add swizzy-sdk

Quick Start

import { SwizzySDK } from 'swizzy-sdk';

// Initialize the SDK
const sdk = new SwizzySDK({
  baseUrl: 'https://your-worker.workers.dev',
  auth: {
    apiKey: 'your-api-key' // or clerkToken
  }
});

// Register built-in tools
sdk.tools.registerBuiltInTool(['calculator', 'query_memory', 'add_to_memory']);

// Create a session
const session = await sdk.sessions.createSession('My Chat Session');

// Send a message with tool calling
const response = await sdk.sessions.sendMessage(session.id, {
  messages: [{ role: 'user', content: 'Calculate 15 * 7 and search my docs for "timeline"' }],
  model: 'switchblade-1',
  tools: true // Enable tool calling
}, {
  onToolCalls: (toolCalls) => console.log('Tools called:', toolCalls),
  onToolResults: (results) => console.log('Tool results:', results)
});

console.log(response.choices[0].message.content);

Core Concepts

Sessions

Sessions are the core building blocks of conversations in the Breadcrumb AI Platform. They provide persistent, stateful chat contexts that maintain conversation history, knowledge bases, tool configurations, and custom rules across multiple interactions.

Session Architecture

Each session encapsulates:

  • Unique ID and Metadata: Sessions can be tagged with custom metadata for organization and filtering
  • Knowledge Base: Dedicated document storage with automatic ingestion and semantic search capabilities
  • Context and Rules: Customizable context information and behavioral rules that guide AI responses
  • Message History: Complete conversation history with pagination support for efficient retrieval
  • Tool Configuration: Registered tools available for function calling within that session
  • State Persistence: All session data persists across requests using Cloudflare Durable Objects

Knowledge Management

The knowledge system allows ingestion of various document types:

  • Supported Formats: PDF, TXT, CSV, MD
  • Automatic Processing: Text extraction, chunking, and vector embeddings
  • Semantic Search: Query documents with natural language
  • Source Tracking: Maintains document metadata and relationships

Tool System

The tool system enables AI models to perform actions beyond text generation by calling predefined functions. This creates more capable and interactive AI assistants that can perform calculations, query knowledge bases, and interact with external services.

How Tool Calling Works

  1. Tool Registration: Define available tools with their parameters and handlers
  2. AI Decision: The model analyzes the conversation and decides which tools to call
  3. Parameter Extraction: SDK parses the tool call parameters from the model's response
  4. Execution: Tools run with proper validation and error handling
  5. Result Integration: Tool results are formatted and sent back to the model for a natural response

Built-in Tools

The SDK includes several ready-to-use tools:

  • calculator: Evaluates mathematical expressions safely

    // AI can automatically calculate: "What is 15 * 7 + 3?"
    // Result: "15 * 7 = 105, plus 3 = 108"
  • query_memory: Searches the session's knowledge base

    // AI can search documents: "What were the key findings from the report?"
    // Result: Returns relevant excerpts from ingested documents
  • add_to_memory: Stores information in the session's knowledge base

    // AI can remember: "Remember that the meeting is at 3 PM tomorrow"
    // Result: Information stored and available for future queries

Custom Tools

Register your own tools with Zod schema validation:

import { z } from 'zod';

// Define a weather tool
sdk.tools.registerTool(
  'get_weather',
  {
    name: 'get_weather',
    description: 'Get current weather for a location',
    parameters: z.object({
      location: z.string().describe('City name or zip code'),
      unit: z.enum(['celsius', 'fahrenheit']).default('celsius').describe('Temperature unit')
    })
  },
  async (args) => {
    // Your weather API implementation
    const response = await fetch(`https://api.weather.com/${args.location}?unit=${args.unit}`);
    const data = await response.json();
    return {
      temperature: data.temp,
      condition: data.condition,
      location: args.location
    };
  }
);

Tool Execution Flow

When tools: true is set in sendMessage, the SDK:

  1. Includes available tools in the request to the AI model
  2. Monitors the response for tool calls
  3. Automatically executes tools when detected
  4. Formats results as tool messages
  5. Sends a follow-up request with tool results
  6. Returns the final AI response incorporating tool outputs

Multimodal Content

Send text and images together:

  • Image URLs: HTTP/HTTPS links to images
  • Base64 Images: Direct embedding with data:image/... URLs
  • Mixed Content: Combine text and images in single messages
  • Validation: Automatic image accessibility checking

API Reference

Initialization

const sdk = new SwizzySDK({
  baseUrl: 'https://your-worker.workers.dev',
  auth: {
    apiKey: 'your-api-key', // API key authentication
    // or
    clerkToken: 'clerk-jwt-token' // Clerk authentication
  },
  observability: {
    enableWebSocket: true,
    enableLogging: true,
    logLevel: 'info'
  },
  timeout: 30000,
  retryAttempts: 3
});

Sessions

Create Session

const session = await sdk.sessions.createSession(
  'Optional Title',
  { tags: ['work', 'project-x'] }
);

Send Message

The sendMessage method is the primary interface for interacting with AI models. It supports both regular and streaming responses, tool calling, and comprehensive callback handling.

const response = await sdk.sessions.sendMessage(sessionId, {
  messages: [
    { role: 'user', content: 'Hello!' }
  ],
  model: 'switchblade-1',
  stream: true,        // Enable streaming for real-time responses
  tools: true          // Enable tool calling for this request
}, {
  // Streaming callbacks
  onToken: (token: string) => {
    // Called for each token in streaming responses
    process.stdout.write(token);
  },

  // Tool calling callbacks
  onToolCalls: (toolCalls: ToolCall[]) => {
    // Called when AI decides to use tools
    console.log('AI wants to execute:', toolCalls.map(tc => tc.function.name));
  },

  onToolResults: (results: ChatMessage[]) => {
    // Called after tools are executed with their results
    console.log('Tool execution complete, results:', results);
  },

  // Completion and error callbacks
  onComplete: (response: ChatCompletionResponse) => {
    // Called when the full response is ready
    console.log('Full response:', response.choices[0].message.content);
  },

  onError: (error: Error | SwizzyError) => {
    // Called on any error during the request
    console.error('Request failed:', error.message);
  },

  // Image validation callback
  onImageError: (error: Error, imageUrl: string) => {
    // Called when image validation fails
    console.error(`Image error for ${imageUrl}:`, error.message);
  }
});
Callback Details
  • onToken: Receives each text token as it's generated during streaming. Useful for real-time UI updates.
  • onToolCalls: Triggered when the AI model decides to call tools. Provides the tool calls before execution.
  • onToolResults: Called after tool execution completes, providing the formatted results that will be sent back to the AI.
  • onComplete: Fires when the entire response is finished, including any tool execution follow-ups.
  • onError: Handles any errors during the request, streaming, or tool execution phases.
  • onImageError: Specific callback for image validation failures in multimodal messages.

Message History

const history = await sdk.sessions.getMessageHistory(sessionId, {
  limit: 50,
  offset: 0,
  sort_by: 'timestamp',
  sort_order: 'desc'
});

Session Context

Enhance AI responses by providing additional context and behavioral rules that persist throughout the conversation.

// Add persistent context information
await sdk.sessions.addContext(sessionId,
  'You are a technical documentation assistant specializing in React and TypeScript. ' +
  'Always provide code examples and explain concepts clearly.'
);

// Set behavioral rules for the AI
await sdk.sessions.setRules(sessionId, [
  'Always provide working code examples',
  'Use modern React patterns and hooks',
  'Explain complex concepts with simple analogies',
  'Include error handling in code examples',
  'Reference official documentation when appropriate'
]);

// Context and rules are automatically included in all future messages
// The AI will follow these guidelines throughout the conversation

// Update context (replaces existing context)
await sdk.sessions.addContext(sessionId,
  'You are now a code review assistant. Focus on best practices, performance, and maintainability.'
);

// Update rules (replaces existing rules)
await sdk.sessions.setRules(sessionId, [
  'Identify potential bugs and security issues',
  'Suggest performance optimizations',
  'Ensure code follows team conventions',
  'Provide constructive feedback'
]);

Context vs Rules:

  • Context: Background information about the domain, role, or scenario
  • Rules: Specific behavioral guidelines and constraints for responses

Persistence: Context and rules persist across all messages in the session until explicitly changed.

Knowledge Management

Ingest Files

Upload and process documents into the session's knowledge base. Files are automatically chunked, embedded, and made searchable.

// Ingest multiple files at once
const result = await sdk.knowledge.ingestFiles(sessionId, fileList, 'optional-source-id');

// FileList from HTML input
const fileInput = document.getElementById('file-input') as HTMLInputElement;
const files = fileInput.files;
if (files) {
  const result = await sdk.knowledge.ingestFiles(sessionId, files, 'user-upload');
  console.log(`Processed ${result.stats.sourcesProcessed} files, stored ${result.stats.vectorsStored} vectors`);
}

// Array of File objects
const files = [pdfFile, textFile, csvFile];
const result = await sdk.knowledge.ingestFiles(sessionId, files, 'batch-upload');

// Result structure
interface IngestResponse {
  success: boolean;
  message: string;
  stats: {
    chunksProcessed: number;
    sourcesProcessed: number;
    vectorsStored: number;
    sessionId: string;
  };
}

Supported File Formats:

  • PDF files (.pdf) - Text is extracted from all pages
  • Text files (.txt) - Plain text content
  • CSV files (.csv) - Structured data with headers
  • Markdown files (.md) - Formatted text with headers and links

File Validation:

  • Maximum size: 10MB per file
  • Automatic type detection and validation
  • Path traversal protection
  • Content length limits per chunk

Ingest Raw Data

For programmatic data ingestion without files:

// Prepare text data for ingestion
const textNodes = sdk.knowledge.prepareTextForIngestion(
  'Your document content here...',
  'document-1',
  'My Document Title',
  1200 // chunk size
);

// Ingest the prepared data
const result = await sdk.knowledge.ingestData(sessionId, textNodes, 'document-1');

// Manual node creation
const nodes = [
  {
    id: 'chunk-1',
    content: 'This is the first chunk of content...',
    metadata: {
      source_id: 'manual-entry',
      source_type: 'text',
      title: 'Manual Entry',
      page: 1,
      paragraph: 1,
      uploaded_at: new Date().toISOString(),
      processed_at: new Date().toISOString()
    }
  }
];

await sdk.knowledge.ingestData(sessionId, nodes);

Query Knowledge Base

Search through ingested documents using natural language queries. Results are ranked by semantic similarity.

// Basic query
const results = await sdk.knowledge.queryKnowledgeBase(sessionId, {
  query: 'What are the key findings from the research?',
  topK: 5 // Return top 5 most relevant results
});

// Advanced query with more results
const results = await sdk.knowledge.queryKnowledgeBase(sessionId, {
  query: 'summarize the methodology and results',
  topK: 10
});

// Result structure
interface KnowledgeResponse {
  query: string;
  results: Array<{
    id: string;
    score: number; // Similarity score (0-1, higher is better)
    content: string; // The actual text chunk
    metadata: {
      source_id: string;
      source_type: string; // 'pdf', 'txt', 'csv', 'md'
      title: string;
      page?: number; // For PDFs
      paragraph?: number; // For text documents
    };
  }>;
  total: number;
}

// Usage in conversation
const searchResults = await sdk.knowledge.queryKnowledgeBase(sessionId, {
  query: 'project timeline and milestones'
});

const contextMessage = searchResults.results
  .map(r => `From ${r.metadata.title}: ${r.content}`)
  .join('\n\n');

await sdk.sessions.sendMessage(sessionId, {
  messages: [
    { role: 'system', content: `Context from knowledge base:\n${contextMessage}` },
    { role: 'user', content: 'What is the current project status?' }
  ],
  model: 'switchblade-1'
});

Manage Session Files

List and manage files that have been ingested into a session.

// Get all files in a session
const fileList = await sdk.knowledge.getSessionFiles(sessionId);

// Result structure
interface FileListResponse {
  files: Array<{
    id: string;
    storageId: string;
    name: string;
    type: string; // MIME type
    size: number; // bytes
    uploadedAt: string;
    processed: boolean;
  }>;
  total: number;
  sessionId: string;
}

// Get detailed information about a specific file
const fileDetails = await sdk.knowledge.getFileDetails(sessionId, fileId);

// Result includes content preview and chunk count
interface FileDetails extends FileMetadata {
  content: string; // Preview of file content
  chunkCount: number; // Number of chunks created
}

// Download original file
const blob = await sdk.knowledge.downloadFile(sessionId, fileId);
const url = URL.createObjectURL(blob);
// Use the blob for download or display

Tool System

Register Built-in Tools

// Single tool
sdk.tools.registerBuiltInTool('calculator');

// Multiple tools
sdk.tools.registerBuiltInTool(['calculator', 'query_memory', 'add_to_memory']);

Register Custom Tools

import { z } from 'zod';

// Example: Weather API tool
sdk.tools.registerTool(
  'get_weather',
  {
    name: 'get_weather',
    description: 'Get current weather conditions for a location',
    parameters: z.object({
      location: z.string().describe('City name or zip code'),
      unit: z.enum(['celsius', 'fahrenheit']).default('celsius').describe('Temperature unit')
    })
  },
  async (args) => {
    // Implement your weather API call
    const response = await fetch(`https://api.weather.example.com/${args.location}?unit=${args.unit}`);
    const data = await response.json();

    return {
      location: args.location,
      temperature: data.temperature,
      condition: data.condition,
      unit: args.unit
    };
  }
);

// Example: Database query tool
sdk.tools.registerTool(
  'query_database',
  {
    name: 'query_database',
    description: 'Execute a read-only database query',
    parameters: z.object({
      table: z.string().describe('Table name to query'),
      conditions: z.record(z.any()).optional().describe('WHERE conditions as key-value pairs'),
      limit: z.number().max(100).default(10).describe('Maximum rows to return')
    })
  },
  async (args) => {
    // Your database logic here
    const results = await db.query(args.table, args.conditions, args.limit);
    return { rows: results, count: results.length };
  }
);

Manual Tool Execution

const results = await sdk.tools.executeToolCallsFromResponse(toolCalls);

Authentication

API Key Management

// Generate new key
const keyData = await sdk.auth.generateApiKey('My App Key');

// List keys
const keys = await sdk.auth.listApiKeys();

// Delete key
await sdk.auth.deleteApiKey(keyId);

User Profile

const profile = await sdk.auth.getUserProfile();

Advanced Features

Multimodal Messages

Image URLs

await sdk.sessions.sendMessage(sessionId, {
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Analyze this chart:' },
      { type: 'image_url', image_url: { url: 'https://example.com/chart.png' } }
    ]
  }],
  model: 'switchblade-1'
});

Base64 Images

await sdk.sessions.sendMessage(sessionId, {
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'What do you see?' },
      { type: 'image_url', image_url: { url: `data:image/jpeg;base64,${base64String}` } }
    ]
  }],
  model: 'switchblade-1'
}, {
  onImageError: (error, url) => console.error('Image error:', error)
});

Tool Calling

The SDK automatically handles the complete tool calling workflow:

  1. Tool Registration: Define available tools
  2. LLM Decision: Model decides which tools to call
  3. Parameter Extraction: SDK parses tool call parameters
  4. Execution: Tools run with proper validation
  5. Result Formatting: Results sent back to LLM
  6. Final Response: LLM provides natural language response
// Tools are automatically included when tools: true
const response = await sdk.sessions.sendMessage(sessionId, {
  messages: [{ role: 'user', content: 'Calculate 25 * 8' }],
  model: 'switchblade-1',
  tools: true
});
// Calculator tool executes automatically, LLM responds with result

Streaming

await sdk.sessions.sendMessage(sessionId, {
  messages: [{ role: 'user', content: 'Tell me a story' }],
  model: 'switchblade-1',
  stream: true
}, {
  onToken: (token) => {
    // Handle each token as it arrives
    process.stdout.write(token);
  },
  onComplete: (response) => {
    console.log('\n--- Complete ---');
  }
});

Event System

// Listen to SDK events
const unsubscribe = sdk.client.on('message_received', (event) => {
  console.log('New message:', event.response);
});

// Session-specific updates
const sessionUnsubscribe = sdk.sessions.onSessionUpdate(sessionId, (update) => {
  if (update.type === 'new_message') {
    console.log('Session message:', update.data);
  }
});

// Cleanup
unsubscribe();
sessionUnsubscribe();

Error Handling and Troubleshooting

The SDK provides comprehensive error handling with specific error codes and detailed error information to help you debug issues effectively.

Error Types

SwizzyError

Custom error class with structured error information:

interface SwizzyError extends Error {
  code: string;           // Error code (e.g., 'IMAGE_NOT_ACCESSIBLE')
  statusCode?: number;    // HTTP status code if applicable
  details?: any;          // Additional error context
}

Common Error Codes

  • REQUEST_FAILED: Network or API request failure
  • IMAGE_NOT_ACCESSIBLE: Image URL is invalid or inaccessible
  • IMAGE_INVALID_FORMAT: Image format not supported
  • TOOL_EXECUTION_FAILED: Tool execution error
  • TOOL_VALIDATION_FAILED: Tool parameter validation error
  • SESSION_NOT_FOUND: Session ID doesn't exist
  • AUTHENTICATION_FAILED: Invalid API key or authentication
  • RATE_LIMIT_EXCEEDED: API rate limit exceeded
  • FILE_TOO_LARGE: Uploaded file exceeds size limits
  • UNSUPPORTED_FILE_TYPE: File format not supported

Error Handling Patterns

Basic Error Handling

try {
  const response = await sdk.sessions.sendMessage(sessionId, request);
} catch (error) {
  if (error instanceof SwizzyError) {
    switch (error.code) {
      case 'IMAGE_NOT_ACCESSIBLE':
        console.error('Image failed to load:', error.details.url);
        // Handle image error (retry with different URL, etc.)
        break;
      case 'TOOL_EXECUTION_FAILED':
        console.error('Tool failed:', error.details.toolName, error.details.error);
        // Handle tool execution failure
        break;
      case 'AUTHENTICATION_FAILED':
        console.error('Authentication failed - check API key');
        // Handle auth error (refresh token, etc.)
        break;
      default:
        console.error(`API Error [${error.code}]:`, error.message);
    }
  } else {
    console.error('Network or unexpected error:', error);
  }
}

Advanced Error Handling with Callbacks

const response = await sdk.sessions.sendMessage(sessionId, request, {
  onError: (error) => {
    // Handle streaming errors
    if (error.code === 'IMAGE_NOT_ACCESSIBLE') {
      this.showImageError(error.details.url);
    } else if (error.code === 'TOOL_EXECUTION_FAILED') {
      this.showToolError(error.details.toolName, error.details.error);
    }
  },
  onImageError: (error, imageUrl) => {
    // Specific image validation errors
    console.error(`Image validation failed for ${imageUrl}:`, error.message);
    this.handleImageFallback(imageUrl);
  }
});

Retry Logic and Resilience

class ResilientChatApp {
  private sdk: SwizzySDK;
  private retryConfig = {
    maxRetries: 3,
    baseDelay: 1000,
    maxDelay: 10000
  };

  async sendMessageWithRetry(sessionId: string, request: any, retries = 0) {
    try {
      return await this.sdk.sessions.sendMessage(sessionId, request);
    } catch (error) {
      if (error instanceof SwizzyError && this.isRetryableError(error.code) && retries < this.retryConfig.maxRetries) {
        const delay = Math.min(
          this.retryConfig.baseDelay * Math.pow(2, retries),
          this.retryConfig.maxDelay
        );

        console.log(`Retrying in ${delay}ms... (attempt ${retries + 1})`);
        await new Promise(resolve => setTimeout(resolve, delay));

        return this.sendMessageWithRetry(sessionId, request, retries + 1);
      }

      throw error;
    }
  }

  private isRetryableError(errorCode: string): boolean {
    const retryableErrors = [
      'REQUEST_FAILED',
      'RATE_LIMIT_EXCEEDED',
      'NETWORK_ERROR'
    ];
    return retryableErrors.includes(errorCode);
  }
}

Troubleshooting Guide

Connection Issues

// Check WebSocket connectivity
const sdk = new SwizzySDK({
  baseUrl: 'https://your-worker.workers.dev',
  auth: { apiKey: 'your-key' },
  observability: {
    enableWebSocket: true,
    onConnectionChange: (connected) => {
      console.log('WebSocket connected:', connected);
    },
    onError: (error) => {
      console.error('WebSocket error:', error);
    }
  }
});

// Test basic connectivity
try {
  await sdk.sessions.listSessions();
  console.log('API connection successful');
} catch (error) {
  console.error('API connection failed:', error);
}

Tool Debugging

// Enable detailed tool logging
const sdk = new SwizzySDK({
  baseUrl: 'https://your-worker.workers.dev',
  auth: { apiKey: 'your-key' },
  observability: {
    enableLogging: true,
    logLevel: 'debug'
  }
});

// Listen for tool events
sdk.client.on('tool_executed', (event) => {
  if (!event.success) {
    console.error(`Tool ${event.toolName} failed:`, event.error);
    console.log('Tool args:', event.args);
  } else {
    console.log(`Tool ${event.toolName} succeeded:`, event.result);
  }
});

// Test tool execution manually
try {
  const result = await sdk.tools.executeTool({
    id: 'test_call',
    type: 'function',
    function: {
      name: 'calculator',
      arguments: JSON.stringify({ expression: '2 + 3' })
    }
  });
  console.log('Tool test result:', result);
} catch (error) {
  console.error('Tool test failed:', error);
}

Image Validation Issues

// Debug image validation
const testImageUrls = [
  'https://example.com/image.jpg',
  'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...',
  'invalid-url'
];

for (const url of testImageUrls) {
  try {
    await sdk.sessions.sendMessage(sessionId, {
      messages: [{
        role: 'user',
        content: [
          { type: 'text', text: 'Test image' },
          { type: 'image_url', image_url: { url } }
        ]
      }],
      model: 'switchblade-1'
    });
    console.log(`✓ Image ${url} is valid`);
  } catch (error) {
    console.error(`✗ Image ${url} failed:`, error.message);
  }
}

Performance Issues

// Monitor request performance
class PerformanceMonitor {
  private requestTimes = new Map<string, number>();

  async monitorRequest<T>(id: string, operation: () => Promise<T>): Promise<T> {
    const startTime = Date.now();
    this.requestTimes.set(id, startTime);

    try {
      const result = await operation();
      const duration = Date.now() - startTime;
      console.log(`Request ${id} completed in ${duration}ms`);
      return result;
    } catch (error) {
      const duration = Date.now() - startTime;
      console.error(`Request ${id} failed after ${duration}ms:`, error);
      throw error;
    }
  }
}

// Usage
const monitor = new PerformanceMonitor();
const response = await monitor.monitorRequest(
  'send_message',
  () => sdk.sessions.sendMessage(sessionId, request)
);

Memory and Session Issues

// Check session health
async function diagnoseSession(sessionId: string) {
  try {
    // Check if session exists
    const session = await sdk.sessions.getSession(sessionId);
    console.log('Session exists:', session.title);

    // Check message history
    const history = await sdk.sessions.getMessageHistory(sessionId, { limit: 1 });
    console.log('Message count:', history.total);

    // Check knowledge base
    const files = await sdk.knowledge.getSessionFiles(sessionId);
    console.log('Files in knowledge base:', files.length);

    // Check context
    const context = await sdk.sessions.getContext(sessionId);
    console.log('Context length:', context.context.length);

  } catch (error) {
    console.error('Session diagnosis failed:', error);
  }
}

Examples

Complete Chat Application with Advanced Features

import { SwizzySDK, SwizzyError } from 'swizzy-sdk';

class AdvancedChatApp {
  private sdk: SwizzySDK;
  private currentSession: any;
  private eventUnsubscribers: (() => void)[] = [];

  constructor(apiKey: string) {
    this.sdk = new SwizzySDK({
      baseUrl: 'https://your-worker.workers.dev',
      auth: { apiKey },
      observability: {
        enableWebSocket: true,
        enableLogging: true,
        logLevel: 'info'
      }
    });

    this.setupTools();
    this.setupEventListeners();
  }

  private setupTools() {
    // Register built-in tools
    this.sdk.tools.registerBuiltInTool(['calculator', 'query_memory', 'add_to_memory']);

    // Register custom tools
    this.sdk.tools.registerTool(
      'search_documents',
      {
        name: 'search_documents',
        description: 'Search through uploaded documents',
        parameters: z.object({
          query: z.string().describe('Search query'),
          fileType: z.enum(['pdf', 'txt', 'md']).optional().describe('Filter by file type')
        })
      },
      async (args) => {
        // Custom document search implementation
        return await this.customDocumentSearch(args.query, args.fileType);
      }
    );
  }

  private setupEventListeners() {
    // Listen for real-time session updates
    const sessionUpdateUnsub = this.sdk.sessions.onSessionUpdate(
      this.currentSession?.id,
      (update) => {
        if (update.type === 'new_message') {
          this.handleNewMessage(update.data);
        }
      }
    );

    // Listen for tool executions
    const toolUnsub = this.sdk.client.on('tool_executed', (event) => {
      this.logToolExecution(event.toolName, event.success, event.error);
    });

    this.eventUnsubscribers.push(sessionUpdateUnsub, toolUnsub);
  }

  async initialize() {
    try {
      this.currentSession = await this.sdk.sessions.createSession('Advanced Chat App', {
        tags: ['production', 'customer-support']
      });

      // Set session context and rules
      await this.sdk.sessions.addContext(
        this.currentSession.id,
        'You are a helpful customer support assistant with access to product documentation.'
      );

      await this.sdk.sessions.setRules(this.currentSession.id, [
        'Always be polite and professional',
        'Use the knowledge base to answer questions accurately',
        'Escalate complex issues to human agents',
        'Remember customer preferences and past interactions'
      ]);

    } catch (error) {
      console.error('Failed to initialize chat app:', error);
      throw error;
    }
  }

  async sendMessage(content: string, options: {
    imageUrl?: string;
    file?: File;
    streaming?: boolean;
  } = {}) {
    try {
      // Handle file uploads if provided
      if (options.file) {
        await this.uploadFile(options.file);
      }

      // Prepare message content
      const messageContent = this.buildMessageContent(content, options.imageUrl);

      const response = await this.sdk.sessions.sendMessage(
        this.currentSession.id,
        {
          messages: [{ role: 'user', content: messageContent }],
          model: 'switchblade-1',
          tools: true,
          stream: options.streaming ?? true
        },
        {
          onToken: (token) => this.streamToken(token),
          onToolCalls: (calls) => this.handleToolCalls(calls),
          onToolResults: (results) => this.handleToolResults(results),
          onComplete: (response) => this.handleComplete(response),
          onError: (error) => this.handleMessageError(error),
          onImageError: (error, url) => this.handleImageError(error, url)
        }
      );

      return response;

    } catch (error) {
      this.handleMessageError(error as Error);
      throw error;
    }
  }

  private buildMessageContent(text: string, imageUrl?: string) {
    if (!imageUrl) return text;

    return [
      { type: 'text', text },
      { type: 'image_url', image_url: { url: imageUrl } }
    ];
  }

  private async uploadFile(file: File) {
    const formData = new FormData();
    formData.append('file', file);

    await this.sdk.knowledge.ingestFiles(this.currentSession.id, [file]);
  }

  // Event handlers
  private streamToken(token: string) {
    // Update UI with streaming token
    this.updateUI('token', token);
  }

  private handleToolCalls(calls: any[]) {
    this.updateUI('tool_calls', calls);
    this.logActivity(`AI called tools: ${calls.map(c => c.function.name).join(', ')}`);
  }

  private handleToolResults(results: any[]) {
    this.updateUI('tool_results', results);
  }

  private handleComplete(response: any) {
    this.updateUI('complete', response);
    this.logActivity('Message completed');
  }

  private handleMessageError(error: Error | SwizzyError) {
    const errorMessage = error instanceof SwizzyError
      ? `API Error [${error.code}]: ${error.message}`
      : `Error: ${error.message}`;

    this.updateUI('error', errorMessage);
    this.logActivity(`Error: ${errorMessage}`);
  }

  private handleImageError(error: Error, url: string) {
    this.updateUI('image_error', { error: error.message, url });
  }

  private handleNewMessage(data: any) {
    // Handle real-time message updates from other sessions/users
    this.updateUI('new_message', data);
  }

  private logToolExecution(toolName: string, success: boolean, error?: string) {
    const status = success ? 'succeeded' : `failed: ${error}`;
    this.logActivity(`Tool '${toolName}' ${status}`);
  }

  private updateUI(type: string, data: any) {
    // Implement your UI update logic here
    console.log(`UI Update [${type}]:`, data);
  }

  private logActivity(message: string) {
    console.log(`[ChatApp] ${new Date().toISOString()}: ${message}`);
  }

  async cleanup() {
    // Unsubscribe from all events
    this.eventUnsubscribers.forEach(unsub => unsub());
    this.eventUnsubscribers = [];

    // Destroy SDK resources
    this.sdk.destroy();
  }

  // Custom document search implementation
  private async customDocumentSearch(query: string, fileType?: string) {
    // Implement custom search logic
    const results = await this.sdk.knowledge.queryKnowledgeBase(
      this.currentSession.id,
      { query, topK: 5 }
    );

    // Filter by file type if specified
    const filtered = fileType
      ? results.results.filter(r => r.metadata.source_type === fileType)
      : results.results;

    return {
      query,
      results: filtered,
      total: filtered.length
    };
  }
}

Document Analysis and Research Assistant

import { SwizzySDK } from 'swizzy-sdk';

class ResearchAssistant {
  private sdk: SwizzySDK;
  private researchSession: any;

  constructor(apiKey: string) {
    this.sdk = new SwizzySDK({
      baseUrl: 'https://your-worker.workers.dev',
      auth: { apiKey }
    });

    // Register research-specific tools
    this.setupResearchTools();
  }

  private setupResearchTools() {
    this.sdk.tools.registerBuiltInTool(['query_memory', 'add_to_memory']);

    // Custom research tools
    this.sdk.tools.registerTool(
      'analyze_sentiment',
      {
        name: 'analyze_sentiment',
        description: 'Analyze sentiment of text content',
        parameters: z.object({
          text: z.string().describe('Text to analyze'),
          aspects: z.array(z.string()).optional().describe('Specific aspects to analyze')
        })
      },
      async (args) => {
        // Implement sentiment analysis
        return await this.analyzeSentiment(args.text, args.aspects);
      }
    );
  }

  async initializeResearchProject(projectName: string, documents: File[]) {
    // Create dedicated research session
    this.researchSession = await this.sdk.sessions.createSession(
      `Research: ${projectName}`,
      { type: 'research', tags: ['analysis', 'documents'] }
    );

    // Set research context and rules
    await this.sdk.sessions.addContext(
      this.researchSession.id,
      'You are a research assistant specializing in document analysis and insight extraction.'
    );

    await this.sdk.sessions.setRules(this.researchSession.id, [
      'Always cite sources when referencing information',
      'Provide evidence-based conclusions',
      'Highlight key findings and implications',
      'Suggest follow-up questions or analyses'
    ]);

    // Ingest all research documents
    if (documents.length > 0) {
      console.log(`Ingesting ${documents.length} documents...`);
      const ingestResult = await this.sdk.knowledge.ingestFiles(
        this.researchSession.id,
        documents
      );
      console.log(`Processed ${ingestResult.stats.sourcesProcessed} sources, ${ingestResult.stats.vectorsStored} vectors`);
    }

    return this.researchSession;
  }

  async performAnalysis(analysisType: 'summary' | 'key_findings' | 'sentiment' | 'trends') {
    const prompts = {
      summary: 'Provide a comprehensive summary of all documents in the knowledge base',
      key_findings: 'Extract and list the key findings from the research documents',
      sentiment: 'Analyze the overall sentiment and tone across all documents',
      trends: 'Identify patterns, trends, and recurring themes in the documents'
    };

    const response = await this.sdk.sessions.sendMessage(
      this.researchSession.id,
      {
        messages: [{ role: 'user', content: prompts[analysisType] }],
        model: 'switchblade-1',
        tools: true
      }
    );

    return response.choices[0].message.content;
  }

  async askResearchQuestion(question: string) {
    const response = await this.sdk.sessions.sendMessage(
      this.researchSession.id,
      {
        messages: [{
          role: 'user',
          content: `Based on the documents in our knowledge base, ${question}`
        }],
        model: 'switchblade-1',
        tools: true
      }
    );

    return response.choices[0].message.content;
  }

  async addResearchNote(note: string, category: string) {
    // Use the add_to_memory tool to store research notes
    await this.sdk.tools.executeToolCallsFromResponse([{
      id: 'note_' + Date.now(),
      type: 'function',
      function: {
        name: 'add_to_memory',
        arguments: JSON.stringify({
          content: note,
          category: category
        })
      }
    }]);
  }

  async getResearchHistory() {
    return await this.sdk.sessions.getMessageHistory(this.researchSession.id, {
      limit: 50,
      sort_by: 'timestamp',
      sort_order: 'desc'
    });
  }

  // Helper method for sentiment analysis
  private async analyzeSentiment(text: string, aspects?: string[]) {
    // Implement sentiment analysis logic
    // This could integrate with external sentiment analysis APIs
    const sentiment = this.calculateSentiment(text);

    return {
      overall_sentiment: sentiment.score > 0 ? 'positive' : sentiment.score < 0 ? 'negative' : 'neutral',
      score: sentiment.score,
      confidence: sentiment.confidence,
      aspects: aspects ? this.analyzeAspects(text, aspects) : undefined
    };
  }

  private calculateSentiment(text: string) {
    // Simple sentiment calculation - replace with actual implementation
    const positiveWords = ['good', 'great', 'excellent', 'amazing', 'wonderful'];
    const negativeWords = ['bad', 'terrible', 'awful', 'horrible', 'disappointing'];

    const words = text.toLowerCase().split(/\s+/);
    let score = 0;

    words.forEach(word => {
      if (positiveWords.includes(word)) score += 1;
      if (negativeWords.includes(word)) score -= 1;
    });

    return {
      score: score / words.length,
      confidence: Math.min(Math.abs(score) / words.length, 1)
    };
  }

  private analyzeAspects(text: string, aspects: string[]) {
    // Analyze sentiment for specific aspects
    return aspects.map(aspect => ({
      aspect,
      sentiment: this.calculateSentiment(text),
      mentions: (text.toLowerCase().match(new RegExp(aspect.toLowerCase(), 'g')) || []).length
    }));
  }
}

// Usage example
async function runResearchAnalysis() {
  const assistant = new ResearchAssistant('your-api-key');

  // Initialize with research documents
  await assistant.initializeResearchProject('Q4 Financial Analysis', [
    /* File objects for PDFs, CSVs, etc. */
  ]);

  // Perform different types of analysis
  const summary = await assistant.performAnalysis('summary');
  const findings = await assistant.performAnalysis('key_findings');
  const sentiment = await assistant.performAnalysis('sentiment');

  // Ask specific research questions
  const trends = await assistant.askResearchQuestion(
    'What are the emerging trends in the financial data?'
  );

  // Add research notes
  await assistant.addResearchNote(
    'Strong correlation between marketing spend and revenue growth',
    'correlation'
  );

  console.log('Research Summary:', summary);
  console.log('Key Findings:', findings);
}

Migration Guide

From v0.x to v1.x

Breaking Changes

  • sendMessageWithTools() method removed - use sendMessage() with tools: true
  • useTools/executeTools parameters merged into single tools boolean
  • Image validation now throws errors for invalid images instead of silently failing
  • WebSocket event structure changed for better type safety

New Features

  • Base64 image support with automatic validation
  • Enhanced error callbacks with specific error types
  • Automatic image accessibility checking
  • Improved tool execution context and session awareness
  • Comprehensive TypeScript types for all APIs

Migration Examples

Old v0.x code:

// Old way
await sdk.sessions.sendMessageWithTools(sessionId, request, callbacks);

New v1.x code:

// New way
await sdk.sessions.sendMessage(sessionId, { ...request, tools: true }, callbacks);

Image handling migration:

// Old: Images might fail silently
await sdk.sessions.sendMessage(sessionId, {
  messages: [{ role: 'user', content: 'Analyze this image', imageUrl: 'invalid-url' }]
});

// New: Images are validated with proper error handling
await sdk.sessions.sendMessage(sessionId, {
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Analyze this image' },
      { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } }
    ]
  }]
}, {
  onImageError: (error, url) => console.error(`Image error: ${error.message}`)
});

Migration Guide

From v0.x to v1.x

Breaking Changes

  • sendMessageWithTools() method removed - use sendMessage() with tools: true
  • useTools/executeTools parameters merged into single tools boolean
  • Image validation now throws errors for invalid images instead of silently failing
  • WebSocket event structure changed for better type safety

New Features

  • Base64 image support with automatic validation
  • Enhanced error callbacks with specific error types
  • Automatic image accessibility checking
  • Improved tool execution context and session awareness
  • Comprehensive TypeScript types for all APIs

Migration Examples

Old v0.x code:

// Old way
await sdk.sessions.sendMessageWithTools(sessionId, request, callbacks);

New v1.x code:

// New way
await sdk.sessions.sendMessage(sessionId, { ...request, tools: true }, callbacks);

Image handling migration:

// Old: Images might fail silently
await sdk.sessions.sendMessage(sessionId, {
  messages: [{ role: 'user', content: 'Analyze this image', imageUrl: 'invalid-url' }]
});

// New: Images are validated with proper error handling
await sdk.sessions.sendMessage(sessionId, {
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Analyze this image' },
      { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } }
    ]
  }]
}, {
  onImageError: (error, url) => console.error(`Image error: ${error.message}`)
});

Contributing

We welcome contributions! Here's how to get started:

Development Setup

# Clone the repository
git clone https://github.com/your-org/swizzy-sdk.git
cd swizzy-sdk

# Install dependencies
pnpm install

# Build the project
pnpm build

# Run tests
pnpm test

# Start development server
pnpm dev

Code Style

  • Use TypeScript for all new code
  • Follow existing naming conventions
  • Add comprehensive JSDoc comments
  • Include unit tests for new features
  • Update documentation for API changes

Pull Request Process

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with tests
  4. Ensure all tests pass (pnpm test)
  5. Update documentation if needed
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Testing

// Example test structure
import { SwizzySDK } from '../src';

describe('SessionManager', () => {
  let sdk: SwizzySDK;

  beforeEach(() => {
    sdk = new SwizzySDK({
      baseUrl: 'http://localhost:8787',
      auth: { apiKey: 'test-key' }
    });
  });

  it('should create a session', async () => {
    const session = await sdk.sessions.createSession('Test Session');
    expect(session.id).toBeDefined();
    expect(session.title).toBe('Test Session');
  });
});

License

MIT License - see LICENSE file for details.

Changelog

v1.0.0 (Current)

  • Complete rewrite with improved architecture
  • Enhanced tool system with Zod validation
  • Multimodal message support (text + images)
  • Real-time streaming with comprehensive callbacks
  • WebSocket observability system
  • Comprehensive error handling and retry logic
  • Full TypeScript support with detailed types

v0.x (Legacy)

  • Basic chat functionality
  • Simple tool integration
  • Limited error handling

Roadmap

Upcoming Features

  • Voice Integration: Real-time voice conversations
  • Multi-user Sessions: Collaborative chat sessions
  • Advanced Analytics: Detailed usage metrics and insights
  • Plugin System: Extensible architecture for custom integrations
  • Offline Support: Local caching and sync capabilities

Community Requests

We're actively seeking feedback on:

  • Additional built-in tools
  • New AI model integrations
  • Enhanced UI components
  • Mobile SDK support

Support

Getting Help

Professional Support

For enterprise support, custom integrations, or priority bug fixes:


Built with ❤️ for the Breadcrumb AI Platform

Empowering developers to build the next generation of AI-powered applications