simple-ai-provider
Version:
A simple and extensible AI provider package for easy integration of multiple AI services
293 lines (292 loc) • 11.2 kB
TypeScript
/**
* 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 { AIProviderConfig, CompletionParams, CompletionResponse, CompletionChunk, ProviderInfo } from '../types/index.js';
import { AIProviderError } from '../types/index.js';
/**
* 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 declare abstract class BaseAIProvider {
/** Validated configuration object for this provider instance */
protected readonly config: AIProviderConfig;
/** Internal flag tracking initialization state */
private initialized;
/**
* 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
* });
* ```
*/
constructor(config: AIProviderConfig);
/**
* 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
* ```
*/
initialize(): Promise<void>;
/**
* Checks if the provider has been initialized.
*
* @returns true if the provider is ready for use, false otherwise
*/
isInitialized(): boolean;
/**
* 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);
* ```
*/
complete<T>(params: CompletionParams<T>): Promise<CompletionResponse<T>>;
complete(params: CompletionParams): Promise<CompletionResponse<string>>;
/**
* 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);
* }
* }
* ```
*/
stream<T = any>(params: CompletionParams<T>): AsyncIterable<CompletionChunk>;
/**
* 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
*/
abstract getInfo(): ProviderInfo;
/**
* 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 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<string>>;
/**
* 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<T = any>(params: CompletionParams<T>): AsyncIterable<CompletionChunk>;
/**
* Regex patterns that valid model names should match.
* Empty array (the default) skips validation entirely.
*/
protected getModelNamePatterns(): RegExp[];
/**
* Send a minimal request to verify the API key and connectivity.
* The default does nothing; SDK-based providers override this and the
* base `validateConnection` handles error mapping.
*/
protected sendValidationProbe(): Promise<void>;
/**
* Map a provider-specific error to a normalized AIProviderError, or
* return null to let the base error mapping handle it generically.
* Override to recognize provider-specific message patterns (e.g. "API
* key", "quota", "model not found").
*/
protected mapProviderError(_error: any): AIProviderError | null;
/**
* Per-HTTP-status message overrides used by `normalizeError` when no
* provider-specific mapping applies. Lets providers add brand-flavored
* hints (console URLs, etc.) without re-implementing the status switch.
*/
protected providerErrorMessages(): Partial<Record<number, string>>;
/**
* Human-readable provider name used in warning messages.
* Default derives from getInfo(); override if construction order makes
* getInfo() unsafe to call here.
*/
protected get providerName(): string;
/**
* Validates a model name against the patterns returned by
* `getModelNamePatterns()`. Throws for invalid input, warns for
* non-matching names (since model lists change frequently).
*/
protected validateModelName(modelName: string): void;
/**
* Wraps `sendValidationProbe()` with consistent error handling: throws
* AIProviderError for provider-recognized failures, warns and continues
* for transient ones.
*/
protected validateConnection(): Promise<void>;
/**
* 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;
/**
* Validates an optional numeric parameter against a range.
* Skips validation when value is undefined so callers can apply defaults afterward.
*/
private validateNumberInRange;
private describeRange;
/**
* Ensures the provider has been initialized before use.
*
* @protected
* @throws {AIProviderError} If the provider is not initialized
*/
protected ensureInitialized(): void;
/**
* 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 any parameter is invalid
*/
protected validateCompletionParams<T = any>(params: CompletionParams<T>): void;
/**
* Validates the messages array parameter.
*
* @private
* @param messages - Messages array to validate
* @throws {AIProviderError} If messages are invalid
*/
private validateMessages;
private validateStopSequences;
/**
* Processes completion parameters to include response type instructions.
*
* This method automatically adds system prompt instructions when a response
* type is specified, ensuring the AI understands the expected output format.
*
* @protected
* @param params - Original completion parameters
* @returns Processed parameters with response type instructions
*/
protected processResponseType<T>(params: CompletionParams<T>): CompletionParams<T>;
/**
* 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;
}