'use strict'; import test from 'node:test'; import assert from 'node:assert/strict'; import { LLMOrbiter, defineTool } from '../src/index.js'; class MockProvider { constructor(responses) { this.responses = [...responses]; this.calls = []; } async chatCompletions(args) { this.calls.push(args); const next = this.responses.shift(); if (!next) throw new Error('MockProvider ran out of mock responses'); return next; } } test('LLMOrbiter loop continues when provider returns no response (no tool calls and no content)', async () => { let toolInvoked = false; const testTool = defineTool({ name: 'test_tool', description: 'Test tool', parameters: { type: 'object', properties: {} }, invoke: async () => { toolInvoked = true; return { ok: true }; }, }); // Sequence: // 1. Tool call // 2. No response (no tool calls, empty content) -> should continue loop! // 3. Final content const mock = new MockProvider([ { tool_calls: [ { id: 'call_1', type: 'function', function: { name: 'test_tool', arguments: '{}' }, }, ], content: null, }, { tool_calls: undefined, content: '', }, { tool_calls: undefined, content: 'Task completed successfully.', }, ]); const orbiter = new LLMOrbiter(mock); const result = await orbiter.run({ prompt: 'Do something', tools: [testTool], maxIterations: 10, }); assert.equal(toolInvoked, true, 'Tool should have been invoked'); assert.equal(result.iterations, 3, 'Should have taken 3 iterations (tool call, no response, final answer)'); assert.equal(result.content, 'Task completed successfully.'); assert.equal(mock.calls.length, 3, 'Provider should have been called 3 times'); }); test('LLMOrbiter stops at maxIterations if provider continually returns no response', async () => { const mock = new MockProvider([ { tool_calls: undefined, content: '' }, { tool_calls: undefined, content: null }, { tool_calls: undefined, content: ' ' }, ]); const orbiter = new LLMOrbiter(mock); const result = await orbiter.run({ prompt: 'Hello', tools: [], maxIterations: 3, }); assert.equal(result.iterations, 3, 'Should have iterated up to maxIterations'); assert.equal(result.content, null, 'Should return content null when no response was ever given'); }); test('LLMOrbiter finishes immediately in 1 iteration when content is returned with no tool calls', async () => { const mock = new MockProvider([ { tool_calls: undefined, content: 'Hello there!' }, ]); const orbiter = new LLMOrbiter(mock); const result = await orbiter.run({ prompt: 'Hi', tools: [], maxIterations: 10, }); assert.equal(result.iterations, 1); assert.equal(result.content, 'Hello there!'); });