From 6cf79096c99f1fb2f96b6f179b80cd271bfd291f Mon Sep 17 00:00:00 2001 From: Lex Date: Sun, 4 Oct 2026 04:23:16 +0800 Subject: [PATCH] fix: order thinking variants and prepare v1.0.62 --- .github/releases/v1.0.62.md | 44 +++++++++++++++++ packages/opencode/src/provider/provider.ts | 1 + packages/opencode/src/provider/transform.ts | 11 +++++ .../opencode/test/provider/provider.test.ts | 49 +++++++++++++++++++ .../opencode/test/provider/transform.test.ts | 35 +++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 .github/releases/v1.0.62.md diff --git a/.github/releases/v1.0.62.md b/.github/releases/v1.0.62.md new file mode 100644 index 0000000000..8cb535ff04 --- /dev/null +++ b/.github/releases/v1.0.62.md @@ -0,0 +1,44 @@ +## opencode 1.0.62 + +Stable release from `main` branch. Keeps existing Ctrl-T model variants in reasoning-effort order. + +--- + +### ๐Ÿ› Bug Fixes + +- Sort existing Ctrl-T model variants by reasoning effort: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, then `ultra`. +- Keep custom non-integer variant names after the known names, preserving their relative order. This changes display order only; it does not add or enable variants or change their saved identity, payload, or default-cycle behavior. + +--- + +### โš™๏ธ CI / Engineering + +- Run GitHub Actions workflows on GitHub-hosted runners. +- Run workspace tests one task at a time on hosted runners to reduce CPU contention and timeout risk. + +--- + +### ๐Ÿงช Test Summary + +```text +Provider, transform, direct-run variant, runtime.boot, and release helpers: 486 passed, 0 failed +Workspace typecheck: 31 tasks passed +Lint: 4834 warnings, 0 errors; unchanged limit of 4850 +Direct footer rendering, layout, and menus: 27 passed, 5 existing skips, 0 failed +app cycle helper: 7 passed, 0 failed +Isolated TUI PTY cycle: default -> none -> minimal -> low -> medium -> high -> xhigh -> max -> ultra -> custom-large -> custom-small -> default; screen and persisted selection matched at every step +``` + +--- + +### ๐Ÿ” Verification + +An isolated source TUI session confirmed the displayed and persisted selection after every Ctrl-T step. The session made no model requests and created no sessions or messages. The test process left no running child process. + +Configuration merging preserves variant parameters, disabled entries, and saved selections. Integer-style custom names keep JavaScript's existing object enumeration behavior. + +Astra's final review of the patch passed. Native CI remains the merge gate. + +--- + +**Full changelog:** [`graphagent-v1.0.61`...`graphagent-v1.0.62`](https://github.com/LeXwDeX/OpenCode-GraphAgent/compare/graphagent-v1.0.61...graphagent-v1.0.62) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 4f89fd38ff..f66a21759b 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1648,6 +1648,7 @@ export const layer = Layer.effect( (v) => omit(v, ["disabled"]), ) } + model.variants = ProviderTransform.sortVariants(model.variants) } if (Object.keys(provider.models).length === 0) { diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index ef3eae58a5..949d73b357 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -568,6 +568,17 @@ export function topK(model: Provider.Model) { const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"] const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"] +const VARIANT_EFFORT_ORDER = [...OPENAI_EFFORTS, "max", "ultra"] + +export function sortVariants(variants: Record): Record { + const rank = (name: string) => { + const index = VARIANT_EFFORT_ORDER.indexOf(name) + return index === -1 ? VARIANT_EFFORT_ORDER.length : index + } + // Custom names have no standard effort ranking. Keep their relative order. + return Object.fromEntries(Object.entries(variants).sort(([a], [b]) => rank(a) - rank(b))) +} + const OPENAI_GPT5_1_EFFORTS = ["none", ...WIDELY_SUPPORTED_EFFORTS] const OPENAI_GPT5_2_PLUS_EFFORTS = [...OPENAI_GPT5_1_EFFORTS, "xhigh"] const OPENAI_GPT5_PRO_EFFORTS = ["high"] diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 9f5de9c2aa..8efb0e1812 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -1428,6 +1428,55 @@ it.instance( }, ) +it.instance( + "model variants are ordered by reasoning effort after config merging", + Effect.gen(function* () { + yield* set("ANTHROPIC_API_KEY", "test-api-key") + const providers = yield* list + const model = providers[ProviderV2.ID.anthropic].models["claude-sonnet-4-20250514"] + expect(Object.keys(model.variants!)).toEqual([ + "none", + "low", + "medium", + "high", + "xhigh", + "max", + "ultra", + "custom-large", + "custom-small", + ]) + expect(model.variants!.high.thinking.budgetTokens).toBe(20000) + expect(model.variants!.max.thinking.budgetTokens).toBe(31999) + expect(model.variants!.minimal).toBeUndefined() + expect(model.variants!.low.reasoningEffort).toBe("low") + expect(model.variants!["custom-large"].budgetTokens).toBe(8000) + expect(model.variants!["custom-small"].budgetTokens).toBe(2000) + }), + { + config: { + provider: { + anthropic: { + models: { + "claude-sonnet-4-20250514": { + variants: { + ultra: { effort: "ultra" }, + "custom-large": { budgetTokens: 8000 }, + xhigh: { effort: "xhigh" }, + medium: { effort: "medium" }, + minimal: { disabled: true }, + low: { reasoningEffort: "low" }, + none: { thinking: { type: "disabled" } }, + high: { thinking: { budgetTokens: 20000 } }, + "custom-small": { budgetTokens: 2000 }, + }, + }, + }, + }, + }, + }, + }, +) + it.instance( "model variants can be customized via config", Effect.gen(function* () { diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 973544e319..50f591620b 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -8,6 +8,41 @@ import { ModelsDev } from "@opencode-ai/core/models-dev" import { generateText, jsonSchema, type ModelMessage } from "ai" import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock" +describe("ProviderTransform.sortVariants", () => { + test("orders known reasoning efforts independently of insertion order", () => { + const variants = Object.fromEntries( + ["max", "medium", "ultra", "high", "low", "xhigh", "none", "minimal"].map((name) => [name, { effort: name }]), + ) + const result = ProviderTransform.sortVariants(variants) + + expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"]) + expect(Object.keys(variants)).toEqual(["max", "medium", "ultra", "high", "low", "xhigh", "none", "minimal"]) + for (const name of Object.keys(variants)) expect(result[name]).toBe(variants[name]) + }) + + test("keeps custom names in their relative order after known efforts", () => { + const variants = { + "custom-large": { budgetTokens: 8000 }, + high: { effort: "high" }, + "custom-small": { budgetTokens: 2000 }, + low: { effort: "low" }, + } + + expect(Object.keys(ProviderTransform.sortVariants(variants))).toEqual([ + "low", + "high", + "custom-large", + "custom-small", + ]) + }) + + test("supports empty, single and custom-only variant sets", () => { + expect(ProviderTransform.sortVariants({})).toEqual({}) + expect(ProviderTransform.sortVariants({ high: {} })).toEqual({ high: {} }) + expect(Object.keys(ProviderTransform.sortVariants({ quality: {}, fast: {} }))).toEqual(["quality", "fast"]) + }) +}) + describe("ProviderTransform.options - setCacheKey", () => { const sessionID = "test-session-123"