TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1import type OpenAI from "openai";2import { type PolyModel, modelKey } from "@/lib/ai/core/types";3import { createOpenAICompatAdapter, listOpenAIModels } from "../shared/openai-compat/factory";4import { CEREBRAS_CATALOG } from "./catalog";56/**7 * Cerebras Inference — OpenAI-compatible at https://api.cerebras.ai/v1 (open models, very fast).8 * `GET /v1/models` exposes ids only; the catalog (docs/provider-research/cerebras.md) provides9 * limits, pricing and which reasoning knob each model accepts.10 */11export function normalizeCerebrasModel(m: { id: string }): PolyModel | null {12 const cat = CEREBRAS_CATALOG.get(m.id);13 return {14 key: modelKey("cerebras", m.id),15 id: m.id,16 provider: "cerebras",17 displayName: cat?.displayName ?? prettify(m.id),18 family: cat?.family ?? (m.id.startsWith("gpt-oss") ? "GPT-OSS" : m.id.startsWith("qwen") ? "Qwen" : m.id.startsWith("gemma") ? "Gemma" : m.id.startsWith("llama") ? "Llama" : "Open models"),19 capabilities: cat?.capabilities ?? { text: true, vision: !m.id.startsWith("gpt-oss"), audioInput: false, audioOutput: false, imageGeneration: false, video: false, reasoning: true, tools: true, structuredOutput: true, streaming: true, files: false, webSearch: false },20 limits: cat?.limits ?? {},21 parameters: cat?.parameters ?? { temperature: true, topP: true, maxTokens: true, stop: true, seed: true, frequencyPenalty: true, presencePenalty: true, topK: false, reasoningEffort: true, reasoningEffortLevels: ["low", "medium", "high"], temperatureRange: { min: 0, max: 2 } },22 status: cat?.status ?? "active",23 pricing: cat?.pricing ?? null,24 metadata: { ...(cat?.metadata ?? {}), sortWeight: cat?.sortWeight ?? (m.id.startsWith("gpt-oss") ? 100 : m.id.startsWith("qwen") ? 90 : 80) },25 };26}2728function prettify(id: string) {29 return id.replace(/gpt-oss/, "GPT-OSS").replace(/qwen/, "Qwen").replace(/gemma/, "Gemma").replace(/llama/, "Llama").replace(/-(\d+)b\b/, " $1B").replace(/-/g, " ");30}3132async function list(client: OpenAI, _k: string, signal?: AbortSignal): Promise<PolyModel[]> {33 return (await listOpenAIModels(client, signal)).map(normalizeCerebrasModel).filter((m): m is PolyModel => Boolean(m));34}3536export const cerebrasAdapter = createOpenAICompatAdapter({37 id: "cerebras",38 name: "Cerebras",39 baseURL: "https://api.cerebras.ai/v1",40 keyDocsUrl: "https://cloud.cerebras.ai/",41 keyPrefixHint: "csk-",42 listModels: list,43 messageOptions: { inlineFiles: true },44 tweakParams: (params, settings, req) => {45 const p = params as unknown as Record<string, unknown>;46 const meta = (req.modelInfo?.metadata ?? {}) as Record<string, unknown>;47 // Cerebras validates strictly: only `reasoning_effort` exists (no disable_reasoning / budget).48 if (req.modelInfo?.capabilities.reasoning && settings.reasoningEffort) {49 const levels = req.modelInfo.parameters.reasoningEffortLevels ?? ["low", "medium", "high"];50 const wanted = settings.reasoningEffort === "minimal" ? "low" : settings.reasoningEffort === "xhigh" || settings.reasoningEffort === "max" ? "high" : settings.reasoningEffort;51 if (levels.includes(wanted)) p.reasoning_effort = wanted;52 }53 // gpt-oss rejects tools + response_format together — tools win, JSON is requested in the prompt instead.54 if (meta.toolsWithResponseFormat === false && params.tools?.length && params.response_format) delete p.response_format;55 // Reasoning tokens consume the output cap; never leave a reasoning model with a tiny cap.56 if (req.modelInfo?.capabilities.reasoning && typeof params.max_completion_tokens === "number" && params.max_completion_tokens < Number(meta.minOutputForReasoning ?? 1000)) params.max_completion_tokens = Number(meta.minOutputForReasoning ?? 1000);57 },58 refineError: (status, code, message) => {59 if (status === 401) return "INVALID_API_KEY";60 if (status === 404) return "MODEL_NOT_FOUND";61 if (status === 429 || code === "token_quota_exceeded" || /quota/i.test(message)) return "RATE_LIMITED";62 if (code === "wrong_api_key" || code === "invalid_api_key") return "INVALID_API_KEY";63 if (code === "model_not_found") return "MODEL_NOT_FOUND";64 if (code === "context_length_exceeded") return "CONTEXT_TOO_LONG";65 return undefined;66 },67});68