@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.
Files changed (70) hide show
  1. package/.eslintrc +49 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/node-red-contrib-ai-intent.iml +12 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/LICENSE +21 -0
  6. package/README.md +233 -0
  7. package/call-intent/icons/promotion-icon.svg +8 -0
  8. package/call-intent/index.html +114 -0
  9. package/call-intent/index.js +110 -0
  10. package/constants.js +31 -0
  11. package/database.js +9 -0
  12. package/examples/home-assistant-automation.json +167 -0
  13. package/examples/llm-chat-node-example.json +208 -0
  14. package/examples/openai-call-registered-intent-example.json +174 -0
  15. package/examples/openai-system-node-example.json +178 -0
  16. package/examples/openai-tool-node-example.json +120 -0
  17. package/examples/openai-user-node-exampe.json +234 -0
  18. package/geminiai-chat/geminiai-configuration/index.html +18 -0
  19. package/geminiai-chat/geminiai-configuration/index.js +7 -0
  20. package/geminiai-chat/icons/diamond.svg +8 -0
  21. package/geminiai-chat/icons/gemini-icon.svg +1 -0
  22. package/geminiai-chat/icons/gemini.svg +8 -0
  23. package/geminiai-chat/index.html +189 -0
  24. package/geminiai-chat/index.js +92 -0
  25. package/globalUtils.js +39 -0
  26. package/images/call_register_intent.jpeg +0 -0
  27. package/images/finally.jpg +0 -0
  28. package/images/set-config-node.gif +0 -0
  29. package/llm-chat/AzureOpenAIHelper.js +204 -0
  30. package/llm-chat/ChatGPTHelper.js +197 -0
  31. package/llm-chat/GeminiHelper.js +260 -0
  32. package/llm-chat/OllamaHelper.js +196 -0
  33. package/llm-chat/icons/bot-message-square.svg +1 -0
  34. package/llm-chat/icons/brain-circuit.svg +1 -0
  35. package/llm-chat/icons/chatgpt-icon.svg +7 -0
  36. package/llm-chat/index.html +205 -0
  37. package/llm-chat/index.js +73 -0
  38. package/llm-chat/platform-configuration/index.html +136 -0
  39. package/llm-chat/platform-configuration/index.js +16 -0
  40. package/localai-chat/icons/gem-icon.svg +1 -0
  41. package/localai-chat/icons/llama.svg +8 -0
  42. package/localai-chat/index.html +244 -0
  43. package/localai-chat/index.js +108 -0
  44. package/localai-chat/localai-configuration/index.html +18 -0
  45. package/localai-chat/localai-configuration/index.js +7 -0
  46. package/openai-chat/icons/chatgpt-icon.svg +7 -0
  47. package/openai-chat/index.html +196 -0
  48. package/openai-chat/index.js +58 -0
  49. package/openai-chat/openai-configuration/index.html +18 -0
  50. package/openai-chat/openai-configuration/index.js +7 -0
  51. package/openai-response/index.html +66 -0
  52. package/openai-response/index.js +154 -0
  53. package/openai-system/index.html +68 -0
  54. package/openai-system/index.js +28 -0
  55. package/openai-tool/index.html +57 -0
  56. package/openai-tool/index.js +50 -0
  57. package/openai-user/index.html +76 -0
  58. package/openai-user/index.js +26 -0
  59. package/package.json +49 -0
  60. package/register-intent/icons/register-icon.svg +8 -0
  61. package/register-intent/index.html +195 -0
  62. package/register-intent/index.js +72 -0
  63. package/register-intent/utils.js +10 -0
  64. package/utilities/chat-controller.js +249 -0
  65. package/utilities/chat-ledger.js +122 -0
  66. package/utilities/conversationHistory.js +68 -0
  67. package/utilities/format.js +94 -0
  68. package/utilities/gemini-controller.js +243 -0
  69. package/utilities/global-context.js +30 -0
  70. package/utilities/validateSchema.js +74 -0
