n4nAI

What OpenAI SDK features n4n.ai supports out of the box

Defines n4n.ai OpenAI SDK feature support: which OpenAI client features work unchanged against the gateway, including streaming, tools, and routing.

n4n Team4 min read979 words

Audio narration

Coming soon — every post will get a voice note here.

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.delta SSE frames.
  • Error mapping: Translates provider 429/5xx into OpenAI-style error objects with type and code.
  • 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 usage block.

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.

Tagsn4n-aiopenai-sdkcompatibilityfeatures

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All openai sdk compatibility comparison posts →