Skip to main content
Deprecation Notice: The Chat and Message API endpoints are being deprecated. Please migrate to Completions v2 API as soon as possible. New projects should start with Completions v2 immediately.

Overview

The Chat and Message API provided a convenient stateful approach to building conversational AI, with automatic session management and history tracking. As we evolve the Gloo platform, we’re transitioning to the Completions v2 API powered by a layered, production-ready AI architecture. This is a more flexible architecture that gives you complete control while delivering values-aligned AI with intelligent model routing. This guide will help you migrate your existing Chat API integrations to Completions v2, maintaining all the conversational capabilities you need while gaining access to powerful new features.

Why Migrate to Completions v2?

Values-Aligned Infrastructure

Access AI infrastructure built for human flourishing with values alignment and safety at every layer

Intelligent Routing

Auto-routing, model family selection, or direct model choiceβ€”optimize for quality, cost, and intent

Full Control

Manage conversation state, history, and context exactly how your application needs it

Theological Alignment

Built-in support for tradition-aware responses (evangelical, catholic, mainline)

Quick Start: Minimal Migration Path

If you want to get started quickly, here’s the fastest path to migrate:
1

Update your endpoint

Change from /ai/v1/message to /ai/v2/chat/completions
2

Set up conversation storage

Create a simple database table for storing chat history
CREATE TABLE messages (
  id UUID PRIMARY KEY,
  chat_id UUID NOT NULL,
  role VARCHAR(50) NOT NULL,
  content TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);
3

Build conversation history

Fetch messages from your database and format them for Completions v2
const messages = await db.query(
  'SELECT role, content FROM messages WHERE chat_id = $1 ORDER BY created_at',
  [chatId]
);
4

Call Completions v2 with auto-routing

Let Gloo automatically select the best model
const response = await fetch(
  'https://platform.ai.gloo.com/ai/v2/chat/completions',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      messages: messages,
      auto_routing: true,
      tradition: 'evangelical' // Optional: maintain theological alignment
    })
  }
);

Understanding the Changes

Architecture Comparison

Gloo managed:
  • Chat session creation
  • Message storage
  • History retrieval
  • Context management

Key Differences

FeatureChat APICompletions v2
State ManagementServer-managed by GlooClient-managed (your database)
Message HistoryAutomatic trackingYou build and send full context
Chat SessionsAuto-generated chat_idYou generate session IDs
API CallsIncremental (one message at a time)Full conversation context each time
Model SelectionFixed modelAuto-routing, family selection, or direct choice
Tradition SupportNot availableBuilt-in (evangelical, catholic, mainline)
Tool UseLimitedFull support
StreamingAvailableEnhanced streaming support

Step-by-Step Migration Guide

Step 1: Design Your Conversation Storage

The first step is creating your own storage for conversations. Here’s a recommended schema that mirrors the Chat API’s capabilities:
-- Chat sessions table
CREATE TABLE chats (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id VARCHAR(255) NOT NULL,
    title VARCHAR(500),
    tradition VARCHAR(50), -- 'evangelical', 'catholic', 'mainline'
    metadata JSONB,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    
    INDEX idx_user_id (user_id),
    INDEX idx_updated_at (updated_at)
);

-- Messages table
CREATE TABLE messages (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    chat_id UUID NOT NULL REFERENCES chats(id) ON DELETE CASCADE,
    role VARCHAR(50) NOT NULL, -- 'user', 'assistant', 'system'
    content TEXT NOT NULL,
    model VARCHAR(100), -- Store which model generated this response
    tokens_used INTEGER, -- Track token usage per message
    metadata JSONB,
    created_at TIMESTAMP DEFAULT NOW(),
    
    INDEX idx_chat_id_created (chat_id, created_at)
);

