# Request normalization
The same OpenAI-style parameter can behave differently across open-model serving stacks: one
honors it, one ignores it, one rejects it with its own error. Keln normalizes requests and
responses per model, so one request shape works everywhere and tool calls and reasoning fields
behave the same on every provider.
## Parameters
Keln honors standard parameters or translates them per model. Support for some parameters varies
by provider (`seed`, `n`, `logprobs`, `logit_bias`, the frequency/presence penalties, `stop`). A
request that uses one goes to a provider that supports it, and tools, JSON modes, and image input
route the same way. When no provider serving the model can honor a hard requirement, Keln returns
a `400` naming the parameter.
Keln passes open-model extensions (`top_k`, `min_p`, `repetition_penalty`) through where the
serving stack supports them, and names any field it accepts but does not act on in the
`x-keln-inert-params` response header.
## Message shapes
- `developer` role is treated as `system`.
- Content can be a string or a text-part array.
- A replayed `reasoning` field folds into `reasoning_content`, and replayed `reasoning_content`
is preserved, so multi-turn tool calling keeps the trace ([reasoning](reasoning.md)).
- `cache_control` blocks are ignored ([caching is automatic](caching.md)).
- Unknown fields are ignored.
## Repairs while the response streams
Keln normalizes every response to the spec shape:
- Streamed tool-call fragments arrive with dense, 0-based indices.
- A `<think>` block that a provider left inside `content` is moved to `reasoning_content`.
- If a provider ignores `max_tokens`, Keln cuts the output at your cap.
- Keln handles stop-sequence differences between providers so you never see them.
## Rejected before billing
Keln counts each request with the model's tokenizer against its context window
(`context_length` in [`/v1/models`](../api/models.md)) and returns `400 context_length_exceeded`
for an oversized request before billing any tokens.
## Stripped before dispatch
Keln removes end-user identifiers (`user`, `safety_identifier`), storage and metadata flags, and
provider-steering fields before a request leaves Keln, as part of
[zero data retention](privacy.md). It also detects and removes system prompts that a provider
injects, so the model sees only your messages.
Full per-parameter behavior: [Chat completions reference ↗](../api/chat-completions.md#parameters)
Request normalization
The same OpenAI-style parameter can behave differently across open-model serving stacks: one
honors it, one ignores it, one rejects it with its own error. Keln normalizes requests and
responses per model, so one request shape works everywhere and tool calls and reasoning fields
behave the same on every provider.
Parameters
Keln honors standard parameters or translates them per model. Support for some parameters varies
by provider (seed, n, logprobs, logit_bias, the frequency/presence penalties, stop). A
request that uses one goes to a provider that supports it, and tools, JSON modes, and image input
route the same way. When no provider serving the model can honor a hard requirement, Keln returns
a 400 naming the parameter.
Keln passes open-model extensions (top_k, min_p, repetition_penalty) through where the
serving stack supports them, and names any field it accepts but does not act on in the
x-keln-inert-params response header.
Message shapes
developer role is treated as system.
Content can be a string or a text-part array.
A replayed reasoning field folds into reasoning_content, and replayed reasoning_content
is preserved, so multi-turn tool calling keeps the trace (reasoning).
Streamed tool-call fragments arrive with dense, 0-based indices.
A <think> block that a provider left inside content is moved to reasoning_content.
If a provider ignores max_tokens, Keln cuts the output at your cap.
Keln handles stop-sequence differences between providers so you never see them.
Rejected before billing
Keln counts each request with the model's tokenizer against its context window
(context_length in /v1/models) and returns 400 context_length_exceeded
for an oversized request before billing any tokens.
Stripped before dispatch
Keln removes end-user identifiers (user, safety_identifier), storage and metadata flags, and
provider-steering fields before a request leaves Keln, as part of
zero data retention. It also detects and removes system prompts that a provider
injects, so the model sees only your messages.