UNPKG

ai-functions

Version:

Core AI primitives for building intelligent applications

193 lines (157 loc) 5.63 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 { aiProxy as 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 as Record<string, (args: unknown) => Promise<unknown>>).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 as Record<string, (args: unknown) => Promise<unknown>>).capitalizeText({ text: 'hello', }) const fn1 = functions.get('capitalizeText') expect(fn1).toBeDefined() // Second call - uses cached definition await (ai as Record<string, (args: unknown) => Promise<unknown>>).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: string) => prompt.toUpperCase()) const result = fn('hello world') expect(result).toBe('HELLO WORLD') }) it('handles tagged template literals', () => { const fn = withTemplate((prompt: string) => prompt.toUpperCase()) const result = fn`hello world` expect(result).toBe('HELLO WORLD') }) it('handles tagged template literals with interpolation', () => { const fn = withTemplate((prompt: string) => prompt.toUpperCase()) const name = 'Alice' const result = fn`hello ${name}!` expect(result).toBe('HELLO ALICE!') }) it('handles multiple interpolations', () => { const fn = withTemplate((prompt: string) => 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: string) => { return `Result: ${prompt}` }) const result = await fn`async test` expect(result).toBe('Result: async test') }) })