/* Copyright (c) 2025 Bernier LLC This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC. */ import { OPENAI_MODELS, getModelInfo, getChatModels, getEmbeddingModels, getVisionModels, supportsFunctionCalling } from '../src/models/model-registry'; describe('Model Registry', () => { describe('OPENAI_MODELS', () => { it('should contain core chat models', () => { const modelIds = OPENAI_MODELS.map(m => m.id); expect(modelIds).toContain('gpt-4-turbo'); expect(modelIds).toContain('gpt-4'); expect(modelIds).toContain('gpt-3.5-turbo'); expect(modelIds).toContain('gpt-4-vision-preview'); }); it('should contain embedding models', () => { const modelIds = OPENAI_MODELS.map(m => m.id); expect(modelIds).toContain('text-embedding-3-small'); expect(modelIds).toContain('text-embedding-3-large'); expect(modelIds).toContain('text-embedding-ada-002'); }); it('should have correct pricing for gpt-4-turbo', () => { const model = OPENAI_MODELS.find(m => m.id === 'gpt-4-turbo'); expect(model).toBeDefined(); expect(model!.pricing).toBeDefined(); expect(model!.pricing!.inputPricePerToken).toBe(0.01 / 1000); expect(model!.pricing!.outputPricePerToken).toBe(0.03 / 1000); expect(model!.pricing!.currency).toBe('USD'); }); it('should have correct pricing for gpt-3.5-turbo', () => { const model = OPENAI_MODELS.find(m => m.id === 'gpt-3.5-turbo'); expect(model).toBeDefined(); expect(model!.pricing).toBeDefined(); expect(model!.pricing!.inputPricePerToken).toBe(0.0005 / 1000); expect(model!.pricing!.outputPricePerToken).toBe(0.0015 / 1000); }); it('should have correct context windows', () => { const gpt4Turbo = OPENAI_MODELS.find(m => m.id === 'gpt-4-turbo'); const gpt4 = OPENAI_MODELS.find(m => m.id === 'gpt-4'); const gpt35 = OPENAI_MODELS.find(m => m.id === 'gpt-3.5-turbo'); expect(gpt4Turbo!.contextWindow).toBe(128000); expect(gpt4!.contextWindow).toBe(8192); expect(gpt35!.contextWindow).toBe(16385); }); it('should have correct capabilities for chat models', () => { const gpt4Turbo = OPENAI_MODELS.find(m => m.id === 'gpt-4-turbo'); expect(gpt4Turbo!.capabilities).toContain('chat'); expect(gpt4Turbo!.capabilities).toContain('completion'); expect(gpt4Turbo!.capabilities).toContain('function-calling'); expect(gpt4Turbo!.capabilities).toContain('vision'); }); it('should have correct capabilities for embedding models', () => { const embedding = OPENAI_MODELS.find(m => m.id === 'text-embedding-3-small'); expect(embedding!.capabilities).toContain('embeddings'); expect(embedding!.capabilities).not.toContain('chat'); }); }); describe('getModelInfo()', () => { it('should return model info for valid model ID', () => { const model = getModelInfo('gpt-4-turbo'); expect(model).toBeDefined(); expect(model!.id).toBe('gpt-4-turbo'); expect(model!.name).toBe('GPT-4 Turbo'); }); it('should return undefined for unknown model ID', () => { const model = getModelInfo('unknown-model'); expect(model).toBeUndefined(); }); it('should return embedding model info', () => { const model = getModelInfo('text-embedding-3-small'); expect(model).toBeDefined(); expect(model!.capabilities).toContain('embeddings'); }); }); describe('getChatModels()', () => { it('should return all chat-capable models', () => { const chatModels = getChatModels(); expect(chatModels.length).toBeGreaterThan(0); chatModels.forEach(model => { const hasChat = model.capabilities.includes('chat') || model.capabilities.includes('completion'); expect(hasChat).toBe(true); }); }); it('should include GPT-4 models', () => { const chatModels = getChatModels(); const modelIds = chatModels.map(m => m.id); expect(modelIds).toContain('gpt-4-turbo'); expect(modelIds).toContain('gpt-4'); expect(modelIds).toContain('gpt-3.5-turbo'); }); it('should not include embedding models', () => { const chatModels = getChatModels(); const hasEmbeddingOnly = chatModels.some(m => m.capabilities.includes('embeddings') && !m.capabilities.includes('chat') && !m.capabilities.includes('completion') ); expect(hasEmbeddingOnly).toBe(false); }); }); describe('getEmbeddingModels()', () => { it('should return all embedding models', () => { const embeddingModels = getEmbeddingModels(); expect(embeddingModels.length).toBeGreaterThan(0); embeddingModels.forEach(model => { expect(model.capabilities).toContain('embeddings'); }); }); it('should include all embedding model variants', () => { const embeddingModels = getEmbeddingModels(); const modelIds = embeddingModels.map(m => m.id); expect(modelIds).toContain('text-embedding-3-small'); expect(modelIds).toContain('text-embedding-3-large'); expect(modelIds).toContain('text-embedding-ada-002'); }); it('should not include chat models', () => { const embeddingModels = getEmbeddingModels(); const hasChatOnly = embeddingModels.some(m => (m.capabilities.includes('chat') || m.capabilities.includes('completion')) && !m.capabilities.includes('embeddings') ); expect(hasChatOnly).toBe(false); }); }); describe('getVisionModels()', () => { it('should return all vision-capable models', () => { const visionModels = getVisionModels(); expect(visionModels.length).toBeGreaterThan(0); visionModels.forEach(model => { expect(model.capabilities).toContain('vision'); }); }); it('should include GPT-4 Vision models', () => { const visionModels = getVisionModels(); const modelIds = visionModels.map(m => m.id); expect(modelIds).toContain('gpt-4-vision-preview'); expect(modelIds).toContain('gpt-4-turbo'); }); it('should not include non-vision models', () => { const visionModels = getVisionModels(); const modelIds = visionModels.map(m => m.id); expect(modelIds).not.toContain('gpt-3.5-turbo'); expect(modelIds).not.toContain('text-embedding-3-small'); }); }); describe('supportsFunctionCalling()', () => { it('should return true for models with function calling capability', () => { expect(supportsFunctionCalling('gpt-4-turbo')).toBe(true); expect(supportsFunctionCalling('gpt-4')).toBe(true); expect(supportsFunctionCalling('gpt-3.5-turbo')).toBe(true); }); it('should return false for embedding models', () => { expect(supportsFunctionCalling('text-embedding-3-small')).toBe(false); expect(supportsFunctionCalling('text-embedding-3-large')).toBe(false); }); it('should return false for unknown models', () => { expect(supportsFunctionCalling('unknown-model')).toBe(false); }); it('should return false for vision-only models without function calling', () => { const visionModel = OPENAI_MODELS.find(m => m.capabilities.includes('vision') && !m.capabilities.includes('function-calling') ); if (visionModel) { expect(supportsFunctionCalling(visionModel.id)).toBe(false); } }); }); describe('Model Completeness', () => { it('should have all required fields for each model', () => { OPENAI_MODELS.forEach(model => { expect(model.id).toBeDefined(); expect(model.name).toBeDefined(); expect(model.contextWindow).toBeGreaterThan(0); expect(model.maxOutputTokens).toBeGreaterThanOrEqual(0); expect(model.capabilities).toBeDefined(); expect(model.capabilities.length).toBeGreaterThan(0); }); }); it('should have pricing for all models', () => { OPENAI_MODELS.forEach(model => { if (model.capabilities.includes('chat') || model.capabilities.includes('completion') || model.capabilities.includes('embeddings')) { expect(model.pricing).toBeDefined(); expect(model.pricing!.inputPricePerToken).toBeGreaterThan(0); expect(model.pricing!.currency).toBe('USD'); } }); }); it('should have valid capability combinations', () => { OPENAI_MODELS.forEach(model => { const capabilities = model.capabilities; // Embedding models shouldn't have chat/completion if (capabilities.includes('embeddings') && capabilities.length === 1) { expect(capabilities).not.toContain('chat'); expect(capabilities).not.toContain('completion'); } // Vision models should also have chat/completion if (capabilities.includes('vision')) { const hasChatOrCompletion = capabilities.includes('chat') || capabilities.includes('completion'); expect(hasChatOrCompletion).toBe(true); } }); }); }); });