From f2df793afa95a59aee9e2c9d310999fc918f60da Mon Sep 17 00:00:00 2001 From: LIlGG <1103069291@qq.com> Date: Sun, 28 Sep 2025 17:34:47 +0800 Subject: [PATCH] pref: simplified AI provider's settings --- .env.example | 172 +++++------------- Dockerfile | 2 +- README.md | 10 +- ...ription.tsx => ChatDescription.client.tsx} | 0 app/components/header/Header.tsx | 2 +- ...HistoryItem.tsx => HistoryItem.client.tsx} | 0 app/components/sidebar/Menu.client.tsx | 2 +- app/lib/.server/auth.ts | 2 +- app/lib/.server/llm/stream-enhancer.ts | 2 +- app/lib/modules/constants.ts | 17 ++ app/lib/modules/llm/base-provider.ts | 49 +---- .../llm/{manager.ts => manager.server.ts} | 137 +++++--------- .../modules/llm/providers/amazon-bedrock.ts | 19 +- app/lib/modules/llm/providers/anthropic.ts | 39 ++-- app/lib/modules/llm/providers/cohere.ts | 19 +- app/lib/modules/llm/providers/deepseek.ts | 16 +- app/lib/modules/llm/providers/github.ts | 19 +- app/lib/modules/llm/providers/google.ts | 28 +-- app/lib/modules/llm/providers/groq.ts | 28 +-- app/lib/modules/llm/providers/huggingface.ts | 19 +- app/lib/modules/llm/providers/hyperbolic.ts | 28 +-- app/lib/modules/llm/providers/lmstudio.ts | 65 +++---- app/lib/modules/llm/providers/mistral.ts | 19 +- app/lib/modules/llm/providers/ollama.ts | 61 +++---- app/lib/modules/llm/providers/open-router.ts | 25 +-- app/lib/modules/llm/providers/openai-like.ts | 29 +-- app/lib/modules/llm/providers/openai.ts | 28 +-- app/lib/modules/llm/providers/perplexity.ts | 19 +- app/lib/modules/llm/providers/together.ts | 29 +-- app/lib/modules/llm/providers/xai.ts | 19 +- app/lib/modules/llm/types.ts | 1 - app/lib/persistence/editor.ts | 2 +- app/routes/api.chat/chat.server.ts | 2 +- app/routes/api.enhancer/route.tsx | 2 +- app/types/model.ts | 1 + app/utils/constants.ts | 29 --- docker-compose-dev.yaml | 31 +--- docker-compose-prod.yaml | 34 +--- docs/index.md | 14 -- 39 files changed, 265 insertions(+), 755 deletions(-) rename app/components/header/{ChatDescription.tsx => ChatDescription.client.tsx} (100%) rename app/components/sidebar/{HistoryItem.tsx => HistoryItem.client.tsx} (100%) create mode 100644 app/lib/modules/constants.ts rename app/lib/modules/llm/{manager.ts => manager.server.ts} (66%) delete mode 100644 docs/index.md diff --git a/.env.example b/.env.example index a0e3161..14554e3 100644 --- a/.env.example +++ b/.env.example @@ -1,103 +1,15 @@ # Rename this file to .env once you have filled in the below environment variables! - -# Get your GROQ API Key here - -# https://console.groq.com/keys -# You only need this environment variable set if you want to use Groq models -GROQ_API_KEY= - -# Get your HuggingFace API Key here - -# https://huggingface.co/settings/tokens -# You only need this environment variable set if you want to use HuggingFace models -HuggingFace_API_KEY= - - -# Get your Open AI API Key by following these instructions - -# https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key -# You only need this environment variable set if you want to use GPT models -OPENAI_API_KEY= - -# Get your Anthropic API Key in your account settings - -# https://console.anthropic.com/settings/keys -# You only need this environment variable set if you want to use Claude models -ANTHROPIC_API_KEY= - -# Get your OpenRouter API Key in your account settings - -# https://openrouter.ai/settings/keys -# You only need this environment variable set if you want to use OpenRouter models -OPEN_ROUTER_API_KEY= - -# Get your Google Generative AI API Key by following these instructions - -# https://console.cloud.google.com/apis/credentials -# You only need this environment variable set if you want to use Google Generative AI models -GOOGLE_GENERATIVE_AI_API_KEY= - -# You only need this environment variable set if you want to use oLLAMA models -# DONT USE http://localhost:11434 due to IPV6 issues -# USE EXAMPLE http://127.0.0.1:11434 -OLLAMA_API_BASE_URL= - -# You only need this environment variable set if you want to use OpenAI Like models -OPENAI_LIKE_API_BASE_URL= - -# You only need this environment variable set if you want to use Together AI models -TOGETHER_API_BASE_URL= - -# You only need this environment variable set if you want to use DeepSeek models through their API -DEEPSEEK_API_KEY= - -# Get your OpenAI Like API Key -OPENAI_LIKE_API_KEY= - -# Get your Together API Key -TOGETHER_API_KEY= - -# You only need this environment variable set if you want to use Hyperbolic models -#Get your Hyperbolics API Key at https://app.hyperbolic.xyz/settings -#baseURL="https://api.hyperbolic.xyz/v1/chat/completions" -HYPERBOLIC_API_KEY= -HYPERBOLIC_API_BASE_URL= - -# Get your Mistral API Key by following these instructions - -# https://console.mistral.ai/api-keys/ -# You only need this environment variable set if you want to use Mistral models -MISTRAL_API_KEY= - -# Get the Cohere Api key by following these instructions - -# https://dashboard.cohere.com/api-keys -# You only need this environment variable set if you want to use Cohere models -COHERE_API_KEY= - -# Get LMStudio Base URL from LM Studio Developer Console -# Make sure to enable CORS -# DONT USE http://localhost:1234 due to IPV6 issues -# Example: http://127.0.0.1:1234 -LMSTUDIO_API_BASE_URL= - -# Get your xAI API key -# https://x.ai/api -# You only need this environment variable set if you want to use xAI models -XAI_API_KEY= - -# Get your Perplexity API Key here - -# https://www.perplexity.ai/settings/api -# You only need this environment variable set if you want to use Perplexity models -PERPLEXITY_API_KEY= - -# Get your AWS configuration -# https://console.aws.amazon.com/iam/home -# The JSON should include the following keys: -# - region: The AWS region where Bedrock is available. -# - accessKeyId: Your AWS access key ID. -# - secretAccessKey: Your AWS secret access key. -# - sessionToken (optional): Temporary session token if using an IAM role or temporary credentials. -# Example JSON: -# {"region": "us-east-1", "accessKeyId": "yourAccessKeyId", "secretAccessKey": "yourSecretAccessKey", "sessionToken": "yourSessionToken"} -AWS_BEDROCK_CONFIG= - -# 是否开启文件日志 -USAGE_LOG_FILE=false +# Whether to enable file logging +USAGE_LOG_FILE=true # Include this environment variable if you want more logging for debugging locally LOG_LEVEL=debug +# Operating environment, different from NODE_ENV. NODE_ENV is determined at build time, while this variable is used for enabling certain features in different environments +# development | production | test +OPERATING_ENV=production +# Resource file storage location +STORAGE_DIR=/public/uploads +# Maximum upload size for attachments +MAX_UPLOAD_SIZE_MB=5 # Example Context Values for qwen2.5-coder:32b # @@ -107,45 +19,49 @@ LOG_LEVEL=debug # DEFAULT_NUM_CTX=6144 # Consumes 24GB of VRAM DEFAULT_NUM_CTX= +# LLM Configuration Options +# Enabled model providers, currently supporting Anthropic, Cohere, Deepseek, Google, Groq, HuggingFace, Hyperbolic, Mistral, Ollama, OpenAI, OpenRouter, OpenAILike, Perplexity, xAI, Together, LMStudio, AmazonBedrock, Github +LLM_PROVIDER= + +# BASE URL of the current model provider, some providers require this to be set, such as OpenAILike, Ollama, LMStudio +# DONT USE http://localhost:11434 due to IPV6 issues +# USE EXAMPLE http://127.0.0.1:11434 +PROVIDER_BASE_URL= + +# API KEY of the current provider, used to request the model API. Some providers do not require this to be set. +# Specifically, if the model provider is AmazonBedrock, this should be a JSON string, reference: +# https://console.aws.amazon.com/iam/home +# The JSON should include the following keys: +# - region: The AWS region where Bedrock is available. +# - accessKeyId: Your AWS access key ID. +# - secretAccessKey: Your AWS secret access key. +# - sessionToken (optional): Temporary session token if using an IAM role or temporary credentials. +# Example JSON: +# {"region": "us-east-1", "accessKeyId": "yourAccessKeyId", "secretAccessKey": "yourSecretAccessKey", "sessionToken": "yourSessionToken"} +PROVIDER_API_KEY= + +# MODEL used for page generation (should correspond to LLM_DEFAULT_PROVIDER) +LLM_DEFAULT_MODEL= + +# MODEL used for auxiliary page generation, such as summarization and pre-analysis. (should correspond to LLM_DEFAULT_PROVIDER) +LLM_MINOR_MODEL= + # Get your Serper API Key https://serper.dev/ SERPER_API_KEY= # Get your Weather API Key https://www.weatherapi.com/my/ WEATHER_API_KEY= -# LLM Configuration Options - -# Default LLM provider to use (e.g.,OpenAILike,OpenAI, Anthropic, Mistral) -LLM_DEFAULT_PROVIDER= - -# 生成页面所使用的 MODEL(应该与 LLM_DEFAULT_PROVIDER 相对应) -LLM_DEFAULT_MODEL= - -# 用于辅助页面生成所使用的 MODEL,例如总结和预分析。(应该与 LLM_DEFAULT_PROVIDER 相对应) -LLM_MINOR_MODEL= - -# Comma-separated list of enabled providers (empty means all providers) -# Example: OpenAILike,OpenAI,Anthropic,Mistral -LLM_ENABLED_PROVIDERS= - -# Logto 集成所需环境变量 -# Logto 地址 +# Environment variables required for Logto integration +# Logto endpoint LOGTO_ENDPOINT= -# Logto 应用 ID +# Logto application ID LOGTO_APP_ID= -# Logto 应用密钥 +# Logto application secret LOGTO_APP_SECRET= -# 应用基础 URL,根据实际部署环境修改 +# Application base URL, modify according to actual deployment environment LOGTO_BASE_URL=http://localhost:5173 -# 随机任意的 36 位字符串,用于加密 Logto 的 cookie。 +# Random 36-character string, used to encrypt Logto cookies. LOGTO_COOKIE_SECRET= -# 是否在开发环境中启用 Logto 认证,设置为 false 则在开发环境不强制认证 -LOGTO_ENABLE_DEV=false -# 运行环境,与 NODE_ENV 有所不同, NODE_ENV 在打包时就已确定,而此变量用于某些功能在不同环境下的开放 -# development | production | test -OPERATING_ENV=production - -# 资源文件存储位置 -STORAGE_DIR=/public/uploads -# 附件上传的最大大小 -MAX_UPLOAD_SIZE_MB=5 +# Whether to enable Logto authentication in development environment, set to false to not enforce authentication in development +LOGTO_ENABLE=false diff --git a/Dockerfile b/Dockerfile index 6ffff48..965db08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ FROM node:20.18.0-alpine AS runtime WORKDIR /app ENV NODE_ENV=production -ENV LOGTO_ENABLE_DEV=false +ENV LOGTO_ENABLE=false ENV PORT=3000 ENV HOST=0.0.0.0 diff --git a/README.md b/README.md index b734474..8b44a29 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ docker run -d \ --name upage \ --restart unless-stopped \ -p 3000:3000 \ - -e LLM_DEFAULT_PROVIDER=OpenAILike \ - -e OPENAI_LIKE_API_KEY=your-openai-like-api-key \ + -e LLM_PROVIDER=OpenAILike \ + -e PROVIDER_BASE_URL=your-provider-base-url \ + -e PROVIDER_API_KEY=your-openai-like-api-key \ -e LLM_DEFAULT_MODEL=your-default-model \ -e LLM_MINOR_MODEL=your-minor-model \ -v ./data:/app/data \ @@ -37,8 +38,9 @@ docker run -d \ ``` 其中参数说明如下: -- `-e LLM_DEFAULT_PROVIDER=OpenAILike`:设置默认的 LLM 提供商为 OpenAILike,即兼容 OpenAI 的 API 接口。 -- `-e OPENAI_LIKE_API_KEY=your-openai-like-api-key`:设置 OpenAILike 的 API 密钥。 +- `-e LLM_PROVIDER=OpenAILike`:设置默认的 LLM 提供商为 OpenAILike,即兼容 OpenAI 的 API 接口。 +- `-e PROVIDER_BASE_URL=your-provider-base-url`:设置 LLM 提供商的 API 基础 URL,部分提供商需要设置此项,例如 OpenAILike, Ollama, LMStudio。 +- `-e PROVIDER_API_KEY=your-openai-like-api-key`:设置 LLM 提供商的 API 密钥,大部分提供商需要设置此项。 - `-e LLM_DEFAULT_MODEL=your-default-model`:设置默认的 LLM 模型,用于构建页面。 - `-e LLM_MINOR_MODEL=your-minor-model`:设置次要的 LLM 模型,用于执行其他任务。 - `-v ./data:/app/data`:挂载数据目录 diff --git a/app/components/header/ChatDescription.tsx b/app/components/header/ChatDescription.client.tsx similarity index 100% rename from app/components/header/ChatDescription.tsx rename to app/components/header/ChatDescription.client.tsx diff --git a/app/components/header/Header.tsx b/app/components/header/Header.tsx index f28d57f..eba8f8a 100644 --- a/app/components/header/Header.tsx +++ b/app/components/header/Header.tsx @@ -5,7 +5,7 @@ import { useAuth } from '~/lib/hooks'; import { aiState } from '~/lib/stores/ai-state'; import { HistorySwitch } from '../sidebar/HistorySwitch'; import { ThemeSwitch } from '../ui/ThemeSwitch'; -import { ChatDescription } from './ChatDescription'; +import { ChatDescription } from './ChatDescription.client'; import { HeaderActionButtons } from './HeaderActionButtons'; import { MinimalAvatarDropdown } from './MinimalAvatarDropdown'; diff --git a/app/components/sidebar/HistoryItem.tsx b/app/components/sidebar/HistoryItem.client.tsx similarity index 100% rename from app/components/sidebar/HistoryItem.tsx rename to app/components/sidebar/HistoryItem.client.tsx diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index e47fbea..08ffc34 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -14,7 +14,7 @@ import { sidebarStore } from '~/lib/stores/sidebar'; import { cubicEasingFn } from '~/utils/easings'; import WithTooltip from '../ui/Tooltip'; import { binDates } from './date-binning'; -import { HistoryItem } from './HistoryItem'; +import { HistoryItem } from './HistoryItem.client'; const menuVariants = { closed: { diff --git a/app/lib/.server/auth.ts b/app/lib/.server/auth.ts index 1fc7ba5..889b526 100644 --- a/app/lib/.server/auth.ts +++ b/app/lib/.server/auth.ts @@ -93,7 +93,7 @@ const config: LogtoConfig = { const originalLogto = makeLogtoRemix(config, { sessionStorage }); export function shouldEnforceAuth(): boolean { - return process.env.LOGTO_ENABLE_DEV !== 'false'; + return process.env.LOGTO_ENABLE === 'true'; } function getMockDevUser(): MockUser { diff --git a/app/lib/.server/llm/stream-enhancer.ts b/app/lib/.server/llm/stream-enhancer.ts index 2fad4d0..edabe44 100644 --- a/app/lib/.server/llm/stream-enhancer.ts +++ b/app/lib/.server/llm/stream-enhancer.ts @@ -1,6 +1,6 @@ import { convertToModelMessages, type LanguageModel, streamText, type UIMessage } from 'ai'; import { createScopedLogger } from '~/lib/.server/logger'; -import { DEFAULT_PROVIDER } from '~/utils/constants'; +import { DEFAULT_PROVIDER } from '~/lib/modules/constants'; import { stripIndents } from '~/utils/strip-indent'; const logger = createScopedLogger('stream-enhancer'); diff --git a/app/lib/modules/constants.ts b/app/lib/modules/constants.ts new file mode 100644 index 0000000..13b85ad --- /dev/null +++ b/app/lib/modules/constants.ts @@ -0,0 +1,17 @@ +import { LLMManager } from '~/lib/modules/llm/manager.server'; + +const llmManager = LLMManager.getInstance(); + +export const DEFAULT_MODEL = llmManager.getDefaultModel(); +export const MINOR_MODEL = llmManager.getMinorModel(); +export const DEFAULT_PROVIDER = llmManager.getDefaultProvider(); + +export const DEFAULT_MODEL_DETAILS = DEFAULT_PROVIDER.staticModels.find((m) => m.name === DEFAULT_MODEL); +export const MINOR_MODEL_DETAILS = DEFAULT_PROVIDER.staticModels.find((m) => m.name === MINOR_MODEL); + +export const getModel = (model: string) => { + return DEFAULT_PROVIDER.getModelInstance({ + model, + providerSettings: llmManager.getConfiguredProviderSettings(), + }); +}; diff --git a/app/lib/modules/llm/base-provider.ts b/app/lib/modules/llm/base-provider.ts index 2d001a6..75c2aa6 100644 --- a/app/lib/modules/llm/base-provider.ts +++ b/app/lib/modules/llm/base-provider.ts @@ -1,12 +1,11 @@ import { createOpenAI } from '@ai-sdk/openai'; import type { LanguageModel } from 'ai'; import type { IProviderSetting } from '~/types/model'; -import type { ModelInfo, ProviderConfig, ProviderInfo } from './types'; +import type { ModelInfo, ProviderInfo } from './types'; export abstract class BaseProvider implements ProviderInfo { abstract name: string; abstract staticModels: ModelInfo[]; - abstract config: ProviderConfig; cachedDynamicModels?: { cacheId: string; models: ModelInfo[]; @@ -16,39 +15,21 @@ export abstract class BaseProvider implements ProviderInfo { labelForGetApiKey?: string; icon?: string; - getProviderBaseUrlAndKey(options: { - apiKeys?: Record; - providerSettings?: IProviderSetting; - defaultBaseUrlKey: string; - defaultApiTokenKey: string; - }) { - const { apiKeys, providerSettings, defaultBaseUrlKey, defaultApiTokenKey } = options; - let settingsBaseUrl = providerSettings?.baseUrl; - - if (settingsBaseUrl && settingsBaseUrl.length == 0) { - settingsBaseUrl = undefined; - } - - const baseUrlKey = this.config.baseUrlKey || defaultBaseUrlKey; - let baseUrl = - settingsBaseUrl || process?.env?.[baseUrlKey] || (import.meta.env as any)?.[baseUrlKey] || this.config.baseUrl; - + getProviderBaseUrlAndKey(providerSettings?: IProviderSetting) { + let baseUrl = providerSettings?.baseUrl; if (baseUrl && baseUrl.endsWith('/')) { baseUrl = baseUrl.slice(0, -1); } - const apiTokenKey = this.config.apiTokenKey || defaultApiTokenKey; - const apiKey = apiKeys?.[this.name] || process?.env?.[apiTokenKey] || (import.meta.env as any)?.[apiTokenKey]; + const apiKey = providerSettings?.apiKey; return { baseUrl, apiKey, }; } - getModelsFromCache(options: { - apiKeys?: Record; - providerSettings?: Record; - }): ModelInfo[] | null { + + getModelsFromCache(options: { providerSettings?: Record }): ModelInfo[] | null { if (!this.cachedDynamicModels) { // console.log('no dynamic models',this.name); return null; @@ -65,22 +46,13 @@ export abstract class BaseProvider implements ProviderInfo { return this.cachedDynamicModels.models; } - getDynamicModelsCacheKey(options: { - apiKeys?: Record; - providerSettings?: Record; - }) { + getDynamicModelsCacheKey(options: { providerSettings?: Record }) { return JSON.stringify({ - apiKeys: options.apiKeys?.[this.name], + apiKeys: options.providerSettings?.[this.name]?.apiKey, providerSettings: options.providerSettings?.[this.name], }); } - storeDynamicModels( - options: { - apiKeys?: Record; - providerSettings?: Record; - }, - models: ModelInfo[], - ) { + storeDynamicModels(options: { providerSettings?: Record }, models: ModelInfo[]) { const cacheId = this.getDynamicModelsCacheKey(options); // console.log('caching dynamic models',this.name,cacheId); @@ -91,11 +63,10 @@ export abstract class BaseProvider implements ProviderInfo { } // Declare the optional getDynamicModels method - getDynamicModels?(apiKeys?: Record, settings?: IProviderSetting): Promise; + getDynamicModels?(settings?: IProviderSetting): Promise; abstract getModelInstance(options: { model: string; - apiKeys?: Record; providerSettings?: Record; }): LanguageModel; } diff --git a/app/lib/modules/llm/manager.ts b/app/lib/modules/llm/manager.server.ts similarity index 66% rename from app/lib/modules/llm/manager.ts rename to app/lib/modules/llm/manager.server.ts index 325028a..b72f936 100644 --- a/app/lib/modules/llm/manager.ts +++ b/app/lib/modules/llm/manager.server.ts @@ -5,12 +5,13 @@ import * as providers from './registry'; import type { ModelInfo, ProviderInfo } from './types'; const logger = createScopedLogger('LLMManager'); + export class LLMManager { private static _instance: LLMManager; private _providers: Map = new Map(); private _modelList: ModelInfo[] = []; - private constructor() { + constructor() { this._registerProvidersFromDirectory(); } @@ -22,7 +23,6 @@ export class LLMManager { return LLMManager._instance; } - // 从环境变量中读取配置的辅助方法 private _getEnvConfig(key: string, defaultValue: T): T { const value = process?.env?.[key] || (import.meta.env as any)?.[key]; @@ -32,9 +32,13 @@ export class LLMManager { if (typeof defaultValue === 'boolean') { return (value === 'true' || value === true) as unknown as T; - } else if (typeof defaultValue === 'number') { + } + + if (typeof defaultValue === 'number') { return Number(value) as unknown as T; - } else if (Array.isArray(defaultValue)) { + } + + if (Array.isArray(defaultValue)) { return (value ? String(value) .split(',') @@ -45,19 +49,35 @@ export class LLMManager { return value as T; } + private _getUnifiedProviderConfig() { + const providerName = this._getEnvConfig('LLM_PROVIDER', ''); + const baseUrl = this._getEnvConfig('PROVIDER_BASE_URL', ''); + const apiKey = this._getEnvConfig('PROVIDER_API_KEY', ''); + + return { + providerName, + baseUrl, + apiKey, + }; + } + + getDefaultProvider(): BaseProvider { + const { providerName } = this._getUnifiedProviderConfig(); + + if (!providerName || !this._providers.has(providerName)) { + throw new Error( + `Provider ${providerName} not found, Effective Provider: ${Array.from(this._providers.values()) + .map((p) => p.name) + .join(', ')}`, + ); + } + + return this._providers.get(providerName)!; + } + private _registerProvidersFromDirectory() { const allProviders: BaseProvider[] = Object.values(providers).map((providerClass) => new providerClass()); - - // 获取环境变量中的启用提供商列表 - const enabledProviders = this._getEnvConfig('LLM_ENABLED_PROVIDERS', []); - - // 过滤提供商,仅保留配置中启用的提供商 - const filteredProviders = - enabledProviders.length > 0 - ? allProviders.filter((provider) => enabledProviders.includes(provider.name)) - : allProviders; - - for (const provider of filteredProviders) { + for (const provider of allProviders) { this.registerProvider(provider); } } @@ -72,24 +92,6 @@ export class LLMManager { this._modelList = [...this._modelList, ...provider.staticModels]; } - getProvider(name: string): BaseProvider | undefined { - return this._providers.get(name); - } - - getAllProviders(): BaseProvider[] { - return Array.from(this._providers.values()); - } - - getDefaultProvider(): BaseProvider { - const defaultProviderName = this._getEnvConfig('LLM_DEFAULT_PROVIDER', ''); - - if (defaultProviderName && this._providers.has(defaultProviderName)) { - return this._providers.get(defaultProviderName)!; - } - - return Array.from(this._providers.values())[0]; - } - getDefaultModel(): string { return this._getEnvConfig('LLM_DEFAULT_MODEL', ''); } @@ -98,58 +100,16 @@ export class LLMManager { return this._getEnvConfig('LLM_MINOR_MODEL', ''); } - getConfiguredApiKeys(): Record { - const apiKeys: Record = {}; - - const allProviders = this.getAllProviders(); - - for (const provider of allProviders) { - if (!provider.config.apiTokenKey) { - continue; - } - - const apiTokenKey = provider.config.apiTokenKey; - - const apiKey = process?.env?.[apiTokenKey] || (import.meta.env as any)?.[apiTokenKey]; - - if (apiKey) { - apiKeys[provider.name] = apiKey; - logger.debug(`Found API key for provider ${provider.name} in environment variables`); - } - } - - return apiKeys; - } - getConfiguredProviderSettings(): Record { const providerSettings: Record = {}; - // 获取所有注册的提供商 - const allProviders = this.getAllProviders(); - - for (const provider of allProviders) { - const providerName = provider.name; - const settings: IProviderSetting = { enabled: true }; - - if (provider.config.baseUrlKey) { - const baseUrlKey = provider.config.baseUrlKey; - const baseUrl = process?.env?.[baseUrlKey] || (import.meta.env as any)?.[baseUrlKey]; - - if (baseUrl) { - settings.baseUrl = baseUrl; - logger.debug(`Found base URL for provider ${providerName} in environment variables: ${baseUrl}`); - } - } - - const enabledKey = `${providerName.toUpperCase()}_ENABLED`; - const isEnabled = this._getEnvConfig(enabledKey, true); - settings.enabled = isEnabled; - - if (Object.keys(settings).length > 1 || settings.enabled === false) { - providerSettings[providerName] = settings; - } - } + const { providerName, baseUrl, apiKey } = this._getUnifiedProviderConfig(); + providerSettings[providerName] = { + enabled: true, + baseUrl, + apiKey, + }; return providerSettings; } @@ -157,11 +117,8 @@ export class LLMManager { return this._modelList; } - async updateModelList(options: { - apiKeys?: Record; - providerSettings?: Record; - }): Promise { - const { apiKeys, providerSettings } = options; + async updateModelList(options: { providerSettings?: Record }): Promise { + const { providerSettings } = options; let enabledProviders = Array.from(this._providers.values()).map((p) => p.name); @@ -185,7 +142,7 @@ export class LLMManager { } const dynamicModels = await provider - .getDynamicModels(apiKeys, providerSettings?.[provider.name]) + .getDynamicModels(providerSettings?.[provider.name]) .then((models) => { logger.info(`Caching ${models.length} dynamic models for ${provider.name}`); provider.storeDynamicModels(options, models); @@ -218,7 +175,6 @@ export class LLMManager { async getModelListFromProvider( providerArg: BaseProvider, options: { - apiKeys?: Record; providerSettings?: Record; }, ): Promise { @@ -234,10 +190,9 @@ export class LLMManager { return staticModels; } - const { apiKeys, providerSettings } = options; + const { providerSettings } = options; const cachedModels = provider.getModelsFromCache({ - apiKeys, providerSettings, }); @@ -249,7 +204,7 @@ export class LLMManager { logger.info(`Getting dynamic models for ${provider.name}`); const dynamicModels = await provider - .getDynamicModels?.(apiKeys, providerSettings?.[provider.name]) + .getDynamicModels?.(providerSettings?.[provider.name]) .then((models) => { logger.info(`Got ${models.length} dynamic models for ${provider.name}`); provider.storeDynamicModels(options, models); diff --git a/app/lib/modules/llm/providers/amazon-bedrock.ts b/app/lib/modules/llm/providers/amazon-bedrock.ts index 73d92ed..59195e4 100644 --- a/app/lib/modules/llm/providers/amazon-bedrock.ts +++ b/app/lib/modules/llm/providers/amazon-bedrock.ts @@ -15,10 +15,6 @@ export default class AmazonBedrockProvider extends BaseProvider { name = 'AmazonBedrock'; getApiKeyLink = 'https://console.aws.amazon.com/iam/home'; - config = { - apiTokenKey: 'AWS_BEDROCK_CONFIG', - }; - staticModels: ModelInfo[] = [ { name: 'anthropic.claude-3-5-sonnet-20241022-v2:0', @@ -91,19 +87,10 @@ export default class AmazonBedrockProvider extends BaseProvider { }; } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'AWS_BEDROCK_CONFIG', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/anthropic.ts b/app/lib/modules/llm/providers/anthropic.ts index 645b81d..63b6ca9 100644 --- a/app/lib/modules/llm/providers/anthropic.ts +++ b/app/lib/modules/llm/providers/anthropic.ts @@ -8,10 +8,6 @@ export default class AnthropicProvider extends BaseProvider { name = 'Anthropic'; getApiKeyLink = 'https://console.anthropic.com/settings/keys'; - config = { - apiTokenKey: 'ANTHROPIC_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'claude-3-7-sonnet-20250219', @@ -42,13 +38,8 @@ export default class AnthropicProvider extends BaseProvider { { name: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku', provider: 'Anthropic', maxTokenAllowed: 8000 }, ]; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: '', - defaultApiTokenKey: 'ANTHROPIC_API_KEY', - }); + async getDynamicModels(settings: IProviderSetting): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey(settings); if (!apiKey) { throw `Missing Api Key configuration for ${this.name} provider`; @@ -74,22 +65,14 @@ export default class AnthropicProvider extends BaseProvider { })); } - getModelInstance: (options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }) => LanguageModel = (options) => { - const { apiKeys, providerSettings, model } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings, - defaultBaseUrlKey: '', - defaultApiTokenKey: 'ANTHROPIC_API_KEY', - }); - const anthropic = createAnthropic({ - apiKey, - }); + getModelInstance: (options: { model: string; providerSettings?: Record }) => LanguageModel = + (options) => { + const { providerSettings, model } = options; + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings); + const anthropic = createAnthropic({ + apiKey, + }); - return anthropic(model); - }; + return anthropic(model); + }; } diff --git a/app/lib/modules/llm/providers/cohere.ts b/app/lib/modules/llm/providers/cohere.ts index 83a1660..e3487bb 100644 --- a/app/lib/modules/llm/providers/cohere.ts +++ b/app/lib/modules/llm/providers/cohere.ts @@ -8,10 +8,6 @@ export default class CohereProvider extends BaseProvider { name = 'Cohere'; getApiKeyLink = 'https://dashboard.cohere.com/api-keys'; - config = { - apiTokenKey: 'COHERE_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'command-r-plus-08-2024', label: 'Command R plus Latest', provider: 'Cohere', maxTokenAllowed: 4096 }, { name: 'command-r-08-2024', label: 'Command R Latest', provider: 'Cohere', maxTokenAllowed: 4096 }, @@ -25,19 +21,10 @@ export default class CohereProvider extends BaseProvider { { name: 'c4ai-aya-expanse-32b', label: 'c4AI Aya Expanse 32b', provider: 'Cohere', maxTokenAllowed: 4096 }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'COHERE_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/deepseek.ts b/app/lib/modules/llm/providers/deepseek.ts index 40c76f4..0afeca4 100644 --- a/app/lib/modules/llm/providers/deepseek.ts +++ b/app/lib/modules/llm/providers/deepseek.ts @@ -10,6 +10,7 @@ export default class DeepseekProvider extends BaseProvider { config = { apiTokenKey: 'DEEPSEEK_API_KEY', + baseUrlKey: '', }; staticModels: ModelInfo[] = [ @@ -18,19 +19,10 @@ export default class DeepseekProvider extends BaseProvider { { name: 'deepseek-reasoner', label: 'Deepseek-Reasoner', provider: 'Deepseek', maxTokenAllowed: 8000 }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'DEEPSEEK_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/github.ts b/app/lib/modules/llm/providers/github.ts index 8db2cc4..652b9de 100644 --- a/app/lib/modules/llm/providers/github.ts +++ b/app/lib/modules/llm/providers/github.ts @@ -8,10 +8,6 @@ export default class GithubProvider extends BaseProvider { name = 'Github'; getApiKeyLink = 'https://github.com/settings/personal-access-tokens'; - config = { - apiTokenKey: 'GITHUB_API_KEY', - }; - // find more in https://github.com/marketplace?type=models staticModels: ModelInfo[] = [ { name: 'gpt-4o', label: 'GPT-4o', provider: 'Github', maxTokenAllowed: 8000 }, @@ -23,19 +19,10 @@ export default class GithubProvider extends BaseProvider { { name: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo', provider: 'Github', maxTokenAllowed: 8000 }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'GITHUB_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/google.ts b/app/lib/modules/llm/providers/google.ts index f75011d..58074cb 100644 --- a/app/lib/modules/llm/providers/google.ts +++ b/app/lib/modules/llm/providers/google.ts @@ -8,10 +8,6 @@ export default class GoogleProvider extends BaseProvider { name = 'Google'; getApiKeyLink = 'https://aistudio.google.com/app/apikey'; - config = { - apiTokenKey: 'GOOGLE_GENERATIVE_AI_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'gemini-1.5-flash-latest', label: 'Gemini 1.5 Flash', provider: 'Google', maxTokenAllowed: 8192 }, { @@ -28,13 +24,8 @@ export default class GoogleProvider extends BaseProvider { { name: 'gemini-exp-1206', label: 'Gemini exp-1206', provider: 'Google', maxTokenAllowed: 8192 }, ]; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: '', - defaultApiTokenKey: 'GOOGLE_GENERATIVE_AI_API_KEY', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey(settings); if (!apiKey) { throw `Missing Api Key configuration for ${this.name} provider`; @@ -59,19 +50,10 @@ export default class GoogleProvider extends BaseProvider { })); } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'GOOGLE_GENERATIVE_AI_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/groq.ts b/app/lib/modules/llm/providers/groq.ts index 93a0741..2076ee8 100644 --- a/app/lib/modules/llm/providers/groq.ts +++ b/app/lib/modules/llm/providers/groq.ts @@ -8,10 +8,6 @@ export default class GroqProvider extends BaseProvider { name = 'Groq'; getApiKeyLink = 'https://console.groq.com/keys'; - config = { - apiTokenKey: 'GROQ_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'llama-3.1-8b-instant', label: 'Llama 3.1 8b (Groq)', provider: 'Groq', maxTokenAllowed: 8000 }, { name: 'llama-3.2-11b-vision-preview', label: 'Llama 3.2 11b (Groq)', provider: 'Groq', maxTokenAllowed: 8000 }, @@ -27,13 +23,8 @@ export default class GroqProvider extends BaseProvider { }, ]; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: '', - defaultApiTokenKey: 'GROQ_API_KEY', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey(settings); if (!apiKey) { throw `Missing Api Key configuration for ${this.name} provider`; @@ -59,19 +50,10 @@ export default class GroqProvider extends BaseProvider { })); } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'GROQ_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/huggingface.ts b/app/lib/modules/llm/providers/huggingface.ts index fe25794..b6aa82d 100644 --- a/app/lib/modules/llm/providers/huggingface.ts +++ b/app/lib/modules/llm/providers/huggingface.ts @@ -8,10 +8,6 @@ export default class HuggingFaceProvider extends BaseProvider { name = 'HuggingFace'; getApiKeyLink = 'https://huggingface.co/settings/tokens'; - config = { - apiTokenKey: 'HuggingFace_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'Qwen/Qwen2.5-Coder-32B-Instruct', @@ -81,19 +77,10 @@ export default class HuggingFaceProvider extends BaseProvider { }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'HuggingFace_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/hyperbolic.ts b/app/lib/modules/llm/providers/hyperbolic.ts index f19abf7..2c39f28 100644 --- a/app/lib/modules/llm/providers/hyperbolic.ts +++ b/app/lib/modules/llm/providers/hyperbolic.ts @@ -8,10 +8,6 @@ export default class HyperbolicProvider extends BaseProvider { name = 'Hyperbolic'; getApiKeyLink = 'https://app.hyperbolic.xyz/settings'; - config = { - apiTokenKey: 'HYPERBOLIC_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'Qwen/Qwen2.5-Coder-32B-Instruct', @@ -45,13 +41,8 @@ export default class HyperbolicProvider extends BaseProvider { }, ]; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { baseUrl: fetchBaseUrl, apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: '', - defaultApiTokenKey: 'HYPERBOLIC_API_KEY', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + const { baseUrl: fetchBaseUrl, apiKey } = this.getProviderBaseUrlAndKey(settings); const baseUrl = fetchBaseUrl || 'https://api.hyperbolic.xyz/v1'; if (!apiKey) { @@ -76,19 +67,10 @@ export default class HyperbolicProvider extends BaseProvider { })); } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'HYPERBOLIC_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw `Missing Api Key configuration for ${this.name} provider`; diff --git a/app/lib/modules/llm/providers/lmstudio.ts b/app/lib/modules/llm/providers/lmstudio.ts index cecc10f..0781986 100644 --- a/app/lib/modules/llm/providers/lmstudio.ts +++ b/app/lib/modules/llm/providers/lmstudio.ts @@ -5,29 +5,21 @@ import type { ModelInfo } from '~/lib/modules/llm/types'; import type { IProviderSetting } from '~/types/model'; import { logger } from '~/utils/logger'; +export const BASE_URL = 'http://127.0.0.1:1234/'; export default class LMStudioProvider extends BaseProvider { name = 'LMStudio'; getApiKeyLink = 'https://lmstudio.ai/'; labelForGetApiKey = 'Get LMStudio'; icon = 'i-ph:cloud-arrow-down'; - config = { - baseUrlKey: 'LMSTUDIO_API_BASE_URL', - baseUrl: 'http://localhost:1234/', - }; - staticModels: ModelInfo[] = []; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - let { baseUrl } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: 'LMSTUDIO_API_BASE_URL', - defaultApiTokenKey: '', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + let { baseUrl } = this.getProviderBaseUrlAndKey(settings); if (!baseUrl) { - throw new Error('No baseUrl found for LMStudio provider'); + logger.debug('No baseUrl found for LMStudio provider, using default: ', BASE_URL); + baseUrl = BASE_URL; } if (typeof window === 'undefined') { @@ -51,37 +43,30 @@ export default class LMStudioProvider extends BaseProvider { maxTokenAllowed: 8000, })); } - getModelInstance: (options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }) => LanguageModel = (options) => { - const { apiKeys, providerSettings, model } = options; - let { baseUrl } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: 'LMSTUDIO_API_BASE_URL', - defaultApiTokenKey: '', - }); + getModelInstance: (options: { model: string; providerSettings?: Record }) => LanguageModel = + (options) => { + const { providerSettings, model } = options; + let { baseUrl } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); - if (!baseUrl) { - throw new Error('No baseUrl found for LMStudio provider'); - } + if (!baseUrl) { + logger.debug('No baseUrl found for LMStudio provider, using default: ', BASE_URL); + baseUrl = BASE_URL; + } - const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true'; + const isDocker = process?.env?.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; - } + 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; + } - logger.debug('LMStudio Base Url used: ', baseUrl); + logger.debug('LMStudio Base Url used: ', baseUrl); - const lmstudio = createOpenAI({ - baseURL: `${baseUrl}/v1`, - apiKey: '', - }); + const lmstudio = createOpenAI({ + baseURL: `${baseUrl}/v1`, + apiKey: '', + }); - return lmstudio(model); - }; + return lmstudio(model); + }; } diff --git a/app/lib/modules/llm/providers/mistral.ts b/app/lib/modules/llm/providers/mistral.ts index d63a6a9..b318a43 100644 --- a/app/lib/modules/llm/providers/mistral.ts +++ b/app/lib/modules/llm/providers/mistral.ts @@ -8,10 +8,6 @@ export default class MistralProvider extends BaseProvider { name = 'Mistral'; getApiKeyLink = 'https://console.mistral.ai/api-keys/'; - config = { - apiTokenKey: 'MISTRAL_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'open-mistral-7b', label: 'Mistral 7B', provider: 'Mistral', maxTokenAllowed: 8000 }, { name: 'open-mixtral-8x7b', label: 'Mistral 8x7B', provider: 'Mistral', maxTokenAllowed: 8000 }, @@ -24,19 +20,10 @@ export default class MistralProvider extends BaseProvider { { name: 'mistral-large-latest', label: 'Mistral Large Latest', provider: 'Mistral', maxTokenAllowed: 8000 }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'MISTRAL_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/ollama.ts b/app/lib/modules/llm/providers/ollama.ts index 63888c8..9e89882 100644 --- a/app/lib/modules/llm/providers/ollama.ts +++ b/app/lib/modules/llm/providers/ollama.ts @@ -27,32 +27,26 @@ export interface OllamaApiResponse { models: OllamaModel[]; } +const BASE_URL = 'http://127.0.0.1:11434'; + export default class OllamaProvider extends BaseProvider { name = 'Ollama'; getApiKeyLink = 'https://ollama.com/download'; labelForGetApiKey = 'Download Ollama'; icon = 'i-ph:cloud-arrow-down'; - config = { - baseUrlKey: 'OLLAMA_API_BASE_URL', - }; - staticModels: ModelInfo[] = []; getDefaultNumCtx(): number { return process.env.DEFAULT_NUM_CTX ? parseInt(process.env.DEFAULT_NUM_CTX, 10) : 32768; } - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - let { baseUrl } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: 'OLLAMA_API_BASE_URL', - defaultApiTokenKey: '', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + let { baseUrl } = this.getProviderBaseUrlAndKey(settings); if (!baseUrl) { - throw new Error('No baseUrl found for OLLAMA provider'); + logger.debug('No baseUrl found for OLLAMA provider, using default: ', BASE_URL); + baseUrl = BASE_URL; } if (typeof window === 'undefined') { @@ -79,35 +73,28 @@ export default class OllamaProvider extends BaseProvider { })); } - getModelInstance: (options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }) => LanguageModel = (options) => { - const { apiKeys, providerSettings, model } = options; + getModelInstance: (options: { model: string; providerSettings?: Record }) => LanguageModel = + (options) => { + const { providerSettings, model } = options; - let { baseUrl } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: 'OLLAMA_API_BASE_URL', - defaultApiTokenKey: '', - }); + let { baseUrl } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); - // Backend: Check if we're running in Docker - if (!baseUrl) { - throw new Error('No baseUrl found for OLLAMA provider'); - } + // Backend: Check if we're running in Docker + if (!baseUrl) { + logger.debug('No baseUrl found for OLLAMA provider, using default: ', BASE_URL); + baseUrl = BASE_URL; + } - const isDocker = process?.env?.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 isDocker = process?.env?.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; - logger.debug('Ollama Base Url used: ', baseUrl); + logger.debug('Ollama Base Url used: ', baseUrl); - const ollama = createOllama({ - baseURL: `${baseUrl}/api`, - }); + const ollama = createOllama({ + baseURL: `${baseUrl}/api`, + }); - return ollama(model); - }; + return ollama(model); + }; } diff --git a/app/lib/modules/llm/providers/open-router.ts b/app/lib/modules/llm/providers/open-router.ts index 06ce60e..e6fd63a 100644 --- a/app/lib/modules/llm/providers/open-router.ts +++ b/app/lib/modules/llm/providers/open-router.ts @@ -22,10 +22,6 @@ export default class OpenRouterProvider extends BaseProvider { name = 'OpenRouter'; getApiKeyLink = 'https://openrouter.ai/settings/keys'; - config = { - apiTokenKey: 'OPEN_ROUTER_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'anthropic/claude-3.5-sonnet', @@ -73,11 +69,7 @@ export default class OpenRouterProvider extends BaseProvider { { name: 'cohere/command', label: 'Cohere Command (OpenRouter)', provider: 'OpenRouter', maxTokenAllowed: 4096 }, ]; - async getDynamicModels( - _apiKeys?: Record, - _settings?: IProviderSetting, - _serverEnv: Record = {}, - ): Promise { + async getDynamicModels(_settings?: IProviderSetting, _serverEnv: Record = {}): Promise { try { const response = await fetch('https://openrouter.ai/api/v1/models', { headers: { @@ -101,19 +93,10 @@ export default class OpenRouterProvider extends BaseProvider { } } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'OPEN_ROUTER_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/openai-like.ts b/app/lib/modules/llm/providers/openai-like.ts index 061245d..aa85435 100644 --- a/app/lib/modules/llm/providers/openai-like.ts +++ b/app/lib/modules/llm/providers/openai-like.ts @@ -7,20 +7,10 @@ export default class OpenAILikeProvider extends BaseProvider { name = 'OpenAILike'; getApiKeyLink = undefined; - config = { - baseUrlKey: 'OPENAI_LIKE_API_BASE_URL', - apiTokenKey: 'OPENAI_LIKE_API_KEY', - }; - staticModels: ModelInfo[] = []; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: 'OPENAI_LIKE_API_BASE_URL', - defaultApiTokenKey: 'OPENAI_LIKE_API_KEY', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey(settings); if (!baseUrl || !apiKey) { return []; @@ -42,19 +32,10 @@ export default class OpenAILikeProvider extends BaseProvider { })); } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: 'OPENAI_LIKE_API_BASE_URL', - defaultApiTokenKey: 'OPENAI_LIKE_API_KEY', - }); + const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!baseUrl || !apiKey) { throw new Error(`Missing configuration for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/openai.ts b/app/lib/modules/llm/providers/openai.ts index d78e52d..88cbd48 100644 --- a/app/lib/modules/llm/providers/openai.ts +++ b/app/lib/modules/llm/providers/openai.ts @@ -8,10 +8,6 @@ export default class OpenAIProvider extends BaseProvider { name = 'OpenAI'; getApiKeyLink = 'https://platform.openai.com/api-keys'; - config = { - apiTokenKey: 'OPENAI_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'gpt-4o', label: 'GPT-4o', provider: 'OpenAI', maxTokenAllowed: 8000 }, { name: 'gpt-4o-mini', label: 'GPT-4o Mini', provider: 'OpenAI', maxTokenAllowed: 8000 }, @@ -20,13 +16,8 @@ export default class OpenAIProvider extends BaseProvider { { name: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo', provider: 'OpenAI', maxTokenAllowed: 8000 }, ]; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: '', - defaultApiTokenKey: 'OPENAI_API_KEY', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey(settings); if (!apiKey) { throw `Missing Api Key configuration for ${this.name} provider`; @@ -56,19 +47,10 @@ export default class OpenAIProvider extends BaseProvider { })); } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'OPENAI_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/perplexity.ts b/app/lib/modules/llm/providers/perplexity.ts index bf7d724..b680b61 100644 --- a/app/lib/modules/llm/providers/perplexity.ts +++ b/app/lib/modules/llm/providers/perplexity.ts @@ -8,10 +8,6 @@ export default class PerplexityProvider extends BaseProvider { name = 'Perplexity'; getApiKeyLink = 'https://www.perplexity.ai/settings/api'; - config = { - apiTokenKey: 'PERPLEXITY_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'llama-3.1-sonar-small-128k-online', @@ -33,19 +29,10 @@ export default class PerplexityProvider extends BaseProvider { }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'PERPLEXITY_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/together.ts b/app/lib/modules/llm/providers/together.ts index 0161567..5703023 100644 --- a/app/lib/modules/llm/providers/together.ts +++ b/app/lib/modules/llm/providers/together.ts @@ -7,11 +7,6 @@ export default class TogetherProvider extends BaseProvider { name = 'Together'; getApiKeyLink = 'https://api.together.xyz/settings/api-keys'; - config = { - baseUrlKey: 'TOGETHER_API_BASE_URL', - apiTokenKey: 'TOGETHER_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'Qwen/Qwen2.5-Coder-32B-Instruct', @@ -33,13 +28,8 @@ export default class TogetherProvider extends BaseProvider { }, ]; - async getDynamicModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - const { baseUrl: fetchBaseUrl, apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: settings, - defaultBaseUrlKey: 'TOGETHER_API_BASE_URL', - defaultApiTokenKey: 'TOGETHER_API_KEY', - }); + async getDynamicModels(settings?: IProviderSetting): Promise { + const { baseUrl: fetchBaseUrl, apiKey } = this.getProviderBaseUrlAndKey(settings); const baseUrl = fetchBaseUrl || 'https://api.together.xyz/v1'; if (!baseUrl || !apiKey) { @@ -65,19 +55,10 @@ export default class TogetherProvider extends BaseProvider { })); } - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: 'TOGETHER_API_BASE_URL', - defaultApiTokenKey: 'TOGETHER_API_KEY', - }); + const { baseUrl, apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!baseUrl || !apiKey) { throw new Error(`Missing configuration for ${this.name} provider`); diff --git a/app/lib/modules/llm/providers/xai.ts b/app/lib/modules/llm/providers/xai.ts index 102d0d8..c9b7bb4 100644 --- a/app/lib/modules/llm/providers/xai.ts +++ b/app/lib/modules/llm/providers/xai.ts @@ -8,28 +8,15 @@ export default class XAIProvider extends BaseProvider { name = 'xAI'; getApiKeyLink = 'https://docs.x.ai/docs/quickstart#creating-an-api-key'; - config = { - apiTokenKey: 'XAI_API_KEY', - }; - staticModels: ModelInfo[] = [ { name: 'grok-beta', label: 'xAI Grok Beta', provider: 'xAI', maxTokenAllowed: 8000 }, { name: 'grok-2-1212', label: 'xAI Grok2 1212', provider: 'xAI', maxTokenAllowed: 8000 }, ]; - getModelInstance(options: { - model: string; - apiKeys?: Record; - providerSettings?: Record; - }): LanguageModel { - const { model, apiKeys, providerSettings } = options; + getModelInstance(options: { model: string; providerSettings?: Record }): LanguageModel { + const { model, providerSettings } = options; - const { apiKey } = this.getProviderBaseUrlAndKey({ - apiKeys, - providerSettings: providerSettings?.[this.name], - defaultBaseUrlKey: '', - defaultApiTokenKey: 'XAI_API_KEY', - }); + const { apiKey } = this.getProviderBaseUrlAndKey(providerSettings?.[this.name]); if (!apiKey) { throw new Error(`Missing API key for ${this.name} provider`); diff --git a/app/lib/modules/llm/types.ts b/app/lib/modules/llm/types.ts index 97c1c09..a56078e 100644 --- a/app/lib/modules/llm/types.ts +++ b/app/lib/modules/llm/types.ts @@ -23,6 +23,5 @@ export interface ProviderInfo { } export interface ProviderConfig { baseUrlKey?: string; - baseUrl?: string; apiTokenKey?: string; } diff --git a/app/lib/persistence/editor.ts b/app/lib/persistence/editor.ts index 2fcd882..8d876d7 100644 --- a/app/lib/persistence/editor.ts +++ b/app/lib/persistence/editor.ts @@ -150,7 +150,7 @@ const logger = createScopedLogger('EditorProjects'); */ export async function openEditorDatabase(): Promise { if (typeof indexedDB === 'undefined') { - logger.error('indexedDB 在当前环境中不可用'); + logger.debug('indexedDB 在当前环境中不可用'); return undefined; } diff --git a/app/routes/api.chat/chat.server.ts b/app/routes/api.chat/chat.server.ts index b84bfc9..c2f1100 100644 --- a/app/routes/api.chat/chat.server.ts +++ b/app/routes/api.chat/chat.server.ts @@ -17,9 +17,9 @@ import { createScopedLogger } from '~/lib/.server/logger'; import { getHistoryChatMessages, saveChatMessages, updateDiscardedMessage } from '~/lib/.server/message'; import { getPageByMessageId } from '~/lib/.server/page'; import { CONTINUE_PROMPT } from '~/lib/common/prompts/prompts'; +import { DEFAULT_MODEL, DEFAULT_MODEL_DETAILS, getModel, MINOR_MODEL } from '~/lib/modules/constants'; import type { Page } from '~/types/actions'; import type { UPageUIMessage } from '~/types/message'; -import { DEFAULT_MODEL, DEFAULT_MODEL_DETAILS, getModel, MINOR_MODEL } from '~/utils/constants'; import { approximateUsageFromContent } from '~/utils/token'; const logger = createScopedLogger('api.chat.chat'); diff --git a/app/routes/api.enhancer/route.tsx b/app/routes/api.enhancer/route.tsx index fe87756..5122405 100644 --- a/app/routes/api.enhancer/route.tsx +++ b/app/routes/api.enhancer/route.tsx @@ -3,8 +3,8 @@ import type { UIMessage } from 'ai'; import { requireAuth } from '~/lib/.server/auth'; import { streamEnhancer } from '~/lib/.server/llm/stream-enhancer'; import { createScopedLogger } from '~/lib/.server/logger'; +import { getModel, MINOR_MODEL } from '~/lib/modules/constants'; import { errorResponse } from '~/utils/api-response'; -import { getModel, MINOR_MODEL } from '~/utils/constants'; export async function action(args: ActionFunctionArgs) { const authResult = await requireAuth(args.request, { isApi: true }); diff --git a/app/types/model.ts b/app/types/model.ts index d16b10a..8985393 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -17,6 +17,7 @@ export type ProviderInfo = { export interface IProviderSetting { enabled?: boolean; baseUrl?: string; + apiKey?: string; } export type IProviderConfig = ProviderInfo & { diff --git a/app/utils/constants.ts b/app/utils/constants.ts index c2de829..8f49557 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -1,31 +1,2 @@ -import { LLMManager } from '~/lib/modules/llm/manager'; - -export const WORK_DIR_NAME = 'project'; export const MODEL_REGEX = /^\[Model: (.*?)\]\n\n/; export const PROVIDER_REGEX = /\[Provider: (.*?)\]\n\n/; - -const llmManager = LLMManager.getInstance(); - -export const DEFAULT_MODEL = llmManager.getDefaultModel(); -export const MINOR_MODEL = llmManager.getMinorModel(); -export const PROVIDER_LIST = llmManager.getAllProviders(); -export const DEFAULT_PROVIDER = llmManager.getDefaultProvider(); - -export const DEFAULT_MODEL_DETAILS = DEFAULT_PROVIDER.staticModels.find((m) => m.name === DEFAULT_MODEL); -export const MINOR_MODEL_DETAILS = DEFAULT_PROVIDER.staticModels.find((m) => m.name === MINOR_MODEL); - -export const providerBaseUrlEnvKeys: Record = {}; -PROVIDER_LIST.forEach((provider) => { - providerBaseUrlEnvKeys[provider.name] = { - baseUrlKey: provider.config.baseUrlKey, - apiTokenKey: provider.config.apiTokenKey, - }; -}); - -export const getModel = (model: string) => { - return DEFAULT_PROVIDER.getModelInstance({ - model, - apiKeys: llmManager.getConfiguredApiKeys(), - providerSettings: llmManager.getConfiguredProviderSettings(), - }); -}; diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index 3cfccb9..d834862 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -8,40 +8,21 @@ services: environment: - OPERATING_ENV=${OPERATING_ENV:-production} - NODE_ENV=${NODE_ENV:-production} - - GROQ_API_KEY=${GROQ_API_KEY} - - HuggingFace_API_KEY=${HuggingFace_API_KEY} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} - - GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} - - OLLAMA_API_BASE_URL=${OLLAMA_API_BASE_URL} - - OPENAI_LIKE_API_BASE_URL=${OPENAI_LIKE_API_BASE_URL} - - TOGETHER_API_BASE_URL=${TOGETHER_API_BASE_URL} - - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} - - OPENAI_LIKE_API_KEY=${OPENAI_LIKE_API_KEY} - - TOGETHER_API_KEY=${TOGETHER_API_KEY} - - HYPERBOLIC_API_KEY=${HYPERBOLIC_API_KEY} - - HYPERBOLIC_API_BASE_URL=${HYPERBOLIC_API_BASE_URL} - - MISTRAL_API_KEY=${MISTRAL_API_KEY} - - COHERE_API_KEY=${COHERE_API_KEY} - - LMSTUDIO_API_BASE_URL=${LMSTUDIO_API_BASE_URL} - - XAI_API_KEY=${XAI_API_KEY} - - PERPLEXITY_API_KEY=${PERPLEXITY_API_KEY} - - AWS_BEDROCK_CONFIG=${AWS_BEDROCK_CONFIG} - LOG_LEVEL=${LOG_LEVEL:-debug} - DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX:-32768} - - SERPER_API_KEY=${SERPER_API_KEY} - - WEATHER_API_KEY=${WEATHER_API_KEY} - - LLM_DEFAULT_PROVIDER=${LLM_DEFAULT_PROVIDER} + - LLM_PROVIDER=${LLM_DEFAULT_PROVIDER} + - PROVIDER_BASE_URL=${PROVIDER_BASE_URL} + - PROVIDER_API_KEY=${PROVIDER_API_KEY} - LLM_DEFAULT_MODEL=${LLM_DEFAULT_MODEL} - LLM_MINOR_MODEL=${LLM_MINOR_MODEL} - - LLM_ENABLED_PROVIDERS=${LLM_ENABLED_PROVIDERS} + - SERPER_API_KEY=${SERPER_API_KEY} + - WEATHER_API_KEY=${WEATHER_API_KEY} - LOGTO_ENDPOINT=${LOGTO_ENDPOINT} - LOGTO_APP_ID=${LOGTO_APP_ID} - LOGTO_APP_SECRET=${LOGTO_APP_SECRET} - LOGTO_COOKIE_SECRET=${LOGTO_COOKIE_SECRET} - LOGTO_BASE_URL=${LOGTO_BASE_URL} - - LOGTO_ENABLE_DEV=${LOGTO_ENABLE_DEV:-false} + - LOGTO_ENABLE=${LOGTO_ENABLE:-false} - USAGE_LOG_FILE=true - STORAGE_DIR=/app/storage - MAX_UPLOAD_SIZE_MB=${MAX_UPLOAD_SIZE_MB:-5} diff --git a/docker-compose-prod.yaml b/docker-compose-prod.yaml index 890b4bd..b5f3e02 100644 --- a/docker-compose-prod.yaml +++ b/docker-compose-prod.yaml @@ -8,41 +8,23 @@ services: environment: - OPERATING_ENV=${OPERATING_ENV:-production} - NODE_ENV=${NODE_ENV:-production} - - GROQ_API_KEY=${GROQ_API_KEY} - - HuggingFace_API_KEY=${HuggingFace_API_KEY} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} - - GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} - - OLLAMA_API_BASE_URL=${OLLAMA_API_BASE_URL} - - OPENAI_LIKE_API_BASE_URL=${OPENAI_LIKE_API_BASE_URL} - - TOGETHER_API_BASE_URL=${TOGETHER_API_BASE_URL} - - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} - - OPENAI_LIKE_API_KEY=${OPENAI_LIKE_API_KEY} - - TOGETHER_API_KEY=${TOGETHER_API_KEY} - - HYPERBOLIC_API_KEY=${HYPERBOLIC_API_KEY} - - HYPERBOLIC_API_BASE_URL=${HYPERBOLIC_API_BASE_URL} - - MISTRAL_API_KEY=${MISTRAL_API_KEY} - - COHERE_API_KEY=${COHERE_API_KEY} - - LMSTUDIO_API_BASE_URL=${LMSTUDIO_API_BASE_URL} - - XAI_API_KEY=${XAI_API_KEY} - - PERPLEXITY_API_KEY=${PERPLEXITY_API_KEY} - - AWS_BEDROCK_CONFIG=${AWS_BEDROCK_CONFIG} - LOG_LEVEL=${LOG_LEVEL:-debug} - DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX:-32768} - - SERPER_API_KEY=${SERPER_API_KEY} - - WEATHER_API_KEY=${WEATHER_API_KEY} - - LLM_DEFAULT_PROVIDER=${LLM_DEFAULT_PROVIDER} + - LLM_PROVIDER=${LLM_DEFAULT_PROVIDER} + - PROVIDER_BASE_URL=${PROVIDER_BASE_URL} + - PROVIDER_API_KEY=${PROVIDER_API_KEY} - LLM_DEFAULT_MODEL=${LLM_DEFAULT_MODEL} - LLM_MINOR_MODEL=${LLM_MINOR_MODEL} - - LLM_ENABLED_PROVIDERS=${LLM_ENABLED_PROVIDERS} + - SERPER_API_KEY=${SERPER_API_KEY} + - WEATHER_API_KEY=${WEATHER_API_KEY} - LOGTO_ENDPOINT=${LOGTO_ENDPOINT} - LOGTO_APP_ID=${LOGTO_APP_ID} - LOGTO_APP_SECRET=${LOGTO_APP_SECRET} - LOGTO_COOKIE_SECRET=${LOGTO_COOKIE_SECRET} - LOGTO_BASE_URL=${LOGTO_BASE_URL} - - LOGTO_ENABLE_DEV=${LOGTO_ENABLE_DEV:-false} - - USAGE_LOG_FILE=${USAGE_LOG_FILE:-true} + - LOGTO_ENABLE=${LOGTO_ENABLE:-false} + - USAGE_LOG_FILE=true + - STORAGE_DIR=/app/storage - MAX_UPLOAD_SIZE_MB=${MAX_UPLOAD_SIZE_MB:-5} volumes: - ./data:/app/data diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 809cadf..0000000 --- a/docs/index.md +++ /dev/null @@ -1,14 +0,0 @@ -# 简介 - -欢迎阅读 UPage 文档。UPage 是一个基于人工智能的可视化网页构建平台,支持多种 AI 提供商集成,能够轻松创建和定制网页内容。 - -## 目录 - -- [简介](#简介) -- [目录](#目录) -- [快速开始](#快速开始) -- [部署指南](#部署指南) -- [常见问题](#常见问题) -- [贡献指南](#贡献指南) -- [开发指南](#开发指南) -- [更新日志](#更新日志)