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
import type { AthenaIntelligence } from '@athenaintel/sdk';import { AthenaIntelligenceClient, AthenaIntelligenceError } from '@athenaintel/sdk';// Production client setupconst client = new AthenaIntelligenceClient({apiKey: process.env.ATHENA_API_KEY,});// Custom API endpoint (if needed)const customClient = new AthenaIntelligenceClient({apiKey: process.env.ATHENA_API_KEY,baseUrl: 'https://your-custom-api.example.com',});// Local developmentconst devClient = new AthenaIntelligenceClient({apiKey: process.env.ATHENA_API_KEY,baseUrl: 'http://localhost:8000',});
TypeScript Type Definitions
Define proper interfaces to avoid using any types:
// Content types for message contenttype MessageContent = string | Array<{ type: string; text?: string }>;// Progress update interfaceinterface ProgressUpdate {attempt: number;maxAttempts: number;status: string;threadId: string;updatedAt: string;}// AOP execution resultinterface AOPExecutionResult {status: 'completed' | 'failed' | 'timeout' | 'error';threadId: string;result?: string;error?: string;conversationAssetId?: string;messageCount?: number | null;threadStatus?: AthenaIntelligence.ThreadStatusResponseOut;}// Complete execution responseinterface ExecutionResponse {startResponse: AthenaIntelligence.AopAsyncExecuteResponseOut;finalResult: AOPExecutionResult;success: boolean;error?: string;statusCode?: number;}
Content Helper Functions
Type-safe helper functions to extract text from message content:
// Helper function to extract text from message contentfunction extractMessageText(content: MessageContent): string {if (typeof content === 'string') {return content;} else if (Array.isArray(content)) {return content.filter(part => part.type === 'text' && part.text).map(part => part.text!).join('\n');}return '';}// Helper function to safely get final assistant message textfunction getFinalAssistantText(conversation: AthenaIntelligence.ConversationResult | undefined): string | null {if (!conversation?.last_assistant_message?.content) {return null;}return extractMessageText(conversation.last_assistant_message.content);}// Helper to safely extract text from conversation assetfunction getConversationAssetText(conversationAsset: AthenaIntelligence.ConversationAssetInfo | undefined): string | null {if (!conversationAsset?.last_message?.content) {return null;}return extractMessageText(conversationAsset.last_message.content);}
Async AOP Execution Pattern
The recommended approach - executeAsync starts execution and returns a thread_id for tracking:
// Execute AOP asynchronously and monitor until completionasync function executeAOPAsync(assetId: string,userInputs: Record<string, string> = {}): Promise<ExecutionResponse> {try {console.log('🚀 Starting async AOP execution...');// Step 1: Start async execution (returns immediately with thread_id)const asyncResponse = await client.aop.executeAsync({asset_id: assetId,user_inputs: userInputs,});// executeAsync only STARTS the execution - it does NOT complete itconsole.log('✅ AOP execution initiated (running in background)');console.log('Thread ID:', asyncResponse.thread_id);console.log('Status:', asyncResponse.status); // Will be "started" or similarconsole.log('AOP Title:', asyncResponse.aop_title);// Step 2: Monitor execution until it actually completesconst result = await monitorExecution(asyncResponse.thread_id);return {startResponse: asyncResponse,finalResult: result,success: true,};} catch (error) {console.error('AOP execution failed:', error);if (error instanceof AthenaIntelligenceError) {return {success: false,error: `API Error: ${error.statusCode} - ${error.message}`,statusCode: error.statusCode,} as ExecutionResponse;}return {success: false,error: error instanceof Error ? error.message : 'Unknown error occurred',} as ExecutionResponse;}}
Production-Ready Progress Monitoring
Monitor execution progress using threads.getStatus with comprehensive error handling:
async function monitorExecution(threadId: string): Promise<AOPExecutionResult> {const maxAttempts = 60; // 5 minutes with 5-second intervalsconst pollInterval = 5000; // 5 seconds between pollslet consecutiveErrors = 0;const maxConsecutiveErrors = 3;console.log(`📊 Starting progress monitoring for thread: ${threadId}`);for (let attempt = 1; attempt <= maxAttempts; attempt++) {try {// Poll thread statusconst statusResponse = await client.threads.getStatus(threadId);// Reset error counter on successful responseconsecutiveErrors = 0;console.log(`[${attempt}/${maxAttempts}] Status: ${statusResponse.status} (Updated: ${statusResponse.updated_at})`);// Check if execution completedif (statusResponse.status === 'completed') {console.log('🎉 Thread execution finished successfully!');// Extract final result safelyconst conversationAsset = statusResponse.conversation_asset;if (conversationAsset) {const finalText = getConversationAssetText(conversationAsset);return {status: 'completed',threadId,result: finalText || 'Thread completed but no final message available',conversationAssetId: conversationAsset.conversation_asset_id,messageCount: conversationAsset.num_messages,threadStatus: statusResponse,};} else {return {status: 'completed',threadId,result: 'Thread completed but no conversation asset available',threadStatus: statusResponse,};}} else if (statusResponse.status === 'failed') {console.error('❌ Thread execution failed');return {status: 'failed',threadId,error: 'Thread execution failed',threadStatus: statusResponse,};} else {// Still runningconsole.log(`⏳ Still running... (${statusResponse.status})`);// Wait before next pollawait new Promise(resolve => setTimeout(resolve, pollInterval));}} catch (error) {consecutiveErrors++;const errorMessage = error instanceof Error ? error.message : 'Unknown error';console.error(`Status check failed (${consecutiveErrors}/${maxConsecutiveErrors}):`, errorMessage);// Fail fast if too many consecutive errorsif (consecutiveErrors >= maxConsecutiveErrors) {return {status: 'error',threadId,error: `Too many consecutive status check failures: ${errorMessage}`,};}// Wait before retryingawait new Promise(resolve => setTimeout(resolve, pollInterval));}}console.warn('⏰ Polling timeout reached');return {status: 'timeout',threadId,error: 'Monitoring timeout reached - execution may still be running',};}
Basic Usage Example
Execute an AOP with custom parameters:
async function basicExample() {const result = await executeAOPAsync('asset_9249292-d118-42d3-95b4-00eccfe0754f',{company: 'Acme Corp',quarter: 'Q1 2024',analysis_type: 'comprehensive',});if (result.success) {console.log('✅ Execution completed successfully');console.log('Final result:', result.finalResult.result);} else {console.error('❌ Execution failed:', result.error);}}// Run the examplebasicExample();
Advanced Usage with Custom Monitoring
Customize monitoring behavior with progress callbacks:
// Custom monitoring with progress callbacksasync function executeWithCustomMonitoring(assetId: string,userInputs: Record<string, string>,onProgress?: (status: ProgressUpdate) => void): Promise<AOPExecutionResult> {try {// Start execution (returns immediately)const asyncResponse = await client.aop.executeAsync({asset_id: assetId,user_inputs: userInputs,});console.log('🚀 AOP initiated:', asyncResponse.aop_title);console.log('📍 Thread ID:', asyncResponse.thread_id);// Monitor with callbacksconst result = await monitorWithCallback(asyncResponse.thread_id, onProgress);return result;} catch (error) {const errorMessage = error instanceof Error ? error.message : 'Unknown error';throw new Error(`AOP execution failed: ${errorMessage}`);}}async function monitorWithCallback(threadId: string,onProgress?: (status: ProgressUpdate) => void): Promise<AOPExecutionResult> {const maxAttempts = 120; // 10 minutes for longer workflowsconst pollInterval = 5000;for (let attempt = 1; attempt <= maxAttempts; attempt++) {try {const status = await client.threads.getStatus(threadId);// Call progress callback if providedif (onProgress) {onProgress({attempt,maxAttempts,status: status.status,updatedAt: status.updated_at,threadId: status.thread_id,});}if (status.status === 'completed') {const finalText = getConversationAssetText(status.conversation_asset);return {status: 'completed',threadId,result: finalText || 'No final result available',threadStatus: status,};} else if (status.status === 'failed') {throw new Error('Thread execution failed');}await new Promise(resolve => setTimeout(resolve, pollInterval));} catch (error) {if (attempt === maxAttempts) {throw error;}const errorMessage = error instanceof Error ? error.message : 'Unknown error';console.warn(`Status check failed, retrying... (${errorMessage})`);await new Promise(resolve => setTimeout(resolve, pollInterval));}}throw new Error('Monitoring timeout reached');}
Multiple AOP Execution
Execute multiple AOPs concurrently:
interface AOPConfig {name: string;assetId: string;inputs: Record<string, string>;}interface AOPStartResult {name: string;threadId?: string;aopTitle?: string;success: boolean;error?: string;}async function executeMultipleAOPs() {const aopConfigs: AOPConfig[] = [{name: 'Market Research',assetId: 'asset_market_research',inputs: { company: 'Tesla', quarter: 'Q3 2024' },},{name: 'Competitor Analysis',assetId: 'asset_competitor_analysis',inputs: { company: 'Tesla', competitors: 'Ford,GM,Rivian' },},{name: 'Financial Summary',assetId: 'asset_financial_analysis',inputs: { company: 'Tesla', period: 'quarterly' },},];console.log(`🔄 Starting ${aopConfigs.length} AOPs concurrently...`);// Step 1: Start all AOPs asynchronously (all return immediately with thread_ids)const startPromises = aopConfigs.map(async (config): Promise<AOPStartResult> => {try {const response = await client.aop.executeAsync({asset_id: config.assetId,user_inputs: config.inputs,});console.log(`✅ ${config.name} started with thread: ${response.thread_id}`);return {name: config.name,threadId: response.thread_id,aopTitle: response.aop_title,success: true,};} catch (error) {const errorMessage = error instanceof Error ? error.message : 'Unknown error';return {name: config.name,error: errorMessage,success: false,};}});const startResults = await Promise.all(startPromises);// Step 2: Monitor all successful starts until completionconst monitorPromises = startResults.filter(result => result.success && result.threadId).map(async (result) => {try {const finalResult = await monitorExecution(result.threadId!);return {name: result.name,threadId: result.threadId,...finalResult,};} catch (error) {const errorMessage = error instanceof Error ? error.message : 'Unknown error';return {name: result.name,threadId: result.threadId,status: 'error' as const,error: errorMessage,};}});const finalResults = await Promise.all(monitorPromises);// Log resultsconsole.log('\n=== Execution Results ===');finalResults.forEach(result => {console.log(`${result.name}: ${result.status}`);if (result.result) {console.log(` Result: ${result.result.substring(0, 100)}...`);}if (result.error) {console.log(` Error: ${result.error}`);}});return finalResults;}
Error Handling and Retry Logic
Implement comprehensive error handling for production:
async function executeAOPWithRetry(assetId: string,userInputs: Record<string, string>,maxRetries: number = 3): Promise<ExecutionResponse> {let lastError: Error | null = null;for (let attempt = 1; attempt <= maxRetries; attempt++) {try {console.log(`🔄 Attempt ${attempt}/${maxRetries} for AOP execution`);const result = await executeAOPAsync(assetId, userInputs);if (result.success) {console.log(`✅ AOP succeeded on attempt ${attempt}`);return result;} else {throw new Error(result.error || 'AOP execution failed');}} catch (error) {lastError = error as Error;// Don't retry on client errors (4xx)if (error instanceof AthenaIntelligenceError &&error.statusCode >= 400 && error.statusCode < 500) {console.error(`❌ Client error (${error.statusCode}): ${error.message}`);throw error;}// Retry on server errors or network issuesif (attempt < maxRetries) {const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);console.log(`⏳ Waiting ${delay}ms before retry...`);await new Promise(resolve => setTimeout(resolve, delay));}}}console.error('❌ AOP failed after all retry attempts');throw lastError;}
Type-Safe Options Interface
Define comprehensive options for AOP execution:
interface AOPExecutionOptions {maxAttempts?: number;pollInterval?: number;onProgress?: (status: ProgressUpdate) => void;retries?: number;timeout?: number;}// Type-safe AOP executorasync function executeAOPTyped(assetId: string,userInputs: Record<string, string> = {},options: AOPExecutionOptions = {}): Promise<AOPExecutionResult> {const {maxAttempts = 60,pollInterval = 5000,onProgress,timeout = 600000, // 10 minutes} = options;try {// Start execution (returns immediately with thread_id)const asyncResponse = await client.aop.executeAsync({asset_id: assetId,user_inputs: userInputs,});console.log('🚀 AOP initiated with thread:', asyncResponse.thread_id);// Monitor with timeoutconst result = await Promise.race([monitorExecutionTyped(asyncResponse.thread_id, maxAttempts, pollInterval, onProgress),createTimeoutPromise(timeout, asyncResponse.thread_id),]);return result;} catch (error) {const errorMessage = error instanceof Error ? error.message : 'Unknown error';return {status: 'error',threadId: 'unknown',error: errorMessage,};}}async function monitorExecutionTyped(threadId: string,maxAttempts: number,pollInterval: number,onProgress?: (status: ProgressUpdate) => void): Promise<AOPExecutionResult> {for (let attempt = 1; attempt <= maxAttempts; attempt++) {try {const status = await client.threads.getStatus(threadId);if (onProgress) {onProgress({attempt,maxAttempts,status: status.status,threadId: status.thread_id,updatedAt: status.updated_at,});}if (status.status === 'completed') {return {status: 'completed',threadId,result: getConversationAssetText(status.conversation_asset) || 'No result available',conversationAssetId: status.conversation_asset?.conversation_asset_id,messageCount: status.conversation_asset?.num_messages,threadStatus: status,};} else if (status.status === 'failed') {return {status: 'failed',threadId,error: 'Thread execution failed',threadStatus: status,};}await new Promise(resolve => setTimeout(resolve, pollInterval));} catch (error) {if (attempt === maxAttempts) {const errorMessage = error instanceof Error ? error.message : 'Unknown error';return {status: 'error',threadId,error: `Monitoring failed: ${errorMessage}`,};}}}return {status: 'timeout',threadId,error: 'Monitoring timeout reached',};}function createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> {return new Promise((resolve) => {setTimeout(() => {resolve({status: 'timeout',threadId,error: `Execution timeout after ${timeoutMs}ms`,});}, timeoutMs);});}
Complete Production Example
Here’s a complete production-ready class with full type safety:
import type { AthenaIntelligence } from '@athenaintel/sdk';import { AthenaIntelligenceClient, AthenaIntelligenceError } from '@athenaintel/sdk';class AOPExecutor {private client: AthenaIntelligenceClient;constructor(apiKey: string, baseUrl?: string) {this.client = new AthenaIntelligenceClient({apiKey,baseUrl,});}async executeAOP(assetId: string,userInputs: Record<string, string> = {},options: AOPExecutionOptions = {}): Promise<AOPExecutionResult> {const { timeout = 600000, onProgress, retries = 3 } = options;let attempt = 0;let lastError: Error | undefined;while (attempt < retries) {try {console.log(`🚀 Starting AOP execution (attempt ${attempt + 1}/${retries})`);// Start async execution (returns immediately with thread_id)const startResponse = await this.client.aop.executeAsync({asset_id: assetId,user_inputs: userInputs,});console.log(`✅ AOP initiated: ${startResponse.aop_title}`);console.log(`📍 Thread ID: ${startResponse.thread_id}`);// Monitor with timeoutconst result = await Promise.race([this.monitorExecution(startResponse.thread_id, onProgress),this.createTimeoutPromise(timeout, startResponse.thread_id),]);if (result.status === 'completed') {console.log('🎉 Thread execution finished successfully');}return result;} catch (error) {attempt++;lastError = error as Error;console.error(`❌ Attempt ${attempt} failed:`, lastError.message);if (attempt >= retries) {break;}// Exponential backoffconst delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);await new Promise(resolve => setTimeout(resolve, delay));}}// All retries failedreturn {status: 'error',threadId: 'unknown',error: lastError?.message || 'AOP execution failed after all retries',};}private async monitorExecution(threadId: string,onProgress?: (update: ProgressUpdate) => void): Promise<AOPExecutionResult> {const maxAttempts = 120; // Allow up to 10 minutesconst pollInterval = 5000;for (let attempt = 1; attempt <= maxAttempts; attempt++) {const status = await this.client.threads.getStatus(threadId);const update: ProgressUpdate = {attempt,maxAttempts,status: status.status,threadId: status.thread_id,updatedAt: status.updated_at,};if (onProgress) {onProgress(update);}console.log(`[${attempt}/${maxAttempts}] Status: ${status.status}`);if (status.status === 'completed') {const result = getConversationAssetText(status.conversation_asset);return {status: 'completed',threadId,result: result || 'Thread finished successfully',conversationAssetId: status.conversation_asset?.conversation_asset_id,messageCount: status.conversation_asset?.num_messages,threadStatus: status,};} else if (status.status === 'failed') {return {status: 'failed',threadId,error: 'Thread execution failed',threadStatus: status,};}await new Promise(resolve => setTimeout(resolve, pollInterval));}return {status: 'timeout',threadId,error: 'Maximum polling attempts reached',};}private createTimeoutPromise(timeoutMs: number, threadId: string): Promise<AOPExecutionResult> {return new Promise((resolve) => {setTimeout(() => {resolve({status: 'timeout',threadId,error: `Execution timeout after ${timeoutMs}ms`,});}, timeoutMs);});}}// Usage example with full type safetyasync function main() {try {const executor = new AOPExecutor(process.env.ATHENA_API_KEY!);const result = await executor.executeAOP('asset_comprehensive_analysis',{company: 'OpenAI',analysis_type: 'comprehensive',time_period: '2024',include_forecasts: 'true',},{timeout: 600000, // 10 minutesonProgress: (update: ProgressUpdate) => {console.log(`📊 Progress: ${update.status} (${update.attempt}/${update.maxAttempts})`);},retries: 3,});if (result.status === 'completed') {console.log('🎉 Final result:', result.result);} else {console.error('❌ Execution did not complete:', result.error);}} catch (error) {console.error('💥 Execution failed:', error instanceof Error ? error.message : 'Unknown error');}}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.
