From 5df3e8f5120fc08842fa506be1f8c894e6d72825 Mon Sep 17 00:00:00 2001 From: leookun Date: Mon, 3 Aug 2026 22:56:17 +0800 Subject: [PATCH] feat(cursor): update model handling to use agentv1 responses and enhance CLI model details - Refactored response handling in newProtoMessage to utilize agentv1 for GetUsableModelsResponse and GetDefaultModelForCliResponse. - Added new tests for buildCLIModelDetails and encodeCLIModels to ensure correct model details and encoding. - Updated buildUsableModelsPayload and buildDefaultModelForCliPayload to leverage buildCLIModelDetails for improved model data structure. --- internal/backend/server/upstream/client.go | 5 +- internal/backend/server/upstream/mocks.go | 39 ++++++++++++---- .../backend/server/upstream/mocks_test.go | 46 +++++++++++++++++++ 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/internal/backend/server/upstream/client.go b/internal/backend/server/upstream/client.go index 47bc314..dfedcd8 100644 --- a/internal/backend/server/upstream/client.go +++ b/internal/backend/server/upstream/client.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "cursor/gen/agentv1" "cursor/gen/aiserverv1" "cursor/internal/logger" "cursor/internal/netproxy" @@ -433,9 +434,9 @@ func newProtoMessage(typeName string) (proto.Message, error) { case "aiserver.v1.ListMarketplacesResponse": return &aiserverv1.ListMarketplacesResponse{}, nil case "aiserver.v1.GetUsableModelsResponse": - return &aiserverv1.GetUsableModelsResponse{}, nil + return &agentv1.GetUsableModelsResponse{}, nil case "aiserver.v1.GetDefaultModelForCliResponse": - return &aiserverv1.GetDefaultModelForCliResponse{}, nil + return &agentv1.GetDefaultModelForCliResponse{}, nil case "aiserver.v1.GetDefaultModelResponse": return &aiserverv1.GetDefaultModelResponse{}, nil case "aiserver.v1.GetGlobalCommandsResponse": diff --git a/internal/backend/server/upstream/mocks.go b/internal/backend/server/upstream/mocks.go index c299104..cc61898 100644 --- a/internal/backend/server/upstream/mocks.go +++ b/internal/backend/server/upstream/mocks.go @@ -431,8 +431,8 @@ func buildServerTimePayload(*RequestContext) (map[string]any, error) { func buildServerConfigPayload(*RequestContext) (map[string]any, error) { return map[string]any{ - "configVersion": "local_cli_sandbox_defaults_disabled_v2", - // "http2Config": "HTTP2_CONFIG_FORCE_ALL_DISABLED", + "configVersion": "local_cli_sandbox_defaults_disabled_v2", + "http2Config": "HTTP2_CONFIG_FORCE_ALL_DISABLED", "cliSandboxDefaultEnabled": true, "indexingConfig": map[string]any{ "defaultUserPathEncryptionKey": localPathEncryptionKey, @@ -451,6 +451,7 @@ func buildAvailableModelsPayload(reqCtx *RequestContext) (map[string]any, error) if len(modelRefs) > 0 { defaultModel = modelRefs[0] } + modelEntries := buildAvailableModelEntries(adapters) return map[string]any{ "backgroundComposerModelConfig": map[string]any{ "bestOfNDefaultModels": append([]string(nil), modelRefs...), @@ -470,7 +471,7 @@ func buildAvailableModelsPayload(reqCtx *RequestContext) (map[string]any, error) "defaultModel": defaultModel, }, "disableUnusedModelsAfterNHours": availableModelsDisableUnusedHours, - "models": buildAvailableModelEntries(adapters), + "models": modelEntries, "planExecutionModelConfig": map[string]any{ "defaultModel": defaultModel, "fallbackModels": append([]string(nil), modelRefs...), @@ -502,12 +503,7 @@ func buildUsableModelsPayload(reqCtx *RequestContext) (map[string]any, error) { if err != nil { return nil, err } - refs := collectModelAdapterRefs(adapters) - models := make([]map[string]any, 0, len(refs)) - for _, ref := range refs { - models = append(models, map[string]any{"modelName": ref}) - } - return map[string]any{"models": models}, nil + return map[string]any{"models": buildCLIModelDetails(adapters)}, nil } func buildDefaultModelForCliPayload(reqCtx *RequestContext) (map[string]any, error) { @@ -515,7 +511,11 @@ func buildDefaultModelForCliPayload(reqCtx *RequestContext) (map[string]any, err if err != nil { return nil, err } - return map[string]any{"model": map[string]any{"modelName": firstModelAdapterRef(adapters)}}, nil + models := buildCLIModelDetails(adapters) + if len(models) == 0 { + return map[string]any{"model": map[string]any{}}, nil + } + return map[string]any{"model": models[0]}, nil } func buildDefaultModelPayload(reqCtx *RequestContext) (map[string]any, error) { @@ -720,6 +720,25 @@ func buildAvailableModelEntries(adapters []legacyruntime.ModelAdapterConfig) []m return output } +func buildCLIModelDetails(adapters []legacyruntime.ModelAdapterConfig) []map[string]any { + models := make([]map[string]any, 0, len(adapters)) + for _, adapter := range adapters { + channelID := strings.TrimSpace(adapter.ID) + if channelID == "" { + continue + } + models = append(models, map[string]any{ + "modelId": channelID, + "displayModelId": channelID, + "apiKeyCredentials": map[string]any{ + "apiKey": strings.TrimSpace(adapter.APIKey), + "baseUrl": strings.TrimSpace(adapter.BaseURL), + }, + }) + } + return models +} + func buildThinkingEffortParameterDefinitions(adapterType string) []map[string]any { values := thinkingEffortValuesForAdapter(adapterType) options := make([]map[string]any, 0, len(values)) diff --git a/internal/backend/server/upstream/mocks_test.go b/internal/backend/server/upstream/mocks_test.go index 5c878b9..48c4fa6 100644 --- a/internal/backend/server/upstream/mocks_test.go +++ b/internal/backend/server/upstream/mocks_test.go @@ -2,9 +2,55 @@ package upstream import ( "encoding/json" + "reflect" "testing" + + "cursor/gen/agentv1" + legacyruntime "cursor/internal/runtime" + + "google.golang.org/protobuf/proto" ) +func TestBuildCLIModelDetailsPreservesChannelCredentials(t *testing.T) { + adapters := []legacyruntime.ModelAdapterConfig{ + {ID: " channel-a ", ModelID: "model-a", APIKey: "provider-secret-a", BaseURL: "https://provider-a.example/v1"}, + {ID: "channel-b", ModelID: "model-a"}, + {ID: "", ModelID: "model-c"}, + } + + got := buildCLIModelDetails(adapters) + want := []map[string]any{ + {"modelId": "channel-a", "displayModelId": "channel-a", "apiKeyCredentials": map[string]any{"apiKey": "provider-secret-a", "baseUrl": "https://provider-a.example/v1"}}, + {"modelId": "channel-b", "displayModelId": "channel-b", "apiKeyCredentials": map[string]any{"apiKey": "", "baseUrl": ""}}, + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("build CLI model details: got %v, want %v", got, want) + } +} + +func TestEncodeCLIModelsUsesAgentModelDetailsWireFormat(t *testing.T) { + payload := map[string]any{"models": buildCLIModelDetails([]legacyruntime.ModelAdapterConfig{{ID: "channel-a", APIKey: "provider-secret", BaseURL: "https://provider.example/v1"}})} + encoded, err := encodeMockProto("aiserver.v1.GetUsableModelsResponse", payload) + if err != nil { + t.Fatalf("encode CLI models: %v", err) + } + + response := &agentv1.GetUsableModelsResponse{} + if err := proto.Unmarshal(encoded, response); err != nil { + t.Fatalf("decode CLI models with agent proto: %v", err) + } + if len(response.Models) != 1 { + t.Fatalf("decoded model count: got %d, want 1", len(response.Models)) + } + model := response.Models[0] + if model.GetModelId() != "channel-a" || model.GetDisplayModelId() != "channel-a" { + t.Fatalf("decoded channel IDs: model=%q display=%q", model.GetModelId(), model.GetDisplayModelId()) + } + if credentials := model.GetApiKeyCredentials(); credentials == nil || credentials.GetApiKey() != "provider-secret" || credentials.GetBaseUrl() != "https://provider.example/v1" { + t.Fatalf("decoded relay credentials: %#v", credentials) + } +} + func TestBuildBootstrapStatsigConfigJSONDisablesAlwaysLocalDecompositionGate(t *testing.T) { payload, err := buildBootstrapStatsigConfigJSON(12345, "test-auth-id") if err != nil {