aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/chatbot/agentsystem/Agent.ts91
-rw-r--r--src/client/views/nodes/chatbot/tools/dynamic/CharacterCountTool.ts33
-rw-r--r--src/client/views/nodes/chatbot/tools/dynamic/InspirationalQuotesTool.ts39
3 files changed, 87 insertions, 76 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.
diff --git a/src/client/views/nodes/chatbot/tools/dynamic/CharacterCountTool.ts b/src/client/views/nodes/chatbot/tools/dynamic/CharacterCountTool.ts
deleted file mode 100644
index 38fed231c..000000000
--- a/src/client/views/nodes/chatbot/tools/dynamic/CharacterCountTool.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Observation } from '../../types/types';
-import { ParametersType, ToolInfo } from '../../types/tool_types';
-import { BaseTool } from '../BaseTool';
-
-const characterCountParams = [
- {
- name: 'text',
- type: 'string',
- description: 'The text to count characters in',
- required: true
- }
- ] as const;
-
- type CharacterCountParamsType = typeof characterCountParams;
-
- const characterCountInfo: ToolInfo<CharacterCountParamsType> = {
- name: 'charactercount',
- description: 'Counts characters in text, excluding spaces',
- citationRules: 'No citation needed.',
- parameterRules: characterCountParams
- };
-
- export class CharacterCountTool extends BaseTool<CharacterCountParamsType> {
- constructor() {
- super(characterCountInfo);
- }
-
- async execute(args: ParametersType<CharacterCountParamsType>): Promise<Observation[]> {
- const { text } = args;
- const count = text ? text.replace(/\s/g, '').length : 0;
- return [{ type: 'text', text: `Character count (excluding spaces): ${count}` }];
- }
- } \ No newline at end of file
diff --git a/src/client/views/nodes/chatbot/tools/dynamic/InspirationalQuotesTool.ts b/src/client/views/nodes/chatbot/tools/dynamic/InspirationalQuotesTool.ts
new file mode 100644
index 000000000..23bbe1d76
--- /dev/null
+++ b/src/client/views/nodes/chatbot/tools/dynamic/InspirationalQuotesTool.ts
@@ -0,0 +1,39 @@
+import { Observation } from '../../types/types';
+import { ParametersType, ToolInfo } from '../../types/tool_types';
+import { BaseTool } from '../BaseTool';
+
+const inspirationalQuotesParams = [
+ {
+ name: 'category',
+ type: 'string',
+ description: 'The category of inspirational quotes to retrieve',
+ required: false
+ }
+ ] as const;
+
+ type InspirationalQuotesParamsType = typeof inspirationalQuotesParams;
+
+ const inspirationalQuotesInfo: ToolInfo<InspirationalQuotesParamsType> = {
+ name: 'inspirationalquotestool',
+ description: 'Provides a random inspirational quote from a predefined list.',
+ citationRules: 'No citation needed.',
+ parameterRules: inspirationalQuotesParams
+ };
+
+ export class InspirationalQuotesTool extends BaseTool<InspirationalQuotesParamsType> {
+ constructor() {
+ super(inspirationalQuotesInfo);
+ }
+
+ async execute(args: ParametersType<InspirationalQuotesParamsType>): Promise<Observation[]> {
+ const quotes = [
+ "The only way to do great work is to love what you do. - Steve Jobs",
+ "The best time to plant a tree was 20 years ago. The second best time is now. - Chinese Proverb",
+ "Your time is limited, so don’t waste it living someone else’s life. - Steve Jobs",
+ "Not everything that is faced can be changed, but nothing can be changed until it is faced. - James Baldwin",
+ "The purpose of our lives is to be happy. - Dalai Lama"
+ ];
+ const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
+ return [{ type: 'text', text: randomQuote }];
+ }
+ } \ No newline at end of file