@@ -0,0 +1,197 @@
1
+ const { GlobalContext } = require("../utilities/global-context");
2
+ const { TOOL_CHOICE} = require("../constants");
3
+ const OpenAI = require("openai");
4
+ const {ContextDatabase} = require("../globalUtils");
5
+ const {ConversationHistory} = require("../utilities/conversationHistory");
6
+ const {Format} = require("../utilities/format");
7
+
8
+ const chatGPTHelper = (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
+
14
+ if (!apiKey) {
15
+ node.status({fill:"red",shape:"dot",text:"Error"});
16
+ return callback("Api key missing for OpenAI. Please add openaiAPIKey key-value pair to the functionGlobalContext.");
17
+ }
18
+
19
+ const openai = new OpenAI({ apiKey });
20
+ const {options = {}, system = "", user = ""} = msg?.payload || {}
21
+ const conversation_id = config.conversation_id;
22
+ const conversationHistory = new ConversationHistory(nodeDB, conversation_id)
23
+
24
+ if(msg.clearChatHistory){
25
+ conversationHistory.clearHistory()
26
+ node.warn("Conversation history cleared")
27
+ }
28
+
29
+ if(!user){
30
+ node.status({fill:"red",shape:"dot",text:"Stopped"});
31
+ return node.warn("payload.user is empty. Stopping the flow ")
32
+ }
33
+
34
+ conversationHistory.addSystemMessage(system)
35
+ conversationHistory.addUserMessage(user)
36
+
37
+ if(conversation_id) {
38
+ conversationHistory.saveHistory()
39
+ }
40
+
41
+ const toolProperties = getToolProperties(config, msg.tools, RED)
42
+ const finalProps = {
43
+ ...options,
44
+ ...toolProperties,
45
+ model,
46
+ messages: conversationHistory.conversation
47
+ };
48
+
49
+ openai.chat.completions
50
+ .create(finalProps)
51
+ .then((response) => {
52
+
53
+ response.choices.forEach(choice => {
54
+ conversationHistory.addAssistantMessage(choice.message.content)
55
+ })
56
+ conversationHistory.saveHistory()
57
+
58
+ return createPayload(finalProps, response, msg, conversationHistory.conversation)
59
+ })
60
+ .then(msg => {
61
+ callback(null, msg)
62
+ })
63
+ .catch((err) => {
64
+ callback(err)
65
+ });
66
+ }
67
+
68
+ const createPayload = (request, response, previousMsg, conversationHistory) => {
69
+ const format = new Format()
70
+ const payload = format.formatPayloadForOpenAI(response.choices)
71
+
72
+ return {
73
+ ...previousMsg,
74
+ payload,
75
+ apiResponse: response,
76
+ _debug: {
77
+ ...request,
78
+ messages: conversationHistory
79
+ },
80
+ }
81
+ }
82
+
83
+ const getAllTools = (RED) => {
84
+ const context = new ContextDatabase(RED);
85
+ const intents = context.getNodeStore() || {};
86
+ return createFunctionsFromContext(intents)
87
+ }
88
+
89
+ /**
90
+ * Converts the raw intents into functions that the LLM can use.
91
+ * @param {*} node
92
+ * @returns
93
+ */
94
+ getRegisteredIntentFunctions = (RED) => {
95
+ const intents = getRawIntents(RED);
96
+ return createFunctionsFromContext(intents);
97
+ };
98
+
99
+ /**
100
+ * This will return all stored Registered Intents throughout the entire system
101
+ * and Tool Nodes that are attached directly to this flow
102
+ * This will return:
103
+ * type RawIntent = {
104
+ * [node_id]: node // could be Registered Intent or Tool node
105
+ * }
106
+ */
107
+ getRawIntents = (RED) => {
108
+ const context = new ContextDatabase(RED);
109
+ return context.getNodeStore() || {};
110
+ };
111
+
112
+ /**
113
+ * converts the registered intents stored in the context into functions that can be used by the LLM.
114
+ * The registered intent will be ignored if excludeFromOpenAi is set to true.
115
+ * rawIntents may have tool nodes included so the values need to be filtered by the node type.
116
+ * rawIntents have the following shape:
117
+ *
118
+ * type RawIntents = {
119
+ * [node_id]: node // node could be Registered Intent or Tool node
120
+ * }
121
+ */
122
+ const createFunctionsFromContext = (rawIntents = {}) => {
123
+ return (
124
+ Object.values(rawIntents)
125
+ .filter((payload) => {
126
+ return payload.type === "Register Intent";
127
+ })
128
+ .map((payload) => {
129
+ if (payload.excludeFromOpenAi) {
130
+ return undefined;
131
+ }
132
+
133
+ const parameters = payload.code?.trim() ?
134
+ JSON.parse(payload.code) : {type: "object", properties: {}, required: []};
135
+
136
+
137
+ //TODO - Remove after all the old versions are deprecated
138
+ const {properties = {}, required = []} = parameters
139
+ required.push("isRegisteredIntent")
140
+ properties.isRegisteredIntent = { type:"boolean", const: true }
141
+
142
+ return {
143
+ type: "function",
144
+ function: {
145
+ name: payload.name,
146
+ description: payload.description,
147
+ parameters: {
148
+ ...parameters,
149
+ properties,
150
+ required,
151
+ additionalProperties: false
152
+ },
153
+ strict: true
154
+ },
155
+ };
156
+ })
157
+ .filter(Boolean) || []
158
+ );
159
+ };
160
+
161
+
162
+ /**
163
+ *
164
+ * @param config
165
+ * @param deprecatedTools
166
+ * @returns {{}}
167
+ */
168
+ const getToolProperties = (
169
+ config,
170
+ deprecatedTools = [],
171
+ RED
172
+ ) => {
173
+
174
+ const tool_choice = config.tool_choice
175
+ const tool_string_ids = config.tools;
176
+ const tool_ids = tool_string_ids.split(",");
177
+ const toolProperties = {}
178
+ const allTools = getAllTools(RED)
179
+ const tools = []
180
+
181
+ if(tool_choice !== TOOL_CHOICE.None){
182
+ [...deprecatedTools, ...allTools].forEach(tool => {
183
+ if(tool_ids.includes(tool.function.name)){
184
+ tools.push(tool);
185
+ }
186
+ })
187
+
188
+ toolProperties.tools = tools
189
+ toolProperties.tool_choice = tool_choice
190
+ }
191
+
192
+ return toolProperties
193
+ }
194
+
195
+ module.exports = {
196
+ chatGPTHelper
197
+ }
@@ -0,0 +1,260 @@
1
+ const { GlobalContext } = require("../utilities/global-context");
2
+ const { TOOL_CHOICE} = require("../constants");
3
+ const {ContextDatabase, end} = require("../globalUtils");
4
+ const {ConversationHistory} = require("../utilities/conversationHistory");
5
+ const {Format} = require("../utilities/format");
6
+ const { GoogleGenerativeAI } = require("@google/generative-ai");
7
+ const Sugar = require("sugar");
8
+
9
+ const geminiHelper = (props,callback) => {
10
+ const {node, config, msg, RED} = props
11
+ const nodeDB = new GlobalContext(node);
12
+ const {model, credentials} = node.platform
13
+ const apiKey = credentials.api
14
+
15
+ if (!apiKey) {
16
+ node.status({fill:"red",shape:"dot",text:"Error"});
17
+ return callback("Api key missing for Gemini. Please update the configuration");
18
+ }
19
+
20
+ const {options , system = "", user = ""} = msg?.payload || {}
21
+ const conversation_id = config.conversation_id;
22
+ const conversationHistory = new ConversationHistory(nodeDB, conversation_id)
23
+
24
+ if(msg.clearChatHistory){
25
+ conversationHistory.clearHistory()
26
+ node.warn("Conversation history cleared")
27
+ }
28
+
29
+ if(!user){
30
+ node.status({fill:"red",shape:"dot",text:"Stopped"});
31
+ return node.warn("payload.user is empty. Stopping the flow ")
32
+ }
33
+
34
+ conversationHistory.addSystemMessage(system)
35
+ conversationHistory.addUserMessage(user)
36
+
37
+ if(conversation_id) {
38
+ conversationHistory.saveHistory()
39
+ }
40
+
41
+ // Access your API key as an environment variable (see "Set up your API key" above)
42
+ const genAI = new GoogleGenerativeAI(apiKey);
43
+ const toolProperties = getToolProperties(config, msg.tools, RED)
44
+ const modelParams = {
45
+ ...toolProperties,
46
+ model
47
+ };
48
+
49
+ if(options && typeof options === "object"){
50
+ modelParams.generationConfig = options
51
+ }
52
+
53
+ // The Gemini 1.5 models are versatile and work with most use cases
54
+ const genModel = genAI.getGenerativeModel(modelParams);
55
+ const {history,message, updated} = convertChatToGeminiCompatibleChat(conversationHistory.conversation)
56
+ const chat = genModel.startChat({
57
+ history,
58
+ });
59
+ const finalProps = {
60
+ ...modelParams,
61
+ messages: updated
62
+ };
63
+ chat
64
+ .sendMessage(message)
65
+ .then((result) => result.response)
66
+ .then((response) => {
67
+ return {
68
+ functions: response.functionCalls() || [],
69
+ text: response.text(),
70
+ };
71
+ })
72
+ .then((payload) => {
73
+ console.log("RESPONSE: ", payload)
74
+ conversationHistory.addAssistantMessage(payload.text)
75
+ conversationHistory.saveHistory()
76
+
77
+ return createPayload(finalProps, payload, msg)
78
+ })
79
+ .then(msg => {
80
+ callback(null, msg)
81
+ })
82
+ .catch((err) => {
83
+ callback(err)
84
+ });
85
+ }
86
+
87
+ const createPayload = (request, response, previousMsg) => {
88
+ const format = new Format()
89
+ const payload = format.formatPayloadForGeminiAI(response)
90
+
91
+ return {
92
+ ...previousMsg,
93
+ payload,
94
+ apiResponse: response,
95
+ _debug: {
96
+ ...request
97
+ },
98
+ }
99
+ }
100
+
101
+ const getAllTools = (RED) => {
102
+ const context = new ContextDatabase(RED);
103
+ const intents = context.getNodeStore() || {};
104
+ return createFunctionsFromContext(intents)
105
+ }
106
+
107
+ /**
108
+ * Converts the raw intents into functions that the LLM can use.
109
+ * @param {*} node
110
+ * @returns
111
+ */
112
+ getRegisteredIntentFunctions = (RED) => {
113
+ const intents = getRawIntents(RED);
114
+ return createFunctionsFromContext(intents);
115
+ };
116
+
117
+ /**
118
+ * This will return all stored Registered Intents throughout the entire system
119
+ * and Tool Nodes that are attached directly to this flow
120
+ * This will return:
121
+ * type RawIntent = {
122
+ * [node_id]: node // could be Registered Intent or Tool node
123
+ * }
124
+ */
125
+ getRawIntents = (RED) => {
126
+ const context = new ContextDatabase(RED);
127
+ return context.getNodeStore() || {};
128
+ };
129
+
130
+ /**
131
+ * converts the registered intents stored in the context into functions that can be used by the LLM.
132
+ * The registered intent will be ignored if excludeFromOpenAi is set to true.
133
+ * rawIntents may have tool nodes included so the values need to be filtered by the node type.
134
+ * rawIntents have the following shape:
135
+ *
136
+ * type RawIntents = {
137
+ * [node_id]: node // node could be Registered Intent or Tool node
138
+ * }
139
+ */
140
+ const createFunctionsFromContext = (rawIntents = {}) => {
141
+ return (
142
+ Object.values(rawIntents)
143
+ .filter((payload) => {
144
+ return payload.type === "Register Intent";
145
+ })
146
+ .map((payload) => {
147
+ if (payload.excludeFromOpenAi) {
148
+ return undefined;
149
+ }
150
+ const parameters = payload.code.trim() ?
151
+ JSON.parse(payload.code) : {type: "object", properties: {}, required: []};
152
+
153
+
154
+ //TODO - Remove after all the old versions are deprecated
155
+ const {properties = {}, required = []} = parameters
156
+ required.push("isRegisteredIntent")
157
+ properties.isRegisteredIntent = { type: "string", enum: ["true"] }
158
+
159
+ return {
160
+ type: "function",
161
+ function: {
162
+ name: payload.name,
163
+ description: payload.description,
164
+ parameters: {
165
+ ...parameters,
166
+ properties,
167
+ required,
168
+ }
169
+ }
170
+ };
171
+ })
172
+ .filter(Boolean) || []
173
+ );
174
+ };
175
+
176
+
177
+ const convertChatToGeminiCompatibleChat = (messages = []) => {
178
+ const original = Sugar.Object.clone(messages, true);
179
+ const updated = messages.map((message) => {
180
+ let role = message.role;
181
+ // Gemini doesn't seem to have a system role. We wil convert it to a user
182
+ if (role === "system") {
183
+ role = "user";
184
+ }
185
+
186
+ return { role, parts: [{ text: message.content }] };
187
+ });
188
+
189
+ const history = Sugar.Object.clone(updated, true);
190
+ const nextMessage = history.pop()
191
+ const message = nextMessage?.parts[0]?.text || "";
192
+
193
+ return {
194
+ original, // contains the full chat in it's original form
195
+ updated, // full list of gemini compatible chat
196
+ message, // the next gemini compatible chat item
197
+ history // contains the gemini compatible chat w/o the user's current message (the last message in the array)
198
+ };
199
+ };
200
+
201
+ const convertToolsToGeminiCompatibleTools = (tools = []) => {
202
+ return {
203
+ functionDeclarations: tools.map((tool) => {
204
+ return tool.function;
205
+ }),
206
+ };
207
+ };
208
+
209
+
210
+ /**
211
+ *
212
+ * @param config
213
+ * @param deprecatedTools
214
+ * @returns {{}}
215
+ */
216
+ const getToolProperties = (
217
+ config,
218
+ deprecatedTools = [],
219
+ RED
220
+ ) => {
221
+ const mode = convertToolChoiceToGeminiCompatibleChoice(config.tool_choice)
222
+ const tool_string_ids = config.tools;
223
+ const allowed_function_names = tool_string_ids.split(",");
224
+ const toolProperties = {}
225
+ const allTools = getAllTools(RED)
226
+ const tools = []
227
+ const tool_config = {
228
+ function_calling_config:{
229
+ mode,
230
+ allowed_function_names
231
+ }
232
+ }
233
+
234
+ if(mode !== TOOL_CHOICE.None){
235
+ [...deprecatedTools, ...allTools].forEach(tool => {
236
+ if(allowed_function_names.includes(tool.function.name)){
237
+ tools.push(tool);
238
+ }
239
+ })
240
+
241
+ toolProperties.tools = convertToolsToGeminiCompatibleTools(tools)
242
+ toolProperties.tool_config = tool_config
243
+ }
244
+
245
+ return toolProperties
246
+ }
247
+
248
+ function convertToolChoiceToGeminiCompatibleChoice(mode){
249
+ switch(mode){
250
+ case TOOL_CHOICE.Required:
251
+ return TOOL_CHOICE.Any.toUpperCase()
252
+ default:
253
+ return mode.toUpperCase()
254
+ }
255
+ }
256
+
257
+
258
+ module.exports = {
259
+ geminiHelper
260
+ }
@@ -0,0 +1,196 @@
1
+ const { GlobalContext } = require("../utilities/global-context");
2
+ const { TOOL_CHOICE} = require("../constants");
3
+ const {ContextDatabase} = require("../globalUtils");
4
+ const {ConversationHistory} = require("../utilities/conversationHistory");
5
+ const {Format} = require("../utilities/format");
6
+ const {Ollama} = require("ollama");
7
+
8
+ const ollamaHelper = (props,callback) => {
9
+ const {node, config, msg, RED} = props
10
+ const nodeDB = new GlobalContext(node);
11
+ const {url: host, model} = node.platform
12
+
13
+ if (!host) {
14
+ node.status({fill:"red",shape:"dot",text:"Error"});
15
+ return callback("URL is missing. Please update the config to point to a valid URL for your local llm");
16
+ }
17
+
18
+ const {options = {}, system = "", user = ""} = msg?.payload || {}
19
+ const conversation_id = config.conversation_id;
20
+ const conversationHistory = new ConversationHistory(nodeDB, conversation_id)
21
+
22
+ if(msg.clearChatHistory){
23
+ conversationHistory.clearHistory()
24
+ node.warn("Conversation history cleared")
25
+ }
26
+
27
+ if(!user){
28
+ node.status({fill:"red",shape:"dot",text:"Stopped"});
29
+ return node.warn("payload.user is empty. Stopping the flow ")
30
+ }
31
+
32
+ conversationHistory.addSystemMessage(system)
33
+ conversationHistory.addUserMessage(user)
34
+
35
+ if(conversation_id) {
36
+ conversationHistory.saveHistory()
37
+ }
38
+
39
+ const toolProperties = getToolProperties(config, msg.tools, RED)
40
+ const finalProps = {
41
+ ...options,
42
+ ...toolProperties,
43
+ model,
44
+ messages: conversationHistory.conversation
45
+ };
46
+
47
+ const ollama = new Ollama({ host });
48
+
49
+ ollama.chat(finalProps)
50
+ .then((response) => {
51
+ console.log("RESPONSE: ", response)
52
+
53
+ conversationHistory.addAssistantMessage(response.message.content)
54
+ conversationHistory.saveHistory()
55
+
56
+ return createPayload(finalProps, response, msg, conversationHistory.conversation)
57
+ })
58
+ .then(msg => {
59
+ callback(null, msg)
60
+ })
61
+ .catch((err) => {
62
+ callback(err)
63
+ });
64
+ }
65
+
66
+ const createPayload = (request, response, previousMsg, conversationHistory) => {
67
+ const format = new Format()
68
+
69
+ const payload = format.formatPayloadForLocalAI(response.message)
70
+
71
+ return {
72
+ ...previousMsg,
73
+ payload,
74
+ apiResponse: response,
75
+ _debug: {
76
+ ...request,
77
+ messages: conversationHistory
78
+ },
79
+ }
80
+ }
81
+
82
+ const getAllTools = (RED) => {
83
+ const context = new ContextDatabase(RED);
84
+ const intents = context.getNodeStore() || {};
85
+ return createFunctionsFromContext(intents)
86
+ }
87
+
88
+ /**
89
+ * Converts the raw intents into functions that the LLM can use.
90
+ * @param {*} node
91
+ * @returns
92
+ */
93
+ getRegisteredIntentFunctions = (RED) => {
94
+ const intents = getRawIntents(RED);
95
+ return createFunctionsFromContext(intents);
96
+ };
97
+
98
+ /**
99
+ * This will return all stored Registered Intents throughout the entire system
100
+ * and Tool Nodes that are attached directly to this flow
101
+ * This will return:
102
+ * type RawIntent = {
103
+ * [node_id]: node // could be Registered Intent or Tool node
104
+ * }
105
+ */
106
+ getRawIntents = (RED) => {
107
+ const context = new ContextDatabase(RED);
108
+ return context.getNodeStore() || {};
109
+ };
110
+
111
+ /**
112
+ * converts the registered intents stored in the context into functions that can be used by the LLM.
113
+ * The registered intent will be ignored if excludeFromOpenAi is set to true.
114
+ * rawIntents may have tool nodes included so the values need to be filtered by the node type.
115
+ * rawIntents have the following shape:
116
+ *
117
+ * type RawIntents = {
118
+ * [node_id]: node // node could be Registered Intent or Tool node
119
+ * }
120
+ */
121
+ const createFunctionsFromContext = (rawIntents = {}) => {
122
+ return (
123
+ Object.values(rawIntents)
124
+ .filter((payload) => {
125
+ return payload.type === "Register Intent";
126
+ })
127
+ .map((payload) => {
128
+ if (payload.excludeFromOpenAi) {
129
+ return undefined;
130
+ }
131
+ const parameters = payload.code.trim() ?
132
+ JSON.parse(payload.code) : {type: "object", properties: {}, required: []};
133
+
134
+
135
+ //TODO - Remove after all the old versions are deprecated
136
+ const {properties = {}, required = []} = parameters
137
+ required.push("isRegisteredIntent")
138
+ properties.isRegisteredIntent = { type:"boolean", const: true }
139
+
140
+ return {
141
+ type: "function",
142
+ function: {
143
+ name: payload.name,
144
+ description: payload.description,
145
+ parameters: {
146
+ ...parameters,
147
+ properties,
148
+ required,
149
+ additionalProperties: false
150
+ },
151
+ strict: true
152
+ },
153
+ };
154
+ })
155
+ .filter(Boolean) || []
156
+ );
157
+ };
158
+
159
+
160
+ /**
161
+ *
162
+ * @param config
163
+ * @param deprecatedTools
164
+ * @returns {{}}
165
+ */
166
+ const getToolProperties = (
167
+ config,
168
+ deprecatedTools = [],
169
+ RED
170
+ ) => {
171
+
172
+ const tool_choice = config.tool_choice
173
+ const tool_string_ids = config.tools;
174
+ const tool_ids = tool_string_ids.split(",");
175
+ const toolProperties = {}
176
+ const allTools = getAllTools(RED)
177
+ const tools = []
178
+
179
+ if(tool_choice !== TOOL_CHOICE.None){
180
+ [...deprecatedTools, ...allTools].forEach(tool => {
181
+ if(tool_ids.includes(tool.function.name)){
182
+ tools.push(tool);
183
+ }
184
+ })
185
+
186
+ toolProperties.tools = tools
187
+ toolProperties.tool_choice = tool_choice
188
+ }
189
+
190
+ return toolProperties
191
+ }
192
+
193
+
194
+ module.exports = {
195
+ ollamaHelper
196
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bot-message-square"><path d="M12 6V2H8"/><path d="m8 18-4 4V8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2Z"/><path d="M2 12h2"/><path d="M9 11v2"/><path d="M15 11v2"/><path d="M20 12h2"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-brain-circuit"><path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/><path d="M9 13a4.5 4.5 0 0 0 3-4"/><path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"/><path d="M3.477 10.896a4 4 0 0 1 .585-.396"/><path d="M6 18a4 4 0 0 1-1.967-.516"/><path d="M12 13h4"/><path d="M12 18h6a2 2 0 0 1 2 2v1"/><path d="M12 8h8"/><path d="M16 8V5a2 2 0 0 1 2-2"/><circle cx="16" cy="13" r=".5"/><circle cx="18" cy="3" r=".5"/><circle cx="20" cy="21" r=".5"/><circle cx="20" cy="8" r=".5"/></svg>
@@ -0,0 +1,7 @@
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 512 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
+ <g>
5
+ <path d="M423.895,220.444C432.789,193.751 429.726,164.509 415.503,140.229C394.114,102.988 351.116,83.829 309.122,92.845C290.44,71.799 263.6,59.831 235.461,60.002C192.536,59.904 154.45,87.541 141.244,128.383C113.669,134.03 89.866,151.291 75.938,175.755C54.39,212.898 59.302,259.718 88.09,291.569C79.196,318.262 82.259,347.504 96.481,371.784C117.87,409.025 160.868,428.184 202.862,419.168C221.531,440.214 248.384,452.182 276.523,451.999C319.472,452.109 357.571,424.448 370.776,383.569C398.351,377.922 422.154,360.661 436.082,336.197C457.606,299.054 452.681,252.271 423.905,220.42L423.895,220.444ZM276.549,426.383C259.362,426.407 242.714,420.393 229.52,409.38C230.12,409.061 231.162,408.486 231.835,408.069L309.894,362.988C313.888,360.722 316.338,356.471 316.313,351.877L316.313,241.833L349.303,260.882C349.658,261.054 349.891,261.397 349.94,261.789L349.94,352.919C349.892,393.442 317.073,426.297 276.549,426.383ZM118.717,358.97C110.105,344.098 107.006,326.666 109.958,309.748C110.534,310.091 111.551,310.716 112.273,311.132L190.332,356.213C194.289,358.528 199.189,358.528 203.158,356.213L298.453,301.185L298.453,339.283C298.478,339.675 298.294,340.055 297.988,340.3L219.084,385.859C183.938,406.096 139.053,394.067 118.73,358.97L118.717,358.97ZM98.173,188.581C106.748,173.685 120.285,162.292 136.406,156.375C136.406,157.049 136.369,158.237 136.369,159.07L136.369,249.244C136.344,253.826 138.795,258.076 142.776,260.343L238.071,315.359L205.081,334.408C204.75,334.628 204.334,334.665 203.966,334.506L125.05,288.91C89.978,268.599 77.948,223.726 98.161,188.593L98.173,188.581ZM369.222,251.657L273.927,196.629L306.917,177.592C307.248,177.372 307.664,177.335 308.032,177.494L386.948,223.053C422.082,243.352 434.124,288.298 413.825,323.432C405.238,338.304 391.713,349.696 375.604,355.626L375.604,262.757C375.641,258.175 373.203,253.937 369.234,251.658L369.222,251.658L369.222,251.657ZM402.053,202.24C401.477,201.885 400.461,201.272 399.738,200.856L321.679,155.775C317.722,153.46 312.822,153.46 308.853,155.775L213.558,210.803L213.558,172.705C213.534,172.313 213.717,171.933 214.023,171.688L292.927,126.166C328.073,105.892 373.007,117.958 393.269,153.117C401.832,167.964 404.931,185.347 402.028,202.241L402.053,202.241L402.053,202.24ZM195.624,270.143L162.622,251.094C162.267,250.923 162.034,250.58 161.985,250.188L161.985,159.058C162.01,118.485 194.926,85.605 235.499,85.63C252.662,85.63 269.273,91.657 282.467,102.633C281.867,102.951 280.838,103.527 280.152,103.944L202.093,149.025C198.099,151.291 195.649,155.53 195.674,160.124L195.625,270.119L195.625,270.143L195.624,270.143ZM213.546,231.506L255.993,206.993L298.44,231.494L298.44,280.507L255.993,305.008L213.546,280.507L213.546,231.506Z" style="fill:white;fill-rule:nonzero;"/>
6
+ </g>
7
+ </svg>