|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { HumanMessage, AIMessage, SystemMessage, ToolMessage } from '../../../../src/index.js';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Calculator {
|
|
|
constructor() {
|
|
|
this.name = 'calculator';
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
execute(operation, a, b) {
|
|
|
const ops = {
|
|
|
'add': (x, y) => x + y,
|
|
|
'subtract': (x, y) => x - y,
|
|
|
'multiply': (x, y) => x * y,
|
|
|
'divide': (x, y) => x / y
|
|
|
};
|
|
|
|
|
|
if (!ops[operation]) {
|
|
|
throw new Error(`Unknown operation: ${operation}`);
|
|
|
}
|
|
|
|
|
|
return ops[operation](a, b);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getDefinition() {
|
|
|
return {
|
|
|
name: 'calculator',
|
|
|
description: 'Performs basic arithmetic operations',
|
|
|
parameters: {
|
|
|
type: 'object',
|
|
|
properties: {
|
|
|
operation: {
|
|
|
type: 'string',
|
|
|
enum: ['add', 'subtract', 'multiply', 'divide'],
|
|
|
description: 'The operation to perform'
|
|
|
},
|
|
|
a: { type: 'number', description: 'First number' },
|
|
|
b: { type: 'number', description: 'Second number' }
|
|
|
},
|
|
|
required: ['operation', 'a', 'b']
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function simulateToolCallFlow(userQuery) {
|
|
|
const messages = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return messages;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function simulateMultiToolFlow(userQuery) {
|
|
|
const messages = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return messages;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function displayConversation(messages) {
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function runTests() {
|
|
|
console.log('🧪 Testing Tool Call Flow...\n');
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('Test 1: Simple calculation with tool');
|
|
|
const conversation1 = simulateToolCallFlow("What's 5 * 3?");
|
|
|
|
|
|
console.log(` Messages created: ${conversation1.length}`);
|
|
|
console.assert(conversation1.length >= 5, 'Should have at least 5 messages');
|
|
|
|
|
|
|
|
|
console.assert(conversation1[0].type === 'system', 'Should start with system');
|
|
|
console.assert(conversation1[1].type === 'human', 'Should have human query');
|
|
|
console.assert(conversation1[2].type === 'ai', 'Should have AI decision');
|
|
|
console.assert(conversation1[3].type === 'tool', 'Should have tool result');
|
|
|
console.assert(conversation1[4].type === 'ai', 'Should have final AI response');
|
|
|
|
|
|
|
|
|
const aiWithTool = conversation1[2];
|
|
|
console.assert(aiWithTool.hasToolCalls(), 'AI message should have tool calls');
|
|
|
|
|
|
console.log('\n Conversation:');
|
|
|
displayConversation(conversation1);
|
|
|
console.log('\n✅ Simple tool call works\n');
|
|
|
|
|
|
|
|
|
console.log('Test 2: Multi-step calculation');
|
|
|
const conversation2 = simulateMultiToolFlow("What's 5*3 and then add 10?");
|
|
|
|
|
|
console.log(` Messages created: ${conversation2.length}`);
|
|
|
|
|
|
|
|
|
const toolMessages = conversation2.filter(m => m.type === 'tool');
|
|
|
console.log(` Tool calls made: ${toolMessages.length}`);
|
|
|
console.assert(toolMessages.length >= 2, 'Should have at least 2 tool calls');
|
|
|
|
|
|
console.log('\n Conversation:');
|
|
|
displayConversation(conversation2);
|
|
|
console.log('\n✅ Multi-step tool calls work\n');
|
|
|
|
|
|
|
|
|
console.log('Test 3: Tool call IDs match');
|
|
|
const testConv = simulateToolCallFlow("Calculate 10 + 5");
|
|
|
|
|
|
|
|
|
const aiMsg = testConv.find(m => m.type === 'ai' && m.hasToolCalls());
|
|
|
const toolMsg = testConv.find(m => m.type === 'tool');
|
|
|
|
|
|
console.assert(aiMsg, 'Should have AI message with tool call');
|
|
|
console.assert(toolMsg, 'Should have tool message');
|
|
|
|
|
|
const toolCallId = aiMsg.toolCalls[0].id;
|
|
|
console.log(` Tool call ID: ${toolCallId}`);
|
|
|
console.log(` Tool message references: ${toolMsg.toolCallId}`);
|
|
|
console.assert(toolCallId === toolMsg.toolCallId, 'IDs should match');
|
|
|
console.log('✅ Tool call IDs link correctly\n');
|
|
|
|
|
|
|
|
|
console.log('Test 4: Calculator tool execution');
|
|
|
const calc = new Calculator();
|
|
|
|
|
|
const result1 = calc.execute('multiply', 5, 3);
|
|
|
const result2 = calc.execute('add', 10, 5);
|
|
|
const result3 = calc.execute('divide', 20, 4);
|
|
|
|
|
|
console.log(` 5 * 3 = ${result1}`);
|
|
|
console.log(` 10 + 5 = ${result2}`);
|
|
|
console.log(` 20 / 4 = ${result3}`);
|
|
|
|
|
|
console.assert(result1 === 15, 'Multiplication should work');
|
|
|
console.assert(result2 === 15, 'Addition should work');
|
|
|
console.assert(result3 === 5, 'Division should work');
|
|
|
console.log('✅ Calculator works\n');
|
|
|
|
|
|
|
|
|
console.log('Test 5: Tool definition format');
|
|
|
const calc5 = new Calculator();
|
|
|
const definition = calc5.getDefinition();
|
|
|
|
|
|
console.log(` Tool name: ${definition.name}`);
|
|
|
console.log(` Parameters: ${Object.keys(definition.parameters.properties).join(', ')}`);
|
|
|
console.assert(definition.name === 'calculator', 'Should have name');
|
|
|
console.assert(definition.parameters, 'Should have parameters');
|
|
|
console.log('✅ Tool definition is correct\n');
|
|
|
|
|
|
console.log('🎉 All tests passed!');
|
|
|
} catch (error) {
|
|
|
console.error('❌ Test failed:', error.message);
|
|
|
console.error(error.stack);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
runTests();
|
|
|
}
|
|
|
|
|
|
export { Calculator, simulateToolCallFlow, simulateMultiToolFlow, displayConversation }; |