-- Optional: Store sources/citations
CREATE TABLE message_sources (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    source_title VARCHAR(500),
    source_url TEXT,
    publisher VARCHAR(255),
    relevance_score DECIMAL(3,2),
    created_at TIMESTAMP DEFAULT NOW()
);
Pro Tip: Include fields for tradition, model, and tokens_used to track theological alignment, model usage patterns, and costs over time.

Step 2: Build Conversation Management

Create functions to manage chat sessions and messages:
import { v4 as uuidv4 } from 'uuid';

interface Message {
  id: string;
  chat_id: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  model?: string;
  tokens_used?: number;
  created_at: Date;
}

interface Chat {
  id: string;
  user_id: string;
  title?: string;
  tradition?: 'evangelical' | 'catholic' | 'mainline';
  created_at: Date;
  updated_at: Date;
}

class ConversationManager {
  private db: any; // Your database client
  
  constructor(db: any) {
    this.db = db;
  }
  
  /**
   * Create a new chat session
   */
  async createChat(
    userId: string, 
    tradition?: Chat['tradition'],
    title?: string
  ): Promise<Chat> {
    const chat: Chat = {
      id: uuidv4(),
      user_id: userId,
      title: title || 'New Conversation',
      tradition,
      created_at: new Date(),
      updated_at: new Date()
    };
    
    await this.db.chats.insert(chat);
    return chat;
  }
  
  /**
   * Add a message to the conversation
   */
  async addMessage(
    chatId: string,
    role: Message['role'],
    content: string,
    metadata?: {
      model?: string;
      tokens_used?: number;
    }
  ): Promise<Message> {
    const message: Message = {
      id: uuidv4(),
      chat_id: chatId,
      role,
      content,
      model: metadata?.model,
      tokens_used: metadata?.tokens_used,
      created_at: new Date()
    };
    
    await this.db.messages.insert(message);
    
    // Update chat's updated_at timestamp
    await this.db.chats.update(
      { id: chatId },
      { updated_at: new Date() }
    );
    
    return message;
  }
  
  /**
   * Get conversation history formatted for Completions v2
   */
  async getConversationHistory(
    chatId: string,
    options?: {
      maxMessages?: number;
      maxTokens?: number;
    }
  ): Promise<Array<{ role: string; content: string }>> {
    const messages = await this.db.messages
      .find({ chat_id: chatId })
      .sort({ created_at: 'asc' })
      .toArray();
    
    // Apply any filtering/truncation based on options
    let filtered = messages;
    
    if (options?.maxMessages) {
      filtered = messages.slice(-options.maxMessages);
    }
    
    if (options?.maxTokens) {
      filtered = this.truncateByTokens(filtered, options.maxTokens);
    }
    
    // Format for Completions v2
    return filtered.map(msg => ({
      role: msg.role,
      content: msg.content
    }));
  }
  
  /**
   * Truncate messages to fit within token limit
   */
  private truncateByTokens(
    messages: Message[],
    maxTokens: number
  ): Message[] {
    let totalTokens = 0;
    const result: Message[] = [];
    
    // Keep system message if present
    if (messages[0]?.role === 'system') {
      result.push(messages[0]);
      totalTokens += this.estimateTokens(messages[0].content);
      messages = messages.slice(1);
    }
    
    // Add messages from most recent, working backwards
    for (let i = messages.length - 1; i >= 0; i--) {
      const tokens = this.estimateTokens(messages[i].content);
      if (totalTokens + tokens > maxTokens) break;
      
      result.unshift(messages[i]);
      totalTokens += tokens;
    }
    
    return result;
  }
  
  /**
   * Rough token estimation (4 chars β‰ˆ 1 token)
   */
  private estimateTokens(text: string): number {
    return Math.ceil(text.length / 4);
  }
  
  /**
   * Get all chats for a user
   */
  async getUserChats(userId: string): Promise<Chat[]> {
    return await this.db.chats
      .find({ user_id: userId })
      .sort({ updated_at: 'desc' })
      .toArray();
  }
  
