mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
b752080204
* feat(ai): add Claude Opus 5 to the model catalogs Adds `claude-opus-5` to both static Claude model lists: - `packages/ai/providers/claude-agent-sdk.ts` — the Ask AI picker - `packages/ui/components/AgentsTab.tsx` — the review-agent picker, which `TOUR_CLAUDE_MODELS` spreads, so tour and guide pick it up too No `claude-opus-5[1m]` variant: the `[1m]` suffix opts the 4.x models into the larger context window, and the 5-series models are already 1M by default — which is why the existing `claude-fable-5` and `claude-sonnet-5` entries carry no variant either. A test pins that invariant. Guide, tour, and marker review need no change — they pass the picked model straight through to the CLI with no server-side allowlist. Tests cover the Bedrock/Vertex family matcher accepting the new bare alias (a matcher keyed on "opus-4" would have silently dropped it) and pin the existing Fable fallthrough, which has no ANTHROPIC_DEFAULT_*_MODEL of its own. Also refreshes the marketing Ask AI model list, which had gone stale at Sonnet 4.6 / Opus 4.6 / Haiku 4.5. * feat(ai): default Claude review agents to Opus 5 Bumps the two Claude review-agent defaults from Opus 4.7 to Opus 5: - `DEFAULT_CLAUDE_MODEL` (packages/ui/hooks/useAgentSettings.ts) — the review-agent picker's default for users with no saved cookie preference - `buildClaudeCommand`'s default parameter (packages/server/claude-review.ts) — the server-side fallback when a job arrives with no model BEHAVIOR CHANGE: users who have never picked a Claude review model move from Opus 4.7 to Opus 5. Saved picks are untouched — the cookie already holds an explicit model for anyone who has chosen one. Kept deliberately separate from the catalog commit so it can be reverted on its own if the maintainer prefers to ship the new models as options only. Ask AI's default (`DEFAULT_MODEL` in claude-agent-sdk.ts) stays on Sonnet 5, and the tour/guide defaults stay on the `sonnet` latest-alias — those are interactive, latency-sensitive surfaces where the cheaper tier is the intentional choice.
114 lines
4.3 KiB
TypeScript
114 lines
4.3 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { CLAUDE_MODELS, CODEX_MODELS, TOUR_CLAUDE_MODELS, codexReasoningOptions } from "./AgentsTab";
|
|
|
|
const catalogEntry = (value: string) => CODEX_MODELS.find((m) => m.value === value);
|
|
|
|
describe("CLAUDE_MODELS catalog", () => {
|
|
test("offers the current 5-series flagships", () => {
|
|
for (const value of ["claude-fable-5", "claude-opus-5", "claude-sonnet-5"]) {
|
|
expect(CLAUDE_MODELS.some((m) => m.value === value)).toBe(true);
|
|
}
|
|
expect(CLAUDE_MODELS.find((m) => m.value === "claude-opus-5")?.label).toBe("Opus 5");
|
|
});
|
|
|
|
// The 5-series models are 1M-context by default, so they carry no `[1m]`
|
|
// variant — that suffix exists only to opt the older 4.x models into the
|
|
// larger window. Pinned so a future entry doesn't invent `claude-opus-5[1m]`.
|
|
test("5-series entries have no [1m] context variant", () => {
|
|
for (const { value } of CLAUDE_MODELS) {
|
|
if (/-5(\[|$)/.test(value)) expect(value).not.toContain("[1m]");
|
|
}
|
|
});
|
|
|
|
test("tour/guide catalog inherits every review model plus the latest aliases", () => {
|
|
for (const { value } of CLAUDE_MODELS) {
|
|
expect(TOUR_CLAUDE_MODELS.some((m) => m.value === value)).toBe(true);
|
|
}
|
|
for (const alias of ["sonnet", "opus", "fable"]) {
|
|
expect(TOUR_CLAUDE_MODELS.some((m) => m.value === alias)).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
test("uses the canonical GPT-5.6 Sol model ID", () => {
|
|
expect(catalogEntry("gpt-5.6-sol")?.label).toBe("GPT-5.6 Sol");
|
|
expect(CODEX_MODELS.some(({ value }) => value === "gpt-5.6")).toBe(false);
|
|
});
|
|
|
|
describe("CODEX_MODELS catalog", () => {
|
|
test("omits models rejected or API-shut-down for every auth mode", () => {
|
|
// gpt-5.3-codex: ChatGPT-account Codex rejects it outright. The other
|
|
// three: API-level shutdown 2026-07-23 per OpenAI's deprecations page.
|
|
for (const value of ["gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-max", "gpt-5.1-codex-mini"]) {
|
|
expect(CODEX_MODELS.some((m) => m.value === value)).toBe(false);
|
|
}
|
|
});
|
|
|
|
test("keeps gpt-5.2 (still API-valid) with the historical effort set", () => {
|
|
expect(catalogEntry("gpt-5.2")?.efforts).toEqual(["low", "medium", "high", "xhigh"]);
|
|
});
|
|
|
|
test("GPT-5.6 family carries the CLI catalog's efforts and defaults", () => {
|
|
expect(catalogEntry("gpt-5.6-sol")).toEqual({
|
|
value: "gpt-5.6-sol",
|
|
label: "GPT-5.6 Sol",
|
|
efforts: ["low", "medium", "high", "xhigh", "max", "ultra"],
|
|
defaultEffort: "low",
|
|
});
|
|
expect(catalogEntry("gpt-5.6-terra")).toEqual({
|
|
value: "gpt-5.6-terra",
|
|
label: "GPT-5.6 Terra",
|
|
efforts: ["low", "medium", "high", "xhigh", "max", "ultra"],
|
|
defaultEffort: "medium",
|
|
});
|
|
expect(catalogEntry("gpt-5.6-luna")).toEqual({
|
|
value: "gpt-5.6-luna",
|
|
label: "GPT-5.6 Luna",
|
|
efforts: ["low", "medium", "high", "xhigh", "max"],
|
|
defaultEffort: "medium",
|
|
});
|
|
});
|
|
|
|
test("spark defaults to high; 5.5/5.4 default to medium", () => {
|
|
expect(catalogEntry("gpt-5.3-codex-spark")?.defaultEffort).toBe("high");
|
|
expect(catalogEntry("gpt-5.5")?.defaultEffort).toBe("medium");
|
|
expect(catalogEntry("gpt-5.4")?.defaultEffort).toBe("medium");
|
|
expect(catalogEntry("gpt-5.4-mini")?.defaultEffort).toBe("medium");
|
|
});
|
|
|
|
test("no model offers minimal, and every default effort is supported", () => {
|
|
for (const model of CODEX_MODELS) {
|
|
expect(model.efforts).not.toContain("minimal");
|
|
expect(model.efforts).toContain(model.defaultEffort);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("codexReasoningOptions", () => {
|
|
test("offers only the selected model's efforts, with Max/Ultra labels", () => {
|
|
expect(codexReasoningOptions("gpt-5.6-sol")).toEqual([
|
|
{ value: "low", label: "Low" },
|
|
{ value: "medium", label: "Medium" },
|
|
{ value: "high", label: "High" },
|
|
{ value: "xhigh", label: "XHigh" },
|
|
{ value: "max", label: "Max" },
|
|
{ value: "ultra", label: "Ultra" },
|
|
]);
|
|
expect(codexReasoningOptions("gpt-5.5").map(({ value }) => value)).toEqual([
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
]);
|
|
});
|
|
|
|
test("falls back to the low..xhigh set for unknown models", () => {
|
|
expect(codexReasoningOptions("future-codex-model").map(({ value }) => value)).toEqual([
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
]);
|
|
});
|
|
});
|