feat(llm): improve type safety and error handling in tool call transformations

This commit is contained in:
perf3ct 2025-05-29 22:03:23 +00:00
parent 2f303b1ae9
commit 7c63652105
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
2 changed files with 36 additions and 19 deletions

View File

@ -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

View File

@ -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
} }
}; };
}); });