  /**
   * Delete a conversation
   */
  async deleteChat(chatId: string): Promise<void> {
    await this.db.messages.deleteMany({ chat_id: chatId });
    await this.db.chats.delete({ id: chatId });
  }
}

Step 3: Integrate with Completions v2

Now integrate your conversation manager with the Completions v2 API:
interface CompletionsV2Config {
  apiKey: string;
  baseUrl?: string;
}

interface CompletionOptions {
  tradition?: 'evangelical' | 'catholic' | 'mainline';
  autoRouting?: boolean;
  model?: string;
  modelFamily?: 'openai' | 'anthropic' | 'google' | 'open source';
  stream?: boolean;
  temperature?: number;
  maxTokens?: number;
}

class GlooCompletionsV2Client {
  private apiKey: string;
  private baseUrl: string;
  
  constructor(config: CompletionsV2Config) {
    this.apiKey = config.apiKey;
    this.baseUrl = config.baseUrl || 'https://platform.ai.gloo.com';
  }
  
  async createCompletion(
    messages: Array<{ role: string; content: string }>,
    options: CompletionOptions = {}
  ) {
    const {
      tradition,
      autoRouting = true,
      model,
      modelFamily,
      stream = false,
      temperature,
      maxTokens
    } = options;
    
    // Build request body based on routing strategy
    const body: any = {
      messages,
      stream
    };
    
    // Routing configuration
    if (autoRouting) {
      body.auto_routing = true;
    } else if (model) {
      body.model = model;
      body.auto_routing = false;
    } else if (modelFamily) {
      body.model_family = modelFamily;
      body.auto_routing = false;
    }
    
    // Optional parameters
    if (tradition) body.tradition = tradition;
    if (temperature !== undefined) body.temperature = temperature;
    if (maxTokens) body.max_tokens = maxTokens;
    
    const response = await fetch(
      `${this.baseUrl}/ai/v2/chat/completions`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body)
      }
    );
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Completions API error: ${error.message || response.statusText}`);
    }
    
    const data = await response.json();
    return data;
  }
}

// Complete chat service combining conversation management and Completions v2
class ChatService {
  private conversationManager: ConversationManager;
  private completionsClient: GlooCompletionsV2Client;
  
  constructor(
    conversationManager: ConversationManager,
    completionsClient: GlooCompletionsV2Client
  ) {
    this.conversationManager = conversationManager;
    this.completionsClient = completionsClient;
  }
  
  /**
   * Create a new conversation
   */
  async createConversation(
    userId: string,
    tradition?: 'evangelical' | 'catholic' | 'mainline',
    title?: string
  ) {
    return await this.conversationManager.createChat(userId, tradition, title);
  }
  
  /**
   * Send a message and get AI response
   */
  async sendMessage(
    chatId: string,
    userMessage: string,
    options?: CompletionOptions
  ): Promise<{
    response: string;
    model: string;
    tokensUsed: number;
  }> {
    // 1. Store user message
    await this.conversationManager.addMessage(chatId, 'user', userMessage);
    
    // 2. Get conversation history
    const history = await this.conversationManager.getConversationHistory(
      chatId,
      {
        maxMessages: 50, // Keep last 50 messages
        maxTokens: 8000  // Adjust based on your model's context window
      }
    );
    
    // 3. Get chat details for tradition
    const chat = await this.conversationManager.db.chats.findOne({ id: chatId });
    const completionOptions = {
      ...options,
      tradition: options?.tradition || chat?.tradition
    };
    
    // 4. Call Completions v2
    const completion = await this.completionsClient.createCompletion(
      history,
      completionOptions
    );
    
    const assistantMessage = completion.choices[0].message.content;
    const tokensUsed = completion.usage.total_tokens;
    const model = completion.model;
    
    // 5. Store assistant response
    await this.conversationManager.addMessage(
      chatId,
      'assistant',
      assistantMessage,
      {
        model,
        tokens_used: tokensUsed
      }
    );
    
    return {
      response: assistantMessage,
      model,
      tokensUsed
    };
  }
  
  /**
   * Get conversation history
   */
  async getHistory(chatId: string) {
    return await this.conversationManager.getConversationHistory(chatId);
  }
  
  /**
   * List user's conversations
   */
  async listConversations(userId: string) {
    return await this.conversationManager.getUserChats(userId);
  }
  
  /**
   * Delete a conversation
   */
  async deleteConversation(chatId: string) {
    await this.conversationManager.deleteChat(chatId);
  }
}

// Usage Example
const conversationManager = new ConversationManager(db);
const completionsClient = new GlooCompletionsV2Client({
  apiKey: process.env.GLOO_API_KEY!
});
const chatService = new ChatService(conversationManager, completionsClient);

// Create a new conversation
const chat = await chatService.createConversation(
  'user-123',
  'evangelical',
  'Finding Purpose'
);

// Send messages
const response1 = await chatService.sendMessage(
  chat.id,
  'How can I find meaning and purpose when facing life\'s greatest challenges?',
  { autoRouting: true }
);

console.log('AI Response:', response1.response);
console.log('Model Used:', response1.model);
console.log('Tokens:', response1.tokensUsed);

// Continue the conversation
const response2 = await chatService.sendMessage(
  chat.id,
  'Can you give me practical steps I can take today?'
);

console.log('Follow-up Response:', response2.response);

Step 4: Migrate Existing Chat Data

Before the Chat API is deprecated, export your existing conversations:
async function migrateExistingChats(userId: string) {
  console.log(`Starting migration for user: ${userId}`);
  
  // 1. Get all chats from old Chat API
  const chatsResponse = await fetch(
    `https://platform.ai.gloo.com/ai/v1/chat?user_id=${userId}`,
    {
      headers: { 'Authorization': `Bearer ${oldApiToken}` }
    }
  );
  const oldChats = await chatsResponse.json();
  
  console.log(`Found ${oldChats.length} chats to migrate`);
  
  for (const oldChat of oldChats) {
    try {
      // 2. Get messages for this chat
      const messagesResponse = await fetch(
        `https://platform.ai.gloo.com/ai/v1/chat/${oldChat.id}/messages`,
        {
          headers: { 'Authorization': `Bearer ${oldApiToken}` }
        }
      );
      const oldMessages = await messagesResponse.json();
      
      // 3. Create new chat in your system
      const newChat = await conversationManager.createChat(
        oldChat.user_id,
        undefined, // Set tradition if you have this info
        oldChat.title || 'Migrated Conversation'
      );
      
      // 4. Migrate all messages
      for (const oldMessage of oldMessages.messages) {
        await conversationManager.addMessage(
          newChat.id,
          oldMessage.role,
          oldMessage.content || oldMessage.message
        );
      }
      
      console.log(`βœ“ Migrated chat ${oldChat.id} β†’ ${newChat.id}`);
      
    } catch (error) {
      console.error(`βœ— Failed to migrate chat ${oldChat.id}:`, error);
    }
  }
  
  console.log('Migration complete!');
}

