UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

288 lines (243 loc) 9.82 kB
import { describe, expect, it } from 'vitest'; import { LOBE_DEFAULT_MODEL_LIST, OpenAIProviderCard } from '@/config/modelProviders'; import { ChatModelCard } from '@/types/llm'; import { parseModelString, transformToChatModelCards } from './parseModels'; describe('parseModelString', () => { it('custom deletion, addition, and renaming of models', () => { const result = parseModelString( '-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k', ); expect(result).toMatchSnapshot(); }); it('duplicate naming model', () => { const result = parseModelString('gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k'); expect(result).toMatchSnapshot(); }); it('only add the model', () => { const result = parseModelString('model1,model2,model3,model4'); expect(result).toMatchSnapshot(); }); it('empty string model', () => { const result = parseModelString('gpt-4-1106-preview=gpt-4-turbo,, ,\n ,+claude-2'); expect(result).toMatchSnapshot(); }); describe('extension capabilities', () => { it('with token', () => { const result = parseModelString('chatglm-6b=ChatGLM 6B<4096>'); expect(result.add[0]).toEqual({ displayName: 'ChatGLM 6B', id: 'chatglm-6b', contextWindowTokens: 4096, }); }); it('token and function calling', () => { const result = parseModelString('spark-v3.5=讯飞星火 v3.5<8192:fc>'); expect(result.add[0]).toEqual({ displayName: '讯飞星火 v3.5', functionCall: true, id: 'spark-v3.5', contextWindowTokens: 8192, }); }); it('token and reasoning', () => { const result = parseModelString('deepseek-r1=Deepseek R1<65536:reasoning>'); expect(result.add[0]).toEqual({ displayName: 'Deepseek R1', reasoning: true, id: 'deepseek-r1', contextWindowTokens: 65_536, }); }); it('multi models', () => { const result = parseModelString( 'gemini-1.5-flash-latest=Gemini 1.5 Flash<16000:vision>,gpt-4-all=ChatGPT Plus<128000:fc:vision:file>', ); expect(result.add).toEqual([ { displayName: 'Gemini 1.5 Flash', vision: true, id: 'gemini-1.5-flash-latest', contextWindowTokens: 16000, }, { displayName: 'ChatGPT Plus', vision: true, functionCall: true, files: true, id: 'gpt-4-all', contextWindowTokens: 128000, }, ]); }); it('should have file with builtin models like gpt-4-0125-preview', () => { const result = parseModelString( '-all,+gpt-4-0125-preview=ChatGPT-4<128000:fc:file>,+gpt-4-turbo-2024-04-09=ChatGPT-4 Vision<128000:fc:vision:file>', ); expect(result.add).toEqual([ { displayName: 'ChatGPT-4', files: true, functionCall: true, id: 'gpt-4-0125-preview', contextWindowTokens: 128000, }, { displayName: 'ChatGPT-4 Vision', files: true, functionCall: true, id: 'gpt-4-turbo-2024-04-09', contextWindowTokens: 128000, vision: true, }, ]); }); it('should handle empty extension capability value', () => { const result = parseModelString('model1<1024:>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 }); }); it('should handle empty extension capability name', () => { const result = parseModelString('model1<1024::file>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, files: true }); }); it('should handle duplicate extension capabilities', () => { const result = parseModelString('model1<1024:vision:vision>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, vision: true }); }); it('should handle case-sensitive extension capability names', () => { const result = parseModelString('model1<1024:VISION:FC:file>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, files: true }); }); it('should handle case-sensitive extension capability values', () => { const result = parseModelString('model1<1024:vision:Fc:File>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, vision: true }); }); it('should handle empty angle brackets', () => { const result = parseModelString('model1<>'); expect(result.add[0]).toEqual({ id: 'model1' }); }); it('should handle not close angle brackets', () => { const result = parseModelString('model1<,model2'); expect(result.add).toEqual([{ id: 'model1' }, { id: 'model2' }]); }); it('should handle multi close angle brackets', () => { const result = parseModelString('model1<>>,model2'); expect(result.add).toEqual([{ id: 'model1' }, { id: 'model2' }]); }); it('should handle only colon inside angle brackets', () => { const result = parseModelString('model1<:>'); expect(result.add[0]).toEqual({ id: 'model1' }); }); it('should handle only non-digit characters inside angle brackets', () => { const result = parseModelString('model1<abc>'); expect(result.add[0]).toEqual({ id: 'model1' }); }); it('should handle non-digit characters followed by digits inside angle brackets', () => { const result = parseModelString('model1<abc123>'); expect(result.add[0]).toEqual({ id: 'model1' }); }); it('should handle digits followed by non-colon characters inside angle brackets', () => { const result = parseModelString('model1<1024abc>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 }); }); it('should handle digits followed by multiple colons inside angle brackets', () => { const result = parseModelString('model1<1024::>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 }); }); it('should handle digits followed by a colon and non-letter characters inside angle brackets', () => { const result = parseModelString('model1<1024:123>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 }); }); it('should handle digits followed by a colon and spaces inside angle brackets', () => { const result = parseModelString('model1<1024: vision>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 }); }); it('should handle digits followed by multiple colons and spaces inside angle brackets', () => { const result = parseModelString('model1<1024: : vision>'); expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 }); }); }); describe('deployment name', () => { it('should have same deployment name as id', () => { const result = parseModelString('model1=Model 1', true); expect(result.add[0]).toEqual({ id: 'model1', displayName: 'Model 1', deploymentName: 'model1', }); }); it('should have diff deployment name as id', () => { const result = parseModelString('gpt-35-turbo->my-deploy=GPT 3.5 Turbo', true); expect(result.add[0]).toEqual({ id: 'gpt-35-turbo', displayName: 'GPT 3.5 Turbo', deploymentName: 'my-deploy', }); }); }); }); describe('transformToChatModelCards', () => { const defaultChatModels: ChatModelCard[] = [ { id: 'model1', displayName: 'Model 1', enabled: true }, { id: 'model2', displayName: 'Model 2', enabled: false }, ]; it('should return undefined when modelString is empty', () => { const result = transformToChatModelCards({ modelString: '', defaultChatModels, }); expect(result).toBeUndefined(); }); it('should remove all models when removeAll is true', () => { const result = transformToChatModelCards({ modelString: '-all', defaultChatModels, }); expect(result).toEqual([]); }); it('should remove specified models', () => { const result = transformToChatModelCards({ modelString: '-model1', defaultChatModels, }); expect(result).toEqual([{ id: 'model2', displayName: 'Model 2', enabled: false }]); }); it('should add a new known model', () => { const knownModel = LOBE_DEFAULT_MODEL_LIST[0]; const result = transformToChatModelCards({ modelString: `${knownModel.id}`, defaultChatModels, }); expect(result).toContainEqual({ ...knownModel, displayName: knownModel.displayName || knownModel.id, enabled: true, }); }); it('should update an existing known model', () => { const knownModel = LOBE_DEFAULT_MODEL_LIST[0]; const result = transformToChatModelCards({ modelString: `+${knownModel.id}=Updated Model`, defaultChatModels: [knownModel], }); expect(result![0]).toEqual({ ...knownModel, displayName: 'Updated Model', enabled: true }); }); it('should add a new custom model', () => { const result = transformToChatModelCards({ modelString: '+custom_model=Custom Model', defaultChatModels, }); expect(result).toContainEqual({ id: 'custom_model', displayName: 'Custom Model', enabled: true, }); }); it('should have file with builtin models like gpt-4-0125-preview', () => { const result = transformToChatModelCards({ modelString: '-all,+gpt-4-0125-preview=ChatGPT-4<128000:fc:file>,+gpt-4-turbo-2024-04-09=ChatGPT-4 Vision<128000:fc:vision:file>', defaultChatModels: OpenAIProviderCard.chatModels, }); expect(result).toMatchSnapshot(); }); });