contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
175 lines (159 loc) โข 7.75 kB
JavaScript
import { ChatService } from '../services/chatService.js';
import fs from 'fs/promises';
import path from 'path';
// Mock LLM that generates multiple turns of XML tool calls
class MockMultiTurnLLM {
constructor() {
this.callCount = 0;
}
async executePrompt(prompt, _options) {
this.callCount++;
console.log(`๐ค Mock LLM call #${this.callCount} - received prompt with length:`, prompt.length);
// First call: User asks to test multi-turn functionality
if (prompt.includes('test multiple tool calls') && this.callCount === 1) {
return {
content: `I'll demonstrate multiple tool calls in sequence. First, let me read the README file.
<tool_call name="read_file" id="1">
<file_path>README.md</file_path>
</tool_call>
After reading the file, I'll create a new test file based on its content.`
};
}
// Second call: After first tool execution, make another tool call
if (this.callCount === 2 && prompt.includes('tool_response')) {
return {
content: `Great! I've read the README file. Now I'll create a test file based on what I learned.
<tool_call name="write_file" id="2">
<file_path>multi-turn-test.md</file_path>
<content><![CDATA[# Multi-Turn Test
This file was created after reading the README file in a multi-turn conversation.
It demonstrates the proper tool call loop functionality.
## Features Tested:
- Sequential tool calls across multiple turns
- Turn counting and max turn limits
- Proper LLM loop continuation until no more tool calls
- Tool response integration
## Turn Flow:
1. User request โ LLM response with tool_call
2. Tool execution โ Tool response fed back to LLM
3. LLM response with another tool_call
4. Tool execution โ Tool response fed back to LLM
5. LLM final response (no tool calls) โ Return to user
]]></content>
</tool_call>
Perfect! I've created a comprehensive test file that documents the multi-turn process.`
};
}
// Third call: After second tool execution, make one more tool call
if (this.callCount === 3 && prompt.includes('tool_response')) {
return {
content: `Excellent! Now let me create one final summary file to complete the test.
<tool_call name="write_file" id="3">
<file_path>turn-summary.txt</file_path>
<content>Multi-Turn Tool Call Test Summary
=====================================
Total LLM Calls: 3
Total Tool Calls: 3
Tools Used: read_file, write_file (x2)
This demonstrates that the chat service properly:
- Continues the loop until no more tool calls
- Tracks turn count correctly
- Feeds tool responses back to LLM
- Only returns to user when LLM response has no tool calls
Test Status: SUCCESS โ
</content>
</tool_call>
All files created successfully! This completes the multi-turn tool call demonstration.`
};
}
// Fourth call: Final response with no tool calls
if (this.callCount === 4) {
return {
content: `๐ Multi-turn tool call test completed successfully!
## Summary:
- **Turn 1**: Read README file
- **Turn 2**: Created multi-turn-test.md based on README content
- **Turn 3**: Created turn-summary.txt with test results
- **Turn 4**: Final response (this message)
The chat service correctly implemented the while loop pattern:
1. โ
Continued processing until no more tool calls
2. โ
Tracked turn count (4 total turns)
3. โ
Fed tool responses back to LLM as user messages
4. โ
Only returned to user when LLM response contained no tool calls
5. โ
Respected max turn limits (10 max, used 4)
This demonstrates the proper agentic tool call flow where the LLM can make multiple sequential tool calls and only returns control to the user when the task is complete.`
};
}
// Fallback response
return {
content: `I understand your request. How can I help you with file operations?`
};
}
}
async function testMultiTurnToolCalls() {
console.log('๐งช Testing Multi-Turn Tool Call Flow');
console.log('='.repeat(50));
// Create a test directory
const testDir = path.join(process.cwd(), 'test-multi-turn');
await fs.mkdir(testDir, { recursive: true });
// Create a test README file
await fs.writeFile(path.join(testDir, 'README.md'), '# Multi-Turn Test Project\n\nThis README will be read by the LLM in the first tool call.\nThe LLM will then create additional files based on this content.\n');
try {
// Mock the LLM factory to return our test LLM
const LLMFactory = (await import('../services/llm/LLMFactory.js')).LLMFactory;
const originalGetConfiguredProvider = LLMFactory.getConfiguredProvider;
const originalGetProvider = LLMFactory.getProvider;
// Mock both methods to return our test LLM
LLMFactory.getConfiguredProvider = async () => new MockMultiTurnLLM();
LLMFactory.getProvider = (providerName) => new MockMultiTurnLLM();
// Create chat service with test directory
const chatService = new ChatService(testDir);
// Create a chat session
const session = chatService.createSession({
systemPrompt: 'You are a helpful assistant that can read and write files. Use XML tool calls to perform file operations.'
});
console.log(`๐ Created chat session: ${session.id}`);
// Send a message that will trigger multiple tool calls
console.log('\n๐ Sending message: "test multiple tool calls"');
const response = await chatService.sendMessage(session.id, 'test multiple tool calls');
console.log('\n๐จ Final Response:');
console.log(response.content);
// Verify files were created
console.log('\n๐ Verifying created files:');
const files = ['multi-turn-test.md', 'turn-summary.txt'];
for (const file of files) {
const filePath = path.join(testDir, file);
try {
const content = await fs.readFile(filePath, 'utf-8');
console.log(`โ
${file} created (${content.length} characters)`);
}
catch (error) {
console.log(`โ ${file} not found`);
}
}
// Show all messages in session (including tool responses)
console.log('\n๐ฌ Complete Message History:');
const allMessages = chatService.getAllMessages(session.id);
allMessages.forEach((msg, index) => {
const roleIcon = msg.role === 'user' ? '๐ค' : msg.role === 'assistant' ? '๐ค' : 'โ๏ธ';
const toolFlag = msg.isToolResponse ? ' [TOOL_RESPONSE]' : '';
console.log(`${index + 1}. ${roleIcon} ${msg.role.toUpperCase()}${toolFlag}:`);
console.log(` ${msg.content.substring(0, 100)}${msg.content.length > 100 ? '...' : ''}`);
});
// Restore original LLM factory methods
LLMFactory.getConfiguredProvider = originalGetConfiguredProvider;
LLMFactory.getProvider = originalGetProvider;
console.log('\n๐ Multi-Turn Tool Call Test Complete!');
console.log('\n๐ฏ Key Findings:');
console.log('- โ
Multi-turn tool calls work correctly');
console.log('- โ
Turn counting implemented properly');
console.log('- โ
Tool responses fed back to LLM');
console.log('- โ
Loop continues until no more tool calls');
console.log('- โ
Final response returned to user only when complete');
}
finally {
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
}
}
// Run the test
testMultiTurnToolCalls().catch(console.error);