Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion cmd/shelley/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ func buildLLMModelSources(ctx context.Context, global GlobalConfig, logger *slog
openAIKey := os.Getenv("OPENAI_API_KEY")
geminiKey := os.Getenv("GEMINI_API_KEY")
fireworksKey := os.Getenv("FIREWORKS_API_KEY")
zaiKey := os.Getenv("ZAI_API_KEY")

var sources []modelsources.Source

Expand Down Expand Up @@ -464,6 +465,9 @@ func buildLLMModelSources(ctx context.Context, global GlobalConfig, logger *slog
if geminiKey != "" {
sources = append(sources, modelsources.Env("", "", geminiKey, ""))
}
if zaiKey != "" {
sources = append(sources, modelsources.ZAIEnv(zaiKey))
}
} else if gateway != "" {
logger.Info("Using LLM gateway", "gateway", gateway)
sources = append(sources, modelsources.Gateway(gateway, anthropicKey, openAIKey, fireworksKey))
Expand All @@ -472,8 +476,16 @@ func buildLLMModelSources(ctx context.Context, global GlobalConfig, logger *slog
if geminiKey != "" {
sources = append(sources, modelsources.Env("", "", geminiKey, ""))
}
} else if anthropicKey != "" || openAIKey != "" || geminiKey != "" || fireworksKey != "" {
if zaiKey != "" {
sources = append(sources, modelsources.ZAIEnv(zaiKey))
}
} else if anthropicKey != "" || openAIKey != "" || geminiKey != "" || fireworksKey != "" || zaiKey != "" {
// 3. Env vars.
// 3a. z.ai gets its own source so z.ai models use ZAI_API_KEY
// instead of OPENAI_API_KEY.
if zaiKey != "" {
sources = append(sources, modelsources.ZAIEnv(zaiKey))
}
sources = append(sources, modelsources.Env(anthropicKey, openAIKey, geminiKey, fireworksKey))
}

Expand Down
37 changes: 37 additions & 0 deletions llm/oai/oai.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
GeminiURL = "https://generativelanguage.googleapis.com/v1beta/openai/"
MistralURL = "https://api.mistral.ai/v1"
MoonshotURL = "https://api.moonshot.ai/v1"
ZAIURL = "https://api.z.ai/api/coding/paas/v4"

// Environment variable names for API keys
OpenAIAPIKeyEnv = "OPENAI_API_KEY"
Expand All @@ -35,6 +36,7 @@ const (
GeminiAPIKeyEnv = "GEMINI_API_KEY"
MistralAPIKeyEnv = "MISTRAL_API_KEY"
MoonshotAPIKeyEnv = "MOONSHOT_API_KEY"
ZAIAPIKeyEnv = "ZAI_API_KEY"
)

//exe:completeinit
Expand Down Expand Up @@ -328,6 +330,36 @@ var (
SupportsImages: false,
}

GLM52ZAI = Model{
UserName: "glm-5.2-zai",
ModelName: "glm-5.2",
URL: ZAIURL,
APIKeyEnv: ZAIAPIKeyEnv,
IsReasoningModel: false,
UseSimplifiedPatch: false,
SupportsImages: false,
}

GLM51ZAI = Model{
UserName: "glm-5.1-zai",
ModelName: "glm-5.1",
URL: ZAIURL,
APIKeyEnv: ZAIAPIKeyEnv,
IsReasoningModel: false,
UseSimplifiedPatch: false,
SupportsImages: false,
}

GLM46ZAI = Model{
UserName: "glm-4.6-zai",
ModelName: "glm-4.6",
URL: ZAIURL,
APIKeyEnv: ZAIAPIKeyEnv,
IsReasoningModel: false,
UseSimplifiedPatch: false,
SupportsImages: false,
}

KimiK26Fireworks = Model{
UserName: "kimi-k2.6-fireworks",
ModelName: "accounts/fireworks/models/kimi-k2p6",
Expand Down Expand Up @@ -552,6 +584,9 @@ var ModelsRegistry = []Model{
DevstralSmall,
GLM52Fireworks,
GLM51Fireworks,
GLM52ZAI,
GLM51ZAI,
GLM46ZAI,
KimiK26Fireworks,
Qwen36PlusFireworks,
GPTOSS120B,
Expand Down Expand Up @@ -1095,6 +1130,8 @@ func (s *Service) TokenContextWindow() int {
return 200000 // 200k for O3 models
case "glm":
return 128000
case "glm-5.2", "glm-5.1", "glm-4.6":
return 128000
case "qwen":
return 256000
case "gpt-oss-20b", "gpt-oss-120b":
Expand Down
18 changes: 18 additions & 0 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,24 @@ func All() []Model {
APIType: APITypeOpenAIChat, DefaultBaseURL: DefaultFireworksBaseURL,
Build: oaiChatSvc(oai.GLM51Fireworks, "fireworks"),
},
{
ID: "glm-5.2-zai", Provider: ProviderOpenAI,
Description: "GLM-5.2 (z.ai Coding Plan)", APIModelName: oai.GLM52ZAI.ModelName,
APIType: APITypeOpenAIChat, DefaultBaseURL: "https://api.z.ai/api/coding/paas/v4",
Build: oaiChatSvc(oai.GLM52ZAI, "zai"),
},
{
ID: "glm-5.1-zai", Provider: ProviderOpenAI,
Description: "GLM-5.1 (z.ai Coding Plan)", APIModelName: oai.GLM51ZAI.ModelName,
APIType: APITypeOpenAIChat, DefaultBaseURL: "https://api.z.ai/api/coding/paas/v4",
Build: oaiChatSvc(oai.GLM51ZAI, "zai"),
},
{
ID: "glm-4.6-zai", Provider: ProviderOpenAI,
Description: "GLM-4.6 (z.ai Coding Plan)", APIModelName: oai.GLM46ZAI.ModelName,
APIType: APITypeOpenAIChat, DefaultBaseURL: "https://api.z.ai/api/coding/paas/v4",
Build: oaiChatSvc(oai.GLM46ZAI, "zai"),
},
{
ID: "gemini-3-pro", Provider: ProviderGemini,
Description: "Gemini 3 Pro", APIModelName: "gemini-3-pro-preview",
Expand Down
21 changes: 21 additions & 0 deletions modelsources/modelsources.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,27 @@ func Env(anthropicKey, openAIKey, geminiKey, fireworksKey string) Source {
return Source{label: "env", providers: prov, providerLabels: labels}
}

// ZAIEnv returns a Source for z.ai Coding Plan models using ZAI_API_KEY.
// z.ai models are catalogued with ProviderOpenAI but use a distinct base
// URL and API key, so they need their own source to avoid being claimed
// by the generic OpenAI env source with the wrong key.
func ZAIEnv(zaiKey string) Source {
if zaiKey == "" {
return Source{}
}
return Source{
label: "$ZAI_API_KEY",
providers: map[models.Provider]*providerConn{
models.ProviderOpenAI: {apiKey: zaiKey},
},
allowedAPIModels: map[string]bool{
"glm-5.2": true,
"glm-5.1": true,
"glm-4.6": true,
},
}
}

// LLMIntegration returns a Source backed by one exe.dev "llm"
// integration. idSuffix, when non-empty, is appended to each
// materialized model ID to disambiguate multiple integrations.
Expand Down