From 5a3fc20fc7535ec48ee480972c4369bfcee52035 Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Tue, 21 Jul 2026 02:40:40 -0400 Subject: [PATCH] Say at startup why the assistant is off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verifying the live deploy, the agent routes stayed 404 while every other route from the same build answered — so the binary was current and Agent.Ready() was simply false. Working out which of the three conditions failed meant reading the source, because pansy logged the enabled case and said nothing at all about the disabled one. It now logs which condition failed, with the hint that actually matters: an orchestrator's stack-level environment is not the container's environment, and a key set in one but not passed through to the other looks exactly like a key that was never set. README says the same thing next to the compose example. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ --- README.md | 5 ++++- internal/api/api.go | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a4b959d..af9385e 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ The garden assistant reads three more. Setting none of them leaves the assistant The assistant acts without asking first, which is only reasonable because every turn is one undoable change set — see the History panel in the editor. +**If you set the key and the assistant still doesn't appear**, check that the variable reaches the *container*, not just your orchestrator's stack config — Compose needs it listed under the service's `environment:`. pansy logs `garden assistant disabled` at startup with which of the three conditions failed, so the answer is in the first few lines of the log. + Local email/password auth is live (`POST /api/v1/auth/register`, `/auth/login`, `/auth/logout`, `GET /auth/me`, `GET /auth/providers`); the session is an HttpOnly cookie (`Secure` when `PANSY_BASE_URL` is https). The first account registered becomes admin, and it may register even when `PANSY_REGISTRATION=closed` to bootstrap the instance. OIDC (Authentik-first) is live too: set `PANSY_OIDC_ISSUER`, `PANSY_OIDC_CLIENT_ID`, `PANSY_OIDC_CLIENT_SECRET`, and `PANSY_BASE_URL` (needed for the redirect URI). Register `PANSY_BASE_URL` + `/api/v1/auth/oidc/callback` as the redirect URI in your IdP. `GET /auth/oidc/login` starts an authorization-code + PKCE flow; first login provisions a user just-in-time (a matching *verified* email links to an existing local account instead of duplicating it). Provider discovery is lazy, so a briefly-unreachable IdP never blocks startup or local auth. Set `PANSY_LOCAL_AUTH=false` for pure-Authentik deployments (local register/login are then rejected and hidden from `/auth/providers`). @@ -111,7 +113,8 @@ services: # PANSY_OIDC_ISSUER: https://auth.example.com/application/o/pansy/ # PANSY_OIDC_CLIENT_ID: ... # PANSY_OIDC_CLIENT_SECRET: ... - # OLLAMA_CLOUD_API_KEY: ... # enables the garden assistant + # OLLAMA_CLOUD_API_KEY: ${OLLAMA_CLOUD_API_KEY} # enables the garden assistant + # PANSY_AGENT_MODEL: ollama-cloud/glm-5.2:cloud restart: unless-stopped volumes: pansy-data: diff --git a/internal/api/api.go b/internal/api/api.go index 22e013b..c84ce0c 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -131,6 +131,17 @@ func New(cfg *config.Config, svc *service.Service) *gin.Engine { // The garden assistant, registered only when it can actually be offered — // the same shape as OIDC. An instance with no API key serves the app // normally and simply doesn't have these routes. + if !cfg.Agent.Ready() { + // Say WHY, at startup, in the logs an operator is already looking at. + // Someone who set the key and sees no assistant otherwise has nothing to + // check — and "is the variable reaching the container?" is exactly the + // question they need answered. + slog.Info("api: garden assistant disabled", + "enabled", cfg.Agent.Enabled, + "hasApiKey", cfg.Agent.OllamaCloudAPIKey != "", + "model", cfg.Agent.Model, + "hint", "needs OLLAMA_CLOUD_API_KEY set in the container's environment (not just the stack's)") + } if cfg.Agent.Ready() { runner, err := agent.NewRunner(svc, cfg) if err != nil {