mirror of
https://github.com/cline/cline.git
synced 2026-10-25 10:26:36 +08:00
feat(vscode): check the saved cloud model against the catalog and key the ClinePass default on the local provider
The cloud default is now: the Settings pick if the cloud catalog offers it, else the local Cline or ClinePass model if offered, else the first ClinePass model when the local provider is ClinePass, else anthropic/claude-sonnet-5.5. The ClinePass plan check now only feeds the Settings subscribe notice.
This commit is contained in:
@@ -753,39 +753,41 @@ describe("SdkCloudSessionCoordinator ownership", () => {
|
||||
await coordinator.dispose()
|
||||
})
|
||||
|
||||
it("starts from the local model the catalog offers, else ClinePass for a subscriber, unless a cloud model was picked", async () => {
|
||||
const plan = deferred<boolean | undefined>()
|
||||
const globalState: Record<string, unknown> = {}
|
||||
let apiConfig: Record<string, unknown> = { actModeApiProvider: "cline-pass", actModeClinePassModelId: "cline-pass/local" }
|
||||
it("starts from the saved or local model the catalog offers, else ClinePass for a local ClinePass user, else Cline's default", async () => {
|
||||
const catalog = deferred<CloudModel[]>()
|
||||
const globalState: Record<string, unknown> = { cloudModelId: "usage/retired-pick" }
|
||||
let apiConfig: Record<string, unknown> = { actModeApiProvider: "cline-pass", actModeClinePassModelId: "cline-free/local" }
|
||||
const { coordinator, options } = makeCoordinator({
|
||||
stateManager: { ...makeStateManager(globalState), getApiConfiguration: () => apiConfig } as never,
|
||||
getAccountScope: () => "user-1:",
|
||||
loadCloudModels: async () => [
|
||||
{ id: "cline-pass/first", name: "First", catalogId: "cline-pass" },
|
||||
{ id: "cline-pass/local", name: "Local", catalogId: "cline-pass" },
|
||||
{ id: "usage/model", name: "Usage", catalogId: "cline" },
|
||||
],
|
||||
checkClinePassSubscription: vi.fn(() => plan.promise),
|
||||
loadCloudModels: () => catalog.promise,
|
||||
// The plan check only feeds the Settings notice; the default doesn't read it.
|
||||
checkClinePassSubscription: async () => false,
|
||||
})
|
||||
|
||||
// A local ClinePass model carries over without waiting for the plan check.
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/local")
|
||||
await vi.waitFor(() => expect(options.postStateToWebview).toHaveBeenCalledTimes(1))
|
||||
apiConfig = { actModeApiProvider: "anthropic" }
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
// Until the catalog is known, the Settings pick is assumed to be offered.
|
||||
expect(coordinator.nextCloudModelId()).toBe("usage/retired-pick")
|
||||
catalog.resolve([
|
||||
{ id: "cline-pass/first", name: "First", catalogId: "cline-pass" },
|
||||
{ id: "cline-pass/local", name: "Local", catalogId: "cline-pass" },
|
||||
{ id: "usage/model", name: "Usage", catalogId: "cline" },
|
||||
])
|
||||
await vi.waitFor(() => expect(options.postStateToWebview).toHaveBeenCalled())
|
||||
|
||||
plan.resolve(true)
|
||||
await vi.waitFor(() => expect(coordinator.clinePassSubscribed()).toBe(true))
|
||||
expect(options.postStateToWebview).toHaveBeenCalledTimes(2)
|
||||
// A pick the catalog no longer offers falls through; a ClinePass provider user's free model isn't offered either.
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/first")
|
||||
globalState.cloudModelId = "usage/model"
|
||||
expect(coordinator.nextCloudModelId()).toBe("usage/model")
|
||||
delete globalState.cloudModelId
|
||||
|
||||
apiConfig = { actModeApiProvider: "cline-pass", actModeClinePassModelId: "cline-pass/local" }
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/local")
|
||||
apiConfig = { actModeApiProvider: "cline", actModeClineModelId: "usage/model" }
|
||||
expect(coordinator.nextCloudModelId()).toBe("usage/model")
|
||||
apiConfig = { actModeApiProvider: "cline", actModeClineModelId: "usage/retired" }
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/first")
|
||||
expect(options.checkClinePassSubscription).toHaveBeenCalledTimes(1)
|
||||
|
||||
globalState.cloudModelId = "usage/picked"
|
||||
expect(coordinator.nextCloudModelId()).toBe("usage/picked")
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
apiConfig = { actModeApiProvider: "anthropic" }
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
await coordinator.dispose()
|
||||
})
|
||||
|
||||
|
||||
@@ -902,16 +902,18 @@ export class SdkCloudSessionCoordinator {
|
||||
|
||||
/**
|
||||
* The Cline model a new sandbox runs: the cloud model picked in Settings,
|
||||
* else the user's Act-mode Cline or ClinePass model when the cloud catalog
|
||||
* offers it, else the first ClinePass model for a subscriber, else Cline's
|
||||
* default model. Until the catalog is known, a local Cline or ClinePass
|
||||
* model is assumed to be offered. Synchronous, and the only source for both
|
||||
* else the user's Act-mode Cline or ClinePass model, each when the cloud
|
||||
* catalog offers it; else the first ClinePass model when the local provider
|
||||
* is ClinePass, else Cline's default model. Until the catalog is known, the
|
||||
* Settings pick and a local Cline or ClinePass model are assumed to be
|
||||
* offered. Synchronous, and the only source for both
|
||||
* the composer label and the start path, so the sandbox runs the model the
|
||||
* user saw when they submitted.
|
||||
*/
|
||||
nextCloudModelId(): string {
|
||||
const catalog = this.accountCloudModels()?.models
|
||||
const chosen = this.options.stateManager.getGlobalStateKey("cloudModelId")?.trim()
|
||||
if (chosen) {
|
||||
if (chosen && (!catalog || catalog.some((model) => model.id === chosen))) {
|
||||
return chosen
|
||||
}
|
||||
const apiConfig = this.options.stateManager.getApiConfiguration()
|
||||
@@ -921,8 +923,6 @@ export class SdkCloudSessionCoordinator {
|
||||
: apiConfig.actModeApiProvider === "cline-pass"
|
||||
? apiConfig.actModeClinePassModelId?.trim()
|
||||
: undefined
|
||||
const account = this.accountCloudModels()
|
||||
const catalog = account?.models
|
||||
// The local ClinePass provider also offers free models, which the cloud does not take.
|
||||
const offered = catalog
|
||||
? catalog.some((model) => model.id === local)
|
||||
@@ -930,7 +930,10 @@ export class SdkCloudSessionCoordinator {
|
||||
if (local && offered) {
|
||||
return local
|
||||
}
|
||||
const firstPass = account?.subscribed ? catalog?.find((model) => model.catalogId === "cline-pass")?.id : undefined
|
||||
const firstPass =
|
||||
apiConfig.actModeApiProvider === "cline-pass"
|
||||
? catalog?.find((model) => model.catalogId === "cline-pass")?.id
|
||||
: undefined
|
||||
return firstPass ?? CLINE_DEFAULT_MODEL_ID
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user