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