ai-functions
Version:
Core AI primitives for building intelligent applications
233 lines (232 loc) • 8.34 kB
JavaScript
/**
* Tests for define and function registry
*
* These tests use real AI calls via the Cloudflare AI Gateway.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { define, defineFunction, functions } from '../src/index.js';
// Skip tests if no gateway configured
const hasGateway = !!process.env.AI_GATEWAY_URL || !!process.env.ANTHROPIC_API_KEY;
describe('functions registry', () => {
beforeEach(() => {
functions.clear();
});
it('starts empty', () => {
expect(functions.list()).toEqual([]);
});
it('tracks defined functions', () => {
const fn = defineFunction({
type: 'generative',
name: 'testFunc',
args: { input: 'Input text' },
output: 'string',
});
functions.set('testFunc', fn);
expect(functions.has('testFunc')).toBe(true);
expect(functions.list()).toContain('testFunc');
});
it('retrieves defined functions', () => {
const fn = defineFunction({
type: 'generative',
name: 'testFunc',
args: { input: 'Input text' },
output: 'string',
});
functions.set('testFunc', fn);
const retrieved = functions.get('testFunc');
expect(retrieved).toBeDefined();
expect(retrieved?.definition.name).toBe('testFunc');
});
it('deletes functions', () => {
const fn = defineFunction({
type: 'generative',
name: 'testFunc',
args: { input: 'Input text' },
output: 'string',
});
functions.set('testFunc', fn);
expect(functions.delete('testFunc')).toBe(true);
expect(functions.has('testFunc')).toBe(false);
});
it('clears all functions', () => {
const fn1 = defineFunction({
type: 'generative',
name: 'func1',
args: {},
output: 'string',
});
const fn2 = defineFunction({
type: 'generative',
name: 'func2',
args: {},
output: 'string',
});
functions.set('func1', fn1);
functions.set('func2', fn2);
functions.clear();
expect(functions.list()).toEqual([]);
});
});
describe('defineFunction', () => {
beforeEach(() => {
functions.clear();
});
it('creates a generative function definition', () => {
const fn = defineFunction({
type: 'generative',
name: 'summarize',
args: { text: 'Text to summarize' },
output: 'string',
system: 'You are a summarizer.',
});
expect(fn.definition.type).toBe('generative');
expect(fn.definition.name).toBe('summarize');
expect(typeof fn.call).toBe('function');
expect(typeof fn.asTool).toBe('function');
});
it('creates an agentic function definition', () => {
const fn = defineFunction({
type: 'agentic',
name: 'research',
args: { topic: 'Research topic' },
instructions: 'Research the topic thoroughly.',
maxIterations: 5,
});
expect(fn.definition.type).toBe('agentic');
expect(fn.definition.name).toBe('research');
});
it('creates a human function definition', () => {
const fn = defineFunction({
type: 'human',
name: 'approve',
args: { amount: 'Amount (number)' },
channel: 'workspace',
instructions: 'Review and approve.',
});
expect(fn.definition.type).toBe('human');
expect(fn.definition.channel).toBe('workspace');
});
it('creates a code function definition', () => {
const fn = defineFunction({
type: 'code',
name: 'implement',
args: { spec: 'Function specification' },
language: 'typescript',
});
expect(fn.definition.type).toBe('code');
expect(fn.definition.language).toBe('typescript');
});
it('generates asTool with correct parameters', () => {
const fn = defineFunction({
type: 'generative',
name: 'translate',
description: 'Translate text to another language',
args: {
text: 'Text to translate',
targetLang: 'Target language',
},
output: 'string',
});
const tool = fn.asTool();
expect(tool.name).toBe('translate');
expect(tool.description).toBe('Translate text to another language');
expect(tool.parameters.type).toBe('object');
expect(tool.parameters.properties).toHaveProperty('text');
expect(tool.parameters.properties).toHaveProperty('targetLang');
expect(tool.parameters.required).toContain('text');
expect(tool.parameters.required).toContain('targetLang');
});
});
describe('define helpers', () => {
beforeEach(() => {
functions.clear();
});
it('define.generative registers function', () => {
const fn = define.generative({
name: 'greet',
args: { name: 'Name to greet' },
output: 'string',
});
expect(functions.has('greet')).toBe(true);
expect(fn.definition.type).toBe('generative');
});
it('define.agentic registers function', () => {
const fn = define.agentic({
name: 'analyze',
args: { data: 'Data to analyze' },
instructions: 'Analyze the data.',
});
expect(functions.has('analyze')).toBe(true);
expect(fn.definition.type).toBe('agentic');
});
it('define.human registers function', () => {
const fn = define.human({
name: 'review',
args: { content: 'Content to review' },
channel: 'web',
instructions: 'Review the content.',
});
expect(functions.has('review')).toBe(true);
expect(fn.definition.type).toBe('human');
});
it('define.code registers function', () => {
const fn = define.code({
name: 'generate',
args: { prompt: 'Code generation prompt' },
language: 'python',
});
expect(functions.has('generate')).toBe(true);
expect(fn.definition.type).toBe('code');
});
});
describe.skipIf(!hasGateway)('generative function execution', () => {
beforeEach(() => {
functions.clear();
});
it('executes a generative string function', async () => {
const greet = define.generative({
name: 'greet',
args: { name: 'Name to greet' },
output: 'string',
promptTemplate: 'Say hello to {{name}}',
});
const result = await greet.call({ name: 'World' });
expect(typeof result).toBe('string');
expect(result.toLowerCase()).toContain('hello');
});
it('executes a generative object function', async () => {
const analyze = define.generative({
name: 'analyze',
args: { text: 'Text to analyze' },
output: 'object',
returnType: {
sentiment: 'positive | negative | neutral',
confidence: 'Confidence 0-1 (number)',
},
promptTemplate: 'Analyze the sentiment of: {{text}}',
});
const result = await analyze.call({ text: 'I love this!' });
expect(result).toBeDefined();
expect(['positive', 'negative', 'neutral']).toContain(result.sentiment);
expect(typeof result.confidence).toBe('number');
});
});
describe.skipIf(!hasGateway)('auto-define', () => {
beforeEach(() => {
functions.clear();
});
it('auto-defines a function from name and args', async () => {
const fn = await define('translateText', {
text: 'Hello',
targetLanguage: 'French',
});
expect(fn).toBeDefined();
expect(fn.definition.name).toBe('translateText');
expect(functions.has('translateText')).toBe(true);
});
it('returns cached function on second call', async () => {
const fn1 = await define('greetUser', { name: 'Alice' });
const fn2 = await define('greetUser', { name: 'Bob' });
expect(fn1).toBe(fn2);
});
});