Node-RED plugin
Add AI to your flows. A set of Node-RED nodes that wrap ollama.js, so you can chat, generate, and embed with any model — local Ollama or Ollama Cloud — without hand-rolling the client calls yourself.

What it is
Instead of calling the Ollama API from a Function node, drop in a config node for your server and wire up dedicated nodes for chat, generation, embeddings and model management. Every node mirrors an ollama.js client call, with per-call parameters resolvable from either the node's own configuration or an incoming msg.payload field.
Works with a local Ollama install, or with Ollama Cloud using an API key — same nodes, same flows.
Requirements
Installation
node-red-contrib-ollamaFrom your Node-RED user directory:
npm install node-red-contrib-ollamaRestart Node-RED to load the new nodes.
Configuration
Every action node connects through a shared Server config node.
Host: localhost (or your server's address)
Port: 11434
Check "Use Ollama Cloud" and paste an API key — host/port are set automatically.
Nodes
Connection to a local Ollama instance or Ollama Cloud (host, port, API key).
Reusable generation options (temperature, context size, and more).
Reusable response format / JSON schema definition.
Reusable tool/function definitions for tool-calling models.
Generate the next message in a chat with a provided model.
Generate a response for a given prompt with a provided model.
Generate embeddings from a model.
List models that are available.
Show details, template, parameters, license and system prompt of a model.
List models that are currently loaded into memory.
Download a model from the Ollama library.
Upload a model to a model library.
Create a model from a Modelfile.
Copy a model to a new name.
Delete a model and its data.
Abort all streamed generations currently running.
Examples
Node-RED wires the logic — these nodes replace the ollama.js client calls you'd otherwise write by hand.
A webhook keeps a running conversation per visitor in flow context: build the message history, get a reply from Ollama, remember it for next time, and answer back over HTTP.
[
{
"id": "tab1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "srv1",
"type": "ollama-config-server",
"name": "Local Ollama",
"host": "localhost",
"port": 11434,
"useCloud": false
},
{
"id": "n1",
"type": "http in",
"name": "POST /chat",
"url": "/chat",
"method": "post",
"upload": false,
"swaggerDoc": "",
"x": 90,
"y": 110,
"wires": [
[
"n2"
]
],
"z": "tab1"
},
{
"id": "n2",
"type": "function",
"name": "Build messages",
"func": "const sessionId = (msg.req && msg.req.headers['x-session-id']) || 'default';\nconst history = flow.get('chat_' + sessionId) || [];\nconst userText = (msg.req && msg.req.body && msg.req.body.message) || '';\nmsg.sessionId = sessionId;\nmsg.userText = userText;\nmsg.payload = {\n model: 'llama3.2',\n messages: history.concat([{ role: 'user', content: userText }])\n};\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 300,
"y": 110,
"wires": [
[
"n3"
]
],
"z": "tab1"
},
{
"id": "n3",
"type": "ollama-chat",
"name": "Chat",
"server": "srv1",
"x": 510,
"y": 110,
"wires": [
[
"n4"
]
],
"z": "tab1"
},
{
"id": "n4",
"type": "function",
"name": "Save history",
"func": "const reply = msg.payload.message;\nconst history = flow.get('chat_' + msg.sessionId) || [];\nhistory.push({ role: 'user', content: msg.userText });\nhistory.push(reply);\nflow.set('chat_' + msg.sessionId, history);\nmsg.payload = { reply: reply.content };\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 720,
"y": 110,
"wires": [
[
"n5"
]
],
"z": "tab1"
},
{
"id": "n5",
"type": "http response",
"name": "Reply",
"statusCode": "",
"headers": {},
"x": 930,
"y": 110,
"wires": [],
"z": "tab1"
}
]Incoming support tickets are acknowledged immediately, then a prompt built from the ticket's subject and body asks Ollama for a one-sentence triage summary — replies flagged urgent get routed to Slack, everything else is just logged.
[
{
"id": "tab1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "srv2",
"type": "ollama-config-server",
"name": "Local Ollama",
"host": "localhost",
"port": 11434,
"useCloud": false
},
{
"id": "n1",
"type": "http in",
"name": "POST /tickets",
"url": "/tickets",
"method": "post",
"upload": false,
"swaggerDoc": "",
"x": 90,
"y": 130,
"wires": [
[
"n2",
"n8"
]
],
"z": "tab1"
},
{
"id": "n8",
"type": "function",
"name": "Acknowledge",
"func": "msg.payload = { status: 'queued' };\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 300,
"y": 210,
"wires": [
[
"n9"
]
],
"z": "tab1"
},
{
"id": "n9",
"type": "http response",
"name": "Accepted",
"statusCode": "202",
"headers": {},
"x": 510,
"y": 210,
"wires": [],
"z": "tab1"
},
{
"id": "n2",
"type": "function",
"name": "Build prompt",
"func": "const ticket = (msg.req && msg.req.body) || {};\nconst subject = ticket.subject || '(no subject)';\nconst body = ticket.body || '';\nmsg.payload = {\n model: 'llama3.2',\n prompt: 'You triage support tickets. Reply with a one-sentence summary, and include the ' +\n 'word URGENT if this needs immediate attention.\\n\\nSubject: ' + subject + '\\nBody: ' + body\n};\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 300,
"y": 130,
"wires": [
[
"n3"
]
],
"z": "tab1"
},
{
"id": "n3",
"type": "ollama-generate",
"name": "Generate",
"server": "srv2",
"x": 510,
"y": 130,
"wires": [
[
"n4"
]
],
"z": "tab1"
},
{
"id": "n4",
"type": "switch",
"name": "Urgent?",
"property": "payload.response",
"propertyType": "msg",
"rules": [
{
"t": "cont",
"v": "URGENT",
"vt": "str"
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 720,
"y": 130,
"wires": [
[
"n5"
],
[
"n7"
]
],
"z": "tab1"
},
{
"id": "n5",
"type": "function",
"name": "Format Slack message",
"func": "msg.payload = { text: '🚨 ' + msg.payload.response };\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 930,
"y": 70,
"wires": [
[
"n6"
]
],
"z": "tab1"
},
{
"id": "n6",
"type": "http request",
"name": "Notify Slack",
"method": "POST",
"ret": "txt",
"paytoqs": "ignore",
"url": "https://hooks.slack.com/services/REPLACE/ME",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 1140,
"y": 70,
"wires": [
[]
],
"z": "tab1"
},
{
"id": "n7",
"type": "debug",
"name": "Log ticket",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 930,
"y": 195,
"wires": [],
"z": "tab1"
}
]Read a file's contents, embed them, and append the vector to a flat-file store, ready for semantic search later — swap the manual trigger for a file-watch node to run it automatically.
[
{
"id": "tab1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "srv3",
"type": "ollama-config-server",
"name": "Local Ollama",
"host": "localhost",
"port": 11434,
"useCloud": false
},
{
"id": "n1",
"type": "inject",
"name": "New file",
"props": [
{
"p": "payload"
},
{
"p": "filename",
"v": "/tmp/example.txt",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 90,
"y": 110,
"wires": [
[
"n2"
]
],
"z": "tab1"
},
{
"id": "n2",
"type": "file in",
"name": "Read file",
"filename": "filename",
"filenameType": "msg",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"allProps": false,
"x": 300,
"y": 110,
"wires": [
[
"n3"
]
],
"z": "tab1"
},
{
"id": "n3",
"type": "function",
"name": "Build embed request",
"func": "msg.text = msg.payload;\nmsg.payload = { model: 'all-minilm', input: msg.payload };\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 510,
"y": 110,
"wires": [
[
"n4"
]
],
"z": "tab1"
},
{
"id": "n4",
"type": "ollama-embed",
"name": "Embed",
"server": "srv3",
"x": 720,
"y": 110,
"wires": [
[
"n5"
]
],
"z": "tab1"
},
{
"id": "n5",
"type": "function",
"name": "Build vector record",
"func": "msg.payload = JSON.stringify({\n id: Date.now(),\n text: msg.text,\n embedding: msg.payload.embeddings[0]\n});\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 930,
"y": 110,
"wires": [
[
"n6"
]
],
"z": "tab1"
},
{
"id": "n6",
"type": "file",
"name": "Append to store",
"filename": "/tmp/vector-store.jsonl",
"filenameType": "str",
"appendNewline": true,
"createDir": true,
"overwriteFile": "false",
"encoding": "none",
"x": 1140,
"y": 110,
"wires": [],
"z": "tab1"
}
]Support the project
This is a free, open-source project maintained in spare time. If it's useful to you, a star or a sponsorship goes a long way.