From 85cfc8fbd4d940b6928b5bb30e053615db095ebf Mon Sep 17 00:00:00 2001 From: perf3ct Date: Fri, 6 Jun 2025 19:22:39 +0000 Subject: [PATCH] feat(llm): have OpenAI provider not require API keys (for endpoints like LM Studio) --- .../client/src/widgets/llm_chat/validation.ts | 2 +- apps/server/src/routes/api/openai.ts | 11 +++---- .../services/llm/providers/openai_service.ts | 6 ++-- .../src/services/llm/providers/providers.ts | 30 ++++++++++++------- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/apps/client/src/widgets/llm_chat/validation.ts b/apps/client/src/widgets/llm_chat/validation.ts index e39b07012..94977f63a 100644 --- a/apps/client/src/widgets/llm_chat/validation.ts +++ b/apps/client/src/widgets/llm_chat/validation.ts @@ -44,7 +44,7 @@ export async function validateEmbeddingProviders(validationWarning: HTMLElement) // Check OpenAI configuration const apiKey = options.get('openaiApiKey'); if (!apiKey) { - configIssues.push(`OpenAI API key is missing`); + configIssues.push(`OpenAI API key is missing (optional for OpenAI-compatible endpoints)`); } } else if (provider === 'anthropic') { // Check Anthropic configuration diff --git a/apps/server/src/routes/api/openai.ts b/apps/server/src/routes/api/openai.ts index ced03ce04..84efad2ca 100644 --- a/apps/server/src/routes/api/openai.ts +++ b/apps/server/src/routes/api/openai.ts @@ -66,12 +66,13 @@ async function listModels(req: Request, res: Response) { const apiKey = await options.getOption('openaiApiKey'); if (!apiKey) { - throw new Error('OpenAI API key is not configured'); + // Log warning but don't throw - some OpenAI-compatible endpoints don't require API keys + log.info('OpenAI API key is not configured when listing models. This may cause issues with official OpenAI endpoints.'); } - // Initialize OpenAI client with the API key and base URL + // Initialize OpenAI client with the API key (or empty string) and base URL const openai = new OpenAI({ - apiKey, + apiKey: apiKey || '', // Default to empty string if no API key baseURL: openaiBaseUrl }); @@ -84,9 +85,9 @@ async function listModels(req: Request, res: Response) { // Include all models as chat models, without filtering by specific model names // This allows models from providers like OpenRouter to be displayed const chatModels = allModels - .filter((model) => + .filter((model) => // Exclude models that are explicitly for embeddings - !model.id.includes('embedding') && + !model.id.includes('embedding') && !model.id.includes('embed') ) .map((model) => ({ diff --git a/apps/server/src/services/llm/providers/openai_service.ts b/apps/server/src/services/llm/providers/openai_service.ts index 09d58498a..411f512f7 100644 --- a/apps/server/src/services/llm/providers/openai_service.ts +++ b/apps/server/src/services/llm/providers/openai_service.ts @@ -14,7 +14,9 @@ export class OpenAIService extends BaseAIService { } override isAvailable(): boolean { - return super.isAvailable() && !!options.getOption('openaiApiKey'); + // Make API key optional to support OpenAI-compatible endpoints that don't require authentication + // The provider is considered available as long as the parent checks pass + return super.isAvailable(); } private getClient(apiKey: string, baseUrl?: string): OpenAI { @@ -29,7 +31,7 @@ export class OpenAIService extends BaseAIService { async generateChatCompletion(messages: Message[], opts: ChatCompletionOptions = {}): Promise { if (!this.isAvailable()) { - throw new Error('OpenAI service is not available. Check API key and AI settings.'); + throw new Error('OpenAI service is not available. Check AI settings.'); } // Get provider-specific options from the central provider manager diff --git a/apps/server/src/services/llm/providers/providers.ts b/apps/server/src/services/llm/providers/providers.ts index 2fe64735a..fd7c603fd 100644 --- a/apps/server/src/services/llm/providers/providers.ts +++ b/apps/server/src/services/llm/providers/providers.ts @@ -134,7 +134,7 @@ export async function createProvidersFromCurrentOptions(): Promise