From 4b86e999b2a431aea8b64f1fc36a08e5487aa678 Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:08:57 +0100 Subject: [PATCH 1/6] fix: improve local model provider robustness and UX - Extract shared Docker URL rewriting and env conversion into BaseProvider to eliminate 4x duplicated code across Ollama and LMStudio - Add error handling and 5s timeouts to all model-listing fetches so one unreachable provider doesn't block the entire model list - Fix Ollama using createOllama() instead of mutating provider internals - Fix LLMManager singleton ignoring env updates on subsequent requests - Narrow cache key to only include provider-relevant env vars instead of the entire server environment - Fix 'as any' casts in LMStudio and OpenAILike by using shared convertEnvToRecord helper - Replace console.log/error with structured logger in OpenAILike - Fix typo: filteredStaticModesl -> filteredStaticModels in manager - Add connection status indicator (green/red dot) for local providers in the ModelSelector dropdown - Show helpful "is X running?" message when local provider has no models Co-Authored-By: Claude Opus 4.6 --- app/components/chat/ModelSelector.tsx | 85 +++++++++++-- app/lib/modules/llm/base-provider.ts | 61 +++++++++- app/lib/modules/llm/manager.ts | 9 +- app/lib/modules/llm/providers/lmstudio.ts | 86 ++++++++------ app/lib/modules/llm/providers/ollama.ts | 119 +++++++++---------- app/lib/modules/llm/providers/openai-like.ts | 22 ++-- 6 files changed, 262 insertions(+), 120 deletions(-) diff --git a/app/components/chat/ModelSelector.tsx b/app/components/chat/ModelSelector.tsx index 2ccb9a5277..c69331fcf4 100644 --- a/app/components/chat/ModelSelector.tsx +++ b/app/components/chat/ModelSelector.tsx @@ -3,6 +3,7 @@ import { useEffect, useState, useRef, useMemo, useCallback } from 'react'; import type { KeyboardEvent } from 'react'; import type { ModelInfo } from '~/lib/modules/llm/types'; import { classNames } from '~/utils/classNames'; +import { LOCAL_PROVIDERS } from '~/lib/stores/settings'; // Fuzzy search utilities const levenshteinDistance = (str1: string, str2: string): number => { @@ -130,6 +131,32 @@ export const ModelSelector = ({ const providerDropdownRef = useRef(null); const [showFreeModelsOnly, setShowFreeModelsOnly] = useState(false); + type ConnectionStatus = 'unknown' | 'connected' | 'disconnected'; + + const [localProviderStatus, setLocalProviderStatus] = useState>({}); + + // Check connectivity of local providers when provider list changes + useEffect(() => { + const checkLocalProviders = async () => { + const statuses: Record = {}; + + for (const p of providerList) { + if (!LOCAL_PROVIDERS.includes(p.name)) { + continue; + } + + // If the provider has models loaded, it's connected + const hasModels = modelList.some((m) => m.provider === p.name); + + statuses[p.name] = hasModels ? 'connected' : 'disconnected'; + } + + setLocalProviderStatus(statuses); + }; + + checkLocalProviders(); + }, [providerList, modelList]); + // Debounce search queries useEffect(() => { const timer = setTimeout(() => { @@ -440,7 +467,28 @@ export const ModelSelector = ({ tabIndex={0} >
-
{provider?.name || 'Select provider'}
+
+ {provider?.name && LOCAL_PROVIDERS.includes(provider.name) && ( + + )} + {provider?.name || 'Select provider'} +
-
+
+ {LOCAL_PROVIDERS.includes(providerOption.name) && ( + + )} + +
)) )} @@ -717,8 +779,17 @@ export const ModelSelector = ({ ? `No models match "${debouncedModelSearchQuery}"${showFreeModelsOnly ? ' (free only)' : ''}` : showFreeModelsOnly ? 'No free models available' - : 'No models available'} + : provider?.name && LOCAL_PROVIDERS.includes(provider.name) + ? `No models found — is ${provider.name} running?` + : 'No models available'}
+ {!debouncedModelSearchQuery && provider?.name && LOCAL_PROVIDERS.includes(provider.name) && ( +
+ Make sure {provider.name} is running and has at least one model loaded. + {provider.name === 'Ollama' && ' Try: ollama pull llama3.2'} + {provider.name === 'LMStudio' && ' Load a model in LM Studio first.'} +
+ )} {debouncedModelSearchQuery && (
Try searching for model names, context sizes (e.g., "128k", "1M"), or capabilities diff --git a/app/lib/modules/llm/base-provider.ts b/app/lib/modules/llm/base-provider.ts index 9cb23403d7..ada2f3a4d2 100644 --- a/app/lib/modules/llm/base-provider.ts +++ b/app/lib/modules/llm/base-provider.ts @@ -4,6 +4,9 @@ import type { IProviderSetting } from '~/types/model'; import { createOpenAI } from '@ai-sdk/openai'; import { LLMManager } from './manager'; +/** Default timeout for model listing API calls (5 seconds) */ +const MODEL_FETCH_TIMEOUT = 5_000; + export abstract class BaseProvider implements ProviderInfo { abstract name: string; abstract staticModels: ModelInfo[]; @@ -17,6 +20,48 @@ export abstract class BaseProvider implements ProviderInfo { labelForGetApiKey?: string; icon?: string; + /** + * Convert Cloudflare Env bindings to a plain Record. + * Useful because provider methods expect Record but + * Cloudflare Workers pass an Env interface. + */ + protected convertEnvToRecord(env?: Env): Record { + if (!env) { + return {}; + } + + return Object.entries(env).reduce( + (acc, [key, value]) => { + acc[key] = String(value); + + return acc; + }, + {} as Record, + ); + } + + /** + * Rewrite localhost / 127.0.0.1 URLs to host.docker.internal when + * running inside Docker. Only applies on the server side. + */ + protected resolveDockerUrl(baseUrl: string, serverEnv?: Record): string { + const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; + + if (!isDocker) { + return baseUrl; + } + + return baseUrl.replace('localhost', 'host.docker.internal').replace('127.0.0.1', 'host.docker.internal'); + } + + /** + * Create an AbortSignal that times out after the given milliseconds. + * Used to prevent model-listing fetches from hanging indefinitely. + */ + protected createTimeoutSignal(ms: number = MODEL_FETCH_TIMEOUT): AbortSignal { + return AbortSignal.timeout(ms); + } + getProviderBaseUrlAndKey(options: { apiKeys?: Record; providerSettings?: IProviderSetting; @@ -59,7 +104,6 @@ export abstract class BaseProvider implements ProviderInfo { serverEnv?: Record; }): ModelInfo[] | null { if (!this.cachedDynamicModels) { - // console.log('no dynamic models',this.name); return null; } @@ -67,8 +111,8 @@ export abstract class BaseProvider implements ProviderInfo { const generatedCacheKey = this.getDynamicModelsCacheKey(options); if (cacheKey !== generatedCacheKey) { - // console.log('cache key mismatch',this.name,cacheKey,generatedCacheKey); this.cachedDynamicModels = undefined; + return null; } @@ -79,10 +123,20 @@ export abstract class BaseProvider implements ProviderInfo { providerSettings?: Record; serverEnv?: Record; }) { + // Only include provider-relevant env keys, not the entire server environment + const relevantEnvKeys = [this.config.baseUrlKey, this.config.apiTokenKey].filter(Boolean) as string[]; + const relevantEnv: Record = {}; + + for (const key of relevantEnvKeys) { + if (options.serverEnv?.[key]) { + relevantEnv[key] = options.serverEnv[key]; + } + } + return JSON.stringify({ apiKeys: options.apiKeys?.[this.name], providerSettings: options.providerSettings?.[this.name], - serverEnv: options.serverEnv, + serverEnv: relevantEnv, }); } storeDynamicModels( @@ -95,7 +149,6 @@ export abstract class BaseProvider implements ProviderInfo { ) { const cacheId = this.getDynamicModelsCacheKey(options); - // console.log('caching dynamic models',this.name,cacheId); this.cachedDynamicModels = { cacheId, models, diff --git a/app/lib/modules/llm/manager.ts b/app/lib/modules/llm/manager.ts index aec9190593..687c399ccf 100644 --- a/app/lib/modules/llm/manager.ts +++ b/app/lib/modules/llm/manager.ts @@ -9,7 +9,7 @@ export class LLMManager { private static _instance: LLMManager; private _providers: Map = new Map(); private _modelList: ModelInfo[] = []; - private readonly _env: any = {}; + private _env: Record = {}; private constructor(_env: Record) { this._registerProvidersFromDirectory(); @@ -19,6 +19,9 @@ export class LLMManager { static getInstance(env: Record = {}): LLMManager { if (!LLMManager._instance) { LLMManager._instance = new LLMManager(env); + } else if (Object.keys(env).length > 0) { + // Update env on subsequent calls so Cloudflare Workers get fresh bindings + LLMManager._instance._env = env; } return LLMManager._instance; @@ -121,10 +124,10 @@ export class LLMManager { const staticModels = Array.from(this._providers.values()).flatMap((p) => p.staticModels || []); const dynamicModelsFlat = dynamicModels.flat(); const dynamicModelKeys = dynamicModelsFlat.map((d) => `${d.name}-${d.provider}`); - const filteredStaticModesl = staticModels.filter((m) => !dynamicModelKeys.includes(`${m.name}-${m.provider}`)); + const filteredStaticModels = staticModels.filter((m) => !dynamicModelKeys.includes(`${m.name}-${m.provider}`)); // Combine static and dynamic models - const modelList = [...dynamicModelsFlat, ...filteredStaticModesl]; + const modelList = [...dynamicModelsFlat, ...filteredStaticModels]; modelList.sort((a, b) => a.name.localeCompare(b.name)); this._modelList = modelList; diff --git a/app/lib/modules/llm/providers/lmstudio.ts b/app/lib/modules/llm/providers/lmstudio.ts index fe5b27cd4a..e7f6793eb9 100644 --- a/app/lib/modules/llm/providers/lmstudio.ts +++ b/app/lib/modules/llm/providers/lmstudio.ts @@ -18,11 +18,11 @@ export default class LMStudioProvider extends BaseProvider { staticModels: ModelInfo[] = []; - async getDynamicModels( + private _resolveBaseUrl( apiKeys?: Record, settings?: IProviderSetting, - serverEnv: Record = {}, - ): Promise { + serverEnv?: Record, + ): string { let { baseUrl } = this.getProviderBaseUrlAndKey({ apiKeys, providerSettings: settings, @@ -35,27 +35,54 @@ export default class LMStudioProvider extends BaseProvider { throw new Error('No baseUrl found for LMStudio provider'); } - if (typeof window === 'undefined') { - /* - * Running in Server - * Backend: Check if we're running in Docker - */ - const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; + baseUrl = this.resolveDockerUrl(baseUrl, serverEnv); - baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; - baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; - } + return baseUrl; + } + + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv: Record = {}, + ): Promise { + const baseUrl = this._resolveBaseUrl(apiKeys, settings, serverEnv); + + try { + const response = await fetch(`${baseUrl}/v1/models`, { + signal: this.createTimeoutSignal(), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = (await response.json()) as { data: Array<{ id: string }> }; + + return data.data.map((model) => ({ + name: model.id, + label: model.id, + provider: this.name, + maxTokenAllowed: 8000, + })); + } catch (error) { + if (error instanceof DOMException && error.name === 'TimeoutError') { + logger.warn('LMStudio model fetch timed out — is LM Studio running?'); + + return []; + } - const response = await fetch(`${baseUrl}/v1/models`); - const data = (await response.json()) as { data: Array<{ id: string }> }; + if (error instanceof TypeError && error.message.includes('fetch')) { + logger.warn(`LMStudio not reachable at ${baseUrl} — is LM Studio running?`); - return data.data.map((model) => ({ - name: model.id, - label: model.id, - provider: this.name, - maxTokenAllowed: 8000, - })); + return []; + } + + logger.error('Error fetching LMStudio models:', error); + + return []; + } } + getModelInstance: (options: { model: string; serverEnv?: Env; @@ -63,24 +90,9 @@ export default class LMStudioProvider extends BaseProvider { providerSettings?: Record; }) => LanguageModelV1 = (options) => { const { apiKeys, providerSettings, serverEnv, model } = options; - let { baseUrl } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - serverEnv: serverEnv as any, - defaultBaseUrlKey: 'LMSTUDIO_API_BASE_URL', - defaultApiTokenKey: '', - }); + const envRecord = this.convertEnvToRecord(serverEnv); - if (!baseUrl) { - throw new Error('No baseUrl found for LMStudio provider'); - } - - const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; - - if (typeof window === 'undefined') { - baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; - baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; - } + const baseUrl = this._resolveBaseUrl(apiKeys, providerSettings?.[this.name], envRecord); logger.debug('LMStudio Base Url used: ', baseUrl); diff --git a/app/lib/modules/llm/providers/ollama.ts b/app/lib/modules/llm/providers/ollama.ts index e50ecae561..3d7f53b8ee 100644 --- a/app/lib/modules/llm/providers/ollama.ts +++ b/app/lib/modules/llm/providers/ollama.ts @@ -2,7 +2,7 @@ import { BaseProvider } from '~/lib/modules/llm/base-provider'; import type { ModelInfo } from '~/lib/modules/llm/types'; import type { IProviderSetting } from '~/types/model'; import type { LanguageModelV1 } from 'ai'; -import { ollama } from 'ollama-ai-provider'; +import { createOllama } from 'ollama-ai-provider'; import { logger } from '~/utils/logger'; interface OllamaModelDetails { @@ -39,31 +39,17 @@ export default class OllamaProvider extends BaseProvider { staticModels: ModelInfo[] = []; - private _convertEnvToRecord(env?: Env): Record { - if (!env) { - return {}; - } - - // Convert Env to a plain object with string values - return Object.entries(env).reduce( - (acc, [key, value]) => { - acc[key] = String(value); - return acc; - }, - {} as Record, - ); - } - getDefaultNumCtx(serverEnv?: Env): number { - const envRecord = this._convertEnvToRecord(serverEnv); + const envRecord = this.convertEnvToRecord(serverEnv); + return envRecord.DEFAULT_NUM_CTX ? parseInt(envRecord.DEFAULT_NUM_CTX, 10) : 32768; } - async getDynamicModels( + private _resolveBaseUrl( apiKeys?: Record, settings?: IProviderSetting, - serverEnv: Record = {}, - ): Promise { + serverEnv?: Record, + ): string { let { baseUrl } = this.getProviderBaseUrlAndKey({ apiKeys, providerSettings: settings, @@ -73,31 +59,55 @@ export default class OllamaProvider extends BaseProvider { }); if (!baseUrl) { - throw new Error('No baseUrl found for OLLAMA provider'); + throw new Error('No baseUrl found for Ollama provider'); } - if (typeof window === 'undefined') { - /* - * Running in Server - * Backend: Check if we're running in Docker - */ - const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; + baseUrl = this.resolveDockerUrl(baseUrl, serverEnv); - baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; - baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; - } + return baseUrl; + } + + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv: Record = {}, + ): Promise { + const baseUrl = this._resolveBaseUrl(apiKeys, settings, serverEnv); + + try { + const response = await fetch(`${baseUrl}/api/tags`, { + signal: this.createTimeoutSignal(), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = (await response.json()) as OllamaApiResponse; - const response = await fetch(`${baseUrl}/api/tags`); - const data = (await response.json()) as OllamaApiResponse; + return data.models.map((model: OllamaModel) => ({ + name: model.name, + label: `${model.name} (${model.details.parameter_size})`, + provider: this.name, + maxTokenAllowed: 8000, + })); + } catch (error) { + if (error instanceof DOMException && error.name === 'TimeoutError') { + logger.warn('Ollama model fetch timed out — is Ollama running?'); - // console.log({ ollamamodels: data.models }); + return []; + } - return data.models.map((model: OllamaModel) => ({ - name: model.name, - label: `${model.name} (${model.details.parameter_size})`, - provider: this.name, - maxTokenAllowed: 8000, - })); + if (error instanceof TypeError && error.message.includes('fetch')) { + logger.warn(`Ollama not reachable at ${baseUrl} — is Ollama running?`); + + return []; + } + + logger.error('Error fetching Ollama models:', error); + + return []; + } } getModelInstance: (options: { @@ -107,33 +117,18 @@ export default class OllamaProvider extends BaseProvider { providerSettings?: Record; }) => LanguageModelV1 = (options) => { const { apiKeys, providerSettings, serverEnv, model } = options; - const envRecord = this._convertEnvToRecord(serverEnv); - - let { baseUrl } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - serverEnv: envRecord, - defaultBaseUrlKey: 'OLLAMA_API_BASE_URL', - defaultApiTokenKey: '', - }); + const envRecord = this.convertEnvToRecord(serverEnv); - // Backend: Check if we're running in Docker - if (!baseUrl) { - throw new Error('No baseUrl found for OLLAMA provider'); - } - - const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || envRecord.RUNNING_IN_DOCKER === 'true'; - baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; - baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; + const baseUrl = this._resolveBaseUrl(apiKeys, providerSettings?.[this.name], envRecord); logger.debug('Ollama Base Url used: ', baseUrl); - const ollamaInstance = ollama(model, { - numCtx: this.getDefaultNumCtx(serverEnv), - }) as LanguageModelV1 & { config: any }; - - ollamaInstance.config.baseURL = `${baseUrl}/api`; + const ollamaProvider = createOllama({ + baseURL: `${baseUrl}/api`, + }); - return ollamaInstance; + return ollamaProvider(model, { + numCtx: this.getDefaultNumCtx(serverEnv), + }); }; } diff --git a/app/lib/modules/llm/providers/openai-like.ts b/app/lib/modules/llm/providers/openai-like.ts index 713da07bc0..85dc42d68b 100644 --- a/app/lib/modules/llm/providers/openai-like.ts +++ b/app/lib/modules/llm/providers/openai-like.ts @@ -2,6 +2,11 @@ import { BaseProvider, getOpenAILikeModel } from '~/lib/modules/llm/base-provide import type { ModelInfo } from '~/lib/modules/llm/types'; import type { IProviderSetting } from '~/types/model'; import type { LanguageModelV1 } from 'ai'; +import { logger } from '~/utils/logger'; + +interface OpenAIModelsResponse { + data: Array<{ id: string }>; +} export default class OpenAILikeProvider extends BaseProvider { name = 'OpenAILike'; @@ -37,29 +42,31 @@ export default class OpenAILikeProvider extends BaseProvider { headers: { Authorization: `Bearer ${apiKey}`, }, + signal: this.createTimeoutSignal(), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } - const res = (await response.json()) as any; + const res = (await response.json()) as OpenAIModelsResponse; - return res.data.map((model: any) => ({ + return res.data.map((model) => ({ 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); + logger.info(`${this.name}: Could not fetch /models endpoint, checking fallback env`, 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}`); + logger.info(`${this.name}: Using OPENAI_LIKE_API_MODELS fallback`); + return this._parseModelsFromEnv(modelsEnv); } @@ -107,11 +114,11 @@ export default class OpenAILikeProvider extends BaseProvider { }); } - console.log(`${this.name}: Parsed Models:`, models); + logger.info(`${this.name}: Parsed ${models.length} models from env`); return models; } catch (error) { - console.error(`${this.name}: Error parsing OPENAI_LIKE_API_MODELS:`, error); + logger.error(`${this.name}: Error parsing OPENAI_LIKE_API_MODELS:`, error); return []; } } @@ -149,11 +156,12 @@ export default class OpenAILikeProvider extends BaseProvider { providerSettings?: Record; }): LanguageModelV1 { const { model, serverEnv, apiKeys, providerSettings } = options; + const envRecord = this.convertEnvToRecord(serverEnv); const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({ apiKeys, providerSettings: providerSettings?.[this.name], - serverEnv: serverEnv as any, + serverEnv: envRecord, defaultBaseUrlKey: 'OPENAI_LIKE_API_BASE_URL', defaultApiTokenKey: 'OPENAI_LIKE_API_KEY', }); From 4691b14d5da87e23a3a1e286d7e38f90385d1bd7 Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:38:28 +0100 Subject: [PATCH 2/6] feat: add Cerebras LLM provider - Add Cerebras provider with 8 models (Llama, GPT OSS, Qwen, ZAI GLM) - Integrate @ai-sdk/cerebras@0.2.16 for compatibility - Add CEREBRAS_API_KEY to environment configuration - Register provider in LLMManager registry Models included: - llama3.1-8b, llama-3.3-70b - gpt-oss-120b (reasoning) - qwen-3-32b, qwen-3-235b variants - zai-glm-4.6, zai-glm-4.7 (reasoning) Co-Authored-By: Claude Sonnet 4.5 --- .env.example | 4 + app/lib/modules/llm/providers/cerebras.ts | 92 ++++ app/lib/modules/llm/registry.ts | 2 + package.json | 1 + pnpm-lock.yaml | 520 ++++++++++-------- ....timestamp-1770328346417-a90f095482a09.mjs | 110 ++++ 6 files changed, 510 insertions(+), 219 deletions(-) create mode 100644 app/lib/modules/llm/providers/cerebras.ts create mode 100644 vite.config.ts.timestamp-1770328346417-a90f095482a09.mjs diff --git a/.env.example b/.env.example index 9bec51ec64..52b95ddd8e 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,10 @@ # Get your API key from: https://console.anthropic.com/ ANTHROPIC_API_KEY=your_anthropic_api_key_here +# Cerebras (High-performance inference) +# Get your API key from: https://cloud.cerebras.ai/settings +CEREBRAS_API_KEY=your_cerebras_api_key_here + # OpenAI GPT models # Get your API key from: https://platform.openai.com/api-keys OPENAI_API_KEY=your_openai_api_key_here diff --git a/app/lib/modules/llm/providers/cerebras.ts b/app/lib/modules/llm/providers/cerebras.ts new file mode 100644 index 0000000000..4e4ca5aaf1 --- /dev/null +++ b/app/lib/modules/llm/providers/cerebras.ts @@ -0,0 +1,92 @@ +import { BaseProvider } from '~/lib/modules/llm/base-provider'; +import type { ModelInfo } from '~/lib/modules/llm/types'; +import type { IProviderSetting } from '~/types/model'; +import type { LanguageModelV1 } from 'ai'; +import { createCerebras } from '@ai-sdk/cerebras'; + +export default class CerebrasProvider extends BaseProvider { + name = 'Cerebras'; + getApiKeyLink = 'https://cloud.cerebras.ai/settings'; + + config = { + apiTokenKey: 'CEREBRAS_API_KEY', + }; + + staticModels: ModelInfo[] = [ + { + name: 'llama3.1-8b', + label: 'Llama 3.1 8B', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'llama-3.3-70b', + label: 'Llama 3.3 70B', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'gpt-oss-120b', + label: 'GPT OSS 120B (Reasoning)', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'qwen-3-32b', + label: 'Qwen 3 32B', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'qwen-3-235b-a22b-instruct-2507', + label: 'Qwen 3 235B A22B Instruct', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'qwen-3-235b-a22b-thinking-2507', + label: 'Qwen 3 235B A22B Thinking', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'zai-glm-4.6', + label: 'ZAI GLM 4.6', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + { + name: 'zai-glm-4.7', + label: 'ZAI GLM 4.7 (Reasoning)', + provider: 'Cerebras', + maxTokenAllowed: 8000, + }, + ]; + + getModelInstance(options: { + model: string; + serverEnv: Env; + apiKeys?: Record; + providerSettings?: Record; + }): LanguageModelV1 { + const { model, serverEnv, apiKeys, providerSettings } = options; + + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: providerSettings?.[this.name], + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'CEREBRAS_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing API key for ${this.name} provider`); + } + + const cerebras = createCerebras({ + apiKey, + }); + + return cerebras(model); + } +} diff --git a/app/lib/modules/llm/registry.ts b/app/lib/modules/llm/registry.ts index a28e4f9f3a..2314b9e103 100644 --- a/app/lib/modules/llm/registry.ts +++ b/app/lib/modules/llm/registry.ts @@ -1,4 +1,5 @@ import AnthropicProvider from './providers/anthropic'; +import CerebrasProvider from './providers/cerebras'; import CohereProvider from './providers/cohere'; import DeepseekProvider from './providers/deepseek'; import GoogleProvider from './providers/google'; @@ -20,6 +21,7 @@ import MoonshotProvider from './providers/moonshot'; export { AnthropicProvider, + CerebrasProvider, CohereProvider, DeepseekProvider, GoogleProvider, diff --git a/package.json b/package.json index b02ba021ec..ae42bc5572 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "dependencies": { "@ai-sdk/amazon-bedrock": "1.0.6", "@ai-sdk/anthropic": "0.0.39", + "@ai-sdk/cerebras": "^0.2.16", "@ai-sdk/cohere": "1.0.3", "@ai-sdk/deepseek": "0.1.3", "@ai-sdk/google": "0.0.52", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4061c9079..f9e869897d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@ai-sdk/anthropic': specifier: 0.0.39 version: 0.0.39(zod@3.25.76) + '@ai-sdk/cerebras': + specifier: ^0.2.16 + version: 0.2.16(zod@3.25.76) '@ai-sdk/cohere': specifier: 1.0.3 version: 1.0.3(zod@3.25.76) @@ -504,6 +507,12 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/cerebras@0.2.16': + resolution: {integrity: sha512-FbT3gFYADXwyjQlpluWxl5fRnkJvGMHX5ahLZZ7qqpDQHH86ZO6X9j9Gk6vcMCwNPpI7+miiK79q1e5wzVHBSQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@ai-sdk/cohere@1.0.3': resolution: {integrity: sha512-SDjPinUcGzTNiSMN+9zs1fuAcP8rU1/+CmDWAGu7eMhwVGDurgiOqscC0Oqs/aLsodLt/sFeOvyqj86DAknpbg==} engines: {node: '>=18'} @@ -534,6 +543,12 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/openai-compatible@0.2.16': + resolution: {integrity: sha512-LkvfcM8slJedRyJa/MiMiaOzcMjV1zNDwzTHEGz7aAsgsQV0maLfmJRi/nuSwf5jmp0EouC+JXXDUj2l94HgQw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@ai-sdk/openai@1.1.2': resolution: {integrity: sha512-9rfcwjl4g1/Bdr2SmgFQr+aw81r62MvIKE7QDHMC4ulFd/Hej2oClROSMpDFZHXzs7RGeb32VkRyCHUWWgN3RQ==} engines: {node: '>=18'} @@ -793,6 +808,10 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.0': resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} @@ -863,6 +882,10 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -928,6 +951,10 @@ packages: resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -1148,14 +1175,14 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.4': - resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1178,14 +1205,14 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.4': - resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1208,14 +1235,14 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.4': - resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1238,14 +1265,14 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.4': - resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1268,14 +1295,14 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.4': - resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1298,14 +1325,14 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.4': - resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1328,14 +1355,14 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.4': - resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1358,14 +1385,14 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.4': - resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1388,14 +1415,14 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.4': - resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1418,14 +1445,14 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.4': - resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1448,14 +1475,14 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.4': - resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1478,14 +1505,14 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.4': - resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1508,14 +1535,14 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.4': - resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1538,14 +1565,14 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.4': - resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1568,14 +1595,14 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.4': - resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1598,14 +1625,14 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.4': - resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1628,26 +1655,26 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.4': - resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.4': - resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1670,14 +1697,14 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.4': - resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1688,14 +1715,14 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.4': - resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1718,20 +1745,20 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.4': - resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1754,14 +1781,14 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.4': - resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1784,14 +1811,14 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.4': - resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1814,14 +1841,14 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.4': - resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1844,14 +1871,14 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.4': - resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -4353,6 +4380,15 @@ packages: supports-color: optional: true + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} @@ -4637,13 +4673,13 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.25.4: - resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} hasBin: true - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} engines: {node: '>=18'} hasBin: true @@ -4834,8 +4870,8 @@ packages: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} - exponential-backoff@3.1.2: - resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} express-rate-limit@7.5.1: resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} @@ -4998,6 +5034,10 @@ packages: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} + fs-extra@11.3.3: + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + engines: {node: '>=14.14'} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -5089,7 +5129,7 @@ packages: glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} @@ -5339,8 +5379,8 @@ packages: inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -5524,9 +5564,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - jsdom@26.1.0: resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} engines: {node: '>=18'} @@ -5586,6 +5623,9 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jspdf@2.5.2: resolution: {integrity: sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==} @@ -5651,6 +5691,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} @@ -7353,8 +7396,8 @@ packages: resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} engines: {node: '>= 10'} - socks@2.8.6: - resolution: {integrity: sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==} + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-js@1.2.1: @@ -8303,6 +8346,13 @@ snapshots: '@ai-sdk/provider-utils': 1.0.9(zod@3.25.76) zod: 3.25.76 + '@ai-sdk/cerebras@0.2.16(zod@3.25.76)': + dependencies: + '@ai-sdk/openai-compatible': 0.2.16(zod@3.25.76) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + '@ai-sdk/cohere@1.0.3(zod@3.25.76)': dependencies: '@ai-sdk/provider': 1.0.1 @@ -8335,6 +8385,12 @@ snapshots: '@ai-sdk/provider-utils': 2.1.2(zod@3.25.76) zod: 3.25.76 + '@ai-sdk/openai-compatible@0.2.16(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + '@ai-sdk/openai@1.1.2(zod@3.25.76)': dependencies: '@ai-sdk/provider': 1.0.6 @@ -8876,6 +8932,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.0': {} '@babel/core@7.28.0': @@ -8982,6 +9044,8 @@ snapshots: '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.27.6': @@ -9050,6 +9114,8 @@ snapshots: '@babel/runtime@7.27.6': {} + '@babel/runtime@7.28.6': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -9319,7 +9385,7 @@ snapshots: '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': dependencies: env-paths: 2.2.1 - exponential-backoff: 3.1.2 + exponential-backoff: 3.1.3 glob: 8.1.0 graceful-fs: 4.2.11 make-fetch-happen: 10.2.1 @@ -9386,8 +9452,8 @@ snapshots: '@electron/windows-sign@1.2.2': dependencies: cross-dirname: 0.1.0 - debug: 4.4.1 - fs-extra: 11.3.0 + debug: 4.4.3 + fs-extra: 11.3.3 minimist: 1.2.8 postject: 1.0.0-alpha.6 transitivePeerDependencies: @@ -9407,10 +9473,10 @@ snapshots: '@esbuild/aix-ppc64@0.23.1': optional: true - '@esbuild/aix-ppc64@0.25.4': + '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.25.8': + '@esbuild/aix-ppc64@0.25.4': optional: true '@esbuild/android-arm64@0.17.6': @@ -9422,10 +9488,10 @@ snapshots: '@esbuild/android-arm64@0.23.1': optional: true - '@esbuild/android-arm64@0.25.4': + '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.25.8': + '@esbuild/android-arm64@0.25.4': optional: true '@esbuild/android-arm@0.17.6': @@ -9437,10 +9503,10 @@ snapshots: '@esbuild/android-arm@0.23.1': optional: true - '@esbuild/android-arm@0.25.4': + '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.25.8': + '@esbuild/android-arm@0.25.4': optional: true '@esbuild/android-x64@0.17.6': @@ -9452,10 +9518,10 @@ snapshots: '@esbuild/android-x64@0.23.1': optional: true - '@esbuild/android-x64@0.25.4': + '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.25.8': + '@esbuild/android-x64@0.25.4': optional: true '@esbuild/darwin-arm64@0.17.6': @@ -9467,10 +9533,10 @@ snapshots: '@esbuild/darwin-arm64@0.23.1': optional: true - '@esbuild/darwin-arm64@0.25.4': + '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.25.8': + '@esbuild/darwin-arm64@0.25.4': optional: true '@esbuild/darwin-x64@0.17.6': @@ -9482,10 +9548,10 @@ snapshots: '@esbuild/darwin-x64@0.23.1': optional: true - '@esbuild/darwin-x64@0.25.4': + '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.25.8': + '@esbuild/darwin-x64@0.25.4': optional: true '@esbuild/freebsd-arm64@0.17.6': @@ -9497,10 +9563,10 @@ snapshots: '@esbuild/freebsd-arm64@0.23.1': optional: true - '@esbuild/freebsd-arm64@0.25.4': + '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.25.8': + '@esbuild/freebsd-arm64@0.25.4': optional: true '@esbuild/freebsd-x64@0.17.6': @@ -9512,10 +9578,10 @@ snapshots: '@esbuild/freebsd-x64@0.23.1': optional: true - '@esbuild/freebsd-x64@0.25.4': + '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.8': + '@esbuild/freebsd-x64@0.25.4': optional: true '@esbuild/linux-arm64@0.17.6': @@ -9527,10 +9593,10 @@ snapshots: '@esbuild/linux-arm64@0.23.1': optional: true - '@esbuild/linux-arm64@0.25.4': + '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.25.8': + '@esbuild/linux-arm64@0.25.4': optional: true '@esbuild/linux-arm@0.17.6': @@ -9542,10 +9608,10 @@ snapshots: '@esbuild/linux-arm@0.23.1': optional: true - '@esbuild/linux-arm@0.25.4': + '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.25.8': + '@esbuild/linux-arm@0.25.4': optional: true '@esbuild/linux-ia32@0.17.6': @@ -9557,10 +9623,10 @@ snapshots: '@esbuild/linux-ia32@0.23.1': optional: true - '@esbuild/linux-ia32@0.25.4': + '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.25.8': + '@esbuild/linux-ia32@0.25.4': optional: true '@esbuild/linux-loong64@0.17.6': @@ -9572,10 +9638,10 @@ snapshots: '@esbuild/linux-loong64@0.23.1': optional: true - '@esbuild/linux-loong64@0.25.4': + '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.25.8': + '@esbuild/linux-loong64@0.25.4': optional: true '@esbuild/linux-mips64el@0.17.6': @@ -9587,10 +9653,10 @@ snapshots: '@esbuild/linux-mips64el@0.23.1': optional: true - '@esbuild/linux-mips64el@0.25.4': + '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.25.8': + '@esbuild/linux-mips64el@0.25.4': optional: true '@esbuild/linux-ppc64@0.17.6': @@ -9602,10 +9668,10 @@ snapshots: '@esbuild/linux-ppc64@0.23.1': optional: true - '@esbuild/linux-ppc64@0.25.4': + '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.25.8': + '@esbuild/linux-ppc64@0.25.4': optional: true '@esbuild/linux-riscv64@0.17.6': @@ -9617,10 +9683,10 @@ snapshots: '@esbuild/linux-riscv64@0.23.1': optional: true - '@esbuild/linux-riscv64@0.25.4': + '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.25.8': + '@esbuild/linux-riscv64@0.25.4': optional: true '@esbuild/linux-s390x@0.17.6': @@ -9632,10 +9698,10 @@ snapshots: '@esbuild/linux-s390x@0.23.1': optional: true - '@esbuild/linux-s390x@0.25.4': + '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.25.8': + '@esbuild/linux-s390x@0.25.4': optional: true '@esbuild/linux-x64@0.17.6': @@ -9647,16 +9713,16 @@ snapshots: '@esbuild/linux-x64@0.23.1': optional: true - '@esbuild/linux-x64@0.25.4': + '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.25.8': + '@esbuild/linux-x64@0.25.4': optional: true - '@esbuild/netbsd-arm64@0.25.4': + '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.25.8': + '@esbuild/netbsd-arm64@0.25.4': optional: true '@esbuild/netbsd-x64@0.17.6': @@ -9668,19 +9734,19 @@ snapshots: '@esbuild/netbsd-x64@0.23.1': optional: true - '@esbuild/netbsd-x64@0.25.4': + '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.25.8': + '@esbuild/netbsd-x64@0.25.4': optional: true '@esbuild/openbsd-arm64@0.23.1': optional: true - '@esbuild/openbsd-arm64@0.25.4': + '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.25.8': + '@esbuild/openbsd-arm64@0.25.4': optional: true '@esbuild/openbsd-x64@0.17.6': @@ -9692,13 +9758,13 @@ snapshots: '@esbuild/openbsd-x64@0.23.1': optional: true - '@esbuild/openbsd-x64@0.25.4': + '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.25.8': + '@esbuild/openbsd-x64@0.25.4': optional: true - '@esbuild/openharmony-arm64@0.25.8': + '@esbuild/openharmony-arm64@0.25.12': optional: true '@esbuild/sunos-x64@0.17.6': @@ -9710,10 +9776,10 @@ snapshots: '@esbuild/sunos-x64@0.23.1': optional: true - '@esbuild/sunos-x64@0.25.4': + '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.25.8': + '@esbuild/sunos-x64@0.25.4': optional: true '@esbuild/win32-arm64@0.17.6': @@ -9725,10 +9791,10 @@ snapshots: '@esbuild/win32-arm64@0.23.1': optional: true - '@esbuild/win32-arm64@0.25.4': + '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.25.8': + '@esbuild/win32-arm64@0.25.4': optional: true '@esbuild/win32-ia32@0.17.6': @@ -9740,10 +9806,10 @@ snapshots: '@esbuild/win32-ia32@0.23.1': optional: true - '@esbuild/win32-ia32@0.25.4': + '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.25.8': + '@esbuild/win32-ia32@0.25.4': optional: true '@esbuild/win32-x64@0.17.6': @@ -9755,10 +9821,10 @@ snapshots: '@esbuild/win32-x64@0.23.1': optional: true - '@esbuild/win32-x64@0.25.4': + '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.25.8': + '@esbuild/win32-x64@0.25.4': optional: true '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@1.21.7))': @@ -11393,8 +11459,8 @@ snapshots: '@testing-library/dom@10.4.0': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.27.6 + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.28.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -12006,7 +12072,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -12802,6 +12868,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.3: + dependencies: + ms: 2.1.3 + decimal.js@10.6.0: {} decode-named-character-reference@1.2.0: @@ -13029,9 +13099,9 @@ snapshots: electron-winstaller@5.4.0: dependencies: '@electron/asar': 3.4.1 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 7.0.1 - lodash: 4.17.21 + lodash: 4.17.23 temp: 0.9.4 optionalDependencies: '@electron/windows-sign': 1.2.2 @@ -13190,6 +13260,35 @@ snapshots: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + esbuild@0.25.4: optionalDependencies: '@esbuild/aix-ppc64': 0.25.4 @@ -13218,35 +13317,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.4 '@esbuild/win32-x64': 0.25.4 - esbuild@0.25.8: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 - escalade@3.2.0: {} escape-html@1.0.3: {} @@ -13460,7 +13530,7 @@ snapshots: expect-type@1.2.2: {} - exponential-backoff@3.1.2: {} + exponential-backoff@3.1.3: {} express-rate-limit@7.5.1(express@5.1.0): dependencies: @@ -13695,6 +13765,13 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fs-extra@11.3.3: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + optional: true + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -14058,7 +14135,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -14079,7 +14156,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -14162,10 +14239,7 @@ snapshots: inline-style-parser@0.2.4: {} - ip-address@9.0.5: - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 + ip-address@10.1.0: {} ipaddr.js@1.9.1: {} @@ -14324,8 +14398,6 @@ snapshots: dependencies: argparse: 2.0.1 - jsbn@1.1.0: {} - jsdom@26.1.0: dependencies: cssstyle: 4.6.0 @@ -14397,6 +14469,13 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + optional: true + jspdf@2.5.2: dependencies: '@babel/runtime': 7.27.6 @@ -14463,6 +14542,8 @@ snapshots: lodash@4.17.21: {} + lodash@4.17.23: {} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 @@ -16680,14 +16761,14 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.1 - socks: 2.8.6 + debug: 4.4.3 + socks: 2.8.7 transitivePeerDependencies: - supports-color - socks@2.8.6: + socks@2.8.7: dependencies: - ip-address: 9.0.5 + ip-address: 10.1.0 smart-buffer: 4.2.0 source-map-js@1.2.1: {} @@ -16721,7 +16802,8 @@ snapshots: spdx-license-ids@3.0.21: {} - sprintf-js@1.1.3: {} + sprintf-js@1.1.3: + optional: true ssri@10.0.6: dependencies: @@ -17006,7 +17088,7 @@ snapshots: tsx@4.20.3: dependencies: - esbuild: 0.25.8 + esbuild: 0.25.12 get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 diff --git a/vite.config.ts.timestamp-1770328346417-a90f095482a09.mjs b/vite.config.ts.timestamp-1770328346417-a90f095482a09.mjs new file mode 100644 index 0000000000..b22de5a82e --- /dev/null +++ b/vite.config.ts.timestamp-1770328346417-a90f095482a09.mjs @@ -0,0 +1,110 @@ +// vite.config.ts +import { cloudflareDevProxyVitePlugin as remixCloudflareDevProxy, vitePlugin as remixVitePlugin } from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/@remix-run+dev@2.16.8_@remix-run+react@2.16.8_react-dom@18.3.1_react@18.3.1__react@18.3.1_typ_uozynh2jdinnz6binv6akofmd4/node_modules/@remix-run/dev/dist/index.js"; +import UnoCSS from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/unocss@0.61.9_postcss@8.5.6_rollup@4.45.1_vite@5.4.19_@types+node@24.1.0_sass-embedded@1.89.2_/node_modules/unocss/dist/vite.mjs"; +import { defineConfig } from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/vite@5.4.19_@types+node@24.1.0_sass-embedded@1.89.2/node_modules/vite/dist/node/index.js"; +import { nodePolyfills } from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/vite-plugin-node-polyfills@0.22.0_rollup@4.45.1_vite@5.4.19_@types+node@24.1.0_sass-embedded@1.89.2_/node_modules/vite-plugin-node-polyfills/dist/index.js"; +import { optimizeCssModules } from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/vite-plugin-optimize-css-modules@1.2.0_vite@5.4.19_@types+node@24.1.0_sass-embedded@1.89.2_/node_modules/vite-plugin-optimize-css-modules/dist/index.mjs"; +import tsconfigPaths from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/vite-tsconfig-paths@4.3.2_typescript@5.8.3_vite@5.4.19_@types+node@24.1.0_sass-embedded@1.89.2_/node_modules/vite-tsconfig-paths/dist/index.mjs"; +import * as dotenv from "file:///Users/stijnus/Documents/GitHub/bolt.diy/node_modules/.pnpm/dotenv@16.6.1/node_modules/dotenv/lib/main.js"; +dotenv.config({ path: ".env.local" }); +dotenv.config({ path: ".env" }); +dotenv.config(); +var vite_config_default = defineConfig((config2) => { + return { + define: { + "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) + }, + build: { + target: "esnext" + }, + plugins: [ + nodePolyfills({ + include: ["buffer", "process", "util", "stream"], + globals: { + Buffer: true, + process: true, + global: true + }, + protocolImports: true, + exclude: ["child_process", "fs", "path"] + }), + { + name: "buffer-polyfill", + transform(code, id) { + if (id.includes("env.mjs")) { + return { + code: `import { Buffer } from 'buffer'; +${code}`, + map: null + }; + } + return null; + } + }, + config2.mode !== "test" && remixCloudflareDevProxy(), + remixVitePlugin({ + future: { + v3_fetcherPersist: true, + v3_relativeSplatPath: true, + v3_throwAbortReason: true, + v3_lazyRouteDiscovery: true + } + }), + UnoCSS(), + tsconfigPaths(), + chrome129IssuePlugin(), + config2.mode === "production" && optimizeCssModules({ apply: "build" }) + ], + envPrefix: [ + "VITE_", + "OPENAI_LIKE_API_BASE_URL", + "OPENAI_LIKE_API_MODELS", + "OLLAMA_API_BASE_URL", + "LMSTUDIO_API_BASE_URL", + "TOGETHER_API_BASE_URL" + ], + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler" + } + } + }, + test: { + exclude: [ + "**/node_modules/**", + "**/dist/**", + "**/cypress/**", + "**/.{idea,git,cache,output,temp}/**", + "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*", + "**/tests/preview/**" + // Exclude preview tests that require Playwright + ] + } + }; +}); +function chrome129IssuePlugin() { + return { + name: "chrome129IssuePlugin", + configureServer(server) { + server.middlewares.use((req, res, next) => { + const raw = req.headers["user-agent"]?.match(/Chrom(e|ium)\/([0-9]+)\./); + if (raw) { + const version = parseInt(raw[2], 10); + if (version === 129) { + res.setHeader("content-type", "text/html"); + res.end( + '

Please use Chrome Canary for testing.

Chrome 129 has an issue with JavaScript modules & Vite local development, see for more information.

Note: This only impacts local development. `pnpm run build` and `pnpm run start` will work fine in this browser.

' + ); + return; + } + } + next(); + }); + } + }; +} +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvc3Rpam51cy9Eb2N1bWVudHMvR2l0SHViL2JvbHQuZGl5XCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvc3Rpam51cy9Eb2N1bWVudHMvR2l0SHViL2JvbHQuZGl5L3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9zdGlqbnVzL0RvY3VtZW50cy9HaXRIdWIvYm9sdC5kaXkvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBjbG91ZGZsYXJlRGV2UHJveHlWaXRlUGx1Z2luIGFzIHJlbWl4Q2xvdWRmbGFyZURldlByb3h5LCB2aXRlUGx1Z2luIGFzIHJlbWl4Vml0ZVBsdWdpbiB9IGZyb20gJ0ByZW1peC1ydW4vZGV2JztcbmltcG9ydCBVbm9DU1MgZnJvbSAndW5vY3NzL3ZpdGUnO1xuaW1wb3J0IHsgZGVmaW5lQ29uZmlnLCB0eXBlIFZpdGVEZXZTZXJ2ZXIgfSBmcm9tICd2aXRlJztcbmltcG9ydCB7IG5vZGVQb2x5ZmlsbHMgfSBmcm9tICd2aXRlLXBsdWdpbi1ub2RlLXBvbHlmaWxscyc7XG5pbXBvcnQgeyBvcHRpbWl6ZUNzc01vZHVsZXMgfSBmcm9tICd2aXRlLXBsdWdpbi1vcHRpbWl6ZS1jc3MtbW9kdWxlcyc7XG5pbXBvcnQgdHNjb25maWdQYXRocyBmcm9tICd2aXRlLXRzY29uZmlnLXBhdGhzJztcbmltcG9ydCAqIGFzIGRvdGVudiBmcm9tICdkb3RlbnYnO1xuXG4vLyBMb2FkIGVudmlyb25tZW50IHZhcmlhYmxlcyBmcm9tIG11bHRpcGxlIGZpbGVzXG5kb3RlbnYuY29uZmlnKHsgcGF0aDogJy5lbnYubG9jYWwnIH0pO1xuZG90ZW52LmNvbmZpZyh7IHBhdGg6ICcuZW52JyB9KTtcbmRvdGVudi5jb25maWcoKTtcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKChjb25maWcpID0+IHtcbiAgcmV0dXJuIHtcbiAgICBkZWZpbmU6IHtcbiAgICAgICdwcm9jZXNzLmVudi5OT0RFX0VOVic6IEpTT04uc3RyaW5naWZ5KHByb2Nlc3MuZW52Lk5PREVfRU5WKSxcbiAgICB9LFxuICAgIGJ1aWxkOiB7XG4gICAgICB0YXJnZXQ6ICdlc25leHQnLFxuICAgIH0sXG4gICAgcGx1Z2luczogW1xuICAgICAgbm9kZVBvbHlmaWxscyh7XG4gICAgICAgIGluY2x1ZGU6IFsnYnVmZmVyJywgJ3Byb2Nlc3MnLCAndXRpbCcsICdzdHJlYW0nXSxcbiAgICAgICAgZ2xvYmFsczoge1xuICAgICAgICAgIEJ1ZmZlcjogdHJ1ZSxcbiAgICAgICAgICBwcm9jZXNzOiB0cnVlLFxuICAgICAgICAgIGdsb2JhbDogdHJ1ZSxcbiAgICAgICAgfSxcbiAgICAgICAgcHJvdG9jb2xJbXBvcnRzOiB0cnVlLFxuICAgICAgICBleGNsdWRlOiBbJ2NoaWxkX3Byb2Nlc3MnLCAnZnMnLCAncGF0aCddLFxuICAgICAgfSksXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdidWZmZXItcG9seWZpbGwnLFxuICAgICAgICB0cmFuc2Zvcm0oY29kZSwgaWQpIHtcbiAgICAgICAgICBpZiAoaWQuaW5jbHVkZXMoJ2Vudi5tanMnKSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgY29kZTogYGltcG9ydCB7IEJ1ZmZlciB9IGZyb20gJ2J1ZmZlcic7XFxuJHtjb2RlfWAsXG4gICAgICAgICAgICAgIG1hcDogbnVsbCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgY29uZmlnLm1vZGUgIT09ICd0ZXN0JyAmJiByZW1peENsb3VkZmxhcmVEZXZQcm94eSgpLFxuICAgICAgcmVtaXhWaXRlUGx1Z2luKHtcbiAgICAgICAgZnV0dXJlOiB7XG4gICAgICAgICAgdjNfZmV0Y2hlclBlcnNpc3Q6IHRydWUsXG4gICAgICAgICAgdjNfcmVsYXRpdmVTcGxhdFBhdGg6IHRydWUsXG4gICAgICAgICAgdjNfdGhyb3dBYm9ydFJlYXNvbjogdHJ1ZSxcbiAgICAgICAgICB2M19sYXp5Um91dGVEaXNjb3Zlcnk6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICAgIFVub0NTUygpLFxuICAgICAgdHNjb25maWdQYXRocygpLFxuICAgICAgY2hyb21lMTI5SXNzdWVQbHVnaW4oKSxcbiAgICAgIGNvbmZpZy5tb2RlID09PSAncHJvZHVjdGlvbicgJiYgb3B0aW1pemVDc3NNb2R1bGVzKHsgYXBwbHk6ICdidWlsZCcgfSksXG4gICAgXSxcbiAgICBlbnZQcmVmaXg6IFtcbiAgICAgICdWSVRFXycsXG4gICAgICAnT1BFTkFJX0xJS0VfQVBJX0JBU0VfVVJMJyxcbiAgICAgICdPUEVOQUlfTElLRV9BUElfTU9ERUxTJyxcbiAgICAgICdPTExBTUFfQVBJX0JBU0VfVVJMJyxcbiAgICAgICdMTVNUVURJT19BUElfQkFTRV9VUkwnLFxuICAgICAgJ1RPR0VUSEVSX0FQSV9CQVNFX1VSTCcsXG4gICAgXSxcbiAgICBjc3M6IHtcbiAgICAgIHByZXByb2Nlc3Nvck9wdGlvbnM6IHtcbiAgICAgICAgc2Nzczoge1xuICAgICAgICAgIGFwaTogJ21vZGVybi1jb21waWxlcicsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0sXG4gICAgdGVzdDoge1xuICAgICAgZXhjbHVkZTogW1xuICAgICAgICAnKiovbm9kZV9tb2R1bGVzLyoqJyxcbiAgICAgICAgJyoqL2Rpc3QvKionLFxuICAgICAgICAnKiovY3lwcmVzcy8qKicsXG4gICAgICAgICcqKi8ue2lkZWEsZ2l0LGNhY2hlLG91dHB1dCx0ZW1wfS8qKicsXG4gICAgICAgICcqKi97a2FybWEscm9sbHVwLHdlYnBhY2ssdml0ZSx2aXRlc3QsamVzdCxhdmEsYmFiZWwsbnljLGN5cHJlc3MsdHN1cCxidWlsZH0uY29uZmlnLionLFxuICAgICAgICAnKiovdGVzdHMvcHJldmlldy8qKicsIC8vIEV4Y2x1ZGUgcHJldmlldyB0ZXN0cyB0aGF0IHJlcXVpcmUgUGxheXdyaWdodFxuICAgICAgXSxcbiAgICB9LFxuICB9O1xufSk7XG5cbmZ1bmN0aW9uIGNocm9tZTEyOUlzc3VlUGx1Z2luKCkge1xuICByZXR1cm4ge1xuICAgIG5hbWU6ICdjaHJvbWUxMjlJc3N1ZVBsdWdpbicsXG4gICAgY29uZmlndXJlU2VydmVyKHNlcnZlcjogVml0ZURldlNlcnZlcikge1xuICAgICAgc2VydmVyLm1pZGRsZXdhcmVzLnVzZSgocmVxLCByZXMsIG5leHQpID0+IHtcbiAgICAgICAgY29uc3QgcmF3ID0gcmVxLmhlYWRlcnNbJ3VzZXItYWdlbnQnXT8ubWF0Y2goL0Nocm9tKGV8aXVtKVxcLyhbMC05XSspXFwuLyk7XG5cbiAgICAgICAgaWYgKHJhdykge1xuICAgICAgICAgIGNvbnN0IHZlcnNpb24gPSBwYXJzZUludChyYXdbMl0sIDEwKTtcblxuICAgICAgICAgIGlmICh2ZXJzaW9uID09PSAxMjkpIHtcbiAgICAgICAgICAgIHJlcy5zZXRIZWFkZXIoJ2NvbnRlbnQtdHlwZScsICd0ZXh0L2h0bWwnKTtcbiAgICAgICAgICAgIHJlcy5lbmQoXG4gICAgICAgICAgICAgICc8Ym9keT48aDE+UGxlYXNlIHVzZSBDaHJvbWUgQ2FuYXJ5IGZvciB0ZXN0aW5nLjwvaDE+PHA+Q2hyb21lIDEyOSBoYXMgYW4gaXNzdWUgd2l0aCBKYXZhU2NyaXB0IG1vZHVsZXMgJiBWaXRlIGxvY2FsIGRldmVsb3BtZW50LCBzZWUgPGEgaHJlZj1cImh0dHBzOi8vZ2l0aHViLmNvbS9zdGFja2JsaXR6L2JvbHQubmV3L2lzc3Vlcy84NiNpc3N1ZWNvbW1lbnQtMjM5NTUxOTI1OFwiPmZvciBtb3JlIGluZm9ybWF0aW9uLjwvYT48L3A+PHA+PGI+Tm90ZTo8L2I+IFRoaXMgb25seSBpbXBhY3RzIDx1PmxvY2FsIGRldmVsb3BtZW50PC91Pi4gYHBucG0gcnVuIGJ1aWxkYCBhbmQgYHBucG0gcnVuIHN0YXJ0YCB3aWxsIHdvcmsgZmluZSBpbiB0aGlzIGJyb3dzZXIuPC9wPjwvYm9keT4nLFxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIG5leHQoKTtcbiAgICAgIH0pO1xuICAgIH0sXG4gIH07XG59Il0sCiAgIm1hcHBpbmdzIjogIjtBQUEwUyxTQUFTLGdDQUFnQyx5QkFBeUIsY0FBYyx1QkFBdUI7QUFDalosT0FBTyxZQUFZO0FBQ25CLFNBQVMsb0JBQXdDO0FBQ2pELFNBQVMscUJBQXFCO0FBQzlCLFNBQVMsMEJBQTBCO0FBQ25DLE9BQU8sbUJBQW1CO0FBQzFCLFlBQVksWUFBWTtBQUdqQixjQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDN0IsY0FBTyxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBQ3ZCLGNBQU87QUFFZCxJQUFPLHNCQUFRLGFBQWEsQ0FBQ0EsWUFBVztBQUN0QyxTQUFPO0FBQUEsSUFDTCxRQUFRO0FBQUEsTUFDTix3QkFBd0IsS0FBSyxVQUFVLFFBQVEsSUFBSSxRQUFRO0FBQUEsSUFDN0Q7QUFBQSxJQUNBLE9BQU87QUFBQSxNQUNMLFFBQVE7QUFBQSxJQUNWO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxjQUFjO0FBQUEsUUFDWixTQUFTLENBQUMsVUFBVSxXQUFXLFFBQVEsUUFBUTtBQUFBLFFBQy9DLFNBQVM7QUFBQSxVQUNQLFFBQVE7QUFBQSxVQUNSLFNBQVM7QUFBQSxVQUNULFFBQVE7QUFBQSxRQUNWO0FBQUEsUUFDQSxpQkFBaUI7QUFBQSxRQUNqQixTQUFTLENBQUMsaUJBQWlCLE1BQU0sTUFBTTtBQUFBLE1BQ3pDLENBQUM7QUFBQSxNQUNEO0FBQUEsUUFDRSxNQUFNO0FBQUEsUUFDTixVQUFVLE1BQU0sSUFBSTtBQUNsQixjQUFJLEdBQUcsU0FBUyxTQUFTLEdBQUc7QUFDMUIsbUJBQU87QUFBQSxjQUNMLE1BQU07QUFBQSxFQUFxQyxJQUFJO0FBQUEsY0FDL0MsS0FBSztBQUFBLFlBQ1A7QUFBQSxVQUNGO0FBRUEsaUJBQU87QUFBQSxRQUNUO0FBQUEsTUFDRjtBQUFBLE1BQ0FBLFFBQU8sU0FBUyxVQUFVLHdCQUF3QjtBQUFBLE1BQ2xELGdCQUFnQjtBQUFBLFFBQ2QsUUFBUTtBQUFBLFVBQ04sbUJBQW1CO0FBQUEsVUFDbkIsc0JBQXNCO0FBQUEsVUFDdEIscUJBQXFCO0FBQUEsVUFDckIsdUJBQXVCO0FBQUEsUUFDekI7QUFBQSxNQUNGLENBQUM7QUFBQSxNQUNELE9BQU87QUFBQSxNQUNQLGNBQWM7QUFBQSxNQUNkLHFCQUFxQjtBQUFBLE1BQ3JCQSxRQUFPLFNBQVMsZ0JBQWdCLG1CQUFtQixFQUFFLE9BQU8sUUFBUSxDQUFDO0FBQUEsSUFDdkU7QUFBQSxJQUNBLFdBQVc7QUFBQSxNQUNUO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxJQUNGO0FBQUEsSUFDQSxLQUFLO0FBQUEsTUFDSCxxQkFBcUI7QUFBQSxRQUNuQixNQUFNO0FBQUEsVUFDSixLQUFLO0FBQUEsUUFDUDtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsSUFDQSxNQUFNO0FBQUEsTUFDSixTQUFTO0FBQUEsUUFDUDtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBLFFBQ0E7QUFBQSxRQUNBO0FBQUE7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDO0FBRUQsU0FBUyx1QkFBdUI7QUFDOUIsU0FBTztBQUFBLElBQ0wsTUFBTTtBQUFBLElBQ04sZ0JBQWdCLFFBQXVCO0FBQ3JDLGFBQU8sWUFBWSxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVM7QUFDekMsY0FBTSxNQUFNLElBQUksUUFBUSxZQUFZLEdBQUcsTUFBTSwwQkFBMEI7QUFFdkUsWUFBSSxLQUFLO0FBQ1AsZ0JBQU0sVUFBVSxTQUFTLElBQUksQ0FBQyxHQUFHLEVBQUU7QUFFbkMsY0FBSSxZQUFZLEtBQUs7QUFDbkIsZ0JBQUksVUFBVSxnQkFBZ0IsV0FBVztBQUN6QyxnQkFBSTtBQUFBLGNBQ0Y7QUFBQSxZQUNGO0FBRUE7QUFBQSxVQUNGO0FBQUEsUUFDRjtBQUVBLGFBQUs7QUFBQSxNQUNQLENBQUM7QUFBQSxJQUNIO0FBQUEsRUFDRjtBQUNGOyIsCiAgIm5hbWVzIjogWyJjb25maWciXQp9Cg== From 62159f00396d6bd1fd08eacd4601dba02062085d Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:41:35 +0100 Subject: [PATCH 3/6] feat: add Fireworks AI LLM provider - Add Fireworks provider with 6 popular models - Integrate @ai-sdk/fireworks@0.2.16 for compatibility - Add FIREWORKS_API_KEY to environment configuration - Register provider in LLMManager registry Models included: - Llama 3.1 variants (405B, 70B, 8B Instruct) - DeepSeek R1 (reasoning model) - Qwen 2.5 72B Instruct - FireFunction V2 Co-Authored-By: Claude Sonnet 4.5 --- .env.example | 4 ++ app/lib/modules/llm/providers/fireworks.ts | 80 ++++++++++++++++++++++ app/lib/modules/llm/registry.ts | 2 + package.json | 1 + pnpm-lock.yaml | 16 +++++ 5 files changed, 103 insertions(+) create mode 100644 app/lib/modules/llm/providers/fireworks.ts diff --git a/.env.example b/.env.example index 52b95ddd8e..150958c3c8 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,10 @@ ANTHROPIC_API_KEY=your_anthropic_api_key_here # Get your API key from: https://cloud.cerebras.ai/settings CEREBRAS_API_KEY=your_cerebras_api_key_here +# Fireworks AI (Fast inference with FireAttention engine) +# Get your API key from: https://fireworks.ai/api-keys +FIREWORKS_API_KEY=your_fireworks_api_key_here + # OpenAI GPT models # Get your API key from: https://platform.openai.com/api-keys OPENAI_API_KEY=your_openai_api_key_here diff --git a/app/lib/modules/llm/providers/fireworks.ts b/app/lib/modules/llm/providers/fireworks.ts new file mode 100644 index 0000000000..827bec06e9 --- /dev/null +++ b/app/lib/modules/llm/providers/fireworks.ts @@ -0,0 +1,80 @@ +import { BaseProvider } from '~/lib/modules/llm/base-provider'; +import type { ModelInfo } from '~/lib/modules/llm/types'; +import type { IProviderSetting } from '~/types/model'; +import type { LanguageModelV1 } from 'ai'; +import { createFireworks } from '@ai-sdk/fireworks'; + +export default class FireworksProvider extends BaseProvider { + name = 'Fireworks'; + getApiKeyLink = 'https://fireworks.ai/api-keys'; + + config = { + apiTokenKey: 'FIREWORKS_API_KEY', + }; + + staticModels: ModelInfo[] = [ + { + name: 'accounts/fireworks/models/llama-v3p1-405b-instruct', + label: 'Llama 3.1 405B Instruct', + provider: 'Fireworks', + maxTokenAllowed: 128000, + }, + { + name: 'accounts/fireworks/models/llama-v3p1-70b-instruct', + label: 'Llama 3.1 70B Instruct', + provider: 'Fireworks', + maxTokenAllowed: 128000, + }, + { + name: 'accounts/fireworks/models/llama-v3p1-8b-instruct', + label: 'Llama 3.1 8B Instruct', + provider: 'Fireworks', + maxTokenAllowed: 128000, + }, + { + name: 'accounts/fireworks/models/deepseek-r1', + label: 'DeepSeek R1 (Reasoning)', + provider: 'Fireworks', + maxTokenAllowed: 64000, + }, + { + name: 'accounts/fireworks/models/qwen2p5-72b-instruct', + label: 'Qwen 2.5 72B Instruct', + provider: 'Fireworks', + maxTokenAllowed: 128000, + }, + { + name: 'accounts/fireworks/models/firefunction-v2', + label: 'FireFunction V2', + provider: 'Fireworks', + maxTokenAllowed: 8000, + }, + ]; + + getModelInstance(options: { + model: string; + serverEnv: Env; + apiKeys?: Record; + providerSettings?: Record; + }): LanguageModelV1 { + const { model, serverEnv, apiKeys, providerSettings } = options; + + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: providerSettings?.[this.name], + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'FIREWORKS_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing API key for ${this.name} provider`); + } + + const fireworks = createFireworks({ + apiKey, + }); + + return fireworks(model); + } +} diff --git a/app/lib/modules/llm/registry.ts b/app/lib/modules/llm/registry.ts index 2314b9e103..039dd8e9a8 100644 --- a/app/lib/modules/llm/registry.ts +++ b/app/lib/modules/llm/registry.ts @@ -2,6 +2,7 @@ import AnthropicProvider from './providers/anthropic'; import CerebrasProvider from './providers/cerebras'; import CohereProvider from './providers/cohere'; import DeepseekProvider from './providers/deepseek'; +import FireworksProvider from './providers/fireworks'; import GoogleProvider from './providers/google'; import GroqProvider from './providers/groq'; import HuggingFaceProvider from './providers/huggingface'; @@ -24,6 +25,7 @@ export { CerebrasProvider, CohereProvider, DeepseekProvider, + FireworksProvider, GoogleProvider, GroqProvider, HuggingFaceProvider, diff --git a/package.json b/package.json index ae42bc5572..5c8c2a977f 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@ai-sdk/cerebras": "^0.2.16", "@ai-sdk/cohere": "1.0.3", "@ai-sdk/deepseek": "0.1.3", + "@ai-sdk/fireworks": "^0.2.16", "@ai-sdk/google": "0.0.52", "@ai-sdk/mistral": "0.0.43", "@ai-sdk/openai": "1.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9e869897d..b397e9814a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@ai-sdk/deepseek': specifier: 0.1.3 version: 0.1.3(zod@3.25.76) + '@ai-sdk/fireworks': + specifier: ^0.2.16 + version: 0.2.16(zod@3.25.76) '@ai-sdk/google': specifier: 0.0.52 version: 0.0.52(zod@3.25.76) @@ -525,6 +528,12 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/fireworks@0.2.16': + resolution: {integrity: sha512-YHUqW9QHMNjEg5vF0cmhnlAQJCMljWVWNAAFmKCPX31Dj4JaoCjOrIInrNEFerFRaO64hEffhlhuC1EmuO2Lyg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@ai-sdk/google@0.0.52': resolution: {integrity: sha512-bfsA/1Ae0SQ6NfLwWKs5SU4MBwlzJjVhK6bTVBicYFjUxg9liK/W76P1Tq/qK9OlrODACz3i1STOIWsFPpIOuQ==} engines: {node: '>=18'} @@ -8366,6 +8375,13 @@ snapshots: '@ai-sdk/provider-utils': 2.1.2(zod@3.25.76) zod: 3.25.76 + '@ai-sdk/fireworks@0.2.16(zod@3.25.76)': + dependencies: + '@ai-sdk/openai-compatible': 0.2.16(zod@3.25.76) + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + '@ai-sdk/google@0.0.52(zod@3.25.76)': dependencies: '@ai-sdk/provider': 0.0.24 From 6c4dc1e05e7b8bed30d13ec19ca02a8c7f76369b Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:56:16 +0100 Subject: [PATCH 4/6] feat: add coding-specific models to existing providers Enhanced providers with state-of-the-art coding models: **DeepSeek Provider:** + DeepSeek V3.2 (integrates thinking + tool-use) + DeepSeek V3.2-Speciale (high-compute variant, beats GPT-5) **Fireworks Provider:** + Qwen3-Coder 480B (262K context, best for coding) + Qwen3-Coder 30B (fast coding specialist) **Cerebras Provider:** + Qwen3-Coder 480B (2000 tokens/sec!) - Removed deprecated models (qwen-3-32b, llama-3.3-70b) Total new models: 4 Total coding models across all providers: 12+ Performance highlights: - Qwen3-Coder: State-of-the-art coding performance - DeepSeek V3.2: Integrates thinking directly into tool-use - ZAI GLM 4.6: 73.8% SWE-bench score - Ultra-fast inference: 2000 tok/s on Cerebras Co-Authored-By: Claude Sonnet 4.5 --- app/lib/modules/llm/providers/cerebras.ts | 18 ++++++------------ app/lib/modules/llm/providers/deepseek.ts | 14 ++++++++++++++ app/lib/modules/llm/providers/fireworks.ts | 12 ++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/app/lib/modules/llm/providers/cerebras.ts b/app/lib/modules/llm/providers/cerebras.ts index 4e4ca5aaf1..82d0853946 100644 --- a/app/lib/modules/llm/providers/cerebras.ts +++ b/app/lib/modules/llm/providers/cerebras.ts @@ -14,14 +14,14 @@ export default class CerebrasProvider extends BaseProvider { staticModels: ModelInfo[] = [ { - name: 'llama3.1-8b', - label: 'Llama 3.1 8B', + name: 'qwen3-coder-480b', + label: 'Qwen3-Coder 480B (2000 tok/s, Best for Coding)', provider: 'Cerebras', - maxTokenAllowed: 8000, + maxTokenAllowed: 262000, }, { - name: 'llama-3.3-70b', - label: 'Llama 3.3 70B', + name: 'llama3.1-8b', + label: 'Llama 3.1 8B', provider: 'Cerebras', maxTokenAllowed: 8000, }, @@ -31,12 +31,6 @@ export default class CerebrasProvider extends BaseProvider { provider: 'Cerebras', maxTokenAllowed: 8000, }, - { - name: 'qwen-3-32b', - label: 'Qwen 3 32B', - provider: 'Cerebras', - maxTokenAllowed: 8000, - }, { name: 'qwen-3-235b-a22b-instruct-2507', label: 'Qwen 3 235B A22B Instruct', @@ -51,7 +45,7 @@ export default class CerebrasProvider extends BaseProvider { }, { name: 'zai-glm-4.6', - label: 'ZAI GLM 4.6', + label: 'ZAI GLM 4.6 (Coding: 73.8% SWE-bench)', provider: 'Cerebras', maxTokenAllowed: 8000, }, diff --git a/app/lib/modules/llm/providers/deepseek.ts b/app/lib/modules/llm/providers/deepseek.ts index 7c9042d7cd..2230fafdc4 100644 --- a/app/lib/modules/llm/providers/deepseek.ts +++ b/app/lib/modules/llm/providers/deepseek.ts @@ -34,6 +34,20 @@ export default class DeepseekProvider extends BaseProvider { maxTokenAllowed: 8000, maxCompletionTokens: 8192, }, + { + name: 'deepseek-v3.2', + label: 'DeepSeek V3.2 (Coding + Tool Use)', + provider: 'Deepseek', + maxTokenAllowed: 64000, + maxCompletionTokens: 8192, + }, + { + name: 'deepseek-v3.2-speciale', + label: 'DeepSeek V3.2 Speciale (High-Compute)', + provider: 'Deepseek', + maxTokenAllowed: 64000, + maxCompletionTokens: 8192, + }, ]; getModelInstance(options: { diff --git a/app/lib/modules/llm/providers/fireworks.ts b/app/lib/modules/llm/providers/fireworks.ts index 827bec06e9..6fda592588 100644 --- a/app/lib/modules/llm/providers/fireworks.ts +++ b/app/lib/modules/llm/providers/fireworks.ts @@ -13,6 +13,18 @@ export default class FireworksProvider extends BaseProvider { }; staticModels: ModelInfo[] = [ + { + name: 'accounts/fireworks/models/qwen3-coder-480b-a35b-instruct', + label: 'Qwen3-Coder 480B (Best for Coding)', + provider: 'Fireworks', + maxTokenAllowed: 262000, + }, + { + name: 'accounts/fireworks/models/qwen3-coder-30b-a3b-instruct', + label: 'Qwen3-Coder 30B (Fast Coding)', + provider: 'Fireworks', + maxTokenAllowed: 262000, + }, { name: 'accounts/fireworks/models/llama-v3p1-405b-instruct', label: 'Llama 3.1 405B Instruct', From e045ae08acac410b10b3ff1b1869249b5186598a Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Fri, 6 Feb 2026 00:02:19 +0100 Subject: [PATCH 5/6] feat: add dynamic model discovery to providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented getDynamicModels() for automatic model discovery: **DeepSeek Provider:** - Fetches models from https://api.deepseek.com/models - Automatically discovers new models as DeepSeek adds them - Filters out static models to avoid duplicates **Cerebras Provider:** - Fetches models from https://api.cerebras.ai/v1/models - Auto-discovers new Cerebras models - Keeps UI up-to-date with latest offerings **Fireworks Provider:** - Fetches from https://api.fireworks.ai/v1/accounts/fireworks/models - Includes context_length from API response - Discovers new Qwen-Coder and other models automatically **Moonshot Provider:** - Fetches from https://api.moonshot.ai/v1/models - OpenAI-compatible endpoint - Auto-discovers new Kimi models Benefits: - ✅ No manual updates needed when providers add new models - ✅ Users always have access to latest models - ✅ Graceful fallback to static models if API fails - ✅ 5-second timeout prevents hanging - ✅ Caching system built into BaseProvider Technical details: - Uses BaseProvider's built-in caching system - Cache invalidates when API keys change - Failed API calls fallback to static models - All endpoints have 5-second timeout protection Co-Authored-By: Claude Sonnet 4.5 --- app/lib/modules/llm/providers/cerebras.ts | 51 ++++++++++++++++++++ app/lib/modules/llm/providers/deepseek.ts | 52 ++++++++++++++++++++ app/lib/modules/llm/providers/fireworks.ts | 55 ++++++++++++++++++++++ app/lib/modules/llm/providers/moonshot.ts | 51 ++++++++++++++++++++ 4 files changed, 209 insertions(+) diff --git a/app/lib/modules/llm/providers/cerebras.ts b/app/lib/modules/llm/providers/cerebras.ts index 82d0853946..3ac15d0eab 100644 --- a/app/lib/modules/llm/providers/cerebras.ts +++ b/app/lib/modules/llm/providers/cerebras.ts @@ -57,6 +57,57 @@ export default class CerebrasProvider extends BaseProvider { }, ]; + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'CEREBRAS_API_KEY', + }); + + if (!apiKey) { + return []; + } + + try { + const response = await fetch('https://api.cerebras.ai/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + signal: this.createTimeoutSignal(5000), + }); + + if (!response.ok) { + console.error(`Cerebras API error: ${response.statusText}`); + return []; + } + + const data = (await response.json()) as any; + const staticModelIds = this.staticModels.map((m) => m.name); + + // Filter out models we already have in staticModels + const dynamicModels = + data.data + ?.filter((model: any) => !staticModelIds.includes(model.id)) + .map((m: any) => ({ + name: m.id, + label: `${m.id} (Dynamic)`, + provider: this.name, + maxTokenAllowed: 32000, // Default, Cerebras typically has good context + })) || []; + + return dynamicModels; + } catch (error) { + console.error(`Failed to fetch Cerebras models:`, error); + return []; + } + } + getModelInstance(options: { model: string; serverEnv: Env; diff --git a/app/lib/modules/llm/providers/deepseek.ts b/app/lib/modules/llm/providers/deepseek.ts index 2230fafdc4..2acc4fda44 100644 --- a/app/lib/modules/llm/providers/deepseek.ts +++ b/app/lib/modules/llm/providers/deepseek.ts @@ -50,6 +50,58 @@ export default class DeepseekProvider extends BaseProvider { }, ]; + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'DEEPSEEK_API_KEY', + }); + + if (!apiKey) { + return []; + } + + try { + const response = await fetch('https://api.deepseek.com/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + signal: this.createTimeoutSignal(5000), + }); + + if (!response.ok) { + console.error(`DeepSeek API error: ${response.statusText}`); + return []; + } + + const data = (await response.json()) as any; + const staticModelIds = this.staticModels.map((m) => m.name); + + // Filter out models we already have in staticModels + const dynamicModels = + data.data + ?.filter((model: any) => !staticModelIds.includes(model.id)) + .map((m: any) => ({ + name: m.id, + label: `${m.id} (Dynamic)`, + provider: this.name, + maxTokenAllowed: 64000, // Default, adjust per model if available + maxCompletionTokens: 8192, + })) || []; + + return dynamicModels; + } catch (error) { + console.error(`Failed to fetch DeepSeek models:`, error); + return []; + } + } + getModelInstance(options: { model: string; serverEnv: Env; diff --git a/app/lib/modules/llm/providers/fireworks.ts b/app/lib/modules/llm/providers/fireworks.ts index 6fda592588..7dfa8bdf80 100644 --- a/app/lib/modules/llm/providers/fireworks.ts +++ b/app/lib/modules/llm/providers/fireworks.ts @@ -63,6 +63,61 @@ export default class FireworksProvider extends BaseProvider { }, ]; + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'FIREWORKS_API_KEY', + }); + + if (!apiKey) { + return []; + } + + try { + // Try the accounts/fireworks/models endpoint which lists public models + const response = await fetch('https://api.fireworks.ai/v1/accounts/fireworks/models?page_size=100', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + signal: this.createTimeoutSignal(5000), + }); + + if (!response.ok) { + console.error(`Fireworks API error: ${response.statusText}`); + return []; + } + + const data = (await response.json()) as any; + const staticModelIds = this.staticModels.map((m) => m.name); + + // Filter out models we already have in staticModels + const dynamicModels = + data.data + ?.filter((model: any) => { + const modelPath = `accounts/fireworks/models/${model.id}`; + return !staticModelIds.includes(modelPath) && !staticModelIds.includes(model.id); + }) + .map((m: any) => ({ + name: `accounts/fireworks/models/${m.id}`, + label: `${m.id} (Dynamic)`, + provider: this.name, + maxTokenAllowed: m.context_length || 128000, + })) || []; + + return dynamicModels; + } catch (error) { + console.error(`Failed to fetch Fireworks models:`, error); + return []; + } + } + getModelInstance(options: { model: string; serverEnv: Env; diff --git a/app/lib/modules/llm/providers/moonshot.ts b/app/lib/modules/llm/providers/moonshot.ts index a59f80e225..2417d43e0b 100644 --- a/app/lib/modules/llm/providers/moonshot.ts +++ b/app/lib/modules/llm/providers/moonshot.ts @@ -41,6 +41,57 @@ export default class MoonshotProvider extends BaseProvider { { name: 'kimi-thinking-preview', label: 'Kimi Thinking', provider: 'Moonshot', maxTokenAllowed: 128000 }, ]; + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'MOONSHOT_API_KEY', + }); + + if (!apiKey) { + return []; + } + + try { + const response = await fetch('https://api.moonshot.ai/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + signal: this.createTimeoutSignal(5000), + }); + + if (!response.ok) { + console.error(`Moonshot API error: ${response.statusText}`); + return []; + } + + const data = (await response.json()) as any; + const staticModelIds = this.staticModels.map((m) => m.name); + + // Filter out models we already have in staticModels + const dynamicModels = + data.data + ?.filter((model: any) => !staticModelIds.includes(model.id)) + .map((m: any) => ({ + name: m.id, + label: `${m.id} (Dynamic)`, + provider: this.name, + maxTokenAllowed: 128000, // Kimi models typically have large context + })) || []; + + return dynamicModels; + } catch (error) { + console.error(`Failed to fetch Moonshot models:`, error); + return []; + } + } + getModelInstance(options: { model: string; serverEnv: Env; From dbcd7742b8ef8913e2989d79ab63025505e3f62e Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Fri, 6 Feb 2026 00:07:52 +0100 Subject: [PATCH 6/6] feat: add Z.AI provider with GLM models and JWT authentication Merged changes from PR #2069 to add Z.AI provider: - Added GLM-4.6 (200K), GLM-4.5 (128K), and GLM-4.5 Flash models - Implemented secure JWT token generation with HMAC-SHA256 signing - Added dynamic model discovery from Z.AI API - Included proper error handling and token validation - GLM-4.6 achieves 73.8% on SWE-bench coding benchmarks Co-Authored-By: Claude Sonnet 4.5 --- .env.example | 4 + app/lib/modules/llm/providers/z-ai.ts | 193 ++++++++++++++++++++++++++ app/lib/modules/llm/registry.ts | 2 + 3 files changed, 199 insertions(+) create mode 100644 app/lib/modules/llm/providers/z-ai.ts diff --git a/.env.example b/.env.example index 150958c3c8..b724838845 100644 --- a/.env.example +++ b/.env.example @@ -67,6 +67,10 @@ XAI_API_KEY=your_xai_api_key_here # Get your API key from: https://platform.moonshot.ai/console/api-keys MOONSHOT_API_KEY=your_moonshot_api_key_here +# Z.AI (GLM models with JWT authentication) +# Get your API key from: https://open.bigmodel.cn/usercenter/apikeys +ZAI_API_KEY=your_zai_api_key_here + # Hugging Face # Get your API key from: https://huggingface.co/settings/tokens HuggingFace_API_KEY=your_huggingface_api_key_here diff --git a/app/lib/modules/llm/providers/z-ai.ts b/app/lib/modules/llm/providers/z-ai.ts new file mode 100644 index 0000000000..d400082015 --- /dev/null +++ b/app/lib/modules/llm/providers/z-ai.ts @@ -0,0 +1,193 @@ +import { BaseProvider } from '~/lib/modules/llm/base-provider'; +import type { IProviderSetting } from '~/types/model'; +import type { LanguageModelV1 } from 'ai'; +import type { ModelInfo } from '~/lib/modules/llm/types'; +import { createOpenAI } from '@ai-sdk/openai'; +import crypto from 'node:crypto'; + +export default class ZaiProvider extends BaseProvider { + name = 'Z.ai'; + getApiKeyLink = 'https://open.bigmodel.cn/usercenter/apikeys'; + + config = { + baseUrlKey: 'ZAI_BASE_URL', + apiTokenKey: 'ZAI_API_KEY', + baseUrl: 'https://api.z.ai/api/coding/paas/v4', //Dedicated endpoint for Coding Plan + }; + + staticModels: ModelInfo[] = [ + { + name: 'glm-4.6', + label: 'GLM-4.6 (200K)', + provider: 'Z.ai', + maxTokenAllowed: 200000, + maxCompletionTokens: 65536, + }, + { + name: 'glm-4.5', + label: 'GLM-4.5 (128K)', + provider: 'Z.ai', + maxTokenAllowed: 128000, + maxCompletionTokens: 65536, + }, + { + name: 'glm-4.5-flash', + label: 'GLM-4.5 Flash (128K)', + provider: 'Z.ai', + maxTokenAllowed: 128000, + maxCompletionTokens: 65536, + }, + ]; + + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: 'ZAI_BASE_URL', + defaultApiTokenKey: 'ZAI_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing Api Key configuration for ${this.name} provider`); + } + + const token = this._generateToken(apiKey); + + if (!this._isValidToken(token)) { + throw new Error(`Invalid API key format for ${this.name} provider`); + } + + try { + const response = await fetch(`${baseUrl}/models`, { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`); + } + + const res = (await response.json()) as any; + const staticModelIds = this.staticModels.map((m) => m.name); + + // Filter out static models and only include GLM models + const data = + res.data?.filter( + (model: any) => + model.object === 'model' && model.id?.startsWith('glm-') && !staticModelIds.includes(model.id), + ) || []; + + return data.map((m: any) => { + let contextWindow = 128000; + let maxCompletionTokens = 65536; + + if (m.id?.includes('glm-4.6')) { + contextWindow = 200000; + maxCompletionTokens = 65536; + } else if (m.id?.includes('glm-4.5')) { + contextWindow = 128000; + maxCompletionTokens = 65536; + } else if (m.id?.includes('glm-4')) { + contextWindow = 128000; + maxCompletionTokens = 8192; + } else if (m.id?.includes('glm-3')) { + contextWindow = 32000; + maxCompletionTokens = 4096; + } + + return { + name: m.id, + label: `${m.id} (${Math.floor(contextWindow / 1000)}k context)`, + provider: this.name, + maxTokenAllowed: contextWindow, + maxCompletionTokens, + }; + }); + } catch (error) { + console.error(`Failed to fetch dynamic models for ${this.name}:`, error); + return []; + } + } + + private _generateToken(apiKey: string): string { + try { + const [id, secret] = apiKey.split('.'); + + if (!id || !secret) { + throw new Error(`Invalid API key format for ${this.name}. Expected: id.secret`); + } + + const now = Date.now(); + const payload = { + apiKey: id, + exp: now + 3600 * 1000, + timestamp: now, + }; + + const header = { alg: 'HS256', sign_type: 'SIGN' }; + + const base64Url = (obj: any) => + Buffer.from(JSON.stringify(obj)).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); + const signature = crypto + .createHmac('sha256', secret) + .update(base64Url(header) + '.' + base64Url(payload)) + .digest('base64') + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); + + return `${base64Url(header)}.${base64Url(payload)}.${signature}`; + } catch (error) { + console.error(`Failed to generate JWT token for ${this.name}:`, error); + throw new Error(`Failed to generate JWT token: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + /** + * Validates JWT token format + */ + private _isValidToken(token: string): boolean { + try { + const parts = token.split('.'); + return parts.length === 3 && parts.every((part) => part.length > 0); + } catch { + return false; + } + } + + getModelInstance(options: { + model: string; + serverEnv: Env; + apiKeys?: Record; + providerSettings?: Record; + }): LanguageModelV1 { + const { model, serverEnv, apiKeys, providerSettings } = options; + + const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: providerSettings?.[this.name], + serverEnv: serverEnv as any, + defaultBaseUrlKey: 'ZAI_BASE_URL', + defaultApiTokenKey: 'ZAI_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing API key for ${this.name} provider`); + } + + const token = this._generateToken(apiKey); + const zaiClient = createOpenAI({ + baseURL: baseUrl, + apiKey: token, + }); + + return zaiClient(model); + } +} diff --git a/app/lib/modules/llm/registry.ts b/app/lib/modules/llm/registry.ts index 039dd8e9a8..01bbe81140 100644 --- a/app/lib/modules/llm/registry.ts +++ b/app/lib/modules/llm/registry.ts @@ -19,6 +19,7 @@ import HyperbolicProvider from './providers/hyperbolic'; import AmazonBedrockProvider from './providers/amazon-bedrock'; import GithubProvider from './providers/github'; import MoonshotProvider from './providers/moonshot'; +import ZaiProvider from './providers/z-ai'; export { AnthropicProvider, @@ -42,4 +43,5 @@ export { LMStudioProvider, AmazonBedrockProvider, GithubProvider, + ZaiProvider, };