The store now starts empty and agents are added/removed exclusively through the web admin page / admin API, persisted to AGENTS_FILE. No AGENTS env var needed in the sops secret.
95 lines
3.4 KiB
Markdown
95 lines
3.4 KiB
Markdown
# n8n-openai-adapter
|
|
|
|
An OpenAI-compatible HTTP adapter that exposes self-hosted **n8n chat agents**
|
|
behind a standard `/v1/chat/completions` API, so any OpenAI client (Cursor,
|
|
LibreChat, the `openai` SDK, a custom app) can talk to your n8n agents as if
|
|
they were OpenAI models.
|
|
|
|
n8n itself does **not** ship an inbound OpenAI-compatible endpoint (its "AI
|
|
Gateway" is an outbound proxy to n8n Cloud). This small Elixir service is the
|
|
bridge: one `/v1/chat/completions` endpoint, routed to whichever n8n agent you
|
|
name in the `model` field.
|
|
|
|
## How it works
|
|
|
|
```
|
|
Your OpenAI client
|
|
POST /v1/chat/completions {"model":"scholar-agent","thread_id":"abc","messages":[...]}
|
|
|
|
|
v
|
|
n8n-openai-adapter (Plug + Bandit)
|
|
- authorize (Bearer <ADAPTER_API_KEY>)
|
|
- look up "scholar-agent" -> n8n chat webhook URL (AgentRegistry GenServer)
|
|
- take the last user message
|
|
- forward to the n8n webhook {sessionId: thread_id, action: sendMessage, chatInput}
|
|
|
|
|
v
|
|
n8n agent (its MCP tools, memory, etc. run as usual)
|
|
|
|
|
v
|
|
returns OpenAI-shaped {"choices":[{"message":{"role":"assistant","content":...}}]}
|
|
```
|
|
|
|
Multiple agents = multiple `model` names, each mapped to a different n8n webhook
|
|
in the `AGENTS` env var.
|
|
|
|
## Configuration (env vars)
|
|
|
|
| Var | Required | Purpose |
|
|
|------------------|----------|---------------------------------------------------------------------|
|
|
| `ADAPTER_API_KEY`| yes | Bearer key that OpenAI clients send. |
|
|
| `ADMIN_API_KEY` | yes | Bearer key for the admin API / web admin page. |
|
|
| `AGENTS_FILE` | no | Path to the JSON store (default `/var/lib/n8n-openai/agents.json`). |
|
|
| `PORT` | no | HTTP port (default `8000`). |
|
|
| `CHAT_WEBHOOK_BASIC` | no | `"user:password"` if your n8n Chat Trigger is Basic-auth protected. |
|
|
|
|
Agents are **not** configured via env — they're managed at runtime through the
|
|
web admin page / admin API and persisted to `AGENTS_FILE`. The store starts
|
|
empty; add agents after boot.
|
|
|
|
## Admin API (manage agents at runtime)
|
|
|
|
Agents are persisted to `AGENTS_FILE` and can be added/removed without a
|
|
redeploy, using the `ADMIN_API_KEY`:
|
|
|
|
```bash
|
|
# list
|
|
curl -H "Authorization: Bearer $ADMIN_API_KEY" https://openai.bueso.eu/admin/agents
|
|
|
|
# add / update an agent
|
|
curl -X POST -H "Authorization: Bearer $ADMIN_API_KEY" -H "Content-Type: application/json" \
|
|
-d '{"model":"media-agent","webhook":"https://n8n.bueso.eu/webhook/<id>/chat"}' \
|
|
https://openai.bueso.eu/admin/agents
|
|
|
|
# remove
|
|
curl -X DELETE -H "Authorization: Bearer $ADMIN_API_KEY" \
|
|
https://openai.bueso.eu/admin/agents/media-agent
|
|
```
|
|
|
|
The store is authoritative and persists across restarts; no env config needed.
|
|
|
|
## Building & running
|
|
|
|
```bash
|
|
mix deps.get
|
|
mix compile
|
|
ADAPTER_API_KEY=secret AGENTS='{"scholar-agent":"https://n8n.bueso.eu/webhook/<id>/chat"}' \
|
|
PORT=8000 mix run --no-halt
|
|
```
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
MIX_ENV=test mix test
|
|
```
|
|
|
|
## Nix
|
|
|
|
The repo ships a `flake.nix` exporting `overlays.default` and a `packages.default`
|
|
(the packaged BEAM release), so it can be consumed as a flake input from your
|
|
NixOS config just like any other flake — e.g.:
|
|
|
|
```nix
|
|
inputs.n8n-openai-adapter.url = "git+https://gitea.bueso.eu/<owner>/n8n-openai-adapter";
|
|
```
|