If you’re building with AutoGen and want to route traffic through n4n.ai, the integration is straightforward — but the details matter. This autogen setup checklist n4n.ai walks through the eight steps that separate a working prototype from something you can actually ship. Each item includes the exact configuration patterns we use in production.
1. Get your n4n.ai API key and verify connectivity
Start by creating an API key in the n4n.ai dashboard. Copy it immediately — you won’t see it again. Store it in your secret manager or a .env file; never commit it to source control.
# .env
N4N_API_KEY="sk-n4n-..."
N4N_BASE_URL="https://api.n4n.ai/v1"
Verify the key works before touching AutoGen. A quick curl confirms the endpoint responds and your key has access to the models you need:
curl -s -H "Authorization: Bearer $N4N_API_KEY" \
"$N4N_BASE_URL/models" | jq '.data[].id' | head -20
You should see a list of model IDs like gpt-4o, claude-3.5-sonnet, llama-3.1-70b, and so on. If the request returns 401 or 403, regenerate the key. If it returns an empty array, check your organization’s model entitlements in the dashboard.
2. Install AutoGen with the right extras
AutoGen’s core package doesn’t include the OpenAI client by default. You need the openai extra, and you’ll want python-dotenv for local development:
pip install "autogen-agentchat[openai]" python-dotenv
If you’re using AutoGen 0.4+ (the current major version), the import paths changed. The OpenAI-compatible client lives in autogen_ext.models.openai. Pin your dependency to avoid surprise breaking changes:
# pyproject.toml or requirements.txt
autogen-agentchat==0.4.*
autogen-ext[openai]==0.4.*
python-dotenv>=1.0
Run a quick import test to confirm the environment is wired correctly:
# test_imports.py
from autogen_ext.models.openai import OpenAIChatCompletionClient
from dotenv import load_dotenv
import os
load_dotenv()
print("Imports OK")
print("API key present:", bool(os.getenv("N4N_API_KEY")))
3. Configure the OpenAI-compatible client for n4n.ai
n4n.ai exposes an OpenAI-compatible endpoint, so you instantiate OpenAIChatCompletionClient with a custom base_url and your API key. This is where most beginners stall — they forget to override the base URL or they pass the key incorrectly.
# config/client.py
from autogen_ext.models.openai import OpenAIChatCompletionClient
from dotenv import load_dotenv
import os
load_dotenv()
def get_n4n_client(model: str = "gpt-4o-mini") -> OpenAIChatCompletionClient:
return OpenAIChatCompletionClient(
model=model,
api_key=os.getenv("N4N_API_KEY"),
base_url=os.getenv("N4N_BASE_URL"),
# Optional: tune timeouts for production
timeout=60.0,
max_retries=3,
)
Note that model here is the n4n.ai model ID (e.g., gpt-4o-mini, claude-3.5-sonnet), not an OpenAI-specific name. The gateway handles the translation. If you need per-request overrides — say, forcing a specific provider — pass extra_create_args with the x-n4n-provider header:
client = get_n4n_client("gpt-4o")
client.create(
messages=[{"role": "user", "content": "Hello"}],
extra_create_args={"extra_headers": {"x-n4n-provider": "openai"}}
)
4. Set up model routing and fallback behavior
One reason to use n4n.ai is automatic fallback when a provider is rate-limited or degraded. You configure this in the dashboard, but your client code should respect the gateway’s routing hints. The x-n4n-model response header tells you which underlying model actually served the request — log it.
# utils/routing.py
import logging
from typing import Any
logger = logging.getLogger(__name__)
def log_routing_info(response: Any) -> None:
"""Extract and log n4n.ai routing headers from the raw response."""
# The OpenAI client wraps the httpx response in ._response
raw = getattr(response, "_response", None)
if raw and hasattr(raw, "headers"):
model = raw.headers.get("x-n4n-model")
provider = raw.headers.get("x-n4n-provider")
if model or provider:
logger.info(f"n4n.ai routing: model={model} provider={provider}")
Attach this as a callback if you’re using the low-level client, or wrap your agent’s on_message hook. Knowing which provider served each request is essential for debugging latency spikes and cost anomalies.
5. Define agent configs with provider-aware model names
AutoGen agents take a model_client parameter. Create a small factory that maps logical agent roles to model IDs. This keeps your agent definitions clean and makes it trivial to swap models per role — e.g., a cheaper model for a critic agent, a stronger one for the planner.
# config/agents.py
from autogen_agentchat.agents import AssistantAgent
from config.client import get_n4n_client
def create_planner_agent() -> AssistantAgent:
return AssistantAgent(
name="planner",
model_client=get_n4n_client("gpt-4o"),
system_message=(
"You are a planning agent. Break down complex tasks into "
"clear, ordered steps. Output JSON only."
),
)
def create_coder_agent() -> AssistantAgent:
return AssistantAgent(
name="coder",
model_client=get_n4n_client("claude-3.5-sonnet"),
system_message=(
"You write clean, well-tested Python code. "
"Prefer standard library over dependencies."
),
)
def create_critic_agent() -> AssistantAgent:
return AssistantAgent(
name="critic",
model_client=get_n4n_client("gpt-4o-mini"), # cheaper for review passes
system_message=(
"You review code for correctness, style, and security. "
"Be concise. List issues as bullet points."
),
)
Notice the model IDs are n4n.ai identifiers. The gateway resolves them to the best available provider per your dashboard routing rules. If you hardcode provider-specific names (like openai/gpt-4o), you bypass the fallback logic — don’t do that.
6. Implement token usage tracking per agent
n4n.ai returns per-token usage in the standard OpenAI usage field. AutoGen surfaces this via the model client’s create response. Capture it at the agent level so you can attribute cost and enforce budgets.
# utils/usage.py
from dataclasses import dataclass, field
from typing import Dict
from autogen_core.models import ChatCompletionClient, ModelFamily
from autogen_core import CancellationToken
@dataclass
class UsageTracker:
totals: Dict[str, Dict[str, int]] = field(default_factory=dict)
def record(self, agent_name: str, usage: Dict[str, int]) -> None:
if agent_name not in self.totals:
self.totals[agent_name] = {"prompt": 0, "completion": 0, "total": 0}
self.totals[agent_name]["prompt"] += usage.get("prompt_tokens", 0)
self.totals[agent_name]["completion"] += usage.get("completion_tokens", 0)
self.totals[agent_name]["total"] += usage.get("total_tokens", 0)
def summary(self) -> str:
lines = ["Token usage summary:"]
for agent, u in self.totals.items():
lines.append(f" {agent}: {u['total']} total ({u['prompt']} prompt + {u['completion']} completion)")
return "\n".join(lines)
# Global instance for simple scripts; use DI in real apps
tracker = UsageTracker()
Wrap your model client to auto-record usage:
# config/tracked_client.py
from functools import wraps
from autogen_ext.models.openai import OpenAIChatCompletionClient
from utils.usage import tracker
class TrackedClient(OpenAIChatCompletionClient):
def __init__(self, agent_name: str, *args, **kwargs):
super().__init__(*args, **kwargs)
self._agent_name = agent_name
async def create(self, *args, **kwargs):
response = await super().create(*args, **kwargs)
if hasattr(response, "usage") and response.usage:
tracker.record(self._agent_name, response.usage.model_dump())
return response
Then update your agent factory to use TrackedClient with the agent’s name. At the end of a run, call tracker.summary() — you’ll see exactly which agent burned tokens.
7. Add request logging and debug hooks
When a multi-agent loop goes sideways, you need visibility into every request and response. The OpenAI client supports an http_client parameter where you can inject a custom httpx.AsyncClient with event hooks. Log request/response bodies (redacting the API key) and latency.
# config/debug_client.py
import httpx
import logging
import time
from autogen_ext.models.openai import OpenAIChatCompletionClient
from dotenv import load_dotenv
import os
load_dotenv()
logger = logging.getLogger("n4n.debug")
class LoggingHTTPClient(httpx.AsyncClient):
async def send(self, request, **kwargs):
# Redact auth header in logs
safe_headers = dict(request.headers)
if "authorization" in safe_headers:
safe_headers["authorization"] = "Bearer ***"
logger.debug(f"→ {request.method} {request.url} headers={safe_headers}")
start = time.perf_counter()
response = await super().send(request, **kwargs)
elapsed = (time.perf_counter() - start) * 1000
logger.debug(f"← {response.status_code} in {elapsed:.0f}ms")
return response
def get_debug_client(model: str = "gpt-4o-mini") -> OpenAIChatCompletionClient:
http_client = LoggingHTTPClient(timeout=60.0)
return OpenAIChatCompletionClient(
model=model,
api_key=os.getenv("N4N_API_KEY"),
base_url=os.getenv("N4N_BASE_URL"),
http_client=http_client,
)
Enable debug logging only in development or when troubleshooting:
# main.py
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("n4n.debug").setLevel(logging.DEBUG)
The logs will show you the exact payload n4n.ai receives and returns, including any x-n4n-* headers. This is invaluable when the gateway falls back to a different provider and the model behaves unexpectedly.
8. Validate end-to-end with a multi-agent smoke test
Don’t assume it works — run a minimal multi-agent task that exercises routing, fallback, usage tracking, and logging in one go. A three-agent pipeline (planner → coder → critic) is the smallest meaningful graph.
# smoke_test.py
import asyncio
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import MaxMessageTermination
from config.agents import create_planner_agent, create_coder_agent, create_critic_agent
from utils.usage import tracker
async def main():
planner = create_planner_agent()
coder = create_coder_agent()
critic = create_critic_agent()
team = RoundRobinGroupChat(
participants=[planner, coder, critic],
termination_condition=MaxMessageTermination(max_messages=6),
)
task = (
"Write a Python function that computes the nth Fibonacci number "
"using memoization. Include type hints and a docstring."
)
print(f"Running smoke test: {task}\n")
result = await team.run(task=task)
print("\n--- Final output ---")
print(result.messages[-1].content if result.messages else "No output")
print("\n" + tracker.summary())
if __name__ == "__main__":
asyncio.run(main())
Run it:
python smoke_test.py
You should see each agent take a turn, the critic approve (or request changes), and a token summary at the end. Check the debug logs for x-n4n-model and x-n4n-provider headers — confirm the gateway actually routed requests. If any agent fails with a 429 or 5xx, verify the fallback kicked in by checking that the provider header changed on retry.
Summary checklist
| Step | What to verify |
|---|---|
| 1. API key | curl /models returns your entitled models |
| 2. Install | pip install "autogen-agentchat[openai]" succeeds |
| 3. Client config | base_url points to https://api.n4n.ai/v1 |
| 4. Routing | Response headers show x-n4n-model and x-n4n-provider |
| 5. Agent configs | Logical roles map to n4n.ai model IDs, not provider-specific names |
| 6. Usage tracking | Per-agent token totals print at end of run |
| 7. Debug logs | Request/response bodies visible at DEBUG level |
| 8. Smoke test | 3-agent pipeline completes and prints usage summary |
Complete these eight steps and you have a production-ready AutoGen + n4n.ai foundation. From here, add your domain-specific agents, swap in SelectorGroupChat or Swarm for more complex topologies, and enforce budget guards using the usage tracker. The gateway handles provider diversity — your code just sees a consistent OpenAI-compatible interface.