diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-26 08:50:20 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-26 08:50:20 +0300 |
| commit | e4a6723bb679b13401020bb4953cd7c4c9564e8c (patch) | |
| tree | 6404ac14586e6207056b83f3db8a1cd9657992d1 /internal/llm/ollama.go | |
| parent | 97e2dde7693618516a42019d7aa7cfda1f5a8811 (diff) | |
feat: optional API key for Ollama provider (Ollama Cloud)
Adds an optional HEXAI_OLLAMA_API_KEY (with OLLAMA_API_KEY fallback) so
the existing Ollama provider can target Ollama Cloud (ollama.ai) in
addition to a local server. When the key is empty the request is
unauthenticated, preserving local-server behavior byte-for-byte; when
set, an Authorization: Bearer header is attached for both Chat and
ChatStream. Documented cloud usage in config.toml.example.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'internal/llm/ollama.go')
| -rw-r--r-- | internal/llm/ollama.go | 33 |
1 files changed, 25 insertions, 8 deletions
diff --git a/internal/llm/ollama.go b/internal/llm/ollama.go index b2cecfa..0916c06 100644 --- a/internal/llm/ollama.go +++ b/internal/llm/ollama.go @@ -1,4 +1,6 @@ -// Ollama client against a local server; supports chat responses and streaming via /api/chat. +// Ollama client supporting both a local server and Ollama Cloud (ollama.ai). +// The optional API key is sent as a Bearer token; when empty, requests are +// unauthenticated so a local Ollama server keeps working unchanged. package llm import ( @@ -14,11 +16,13 @@ import ( "codeberg.org/snonux/hexai/internal/logging" ) -// ollamaClient implements Client against a local Ollama server. +// ollamaClient implements Client against a local Ollama server or Ollama Cloud. +// apiKey is optional: empty for local, non-empty enables Bearer auth for cloud. type ollamaClient struct { httpClient *http.Client baseURL string defaultModel string + apiKey string chatLogger logging.ChatLogger defaultTemperature *float64 } @@ -45,21 +49,23 @@ type ollamaChatResponse struct { Error string `json:"error,omitempty"` } -func ollamaProviderFactory(cfg Config, _ ProviderKeys) (Client, error) { +func ollamaProviderFactory(cfg Config, keys ProviderKeys) (Client, error) { return newOllamaWithTimeout( cfg.OllamaBaseURL, cfg.OllamaModel, + keys.OllamaAPIKey, withDefaultTemperature(cfg.OllamaTemperature, 0.2), cfg.RequestTimeout, ), nil } -// Constructor (kept among the first functions by convention) -func newOllama(baseURL, model string, defaultTemp *float64) Client { - return newOllamaWithTimeout(baseURL, model, defaultTemp, 0) +// Constructor (kept among the first functions by convention). +// apiKey may be empty for local Ollama; pass a non-empty key for Ollama Cloud. +func newOllama(baseURL, model string, defaultTemp *float64, apiKey string) Client { + return newOllamaWithTimeout(baseURL, model, apiKey, defaultTemp, 0) } -func newOllamaWithTimeout(baseURL, model string, defaultTemp *float64, timeoutSec int) Client { +func newOllamaWithTimeout(baseURL, model, apiKey string, defaultTemp *float64, timeoutSec int) Client { if strings.TrimSpace(baseURL) == "" { baseURL = "http://localhost:11434" } @@ -73,6 +79,7 @@ func newOllamaWithTimeout(baseURL, model string, defaultTemp *float64, timeoutSe httpClient: &http.Client{Timeout: time.Duration(timeoutSec) * time.Second}, baseURL: strings.TrimRight(baseURL, "/"), defaultModel: model, + apiKey: strings.TrimSpace(apiKey), chatLogger: logging.NewChatLogger("ollama"), defaultTemperature: defaultTemp, } @@ -228,7 +235,17 @@ func buildOllamaRequest(o Options, messages []Message, defaultTemp *float64, str } func (c ollamaClient) doJSON(ctx context.Context, url string, body []byte) (*http.Response, error) { - return doJSONRequest(ctx, c.httpClient, url, body, nil, "") + return doJSONRequest(ctx, c.httpClient, url, body, c.authHeaders(), "") +} + +// authHeaders returns Bearer auth for Ollama Cloud, or nil for unauthenticated +// local Ollama. Returning nil keeps the local request shape byte-identical to +// the previous implementation. +func (c ollamaClient) authHeaders() map[string]string { + if c.apiKey == "" { + return nil + } + return map[string]string{"Authorization": "Bearer " + c.apiKey} } func handleOllamaNon2xx(resp *http.Response, start time.Time) error { |
