Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is intentionally lightweight and human-readable. Group entries by rel

- Added `foundrygate-onboarding-report` plus a testable onboarding report module for many-provider and many-client readiness checks
- Added `foundrygate-onboarding-validate` so onboarding blockers can fail fast in local setup and CI-style validation flows
- Added built-in OpenClaw, n8n, and CLI quickstart examples to the onboarding report and integration docs so client onboarding can stay copy/paste friendly

## v0.7.0 - 2026-03-12

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ $EDITOR .env
./scripts/foundrygate-onboarding-report
```

The onboarding report now includes concrete OpenClaw, n8n, and CLI quickstart hints so you can move from a generic health check to a client-specific integration path without leaving the terminal.

If you prefer the Linux service path instead of a manual Python run, jump to [Helper Scripts](#helper-scripts) and use `./scripts/foundrygate-install`.

If you install the project as a package, the `foundrygate` and `foundrygate-stats` console scripts are available.
Expand Down Expand Up @@ -837,7 +839,7 @@ Running `./scripts/foundrygate-install` also creates symlinks in `/usr/local/bin
| --- | --- |
| `foundrygate-bootstrap` | Creates `.env` from `.env.example` if needed, creates a local state dir, and appends a safe local `FOUNDRYGATE_DB_PATH` if none is set |
| `foundrygate-doctor` | Checks for config/env presence, writable DB path, at least one configured provider key, and optional local health endpoints |
| `foundrygate-onboarding-report` | Summarizes provider readiness, client-profile coverage, routing layers, and onboarding suggestions for many-provider and many-client setups |
| `foundrygate-onboarding-report` | Summarizes provider readiness, client-profile coverage, routing layers, onboarding suggestions, and concrete OpenClaw/n8n/CLI quickstarts |
| `foundrygate-onboarding-validate` | Exits non-zero when onboarding blockers exist and prints warnings for common multi-provider and multi-client misconfigurations |
| `foundrygate-install` | Installs the unit file, creates `/var/lib/foundrygate`, creates helper symlinks, reloads `systemd`, and starts the service |
| `foundrygate-start` | Runs `systemctl start foundrygate.service` |
Expand Down
24 changes: 24 additions & 0 deletions docs/INTEGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ Use:
- [openclaw-integration.jsonc](../openclaw-integration.jsonc)
- `client_profiles.presets: ["openclaw"]` for a standard starting point

Minimal direction:

```json
{
"baseUrl": "http://127.0.0.1:8090/v1",
"primary": "foundrygate/auto"
}
```

## n8n

n8n can use FoundryGate as a stable local model gateway.
Expand All @@ -39,6 +48,14 @@ This gives you:
- shared fallback behavior
- route debugging through `POST /api/route`

Minimal direction:

```text
Base URL: http://127.0.0.1:8090/v1
Model: auto
Header: X-FoundryGate-Client: n8n
```

## CLI clients

CLI tools should also use the same local gateway where possible.
Expand All @@ -60,6 +77,13 @@ Recommended pattern:
- `X-FoundryGate-Locality`
- `X-FoundryGate-Profile`

Minimal direction:

```bash
export OPENAI_BASE_URL=http://127.0.0.1:8090/v1
export OPENAI_API_KEY=local
```

## Provider onboarding