// Run migration
await migrateExistingChats('user-123');
Important: Run your migration script as soon as you can to ensure all data is safely transferred. Test thoroughly with a subset of users first.

Leveraging Completions v2 Features

1. Intelligent Model Routing

Take advantage of Gloo’s routing capabilities:

2. Tradition-Aware Responses

Maintain theological alignment throughout conversations:
// Set tradition at chat creation
const chat = await chatService.createConversation(
  userId,
  'catholic', // evangelical, catholic, or mainline
  'Understanding the Trinity'
);

// The tradition is automatically applied to all subsequent messages
const response = await chatService.sendMessage(
  chat.id,
  'How do we understand the Holy Spirit?'
);
// Response will be aligned with Catholic theological perspective

3. Streaming Responses

Implement real-time streaming for better UX:
async function* streamChatResponse(
  chatId: string,
  userMessage: string
) {
  // Store user message
  await conversationManager.addMessage(chatId, 'user', userMessage);
  
  // Get history
  const history = await conversationManager.getConversationHistory(chatId);
  
  // Stream from Completions v2
  const response = await fetch(
    'https://platform.ai.gloo.com/ai/v2/chat/completions',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        messages: history,
        auto_routing: true,
        stream: true
      })
    }
  );
  
  const reader = response.body?.getReader();
  const decoder = new TextDecoder();
  let fullResponse = '';
  
  while (true) {
    const { done, value } = await reader!.read();
    if (done) break;
    
    const chunk = decoder.decode(value);
    const lines = chunk.split('\n').filter(line => line.trim());
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6);
        if (data === '[DONE]') {
          // Store complete response
          await conversationManager.addMessage(
            chatId,
            'assistant',
            fullResponse
          );
          return;
        }
        
        const parsed = JSON.parse(data);
        const content = parsed.choices[0]?.delta?.content;
        if (content) {
          fullResponse += content;
          yield content;
        }
      }
    }
  }
}

