Skip to content

feat(workflows): make the LLM summarizer template real - #2497

Open
Hoang130203 wants to merge 1 commit into
Osmantic:mainfrom
Hoang130203:feat/n8n-summarizer-workflow
Open

feat(workflows): make the LLM summarizer template real#2497
Hoang130203 wants to merge 1 commit into
Osmantic:mainfrom
Hoang130203:feat/n8n-summarizer-workflow

Conversation

@Hoang130203

Copy link
Copy Markdown

Summary

config/n8n/llm-summarizer.json was a placeholder — a manualTrigger, a
sticky note reading "This is a template workflow… Customize the nodes below to
match your setup"
, and "connections": {}. The catalog card advertises
"Summarize long text with your local LLM" with "setupTime": "1 minute";
importing it gave you an empty canvas.

This makes it a working workflow, per ods/CONTRIBUTING.md's ask for
"Workflow templates — pre-built n8n workflows that solve actual problems
people have."

Webhook POST /webhook/ods-summarize
  -> Read Request   (clamp text, validate style, clamp max_words)
  -> Text Present?  (IF)
       true  -> llama-server /v1/chat/completions -> Return Summary (200 JSON)
       false -> Return 400
curl -X POST http://localhost:5678/webhook/ods-summarize \
  -H 'Content-Type: application/json' \
  -d '{"text": "<a long article>", "style": "bullets", "max_words": 120}'

Choices worth reviewing

  • Input is clamped, not trusted. text is cut to 24000 characters before
    the call so a large paste cannot overrun the context window and fail the
    whole request; the response carries truncated: true so the caller knows.
    max_words is clamped to 20–800 and style is validated against
    bullets / paragraph / tldr, so a bad value degrades to the default
    instead of being interpolated into the prompt verbatim.
  • The system prompt is defensive about the truncation — it tells the model
    to summarize only what it was given and not to add facts.
  • Missing text returns 400 naming the field, rather than sending an empty
    prompt to the model and returning whatever comes back.
  • Calls http://llama-server:8080 — the manifest's in-network port, not the
    published external_port_default, so the request stays on ods-network.

AI Assistance

AI assisted with drafting the node graph, the prompt wording, and this
description. I chose the endpoint and clamps against the service manifests and
validated the file against the real node package before pushing.

Release Lane

  • Stable hotfix targeting release/2.6.x
  • Mainline change targeting main
  • Next-minor work targeting the next feature/minor release
  • Not sure; reviewer should help classify

Stable hotfix reason:

n/a

Changed Surface

  • Docs only
  • Tests only
  • Dashboard UI
  • Dashboard API / host agent
  • Installer / bootstrap / lifecycle
  • Docker Compose / service manifests
  • Model routing / Hermes / capabilities
  • Network exposure / auth / proxy
  • Dependencies / runtime wiring

(One JSON file under config/n8n/. It is an import payload for n8n; no ODS
code executes it. The catalog entry — id, file, name, description, category,
dependencies — is unchanged.)

Risk And Validation

  • Risk level: Low
  • Validation run:
    • git diff --check
    • Markdown/link sanity for docs
    • Focused tests listed below
    • Dashboard lint/test/build
    • Extension audit / compose validation
    • Release-grade fleet or scoped hardware validation
    • Stable-lane patch validation, if targeting release/2.6.x

Commands/results:

# Validated against the exact node package ODS ships. compose.yaml pins
# n8nio/n8n:2.6.4; `npm view n8n@2.6.4 dependencies.n8n-nodes-base` -> 2.6.2.

$ node verify.js llm-summarizer.json
n8n-nodes-base version: 2.6.2
node types loaded: 417
  checked llm-summarizer.json: 7 nodes

ALL WORKFLOWS VALID

# The harness loads every node class out of n8n-nodes-base and asserts, per node:
#   - the `type` string resolves to a real node
#   - `typeVersion` is one the node declares
#   - every parameter key set is a property the node actually declares
#   - node names are unique
#   - every connection source and target names a node in the file

Caveat: Docker is not running on my dev host, so I could not stand up
n8n + llama-server and fire a real request end to end. The validation above is
static against the real node definitions — it proves the file imports and that
no node carries an invented parameter or version, but not that the response
flows through the expressions at runtime. Return Summary reads
$json.choices[0].message.content, the standard OpenAI-compatible chat shape
llama.cpp's server returns and the same path scripts/ods-test-functional.sh
asserts on. Happy to get a live run on a machine with Docker if you want one
before merging.

Operational Change Check

An import payload for n8n. Nothing in the installer, compose stack, ods-cli,
or dashboard-api executes it — dashboard-api reads only catalog.json for the
Workflows listing, and the file is handed to n8n's import API when a user
clicks install. No existing install changes until a user imports it.

  • This is not an operational change.
  • This is an operational change and validation is recorded above.
  • This is an operational change and validation is intentionally deferred for:

Notes For Reviewers

Same two open questions as #2496, repeated here so this PR stands alone:

  • Model id. The request sends "model": "local-model". llama.cpp ignores
    it and serves whatever it was started with. Reading the configured
    LLM_MODEL instead would need n8n to have it in its environment, which
    compose.yaml does not pass today — I would rather send that as its own
    change than smuggle an env addition into a workflow PR.
  • Webhook path. ods-summarize. No other catalog workflow claims it. Tell
    me if there is a naming convention for ODS-shipped webhooks.

The 24000-character clamp is a guess at a safe default — it is comfortably
inside a 32K-context tier but wasteful on a 128K one. If you would rather it
scale with MAX_CONTEXT, that needs the same env plumbing as the model id.

config/n8n/llm-summarizer.json was a manualTrigger plus a sticky note
saying "Customize the nodes below to match your setup", with
"connections": {} — an empty canvas behind a catalog card advertising
"Summarize long text with your local LLM".

Now: POST /webhook/ods-summarize with {text, style?, max_words?} ->
llama-server /v1/chat/completions -> JSON summary.

Three things the node graph handles rather than leaving to the caller:

- `text` is clamped to 24000 characters before the call, so a large paste
  cannot overrun the context window and fail the whole request. The
  response reports `truncated` so the caller knows it happened.
- `style` is validated against bullets/paragraph/tldr and `max_words` is
  clamped to 20..800, so a bad value degrades to the default instead of
  going into the prompt verbatim.
- A missing `text` returns 400 naming the field, instead of sending an
  empty prompt to the model.

The system prompt tells the model not to add facts and to summarize only
what it was given, which matters given the truncation above.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant