Spaces:
Runtime error
Runtime error
| import { describe, it, expect, beforeAll } from 'vitest'; | |
| import type { Express } from 'express'; | |
| import { createApp } from '../../app.js'; | |
| import { initDb, getDb, getUnifiedApiKey } from '../../db/index.js'; | |
| async function post(app: Express, path: string, body: any, key: string) { | |
| const server = app.listen(0); | |
| const addr = server.address() as any; | |
| const res = await fetch(`http://127.0.0.1:${addr.port}${path}`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` }, | |
| body: JSON.stringify(body), | |
| }); | |
| const text = await res.text(); | |
| server.close(); | |
| let json: any = null; | |
| try { json = JSON.parse(text); } catch {} | |
| return { status: res.status, body: json }; | |
| } | |
| const IMAGE_MESSAGE = { | |
| messages: [{ | |
| role: 'user', | |
| content: [ | |
| { type: 'text', text: 'what is in this image?' }, | |
| { type: 'image_url', image_url: { url: 'data:image/png;base64,iVBORw0KGgo=' } }, | |
| ], | |
| }], | |
| }; | |
| describe('Vision-aware routing (#118, #125)', () => { | |
| let app: Express; | |
| let key: string; | |
| beforeAll(() => { | |
| process.env.ENCRYPTION_KEY = '0'.repeat(64); | |
| initDb(':memory:'); | |
| app = createApp(); | |
| key = getUnifiedApiKey(); | |
| }); | |
| it('seeds supports_vision: true for vision models, false for text-only', () => { | |
| const db = getDb(); | |
| const vision = db.prepare("SELECT supports_vision FROM models WHERE model_id = 'gemini-2.5-flash'").get() as { supports_vision: number }; | |
| expect(vision.supports_vision).toBe(1); | |
| // The known vision set is flagged; plenty of text-only models remain at 0. | |
| const visionCount = (db.prepare('SELECT COUNT(*) c FROM models WHERE supports_vision = 1').get() as { c: number }).c; | |
| const textCount = (db.prepare('SELECT COUNT(*) c FROM models WHERE supports_vision = 0').get() as { c: number }).c; | |
| expect(visionCount).toBeGreaterThanOrEqual(4); | |
| expect(textCount).toBeGreaterThan(0); | |
| }); | |
| it('lets an image request through routing when a vision model is enabled (no 422)', async () => { | |
| // Seed has Gemini/Llama-4 vision models enabled but no provider keys, so | |
| // routing exhausts → 429. The point: it is NOT the 422 "no vision model" | |
| // error, proving the precheck passed and routing was attempted. | |
| const { status, body } = await post(app, '/v1/chat/completions', IMAGE_MESSAGE, key); | |
| expect(status).not.toBe(422); | |
| expect(body?.error?.code).not.toBe('no_vision_model'); | |
| }); | |
| it('rejects an image request with a clear 422 when no vision model is enabled', async () => { | |
| // Disable every vision-capable model in the chain. | |
| getDb().prepare('UPDATE models SET enabled = 0 WHERE supports_vision = 1').run(); | |
| const { status, body } = await post(app, '/v1/chat/completions', IMAGE_MESSAGE, key); | |
| expect(status).toBe(422); | |
| expect(body.error.code).toBe('no_vision_model'); | |
| expect(body.error.type).toBe('invalid_request_error'); | |
| // Restore so we don't leak state to other expectations. | |
| getDb().prepare('UPDATE models SET enabled = 1 WHERE supports_vision = 1').run(); | |
| }); | |
| it('does not apply the vision gate to a text-only request', async () => { | |
| // Disable all vision models; a plain text request must still route normally | |
| // (it 429s on exhaustion here, but never the 422 vision error). | |
| getDb().prepare('UPDATE models SET enabled = 0 WHERE supports_vision = 1').run(); | |
| const { status, body } = await post(app, '/v1/chat/completions', { | |
| messages: [{ role: 'user', content: 'hello' }], | |
| }, key); | |
| expect(status).not.toBe(422); | |
| expect(body?.error?.code).not.toBe('no_vision_model'); | |
| getDb().prepare('UPDATE models SET enabled = 1 WHERE supports_vision = 1').run(); | |
| }); | |
| }); | |