@cpwc/node-red-contrib-ai-intent 3.1.0-alpha
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/.eslintrc +49 -0
- package/.idea/modules.xml +8 -0
- package/.idea/node-red-contrib-ai-intent.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/call-intent/icons/promotion-icon.svg +8 -0
- package/call-intent/index.html +114 -0
- package/call-intent/index.js +110 -0
- package/constants.js +31 -0
- package/database.js +9 -0
- package/examples/home-assistant-automation.json +167 -0
- package/examples/llm-chat-node-example.json +208 -0
- package/examples/openai-call-registered-intent-example.json +174 -0
- package/examples/openai-system-node-example.json +178 -0
- package/examples/openai-tool-node-example.json +120 -0
- package/examples/openai-user-node-exampe.json +234 -0
- package/geminiai-chat/geminiai-configuration/index.html +18 -0
- package/geminiai-chat/geminiai-configuration/index.js +7 -0
- package/geminiai-chat/icons/diamond.svg +8 -0
- package/geminiai-chat/icons/gemini-icon.svg +1 -0
- package/geminiai-chat/icons/gemini.svg +8 -0
- package/geminiai-chat/index.html +189 -0
- package/geminiai-chat/index.js +92 -0
- package/globalUtils.js +39 -0
- package/images/call_register_intent.jpeg +0 -0
- package/images/finally.jpg +0 -0
- package/images/set-config-node.gif +0 -0
- package/llm-chat/AzureOpenAIHelper.js +204 -0
- package/llm-chat/ChatGPTHelper.js +197 -0
- package/llm-chat/GeminiHelper.js +260 -0
- package/llm-chat/OllamaHelper.js +196 -0
- package/llm-chat/icons/bot-message-square.svg +1 -0
- package/llm-chat/icons/brain-circuit.svg +1 -0
- package/llm-chat/icons/chatgpt-icon.svg +7 -0
- package/llm-chat/index.html +205 -0
- package/llm-chat/index.js +73 -0
- package/llm-chat/platform-configuration/index.html +136 -0
- package/llm-chat/platform-configuration/index.js +16 -0
- package/localai-chat/icons/gem-icon.svg +1 -0
- package/localai-chat/icons/llama.svg +8 -0
- package/localai-chat/index.html +244 -0
- package/localai-chat/index.js +108 -0
- package/localai-chat/localai-configuration/index.html +18 -0
- package/localai-chat/localai-configuration/index.js +7 -0
- package/openai-chat/icons/chatgpt-icon.svg +7 -0
- package/openai-chat/index.html +196 -0
- package/openai-chat/index.js +58 -0
- package/openai-chat/openai-configuration/index.html +18 -0
- package/openai-chat/openai-configuration/index.js +7 -0
- package/openai-response/index.html +66 -0
- package/openai-response/index.js +154 -0
- package/openai-system/index.html +68 -0
- package/openai-system/index.js +28 -0
- package/openai-tool/index.html +57 -0
- package/openai-tool/index.js +50 -0
- package/openai-user/index.html +76 -0
- package/openai-user/index.js +26 -0
- package/package.json +49 -0
- package/register-intent/icons/register-icon.svg +8 -0
- package/register-intent/index.html +195 -0
- package/register-intent/index.js +72 -0
- package/register-intent/utils.js +10 -0
- package/utilities/chat-controller.js +249 -0
- package/utilities/chat-ledger.js +122 -0
- package/utilities/conversationHistory.js +68 -0
- package/utilities/format.js +94 -0
- package/utilities/gemini-controller.js +243 -0
- package/utilities/global-context.js +30 -0
- package/utilities/validateSchema.js +74 -0
package/.eslintrc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
{
|
2
|
+
"root": true,
|
3
|
+
"globals": {
|
4
|
+
"monaco": true,
|
5
|
+
"RED": true
|
6
|
+
},
|
7
|
+
"env": {
|
8
|
+
"browser": true,
|
9
|
+
"commonjs": true,
|
10
|
+
"es2021": true,
|
11
|
+
"mocha": true,
|
12
|
+
"jquery": true
|
13
|
+
},
|
14
|
+
"extends": [
|
15
|
+
"eslint:recommended",
|
16
|
+
"standard"
|
17
|
+
],
|
18
|
+
"plugins": [
|
19
|
+
"eslint-plugin-html",
|
20
|
+
"no-only-tests"
|
21
|
+
],
|
22
|
+
"parserOptions": {
|
23
|
+
"ecmaVersion": 12
|
24
|
+
},
|
25
|
+
"rules": {
|
26
|
+
// Built-in rules
|
27
|
+
"indent": [
|
28
|
+
"error",
|
29
|
+
2
|
30
|
+
],
|
31
|
+
"object-shorthand": [
|
32
|
+
"error"
|
33
|
+
],
|
34
|
+
"no-console": [
|
35
|
+
"error",
|
36
|
+
{
|
37
|
+
"allow": [
|
38
|
+
"debug",
|
39
|
+
"info",
|
40
|
+
"warn",
|
41
|
+
"error"
|
42
|
+
]
|
43
|
+
}
|
44
|
+
],
|
45
|
+
// plugin:no-only-tests
|
46
|
+
"no-only-tests/no-only-tests": "error",
|
47
|
+
"n/no-callback-literal": 0
|
48
|
+
}
|
49
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="ProjectModuleManager">
|
4
|
+
<modules>
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/node-red-contrib-ai-intent.iml" filepath="$PROJECT_DIR$/.idea/node-red-contrib-ai-intent.iml" />
|
6
|
+
</modules>
|
7
|
+
</component>
|
8
|
+
</project>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module type="WEB_MODULE" version="4">
|
3
|
+
<component name="NewModuleRootManager">
|
4
|
+
<content url="file://$MODULE_DIR$">
|
5
|
+
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
6
|
+
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
7
|
+
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
8
|
+
</content>
|
9
|
+
<orderEntry type="inheritedJdk" />
|
10
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
11
|
+
</component>
|
12
|
+
</module>
|
package/.idea/vcs.xml
ADDED
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Michael Montaque
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
# AI Intent Version 3
|
2
|
+
|
3
|
+
This is a collection of nodes to help enhance existing automations to be utilized by chatbots and take advantage of LLM's like GPT. There are 4 Nodes in this collection:
|
4
|
+
|
5
|
+
> Note: This uses GPT's Chat Completion API which is now considered Legacy. If it becomes deprecated. I will do my best to update this to leverage something equivalent.
|
6
|
+
### Breaking Changes
|
7
|
+
|
8
|
+
#### v1 - v2
|
9
|
+
There were changes under the hood how Intents are saved. This may cause some of your **Register Intent** and **Call Intent**
|
10
|
+
nodes to become invalid. Usually opening and closing the configuration of the nodes and redeploying should work.
|
11
|
+
If this doesn't work, you may need to redo the nodes from the palette.
|
12
|
+
|
13
|
+
#### v2 - v3
|
14
|
+
A lot of the nodes are being deprecated. The **User node**, **System Node**, **Response Node**, **Tool Node**, **Response Node**
|
15
|
+
**ChatGPT Node**, **Gemini Node**, **LocalAI Node**. Instead, use the **LLM Chat node**. This node replaces all of those nodes.
|
16
|
+
You can now write Function tools in the Register intent node which gives greater versatility.
|
17
|
+
> **PLEASE MIGRATE YOUR AUTOMATIONS TO USE THE LLM CHAT NODE.**
|
18
|
+
---
|
19
|
+
|
20
|
+
## LLM Chat Node
|
21
|
+
|
22
|
+
The **LLM Chat** node enables users to interact with a Large Language Model (LLM) and receive responses. It supports system and user messages, conversation tracking, and additional configuration options.
|
23
|
+
> Watch tutorial: https://youtu.be/2Efb1X6F5UY
|
24
|
+
---
|
25
|
+
|
26
|
+
## **Important**
|
27
|
+
To use this node, you need to configure the connection details. Consult the documentation for your chosen LLM platform (e.g., OpenAI, Ollama, Gemini) for specific instructions.
|
28
|
+
|
29
|
+
---
|
30
|
+
|
31
|
+
## **Inputs**
|
32
|
+
|
33
|
+
### **1. Main Input (`msg.payload`)**
|
34
|
+
The primary input for the LLM. It must include a `user` message and may optionally contain a `system` message.
|
35
|
+
|
36
|
+
#### **Example:**
|
37
|
+
```javascript
|
38
|
+
msg.payload = {
|
39
|
+
system: "You are a helpful assistant.",
|
40
|
+
user: "What is the capital of Alaska?"
|
41
|
+
};
|
42
|
+
```
|
43
|
+
- **`system`** (optional): Provides instructions to guide the model’s behavior.
|
44
|
+
- **`user`** (required): Contains the user's query or command.
|
45
|
+
|
46
|
+
---
|
47
|
+
|
48
|
+
### **2. Additional Options (`msg.payload.options`)**
|
49
|
+
Allows users to send extra parameters to customize LLM responses.
|
50
|
+
|
51
|
+
#### **Example:**
|
52
|
+
```javascript
|
53
|
+
msg.payload = {
|
54
|
+
user: "What is the capital of Alaska? Respond using JSON.",
|
55
|
+
options: {
|
56
|
+
format: "json" // Example for Ollama
|
57
|
+
}
|
58
|
+
};
|
59
|
+
```
|
60
|
+
|
61
|
+
The available options depend on the LLM platform and may include parameters such as:
|
62
|
+
- **`temperature`**: Controls randomness.
|
63
|
+
- **`max_tokens`**: Limits response length.
|
64
|
+
- **`format`**: Defines response structure (e.g., JSON).
|
65
|
+
|
66
|
+
Refer to the LLM's documentation for more details.
|
67
|
+
|
68
|
+
---
|
69
|
+
|
70
|
+
### **3. Clear Chat History (`msg.clearChatHistory`)**
|
71
|
+
- **Type**: `boolean` (optional)
|
72
|
+
- **Behavior**: If set to `true`, the conversation history will be cleared for the specified **Conversation Id**.
|
73
|
+
|
74
|
+
#### **Example:**
|
75
|
+
```javascript
|
76
|
+
msg.clearChatHistory = true;
|
77
|
+
```
|
78
|
+
|
79
|
+
---
|
80
|
+
|
81
|
+
## **Node Configuration**
|
82
|
+
|
83
|
+
### **1. Conversation Id**
|
84
|
+
- **Type**: `string`
|
85
|
+
- **Description**: This value is used as a key to store and track conversation history. If multiple nodes share the same Conversation Id, they will share the same conversation context.
|
86
|
+
- **If omitted**: Each call to the LLM will be independent, without historical context.
|
87
|
+
|
88
|
+
---
|
89
|
+
|
90
|
+
### **2. Tool Choice**
|
91
|
+
Defines whether the LLM should use function calling.
|
92
|
+
|
93
|
+
#### **Options:**
|
94
|
+
- **`None`**: Disables tools; the LLM won’t use functions.
|
95
|
+
- **`Automatic`**: Allows the LLM to decide when to use selected tools.
|
96
|
+
- **`Required`**: Forces the LLM to use at least one selected tool.
|
97
|
+
|
98
|
+
> **Note:** For Ollama, not all models support function calling.
|
99
|
+
|
100
|
+
---
|
101
|
+
|
102
|
+
### **3. Tools**
|
103
|
+
- **Type**: `string`
|
104
|
+
- **Description**: This field is populated by **Register Intent** nodes. It allows selecting one or more functions that the LLM can use.
|
105
|
+
- **Behavior**: The LLM will consider calling the selected functions based on the **Tool Choice** setting.
|
106
|
+
|
107
|
+
---
|
108
|
+
|
109
|
+
## **System and User Messages**
|
110
|
+
Messages must be passed in the `msg.payload` object:
|
111
|
+
```javascript
|
112
|
+
msg.payload = {
|
113
|
+
system: "You are a helpful assistant.",
|
114
|
+
user: "What is the capital of Alaska?"
|
115
|
+
};
|
116
|
+
```
|
117
|
+
|
118
|
+
The **system message** is optional but helps set the LLM’s behavior. The **user message** is required for every interaction.
|
119
|
+
|
120
|
+
To include additional response options:
|
121
|
+
```javascript
|
122
|
+
msg.payload = {
|
123
|
+
user: "What is the capital of Alaska? Respond using JSON.",
|
124
|
+
options: {
|
125
|
+
format: "json" // Example for Ollama
|
126
|
+
}
|
127
|
+
};
|
128
|
+
```
|
129
|
+
|
130
|
+
---
|
131
|
+
|
132
|
+
## **Summary**
|
133
|
+
- Use `msg.payload` with `user` (required) and `system` (optional) messages.
|
134
|
+
- Customize responses with `msg.payload.options`.
|
135
|
+
- Set `msg.clearChatHistory = true` to reset conversation history.
|
136
|
+
- Configure **Conversation Id** to maintain or separate conversation history.
|
137
|
+
- Choose **Tool Choice** and **Tools** to enable function calling when supported.
|
138
|
+
|
139
|
+
For more details, refer to the documentation of your selected LLM platform.
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
---
|
144
|
+
## Register Intent Node
|
145
|
+
|
146
|
+
### Purpose
|
147
|
+
The **Register Intent Node** allows users to define custom actions (intents) that can be triggered by the **Call Intent Node** or AI assistants like OpenAI.
|
148
|
+
> Watch Tutorial: https://youtu.be/FvP04OToeLQ
|
149
|
+
|
150
|
+
### Configuration
|
151
|
+
- **Name (string):** A unique identifier for the intent (alphanumeric, underscores, hyphens, max 64 characters).
|
152
|
+
- **Description (string):** A clear explanation of the intent’s purpose. If using AI assistants, this helps them understand when to trigger the intent.
|
153
|
+
- **Advanced Mode (boolean):** Enables structured AI interactions using a JSON-based schema.
|
154
|
+
- **Tool Schema (JSON, required if Advanced Mode is enabled):** Defines the expected parameters for AI-generated inputs.
|
155
|
+
|
156
|
+
### Usage
|
157
|
+
Place this node at the beginning of a flow to register an intent. Other flows or AI models can then call this intent dynamically. For AI integrations, ensure the description field is precise to improve intent recognition.
|
158
|
+
|
159
|
+
### Example JSON Schema for OpenAI
|
160
|
+
```json
|
161
|
+
{
|
162
|
+
"type": "object",
|
163
|
+
"properties": {
|
164
|
+
"eventName": {
|
165
|
+
"type": "string",
|
166
|
+
"description": "Unique event identifier."
|
167
|
+
},
|
168
|
+
"eventTime": {
|
169
|
+
"type": "string",
|
170
|
+
"format": "date-time",
|
171
|
+
"description": "ISO8601 formatted event time."
|
172
|
+
},
|
173
|
+
"eventPayload": {
|
174
|
+
"type": "string",
|
175
|
+
"description": "Command for smart home actions."
|
176
|
+
}
|
177
|
+
},
|
178
|
+
"required": ["eventName", "eventTime"],
|
179
|
+
"additionalProperties": false
|
180
|
+
}
|
181
|
+
```
|
182
|
+
|
183
|
+
### AI Platform Considerations
|
184
|
+
Different AI platforms may have unique function calling mechanisms. Refer to the official documentation for specifics:
|
185
|
+
- [OpenAI Function Calling](https://platform.openai.com/docs/guides/function-calling)
|
186
|
+
- [Google Gemini API](https://ai.google.dev/gemini-api/docs/function-calling)
|
187
|
+
- [Ollama Tool Support](https://ollama.com/blog/tool-support)
|
188
|
+
|
189
|
+
---
|
190
|
+
## Call Intent Node
|
191
|
+
|
192
|
+
### Purpose
|
193
|
+
The **Call Intent Node** is used to trigger an intent that has been previously registered using the **Register Intent Node**.
|
194
|
+
|
195
|
+
### Configuration
|
196
|
+
- **Name (string):** A label for the node.
|
197
|
+
- **Registered Node Name (string):** Select from a dropdown list of available registered intents or pass the intent dynamically via `msg.payload.nodeName`.
|
198
|
+
|
199
|
+
### Usage
|
200
|
+
This node acts as a trigger for registered intents. It can:
|
201
|
+
- Directly call an intent similar to a link node.
|
202
|
+
- Be placed after an **LLM Chat Node**, allowing AI to determine which intent to execute dynamically.
|
203
|
+
- Trigger multiple intents if the payload contains an array of intent names.
|
204
|
+
|
205
|
+
#### Example Payload for Multiple Calls
|
206
|
+
```json
|
207
|
+
{
|
208
|
+
"payload": [
|
209
|
+
{"nodeName": "turn_on_lights"},
|
210
|
+
{"nodeName": "set_thermostat"}
|
211
|
+
]
|
212
|
+
}
|
213
|
+
```
|
214
|
+
|
215
|
+
---
|
216
|
+
## LLM Chat Node
|
217
|
+
|
218
|
+
### Purpose
|
219
|
+
The **LLM Chat Node** integrates with various AI models to process and respond to natural language input dynamically.
|
220
|
+
|
221
|
+
### Usage
|
222
|
+
Place this node in a flow where dynamic AI-driven responses are required. It can:
|
223
|
+
- Answer user queries.
|
224
|
+
- Process input and pass structured responses to downstream nodes.
|
225
|
+
- Work in conjunction with **Register Intent** and **Call Intent Nodes** to enable complex AI-driven automations.
|
226
|
+
|
227
|
+
### Example Use Case
|
228
|
+
A user asks: *"What’s the weather today?"*
|
229
|
+
1. **LLM Chat Node** processes the request.
|
230
|
+
2. If the AI determines a weather function is needed, it triggers a **Call Intent Node**.
|
231
|
+
3. The **Call Intent Node** invokes a flow that retrieves weather data and returns a response.
|
232
|
+
|
233
|
+
---
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
3
|
+
<svg width="100%" height="100%" viewBox="0 0 513 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
4
|
+
<use xlink:href="#_Image1" x="70.593" y="80.545" width="370.865px" height="350.938px" transform="matrix(0.999637,0,0,0.999824,0,0)"/>
|
5
|
+
<defs>
|
6
|
+
<image id="_Image1" width="371px" height="351px" xlink:href="data:image/png;base64,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"/>
|
7
|
+
</defs>
|
8
|
+
</svg>
|
@@ -0,0 +1,114 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
const DYNAMIC_OPTION = {value:"", label:"Use payload.nodeName"}
|
3
|
+
|
4
|
+
|
5
|
+
const getIntentOptions = (intents = []) => {
|
6
|
+
const options = intents
|
7
|
+
.filter(option => {
|
8
|
+
return option.type === "Register Intent" && !option.excludeFromOpenAi
|
9
|
+
}).map(intent => {
|
10
|
+
return { value: intent.id, label: intent.name}
|
11
|
+
})
|
12
|
+
|
13
|
+
options.push(DYNAMIC_OPTION)
|
14
|
+
|
15
|
+
return options
|
16
|
+
}
|
17
|
+
$.getJSON('registered-intents', function (data = RED.settings.callIntentRegistry) {
|
18
|
+
window.__intentOptions = getIntentOptions(data)
|
19
|
+
|
20
|
+
initialize()
|
21
|
+
});
|
22
|
+
|
23
|
+
|
24
|
+
const initialize = () => {
|
25
|
+
RED.nodes.registerType("Call Intent", {
|
26
|
+
category: 'AI Intent',
|
27
|
+
color: '#1abc9c',
|
28
|
+
icon: "promotion-icon.svg",
|
29
|
+
defaults: {
|
30
|
+
name: { value: "" },
|
31
|
+
registeredNodeId: {
|
32
|
+
value: getIntentOptions(RED.settings.callIntentRegistry),
|
33
|
+
validate: (value) => {
|
34
|
+
return window.__intentOptions.some(option => {
|
35
|
+
return option.value === value
|
36
|
+
})
|
37
|
+
}
|
38
|
+
},
|
39
|
+
},
|
40
|
+
inputs: 1,
|
41
|
+
outputs: 0,
|
42
|
+
|
43
|
+
label: function () {
|
44
|
+
return this.name || "Call Intent";
|
45
|
+
},
|
46
|
+
|
47
|
+
oneditprepare: function () {
|
48
|
+
$("#node-input-message").typedInput({
|
49
|
+
type: "str",
|
50
|
+
types: ["str", "num", "bool", "json"],
|
51
|
+
typeField: "#node-input-message-type"
|
52
|
+
})
|
53
|
+
|
54
|
+
$.getJSON('registered-intents', function (data = RED.settings.callIntentRegistry) {
|
55
|
+
window.__intentOptions = getIntentOptions(data)
|
56
|
+
|
57
|
+
$("#node-input-registeredNodeId").typedInput({
|
58
|
+
types: [
|
59
|
+
{
|
60
|
+
value: "",
|
61
|
+
options: window.__intentOptions
|
62
|
+
}
|
63
|
+
]
|
64
|
+
})
|
65
|
+
});
|
66
|
+
|
67
|
+
|
68
|
+
}
|
69
|
+
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
</script>
|
76
|
+
|
77
|
+
<script type="text/html" data-template-name="Call Intent">
|
78
|
+
<div style="display: flex; justify-content: center; margin-bottom: 25px;">
|
79
|
+
<a href="https://youtu.be/FvP04OToeLQ" target="_blank" referrerpolicy="no-referrer" style="color: #f53b57"><i
|
80
|
+
class="fa fa-youtube"></i><span style="padding-left: 10px;">Watch
|
81
|
+
Call Intent Node Tutorial</span></a>
|
82
|
+
</div>
|
83
|
+
<div class="form-row">
|
84
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
85
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
86
|
+
</div>
|
87
|
+
|
88
|
+
<div class="form-row">
|
89
|
+
<label for="node-input-registeredNodeId"><i class="fa fa-tag"></i> Registered Node Name</label>
|
90
|
+
<input type="text" id="node-input-registeredNodeId" placeholder="Name of registered node">
|
91
|
+
</div>
|
92
|
+
|
93
|
+
</script>
|
94
|
+
|
95
|
+
<script type="text/html" data-help-name="Call Intent">
|
96
|
+
<p>Triggers the Register Intent node that has the corresponding id.</p>
|
97
|
+
|
98
|
+
<h3>Inputs</h3>
|
99
|
+
<dl class="message-properties">
|
100
|
+
<dt>Registered Node Name
|
101
|
+
<span class="property-type">string</span>
|
102
|
+
</dt>
|
103
|
+
<dd>Name of the registed intent node you want to trigger. For convenience you can choose the name from the
|
104
|
+
the dropdown however, you can choose payload.nodeName to allow the node to read the
|
105
|
+
intent name from the msg object - <code>msg.payload.nodeName</code>. This function can also fire multiple times
|
106
|
+
if the payload is an array of the objects with nodeName <code>msg.payload = [{nodeName: "fake_name"}]</code>.
|
107
|
+
</dd>
|
108
|
+
</dl>
|
109
|
+
|
110
|
+
<h3>Details</h3>
|
111
|
+
<p>This node can be used as a standalone way to trigger custom intents (this will act very similar to the link in/out nodes)
|
112
|
+
or this node can be used immediately after the <code>OpenAI Response</code> node. If used after the aforementioned node
|
113
|
+
OpenAI can dynamically trigger any registered intents.</p>
|
114
|
+
</script>
|
@@ -0,0 +1,110 @@
|
|
1
|
+
const PubSub = require("pubsub-js");
|
2
|
+
const { end, ContextDatabase } = require("../globalUtils");
|
3
|
+
const { TYPES} = require("../constants");
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Searches context for an object whose `name` or `id` property matches the given name parameter
|
7
|
+
* and returns the matching object.
|
8
|
+
* @param {string} name
|
9
|
+
* @param {Record<string, object>} context
|
10
|
+
* @returns
|
11
|
+
*/
|
12
|
+
const getMatchingIntentFromContext = (nameOrID = "", context) => {
|
13
|
+
return Object.values(context).find((intent) => {
|
14
|
+
return (
|
15
|
+
(intent.name === nameOrID || intent.id === nameOrID) &&
|
16
|
+
intent.type === TYPES.RegisterIntent
|
17
|
+
);
|
18
|
+
});
|
19
|
+
};
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Fires the callback with either an error string or with the node matching the `nameOrId` value
|
23
|
+
* from the context.
|
24
|
+
* @param {string} nameOrId
|
25
|
+
* @param {object} context
|
26
|
+
* @param {function} callback
|
27
|
+
*/
|
28
|
+
const getNode = (nameOrId, context, callback) => {
|
29
|
+
const node = getMatchingIntentFromContext(nameOrId, context);
|
30
|
+
if (!nameOrId) {
|
31
|
+
callback("payload is missing nodeName property");
|
32
|
+
} else if (!node) {
|
33
|
+
callback(`There is no registered intent with name or id of "${nameOrId}"`);
|
34
|
+
} else {
|
35
|
+
callback(null, node);
|
36
|
+
}
|
37
|
+
};
|
38
|
+
|
39
|
+
const normalizeNames = (intents = []) => {
|
40
|
+
return intents.map((intent) => {
|
41
|
+
if (!intent.name && intent.type === TYPES.OpenAITool) {
|
42
|
+
const tool = JSON.parse(intent.tool);
|
43
|
+
return { ...intent, name: tool.function.name };
|
44
|
+
}
|
45
|
+
return intent;
|
46
|
+
});
|
47
|
+
};
|
48
|
+
|
49
|
+
module.exports = function (RED) {
|
50
|
+
const nodeDB = new ContextDatabase(RED);
|
51
|
+
|
52
|
+
function CallIntentHandlerNode(config) {
|
53
|
+
RED.nodes.createNode(this, config);
|
54
|
+
const node = this;
|
55
|
+
|
56
|
+
this.on("input", function (msg, send, done = () => {}) {
|
57
|
+
const nodeStore = nodeDB.getNodeStore();
|
58
|
+
const { registeredNodeId = "" } = config;
|
59
|
+
|
60
|
+
send =
|
61
|
+
send ||
|
62
|
+
function () {
|
63
|
+
node.send.apply(node, arguments);
|
64
|
+
};
|
65
|
+
|
66
|
+
if (Array.isArray(msg.payload)) {
|
67
|
+
msg.payload.forEach((payload, index) => {
|
68
|
+
const { nodeName } = payload;
|
69
|
+
const body = {...msg, index}
|
70
|
+
getNode(nodeName, nodeStore, (err, registeredNode) => {
|
71
|
+
if (err) {
|
72
|
+
node.warn(err);
|
73
|
+
} else {
|
74
|
+
PubSub.publishSync(registeredNode.id, body);
|
75
|
+
send(body);
|
76
|
+
}
|
77
|
+
});
|
78
|
+
});
|
79
|
+
}
|
80
|
+
else {
|
81
|
+
const nameOrId = msg.payload?.nodeName || registeredNodeId;
|
82
|
+
|
83
|
+
getNode(nameOrId, nodeStore, (err, registeredNode) => {
|
84
|
+
if (err) {
|
85
|
+
node.warn(err);
|
86
|
+
} else {
|
87
|
+
PubSub.publishSync(registeredNode.id, msg);
|
88
|
+
send(msg);
|
89
|
+
}
|
90
|
+
});
|
91
|
+
}
|
92
|
+
end(done);
|
93
|
+
});
|
94
|
+
}
|
95
|
+
|
96
|
+
RED.httpAdmin.get("/registered-intents", function (req, res) {
|
97
|
+
const nodeStore = nodeDB.getNodeStore();
|
98
|
+
const intents = normalizeNames(Object.values(nodeStore))
|
99
|
+
res.json(intents);
|
100
|
+
});
|
101
|
+
|
102
|
+
RED.nodes.registerType(TYPES.CallIntent, CallIntentHandlerNode, {
|
103
|
+
settings: {
|
104
|
+
callIntentRegistry: {
|
105
|
+
value: [],
|
106
|
+
exportable: true,
|
107
|
+
},
|
108
|
+
},
|
109
|
+
});
|
110
|
+
};
|
package/constants.js
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module.exports = {
|
2
|
+
OPEN_AI_KEY: "openaiAPIKey",
|
3
|
+
GEMINI_AI_KEY: "geminiaiAPIKey",
|
4
|
+
LOCAL_STORAGE_PATH: "localStoragePath",
|
5
|
+
INTENT_STORE: "intent_store",
|
6
|
+
ACTIVE_CONVERSATION: "active_conversation_context",
|
7
|
+
CONVERSATION_CONTEXT: "conversation_context",
|
8
|
+
TYPES: {
|
9
|
+
RegisterIntent: "Register Intent",
|
10
|
+
CallIntent: "Call Intent",
|
11
|
+
OpenAIChat: "OpenAI Chat",
|
12
|
+
LLMChat: "LLM Chat",
|
13
|
+
LocalAIChat: "LocalAI Chat",
|
14
|
+
GeminiaiChat: "GeminiAI Chat",
|
15
|
+
OpenAITool: "OpenAI Tool",
|
16
|
+
OpenAIUser: "OpenAI User",
|
17
|
+
OpenAISystem: "OpenAI System",
|
18
|
+
OpenAIResponse: "OpenAI Response",
|
19
|
+
},
|
20
|
+
ROLES: {
|
21
|
+
User: "user",
|
22
|
+
System: "system",
|
23
|
+
Assistant: "assistant",
|
24
|
+
},
|
25
|
+
TOOL_CHOICE: {
|
26
|
+
Any: "any",
|
27
|
+
None: "none",
|
28
|
+
Auto: "auto",
|
29
|
+
Required: "required"
|
30
|
+
},
|
31
|
+
};
|
package/database.js
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
if (typeof localStorage === "undefined" || localStorage === null) {
|
2
|
+
var LocalStorage = require("node-localstorage").LocalStorage;
|
3
|
+
}
|
4
|
+
|
5
|
+
const getStorageAtLocation = (location = "./localstore") => {
|
6
|
+
return new LocalStorage(location);
|
7
|
+
}
|
8
|
+
|
9
|
+
module.exports = { getStorageAtLocation };
|