Add Cerebras platform + generalize StrictOpenAi dialect (provider 10)

The 13th registry row: id "cerebras", CEREBRAS_API_KEY > auth.json
"cerebras" scope, https://api.cerebras.ai/v1 with KIGI_CEREBRAS_BASE_URL
override, Bearer, ChatCompletions, enrichment-backed metadata
(models_dev_id cerebras).

Cerebras' catalog is all chat LLMs (no embedding/tts pollution) and its
/models is minimal (ids only), so restrict_to_enriched=FALSE: keep every
live model, enrich the known ones (context + effort menus low/medium/high),
unknown ones keep the default context. The e2e pins this enrich-without-
restrict path (new — prior enrichment providers all used restrict=true).

Review caught a likely-DOA defect: Cerebras uses strict
additionalProperties:false validation (confirmed 400-rejecting store,
maxTokens, thinking, nested reasoning_content), and stream_options is not
in its schema — so Passthrough (which keeps the stream_options.include_usage
kigi injects on every streaming request) would very likely 400 all
streaming. Generalized ChatCompat::Mistral -> ChatCompat::StrictOpenAi
(serde alias "mistral" keeps pre-rename persisted sessions loading), which
strips stream_options + private fields for any strict OpenAI-compat
validator; both Mistral and Cerebras now map to it. Future strict-validator
candidates (NVIDIA/Azure/Xiaomi/OpenCode) noted for the same check.

reasoning_effort (incl. "none") passes through; /v1/models requires auth
so key validation works; console cloud.cerebras.ai.
This commit is contained in:
2026-07-21 14:04:34 -04:00
parent 0a147bd8a5
commit f825132983
7 changed files with 205 additions and 23 deletions
@@ -1078,11 +1078,15 @@ pub enum ChatCompat {
DeepSeek,
/// Leave the body as-is (OpenAI-style `reasoning_effort` passes through).
Passthrough,
/// Mistral wire: OpenAI-style `reasoning_effort` passes through, but the
/// strict Pydantic validator 422-rejects `stream_options` (the SDK's
/// request model has no such field), so it must be stripped. Also strips
/// the kigi-private message fields like Passthrough.
Mistral,
/// Strict OpenAI-compatible validators (Mistral, Cerebras) reject any
/// out-of-schema request field with a 4xx (`additionalProperties:false`).
/// kigi injects `stream_options.include_usage` on every streaming
/// request, which such validators reject, so it is stripped (streaming
/// usage falls back to token estimation). `reasoning_effort` passes
/// through; private message fields are stripped like Passthrough.
/// (Serde alias `mistral` keeps sessions persisted before the rename.)
#[serde(alias = "mistral")]
StrictOpenAi,
}
pub const REASONING_EFFORT_META_KEY: &str = "reasoningEffort";
@@ -1449,6 +1453,21 @@ mod tests {
/// String content (the only shape non-Mistral providers send) stays the
/// answer verbatim with no thinking — byte-identical to the pre-change
/// deserialization.
/// The StrictOpenAi dialect kept the serde alias `mistral`, so sessions
/// persisted before the rename still deserialize.
#[test]
fn chat_compat_mistral_alias_deserializes_to_strict_openai() {
let v: ChatCompat = serde_json::from_str("\"mistral\"").unwrap();
assert_eq!(v, ChatCompat::StrictOpenAi);
// New value round-trips as strict_open_ai.
let v: ChatCompat = serde_json::from_str("\"strict_open_ai\"").unwrap();
assert_eq!(v, ChatCompat::StrictOpenAi);
assert_eq!(
serde_json::to_string(&ChatCompat::StrictOpenAi).unwrap(),
"\"strict_open_ai\""
);
}
#[test]
fn chunk_delta_string_content_unchanged() {
let delta: ChatChunkDelta =