mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	feat(llm): improve type safety and error handling in tool call transformations
This commit is contained in:
		@@ -69,7 +69,9 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Check if the registry has any tools
 | 
					        // Check if the registry has any tools
 | 
				
			||||||
        const availableTools: ToolInterface[] = toolRegistry.getAllTools() as unknown as ToolInterface[];
 | 
					        // Convert from ToolHandler[] to ToolInterface[] with proper type conversion
 | 
				
			||||||
 | 
					        const registryTools = toolRegistry.getAllTools();
 | 
				
			||||||
 | 
					        const availableTools: ToolInterface[] = registryTools.map(tool => tool as unknown as ToolInterface);
 | 
				
			||||||
        log.info(`Available tools in registry: ${availableTools.length}`);
 | 
					        log.info(`Available tools in registry: ${availableTools.length}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Log available tools for debugging
 | 
					        // Log available tools for debugging
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ import type { ToolCall, Tool } from '../tools/tool_interfaces.js';
 | 
				
			|||||||
import toolRegistry from '../tools/tool_registry.js';
 | 
					import toolRegistry from '../tools/tool_registry.js';
 | 
				
			||||||
import type { OllamaOptions } from './provider_options.js';
 | 
					import type { OllamaOptions } from './provider_options.js';
 | 
				
			||||||
import { getOllamaOptions } from './providers.js';
 | 
					import { getOllamaOptions } from './providers.js';
 | 
				
			||||||
import { Ollama, type ChatRequest, type ChatResponse as OllamaChatResponse } from 'ollama';
 | 
					import { Ollama, type ChatRequest } from 'ollama';
 | 
				
			||||||
import options from '../../options.js';
 | 
					import options from '../../options.js';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StreamProcessor,
 | 
					    StreamProcessor,
 | 
				
			||||||
@@ -366,9 +366,17 @@ export class OllamaService extends BaseAIService {
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
                async (callback) => {
 | 
					                async (callback) => {
 | 
				
			||||||
                    let completeText = '';
 | 
					                    let completeText = '';
 | 
				
			||||||
                    let responseToolCalls: any[] = [];
 | 
					                    let responseToolCalls: ToolCall[] = [];
 | 
				
			||||||
                    let chunkCount = 0;
 | 
					                    let chunkCount = 0;
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
 | 
					                    // Create a response object that will be updated during streaming
 | 
				
			||||||
 | 
					                    const response: ChatResponse = {
 | 
				
			||||||
 | 
					                        text: '',
 | 
				
			||||||
 | 
					                        model: providerOptions.model,
 | 
				
			||||||
 | 
					                        provider: this.getName(),
 | 
				
			||||||
 | 
					                        tool_calls: []
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
                        // Perform health check
 | 
					                        // Perform health check
 | 
				
			||||||
                        await performProviderHealthCheck(
 | 
					                        await performProviderHealthCheck(
 | 
				
			||||||
@@ -400,8 +408,12 @@ export class OllamaService extends BaseAIService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                            // Extract any tool calls
 | 
					                            // Extract any tool calls
 | 
				
			||||||
                            const toolCalls = StreamProcessor.extractToolCalls(chunk);
 | 
					                            const toolCalls = StreamProcessor.extractToolCalls(chunk);
 | 
				
			||||||
 | 
					                            // Update response tool calls if any are found
 | 
				
			||||||
                            if (toolCalls.length > 0) {
 | 
					                            if (toolCalls.length > 0) {
 | 
				
			||||||
 | 
					                                // Update tool calls in the overall response
 | 
				
			||||||
                                responseToolCalls = toolCalls;
 | 
					                                responseToolCalls = toolCalls;
 | 
				
			||||||
 | 
					                                // Also update the response object's tool_calls for final return
 | 
				
			||||||
 | 
					                                response.tool_calls = toolCalls;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            // Send to callback - directly pass the content without accumulating
 | 
					                            // Send to callback - directly pass the content without accumulating
 | 
				
			||||||
@@ -438,35 +450,38 @@ export class OllamaService extends BaseAIService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Transform Ollama tool calls to the standard format expected by the pipeline
 | 
					     * Transform Ollama tool calls to the standard format expected by the pipeline
 | 
				
			||||||
 | 
					     * @param toolCalls Array of tool calls from Ollama response or undefined
 | 
				
			||||||
 | 
					     * @returns Standardized ToolCall array for consistent handling in the pipeline
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private transformToolCalls(toolCalls: any[] | undefined): ToolCall[] {
 | 
					    private transformToolCalls(toolCalls: unknown[] | undefined): ToolCall[] {
 | 
				
			||||||
        if (!toolCalls || !Array.isArray(toolCalls) || toolCalls.length === 0) {
 | 
					        if (!toolCalls || !Array.isArray(toolCalls) || toolCalls.length === 0) {
 | 
				
			||||||
            return [];
 | 
					            return [];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return toolCalls.map((toolCall, index) => {
 | 
					        return toolCalls.map((toolCall, index) => {
 | 
				
			||||||
 | 
					            // Use type guards to safely access properties
 | 
				
			||||||
 | 
					            const toolCallObj = toolCall as { id?: string; function?: { name?: string; arguments?: string } };
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            // Generate a unique ID if none is provided
 | 
					            // Generate a unique ID if none is provided
 | 
				
			||||||
            const id = toolCall.id || `tool-call-${Date.now()}-${index}`;
 | 
					            const id = typeof toolCallObj.id === 'string' ? toolCallObj.id : `tool-call-${Date.now()}-${index}`;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Handle arguments based on their type
 | 
					            // Safely extract function name and arguments with defaults
 | 
				
			||||||
            let processedArguments: Record<string, any> | string = toolCall.function?.arguments || {};
 | 
					            const functionName = toolCallObj.function && typeof toolCallObj.function.name === 'string' 
 | 
				
			||||||
 | 
					                ? toolCallObj.function.name 
 | 
				
			||||||
 | 
					                : 'unknown_function';
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
            if (typeof processedArguments === 'string') {
 | 
					            const functionArgs = toolCallObj.function && typeof toolCallObj.function.arguments === 'string'
 | 
				
			||||||
                try {
 | 
					                ? toolCallObj.function.arguments
 | 
				
			||||||
                    processedArguments = JSON.parse(processedArguments);
 | 
					                : '{}';
 | 
				
			||||||
                } catch (error) {
 | 
					
 | 
				
			||||||
                    // If we can't parse as JSON, create a simple object
 | 
					            // Return a properly typed ToolCall object
 | 
				
			||||||
                    log.info(`Could not parse tool arguments as JSON in transformToolCalls: ${error}`);
 | 
					 | 
				
			||||||
                    processedArguments = { raw: processedArguments };
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
                id,
 | 
					                id,
 | 
				
			||||||
                type: 'function',
 | 
					                type: 'function',
 | 
				
			||||||
                function: {
 | 
					                function: {
 | 
				
			||||||
                    name: toolCall.function?.name || '',
 | 
					                    name: functionName,
 | 
				
			||||||
                    arguments: processedArguments
 | 
					                    arguments: functionArgs
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user