forked from lobehub/lobe-chat
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ feat: Support define default agent config with `DEFAULT_AGENT_CONFI…
…G` ENV (lobehub#1291) * ✨ feat: add parser * ✨ feat: add parser * ✨ feat: support DEFAULT_AGENT_CONFIG env * ✨ feat: add parser * 📝 docs: add docs * 🐛 fix: fix max token config
- Loading branch information
Showing
8 changed files
with
288 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
|
||
import { parseAgentConfig } from './parseDefaultAgent'; | ||
|
||
describe('parseAgentConfig', () => { | ||
describe('single functional feature', () => { | ||
it('parses single key-value pair correctly', () => { | ||
const envStr = 'model=gpt-4'; | ||
const expected = { model: 'gpt-4' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses nested fields correctly', () => { | ||
const envStr = 'tts.sttLocale=en-US'; | ||
const expected = { tts: { sttLocale: 'en-US' } }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses array values with commas correctly', () => { | ||
const envStr = 'plugins=search-engine,lobe-image-designer'; | ||
const expected = { plugins: ['search-engine', 'lobe-image-designer'] }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses array values with Chinese commas correctly', () => { | ||
const envStr = 'plugins=search-engine,lobe-image-designer'; | ||
const expected = { plugins: ['search-engine', 'lobe-image-designer'] }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses multiple key-value pairs correctly', () => { | ||
const envStr = 'model=gpt-4;version=1.0.0'; | ||
const expected = { model: 'gpt-4', version: '1.0.0' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试数字值是否被正确解析为数字 | ||
it('parses numerical values correctly', () => { | ||
const envStr = 'params.max_tokens=300'; | ||
const expected = { params: { max_tokens: 300 } }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试多级嵌套属性是否被正确解析 | ||
it('parses deeply nested fields correctly', () => { | ||
const envStr = 'tts.voice.openai=english-voice'; | ||
const expected = { tts: { voice: { openai: 'english-voice' } } }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses boolean values correctly', () => { | ||
const envStr = 'enableAutoCreateTopic=true;enableCompressThreshold=false'; | ||
const expected = { | ||
enableAutoCreateTopic: true, | ||
enableCompressThreshold: false, | ||
}; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses fewShots array with cascading keys correctly', () => { | ||
const envStr = | ||
'fewShots.0.content=Hello;fewShots.0.role=user;fewShots.1.content=Hi;fewShots.1.role=system'; | ||
const expected = { | ||
fewShots: [ | ||
{ content: 'Hello', role: 'user' }, | ||
{ content: 'Hi', role: 'system' }, | ||
], | ||
}; | ||
|
||
// Assuming parseAgentConfig function has been implemented to understand and correctly parse the cascading keys format for fewShots | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses tts voice configuration correctly', () => { | ||
const envStr = 'tts.voice.openai=english-voice;tts.voice.microsoft=spanish-voice'; | ||
const expected = { | ||
tts: { voice: { openai: 'english-voice', microsoft: 'spanish-voice' } }, | ||
}; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('parses inputTemplate with special characters correctly', () => { | ||
const envStr = 'inputTemplate="Hello, I am {name}"'; | ||
const expected = { inputTemplate: 'Hello, I am {name}' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
}); | ||
|
||
describe('complex environment', () => { | ||
it.skip('parses a complete environment variable string correctly', () => { | ||
const envStr = | ||
'model=gpt-4-1106-preview;params.max_tokens=300;plugins=search-engine,lobe-image-designer'; | ||
const expected = { | ||
model: 'gpt-4-1106-preview', | ||
params: { max_tokens: 300 }, | ||
plugins: ['search-engine', 'lobe-image-designer'], | ||
}; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试当配置字符串包含所有可能字段时的行为 | ||
it('parses a complex environment variable string correctly', () => { | ||
const envStr = | ||
'model=gpt-4-1106-preview;params.max_tokens=300;params.temperature=0.7;plugins=search-engine,lobe-image-designer;tts.voice.openai=english-voice'; | ||
const expected = { | ||
model: 'gpt-4-1106-preview', | ||
params: { max_tokens: 300, temperature: 0.7 }, | ||
plugins: ['search-engine', 'lobe-image-designer'], | ||
tts: { voice: { openai: 'english-voice' } }, | ||
}; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
}); | ||
|
||
describe('Error Boundary', () => { | ||
it('handles empty string input', () => { | ||
const envStr = ''; | ||
const expected = {}; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('ignores entries without an equal sign', () => { | ||
const envStr = 'model=gpt-4;invalidentry'; | ||
const expected = { model: 'gpt-4' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('handles entries with missing value', () => { | ||
const envStr = 'model=gpt-4;version='; | ||
const expected = { model: 'gpt-4', version: undefined }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('handles entries with missing key', () => { | ||
const envStr = '=gpt-4;version=1.0.0'; | ||
const expected = { version: '1.0.0' }; // Assuming the parser ignores entries with no key | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
it('handles multiple consecutive semicolons', () => { | ||
const envStr = 'model=gpt-4;;version=1.0.0'; | ||
const expected = { model: 'gpt-4', version: '1.0.0' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试键重复时的覆盖行为 | ||
it('overrides duplicate keys with the last occurrence', () => { | ||
const envStr = 'model=gpt-4;model=gpt-4-1106-preview'; | ||
const expected = { model: 'gpt-4-1106-preview' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试未提供的数组值是否返回空数组 | ||
it('parses missing array values as undefined', () => { | ||
const envStr = 'plugins='; | ||
const expected = {}; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
}); | ||
|
||
// 测试值中包含分号的情况 | ||
it('handles values with semicolons correctly', () => { | ||
const envStr = 'inputTemplate="Hello; I am a bot;"'; | ||
const expected = { inputTemplate: 'Hello; I am a bot;' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试值中包含等号的情况 | ||
it('handles values with equals signs correctly', () => { | ||
const envStr = 'inputTemplate="Hello=world"'; | ||
const expected = { inputTemplate: 'Hello=world' }; | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
|
||
// 测试空值是否返回undefined或空字符串 | ||
it('parses empty values as undefined or empty string', () => { | ||
const envStr = 'model='; | ||
const expected = { model: undefined }; // 或 { model: '' },取决于应用逻辑 | ||
expect(parseAgentConfig(envStr)).toEqual(expected); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { set } from 'lodash-es'; | ||
|
||
/** | ||
* Improved parsing function that handles numbers, booleans, semicolons, and equals signs in values. | ||
* @param {string} envStr - The environment variable string to be parsed. | ||
*/ | ||
export const parseAgentConfig = (envStr: string) => { | ||
const config = {}; | ||
// use regex to match key-value pairs, considering the possibility of semicolons in values | ||
const regex = /([^;=]+)=("[^"]+"|[^;]+)/g; | ||
let match; | ||
|
||
while ((match = regex.exec(envStr)) !== null) { | ||
const key = match[1].trim(); | ||
let value = match[2].trim(); | ||
if (!key || !value) return; | ||
|
||
let finalValue: any = value; | ||
|
||
// Handle string value | ||
if (value.startsWith('"') && value.endsWith('"')) { | ||
finalValue = value.slice(1, -1); | ||
} | ||
// Handle numeric values | ||
else if (!isNaN(value as any)) { | ||
finalValue = Number(value); | ||
} | ||
// Handle boolean values | ||
else if (value.toLowerCase() === 'true' || value.toLowerCase() === 'false') { | ||
finalValue = value.toLowerCase() === 'true'; | ||
} | ||
// Handle arrays | ||
else if (value.includes(',') || value.includes(',')) { | ||
const array = value.replaceAll(',', ',').split(','); | ||
finalValue = array.map((item) => (isNaN(item as any) ? item : Number(item))); | ||
} | ||
|
||
set(config, key, finalValue); | ||
} | ||
|
||
return config; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters