Skip to content

Commit 9223b8e

Browse files
Feature: Auto-Discovery for n8n Workflows
Implements automatic discovery of n8n workflows as OpenAI models via tags, eliminating the need for manual models.json maintenance. ## Overview The bridge can now automatically discover n8n workflows tagged with 'n8n-openai-model' and expose them as OpenAI models. This is an opt-in experimental feature that maintains full backward compatibility. ## Key Features - Automatic discovery via n8n API and workflow tags - Configurable polling interval (60-600 seconds) - Graceful fallback to existing models.json if API unavailable - Exponential backoff after consecutive failures - Admin endpoints for manual trigger and reload - Hybrid mode: Runtime discovery with models.json as cache ## Implementation New Files: - src/services/modelDiscoveryService.js - Core discovery service - tests/services/modelDiscoveryService.test.js - 17 unit tests - tests/benchmark-discovery.test.js - 11 performance tests Modified Files: - src/config.js - New environment variables - src/n8nClient.js - Added getWorkflows() and extractWebhookUrl() - src/server.js - Discovery service integration - tests/load/mock-n8n-server.js - n8n API simulation - Makefile - Restructured load test targets - README.md - Comprehensive documentation (marked experimental) ## Testing - 198 tests passing (was 166) - Coverage: 77.06% overall, 94.89% for discovery service - Load tests for both manual config and auto-discovery modes - Performance: 500 workflows processed in <1s ## Configuration Enable auto-discovery: - AUTO_FETCH_MODELS_BY_TAG=true - AUTO_DISCOVERY_TAG=n8n-openai-model - AUTO_DISCOVERY_POLLING=300 - N8N_API_URL=https://n8n.yourdomain.com - N8N_API_BEARER_TOKEN=your-token ## Breaking Changes None. Default behavior unchanged (AUTO_FETCH_MODELS_BY_TAG=false). ## Deprecations - N8N_BEARER_TOKEN -> Use N8N_WEBHOOK_BEARER_TOKEN (with warning) ## Documentation - README restructured: Manual setup first, auto-discovery as advanced feature - Edge cases documented (duplicate workflow names) - Migration guide included - All emojis removed from codebase ## Code Quality - Systematic code review completed - Mock server pagination fixed - Config validation at startup - Duplicate workflow names behavior documented
1 parent cf96d82 commit 9223b8e

24 files changed

+2909
-847
lines changed

.env.example

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,21 @@ PORT=3333
55
BEARER_TOKEN=your-secret-api-key-here
66

77
# n8n Configuration
8+
# N8N_BEARER_TOKEN is deprecated, use N8N_WEBHOOK_BEARER_TOKEN instead
89
N8N_BEARER_TOKEN=
10+
N8N_WEBHOOK_BEARER_TOKEN=
11+
N8N_API_BEARER_TOKEN=
12+
13+
# n8n URLs
14+
N8N_API_URL=https://n8n.yourdomain.com
15+
N8N_WEBHOOK_BASE_URL=https://n8n.yourdomain.com
16+
17+
# Auto-Discovery Configuration
18+
# Set to 'true' to automatically discover workflows tagged with AUTO_DISCOVERY_TAG
19+
AUTO_FETCH_MODELS_BY_TAG=false
20+
AUTO_DISCOVERY_TAG=n8n-openai-model
21+
# Polling interval in seconds (60-600, 0 to disable polling after initial discovery)
22+
AUTO_DISCOVERY_POLLING=300
923

1024
# Models Configuration
1125
MODELS_CONFIG=./models.json

.github/workflows/README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,13 @@ gh pr merge --squash --delete-branch
139139