// Usage with Express
app.post('/api/chat/:chatId/stream', async (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  
  for await (const chunk of streamChatResponse(req.params.chatId, req.body.message)) {
    res.write(`data: ${JSON.stringify({ chunk })}\n\n`);
  }
  
  res.write('data: [DONE]\n\n');
  res.end();
});

Advanced Patterns

Pattern 1: Conversation Branching

Create alternative conversation paths:
async function createBranch(
  originalChatId: string,
  fromMessageId: string,
  newBranchTitle: string
): Promise<Chat> {
  // Get original chat
  const originalChat = await db.chats.findOne({ id: originalChatId });
  
  // Create new chat
  const branchChat = await conversationManager.createChat(
    originalChat.user_id,
    originalChat.tradition,
    `${newBranchTitle} (Branch)`
  );
  
  // Copy messages up to branch point
  const messages = await db.messages
    .find({ chat_id: originalChatId })
    .sort({ created_at: 'asc' })
    .toArray();
  
  for (const msg of messages) {
    if (msg.id === fromMessageId) break;
    await conversationManager.addMessage(branchChat.id, msg.role, msg.content);
  }
  
  return branchChat;
}

Pattern 2: Multi-Source Integration

Leverage multiple content publishers:
interface PublisherFilter {
  publishers?: string[];
  sources_limit?: number;
}

async function sendMessageWithSources(
  chatId: string,
  userMessage: string,
  filters?: PublisherFilter
): Promise<any> {
  await conversationManager.addMessage(chatId, 'user', userMessage);
  
  const history = await conversationManager.getConversationHistory(chatId);
  
  // Add system message with publisher context if needed
  if (filters?.publishers) {
    history.unshift({
      role: 'system',
      content: `Focus on information from these publishers: ${filters.publishers.join(', ')}`
    });
  }
  
  const completion = await completionsClient.createCompletion(history, {
    autoRouting: true
  });
  
  // Store response with source metadata
  const response = completion.choices[0].message.content;
  await conversationManager.addMessage(chatId, 'assistant', response);
  
  return {
    response,
    sources: completion.sources || []
  };
}

Pattern 3: Conversation Summarization

Auto-summarize long conversations:
async function summarizeConversation(chatId: string): Promise<string> {
  const messages = await db.messages
    .find({ chat_id: chatId })
    .sort({ created_at: 'asc' })
    .toArray();
  
  if (messages.length < 10) {
    return ''; // Don't summarize short conversations
  }
  
  // Create summary prompt
  const conversationText = messages
    .map(m => `${m.role}: ${m.content}`)
    .join('\n\n');
  
  const completion = await completionsClient.createCompletion(
    [
      {
        role: 'system',
        content: 'You are a helpful assistant that creates concise 2-3 sentence summaries of conversations.'
      },
      {
        role: 'user',
        content: `Please summarize this conversation:\n\n${conversationText}`
      }
    ],
    {
      autoRouting: true,
      maxTokens: 150
    }
  );
  
  const summary = completion.choices[0].message.content;
  
  // Update chat title with summary
  await db.chats.update(
    { id: chatId },
    {
      title: summary.slice(0, 100),
      metadata: { full_summary: summary }
    }
  );
  
  return summary;
}

