@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
@@ -0,0 +1,189 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
(() => {
|
3
|
+
const DYNAMIC_OPTIONS = [
|
4
|
+
{ value: "auto", label: "Automatic" },
|
5
|
+
{ value: "any", label: "Any" },
|
6
|
+
{ value: "none", label: "None" }
|
7
|
+
]
|
8
|
+
const removeDuplicates = (data) => {
|
9
|
+
const intents = {}
|
10
|
+
const tools = []
|
11
|
+
|
12
|
+
data.forEach((intent => {
|
13
|
+
if (intent.type === "OpenAI Tool") {
|
14
|
+
if (!intents[intent.name]) {
|
15
|
+
intents[intent.name] = true
|
16
|
+
tools.push(intent)
|
17
|
+
}
|
18
|
+
} else {
|
19
|
+
tools.push(intent)
|
20
|
+
}
|
21
|
+
}))
|
22
|
+
return tools
|
23
|
+
}
|
24
|
+
|
25
|
+
$.getJSON('registered-intents', function (data = RED.settings.callIntentRegistry) {
|
26
|
+
const tools = removeDuplicates(data)
|
27
|
+
window.__tools = getToolOptions(tools)
|
28
|
+
initialize()
|
29
|
+
});
|
30
|
+
|
31
|
+
const getToolOptions = (intents = []) => {
|
32
|
+
const options = intents.map(intent => {
|
33
|
+
const suffix = intent.type === "Register Intent" ? " (Registered Intent)" : ""
|
34
|
+
return { value: intent.id, label: `${intent.name}${suffix}` }
|
35
|
+
})
|
36
|
+
|
37
|
+
return [...DYNAMIC_OPTIONS, ...options]
|
38
|
+
}
|
39
|
+
|
40
|
+
RED.nodes.registerType("GeminiAI Chat", {
|
41
|
+
category: 'AI Intent',
|
42
|
+
color: 'rgba(255, 0, 119, .75)',
|
43
|
+
icon: "gemini-icon.svg",
|
44
|
+
defaults: {
|
45
|
+
name: { value: "" },
|
46
|
+
tool_choice: {
|
47
|
+
value: getToolOptions(RED.settings.callIntentRegistry),
|
48
|
+
},
|
49
|
+
conversation_id: { value: "" },
|
50
|
+
token: { value: "", type: "geminiai-configuration", required: false },
|
51
|
+
model: { value: "gemini-1.5-flash", required: true },
|
52
|
+
temperature: { value: .7, required: true },
|
53
|
+
max_tokens: { value: 1200, required: true },
|
54
|
+
top_p: { value: 1, required: true },
|
55
|
+
top_k: { value: 16, required: true },
|
56
|
+
},
|
57
|
+
inputs: 1,
|
58
|
+
outputs: 1,
|
59
|
+
paletteLabel: "Gemini (Deprecated)",
|
60
|
+
label: function () {
|
61
|
+
return this.name +" (Deprecated)" || "GeminiAI (Deprecated)";
|
62
|
+
},
|
63
|
+
|
64
|
+
oneditprepare: function (x) {
|
65
|
+
$("#node-input-temperature").typedInput({
|
66
|
+
type: "num",
|
67
|
+
})
|
68
|
+
$("#node-input-max_tokens").typedInput({
|
69
|
+
type: "num",
|
70
|
+
})
|
71
|
+
$("#node-input-top_p").typedInput({
|
72
|
+
type: "num",
|
73
|
+
})
|
74
|
+
$("#node-input-top_k").typedInput({
|
75
|
+
type: "num",
|
76
|
+
})
|
77
|
+
|
78
|
+
$.getJSON('registered-intents', function (data = RED.settings.callIntentRegistry) {
|
79
|
+
const tools = removeDuplicates(data)
|
80
|
+
window.__tools = getToolOptions(tools)
|
81
|
+
|
82
|
+
$("#node-input-tool_choice").typedInput({
|
83
|
+
types: [
|
84
|
+
{
|
85
|
+
value: "",
|
86
|
+
options: window.__tools
|
87
|
+
}
|
88
|
+
]
|
89
|
+
})
|
90
|
+
});
|
91
|
+
}
|
92
|
+
});
|
93
|
+
})()
|
94
|
+
|
95
|
+
</script>
|
96
|
+
|
97
|
+
<script type="text/html" data-template-name="GeminiAI Chat">
|
98
|
+
<div class="form-row">
|
99
|
+
<label for="node-input-name"> Name</label>
|
100
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
101
|
+
</div>
|
102
|
+
<div class="form-row">
|
103
|
+
<label for="node-input-token"> Token</label>
|
104
|
+
<input type="text" id="node-input-token" placeholder="0a1b2c3b4d5e6f">
|
105
|
+
</div>
|
106
|
+
<div class="form-row">
|
107
|
+
<label for="node-input-conversation_id"><i class="fa fa-tag"></i> Conversation Id</label>
|
108
|
+
<input type="text" id="node-input-conversation_id" placeholder="any arbitrary name">
|
109
|
+
</div>
|
110
|
+
<div class="form-row">
|
111
|
+
<label for="node-input-model"> Model</label>
|
112
|
+
<input type="text" id="node-input-model" placeholder="gpt-3.5-turbo">
|
113
|
+
</div>
|
114
|
+
<div class="form-row">
|
115
|
+
<label for="node-input-tool_choice"> Tool Choice</label>
|
116
|
+
<input type="text" id="node-input-tool_choice" placeholder="Automatic">
|
117
|
+
</div>
|
118
|
+
<div class="form-row">
|
119
|
+
<label for="node-input-temperature"> Temperature</label>
|
120
|
+
<input type="number" id="node-input-temperature" placeholder="0.7">
|
121
|
+
</div>
|
122
|
+
<div class="form-row">
|
123
|
+
<label for="node-input-max_tokens"> Max Tokens</label>
|
124
|
+
<input type="number" id="node-input-max_tokens" placeholder="1200">
|
125
|
+
</div>
|
126
|
+
<div class="form-row">
|
127
|
+
<label for="node-input-top_p"> Top P</label>
|
128
|
+
<input type="number" id="node-input-top_p" placeholder="0">
|
129
|
+
</div>
|
130
|
+
<div class="form-row">
|
131
|
+
<label for="node-input-top_k">Top K</label>
|
132
|
+
<input type="number" id="node-input-top_k" placeholder="16">
|
133
|
+
</div>
|
134
|
+
|
135
|
+
|
136
|
+
</script>
|
137
|
+
|
138
|
+
<script type="text/html" data-help-name="GeminiAI Chat">
|
139
|
+
<p>Calls Gemini and returns the response</p>
|
140
|
+
|
141
|
+
<h3>Important</h3>
|
142
|
+
<p>To use this node you need an API Key from Gemini. Add the API Key to the settings.js file in the node-red folder under
|
143
|
+
the functonGlobalContext section using the key "geminiaiAPIKey"</p>
|
144
|
+
<pre>
|
145
|
+
functionGlobalContext: {
|
146
|
+
geminiaiAPIKey: "Your Key Goes Here",
|
147
|
+
}
|
148
|
+
</pre>
|
149
|
+
|
150
|
+
Alternatively, you can add the token via the token configuration dropdown.
|
151
|
+
See the <a href="https://github.com/montaque22/node-red-contrib-ai-intent" target="_blank">Read Me</a> for more information
|
152
|
+
|
153
|
+
<h3>Inputs</h3>
|
154
|
+
<dl class="message-properties">
|
155
|
+
<dt class="optional">Conversation ID
|
156
|
+
<span class="property-type">string</span>
|
157
|
+
</dt>
|
158
|
+
<dd> By including this identifier, AI-Intent will save the conversation in the global context
|
159
|
+
and will pass the entire conversation to the LLM when any Chat node with the same conversation id
|
160
|
+
is triggered. This mean that you can have many Chat nodes in different flows with the same conversation id
|
161
|
+
and AI-Intent will ensure that conversation context is maintained between them. It should be noted that only
|
162
|
+
<b>one</b> System message will be maintained. As a result the flow with the current System node will
|
163
|
+
overwrite any system prompt that was previously saved.</dd>
|
164
|
+
</dl>
|
165
|
+
<dl class="message-properties">
|
166
|
+
<dt class="required">model
|
167
|
+
<span class="property-type">string</span>
|
168
|
+
</dt>
|
169
|
+
<dd> Required field that dictates the model to use when calling Gemini </dd>
|
170
|
+
</dl>
|
171
|
+
|
172
|
+
<h3>Details</h3>
|
173
|
+
<p>At minimum the User node should be used before this node. The OpenAI User node adds necessary
|
174
|
+
information to the msg object to allow the chat to work.</p>
|
175
|
+
|
176
|
+
<dl class="message-properties">
|
177
|
+
<dt class="required">Tool Choice
|
178
|
+
<span class="property-type">Dropdown</span>
|
179
|
+
</dt>
|
180
|
+
<dd> This setting controls how GPT uses the provided functions to respond.
|
181
|
+
If you want to force GPT to use a specific function, selected it from the dropdown.
|
182
|
+
<b>Automatic</b> lets GPT decides what is best (This is the default). <b>None</b> disables the use of functions.
|
183
|
+
to further optimize the API call, AI-Intent will not pass any of the tools
|
184
|
+
to save tokens if "None" is selected. Check documentation for more detail on
|
185
|
+
how tools work: <a href="https://ai.google.dev/gemini-api/docs/function-calling" target="_blank">Function Calling & Tools</a>
|
186
|
+
</dd>
|
187
|
+
</dl>
|
188
|
+
|
189
|
+
</script>
|
@@ -0,0 +1,92 @@
|
|
1
|
+
const { GEMINI_AI_KEY, TYPES } = require("../constants");
|
2
|
+
const { end } = require("../globalUtils");
|
3
|
+
const { GoogleGenerativeAI } = require("@google/generative-ai");
|
4
|
+
const { GlobalContext } = require("../utilities/global-context");
|
5
|
+
const { GeminiController } = require("../utilities/gemini-controller");
|
6
|
+
|
7
|
+
module.exports = function (RED) {
|
8
|
+
function GeminiAIChatHandlerNode(config) {
|
9
|
+
RED.nodes.createNode(this, config);
|
10
|
+
// Retrieve the config node with API token data.
|
11
|
+
this.token = RED.nodes.getNode(config.token);
|
12
|
+
const node = this;
|
13
|
+
|
14
|
+
this.on("input", function (msg, send, done = () => {}) {
|
15
|
+
const controller = new GeminiController(node, config, msg, RED);
|
16
|
+
const nodeDB = new GlobalContext(node);
|
17
|
+
const apiKey =
|
18
|
+
node.token?.api || nodeDB.getValueFromGlobalContext(GEMINI_AI_KEY);
|
19
|
+
|
20
|
+
send =
|
21
|
+
send ||
|
22
|
+
function () {
|
23
|
+
config.send.apply(node, arguments);
|
24
|
+
};
|
25
|
+
|
26
|
+
if (!apiKey) {
|
27
|
+
return end(
|
28
|
+
done,
|
29
|
+
`Api key missing for Gemini. Please add ${GEMINI_AI_KEY} key-value pair to the functionGlobalContext.
|
30
|
+
Or add it to the config within the GeminiAI-Chat node`
|
31
|
+
);
|
32
|
+
}
|
33
|
+
|
34
|
+
const { apiProperties, toolProperties } = controller;
|
35
|
+
|
36
|
+
const generationConfig = {
|
37
|
+
maxOutputTokens: apiProperties.max_tokens,
|
38
|
+
temperature: apiProperties.temperature,
|
39
|
+
topP: apiProperties.top_p,
|
40
|
+
topK: apiProperties.top_k,
|
41
|
+
};
|
42
|
+
|
43
|
+
const finalProps = {
|
44
|
+
...apiProperties,
|
45
|
+
...toolProperties,
|
46
|
+
};
|
47
|
+
|
48
|
+
// Access your API key as an environment variable (see "Set up your API key" above)
|
49
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
50
|
+
|
51
|
+
// ...
|
52
|
+
|
53
|
+
const modelParams = {
|
54
|
+
model: finalProps.model,
|
55
|
+
generationConfig,
|
56
|
+
tools: finalProps.tools,
|
57
|
+
tool_config: {
|
58
|
+
function_calling_config: {
|
59
|
+
mode: finalProps.tool_choice.toUpperCase(),
|
60
|
+
allowed_function_names: finalProps.allowFunctionNames,
|
61
|
+
},
|
62
|
+
},
|
63
|
+
};
|
64
|
+
|
65
|
+
// The Gemini 1.5 models are versatile and work with most use cases
|
66
|
+
const model = genAI.getGenerativeModel(modelParams);
|
67
|
+
|
68
|
+
const chat = model.startChat({
|
69
|
+
history: finalProps.history,
|
70
|
+
});
|
71
|
+
|
72
|
+
chat
|
73
|
+
.sendMessage(finalProps.message)
|
74
|
+
.then((result) => result.response)
|
75
|
+
.then((response) => {
|
76
|
+
return {
|
77
|
+
functions: response.functionCalls(),
|
78
|
+
text: response.text(),
|
79
|
+
};
|
80
|
+
})
|
81
|
+
.then((payload) => {
|
82
|
+
send(controller.mergeResponseWithMessage(payload, finalProps));
|
83
|
+
end(done);
|
84
|
+
})
|
85
|
+
.catch((err) => {
|
86
|
+
end(done, err);
|
87
|
+
});
|
88
|
+
});
|
89
|
+
}
|
90
|
+
|
91
|
+
RED.nodes.registerType(TYPES.GeminiaiChat, GeminiAIChatHandlerNode);
|
92
|
+
};
|
package/globalUtils.js
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
const { INTENT_STORE, LOCAL_STORAGE_PATH} = require("./constants");
|
2
|
+
const { getStorageAtLocation } = require("./database");
|
3
|
+
|
4
|
+
function end(done, error) {
|
5
|
+
if (done) {
|
6
|
+
done(error);
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
class ContextDatabase {
|
11
|
+
constructor(RED) {
|
12
|
+
const {functionGlobalContext = {} } = RED.settings
|
13
|
+
const path = functionGlobalContext[LOCAL_STORAGE_PATH]
|
14
|
+
|
15
|
+
this.globalContext = getStorageAtLocation(path);
|
16
|
+
}
|
17
|
+
|
18
|
+
getNodeStore() {
|
19
|
+
const stringStore = this.globalContext.getItem(INTENT_STORE) || "{}";
|
20
|
+
|
21
|
+
return JSON.parse(stringStore);
|
22
|
+
}
|
23
|
+
|
24
|
+
saveIntent(config) {
|
25
|
+
const nodeStore = this.getNodeStore();
|
26
|
+
nodeStore[config.id] = config;
|
27
|
+
|
28
|
+
this.globalContext.setItem(INTENT_STORE, JSON.stringify(nodeStore));
|
29
|
+
}
|
30
|
+
|
31
|
+
removeIntent(config) {
|
32
|
+
const nodeStore = this.getNodeStore();
|
33
|
+
delete nodeStore[config.id];
|
34
|
+
|
35
|
+
this.globalContext.setItem(INTENT_STORE, JSON.stringify(nodeStore));
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
module.exports = { end, ContextDatabase };
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,204 @@
|
|
1
|
+
const { GlobalContext } = require("../utilities/global-context");
|
2
|
+
const { TOOL_CHOICE } = require("../constants");
|
3
|
+
const AzureOpenAI = require("openai");
|
4
|
+
const { ContextDatabase } = require("../globalUtils");
|
5
|
+
const { ConversationHistory } = require("../utilities/conversationHistory");
|
6
|
+
const { Format } = require("../utilities/format");
|
7
|
+
|
8
|
+
const azureOpenAIHelper = (props, callback) => {
|
9
|
+
const { node, config, msg, RED } = props
|
10
|
+
const nodeDB = new GlobalContext(node);
|
11
|
+
const { model, credentials } = node.platform
|
12
|
+
const apiKey = credentials.api
|
13
|
+
const endpoint = credentials.endpoint
|
14
|
+
const apiVersion = "2025-01-01-preview"
|
15
|
+
|
16
|
+
if (!apiKey) {
|
17
|
+
node.status({ fill: "red", shape: "dot", text: "Error" });
|
18
|
+
return callback("API key missing for AzureOpenAI. Please add openaiAPIKey key-value pair to the functionGlobalContext.");
|
19
|
+
}
|
20
|
+
if (!endpoint) {
|
21
|
+
node.status({ fill: "red", shape: "dot", text: "Error" });
|
22
|
+
return callback("Endpoint missing for AzureOpenAI. Please add openaiAPIKey key-value pair to the functionGlobalContext.");
|
23
|
+
}
|
24
|
+
|
25
|
+
const openai = new AzureOpenAI({ endpoint, apiKey, apiVersion });
|
26
|
+
const { options = {}, system = "", user = "" } = msg?.payload || {}
|
27
|
+
const conversation_id = config.conversation_id;
|
28
|
+
const conversationHistory = new ConversationHistory(nodeDB, conversation_id)
|
29
|
+
|
30
|
+
if (msg.clearChatHistory) {
|
31
|
+
conversationHistory.clearHistory()
|
32
|
+
node.warn("Conversation history cleared")
|
33
|
+
}
|
34
|
+
|
35
|
+
if (!user) {
|
36
|
+
node.status({ fill: "red", shape: "dot", text: "Stopped" });
|
37
|
+
return node.warn("payload.user is empty. Stopping the flow ")
|
38
|
+
}
|
39
|
+
|
40
|
+
conversationHistory.addSystemMessage(system)
|
41
|
+
conversationHistory.addUserMessage(user)
|
42
|
+
|
43
|
+
if (conversation_id) {
|
44
|
+
conversationHistory.saveHistory()
|
45
|
+
}
|
46
|
+
|
47
|
+
const toolProperties = getToolProperties(config, msg.tools, RED)
|
48
|
+
const finalProps = {
|
49
|
+
...options,
|
50
|
+
...toolProperties,
|
51
|
+
model,
|
52
|
+
messages: conversationHistory.conversation
|
53
|
+
};
|
54
|
+
|
55
|
+
openai.chat.completions
|
56
|
+
.create(finalProps)
|
57
|
+
.then((response) => {
|
58
|
+
|
59
|
+
response.choices.forEach(choice => {
|
60
|
+
conversationHistory.addAssistantMessage(choice.message.content)
|
61
|
+
})
|
62
|
+
conversationHistory.saveHistory()
|
63
|
+
|
64
|
+
return createPayload(finalProps, response, msg, conversationHistory.conversation)
|
65
|
+
})
|
66
|
+
.then(msg => {
|
67
|
+
callback(null, msg)
|
68
|
+
})
|
69
|
+
.catch((err) => {
|
70
|
+
callback(err)
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
const createPayload = (request, response, previousMsg, conversationHistory) => {
|
75
|
+
const format = new Format()
|
76
|
+
const payload = format.formatPayloadForOpenAI(response.choices)
|
77
|
+
|
78
|
+
return {
|
79
|
+
...previousMsg,
|
80
|
+
payload,
|
81
|
+
apiResponse: response,
|
82
|
+
_debug: {
|
83
|
+
...request,
|
84
|
+
messages: conversationHistory
|
85
|
+
},
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
const getAllTools = (RED) => {
|
90
|
+
const context = new ContextDatabase(RED);
|
91
|
+
const intents = context.getNodeStore() || {};
|
92
|
+
return createFunctionsFromContext(intents)
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Converts the raw intents into functions that the LLM can use.
|
97
|
+
* @param {*} node
|
98
|
+
* @returns
|
99
|
+
*/
|
100
|
+
getRegisteredIntentFunctions = (RED) => {
|
101
|
+
const intents = getRawIntents(RED);
|
102
|
+
return createFunctionsFromContext(intents);
|
103
|
+
};
|
104
|
+
|
105
|
+
/**
|
106
|
+
* This will return all stored Registered Intents throughout the entire system
|
107
|
+
* and Tool Nodes that are attached directly to this flow
|
108
|
+
* This will return:
|
109
|
+
* type RawIntent = {
|
110
|
+
* [node_id]: node // could be Registered Intent or Tool node
|
111
|
+
* }
|
112
|
+
*/
|
113
|
+
getRawIntents = (RED) => {
|
114
|
+
const context = new ContextDatabase(RED);
|
115
|
+
return context.getNodeStore() || {};
|
116
|
+
};
|
117
|
+
|
118
|
+
/**
|
119
|
+
* converts the registered intents stored in the context into functions that can be used by the LLM.
|
120
|
+
* The registered intent will be ignored if excludeFromOpenAi is set to true.
|
121
|
+
* rawIntents may have tool nodes included so the values need to be filtered by the node type.
|
122
|
+
* rawIntents have the following shape:
|
123
|
+
*
|
124
|
+
* type RawIntents = {
|
125
|
+
* [node_id]: node // node could be Registered Intent or Tool node
|
126
|
+
* }
|
127
|
+
*/
|
128
|
+
const createFunctionsFromContext = (rawIntents = {}) => {
|
129
|
+
return (
|
130
|
+
Object.values(rawIntents)
|
131
|
+
.filter((payload) => {
|
132
|
+
return payload.type === "Register Intent";
|
133
|
+
})
|
134
|
+
.map((payload) => {
|
135
|
+
if (payload.excludeFromOpenAi) {
|
136
|
+
return undefined;
|
137
|
+
}
|
138
|
+
|
139
|
+
const parameters = payload.code?.trim() ?
|
140
|
+
JSON.parse(payload.code) : { type: "object", properties: {}, required: [] };
|
141
|
+
|
142
|
+
|
143
|
+
//TODO - Remove after all the old versions are deprecated
|
144
|
+
const { properties = {}, required = [] } = parameters
|
145
|
+
required.push("isRegisteredIntent")
|
146
|
+
properties.isRegisteredIntent = { type: "boolean", const: true }
|
147
|
+
|
148
|
+
return {
|
149
|
+
type: "function",
|
150
|
+
function: {
|
151
|
+
name: payload.name,
|
152
|
+
description: payload.description,
|
153
|
+
parameters: {
|
154
|
+
...parameters,
|
155
|
+
properties,
|
156
|
+
required,
|
157
|
+
additionalProperties: false
|
158
|
+
},
|
159
|
+
strict: true
|
160
|
+
},
|
161
|
+
};
|
162
|
+
})
|
163
|
+
.filter(Boolean) || []
|
164
|
+
);
|
165
|
+
};
|
166
|
+
|
167
|
+
|
168
|
+
/**
|
169
|
+
*
|
170
|
+
* @param config
|
171
|
+
* @param deprecatedTools
|
172
|
+
* @returns {{}}
|
173
|
+
*/
|
174
|
+
const getToolProperties = (
|
175
|
+
config,
|
176
|
+
deprecatedTools = [],
|
177
|
+
deprecatedTools = [],
|
178
|
+
RED
|
179
|
+
) => {
|
180
|
+
|
181
|
+
const tool_choice = config.tool_choice
|
182
|
+
const tool_string_ids = config.tools;
|
183
|
+
const tool_ids = tool_string_ids.split(",");
|
184
|
+
const toolProperties = {}
|
185
|
+
const allTools = getAllTools(RED)
|
186
|
+
const tools = []
|
187
|
+
|
188
|
+
if (tool_choice !== TOOL_CHOICE.None) {
|
189
|
+
[...deprecatedTools, ...allTools].forEach(tool => {
|
190
|
+
if (tool_ids.includes(tool.function.name)) {
|
191
|
+
tools.push(tool);
|
192
|
+
}
|
193
|
+
})
|
194
|
+
|
195
|
+
toolProperties.tools = tools
|
196
|
+
toolProperties.tool_choice = tool_choice
|
197
|
+
}
|
198
|
+
|
199
|
+
return toolProperties
|
200
|
+
}
|
201
|
+
|
202
|
+
module.exports = {
|
203
|
+
azureOpenAIHelper
|
204
|
+
}
|