From 0dfcf8dd3fa5c2950b8cb996a873b8b9d0a89ba7 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 28 May 2024 19:20:36 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=84=20style:=20add=20`SYSTEM=5FAGENT`?= =?UTF-8?q?=20env=20(#2694)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/app.ts | 2 + src/server/globalConfig/index.ts | 5 +- .../globalConfig/parseSystemAgent.test.ts | 95 +++++++++++++++++++ src/server/globalConfig/parseSystemAgent.ts | 39 ++++++++ src/store/user/slices/common/action.ts | 2 + src/types/serverConfig.ts | 7 +- 6 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 src/server/globalConfig/parseSystemAgent.test.ts create mode 100644 src/server/globalConfig/parseSystemAgent.ts diff --git a/src/config/app.ts b/src/config/app.ts index 2f427dfffcab..4b7917924d22 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -25,6 +25,7 @@ export const getAppConfig = () => { AGENTS_INDEX_URL: z.string().url(), DEFAULT_AGENT_CONFIG: z.string(), + SYSTEM_AGENT: z.string().optional(), PLUGINS_INDEX_URL: z.string().url(), PLUGIN_SETTINGS: z.string().optional(), @@ -44,6 +45,7 @@ export const getAppConfig = () => { : 'https://chat-agents.lobehub.com', DEFAULT_AGENT_CONFIG: process.env.DEFAULT_AGENT_CONFIG || '', + SYSTEM_AGENT: process.env.SYSTEM_AGENT, PLUGINS_INDEX_URL: !!process.env.PLUGINS_INDEX_URL ? process.env.PLUGINS_INDEX_URL diff --git a/src/server/globalConfig/index.ts b/src/server/globalConfig/index.ts index 8ff2dfa04c98..20038cc7dc2a 100644 --- a/src/server/globalConfig/index.ts +++ b/src/server/globalConfig/index.ts @@ -1,4 +1,4 @@ -import { getAppConfig } from '@/config/app'; +import { appEnv, getAppConfig } from '@/config/app'; import { fileEnv } from '@/config/file'; import { langfuseEnv } from '@/config/langfuse'; import { getLLMConfig } from '@/config/llm'; @@ -9,6 +9,7 @@ import { TogetherAIProviderCard, } from '@/config/modelProviders'; import { enableNextAuth } from '@/const/auth'; +import { parseSystemAgent } from '@/server/globalConfig/parseSystemAgent'; import { GlobalServerConfig } from '@/types/serverConfig'; import { extractEnabledModels, transformToChatModelCards } from '@/utils/parseModels'; @@ -51,7 +52,6 @@ export const getServerGlobalConfig = () => { defaultAgent: { config: parseAgentConfig(DEFAULT_AGENT_CONFIG), }, - enableUploadFileToServer: !!fileEnv.S3_SECRET_ACCESS_KEY, enabledAccessCode: ACCESS_CODES?.length > 0, enabledOAuthSSO: enableNextAuth, @@ -114,6 +114,7 @@ export const getServerGlobalConfig = () => { zeroone: { enabled: ENABLED_ZEROONE }, zhipu: { enabled: ENABLED_ZHIPU }, }, + systemAgent: parseSystemAgent(appEnv.SYSTEM_AGENT), telemetry: { langfuse: langfuseEnv.ENABLE_LANGFUSE, }, diff --git a/src/server/globalConfig/parseSystemAgent.test.ts b/src/server/globalConfig/parseSystemAgent.test.ts new file mode 100644 index 000000000000..d769c1480db7 --- /dev/null +++ b/src/server/globalConfig/parseSystemAgent.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, it } from 'vitest'; + +import { parseSystemAgent } from './parseSystemAgent'; + +describe('parseSystemAgent', () => { + it('should parse a valid environment variable string correctly', () => { + const envValue = 'topic=openai/gpt-3.5-turbo,translation=anthropic/claude-1'; + const expected = { + topic: { provider: 'openai', model: 'gpt-3.5-turbo' }, + translation: { provider: 'anthropic', model: 'claude-1' }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should handle empty environment variable string', () => { + const envValue = ''; + const expected = {}; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should ignore unknown keys in environment variable string', () => { + const envValue = 'topic=openai/gpt-3.5-turbo,unknown=test/model'; + const expected = { + topic: { provider: 'openai', model: 'gpt-3.5-turbo' }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should throw an error for missing model or provider values', () => { + const envValue1 = 'topic=openai,translation=/claude-1'; + const envValue2 = 'topic=/gpt-3.5-turbo,translation=anthropic/'; + + expect(() => parseSystemAgent(envValue1)).toThrowError(/Missing model or provider/); + expect(() => parseSystemAgent(envValue2)).toThrowError(/Missing model or provider/); + }); + + it('should throw an error for invalid environment variable format', () => { + const envValue = 'topic-openai/gpt-3.5-turbo'; + + expect(() => parseSystemAgent(envValue)).toThrowError(/Invalid environment variable format/); + }); + + it('should handle provider or model names with special characters', () => { + const envValue = 'topic=openrouter/mistralai/mistral-7b-instruct:free'; + const expected = { + topic: { provider: 'openrouter', model: 'mistralai/mistral-7b-instruct:free' }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should handle extra whitespace in environment variable string', () => { + const envValue = ' topic = openai/gpt-3.5-turbo , translation = anthropic/claude-1 '; + const expected = { + topic: { provider: 'openai', model: 'gpt-3.5-turbo' }, + translation: { provider: 'anthropic', model: 'claude-1' }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should handle full-width comma in environment variable string', () => { + const envValue = 'topic=openai/gpt-3.5-turbo,translation=anthropic/claude-1'; + const expected = { + topic: { provider: 'openai', model: 'gpt-3.5-turbo' }, + translation: { provider: 'anthropic', model: 'claude-1' }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should handle extra whitespace around provider and model names', () => { + const envValue = 'topic= openai / gpt-3.5-turbo ,translation= anthropic / claude-1 '; + const expected = { + topic: { provider: 'openai', model: 'gpt-3.5-turbo' }, + translation: { provider: 'anthropic', model: 'claude-1' }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); + + it('should handle an excessively long environment variable string', () => { + const longProviderName = 'a'.repeat(100); + const longModelName = 'b'.repeat(100); + const envValue = `topic=${longProviderName}/${longModelName}`; + const expected = { + topic: { provider: longProviderName, model: longModelName }, + }; + + expect(parseSystemAgent(envValue)).toEqual(expected); + }); +}); diff --git a/src/server/globalConfig/parseSystemAgent.ts b/src/server/globalConfig/parseSystemAgent.ts new file mode 100644 index 000000000000..85504dd4bc6a --- /dev/null +++ b/src/server/globalConfig/parseSystemAgent.ts @@ -0,0 +1,39 @@ +import { DEFAULT_SYSTEM_AGENT_CONFIG } from '@/const/settings'; +import { UserSystemAgentConfig } from '@/types/user/settings'; + +const protectedKeys = Object.keys(DEFAULT_SYSTEM_AGENT_CONFIG); + +export const parseSystemAgent = (envString: string = ''): Partial => { + if (!envString) return {}; + + const config: Partial = {}; + + // 处理全角逗号和多余空格 + let envValue = envString.replaceAll(',', ',').trim(); + + const pairs = envValue.split(','); + + for (const pair of pairs) { + const [key, value] = pair.split('=').map((s) => s.trim()); + + if (key && value) { + const [provider, ...modelParts] = value.split('/'); + const model = modelParts.join('/'); + + if (!provider || !model) { + throw new Error('Missing model or provider value'); + } + + if (protectedKeys.includes(key)) { + config[key as keyof UserSystemAgentConfig] = { + model: model.trim(), + provider: provider.trim(), + }; + } + } else { + throw new Error('Invalid environment variable format'); + } + } + + return config; +}; diff --git a/src/store/user/slices/common/action.ts b/src/store/user/slices/common/action.ts index d740ebfd3e67..e68f5b14a535 100644 --- a/src/store/user/slices/common/action.ts +++ b/src/store/user/slices/common/action.ts @@ -81,7 +81,9 @@ export const createCommonSlice: StateCreator< const serverSettings: DeepPartial = { defaultAgent: serverConfig.defaultAgent, languageModel: serverConfig.languageModel, + systemAgent: serverConfig.systemAgent, }; + const defaultSettings = merge(get().defaultSettings, serverSettings); // merge preference diff --git a/src/types/serverConfig.ts b/src/types/serverConfig.ts index 0574271d08c0..c2d169f27744 100644 --- a/src/types/serverConfig.ts +++ b/src/types/serverConfig.ts @@ -1,7 +1,11 @@ import { DeepPartial } from 'utility-types'; import { ChatModelCard } from '@/types/llm'; -import { UserDefaultAgent, GlobalLLMProviderKey } from '@/types/user/settings'; +import { + GlobalLLMProviderKey, + UserDefaultAgent, + UserSystemAgentConfig, +} from '@/types/user/settings'; export interface ServerModelProviderConfig { enabled?: boolean; @@ -21,6 +25,7 @@ export interface GlobalServerConfig { enabledAccessCode?: boolean; enabledOAuthSSO?: boolean; languageModel?: ServerLanguageModel; + systemAgent?: DeepPartial; telemetry: { langfuse?: boolean; };