Skip to content
Open
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
12 changes: 7 additions & 5 deletions internal/bootstrap/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,11 @@ func resolveInput(opts Options) (*resolvedInput, error) {

func ensureProviderSetup(cwd string, settings config.Resolved, nonTTY bool) (config.Resolved, string, error) {
configExists := config.GlobalConfigExists() || config.ProjectConfigExists(cwd)
if configExists {
if hasConfiguredProviderCredentials(settings, settings.Provider) {
return settings, "", nil
}
return settings, "", fmt.Errorf("configuration error: settings.provider=%q is missing or not configured in settings.json", settings.Provider)
if configExists && hasConfiguredProviderCredentials(settings, settings.Provider) {
return settings, "", nil
}

// Even with a config file, try env var fallback when api_key is empty.
apiKey, _ := settings.ProviderCredentials(settings.Provider)
if apiKey != "" {
return settings, envHintFor(settings), nil
Expand All @@ -112,6 +110,10 @@ func ensureProviderSetup(cwd string, settings config.Resolved, nonTTY bool) (con
return settings, fmt.Sprintf("Using %s from environment", envKey), nil
}

if configExists {
return settings, "", fmt.Errorf("configuration error: settings.provider=%q is missing or not configured in settings.json", settings.Provider)
}

if nonTTY {
return settings, "", fmt.Errorf("api key not set; set %s or configure providers in %s",
config.ProviderEnvKey(settings.Provider), filepath.Join(config.UserConfigDir(), "settings.json"))
Expand Down
12 changes: 10 additions & 2 deletions internal/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var KnownProviderTypes = map[string]string{
"openai": "openai",
"openrouter": "openrouter",
"gemini": "gemini",
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provider.go now supports the provider type "glm", but KnownProviderTypes doesn't include a "glm" entry. If a user sets settings.provider to "glm" (mirroring how other providers work), ProviderType() will currently default to "openai" and route through the OpenAI client instead of the GLM/LiteLLM path. Consider adding "glm": "glm" here (and aligning env var lookup accordingly) so "glm" works as a first-class provider key, not only as an inferred type for "zhipu".

Suggested change
"gemini": "gemini",
"gemini": "gemini",
"glm": "glm",

Copilot uses AI. Check for mistakes.
"zhipu": "glm",
}

// ProviderConfig holds credentials and model configuration for a single provider.
Expand Down Expand Up @@ -116,15 +117,22 @@ var providerEnvVars = map[string]struct{ key, base string }{
"openai": {"OPENAI_API_KEY", "OPENAI_BASE_URL"},
"openrouter": {"OPENROUTER_API_KEY", "OPENROUTER_BASE_URL"},
"gemini": {"GEMINI_API_KEY", "GEMINI_BASE_URL"},
"zhipu": {"ZHIPU_API_KEY", "ZHIPU_BASE_URL"},
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnvCredentials() only checks providerEnvVars, but providerEnvVars has a "zhipu" entry and no corresponding "glm" entry. This means settings.provider="glm" (or any flow that queries env creds for "glm") will never pick up ZHIPU_API_KEY / ZHIPU_BASE_URL. Consider adding a "glm" mapping (likely to the same env vars) or otherwise ensuring env fallback works for the provider type name as well.

Suggested change
"zhipu": {"ZHIPU_API_KEY", "ZHIPU_BASE_URL"},
"zhipu": {"ZHIPU_API_KEY", "ZHIPU_BASE_URL"},
"glm": {"ZHIPU_API_KEY", "ZHIPU_BASE_URL"},

Copilot uses AI. Check for mistakes.
}

// ProviderCredentials returns API key and base URL for the given provider.
// It checks the providers map first, then falls back to standard environment variables.
// Config base URL is preserved even when the API key comes from the environment.
func (r Resolved) ProviderCredentials(prov string) (apiKey, baseURL string) {
if pc, ok := r.Providers[prov]; ok && pc.APIKey != "" {
pc, hasConfig := r.Providers[prov]
if hasConfig && pc.APIKey != "" {
return pc.APIKey, pc.BaseURL
}
return EnvCredentials(prov)
apiKey, baseURL = EnvCredentials(prov)
if hasConfig && baseURL == "" {
baseURL = pc.BaseURL
}
return apiKey, baseURL
}

// ProviderEnvKey returns the standard environment variable name for a provider's API key.
Expand Down
16 changes: 16 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package provider

import (
"fmt"
"strings"

"github.com/voocel/agentcore"
"github.com/voocel/agentcore/llm"
"github.com/voocel/litellm"
)

// CreateModel creates a ChatModel for the given provider, model name, API key, and optional base URL.
Expand Down Expand Up @@ -40,6 +42,8 @@ func newProviderModel(prov, name, apiKey, baseURL string) (agentcore.ChatModel,
return llm.NewOpenAIModel(name, apiKey, baseURL)
}
return llm.NewOpenAIModel(name, apiKey)
case "glm":
return newGLMModel(name, apiKey, baseURL)
default:
if baseURL != "" {
return llm.NewOpenAIModel(name, apiKey, baseURL)
Expand All @@ -48,6 +52,18 @@ func newProviderModel(prov, name, apiKey, baseURL string) (agentcore.ChatModel,
}
}

func newGLMModel(model, apiKey, baseURL string) (agentcore.ChatModel, error) {
cfg := litellm.ProviderConfig{APIKey: apiKey}
if baseURL != "" {
cfg.BaseURL = baseURL
}
client, err := litellm.NewWithProvider("glm", cfg)
if err != nil {
return nil, fmt.Errorf("glm: %w", err)
}
return llm.NewLiteLLMAdapter(model, client), nil
}

func applyProviderDefaults(prov, modelName string, model agentcore.ChatModel) {
cfgOwner, ok := model.(interface {
GetConfig() *llm.GenerationConfig
Expand Down
Loading