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.
This commit is contained in:
Nikhil-Doye
2025-10-20 17:10:26 -04:00
parent 5f1bc5aa00
commit 4607c276a3
5 changed files with 489 additions and 34 deletions
+8
View File
@@ -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,
};
}
}
+278 -34
View File
@@ -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<AgentResult> {
const startTime = Date.now();
const toolsUsed: string[] = [];
const partialResults: Record<string, any> = {};
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<string, any>,
stage: string,
partialResults: Record<string, any>
): Promise<any> {
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<string, any>,
partialResults: Record<string, any>,
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<string, any>,
partialResults: Record<string, any>,
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, any>
): 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;
}
}
+16
View File
@@ -835,6 +835,22 @@ export const useWorkflowStore = create<WorkflowStore>((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;
}
},
+20
View File
@@ -59,6 +59,26 @@ export interface AgentResult {
toolsUsed: string[];
executionTime: number;
confidence: number;
errorContext?: ErrorContext;
}
export interface ErrorContext {
stage: string;
toolName?: string;
inputParameters?: Record<string, any>;
partialResults?: Record<string, any>;
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 {
+167
View File
@@ -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,
};
}
}