Testing Your Migration

Unit Tests

import { describe, it, expect, beforeEach } from 'vitest';

describe('Chat Service Migration', () => {
  let chatService: ChatService;
  let testUserId: string;
  
  beforeEach(() => {
    const conversationManager = new ConversationManager(testDb);
    const completionsClient = new GlooCompletionsV2Client({
      apiKey: process.env.TEST_GLOO_API_KEY!
    });
    chatService = new ChatService(conversationManager, completionsClient);
    testUserId = 'test-user-123';
  });
  
  it('should create a new conversation with tradition', async () => {
    const chat = await chatService.createConversation(
      testUserId,
      'evangelical',
      'Test Chat'
    );
    
    expect(chat.id).toBeDefined();
    expect(chat.user_id).toBe(testUserId);
    expect(chat.tradition).toBe('evangelical');
  });
  
  it('should send and receive messages', async () => {
    const chat = await chatService.createConversation(testUserId);
    const result = await chatService.sendMessage(
      chat.id,
      'What brings meaning to life?',
      { autoRouting: true }
    );
    
    expect(result.response).toBeDefined();
    expect(result.model).toBeDefined();
    expect(result.tokensUsed).toBeGreaterThan(0);
  });
  
  it('should maintain conversation context', async () => {
    const chat = await chatService.createConversation(testUserId);
    
    await chatService.sendMessage(chat.id, 'My name is John');
    await chatService.sendMessage(chat.id, 'What is my name?');
    
    const history = await chatService.getHistory(chat.id);
    expect(history.length).toBe(4); // 2 user + 2 assistant messages
  });
  
  it('should respect tradition parameter', async () => {
    const chat = await chatService.createConversation(
      testUserId,
      'catholic'
    );
    
    const result = await chatService.sendMessage(
      chat.id,
      'Tell me about the Eucharist'
    );
    
    // Response should reflect Catholic perspective
    expect(result.response).toBeDefined();
  });
});

Integration Tests

describe('End-to-End Conversation Flow', () => {
  it('should handle complete conversation lifecycle', async () => {
    // Create conversation
    const chat = await chatService.createConversation(
      'user-123',
      'evangelical',
      'Purpose and Meaning'
    );
    
    // Send multiple messages
    const response1 = await chatService.sendMessage(
      chat.id,
      'How can I find purpose in difficult times?',
      { autoRouting: true }
    );
    expect(response1.response).toContain('purpose' || 'meaning');
    
    const response2 = await chatService.sendMessage(
      chat.id,
      'Can you give practical steps?'
    );
    expect(response2.response).toBeDefined();
    
    // Verify history
    const history = await chatService.getHistory(chat.id);
    expect(history.length).toBe(4);
    
    // Test context retention
    const response3 = await chatService.sendMessage(
      chat.id,
      'Tell me more about the first step'
    );
    expect(response3.response).toBeDefined();
    
    // Delete conversation
    await chatService.deleteConversation(chat.id);
    const chats = await chatService.listConversations('user-123');
    expect(chats.find(c => c.id === chat.id)).toBeUndefined();
  });
});

Troubleshooting

Problem: API returns error about token limitSolution: Implement proper context truncation in your getConversationHistory method:
const history = await conversationManager.getConversationHistory(
  chatId,
  {
    maxMessages: 50,
    maxTokens: 8000 // Adjust based on model's context window
  }
);
For models with smaller context windows, consider conversation summarization to maintain essential context while reducing token count.
Problem: Messages appearing in wrong chronological orderSolution: Ensure your database queries always sort by created_at ascending:
SELECT * FROM messages 
WHERE chat_id = ? 
ORDER BY created_at ASC  -- Critical!
Also use transactions when writing multiple messages to prevent race conditions.
Problem: Responses don’t reflect the specified theological traditionSolution: Verify tradition is being passed to Completions v2:
// Check that tradition is in the request
console.log('Tradition:', completionOptions.tradition);

