diff --git a/app/lib/modules/llm/providers/openai-like.ts b/app/lib/modules/llm/providers/openai-like.ts index f33f21ede0..713da07bc0 100644 --- a/app/lib/modules/llm/providers/openai-like.ts +++ b/app/lib/modules/llm/providers/openai-like.ts @@ -10,6 +10,7 @@ export default class OpenAILikeProvider extends BaseProvider { config = { baseUrlKey: 'OPENAI_LIKE_API_BASE_URL', apiTokenKey: 'OPENAI_LIKE_API_KEY', + modelsKey: 'OPENAI_LIKE_API_MODELS', }; staticModels: ModelInfo[] = []; @@ -31,20 +32,114 @@ export default class OpenAILikeProvider extends BaseProvider { return []; } - const response = await fetch(`${baseUrl}/models`, { - headers: { - Authorization: `Bearer ${apiKey}`, - }, - }); + try { + const response = await fetch(`${baseUrl}/models`, { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const res = (await response.json()) as any; + + return res.data.map((model: any) => ({ + name: model.id, + label: model.id, + provider: this.name, + maxTokenAllowed: 8000, + })); + } catch (error) { + console.log(`${this.name}: Not allowed to GET /models endpoint for provider`, error); + + // Fallback to OPENAI_LIKE_API_MODELS if available + // eslint-disable-next-line dot-notation + const modelsEnv = serverEnv['OPENAI_LIKE_API_MODELS'] || settings?.OPENAI_LIKE_API_MODELS; + + if (modelsEnv) { + console.log(`${this.name}: OPENAI_LIKE_API_MODELS=${modelsEnv}`); + return this._parseModelsFromEnv(modelsEnv); + } + + return []; + } + } + + /** + * Parse OPENAI_LIKE_API_MODELS environment variable + * Format: path/to/model1:limit;path/to/model2:limit;path/to/model3:limit + */ + private _parseModelsFromEnv(modelsEnv: string): ModelInfo[] { + if (!modelsEnv) { + return []; + } + + try { + const models: ModelInfo[] = []; + const modelEntries = modelsEnv.split(';'); + + for (const entry of modelEntries) { + const trimmedEntry = entry.trim(); - const res = (await response.json()) as any; + if (!trimmedEntry) { + continue; + } + + const [modelPath, limitStr] = trimmedEntry.split(':'); + + if (!modelPath) { + continue; + } + + const limit = limitStr ? parseInt(limitStr.trim(), 10) : 8000; + const modelName = modelPath.trim(); + + // Generate a readable label from the model path + const label = this._generateModelLabel(modelName); + + models.push({ + name: modelName, + label, + provider: this.name, + maxTokenAllowed: limit, + }); + } + + console.log(`${this.name}: Parsed Models:`, models); + + return models; + } catch (error) { + console.error(`${this.name}: Error parsing OPENAI_LIKE_API_MODELS:`, error); + return []; + } + } + + /** + * Generate a readable label from model path + */ + private _generateModelLabel(modelPath: string): string { + // Extract the last part of the path and clean it up + const parts = modelPath.split('/'); + const lastPart = parts[parts.length - 1]; + + // Remove common prefixes and clean up the name + let label = lastPart + .replace(/^accounts\//, '') + .replace(/^fireworks\/models\//, '') + .replace(/^models\//, '') + // Capitalize first letter of each word + .replace(/\b\w/g, (l) => l.toUpperCase()) + // Replace spaces with hyphens for a cleaner look + .replace(/\s+/g, '-'); + + // Add provider suffix if not already present + if (!label.includes('Fireworks') && !label.includes('OpenAI')) { + label += ' (OpenAI Compatible)'; + } - return res.data.map((model: any) => ({ - name: model.id, - label: model.id, - provider: this.name, - maxTokenAllowed: 8000, - })); + return label; } getModelInstance(options: { diff --git a/app/types/model.ts b/app/types/model.ts index d16b10ae4c..ffd68dca6b 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -17,6 +17,7 @@ export type ProviderInfo = { export interface IProviderSetting { enabled?: boolean; baseUrl?: string; + OPENAI_LIKE_API_MODELS?: string; } export type IProviderConfig = ProviderInfo & { diff --git a/vite-electron.config.ts b/vite-electron.config.ts index 2c7b8183ce..e8efa61eda 100644 --- a/vite-electron.config.ts +++ b/vite-electron.config.ts @@ -60,6 +60,7 @@ export default defineConfig((config) => { envPrefix: [ 'VITE_', 'OPENAI_LIKE_API_BASE_URL', + 'OPENAI_LIKE_API_MODELS', 'OLLAMA_API_BASE_URL', 'LMSTUDIO_API_BASE_URL', 'TOGETHER_API_BASE_URL', diff --git a/vite.config.ts b/vite.config.ts index 7847a17cbd..e0b096c8e8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -60,6 +60,7 @@ export default defineConfig((config) => { envPrefix: [ 'VITE_', 'OPENAI_LIKE_API_BASE_URL', + 'OPENAI_LIKE_API_MODELS', 'OLLAMA_API_BASE_URL', 'LMSTUDIO_API_BASE_URL', 'TOGETHER_API_BASE_URL', diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index b2ae1ce79e..6dd21f9487 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -9,6 +9,7 @@ interface Env { OLLAMA_API_BASE_URL: string; OPENAI_LIKE_API_KEY: string; OPENAI_LIKE_API_BASE_URL: string; + OPENAI_LIKE_API_MODELS: string; TOGETHER_API_KEY: string; TOGETHER_API_BASE_URL: string; DEEPSEEK_API_KEY: string;