feat: add tool framework foundation (types, registry, executor, shell tool, model types, SOUL.md)
- Task 0: SOUL.md + loadSystemPrompt() in daemon - Task 1: Tool type definitions (Tool, ToolCall, ToolResult, etc.) - Task 2: ToolRegistry with Anthropic/OpenAI serialization - Task 3: ToolExecutor with hooks, timeout, truncation - Task 4: shell.exec builtin tool - Task 8: Model types updated for tool use (ToolDefinition, ModelToolCall, etc.) - Task 15: Model index exports for tool types
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ToolRegistry } from './registry.js';
|
||||
import type { Tool } from './types.js';
|
||||
|
||||
const echoTool: Tool = {
|
||||
name: 'test.echo',
|
||||
description: 'Echoes input back',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { text: { type: 'string', description: 'Text to echo' } },
|
||||
required: ['text'],
|
||||
},
|
||||
execute: async (args) => ({ success: true, output: String((args as { text: string }).text) }),
|
||||
};
|
||||
|
||||
const greetTool: Tool = {
|
||||
name: 'test.greet',
|
||||
description: 'Greets someone',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { name: { type: 'string' } },
|
||||
required: ['name'],
|
||||
},
|
||||
execute: async (args) => ({ success: true, output: `Hello ${(args as { name: string }).name}` }),
|
||||
};
|
||||
|
||||
describe('ToolRegistry', () => {
|
||||
it('registers and retrieves tools by name', () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register(echoTool);
|
||||
|
||||
expect(registry.get('test.echo')).toBe(echoTool);
|
||||
expect(registry.get('nonexistent')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('lists all registered tools', () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register(echoTool);
|
||||
registry.register(greetTool);
|
||||
|
||||
const tools = registry.list();
|
||||
expect(tools).toHaveLength(2);
|
||||
expect(tools.map(t => t.name)).toContain('test.echo');
|
||||
expect(tools.map(t => t.name)).toContain('test.greet');
|
||||
});
|
||||
|
||||
it('throws on duplicate registration', () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register(echoTool);
|
||||
expect(() => registry.register(echoTool)).toThrow('already registered');
|
||||
});
|
||||
|
||||
it('serializes to Anthropic format', () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register(echoTool);
|
||||
|
||||
const anthropicTools = registry.toAnthropicFormat();
|
||||
expect(anthropicTools).toEqual([{
|
||||
name: 'test.echo',
|
||||
description: 'Echoes input back',
|
||||
input_schema: echoTool.inputSchema,
|
||||
}]);
|
||||
});
|
||||
|
||||
it('serializes to OpenAI format', () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register(echoTool);
|
||||
|
||||
const openaiTools = registry.toOpenAIFormat();
|
||||
expect(openaiTools).toEqual([{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'test.echo',
|
||||
description: 'Echoes input back',
|
||||
parameters: echoTool.inputSchema,
|
||||
},
|
||||
}]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user