Add Z.ai coding plan support (#8003)

This commit is contained in:
Daniel 2025-09-15 16:53:20 -05:00 committed by GitHub
parent 94b4511053
commit 3fe2918e67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 41 additions and 14 deletions

View file

@ -308,9 +308,13 @@ const sambaNovaSchema = apiModelIdProviderModelSchema.extend({
sambaNovaApiKey: z.string().optional(),
})
export const zaiApiLineSchema = z.enum(["international_coding", "international", "china_coding", "china"])
export type ZaiApiLine = z.infer<typeof zaiApiLineSchema>
const zaiSchema = apiModelIdProviderModelSchema.extend({
zaiApiKey: z.string().optional(),
zaiApiLine: z.union([z.literal("china"), z.literal("international")]).optional(),
zaiApiLine: zaiApiLineSchema.optional(),
})
const fireworksSchema = apiModelIdProviderModelSchema.extend({

View file

@ -1,4 +1,5 @@
import type { ModelInfo } from "../model.js"
import { ZaiApiLine } from "../provider-settings.js"
// Z AI
// https://docs.z.ai/guides/llm/glm-4.5
@ -103,3 +104,14 @@ export const mainlandZAiModels = {
} as const satisfies Record<string, ModelInfo>
export const ZAI_DEFAULT_TEMPERATURE = 0
export const zaiApiLineConfigs = {
international_coding: {
name: "International Coding Plan",
baseUrl: "https://api.z.ai/api/coding/paas/v4",
isChina: false,
},
international: { name: "International Standard", baseUrl: "https://api.z.ai/api/paas/v4", isChina: false },
china_coding: { name: "China Coding Plan", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4", isChina: true },
china: { name: "China Standard", baseUrl: "https://open.bigmodel.cn/api/paas/v4", isChina: true },
} satisfies Record<ZaiApiLine, { name: string; baseUrl: string; isChina: boolean }>

View file

@ -41,7 +41,11 @@ describe("ZAiHandler", () => {
it("should use the correct international Z AI base URL", () => {
new ZAiHandler({ zaiApiKey: "test-zai-api-key", zaiApiLine: "international" })
expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ baseURL: "https://api.z.ai/api/paas/v4" }))
expect(OpenAI).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: "https://api.z.ai/api/paas/v4",
}),
)
})
it("should use the provided API key for international", () => {
@ -109,7 +113,11 @@ describe("ZAiHandler", () => {
describe("Default behavior", () => {
it("should default to international when no zaiApiLine is specified", () => {
const handlerDefault = new ZAiHandler({ zaiApiKey: "test-zai-api-key" })
expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ baseURL: "https://api.z.ai/api/paas/v4" }))
expect(OpenAI).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: "https://api.z.ai/api/coding/paas/v4",
}),
)
const model = handlerDefault.getModel()
expect(model.id).toBe(internationalZAiDefaultModelId)

View file

@ -6,6 +6,7 @@ import {
type InternationalZAiModelId,
type MainlandZAiModelId,
ZAI_DEFAULT_TEMPERATURE,
zaiApiLineConfigs,
} from "@roo-code/types"
import type { ApiHandlerOptions } from "../../shared/api"
@ -14,14 +15,14 @@ import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
export class ZAiHandler extends BaseOpenAiCompatibleProvider<InternationalZAiModelId | MainlandZAiModelId> {
constructor(options: ApiHandlerOptions) {
const isChina = options.zaiApiLine === "china"
const isChina = zaiApiLineConfigs[options.zaiApiLine ?? "international_coding"].isChina
const models = isChina ? mainlandZAiModels : internationalZAiModels
const defaultModelId = isChina ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId
super({
...options,
providerName: "Z AI",
baseURL: isChina ? "https://open.bigmodel.cn/api/paas/v4" : "https://api.z.ai/api/paas/v4",
baseURL: zaiApiLineConfigs[options.zaiApiLine ?? "international_coding"].baseUrl,
apiKey: options.zaiApiKey ?? "not-provided",
defaultProviderModelId: defaultModelId,
providerModels: models,

View file

@ -1,7 +1,7 @@
import { useCallback } from "react"
import { VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import type { ProviderSettings } from "@roo-code/types"
import { zaiApiLineConfigs, zaiApiLineSchema, type ProviderSettings } from "@roo-code/types"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@ -33,15 +33,17 @@ export const ZAi = ({ apiConfiguration, setApiConfigurationField }: ZAiProps) =>
<div>
<label className="block font-medium mb-1">{t("settings:providers.zaiEntrypoint")}</label>
<VSCodeDropdown
value={apiConfiguration.zaiApiLine || "international"}
value={apiConfiguration.zaiApiLine || zaiApiLineSchema.enum.international_coding}
onChange={handleInputChange("zaiApiLine")}
className={cn("w-full")}>
<VSCodeOption value="international" className="p-2">
api.z.ai
</VSCodeOption>
<VSCodeOption value="china" className="p-2">
open.bigmodel.cn
</VSCodeOption>
{zaiApiLineSchema.options.map((zaiApiLine) => {
const config = zaiApiLineConfigs[zaiApiLine]
return (
<VSCodeOption key={zaiApiLine} value={zaiApiLine} className="p-2">
{config.name} ({config.baseUrl})
</VSCodeOption>
)
})}
</VSCodeDropdown>
<div className="text-xs text-vscode-descriptionForeground mt-1">
{t("settings:providers.zaiEntrypointDescription")}
@ -62,7 +64,7 @@ export const ZAi = ({ apiConfiguration, setApiConfigurationField }: ZAiProps) =>
{!apiConfiguration?.zaiApiKey && (
<VSCodeButtonLink
href={
apiConfiguration.zaiApiLine === "china"
zaiApiLineConfigs[apiConfiguration.zaiApiLine ?? "international_coding"].isChina
? "https://open.bigmodel.cn/console/overview"
: "https://z.ai/manage-apikey/apikey-list"
}