Files
promptfoo--promptfoo/site/docs/integrations/n8n.md
T
wehub-resource-sync 0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

226 lines
9.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
sidebar_label: n8n
title: Using Promptfoo in n8n Workflows
description: Learn how to integrate Promptfoo's LLM evaluation into your n8n workflows for automated testing, security and quality gates, and result sharing
---
# Using Promptfoo in n8n Workflows
This guide shows how to run Promptfoo evaluations from an **n8n** workflow so you can:
- schedule nightly or adhoc LLM tests,
- gate downstream steps (Slack/Teams alerts, merge approvals,etc.) on passrates, and
- publish rich results links generated by Promptfoo.
## Prerequisites
| What | Why |
| ------------------------------------------------------------------------------------ | ----------------------------------------------------- |
| **Selfhosted n8n ≥ v1** (Docker or baremetal) | Gives access to the “ExecuteCommand” node. |
| **Promptfoo CLI** available in the container/host | Needed to run `promptfoo eval`. |
| (Optional) **LLM provider API keys** set as environment variables or n8n credentials | Example: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, … |
| (Optional) **Slack / email / GitHub nodes** in the same workflow | For notifications or comments once the eval finishes. |
### Shipping a custom Docker image (recommended)
The easiest way is to bake Promptfoo into your n8n image so every workflow run already has the CLI:
```dockerfile
# Dockerfile
FROM n8nio/n8n:latest # or a fixed tag
USER root # gain perms to install packages
RUN npm install -g promptfoo # installs CLI systemwide
ENV PROMPTFOO_RUNNING_IN_DOCKER=1
USER node # drop back to nonroot
```
Update **`dockercompose.yml`**:
```yaml
services:
n8n:
build: .
env_file: .env # where your OPENAI_API_KEY lives
volumes:
- ./data:/data # prompts & configs live here
```
If you prefer not to rebuild the image you _can_ install Promptfoo on the fly inside the
**ExecuteCommand** node, but that adds 1015s to every execution.
## Basic “Run & Alert” workflow
Below is the minimal pattern most teams start with:
| # | Node | Purpose |
| --- | ----------------------------- | ----------------------------------------------------------------- |
| 1 | **Trigger** (Cron or Webhook) | Decide _when_ to evaluate (nightly, on Git push webhook …). |
| 2 | **Execute Command** | Runs Promptfoo and emits raw stdout / stderr. |
| 3 | **Code / Set** node | Parses the resulting JSON, extracts pass/fail counts & shareURL. |
| 4 | **IF** node | Branches on “failures > 0”. |
| 5 | **Slack / Email / GitHub** | Sends alert or PR comment when the gate fails. |
### Execute Command node configuration
```sh
promptfoo eval \
-c /data/promptfooconfig.yaml \
--prompts "/data/prompts/**/*.json" \
--output /tmp/pf-results.json \
--share --fail-on-error
cat /tmp/pf-results.json
```
Set the working directory to `/data` (mount it with Docker volume) and set it to execute once (one run per trigger).
The node writes a machinereadable results file **and** prints it to stdout,
so the next node can simply `JSON.parse($json["stdout"])`.
:::info
The **ExecuteCommand** node that we rely on is only available in **selfhosted** n8n. n8nCloud does **not** expose it yet.
:::
### Sample “Parse & alert” snippet (Code node, TypeScript)
```ts
// Input: raw JSON string from previous node
const output = JSON.parse(items[0].json.stdout as string);
const { successes, failures } = output.results.stats;
items[0].json.passRate = successes / (successes + failures);
items[0].json.failures = failures;
items[0].json.shareUrl = output.shareableUrl;
return items;
```
An **IF** node can then route execution:
- **failures = 0** → take _green_ path (maybe just archive the results).
- **failures > 0** → post to Slack or comment on the pull request.
## Evaluating n8n AI Agent prompts and outputs
If your goal is to test the **prompt inside an n8n AI Agent / OpenAI node** (not just run Promptfoo from a workflow), treat the n8n node like any other app contract:
1. Put the agent prompt in a file,
2. Map incoming n8n fields to `tests.vars`, and
3. Assert on the exact JSON or tool-call shape that downstream n8n nodes expect.
This works well when you want to regression-test an agent before wiring it into a larger workflow.
### Validate JSON that downstream n8n nodes consume
If your agent is supposed to emit structured data for a **Set**, **Code**, **Switch**, or **HTTP Request** node, validate the payload directly.
```yaml title="promptfooconfig.yaml"
prompts:
- file://./prompts/n8n-support-router.txt
providers:
- openai:gpt-5-mini
tests:
- vars:
customer_message: 'Customer wants to cancel order #4815 and asks for a refund'
assert:
- type: contains-json
value:
type: object
required: [route, priority, reply]
properties:
route:
type: string
enum: [billing, support, sales]
priority:
type: string
enum: [low, medium, high]
reply:
type: string
```
Use `contains-json` when the model may wrap JSON in prose or a markdown code block. If your node must return **only** JSON, use [`is-json`](/docs/guides/evaluate-json) instead.
### Validate tool calls for agent workflows
If your n8n setup uses an OpenAI-compatible agent that should call tools before continuing, validate that Promptfoo sees a real tool call and that it matches your schema.
```yaml title="promptfooconfig.yaml"
prompts:
- file://./prompts/n8n-calendar-agent.txt
providers:
- id: openai:gpt-5-mini
config:
tools: file://./tools/calendar-tools.yaml
tests:
- vars:
user_request: "Move tomorrow's standup to 3pm and notify the team"
assert:
- type: finish-reason
value: tool_calls
- type: is-valid-openai-tools-call
```
That pattern is especially useful when your n8n workflow branches on whether the LLM produced a tool invocation versus a final answer.
### Useful building blocks
- [`/docs/configuration/tools`](/docs/configuration/tools) for defining tool schemas
- [`/docs/guides/evaluate-json`](/docs/guides/evaluate-json) for JSON and schema assertions
- [`examples/openai-tools-call`](https://github.com/promptfoo/promptfoo/tree/main/examples/openai-tools-call) for a concrete OpenAI tool-calling config
- [`examples/eval-tool-use`](https://github.com/promptfoo/promptfoo/tree/main/examples/eval-tool-use) for finish-reason and tool-use checks across providers
## Advanced patterns
### Run different configs in parallel
Make the first **ExecuteCommand** node loop over an array of model IDs or config
files and push each run as a separate item.
Downstream nodes will automatically fanout and handle each result independently.
### Versioncontrolled prompts
Mount your prompts directory and config file into the container at
`/data`. When you commit new prompts toGit, your CI/CD system can call the
**n8n REST API** or a **Webhook trigger** to reevaluate immediately.
### Autofail the whole workflow
If you run n8n **headless** via `n8n start --tunnel`, you can call this workflow
from CI pipelines (GitHub Actions, GitLab, …) with the [CLI `n8n execute`
command](https://docs.n8n.io/hosting/cli-commands/) and then check the HTTP
response code; returning `exit 1` from the ExecuteCommand node will propagate
the failure.
## Security & best practices
- **Keep API keys secret** store them in the n8n credential store or inject as
environment variables from Docker secrets, not hardcoded in workflows.
- **Resource usage** Promptfoo supports caching via
`PROMPTFOO_CACHE_PATH`; mount that directory to persist across runs.
- **Timeouts** wrap `promptfoo eval` with `timeout --signal=SIGKILL 15m …`
(Linux) if you need hard execution limits.
- **Logging** route the `stderr` field of ExecuteCommand to a dedicated log
channel so you dont miss stack traces.
## Troubleshooting
| Symptom | Likely cause / fix |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **`Execute Command node not available`** | Youre on n8nCloud; switch to selfhosted. |
| **`promptfoo: command not found`** | Promptfoo not installed inside the container. Rebuild your Docker image or add an install step. |
| **Run fails with `ENOENT` on config paths** | Make sure the prompts/config volume is mounted at the same path you reference in the command. |
| **Large evals timeout** | Increase the nodes “Timeout (s)” setting _or_ chunk your test cases and iterate inside the workflow. |
## Next steps
1. Combine Promptfoo with the **n8n AI Transform** node to chain evaluations
into multistep RAG pipelines.
2. Use **n8n Insights** (selfhosted EE) to monitor historical passrates and
surface regressions.
3. Check out the other [CI integrations](/docs/integrations/ci-cd) ([GitHubActions](/docs/integrations/github-action), [CircleCI](/docs/integrations/circle-ci), etc) for inspiration.
Happy automating!