aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/chatbot/agentsystem
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/chatbot/agentsystem')
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/Agent.ts91
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.