fix: retire dead cloud models and make the canary suites trustworthy (#1722)

* fix(models): drop cloud models retired upstream and remap saved Groq selections

Groq shut down qwen/qwen3-32b, llama-3.3-70b-versatile, and
llama-3.1-8b-instant on 2026-08-16, and Tinfoil replaced kimi-k2-6 with
kimi-k3 — all four registry entries now 404 at the provider, so a BYOK
user picking one gets model_not_found (caught by the first keyed
llm-canary run). Remove the entries, their locale description keys, and
the kimi entry in the Tinfoil description-key map.

Persisted scope selections pointing at a retired Groq model are remapped
by a one-shot migration to the gpt-oss replacement Groq recommends;
non-Groq providers that still serve those ids (OpenRouter) are left
alone. Tinfoil needs no migration: its live-catalog reconcile already
moves users off retired models.

The qwen entry was the only registry model flagged disableThinking, so
the request-shape matrix row pinning that flag is removed with it; the
suppression mechanic itself stays (any future registry or catalog entry
can set the flag).

* fix(canary): validate post-handshake auth, normalize Gemini ids, probe registry models

Three fixes so a red canary means provider news, not canary bugs:

- The openai-realtime STT probe resolved on WebSocket open, but OpenAI
  completes the upgrade before checking the key and only then sends an
  error event — a garbage key passed (proven green in run 32156015458).
  The probe now waits for the first server event and fails on error or
  close-before-event. The tinfoil-realtime row cannot reach the network
  without the SDK's enclave attestation, so its report cell now says
  key present (transport not probed) instead of a bare check.

- Gemini's OpenAI-compat /models returns models/-prefixed ids, so every
  registry id read as vanished from the live catalog while the probe
  itself passed. Strip the prefix before diffing.

- The LLM probe list pinned gpt-4o-mini, which is not in the registry,
  so the suppression gate never disarmed and OpenAI 400'd on
  reasoning_effort + chat_template_kwargs — a red the app never hits
  (it self-heals via fetchWithParamFallback; the canary deliberately
  does not). Probe gpt-4.1-mini instead, and replace Groq's retired
  qwen/llama probes with the gpt-oss pair the registry still serves.

Verified locally: STT canary with a garbage OpenAI key now exits 1
(server error event: invalid_api_key).
This commit is contained in:
Gabriel Stein
2026-08-18 17:52:22 -07:00
committed by GitHub
parent 90f79b6879
commit 9b3f57a4c6
17 changed files with 128 additions and 87 deletions
+9 -3
View File
@@ -24,13 +24,15 @@ const PROVIDERS = [
id: "openai",
keyEnv: "LLM_CANARY_OPENAI_KEY",
base: "https://api.openai.com/v1",
models: ["gpt-4o-mini", "gpt-5-mini"],
// Registry models only — a non-registry id skips the suppression gate and
// draws a 400 from OpenAI's strict parser that no app user ever hits.
models: ["gpt-4.1-mini", "gpt-5-mini"],
},
{
id: "groq",
keyEnv: "LLM_CANARY_GROQ_KEY",
base: "https://api.groq.com/openai/v1",
models: ["openai/gpt-oss-120b", "qwen/qwen3-32b", "llama-3.3-70b-versatile"],
models: ["openai/gpt-oss-120b", "openai/gpt-oss-20b"],
},
{
id: "tinfoil",
@@ -127,7 +129,11 @@ async function catalogDiff(entry, apiKey) {
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) return { error: `models list HTTP ${res.status}` };
const live = ((await res.json())?.data ?? []).map((m) => m.id).filter(Boolean);
// Gemini's OpenAI-compat /models returns "models/"-prefixed ids; the
// registry pins bare ids, so compare without the prefix.
const live = ((await res.json())?.data ?? [])
.map((m) => m.id?.replace(/^models\//, ""))
.filter(Boolean);
const pinned = registryIdsByProvider.get(entry.id) ?? [];
const vanished = pinned.filter((id) => !live.includes(id));
const probed = new Set(entry.models);
+40 -12
View File
@@ -22,22 +22,44 @@ const HANDSHAKE_TIMEOUT_MS = 15000;
// Mirrors the app's dial: openaiRealtimeStreaming.js connects with a bare
// Bearer header; deepgramStreaming.js passes the key as the bearer token.
function probeWebSocket(url, token) {
// `awaitServerEvent` is for providers that authenticate AFTER the upgrade:
// OpenAI opens the socket for any key and only then sends an `error` event
// for a bad one, so resolving on `open` validates nothing (#1624 class).
function probeWebSocket(url, token, { awaitServerEvent = false } = {}) {
return new Promise((resolve) => {
const ws = new WebSocket(url, { headers: { Authorization: `Bearer ${token}` } });
const timer = setTimeout(() => {
ws.terminate();
resolve({ ok: false, detail: `no handshake within ${HANDSHAKE_TIMEOUT_MS}ms` });
}, HANDSHAKE_TIMEOUT_MS);
ws.on("open", () => {
clearTimeout(timer);
ws.close();
resolve({ ok: true });
});
ws.on("unexpected-response", (_req, res) => {
const finish = (result) => {
clearTimeout(timer);
ws.terminate();
resolve({ ok: false, detail: `handshake rejected: HTTP ${res.statusCode}` });
resolve(result);
};
ws.on("open", () => {
if (!awaitServerEvent) finish({ ok: true });
});
ws.on("message", (data) => {
if (!awaitServerEvent) return;
let event;
try {
event = JSON.parse(data);
} catch {
event = {};
}
if (event.type === "error") {
finish({ ok: false, detail: `server error event: ${event.error?.code ?? "unknown"}` });
} else {
finish({ ok: true });
}
});
ws.on("close", (code) => {
if (awaitServerEvent)
finish({ ok: false, detail: `closed before a server event (code ${code})` });
});
ws.on("unexpected-response", (_req, res) => {
finish({ ok: false, detail: `handshake rejected: HTTP ${res.statusCode}` });
});
ws.on("error", (err) => {
clearTimeout(timer);
@@ -64,7 +86,9 @@ const PROBES = [
const token = await fetchRealtimeTokenForProvider("openai-realtime", tokenDeps(key), {
mode: "byok",
});
return probeWebSocket("wss://api.openai.com/v1/realtime?intent=transcription", token);
return probeWebSocket("wss://api.openai.com/v1/realtime?intent=transcription", token, {
awaitServerEvent: true,
});
},
},
{
@@ -96,12 +120,15 @@ const PROBES = [
id: "tinfoil-realtime",
keyEnv: "STT_CANARY_TINFOIL_KEY",
// The attested socket needs the Tinfoil SDK's enclave verification; the
// canary stops at credential resolution through the registry.
// canary stops at credential resolution, so this row only proves the
// secret exists.
run: async (key) => {
const token = await fetchRealtimeTokenForProvider("tinfoil-realtime", tokenDeps(key), {
mode: "byok",
});
return token ? { ok: true } : { ok: false, detail: "empty token" };
return token
? { ok: true, note: "key present (transport not probed)" }
: { ok: false, detail: "empty token" };
},
},
];
@@ -117,7 +144,8 @@ for (const probe of PROBES) {
continue;
}
const result = await probe.run(key).catch((err) => ({ ok: false, detail: err.message }));
report.push(`| ${probe.id} | ${result.ok ? "✅" : `❌ ${result.detail}`} |`);
const cell = result.ok ? `✅${result.note ? ` ${result.note}` : ""}` : `❌ ${result.detail}`;
report.push(`| ${probe.id} | ${cell} |`);
if (!result.ok) failures.push(`${probe.id}: ${result.detail}`);
}
-4
View File
@@ -2291,11 +2291,8 @@
"gemini_gemini_2_5_flash_lite": "Niedrigste Latenz und Kosten",
"gemini_gemma_4_31b_it": "Googles größtes Gemma 4, 31B dicht, 256K Kontext",
"gemini_gemma_4_26b_a4b_it": "Googles Gemma 4 MoE, 4B aktive Parameter, 256K Kontext",
"groq_qwen_qwen3_32b": "Leistungsstarkes Reasoning-Modell, 131K Kontext",
"groq_openai_gpt_oss_120b": "OpenAIs Open-Source-Flaggschiff, 500 T/s",
"groq_openai_gpt_oss_20b": "Schnelles Open-Source-Modell, 1000 T/s",
"groq_llama_3_3_70b_versatile": "Vielseitiges Modell von Meta, 280 T/s",
"groq_llama_3_1_8b_instant": "Ultraschnell 560 T/s, 131K Kontext",
"groq_compound": "Groqs Compound-System, 450 T/s",
"groq_compound_mini": "Schnelles Compound-System, 3x niedrigere Latenz",
"openai_gpt_5_6_sol": "Flaggschiff-GPT-5.6 für komplexes Coding und Forschung",
@@ -2309,7 +2306,6 @@
"anthropic_claude_opus_4_5": "Früheres Opus-Modell",
"tinfoil_deepseek_v4_pro": "Privates Long-Context-Reasoning in sicheren Enklaven, 800K Kontext",
"tinfoil_glm_5_2": "Privates agentisches Engineering-Modell in sicheren Enklaven, 384K Kontext",
"tinfoil_kimi_k2_6": "Privates Modell für langfristiges Coding und Multimodalität, 256K Kontext",
"tinfoil_gemma4_31b": "Privates mehrsprachiges Reasoning-Modell mit Tool-Aufrufen, 256K Kontext",
"tinfoil_gpt_oss_120b": "Privates Open-Weight-Reasoning-Modell mit Tool-Nutzung, 131K Kontext",
"tinfoil_llama3_3_70b": "Privates Konversationsmodell mit Tool-Aufrufen, 128K Kontext",
-4
View File
@@ -2291,11 +2291,8 @@
"gemini_gemini_2_5_flash_lite": "Lowest latency and cost",
"gemini_gemma_4_31b_it": "Google's largest Gemma 4, 31B dense, 256K context",
"gemini_gemma_4_26b_a4b_it": "Google's Gemma 4 MoE, 4B active params, 256K context",
"groq_qwen_qwen3_32b": "Powerful reasoning model, 131K context",
"groq_openai_gpt_oss_120b": "OpenAI's open-source flagship, 500 T/sec",
"groq_openai_gpt_oss_20b": "Fast open-source model, 1000 T/sec",
"groq_llama_3_3_70b_versatile": "Meta's versatile model, 280 T/sec",
"groq_llama_3_1_8b_instant": "Ultra-fast 560 T/sec, 131K context",
"groq_compound": "Groq's compound system, 450 T/sec",
"groq_compound_mini": "Fast compound system, 3x lower latency",
"openai_gpt_5_6_sol": "Flagship GPT-5.6 for complex coding and research",
@@ -2309,7 +2306,6 @@
"anthropic_claude_opus_4_5": "Earlier Opus model",
"tinfoil_deepseek_v4_pro": "Private long-context reasoning in secure enclaves, 800K context",
"tinfoil_glm_5_2": "Private agentic engineering model in secure enclaves, 384K context",
"tinfoil_kimi_k2_6": "Private long-horizon coding and multimodal model, 256K context",
"tinfoil_gemma4_31b": "Private multilingual reasoning model with tool calling, 256K context",
"tinfoil_gpt_oss_120b": "Private open-weight reasoning model with tool use, 131K context",
"tinfoil_llama3_3_70b": "Private conversational model with tool calling, 128K context",
-4
View File
@@ -2247,11 +2247,8 @@
"gemini_gemini_2_5_flash_lite": "Menor latencia y costo",
"gemini_gemma_4_31b_it": "El mayor Gemma 4 de Google, 31B denso, contexto 256K",
"gemini_gemma_4_26b_a4b_it": "Gemma 4 MoE de Google, 4B parámetros activos, contexto 256K",
"groq_qwen_qwen3_32b": "Potente modelo de razonamiento, contexto de 131K",
"groq_openai_gpt_oss_120b": "Modelo insignia de código abierto de OpenAI, 500 T/seg",
"groq_openai_gpt_oss_20b": "Modelo de código abierto rápido, 1000 T/seg",
"groq_llama_3_3_70b_versatile": "Modelo versátil de Meta, 280 T/seg",
"groq_llama_3_1_8b_instant": "Ultrarrápido 560 T/seg, contexto de 131K",
"groq_compound": "Sistema compound de Groq, 450 T/seg",
"groq_compound_mini": "Sistema compound rápido, 3x menor latencia",
"openai_gpt_5_6_sol": "GPT-5.6 insignia para programación e investigación complejas",
@@ -2265,7 +2262,6 @@
"anthropic_claude_opus_4_5": "Modelo Opus anterior",
"tinfoil_deepseek_v4_pro": "Razonamiento privado de contexto largo en enclaves seguros, contexto de 800K",
"tinfoil_glm_5_2": "Modelo privado de ingeniería agéntica en enclaves seguros, contexto de 384K",
"tinfoil_kimi_k2_6": "Modelo privado de codificación a largo plazo y multimodal, contexto de 256K",
"tinfoil_gemma4_31b": "Modelo privado de razonamiento multilingüe con llamada a herramientas, contexto de 256K",
"tinfoil_gpt_oss_120b": "Modelo privado de razonamiento de pesos abiertos con uso de herramientas, contexto de 131K",
"tinfoil_llama3_3_70b": "Modelo privado conversacional con llamada a herramientas, contexto de 128K",
-4
View File
@@ -2291,11 +2291,8 @@
"gemini_gemini_2_5_flash_lite": "Latence et coût les plus faibles",
"gemini_gemma_4_31b_it": "Le plus grand Gemma 4 de Google, 31B dense, contexte 256K",
"gemini_gemma_4_26b_a4b_it": "Gemma 4 MoE de Google, 4B paramètres actifs, contexte 256K",
"groq_qwen_qwen3_32b": "Modèle de raisonnement puissant, contexte 131K",
"groq_openai_gpt_oss_120b": "Modèle phare open source d'OpenAI, 500 T/s",
"groq_openai_gpt_oss_20b": "Modèle open source rapide, 1000 T/s",
"groq_llama_3_3_70b_versatile": "Modèle polyvalent de Meta, 280 T/s",
"groq_llama_3_1_8b_instant": "Ultra-rapide 560 T/s, contexte 131K",
"groq_compound": "Système compound de Groq, 450 T/s",
"groq_compound_mini": "Système compound rapide, 3x latence réduite",
"openai_gpt_5_6_sol": "GPT-5.6 phare pour le codage et la recherche complexes",
@@ -2309,7 +2306,6 @@
"anthropic_claude_opus_4_5": "Modèle Opus antérieur",
"tinfoil_deepseek_v4_pro": "Raisonnement privé à long contexte dans des enclaves sécurisées, contexte 800K",
"tinfoil_glm_5_2": "Modèle privé d'ingénierie agentique dans des enclaves sécurisées, contexte 384K",
"tinfoil_kimi_k2_6": "Modèle privé de codage longue durée et multimodal, contexte 256K",
"tinfoil_gemma4_31b": "Modèle privé de raisonnement multilingue avec appel d'outils, contexte 256K",
"tinfoil_gpt_oss_120b": "Modèle privé de raisonnement à poids ouverts avec usage d'outils, contexte 131K",
"tinfoil_llama3_3_70b": "Modèle privé conversationnel avec appel d'outils, contexte 128K",
-4
View File
@@ -2199,11 +2199,8 @@
"gemini_gemini_2_5_flash_lite": "Latenza e costo minimi",
"gemini_gemma_4_31b_it": "Il più grande Gemma 4 di Google, 31B denso, contesto 256K",
"gemini_gemma_4_26b_a4b_it": "Gemma 4 MoE di Google, 4B parametri attivi, contesto 256K",
"groq_qwen_qwen3_32b": "Potente modello di ragionamento, contesto 131K",
"groq_openai_gpt_oss_120b": "Ammiraglia open source di OpenAI, 500 T/sec",
"groq_openai_gpt_oss_20b": "Modello open source veloce, 1000 T/sec",
"groq_llama_3_3_70b_versatile": "Modello versatile di Meta, 280 T/sec",
"groq_llama_3_1_8b_instant": "Ultra-veloce 560 T/sec, contesto 131K",
"groq_compound": "Sistema compound di Groq, 450 T/sec",
"groq_compound_mini": "Sistema compound veloce, 3x latenza ridotta",
"openai_gpt_5_6_sol": "GPT-5.6 di punta per programmazione e ricerca complesse",
@@ -2217,7 +2214,6 @@
"anthropic_claude_opus_4_5": "Modello Opus meno recente",
"tinfoil_deepseek_v4_pro": "Ragionamento privato a contesto lungo in enclave sicure, contesto 800K",
"tinfoil_glm_5_2": "Modello privato di ingegneria agentica in enclave sicure, contesto 384K",
"tinfoil_kimi_k2_6": "Modello privato di coding a lungo termine e multimodale, contesto 256K",
"tinfoil_gemma4_31b": "Modello privato di ragionamento multilingue con chiamata di strumenti, contesto 256K",
"tinfoil_gpt_oss_120b": "Modello privato di ragionamento open-weight con uso di strumenti, contesto 131K",
"tinfoil_llama3_3_70b": "Modello privato conversazionale con chiamata di strumenti, contesto 128K",
-4
View File
@@ -2199,11 +2199,8 @@
"gemini_gemini_2_5_flash_lite": "最低レイテンシーとコスト",
"gemini_gemma_4_31b_it": "Google 最大の Gemma 4、31B デンス、256K コンテキスト",
"gemini_gemma_4_26b_a4b_it": "Google の Gemma 4 MoE、4B アクティブパラメータ、256K コンテキスト",
"groq_qwen_qwen3_32b": "強力な推論モデル、131K コンテキスト",
"groq_openai_gpt_oss_120b": "OpenAI のオープンソースフラッグシップ、500 T/秒",
"groq_openai_gpt_oss_20b": "高速オープンソースモデル、1000 T/秒",
"groq_llama_3_3_70b_versatile": "Meta の汎用モデル、280 T/秒",
"groq_llama_3_1_8b_instant": "超高速 560 T/秒、131K コンテキスト",
"groq_compound": "Groq の Compound システム、450 T/秒",
"groq_compound_mini": "高速 Compound システム、3x 低レイテンシー",
"openai_gpt_5_6_sol": "複雑なコーディングと研究のためのフラッグシップ GPT-5.6",
@@ -2217,7 +2214,6 @@
"anthropic_claude_opus_4_5": "旧世代の Opus モデル",
"tinfoil_deepseek_v4_pro": "セキュアエンクレーブ内のプライベート長文脈推論、800K コンテキスト",
"tinfoil_glm_5_2": "セキュアエンクレーブ内のプライベート・エージェント型エンジニアリングモデル、384K コンテキスト",
"tinfoil_kimi_k2_6": "プライベートな長期コーディング・マルチモーダルモデル、256K コンテキスト",
"tinfoil_gemma4_31b": "ツール呼び出し対応のプライベート多言語推論モデル、256K コンテキスト",
"tinfoil_gpt_oss_120b": "ツール使用対応のプライベート・オープンウェイト推論モデル、131K コンテキスト",
"tinfoil_llama3_3_70b": "ツール呼び出し対応のプライベート会話モデル、128K コンテキスト",
-4
View File
@@ -2178,11 +2178,8 @@
"gemini_gemini_2_5_flash_lite": "Menor latência e custo",
"gemini_gemma_4_31b_it": "O maior Gemma 4 do Google, 31B denso, contexto 256K",
"gemini_gemma_4_26b_a4b_it": "Gemma 4 MoE do Google, 4B parâmetros ativos, contexto 256K",
"groq_qwen_qwen3_32b": "Modelo de raciocínio poderoso, contexto de 131K",
"groq_openai_gpt_oss_120b": "Modelo principal open source da OpenAI, 500 T/seg",
"groq_openai_gpt_oss_20b": "Modelo open source rápido, 1000 T/seg",
"groq_llama_3_3_70b_versatile": "Modelo versátil da Meta, 280 T/seg",
"groq_llama_3_1_8b_instant": "Ultrarápido 560 T/seg, contexto de 131K",
"groq_compound": "Sistema compound da Groq, 450 T/seg",
"groq_compound_mini": "Sistema compound rápido, 3x menor latência",
"openai_gpt_5_6_sol": "GPT-5.6 principal para programação e pesquisa complexas",
@@ -2196,7 +2193,6 @@
"anthropic_claude_opus_4_5": "Modelo Opus mais antigo",
"tinfoil_deepseek_v4_pro": "Raciocínio privado de contexto longo em enclaves seguros, contexto de 800K",
"tinfoil_glm_5_2": "Modelo privado de engenharia agêntica em enclaves seguros, contexto de 384K",
"tinfoil_kimi_k2_6": "Modelo privado de codificação de longo prazo e multimodal, contexto de 256K",
"tinfoil_gemma4_31b": "Modelo privado de raciocínio multilíngue com chamada de ferramentas, contexto de 256K",
"tinfoil_gpt_oss_120b": "Modelo privado de raciocínio de pesos abertos com uso de ferramentas, contexto de 131K",
"tinfoil_llama3_3_70b": "Modelo privado conversacional com chamada de ferramentas, contexto de 128K",
-4
View File
@@ -2213,11 +2213,8 @@
"gemini_gemini_2_5_flash_lite": "Минимальная задержка и стоимость",
"gemini_gemma_4_31b_it": "Крупнейшая Gemma 4 от Google, 31B плотная, контекст 256K",
"gemini_gemma_4_26b_a4b_it": "Gemma 4 MoE от Google, 4B активных параметров, контекст 256K",
"groq_qwen_qwen3_32b": "Мощная модель с рассуждением, контекст 131K",
"groq_openai_gpt_oss_120b": "Открытая флагманская модель OpenAI, 500 т/с",
"groq_openai_gpt_oss_20b": "Быстрая открытая модель, 1000 т/с",
"groq_llama_3_3_70b_versatile": "Универсальная модель Meta, 280 т/с",
"groq_llama_3_1_8b_instant": "Сверхбыстрая 560 т/с, контекст 131K",
"groq_compound": "Система Compound от Groq, 450 т/с",
"groq_compound_mini": "Быстрая система Compound, 3x ниже задержка",
"openai_gpt_5_6_sol": "Флагманская GPT-5.6 для сложного программирования и исследований",
@@ -2231,7 +2228,6 @@
"anthropic_claude_opus_4_5": "Более ранняя модель Opus",
"tinfoil_deepseek_v4_pro": "Приватное рассуждение с длинным контекстом в защищённых анклавах, контекст 800K",
"tinfoil_glm_5_2": "Приватная агентная инженерная модель в защищённых анклавах, контекст 384K",
"tinfoil_kimi_k2_6": "Приватная модель для длительного кодинга и мультимодальности, контекст 256K",
"tinfoil_gemma4_31b": "Приватная многоязычная модель рассуждений с вызовом инструментов, контекст 256K",
"tinfoil_gpt_oss_120b": "Приватная модель рассуждений с открытыми весами и использованием инструментов, контекст 131K",
"tinfoil_llama3_3_70b": "Приватная разговорная модель с вызовом инструментов, контекст 128K",
-4
View File
@@ -2199,11 +2199,8 @@
"gemini_gemini_2_5_flash_lite": "最低延迟和成本",
"gemini_gemma_4_31b_it": "Google 最大的 Gemma 4,31B 稠密,256K 上下文",
"gemini_gemma_4_26b_a4b_it": "Google Gemma 4 MoE,4B 活跃参数,256K 上下文",
"groq_qwen_qwen3_32b": "强大的推理模型,131K 上下文",
"groq_openai_gpt_oss_120b": "OpenAI 开源旗舰,500 T/秒",
"groq_openai_gpt_oss_20b": "快速开源模型,1000 T/秒",
"groq_llama_3_3_70b_versatile": "Meta 多功能模型,280 T/秒",
"groq_llama_3_1_8b_instant": "超快 560 T/秒,131K 上下文",
"groq_compound": "Groq Compound 系统,450 T/秒",
"groq_compound_mini": "快速 Compound 系统,3x 更低延迟",
"openai_gpt_5_6_sol": "旗舰 GPT-5.6,适用于复杂编码与研究",
@@ -2217,7 +2214,6 @@
"anthropic_claude_opus_4_5": "更早的 Opus 模型",
"tinfoil_deepseek_v4_pro": "安全飞地中的私密长上下文推理,800K 上下文",
"tinfoil_glm_5_2": "安全飞地中的私密智能体工程模型,384K 上下文",
"tinfoil_kimi_k2_6": "私密长程编码与多模态模型,256K 上下文",
"tinfoil_gemma4_31b": "支持工具调用的私密多语言推理模型,256K 上下文",
"tinfoil_gpt_oss_120b": "支持工具使用的私密开源权重推理模型,131K 上下文",
"tinfoil_llama3_3_70b": "支持工具调用的私密对话模型,128K 上下文",
-4
View File
@@ -2199,11 +2199,8 @@
"gemini_gemini_2_5_flash_lite": "最低延遲和成本",
"gemini_gemma_4_31b_it": "Google 最大的 Gemma 4,31B 稠密,256K 上下文",
"gemini_gemma_4_26b_a4b_it": "Google Gemma 4 MoE,4B 活躍參數,256K 上下文",
"groq_qwen_qwen3_32b": "強大推理模型,131K 上下文",
"groq_openai_gpt_oss_120b": "OpenAI 開源旗艦,500 T/sec",
"groq_openai_gpt_oss_20b": "快速開源模型,1000 T/sec",
"groq_llama_3_3_70b_versatile": "Meta 多功能模型,280 T/sec",
"groq_llama_3_1_8b_instant": "超快速 560 T/sec,131K 上下文",
"groq_compound": "Groq Compound 系統,450 T/sec",
"groq_compound_mini": "快速 Compound 系統,3x 更低延遲",
"openai_gpt_5_6_sol": "旗艦 GPT-5.6,適用於複雜編碼與研究",
@@ -2217,7 +2214,6 @@
"anthropic_claude_opus_4_5": "較早的 Opus 模型",
"tinfoil_deepseek_v4_pro": "安全飛地中的私密長上下文推理,800K 上下文",
"tinfoil_glm_5_2": "安全飛地中的私密智能體工程模型,384K 上下文",
"tinfoil_kimi_k2_6": "私密長程編碼與多模態模型,256K 上下文",
"tinfoil_gemma4_31b": "支援工具呼叫的私密多語言推理模型,256K 上下文",
"tinfoil_gpt_oss_120b": "支援工具使用的私密開放權重推理模型,131K 上下文",
"tinfoil_llama3_3_70b": "支援工具呼叫的私密對話模型,128K 上下文",
-29
View File
@@ -526,14 +526,6 @@
"id": "groq",
"name": "Groq",
"models": [
{
"id": "qwen/qwen3-32b",
"name": "Qwen3 32B",
"description": "Powerful reasoning model, 131K context",
"disableThinking": true,
"supportsThinking": true,
"descriptionKey": "models.descriptions.cloud.groq_qwen_qwen3_32b"
},
{
"id": "openai/gpt-oss-120b",
"name": "GPT-OSS 120B",
@@ -548,18 +540,6 @@
"supportsThinking": true,
"descriptionKey": "models.descriptions.cloud.groq_openai_gpt_oss_20b"
},
{
"id": "llama-3.3-70b-versatile",
"name": "LLaMA 3.3 70B",
"description": "Meta's versatile model, 280 T/sec",
"descriptionKey": "models.descriptions.cloud.groq_llama_3_3_70b_versatile"
},
{
"id": "llama-3.1-8b-instant",
"name": "LLaMA 3.1 8B",
"description": "Ultra-fast 560 T/sec, 131K context",
"descriptionKey": "models.descriptions.cloud.groq_llama_3_1_8b_instant"
},
{
"id": "groq/compound",
"name": "Compound",
@@ -578,15 +558,6 @@
"id": "tinfoil",
"name": "Tinfoil",
"models": [
{
"id": "kimi-k2-6",
"name": "Kimi K2.6",
"description": "Private long-horizon coding and multimodal model, 256K context",
"tokenParam": "max_tokens",
"supportsTemperature": true,
"descriptionKey": "models.descriptions.cloud.tinfoil_kimi_k2_6",
"supportsThinking": true
},
{
"id": "glm-5-2",
"name": "GLM-5.2",
-1
View File
@@ -23,7 +23,6 @@ export interface TinfoilCatalogModel {
const DESCRIPTION_KEYS: Record<string, string> = {
"deepseek-v4-pro": "models.descriptions.cloud.tinfoil_deepseek_v4_pro",
"glm-5-2": "models.descriptions.cloud.tinfoil_glm_5_2",
"kimi-k2-6": "models.descriptions.cloud.tinfoil_kimi_k2_6",
"gemma4-31b": "models.descriptions.cloud.tinfoil_gemma4_31b",
"gpt-oss-120b": "models.descriptions.cloud.tinfoil_gpt_oss_120b",
"llama3-3-70b": "models.descriptions.cloud.tinfoil_llama3_3_70b",
+24
View File
@@ -558,6 +558,30 @@ function migrateLLMScopeKeys() {
migrateLLMScopeKeys();
// Groq retired these models on 2026-08-16, so a scope still pointing at one
// 404s on every request. Remap to the closest replacement Groq still serves.
// Runs after migrateLLMScopeKeys so scope values live under their final keys.
const RETIRED_GROQ_MODELS: Record<string, string> = {
"qwen/qwen3-32b": "openai/gpt-oss-120b",
"llama-3.3-70b-versatile": "openai/gpt-oss-120b",
"llama-3.1-8b-instant": "openai/gpt-oss-20b",
};
function migrateRetiredGroqModels() {
if (!isBrowser) return;
if (localStorage.getItem("_retiredGroqModelsMigrated") === "1") return;
for (const { storeKeys } of Object.values(INFERENCE_SCOPES)) {
if (localStorage.getItem(storeKeys.provider) !== "groq") continue;
const replacement = RETIRED_GROQ_MODELS[localStorage.getItem(storeKeys.model) ?? ""];
if (replacement) localStorage.setItem(storeKeys.model, replacement);
}
localStorage.setItem("_retiredGroqModelsMigrated", "1");
}
migrateRetiredGroqModels();
export interface SettingsState
extends
TranscriptionSettings,
@@ -0,0 +1,55 @@
const test = require("node:test");
const assert = require("node:assert/strict");
const { createRendererServer, installBrowserGlobals } = require("../lib/rendererTestHarness");
const modelRegistryData = require("../../src/models/modelRegistryData.json");
// Groq retired qwen3-32b and both Llamas on 2026-08-16. The registry entries
// are gone, so a persisted scope selection would 404 on every request until
// the user re-picked — the migration remaps it to a model Groq still serves.
test("retired groq model selections migrate to served replacements", async (t) => {
const groqIds = modelRegistryData.cloudProviders
.find((provider) => provider.id === "groq")
.models.map((model) => model.id);
assert.ok(
!groqIds.includes("llama-3.3-70b-versatile"),
"retired ids must be out of the registry"
);
const { storage } = installBrowserGlobals(t);
const vite = await createRendererServer(t, {
cachePrefix: "openwhispr-retired-groq-models-test-",
});
const load = async () => {
vite.moduleGraph.invalidateAll();
return vite.ssrLoadModule("/stores/settingsStore.ts");
};
await t.test("groq scopes remap, other providers keep their model", async () => {
storage.clear();
storage.setItem("cleanupProvider", "groq");
storage.setItem("cleanupModel", "llama-3.3-70b-versatile");
storage.setItem("dictationAgentProvider", "groq");
storage.setItem("dictationAgentModel", "llama-3.1-8b-instant");
storage.setItem("chatAgentProvider", "openrouter");
storage.setItem("chatAgentModel", "llama-3.3-70b-versatile");
const { useSettingsStore } = await load();
const state = useSettingsStore.getState();
assert.equal(state.cleanupModel, "openai/gpt-oss-120b");
assert.equal(state.dictationAgentModel, "openai/gpt-oss-20b");
assert.equal(state.chatAgentModel, "llama-3.3-70b-versatile", "openrouter still serves the id");
});
await t.test("migration is one-shot", async () => {
storage.clear();
storage.setItem("_retiredGroqModelsMigrated", "1");
storage.setItem("cleanupProvider", "groq");
storage.setItem("cleanupModel", "llama-3.3-70b-versatile");
const { useSettingsStore } = await load();
assert.equal(useSettingsStore.getState().cleanupModel, "llama-3.3-70b-versatile");
});
});
@@ -24,8 +24,6 @@ const MATRIX = [
{ max_tokens: MAX_TOKENS, temperature: 0, reasoning_effort: "low" }],
["groq gpt-oss suppression floors at low, never none (#990)", "Groq", "openai/gpt-oss-120b", null, CLEANUP_NO_THINK,
{ max_tokens: MAX_TOKENS, temperature: 0, reasoning_effort: "low" }],
["groq qwen is registry-flagged disableThinking and always suppressed", "Groq", "qwen/qwen3-32b", null, CLEANUP,
{ max_tokens: MAX_TOKENS, temperature: 0, reasoning_effort: "none" }],
["groq unknown family gets no guessed reasoning params", "Groq", "llama-3.3-70b-versatile", null, CLEANUP_NO_THINK,
{ max_tokens: MAX_TOKENS, temperature: 0 }],