140140
#### 4. Automatic Release Process
141141
After merge to `main` (with `release` label):
142-
1. `release-on-merge` workflow triggers
143-
2. Finds latest tag (e.g., `v0.0.6`)
144-
3. Creates new tag (e.g., `v0.0.7`)
145-
4. Creates GitHub Release with auto-generated notes
146-
5. Docker image builds for amd64 and arm64
147-
6. Image pushed to `ghcr.io/sveneisenschmidt/n8n-openai-bridge`
148-
7. Tagged with `0.0.7`, `0.0`, `0`, and `latest`
142+
1. OK `release-on-merge` workflow triggers
143+
2. OK Finds latest tag (e.g., `v0.0.6`)
144+
3. OK Creates new tag (e.g., `v0.0.7`)
145+
4. OK Creates GitHub Release with auto-generated notes
146+
5. OK Docker image builds for amd64 and arm64
147+
6. OK Image pushed to `ghcr.io/sveneisenschmidt/n8n-openai-bridge`
148+
7. OK Tagged with `0.0.7`, `0.0`, `0`, and `latest`
149149

150150
**That's it!** Everything happens in one workflow - no manual steps needed.
151151

@@ -205,17 +205,17 @@ After release `v1.0.0`, the following tags are available:
205205
Configure in GitHub Settings → Branches → Branch protection rules:
206206

207207
### Main Branch
208-
- Require a pull request before merging
209-
- Require approvals: 1
210-
- Dismiss stale pull request approvals when new commits are pushed
211-
- Require status checks to pass before merging:
208+
- OK Require a pull request before merging
209+
- OK Require approvals: 1
210+
- OK Dismiss stale pull request approvals when new commits are pushed
211+
- OK Require status checks to pass before merging:
212212
- `test`
213213
- `docker-build`
214214
- `lint`
215215
- `security-scan`
216-
- Require branches to be up to date before merging
217-
- Require conversation resolution before merging
218-
- Do not allow bypassing the above settings
216+
- OK Require branches to be up to date before merging
217+
- OK Require conversation resolution before merging
218+
- OK Do not allow bypassing the above settings
219219

220220
## Secrets Configuration
221221

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ jobs:
9696
echo "Waiting for container to start..."
9797
for i in {1..30}; do
9898
if docker logs test-container 2>&1 | grep -q "Server running on port"; then
99-
echo " Container started"
99+
echo "OK Container started"
100100
break
101101
fi
102102
if [ $i -eq 30 ]; then
103-
echo " Container failed to start"
103+
echo "FAIL Container failed to start"
104104
docker logs test-container
105105
exit 1
106106
fi

.github/workflows/manual-docker-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060

6161
- name: Summary
6262
run: |
63-
echo " Docker images published for ${{ inputs.version_tag }}:"
63+
echo "OK Docker images published for ${{ inputs.version_tag }}:"
6464
echo ""
6565
echo "- ghcr.io/${{ github.repository_owner }}/n8n-openai-bridge:latest"
6666
echo "- ghcr.io/${{ github.repository_owner }}/n8n-openai-bridge:${{ steps.version.outputs.version }}"

Makefile

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help build rebuild start stop restart clean logs test test-unit test-image test-all test-load verify
1+
.PHONY: help build rebuild start stop restart clean logs test test-unit test-image test-all test-load test-load-discovery verify
22

33
help:
44
@echo "Available commands:"
@@ -14,11 +14,12 @@ help:
1414
@echo " make clean - Stop and remove containers, images, and volumes"
1515
@echo ""
1616
@echo "Testing:"
17-
@echo " make test - Run all tests (unit + image build)"
18-
@echo " make test-unit - Run unit tests for server logic only"
19-
@echo " make test-image - Run Docker image build validation tests"
20-
@echo " make test-all - Alias for test"
21-
@echo " make test-load - Run load tests with k6 (20 users, 1min)"
17+
@echo " make test - Run all tests (unit + image build)"
18+
@echo " make test-unit - Run unit tests for server logic only"
19+
@echo " make test-image - Run Docker image build validation tests"
20+
@echo " make test-load - Run all load tests (manual config + auto-discovery)"
21+
@echo " make test-load-manual-config - Run load tests (manual models.json mode)"
22+
@echo " make test-load-discovery - Run load tests (auto-discovery mode)"
2223