// Ensure it's a valid value
const validTraditions = ['evangelical', 'catholic', 'mainline'];
if (tradition && !validTraditions.includes(tradition)) {
  throw new Error(`Invalid tradition: ${tradition}`);
}
Problem: Responses taking too longSolutions:
  1. Use Streaming: Provides immediate feedback to users
    { stream: true }
    
  2. Optimize Context: Only send necessary messages
    { maxMessages: 20 } // Fewer messages = faster
    
  3. Cache Recent Conversations: Use Redis for hot data
    const cached = await redis.get(`chat:${chatId}:messages`);
    if (cached) return JSON.parse(cached);
    
  4. Choose Appropriate Models: Use model family selection
    { modelFamily: 'openai' } // Generally faster than anthropic
    
Problem: Race conditions with simultaneous messagesSolution: Use database transactions and row-level locking:
await db.transaction(async (trx) => {
  // Lock the chat row
  await trx.raw('SELECT * FROM chats WHERE id = ? FOR UPDATE', [chatId]);
  
  // Store message
  await trx('messages').insert(userMessage);
  
  // Get history and call API
  const history = await trx('messages')
    .where({ chat_id: chatId })
    .orderBy('created_at');
  
  const completion = await completionsClient.createCompletion(history);
  
  // Store response
  await trx('messages').insert(assistantMessage);
});

Migration Checklist

1

Planning & Design

  • Review current Chat API usage patterns
  • Design database schema for conversations
  • Plan context window management strategy
  • Decide on model routing strategy (auto-routing recommended)
  • Identify which chats need tradition parameter
  • Create migration timeline
2

Development

  • Set up conversation storage (PostgreSQL/MongoDB)
  • Implement ConversationManager class
  • Integrate GlooCompletionsV2Client
  • Build ChatService combining both
  • Add context window management
  • Implement error handling and retries
  • Add logging and monitoring
  • Set up streaming support (optional)
3

Testing

  • Write unit tests for conversation management
  • Write integration tests for full chat flows
  • Test with various message lengths
  • Test context window truncation
  • Test concurrent request handling
  • Verify tradition parameter works correctly
  • Load test with production-like volumes
4

Data Migration

  • Create data export script
  • Export existing chats from Chat API
  • Validate exported data
  • Import into new system
  • Verify data integrity
  • Test sample conversations work correctly
5

Deployment

  • Deploy new infrastructure
  • Run migration script for all users
  • Enable new system for subset of users
  • Monitor performance and errors closely
  • Gradually roll out to all users
  • Keep Chat API as fallback initially
6

Cutover

  • Switch all traffic to new system
  • Monitor for 48 hours
  • Disable Chat API integrations
  • Update documentation
  • Communicate completion to team
  • Celebrate! πŸŽ‰

Benefits of Migrating

Values-Aligned AI

Built-in values alignment and safety at every layer

Intelligent Routing

Auto-routing optimizes every query for quality, cost, and intent automatically

Full Control

Complete control over storage, retention, context management, and conversation workflows

Theological Alignment

Native support for tradition-aware responses (evangelical, catholic, mainline)

Advanced Features

Build sophisticated UX with branching, editing, summarization, and more

Cost Optimization

Control what context you send and optimize token usage based on your needs

Better Performance

Optimize with caching, indexing, and custom logic for your specific use case

Enhanced Privacy

Keep sensitive conversations in your own infrastructure with full control

Support and Resources

Next Steps

Questions about migration? We’re here to help! Reach out to hello@gloo.us or use the support link in the top navigation.