From a7f3577ee2965fa4f4f68fe872175006355f8f25 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 8 Apr 2026 17:20:03 +0200 Subject: [PATCH] =?UTF-8?q?fix(voice):=20set=20MANA=5FLLM=5FURL=20on=20man?= =?UTF-8?q?a-web=20=E2=80=94=20$env/dynamic/private=20hides=20PUBLIC=5F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first prod deploy of voice quick-add (3b41b39a3) silently fell back for every transcript: title=transcript verbatim, dueDate=null, priority=null, labels=[]. The endpoint code was reaching the fallback() path even though mana-llm was healthy and reachable from inside the mana-web container. Root cause: SvelteKit's $env/dynamic/private explicitly excludes any env var that starts with the public prefix (default PUBLIC_). The parse-task code read env.MANA_LLM_URL || env.PUBLIC_MANA_LLM_URL || 'http://localhost:3025' expecting to fall back to PUBLIC_MANA_LLM_URL when MANA_LLM_URL was unset, but $env/dynamic/private treats PUBLIC_MANA_LLM_URL as if it didn't exist on the server side. So it always fell through to http://localhost:3025, which from inside mana-web is nothing, fetch threw, and coerce returned the fallback shape. Two fixes: 1. docker-compose.macmini.yml — set MANA_LLM_URL (no prefix) on mana-web alongside PUBLIC_MANA_LLM_URL. The PUBLIC_ var is still needed for the browser-side playground and status page; the private one is what the parse endpoints actually read. 2. parse-task and parse-habit — drop the dead env.PUBLIC_MANA_LLM_URL fallback so the next dev who reads the code doesn't think it'd ever work. Add a comment explaining the SvelteKit gotcha so the next person setting up a new env var doesn't repeat this mistake. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../routes/api/v1/voice/parse-habit/+server.ts | 4 +++- .../src/routes/api/v1/voice/parse-task/+server.ts | 5 ++++- docker-compose.macmini.yml | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/mana/apps/web/src/routes/api/v1/voice/parse-habit/+server.ts b/apps/mana/apps/web/src/routes/api/v1/voice/parse-habit/+server.ts index ca3670673..753c2e9b7 100644 --- a/apps/mana/apps/web/src/routes/api/v1/voice/parse-habit/+server.ts +++ b/apps/mana/apps/web/src/routes/api/v1/voice/parse-habit/+server.ts @@ -112,7 +112,9 @@ export const POST: RequestHandler = async ({ request }) => { const language = body.language ?? 'de'; if (!transcript || habits.length === 0) return json(fallback()); - const llmUrl = env.MANA_LLM_URL || env.PUBLIC_MANA_LLM_URL || 'http://localhost:3025'; + // $env/dynamic/private excludes PUBLIC_-prefixed vars; compose must + // set MANA_LLM_URL (no prefix) for this to reach mana-llm in prod. + const llmUrl = env.MANA_LLM_URL || 'http://localhost:3025'; const apiKey = env.MANA_LLM_API_KEY; let response: Response; diff --git a/apps/mana/apps/web/src/routes/api/v1/voice/parse-task/+server.ts b/apps/mana/apps/web/src/routes/api/v1/voice/parse-task/+server.ts index c47878bf6..4c765c7c2 100644 --- a/apps/mana/apps/web/src/routes/api/v1/voice/parse-task/+server.ts +++ b/apps/mana/apps/web/src/routes/api/v1/voice/parse-task/+server.ts @@ -87,7 +87,10 @@ export const POST: RequestHandler = async ({ request }) => { const language = body.language ?? 'de'; if (!transcript) return json(fallback('')); - const llmUrl = env.MANA_LLM_URL || env.PUBLIC_MANA_LLM_URL || 'http://localhost:3025'; + // $env/dynamic/private explicitly excludes vars with the PUBLIC_ + // prefix, so the compose file MUST set MANA_LLM_URL (no prefix) + // alongside PUBLIC_MANA_LLM_URL for this to reach mana-llm in prod. + const llmUrl = env.MANA_LLM_URL || 'http://localhost:3025'; const apiKey = env.MANA_LLM_API_KEY; let response: Response; diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index 874cea160..2d32c388b 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -766,11 +766,20 @@ services: # Analytics & Error Tracking PUBLIC_UMAMI_WEBSITE_ID: 32777167-e026-4618-933a-3429120b479b PUBLIC_GLITCHTIP_DSN: ${GLITCHTIP_DSN_MANA_WEB:-} - # Speech-to-Text proxy: SvelteKit /api/v1/memoro/transcribe and - # /api/v1/dreams/transcribe forward to mana-stt via Cloudflare Tunnel. - # The browser never sees the API key — it stays server-side. + # Speech-to-Text proxy: SvelteKit /api/v1/voice/transcribe forwards + # to mana-stt via Cloudflare Tunnel. The browser never sees the API + # key — it stays server-side. MANA_STT_URL: https://gpu-stt.mana.how MANA_STT_API_KEY: ${MANA_STT_API_KEY:-} + # LLM proxy: /api/v1/voice/parse-task and /api/v1/voice/parse-habit + # call mana-llm for structured extraction. Set WITHOUT the PUBLIC_ + # prefix because $env/dynamic/private explicitly excludes vars + # that start with the public prefix — so the parse endpoints + # would never see PUBLIC_MANA_LLM_URL even though it's right + # there in the compose env. Both vars exist; the public one + # is read by the browser-side playground and status page. + MANA_LLM_URL: http://mana-llm:3025 + MANA_LLM_API_KEY: ${MANA_LLM_API_KEY:-} ports: - "5000:5000" healthcheck: