diff --git a/internal/gateway/desktop.go b/internal/gateway/desktop.go index 23586f93..dc30879c 100644 --- a/internal/gateway/desktop.go +++ b/internal/gateway/desktop.go @@ -150,10 +150,12 @@ func DesktopID(e provider.Entry) string { return claudeLooking(e) } // picked for it (provider.CatalogFor, the Agents page's model list) by an id // it keeps (claudeLooking), named by its own name alone (蓝猫: "DeepSeek // V4.1 Flash", not with its provider's beside it). Desktop shows the name, -// not the id, and its description after it, so none is given; it folds rows +// not the id, and its description after it, so none is given but a 1M +// entry's (desktop1M); it folds rows // of one name into one entry, so two models of one name keep their // provider's after it (desktopNames). A model of 1M or more is listed by -// its "[1m]" id under settings.DesktopLongest (desktop1M). +// its "[1m]" id under settings.DesktopLongest, described as Desktop's own +// 1M entry is (desktop1M). func desktopModels(entries []provider.Entry) []map[string]any { names := desktopNames(entries) // the tier each model stands in for: the user's pick (a model picked @@ -174,6 +176,7 @@ func desktopModels(entries []provider.Entry) []map[string]any { m["id"] = claudeLooking(e) if longest && e.Context >= desktop1M { m["id"] = m["id"].(string) + "[1m]" + m["description"] = desktop1MSaid } if t := tierOf[e.ID]; t != "" { m["anthropic_family_tier"], m["is_family_default"] = t, true @@ -192,8 +195,22 @@ func desktopModels(entries []provider.Entry) []map[string]any { // model is listed by that 1M id alone (#1272), which magpie serves as the // plain one (aliased, provider.Resolve take "[1m]" off), so a session // saved on the plain id still runs. +// +// Desktop says which entry is the 1M one only on the entry it adds: its +// description is "1M context window" (WLt in JLt, 2.7032). A row of +// magpie's whose id has "[1m]" gets nothing of that (PIt gives supports1m +// to a bare id alone, and OIt folds a "[1m]" row only into a bare one +// beside it), so it was shown as the plain model, by name alone, and the +// switch read as doing the reverse of what it says (杰多夫 on Discord). +// The row is described as Desktop describes its own 1M entry +// (desktop1MSaid); its id, which Desktop hands Claude Code, has the "[1m]" +// that gives a session the 1M window. const desktop1M = 1_000_000 +// desktop1MSaid is the description Claude Desktop gives the 1M entry it +// adds (WLt in its app.asar, 2.7032). +const desktop1MSaid = "1M context window" + // Claude Desktop's Code tab runs Claude Code with ANTHROPIC_DEFAULT__MODEL // set to "" for every tier, unless the gateway's /v1/models tags a model // with anthropic_family_tier (shortnameIdentityOverrides in its app.asar, diff --git a/internal/gateway/desktop_longest_test.go b/internal/gateway/desktop_longest_test.go index 86c5e8b7..0eb093a2 100644 --- a/internal/gateway/desktop_longest_test.go +++ b/internal/gateway/desktop_longest_test.go @@ -13,7 +13,9 @@ import ( // Claude Desktop shows a gateway model of 1M tokens or more twice, its // plain entry and a "1M context window" one it adds itself (#1272: 21 // models made 34 entries). With DesktopLongest on, such a model is listed -// by its "[1m]" id alone, so Desktop's menu has one entry per model; one +// by its "[1m]" id alone, so Desktop's menu has one entry per model, told +// as the 1M one by the "1M context window" Desktop gives its own (a lone +// "[1m]" row was shown as the plain model: 杰多夫 on Discord); one // under 1M (872K) keeps its own id and window, and a request on the plain id // a session was saved with is still served. func TestClaudeDesktopLongestOnly(t *testing.T) { @@ -37,6 +39,7 @@ func TestClaudeDesktopLongestOnly(t *testing.T) { type row struct { ID string `json:"id"` MaxInput int `json:"max_input_tokens"` + Said string `json:"description"` } list := func() map[string]row { t.Helper() @@ -96,6 +99,16 @@ func TestClaudeDesktopLongestOnly(t *testing.T) { t.Errorf("on: %s listed as %q, want %q", id, r.ID, want) } } + for id, r := range on { + if want := map[bool]string{true: "1M context window"}[strings.HasSuffix(r.ID, "[1m]")]; r.Said != want { + t.Errorf("on: %s (%s) described %q, want %q", id, r.ID, r.Said, want) + } + } + for id, r := range off { + if r.Said != "" { + t.Errorf("off: %s described %q", id, r.Said) + } + } if r := on["vend/exo-free"]; r.MaxInput != 872_000 { t.Errorf("exo-free's window told as %d, want its own 872000", r.MaxInput) }