diff --git a/app/src/client/lib/store.ts b/app/src/client/lib/store.ts index f5986c5..9ac77bc 100644 --- a/app/src/client/lib/store.ts +++ b/app/src/client/lib/store.ts @@ -1,6 +1,7 @@ import { useSyncExternalStore } from "react"; import { DEFAULT_APPS, + PI_MODELS, type AppKey, type PiModel, type PiSettings, @@ -64,10 +65,14 @@ function getPrefs(netid: string): Prefs { if (!prefsCache.has(netid)) { try { const raw = localStorage.getItem(prefsKey(netid)); - prefsCache.set( - netid, - raw ? { ...DEFAULT_PREFS, ...JSON.parse(raw) } : DEFAULT_PREFS - ); + const merged: Prefs = raw + ? { ...DEFAULT_PREFS, ...JSON.parse(raw) } + : DEFAULT_PREFS; + // A retired model choice (e.g. the old "campus") falls back to default. + if (!PI_MODELS.some((m) => m.value === merged.model)) { + merged.model = DEFAULT_PREFS.model; + } + prefsCache.set(netid, merged); } catch { prefsCache.set(netid, DEFAULT_PREFS); } diff --git a/app/src/server/pi.ts b/app/src/server/pi.ts index faba73d..7e20c62 100644 --- a/app/src/server/pi.ts +++ b/app/src/server/pi.ts @@ -37,8 +37,6 @@ export type PiState = { const CLAUDE_MODELS = new Set(["claude-opus-5", "claude-sonnet-5"]); const DEFAULT_CLAUDE_MODEL = "claude-opus-5"; -/** Free-plan-compatible Workers AI model with solid tool calling. */ -const CAMPUS_MODEL = "@cf/zai-org/glm-4.7-flash"; /** The apps PI may ask a student to switch on. */ const APP_KEYS = PI_APPS.map((a) => a.key) as [AppKey, ...AppKey[]]; @@ -153,13 +151,15 @@ export class Pi extends Think { const settings = this.getConfig(); const preference = settings?.model ?? DEFAULT_CLAUDE_MODEL; const key = this.env.ANTHROPIC_API_KEY; - if (preference !== "campus" && key) { - const model = CLAUDE_MODELS.has(preference) - ? preference - : DEFAULT_CLAUDE_MODEL; - return createAnthropic({ apiKey: key })(model); + if (!key) { + // No silent fallback model: fail the turn loudly so ops notices. + throw new Error("ANTHROPIC_API_KEY is not configured"); } - return CAMPUS_MODEL; + // Stale configs may still carry the retired "campus" preference. + const model = CLAUDE_MODELS.has(preference) + ? preference + : DEFAULT_CLAUDE_MODEL; + return createAnthropic({ apiKey: key })(model); } getSystemPrompt() { diff --git a/app/src/shared/apps.ts b/app/src/shared/apps.ts index d9fb711..e1eeaf8 100644 --- a/app/src/shared/apps.ts +++ b/app/src/shared/apps.ts @@ -177,13 +177,12 @@ export function countConnected( export const DEFAULT_ENGINE_BASE = "https://junction-engine.tigerapps.org"; -/** Models the switcher offers. Claude models fall back to campus without a key. */ -export type PiModel = "claude-opus-5" | "claude-sonnet-5" | "campus"; +/** Models the switcher offers. */ +export type PiModel = "claude-opus-5" | "claude-sonnet-5"; export const PI_MODELS: Array<{ value: PiModel; label: string }> = [ { value: "claude-opus-5", label: "Opus 5, sharpest" }, { value: "claude-sonnet-5", label: "Sonnet 5, quicker" }, - { value: "campus", label: "Campus, lightest" }, ]; /** Settings the client pushes to a Pi agent instance. */