UNPKG

ai-functions

Version:

Core AI primitives for building intelligent applications

158 lines (157 loc) 6.07 kB
/** * Tests for the ai proxy and AI() schema functions * * These tests use real AI calls via the Cloudflare AI Gateway. */ import { describe, it, expect, beforeEach } from 'vitest'; import { ai, AI, functions, withTemplate } from '../src/index.js'; // Skip tests if no gateway configured const hasGateway = !!process.env.AI_GATEWAY_URL || !!process.env.ANTHROPIC_API_KEY; describe('ai proxy', () => { beforeEach(() => { functions.clear(); }); it('exposes functions registry', () => { expect(ai.functions).toBeDefined(); expect(typeof ai.functions.list).toBe('function'); expect(typeof ai.functions.get).toBe('function'); expect(typeof ai.functions.set).toBe('function'); expect(typeof ai.functions.has).toBe('function'); expect(typeof ai.functions.clear).toBe('function'); expect(typeof ai.functions.delete).toBe('function'); }); it('exposes define helpers', () => { expect(ai.define).toBeDefined(); expect(typeof ai.define).toBe('function'); expect(typeof ai.define.generative).toBe('function'); expect(typeof ai.define.agentic).toBe('function'); expect(typeof ai.define.human).toBe('function'); expect(typeof ai.define.code).toBe('function'); }); it('exposes defineFunction', () => { expect(typeof ai.defineFunction).toBe('function'); }); }); describe.skipIf(!hasGateway)('ai proxy auto-define', () => { beforeEach(() => { functions.clear(); }); it('auto-defines a function on first call', async () => { expect(functions.has('greetPerson')).toBe(false); const result = await ai.greetPerson({ name: 'Alice', style: 'friendly', }); expect(result).toBeDefined(); expect(functions.has('greetPerson')).toBe(true); }); it('uses cached definition on second call', async () => { // First call - defines the function await ai.capitalizeText({ text: 'hello', }); const fn1 = functions.get('capitalizeText'); expect(fn1).toBeDefined(); // Second call - uses cached definition await ai.capitalizeText({ text: 'world', }); const fn2 = functions.get('capitalizeText'); expect(fn1).toBe(fn2); // Same cached function }); }); describe.skipIf(!hasGateway)('AI() schema functions', () => { it('creates schema-based functions', async () => { const client = AI({ sentiment: { sentiment: 'positive | negative | neutral', score: 'Confidence score 0-1 (number)', explanation: 'Brief explanation', }, }); expect(client.sentiment).toBeDefined(); expect(typeof client.sentiment).toBe('function'); }); it('generates structured output from schema', async () => { const client = AI({ person: { name: 'Full name', age: 'Age (number)', occupation: 'Job title', }, }); const result = await client.person('A software engineer named Alice who is 30'); expect(result).toBeDefined(); expect(typeof result.name).toBe('string'); expect(typeof result.age).toBe('number'); expect(typeof result.occupation).toBe('string'); }); it('generates nested objects', async () => { const client = AI({ profile: { user: { name: 'Name', email: 'Email address', }, preferences: { theme: 'light | dark', notifications: 'Enabled? (boolean)', }, }, }); const result = await client.profile('User Alice who prefers dark mode and has notifications on'); expect(result).toBeDefined(); expect(result.user).toBeDefined(); expect(result.preferences).toBeDefined(); expect(['light', 'dark']).toContain(result.preferences.theme); expect(typeof result.preferences.notifications).toBe('boolean'); }); it('generates arrays', async () => { const client = AI({ todoList: { title: 'List title', items: ['Todo items'], priority: 'high | medium | low', }, }); const result = await client.todoList('A high priority shopping list with 3 items'); expect(result).toBeDefined(); expect(typeof result.title).toBe('string'); expect(Array.isArray(result.items)).toBe(true); expect(result.items.length).toBeGreaterThan(0); expect(['high', 'medium', 'low']).toContain(result.priority); }); }); describe('withTemplate helper', () => { it('handles regular function calls', () => { const fn = withTemplate((prompt) => prompt.toUpperCase()); const result = fn('hello world'); expect(result).toBe('HELLO WORLD'); }); it('handles tagged template literals', () => { const fn = withTemplate((prompt) => prompt.toUpperCase()); const result = fn `hello world`; expect(result).toBe('HELLO WORLD'); }); it('handles tagged template literals with interpolation', () => { const fn = withTemplate((prompt) => prompt.toUpperCase()); const name = 'Alice'; const result = fn `hello ${name}!`; expect(result).toBe('HELLO ALICE!'); }); it('handles multiple interpolations', () => { const fn = withTemplate((prompt) => prompt); const a = 'one'; const b = 'two'; const c = 'three'; const result = fn `${a}, ${b}, ${c}`; expect(result).toBe('one, two, three'); }); it('works with async functions', async () => { const fn = withTemplate(async (prompt) => { return `Result: ${prompt}`; }); const result = await fn `async test`; expect(result).toBe('Result: async test'); }); });