swizzy-sdk
v1.0.0
Published
Swizzy SDK - Intelligent Chat with Memory for AI Applications
Downloads
11
Maintainers
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
- Installation
- Quick Start
- Core Concepts
- API Reference
- Advanced Features
- Error Handling and Troubleshooting
- Examples
- Migration Guide
- Contributing
- License
- Changelog
- Roadmap
- Support
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-sdkQuick 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
- Tool Registration: Define available tools with their parameters and handlers
- AI Decision: The model analyzes the conversation and decides which tools to call
- Parameter Extraction: SDK parses the tool call parameters from the model's response
- Execution: Tools run with proper validation and error handling
- 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 documentsadd_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:
- Includes available tools in the request to the AI model
- Monitors the response for tool calls
- Automatically executes tools when detected
- Formats results as tool messages
- Sends a follow-up request with tool results
- 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 displayTool 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:
- Tool Registration: Define available tools
- LLM Decision: Model decides which tools to call
- Parameter Extraction: SDK parses tool call parameters
- Execution: Tools run with proper validation
- Result Formatting: Results sent back to LLM
- 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 resultStreaming
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 failureIMAGE_NOT_ACCESSIBLE: Image URL is invalid or inaccessibleIMAGE_INVALID_FORMAT: Image format not supportedTOOL_EXECUTION_FAILED: Tool execution errorTOOL_VALIDATION_FAILED: Tool parameter validation errorSESSION_NOT_FOUND: Session ID doesn't existAUTHENTICATION_FAILED: Invalid API key or authenticationRATE_LIMIT_EXCEEDED: API rate limit exceededFILE_TOO_LARGE: Uploaded file exceeds size limitsUNSUPPORTED_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 - usesendMessage()withtools: trueuseTools/executeToolsparameters merged into singletoolsboolean- 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 - usesendMessage()withtools: trueuseTools/executeToolsparameters merged into singletoolsboolean- 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 devCode 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
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Ensure all tests pass (
pnpm test) - Update documentation if needed
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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
- GitHub Issues: Report bugs and request features
- Documentation: Full API Reference
- Discord Community: Join our community
- Stack Overflow: Tag questions with
swizzy-sdk
Professional Support
For enterprise support, custom integrations, or priority bug fixes:
- Email: [email protected]
- Enterprise plans available with dedicated support
Built with ❤️ for the Breadcrumb AI Platform
Empowering developers to build the next generation of AI-powered applications
