Current File : /home/mdkeenpw/www/wp-content/plugins/ai-engine/classes/exceptions/function-call-exception.php |
<?php
/**
* Specialized exception for function calling errors.
*
* Provides detailed context about what went wrong during
* function calling operations.
*/
class Meow_MWAI_FunctionCallException extends Exception {
private array $context = [];
/**
* Error codes for different scenarios
*/
public const ERROR_NO_TOOL_CALL_FOUND = 'no_tool_call_found';
public const ERROR_INVALID_RESPONSE_ID = 'invalid_response_id';
public const ERROR_FUNCTION_EXECUTION_FAILED = 'function_execution_failed';
public const ERROR_MISSING_FUNCTION_HANDLER = 'missing_function_handler';
public const ERROR_INVALID_ARGUMENTS = 'invalid_arguments';
public const ERROR_LOOP_DETECTED = 'loop_detected';
public function __construct(
string $message,
string $errorCode,
array $context = [],
?Throwable $previous = null
) {
$this->context = $context;
$detailedMessage = $this->build_detailed_message( $message, $errorCode, $context );
parent::__construct( $detailedMessage, 0, $previous );
}
/**
* Build a detailed error message with context
*/
private function build_detailed_message( string $message, string $errorCode, array $context ): string {
$details = [$message];
switch ( $errorCode ) {
case self::ERROR_NO_TOOL_CALL_FOUND:
$callId = $context['call_id'] ?? 'unknown';
$details[] = sprintf(
'Function call mismatch: Expected call_id "%s" not found in conversation.',
$callId
);
$details[] = 'Ensure previous_response_id is set and function_call is echoed before function_call_output.';
if ( !empty( $context['available_calls'] ) ) {
$details[] = 'Available call IDs: ' . implode( ', ', $context['available_calls'] );
}
break;
case self::ERROR_INVALID_RESPONSE_ID:
$responseId = $context['response_id'] ?? 'unknown';
$expectedFormat = $context['expected_format'] ?? 'unknown';
$details[] = sprintf(
'Invalid response ID format: "%s" does not match expected format "%s".',
$responseId,
$expectedFormat
);
if ( !empty( $context['api_type'] ) ) {
$details[] = sprintf(
'The %s requires response IDs starting with "%s".',
$context['api_type'],
$context['expected_prefix'] ?? ''
);
}
break;
case self::ERROR_FUNCTION_EXECUTION_FAILED:
$functionName = $context['function_name'] ?? 'unknown';
$details[] = sprintf( 'Function "%s" execution failed.', $functionName );
if ( !empty( $context['error_details'] ) ) {
$details[] = 'Error details: ' . $context['error_details'];
}
break;
case self::ERROR_MISSING_FUNCTION_HANDLER:
$functionName = $context['function_name'] ?? 'unknown';
$details[] = sprintf(
'No handler registered for function "%s".',
$functionName
);
$details[] = 'Ensure the function is registered using add_filter("mwai_functions_list", ...).';
break;
case self::ERROR_INVALID_ARGUMENTS:
$functionName = $context['function_name'] ?? 'unknown';
$details[] = sprintf(
'Invalid arguments provided for function "%s".',
$functionName
);
if ( !empty( $context['validation_errors'] ) ) {
$details[] = 'Validation errors: ' . implode( ', ', $context['validation_errors'] );
}
break;
case self::ERROR_LOOP_DETECTED:
$maxDepth = $context['max_depth'] ?? 5;
$details[] = sprintf(
'Function call loop detected after %d iterations.',
$maxDepth
);
$details[] = 'The AI model is repeatedly calling functions without reaching a conclusion.';
if ( !empty( $context['call_stack'] ) ) {
$details[] = 'Call stack: ' . implode( ' → ', $context['call_stack'] );
}
break;
}
// Add debug information if available
if ( !empty( $context['debug_info'] ) ) {
$details[] = 'Debug info: ' . json_encode( $context['debug_info'] );
}
return implode( ' ', $details );
}
/**
* Get the error context
*/
public function get_context(): array {
return $this->context;
}
/**
* Create specific error instances
*/
public static function no_tool_call_found( string $callId, array $availableCalls = [] ): self {
return new self(
'No tool call found for function call output.',
self::ERROR_NO_TOOL_CALL_FOUND,
[
'call_id' => $callId,
'available_calls' => $availableCalls
]
);
}
public static function invalid_response_id( string $responseId, string $apiType, string $expectedPrefix ): self {
return new self(
'Invalid response ID format.',
self::ERROR_INVALID_RESPONSE_ID,
[
'response_id' => $responseId,
'api_type' => $apiType,
'expected_format' => $expectedPrefix . '...',
'expected_prefix' => $expectedPrefix
]
);
}
public static function function_execution_failed( string $functionName, string $errorDetails ): self {
return new self(
'Function execution failed.',
self::ERROR_FUNCTION_EXECUTION_FAILED,
[
'function_name' => $functionName,
'error_details' => $errorDetails
]
);
}
public static function missing_function_handler( string $functionName ): self {
return new self(
'Missing function handler.',
self::ERROR_MISSING_FUNCTION_HANDLER,
[
'function_name' => $functionName
]
);
}
public static function loop_detected( int $maxDepth, array $callStack = [] ): self {
return new self(
'Function call loop detected.',
self::ERROR_LOOP_DETECTED,
[
'max_depth' => $maxDepth,
'call_stack' => $callStack
]
);
}
}