this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 203 lines 6.6 kB view raw
1/** 2 * Letta client and provider bootstrap module 3 * 4 * Provides: 5 * - Singleton Letta client instance 6 * - Anthropic provider creation/management 7 * - Agent creation (placeholder for M1) 8 */ 9 10import { Letta } from '@letta-ai/letta-client'; 11import { config } from './config'; 12import { getAllLettaToolsCreate } from './tools'; 13 14/** 15 * Singleton Letta client instance 16 */ 17let lettaClient: Letta | null = null; 18 19/** 20 * Map of tool name -> Letta tool ID 21 * Populated during initialization 22 */ 23const registeredToolIds = new Map<string, string>(); 24 25/** 26 * Get all registered tool IDs for attaching to agents 27 */ 28export function getRegisteredToolIds(): string[] { 29 return Array.from(registeredToolIds.values()); 30} 31 32/** 33 * Get or create the Letta client singleton 34 */ 35export function getLettaClient(): Letta { 36 lettaClient ??= new Letta({ 37 baseURL: config.LETTA_BASE_URL, 38 apiKey: config.LETTA_SERVER_PASSWORD || undefined, 39 }); 40 return lettaClient; 41} 42 43/** 44 * Ensure the Anthropic provider is configured 45 * 46 * NOTE: The current @letta-ai/letta-client SDK (v1.3.3) doesn't expose a 47 * `providers` API. Models are specified directly in the agent configuration 48 * using the format "provider/model-name" (e.g., "anthropic/claude-opus-4-5-20251101"). 49 * 50 * This function verifies Letta connectivity by listing available models. 51 * Provider configuration (API keys, base URLs) is handled via environment 52 * variables or Letta's server configuration, not the client SDK. 53 * 54 * For the anthropic-proxy setup: 55 * - The proxy should be configured as an Anthropic provider in Letta server 56 * - This is typically done via Letta's admin interface or configuration files 57 * - The client SDK just references models by their provider/name handle 58 * 59 * @returns A status message 60 */ 61export async function ensureProvider(): Promise<string> { 62 const client = getLettaClient(); 63 64 try { 65 console.log('Verifying Letta connectivity...'); 66 67 // List available models to verify connectivity 68 const llmModels = await client.models.list(); 69 const embeddingModels = await client.models.embeddings.list(); 70 console.log( 71 `Letta is accessible. Found ${llmModels.length.toString()} LLM models and ${embeddingModels.length.toString()} embedding models.` 72 ); 73 74 // Log available Claude models (via LiteLLM/openai-proxy or native Anthropic) 75 const claudeModels = llmModels.filter( 76 (m) => 77 m.provider_type === 'anthropic' || 78 (m.provider_name?.includes('litellm') ?? false) || 79 (m.handle?.includes('claude') ?? false) || 80 (m.handle?.includes('openai-proxy') ?? false) || 81 m.name.includes('claude') 82 ); 83 84 if (claudeModels.length > 0) { 85 console.log(`Found ${claudeModels.length.toString()} Claude model(s):`); 86 claudeModels.forEach((m) => { 87 console.log(` - ${m.handle ?? m.name}`); 88 }); 89 } else { 90 console.warn( 91 '⚠️ No Claude models found. ' + 92 'Make sure the anthropic-proxy is configured as a provider in Letta server. ' + 93 'Run: bun run setup:letta' 94 ); 95 } 96 97 return 'Letta connectivity verified'; 98 } catch (error: unknown) { 99 console.error('Failed to verify Letta connectivity:', error); 100 throw error; 101 } 102} 103 104/** 105 * Register all tools with Letta 106 * 107 * Creates tools in Letta if they don't exist, or updates existing ones. 108 * Tool IDs are stored for later attachment to agents. 109 */ 110async function registerTools(): Promise<void> { 111 const client = getLettaClient(); 112 const toolDefs = getAllLettaToolsCreate(); 113 114 console.log(`Registering ${String(toolDefs.length)} tools with Letta...`); 115 116 // Build a set of existing tool names to avoid duplicates 117 const existingTools = new Map<string, string>(); 118 for await (const tool of client.tools.list()) { 119 if (tool.name !== null && tool.name !== undefined) { 120 existingTools.set(tool.name, tool.id); 121 } 122 } 123 124 for (const def of toolDefs) { 125 try { 126 // Extract expected tool name from Python source code (function name) 127 const funcMatch = /^def\s+(\w+)\s*\(/m.exec(def.source_code); 128 const expectedName = funcMatch?.[1] ?? 'unknown'; 129 130 // Check if tool already exists 131 const existingId = existingTools.get(expectedName); 132 if (existingId !== undefined) { 133 // Update existing tool with new source code and json_schema 134 await client.tools.update(existingId, { 135 source_code: def.source_code, 136 description: def.description ?? null, 137 json_schema: def.json_schema ?? null, 138 }); 139 console.log(` Updated tool '${expectedName}' (${existingId})`); 140 registeredToolIds.set(expectedName, existingId); 141 } else { 142 // Create new tool - Letta extracts name from source_code 143 const created = await client.tools.create(def); 144 const createdName = created.name ?? expectedName; 145 console.log(` Created tool '${createdName}' (${created.id})`); 146 registeredToolIds.set(createdName, created.id); 147 } 148 } catch (error: unknown) { 149 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 150 console.error(` Failed to register tool:`, errorMessage); 151 // Continue with other tools 152 } 153 } 154 155 console.log(`Registered ${String(registeredToolIds.size)} tools`); 156} 157 158/** 159 * Get or create the ADHD assistant agent 160 * 161 * This is a placeholder for M1 implementation. 162 * For now, it just logs a message and returns null. 163 * 164 * @returns Agent ID once implemented, null for now 165 */ 166export function getOrCreateAgent(): Promise<string | null> { 167 console.log('getOrCreateAgent() called - placeholder for M1 implementation'); 168 console.log('Agent creation will be implemented in milestone M1'); 169 return Promise.resolve(null); 170} 171 172/** 173 * Initialize Letta on application startup 174 * 175 * - Creates the Letta client 176 * - Ensures the anthropic-proxy provider exists 177 * - Eventually will create/get the agent (M1) 178 */ 179export async function initializeLetta(): Promise<void> { 180 console.log('Initializing Letta...'); 181 182 // Get client (creates singleton) 183 getLettaClient(); 184 console.log(`Letta client initialized (base URL: ${config.LETTA_BASE_URL})`); 185 186 // Ensure provider exists 187 try { 188 await ensureProvider(); 189 } catch (error) { 190 console.error('Failed to ensure provider during initialization:', error); 191 throw error; 192 } 193 194 // Register tools with Letta 195 try { 196 await registerTools(); 197 } catch (error) { 198 console.error('Failed to register tools during initialization:', error); 199 // Non-fatal - continue without tools 200 } 201 202 console.log('Letta initialization complete'); 203}