3e81fbd540
- README + CLAUDE.md: upfront "this is a vibe-coded project" disclosure for going public. - Replace internal LAN hostnames (*.orgrimmar.dudenhoeffer.casa) with example.com across README, ADR-0004, the envproviders example, and env_test.go (assertions updated together; suite still green). Token was already a "change-me" placeholder, not a real secret. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.6 KiB
2.6 KiB
ADR-0004: LLM_* env-DSN provider definitions (go-llm parity, plus eager load)
Status: Accepted — 2026-06-10
Context
Steve's deployments define providers via env vars that must keep working unchanged:
LLM_M1=foreman://token@foreman-m1.example.com
LLM_M5=foreman://token@foreman-m5.example.com
go-llm (v2/parse.go) implements this lazily only: Parse("m5/x") misses
the registry, computes LLM_ + UPPER(name) with -→_, reads exactly that
var, parses scheme://[token@]host[/path] by plain string splits, requires
the scheme to be a registered provider, and dials https:// + host. There is
no environment scan. The kickoff additionally requires New() to load LLM_*
providers eagerly and a testable LoadEnv(map).
Decision
Implement both paths over one DSN parser (byte-for-byte go-llm
semantics — :// split, first-@ split, trailing-/ trim, ErrInvalidDSN on
missing scheme/host, base URL always https://host[/path]):
- Eager:
New()scans the process environment forLLM_<NAME>and registers each as providerlower(<NAME>)(underscores preserved:LLM_MY_BOX→my_box).LoadEnv(map[string]string)is the explicit, testable entry. Malformed entries never fail construction: they are recorded per-name, returned joined from LoadEnv, and surface from Parse only when that name is actually referenced (matching go-llm's fail-on-use behavior). - Lazy (go-llm parity): an unknown provider name in Parse falls back to
LLM_{UPPER(name, - → _)}, so hyphenated spec names (my-prov/x→LLM_MY_PROV) work exactly as in go-llm. Lazily resolved providers are cached in the registry. - The DSN scheme selects a
SchemeFactory(foreman, ollama, ollama-cloud, openai, anthropic, google, gemini; extensible viaRegisterScheme). The factory receives the registry name and the parsed DSN (token = credential,https://host= base URL).
Consequences
- Existing muscle memory carries over: every go-llm-resolvable LLM_* var resolves identically here.
- Eager loading additionally makes env providers visible to discovery
(
Provider(name)) before first use. - An env DSN cannot express plain-http endpoints (https is forced) — same
limitation as go-llm, kept deliberately for parity; local Ollama uses the
ollamaprovider's own default (http://localhost:11434) rather than a DSN.
Alternatives considered
url.Parse-based DSN parsing: subtly different (percent-decoding, userinfo passwords). Parity wins. Rejected.- Failing New() on malformed LLM_* vars: one stray var would break every consumer at startup. Rejected.