From 55e6da71aa88d448a1e686003ab572d3011d00a2 Mon Sep 17 00:00:00 2001 From: Halleluyah Oludele Date: Thu, 28 May 2026 16:35:07 +0100 Subject: [PATCH] feat(llm): Anthropic driver base_url override (GLM/Zhipu support) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds AnthropicBlock.BaseURL, threaded into both binaries' buildLLM via llmgate's anthropic.Config.BaseURL (which the provider already supports). Empty = official api.anthropic.com. This lets the existing `anthropic` driver target any Anthropic-compatible gateway. The immediate use is GLM/Zhipu's https://api.z.ai/api/anthropic endpoint (glm-4.6), which speaks the Messages API and sidesteps the Gemini free-tier daily quota that was 429-ing every ingest's summarize / HyDE / TOC-builder calls. Wiring: - pkg/config: AnthropicBlock.BaseURL (yaml: base_url) + VLE_LLM_ANTHROPIC_{API_KEY,BASE_URL,MODEL} env overrides. - internal/config (server): forwards VLS_/VLE_LLM_ANTHROPIC_BASE_URL and _MODEL onto the nested engine config so the deployed cmd/server can be flipped to GLM by env var alone — no secret edit. - cmd/engine + cmd/server buildLLM: pass BaseURL through. go build/vet/test green. --- cmd/engine/main.go | 1 + cmd/server/main.go | 1 + internal/config/config.go | 10 ++++++++++ pkg/config/config.go | 19 +++++++++++++++++++ 4 files changed, 31 insertions(+) diff --git a/cmd/engine/main.go b/cmd/engine/main.go index 5e1968f..3f2742b 100644 --- a/cmd/engine/main.go +++ b/cmd/engine/main.go @@ -350,6 +350,7 @@ func buildLLM(c config.LLMConfig) (llmgate.Client, error) { APIKey: c.Anthropic.APIKey, Model: c.Anthropic.Model, ReasoningModel: c.Anthropic.ReasoningModel, + BaseURL: c.Anthropic.BaseURL, }) case "openai": return openai.New(openai.Config{ diff --git a/cmd/server/main.go b/cmd/server/main.go index 697ecb9..a241eda 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -393,6 +393,7 @@ func buildLLM(c enginecfg.LLMConfig) (llmgate.Client, error) { APIKey: c.Anthropic.APIKey, Model: c.Anthropic.Model, ReasoningModel: c.Anthropic.ReasoningModel, + BaseURL: c.Anthropic.BaseURL, }) case "openai": return openai.New(openai.Config{ diff --git a/internal/config/config.go b/internal/config/config.go index c6ca1cf..82b8118 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -320,6 +320,16 @@ func applyEnvOverrides(c *Config) { if v := firstEnv("VLS_LLM_DRIVER", "VLE_LLM_DRIVER"); v != "" { c.Engine.LLM.Driver = v } + // Anthropic-compatible gateway overrides (e.g. GLM/Zhipu via + // https://api.z.ai/api/anthropic): base URL + model, so the + // anthropic driver can run a non-Anthropic model without a secret + // edit. + if v := firstEnv("VLS_LLM_ANTHROPIC_BASE_URL", "VLE_LLM_ANTHROPIC_BASE_URL"); v != "" { + c.Engine.LLM.Anthropic.BaseURL = v + } + if v := firstEnv("VLS_LLM_ANTHROPIC_MODEL", "VLE_LLM_ANTHROPIC_MODEL"); v != "" { + c.Engine.LLM.Anthropic.Model = v + } } // firstEnv returns the first non-empty value from the named env vars. diff --git a/pkg/config/config.go b/pkg/config/config.go index ba40628..3add5b0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -332,6 +332,12 @@ type AnthropicBlock struct { APIKey string `yaml:"api_key"` Model string `yaml:"model"` ReasoningModel string `yaml:"reasoning_model"` + // BaseURL overrides the Anthropic API endpoint. Empty = official + // api.anthropic.com. Set this to point the Anthropic driver at any + // Anthropic-compatible gateway — e.g. GLM/Zhipu's + // https://api.z.ai/api/anthropic — so the same driver can drive a + // non-Anthropic model that speaks the Messages API. + BaseURL string `yaml:"base_url"` } // OpenAIBlock configures the OpenAI provider. @@ -805,6 +811,19 @@ func applyEnvOverrides(c *Config) { if v := os.Getenv("VLE_LLM_DRIVER"); v != "" { c.LLM.Driver = v } + // Anthropic-driver overrides. These let an operator point the + // anthropic driver at an Anthropic-compatible gateway (e.g. GLM via + // https://api.z.ai/api/anthropic) without baking the values into the + // config file or secret. + if v := os.Getenv("VLE_LLM_ANTHROPIC_API_KEY"); v != "" { + c.LLM.Anthropic.APIKey = v + } + if v := os.Getenv("VLE_LLM_ANTHROPIC_BASE_URL"); v != "" { + c.LLM.Anthropic.BaseURL = v + } + if v := os.Getenv("VLE_LLM_ANTHROPIC_MODEL"); v != "" { + c.LLM.Anthropic.Model = v + } if v := os.Getenv("VLE_TLS_CERT_FILE"); v != "" { c.Server.TLS.CertFile = v }