2324
rebuild: stop build start
2425
@echo "Rebuild complete!"
@@ -67,11 +68,9 @@ verify:
6768
test: test-unit test-image
6869
@echo ""
6970
@echo "======================================"
70-
@echo " All tests passed successfully!"
71+
@echo "OK All tests passed successfully!"
7172
@echo "======================================"
7273

73-
test-all: test
74-
7574
# Test 1: Unit tests for server logic
7675
test-unit:
7776
@echo "======================================"
@@ -95,24 +94,55 @@ test-image:
9594
@echo ""
9695
@bash tests/test-image-build.sh
9796

98-
# Test 3: Load testing with k6 (via docker-compose)
99-
test-load:
97+
# Test 3: Load testing with k6 (via docker-compose) - All modes
98+
test-load: test-load-manual-config test-load-discovery
99+
@echo ""
100+
@echo "======================================"
101+
@echo "All load tests completed!"
102+
@echo "======================================"
103+
104+
# Test 3a: Load testing - Manual models.json configuration
105+
test-load-manual-config:
100106
@echo ""
101107
@echo "======================================"
102-
@echo "Running Load Tests (20 users, 1min)"
108+
@echo "Running Load Tests (Manual Config)"
109+
@echo "20 users, 1min, models.json"
103110
@echo "======================================"
104111
@echo ""
105112
@echo "Building images..."
106-
@VUS=20 DURATION=1m docker compose -f docker/docker-compose.loadtest.yml build
113+
@VUS=20 DURATION=1m AUTO_FETCH_MODELS_BY_TAG=false docker compose -f docker/docker-compose.loadtest.yml build
107114
@echo ""
108115
@echo "Starting services (mock-n8n, bridge, k6)..."
109-
@VUS=20 DURATION=1m docker compose -f docker/docker-compose.loadtest.yml up --abort-on-container-exit --exit-code-from k6
116+
@VUS=20 DURATION=1m AUTO_FETCH_MODELS_BY_TAG=false docker compose -f docker/docker-compose.loadtest.yml up --abort-on-container-exit --exit-code-from k6
110117
@echo ""
111118
@echo "Cleaning up..."
112119
@docker compose -f docker/docker-compose.loadtest.yml down -v
113120
@echo ""
114-
@echo " Load tests completed!"
121+
@echo "OK Load tests (Manual Config) completed!"
115122
@echo ""
116123
@if [ -f tests/load/summary.json ]; then \
117-
echo "📊 Detailed results saved to: tests/load/summary.json"; \
118-
fi
124+
echo "Detailed results saved to: tests/load/summary.json"; \
125+
fi
126+
127+
# Test 3b: Load testing - Auto-Discovery mode
128+
test-load-discovery:
129+
@echo ""
130+
@echo "======================================"
131+
@echo "Running Load Tests (Auto-Discovery)"
132+
@echo "20 users, 1min, auto-discovery mode"
133+
@echo "======================================"
134+
@echo ""
135+
@echo "Building images..."
136+
@VUS=20 DURATION=1m AUTO_FETCH_MODELS_BY_TAG=true N8N_API_BEARER_TOKEN=mock-token docker compose -f docker/docker-compose.loadtest.yml build
137+
@echo ""
138+
@echo "Starting services (mock-n8n, bridge, k6)..."
139+
@VUS=20 DURATION=1m AUTO_FETCH_MODELS_BY_TAG=true N8N_API_BEARER_TOKEN=mock-token docker compose -f docker/docker-compose.loadtest.yml up --abort-on-container-exit --exit-code-from k6
140+
@echo ""
141+
@echo "Cleaning up..."
142+
@docker compose -f docker/docker-compose.loadtest.yml down -v
143+
@echo ""
144+
@echo "OK Load tests (Auto-Discovery) completed!"
145+
@echo ""
146+
@if [ -f tests/load/summary.json ]; then \
147+
echo "Detailed results saved to: tests/load/summary.json"; \
148+
fi

0 commit comments

Comments
 (0)