From c95b9f798c011b46c0893cb66f5068d37efccd5b Mon Sep 17 00:00:00 2001 From: perf3ct Date: Wed, 9 Apr 2025 01:24:32 +0000 Subject: [PATCH] recursive thinking? --- src/services/llm/ai_interface.ts | 1 + src/services/llm/pipeline/chat_pipeline.ts | 71 +++++++++++++++++++- src/services/llm/providers/ollama_service.ts | 58 ++++++++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/src/services/llm/ai_interface.ts b/src/services/llm/ai_interface.ts index f08977c8a..2e555c227 100644 --- a/src/services/llm/ai_interface.ts +++ b/src/services/llm/ai_interface.ts @@ -35,6 +35,7 @@ export interface ChatCompletionOptions { enableTools?: boolean; // Whether to enable tool calling tools?: any[]; // Tools to provide to the LLM useAdvancedContext?: boolean; // Whether to use advanced context enrichment + toolExecutionStatus?: any[]; // Status information about executed tools for feedback } export interface ChatResponse { diff --git a/src/services/llm/pipeline/chat_pipeline.ts b/src/services/llm/pipeline/chat_pipeline.ts index 41c1b9e2a..7c805440c 100644 --- a/src/services/llm/pipeline/chat_pipeline.ts +++ b/src/services/llm/pipeline/chat_pipeline.ts @@ -367,6 +367,29 @@ export class ChatPipeline { await streamCallback('[Generating response with tool results...]\n\n', false); } + // Extract tool execution status information for Ollama feedback + let toolExecutionStatus; + + if (currentResponse.provider === 'Ollama') { + // Collect tool execution status from the tool results + toolExecutionStatus = toolResultMessages.map(msg => { + // Determine if this was a successful tool call + const isError = msg.content.startsWith('Error:'); + return { + toolCallId: msg.tool_call_id || '', + name: msg.name || 'unknown', + success: !isError, + result: msg.content, + error: isError ? msg.content.substring(7) : undefined + }; + }); + + log.info(`Created tool execution status for Ollama: ${toolExecutionStatus.length} entries`); + toolExecutionStatus.forEach((status, idx) => { + log.info(`Tool status ${idx + 1}: ${status.name} - ${status.success ? 'success' : 'failed'}`); + }); + } + // Generate a new completion with the updated messages const followUpStartTime = Date.now(); const followUpCompletion = await this.stages.llmCompletion.execute({ @@ -376,7 +399,9 @@ export class ChatPipeline { // Ensure tool support is still enabled for follow-up requests enableTools: true, // Disable streaming during tool execution follow-ups - stream: false + stream: false, + // Add tool execution status for Ollama provider + ...(currentResponse.provider === 'Ollama' ? { toolExecutionStatus } : {}) } }); this.updateStageMetrics('llmCompletion', followUpStartTime); @@ -418,10 +443,31 @@ export class ChatPipeline { await streamCallback(`[Tool execution error: ${error.message || 'unknown error'}]\n\n`, false); } + // For Ollama, create tool execution status with the error + let toolExecutionStatus; + if (currentResponse.provider === 'Ollama' && currentResponse.tool_calls) { + // We need to create error statuses for all tool calls that failed + toolExecutionStatus = currentResponse.tool_calls.map(toolCall => { + return { + toolCallId: toolCall.id || '', + name: toolCall.function?.name || 'unknown', + success: false, + result: `Error: ${error.message || 'unknown error'}`, + error: error.message || 'unknown error' + }; + }); + + log.info(`Created error tool execution status for Ollama: ${toolExecutionStatus.length} entries`); + } + // Make a follow-up request to the LLM with the error information const errorFollowUpCompletion = await this.stages.llmCompletion.execute({ messages: currentMessages, - options: modelSelection.options + options: { + ...modelSelection.options, + // For Ollama, include tool execution status + ...(currentResponse.provider === 'Ollama' ? { toolExecutionStatus } : {}) + } }); // Update current response and break the tool loop @@ -445,12 +491,31 @@ export class ChatPipeline { await streamCallback(`[Reached maximum of ${maxToolCallIterations} tool calls. Finalizing response...]\n\n`, false); } + // For Ollama, create a status about reaching max iterations + let toolExecutionStatus; + if (currentResponse.provider === 'Ollama' && currentResponse.tool_calls) { + // Create a special status message about max iterations + toolExecutionStatus = [ + { + toolCallId: 'max-iterations', + name: 'system', + success: false, + result: `Maximum tool call iterations (${maxToolCallIterations}) reached.`, + error: `Reached the maximum number of allowed tool calls (${maxToolCallIterations}). Please provide a final response with the information gathered so far.` + } + ]; + + log.info(`Created max iterations status for Ollama`); + } + // Make a final request to get a summary response const finalFollowUpCompletion = await this.stages.llmCompletion.execute({ messages: currentMessages, options: { ...modelSelection.options, - enableTools: false // Disable tools for the final response + enableTools: false, // Disable tools for the final response + // For Ollama, include tool execution status + ...(currentResponse.provider === 'Ollama' ? { toolExecutionStatus } : {}) } }); diff --git a/src/services/llm/providers/ollama_service.ts b/src/services/llm/providers/ollama_service.ts index 68aa746f6..7b2f04b80 100644 --- a/src/services/llm/providers/ollama_service.ts +++ b/src/services/llm/providers/ollama_service.ts @@ -39,6 +39,15 @@ interface OllamaResponse { eval_duration: number; } +// Add an interface for tool execution feedback status +interface ToolExecutionStatus { + toolCallId: string; + name: string; + success: boolean; + result: string; + error?: string; +} + export class OllamaService extends BaseAIService { private formatter: OllamaMessageFormatter; @@ -72,6 +81,12 @@ export class OllamaService extends BaseAIService { const systemPrompt = this.getSystemPrompt(opts.systemPrompt || options.getOption('aiSystemPrompt')); try { + // Check if we should add tool execution feedback + if (opts.toolExecutionStatus && Array.isArray(opts.toolExecutionStatus) && opts.toolExecutionStatus.length > 0) { + log.info(`Adding tool execution feedback to messages`); + messages = this.addToolExecutionFeedback(messages, opts.toolExecutionStatus); + } + // Determine whether to use the formatter or send messages directly let messagesToSend: Message[]; @@ -465,6 +480,49 @@ export class OllamaService extends BaseAIService { return 8192; // Default to 8192 tokens if there's an error } } + + /** + * Adds a system message with feedback about tool execution status + * @param messages The current message array + * @param toolExecutionStatus Array of tool execution status objects + * @returns Updated message array with feedback + */ + private addToolExecutionFeedback(messages: Message[], toolExecutionStatus: ToolExecutionStatus[]): Message[] { + if (!toolExecutionStatus || toolExecutionStatus.length === 0) { + return messages; + } + + // Create a copy of the messages + const updatedMessages = [...messages]; + + // Create a feedback message that explains what happened with each tool call + let feedbackContent = `Tool execution feedback:\n\n`; + + toolExecutionStatus.forEach((status, index) => { + // Add status for each tool + const statusText = status.success ? 'successfully executed' : 'failed to execute'; + const toolName = status.name || 'unknown tool'; + + feedbackContent += `Tool call ${index + 1} (${toolName}): ${statusText}\n`; + + // Add error information if available and tool failed + if (!status.success && status.error) { + feedbackContent += `Error: ${status.error}\n`; + feedbackContent += `Please fix this issue in your next response or try a different approach.\n`; + } + + feedbackContent += `\n`; + }); + + // Add feedback message to the conversation + updatedMessages.push({ + role: 'system', + content: feedbackContent + }); + + log.info(`Added tool execution feedback: ${toolExecutionStatus.length} statuses`); + return updatedMessages; + } } /**