n8n-nodes-lenz 0.4.1 → 0.5.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.
package/README.md CHANGED
@@ -27,9 +27,9 @@ Operations are grouped under a **Resource** picker. (Nodes added before v0.1.10
27
27
 
28
28
  | Operation | What it does |
29
29
  |---|---|
30
- | **Verify (Deep)** *(default)* | Full multi-model pipeline (research → debate → adjudication), ~90 seconds. Returns a verdict, confidence, `lenz_score` (1-10), `key_finding`, sourced citations, and an executive summary. Reserve for high-stakes claims that need a thorough, cited answer. **Depth** buys breadth: *Low* searches fewer sources and skips the recovery fetch tiers, for **5 credits instead of 10**. |
30
+ | **Verify (Deep)** *(default)* | Full multi-model pipeline (research → debate → adjudication), ~90 seconds. Returns a verdict, confidence, `lenz_score` (1-10), `key_finding`, sourced citations, and an executive summary. Reserve for high-stakes claims that need a thorough, cited answer. **Depth** trades work for price: *Low* searches fewer sources, skips the recovery fetch tiers and stops the debate after the opening arguments, for **5 credits instead of 10**. |
31
31
  | **Assess (Fast)** | A quick 3-model panel verdict, ~10 seconds, one entry per claim identified in the input text. Good default for lower-stakes checks. |
32
- | **Extract Claims** | Free — pulls the verifiable factual claims out of a block of text without checking them. Useful as a first step before running Assess or Verify on each claim individually. **Focus** narrows the result to the claims you describe (300 characters, no extra cost); when none of them match, `status` comes back as `no_match` with an empty list rather than the unfocused claims. |
32
+ | **Extract Claims** | Free — pulls the verifiable factual claims out of a block of text without checking them. Useful as a first step before running Assess or Verify on each claim individually. **Focus** narrows the result to the claims you describe (300 characters, no extra cost); when none of them match, `status` comes back as `no_match` with an empty list rather than the unfocused claims. **Text** can also be a single public web page URL: Lenz reads the page, or a YouTube video's transcript, and extracts the claims from its first 50,000 characters. Pages behind a login (Facebook, Instagram, Threads, LinkedIn) can't be read, and a URL call typically takes 5-40 seconds. |
33
33
 
34
34
  ### Verification — manage submitted and stored work
35
35
 
@@ -55,19 +55,21 @@ Operations are grouped under a **Resource** picker. (Nodes added before v0.1.10
55
55
 
56
56
  | Operation | What it does |
57
57
  |---|---|
58
- | **Get Usage** | Returns your credit balance and the per-endpoint price list (`costs`), plus the same balance projected into each capability (`assess` / `verify` / `ask`), the `extract` daily cap, your current plan, and when credits reset. Prices that depend on a request parameter are under `cost_options` instead, nested capability → parameter → value — today `cost_options.verify.depth.low` is 5 against a default of 10. That same 5 also appears in `costs` under `verify_low` — a price, not a capability, so there is no `verify_low` block holding a balance of its own; divide `credits.remaining` by it to size how many low-depth checks you can afford. |
58
+ | **Get Usage** | Returns your credit balance and the per-endpoint price list (`costs`), plus the same balance projected into each capability (`assess` / `verify` / `ask`), the `extract` daily cap, your current plan, and when credits reset. Prices that depend on a request parameter are under `cost_options` instead, nested capability → parameter → value — today `cost_options.verify.depth.low` is 5 against a default of 10. That is the only place it appears: it is a price rather than a capability, so it has no balance block of its own, and `costs` carries just the four capability keys (`verify`, `assess`, `ask`, `extract`). To size how many low-depth checks you can afford, divide `credits.remaining` by `cost_options.verify.depth.low`. If that block is missing, `costs.verify` is a safe fallback but it is the *standard* price, so it understates the answer by half. |
59
59
 
60
- The per-capability blocks (`verify` / `ask` / `assess`) and each block's `credits` alias are **deprecated, and the API removes them on 2026-11-29**. They are projections of the one balance, not separate allowances, so derive them instead and branch on the balance itself: `remaining = credits.remaining // costs[capability]`. An **IF** node reading `{{ $json.verify.remaining }}` today will stop resolving on that date.
60
+ The per-capability blocks (`verify` / `ask` / `assess`) and each block's `credits` alias are **deprecated, and the API removes them on 2026-11-29**. They are projections of the one balance, not separate allowances, so derive them instead and branch on the balance itself: `Math.floor(credits.remaining / costs[capability])`. Note the single slash — in an n8n expression `//` starts a comment, so the older form silently returned the raw balance. Only divide for a capability that costs credits: `costs.extract` is `0`. An **IF** node reading `{{ $json.verify.remaining }}` today will stop resolving on that date.
61
61
 
62
62
  Every claim-checking operation returns a branch-ready `passed` boolean (derived from the verdict) alongside the raw verdict/confidence/citations, so you can wire an **IF** node directly off the result — e.g. route failed claims to human review.
63
63
 
64
- Verify and Get also expose an **Include Audit Trail** toggle, which adds the adjudication reasoning, debate transcript, per-panelist assessments, and panel agreement under `audit`. It's off by default because it's a lot of data per item.
64
+ Verify and Get also expose an **Include Audit Trail** toggle, which adds the adjudication reasoning, debate transcript, per-panelist assessments, and panel agreement under `audit`. It's off by default because it's a lot of data per item. At Low depth the debate transcript carries no rebuttals, because that round does not run.
65
65
 
66
66
  ### Retry safety
67
67
 
68
- Billable calls (Verify, Assess, Extract, Submit Batch, Select Claims) send an `Idempotency-Key` derived from the execution ID, node name, item index, and a fingerprint of the request body. If n8n retries the node — via **Retry On Fail**, or after a dropped response — the input is identical, so Lenz replays the original response instead of charging you a second time. A fresh run of the workflow is a new execution, so it bills normally.
68
+ Billable calls (Verify, Assess, Extract, Submit Batch, Select Claims, Ask Follow-Up) send an `Idempotency-Key` derived from the execution ID, a hash of the node's identity, the item index, and a fingerprint of the request — its path as well as its body. If n8n retries the node — via **Retry On Fail**, or after a dropped response — the input is identical, so Lenz replays the original response instead of charging you a second time. A fresh run of the workflow is a new execution, so it bills normally.
69
69
 
70
- Including the body in the key is what makes repeated runs safe: **Loop Over Items** and **AI Agent** tool calls both execute the node several times within a single execution, each time restarting the item index at 0, so a position-only key would send one key with different inputs and the API would reject it.
70
+ On **Ask Follow-Up** the key also keeps the conversation clean: an unkeyed retry asks the question again, so the question and a second answer are appended to the history that **Get Ask History** returns and that the next follow-up reads as context. A retry that arrives while the first question is still being answered gets a `409` — there is no answer yet to replay.
71
+
72
+ Including the request in the key is what makes repeated runs safe: **Loop Over Items** and **AI Agent** tool calls both execute the node several times within a single execution, each time restarting the item index at 0, so a position-only key would send one key with different inputs and the API would reject it. The path is part of it because **Ask Follow-Up** and **Select Claims** name what they act on in the URL rather than in the body — asking one fixed question of several verifications, the wiring suggested below, is otherwise indistinguishable from a retry of the first.
71
73
 
72
74
  Note that **Assess bills per claim found in the text**, not per request: a paragraph containing five claims spends five assess units.
73
75
 
@@ -85,6 +87,8 @@ Built against `n8n-workflow` (n8n API version 1) and tested against n8n v2.30.4.
85
87
  ## Usage
86
88
 
87
89
  - **Verify (Deep) takes ~90 seconds** — it's the full multi-model pipeline, not an instant call. The node blocks/polls until the result is ready, so no separate polling setup is needed on your end.
90
+ - **If it outlasts Max Wait (Seconds)**, the node returns `status: "timeout"` with the `task_id` and `passed: null` instead of a verdict. Nothing is lost — giving up does not cancel anything, the verification keeps running server-side, and the credits were spent at submit either way. Collect it later with **Get Verify Status**, or raise **Max Wait (Seconds)** (default 120). A poll that fails with a 5xx, a 429 or a 408/425 is retried for as long as the window lasts rather than failing the item; a 429 waits the reopening time the API states instead of returning on the node's own cadence. A failure carrying no HTTP status at all — a DNS or TLS problem, say — is different: it could equally be a bug or a bad credential, so it gets two retries and is then surfaced as the error it is. If the window ends on a failed poll, the `timeout` result names it in `last_error` and `last_error_status`.
91
+ - **Low depth drops a debate round, not just sources.** As well as searching fewer sources and skipping the recovery fetch tiers, *Low* stops the debate after the opening arguments — so with **Include Audit Trail** on, `audit.debate_pro.rebuttal` and `audit.debate_con.rebuttal` come back as empty strings. Every step that does run uses the same models, so Low is a volume lever rather than a model downgrade — but it is one round of argument fewer, not merely a narrower search.
88
92
  - **You are charged for the Depth you asked for, not the one you got.** A *Low* request that Lenz can answer from an existing *standard* verdict still costs 5 — and the `depth` field on the result reads `standard`, because it describes the evidence behind the verdict rather than the request. Seeing `standard` come back from a *Low* request is correct, not a bug.
89
93
  - To feed data from a previous node instead of a fixed value, toggle a field to **Expression** and reference it, e.g. `{{ $json.output }}`.
90
94
  - For **Ask Follow-Up**, keep the Question field as a fixed, generic string (e.g. `"What are the main sources supporting this verdict?"`) and only make the Verification ID dynamic via expression — that way the same follow-up question works for whatever claim was just verified.
@@ -105,8 +109,11 @@ A simple "fact-check gate" pattern — verify an LLM's output before acting on i
105
109
 
106
110
  1. Add an **LLM node** (or any node producing text) upstream.
107
111
  2. Add the **Lenz node**, set Operation to **Verify (Deep)**, and set the Claim field to an expression referencing the upstream output, e.g. `{{ $json.text }}`.
108
- 3. Add an **IF node** after Lenz with the condition `{{ $json.passed }}` **is true**.
109
- 4. Wire the true branch to continue the workflow normally, and the false branch to whatever your "needs review" path is (Slack alert, email, a manual-approval step, etc.).
112
+ 3. Add an **IF node** after Lenz with the condition `{{ $json.status }}` **equals** `completed`. Send the false branch wherever unfinished work should go — a timeout, a `failed` pipeline or a `needs_input` interrupt is not a verdict, and its `passed` is `null`.
113
+ 4. After that, add a second **IF node** with the condition `{{ $json.passed }}` **is true**.
114
+ 5. Wire its true branch to continue the workflow normally, and its false branch to whatever your "needs review" path is (Slack alert, email, a manual-approval step, etc.).
115
+
116
+ > **Check `status` before `passed`.** `passed` only means something once a verdict exists. A timeout, a `failed` pipeline or a `needs_input` interrupt has no verdict, and `passed` is `null` or absent — which an IF reads as false. Branching straight on `passed` therefore reports "this claim did not pass" for a claim nobody ever checked, and a provider outage becomes a debunking.
110
117
 
111
118
  For a lighter check on lower-stakes content, swap the Lenz operation to **Assess (Fast)** instead — same wiring, ~10s instead of ~90s.
112
119
 
@@ -209,12 +216,22 @@ So the Wait node has a number to read, the error output carries the refusal as f
209
216
 
210
217
  * **0.3.0** — Lenz replaced its six per-endpoint quotas with **one credit pool** per account, and the node now speaks it. Out-of-credits messages quote the two new fields on the 402 body — `cost` (what this call needed) beside `credits_remaining` (what you hold) — which is the difference between "you have 4 credits and this costs 10", one top-up away, and "you have nothing", a plan decision. **Get Usage** returns the balance and the live price list (`costs`) alongside the per-capability numbers, which are now projections of that one balance rather than separate allowances: spending on `assess` reduces what is left for `verify`, and those blocks are deprecated — the API removes them on 2026-11-29. `/extract` still costs nothing and keeps its own daily fair-use cap, which rejects 429, not 402. The error output carries both numbers as fields — `cost` and `credits_remaining` beside the existing `retry_after` / `code` / `status_code` — so an IF node can tell a shortfall from an empty balance without parsing the message. **Get Usage**'s description no longer says "quota" or "for the current API key": the pool is per account, shared across your keys, the same correction the extract cap description already carries. No breaking change to any node output: every field the node emitted before is still emitted.
211
218
 
212
- * **0.4.0** — Exposes the two API parameters the node could not reach. **Verify (Deep)** and **Submit Batch** gain **Depth**: *Low* searches fewer sources and skips the recovery fetch tiers for **half the credits** (5 against 10), running the same models at every step — it buys less research, not weaker reasoning. In a batch each claim can override the batch-wide default, so one submission can mix 5- and 10-credit checks. A completed verification now reports the `depth` its verdict was actually produced with, which is not always the one requested: a *Low* request that Lenz answers from an existing standard verdict is charged 5 and reads back `standard`. The echo describes the evidence, the charge follows the request, and without the field there was no way to tell the two apart. **Extract Claims** gains **Focus**, a free-text hint (300 characters, no extra cost) that narrows the result to the claims you describe. It only selects from what the extractor already found — it cannot add, reword or reorder claims. When nothing matches, the API answers `status: "no_match"` with an empty list rather than substituting the unfocused claims, and the node names that case in a `message` so an empty result is not mistaken for "nothing here". An over-long focus is refused before the request is sent, measured the way the API measures it — after collapsing whitespace — and never silently truncated, since a shortened focus returns a subset with nothing to show it happened. `costs` on **Get Usage** now also carries `verify_low`; it is a price rather than a capability, so there is no matching balance block to read. One UI consequence: adding Depth makes the per-claim batch row a five-field collection, which the n8n linter alphabetizes, so those fields render in a new order. (0.4.1 relabelled that row’s **Text** field to **Claim**, which sorts first again, so the required field is back at the top.) No output field was removed or renamed. Separately, the **Verify (Deep)** description no longer advertises a fixed "8-model" pipeline. The count has drifted once already — it read "7-model" until a docs sweep corrected it — so the copy now says "multi-model" and leaves the stage names (research, debate, adjudication) to carry the specificity they were always the ones carrying.
219
+ * **0.4.0** — Exposes the two API parameters the node could not reach. **Verify (Deep)** and **Submit Batch** gain **Depth**: *Low* searches fewer sources, skips the recovery fetch tiers and stops the debate after the opening arguments for **half the credits** (5 against 10), running the same models at every step it runs — a volume lever, not a model downgrade. In a batch each claim can override the batch-wide default, so one submission can mix 5- and 10-credit checks. A completed verification now reports the `depth` its verdict was actually produced with, which is not always the one requested: a *Low* request that Lenz answers from an existing standard verdict is charged 5 and reads back `standard`. The echo describes the evidence, the charge follows the request, and without the field there was no way to tell the two apart. **Extract Claims** gains **Focus**, a free-text hint (300 characters, no extra cost) that narrows the result to the claims you describe. It only selects from what the extractor already found — it cannot add, reword or reorder claims. When nothing matches, the API answers `status: "no_match"` with an empty list rather than substituting the unfocused claims, and the node names that case in a `message` so an empty result is not mistaken for "nothing here". An over-long focus is refused before the request is sent, measured the way the API measures it — after collapsing whitespace — and never silently truncated, since a shortened focus returns a subset with nothing to show it happened. The low-depth price is readable on **Get Usage** at `cost_options.verify.depth.low`; it is a price rather than a capability, so it has no balance block of its own and never appears in `costs`. One UI consequence: adding Depth makes the per-claim batch row a five-field collection, which the n8n linter alphabetizes, so those fields render in a new order. (0.4.1 relabelled that row’s **Text** field to **Claim**, which sorts first again, so the required field is back at the top.) No output field was removed or renamed. *(Two claims in this entry were wrong — the low-depth price location and what Low actually skips. The wording above is the corrected text; see 0.4.2.)* Separately, the **Verify (Deep)** description no longer advertises a fixed "8-model" pipeline. The count has drifted once already — it read "7-model" until a docs sweep corrected it — so the copy now says "multi-model" and leaves the stage names (research, debate, adjudication) to carry the specificity they were always the ones carrying.
213
220
 
214
221
  **0.4.0 has no npm release.** The version bump reached `main` but was never tagged, so nothing published it; everything described above shipped inside 0.4.1. npm goes 0.3.0 → 0.4.1.
215
222
 
216
223
  * **0.4.1** — The input field on **Assess (Fast)** and on each **Verify (Batch)** item is now labelled **Claim** instead of **Text**, matching the API's vocabulary: a document is `text` (**Extract Claims** keeps that label), a claim is `claim`. Labels only: the parameter keys saved in your workflows are unchanged, and so is every request the node sends.
217
224
 
225
+ * **0.4.2** — Two corrections to what 0.4.x said about **Depth**, both of them wrong rather than merely unclear. The low-depth price was documented as appearing in `costs` under `verify_low`, with the advice to divide `credits.remaining` by it: that key does not exist — the API carries the four capability keys in `costs` and nothing else — so the suggested expression evaluated to `NaN`. The price is readable only at `cost_options.verify.depth.low`, which is where the docs now point. Separately, *Low* was described as searching fewer sources and skipping the recovery fetch tiers; it also stops the debate after the opening arguments, so a Low verdict has no rebuttal round and comes back with an empty `rebuttal` on its debate entries under **Include Audit Trail**. Every description of Depth in the node and the README now says so.
226
+
227
+ * **0.5.0** — **Verify (Deep) no longer loses a verification you have already paid for.** Credits are debited the moment a submit is accepted, and the poll loop that waits for the verdict had no tolerance for a failed poll: one 502 or network blip on any of roughly fifteen polls threw straight out of the operation and took the `task_id` with it, leaving a verification that was charged for, still running server-side, and unreachable — not even **Get Verify Status** could fetch it back. Transient poll failures (5xx, 429, and the 408/425 an intervening proxy produces) are now retried for as long as the wait lasts, and a 429 waits the reopening time the API states rather than returning on the node's own cadence, which is what tripped the limiter to begin with. A failure carrying no HTTP status is treated differently on purpose — a DNS or TLS problem is indistinguishable from a bug or a bad credential, so it gets a small bounded number of attempts and is then surfaced as the error it is, rather than retried for the window and reported as a timeout, which would invent a fact. The `task_id` now rides every post-submit failure, on the error output and on the thrown error alike. The hardcoded 120-second deadline becomes **Max Wait (Seconds)**, validated and clamped before the claim is submitted — validated afterwards, a non-numeric expression got the claim charged and then threw an error with nowhere to carry the task id, which is the same bug by another route. Non-verdict terminals now carry `passed: null` so the key is visible in n8n's output schema; be aware that null is still falsy, so an **IF** node reading `{{ $json.passed }}` alone routes a timeout down the false arm exactly as before. Checking `status` first is the actual fix, and the README's gate pattern now leads with it — a provider outage reading as a debunking was the real bug. A timeout that ends on a failed poll names it in `last_error` and `last_error_status`, instead of reporting success-shaped JSON that implies the task was seen running. Alongside that, four correctness fixes a first external user would hit: **Get Many** duplicated and skipped rows above Limit 100 (#21), **Select Claims** collided two paused tasks that offered the same claim text onto one Idempotency-Key (#22), validation failures surfaced as API errors with no HTTP code (#23), and **Ask Follow-Up** sent no Idempotency-Key, so a retry appended a second question and answer to the stored conversation. No output field was removed or renamed.
228
+
229
+ It also carries two fixes that are behaviour, not wording. The `Idempotency-Key` was built from the node's **display name**, which is user-editable free text. Node refuses to send a header value containing anything above U+00FF, so a node named in Cyrillic, Greek, Hebrew, Arabic or any CJK script — or carrying an emoji — failed **every billable call** before the request left the machine, with an error naming the header rather than the node. Latin-1 names such as `Prüfung` and `Vérification` were never affected, despite what an earlier draft of this entry said. The identity is now *hashed* into the key rather than written into it, which makes the guarantee structural rather than an assumption about what the id contains; the node id is preferred, so a rename mid-execution no longer changes the key, and the name never reaches the wire.
230
+
231
+ Separately, a `sources` value that was not a list — or a list containing `null` — threw while mapping the result, failing a verification that had already completed and been charged for. A `null` entry now costs that one citation; a `sources` that is not a list returns none at all, which is worth knowing if you branch on `citations.length`. No output field was removed or renamed. Two things changed on the wire: the `User-Agent` version, and the `Idempotency-Key` format.
232
+
233
+ This release also adds a `pre-push` git hook that refuses to push agent commits which have not been reviewed. It only affects contributors, needs `git config core.hooksPath .githooks` once per clone, and never gates a human pushing by hand — see AGENTS.md.
234
+
218
235
  ## Maintainer
219
236
 
220
237
  [@David19782](https://github.com/David19782)