n4n.ai OpenAI SDK feature support describes the set of OpenAI API behaviors that the n4n.ai gateway implements behind a single OpenAI-compatible endpoint, letting standard openai client libraries call 240+ models without modification. It is a contract compatibility layer, not a fork: request shapes, response schemas, streaming frames, and SDK-level conveniences map to underlying provider semantics where possible.
What “OpenAI SDK feature support” actually means
The OpenAI Python and TypeScript SDKs are strict about request and response shapes. They serialize messages, tools, response_format, and stream SSE frames in a specific way. A gateway that claims compatibility must accept those exact structures and return responses the SDK can parse without custom adapters.
Feature support is not binary. Some fields are passed through verbatim; others are translated per provider. For example, Anthropic’s Claude expects system prompts in a top-level system field, while OpenAI packs them into messages with role: "system". A compatible gateway normalizes this so the SDK never knows the difference.
The practical definition: if from openai import OpenAI; client = OpenAI(base_url=...) works against the gateway with the same method signatures and response objects you use against api.openai.com, then the gateway has OpenAI SDK feature support for those code paths.
How the compatibility layer works
Under the hood, the gateway exposes /v1/chat/completions, /v1/embeddings, /v1/models, and the other standard paths. It validates the OpenAI-shaped request, then routes to a backend provider using internal mappings.
Key mechanics:
- Request translation: Converts OpenAI message roles and tool schemas to provider-native formats.
- Streaming normalization: Repackages provider token streams into OpenAI
choices.deltaSSE frames. - Error mapping: Translates provider 429/5xx into OpenAI-style error objects with
typeandcode. - Fallback: If the primary provider is rate-limited or degraded, the gateway retries against another that supports the same model family, without client involvement.
- Metering: Per-token usage is counted on every request and returned in the standard
usageblock.
When auditing n4n.ai OpenAI SDK feature support, the most overlooked capability is honoring client routing directives via headers while forwarding provider cache-control hints. That means you can pin a provider or set cache_control through extra_body and the gateway passes it through to the upstream.
Why this matters for production systems
Engineers rarely want to rewrite application code when they add a new model provider. If your codebase uses client.chat.completions.create(...), swapping the base_url should be enough to unlock 240+ models, fallback, and per-token metering.
Without compatibility, you accumulate provider-specific branches: one for Anthropic, one for Gemini, one for Mistral. Each has different auth headers, streaming terminators, and tool-call shapes. The compatibility layer collapses that into one surface area, which reduces test matrix size and incident scope.
Concrete example: minimal code change
Assume an existing OpenAI call:
from openai import OpenAI
client = OpenAI() # defaults to api.openai.com
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Summarize the RFC."}],
)
print(resp.choices[0].message.content)
To route through the gateway instead, only the client constructor changes:
from openai import OpenAI
client = OpenAI(
base_url="https://api.example.com/v1", # OpenAI-compatible endpoint
api_key="sk-gateway-...",
)
# Same call shape, different backend model
stream = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Summarize the RFC."}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
Tool calls work identically:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}}
}
}]
resp = client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Weather in Paris?"}],
tools=tools,
)
print(resp.choices[0].message.tool_calls)
The SDK parses tool_calls exactly as if it hit OpenAI directly. No middleware, no response post-processing.
Supported feature surface
Chat completions and streaming
Full messages array, temperature, max_tokens, stop, stream. SSE frames match OpenAI’s data: {...}\n\n format, including the final data: [DONE]. A provider that streams raw text deltas gets wrapped:
{"choices":[{"delta":{"content":"Hello"},"index":0,"finish_reason":null}]}
versus a provider-native frame like:
{"type":"content_block_delta","delta":{"type":"text_delta","text":"Hello"}}
The gateway does this translation per chunk.
Tool and function calling
tools and tool_choice are translated to provider-native function schemas. Responses populate message.tool_calls with id, type, function.name, function.arguments (stringified JSON). If a provider returns native tool use in a different shape, the gateway flattens it.
JSON mode and structured output
response_format={"type": "json_object"} is enforced where the provider supports it; otherwise the gateway applies a lightweight constraint wrapper and repairs malformed frames. response_format={"type": "text"} is passed through.
Embeddings
client.embeddings.create(model=..., input=...) returns data[].embedding with the same dimension ordering as OpenAI. Batch inputs return one object per input in the same order.
Provider routing and cache control
Headers let the client express routing intent (e.g., prefer a specific provider). extra_body={"cache_control": {"type": "ephemeral", "index": 0}} is forwarded to providers that support prompt caching, so you keep cache hits across the gateway.
How streaming translation works in practice
A raw Anthropic stream emits events keyed by type. The gateway buffers those, extracts text or partial JSON, and emits OpenAI-compatible delta objects. This is why stream=True does not require a different loop in your code.
The translation must also handle finish reasons. OpenAI uses stop, length, tool_calls, content_filter. The gateway maps provider termination causes to the closest OpenAI equivalent so your finish_reason switch statement still works.
Per-token usage metering details
Every response includes:
"usage": {
"prompt_tokens": 42,
"completion_tokens": 17,
"total_tokens": 59
}
The gateway computes these from the provider’s token counts where available, or from a local tokenizer fallback. This gives you a single metering point for 240+ models instead of per-provider billing scrapers.
Common misconceptions
“It’s just a dumb proxy”
A proxy that only forwards to one provider is trivial. Real compatibility requires translating between differing role systems, tokenization boundaries, and stream terminators. The gateway also performs automatic fallback when a provider is rate-limited or degraded, which a raw proxy does not.
“All models behave identically”
OpenAI SDK feature support guarantees the request/response shape, not model capability parity. A 7B open-weight model will not reason like a frontier model even if both accept the same messages array. You still need evals and guardrails.
“SDK version lock-in”
The OpenAI SDK is backward compatible across minor versions. Because the gateway speaks the stable /v1 contract, you can upgrade the SDK without waiting on gateway changes. The reverse is also true: gateway additions rarely break old SDKs.
“Cache control gets stripped”
Because the gateway forwards provider cache-control hints, you retain prompt caching with supported backends. You just pass them via extra_body as you would with a direct provider client.
“Per-token metering means hidden cost”
Metering simply counts tokens as they flow, giving you usage fields in the response. It does not alter model behavior or inject padding. You get the same usage object OpenAI returns.
Closing notes
Treat OpenAI SDK compatibility as an interface contract. Read the request schema, not the marketing. If the gateway honors client routing directives and forwards cache-control hints, you retain fine-grained control while still writing one code path. That is the practical win for any system that ships LLM features to production.