diff options
Diffstat (limited to 'src/client/views/nodes/chatbot/agentsystem')
| -rw-r--r-- | src/client/views/nodes/chatbot/agentsystem/Agent.ts | 91 |
1 files changed, 48 insertions, 43 deletions
diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts index c3d37fd0e..8516f054b 100644 --- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts +++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts @@ -113,65 +113,62 @@ export class Agent { } /** - * Loads existing dynamic tools by checking the current registry and ensuring all stored tools are available + * Loads every dynamic tool that the server reports via /getDynamicTools. + * • Uses dynamic `import()` so webpack/vite will code-split each tool automatically. + * • Registers the tool in `dynamicToolRegistry` under the name it advertises via + * `toolInfo.name`; also registers the legacy camel-case key if different. */ private async loadExistingDynamicTools(): Promise<void> { try { - console.log('Loading dynamic tools...'); + console.log('Loading dynamic tools from server…'); + const toolFiles = await this.fetchDynamicToolList(); - // Since we're in a browser environment, we can't use filesystem operations - // Instead, we'll maintain tools in the registry and try to load known tools + let loaded = 0; + for (const { name: className, path } of toolFiles) { + // Legacy key (e.g., CharacterCountTool → characterCountTool) + const legacyKey = className.replace(/^[A-Z]/, m => m.toLowerCase()); - // Try to manually load the known dynamic tools that exist - const knownDynamicTools = [ - { name: 'CharacterCountTool', actionName: 'charactercount' }, - { name: 'WordCountTool', actionName: 'wordcount' }, - { name: 'TestTool', actionName: 'test' }, - ]; + // Skip if we already have the legacy key + if (this.dynamicToolRegistry.has(legacyKey)) continue; - let loadedCount = 0; - for (const toolInfo of knownDynamicTools) { try { - // Check if tool is already in registry - if (this.dynamicToolRegistry.has(toolInfo.actionName)) { - console.log(`✓ Tool ${toolInfo.actionName} already loaded`); - loadedCount++; + // @vite-ignore keeps Vite/Webpack from trying to statically analyse the variable part + const ToolClass = require(`../tools/${path}`)[className]; + + if (!ToolClass || !(ToolClass.prototype instanceof BaseTool)) { + console.warn(`File ${path} does not export a valid BaseTool subclass`); continue; } - // Try to load the tool using require (works better in webpack environment) - let toolInstance = null; - try { - // Use require with the relative path - const toolModule = require(`../tools/dynamic/${toolInfo.name}`); - const ToolClass = toolModule[toolInfo.name]; + const instance: BaseTool<ReadonlyArray<Parameter>> = new ToolClass(); - if (ToolClass && typeof ToolClass === 'function') { - toolInstance = new ToolClass(); + // Prefer the tool’s self-declared name (matches <action> tag) + const key = (instance.name || '').trim() || legacyKey; - if (toolInstance instanceof BaseTool) { - this.dynamicToolRegistry.set(toolInfo.actionName, toolInstance); - loadedCount++; - console.log(`✓ Loaded dynamic tool: ${toolInfo.actionName} (from ${toolInfo.name})`); - } - } - } catch (requireError) { - // Tool file doesn't exist or can't be loaded, which is fine - console.log(`Tool ${toolInfo.name} not available:`, (requireError as Error).message); + // Check for duplicates + if (this.dynamicToolRegistry.has(key)) { + console.warn(`Dynamic tool key '${key}' already registered – keeping existing instance`); + continue; } - } catch (error) { - console.warn(`⚠ Failed to load ${toolInfo.name}:`, error); - } - } - console.log(`Successfully loaded ${loadedCount} dynamic tools`); + // ✅ register under the preferred key + this.dynamicToolRegistry.set(key, instance); - // Log all currently registered dynamic tools - if (this.dynamicToolRegistry.size > 0) { - console.log('Currently registered dynamic tools:', Array.from(this.dynamicToolRegistry.keys())); + // optional: also register the legacy key for safety + if (key !== legacyKey && !this.dynamicToolRegistry.has(legacyKey)) { + this.dynamicToolRegistry.set(legacyKey, instance); + } + + loaded++; + console.info(`✓ Loaded dynamic tool '${key}' from '${path}'`); + } catch (err) { + console.error(`✗ Failed to load '${path}':`, err); + } } - } catch (error) { - console.error('Error loading dynamic tools:', error); + + console.log(`Dynamic-tool load complete – ${loaded}/${toolFiles.length} added`); + } catch (err) { + console.error('Dynamic-tool bootstrap failed:', err); } } @@ -246,6 +243,14 @@ export class Agent { this.loadExistingDynamicTools(); } + private async fetchDynamicToolList(): Promise<{ name: string; path: string }[]> { + const res = await fetch('/getDynamicTools'); + if (!res.ok) throw new Error(`Failed to fetch dynamic tool list – ${res.statusText}`); + const json = await res.json(); + console.log('Dynamic tools fetched:', json.tools); + return json.tools ?? []; + } + /** * This method handles the conversation flow with the assistant, processes user queries, * and manages the assistant's decision-making process, including tool actions. |
