gateway: Cursor Private Inference shows its Reasoning control for magpie's models (mamba on Discord) — its picker (cursor-agent-exec 3.23.12) keeps a /models row's capabilities only when they say the model calls tools, streams and writes text, and magpie's said neither, so every model was listed with no Reasoning control. The rows Cursor Private Inference is shown now carry supports_tool_use, supports_streaming, output_modalities [text], supports_reasoning and the model's levels (reasoning_effort), and a Claude served on Anthropic Messages is listed there alone, the only API Cursor sends a Claude's level (output_config.effort) on. Which levels it offers Cursor still decides from the id (Claude Opus/Sonnet 4.5+, GPT-5.x, Grok 4.x, Gemini); it has no Max toggle for a gateway's models. TestCursorLocalModels fails without the change. go vet, go build (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass but for TestAccountsOfAPlugin, which fails on main as well.
This commit is contained in:
1 parent
1aaac93b52
commit
7118aaabcc
2 files changed
+50
-3
No files matched your search
@@ -17,10 +17,16 @@ import (
|
||||
// by any other client, the list is as it was.
|
||||
func TestCursorLocalModels(t *testing.T) {
|
||||
setup(t, provider.Anthropic, &fake{})
|
||||
if err := provider.Save(provider.Provider{ID: "ds", Name: "DS", Key: "k", Chat: "http://127.0.0.1:1/v1", Models: []string{"flash"}}); err != nil {
|
||||
if err := provider.Save(provider.Provider{ID: "ds", Name: "DS", Key: "k", Chat: "http://127.0.0.1:1/v1", Models: []string{"flash", "claude-sonnet-4-6"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := catalog.SaveLive("ds", "http://127.0.0.1:1/v1", []catalog.Model{{ID: "flash", Context: 128000, Output: 8192}}); err != nil {
|
||||
if err := catalog.SaveLive("ds", "http://127.0.0.1:1/v1", []catalog.Model{{ID: "flash", Context: 128000, Output: 8192}, {ID: "claude-sonnet-4-6"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := provider.Save(provider.Provider{ID: "or", Name: "OR", Key: "k", Chat: "http://127.0.0.1:2/v1", Anthropic: "http://127.0.0.1:2", Models: []string{"claude-opus-4-7"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := catalog.SaveLive("or", "http://127.0.0.1:2/v1", []catalog.Model{{ID: "claude-opus-4-7", Efforts: []string{"low", "medium", "high"}}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
list := func(key string) map[string]map[string]any {
|
||||
@@ -64,6 +70,31 @@ func TestCursorLocalModels(t *testing.T) {
|
||||
if c["context_length"] != float64(128000) || c["max_output_tokens"] != float64(8192) {
|
||||
t.Errorf("capabilities = %v", ds["capabilities"])
|
||||
}
|
||||
// its picker drops capabilities that don't say the model streams, calls
|
||||
// tools and writes text, and with them its Reasoning control (mamba on
|
||||
// Discord); one that thinks says so, with its levels
|
||||
if c["supports_tool_use"] != true || c["supports_streaming"] != true || c["supports_reasoning"] != false {
|
||||
t.Errorf("capabilities = %v", c)
|
||||
}
|
||||
if mods, _ := c["output_modalities"].([]any); len(mods) != 1 || mods[0] != "text" {
|
||||
t.Errorf("output_modalities = %v", c["output_modalities"])
|
||||
}
|
||||
cl := got["or/claude-opus-4-7"]
|
||||
if cl == nil {
|
||||
t.Fatalf("models: %v", got)
|
||||
}
|
||||
cc, _ := cl["capabilities"].(map[string]any)
|
||||
if efforts, _ := cc["reasoning_effort"].([]any); cc["supports_reasoning"] != true || len(efforts) != 3 {
|
||||
t.Errorf("a thinking model's capabilities = %v", cc)
|
||||
}
|
||||
// it sends a Claude's level on Anthropic Messages only, so a Claude
|
||||
// served on both is listed there alone; a Chat-only one stays on Chat
|
||||
if ts := types(cl); !slices.Equal(ts, []string{"anthropic_messages"}) {
|
||||
t.Errorf("a Claude on Chat and Messages: api_types = %v", ts)
|
||||
}
|
||||
if ts := types(got["ds/claude-sonnet-4-6"]); !slices.Equal(ts, []string{"chat_completions"}) {
|
||||
t.Errorf("a Claude on Chat alone: api_types = %v", ts)
|
||||
}
|
||||
|
||||
for _, key := range []string{Token, TokenFor("cursor"), TokenFor("opencode")} {
|
||||
for id, m := range list(key) {
|
||||
|
||||
@@ -570,6 +570,14 @@ func modelObject(e provider.Entry) map[string]any {
|
||||
// keeps its thinking; any, for one translated anyway — and capabilities,
|
||||
// its window, output and whether it takes images, without which it falls
|
||||
// back to Chat and limits of its own.
|
||||
//
|
||||
// Its picker (cursor-agent-exec, 3.23.12) keeps a row's capabilities only
|
||||
// when they say it streams, calls tools and writes text (supports_tool_use,
|
||||
// supports_streaming, output_modalities); without them it shows the model
|
||||
// with no Reasoning control (mamba on Discord). Which levels it offers it
|
||||
// decides itself, from the id after its last "/" (claude-opus-4-7, gpt-5.5,
|
||||
// gemini-*, which needs supports_reasoning), and it sends a Claude's level
|
||||
// only on Anthropic Messages, so a Claude any API would do is asked there.
|
||||
func cursorLocalModel(m map[string]any, e provider.Entry) {
|
||||
names := map[string]string{"/v1/chat/completions": "chat_completions", "/v1/responses": "responses", "/v1/messages": "anthropic_messages"}
|
||||
types := []string{}
|
||||
@@ -579,8 +587,16 @@ func cursorLocalModel(m map[string]any, e provider.Entry) {
|
||||
if len(types) == 0 {
|
||||
types = []string{"chat_completions", "responses", "anthropic_messages"}
|
||||
}
|
||||
bare := strings.ToLower(e.ID[strings.LastIndex(e.ID, "/")+1:])
|
||||
if strings.HasPrefix(bare, "claude-") && slices.Contains(types, "anthropic_messages") {
|
||||
types = []string{"anthropic_messages"}
|
||||
}
|
||||
m["api_types"] = types
|
||||
c := map[string]any{}
|
||||
c := map[string]any{"supports_tool_use": true, "supports_streaming": true, "output_modalities": []string{"text"},
|
||||
"supports_reasoning": e.Reasoning || len(e.Efforts) > 0}
|
||||
if len(e.Efforts) > 0 {
|
||||
c["reasoning_effort"] = e.Efforts
|
||||
}
|
||||
if e.Context > 0 {
|
||||
c["context_length"] = e.Context
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user