refactor: update provider documentation and improve comments
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
/**
|
||||
* Abstract base class for all AI providers
|
||||
* Provides common functionality and enforces a consistent interface
|
||||
* Abstract Base Provider for AI Services
|
||||
*
|
||||
* This module provides the foundation for all AI provider implementations,
|
||||
* ensuring consistency, proper error handling, and maintainable code structure.
|
||||
*
|
||||
* Design Principles:
|
||||
* - Template Method Pattern: Define algorithm structure, let subclasses implement specifics
|
||||
* - Fail-Fast: Validate inputs early and provide clear error messages
|
||||
* - Separation of Concerns: Configuration, validation, execution are clearly separated
|
||||
* - Type Safety: Comprehensive TypeScript support for better DX
|
||||
*
|
||||
* @author Jan-Marlon Leibl
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
import type {
|
||||
@@ -12,120 +23,333 @@ import type {
|
||||
} from '../types/index.js';
|
||||
import { AIProviderError, AIErrorType } from '../types/index.js';
|
||||
|
||||
// ============================================================================
|
||||
// ABSTRACT BASE PROVIDER CLASS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Abstract base class that all AI providers must extend
|
||||
* Abstract base class that all AI providers must extend.
|
||||
*
|
||||
* This class implements the Template Method pattern, providing a consistent
|
||||
* interface and workflow while allowing providers to implement their specific
|
||||
* logic in protected abstract methods.
|
||||
*
|
||||
* Lifecycle:
|
||||
* 1. Construction: validateConfig() called automatically
|
||||
* 2. Initialization: initialize() must be called before use
|
||||
* 3. Usage: complete() and stream() methods available
|
||||
* 4. Error Handling: All errors are normalized to AIProviderError
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class MyProvider extends BaseAIProvider {
|
||||
* protected async doInitialize(): Promise<void> {
|
||||
* // Initialize your client/connection here
|
||||
* }
|
||||
*
|
||||
* protected async doComplete(params: CompletionParams): Promise<CompletionResponse> {
|
||||
* // Implement completion logic here
|
||||
* }
|
||||
*
|
||||
* // ... other required methods
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export abstract class BaseAIProvider {
|
||||
protected config: AIProviderConfig;
|
||||
protected initialized: boolean = false;
|
||||
// ========================================================================
|
||||
// INSTANCE PROPERTIES
|
||||
// ========================================================================
|
||||
|
||||
constructor(config: AIProviderConfig) {
|
||||
this.config = this.validateConfig(config);
|
||||
}
|
||||
/** Validated configuration object for this provider instance */
|
||||
protected readonly config: AIProviderConfig;
|
||||
|
||||
/** Internal flag tracking initialization state */
|
||||
private initialized: boolean = false;
|
||||
|
||||
// ========================================================================
|
||||
// CONSTRUCTOR & CONFIGURATION
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Validates the provider configuration
|
||||
* @param config - Configuration object to validate
|
||||
* @returns Validated configuration
|
||||
* @throws AIProviderError if configuration is invalid
|
||||
* Constructs a new provider instance with validated configuration.
|
||||
*
|
||||
* @param config - Provider configuration object
|
||||
* @throws {AIProviderError} If configuration is invalid
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const provider = new MyProvider({
|
||||
* apiKey: 'your-api-key',
|
||||
* timeout: 30000,
|
||||
* maxRetries: 3
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
protected validateConfig(config: AIProviderConfig): AIProviderConfig {
|
||||
if (!config.apiKey || typeof config.apiKey !== 'string') {
|
||||
throw new AIProviderError(
|
||||
'API key is required and must be a string',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
// Set default values
|
||||
const validatedConfig = {
|
||||
...config,
|
||||
timeout: config.timeout ?? 30000,
|
||||
maxRetries: config.maxRetries ?? 3
|
||||
};
|
||||
|
||||
return validatedConfig;
|
||||
constructor(config: AIProviderConfig) {
|
||||
this.config = this.validateAndNormalizeConfig(config);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// PUBLIC API METHODS
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Initialize the provider (setup connections, validate credentials, etc.)
|
||||
* Must be called before using the provider
|
||||
* Initializes the provider for use.
|
||||
*
|
||||
* This method must be called before any completion requests. It handles
|
||||
* provider-specific setup such as client initialization and connection testing.
|
||||
*
|
||||
* @returns Promise that resolves when initialization is complete
|
||||
* @throws {AIProviderError} If initialization fails
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* await provider.initialize();
|
||||
* // Provider is now ready for use
|
||||
* ```
|
||||
*/
|
||||
public async initialize(): Promise<void> {
|
||||
try {
|
||||
await this.doInitialize();
|
||||
this.initialized = true;
|
||||
} catch (error) {
|
||||
throw this.handleError(error as Error);
|
||||
// Normalize any error to our standard format
|
||||
throw this.normalizeError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the provider is initialized
|
||||
* Checks if the provider has been initialized.
|
||||
*
|
||||
* @returns true if the provider is ready for use, false otherwise
|
||||
*/
|
||||
public isInitialized(): boolean {
|
||||
return this.initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a completion based on the provided parameters
|
||||
* @param params - Parameters for the completion request
|
||||
* @returns Promise resolving to completion response
|
||||
* Generates a text completion based on the provided parameters.
|
||||
*
|
||||
* This method handles validation, error normalization, and delegates to
|
||||
* the provider-specific implementation via the Template Method pattern.
|
||||
*
|
||||
* @param params - Completion parameters including messages, model options, etc.
|
||||
* @returns Promise resolving to the completion response
|
||||
* @throws {AIProviderError} If the request fails or parameters are invalid
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const response = await provider.complete({
|
||||
* messages: [{ role: 'user', content: 'Hello!' }],
|
||||
* maxTokens: 100,
|
||||
* temperature: 0.7
|
||||
* });
|
||||
* console.log(response.content);
|
||||
* ```
|
||||
*/
|
||||
public async complete(params: CompletionParams): Promise<CompletionResponse> {
|
||||
// Ensure provider is ready for use
|
||||
this.ensureInitialized();
|
||||
|
||||
// Validate input parameters early
|
||||
this.validateCompletionParams(params);
|
||||
|
||||
try {
|
||||
// Delegate to provider-specific implementation
|
||||
return await this.doComplete(params);
|
||||
} catch (error) {
|
||||
throw this.handleError(error as Error);
|
||||
// Normalize error to our standard format
|
||||
throw this.normalizeError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a streaming completion
|
||||
* @param params - Parameters for the completion request
|
||||
* @returns AsyncIterable of completion chunks
|
||||
* Generates a streaming text completion.
|
||||
*
|
||||
* This method returns an async iterable that yields chunks of the response
|
||||
* as they become available, enabling real-time UI updates.
|
||||
*
|
||||
* @param params - Completion parameters
|
||||
* @returns AsyncIterable yielding completion chunks
|
||||
* @throws {AIProviderError} If the request fails or parameters are invalid
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* for await (const chunk of provider.stream(params)) {
|
||||
* if (!chunk.isComplete) {
|
||||
* process.stdout.write(chunk.content);
|
||||
* } else {
|
||||
* console.log('\nDone! Usage:', chunk.usage);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public async *stream(params: CompletionParams): AsyncIterable<CompletionChunk> {
|
||||
// Ensure provider is ready for use
|
||||
this.ensureInitialized();
|
||||
|
||||
// Validate input parameters early
|
||||
this.validateCompletionParams(params);
|
||||
|
||||
try {
|
||||
// Delegate to provider-specific implementation
|
||||
yield* this.doStream(params);
|
||||
} catch (error) {
|
||||
throw this.handleError(error as Error);
|
||||
// Normalize error to our standard format
|
||||
throw this.normalizeError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about this provider
|
||||
* @returns Provider information and capabilities
|
||||
* Returns information about this provider and its capabilities.
|
||||
*
|
||||
* This method provides metadata about the provider including supported
|
||||
* models, context length, streaming support, and special capabilities.
|
||||
*
|
||||
* @returns Provider information object
|
||||
*/
|
||||
public abstract getInfo(): ProviderInfo;
|
||||
|
||||
// ========================================================================
|
||||
// ABSTRACT METHODS (TEMPLATE METHOD PATTERN)
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Provider-specific initialization logic
|
||||
* Override this method in concrete implementations
|
||||
* Provider-specific initialization logic.
|
||||
*
|
||||
* Subclasses should implement this method to handle their specific
|
||||
* initialization requirements such as:
|
||||
* - Creating API clients
|
||||
* - Testing connections
|
||||
* - Validating credentials
|
||||
* - Setting up authentication
|
||||
*
|
||||
* @protected
|
||||
* @returns Promise that resolves when initialization is complete
|
||||
* @throws {Error} If initialization fails (will be normalized to AIProviderError)
|
||||
*/
|
||||
protected abstract doInitialize(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Provider-specific completion logic
|
||||
* Override this method in concrete implementations
|
||||
* Provider-specific completion implementation.
|
||||
*
|
||||
* Subclasses should implement this method to handle text completion
|
||||
* using their specific API. The base class handles validation and
|
||||
* error normalization.
|
||||
*
|
||||
* @protected
|
||||
* @param params - Validated completion parameters
|
||||
* @returns Promise resolving to completion response
|
||||
* @throws {Error} If completion fails (will be normalized to AIProviderError)
|
||||
*/
|
||||
protected abstract doComplete(params: CompletionParams): Promise<CompletionResponse>;
|
||||
|
||||
/**
|
||||
* Provider-specific streaming logic
|
||||
* Override this method in concrete implementations
|
||||
* Provider-specific streaming implementation.
|
||||
*
|
||||
* Subclasses should implement this method to handle streaming completions
|
||||
* using their specific API. The base class handles validation and
|
||||
* error normalization.
|
||||
*
|
||||
* @protected
|
||||
* @param params - Validated completion parameters
|
||||
* @returns AsyncIterable yielding completion chunks
|
||||
* @throws {Error} If streaming fails (will be normalized to AIProviderError)
|
||||
*/
|
||||
protected abstract doStream(params: CompletionParams): AsyncIterable<CompletionChunk>;
|
||||
|
||||
// ========================================================================
|
||||
// PROTECTED UTILITY METHODS
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Ensures the provider is initialized before use
|
||||
* @throws AIProviderError if not initialized
|
||||
* Validates and normalizes provider configuration.
|
||||
*
|
||||
* This method ensures all required fields are present and sets sensible
|
||||
* defaults for optional fields. It follows the fail-fast principle.
|
||||
*
|
||||
* @protected
|
||||
* @param config - Raw configuration object
|
||||
* @returns Validated and normalized configuration
|
||||
* @throws {AIProviderError} If configuration is invalid
|
||||
*/
|
||||
protected validateAndNormalizeConfig(config: AIProviderConfig): AIProviderConfig {
|
||||
// Validate required fields
|
||||
if (!config) {
|
||||
throw new AIProviderError(
|
||||
'Configuration object is required',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
if (!config.apiKey || typeof config.apiKey !== 'string' || config.apiKey.trim() === '') {
|
||||
throw new AIProviderError(
|
||||
'API key is required and must be a non-empty string',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
// Apply defaults and return normalized config
|
||||
return {
|
||||
...config,
|
||||
timeout: this.validateTimeout(config.timeout),
|
||||
maxRetries: this.validateMaxRetries(config.maxRetries)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates timeout configuration value.
|
||||
*
|
||||
* @private
|
||||
* @param timeout - Timeout value to validate
|
||||
* @returns Validated timeout value with default if needed
|
||||
*/
|
||||
private validateTimeout(timeout?: number): number {
|
||||
const defaultTimeout = 30000; // 30 seconds
|
||||
|
||||
if (timeout === undefined) {
|
||||
return defaultTimeout;
|
||||
}
|
||||
|
||||
if (typeof timeout !== 'number' || timeout < 1000 || timeout > 300000) {
|
||||
throw new AIProviderError(
|
||||
'Timeout must be a number between 1000ms (1s) and 300000ms (5min)',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates max retries configuration value.
|
||||
*
|
||||
* @private
|
||||
* @param maxRetries - Max retries value to validate
|
||||
* @returns Validated max retries value with default if needed
|
||||
*/
|
||||
private validateMaxRetries(maxRetries?: number): number {
|
||||
const defaultMaxRetries = 3;
|
||||
|
||||
if (maxRetries === undefined) {
|
||||
return defaultMaxRetries;
|
||||
}
|
||||
|
||||
if (typeof maxRetries !== 'number' || maxRetries < 0 || maxRetries > 10) {
|
||||
throw new AIProviderError(
|
||||
'Max retries must be a number between 0 and 10',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
return maxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the provider has been initialized before use.
|
||||
*
|
||||
* @protected
|
||||
* @throws {AIProviderError} If the provider is not initialized
|
||||
*/
|
||||
protected ensureInitialized(): void {
|
||||
if (!this.initialized) {
|
||||
@@ -137,127 +361,250 @@ export abstract class BaseAIProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates completion parameters
|
||||
* Validates completion parameters comprehensively.
|
||||
*
|
||||
* This method performs thorough validation of all completion parameters
|
||||
* to ensure they meet the requirements and constraints.
|
||||
*
|
||||
* @protected
|
||||
* @param params - Parameters to validate
|
||||
* @throws AIProviderError if parameters are invalid
|
||||
* @throws {AIProviderError} If any parameter is invalid
|
||||
*/
|
||||
protected validateCompletionParams(params: CompletionParams): void {
|
||||
if (!params.messages || !Array.isArray(params.messages) || params.messages.length === 0) {
|
||||
if (!params || typeof params !== 'object') {
|
||||
throw new AIProviderError(
|
||||
'Messages array is required and must not be empty',
|
||||
'Completion parameters object is required',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
for (const message of params.messages) {
|
||||
if (!message.role || !['system', 'user', 'assistant'].includes(message.role)) {
|
||||
// Validate messages array
|
||||
this.validateMessages(params.messages);
|
||||
|
||||
// Validate optional parameters
|
||||
this.validateTemperature(params.temperature);
|
||||
this.validateTopP(params.topP);
|
||||
this.validateMaxTokens(params.maxTokens);
|
||||
this.validateStopSequences(params.stopSequences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the messages array parameter.
|
||||
*
|
||||
* @private
|
||||
* @param messages - Messages array to validate
|
||||
* @throws {AIProviderError} If messages are invalid
|
||||
*/
|
||||
private validateMessages(messages: any): void {
|
||||
if (!Array.isArray(messages) || messages.length === 0) {
|
||||
throw new AIProviderError(
|
||||
'Messages must be a non-empty array',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
const validRoles = ['system', 'user', 'assistant'] as const;
|
||||
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages[i];
|
||||
|
||||
if (!message || typeof message !== 'object') {
|
||||
throw new AIProviderError(
|
||||
'Each message must have a valid role (system, user, or assistant)',
|
||||
`Message at index ${i} must be an object`,
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
if (!message.content || typeof message.content !== 'string') {
|
||||
if (!validRoles.includes(message.role)) {
|
||||
throw new AIProviderError(
|
||||
'Each message must have non-empty string content',
|
||||
`Message at index ${i} has invalid role '${message.role}'. Must be: ${validRoles.join(', ')}`,
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.temperature !== undefined && (params.temperature < 0 || params.temperature > 1)) {
|
||||
throw new AIProviderError(
|
||||
'Temperature must be between 0.0 and 1.0',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
if (params.topP !== undefined && (params.topP < 0 || params.topP > 1)) {
|
||||
throw new AIProviderError(
|
||||
'Top-p must be between 0.0 and 1.0',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
if (params.maxTokens !== undefined && params.maxTokens < 1) {
|
||||
throw new AIProviderError(
|
||||
'Max tokens must be a positive integer',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
if (typeof message.content !== 'string' || message.content.trim() === '') {
|
||||
throw new AIProviderError(
|
||||
`Message at index ${i} must have non-empty string content`,
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles and transforms errors into AIProviderError instances
|
||||
* @param error - The original error
|
||||
* @returns AIProviderError with appropriate type and context
|
||||
* Validates temperature parameter.
|
||||
*
|
||||
* @private
|
||||
* @param temperature - Temperature value to validate
|
||||
* @throws {AIProviderError} If temperature is invalid
|
||||
*/
|
||||
protected handleError(error: Error): AIProviderError {
|
||||
private validateTemperature(temperature?: number): void {
|
||||
if (temperature !== undefined) {
|
||||
if (typeof temperature !== 'number' || temperature < 0 || temperature > 1) {
|
||||
throw new AIProviderError(
|
||||
'Temperature must be a number between 0.0 and 1.0',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates top-p parameter.
|
||||
*
|
||||
* @private
|
||||
* @param topP - Top-p value to validate
|
||||
* @throws {AIProviderError} If top-p is invalid
|
||||
*/
|
||||
private validateTopP(topP?: number): void {
|
||||
if (topP !== undefined) {
|
||||
if (typeof topP !== 'number' || topP <= 0 || topP > 1) {
|
||||
throw new AIProviderError(
|
||||
'Top-p must be a number between 0.0 (exclusive) and 1.0 (inclusive)',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates max tokens parameter.
|
||||
*
|
||||
* @private
|
||||
* @param maxTokens - Max tokens value to validate
|
||||
* @throws {AIProviderError} If max tokens is invalid
|
||||
*/
|
||||
private validateMaxTokens(maxTokens?: number): void {
|
||||
if (maxTokens !== undefined) {
|
||||
if (typeof maxTokens !== 'number' || !Number.isInteger(maxTokens) || maxTokens < 1) {
|
||||
throw new AIProviderError(
|
||||
'Max tokens must be a positive integer',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates stop sequences parameter.
|
||||
*
|
||||
* @private
|
||||
* @param stopSequences - Stop sequences to validate
|
||||
* @throws {AIProviderError} If stop sequences are invalid
|
||||
*/
|
||||
private validateStopSequences(stopSequences?: string[]): void {
|
||||
if (stopSequences !== undefined) {
|
||||
if (!Array.isArray(stopSequences)) {
|
||||
throw new AIProviderError(
|
||||
'Stop sequences must be an array of strings',
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < stopSequences.length; i++) {
|
||||
if (typeof stopSequences[i] !== 'string') {
|
||||
throw new AIProviderError(
|
||||
`Stop sequence at index ${i} must be a string`,
|
||||
AIErrorType.INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes any error into a standardized AIProviderError.
|
||||
*
|
||||
* This method provides consistent error handling across all providers,
|
||||
* mapping various error types and status codes to appropriate categories.
|
||||
*
|
||||
* @protected
|
||||
* @param error - The original error to normalize
|
||||
* @returns Normalized AIProviderError with appropriate type and context
|
||||
*/
|
||||
protected normalizeError(error: Error): AIProviderError {
|
||||
// If already normalized, return as-is
|
||||
if (error instanceof AIProviderError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
// Handle common HTTP status codes
|
||||
if ('status' in error) {
|
||||
const status = (error as any).status;
|
||||
// Extract status code if available
|
||||
const status = (error as any).status || (error as any).statusCode;
|
||||
const message = error.message || 'Unknown error occurred';
|
||||
|
||||
// Map HTTP status codes to error types
|
||||
if (status) {
|
||||
switch (status) {
|
||||
case 400:
|
||||
return new AIProviderError(
|
||||
`Bad request: ${message}`,
|
||||
AIErrorType.INVALID_REQUEST,
|
||||
status,
|
||||
error
|
||||
);
|
||||
|
||||
case 401:
|
||||
case 403:
|
||||
return new AIProviderError(
|
||||
'Authentication failed. Please check your API key.',
|
||||
'Authentication failed. Please verify your API key is correct and has the necessary permissions.',
|
||||
AIErrorType.AUTHENTICATION,
|
||||
status,
|
||||
error
|
||||
);
|
||||
case 429:
|
||||
return new AIProviderError(
|
||||
'Rate limit exceeded. Please try again later.',
|
||||
AIErrorType.RATE_LIMIT,
|
||||
status,
|
||||
error
|
||||
);
|
||||
|
||||
case 404:
|
||||
return new AIProviderError(
|
||||
'Model or endpoint not found.',
|
||||
'The specified model or endpoint was not found. Please check the model name and availability.',
|
||||
AIErrorType.MODEL_NOT_FOUND,
|
||||
status,
|
||||
error
|
||||
);
|
||||
case 400:
|
||||
|
||||
case 429:
|
||||
return new AIProviderError(
|
||||
'Invalid request parameters.',
|
||||
AIErrorType.INVALID_REQUEST,
|
||||
'Rate limit exceeded. Please reduce your request frequency and try again later.',
|
||||
AIErrorType.RATE_LIMIT,
|
||||
status,
|
||||
error
|
||||
);
|
||||
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
return new AIProviderError(
|
||||
'Service temporarily unavailable. Please try again in a few moments.',
|
||||
AIErrorType.NETWORK,
|
||||
status,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle timeout errors
|
||||
if (error.message.includes('timeout') || error.message.includes('ETIMEDOUT')) {
|
||||
// Map common error patterns
|
||||
if (message.includes('timeout') || message.includes('ETIMEDOUT')) {
|
||||
return new AIProviderError(
|
||||
'Request timed out. Please try again.',
|
||||
'Request timed out. The operation took longer than expected.',
|
||||
AIErrorType.TIMEOUT,
|
||||
undefined,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
// Handle network errors
|
||||
if (error.message.includes('network') || error.message.includes('ENOTFOUND')) {
|
||||
if (message.includes('network') || message.includes('ENOTFOUND') || message.includes('ECONNREFUSED')) {
|
||||
return new AIProviderError(
|
||||
'Network error occurred. Please check your connection.',
|
||||
'Network error occurred. Please check your internet connection and try again.',
|
||||
AIErrorType.NETWORK,
|
||||
undefined,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
// Default to unknown error
|
||||
// Default to unknown error type
|
||||
return new AIProviderError(
|
||||
`Unknown error: ${error.message}`,
|
||||
`Provider error: ${message}`,
|
||||
AIErrorType.UNKNOWN,
|
||||
undefined,
|
||||
status,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user