mirror of
https://github.com/cline/cline.git
synced 2026-09-12 00:50:27 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fb7619a18 | ||
|
|
f81afb51b5 |
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [4.0.1]
|
||||
|
||||
### Changed
|
||||
|
||||
- Roll the stable VS Code extension back to the pre-SDK-migration codebase to resolve regressions reported in 4.0.0. This release ships the 3.89.2 extension code under a higher version number so existing 4.0.0 users receive the update. SDK-migration work continues separately on `main`.
|
||||
|
||||
## [3.89.2]
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "claude-dev",
|
||||
"displayName": "Cline",
|
||||
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
|
||||
"version": "3.89.2",
|
||||
"version": "4.0.1",
|
||||
"icon": "assets/icons/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.84.0"
|
||||
|
||||
@@ -921,6 +921,7 @@ export class Controller {
|
||||
const environment = clineConfig.environment
|
||||
const banners = BannerService.get().getActiveBanners() ?? []
|
||||
const welcomeBanners = BannerService.get().getWelcomeBanners() ?? []
|
||||
const modelsDevProviderModels = this.stateManager.getModelsDevProviderModelsCache() ?? undefined
|
||||
|
||||
// Check OpenAI Codex authentication status
|
||||
const { openAiCodexOAuthManager } = await import("@/integrations/openai-codex/oauth")
|
||||
@@ -969,6 +970,7 @@ export class Controller {
|
||||
isNewUser,
|
||||
welcomeViewCompleted,
|
||||
onboardingModels,
|
||||
modelsDevProviderModels,
|
||||
mcpResponsesCollapsed,
|
||||
terminalOutputLineLimit,
|
||||
maxConsecutiveMistakes,
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import { ensureCacheDirectoryExists, GlobalFileNames } from "@core/storage/disk"
|
||||
import { applyModelsDevProviderModels, type ModelsDevProviderModels, normalizeModelsDevProviderModels } from "@shared/models-dev"
|
||||
import { fileExistsAtPath } from "@utils/fs"
|
||||
import axios from "axios"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { StateManager } from "@/core/storage/StateManager"
|
||||
import { getAxiosSettings } from "@/shared/net"
|
||||
import { Logger } from "@/shared/services/Logger"
|
||||
|
||||
export const MODELS_DEV_CATALOG_URL = "https://models.dev/api.json"
|
||||
|
||||
let pendingRefresh: Promise<ModelsDevProviderModels> | null = null
|
||||
|
||||
export async function refreshModelsDevProviderModels(): Promise<ModelsDevProviderModels> {
|
||||
const cache = StateManager.get().getModelsDevProviderModelsCache()
|
||||
if (cache) {
|
||||
applyModelsDevProviderModels(cache)
|
||||
return cache
|
||||
}
|
||||
|
||||
if (pendingRefresh) {
|
||||
return pendingRefresh
|
||||
}
|
||||
|
||||
pendingRefresh = (async () => {
|
||||
try {
|
||||
return await fetchAndCacheModelsDevProviderModels()
|
||||
} finally {
|
||||
pendingRefresh = null
|
||||
}
|
||||
})()
|
||||
|
||||
return pendingRefresh
|
||||
}
|
||||
|
||||
async function fetchAndCacheModelsDevProviderModels(): Promise<ModelsDevProviderModels> {
|
||||
const modelsDevProviderModelsFilePath = path.join(await ensureCacheDirectoryExists(), GlobalFileNames.modelsDevProviderModels)
|
||||
|
||||
let providerModels: ModelsDevProviderModels = {}
|
||||
try {
|
||||
const response = await axios.get(MODELS_DEV_CATALOG_URL, getAxiosSettings())
|
||||
providerModels = normalizeModelsDevProviderModels(response.data)
|
||||
|
||||
if (Object.keys(providerModels).length === 0) {
|
||||
throw new Error("No supported models.dev provider models found")
|
||||
}
|
||||
|
||||
await fs.writeFile(modelsDevProviderModelsFilePath, JSON.stringify(providerModels))
|
||||
Logger.log("models.dev provider models fetched and saved")
|
||||
} catch (error) {
|
||||
Logger.error("Error fetching models.dev provider models:", error)
|
||||
|
||||
const cachedModels = await readModelsDevProviderModelsFromCache()
|
||||
if (cachedModels && Object.keys(cachedModels).length > 0) {
|
||||
providerModels = cachedModels
|
||||
Logger.log("Loaded models.dev provider models from cache")
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(providerModels).length > 0) {
|
||||
applyModelsDevProviderModels(providerModels)
|
||||
StateManager.get().setModelsDevProviderModelsCache(providerModels)
|
||||
}
|
||||
|
||||
return providerModels
|
||||
}
|
||||
|
||||
export async function readModelsDevProviderModelsFromCache(): Promise<ModelsDevProviderModels | undefined> {
|
||||
try {
|
||||
const modelsDevProviderModelsFilePath = path.join(
|
||||
await ensureCacheDirectoryExists(),
|
||||
GlobalFileNames.modelsDevProviderModels,
|
||||
)
|
||||
const fileExists = await fileExistsAtPath(modelsDevProviderModelsFilePath)
|
||||
if (fileExists) {
|
||||
const fileContents = await fs.readFile(modelsDevProviderModelsFilePath, "utf8")
|
||||
return JSON.parse(fileContents)
|
||||
}
|
||||
} catch (error) {
|
||||
Logger.error("Error reading cached models.dev provider models:", error)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { refreshClineModels } from "../models/refreshClineModels"
|
||||
import { refreshGroqModels } from "../models/refreshGroqModels"
|
||||
import { refreshHicapModels } from "../models/refreshHicapModels"
|
||||
import { refreshLiteLlmModels } from "../models/refreshLiteLlmModels"
|
||||
import { refreshModelsDevProviderModels } from "../models/refreshModelsDevProviderModels"
|
||||
import { refreshOpenRouterModels } from "../models/refreshOpenRouterModels"
|
||||
import { sendOpenRouterModelsEvent } from "../models/subscribeToOpenRouterModels"
|
||||
|
||||
@@ -28,6 +29,14 @@ export async function initializeWebview(controller: Controller, _request: EmptyR
|
||||
sendOpenRouterModelsEvent(OpenRouterCompatibleModelInfo.create({ models: lastCachedModels }))
|
||||
}
|
||||
|
||||
refreshModelsDevProviderModels()
|
||||
.then(async (models) => {
|
||||
if (models && Object.keys(models).length > 0) {
|
||||
await controller.postStateToWebview()
|
||||
}
|
||||
})
|
||||
.catch((error) => Logger.error("Failed to refresh models.dev provider models:", error))
|
||||
|
||||
// Refresh OpenRouter models from API
|
||||
refreshOpenRouterModels(controller).then(async (models) => {
|
||||
if (models && Object.keys(models).length > 0) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ApiConfiguration, ModelInfo } from "@shared/api"
|
||||
import type { ModelsDevProviderModels } from "@shared/models-dev"
|
||||
import {
|
||||
ApiHandlerSettingsKeys,
|
||||
type GlobalState,
|
||||
@@ -102,6 +103,7 @@ export class StateManager {
|
||||
liteLlmModels: null,
|
||||
vercelModels: null,
|
||||
}
|
||||
private modelsDevProviderModelsCache: { data: ModelsDevProviderModels; timestamp: number } | null = null
|
||||
|
||||
// Debounced persistence state
|
||||
private pendingGlobalState = new Set<GlobalStateAndSettingsKey>()
|
||||
@@ -502,6 +504,24 @@ export class StateManager {
|
||||
return cached.data
|
||||
}
|
||||
|
||||
setModelsDevProviderModelsCache(models: ModelsDevProviderModels): void {
|
||||
this.modelsDevProviderModelsCache = { data: models, timestamp: Date.now() }
|
||||
}
|
||||
|
||||
getModelsDevProviderModelsCache(): ModelsDevProviderModels | null {
|
||||
const cached = this.modelsDevProviderModelsCache
|
||||
if (!cached) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (Date.now() - cached.timestamp > this.MODEL_CACHE_TTL_MS) {
|
||||
this.modelsDevProviderModelsCache = null
|
||||
return null
|
||||
}
|
||||
|
||||
return cached.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model info by provider and model ID (from in-memory cache)
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,7 @@ export const GlobalFileNames = {
|
||||
uiMessages: "ui_messages.json",
|
||||
clineRecommendedModels: "cline_recommended_models.json",
|
||||
clineModels: "cline_models.json",
|
||||
modelsDevProviderModels: "models_dev_provider_models.json",
|
||||
openRouterModels: "openrouter_models.json",
|
||||
vercelAiGatewayModels: "vercel_ai_gateway_models.json",
|
||||
groqModels: "groq_models.json",
|
||||
|
||||
@@ -13,6 +13,7 @@ import { FocusChainSettings } from "./FocusChainSettings"
|
||||
import { HistoryItem } from "./HistoryItem"
|
||||
import { McpDisplayMode } from "./McpDisplayMode"
|
||||
import { ClineMessageModelInfo } from "./messages"
|
||||
import type { ModelsDevProviderModels } from "./models-dev"
|
||||
import { OnboardingModelGroup } from "./proto/cline/state"
|
||||
import { Mode } from "./storage/types"
|
||||
import { TelemetrySetting } from "./TelemetrySetting"
|
||||
@@ -40,6 +41,7 @@ export interface ExtensionState {
|
||||
isNewUser: boolean
|
||||
welcomeViewCompleted: boolean
|
||||
onboardingModels: OnboardingModelGroup | undefined
|
||||
modelsDevProviderModels?: ModelsDevProviderModels
|
||||
apiConfiguration?: ApiConfiguration
|
||||
autoApprovalSettings: AutoApprovalSettings
|
||||
browserSettings: BrowserSettings
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { expect } from "chai"
|
||||
import { afterEach, describe, it } from "mocha"
|
||||
import { anthropicModels, type ModelInfo } from "./api"
|
||||
import {
|
||||
applyModelsDevProviderModels,
|
||||
type ModelsDevPayload,
|
||||
mergeModelsDevModels,
|
||||
normalizeModelsDevProviderModels,
|
||||
} from "./models-dev"
|
||||
|
||||
describe("models.dev static provider augmentation", () => {
|
||||
const augmentedAnthropicModelId = "claude-test-model-from-models-dev"
|
||||
|
||||
afterEach(() => {
|
||||
delete (anthropicModels as Record<string, ModelInfo>)[augmentedAnthropicModelId]
|
||||
})
|
||||
|
||||
it("normalizes supported models.dev models and filters unsupported entries", () => {
|
||||
const payload: ModelsDevPayload = {
|
||||
anthropic: {
|
||||
models: {
|
||||
[augmentedAnthropicModelId]: {
|
||||
name: "Claude Test",
|
||||
tool_call: true,
|
||||
reasoning: true,
|
||||
reasoning_options: [{ type: "effort", values: ["low", "medium", "high"] }],
|
||||
release_date: "2026-01-01",
|
||||
limit: {
|
||||
context: 200_000,
|
||||
output: 64_000,
|
||||
},
|
||||
cost: {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cache_read: 0.3,
|
||||
cache_write: 3.75,
|
||||
},
|
||||
modalities: {
|
||||
input: ["text", "image"],
|
||||
},
|
||||
},
|
||||
"claude-deprecated": {
|
||||
tool_call: true,
|
||||
status: "deprecated",
|
||||
},
|
||||
"claude-no-tools": {
|
||||
tool_call: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const providerModels = normalizeModelsDevProviderModels(payload)
|
||||
const model = providerModels.anthropic?.[augmentedAnthropicModelId]
|
||||
|
||||
expect(model).to.not.equal(undefined)
|
||||
expect(model?.name).to.equal("Claude Test")
|
||||
expect(model?.contextWindow).to.equal(200_000)
|
||||
expect(model?.maxTokens).to.equal(64_000)
|
||||
expect(model?.supportsImages).to.equal(true)
|
||||
expect(model?.supportsPromptCache).to.equal(true)
|
||||
expect(model?.supportsReasoning).to.equal(true)
|
||||
expect(model?.supportsReasoningEffort).to.equal(true)
|
||||
expect(model?.inputPrice).to.equal(3)
|
||||
expect(providerModels.anthropic?.["claude-deprecated"]).to.equal(undefined)
|
||||
expect(providerModels.anthropic?.["claude-no-tools"]).to.equal(undefined)
|
||||
})
|
||||
|
||||
it("keeps hardcoded model info while appending missing models.dev ids", () => {
|
||||
const staticModels: Record<string, ModelInfo> = {
|
||||
existing: {
|
||||
maxTokens: 1,
|
||||
contextWindow: 1,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 1,
|
||||
outputPrice: 1,
|
||||
},
|
||||
}
|
||||
const modelsDevModels: Record<string, ModelInfo> = {
|
||||
existing: {
|
||||
maxTokens: 2,
|
||||
contextWindow: 2,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2,
|
||||
outputPrice: 2,
|
||||
},
|
||||
added: {
|
||||
maxTokens: 3,
|
||||
contextWindow: 3,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 3,
|
||||
outputPrice: 3,
|
||||
},
|
||||
}
|
||||
|
||||
const merged = mergeModelsDevModels(staticModels, modelsDevModels)
|
||||
|
||||
expect(merged.existing.maxTokens).to.equal(1)
|
||||
expect(merged.added.maxTokens).to.equal(3)
|
||||
expect(Object.keys(merged)).to.deep.equal(["existing", "added"])
|
||||
})
|
||||
|
||||
it("applies missing models.dev ids to existing static provider maps", () => {
|
||||
const modelInfo: ModelInfo = {
|
||||
maxTokens: 64_000,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
supportsReasoning: true,
|
||||
inputPrice: 3,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.3,
|
||||
cacheWritesPrice: 3.75,
|
||||
}
|
||||
|
||||
applyModelsDevProviderModels({
|
||||
anthropic: {
|
||||
[augmentedAnthropicModelId]: modelInfo,
|
||||
},
|
||||
})
|
||||
|
||||
expect((anthropicModels as Record<string, ModelInfo>)[augmentedAnthropicModelId]).to.deep.equal(modelInfo)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,236 @@
|
||||
import {
|
||||
type ApiProvider,
|
||||
anthropicModels,
|
||||
bedrockModels,
|
||||
cerebrasModels,
|
||||
deepSeekModels,
|
||||
fireworksModels,
|
||||
geminiModels,
|
||||
huggingFaceModels,
|
||||
internationalZAiModels,
|
||||
type ModelInfo,
|
||||
mainlandZAiModels,
|
||||
minimaxModels,
|
||||
mistralModels,
|
||||
moonshotModels,
|
||||
nebiusModels,
|
||||
nousResearchModels,
|
||||
openAiNativeModels,
|
||||
sambanovaModels,
|
||||
vertexModels,
|
||||
wandbModels,
|
||||
xaiModels,
|
||||
} from "./api"
|
||||
|
||||
export type ModelsDevModelInfo = ModelInfo & {
|
||||
releaseDate?: string
|
||||
family?: string
|
||||
supportsReasoningEffort?: boolean
|
||||
supportsTools?: boolean
|
||||
}
|
||||
|
||||
export type ModelsDevProviderModels = Partial<Record<ApiProvider, Record<string, ModelsDevModelInfo>>>
|
||||
|
||||
export interface ModelsDevModel {
|
||||
name?: string
|
||||
tool_call?: boolean
|
||||
reasoning?: boolean
|
||||
structured_output?: boolean
|
||||
temperature?: boolean
|
||||
reasoning_options?: {
|
||||
type?: string
|
||||
values?: string[]
|
||||
min?: number
|
||||
}[]
|
||||
release_date?: string
|
||||
family?: string
|
||||
limit?: {
|
||||
context?: number
|
||||
input?: number
|
||||
output?: number
|
||||
}
|
||||
cost?: {
|
||||
input?: number
|
||||
output?: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
}
|
||||
modalities?: {
|
||||
input?: string[]
|
||||
}
|
||||
status?: string
|
||||
}
|
||||
|
||||
export type ModelsDevPayload = Record<string, { models?: Record<string, ModelsDevModel> }>
|
||||
|
||||
const DEFAULT_MAX_TOKENS = 4096
|
||||
|
||||
const MODELS_DEV_PROVIDER_KEY_MAP: Record<string, ApiProvider> = {
|
||||
"amazon-bedrock": "bedrock",
|
||||
anthropic: "anthropic",
|
||||
cerebras: "cerebras",
|
||||
deepseek: "deepseek",
|
||||
"fireworks-ai": "fireworks",
|
||||
google: "gemini",
|
||||
"google-vertex": "vertex",
|
||||
huggingface: "huggingface",
|
||||
minimax: "minimax",
|
||||
mistral: "mistral",
|
||||
moonshotai: "moonshot",
|
||||
nebius: "nebius",
|
||||
"nous-research": "nousResearch",
|
||||
openai: "openai-native",
|
||||
sambanova: "sambanova",
|
||||
wandb: "wandb",
|
||||
xai: "xai",
|
||||
zai: "zai",
|
||||
}
|
||||
|
||||
const STATIC_MODELS_BY_PROVIDER: Partial<Record<ApiProvider, Record<string, ModelInfo>>> = {
|
||||
anthropic: anthropicModels as Record<string, ModelInfo>,
|
||||
bedrock: bedrockModels as Record<string, ModelInfo>,
|
||||
cerebras: cerebrasModels as Record<string, ModelInfo>,
|
||||
deepseek: deepSeekModels as Record<string, ModelInfo>,
|
||||
fireworks: fireworksModels as Record<string, ModelInfo>,
|
||||
gemini: geminiModels as Record<string, ModelInfo>,
|
||||
huggingface: huggingFaceModels as Record<string, ModelInfo>,
|
||||
minimax: minimaxModels as Record<string, ModelInfo>,
|
||||
mistral: mistralModels as Record<string, ModelInfo>,
|
||||
moonshot: moonshotModels as Record<string, ModelInfo>,
|
||||
nebius: nebiusModels as Record<string, ModelInfo>,
|
||||
nousResearch: nousResearchModels as Record<string, ModelInfo>,
|
||||
"openai-native": openAiNativeModels as Record<string, ModelInfo>,
|
||||
sambanova: sambanovaModels as Record<string, ModelInfo>,
|
||||
vertex: vertexModels as Record<string, ModelInfo>,
|
||||
wandb: wandbModels as Record<string, ModelInfo>,
|
||||
xai: xaiModels as Record<string, ModelInfo>,
|
||||
zai: internationalZAiModels as Record<string, ModelInfo>,
|
||||
}
|
||||
|
||||
function parseReleaseDate(value: string | undefined): number {
|
||||
if (!value) {
|
||||
return Number.NEGATIVE_INFINITY
|
||||
}
|
||||
const timestamp = Date.parse(value)
|
||||
return Number.isNaN(timestamp) ? Number.NEGATIVE_INFINITY : timestamp
|
||||
}
|
||||
|
||||
function sortModelsByReleaseDate(models: Record<string, ModelsDevModelInfo>): Record<string, ModelsDevModelInfo> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(models).sort(([modelIdA, modelA], [modelIdB, modelB]) => {
|
||||
const releaseDateA = parseReleaseDate(modelA.releaseDate)
|
||||
const releaseDateB = parseReleaseDate(modelB.releaseDate)
|
||||
if (releaseDateA !== releaseDateB) {
|
||||
return releaseDateB - releaseDateA
|
||||
}
|
||||
return modelIdA.localeCompare(modelIdB)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function hasCachePricing(cost: ModelsDevModel["cost"]): boolean {
|
||||
return typeof cost?.cache_read === "number" || typeof cost?.cache_write === "number"
|
||||
}
|
||||
|
||||
function supportsReasoningEffort(model: ModelsDevModel): boolean {
|
||||
return model.reasoning_options?.some((option) => option.type === "effort") ?? false
|
||||
}
|
||||
|
||||
function toModelInfo(modelId: string, model: ModelsDevModel): ModelsDevModelInfo {
|
||||
const supportsPromptCache = hasCachePricing(model.cost)
|
||||
const supportsReasoning = model.reasoning === true
|
||||
const supportsEffort = supportsReasoningEffort(model)
|
||||
const info: ModelsDevModelInfo = {
|
||||
name: model.name || modelId,
|
||||
maxTokens: Math.floor(model.limit?.output ?? DEFAULT_MAX_TOKENS),
|
||||
contextWindow: model.limit?.context,
|
||||
supportsImages: model.modalities?.input?.includes("image") ?? false,
|
||||
supportsPromptCache,
|
||||
supportsReasoning,
|
||||
inputPrice: model.cost?.input ?? 0,
|
||||
outputPrice: model.cost?.output ?? 0,
|
||||
cacheReadsPrice: model.cost?.cache_read,
|
||||
cacheWritesPrice: model.cost?.cache_write,
|
||||
description: "",
|
||||
thinkingConfig: supportsReasoning ? { maxBudget: model.limit?.output ?? DEFAULT_MAX_TOKENS } : undefined,
|
||||
releaseDate: model.release_date,
|
||||
family: model.family,
|
||||
supportsReasoningEffort: supportsEffort,
|
||||
supportsTools: model.tool_call === true,
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
function isSupportedModelsDevModel(model: ModelsDevModel): boolean {
|
||||
return model.tool_call === true && model.status !== "deprecated"
|
||||
}
|
||||
|
||||
export function normalizeModelsDevProviderModels(payload: ModelsDevPayload): ModelsDevProviderModels {
|
||||
const providerModels: ModelsDevProviderModels = {}
|
||||
|
||||
for (const [modelsDevProviderKey, providerId] of Object.entries(MODELS_DEV_PROVIDER_KEY_MAP)) {
|
||||
const sourceModels = payload[modelsDevProviderKey]?.models
|
||||
if (!sourceModels) {
|
||||
continue
|
||||
}
|
||||
|
||||
const models: Record<string, ModelsDevModelInfo> = {}
|
||||
for (const [modelId, model] of Object.entries(sourceModels)) {
|
||||
if (!isSupportedModelsDevModel(model)) {
|
||||
continue
|
||||
}
|
||||
models[modelId] = toModelInfo(modelId, model)
|
||||
}
|
||||
|
||||
if (Object.keys(models).length > 0) {
|
||||
providerModels[providerId] = sortModelsByReleaseDate(models)
|
||||
}
|
||||
}
|
||||
|
||||
return providerModels
|
||||
}
|
||||
|
||||
export function getStaticModelsForModelsDevProvider(providerId: ApiProvider): Record<string, ModelInfo> | undefined {
|
||||
return STATIC_MODELS_BY_PROVIDER[providerId]
|
||||
}
|
||||
|
||||
export function mergeModelsDevModels(
|
||||
staticModels: Record<string, ModelInfo>,
|
||||
modelsDevModels: Record<string, ModelInfo> | undefined,
|
||||
): Record<string, ModelInfo> {
|
||||
if (!modelsDevModels || Object.keys(modelsDevModels).length === 0) {
|
||||
return staticModels
|
||||
}
|
||||
|
||||
const additions = Object.fromEntries(Object.entries(modelsDevModels).filter(([modelId]) => !(modelId in staticModels)))
|
||||
return {
|
||||
...staticModels,
|
||||
...additions,
|
||||
}
|
||||
}
|
||||
|
||||
export function applyModelsDevProviderModels(providerModels: ModelsDevProviderModels | undefined): void {
|
||||
if (!providerModels) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const [providerId, models] of Object.entries(providerModels) as [ApiProvider, Record<string, ModelInfo>][]) {
|
||||
const staticModels = getStaticModelsForModelsDevProvider(providerId)
|
||||
if (!staticModels) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const [modelId, modelInfo] of Object.entries(models)) {
|
||||
if (!(modelId in staticModels)) {
|
||||
staticModels[modelId] = modelInfo
|
||||
}
|
||||
if (providerId === "zai") {
|
||||
const mainlandModels = mainlandZAiModels as Record<string, ModelInfo>
|
||||
if (!(modelId in mainlandModels)) {
|
||||
mainlandModels[modelId] = modelInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
|
||||
import { DEFAULT_PLATFORM, type ExtensionState } from "@shared/ExtensionMessage"
|
||||
import { DEFAULT_FOCUS_CHAIN_SETTINGS } from "@shared/FocusChainSettings"
|
||||
import { DEFAULT_MCP_DISPLAY_MODE } from "@shared/McpDisplayMode"
|
||||
import { applyModelsDevProviderModels } from "@shared/models-dev"
|
||||
import type { UserInfo } from "@shared/proto/cline/account"
|
||||
import { EmptyRequest } from "@shared/proto/cline/common"
|
||||
import type { OpenRouterCompatibleModelInfo } from "@shared/proto/cline/models"
|
||||
@@ -359,6 +360,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
if (response.stateJson) {
|
||||
try {
|
||||
const stateData = JSON.parse(response.stateJson) as ExtensionState
|
||||
applyModelsDevProviderModels(stateData.modelsDevProviderModels)
|
||||
setState((prevState) => {
|
||||
// Versioning logic for autoApprovalSettings
|
||||
const incomingVersion = stateData.autoApprovalSettings?.version ?? 1
|
||||
|
||||
Reference in New Issue
Block a user