From 7865634737116c5bf3e6ab4d5b15814e230e7c93 Mon Sep 17 00:00:00 2001 From: Maximilian Kaske <56969857+mxkaske@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:25:24 +0200 Subject: [PATCH] feat(ai): use Claude Haiku 5.5 (free) and Sonnet 5.5 (paid) (#2869) * feat(ai): use Claude Haiku 5.5 for free-tier workspaces Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PrErTUMXrWWrwj9e8adRzY * feat(ai): use Claude Sonnet 5.5 for paid workspaces Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PrErTUMXrWWrwj9e8adRzY * fix(dashboard): force native structured output for AI filters Newer Claude models reject the forced tool_choice the AI SDK's JSON-tool fallback relies on; the upstream 400 surfaced client-side as an empty stream ("Type validation failed: Value: undefined"). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PrErTUMXrWWrwj9e8adRzY * fix(dashboard): pass AI filter system prompt via instructions AI SDK v7 rejects system messages inside `messages` (AI_InvalidPromptError), which surfaced client-side as an empty stream. Also reverts the speculative structuredOutputMode override from 6c32f6a, which wasn't the cause. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PrErTUMXrWWrwj9e8adRzY --------- Co-authored-by: Claude --- .../src/lib/ai/create-ai-filter-handler.ts | 15 ++++++++------- packages/ai/src/resolve-model.test.ts | 4 ++-- packages/ai/src/resolve-model.ts | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/src/lib/ai/create-ai-filter-handler.ts b/apps/dashboard/src/lib/ai/create-ai-filter-handler.ts index a855c78bf..ef9e53ed0 100644 --- a/apps/dashboard/src/lib/ai/create-ai-filter-handler.ts +++ b/apps/dashboard/src/lib/ai/create-ai-filter-handler.ts @@ -61,14 +61,15 @@ export function createAIFilterHandler({ const result = streamObject({ model, schema: outputSchema, - messages: [ - { - role: "system", - content: staticPrompt, - providerOptions: { - anthropic: { cacheControl: { type: "ephemeral" } }, - }, + // AI SDK v7 rejects system messages inside `messages`. + instructions: { + role: "system", + content: staticPrompt, + providerOptions: { + anthropic: { cacheControl: { type: "ephemeral" } }, }, + }, + messages: [ { role: "user", content: `Current date/time: ${now}\n\nQuery: ${query}`, diff --git a/packages/ai/src/resolve-model.test.ts b/packages/ai/src/resolve-model.test.ts index 21181ceaf..c9d7c6921 100644 --- a/packages/ai/src/resolve-model.test.ts +++ b/packages/ai/src/resolve-model.test.ts @@ -25,14 +25,14 @@ describe("resolveChatModel", () => { test("gateway path: free plan resolves the cheaper model string", () => { process.env.AI_GATEWAY_API_KEY = "gw-key"; expect(resolveChatModel({ plan: "free" })).toBe( - "anthropic/claude-haiku-4.5", + "anthropic/claude-haiku-5.5", ); }); test("gateway path: paid plan resolves the stronger model string", () => { process.env.AI_GATEWAY_API_KEY = "gw-key"; expect(resolveChatModel({ plan: "team" })).toBe( - "anthropic/claude-sonnet-5", + "anthropic/claude-sonnet-5.5", ); }); diff --git a/packages/ai/src/resolve-model.ts b/packages/ai/src/resolve-model.ts index 97916aea0..f81c47551 100644 --- a/packages/ai/src/resolve-model.ts +++ b/packages/ai/src/resolve-model.ts @@ -5,8 +5,8 @@ import type { LanguageModel } from "ai"; // Hosted openstatus.dev defaults, resolved through the Vercel AI Gateway as // bare model strings. Free workspaces get the cheaper model because we pay for // their inference; this tiering applies ONLY to the gateway path. -const GATEWAY_MODEL_FREE = "anthropic/claude-haiku-4.5"; -const GATEWAY_MODEL_PAID = "anthropic/claude-sonnet-5"; +const GATEWAY_MODEL_FREE = "anthropic/claude-haiku-5.5"; +const GATEWAY_MODEL_PAID = "anthropic/claude-sonnet-5.5"; // `||` (not `??`) so empty / whitespace-only env values fall back instead of // being passed through as a real value. -- 2.51.2