When onboarding a new provider:
Expand Down
28 changes: 28 additions & 0 deletions docs/ONBOARDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ $EDITOR .env
./scripts/foundrygate-onboarding-report
```

`foundrygate-onboarding-report` now includes concrete OpenClaw, n8n, and CLI quickstart hints. Use it after every provider or client change to keep the deployment understandable for the next operator as well.

### 1. Add one provider

- define the provider in `config.yaml`
Expand Down Expand Up @@ -80,6 +82,32 @@ Start with:

Then tighten it only if the default is not good enough.

### 3a. Start from one of the built-in quickstarts

OpenClaw:

```json
{
"baseUrl": "http://127.0.0.1:8090/v1",
"primary": "foundrygate/auto"
}
```

n8n:

```text
Base URL: http://127.0.0.1:8090/v1
Model: auto
Header: X-FoundryGate-Client: n8n
```

CLI:

```bash
export OPENAI_BASE_URL=http://127.0.0.1:8090/v1
export OPENAI_API_KEY=local
```

### 4. Add request hooks only if needed

Keep hooks opt-in and narrow. Good uses are:
Expand Down
58 changes: 58 additions & 0 deletions foundrygate/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,53 @@ def build_onboarding_report(
if update_check.get("enabled") and not auto_update.get("enabled"):
suggestions.append("Keep auto_update disabled until the provider and client set is stable.")

enabled_presets = set(client_profiles.get("presets", []))
profile_names = set(client_profiles.get("profiles", {}).keys())
integration_examples = {
"openclaw": {
"recommended": "openclaw" in enabled_presets or "openclaw" in profile_names,
"header": "x-openclaw-source: planner",
"profile": "openclaw",
"snippet": [
'"baseUrl": "http://127.0.0.1:8090/v1"',
'"primary": "foundrygate/auto"',
],
"notes": [
"Keep one-agent and many-agent traffic on the same OpenAI-compatible base URL.",
"Use x-openclaw-source when you want sub-agent traffic to resolve differently.",
],
},
"n8n": {
"recommended": "n8n" in enabled_presets or "n8n" in profile_names,
"header": "X-FoundryGate-Client: n8n",
"profile": "n8n",
"snippet": [
"Base URL: http://127.0.0.1:8090/v1",
"Model: auto",
],
"notes": [
"Start workflow traffic with the n8n preset before adding custom policy rules.",
"Use route dry-runs to confirm cheaper or local-first defaults before"
" production runs.",
],
},
"cli": {
"recommended": "cli" in enabled_presets or "cli" in profile_names,
"header": "X-FoundryGate-Client: codex",
"profile": "cli",
"snippet": [
"export OPENAI_BASE_URL=http://127.0.0.1:8090/v1",
"export OPENAI_API_KEY=local",
],
"notes": [
"Use a stable client tag such as codex, claude, or kilocode to keep"
" traces readable.",
"Only add hook-based locality or provider overrides when one CLI flow"
" truly needs them.",
],
},
}

return {
"config_path": str(Path(config_path) if config_path else Path.cwd() / "config.yaml"),
"env_file": str(resolved_env),
Expand Down Expand Up @@ -146,6 +193,7 @@ def build_onboarding_report(
"auto_update_enabled": bool(auto_update.get("enabled")),
"rollout_ring": auto_update.get("rollout_ring", "early"),
},
"integrations": integration_examples,
"suggestions": suggestions,
}

Expand Down Expand Up @@ -196,6 +244,7 @@ def render_onboarding_report(report: dict[str, Any]) -> str:
client_block = report["clients"]
routing_block = report["routing"]
ops_block = report["operations"]
integration_block = report["integrations"]
preset_text = ", ".join(client_block["presets"]) if client_block["presets"] else "none"
fallback_text = (
", ".join(routing_block["fallback_chain"]) if routing_block["fallback_chain"] else "none"
Expand Down Expand Up @@ -251,6 +300,15 @@ def render_onboarding_report(report: dict[str, Any]) -> str:
]
)

lines.extend(["", "Integration quickstarts"])
for client_name, data in integration_block.items():
readiness = "ready" if data["recommended"] else "needs preset or custom profile"
lines.append(f"- {client_name}: {readiness}")
lines.append(f" header: {data['header']}")
lines.append(f" profile: {data['profile']}")
for snippet_line in data["snippet"]:
lines.append(f" example: {snippet_line}")

if report["suggestions"]:
lines.extend(["", "Suggestions"])
lines.extend(f"- {item}" for item in report["suggestions"])
Expand Down
48 changes: 48 additions & 0 deletions tests/test_onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ def test_onboarding_report_marks_missing_api_keys_and_presets(tmp_path: Path):
assert report["providers"]["ready"] == 0
assert report["providers"]["missing_api_keys"] == ["deepseek-chat"]
assert report["clients"]["presets"] == ["openclaw"]
assert report["integrations"]["openclaw"]["recommended"] is True
assert report["integrations"]["n8n"]["recommended"] is False
assert (
"Keep auto_update disabled until the provider and client set is stable."
in report["suggestions"]
Expand Down Expand Up @@ -105,6 +107,8 @@ def test_onboarding_report_marks_local_worker_ready(tmp_path: Path):
assert report["providers"]["ready"] == 1
assert report["providers"]["local_workers"] == 1
assert "local-worker: local-worker / openai-compat / local / ready" in text
assert "Integration quickstarts" in text
assert "header: X-FoundryGate-Client: codex" in text


def test_onboarding_validation_blocks_missing_env_and_unready_providers(
Expand Down Expand Up @@ -204,3 +208,47 @@ def test_onboarding_validation_passes_for_ready_multi_provider_setup(tmp_path: P

assert validation["ok"] is True
assert validation["blockers"] == []


def test_onboarding_report_marks_all_builtin_integrations_ready(tmp_path: Path):
env_file = tmp_path / ".env"
env_file.write_text("DEEPSEEK_API_KEY=sk-demo\n", encoding="utf-8")

config_file = tmp_path / "config.yaml"
config_file.write_text(
"""
fallback_chain:
- deepseek-chat
providers:
deepseek-chat:
backend: openai-compat
base_url: "https://api.deepseek.com/v1"
api_key: "${DEEPSEEK_API_KEY}"
model: "deepseek-chat"
tier: default
client_profiles:
enabled: true
default: generic
presets: ["openclaw", "n8n", "cli"]
profiles:
generic: {}
rules: []
routing_policies:
enabled: false
rules: []
request_hooks:
enabled: false
hooks: []
update_check:
enabled: true
auto_update:
enabled: false
""".strip(),
encoding="utf-8",
)

report = build_onboarding_report(config_path=config_file, env_file=env_file)

assert report["integrations"]["openclaw"]["recommended"] is True
assert report["integrations"]["n8n"]["recommended"] is True
assert report["integrations"]["cli"]["recommended"] is True
Loading