Compare commits

...
Author SHA1 Message Date
abeatrix 71ad32063d Refactor Qwen model defaults to use first model dynamically
Move type definitions and enums after model objects and set default
models by selecting the first key from each model object instead of
hardcoding specific model IDs.
2025-07-25 01:47:18 -07:00
abeatrix 2c249c5dfd fix type with conversion 2025-07-25 00:46:31 -07:00
BeeandCopilot d89ed5ce06 Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-25 00:41:58 -07:00
abeatrix 00013bc783 changeset added 2025-07-25 00:10:31 -07:00
abeatrix cd0d4190a6 Fix Qwen API option inconsistency
Refactor Qwen API region handling with enum and improved type safety

Changes:

- Replace string literals with QwenApiRegions enum for better type safety
- Add default region initialization in QwenHandler constructor
- Extract useChinaApi() method for cleaner conditional logic
- Update UI dropdown to use enum values with proper memoization
- Improve code maintainability and reduce magic strings
2025-07-25 00:03:53 -07:00
5 changed files with 51 additions and 19 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix Qwen Api option inconsistency between UI and API layer
+3 -2
View File
@@ -1,5 +1,5 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ApiConfiguration, ModelInfo } from "../shared/api"
import { ApiConfiguration, ModelInfo, QwenApiRegions } from "../shared/api"
import { AnthropicHandler } from "./providers/anthropic"
import { AwsBedrockHandler } from "./providers/bedrock"
import { OpenRouterHandler } from "./providers/openrouter"
@@ -166,7 +166,8 @@ function createHandlerForProvider(
case "qwen":
return new QwenHandler({
qwenApiKey: options.qwenApiKey,
qwenApiLine: options.qwenApiLine,
qwenApiLine:
options.qwenApiLine === QwenApiRegions.INTERNATIONAL ? QwenApiRegions.INTERNATIONAL : QwenApiRegions.CHINA,
apiModelId: mode === "plan" ? options.planModeApiModelId : options.actModeApiModelId,
thinkingBudgetTokens:
mode === "plan" ? options.planModeThinkingBudgetTokens : options.actModeThinkingBudgetTokens,
+15 -7
View File
@@ -9,6 +9,7 @@ import {
internationalQwenDefaultModelId,
MainlandQwenModelId,
InternationalQwenModelId,
QwenApiRegions,
} from "@shared/api"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
@@ -17,7 +18,7 @@ import { withRetry } from "../retry"
interface QwenHandlerOptions {
qwenApiKey?: string
qwenApiLine?: string
qwenApiLine?: QwenApiRegions
apiModelId?: string
thinkingBudgetTokens?: number
}
@@ -27,7 +28,15 @@ export class QwenHandler implements ApiHandler {
private client: OpenAI | undefined
constructor(options: QwenHandlerOptions) {
this.options = options
// Ensure options start with defaults but allow overrides
this.options = {
qwenApiLine: QwenApiRegions.CHINA,
...options,
}
}
private useChinaApi(): boolean {
return this.options.qwenApiLine === QwenApiRegions.CHINA
}
private ensureClient(): OpenAI {
@@ -37,10 +46,9 @@ export class QwenHandler implements ApiHandler {
}
try {
this.client = new OpenAI({
baseURL:
this.options.qwenApiLine === "china"
? "https://dashscope.aliyuncs.com/compatible-mode/v1"
: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
baseURL: this.useChinaApi()
? "https://dashscope.aliyuncs.com/compatible-mode/v1"
: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
apiKey: this.options.qwenApiKey,
})
} catch (error: any) {
@@ -53,7 +61,7 @@ export class QwenHandler implements ApiHandler {
getModel(): { id: MainlandQwenModelId | InternationalQwenModelId; info: ModelInfo } {
const modelId = this.options.apiModelId
// Branch based on API line to let poor typescript know what to do
if (this.options.qwenApiLine === "china") {
if (this.useChinaApi()) {
return {
id: (modelId as MainlandQwenModelId) ?? mainlandQwenDefaultModelId,
info: mainlandQwenModels[modelId as MainlandQwenModelId] ?? mainlandQwenModels[mainlandQwenDefaultModelId],
+12 -4
View File
@@ -1151,10 +1151,7 @@ export const huggingFaceModels = {
// Qwen
// https://bailian.console.aliyun.com/
export type MainlandQwenModelId = keyof typeof mainlandQwenModels
export type InternationalQwenModelId = keyof typeof internationalQwenModels
export const internationalQwenDefaultModelId: InternationalQwenModelId = "qwen-coder-plus-latest"
export const mainlandQwenDefaultModelId: MainlandQwenModelId = "qwen-coder-plus-latest"
// The first model in the list is used as the default model for each region
export const internationalQwenModels = {
"qwen3-coder-plus": {
maxTokens: 65_536,
@@ -1836,6 +1833,17 @@ export const mainlandQwenModels = {
cacheReadsPrice: 4.5,
},
} as const satisfies Record<string, ModelInfo>
export enum QwenApiRegions {
CHINA = "china",
INTERNATIONAL = "international",
}
export type MainlandQwenModelId = keyof typeof mainlandQwenModels
export type InternationalQwenModelId = keyof typeof internationalQwenModels
// Set first model in the list as the default model for each region
export const internationalQwenDefaultModelId: InternationalQwenModelId = Object.keys(
internationalQwenModels,
)[0] as InternationalQwenModelId
export const mainlandQwenDefaultModelId: MainlandQwenModelId = Object.keys(mainlandQwenModels)[0] as MainlandQwenModelId
// Doubao
// https://www.volcengine.com/docs/82379/1298459
@@ -1,4 +1,4 @@
import { ApiConfiguration, internationalQwenModels, mainlandQwenModels } from "@shared/api"
import { internationalQwenModels, mainlandQwenModels, QwenApiRegions } from "@shared/api"
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { ApiKeyField } from "../common/ApiKeyField"
import { ModelSelector, DropdownContainer } from "../common/ModelSelector"
@@ -9,6 +9,7 @@ import { DROPDOWN_Z_INDEX } from "../ApiOptions"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
import { Mode } from "@shared/ChatSettings"
import { useMemo } from "react"
const SUPPORTED_THINKING_MODELS = [
"qwen3-235b-a22b",
@@ -32,6 +33,9 @@ interface QwenProviderProps {
currentMode: Mode
}
// Turns enum into an array of values for dropdown options
export const qwenApiOptions: QwenApiRegions[] = Object.values(QwenApiRegions)
/**
* The Alibaba Qwen provider configuration component
*/
@@ -43,7 +47,10 @@ export const QwenProvider = ({ showModelOptions, isPopup, currentMode }: QwenPro
const { selectedModelId, selectedModelInfo } = normalizeApiConfiguration(apiConfiguration, currentMode)
// Determine which models to use based on API line selection
const qwenModels = apiConfiguration?.qwenApiLine === "china" ? mainlandQwenModels : internationalQwenModels
const qwenModels = useMemo(
() => (apiConfiguration?.qwenApiLine === QwenApiRegions.CHINA ? mainlandQwenModels : internationalQwenModels),
[apiConfiguration?.qwenApiLine],
)
return (
<div>
@@ -53,14 +60,17 @@ export const QwenProvider = ({ showModelOptions, isPopup, currentMode }: QwenPro
</label>
<VSCodeDropdown
id="qwen-line-provider"
value={apiConfiguration?.qwenApiLine || "china"}
onChange={(e: any) => handleFieldChange("qwenApiLine", e.target.value)}
value={apiConfiguration?.qwenApiLine || qwenApiOptions[0]}
onChange={(e: any) => handleFieldChange("qwenApiLine", e.target.value as QwenApiRegions)}
style={{
minWidth: 130,
position: "relative",
}}>
<VSCodeOption value="china">China API</VSCodeOption>
<VSCodeOption value="international">International API</VSCodeOption>
{qwenApiOptions.map((line) => (
<VSCodeOption key={line} value={line}>
{line.charAt(0).toUpperCase() + line.slice(1)} API
</VSCodeOption>
))}
</VSCodeDropdown>
</DropdownContainer>
<p