diff --git a/internal/bootstrap/input.go b/internal/bootstrap/input.go index aec08c3..5a2a549 100644 --- a/internal/bootstrap/input.go +++ b/internal/bootstrap/input.go @@ -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 @@ -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")) diff --git a/internal/config/settings.go b/internal/config/settings.go index e606e4d..5381757 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -18,6 +18,7 @@ var KnownProviderTypes = map[string]string{ "openai": "openai", "openrouter": "openrouter", "gemini": "gemini", + "zhipu": "glm", } // ProviderConfig holds credentials and model configuration for a single provider. @@ -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"}, } // 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. diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 6f1b20e..d57bd01 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -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. @@ -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) @@ -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