From 4607c276a36fe34c40fe2f4dc8cf4b8f547b53e8 Mon Sep 17 00:00:00 2001 From: Nikhil-Doye Date: Mon, 20 Oct 2025 17:10:26 -0400 Subject: [PATCH] Enhance error handling and reporting in WorkflowAgent and AgentManager - Improved error handling in WorkflowAgent and AgentManager to include detailed error context, allowing for better debugging and user feedback. - Introduced a new DetailedError class to encapsulate error information, including stage, tool name, input parameters, and suggestions for resolution. - Updated methods to utilize the new error handling structure, ensuring that errors are logged with comprehensive context. - Added utility functions for formatting error messages for both users and developers, enhancing the overall error reporting mechanism. - Enhanced the workflow store to log detailed error context when generating workflows, improving traceability of issues. --- src/services/agents/AgentManager.ts | 8 + src/services/agents/WorkflowAgent.ts | 312 ++++++++++++++++++++++++--- src/store/workflowStore.ts | 16 ++ src/types/tools.ts | 20 ++ src/utils/errorReporting.ts | 167 ++++++++++++++ 5 files changed, 489 insertions(+), 34 deletions(-) create mode 100644 src/utils/errorReporting.ts diff --git a/src/services/agents/AgentManager.ts b/src/services/agents/AgentManager.ts index 1018b38..13f0121 100644 --- a/src/services/agents/AgentManager.ts +++ b/src/services/agents/AgentManager.ts @@ -92,12 +92,20 @@ export class AgentManager { return result; } catch (error) { console.error("Error in AgentManager:", error); + + // Preserve error context if available + const errorContext = + error instanceof Error && "errorContext" in error + ? (error as any).errorContext + : undefined; + return { success: false, error: error instanceof Error ? error.message : "Unknown error", toolsUsed: [], executionTime: 0, confidence: 0.0, + errorContext, }; } } diff --git a/src/services/agents/WorkflowAgent.ts b/src/services/agents/WorkflowAgent.ts index 2b42b32..9537628 100644 --- a/src/services/agents/WorkflowAgent.ts +++ b/src/services/agents/WorkflowAgent.ts @@ -2,6 +2,7 @@ import { AgentResult, AgentContext, ToolExecutionPlan, + ErrorContext, } from "../../types/tools"; import { toolRegistry } from "../tools/ToolRegistry"; import { ParsedIntent } from "../../types"; @@ -16,80 +17,130 @@ export class WorkflowAgent { async processRequest(userInput: string): Promise { const startTime = Date.now(); const toolsUsed: string[] = []; + const partialResults: Record = {}; try { // Step 1: Check cache first const cacheTool = toolRegistry.getTool("cache_lookup"); if (cacheTool) { - const cacheKey = this.generateCacheKey(userInput); - const cacheResult = await cacheTool.execute({ key: cacheKey }); + try { + const cacheKey = this.generateCacheKey(userInput); + const cacheResult = await cacheTool.execute({ key: cacheKey }); - if (cacheResult.success && cacheResult.data) { - return { - success: true, - data: cacheResult.data, - toolsUsed: ["cache_lookup"], - executionTime: Date.now() - startTime, - confidence: 0.9, - }; + if (cacheResult.success && cacheResult.data) { + return { + success: true, + data: cacheResult.data, + toolsUsed: ["cache_lookup"], + executionTime: Date.now() - startTime, + confidence: 0.9, + }; + } + toolsUsed.push("cache_lookup"); + } catch (error) { + console.warn("Cache lookup failed, continuing without cache:", error); + // Continue execution without cache } - toolsUsed.push("cache_lookup"); } // Step 2: Classify intent - const intentResult = await this.executeTool("classify_intent", { - userInput, - }); + const intentResult = await this.executeToolWithContext( + "classify_intent", + { userInput }, + "intent_classification", + partialResults + ); if (!intentResult.success) { - throw new Error("Failed to classify intent"); + throw this.createDetailedError( + "Failed to classify intent", + "intent_classification", + "classify_intent", + { userInput }, + partialResults, + "tool_execution_failure", + userInput + ); } toolsUsed.push("classify_intent"); + partialResults.intent = intentResult.data; // Step 3: Extract entities - const entitiesResult = await this.executeTool("extract_entities", { - userInput, - intent: intentResult.data, - }); + const entitiesResult = await this.executeToolWithContext( + "extract_entities", + { userInput, intent: intentResult.data }, + "entity_extraction", + partialResults + ); if (!entitiesResult.success) { - throw new Error("Failed to extract entities"); + throw this.createDetailedError( + "Failed to extract entities", + "entity_extraction", + "extract_entities", + { userInput, intent: intentResult.data }, + partialResults, + "tool_execution_failure", + userInput + ); } toolsUsed.push("extract_entities"); + partialResults.entities = entitiesResult.data; // Step 4: Generate workflow - const workflowResult = await this.executeTool("generate_workflow", { - userInput, - intent: intentResult.data, - entities: entitiesResult.data, - }); + const workflowResult = await this.executeToolWithContext( + "generate_workflow", + { userInput, intent: intentResult.data, entities: entitiesResult.data }, + "workflow_generation", + partialResults + ); if (!workflowResult.success) { - throw new Error("Failed to generate workflow"); + throw this.createDetailedError( + "Failed to generate workflow", + "workflow_generation", + "generate_workflow", + { + userInput, + intent: intentResult.data, + entities: entitiesResult.data, + }, + partialResults, + "tool_execution_failure", + userInput + ); } toolsUsed.push("generate_workflow"); + partialResults.workflow = workflowResult.data; // Step 5: Validate workflow - const validationResult = await this.executeTool("validate_workflow", { - workflow: workflowResult.data, - originalInput: userInput, - }); + const validationResult = await this.executeToolWithContext( + "validate_workflow", + { workflow: workflowResult.data, originalInput: userInput }, + "workflow_validation", + partialResults + ); if (!validationResult.success) { console.warn( "Workflow validation failed, but continuing with generated workflow" ); + // Don't throw error for validation failures, just log warning } toolsUsed.push("validate_workflow"); + partialResults.validation = validationResult.data; // Step 6: Generate suggestions - const suggestionsResult = await this.executeTool("generate_suggestions", { - workflow: workflowResult.data, - context: userInput, - }); + const suggestionsResult = await this.executeToolWithContext( + "generate_suggestions", + { workflow: workflowResult.data, context: userInput }, + "suggestion_generation", + partialResults + ); if (suggestionsResult.success) { toolsUsed.push("generate_suggestions"); + partialResults.suggestions = suggestionsResult.data; } // Create final result @@ -122,12 +173,28 @@ export class WorkflowAgent { }; } catch (error) { console.error("Error in WorkflowAgent:", error); + + // If it's already a detailed error, use it; otherwise create one + const errorContext = + error instanceof DetailedError + ? error.errorContext + : this.createErrorContext( + "unknown", + undefined, + { userInput }, + partialResults, + "unknown", + userInput, + error instanceof Error ? error.stack : undefined + ); + return { success: false, error: error instanceof Error ? error.message : "Unknown error", toolsUsed, executionTime: Date.now() - startTime, confidence: 0.0, + errorContext, }; } } @@ -149,6 +216,170 @@ export class WorkflowAgent { return result; } + private async executeToolWithContext( + toolName: string, + params: Record, + stage: string, + partialResults: Record + ): Promise { + try { + const tool = toolRegistry.getTool(toolName); + if (!tool) { + throw this.createDetailedError( + `Tool not found: ${toolName}`, + stage, + toolName, + params, + partialResults, + "tool_registry_miss", + this.context.userRequest + ); + } + + const result = await tool.execute(params); + if (!result.success) { + throw this.createDetailedError( + `Tool ${toolName} failed: ${result.error}`, + stage, + toolName, + params, + partialResults, + "tool_execution_failure", + this.context.userRequest + ); + } + + return result; + } catch (error) { + if (error instanceof DetailedError) { + throw error; + } + + // Convert generic errors to detailed errors + throw this.createDetailedError( + error instanceof Error ? error.message : "Unknown tool execution error", + stage, + toolName, + params, + partialResults, + "tool_execution_failure", + this.context.userRequest, + error instanceof Error ? error.stack : undefined + ); + } + } + + private createDetailedError( + message: string, + stage: string, + toolName: string | undefined, + inputParameters: Record, + partialResults: Record, + errorType: ErrorContext["errorType"], + userInput: string, + stackTrace?: string + ): DetailedError { + const errorContext = this.createErrorContext( + stage, + toolName, + inputParameters, + partialResults, + errorType, + userInput, + stackTrace + ); + + return new DetailedError(message, errorContext); + } + + private createErrorContext( + stage: string, + toolName: string | undefined, + inputParameters: Record, + partialResults: Record, + errorType: ErrorContext["errorType"], + userInput: string, + stackTrace?: string + ): ErrorContext { + return { + stage, + toolName, + inputParameters, + partialResults, + errorType, + timestamp: Date.now(), + sessionId: this.context.sessionId, + userInput, + stackTrace, + suggestions: this.generateErrorSuggestions( + stage, + errorType, + partialResults + ), + }; + } + + private generateErrorSuggestions( + stage: string, + errorType: ErrorContext["errorType"], + partialResults: Record + ): string[] { + const suggestions: string[] = []; + + switch (errorType) { + case "tool_registry_miss": + suggestions.push("Check if the required tool is properly registered"); + suggestions.push("Verify tool name spelling and availability"); + break; + + case "tool_execution_failure": + suggestions.push("Check tool configuration and parameters"); + suggestions.push("Verify external service availability (OpenAI, etc.)"); + if (stage === "intent_classification") { + suggestions.push("Try rephrasing your request with clearer intent"); + } else if (stage === "entity_extraction") { + suggestions.push( + "Ensure your input contains recognizable entities (URLs, data types, etc.)" + ); + } else if (stage === "workflow_generation") { + suggestions.push( + "Try breaking down complex requests into simpler steps" + ); + } + break; + + case "llm_error": + suggestions.push("Check API key configuration and quota limits"); + suggestions.push("Try reducing request complexity or length"); + suggestions.push("Verify network connectivity to AI services"); + break; + + case "validation_error": + suggestions.push( + "Review generated workflow structure for completeness" + ); + suggestions.push("Check node configurations and connections"); + break; + + case "cache_error": + suggestions.push("Cache error is non-critical, workflow will continue"); + break; + + default: + suggestions.push("Check system logs for additional error details"); + suggestions.push("Try the request again after a brief delay"); + } + + // Add stage-specific suggestions + if (Object.keys(partialResults).length > 0) { + suggestions.push( + "Partial results available - some processing completed successfully" + ); + } + + return suggestions; + } + private generateCacheKey(userInput: string): string { return userInput.toLowerCase().trim().replace(/\s+/g, "_"); } @@ -240,3 +471,16 @@ export class WorkflowAgent { return this.context; } } + +/** + * Custom error class that includes detailed execution context + */ +class DetailedError extends Error { + public readonly errorContext: ErrorContext; + + constructor(message: string, errorContext: ErrorContext) { + super(message); + this.name = "DetailedError"; + this.errorContext = errorContext; + } +} diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index 0358d7f..6290410 100644 --- a/src/store/workflowStore.ts +++ b/src/store/workflowStore.ts @@ -835,6 +835,22 @@ export const useWorkflowStore = create((set, get) => ({ saveWorkflowsToStorage(newWorkflows); } catch (error) { console.error("Error generating workflow from description:", error); + + // Enhanced error handling with detailed context + if (error instanceof Error && "errorContext" in error) { + const errorContext = (error as any).errorContext; + console.error("Detailed error context:", { + stage: errorContext.stage, + toolName: errorContext.toolName, + errorType: errorContext.errorType, + suggestions: errorContext.suggestions, + partialResults: Object.keys(errorContext.partialResults || {}), + }); + + // You could also dispatch an error event with detailed context here + // for better user experience and debugging + } + throw error; } }, diff --git a/src/types/tools.ts b/src/types/tools.ts index cc3a32b..1508ad6 100644 --- a/src/types/tools.ts +++ b/src/types/tools.ts @@ -59,6 +59,26 @@ export interface AgentResult { toolsUsed: string[]; executionTime: number; confidence: number; + errorContext?: ErrorContext; +} + +export interface ErrorContext { + stage: string; + toolName?: string; + inputParameters?: Record; + partialResults?: Record; + errorType: + | "tool_registry_miss" + | "tool_execution_failure" + | "llm_error" + | "validation_error" + | "cache_error" + | "unknown"; + timestamp: number; + sessionId: string; + userInput: string; + stackTrace?: string; + suggestions?: string[]; } export interface AgentContext { diff --git a/src/utils/errorReporting.ts b/src/utils/errorReporting.ts new file mode 100644 index 0000000..ed1e07b --- /dev/null +++ b/src/utils/errorReporting.ts @@ -0,0 +1,167 @@ +import { ErrorContext } from "../types/tools"; + +/** + * Utility functions for enhanced error handling and debugging + */ +export class ErrorReportingUtils { + /** + * Format error context for user-friendly display + */ + static formatErrorForUser(errorContext: ErrorContext): string { + const lines: string[] = []; + + lines.push(`āŒ Error occurred during ${errorContext.stage}`); + + if (errorContext.toolName) { + lines.push(`šŸ”§ Tool: ${errorContext.toolName}`); + } + + lines.push(`šŸ“… Time: ${new Date(errorContext.timestamp).toLocaleString()}`); + lines.push(`šŸ†” Session: ${errorContext.sessionId}`); + + if (errorContext.suggestions && errorContext.suggestions.length > 0) { + lines.push("\nšŸ’” Suggestions:"); + errorContext.suggestions.forEach((suggestion, index) => { + lines.push(` ${index + 1}. ${suggestion}`); + }); + } + + if ( + errorContext.partialResults && + Object.keys(errorContext.partialResults).length > 0 + ) { + lines.push("\nšŸ“Š Partial Results Available:"); + Object.keys(errorContext.partialResults).forEach((key) => { + lines.push(` • ${key}: ${typeof errorContext.partialResults![key]}`); + }); + } + + return lines.join("\n"); + } + + /** + * Format error context for developer debugging + */ + static formatErrorForDeveloper(errorContext: ErrorContext): string { + return JSON.stringify( + { + stage: errorContext.stage, + toolName: errorContext.toolName, + errorType: errorContext.errorType, + inputParameters: errorContext.inputParameters, + partialResults: errorContext.partialResults, + timestamp: errorContext.timestamp, + sessionId: errorContext.sessionId, + userInput: errorContext.userInput, + stackTrace: errorContext.stackTrace, + suggestions: errorContext.suggestions, + }, + null, + 2 + ); + } + + /** + * Check if error is recoverable based on context + */ + static isRecoverableError(errorContext: ErrorContext): boolean { + const recoverableTypes = ["cache_error", "validation_error"]; + return recoverableTypes.includes(errorContext.errorType); + } + + /** + * Get retry recommendation based on error type + */ + static getRetryRecommendation(errorContext: ErrorContext): { + shouldRetry: boolean; + delayMs: number; + reason: string; + } { + switch (errorContext.errorType) { + case "cache_error": + return { + shouldRetry: true, + delayMs: 0, + reason: "Cache error is non-critical", + }; + + case "llm_error": + return { + shouldRetry: true, + delayMs: 2000, + reason: "LLM service may be temporarily unavailable", + }; + + case "tool_execution_failure": + return { + shouldRetry: true, + delayMs: 1000, + reason: "Tool execution may succeed on retry", + }; + + case "tool_registry_miss": + return { + shouldRetry: false, + delayMs: 0, + reason: "Tool registration issue requires system fix", + }; + + case "validation_error": + return { + shouldRetry: false, + delayMs: 0, + reason: "Validation error requires user input correction", + }; + + default: + return { + shouldRetry: true, + delayMs: 5000, + reason: "Unknown error - retry with delay", + }; + } + } + + /** + * Generate error report for support team + */ + static generateSupportReport(errorContext: ErrorContext): { + summary: string; + technicalDetails: any; + userImpact: string; + recommendedActions: string[]; + } { + const summary = `Error in ${errorContext.stage} during ${errorContext.errorType}`; + + const technicalDetails = { + stage: errorContext.stage, + toolName: errorContext.toolName, + errorType: errorContext.errorType, + timestamp: errorContext.timestamp, + sessionId: errorContext.sessionId, + stackTrace: errorContext.stackTrace, + }; + + let userImpact = "Workflow generation failed"; + if ( + errorContext.partialResults && + Object.keys(errorContext.partialResults).length > 0 + ) { + userImpact = + "Partial workflow generated - some steps completed successfully"; + } + + const recommendedActions = [ + ...(errorContext.suggestions || []), + "Check system logs for additional context", + "Verify external service availability", + ]; + + return { + summary, + technicalDetails, + userImpact, + recommendedActions, + }; + } +}