Execute AOPs
This example shows how to execute Agent Operating Procedures (AOPs) using the TypeScript SDK. AOPs are pre-configured AI workflows that can perform complex tasks like research, analysis, and content generation with optional user inputs for customization.
Key features:
- Execute pre-built AI workflows with custom parameters
- Support for both synchronous and asynchronous execution
- Full TypeScript support with type safety
- Track execution progress with thread monitoring
- Proper error handling for production use
Set Up Client
1 import type { AthenaIntelligence } from 'athena-intelligence'; 2 import { AthenaIntelligenceClient, AthenaIntelligenceError } from 'athena-intelligence'; 3 4 // Basic client setup (uses default production API) 5 const client = new AthenaIntelligenceClient({ 6 apiKey: process.env.ATHENA_API_KEY, 7 }); 8 9 // Override baseUrl for custom API endpoints 10 const customClient = new AthenaIntelligenceClient({ 11 apiKey: process.env.ATHENA_API_KEY, 12 baseUrl: 'https://your-custom-api.example.com', // Custom API endpoint 13 }); 14 15 // Use with local development server 16 const devClient = new AthenaIntelligenceClient({ 17 apiKey: process.env.ATHENA_API_KEY, 18 baseUrl: 'http://localhost:8000', // Local development 19 });
Basic AOP Execution
Execute an AOP synchronously and wait for completion:
1 const aopRequest: AthenaIntelligence.AopExecuteRequestIn = { 2 asset_id: 'asset_9249292-d118-42d3-95b4-00eccfe0754f', 3 user_inputs: { 4 company: 'Acme Corp', 5 quarter: 'Q1 2024', 6 }, 7 }; 8 9 try { 10 console.log('🚀 Executing AOP...'); 11 const response = await client.aop.execute(aopRequest); 12 13 console.log('✅ AOP execution completed'); 14 console.log('Status:', response.status); 15 console.log('AOP Title:', response.aop_title); 16 17 // Access the conversation result 18 if (response.conversation) { 19 console.log('Last Assistant Message:', response.conversation.last_assistant_message?.content); 20 console.log('Total Messages:', response.conversation.num_messages); 21 } 22 } catch (error) { 23 if (error instanceof AthenaIntelligenceError) { 24 console.error(`AOP execution failed: ${error.statusCode} - ${error.message}`); 25 } else { 26 console.error('Unexpected error:', error); 27 } 28 }
Asynchronous AOP Execution
For long-running workflows, use async execution to avoid timeouts:
1 const asyncAopRequest: AthenaIntelligence.AopExecuteRequestIn = { 2 asset_id: 'asset_9249292-d118-42d3-95b4-00eccfe0754f', 3 user_inputs: { 4 company: 'TechStartup Inc', 5 analysis_type: 'comprehensive', 6 time_period: '2024', 7 }, 8 }; 9 10 try { 11 console.log('🚀 Starting async AOP execution...'); 12 const asyncResponse = await client.aop.executeAsync(asyncAopRequest); 13 14 console.log('✅ AOP execution started'); 15 console.log('Thread ID:', asyncResponse.thread_id); 16 console.log('Status:', asyncResponse.status); 17 console.log('Sync Server:', asyncResponse.sync_server); 18 19 // Store the thread_id to track progress 20 const threadId = asyncResponse.thread_id; 21 22 // Poll for completion (see monitoring section below) 23 await monitorExecution(threadId); 24 } catch (error) { 25 if (error instanceof AthenaIntelligenceError) { 26 console.error(`Async AOP execution failed: ${error.statusCode} - ${error.message}`); 27 } else { 28 console.error('Unexpected error:', error); 29 } 30 }
Monitor Execution Progress
Track the progress of asynchronous AOP executions:
1 async function monitorExecution(threadId: string): Promise<void> { 2 const maxAttempts = 30; // Maximum polling attempts 3 const pollInterval = 5000; // 5 seconds between polls 4 5 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 6 try { 7 console.log(`📊 Checking execution status (attempt ${attempt}/${maxAttempts})...`); 8 9 const statusResponse = await client.threads.getStatus(threadId); 10 11 console.log('Current status:', statusResponse.status); 12 console.log('Updated at:', statusResponse.updated_at); 13 14 if (statusResponse.status === 'completed') { 15 console.log('🎉 AOP execution completed successfully!'); 16 17 // Access the final conversation results 18 if (statusResponse.conversation_asset) { 19 console.log('Final result available in conversation asset:', statusResponse.conversation_asset.conversation_asset_id); 20 21 if (statusResponse.conversation_asset.last_message) { 22 console.log('Final message:', statusResponse.conversation_asset.last_message.content); 23 } 24 } 25 break; 26 } else if (statusResponse.status === 'failed') { 27 console.error('❌ AOP execution failed'); 28 break; 29 } else { 30 console.log(`⏳ Still running... (${statusResponse.status})`); 31 32 // Wait before next poll 33 await new Promise(resolve => setTimeout(resolve, pollInterval)); 34 } 35 } catch (error) { 36 if (error instanceof AthenaIntelligenceError) { 37 console.error(`Status check failed: ${error.statusCode} - ${error.message}`); 38 } else { 39 console.error('Unexpected error during status check:', error); 40 } 41 break; 42 } 43 } 44 }
AOP with Custom Parameters
AOPs can accept various user inputs to customize their behavior:
1 // Market research AOP with detailed parameters 2 const marketResearchRequest: AthenaIntelligence.AopExecuteRequestIn = { 3 asset_id: 'asset_market_research_aop', 4 user_inputs: { 5 company: 'Tesla Inc', 6 industry: 'Electric Vehicles', 7 time_frame: '2023-2024', 8 analysis_depth: 'comprehensive', 9 include_competitors: 'true', 10 target_market: 'North America', 11 }, 12 }; 13 14 // Financial analysis AOP 15 const financialAnalysisRequest: AthenaIntelligence.AopExecuteRequestIn = { 16 asset_id: 'asset_financial_analysis_aop', 17 user_inputs: { 18 ticker: 'TSLA', 19 period: 'quarterly', 20 metrics: 'revenue,profit,growth', 21 comparison: 'industry_average', 22 }, 23 }; 24 25 // Execute multiple AOPs in parallel 26 const [marketResult, financialResult] = await Promise.all([ 27 client.aop.execute(marketResearchRequest), 28 client.aop.execute(financialAnalysisRequest), 29 ]); 30 31 console.log('Market Analysis:', marketResult.conversation?.last_assistant_message?.content); 32 console.log('Financial Analysis:', financialResult.conversation?.last_assistant_message?.content);
Understanding AOP Responses
AOP responses contain extensive metadata and the complete conversation history. Here’s how to access all the data:
1 // Execute an AOP and get the full response 2 const response = await client.aop.execute({ 3 asset_id: 'asset_market_research_aop', 4 user_inputs: { 5 company: 'Nvidia Corporation', 6 quarter: 'Q3 2024', 7 analysis_type: 'comprehensive', 8 }, 9 }) as AopExecuteResponse; 10 11 console.log('=== AOP Execution Metadata ==='); 12 console.log(`AOP Title: ${response.aop_title}`); 13 console.log(`Asset ID: ${response.aop_asset_id}`); 14 console.log(`Status: ${response.status}`); 15 console.log(`Thread ID: ${response.thread_id}`); 16 console.log(`Trigger Type: ${response.trigger_type}`); 17 console.log(`Sync Server: ${response.sync_server}`); 18 19 console.log('\n=== Prompt Information ==='); 20 console.log('Base Prompt:'); 21 console.log(response.base_prompt); 22 console.log('\nFinal Prompt (with user inputs):'); 23 console.log(response.final_prompt); 24 25 console.log('\n=== AOP Configuration ==='); 26 console.log(JSON.stringify(response.aop_config, null, 2)); 27 28 // Access the complete conversation 29 if (response.conversation) { 30 const conv = response.conversation; 31 32 console.log('\n=== Conversation Summary ==='); 33 console.log(`Conversation ID: ${conv.conversation_id}`); 34 console.log(`Title: ${conv.title}`); 35 console.log(`Total Messages: ${conv.num_messages}`); 36 console.log(`Messages Source: ${conv.messages_source}`); 37 console.log(`Created: ${conv.created_at}`); 38 console.log(`Last Updated: ${conv.updated_at}`); 39 40 console.log('\n=== Complete Message History ==='); 41 conv.messages.forEach((message, index) => { 42 console.log(`\n--- Message ${index + 1} ---`); 43 console.log(`ID: ${message.id}`); 44 console.log(`Role: ${message.role}`); 45 console.log(`Content Type: ${typeof message.content}`); 46 47 // Handle different content types 48 if (typeof message.content === 'string') { 49 console.log(`Content: ${message.content.length > 200 ? message.content.substring(0, 200) + '...' : message.content}`); 50 } else if (Array.isArray(message.content)) { 51 console.log(`Multimodal Content (${message.content.length} parts):`); 52 message.content.forEach((part, partIndex) => { 53 if (part.type === 'text') { 54 const truncated = part.text.length > 100 ? part.text.substring(0, 100) + '...' : part.text; 55 console.log(` Part ${partIndex + 1} [text]: ${truncated}`); 56 } else if (part.type === 'image_url') { 57 console.log(` Part ${partIndex + 1} [image]: ${JSON.stringify(part.image_url)}`); 58 } 59 }); 60 } 61 62 // Show additional message metadata 63 if (message.additional_kwargs && Object.keys(message.additional_kwargs).length > 0) { 64 console.log(`Additional Kwargs: ${JSON.stringify(message.additional_kwargs, null, 2)}`); 65 } 66 67 if (message.tool_calls && message.tool_calls.length > 0) { 68 console.log(`Tool Calls (${message.tool_calls.length}):`); 69 message.tool_calls.forEach((toolCall, toolIndex) => { 70 console.log(` Tool ${toolIndex + 1}: ${JSON.stringify(toolCall, null, 2)}`); 71 }); 72 } 73 74 if (message.response_metadata) { 75 console.log(`Response Metadata: ${JSON.stringify(message.response_metadata, null, 2)}`); 76 } 77 78 if (message.usage_metadata) { 79 console.log(`Usage Metadata: ${JSON.stringify(message.usage_metadata, null, 2)}`); 80 } 81 }); 82 83 // Check for final assistant response (nullable) 84 if (conv.last_assistant_message) { 85 console.log('\n=== Final Assistant Response ==='); 86 const finalMsg = conv.last_assistant_message; 87 console.log(`Message ID: ${finalMsg.id}`); 88 console.log(`Role: ${finalMsg.role}`); 89 90 if (typeof finalMsg.content === 'string') { 91 console.log('Final Content:'); 92 console.log(finalMsg.content); 93 } else if (Array.isArray(finalMsg.content)) { 94 console.log('Final Multimodal Content:'); 95 finalMsg.content.forEach((part, index) => { 96 if (part.type === 'text') { 97 console.log(`Text Part ${index + 1}: ${part.text}`); 98 } else if (part.type === 'image_url') { 99 console.log(`Image Part ${index + 1}: ${JSON.stringify(part.image_url, null, 2)}`); 100 } 101 }); 102 } 103 104 if (finalMsg.tool_calls && finalMsg.tool_calls.length > 0) { 105 console.log(`Final Message Tool Calls: ${JSON.stringify(finalMsg.tool_calls, null, 2)}`); 106 } 107 108 if (finalMsg.usage_metadata) { 109 console.log(`Token Usage: ${JSON.stringify(finalMsg.usage_metadata, null, 2)}`); 110 } 111 } else { 112 console.log('\n=== No Final Assistant Response Available ==='); 113 console.log('The conversation may still be running or may not have assistant responses yet.'); 114 } 115 } 116 117 // Handle errors in the response 118 if (response.error) { 119 console.error('\n=== Execution Error ==='); 120 console.error(response.error); 121 }
Working with Conversation Data
Extract and process conversation data for further use:
1 // Function to extract all text content from a conversation 2 function extractConversationText(conversation: ConversationResult): string[] { 3 return conversation.messages 4 .map(message => { 5 if (typeof message.content === 'string') { 6 return message.content; 7 } else if (Array.isArray(message.content)) { 8 return message.content 9 .filter(part => part.type === 'text') 10 .map(part => (part as TextContent).text) 11 .join('\n'); 12 } 13 return ''; 14 }) 15 .filter(content => content.length > 0); 16 } 17 18 // Function to get conversation summary 19 function getConversationSummary(conversation: ConversationResult) { 20 return { 21 id: conversation.conversation_id, 22 title: conversation.title, 23 messageCount: conversation.num_messages, 24 duration: new Date(conversation.updated_at).getTime() - new Date(conversation.created_at).getTime(), 25 finalResponse: conversation.last_assistant_message?.content || null, // Handle nullable 26 allTextContent: extractConversationText(conversation), 27 hasFinalAssistantMessage: !!conversation.last_assistant_message, 28 }; 29 } 30 31 // Function to analyze conversation for insights 32 function analyzeConversation(conversation: ConversationResult) { 33 const userMessages = conversation.messages.filter(m => m.role === 'user'); 34 const assistantMessages = conversation.messages.filter(m => m.role === 'assistant'); 35 const toolCalls = conversation.messages.flatMap(m => m.tool_calls || []); 36 37 return { 38 userMessageCount: userMessages.length, 39 assistantMessageCount: assistantMessages.length, 40 totalToolCalls: toolCalls.length, 41 averageResponseLength: assistantMessages.reduce((sum, msg) => { 42 const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content); 43 return sum + content.length; 44 }, 0) / assistantMessages.length, 45 conversationDuration: new Date(conversation.updated_at).getTime() - new Date(conversation.created_at).getTime(), 46 hasMultimodalContent: conversation.messages.some(m => 47 Array.isArray(m.content) && m.content.some(part => part.type === 'image_url') 48 ), 49 }; 50 } 51 52 // Usage examples 53 const aopResult = await client.aop.execute({ 54 asset_id: 'asset_analysis_aop', 55 user_inputs: { company: 'Microsoft', focus: 'cloud_services' }, 56 }) as AopExecuteResponse; 57 58 if (aopResult.conversation) { 59 // Get summary 60 const summary = getConversationSummary(aopResult.conversation); 61 console.log('Conversation Summary:', summary); 62 63 // Analyze conversation 64 const analysis = analyzeConversation(aopResult.conversation); 65 console.log('Conversation Analysis:', analysis); 66 67 // Extract all text for further processing 68 const allText = extractConversationText(aopResult.conversation); 69 console.log('All conversation text extracted:', allText.length, 'segments'); 70 }
Error Handling and Best Practices
Implement comprehensive error handling for production applications:
1 async function executeAopSafely( 2 assetId: string, 3 userInputs?: Record<string, string> 4 ): Promise<AthenaIntelligence.AopExecuteResponseOut | null> { 5 try { 6 // Validate inputs 7 if (!assetId) { 8 throw new Error('Asset ID is required'); 9 } 10 11 const request: AthenaIntelligence.AopExecuteRequestIn = { 12 asset_id: assetId, 13 user_inputs: userInputs, 14 }; 15 16 console.log('Executing AOP with inputs:', JSON.stringify(userInputs, null, 2)); 17 18 const response = await client.aop.execute(request); 19 20 // Validate response 21 if (!response.conversation && response.status === 'completed') { 22 console.warn('AOP completed but no conversation data available'); 23 } 24 25 return response; 26 } catch (error) { 27 if (error instanceof AthenaIntelligenceError) { 28 // Handle specific API errors 29 switch (error.statusCode) { 30 case 401: 31 console.error('Authentication failed - check your API key'); 32 break; 33 case 404: 34 console.error('AOP asset not found - check the asset_id'); 35 break; 36 case 400: 37 console.error('Invalid request - check your user_inputs format'); 38 break; 39 default: 40 console.error(`AOP execution failed: ${error.statusCode} - ${error.message}`); 41 } 42 } else { 43 console.error('Unexpected error during AOP execution:', error); 44 } 45 return null; 46 } 47 } 48 49 // Usage 50 const result = await executeAopSafely( 51 'asset_9249292-d118-42d3-95b4-00eccfe0754f', 52 { company: 'Example Corp', quarter: 'Q1 2024' } 53 );
Complete Working Example
Here’s a complete example demonstrating both sync and async AOP execution:
1 import type { AthenaIntelligence } from 'athena-intelligence'; 2 import { AthenaIntelligenceClient, AthenaIntelligenceError } from 'athena-intelligence'; 3 4 async function runAopExample() { 5 try { 6 const client = new AthenaIntelligenceClient({ 7 apiKey: process.env.ATHENA_API_KEY, 8 }); 9 10 // Example 1: Synchronous execution for quick tasks 11 console.log('=== Synchronous AOP Execution ==='); 12 13 const syncRequest: AthenaIntelligence.AopExecuteRequestIn = { 14 asset_id: 'asset_quick_analysis_aop', 15 user_inputs: { 16 topic: 'AI market trends', 17 focus: 'investment analysis', 18 }, 19 }; 20 21 const syncResponse = await client.aop.execute(syncRequest); 22 23 // Safe access to conversation result 24 if (syncResponse.conversation?.last_assistant_message?.content) { 25 console.log('Sync Result:', syncResponse.conversation.last_assistant_message.content); 26 } else { 27 console.log('Sync Result: No final assistant message available'); 28 console.log('Response status:', syncResponse.status); 29 } 30 31 // Example 2: Asynchronous execution for complex workflows 32 console.log('\n=== Asynchronous AOP Execution ==='); 33 34 const asyncRequest: AthenaIntelligence.AopExecuteRequestIn = { 35 asset_id: 'asset_comprehensive_research_aop', 36 user_inputs: { 37 company: 'Microsoft Corporation', 38 analysis_type: 'full_competitive_analysis', 39 time_horizon: '2024-2025', 40 include_financials: 'true', 41 }, 42 }; 43 44 const asyncResponse = await client.aop.executeAsync(asyncRequest); 45 console.log('Async execution started with thread:', asyncResponse.thread_id); 46 47 // Monitor the async execution 48 await monitorAsyncExecution(client, asyncResponse.thread_id); 49 50 } catch (error) { 51 if (error instanceof AthenaIntelligenceError) { 52 console.error(`AOP example failed: ${error.statusCode} - ${error.message}`); 53 } else { 54 console.error('Unexpected error:', error); 55 } 56 } 57 } 58 59 async function monitorAsyncExecution( 60 client: AthenaIntelligenceClient, 61 threadId: string 62 ): Promise<void> { 63 const maxAttempts = 24; // 2 minutes with 5-second intervals 64 const pollInterval = 5000; 65 66 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 67 try { 68 const status = await client.threads.getStatus(threadId); 69 70 console.log(`[${attempt}/${maxAttempts}] Status: ${status.status} (Updated: ${status.updated_at})`); 71 72 if (status.status === 'completed') { 73 console.log('🎉 AOP execution completed successfully!'); 74 75 if (status.conversation_asset?.last_message) { 76 console.log('Final Result:'); 77 console.log(status.conversation_asset.last_message.content); 78 } 79 return; 80 } else if (status.status === 'failed') { 81 console.error('❌ AOP execution failed'); 82 return; 83 } 84 85 // Continue polling 86 await new Promise(resolve => setTimeout(resolve, pollInterval)); 87 } catch (error) { 88 console.error('Status check failed:', error); 89 return; 90 } 91 } 92 93 console.warn('⏰ Polling timeout reached - execution may still be running'); 94 } 95 96 runAopExample();
Working with Different AOP Types
AOPs can be designed for various use cases. Here are examples for common scenarios:
1 // Research and Analysis AOP 2 const researchAop: AthenaIntelligence.AopExecuteRequestIn = { 3 asset_id: 'asset_research_aop', 4 user_inputs: { 5 research_topic: 'Quantum computing breakthroughs', 6 depth: 'comprehensive', 7 sources: 'academic,industry,news', 8 time_range: 'last_12_months', 9 }, 10 }; 11 12 // Content Generation AOP 13 const contentAop: AthenaIntelligence.AopExecuteRequestIn = { 14 asset_id: 'asset_content_generator_aop', 15 user_inputs: { 16 content_type: 'blog_post', 17 topic: 'AI in healthcare', 18 tone: 'professional', 19 length: '1500_words', 20 target_audience: 'healthcare_professionals', 21 }, 22 }; 23 24 // Data Analysis AOP 25 const dataAnalysisAop: AthenaIntelligence.AopExecuteRequestIn = { 26 asset_id: 'asset_data_analysis_aop', 27 user_inputs: { 28 dataset_id: 'sales_q1_2024', 29 analysis_type: 'trend_analysis', 30 metrics: 'revenue,growth,customer_acquisition', 31 visualization: 'charts_and_tables', 32 }, 33 }; 34 35 // Execute different AOPs based on use case 36 const results = await Promise.allSettled([ 37 client.aop.execute(researchAop), 38 client.aop.execute(contentAop), 39 client.aop.execute(dataAnalysisAop), 40 ]); 41 42 results.forEach((result, index) => { 43 const aopNames = ['Research', 'Content', 'Data Analysis']; 44 45 if (result.status === 'fulfilled') { 46 console.log(`${aopNames[index]} AOP completed:`, result.value.status); 47 } else { 48 console.error(`${aopNames[index]} AOP failed:`, result.reason); 49 } 50 });
Advanced Configuration
Some AOPs may require specific configuration or have optional parameters:
1 // AOP with minimal inputs 2 const minimalRequest: AthenaIntelligence.AopExecuteRequestIn = { 3 asset_id: 'asset_simple_aop', 4 // No user_inputs required for this AOP 5 }; 6 7 // AOP with complex nested inputs 8 const complexRequest: AthenaIntelligence.AopExecuteRequestIn = { 9 asset_id: 'asset_advanced_aop', 10 user_inputs: { 11 // Simple parameters 12 company_name: 'OpenAI', 13 analysis_date: '2024-01-15', 14 15 // JSON-stringified complex parameters 16 search_criteria: JSON.stringify({ 17 industries: ['technology', 'ai', 'saas'], 18 market_cap_min: 1000000000, 19 regions: ['north_america', 'europe'], 20 }), 21 22 // Configuration flags 23 include_charts: 'true', 24 export_format: 'pdf', 25 email_results: 'false', 26 }, 27 }; 28 29 const complexResponse = await client.aop.execute(complexRequest);
TypeScript Type Definitions
For better type safety, define comprehensive interfaces that match the full API response structure:
1 // Content types for messages 2 interface TextContent { 3 type: 'text'; 4 text: string; 5 } 6 7 interface ImageUrlContent { 8 type: 'image_url'; 9 image_url: Record<string, string>; 10 } 11 12 type MessageContent = string | (TextContent | ImageUrlContent)[]; 13 14 // Message structure 15 interface ConversationMessage { 16 id: string; 17 role: 'user' | 'assistant' | 'system' | 'tool'; 18 content: MessageContent; 19 additional_kwargs?: Record<string, any>; 20 name?: string; 21 tool_calls?: Record<string, any>[]; 22 tool_call_id?: string; 23 response_metadata?: Record<string, any>; 24 usage_metadata?: Record<string, any>; 25 } 26 27 // Complete conversation result structure 28 interface ConversationResult { 29 conversation_id: string; 30 created_at: string; 31 updated_at: string; 32 title: string; 33 num_messages: number; 34 messages_source: string; 35 messages: ConversationMessage[]; 36 last_assistant_message?: ConversationMessage; // Optional - may not always be present 37 } 38 39 // Conversation asset information (from OpenAPI ConversationAssetInfo) 40 interface ConversationAssetInfo { 41 conversation_asset_id: string; 42 title: string; 43 created_at: string; 44 updated_at: string; 45 created_by: string; 46 workspace_id: string; 47 state: string | null; 48 start_channel: string | null; 49 last_channel: string | null; 50 num_messages: number | null; 51 last_message: ConversationMessage | null; // Explicitly nullable in schema 52 linked_aops: Record<string, any>[] | null; 53 linked_projects: Record<string, any>[] | null; 54 agent: string | null; 55 model: string | null; 56 athena_metadata: Record<string, any> | null; 57 messages: ConversationMessage[] | null; // Nullable in schema - may not always be loaded 58 } 59 60 // Complete AOP execution response 61 interface AopExecuteResponse { 62 status: string; 63 thread_id: string; 64 sync_server: string; 65 trigger_type: string; 66 aop_asset_id: string; 67 aop_title: string; 68 base_prompt: string; 69 final_prompt: string; 70 aop_config: Record<string, any>; 71 conversation?: ConversationResult | null; 72 error?: string | null; 73 } 74 75 // Async AOP response 76 interface AopAsyncExecuteResponse { 77 status: string; 78 thread_id: string; 79 sync_server: string; 80 trigger_type: string; 81 message: string; 82 aop_asset_id: string; 83 aop_title: string; 84 base_prompt: string; 85 final_prompt: string; 86 aop_config: Record<string, any>; 87 } 88 89 // Thread status response 90 interface ThreadStatusResponse { 91 thread_id: string; 92 status: string; 93 created_at: string; 94 updated_at: string; 95 conversation_asset: ConversationAssetInfo | null; 96 } 97 98 // AOP input types 99 interface MarketResearchInputs { 100 company: string; 101 quarter: string; 102 analysis_depth?: 'basic' | 'comprehensive' | 'deep'; 103 include_competitors?: 'true' | 'false'; 104 target_market?: string; 105 industry?: string; 106 time_horizon?: string; 107 } 108 109 interface ContentGenerationInputs { 110 topic: string; 111 content_type: 'blog_post' | 'report' | 'summary' | 'presentation'; 112 tone: 'professional' | 'casual' | 'technical' | 'friendly'; 113 length: string; 114 target_audience: string; 115 keywords?: string; 116 outline_required?: 'true' | 'false'; 117 } 118 119 interface DataAnalysisInputs { 120 dataset_id: string; 121 analysis_type: 'trend_analysis' | 'comparative' | 'predictive'; 122 metrics: string; 123 time_period?: string; 124 visualization?: 'charts_and_tables' | 'dashboard' | 'report'; 125 export_format?: 'json' | 'csv' | 'pdf'; 126 } 127 128 // Type-safe AOP execution functions 129 async function executeMarketResearchAop( 130 assetId: string, 131 inputs: MarketResearchInputs 132 ): Promise<AopExecuteResponse> { 133 const request: AthenaIntelligence.AopExecuteRequestIn = { 134 asset_id: assetId, 135 user_inputs: inputs as Record<string, string>, 136 }; 137 138 const response = await client.aop.execute(request); 139 return response as AopExecuteResponse; 140 } 141 142 async function executeContentGenerationAop( 143 assetId: string, 144 inputs: ContentGenerationInputs 145 ): Promise<AopExecuteResponse> { 146 const request: AthenaIntelligence.AopExecuteRequestIn = { 147 asset_id: assetId, 148 user_inputs: inputs as Record<string, string>, 149 }; 150 151 const response = await client.aop.execute(request); 152 return response as AopExecuteResponse; 153 } 154 155 // Type-safe async execution with monitoring 156 async function executeAopAsync<T extends Record<string, string>>( 157 assetId: string, 158 inputs: T 159 ): Promise<{ response: AopAsyncExecuteResponse; finalResult: ThreadStatusResponse }> { 160 // Start async execution 161 const asyncResponse = await client.aop.executeAsync({ 162 asset_id: assetId, 163 user_inputs: inputs, 164 }) as AopAsyncExecuteResponse; 165 166 // Monitor until completion 167 const finalResult = await monitorExecutionTyped(asyncResponse.thread_id); 168 169 return { response: asyncResponse, finalResult }; 170 } 171 172 // Type-safe monitoring function 173 async function monitorExecutionTyped(threadId: string): Promise<ThreadStatusResponse> { 174 const maxAttempts = 30; 175 const pollInterval = 5000; 176 177 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 178 const status = await client.threads.getStatus(threadId) as ThreadStatusResponse; 179 180 console.log(`[${attempt}/${maxAttempts}] Status: ${status.status}`); 181 182 if (status.status === 'completed' || status.status === 'failed') { 183 return status; 184 } 185 186 await new Promise(resolve => setTimeout(resolve, pollInterval)); 187 } 188 189 throw new Error('Monitoring timeout reached'); 190 } 191 192 // Usage with full type safety and conversation access 193 const marketResult = await executeMarketResearchAop( 194 'asset_market_research_aop', 195 { 196 company: 'Apple Inc', 197 quarter: 'Q4 2024', 198 analysis_depth: 'comprehensive', 199 include_competitors: 'true', 200 target_market: 'global', 201 industry: 'technology', 202 } 203 ); 204 205 // Access typed conversation data 206 if (marketResult.conversation) { 207 const conversation = marketResult.conversation; 208 209 console.log('Conversation Details:'); 210 console.log(`- ID: ${conversation.conversation_id}`); 211 console.log(`- Title: ${conversation.title}`); 212 console.log(`- Messages: ${conversation.num_messages}`); 213 console.log(`- Created: ${conversation.created_at}`); 214 console.log(`- Updated: ${conversation.updated_at}`); 215 216 // Access all messages with type safety 217 conversation.messages.forEach((message, index) => { 218 console.log(`Message ${index + 1} (${message.role}):`); 219 220 if (typeof message.content === 'string') { 221 console.log(` Content: ${message.content}`); 222 } else if (Array.isArray(message.content)) { 223 message.content.forEach((part, partIndex) => { 224 if (part.type === 'text') { 225 console.log(` Text Part ${partIndex + 1}: ${part.text}`); 226 } else if (part.type === 'image_url') { 227 console.log(` Image Part ${partIndex + 1}: ${JSON.stringify(part.image_url)}`); 228 } 229 }); 230 } 231 232 if (message.tool_calls && message.tool_calls.length > 0) { 233 console.log(` Tool Calls: ${message.tool_calls.length}`); 234 } 235 236 if (message.additional_kwargs) { 237 console.log(` Additional Data: ${JSON.stringify(message.additional_kwargs, null, 2)}`); 238 } 239 }); 240 241 // Access the final assistant response (if available) 242 if (conversation.last_assistant_message) { 243 const finalMessage = conversation.last_assistant_message; 244 console.log('\nFinal Assistant Response:'); 245 console.log(`- ID: ${finalMessage.id}`); 246 console.log(`- Role: ${finalMessage.role}`); 247 console.log(`- Content: ${typeof finalMessage.content === 'string' ? finalMessage.content : JSON.stringify(finalMessage.content)}`); 248 249 if (finalMessage.usage_metadata) { 250 console.log(`- Usage Metadata: ${JSON.stringify(finalMessage.usage_metadata, null, 2)}`); 251 } 252 } else { 253 console.log('\nNo final assistant message available'); 254 } 255 } 256 257 // Example with async execution and full conversation access 258 const asyncResult = await executeAopAsync('asset_comprehensive_analysis_aop', { 259 company: 'Tesla Inc', 260 analysis_type: 'full_market_analysis', 261 time_period: '2024', 262 include_forecasts: 'true', 263 }); 264 265 console.log('Async Execution Started:', asyncResult.response.thread_id); 266 console.log('Final Status:', asyncResult.finalResult.status); 267 268 // Access the full conversation from the final result (handle nullable messages) 269 if (asyncResult.finalResult.conversation_asset?.messages) { 270 const messages = asyncResult.finalResult.conversation_asset.messages; 271 272 console.log(`\nFull Conversation (${messages.length} messages):`); 273 274 messages.forEach((msg, index) => { 275 console.log(`${index + 1}. [${msg.role}] ${typeof msg.content === 'string' ? msg.content.substring(0, 100) + '...' : '[Complex Content]'}`); 276 }); 277 } else if (asyncResult.finalResult.conversation_asset?.last_message) { 278 // If full messages aren't available, show at least the last message 279 const lastMsg = asyncResult.finalResult.conversation_asset.last_message; 280 console.log(`\nLast Message Available: [${lastMsg.role}] ${typeof lastMsg.content === 'string' ? lastMsg.content.substring(0, 100) + '...' : '[Complex Content]'}`); 281 } else { 282 console.log('\nNo conversation messages available - conversation may still be loading'); 283 }
Handling Nullable Fields and Conversation States
The API returns nullable fields in various scenarios. Always check for null values before accessing nested properties:
1 // Safe conversation access function 2 function safelyAccessConversation(response: AopExecuteResponse): void { 3 console.log('=== Safe Conversation Access ==='); 4 5 // 1. Check if conversation exists at all 6 if (!response.conversation) { 7 console.log('❌ No conversation data available'); 8 console.log('This may happen if:'); 9 console.log(' - AOP execution failed before starting'); 10 console.log(' - AOP is still initializing'); 11 console.log(' - Response format changed'); 12 return; 13 } 14 15 const conv = response.conversation; 16 console.log('✅ Conversation data available'); 17 18 // 2. Check messages array 19 if (!conv.messages || conv.messages.length === 0) { 20 console.log('❌ No messages in conversation'); 21 console.log('This may happen if:'); 22 console.log(' - Conversation just started'); 23 console.log(' - Messages not yet loaded from checkpoints'); 24 } else { 25 console.log(`✅ ${conv.messages.length} messages available`); 26 27 // Process messages safely 28 conv.messages.forEach((msg, index) => { 29 console.log(`Message ${index + 1}: [${msg.role}]`); 30 31 // Safe content access 32 if (typeof msg.content === 'string') { 33 console.log(` Text: ${msg.content.substring(0, 100)}...`); 34 } else if (Array.isArray(msg.content)) { 35 console.log(` Multimodal content with ${msg.content.length} parts`); 36 } else { 37 console.log(' No content available'); 38 } 39 }); 40 } 41 42 // 3. Check last assistant message 43 if (!conv.last_assistant_message) { 44 console.log('❌ No final assistant message'); 45 console.log('This may happen if:'); 46 console.log(' - Conversation ended with user message'); 47 console.log(' - Assistant response not yet generated'); 48 console.log(' - Execution was interrupted'); 49 } else { 50 console.log('✅ Final assistant message available'); 51 const finalContent = conv.last_assistant_message.content; 52 if (typeof finalContent === 'string') { 53 console.log(`Final response: ${finalContent.substring(0, 150)}...`); 54 } 55 } 56 57 // 4. Show conversation metadata with safe access 58 console.log('\n=== Conversation Metadata ==='); 59 console.log(`ID: ${conv.conversation_id}`); 60 console.log(`Title: ${conv.title}`); 61 console.log(`Message Count: ${conv.num_messages}`); 62 console.log(`Source: ${conv.messages_source}`); 63 console.log(`Created: ${conv.created_at}`); 64 console.log(`Updated: ${conv.updated_at}`); 65 } 66 67 // Safe thread status access 68 function safelyAccessThreadStatus(status: ThreadStatusResponse): void { 69 console.log('=== Safe Thread Status Access ==='); 70 console.log(`Thread ID: ${status.thread_id}`); 71 console.log(`Status: ${status.status}`); 72 console.log(`Created: ${status.created_at}`); 73 console.log(`Updated: ${status.updated_at}`); 74 75 // Check conversation asset (nullable) 76 if (!status.conversation_asset) { 77 console.log('❌ No conversation asset available'); 78 return; 79 } 80 81 const asset = status.conversation_asset; 82 console.log('✅ Conversation asset available'); 83 84 // Safe access to nullable fields 85 console.log(`Asset ID: ${asset.conversation_asset_id}`); 86 console.log(`Title: ${asset.title}`); 87 console.log(`State: ${asset.state || 'unknown'}`); 88 console.log(`Agent: ${asset.agent || 'not specified'}`); 89 console.log(`Model: ${asset.model || 'not specified'}`); 90 console.log(`Start Channel: ${asset.start_channel || 'unknown'}`); 91 console.log(`Last Channel: ${asset.last_channel || 'unknown'}`); 92 console.log(`Message Count: ${asset.num_messages ?? 'unknown'}`); 93 94 // Check last message (nullable) 95 if (asset.last_message) { 96 console.log('✅ Last message available'); 97 console.log(`Last Message Role: ${asset.last_message.role}`); 98 const content = asset.last_message.content; 99 if (typeof content === 'string') { 100 console.log(`Last Message Content: ${content.substring(0, 100)}...`); 101 } 102 } else { 103 console.log('❌ No last message available'); 104 } 105 106 // Check messages array (nullable) 107 if (asset.messages && asset.messages.length > 0) { 108 console.log(`✅ ${asset.messages.length} messages loaded from asset`); 109 } else { 110 console.log('❌ Messages not loaded in asset (use conversation field from sync response instead)'); 111 } 112 113 // Check metadata (nullable) 114 if (asset.athena_metadata) { 115 console.log('✅ Athena metadata available:', Object.keys(asset.athena_metadata)); 116 } else { 117 console.log('❌ No athena metadata'); 118 } 119 120 // Check linked assets (nullable) 121 if (asset.linked_aops && asset.linked_aops.length > 0) { 122 console.log(`✅ ${asset.linked_aops.length} linked AOPs`); 123 } 124 125 if (asset.linked_projects && asset.linked_projects.length > 0) { 126 console.log(`✅ ${asset.linked_projects.length} linked projects`); 127 } 128 } 129 130 // Example usage with comprehensive null checking 131 const aopResponse = await client.aop.execute({ 132 asset_id: 'asset_example_aop', 133 user_inputs: { param: 'value' }, 134 }) as AopExecuteResponse; 135 136 // Use safe access functions 137 safelyAccessConversation(aopResponse); 138 139 // For async executions 140 const asyncResponse = await client.aop.executeAsync({ 141 asset_id: 'asset_example_aop', 142 user_inputs: { param: 'value' }, 143 }) as AopAsyncExecuteResponse; 144 145 // Monitor and safely access final status 146 const finalStatus = await monitorExecutionTyped(asyncResponse.thread_id); 147 safelyAccessThreadStatus(finalStatus);
Error Recovery and Retry Logic
Implement robust error handling with retry logic:
1 async function executeAopWithRetry( 2 request: AthenaIntelligence.AopExecuteRequestIn, 3 maxRetries: number = 3 4 ): Promise<AthenaIntelligence.AopExecuteResponseOut | null> { 5 let lastError: Error | null = null; 6 7 for (let attempt = 1; attempt <= maxRetries; attempt++) { 8 try { 9 console.log(`Attempt ${attempt}/${maxRetries} for AOP execution...`); 10 11 const response = await client.aop.execute(request); 12 console.log(`✅ AOP execution successful on attempt ${attempt}`); 13 return response; 14 15 } catch (error) { 16 lastError = error as Error; 17 18 if (error instanceof AthenaIntelligenceError) { 19 // Don't retry on client errors (4xx) 20 if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) { 21 console.error(`Client error (${error.statusCode}): ${error.message}`); 22 break; 23 } 24 25 // Retry on server errors (5xx) or network issues 26 console.warn(`Server error on attempt ${attempt}: ${error.statusCode} - ${error.message}`); 27 } else { 28 console.warn(`Network error on attempt ${attempt}:`, error.message); 29 } 30 31 // Wait before retrying (exponential backoff) 32 if (attempt < maxRetries) { 33 const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000); 34 console.log(`Waiting ${delay}ms before retry...`); 35 await new Promise(resolve => setTimeout(resolve, delay)); 36 } 37 } 38 } 39 40 console.error('❌ AOP execution failed after all retry attempts'); 41 throw lastError; 42 } 43 44 // Usage with retry logic 45 const retriedResult = await executeAopWithRetry({ 46 asset_id: 'asset_important_aop', 47 user_inputs: { critical_param: 'value' }, 48 });