UNPKG

modelmix

Version:

🧬 ModelMix - Unified API for Diverse AI LLM.

295 lines (246 loc) • 9.87 kB
const { expect } = require('chai'); const sinon = require('sinon'); const nock = require('nock'); const { ModelMix } = require('../index.js'); const generateJsonSchema = require('../schema.js'); describe('JSON Schema and Structured Output Tests', () => { // Setup test hooks if (global.setupTestHooks) { global.setupTestHooks(); } afterEach(() => { nock.cleanAll(); sinon.restore(); }); describe('JSON Schema Generation', () => { it('should generate schema for simple object', () => { const example = { name: 'Alice', age: 30, isAdmin: false }; const schema = generateJsonSchema(example); expect(schema).to.deep.equal({ type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' }, isAdmin: { type: 'boolean' } }, required: ['name', 'age', 'isAdmin'] }); }); it('should generate schema with email format detection', () => { const example = { email: 'test@example.com' }; const schema = generateJsonSchema(example); expect(schema.properties.email).to.deep.equal({ type: 'string', format: 'email' }); }); it('should generate schema with date format detection', () => { const example = { birthDate: '1990-01-01' }; const schema = generateJsonSchema(example); expect(schema.properties.birthDate).to.deep.equal({ type: 'string', format: 'date', description: 'Date in format YYYY-MM-DD' }); }); it('should generate schema with time format detection', () => { const example = { time1: '14:30', time2: '09:15:45' }; const schema = generateJsonSchema(example); expect(schema.properties.time1).to.deep.equal({ type: 'string', format: 'time', description: 'Time in format HH:MM' }); expect(schema.properties.time2).to.deep.equal({ type: 'string', format: 'time', description: 'Time in format HH:MM:SS' }); }); it('should handle nested objects', () => { const example = { user: { name: 'Bob', preferences: { theme: 'dark' } } }; const schema = generateJsonSchema(example); expect(schema.properties.user.type).to.equal('object'); expect(schema.properties.user.properties.name).to.deep.equal({ type: 'string' }); expect(schema.properties.user.properties.preferences.type).to.equal('object'); }); it('should handle arrays of objects', () => { const example = { users: [{ name: 'Alice', age: 30 }] }; const schema = generateJsonSchema(example); expect(schema.properties.users).to.deep.equal({ type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name', 'age'] } }); }); it('should handle arrays of primitives', () => { const example = { tags: ['admin', 'user'], numbers: [1, 2, 3] }; const schema = generateJsonSchema(example); expect(schema.properties.tags).to.deep.equal({ type: 'array', items: { type: 'string' } }); expect(schema.properties.numbers).to.deep.equal({ type: 'array', items: { type: 'integer' } }); }); it('should handle custom descriptions', () => { const example = { name: 'Alice', age: 30 }; const descriptions = { name: 'Full name of the user', age: 'User age in years' }; const schema = generateJsonSchema(example, descriptions); expect(schema.properties.name.description).to.equal('Full name of the user'); expect(schema.properties.age.description).to.equal('User age in years'); }); it('should handle null values', () => { const example = { data: null }; const schema = generateJsonSchema(example); expect(schema.properties.data).to.deep.equal({ type: 'null' }); }); it('should distinguish between integer and float', () => { const example = { count: 42, price: 19.99 }; const schema = generateJsonSchema(example); expect(schema.properties.count).to.deep.equal({ type: 'integer' }); expect(schema.properties.price).to.deep.equal({ type: 'number' }); }); it('should handle empty arrays', () => { const example = { items: [] }; const schema = generateJsonSchema(example); expect(schema.properties.items).to.deep.equal({ type: 'array', items: {} }); }); }); describe('ModelMix JSON Output', () => { let model; beforeEach(() => { model = ModelMix.new({ config: { debug: false } }); }); it('should prepare JSON schema for structured output', async () => { const example = { countries: [{ name: 'France', capital: 'Paris' }] }; model.gpt4o().addText('List 3 countries'); // Mock the API response nock('https://api.openai.com') .post('/v1/chat/completions') .reply(200, { choices: [{ message: { role: 'assistant', content: JSON.stringify({ countries: [ { name: 'France', capital: 'Paris' }, { name: 'Germany', capital: 'Berlin' }, { name: 'Spain', capital: 'Madrid' } ] }) } }] }); const result = await model.json(example); expect(result).to.have.property('countries'); expect(result.countries).to.be.an('array'); expect(result.countries).to.have.length(3); expect(result.countries[0]).to.have.property('name'); expect(result.countries[0]).to.have.property('capital'); }); it('should handle complex nested JSON schema', async () => { const example = { user: { personal: { name: 'John', age: 25 }, contact: { email: 'john@example.com', phone: '123-456-7890' }, preferences: { notifications: true, theme: 'dark' } }, metadata: { createdAt: '2023-01-01', tags: ['premium', 'verified'] } }; model.sonnet4().addText('Generate user data'); // Mock the API response nock('https://api.anthropic.com') .post('/v1/messages') .reply(200, { content: [{ type: 'text', text: JSON.stringify(example) }] }); const result = await model.json(example); expect(result.user.personal.name).to.equal('John'); expect(result.user.contact.email).to.equal('john@example.com'); expect(result.metadata.tags).to.be.an('array'); expect(result.metadata.tags).to.deep.equal(['premium', 'verified']); }); it('should handle JSON parsing errors gracefully', async () => { model.gpt4o().addText('Generate invalid JSON'); // Mock invalid JSON response nock('https://api.openai.com') .post('/v1/chat/completions') .reply(200, { choices: [{ message: { role: 'assistant', content: 'This is not valid JSON' } }] }); try { await model.json({ name: 'test' }); expect.fail('Should have thrown an error'); } catch (error) { expect(error.message).to.include('JSON'); } }); }); });