node-red-contrib-ollama 0.2.6 → 0.4.0

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.
@@ -37,16 +37,14 @@
37
37
  payload <span class="property-type">object</span>
38
38
  </dt>
39
39
  <dd>
40
- The payload object.
40
+ The payload object is an alternative way to configure the node.<br />
41
+ Any properties in the payload object will override the node configuration.
41
42
 
42
43
  <dl class="message-properties">
43
44
  <dt class="optional">
44
45
  payload.host <span class="property-type">string</span>
45
46
  </dt>
46
- <dd>
47
- The host of the Ollama instance.<br />
48
- <em>Alternative to the <code>Server</code> field.</em>
49
- </dd>
47
+ <dd>The host of the Ollama instance.</dd>
50
48
  </dl>
51
49
  </dd>
52
50
  </dl>
@@ -6,13 +6,9 @@ module.exports = function( RED ) {
6
6
  const node = this
7
7
 
8
8
  node.on( 'input', async function( msg ) {
9
- const {
10
- host: payloadHost
11
- } = msg.payload
12
-
13
9
  const server = RED.nodes.getNode( config.server )
14
10
 
15
- const host = ( server ) ? server.host + ':' + server.port : payloadHost
11
+ const host = msg?.payload?.host || ( server ) ? server.host + ':' + server.port : null
16
12
 
17
13
  const ollama = new Ollama( { host } )
18
14
 
@@ -5,8 +5,15 @@
5
5
  defaults: {
6
6
  name: { value: 'Ollama Chat' },
7
7
  server: { value: '', type: 'ollama-config-server', required: false },
8
- model: { value: '', type: 'ollama-config-model', required: true },
8
+ model: { value: '', required: false },
9
+ modelType: { value: 'str', required: false },
10
+ messages: { value: 'payload', required: false },
11
+ messagesType: { value: 'msg', required: false },
9
12
  format: { value: '', type: 'ollama-config-format', required: false },
13
+ stream: { value: false },
14
+ think: { value: false },
15
+ keepAlive: { value: '5m', required: false },
16
+ keepAliveType: { value: 'str', required: false },
10
17
  tools: { value: '', type: 'ollama-config-tools', required: false },
11
18
  options: { value: '', type: 'ollama-config-options', required: false },
12
19
  },
@@ -14,8 +21,39 @@
14
21
  outputs: 1,
15
22
  icon: 'ollama.png',
16
23
  label: function() {
17
- return this.name || 'ollama-chat';
24
+ return this.name || 'ollama-chat'
18
25
  },
26
+ oneditprepare: function() {
27
+ if( !this.modelType ) {
28
+ this.modelType = 'str'
29
+ }
30
+
31
+ $( '#node-input-model' ).typedInput( {
32
+ type: 'str',
33
+ types: [ 'str', 'msg', 'flow', 'global' ],
34
+ typeField: '#node-input-modelType'
35
+ } )
36
+
37
+ if( !this.messagesType ) {
38
+ this.messagesType = 'msg'
39
+ }
40
+
41
+ $( '#node-input-messages' ).typedInput( {
42
+ type: 'msg',
43
+ types: [ 'msg', 'flow', 'global', 'json' ],
44
+ typeField: '#node-input-messagesType'
45
+ } )
46
+
47
+ if( !this.keepAliveType ) {
48
+ this.keepAliveType = 'str'
49
+ }
50
+
51
+ $( '#node-input-keepAlive' ).typedInput( {
52
+ type: 'str',
53
+ types: [ 'str', 'num' ],
54
+ typeField: '#node-input-keepAliveType'
55
+ } )
56
+ }
19
57
  } )
20
58
  </script>
21
59
 
@@ -31,8 +69,15 @@
31
69
  </div>
32
70
 
33
71
  <div class="form-row">
34
- <label for="node-input-model"><i class="fa fa-bookmark"></i> Model</label>
72
+ <label for="node-input-model"><i class="fa fa-comment"></i> Model</label>
35
73
  <input type="text" id="node-input-model" />
74
+ <input type="hidden" id="node-input-modelType" />
75
+ </div>
76
+
77
+ <div class="form-row">
78
+ <label for="node-input-messages"><i class="fa fa-comment"></i> Messages</label>
79
+ <input type="text" id="node-input-messages" />
80
+ <input type="hidden" id="node-input-messagesType" />
36
81
  </div>
37
82
 
38
83
  <div class="form-row">
@@ -40,6 +85,22 @@
40
85
  <input type="text" id="node-input-format" />
41
86
  </div>
42
87
 
88
+ <div class="form-row">
89
+ <label for="node-input-stream"><i class="fa fa-cog"></i> Stream</label>
90
+ <input type="checkbox" id="node-input-stream" />
91
+ </div>
92
+
93
+ <div class="form-row">
94
+ <label for="node-input-think"><i class="fa fa-cog"></i> Think</label>
95
+ <input type="checkbox" id="node-input-think" />
96
+ </div>
97
+
98
+ <div class="form-row">
99
+ <label for="node-input-keepAlive"><i class="fa fa-cog"></i> Keep Alive</label>
100
+ <input type="text" id="node-input-keepAlive" />
101
+ <input type="hidden" id="node-input-keepAliveType" />
102
+ </div>
103
+
43
104
  <div class="form-row">
44
105
  <label for="node-input-tools"><i class="fa fa-cog"></i> Tools</label>
45
106
  <input type="text" id="node-input-tools" />
@@ -62,49 +123,44 @@
62
123
  </dt>
63
124
 
64
125
  <dd>
65
- The payload object.
126
+ The payload object is an alternative way to configure the node.<br />
127
+ Any properties in the payload object will override the node configuration.
66
128
 
67
129
  <dl class="message-properties">
68
130
  <dt class="optional">
69
131
  payload.host <span class="property-type">string</span>
70
132
  </dt>
71
- <dd>
72
- The host of the Ollama instance.<br />
73
- <em>Alternative to the <code>Server</code> field.</em>
74
- </dd>
133
+ <dd>The host of the Ollama instance.</dd>
75
134
 
76
135
  <dt class="optional">
77
136
  payload.model <span class="property-type">string</span>
78
137
  </dt>
79
- <dd>
80
- The model to use for generating the next message.<br />
81
- <em>Alternative to the <code>Model</code> field.</em>
82
- </dd>
138
+ <dd>The model to use for generating the next message.</dd>
83
139
 
84
- <dt>
140
+ <dt class="optional">
85
141
  payload.messages <span class="property-type">array</span>
86
142
  </dt>
87
143
  <dd>
88
- The chat history to use for generating the next message.<br />
144
+ The chat history to use for generating the next message.
89
145
 
90
146
  <h4>Message</h4>
91
147
  <dl class="message-properties">
92
- <dt>
148
+ <dt class="optional">
93
149
  role <span class="property-type">string</span>
94
150
  </dt>
95
151
  <dd>The role of the message sender ('user', 'system', or 'assistant').</dd>
96
152
 
97
- <dt>
153
+ <dt class="optional">
98
154
  content <span class="property-type">string</span>
99
155
  </dt>
100
156
  <dd>The content of the message.</dd>
101
157
 
102
- <dt>
158
+ <dt class="optional">
103
159
  images <span class="property-type">array</span>
104
160
  </dt>
105
161
  <dd>The images attached to the message, either as Uint8Array or base64 encoded strings.</dd>
106
162
 
107
- <dt>
163
+ <dt class="optional">
108
164
  tool_calls <span class="property-type">array</span>
109
165
  </dt>
110
166
  <dd>A list of tools the model wants to use.</dd>
@@ -114,15 +170,21 @@
114
170
  <dt class="optional">
115
171
  payload.format <span class="property-type">string</span>
116
172
  </dt>
173
+ <dd>The format of the response (only <code>json</code> for now).</dd>
174
+
175
+ <dt class="optional">
176
+ payload.stream <span class="property-type">boolean</span>
177
+ </dt>
117
178
  <dd>
118
- The format of the response (only <code>json</code> for now).<br />
119
- <em>Alternative to the <code>Format</code> field.</em>
179
+ When true an <code>AsyncGenerator</code> is returned.
120
180
  </dd>
121
181
 
122
182
  <dt class="optional">
123
- payload.stream <span class="property-type">boolean</span>
183
+ payload.think <span class="property-type">boolean</span>
124
184
  </dt>
125
- <dd>When true an AsyncGenerator is returned.</dd>
185
+ <dd>
186
+ When true, the model will think about the response before responding. Requires thinking support from the model.
187
+ </dd>
126
188
 
127
189
  <dt class="optional">
128
190
  payload.keep_alive <span class="property-type">array</span>
@@ -134,8 +196,7 @@
134
196
  </dt>
135
197
  <dd>
136
198
  Tools for the model to use if supported.<br />
137
- Requires <code>stream</code> to be set to <code>false</code>.<br />
138
- <em>Alternative to the <code>Model</code> field.</em>
199
+ Requires <code>stream</code> to be set to <code>false</code>.
139
200
 
140
201
  <p>
141
202
  For more information, see the Ollama API documentation:<br />
@@ -147,145 +208,144 @@
147
208
  payload.options <span class="property-type">object</span>
148
209
  </dt>
149
210
  <dd>
150
- Options to configure the runtime.<br />
151
- <em>Alternative to the <code>Options</code> field.</em>
211
+ Options to configure the runtime.
152
212
 
153
213
  <h4>Options</h4>
154
214
  <dl class="message-properties">
155
- <dt>
215
+ <dt class="optional">
156
216
  numa <span class="property-type">boolean</span>
157
217
  </dt>
158
218
  <dd>Use NUMA for the runtime.</dd>
159
219
 
160
- <dt>
220
+ <dt class="optional">
161
221
  num_ctx <span class="property-type">number</span>
162
222
  </dt>
163
223
  <dd>The number of contexts to use.</dd>
164
224
 
165
- <dt>
225
+ <dt class="optional">
166
226
  num_batch <span class="property-type">number</span>
167
227
  </dt>
168
228
  <dd>The number of batches to use.</dd>
169
229
 
170
- <dt>
230
+ <dt class="optional">
171
231
  main_gpu <span class="property-type">number</span>
172
232
  </dt>
173
233
  <dd>The main GPU to use.</dd>
174
234
 
175
- <dt>
235
+ <dt class="optional">
176
236
  low_vram <span class="property-type">boolean</span>
177
237
  </dt>
178
238
  <dd>Use low VRAM.</dd>
179
239
 
180
- <dt>
240
+ <dt class="optional">
181
241
  f16_kv <span class="property-type">boolean</span>
182
242
  </dt>
183
243
  <dd>Use f16 key-value.</dd>
184
244
 
185
- <dt>
245
+ <dt class="optional">
186
246
  logits_all <span class="property-type">boolean</span>
187
247
  </dt>
188
248
  <dd>Return all logits.</dd>
189
249
 
190
- <dt>
250
+ <dt class="optional">
191
251
  vocab_only <span class="property-type">boolean</span>
192
252
  </dt>
193
253
  <dd>Return only the vocabulary.</dd>
194
254
 
195
- <dt>
255
+ <dt class="optional">
196
256
  use_nmap <span class="property-type">boolean</span>
197
257
  </dt>
198
258
  <dd>Use NMAP.</dd>
199
259
 
200
- <dt>
260
+ <dt class="optional">
201
261
  use_mlock <span class="property-type">boolean</span>
202
262
  </dt>
203
263
  <dd>Use mlock.</dd>
204
264
 
205
- <dt>
265
+ <dt class="optional">
206
266
  embedding_only <span class="property-type">boolean</span>
207
267
  </dt>
208
268
  <dd>Return only the embeddings.</dd>
209
269
 
210
- <dt>
270
+ <dt class="optional">
211
271
  num_thread <span class="property-type">number</span>
212
272
  </dt>
213
273
  <dd>The number of threads to use.</dd>
214
274
 
215
- <dt>
275
+ <dt class="optional">
216
276
  num_keep <span class="property-type">number</span>
217
277
  </dt>
218
278
  <dd>The number of models to keep.</dd>
219
279
 
220
- <dt>
280
+ <dt class="optional">
221
281
  seed <span class="property-type">number</span>
222
282
  </dt>
223
283
  <dd>The seed to use.</dd>
224
284
 
225
- <dt>
285
+ <dt class="optional">
226
286
  num_predict <span class="property-type">number</span>
227
287
  </dt>
228
288
  <dd>The number of predictions to use.</dd>
229
289
 
230
- <dt>
290
+ <dt class="optional">
231
291
  top_k <span class="property-type">number</span>
232
292
  </dt>
233
293
  <dd>The top k to use.</dd>
234
294
 
235
- <dt>
295
+ <dt class="optional">
236
296
  top_p <span class="property-type">number</span>
237
297
  </dt>
238
298
  <dd>The top p to use.</dd>
239
299
 
240
- <dt>
300
+ <dt class="optional">
241
301
  typical_p <span class="property-type">number</span>
242
302
  </dt>
243
303
  <dd>The typical p to use.</dd>
244
304
 
245
- <dt>
305
+ <dt class="optional">
246
306
  repeat_last_n <span class="property-type">number</span>
247
307
  </dt>
248
308
  <dd>The repeat last n to use.</dd>
249
309
 
250
- <dt>
310
+ <dt class="optional">
251
311
  temperature <span class="property-type">number</span>
252
312
  </dt>
253
313
  <dd>The temperature to use.</dd>
254
314
 
255
- <dt>
315
+ <dt class="optional">
256
316
  repeat_pentalty <span class="property-type">number</span>
257
317
  </dt>
258
318
  <dd>The repeat pentalty to use.</dd>
259
319
 
260
- <dt>
320
+ <dt class="optional">
261
321
  presence_penalty <span class="property-type">number</span>
262
322
  </dt>
263
323
  <dd>The presence penalty to use.</dd>
264
324
 
265
- <dt>
325
+ <dt class="optional">
266
326
  frequency_penalty <span class="property-type">number</span>
267
327
  </dt>
268
328
  <dd>The frequency penalty to use.</dd>
269
329
 
270
- <dt>
330
+ <dt class="optional">
271
331
  mirostat <span class="property-type">boolean</span>
272
332
  </dt>
273
333
  <dd>Use mirostat.</dd>
274
334
 
275
- <dt>
335
+ <dt class="optional">
276
336
  mirostat_tau <span class="property-type">number</span>
277
337
  </dt>
278
338
 
279
- <dt>
339
+ <dt class="optional">
280
340
  mirostat_eta <span class="property-type">number</span>
281
341
  </dt>
282
342
 
283
- <dt>
343
+ <dt class="optional">
284
344
  penalize_newline <span class="property-type">boolean</span>
285
345
  </dt>
286
346
  <dd>Penalize newline.</dd>
287
347
 
288
- <dt>
348
+ <dt class="optional">
289
349
  stop <span class="property-type">array</span>
290
350
  </dt>
291
351
  <dd>The stop to use.</dd>
@@ -1,44 +1,81 @@
1
1
  module.exports = function( RED ) {
2
2
  const { Ollama } = require( 'ollama' )
3
3
 
4
- function OllamaChatNode( config ) {
4
+ function OllamaChatNode( config ) {
5
5
  RED.nodes.createNode( this, config )
6
- const node = this
7
6
 
8
- node.on( 'input', async function( msg ) {
9
- const {
10
- host: payloadHost,
11
- model: payloadModel,
12
- messages,
13
- format: payloadFormat,
14
- stream,
15
- keep_alive,
16
- tools: payloadTools,
17
- options: payloadOptions
18
- } = msg.payload
7
+ this.modelType = config.modelType || 'str'
8
+ this.messagesType = config.messagesType || 'msg'
9
+ this.stream = config.stream || false
10
+ this.keepAliveType = config.keepAliveType || 'str'
19
11
 
12
+ const node = this
13
+ node.on( 'input', async function( msg ) {
20
14
  const server = RED.nodes.getNode( config.server )
21
- const host = ( server ) ? server.host + ':' + server.port : payloadHost
15
+ const host = msg?.payload?.host || ( server ) ? server.host + ':' + server.port : null
22
16
 
23
17
  const ollama = new Ollama( { host } )
24
18
 
25
- const modelConfig = RED.nodes.getNode( config.model )
26
- const model = ( modelConfig ) ? modelConfig.name : payloadModel
19
+ let model = null
20
+ if( msg?.payload?.model ) {
21
+ model = msg?.payload?.model
22
+ } else if( !!config.model ) {
23
+ if( node.modelType === 'str' ) {
24
+ model = config.model
25
+ } else if( node.modelType === 'msg' ) {
26
+ model = msg[ config.model ]
27
+ } else if( node.modelType === 'flow' ) {
28
+ model = node.context().flow.get( config.model )
29
+ } else if( node.modelType === 'global' ) {
30
+ model = node.context().global.get( config.model )
31
+ }
32
+ }
33
+
34
+ let messages = null
35
+ if( msg?.payload?.messages ) {
36
+ messages = msg?.payload?.messages
37
+ } else if( !!config.messages ) {
38
+ if( node.messagesType === 'msg' ) {
39
+ messages = msg[ config.messages ]
40
+ } else if( node.messagesType === 'flow' ) {
41
+ messages = node.context().flow.get( config.messages )
42
+ } else if( node.messagesType === 'global' ) {
43
+ messages = node.context().global.get( config.messages )
44
+ } else if( node.messagesType === 'json' ) {
45
+ messages = JSON.parse( config.messages )
46
+ }
47
+ }
27
48
 
28
49
  const formatConfig = RED.nodes.getNode( config.format )
29
- const format = ( formatConfig ) ? formatConfig.json : payloadFormat
50
+ const format = msg?.payload?.format || ( formatConfig ) ? formatConfig.json : null
51
+
52
+ const stream = ( msg?.payload?.stream !== undefined ) ? msg?.payload?.stream : node.stream
53
+
54
+ const think = ( msg?.payload?.think !== undefined ) ? msg?.payload?.think : config.think || false
55
+
56
+ let keep_alive = null
57
+ if( msg?.payload?.keep_alive ) {
58
+ keep_alive = msg?.payload?.keep_alive
59
+ } else if( !!config.keepAlive ) {
60
+ if( node.keepAliveType === 'str' ) {
61
+ keep_alive = config.keepAlive
62
+ } else if( node.keepAliveType === 'num' ) {
63
+ keep_alive = Number( config.keepAlive )
64
+ }
65
+ }
30
66
 
31
67
  const toolsConfig = RED.nodes.getNode( config.tools )
32
- const tools = ( toolsConfig ) ? toolsConfig.json : payloadTools
68
+ const tools = msg?.payload?.tools || ( toolsConfig ) ? JSON.parse( toolsConfig.json ) : null
33
69
 
34
70
  const optionsConfig = RED.nodes.getNode( config.options )
35
- const options = ( optionsConfig ) ? optionsConfig.json : payloadOptions
71
+ const options = msg?.payload?.options || ( optionsConfig ) ? JSON.parse( optionsConfig.json ) : null
36
72
 
37
73
  const response = await ollama.chat( {
38
74
  model,
39
75
  messages,
40
76
  format,
41
77
  stream,
78
+ think,
42
79
  keep_alive,
43
80
  tools,
44
81
  options
@@ -4,14 +4,39 @@
4
4
  color: 'rgb(186 230 253)',
5
5
  defaults: {
6
6
  name: { value: 'Ollama Copy' },
7
- server: { value: '', type: 'ollama-config-server', required: false }
7
+ server: { value: '', type: 'ollama-config-server', required: false },
8
+ source: { value: '', required: true },
9
+ sourceType: { value: 'str' },
10
+ destination: { value: '', required: true },
11
+ destinationType: { value: 'str' },
8
12
  },
9
13
  inputs: 1,
10
14
  outputs: 1,
11
15
  icon: 'ollama.png',
12
16
  label: function() {
13
- return this.name || 'ollama-copy';
17
+ return this.name || 'ollama-copy'
14
18
  },
19
+ oneditprepare: function() {
20
+ if( !this.sourceType ) {
21
+ this.sourceType = 'msg'
22
+ }
23
+
24
+ $( '#node-input-source' ).typedInput( {
25
+ type: 'str',
26
+ types: [ 'str', 'msg', 'flow', 'global' ],
27
+ typeField: '#node-input-sourceType'
28
+ } )
29
+
30
+ if( !this.destinationType ) {
31
+ this.destinationType = 'msg'
32
+ }
33
+
34
+ $( '#node-input-destination' ).typedInput( {
35
+ type: 'str',
36
+ types: [ 'str', 'msg', 'flow', 'global' ],
37
+ typeField: '#node-input-destinationType'
38
+ } )
39
+ }
15
40
  } )
16
41
  </script>
17
42
 
@@ -25,6 +50,18 @@
25
50
  <label for="node-input-server"><i class="fa fa-server"></i> Server</label>
26
51
  <input type="text" id="node-input-server" />
27
52
  </div>
53
+
54
+ <div class="form-row">
55
+ <label for="node-input-source"><i class="fa fa-comment"></i> Source</label>
56
+ <input type="text" id="node-input-source" />
57
+ <input type="hidden" id="node-input-sourceType" />
58
+ </div>
59
+
60
+ <div class="form-row">
61
+ <label for="node-input-destination"><i class="fa fa-comment"></i> Destination</label>
62
+ <input type="text" id="node-input-destination" />
63
+ <input type="hidden" id="node-input-destinationType" />
64
+ </div>
28
65
  </script>
29
66
 
30
67
  <script type="text/html" data-help-name="ollama-copy">
@@ -38,23 +75,21 @@
38
75
  </dt>
39
76
 
40
77
  <dd>
41
- The payload object.
78
+ The payload object is an alternative way to configure the node.<br />
79
+ Any properties in the payload object will override the node configuration.
42
80
 
43
81
  <dl class="message-properties">
44
82
  <dt class="optional">
45
83
  payload.host <span class="property-type">string</span>
46
84
  </dt>
47
- <dd>
48
- The host of the Ollama instance.<br />
49
- <em>Alternative to the <code>Server</code> field.</em>
50
- </dd>
85
+ <dd>The host of the Ollama instance.</dd>
51
86
 
52
- <dt>
87
+ <dt class="optional">
53
88
  payload.source <span class="property-type">string</span>
54
89
  </dt>
55
90
  <dd>The name of the model to copy.</dd>
56
91
 
57
- <dt>
92
+ <dt class="optional">
58
93
  payload.destination <span class="property-type">string</span>
59
94
  </dt>
60
95
  <dd>The name of the new model.</dd>
@@ -3,22 +3,52 @@ module.exports = function( RED ) {
3
3
 
4
4
  function OllamaCopyNode( config ) {
5
5
  RED.nodes.createNode( this, config )
6
- const node = this
7
6
 
8
- node.on( 'input', async function( msg ) {
9
- const {
10
- host: payloadHost,
11
- source,
12
- destination
13
- } = msg.payload
7
+ this.sourceType = config.sourceType || 'str'
8
+ this.destinationType = config.destinationType || 'str'
14
9
 
10
+ const node = this
11
+ node.on( 'input', async function( msg ) {
15
12
  const server = RED.nodes.getNode( config.server )
16
13
 
17
- const host = ( server ) ? server.host + ':' + server.port : payloadHost
14
+ const host = msg?.payload?.host || ( server ) ? server.host + ':' + server.port : null
18
15
 
19
16
  const ollama = new Ollama( { host } )
20
17
 
21
- const response = await ollama.copy( { source, destination } )
18
+ let source = null
19
+ if( msg?.payload?.source ) {
20
+ source = msg?.payload?.source
21
+ } else if( !!config.source ) {
22
+ if( node.sourceType === 'str' ) {
23
+ source = config.source
24
+ } else if( node.sourceType === 'msg' ) {
25
+ source = msg[ config.source ]
26
+ } else if( node.sourceType === 'flow' ) {
27
+ source = node.context().flow.get( config.source )
28
+ } else if( node.sourceType === 'global' ) {
29
+ source = node.context().global.get( config.source )
30
+ }
31
+ }
32
+
33
+ let destination = null
34
+ if( msg?.payload?.destination ) {
35
+ destination = msg?.payload?.destination
36
+ } else if( !!config.destination ) {
37
+ if( node.destinationType === 'str' ) {
38
+ destination = config.destination
39
+ } else if( node.destinationType === 'msg' ) {
40
+ destination = msg[ config.destination ]
41
+ } else if( node.destinationType === 'flow' ) {
42
+ destination = node.context().flow.get( config.destination )
43
+ } else if( node.destinationType === 'global' ) {
44
+ destination = node.context().global.get( config.destination )
45
+ }
46
+ }
47
+
48
+ const response = await ollama.copy( {
49
+ source,
50
+ destination
51
+ } )
22
52
  .catch( error => {
23
53
  node.error( error )
24
54
  } )