Skip to main content
Glama
conversation-manager.ts3.76 kB
import { randomBytes } from "node:crypto"; import { logger } from "./logger.js"; import type { ChatMessage, ConversationState } from "./types.js"; export class ConversationManager { private conversations: Map<string, ConversationState> = new Map(); private truncateLimit: number; constructor(truncateLimit = 500) { this.truncateLimit = truncateLimit; } /** * Create a new conversation or get existing one */ createConversation(modelId: string): string { // Generate a short 4-character hex ID (16 bits = 65536 possible values) const conversationId = randomBytes(2).toString("hex"); const conversation: ConversationState = { id: conversationId, modelId, messages: [], createdAt: Date.now(), updatedAt: Date.now(), }; this.conversations.set(conversationId, conversation); logger.debug("Created new conversation", { conversationId, modelId }); return conversationId; } /** * Get a conversation by ID */ getConversation(conversationId: string): ConversationState | null { return this.conversations.get(conversationId) || null; } /** * Add a message to a conversation */ addMessage(conversationId: string, role: "user" | "assistant", content: string): void { const conversation = this.conversations.get(conversationId); if (!conversation) { throw new Error(`Conversation not found: ${conversationId}`); } conversation.messages.push({ role, content }); conversation.updatedAt = Date.now(); logger.debug("Added message to conversation", { conversationId, role, contentLength: content.length, }); } /** * Get conversation history with optional truncation */ getHistory(conversationId: string): ChatMessage[] { const conversation = this.conversations.get(conversationId); if (!conversation) { throw new Error(`Conversation not found: ${conversationId}`); } return this.truncateMessages(conversation.messages); } /** * Clone a conversation with a new model */ cloneConversation(sourceConversationId: string, newModelId: string): string { const source = this.conversations.get(sourceConversationId); if (!source) { throw new Error(`Source conversation not found: ${sourceConversationId}`); } const newConversationId = this.createConversation(newModelId); const newConversation = this.conversations.get(newConversationId); if (!newConversation) { throw new Error("Failed to create cloned conversation"); } // Copy messages from source newConversation.messages = source.messages.map((msg) => ({ ...msg })); newConversation.updatedAt = Date.now(); logger.info("Cloned conversation with new model", { sourceConversationId, newConversationId, oldModelId: source.modelId, newModelId, messageCount: source.messages.length, }); return newConversationId; } /** * Get full conversation state */ getConversationState(conversationId: string): ConversationState | null { return this.getConversation(conversationId); } /** * Truncate messages to limit context size * Keeps first N and last N messages if total exceeds limit */ private truncateMessages(messages: ChatMessage[]): ChatMessage[] { if (messages.length <= this.truncateLimit) { return messages; } const kept = Math.floor(this.truncateLimit / 2); const first = messages.slice(0, kept); const last = messages.slice(-kept); // Add marker for truncated content const marker: ChatMessage = { role: "assistant", content: "[... conversation history truncated to save context ...]", }; return [...first, marker, ...last]; } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/danielwpz/polybrain-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server