From 15fb8e19a87ee55bd36fecc291d50ff2ba9dc7f2 Mon Sep 17 00:00:00 2001 From: steve Date: Sat, 23 May 2026 22:36:31 +0000 Subject: [PATCH] feat: add llm.Foreman() constructor for foreman daemon integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foreman is a private queued Ollama endpoint with observability. Since it speaks native Ollama on the wire, the constructor delegates to the existing ollama provider — no new code paths. Streaming, tool use, and think all work transparently. baseURL is positional and required (no sensible default for a private daemon). apiKey can be empty for network-trusted deployments. See ADR-0011 in the foreman repo for the integration design. --- v2/constructors.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/v2/constructors.go b/v2/constructors.go index c819505..434ad70 100644 --- a/v2/constructors.go +++ b/v2/constructors.go @@ -137,3 +137,27 @@ func OllamaCloud(apiKey string, opts ...ClientOption) *Client { } return NewClient(ollamaProvider.New(apiKey, baseURL)) } + +// Foreman creates a client targeting a foreman daemon (a private, authenticated +// Ollama endpoint with queuing and observability). baseURL is required +// (e.g. "http://foreman:8080"). apiKey is the optional static bearer token; pass +// "" for an unauthenticated (network-trusted) deployment. +// +// foreman speaks native Ollama on the wire, so this delegates to the ollama +// provider unchanged — streaming, tool use, and think all work transparently. +// +// See: https://gitea.stevedudenhoeffer.com/steve/foreman +// +// Example: +// +// model := llm.Foreman("http://foreman.orgrimmar:8080", token).Model("qwen3:30b") +func Foreman(baseURL, apiKey string, opts ...ClientOption) *Client { + cfg := &clientConfig{} + for _, opt := range opts { + opt(cfg) + } + if cfg.baseURL != "" { + baseURL = cfg.baseURL + } + return NewClient(ollamaProvider.New(apiKey, baseURL)) +}