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:
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user