Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions app/src/client/lib/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useSyncExternalStore } from "react";
import {
DEFAULT_APPS,
PI_MODELS,
type AppKey,
type PiModel,
type PiSettings,
Expand Down Expand Up @@ -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);
}
Expand Down
16 changes: 8 additions & 8 deletions app/src/server/pi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[]];
Expand Down Expand Up @@ -153,13 +151,15 @@ export class Pi extends Think<Env, PiState> {
const settings = this.getConfig<PiSettings>();
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() {
Expand Down
5 changes: 2 additions & 3 deletions app/src/shared/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Loading