1
import { describe, expect, it } from 'vitest';
3
import { LOBE_DEFAULT_MODEL_LIST, OpenAIProviderCard } from '@/config/modelProviders';
4
import { ChatModelCard } from '@/types/llm';
6
import { parseModelString, transformToChatModelCards } from './parseModels';
8
describe('parseModelString', () => {
9
it('custom deletion, addition, and renaming of models', () => {
10
const result = parseModelString(
11
'-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
14
expect(result).toMatchSnapshot();
17
it('duplicate naming model', () => {
18
const result = parseModelString('gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k');
19
expect(result).toMatchSnapshot();
22
it('only add the model', () => {
23
const result = parseModelString('model1,model2,model3,model4');
25
expect(result).toMatchSnapshot();
28
describe('extension capabilities', () => {
29
it('with token', () => {
30
const result = parseModelString('chatglm-6b=ChatGLM 6B<4096>');
32
expect(result.add[0]).toEqual({
33
displayName: 'ChatGLM 6B',
39
it('token and function calling', () => {
40
const result = parseModelString('spark-v3.5=讯飞星火 v3.5<8192:fc>');
42
expect(result.add[0]).toEqual({
43
displayName: '讯飞星火 v3.5',
50
it('multi models', () => {
51
const result = parseModelString(
52
'gemini-1.5-flash-latest=Gemini 1.5 Flash<16000:vision>,gpt-4-all=ChatGPT Plus<128000:fc:vision:file>',
55
expect(result.add).toEqual([
57
displayName: 'Gemini 1.5 Flash',
59
id: 'gemini-1.5-flash-latest',
63
displayName: 'ChatGPT Plus',
73
it('should have file with builtin models like gpt-4-0125-preview', () => {
74
const result = parseModelString(
75
'-all,+gpt-4-0125-preview=ChatGPT-4<128000:fc:file>,+gpt-4-turbo-2024-04-09=ChatGPT-4 Vision<128000:fc:vision:file>',
77
expect(result.add).toEqual([
79
displayName: 'ChatGPT-4',
82
id: 'gpt-4-0125-preview',
86
displayName: 'ChatGPT-4 Vision',
89
id: 'gpt-4-turbo-2024-04-09',
96
it('should handle empty extension capability value', () => {
97
const result = parseModelString('model1<1024:>');
98
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024 });
101
it('should handle empty extension capability name', () => {
102
const result = parseModelString('model1<1024::file>');
103
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024, files: true });
106
it('should handle duplicate extension capabilities', () => {
107
const result = parseModelString('model1<1024:vision:vision>');
108
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024, vision: true });
111
it('should handle case-sensitive extension capability names', () => {
112
const result = parseModelString('model1<1024:VISION:FC:file>');
113
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024, files: true });
116
it('should handle case-sensitive extension capability values', () => {
117
const result = parseModelString('model1<1024:vision:Fc:File>');
118
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024, vision: true });
121
it('should handle empty angle brackets', () => {
122
const result = parseModelString('model1<>');
123
expect(result.add[0]).toEqual({ id: 'model1' });
126
it('should handle not close angle brackets', () => {
127
const result = parseModelString('model1<,model2');
128
expect(result.add).toEqual([{ id: 'model1' }, { id: 'model2' }]);
131
it('should handle multi close angle brackets', () => {
132
const result = parseModelString('model1<>>,model2');
133
expect(result.add).toEqual([{ id: 'model1' }, { id: 'model2' }]);
136
it('should handle only colon inside angle brackets', () => {
137
const result = parseModelString('model1<:>');
138
expect(result.add[0]).toEqual({ id: 'model1' });
141
it('should handle only non-digit characters inside angle brackets', () => {
142
const result = parseModelString('model1<abc>');
143
expect(result.add[0]).toEqual({ id: 'model1' });
146
it('should handle non-digit characters followed by digits inside angle brackets', () => {
147
const result = parseModelString('model1<abc123>');
148
expect(result.add[0]).toEqual({ id: 'model1' });
151
it('should handle digits followed by non-colon characters inside angle brackets', () => {
152
const result = parseModelString('model1<1024abc>');
153
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024 });
156
it('should handle digits followed by multiple colons inside angle brackets', () => {
157
const result = parseModelString('model1<1024::>');
158
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024 });
161
it('should handle digits followed by a colon and non-letter characters inside angle brackets', () => {
162
const result = parseModelString('model1<1024:123>');
163
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024 });
166
it('should handle digits followed by a colon and spaces inside angle brackets', () => {
167
const result = parseModelString('model1<1024: vision>');
168
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024 });
171
it('should handle digits followed by multiple colons and spaces inside angle brackets', () => {
172
const result = parseModelString('model1<1024: : vision>');
173
expect(result.add[0]).toEqual({ id: 'model1', tokens: 1024 });
177
describe('deployment name', () => {
178
it('should have same deployment name as id', () => {
179
const result = parseModelString('model1=Model 1', true);
180
expect(result.add[0]).toEqual({
182
displayName: 'Model 1',
183
deploymentName: 'model1',
187
it('should have diff deployment name as id', () => {
188
const result = parseModelString('gpt-35-turbo->my-deploy=GPT 3.5 Turbo', true);
189
expect(result.add[0]).toEqual({
191
displayName: 'GPT 3.5 Turbo',
192
deploymentName: 'my-deploy',
198
describe('transformToChatModelCards', () => {
199
const defaultChatModels: ChatModelCard[] = [
200
{ id: 'model1', displayName: 'Model 1', enabled: true },
201
{ id: 'model2', displayName: 'Model 2', enabled: false },
204
it('should return undefined when modelString is empty', () => {
205
const result = transformToChatModelCards({
209
expect(result).toBeUndefined();
212
it('should remove all models when removeAll is true', () => {
213
const result = transformToChatModelCards({
217
expect(result).toEqual([]);
220
it('should remove specified models', () => {
221
const result = transformToChatModelCards({
222
modelString: '-model1',
225
expect(result).toEqual([{ id: 'model2', displayName: 'Model 2', enabled: false }]);
228
it('should add a new known model', () => {
229
const knownModel = LOBE_DEFAULT_MODEL_LIST[0];
230
const result = transformToChatModelCards({
231
modelString: `${knownModel.id}`,
234
expect(result).toContainEqual({
236
displayName: knownModel.displayName || knownModel.id,
241
it('should update an existing known model', () => {
242
const knownModel = LOBE_DEFAULT_MODEL_LIST[0];
243
const result = transformToChatModelCards({
244
modelString: `+${knownModel.id}=Updated Model`,
245
defaultChatModels: [knownModel],
247
expect(result![0]).toEqual({ ...knownModel, displayName: 'Updated Model', enabled: true });
250
it('should add a new custom model', () => {
251
const result = transformToChatModelCards({
252
modelString: '+custom_model=Custom Model',
255
expect(result).toContainEqual({
257
displayName: 'Custom Model',
262
it('should have file with builtin models like gpt-4-0125-preview', () => {
263
const result = transformToChatModelCards({
265
'-all,+gpt-4-0125-preview=ChatGPT-4<128000:fc:file>,+gpt-4-turbo-2024-04-09=ChatGPT-4 Vision<128000:fc:vision:file>',
266
defaultChatModels: OpenAIProviderCard.chatModels,
269
expect(result).toMatchSnapshot();