@minded-ai/mindedjs 2.0.14-beta-1 → 2.0.15-beta-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +17 -7
- package/dist/agent.js.map +1 -1
- package/dist/browserTask/executeBrowserTask.d.ts +2 -2
- package/dist/browserTask/executeBrowserTask.d.ts.map +1 -1
- package/dist/browserTask/executeBrowserTask.js +16 -5
- package/dist/browserTask/executeBrowserTask.js.map +1 -1
- package/dist/browserTask/executeBrowserTask.py +0 -1
- package/dist/browserTask/localBrowserTask.js +17 -7
- package/dist/browserTask/localBrowserTask.js.map +1 -1
- package/dist/checkpointer/checkpointSaverFactory.js +17 -7
- package/dist/checkpointer/checkpointSaverFactory.js.map +1 -1
- package/dist/cli/index.js +45 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lambdaHandlerTemplate.d.ts.map +1 -1
- package/dist/cli/localOperatorSetup.d.ts +6 -0
- package/dist/cli/localOperatorSetup.d.ts.map +1 -0
- package/dist/cli/localOperatorSetup.js +310 -0
- package/dist/cli/localOperatorSetup.js.map +1 -0
- package/dist/edges/createDirectEdge.d.ts.map +1 -1
- package/dist/edges/createLogicalRouter.d.ts.map +1 -1
- package/dist/edges/createLogicalRouter.js +17 -7
- package/dist/edges/createLogicalRouter.js.map +1 -1
- package/dist/edges/createPromptRouter.d.ts.map +1 -1
- package/dist/edges/edgeFactory.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/interfaces/zendesk.js +17 -7
- package/dist/interfaces/zendesk.js.map +1 -1
- package/dist/internalTools/appActionRunnerTool.d.ts.map +1 -1
- package/dist/internalTools/appActionRunnerTool.js +17 -7
- package/dist/internalTools/appActionRunnerTool.js.map +1 -1
- package/dist/internalTools/documentExtraction/documentExtraction.js +17 -7
- package/dist/internalTools/documentExtraction/documentExtraction.js.map +1 -1
- package/dist/internalTools/libraryActionRunnerTool.d.ts.map +1 -1
- package/dist/internalTools/timer.js +17 -7
- package/dist/internalTools/timer.js.map +1 -1
- package/dist/internalTools/voice/escalateVoiceCall.d.ts.map +1 -1
- package/dist/internalTools/voice/escalateVoiceCall.js +17 -7
- package/dist/internalTools/voice/escalateVoiceCall.js.map +1 -1
- package/dist/internalTools/voice/retell.js +17 -7
- package/dist/internalTools/voice/retell.js.map +1 -1
- package/dist/internalTools/voice/sendPlaceholderMessage.js +17 -7
- package/dist/internalTools/voice/sendPlaceholderMessage.js.map +1 -1
- package/dist/interrupts/MindedInterruptSessionManager.js +17 -7
- package/dist/interrupts/MindedInterruptSessionManager.js.map +1 -1
- package/dist/interrupts/interruptSessionManagerFactory.js +17 -7
- package/dist/interrupts/interruptSessionManagerFactory.js.map +1 -1
- package/dist/llm/createLlmInstance.d.ts.map +1 -1
- package/dist/nodes/addAppToolNode.d.ts.map +1 -1
- package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
- package/dist/nodes/addBrowserTaskNode.js.map +1 -1
- package/dist/nodes/addBrowserTaskRunNode.d.ts.map +1 -1
- package/dist/nodes/addHumanInTheLoopNode.d.ts.map +1 -1
- package/dist/nodes/addJumpToNode.d.ts.map +1 -1
- package/dist/nodes/addJunctionNode.d.ts.map +1 -1
- package/dist/nodes/addPromptNode.d.ts.map +1 -1
- package/dist/nodes/addRpaNode.d.ts.map +1 -1
- package/dist/nodes/addRpaNode.js +12 -1
- package/dist/nodes/addRpaNode.js.map +1 -1
- package/dist/nodes/addToolNode.d.ts.map +1 -1
- package/dist/nodes/addToolRunNode.d.ts.map +1 -1
- package/dist/nodes/addTriggerNode.d.ts.map +1 -1
- package/dist/nodes/compilePrompt.js +17 -7
- package/dist/nodes/compilePrompt.js.map +1 -1
- package/dist/nodes/nodeFactory.d.ts.map +1 -1
- package/dist/platform/mindedCheckpointSaver.js +17 -7
- package/dist/platform/mindedCheckpointSaver.js.map +1 -1
- package/dist/platform/mindedConnection.d.ts.map +1 -1
- package/dist/platform/mindedConnection.js +17 -7
- package/dist/platform/mindedConnection.js.map +1 -1
- package/dist/platform/piiGateway/gateway.js +17 -7
- package/dist/platform/piiGateway/gateway.js.map +1 -1
- package/dist/platform/utils/parseAttachments.d.ts.map +1 -1
- package/dist/platform/utils/tools.d.ts.map +1 -1
- package/dist/playbooks/playbooks.js +17 -7
- package/dist/playbooks/playbooks.js.map +1 -1
- package/dist/toolsLibrary/classifier.d.ts +12 -20
- package/dist/toolsLibrary/classifier.d.ts.map +1 -1
- package/dist/toolsLibrary/classifier.js +71 -109
- package/dist/toolsLibrary/classifier.js.map +1 -1
- package/dist/toolsLibrary/extraction.d.ts.map +1 -1
- package/dist/toolsLibrary/index.js +17 -7
- package/dist/toolsLibrary/index.js.map +1 -1
- package/dist/toolsLibrary/parseDocument.d.ts +3 -3
- package/dist/types/LLM.types.js.map +1 -1
- package/dist/types/LangGraph.types.d.ts.map +1 -1
- package/dist/utils/agentUtils.js +17 -7
- package/dist/utils/agentUtils.js.map +1 -1
- package/dist/utils/history.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +2 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/wait.d.ts.map +1 -1
- package/dist/voice/voiceSession.js +17 -7
- package/dist/voice/voiceSession.js.map +1 -1
- package/package.json +1 -1
- package/src/browserTask/executeBrowserTask.py +0 -1
- package/src/browserTask/executeBrowserTask.ts +34 -20
- package/src/cli/index.ts +25 -7
- package/src/cli/localOperatorSetup.ts +308 -0
- package/src/nodes/addBrowserTaskNode.ts +2 -2
- package/src/nodes/addRpaNode.ts +13 -2
- package/src/toolsLibrary/classifier.ts +86 -114
- package/src/types/LLM.types.ts +2 -2
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { execSync, spawn } from 'child_process';
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
|
|
6
|
+
const VENV_DIR = '.venv';
|
|
7
|
+
const SETUP_MARKER_FILE = '.minded-local-operator-setup';
|
|
8
|
+
|
|
9
|
+
interface SetupPackage {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
command: string;
|
|
13
|
+
checkCommand?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const REQUIRED_PACKAGES: SetupPackage[] = [
|
|
17
|
+
{
|
|
18
|
+
name: 'uv',
|
|
19
|
+
description: 'Python package manager for fast dependency installation',
|
|
20
|
+
command: 'curl -LsSf https://astral.sh/uv/install.sh | sh',
|
|
21
|
+
checkCommand: 'uv --version',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'Python 3.12',
|
|
25
|
+
description: 'Python runtime for browser automation',
|
|
26
|
+
command: 'python3.12 --version',
|
|
27
|
+
checkCommand: 'python3.12 --version',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'browser-use',
|
|
31
|
+
description: 'Browser automation library',
|
|
32
|
+
command: 'uv pip install browser-use',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Playwright Chromium',
|
|
36
|
+
description: 'Chromium browser for automation',
|
|
37
|
+
command: 'uvx playwright install chromium --with-deps',
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export function isLocalOperatorSetup(): boolean {
|
|
42
|
+
const setupMarkerPath = path.join(process.cwd(), SETUP_MARKER_FILE);
|
|
43
|
+
const venvPath = path.join(process.cwd(), VENV_DIR);
|
|
44
|
+
|
|
45
|
+
// Check if both the marker file and venv directory exist
|
|
46
|
+
return fs.existsSync(setupMarkerPath) && fs.existsSync(venvPath);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function checkCommand(command: string): boolean {
|
|
50
|
+
try {
|
|
51
|
+
execSync(command, { stdio: 'ignore' });
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function promptUser(question: string): Promise<boolean> {
|
|
59
|
+
// Check if we're in an interactive terminal
|
|
60
|
+
if (!process.stdin.isTTY) {
|
|
61
|
+
console.warn('Non-interactive terminal detected, skipping prompt and cancelling installation');
|
|
62
|
+
return Promise.resolve(false);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
const rl = readline.createInterface({
|
|
67
|
+
input: process.stdin,
|
|
68
|
+
output: process.stdout,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Set a timeout in case the prompt hangs
|
|
72
|
+
const timeout = setTimeout(() => {
|
|
73
|
+
console.warn('Prompt timeout (30s), cancelling installation');
|
|
74
|
+
rl.close();
|
|
75
|
+
resolve(false);
|
|
76
|
+
}, 30000); // 30 second timeout
|
|
77
|
+
|
|
78
|
+
console.log('Displaying prompt to user');
|
|
79
|
+
|
|
80
|
+
rl.question(question, (answer) => {
|
|
81
|
+
clearTimeout(timeout);
|
|
82
|
+
console.log(`User answered: "${answer}"`);
|
|
83
|
+
|
|
84
|
+
rl.close();
|
|
85
|
+
|
|
86
|
+
const confirmed = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
87
|
+
console.log(`Prompt result: ${confirmed}`);
|
|
88
|
+
|
|
89
|
+
resolve(confirmed);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Handle potential readline errors
|
|
93
|
+
rl.on('error', (error) => {
|
|
94
|
+
console.error('Readline error, cancelling installation:', error.message);
|
|
95
|
+
clearTimeout(timeout);
|
|
96
|
+
rl.close();
|
|
97
|
+
resolve(false);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function runCommand(command: string, description: string): Promise<void> {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
console.log(`Installing ${description}...`);
|
|
105
|
+
|
|
106
|
+
const isComplexCommand = command.includes('|') || command.includes('&&');
|
|
107
|
+
|
|
108
|
+
if (isComplexCommand) {
|
|
109
|
+
// For complex commands with pipes or &&, use shell
|
|
110
|
+
const child = spawn('sh', ['-c', command], {
|
|
111
|
+
stdio: 'inherit',
|
|
112
|
+
shell: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
child.on('error', (error) => {
|
|
116
|
+
console.error(`Failed to install ${description}:`, error.message);
|
|
117
|
+
reject(error);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
child.on('close', (code) => {
|
|
121
|
+
if (code === 0) {
|
|
122
|
+
console.log(`✓ ${description} installed successfully`);
|
|
123
|
+
resolve();
|
|
124
|
+
} else {
|
|
125
|
+
reject(new Error(`Command exited with code ${code}`));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
// For simple commands, use execSync
|
|
130
|
+
try {
|
|
131
|
+
execSync(command, { stdio: 'inherit' });
|
|
132
|
+
console.log(`✓ ${description} installed successfully`);
|
|
133
|
+
resolve();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error(`Failed to install ${description}:`, error);
|
|
136
|
+
reject(error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function setupLocalOperator(skipPrompt: boolean = false): Promise<void> {
|
|
143
|
+
console.log('\n🤖 Minded Local Operator Setup\n');
|
|
144
|
+
|
|
145
|
+
// Check if already setup
|
|
146
|
+
if (isLocalOperatorSetup()) {
|
|
147
|
+
console.log('✓ Local operator is already set up');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Display what will be installed
|
|
152
|
+
console.log('The following packages will be installed for local browser automation:\n');
|
|
153
|
+
REQUIRED_PACKAGES.forEach((pkg, index) => {
|
|
154
|
+
console.log(` ${index + 1}. ${pkg.name} - ${pkg.description}`);
|
|
155
|
+
});
|
|
156
|
+
console.log('');
|
|
157
|
+
|
|
158
|
+
// Ask for confirmation unless skipped
|
|
159
|
+
if (!skipPrompt) {
|
|
160
|
+
console.log('Waiting for user confirmation...');
|
|
161
|
+
const confirmed = await promptUser('Do you want to proceed with the installation? (y/n): ');
|
|
162
|
+
if (!confirmed) {
|
|
163
|
+
console.log('Installation cancelled by user');
|
|
164
|
+
process.exit(0);
|
|
165
|
+
}
|
|
166
|
+
console.log('User confirmed, proceeding with installation');
|
|
167
|
+
} else {
|
|
168
|
+
console.log('Skipping confirmation prompt, proceeding with installation...');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log('\n📦 Starting installation...\n');
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
// Check and install uv if needed
|
|
175
|
+
if (!checkCommand('uv --version')) {
|
|
176
|
+
console.log('Installing uv package manager...');
|
|
177
|
+
await runCommand('curl -LsSf https://astral.sh/uv/install.sh | sh', 'uv');
|
|
178
|
+
|
|
179
|
+
// Add uv to PATH for current session
|
|
180
|
+
const uvPath = path.join(process.env.HOME || '', '.cargo', 'bin');
|
|
181
|
+
process.env.PATH = `${uvPath}:${process.env.PATH}`;
|
|
182
|
+
} else {
|
|
183
|
+
console.log('✓ uv is already installed');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Check Python 3.12
|
|
187
|
+
if (!checkCommand('python3.12 --version')) {
|
|
188
|
+
console.error('Python 3.12 is required but not found');
|
|
189
|
+
console.log('Please install Python 3.12 manually:');
|
|
190
|
+
console.log(' macOS: brew install python@3.12');
|
|
191
|
+
console.log(' Ubuntu/Debian: sudo apt install python3.12');
|
|
192
|
+
console.log(' Or visit: https://www.python.org/downloads/');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
} else {
|
|
195
|
+
console.log('✓ Python 3.12 is available');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Create virtual environment
|
|
199
|
+
console.log('Creating Python virtual environment...');
|
|
200
|
+
execSync(`uv venv --python 3.12 ${VENV_DIR}`, { stdio: 'inherit' });
|
|
201
|
+
console.log('✓ Virtual environment created');
|
|
202
|
+
|
|
203
|
+
// Install browser-use in the venv
|
|
204
|
+
console.log('Installing browser-use package...');
|
|
205
|
+
execSync(`uv pip install --python ${path.join(process.cwd(), VENV_DIR, 'bin', 'python')} browser-use`, {
|
|
206
|
+
stdio: 'inherit',
|
|
207
|
+
});
|
|
208
|
+
console.log('✓ browser-use installed');
|
|
209
|
+
|
|
210
|
+
// Install Playwright Chromium
|
|
211
|
+
console.log('Installing Playwright Chromium browser...');
|
|
212
|
+
execSync(`uvx playwright install chromium --with-deps`, { stdio: 'inherit' });
|
|
213
|
+
console.log('✓ Playwright Chromium installed');
|
|
214
|
+
|
|
215
|
+
// Create setup marker file
|
|
216
|
+
const setupMarkerPath = path.join(process.cwd(), SETUP_MARKER_FILE);
|
|
217
|
+
const setupInfo = {
|
|
218
|
+
version: '1.0.0',
|
|
219
|
+
setupDate: new Date().toISOString(),
|
|
220
|
+
packages: REQUIRED_PACKAGES.map((p) => p.name),
|
|
221
|
+
venvPath: VENV_DIR,
|
|
222
|
+
};
|
|
223
|
+
fs.writeFileSync(setupMarkerPath, JSON.stringify(setupInfo, null, 2));
|
|
224
|
+
|
|
225
|
+
// Add to .gitignore if not already there
|
|
226
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
227
|
+
if (fs.existsSync(gitignorePath)) {
|
|
228
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
229
|
+
const itemsToIgnore = [VENV_DIR, SETUP_MARKER_FILE, 'downloads/'];
|
|
230
|
+
const linesToAdd: string[] = [];
|
|
231
|
+
|
|
232
|
+
itemsToIgnore.forEach((item) => {
|
|
233
|
+
if (!gitignoreContent.includes(item)) {
|
|
234
|
+
linesToAdd.push(item);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (linesToAdd.length > 0) {
|
|
239
|
+
const newContent = gitignoreContent.trimEnd() + '\n\n# Minded Local Operator\n' + linesToAdd.join('\n') + '\n';
|
|
240
|
+
fs.writeFileSync(gitignorePath, newContent);
|
|
241
|
+
console.log('✓ Updated .gitignore');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Add BROWSER_TASK_MODE to .env file
|
|
246
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
247
|
+
const browserTaskModeVar = 'BROWSER_TASK_MODE=localRun';
|
|
248
|
+
|
|
249
|
+
if (fs.existsSync(envPath)) {
|
|
250
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
251
|
+
|
|
252
|
+
// Check if BROWSER_TASK_MODE is already set
|
|
253
|
+
if (!envContent.includes('BROWSER_TASK_MODE')) {
|
|
254
|
+
// Append to existing .env file
|
|
255
|
+
const newEnvContent = envContent.trimEnd() + '\n' + browserTaskModeVar + '\n';
|
|
256
|
+
fs.writeFileSync(envPath, newEnvContent);
|
|
257
|
+
console.log('✓ Added BROWSER_TASK_MODE=localRun to .env');
|
|
258
|
+
} else if (!envContent.includes('BROWSER_TASK_MODE=localRun')) {
|
|
259
|
+
// BROWSER_TASK_MODE exists but with different value, update it
|
|
260
|
+
const updatedEnvContent = envContent.replace(/BROWSER_TASK_MODE=.*$/m, browserTaskModeVar);
|
|
261
|
+
fs.writeFileSync(envPath, updatedEnvContent);
|
|
262
|
+
console.log('✓ Updated BROWSER_TASK_MODE to localRun in .env');
|
|
263
|
+
} else {
|
|
264
|
+
console.log('✓ BROWSER_TASK_MODE=localRun already configured in .env');
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
// Create new .env file
|
|
268
|
+
fs.writeFileSync(envPath, browserTaskModeVar + '\n');
|
|
269
|
+
console.log('✓ Created .env file with BROWSER_TASK_MODE=localRun');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
console.log('\n' + '='.repeat(50));
|
|
273
|
+
console.log('✅ Local operator setup completed successfully!');
|
|
274
|
+
console.log('='.repeat(50) + '\n');
|
|
275
|
+
|
|
276
|
+
console.log('You can now use browser automation features locally.');
|
|
277
|
+
console.log('The virtual environment will be activated automatically when needed.');
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error('Setup failed:', error);
|
|
280
|
+
|
|
281
|
+
// Clean up partial installation
|
|
282
|
+
try {
|
|
283
|
+
if (fs.existsSync(path.join(process.cwd(), VENV_DIR))) {
|
|
284
|
+
fs.rmSync(path.join(process.cwd(), VENV_DIR), { recursive: true });
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
// Ignore cleanup errors
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function validateLocalOperatorSetup(): void {
|
|
295
|
+
if (!isLocalOperatorSetup()) {
|
|
296
|
+
console.error('Local operator is not set up');
|
|
297
|
+
console.log('Please run: npx minded setup-local-operator');
|
|
298
|
+
throw new Error('Local operator not configured');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function getVenvPath(): string {
|
|
303
|
+
return path.join(process.cwd(), VENV_DIR);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function getVenvBinPath(): string {
|
|
307
|
+
return path.join(getVenvPath(), 'bin');
|
|
308
|
+
}
|
|
@@ -31,10 +31,10 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
|
|
|
31
31
|
const zodSchema = createZodSchemaFromFields(node.inputSchema);
|
|
32
32
|
|
|
33
33
|
// Create langchain tool
|
|
34
|
-
const tool = langchainTool(() => {
|
|
34
|
+
const tool = langchainTool(() => {}, {
|
|
35
35
|
name: 'browser-task',
|
|
36
36
|
description: node.prompt,
|
|
37
|
-
schema: zodSchema,
|
|
37
|
+
schema: zodSchema as any,
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const combinedPlaybooks = combinePlaybooks(agent.playbooks);
|
package/src/nodes/addRpaNode.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { LLMProviders } from '../types/LLM.types';
|
|
|
12
12
|
import { AIMessage, ToolMessage } from '@langchain/core/messages';
|
|
13
13
|
import { v4 as uuidv4 } from 'uuid';
|
|
14
14
|
import { executeRpaStep } from './rpaStepsExecutor';
|
|
15
|
+
import { createBrowserSession } from '../browserTask/executeBrowserTask';
|
|
16
|
+
import { getConfig } from '../platform/config';
|
|
15
17
|
|
|
16
18
|
type AddRpaNodeParams = {
|
|
17
19
|
graph: PreCompiledGraph;
|
|
@@ -29,13 +31,22 @@ export const addRpaNode = async ({ graph, node, agent, llm }: AddRpaNodeParams)
|
|
|
29
31
|
|
|
30
32
|
let browser: Browser | null = null;
|
|
31
33
|
let page: Page | null = null;
|
|
34
|
+
const { browserTaskMode } = getConfig();
|
|
32
35
|
|
|
36
|
+
const session = await createBrowserSession({
|
|
37
|
+
sessionId: state.sessionId,
|
|
38
|
+
browserTaskMode,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!session.sessionId || !session.cdpUrl) {
|
|
42
|
+
throw new Error('Failed to create browser session: missing session details');
|
|
43
|
+
}
|
|
33
44
|
// Create tool call for RPA execution
|
|
34
45
|
const toolCallId = uuidv4();
|
|
35
46
|
const aiMessageId = uuidv4();
|
|
36
|
-
|
|
47
|
+
state.cdpUrl = session.cdpUrl;
|
|
37
48
|
// Get CDP URL from state
|
|
38
|
-
const cdpUrl =
|
|
49
|
+
const cdpUrl = session.cdpUrl;
|
|
39
50
|
|
|
40
51
|
const toolCall = {
|
|
41
52
|
id: toolCallId,
|
|
@@ -5,6 +5,15 @@ import { JsonOutputParser } from '@langchain/core/output_parsers';
|
|
|
5
5
|
import { SystemMessage } from '@langchain/core/messages';
|
|
6
6
|
import { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
7
7
|
|
|
8
|
+
// Type guard for checking if LLM supports structured output
|
|
9
|
+
interface StructuredOutputLLM extends BaseLanguageModel {
|
|
10
|
+
withStructuredOutput<T extends z.ZodType>(schema: T): BaseLanguageModel;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function supportsStructuredOutput(llm: BaseLanguageModel): llm is StructuredOutputLLM {
|
|
14
|
+
return 'withStructuredOutput' in llm && typeof (llm as any).withStructuredOutput === 'function';
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
// Type definitions for classifier configuration
|
|
9
18
|
export interface ClassDefinition {
|
|
10
19
|
name: string;
|
|
@@ -14,53 +23,34 @@ export interface ClassDefinition {
|
|
|
14
23
|
export interface ClassifierConfig {
|
|
15
24
|
classes: ClassDefinition[];
|
|
16
25
|
systemPrompt?: string;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
defaultClass?: string;
|
|
20
|
-
defaultReason?: string;
|
|
26
|
+
defaultClass: string;
|
|
27
|
+
defaultReason: string;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
export interface ClassificationResult {
|
|
24
31
|
class: string;
|
|
25
|
-
reason
|
|
26
|
-
confidence
|
|
32
|
+
reason: string;
|
|
33
|
+
confidence: number;
|
|
27
34
|
[key: string]: any; // Allow additional fields
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
// Default configuration
|
|
31
|
-
const DEFAULT_CONFIG: Partial<ClassifierConfig> = {
|
|
32
|
-
outputFormat: 'json',
|
|
33
|
-
includeReason: true,
|
|
34
|
-
defaultClass: 'unknown',
|
|
35
|
-
defaultReason: 'Unable to determine classification',
|
|
36
|
-
};
|
|
37
|
-
|
|
38
37
|
/**
|
|
39
|
-
*
|
|
40
|
-
* @param result The classification result to validate
|
|
41
|
-
* @param config The classifier configuration
|
|
42
|
-
* @returns True if the result is valid, false otherwise
|
|
38
|
+
* Create a dynamic Zod schema for classification result based on valid classes
|
|
43
39
|
*/
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
if (!result || !result.class) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
40
|
+
function createClassificationSchema(config: ClassifierConfig): z.ZodSchema {
|
|
41
|
+
const validClassNames = config.classes.map((c) => c.name);
|
|
49
42
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
if (!validClasses.includes(result.class)) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
43
|
+
// Create a union type for valid class names
|
|
44
|
+
const classEnum = z.enum(validClassNames as [string, ...string[]]);
|
|
55
45
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
46
|
+
// Build the schema - always include all fields
|
|
47
|
+
const schemaShape: Record<string, z.ZodTypeAny> = {
|
|
48
|
+
class: classEnum.describe('The selected classification category'),
|
|
49
|
+
reason: z.string().describe('Explanation for the classification'),
|
|
50
|
+
confidence: z.number().min(0).max(1).describe('Confidence score between 0 and 1'),
|
|
51
|
+
};
|
|
62
52
|
|
|
63
|
-
return
|
|
53
|
+
return z.object(schemaShape);
|
|
64
54
|
}
|
|
65
55
|
|
|
66
56
|
/**
|
|
@@ -71,65 +61,49 @@ function validateClassificationResult(result: ClassificationResult, config: Clas
|
|
|
71
61
|
* @returns The classification result
|
|
72
62
|
*/
|
|
73
63
|
export async function classify(content: string, config: ClassifierConfig, llm: BaseLanguageModel): Promise<ClassificationResult> {
|
|
74
|
-
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
75
64
|
const MAX_RETRIES = 3;
|
|
76
65
|
let lastError: Error | null = null;
|
|
77
|
-
|
|
66
|
+
|
|
67
|
+
// Check if LLM supports structured output upfront
|
|
68
|
+
if (!supportsStructuredOutput(llm)) {
|
|
69
|
+
throw new Error('LLM does not support structured output, which is required for classification');
|
|
70
|
+
}
|
|
78
71
|
|
|
79
72
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
80
73
|
try {
|
|
81
74
|
// Build the classification prompt
|
|
82
|
-
const classesDescription =
|
|
83
|
-
const validClassNames = mergedConfig.classes.map((c) => c.name);
|
|
75
|
+
const classesDescription = config.classes.map((c) => `${c.name}: ${c.description}`).join('\n');
|
|
84
76
|
|
|
85
77
|
const basePrompt =
|
|
86
|
-
|
|
87
|
-
'You are a classifier. Your task is to classify the given content into one of the following categories:';
|
|
78
|
+
config.systemPrompt || 'You are a classifier. Your task is to classify the given content into one of the following categories:';
|
|
88
79
|
|
|
89
80
|
let prompt = `${basePrompt}\n\n${classesDescription}\n\n`;
|
|
90
81
|
|
|
91
82
|
// Add retry feedback if this is not the first attempt
|
|
92
|
-
if (attempt > 1
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
prompt += `You selected "${lastInvalidResult.class}" which is NOT in the list of valid classes. `;
|
|
96
|
-
} else if (!lastInvalidResult.class) {
|
|
97
|
-
prompt += `You did not provide a class value. `;
|
|
98
|
-
}
|
|
83
|
+
if (attempt > 1) {
|
|
84
|
+
const validClassNames = config.classes.map((c) => c.name);
|
|
85
|
+
prompt += `\n⚠️ IMPORTANT: Your previous classification attempt failed. `;
|
|
99
86
|
prompt += `You MUST select ONLY from these valid classes:\n${validClassNames.join(', ')}\n\n`;
|
|
100
87
|
}
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"class": "<selected class name>",
|
|
105
|
-
${mergedConfig.includeReason ? '"reason": "<explanation for the classification>",' : ''}
|
|
106
|
-
"confidence": <confidence score between 0 and 1>
|
|
107
|
-
}.\nReturn JSON and nothing more.`;
|
|
108
|
-
} else {
|
|
109
|
-
prompt += 'Return only the class name.';
|
|
110
|
-
}
|
|
89
|
+
// Create dynamic schema based on valid classes
|
|
90
|
+
const classificationSchema = createClassificationSchema(config);
|
|
111
91
|
|
|
92
|
+
// Add content to classify to the prompt
|
|
112
93
|
prompt += `\n\nContent to classify:\n${content}`;
|
|
113
94
|
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const parsedResult = await llm.pipe(parser).invoke([new SystemMessage(prompt)]);
|
|
119
|
-
result = parsedResult as ClassificationResult;
|
|
120
|
-
} else {
|
|
121
|
-
const llmResult = await llm.invoke([new SystemMessage(prompt)]);
|
|
122
|
-
const classText = typeof llmResult.content === 'string' ? llmResult.content.trim() : '';
|
|
123
|
-
result = { class: classText };
|
|
124
|
-
}
|
|
95
|
+
// Use structured output for guaranteed compliance
|
|
96
|
+
const structuredLLM = llm.withStructuredOutput(classificationSchema);
|
|
97
|
+
const messages = [new SystemMessage(prompt)];
|
|
98
|
+
const result = (await structuredLLM.invoke(messages)) as ClassificationResult;
|
|
125
99
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
100
|
+
// The result is already validated by withStructuredOutput
|
|
101
|
+
logger.debug({
|
|
102
|
+
message: 'Classification successful with structured output',
|
|
103
|
+
attempt,
|
|
104
|
+
class: result.class,
|
|
105
|
+
});
|
|
131
106
|
|
|
132
|
-
// Success - return the valid result
|
|
133
107
|
return result;
|
|
134
108
|
} catch (err) {
|
|
135
109
|
lastError = err as Error;
|
|
@@ -138,7 +112,6 @@ export async function classify(content: string, config: ClassifierConfig, llm: B
|
|
|
138
112
|
attempt,
|
|
139
113
|
maxRetries: MAX_RETRIES,
|
|
140
114
|
error: lastError.message,
|
|
141
|
-
invalidResult: lastInvalidResult,
|
|
142
115
|
});
|
|
143
116
|
|
|
144
117
|
// If this is not the last attempt, continue to retry
|
|
@@ -153,15 +126,14 @@ export async function classify(content: string, config: ClassifierConfig, llm: B
|
|
|
153
126
|
message: 'Classification failed after max retries, using default class',
|
|
154
127
|
maxRetries: MAX_RETRIES,
|
|
155
128
|
lastError: lastError?.message,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
defaultReason: mergedConfig.defaultReason,
|
|
129
|
+
defaultClass: config.defaultClass,
|
|
130
|
+
defaultReason: config.defaultReason,
|
|
159
131
|
});
|
|
160
132
|
|
|
161
133
|
// Return default classification
|
|
162
134
|
return {
|
|
163
|
-
class:
|
|
164
|
-
reason:
|
|
135
|
+
class: config.defaultClass,
|
|
136
|
+
reason: config.defaultReason,
|
|
165
137
|
confidence: 0,
|
|
166
138
|
};
|
|
167
139
|
}
|
|
@@ -169,10 +141,10 @@ export async function classify(content: string, config: ClassifierConfig, llm: B
|
|
|
169
141
|
/**
|
|
170
142
|
* Create a classifier from a simple class list
|
|
171
143
|
* @param classes Array of class names or [name, description] tuples
|
|
172
|
-
* @param options
|
|
144
|
+
* @param options Configuration options (includeReason, defaultClass, defaultReason are required)
|
|
173
145
|
* @returns A configured classify function
|
|
174
146
|
*/
|
|
175
|
-
export function createClassifier(classes: string[] | [string, string][], options
|
|
147
|
+
export function createClassifier(classes: string[] | [string, string][], options: Omit<ClassifierConfig, 'classes'>) {
|
|
176
148
|
const classDefinitions: ClassDefinition[] = classes.map((c) => {
|
|
177
149
|
if (typeof c === 'string') {
|
|
178
150
|
return { name: c, description: '' };
|
|
@@ -205,20 +177,18 @@ export const schema = z.object({
|
|
|
205
177
|
.optional()
|
|
206
178
|
.describe('Classes to classify into. Can be strings, [name, description] tuples, or objects with name and description'),
|
|
207
179
|
systemPrompt: z.string().optional().describe('Custom system prompt for classification'),
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
defaultClass: z.string().optional().describe('Default class to use if classification fails'),
|
|
211
|
-
defaultReason: z.string().optional().describe('Default reason to use if classification fails'),
|
|
180
|
+
defaultClass: z.string().describe('Default class to use if classification fails'),
|
|
181
|
+
defaultReason: z.string().describe('Default reason to use if classification fails'),
|
|
212
182
|
});
|
|
213
183
|
|
|
214
184
|
const classifierTool: Tool<typeof schema, any> = {
|
|
215
185
|
name: 'minded-classifier',
|
|
216
186
|
description:
|
|
217
|
-
'Classify content into predefined categories using AI. Supports custom classes
|
|
187
|
+
'Classify content into predefined categories using AI. Supports custom classes and system prompts. Uses structured output for guaranteed schema compliance. Requires default fallback values.',
|
|
218
188
|
input: schema,
|
|
219
189
|
isGlobal: false,
|
|
220
190
|
execute: async ({ input, state, agent }) => {
|
|
221
|
-
const { content, classes, systemPrompt,
|
|
191
|
+
const { content, classes, systemPrompt, defaultClass, defaultReason } = input;
|
|
222
192
|
|
|
223
193
|
logger.info({
|
|
224
194
|
message: 'Classifying content',
|
|
@@ -253,8 +223,6 @@ const classifierTool: Tool<typeof schema, any> = {
|
|
|
253
223
|
const config: ClassifierConfig = {
|
|
254
224
|
classes: classDefinitions,
|
|
255
225
|
systemPrompt,
|
|
256
|
-
includeReason,
|
|
257
|
-
outputFormat,
|
|
258
226
|
defaultClass,
|
|
259
227
|
defaultReason,
|
|
260
228
|
};
|
|
@@ -298,46 +266,50 @@ export async function multiClassify(
|
|
|
298
266
|
config: ClassifierConfig & { maxClasses?: number },
|
|
299
267
|
llm: BaseLanguageModel,
|
|
300
268
|
): Promise<ClassificationResult[]> {
|
|
301
|
-
const
|
|
302
|
-
|
|
269
|
+
const maxClasses = config.maxClasses || 3;
|
|
270
|
+
|
|
271
|
+
// Check if LLM supports structured output
|
|
272
|
+
if (!supportsStructuredOutput(llm)) {
|
|
273
|
+
throw new Error('LLM does not support structured output, which is required for multi-classification');
|
|
274
|
+
}
|
|
303
275
|
|
|
304
276
|
try {
|
|
305
|
-
const classesDescription =
|
|
277
|
+
const classesDescription = config.classes.map((c) => `${c.name}: ${c.description}`).join('\n');
|
|
278
|
+
const validClassNames = config.classes.map((c) => c.name);
|
|
306
279
|
|
|
307
|
-
const basePrompt =
|
|
308
|
-
mergedConfig.systemPrompt || 'You are a multi-label classifier. Select all applicable categories for the given content:';
|
|
280
|
+
const basePrompt = config.systemPrompt || 'You are a multi-label classifier. Select all applicable categories for the given content:';
|
|
309
281
|
|
|
310
282
|
const prompt = `${basePrompt}\n\n${classesDescription}\n\n
|
|
311
|
-
|
|
312
|
-
[
|
|
313
|
-
{
|
|
314
|
-
"class": "<class name>",
|
|
315
|
-
${mergedConfig.includeReason ? '"reason": "<explanation>",' : ''}
|
|
316
|
-
"confidence": <confidence score between 0 and 1>
|
|
317
|
-
},
|
|
318
|
-
...
|
|
319
|
-
]
|
|
320
|
-
Return JSON and nothing more.
|
|
283
|
+
Select up to ${maxClasses} classifications, ordered by relevance.
|
|
321
284
|
|
|
322
285
|
Content to classify:
|
|
323
286
|
${content}`;
|
|
324
287
|
|
|
325
|
-
|
|
326
|
-
const
|
|
288
|
+
// Create schema for array of classifications
|
|
289
|
+
const classEnum = z.enum(validClassNames as [string, ...string[]]);
|
|
290
|
+
const multiClassSchema = z
|
|
291
|
+
.array(
|
|
292
|
+
z.object({
|
|
293
|
+
class: classEnum.describe('The selected classification category'),
|
|
294
|
+
reason: z.string().describe('Explanation for the classification'),
|
|
295
|
+
confidence: z.number().min(0).max(1).describe('Confidence score between 0 and 1'),
|
|
296
|
+
}),
|
|
297
|
+
)
|
|
298
|
+
.min(1)
|
|
299
|
+
.max(maxClasses);
|
|
327
300
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
301
|
+
const structuredLLM = llm.withStructuredOutput(multiClassSchema);
|
|
302
|
+
const messages = [new SystemMessage(prompt)];
|
|
303
|
+
const result = (await structuredLLM.invoke(messages)) as ClassificationResult[];
|
|
331
304
|
|
|
332
|
-
|
|
333
|
-
return [result as ClassificationResult];
|
|
305
|
+
return result;
|
|
334
306
|
} catch (err) {
|
|
335
307
|
logger.error({ message: 'Multi-classification failed', err });
|
|
336
308
|
|
|
337
309
|
return [
|
|
338
310
|
{
|
|
339
|
-
class:
|
|
340
|
-
reason:
|
|
311
|
+
class: config.defaultClass,
|
|
312
|
+
reason: config.defaultReason,
|
|
341
313
|
confidence: 0,
|
|
342
314
|
},
|
|
343
315
|
];
|