139 lines
4.7 KiB
TypeScript
139 lines
4.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { normModelKey, findDevModel, sanitizePositiveInt, mergeModelCapabilities } from '../src/client/modelsDev';
|
|
import { I18N_DICT } from '../src/client/i18n';
|
|
|
|
describe('modelsDev utils', () => {
|
|
it('normalizes model keys consistently', () => {
|
|
expect(normModelKey('openai/gpt-4o-mini-20240718')).toBe('gpt4omini');
|
|
expect(normModelKey('claude-3-5-sonnet-20241022')).toBe('claude35sonnet');
|
|
expect(normModelKey('deepseek/deepseek-chat')).toBe('deepseekchat');
|
|
});
|
|
|
|
it('matches fuzzy dev models', () => {
|
|
const list = [
|
|
{ id: 'openai/gpt-4o', rawKey: 'gpt-4o', name: 'GPT-4o', modalities: { input: ['text', 'image'] }, reasoning: false },
|
|
{ id: 'anthropic/claude-3-5-sonnet', rawKey: 'claude-3-5-sonnet', name: 'Claude 3.5 Sonnet', modalities: { input: ['text', 'image'] }, reasoning: false },
|
|
{ id: 'deepseek/deepseek-r1', rawKey: 'deepseek-r1', name: 'DeepSeek R1', modalities: { input: ['text'] }, reasoning: true }
|
|
];
|
|
|
|
expect(findDevModel('gpt-4o', list)?.id).toBe('openai/gpt-4o');
|
|
expect(findDevModel('claude-3-5-sonnet-20241022', list)?.id).toBe('anthropic/claude-3-5-sonnet');
|
|
expect(findDevModel('deepseek-r1', list)?.id).toBe('deepseek/deepseek-r1');
|
|
});
|
|
|
|
it('sanitizes positive integers correctly and filters non-positive or non-finite values', () => {
|
|
expect(sanitizePositiveInt(128000)).toBe(128000);
|
|
expect(sanitizePositiveInt(1)).toBe(1);
|
|
expect(sanitizePositiveInt('4096')).toBe(4096);
|
|
|
|
expect(sanitizePositiveInt(0)).toBeUndefined();
|
|
expect(sanitizePositiveInt(-10)).toBeUndefined();
|
|
expect(sanitizePositiveInt(NaN)).toBeUndefined();
|
|
expect(sanitizePositiveInt(Infinity)).toBeUndefined();
|
|
expect(sanitizePositiveInt(-Infinity)).toBeUndefined();
|
|
expect(sanitizePositiveInt(128.5)).toBeUndefined();
|
|
expect(sanitizePositiveInt('0')).toBeUndefined();
|
|
expect(sanitizePositiveInt('-1')).toBeUndefined();
|
|
expect(sanitizePositiveInt('abc')).toBeUndefined();
|
|
expect(sanitizePositiveInt(null)).toBeUndefined();
|
|
expect(sanitizePositiveInt(undefined)).toBeUndefined();
|
|
});
|
|
|
|
it('merges model capabilities without writing zero or invalid contextWindow/maxTokens', () => {
|
|
const matchedWithZero = {
|
|
id: 'test/model-zero',
|
|
rawKey: 'model-zero',
|
|
name: 'Model Zero',
|
|
limit: {
|
|
context: 0,
|
|
output: 0
|
|
},
|
|
modalities: { input: ['text'] },
|
|
reasoning: false
|
|
};
|
|
|
|
const targetModel = {
|
|
id: 'model-zero',
|
|
name: 'Existing Name'
|
|
};
|
|
|
|
const merged = mergeModelCapabilities(targetModel, matchedWithZero as any);
|
|
expect(merged.id).toBe('model-zero');
|
|
expect(merged.name).toBe('Existing Name');
|
|
expect(merged.contextWindow).toBeUndefined();
|
|
expect('contextWindow' in merged).toBe(false);
|
|
expect(merged.maxTokens).toBeUndefined();
|
|
expect('maxTokens' in merged).toBe(false);
|
|
});
|
|
|
|
it('preserves valid existing contextWindow/maxTokens when incoming is zero or missing', () => {
|
|
const matchedWithZero = {
|
|
id: 'test/model-zero',
|
|
rawKey: 'model-zero',
|
|
name: 'Model Zero',
|
|
limit: {
|
|
context: 0,
|
|
output: 0
|
|
}
|
|
};
|
|
|
|
const existingModel = {
|
|
id: 'model-zero',
|
|
contextWindow: 131072,
|
|
maxTokens: 8192
|
|
};
|
|
|
|
const merged = mergeModelCapabilities(existingModel, matchedWithZero as any);
|
|
expect(merged.contextWindow).toBe(131072);
|
|
expect(merged.maxTokens).toBe(8192);
|
|
});
|
|
|
|
it('populates valid positive contextWindow/maxTokens from models.dev when existing is missing', () => {
|
|
const matchedValid = {
|
|
id: 'openai/gpt-4o',
|
|
rawKey: 'gpt-4o',
|
|
name: 'GPT-4o',
|
|
limit: {
|
|
context: 128000,
|
|
output: 16384
|
|
}
|
|
};
|
|
|
|
const existingModel = {
|
|
id: 'gpt-4o'
|
|
};
|
|
|
|
const merged = mergeModelCapabilities(existingModel, matchedValid as any);
|
|
expect(merged.contextWindow).toBe(128000);
|
|
expect(merged.maxTokens).toBe(16384);
|
|
});
|
|
|
|
it('cleans up invalid existing values if present and matched is missing or zero', () => {
|
|
const matchedWithZero = {
|
|
id: 'test/model-bad',
|
|
rawKey: 'model-bad',
|
|
limit: {
|
|
context: 0,
|
|
output: -1
|
|
}
|
|
};
|
|
|
|
const existingModelWithZero = {
|
|
id: 'model-bad',
|
|
contextWindow: 0,
|
|
maxTokens: -5
|
|
};
|
|
|
|
const merged = mergeModelCapabilities(existingModelWithZero, matchedWithZero as any);
|
|
expect('contextWindow' in merged).toBe(false);
|
|
expect('maxTokens' in merged).toBe(false);
|
|
});
|
|
|
|
it('provides bilingual dictionary keys', () => {
|
|
expect(I18N_DICT.zh.modalTitle).toBeDefined();
|
|
expect(I18N_DICT.en.modalTitle).toBeDefined();
|
|
expect(I18N_DICT.zh.batchBtn).toBeDefined();
|
|
expect(I18N_DICT.en.batchBtn).toBeDefined();
|
|
});
|
|
});
|