From f6514314c0cd653e4af920df3d3b19b0f78ab99f Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Thu, 4 Dec 2025 16:59:12 +1000 Subject: [PATCH] feat: add collated error reporting to batch tool loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve visibility when multiple tools fail during batch loading: - Track success/failure status for each tool in batch - Log consolidated summary with counts (āœ… Successful: X/Y, āŒ Failed: Y/Z) - List all failed tools together with automatic health check confirmation - Provide guidance to check individual error logs for detailed reasons Also fix linting issues: - Remove non-null assertions for safer code - Fix template literals that don't need interpolation - Add biome-ignore comments for AI SDK any types This addresses error collation when multiple Railway tools fail, making it easier to see the big picture while maintaining detailed individual error logs and health check triggers. šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../playground/src/lib/dynamic-tool-loader.ts | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/apps/playground/src/lib/dynamic-tool-loader.ts b/apps/playground/src/lib/dynamic-tool-loader.ts index 6f966b8..9133d55 100644 --- a/apps/playground/src/lib/dynamic-tool-loader.ts +++ b/apps/playground/src/lib/dynamic-tool-loader.ts @@ -87,7 +87,6 @@ async function reportToolFailure( * Dynamically load a tool via Railway service * Railway service runs with --experimental-network-imports and can import from esm.sh */ -// biome-ignore lint/suspicious/noExplicitAny: Tool types from AI SDK are complex export async function loadToolDynamically( packageName: string, exportName: string, @@ -95,6 +94,7 @@ export async function loadToolDynamically( conversationId: string, importUrl?: string, env?: Record + // biome-ignore lint/suspicious/noExplicitAny: Tool types from AI SDK are complex ): Promise { const cacheKey = getCacheKey(packageName, exportName); @@ -216,15 +216,14 @@ export async function loadToolDynamically( return toolWrapper; } catch (error) { console.error(`āŒ Failed to load ${packageName}#${exportName}:`, error); - console.error(` Stack:`, error instanceof Error ? error.stack : 'No stack trace'); + console.error(' Stack:', error instanceof Error ? error.stack : 'No stack trace'); return null; } } /** - * Load multiple tools in parallel + * Load multiple tools in parallel with collated error reporting */ -// biome-ignore lint/suspicious/noExplicitAny: Tool types from AI SDK are complex export async function loadToolsBatch( toolMetadata: Array<{ packageName: string; @@ -234,9 +233,10 @@ export async function loadToolsBatch( }>, conversationId: string, env?: Record + // biome-ignore lint/suspicious/noExplicitAny: Tool types from AI SDK are complex ): Promise> { console.log(`šŸ“¦ Loading ${toolMetadata.length} tools for conversation ${conversationId}`); - console.log(`šŸ”‘ Env vars being passed:`, Object.keys(env || {})); + console.log('šŸ”‘ Env vars being passed:', Object.keys(env || {})); const promises = toolMetadata.map((meta) => loadToolDynamically( @@ -247,19 +247,40 @@ export async function loadToolsBatch( meta.importUrl, env ).then((tool) => ({ + packageName: meta.packageName, + exportName: meta.exportName, key: getCacheKey(meta.packageName, meta.exportName), tool, + success: tool !== null, })) ); const results = await Promise.all(promises); + // Separate successful and failed tools + const successful = results.filter((r) => r.success); + const failed = results.filter((r) => !r.success); + + // Build tools object // biome-ignore lint/suspicious/noExplicitAny: Tool types from AI SDK are complex const tools: Record = {}; - for (const { key, tool } of results) { - if (tool) { - tools[key] = tool; + for (const result of successful) { + tools[result.key] = result.tool; + } + + // Log collated error summary + console.log('\nšŸ“Š Batch Load Summary:'); + console.log(` āœ… Successful: ${successful.length}/${toolMetadata.length}`); + console.log(` āŒ Failed: ${failed.length}/${toolMetadata.length}`); + + if (failed.length > 0) { + console.log('\nāŒ Failed Tools:'); + for (const result of failed) { + console.log(` - ${result.packageName}/${result.exportName}`); + console.log(' (Health check triggered automatically)'); } + console.log('\nšŸ’” Note: Failed tools have been marked as broken in the database.'); + console.log(' Check individual error logs above for detailed failure reasons.'); } return tools; @@ -272,7 +293,8 @@ export function addConversationTools(conversationId: string, toolKeys: string[]) if (!conversationTools.has(conversationId)) { conversationTools.set(conversationId, new Set()); } - const tools = conversationTools.get(conversationId)!; + const tools = conversationTools.get(conversationId); + if (!tools) return; // Should never happen after the check above for (const key of toolKeys) { tools.add(key); }