refactor: extract constants, parameterize validators, simplify factory
Quick-win refactors from the Refactoring Guru catalog: - Replace Magic Number with Symbolic Constant: extract DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, default models per provider, and validation prompt into src/constants.ts. - Parameterize Method: collapse validateTemperature / validateTopP / validateMaxTokens (and the inline timeout/maxRetries checks) into a single validateNumberInRange helper with bounds metadata. - Inline Class / Remove Middle Man: drop the unused ProviderRegistry class from utils/factory.ts. createProvider now dispatches through the PROVIDER_REGISTRY const directly instead of a parallel switch. Also fixes stale VERSION constant in src/index.ts (was 1.3.1).
This commit is contained in:
35
src/constants.ts
Normal file
35
src/constants.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Shared defaults used across all providers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const DEFAULT_TIMEOUT_MS = 30_000;
|
||||||
|
export const DEFAULT_MAX_RETRIES = 3;
|
||||||
|
export const DEFAULT_MAX_TOKENS = 1000;
|
||||||
|
export const DEFAULT_TEMPERATURE = 0.7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal prompt used to validate API credentials on initialization.
|
||||||
|
*/
|
||||||
|
export const VALIDATION_PROMPT = 'Hi';
|
||||||
|
|
||||||
|
export const DEFAULT_MODELS = {
|
||||||
|
claude: 'claude-3-5-sonnet-20241022',
|
||||||
|
openai: 'gpt-4o',
|
||||||
|
gemini: 'gemini-2.5-flash',
|
||||||
|
openwebui: 'llama3.1:latest'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const DEFAULT_OPENWEBUI_BASE_URL = 'http://localhost:3000';
|
||||||
|
|
||||||
|
export const DEFAULT_ANTHROPIC_VERSION = '2023-06-01';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bounds for configuration value validation.
|
||||||
|
*/
|
||||||
|
export const CONFIG_BOUNDS = {
|
||||||
|
timeoutMs: { min: 1000, max: 300_000 },
|
||||||
|
maxRetries: { min: 0, max: 10 },
|
||||||
|
temperature: { min: 0, max: 1 },
|
||||||
|
topP: { min: 0, max: 1, exclusiveMin: true },
|
||||||
|
maxTokens: { min: 1 }
|
||||||
|
} as const;
|
||||||
@@ -58,4 +58,4 @@ export const SUPPORTED_PROVIDERS = ['claude', 'openai', 'gemini', 'openwebui'] a
|
|||||||
/**
|
/**
|
||||||
* Package version
|
* Package version
|
||||||
*/
|
*/
|
||||||
export const VERSION = '1.3.1';
|
export const VERSION = '2.0.0';
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ import type {
|
|||||||
ResponseType
|
ResponseType
|
||||||
} from '../types/index.js';
|
} from '../types/index.js';
|
||||||
import { AIProviderError, AIErrorType, generateResponseTypePrompt, parseAndValidateResponseType } from '../types/index.js';
|
import { AIProviderError, AIErrorType, generateResponseTypePrompt, parseAndValidateResponseType } from '../types/index.js';
|
||||||
|
import {
|
||||||
|
CONFIG_BOUNDS,
|
||||||
|
DEFAULT_MAX_RETRIES,
|
||||||
|
DEFAULT_TIMEOUT_MS
|
||||||
|
} from '../constants.js';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// ABSTRACT BASE PROVIDER CLASS
|
// ABSTRACT BASE PROVIDER CLASS
|
||||||
@@ -314,60 +319,78 @@ export abstract class BaseAIProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply defaults and return normalized config
|
this.validateNumberInRange('timeout', config.timeout, {
|
||||||
|
...CONFIG_BOUNDS.timeoutMs,
|
||||||
|
label: `${CONFIG_BOUNDS.timeoutMs.min}ms and ${CONFIG_BOUNDS.timeoutMs.max}ms`
|
||||||
|
});
|
||||||
|
this.validateNumberInRange('maxRetries', config.maxRetries, {
|
||||||
|
...CONFIG_BOUNDS.maxRetries,
|
||||||
|
integer: true
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
timeout: this.validateTimeout(config.timeout),
|
timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
|
||||||
maxRetries: this.validateMaxRetries(config.maxRetries)
|
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates timeout configuration value.
|
* Validates an optional numeric parameter against a range.
|
||||||
*
|
* Skips validation when value is undefined so callers can apply defaults afterward.
|
||||||
* @private
|
|
||||||
* @param timeout - Timeout value to validate
|
|
||||||
* @returns Validated timeout value with default if needed
|
|
||||||
*/
|
*/
|
||||||
private validateTimeout(timeout?: number): number {
|
private validateNumberInRange(
|
||||||
const defaultTimeout = 30000; // 30 seconds
|
name: string,
|
||||||
|
value: number | undefined,
|
||||||
if (timeout === undefined) {
|
bounds: {
|
||||||
return defaultTimeout;
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
exclusiveMin?: boolean;
|
||||||
|
integer?: boolean;
|
||||||
|
label?: string;
|
||||||
}
|
}
|
||||||
|
): void {
|
||||||
|
if (value === undefined) return;
|
||||||
|
|
||||||
if (typeof timeout !== 'number' || timeout < 1000 || timeout > 300000) {
|
const { min, max, exclusiveMin, integer, label } = bounds;
|
||||||
|
const range = label ?? this.describeRange(min, max, exclusiveMin);
|
||||||
|
|
||||||
|
if (typeof value !== 'number' || Number.isNaN(value)) {
|
||||||
throw new AIProviderError(
|
throw new AIProviderError(
|
||||||
'Timeout must be a number between 1000ms (1s) and 300000ms (5min)',
|
`${name} must be a number between ${range}`,
|
||||||
AIErrorType.INVALID_REQUEST
|
AIErrorType.INVALID_REQUEST
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeout;
|
if (integer && !Number.isInteger(value)) {
|
||||||
|
throw new AIProviderError(
|
||||||
|
`${name} must be an integer`,
|
||||||
|
AIErrorType.INVALID_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min !== undefined && (exclusiveMin ? value <= min : value < min)) {
|
||||||
|
throw new AIProviderError(
|
||||||
|
`${name} must be ${exclusiveMin ? 'greater than' : 'at least'} ${min}`,
|
||||||
|
AIErrorType.INVALID_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max !== undefined && value > max) {
|
||||||
|
throw new AIProviderError(
|
||||||
|
`${name} must be at most ${max}`,
|
||||||
|
AIErrorType.INVALID_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private describeRange(min?: number, max?: number, exclusiveMin?: boolean): string {
|
||||||
* Validates max retries configuration value.
|
if (min !== undefined && max !== undefined) {
|
||||||
*
|
return `${exclusiveMin ? '>' : '>='}${min} and <=${max}`;
|
||||||
* @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 (min !== undefined) return `${exclusiveMin ? '>' : '>='}${min}`;
|
||||||
if (typeof maxRetries !== 'number' || maxRetries < 0 || maxRetries > 10) {
|
if (max !== undefined) return `<=${max}`;
|
||||||
throw new AIProviderError(
|
return 'a valid number';
|
||||||
'Max retries must be a number between 0 and 10',
|
|
||||||
AIErrorType.INVALID_REQUEST
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxRetries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -403,13 +426,11 @@ export abstract class BaseAIProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate messages array
|
|
||||||
this.validateMessages(params.messages);
|
this.validateMessages(params.messages);
|
||||||
|
|
||||||
// Validate optional parameters
|
this.validateNumberInRange('temperature', params.temperature, CONFIG_BOUNDS.temperature);
|
||||||
this.validateTemperature(params.temperature);
|
this.validateNumberInRange('topP', params.topP, CONFIG_BOUNDS.topP);
|
||||||
this.validateTopP(params.topP);
|
this.validateNumberInRange('maxTokens', params.maxTokens, { ...CONFIG_BOUNDS.maxTokens, integer: true });
|
||||||
this.validateMaxTokens(params.maxTokens);
|
|
||||||
this.validateStopSequences(params.stopSequences);
|
this.validateStopSequences(params.stopSequences);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,67 +477,6 @@ export abstract class BaseAIProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates temperature parameter.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @param temperature - Temperature value to validate
|
|
||||||
* @throws {AIProviderError} If temperature is invalid
|
|
||||||
*/
|
|
||||||
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 {
|
private validateStopSequences(stopSequences?: string[]): void {
|
||||||
if (stopSequences !== undefined) {
|
if (stopSequences !== undefined) {
|
||||||
if (!Array.isArray(stopSequences)) {
|
if (!Array.isArray(stopSequences)) {
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ import type {
|
|||||||
} from '../types/index.js';
|
} from '../types/index.js';
|
||||||
import { BaseAIProvider } from './base.js';
|
import { BaseAIProvider } from './base.js';
|
||||||
import { AIProviderError, AIErrorType } from '../types/index.js';
|
import { AIProviderError, AIErrorType } from '../types/index.js';
|
||||||
|
import {
|
||||||
|
DEFAULT_ANTHROPIC_VERSION,
|
||||||
|
DEFAULT_MAX_TOKENS,
|
||||||
|
DEFAULT_MODELS,
|
||||||
|
DEFAULT_TEMPERATURE,
|
||||||
|
VALIDATION_PROMPT
|
||||||
|
} from '../constants.js';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TYPES AND INTERFACES
|
// TYPES AND INTERFACES
|
||||||
@@ -153,9 +160,8 @@ export class ClaudeProvider extends BaseAIProvider {
|
|||||||
constructor(config: ClaudeConfig) {
|
constructor(config: ClaudeConfig) {
|
||||||
super(config);
|
super(config);
|
||||||
|
|
||||||
// Set Claude-specific defaults
|
this.defaultModel = config.defaultModel || DEFAULT_MODELS.claude;
|
||||||
this.defaultModel = config.defaultModel || 'claude-3-5-sonnet-20241022';
|
this.version = config.version || DEFAULT_ANTHROPIC_VERSION;
|
||||||
this.version = config.version || '2023-06-01';
|
|
||||||
|
|
||||||
// Validate model name format
|
// Validate model name format
|
||||||
this.validateModelName(this.defaultModel);
|
this.validateModelName(this.defaultModel);
|
||||||
@@ -329,11 +335,10 @@ export class ClaudeProvider extends BaseAIProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Make minimal request to test connection and permissions
|
|
||||||
await this.client.messages.create({
|
await this.client.messages.create({
|
||||||
model: this.defaultModel,
|
model: this.defaultModel,
|
||||||
max_tokens: 1,
|
max_tokens: 1,
|
||||||
messages: [{ role: 'user', content: 'Hi' }]
|
messages: [{ role: 'user', content: VALIDATION_PROMPT }]
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -449,8 +454,8 @@ export class ClaudeProvider extends BaseAIProvider {
|
|||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
model: params.model || this.defaultModel,
|
model: params.model || this.defaultModel,
|
||||||
max_tokens: params.maxTokens || 1000,
|
max_tokens: params.maxTokens || DEFAULT_MAX_TOKENS,
|
||||||
temperature: params.temperature ?? 0.7,
|
temperature: params.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
top_p: params.topP,
|
top_p: params.topP,
|
||||||
stop_sequences: params.stopSequences,
|
stop_sequences: params.stopSequences,
|
||||||
system: system || undefined,
|
system: system || undefined,
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ import type {
|
|||||||
} from '../types/index.js';
|
} from '../types/index.js';
|
||||||
import { BaseAIProvider } from './base.js';
|
import { BaseAIProvider } from './base.js';
|
||||||
import { AIProviderError, AIErrorType } from '../types/index.js';
|
import { AIProviderError, AIErrorType } from '../types/index.js';
|
||||||
|
import {
|
||||||
|
DEFAULT_MAX_TOKENS,
|
||||||
|
DEFAULT_MODELS,
|
||||||
|
DEFAULT_TEMPERATURE,
|
||||||
|
VALIDATION_PROMPT
|
||||||
|
} from '../constants.js';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TYPES AND INTERFACES
|
// TYPES AND INTERFACES
|
||||||
@@ -95,7 +101,7 @@ export class GeminiProvider extends BaseAIProvider {
|
|||||||
constructor(config: GeminiConfig) {
|
constructor(config: GeminiConfig) {
|
||||||
super(config);
|
super(config);
|
||||||
|
|
||||||
this.defaultModel = config.defaultModel || 'gemini-2.5-flash';
|
this.defaultModel = config.defaultModel || DEFAULT_MODELS.gemini;
|
||||||
this.safetySettings = config.safetySettings;
|
this.safetySettings = config.safetySettings;
|
||||||
this.defaultGenerationConfig = config.generationConfig;
|
this.defaultGenerationConfig = config.generationConfig;
|
||||||
|
|
||||||
@@ -207,7 +213,7 @@ export class GeminiProvider extends BaseAIProvider {
|
|||||||
try {
|
try {
|
||||||
await this.client.models.generateContent({
|
await this.client.models.generateContent({
|
||||||
model: this.defaultModel,
|
model: this.defaultModel,
|
||||||
contents: [{ role: 'user', parts: [{ text: 'Hi' }] }],
|
contents: [{ role: 'user', parts: [{ text: VALIDATION_PROMPT }] }],
|
||||||
config: { maxOutputTokens: 1 }
|
config: { maxOutputTokens: 1 }
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -291,8 +297,8 @@ export class GeminiProvider extends BaseAIProvider {
|
|||||||
const defaults = this.defaultGenerationConfig;
|
const defaults = this.defaultGenerationConfig;
|
||||||
|
|
||||||
const config: GenerateContentConfig = {
|
const config: GenerateContentConfig = {
|
||||||
temperature: params.temperature ?? defaults?.temperature ?? 0.7,
|
temperature: params.temperature ?? defaults?.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
maxOutputTokens: params.maxTokens ?? defaults?.maxOutputTokens ?? 1000
|
maxOutputTokens: params.maxTokens ?? defaults?.maxOutputTokens ?? DEFAULT_MAX_TOKENS
|
||||||
};
|
};
|
||||||
|
|
||||||
const topP = params.topP ?? defaults?.topP;
|
const topP = params.topP ?? defaults?.topP;
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ import type {
|
|||||||
} from '../types/index.js';
|
} from '../types/index.js';
|
||||||
import { BaseAIProvider } from './base.js';
|
import { BaseAIProvider } from './base.js';
|
||||||
import { AIProviderError, AIErrorType } from '../types/index.js';
|
import { AIProviderError, AIErrorType } from '../types/index.js';
|
||||||
|
import {
|
||||||
|
DEFAULT_MAX_TOKENS,
|
||||||
|
DEFAULT_MODELS,
|
||||||
|
DEFAULT_TEMPERATURE,
|
||||||
|
VALIDATION_PROMPT
|
||||||
|
} from '../constants.js';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TYPES AND INTERFACES
|
// TYPES AND INTERFACES
|
||||||
@@ -173,7 +179,7 @@ export class OpenAIProvider extends BaseAIProvider {
|
|||||||
super(config);
|
super(config);
|
||||||
|
|
||||||
// Set OpenAI-specific defaults
|
// Set OpenAI-specific defaults
|
||||||
this.defaultModel = config.defaultModel || 'gpt-4o';
|
this.defaultModel = config.defaultModel || DEFAULT_MODELS.openai;
|
||||||
this.organization = config.organization;
|
this.organization = config.organization;
|
||||||
this.project = config.project;
|
this.project = config.project;
|
||||||
|
|
||||||
@@ -352,10 +358,9 @@ export class OpenAIProvider extends BaseAIProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Make minimal request to test connection and permissions
|
|
||||||
await this.client.chat.completions.create({
|
await this.client.chat.completions.create({
|
||||||
model: this.defaultModel,
|
model: this.defaultModel,
|
||||||
messages: [{ role: 'user', content: 'Hi' }],
|
messages: [{ role: 'user', content: VALIDATION_PROMPT }],
|
||||||
max_tokens: 1
|
max_tokens: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -435,8 +440,8 @@ export class OpenAIProvider extends BaseAIProvider {
|
|||||||
return {
|
return {
|
||||||
model: params.model || this.defaultModel,
|
model: params.model || this.defaultModel,
|
||||||
messages: this.convertMessages(params.messages),
|
messages: this.convertMessages(params.messages),
|
||||||
max_tokens: params.maxTokens || 1000,
|
max_tokens: params.maxTokens || DEFAULT_MAX_TOKENS,
|
||||||
temperature: params.temperature ?? 0.7,
|
temperature: params.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
top_p: params.topP,
|
top_p: params.topP,
|
||||||
stop: params.stopSequences,
|
stop: params.stopSequences,
|
||||||
stream,
|
stream,
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ import type {
|
|||||||
} from '../types/index.js';
|
} from '../types/index.js';
|
||||||
import { BaseAIProvider } from './base.js';
|
import { BaseAIProvider } from './base.js';
|
||||||
import { AIProviderError, AIErrorType } from '../types/index.js';
|
import { AIProviderError, AIErrorType } from '../types/index.js';
|
||||||
|
import {
|
||||||
|
DEFAULT_MAX_TOKENS,
|
||||||
|
DEFAULT_MODELS,
|
||||||
|
DEFAULT_OPENWEBUI_BASE_URL,
|
||||||
|
DEFAULT_TEMPERATURE
|
||||||
|
} from '../constants.js';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TYPES AND INTERFACES
|
// TYPES AND INTERFACES
|
||||||
@@ -281,8 +287,8 @@ export class OpenWebUIProvider extends BaseAIProvider {
|
|||||||
super(config);
|
super(config);
|
||||||
|
|
||||||
// Set OpenWebUI-specific defaults and normalize configuration
|
// Set OpenWebUI-specific defaults and normalize configuration
|
||||||
this.defaultModel = config.defaultModel || 'llama3.1:latest';
|
this.defaultModel = config.defaultModel || DEFAULT_MODELS.openwebui;
|
||||||
this.baseUrl = this.normalizeBaseUrl(config.baseUrl || 'http://localhost:3000');
|
this.baseUrl = this.normalizeBaseUrl(config.baseUrl || DEFAULT_OPENWEBUI_BASE_URL);
|
||||||
this.useOllamaProxy = config.useOllamaProxy ?? false;
|
this.useOllamaProxy = config.useOllamaProxy ?? false;
|
||||||
this.dangerouslyAllowInsecureConnections = config.dangerouslyAllowInsecureConnections ?? true;
|
this.dangerouslyAllowInsecureConnections = config.dangerouslyAllowInsecureConnections ?? true;
|
||||||
|
|
||||||
@@ -689,8 +695,8 @@ export class OpenWebUIProvider extends BaseAIProvider {
|
|||||||
const requestBody = {
|
const requestBody = {
|
||||||
model: params.model || this.defaultModel,
|
model: params.model || this.defaultModel,
|
||||||
messages: this.convertMessages(params.messages),
|
messages: this.convertMessages(params.messages),
|
||||||
max_tokens: params.maxTokens || 1000,
|
max_tokens: params.maxTokens || DEFAULT_MAX_TOKENS,
|
||||||
temperature: params.temperature ?? 0.7,
|
temperature: params.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
top_p: params.topP,
|
top_p: params.topP,
|
||||||
stop: params.stopSequences,
|
stop: params.stopSequences,
|
||||||
stream: false
|
stream: false
|
||||||
@@ -724,9 +730,9 @@ export class OpenWebUIProvider extends BaseAIProvider {
|
|||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
stream: false,
|
stream: false,
|
||||||
options: {
|
options: {
|
||||||
temperature: params.temperature ?? 0.7,
|
temperature: params.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
top_p: params.topP,
|
top_p: params.topP,
|
||||||
num_predict: params.maxTokens || 1000,
|
num_predict: params.maxTokens || DEFAULT_MAX_TOKENS,
|
||||||
stop: params.stopSequences
|
stop: params.stopSequences
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -754,8 +760,8 @@ export class OpenWebUIProvider extends BaseAIProvider {
|
|||||||
const requestBody = {
|
const requestBody = {
|
||||||
model: params.model || this.defaultModel,
|
model: params.model || this.defaultModel,
|
||||||
messages: this.convertMessages(params.messages),
|
messages: this.convertMessages(params.messages),
|
||||||
max_tokens: params.maxTokens || 1000,
|
max_tokens: params.maxTokens || DEFAULT_MAX_TOKENS,
|
||||||
temperature: params.temperature ?? 0.7,
|
temperature: params.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
top_p: params.topP,
|
top_p: params.topP,
|
||||||
stop: params.stopSequences,
|
stop: params.stopSequences,
|
||||||
stream: true
|
stream: true
|
||||||
@@ -850,9 +856,9 @@ export class OpenWebUIProvider extends BaseAIProvider {
|
|||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
stream: true,
|
stream: true,
|
||||||
options: {
|
options: {
|
||||||
temperature: params.temperature ?? 0.7,
|
temperature: params.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
top_p: params.topP,
|
top_p: params.topP,
|
||||||
num_predict: params.maxTokens || 1000,
|
num_predict: params.maxTokens || DEFAULT_MAX_TOKENS,
|
||||||
stop: params.stopSequences
|
stop: params.stopSequences
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
/**
|
/**
|
||||||
* Factory utilities for creating AI providers
|
* Factory utilities for creating AI providers.
|
||||||
* Provides convenient methods for instantiating and configuring providers
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { AIProviderConfig } from '../types/index.js';
|
|
||||||
import { ClaudeProvider, type ClaudeConfig } from '../providers/claude.js';
|
import { ClaudeProvider, type ClaudeConfig } from '../providers/claude.js';
|
||||||
import { OpenAIProvider, type OpenAIConfig } from '../providers/openai.js';
|
import { OpenAIProvider, type OpenAIConfig } from '../providers/openai.js';
|
||||||
import { GeminiProvider, type GeminiConfig } from '../providers/gemini.js';
|
import { GeminiProvider, type GeminiConfig } from '../providers/gemini.js';
|
||||||
import { OpenWebUIProvider, type OpenWebUIConfig } from '../providers/openwebui.js';
|
import { OpenWebUIProvider, type OpenWebUIConfig } from '../providers/openwebui.js';
|
||||||
import { BaseAIProvider } from '../providers/base.js';
|
import { BaseAIProvider } from '../providers/base.js';
|
||||||
|
|
||||||
/**
|
export const PROVIDER_REGISTRY = {
|
||||||
* Supported AI provider types
|
claude: ClaudeProvider,
|
||||||
*/
|
openai: OpenAIProvider,
|
||||||
export type ProviderType = 'claude' | 'openai' | 'gemini' | 'openwebui';
|
gemini: GeminiProvider,
|
||||||
|
openwebui: OpenWebUIProvider
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type ProviderType = keyof typeof PROVIDER_REGISTRY;
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration map for different provider types
|
|
||||||
*/
|
|
||||||
export interface ProviderConfigMap {
|
export interface ProviderConfigMap {
|
||||||
claude: ClaudeConfig;
|
claude: ClaudeConfig;
|
||||||
openai: OpenAIConfig;
|
openai: OpenAIConfig;
|
||||||
@@ -25,144 +24,38 @@ export interface ProviderConfigMap {
|
|||||||
openwebui: OpenWebUIConfig;
|
openwebui: OpenWebUIConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory function to create AI providers
|
|
||||||
* @param type - The type of provider to create
|
|
||||||
* @param config - Configuration for the provider
|
|
||||||
* @returns Configured AI provider instance
|
|
||||||
*/
|
|
||||||
export function createProvider<T extends ProviderType>(
|
export function createProvider<T extends ProviderType>(
|
||||||
type: T,
|
type: T,
|
||||||
config: ProviderConfigMap[T]
|
config: ProviderConfigMap[T]
|
||||||
): BaseAIProvider {
|
): BaseAIProvider {
|
||||||
switch (type) {
|
const ProviderClass = PROVIDER_REGISTRY[type];
|
||||||
case 'claude':
|
if (!ProviderClass) {
|
||||||
return new ClaudeProvider(config as ClaudeConfig);
|
throw new Error(`Unsupported provider type: ${type}`);
|
||||||
case 'openai':
|
|
||||||
return new OpenAIProvider(config as OpenAIConfig);
|
|
||||||
case 'gemini':
|
|
||||||
return new GeminiProvider(config as GeminiConfig);
|
|
||||||
case 'openwebui':
|
|
||||||
return new OpenWebUIProvider(config as OpenWebUIConfig);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported provider type: ${type}`);
|
|
||||||
}
|
}
|
||||||
|
return new ProviderClass(config as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Claude provider with simplified configuration
|
|
||||||
* @param apiKey - Anthropic API key
|
|
||||||
* @param options - Optional additional configuration
|
|
||||||
* @returns Configured Claude provider instance
|
|
||||||
*/
|
|
||||||
export function createClaudeProvider(
|
export function createClaudeProvider(
|
||||||
apiKey: string,
|
apiKey: string,
|
||||||
options: Partial<Omit<ClaudeConfig, 'apiKey'>> = {}
|
options: Partial<Omit<ClaudeConfig, 'apiKey'>> = {}
|
||||||
): ClaudeProvider {
|
): ClaudeProvider {
|
||||||
return new ClaudeProvider({
|
return new ClaudeProvider({ apiKey, ...options });
|
||||||
apiKey,
|
|
||||||
...options
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an OpenAI provider with simplified configuration
|
|
||||||
* @param apiKey - OpenAI API key
|
|
||||||
* @param options - Optional additional configuration
|
|
||||||
* @returns Configured OpenAI provider instance
|
|
||||||
*/
|
|
||||||
export function createOpenAIProvider(
|
export function createOpenAIProvider(
|
||||||
apiKey: string,
|
apiKey: string,
|
||||||
options: Partial<Omit<OpenAIConfig, 'apiKey'>> = {}
|
options: Partial<Omit<OpenAIConfig, 'apiKey'>> = {}
|
||||||
): OpenAIProvider {
|
): OpenAIProvider {
|
||||||
return new OpenAIProvider({
|
return new OpenAIProvider({ apiKey, ...options });
|
||||||
apiKey,
|
|
||||||
...options
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Gemini provider with simplified configuration
|
|
||||||
* @param apiKey - Google AI API key
|
|
||||||
* @param options - Optional additional configuration
|
|
||||||
* @returns Configured Gemini provider instance
|
|
||||||
*/
|
|
||||||
export function createGeminiProvider(
|
export function createGeminiProvider(
|
||||||
apiKey: string,
|
apiKey: string,
|
||||||
options: Partial<Omit<GeminiConfig, 'apiKey'>> = {}
|
options: Partial<Omit<GeminiConfig, 'apiKey'>> = {}
|
||||||
): GeminiProvider {
|
): GeminiProvider {
|
||||||
return new GeminiProvider({
|
return new GeminiProvider({ apiKey, ...options });
|
||||||
apiKey,
|
|
||||||
...options
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an OpenWebUI provider instance
|
|
||||||
*/
|
|
||||||
export function createOpenWebUIProvider(config: OpenWebUIConfig): OpenWebUIProvider {
|
export function createOpenWebUIProvider(config: OpenWebUIConfig): OpenWebUIProvider {
|
||||||
return new OpenWebUIProvider(config);
|
return new OpenWebUIProvider(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider registry for dynamic provider creation
|
|
||||||
*/
|
|
||||||
export class ProviderRegistry {
|
|
||||||
private static providers = new Map<string, new (config: AIProviderConfig) => BaseAIProvider>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a new provider type
|
|
||||||
* @param name - Name of the provider
|
|
||||||
* @param providerClass - Provider class constructor
|
|
||||||
*/
|
|
||||||
static register(name: string, providerClass: new (config: AIProviderConfig) => BaseAIProvider): void {
|
|
||||||
this.providers.set(name.toLowerCase(), providerClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a provider by name
|
|
||||||
* @param name - Name of the provider
|
|
||||||
* @param config - Configuration for the provider
|
|
||||||
* @returns Provider instance
|
|
||||||
*/
|
|
||||||
static create(name: string, config: AIProviderConfig): BaseAIProvider {
|
|
||||||
const ProviderClass = this.providers.get(name.toLowerCase());
|
|
||||||
if (!ProviderClass) {
|
|
||||||
throw new Error(`Provider '${name}' is not registered`);
|
|
||||||
}
|
|
||||||
return new ProviderClass(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of registered provider names
|
|
||||||
* @returns Array of registered provider names
|
|
||||||
*/
|
|
||||||
static getRegisteredProviders(): string[] {
|
|
||||||
return Array.from(this.providers.keys());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a provider is registered
|
|
||||||
* @param name - Name of the provider
|
|
||||||
* @returns True if provider is registered
|
|
||||||
*/
|
|
||||||
static isRegistered(name: string): boolean {
|
|
||||||
return this.providers.has(name.toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-register built-in providers
|
|
||||||
ProviderRegistry.register('claude', ClaudeProvider);
|
|
||||||
ProviderRegistry.register('openai', OpenAIProvider);
|
|
||||||
ProviderRegistry.register('gemini', GeminiProvider);
|
|
||||||
ProviderRegistry.register('openwebui', OpenWebUIProvider);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry of all available providers
|
|
||||||
*/
|
|
||||||
export const PROVIDER_REGISTRY = {
|
|
||||||
claude: ClaudeProvider,
|
|
||||||
openai: OpenAIProvider,
|
|
||||||
gemini: GeminiProvider,
|
|
||||||
openwebui: OpenWebUIProvider
|
|
||||||
} as const;
|
|
||||||
Reference in New Issue
Block a user