node-red-contrib-ai-agent 0.5.4 → 0.5.6
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/agent/ai-agent.html +1 -1
- package/model/ai-model.html +1 -1
- package/orchestrator/orchestrator.html +1 -1
- package/orchestrator-agent/orchestrator-agent.html +2 -2
- package/package.json +2 -1
- package/tool-approval/ai-tool-approval.html +59 -0
- package/tool-approval/ai-tool-approval.js +102 -0
- package/agent/ai-agent-icon.svg +0 -11
package/agent/ai-agent.html
CHANGED
package/model/ai-model.html
CHANGED
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
},
|
|
16
16
|
inputs: 1,
|
|
17
17
|
outputs: 1,
|
|
18
|
-
icon: '
|
|
18
|
+
icon: 'font-awesome/fa-sitemap',
|
|
19
19
|
label: function () {
|
|
20
20
|
return this.name || 'AI Orchestrator Agent';
|
|
21
21
|
},
|
|
22
|
-
paletteLabel: 'Orchestrator Agent',
|
|
22
|
+
paletteLabel: 'AI Orchestrator Agent',
|
|
23
23
|
oneditprepare: function () {
|
|
24
24
|
}
|
|
25
25
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-ai-agent",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "AI Agent for Node-RED",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"orchestrator/*",
|
|
35
35
|
"orchestrator-agent/*",
|
|
36
36
|
"tool/*",
|
|
37
|
+
"tool-approval/*",
|
|
37
38
|
"tool-function/*",
|
|
38
39
|
"tool-http/*"
|
|
39
40
|
],
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('ai-tool-approval', {
|
|
3
|
+
category: 'AI Agent',
|
|
4
|
+
color: '#FFCC00',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: { value: "" },
|
|
7
|
+
toolName: { value: "human_approval", required: true },
|
|
8
|
+
description: { value: "Ask a human for approval or information.", required: true }
|
|
9
|
+
},
|
|
10
|
+
inputs: 1,
|
|
11
|
+
outputs: 2,
|
|
12
|
+
outputLabels: ["agent flow", "approval requests"],
|
|
13
|
+
icon: "font-awesome/fa-user-check",
|
|
14
|
+
label: function () {
|
|
15
|
+
return this.name || this.toolName || "Approval Tool";
|
|
16
|
+
},
|
|
17
|
+
paletteLabel: "approval tool"
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script type="text/html" data-template-name="ai-tool-approval">
|
|
22
|
+
<div class="form-row">
|
|
23
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
24
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
25
|
+
</div>
|
|
26
|
+
<div class="form-row">
|
|
27
|
+
<label for="node-input-toolName"><i class="fa fa-code"></i> Tool Name</label>
|
|
28
|
+
<input type="text" id="node-input-toolName" placeholder="human_approval">
|
|
29
|
+
</div>
|
|
30
|
+
<div class="form-row">
|
|
31
|
+
<label for="node-input-description"><i class="fa fa-comment"></i> Description</label>
|
|
32
|
+
<textarea id="node-input-description" rows="3" style="width:70%;" placeholder="Request human intervention..."></textarea>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="form-tips">
|
|
35
|
+
<b>Tip:</b> This node creates a tool that the AI can call to pause execution and wait for human input.
|
|
36
|
+
Link the second output to a UI or notification node, and feed the response back into the input of this node.
|
|
37
|
+
</div>
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<script type="text/html" data-help-name="ai-tool-approval">
|
|
41
|
+
<p>Registers a human-in-the-loop approval tool with the AI Agent.</p>
|
|
42
|
+
|
|
43
|
+
<h3>Configuration</h3>
|
|
44
|
+
<dl class="message-properties">
|
|
45
|
+
<dt>Tool Name <span class="property-type">string</span></dt>
|
|
46
|
+
<dd>The name the LLM will see (e.g., 'approve_purchase').</dd>
|
|
47
|
+
<dt>Description <span class="property-type">string</span></dt>
|
|
48
|
+
<dd>Guidance for the LLM on when and why to use this tool.</dd>
|
|
49
|
+
</dl>
|
|
50
|
+
|
|
51
|
+
<h3>Functionality</h3>
|
|
52
|
+
<p>When the AI calls this tool:</p>
|
|
53
|
+
<ol>
|
|
54
|
+
<li>The Agent's execution pauses.</li>
|
|
55
|
+
<li>A request message is sent out of the <b>second output</b>.</li>
|
|
56
|
+
<li>The node waits for an input message containing the human's response in <code>msg.payload</code>.</li>
|
|
57
|
+
<li>Once received, the Agent resumes with the human's input as the tool's result.</li>
|
|
58
|
+
</ol>
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
function AIToolApprovalNode(config) {
|
|
5
|
+
RED.nodes.createNode(this, config);
|
|
6
|
+
const node = this;
|
|
7
|
+
|
|
8
|
+
// Configuration
|
|
9
|
+
node.name = config.name || 'human_approval_tool';
|
|
10
|
+
node.toolName = config.toolName || `approve_${Date.now()}`;
|
|
11
|
+
node.description = config.description || 'Request human approval or intervention before proceeding';
|
|
12
|
+
|
|
13
|
+
// Registry for pending approvals
|
|
14
|
+
node.pendingApprovals = new Map();
|
|
15
|
+
|
|
16
|
+
// Process incoming "human" messages (approvals/denials)
|
|
17
|
+
node.on('input', function (msg, send, done) {
|
|
18
|
+
// Check if this is a response to a pending approval
|
|
19
|
+
const approvalId = msg.approvalId || 'default';
|
|
20
|
+
if (node.pendingApprovals.has(approvalId)) {
|
|
21
|
+
const resolver = node.pendingApprovals.get(approvalId);
|
|
22
|
+
node.pendingApprovals.delete(approvalId);
|
|
23
|
+
|
|
24
|
+
node.status({ fill: 'green', shape: 'dot', text: 'Approved' });
|
|
25
|
+
resolver(msg.payload);
|
|
26
|
+
if (done) done();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Normal operation: Register tool
|
|
31
|
+
try {
|
|
32
|
+
const newMsg = RED.util.cloneMessage(msg);
|
|
33
|
+
newMsg.aiagent = newMsg.aiagent || {};
|
|
34
|
+
newMsg.aiagent.tools = newMsg.aiagent.tools || [];
|
|
35
|
+
|
|
36
|
+
const toolDef = {
|
|
37
|
+
type: 'function',
|
|
38
|
+
function: {
|
|
39
|
+
name: node.toolName,
|
|
40
|
+
description: node.description,
|
|
41
|
+
parameters: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
reason: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'The reason why human intervention is required'
|
|
47
|
+
},
|
|
48
|
+
question: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'The specific question or prompt for the human'
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
required: ['reason', 'question']
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
execute: async (args) => {
|
|
57
|
+
const approvalId = Date.now().toString();
|
|
58
|
+
node.status({ fill: 'yellow', shape: 'dot', text: 'Waiting for approval...' });
|
|
59
|
+
|
|
60
|
+
// Send request to output 2
|
|
61
|
+
node.send([null, {
|
|
62
|
+
payload: args,
|
|
63
|
+
approvalId: approvalId,
|
|
64
|
+
topic: 'approval_request'
|
|
65
|
+
}]);
|
|
66
|
+
|
|
67
|
+
// Return a promise that resolves when the input is received
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
node.pendingApprovals.set(approvalId, resolve);
|
|
70
|
+
|
|
71
|
+
// Timeout handling (optional)
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
if (node.pendingApprovals.has(approvalId)) {
|
|
74
|
+
node.pendingApprovals.delete(approvalId);
|
|
75
|
+
node.status({ fill: 'red', shape: 'dot', text: 'Timed out' });
|
|
76
|
+
resolve({ status: 'rejected', error: 'Approval timed out' });
|
|
77
|
+
}
|
|
78
|
+
}, 300000); // 5 minutes timeout
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
newMsg.aiagent.tools.push(toolDef);
|
|
84
|
+
node.status({ fill: 'green', shape: 'dot', text: 'Ready' });
|
|
85
|
+
send([newMsg, null]);
|
|
86
|
+
if (done) done();
|
|
87
|
+
} catch (error) {
|
|
88
|
+
node.status({ fill: 'red', shape: 'ring', text: 'Error' });
|
|
89
|
+
node.error('Error in AI Tool Approval node: ' + error.message, msg);
|
|
90
|
+
if (done) done();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
node.on('close', function (done) {
|
|
95
|
+
node.status({});
|
|
96
|
+
node.pendingApprovals.clear();
|
|
97
|
+
if (done) done();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
RED.nodes.registerType('ai-tool-approval', AIToolApprovalNode);
|
|
102
|
+
};
|
package/agent/ai-agent-icon.svg
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
-
<defs>
|
|
4
|
-
<linearGradient id="agentGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5
|
-
<stop offset="0%" style="stop-color:#4a90e2;stop-opacity:1" />
|
|
6
|
-
<stop offset="100%" style="stop-color:#7b68ee;stop-opacity:1" />
|
|
7
|
-
</linearGradient>
|
|
8
|
-
</defs>
|
|
9
|
-
<circle cx="50" cy="50" r="45" fill="url(#agentGradient)" stroke="#3a7bc8" stroke-width="2"/>
|
|
10
|
-
<path d="M35 40 Q50 25 65 40 M35 60 Q50 75 65 60" stroke="white" stroke-width="5" stroke-linecap="round" fill="none"/>
|
|
11
|
-
</svg>
|