ai.matey.core
The foundational package providing the Bridge and Router classes - the heart of ai.matey’s universal adapter system.
Installation
Section titled “Installation”npm install ai.matey.core ai.matey.frontend ai.matey.backendOverview
Section titled “Overview”ai.matey.core provides two main classes:
- Bridge: Connects a single frontend adapter to a single backend adapter
- Router: Connects a frontend adapter to multiple backend adapters with intelligent routing
Bridge
Section titled “Bridge”Basic Usage
Section titled “Basic Usage”import { Bridge } from 'ai.matey.core';import { OpenAIFrontendAdapter } from 'ai.matey.frontend/openai';import { AnthropicBackendAdapter } from 'ai.matey.backend/anthropic';
const bridge = new Bridge( new OpenAIFrontendAdapter(), new AnthropicBackendAdapter({ apiKey: 'your-key' }));
const response = await bridge.chat({ model: 'gpt-4', messages: [{ role: 'user', content: 'Hello!' }]});Constructor
Section titled “Constructor”constructor( frontendAdapter: FrontendAdapter, backendAdapter: BackendAdapter)Parameters:
frontendAdapter: Adapter that defines the input format (OpenAI, Anthropic, etc.)backendAdapter: Adapter that defines the AI provider (OpenAI, Anthropic, Gemini, etc.)
Methods
Section titled “Methods”chat()
Section titled “chat()”Execute a non-streaming chat completion:
async chat(request: IRChatCompletionRequest): Promise<IRChatCompletionResponse>Parameters:
request: Chat completion request in the frontend adapter’s format
Returns:
- Promise resolving to the response in the frontend adapter’s format
Example:
const response = await bridge.chat({ model: 'gpt-4', messages: [ { role: 'system', content: 'You are helpful.' }, { role: 'user', content: 'What is 2+2?' } ], temperature: 0.7, max_tokens: 100});
console.log(response.choices[0].message.content); // "4"chatStream()
Section titled “chatStream()”Execute a streaming chat completion:
async chatStream(request: IRChatCompletionRequest): Promise<AsyncIterable<IRChatCompletionChunk>>Parameters:
request: Chat completion request withstream: true
Returns:
- Promise resolving to an async iterable of response chunks
Example:
const stream = await bridge.chatStream({ model: 'gpt-4', messages: [{ role: 'user', content: 'Count to 5' }], stream: true});
for await (const chunk of stream) { const content = chunk.choices?.[0]?.delta?.content; if (content) { process.stdout.write(content); }}Add middleware to the request/response pipeline:
use(middleware: Middleware): voidParameters:
middleware: Middleware function or object
Example:
import { createLoggingMiddleware } from 'ai.matey.middleware';
bridge.use(createLoggingMiddleware({ level: 'info' }));bridge.use(createCachingMiddleware({ ttl: 3600 }));Properties
Section titled “Properties”frontendAdapter
Section titled “frontendAdapter”The frontend adapter instance (read-only):
readonly frontendAdapter: FrontendAdapterbackendAdapter
Section titled “backendAdapter”The backend adapter instance (read-only):
readonly backendAdapter: BackendAdapterRouter
Section titled “Router”The Router extends Bridge functionality to support multiple backend adapters with intelligent routing strategies.
Basic Usage
Section titled “Basic Usage”import { Router } from 'ai.matey.core';import { OpenAIFrontendAdapter } from 'ai.matey.frontend/openai';import { AnthropicBackendAdapter } from 'ai.matey.backend/anthropic';import { OpenAIBackendAdapter } from 'ai.matey.backend/openai';
const router = new Router(new OpenAIFrontendAdapter(), { backends: [ new AnthropicBackendAdapter({ apiKey: process.env.ANTHROPIC_API_KEY }), new OpenAIBackendAdapter({ apiKey: process.env.OPENAI_API_KEY }) ], strategy: 'round-robin'});Constructor
Section titled “Constructor”constructor( frontendAdapter: FrontendAdapter, options: RouterOptions)Parameters:
frontendAdapter: Frontend adapter instanceoptions: Router configuration
RouterOptions
Section titled “RouterOptions”interface RouterOptions { backends: BackendAdapter[]; strategy: 'round-robin' | 'priority' | 'random' | 'weighted' | 'custom'; customStrategy?: (request: IRChatCompletionRequest, backends: BackendAdapter[]) => number; fallbackOnError?: boolean; healthCheck?: { enabled: boolean; interval: number; timeout?: number; };}Fields:
backends: Array of backend adapter instancesstrategy: Routing strategy to usecustomStrategy: Custom routing function (required if strategy is ‘custom’)fallbackOnError: Automatically try next backend on failure (default: false)healthCheck: Health check configuration (optional)
Routing Strategies
Section titled “Routing Strategies”Round-Robin
Section titled “Round-Robin”Distributes requests evenly across all backends:
const router = new Router(new OpenAIFrontendAdapter(), { backends: [backend1, backend2, backend3], strategy: 'round-robin'});
// Request 1 → backend1// Request 2 → backend2// Request 3 → backend3// Request 4 → backend1 (cycles)Priority
Section titled “Priority”Uses backends in order, falling back on failure:
const router = new Router(new OpenAIFrontendAdapter(), { backends: [primaryBackend, secondaryBackend, tertiaryBackend], strategy: 'priority', fallbackOnError: true});
// Always tries primaryBackend first// Falls back to secondaryBackend if primary fails// Falls back to tertiaryBackend if secondary failsRandom
Section titled “Random”Randomly selects a backend for each request:
const router = new Router(new OpenAIFrontendAdapter(), { backends: [backend1, backend2, backend3], strategy: 'random'});Weighted
Section titled “Weighted”Distributes requests based on weights:
const router = new Router(new OpenAIFrontendAdapter(), { backends: [ { backend: backend1, weight: 70 }, // 70% of traffic { backend: backend2, weight: 20 }, // 20% of traffic { backend: backend3, weight: 10 } // 10% of traffic ], strategy: 'weighted'});Custom
Section titled “Custom”Implement your own routing logic:
const router = new Router(new OpenAIFrontendAdapter(), { backends: [cheapBackend, fastBackend, powerfulBackend], strategy: 'custom', customStrategy: (request, backends) => { // Route based on message length const messageLength = JSON.stringify(request.messages).length;
if (messageLength < 100) return 0; // Cheap if (messageLength < 500) return 1; // Fast return 2; // Powerful }});Methods
Section titled “Methods”Router inherits all Bridge methods (chat(), chatStream(), use()) plus:
getBackendHealth()
Section titled “getBackendHealth()”Get health status of all backends:
async getBackendHealth(): Promise<Record<string, BackendHealth>>Returns:
- Object mapping backend names to health metrics
Example:
const health = await router.getBackendHealth();console.log(health);/*{ anthropic: { healthy: true, latency: 1200, errorRate: 0 }, openai: { healthy: true, latency: 1500, errorRate: 0.02 }, groq: { healthy: false, latency: 5000, errorRate: 0.5 }}*/Events
Section titled “Events”Router emits events for monitoring:
router.on('backend:failed', ({ backend, error }) => { console.log(`Backend ${backend} failed: ${error.message}`);});
router.on('backend:switch', ({ from, to }) => { console.log(`Switched from ${from} to ${to}`);});
router.on('backend:health', ({ backend, healthy }) => { console.log(`${backend}: ${healthy ? 'Healthy' : 'Unhealthy'}`);});Middleware System
Section titled “Middleware System”Both Bridge and Router support middleware for intercepting and transforming requests/responses.
Middleware Interface
Section titled “Middleware Interface”interface Middleware { name: string; execute( request: IRChatCompletionRequest, next: (request: IRChatCompletionRequest) => Promise<IRChatCompletionResponse> ): Promise<IRChatCompletionResponse>;}Creating Custom Middleware
Section titled “Creating Custom Middleware”function createTimingMiddleware() { return { name: 'timing', async execute(request, next) { const start = Date.now(); const response = await next(request); const duration = Date.now() - start;
console.log(`Request took ${duration}ms`);
return response; } };}
bridge.use(createTimingMiddleware());Middleware Order
Section titled “Middleware Order”Middleware executes in reverse order (last added runs first):
bridge.use(middleware1); // Runs 3rdbridge.use(middleware2); // Runs 2ndbridge.use(middleware3); // Runs 1st
// Request → middleware3 → middleware2 → middleware1 → Backend// Response ← middleware3 ← middleware2 ← middleware1 ← BackendBest practice order:
- Logging (first - sees everything)
- Retry (second - handles failures)
- Caching (third - caches successful responses)
- Transform (fourth - modifies data)
Error Handling
Section titled “Error Handling”Error Types
Section titled “Error Types”class BridgeError extends Error { constructor(message: string, public code: string) { super(message); this.name = 'BridgeError'; }}Error codes:
ADAPTER_ERROR: Frontend/backend adapter errorVALIDATION_ERROR: Invalid request formatNETWORK_ERROR: Network/connection errorRATE_LIMIT_ERROR: Rate limit exceededTIMEOUT_ERROR: Request timeoutAUTH_ERROR: Authentication failed
Handling Errors
Section titled “Handling Errors”try { const response = await bridge.chat(request);} catch (error) { if (error instanceof BridgeError) { switch (error.code) { case 'RATE_LIMIT_ERROR': console.log('Rate limited, retrying...'); break; case 'AUTH_ERROR': console.log('Invalid API key'); break; default: console.log('Unknown error:', error.message); } }}TypeScript Types
Section titled “TypeScript Types”Key Interfaces
Section titled “Key Interfaces”// Requestinterface IRChatCompletionRequest { model: string; messages: IRMessage[]; temperature?: number; max_tokens?: number; top_p?: number; stream?: boolean; tools?: IRTool[]; // ... more parameters}
// Responseinterface IRChatCompletionResponse { id: string; object: 'chat.completion'; created: number; model: string; choices: IRChoice[]; usage?: IRUsage;}
// Messageinterface IRMessage { role: 'system' | 'user' | 'assistant' | 'tool'; content: string | IRContent[]; name?: string;}
// Streaming chunkinterface IRChatCompletionChunk { id: string; object: 'chat.completion.chunk'; created: number; model: string; choices: IRStreamChoice[];}See IR Format Documentation for complete type definitions.
Best Practices
Section titled “Best Practices”1. Reuse Instances
Section titled “1. Reuse Instances”Create Bridge/Router instances once and reuse:
// ✅ Good - reuse instanceconst bridge = new Bridge(frontend, backend);
async function chat(message) { return await bridge.chat({ model: 'gpt-4', messages: [{ role: 'user', content: message }] });}
// ❌ Bad - creates new instance each callasync function chat(message) { const bridge = new Bridge(frontend, backend); return await bridge.chat({ model: 'gpt-4', messages: [{ role: 'user', content: message }] });}2. Use TypeScript
Section titled “2. Use TypeScript”Take advantage of full type safety:
import type { IRChatCompletionRequest, IRChatCompletionResponse } from 'ai.matey.types';
async function chat(request: IRChatCompletionRequest): Promise<IRChatCompletionResponse> { return await bridge.chat(request);}3. Handle Errors
Section titled “3. Handle Errors”Always wrap calls in try-catch:
try { const response = await bridge.chat(request); return response.choices[0].message.content;} catch (error) { console.error('Chat failed:', error); throw error;}4. Use Middleware
Section titled “4. Use Middleware”Add production features with middleware:
import { createLoggingMiddleware, createRetryMiddleware, createCachingMiddleware } from 'ai.matey.middleware';
bridge.use(createLoggingMiddleware({ level: 'info' }));bridge.use(createRetryMiddleware({ maxAttempts: 3 }));bridge.use(createCachingMiddleware({ ttl: 3600 }));See Also
Section titled “See Also”- Frontend Adapters - Available frontend adapters
- Backend Adapters - Available backend adapters
- Middleware - Available middleware
- IR Format - Intermediate representation details
- Tutorial: Simple Bridge - Step-by-step guide