Execute AOPs
This guide shows how to execute Agent Operating Procedures (AOPs) using the TypeScript SDK with asynchronous execution - the recommended approach for production applications. AOPs are pre-configured AI workflows that can perform complex tasks like research, analysis, and content generation with optional user inputs for customization.
Why Async Execution? Long-running AOPs can take minutes to complete. Asynchronous execution prevents timeouts, provides real-time progress tracking, and ensures reliable execution of complex workflows.
Key features:
- Production-ready async execution - Prevents timeouts for long-running workflows
- Real-time progress monitoring - Track execution status with polling
- Robust error handling - Comprehensive error handling and retry logic
- Full TypeScript support - Complete type safety with proper interfaces
- Thread-based tracking - Monitor execution progress via thread IDs
Set Up Client
1 import type { AthenaIntelligence } from 'athena-intelligence'; 2 import { AthenaIntelligenceClient, AthenaIntelligenceError } from 'athena-intelligence'; 3 4 // Production client setup 5 const client = new AthenaIntelligenceClient({ 6 apiKey: process.env.ATHENA_API_KEY, 7 }); 8 9 // Custom API endpoint (if needed) 10 const customClient = new AthenaIntelligenceClient({ 11 apiKey: process.env.ATHENA_API_KEY, 12 baseUrl: 'https://your-custom-api.example.com', 13 }); 14 15 // Local development 16 const devClient = new AthenaIntelligenceClient({ 17 apiKey: process.env.ATHENA_API_KEY, 18 baseUrl: 'http://localhost:8000', 19 });
TypeScript Type Definitions
Define proper interfaces to avoid using any types:
1 // Content types for message content 2 type MessageContent = string | Array<{ type: string; text?: string }>; 3 4 // Progress update interface 5 interface ProgressUpdate { 6 attempt: number; 7 maxAttempts: number; 8 status: string; 9 threadId: string; 10 updatedAt: string; 11 } 12 13 // AOP execution result 14 interface AOPExecutionResult { 15 status: 'completed' | 'failed' | 'timeout' | 'error'; 16 threadId: string; 17 result?: string; 18 error?: string; 19 conversationAssetId?: string; 20 messageCount?: number | null; 21 threadStatus?: AthenaIntelligence.ThreadStatusResponseOut; 22 } 23 24 // Complete execution response 25 interface ExecutionResponse { 26 startResponse: AthenaIntelligence.AopAsyncExecuteResponseOut; 27 finalResult: AOPExecutionResult; 28 success: boolean; 29 error?: string; 30 statusCode?: number; 31 }
Content Helper Functions
Type-safe helper functions to extract text from message content:
1 // Helper function to extract text from message content 2 function extractMessageText(content: MessageContent): string { 3 if (typeof content === 'string') { 4 return content; 5 } else if (Array.isArray(content)) { 6 return content 7 .filter(part => part.type === 'text' && part.text) 8 .map(part => part.text!) 9 .join('\n'); 10 } 11 return ''; 12 } 13 14 // Helper function to safely get final assistant message text 15 function getFinalAssistantText(conversation: AthenaIntelligence.ConversationResult | undefined): string | null { 16 if (!conversation?.last_assistant_message?.content) { 17 return null; 18 } 19 return extractMessageText(conversation.last_assistant_message.content); 20 } 21 22 // Helper to safely extract text from conversation asset 23 function getConversationAssetText(conversationAsset: AthenaIntelligence.ConversationAssetInfo | undefined): string | null { 24 if (!conversationAsset?.last_message?.content) { 25 return null; 26 } 27 return extractMessageText(conversationAsset.last_message.content); 28 }
Async AOP Execution Pattern
The recommended approach - executeAsync starts execution and returns a thread_id for tracking:
1 // Execute AOP asynchronously and monitor until completion 2 async function executeAOPAsync( 3 assetId: string, 4 userInputs: Record<string, string> = {} 5 ): Promise<ExecutionResponse> { 6 try { 7 console.log('🚀 Starting async AOP execution...'); 8 9 // Step 1: Start async execution (returns immediately with thread_id) 10 const asyncResponse = await client.aop.executeAsync({ 11 asset_id: assetId, 12 user_inputs: userInputs, 13 }); 14 15 // executeAsync only STARTS the execution - it does NOT complete it 16 console.log('✅ AOP execution initiated (running in background)'); 17 console.log('Thread ID:', asyncResponse.thread_id); 18 console.log('Status:', asyncResponse.status); // Will be "started" or similar 19 console.log('AOP Title:', asyncResponse.aop_title); 20 21 // Step 2: Monitor execution until it actually completes 22 const result = await monitorExecution(asyncResponse.thread_id); 23 24 return { 25 startResponse: asyncResponse, 26 finalResult: result, 27 success: true, 28 }; 29 } catch (error) { 30 console.error('AOP execution failed:', error); 31 32 if (error instanceof AthenaIntelligenceError) { 33 return { 34 success: false, 35 error: `API Error: ${error.statusCode} - ${error.message}`, 36 statusCode: error.statusCode, 37 } as ExecutionResponse; 38 } 39 40 return { 41 success: false, 42 error: error instanceof Error ? error.message : 'Unknown error occurred', 43 } as ExecutionResponse; 44 } 45 }
Production-Ready Progress Monitoring
Monitor execution progress using threads.getStatus with comprehensive error handling:
1 async function monitorExecution(threadId: string): Promise<AOPExecutionResult> { 2 const maxAttempts = 60; // 5 minutes with 5-second intervals 3 const pollInterval = 5000; // 5 seconds between polls 4 let consecutiveErrors = 0; 5 const maxConsecutiveErrors = 3; 6 7 console.log(`📊 Starting progress monitoring for thread: ${threadId}`); 8 9 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 10 try { 11 // Poll thread status 12 const statusResponse = await client.threads.getStatus(threadId); 13 14 // Reset error counter on successful response 15 consecutiveErrors = 0; 16 17 console.log(`[${attempt}/${maxAttempts}] Status: ${statusResponse.status} (Updated: ${statusResponse.updated_at})`); 18 19 // Check if execution completed 20 if (statusResponse.status === 'completed') { 21 console.log('🎉 Thread execution finished successfully!'); 22 23 // Extract final result safely 24 const conversationAsset = statusResponse.conversation_asset; 25 if (conversationAsset) { 26 const finalText = getConversationAssetText(conversationAsset); 27 28 return { 29 status: 'completed', 30 threadId, 31 result: finalText || 'Thread completed but no final message available', 32 conversationAssetId: conversationAsset.conversation_asset_id, 33 messageCount: conversationAsset.num_messages, 34 threadStatus: statusResponse, 35 }; 36 } else { 37 return { 38 status: 'completed', 39 threadId, 40 result: 'Thread completed but no conversation asset available', 41 threadStatus: statusResponse, 42 }; 43 } 44 } else if (statusResponse.status === 'failed') { 45 console.error('❌ Thread execution failed'); 46 return { 47 status: 'failed', 48 threadId, 49 error: 'Thread execution failed', 50 threadStatus: statusResponse, 51 }; 52 } else { 53 // Still running 54 console.log(`⏳ Still running... (${statusResponse.status})`); 55 56 // Wait before next poll 57 await new Promise(resolve => setTimeout(resolve, pollInterval)); 58 } 59 } catch (error) { 60 consecutiveErrors++; 61 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 62 console.error(`Status check failed (${consecutiveErrors}/${maxConsecutiveErrors}):`, errorMessage); 63 64 // Fail fast if too many consecutive errors 65 if (consecutiveErrors >= maxConsecutiveErrors) { 66 return { 67 status: 'error', 68 threadId, 69 error: `Too many consecutive status check failures: ${errorMessage}`, 70 }; 71 } 72 73 // Wait before retrying 74 await new Promise(resolve => setTimeout(resolve, pollInterval)); 75 } 76 } 77 78 console.warn('⏰ Polling timeout reached'); 79 return { 80 status: 'timeout', 81 threadId, 82 error: 'Monitoring timeout reached - execution may still be running', 83 }; 84 }
Basic Usage Example
Execute an AOP with custom parameters:
1 async function basicExample() { 2 const result = await executeAOPAsync( 3 'asset_9249292-d118-42d3-95b4-00eccfe0754f', 4 { 5 company: 'Acme Corp', 6 quarter: 'Q1 2024', 7 analysis_type: 'comprehensive', 8 } 9 ); 10 11 if (result.success) { 12 console.log('✅ Execution completed successfully'); 13 console.log('Final result:', result.finalResult.result); 14 } else { 15 console.error('❌ Execution failed:', result.error); 16 } 17 } 18 19 // Run the example 20 basicExample();
Advanced Usage with Custom Monitoring
Customize monitoring behavior with progress callbacks:
1 // Custom monitoring with progress callbacks 2 async function executeWithCustomMonitoring( 3 assetId: string, 4 userInputs: Record<string, string>, 5 onProgress?: (status: ProgressUpdate) => void 6 ): Promise<AOPExecutionResult> { 7 try { 8 // Start execution (returns immediately) 9 const asyncResponse = await client.aop.executeAsync({ 10 asset_id: assetId, 11 user_inputs: userInputs, 12 }); 13 14 console.log('🚀 AOP initiated:', asyncResponse.aop_title); 15 console.log('📍 Thread ID:', asyncResponse.thread_id); 16 17 // Monitor with callbacks 18 const result = await monitorWithCallback(asyncResponse.thread_id, onProgress); 19 20 return result; 21 } catch (error) { 22 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 23 throw new Error(`AOP execution failed: ${errorMessage}`); 24 } 25 } 26 27 async function monitorWithCallback( 28 threadId: string, 29 onProgress?: (status: ProgressUpdate) => void 30 ): Promise<AOPExecutionResult> { 31 const maxAttempts = 120; // 10 minutes for longer workflows 32 const pollInterval = 5000; 33 34 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 35 try { 36 const status = await client.threads.getStatus(threadId); 37 38 // Call progress callback if provided 39 if (onProgress) { 40 onProgress({ 41 attempt, 42 maxAttempts, 43 status: status.status, 44 updatedAt: status.updated_at, 45 threadId: status.thread_id, 46 }); 47 } 48 49 if (status.status === 'completed') { 50 const finalText = getConversationAssetText(status.conversation_asset); 51 return { 52 status: 'completed', 53 threadId, 54 result: finalText || 'No final result available', 55 threadStatus: status, 56 }; 57 } else if (status.status === 'failed') { 58 throw new Error('Thread execution failed'); 59 } 60 61 await new Promise(resolve => setTimeout(resolve, pollInterval)); 62 } catch (error) { 63 if (attempt === maxAttempts) { 64 throw error; 65 } 66 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 67 console.warn(`Status check failed, retrying... (${errorMessage})`); 68 await new Promise(resolve => setTimeout(resolve, pollInterval)); 69 } 70 } 71 72 throw new Error('Monitoring timeout reached'); 73 }
Multiple AOP Execution
Execute multiple AOPs concurrently:
1 interface AOPConfig { 2 name: string; 3 assetId: string; 4 inputs: Record<string, string>; 5 } 6 7 interface AOPStartResult { 8 name: string; 9 threadId?: string; 10 aopTitle?: string; 11 success: boolean; 12 error?: string; 13 } 14 15 async function executeMultipleAOPs() { 16 const aopConfigs: AOPConfig[] = [ 17 { 18 name: 'Market Research', 19 assetId: 'asset_market_research', 20 inputs: { company: 'Tesla', quarter: 'Q3 2024' }, 21 }, 22 { 23 name: 'Competitor Analysis', 24 assetId: 'asset_competitor_analysis', 25 inputs: { company: 'Tesla', competitors: 'Ford,GM,Rivian' }, 26 }, 27 { 28 name: 'Financial Summary', 29 assetId: 'asset_financial_analysis', 30 inputs: { company: 'Tesla', period: 'quarterly' }, 31 }, 32 ]; 33 34 console.log(`🔄 Starting ${aopConfigs.length} AOPs concurrently...`); 35 36 // Step 1: Start all AOPs asynchronously (all return immediately with thread_ids) 37 const startPromises = aopConfigs.map(async (config): Promise<AOPStartResult> => { 38 try { 39 const response = await client.aop.executeAsync({ 40 asset_id: config.assetId, 41 user_inputs: config.inputs, 42 }); 43 44 console.log(`✅ ${config.name} started with thread: ${response.thread_id}`); 45 46 return { 47 name: config.name, 48 threadId: response.thread_id, 49 aopTitle: response.aop_title, 50 success: true, 51 }; 52 } catch (error) { 53 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 54 return { 55 name: config.name, 56 error: errorMessage, 57 success: false, 58 }; 59 } 60 }); 61 62 const startResults = await Promise.all(startPromises); 63 64 // Step 2: Monitor all successful starts until completion 65 const monitorPromises = startResults 66 .filter(result => result.success && result.threadId) 67 .map(async (result) => { 68 try { 69 const finalResult = await monitorExecution(result.threadId!); 70 return { 71 name: result.name, 72 threadId: result.threadId, 73 ...finalResult, 74 }; 75 } catch (error) { 76 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 77 return { 78 name: result.name, 79 threadId: result.threadId, 80 status: 'error' as const, 81 error: errorMessage, 82 }; 83 } 84 }); 85 86 const finalResults = await Promise.all(monitorPromises); 87 88 // Log results 89 console.log('\n=== Execution Results ==='); 90 finalResults.forEach(result => { 91 console.log(`${result.name}: ${result.status}`); 92 if (result.result) { 93 console.log(` Result: ${result.result.substring(0, 100)}...`); 94 } 95 if (result.error) { 96 console.log(` Error: ${result.error}`); 97 } 98 }); 99 100 return finalResults; 101 }
Error Handling and Retry Logic
Implement comprehensive error handling for production:
1 async function executeAOPWithRetry( 2 assetId: string, 3 userInputs: Record<string, string>, 4 maxRetries: number = 3 5 ): Promise<ExecutionResponse> { 6 let lastError: Error | null = null; 7 8 for (let attempt = 1; attempt <= maxRetries; attempt++) { 9 try { 10 console.log(`🔄 Attempt ${attempt}/${maxRetries} for AOP execution`); 11 12 const result = await executeAOPAsync(assetId, userInputs); 13 14 if (result.success) { 15 console.log(`✅ AOP succeeded on attempt ${attempt}`); 16 return result; 17 } else { 18 throw new Error(result.error || 'AOP execution failed'); 19 } 20 } catch (error) { 21 lastError = error as Error; 22 23 // Don't retry on client errors (4xx) 24 if (error instanceof AthenaIntelligenceError && 25 error.statusCode >= 400 && error.statusCode < 500) { 26 console.error(`❌ Client error (${error.statusCode}): ${error.message}`); 27 throw error; 28 } 29 30 // Retry on server errors or network issues 31 if (attempt < maxRetries) { 32 const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000); 33 console.log(`⏳ Waiting ${delay}ms before retry...`); 34 await new Promise(resolve => setTimeout(resolve, delay)); 35 } 36 } 37 } 38 39 console.error('❌ AOP failed after all retry attempts'); 40 throw lastError; 41 }
Type-Safe Options Interface
Define comprehensive options for AOP execution:
1 interface AOPExecutionOptions { 2 maxAttempts?: number; 3 pollInterval?: number; 4 onProgress?: (status: ProgressUpdate) => void; 5 retries?: number; 6 timeout?: number; 7 } 8 9 // Type-safe AOP executor 10 async function executeAOPTyped( 11 assetId: string, 12 userInputs: Record<string, string> = {}, 13 options: AOPExecutionOptions = {} 14 ): Promise<AOPExecutionResult> { 15 const { 16 maxAttempts = 60, 17 pollInterval = 5000, 18 onProgress, 19 timeout = 600000, // 10 minutes 20 } = options; 21 22 try { 23 // Start execution (returns immediately with thread_id) 24 const asyncResponse = await client.aop.executeAsync({ 25 asset_id: assetId, 26 user_inputs: userInputs, 27 }); 28 29 console.log('🚀 AOP initiated with thread:', asyncResponse.thread_id); 30 31 // Monitor with timeout 32 const result = await Promise.race([ 33 monitorExecutionTyped(asyncResponse.thread_id, maxAttempts, pollInterval, onProgress), 34 createTimeoutPromise(timeout, asyncResponse.thread_id), 35 ]); 36 37 return result; 38 } catch (error) { 39 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 40 return { 41 status: 'error', 42 threadId: 'unknown', 43 error: errorMessage, 44 }; 45 } 46 } 47 48 async function monitorExecutionTyped( 49 threadId: string, 50 maxAttempts: number, 51 pollInterval: number, 52 onProgress?: (status: ProgressUpdate) => void 53 ): Promise<AOPExecutionResult> { 54 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 55 try { 56 const status = await client.threads.getStatus(threadId); 57 58 if (onProgress) { 59 onProgress({ 60 attempt, 61 maxAttempts, 62 status: status.status, 63 threadId: status.thread_id, 64 updatedAt: status.updated_at, 65 }); 66 } 67 68 if (status.status === 'completed') { 69 return { 70 status: 'completed', 71 threadId, 72 result: getConversationAssetText(status.conversation_asset) || 'No result available', 73 conversationAssetId: status.conversation_asset?.conversation_asset_id, 74 messageCount: status.conversation_asset?.num_messages, 75 threadStatus: status, 76 }; 77 } else if (status.status === 'failed') { 78 return { 79 status: 'failed', 80 threadId, 81 error: 'Thread execution failed', 82 threadStatus: status, 83 }; 84 } 85 86 await new Promise(resolve => setTimeout(resolve, pollInterval)); 87 } catch (error) { 88 if (attempt === maxAttempts) { 89 const errorMessage = error instanceof Error ? error.message : 'Unknown error'; 90 return { 91 status: 'error', 92 threadId, 93 error: `Monitoring failed: ${errorMessage}`, 94 }; 95 } 96 } 97 } 98 99 return { 100 status: 'timeout', 101 threadId, 102 error: 'Monitoring timeout reached', 103 }; 104 } 105 106 function createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> { 107 return new Promise((resolve) => { 108 setTimeout(() => { 109 resolve({ 110 status: 'timeout', 111 threadId, 112 error: `Execution timeout after ${timeoutMs}ms`, 113 }); 114 }, timeoutMs); 115 }); 116 }
Complete Production Example
Here’s a complete production-ready class with full type safety:
1 import type { AthenaIntelligence } from 'athena-intelligence'; 2 import { AthenaIntelligenceClient, AthenaIntelligenceError } from 'athena-intelligence'; 3 4 class AOPExecutor { 5 private client: AthenaIntelligenceClient; 6 7 constructor(apiKey: string, baseUrl?: string) { 8 this.client = new AthenaIntelligenceClient({ 9 apiKey, 10 baseUrl, 11 }); 12 } 13 14 async executeAOP( 15 assetId: string, 16 userInputs: Record<string, string> = {}, 17 options: AOPExecutionOptions = {} 18 ): Promise<AOPExecutionResult> { 19 const { timeout = 600000, onProgress, retries = 3 } = options; 20 21 let attempt = 0; 22 let lastError: Error | undefined; 23 24 while (attempt < retries) { 25 try { 26 console.log(`🚀 Starting AOP execution (attempt ${attempt + 1}/${retries})`); 27 28 // Start async execution (returns immediately with thread_id) 29 const startResponse = await this.client.aop.executeAsync({ 30 asset_id: assetId, 31 user_inputs: userInputs, 32 }); 33 34 console.log(`✅ AOP initiated: ${startResponse.aop_title}`); 35 console.log(`📍 Thread ID: ${startResponse.thread_id}`); 36 37 // Monitor with timeout 38 const result = await Promise.race([ 39 this.monitorExecution(startResponse.thread_id, onProgress), 40 this.createTimeoutPromise(timeout, startResponse.thread_id), 41 ]); 42 43 if (result.status === 'completed') { 44 console.log('🎉 Thread execution finished successfully'); 45 } 46 47 return result; 48 } catch (error) { 49 attempt++; 50 lastError = error as Error; 51 console.error(`❌ Attempt ${attempt} failed:`, lastError.message); 52 53 if (attempt >= retries) { 54 break; 55 } 56 57 // Exponential backoff 58 const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000); 59 await new Promise(resolve => setTimeout(resolve, delay)); 60 } 61 } 62 63 // All retries failed 64 return { 65 status: 'error', 66 threadId: 'unknown', 67 error: lastError?.message || 'AOP execution failed after all retries', 68 }; 69 } 70 71 private async monitorExecution( 72 threadId: string, 73 onProgress?: (update: ProgressUpdate) => void 74 ): Promise<AOPExecutionResult> { 75 const maxAttempts = 120; // Allow up to 10 minutes 76 const pollInterval = 5000; 77 78 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 79 const status = await this.client.threads.getStatus(threadId); 80 81 const update: ProgressUpdate = { 82 attempt, 83 maxAttempts, 84 status: status.status, 85 threadId: status.thread_id, 86 updatedAt: status.updated_at, 87 }; 88 89 if (onProgress) { 90 onProgress(update); 91 } 92 93 console.log(`[${attempt}/${maxAttempts}] Status: ${status.status}`); 94 95 if (status.status === 'completed') { 96 const result = getConversationAssetText(status.conversation_asset); 97 return { 98 status: 'completed', 99 threadId, 100 result: result || 'Thread finished successfully', 101 conversationAssetId: status.conversation_asset?.conversation_asset_id, 102 messageCount: status.conversation_asset?.num_messages, 103 threadStatus: status, 104 }; 105 } else if (status.status === 'failed') { 106 return { 107 status: 'failed', 108 threadId, 109 error: 'Thread execution failed', 110 threadStatus: status, 111 }; 112 } 113 114 await new Promise(resolve => setTimeout(resolve, pollInterval)); 115 } 116 117 return { 118 status: 'timeout', 119 threadId, 120 error: 'Maximum polling attempts reached', 121 }; 122 } 123 124 private createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> { 125 return new Promise((resolve) => { 126 setTimeout(() => { 127 resolve({ 128 status: 'timeout', 129 threadId, 130 error: `Execution timeout after ${timeoutMs}ms`, 131 }); 132 }, timeoutMs); 133 }); 134 } 135 } 136 137 // Usage example with full type safety 138 async function main() { 139 try { 140 const executor = new AOPExecutor(process.env.ATHENA_API_KEY!); 141 142 const result = await executor.executeAOP( 143 'asset_comprehensive_analysis', 144 { 145 company: 'OpenAI', 146 analysis_type: 'comprehensive', 147 time_period: '2024', 148 include_forecasts: 'true', 149 }, 150 { 151 timeout: 600000, // 10 minutes 152 onProgress: (update: ProgressUpdate) => { 153 console.log(`📊 Progress: ${update.status} (${update.attempt}/${update.maxAttempts})`); 154 }, 155 retries: 3, 156 } 157 ); 158 159 if (result.status === 'completed') { 160 console.log('🎉 Final result:', result.result); 161 } else { 162 console.error('❌ Execution did not complete:', result.error); 163 } 164 } catch (error) { 165 console.error('💥 Execution failed:', error instanceof Error ? error.message : 'Unknown error'); 166 } 167 } 168 169 main();
Key Recommendations
- Always use
executeAsync- It returns immediately with a thread_id for tracking - Poll with
threads.getStatus- The thread status shows actual completion - Never use
anytypes - Use proper TypeScript interfaces for type safety - Implement proper error handling - Handle network failures, timeouts, and API errors
- Use retry logic - Implement exponential backoff for resilient execution
- Monitor progress - Use callbacks to provide real-time updates
- Set appropriate timeouts - Based on expected AOP complexity
Important: executeAsync only starts execution and returns a thread_id. You must poll threads.getStatus to monitor actual completion. The execution happens asynchronously in the background.
