SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
4.2 KB · 68 lines typescript
Raw Blame History
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