From efbec242ff09e273fe50f1e19a1b22c391d7f558 Mon Sep 17 00:00:00 2001 From: Norberia <30403707+norberia@users.noreply.github.com> Date: Thu, 10 Sep 2026 17:49:29 +0800 Subject: [PATCH 1/2] fix: standardize Langfuse endpoint on LANGFUSE_BASE_URL --- CHANGELOG.md | 9 +++++++ apps/ui/.env.example | 4 +-- .../task/runner.github-ai-proxy.test.ts | 25 ++++++++++++++++--- apps/ui/src/features/deploy/task/runner.ts | 6 ++--- .../lib/observability/langfuse-core.test.ts | 19 +++++++++++--- .../ui/src/lib/observability/langfuse-core.ts | 3 ++- .../ui/src/lib/observability/langfuse.test.ts | 4 +-- charts/brain-system/README.md | 2 +- charts/brain-system/values.local.example.yaml | 4 +-- charts/brain-system/values.yaml | 4 +-- turbo.json | 2 +- 11 files changed, 60 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4481103..660aa5fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to Brain are documented in this file. +## [Unreleased] + +### Changed + +- Rename the Langfuse endpoint environment variable to `LANGFUSE_BASE_URL` + for Chat Assistant tracing and newly created GitHub Deploy Devboxes. + Deployments must rename `LANGFUSE_HOST`; the old name is no longer read or + forwarded. Recreate existing Devboxes to apply the updated environment. + ## [2.0.14] - 2026-09-10 ### Added diff --git a/apps/ui/.env.example b/apps/ui/.env.example index 71ff383d..3740001d 100644 --- a/apps/ui/.env.example +++ b/apps/ui/.env.example @@ -74,13 +74,13 @@ GITHUB_DEPLOY_MODEL= # of SYSTEM_OPENAI_* and host CODEX_GATEWAY_OPENAI_* values. GITHUB_DEPLOY_OPENAI_API_KEY= GITHUB_DEPLOY_OPENAI_BASE_URL= -# Optional Chat Assistant telemetry. Set both keys and LANGFUSE_HOST to enable. +# Optional Chat Assistant telemetry. Set both keys and LANGFUSE_BASE_URL to enable. # Also forwarded into newly created GitHub Deploy Devboxes for Codex traces. LANGFUSE_PUBLIC_KEY= LANGFUSE_SECRET_KEY= # Langfuse instance root URL. Blank disables telemetry; no Cloud fallback. # Self-hosted Langfuse is supported. Do not append /api/public/otel. -LANGFUSE_HOST= +LANGFUSE_BASE_URL= GITHUB_APP_ID= GITHUB_APP_PRIVATE_KEY= diff --git a/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts b/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts index 41a865aa..2750ffaa 100644 --- a/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts +++ b/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts @@ -27,7 +27,7 @@ const ENV_KEYS = [ "GITHUB_DEPLOY_MODEL", "GITHUB_DEPLOY_OPENAI_API_KEY", "GITHUB_DEPLOY_OPENAI_BASE_URL", - "LANGFUSE_HOST", + "LANGFUSE_BASE_URL", "LANGFUSE_PUBLIC_KEY", "LANGFUSE_SECRET_KEY", "SYSTEM_OPENAI_API_KEY", @@ -244,7 +244,7 @@ describe("deployment AI Proxy credentials", () => { "https://brain.test/api/deploy-agent/mcp/v1"; delete process.env.GITHUB_DEPLOY_OPENAI_API_KEY; delete process.env.GITHUB_DEPLOY_OPENAI_BASE_URL; - delete process.env.LANGFUSE_HOST; + delete process.env.LANGFUSE_BASE_URL; delete process.env.LANGFUSE_PUBLIC_KEY; delete process.env.LANGFUSE_SECRET_KEY; }); @@ -386,17 +386,34 @@ describe("deployment AI Proxy credentials", () => { it("forwards trimmed LANGFUSE_* values when they are set", () => { process.env.LANGFUSE_PUBLIC_KEY = " pk-lf-test "; process.env.LANGFUSE_SECRET_KEY = " sk-lf-test "; - process.env.LANGFUSE_HOST = " https://langfuse.example.com "; + process.env.LANGFUSE_BASE_URL = " https://langfuse.example.com "; expect(buildCodexGatewayEnv(RESOLVED_GATEWAY_CREDENTIALS)).toEqual({ CODEX_GATEWAY_MODEL: "deploy-model", CODEX_GATEWAY_OPENAI_API_KEY: "resolved-key", CODEX_GATEWAY_OPENAI_BASE_URL: "https://resolved.example/v1", LANGFUSE_PUBLIC_KEY: "pk-lf-test", LANGFUSE_SECRET_KEY: "sk-lf-test", - LANGFUSE_HOST: "https://langfuse.example.com", + LANGFUSE_BASE_URL: "https://langfuse.example.com", }); }); + it("does not forward the removed Langfuse host variable", () => { + const previous = process.env.LANGFUSE_HOST; + try { + process.env.LANGFUSE_HOST = "https://old-langfuse.example.com"; + delete process.env.LANGFUSE_BASE_URL; + const env = buildCodexGatewayEnv(RESOLVED_GATEWAY_CREDENTIALS); + expect(env.LANGFUSE_HOST).toBeUndefined(); + expect(env.LANGFUSE_BASE_URL).toBeUndefined(); + } finally { + if (previous === undefined) { + delete process.env.LANGFUSE_HOST; + } else { + process.env.LANGFUSE_HOST = previous; + } + } + }); + it("uses GITHUB_DEPLOY_OPENAI_* when both are set", () => { process.env.GITHUB_DEPLOY_OPENAI_API_KEY = " github-override-key "; process.env.GITHUB_DEPLOY_OPENAI_BASE_URL = " https://override.example/v1 "; diff --git a/apps/ui/src/features/deploy/task/runner.ts b/apps/ui/src/features/deploy/task/runner.ts index fbadec48..81a9c5f2 100644 --- a/apps/ui/src/features/deploy/task/runner.ts +++ b/apps/ui/src/features/deploy/task/runner.ts @@ -1116,15 +1116,15 @@ export function buildCodexGatewayEnv( const langfusePublicKey = compactEnvValue(process.env.LANGFUSE_PUBLIC_KEY); const langfuseSecretKey = compactEnvValue(process.env.LANGFUSE_SECRET_KEY); - const langfuseHost = compactEnvValue(process.env.LANGFUSE_HOST); + const langfuseBaseUrl = compactEnvValue(process.env.LANGFUSE_BASE_URL); if (langfusePublicKey != null) { env.LANGFUSE_PUBLIC_KEY = langfusePublicKey; } if (langfuseSecretKey != null) { env.LANGFUSE_SECRET_KEY = langfuseSecretKey; } - if (langfuseHost != null) { - env.LANGFUSE_HOST = langfuseHost; + if (langfuseBaseUrl != null) { + env.LANGFUSE_BASE_URL = langfuseBaseUrl; } return env; diff --git a/apps/ui/src/lib/observability/langfuse-core.test.ts b/apps/ui/src/lib/observability/langfuse-core.test.ts index 722925b1..3cee1210 100644 --- a/apps/ui/src/lib/observability/langfuse-core.test.ts +++ b/apps/ui/src/lib/observability/langfuse-core.test.ts @@ -15,12 +15,23 @@ test("disables Langfuse when credentials are absent or incomplete", () => { ); }); +test("does not accept the removed host environment variable", () => { + assert.equal( + getLangfuseConfigFromEnv({ + LANGFUSE_PUBLIC_KEY: "pk-lf-test", + LANGFUSE_SECRET_KEY: "sk-lf-test", + LANGFUSE_HOST: "https://langfuse.example.test", + }), + null + ); +}); + test("trims credentials and requires an explicit host", () => { assert.deepEqual( getLangfuseConfigFromEnv({ LANGFUSE_PUBLIC_KEY: " pk-lf-test ", LANGFUSE_SECRET_KEY: " sk-lf-test ", - LANGFUSE_HOST: " https://langfuse.example.test/// ", + LANGFUSE_BASE_URL: " https://langfuse.example.test/// ", }), { publicKey: "pk-lf-test", @@ -32,7 +43,7 @@ test("trims credentials and requires an explicit host", () => { getLangfuseConfigFromEnv({ LANGFUSE_PUBLIC_KEY: "pk-lf-test", LANGFUSE_SECRET_KEY: "sk-lf-test", - LANGFUSE_HOST: " ", + LANGFUSE_BASE_URL: " ", }), null ); @@ -43,7 +54,7 @@ for (const host of [undefined, "", " "]) { const env = { LANGFUSE_PUBLIC_KEY: "pk-test", LANGFUSE_SECRET_KEY: "sk-test", - LANGFUSE_HOST: host, + LANGFUSE_BASE_URL: host, }; assert.equal(getLangfuseConfigFromEnv(env), null); }); @@ -54,7 +65,7 @@ test("allows explicitly configured Langfuse Cloud", () => { getLangfuseConfigFromEnv({ LANGFUSE_PUBLIC_KEY: "pk-test", LANGFUSE_SECRET_KEY: "sk-test", - LANGFUSE_HOST: "https://cloud.langfuse.com", + LANGFUSE_BASE_URL: "https://cloud.langfuse.com", })?.baseUrl, "https://cloud.langfuse.com" ); diff --git a/apps/ui/src/lib/observability/langfuse-core.ts b/apps/ui/src/lib/observability/langfuse-core.ts index be77768b..5960099b 100644 --- a/apps/ui/src/lib/observability/langfuse-core.ts +++ b/apps/ui/src/lib/observability/langfuse-core.ts @@ -20,7 +20,8 @@ export function getLangfuseConfigFromEnv( const publicKey = env.LANGFUSE_PUBLIC_KEY?.trim() ?? ""; const secretKey = env.LANGFUSE_SECRET_KEY?.trim() ?? ""; - const baseUrl = env.LANGFUSE_HOST?.trim().replace(TRAILING_SLASHES, "") ?? ""; + const baseUrl = + env.LANGFUSE_BASE_URL?.trim().replace(TRAILING_SLASHES, "") ?? ""; if (publicKey === "" || secretKey === "" || baseUrl === "") { return null; diff --git a/apps/ui/src/lib/observability/langfuse.test.ts b/apps/ui/src/lib/observability/langfuse.test.ts index 7e164064..d505a487 100644 --- a/apps/ui/src/lib/observability/langfuse.test.ts +++ b/apps/ui/src/lib/observability/langfuse.test.ts @@ -33,13 +33,13 @@ const { test("failed integration registration disables telemetry and shuts down the SDK", async () => { const previous = { - LANGFUSE_HOST: process.env.LANGFUSE_HOST, + LANGFUSE_BASE_URL: process.env.LANGFUSE_BASE_URL, LANGFUSE_PUBLIC_KEY: process.env.LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY: process.env.LANGFUSE_SECRET_KEY, }; try { Object.assign(process.env, { - LANGFUSE_HOST: "https://langfuse.example.test", + LANGFUSE_BASE_URL: "https://langfuse.example.test", LANGFUSE_PUBLIC_KEY: "pk-test", LANGFUSE_SECRET_KEY: "sk-test", }); diff --git a/charts/brain-system/README.md b/charts/brain-system/README.md index 265d2727..b0b652cf 100644 --- a/charts/brain-system/README.md +++ b/charts/brain-system/README.md @@ -41,7 +41,7 @@ then edit `$private_values_file`, especially: - `GITHUB_USER_TOKEN_ENCRYPTION_KEY`: keep stable; changing it prevents decrypting previously stored GitHub user tokens - Assistant chat values: `SYSTEM_OPENAI_*` funds `FREE_CHAT_TURNS` successful turns for eligible Active Free Trial workspaces; later turns use the caller's AI Proxy (`AI_PROXY_TOKEN_NAME`). `ASSISTANT_GATEWAY_MODEL` optionally selects the Chat Agent model - optional GitHub Deploy model (`GITHUB_DEPLOY_MODEL`); blank uses `gpt-5.5`. Independent of `ASSISTANT_GATEWAY_MODEL`, which selects the Chat Agent model -- optional Chat Assistant Langfuse tracing (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`); all three values must be set. An empty host disables tracing; Cloud requires an explicit `https://cloud.langfuse.com` or `https://us.cloud.langfuse.com`. Use an instance root URL without `/api/public/otel`. These credentials enable Chat tracing in Brain UI and are forwarded into newly created GitHub Deploy Devboxes for Codex traces. Existing Devboxes retain their old environment until recreated. +- optional Chat Assistant Langfuse tracing (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_BASE_URL`); all three values must be set. An empty host disables tracing; Cloud requires an explicit `https://cloud.langfuse.com` or `https://us.cloud.langfuse.com`. Use an instance root URL without `/api/public/otel`. These credentials enable Chat tracing in Brain UI and are forwarded into newly created GitHub Deploy Devboxes for Codex traces. Existing Devboxes retain their old environment until recreated. - optional platform-funded GitHub Deploy connection (`GITHUB_DEPLOY_OPENAI_API_KEY`, `GITHUB_DEPLOY_OPENAI_BASE_URL`); when both are set, GitHub Deploy uses them. When both are blank, it uses the caller's AI Proxy. A partial pair is an error; it never reuses Chat Agent or host Codex credentials - `MARKETING_EVENTS_INGEST_SECRET`: shared bearer secret for trusted lifecycle event producers - `MARKETING_CONSENT_SIGNING_KEY`: shared HS256 secret used to verify Desktop-issued consent tokens diff --git a/charts/brain-system/values.local.example.yaml b/charts/brain-system/values.local.example.yaml index a69420ab..bbe0bb01 100644 --- a/charts/brain-system/values.local.example.yaml +++ b/charts/brain-system/values.local.example.yaml @@ -67,13 +67,13 @@ ui: SYSTEM_OPENAI_API_KEY: "REPLACE_ME_OPENAI_COMPATIBLE_KEY" SYSTEM_OPENAI_API_BASE_URL: "https://api.openai.com/v1" FREE_CHAT_TURNS: "5" - # Optional Chat Assistant telemetry. Set both keys and LANGFUSE_HOST to enable. + # Optional Chat Assistant telemetry. Set both keys and LANGFUSE_BASE_URL to enable. # Also forwarded into newly created GitHub Deploy Devboxes for Codex traces. LANGFUSE_PUBLIC_KEY: "" LANGFUSE_SECRET_KEY: "" # Langfuse instance root URL. Blank disables telemetry; no Cloud fallback. # Self-hosted Langfuse is supported. Do not append /api/public/otel. - LANGFUSE_HOST: "" + LANGFUSE_BASE_URL: "" # User-billed turns use the Sealos AI proxy. # Later Chat Agent turns use the caller's Sealos AI Proxy. AI_PROXY_TOKEN_NAME: "sealos-brain" diff --git a/charts/brain-system/values.yaml b/charts/brain-system/values.yaml index 380f1915..bebae256 100644 --- a/charts/brain-system/values.yaml +++ b/charts/brain-system/values.yaml @@ -138,13 +138,13 @@ ui: # of SYSTEM_OPENAI_* and host CODEX_GATEWAY_OPENAI_* values. GITHUB_DEPLOY_OPENAI_API_KEY: "" GITHUB_DEPLOY_OPENAI_BASE_URL: "" - # Optional Chat Assistant telemetry. Set both keys and LANGFUSE_HOST to enable. + # Optional Chat Assistant telemetry. Set both keys and LANGFUSE_BASE_URL to enable. # Also forwarded into newly created GitHub Deploy Devboxes for Codex traces. LANGFUSE_PUBLIC_KEY: "" LANGFUSE_SECRET_KEY: "" # Langfuse instance root URL. Blank disables telemetry; no Cloud fallback. # Self-hosted Langfuse is supported. Do not append /api/public/otel. - LANGFUSE_HOST: "" + LANGFUSE_BASE_URL: "" GITHUB_OAUTH_CLIENT_ID: "" GITHUB_OAUTH_CLIENT_SECRET: "" GITHUB_USER_TOKEN_ENCRYPTION_KEY: "" diff --git a/turbo.json b/turbo.json index eeb0ea05..d037710c 100644 --- a/turbo.json +++ b/turbo.json @@ -46,7 +46,7 @@ "KUBERNETES_SERVICE_HOST", "KUBERNETES_SERVICE_PORT", "KUBECONFIG", - "LANGFUSE_HOST", + "LANGFUSE_BASE_URL", "LANGFUSE_PUBLIC_KEY", "LANGFUSE_SECRET_KEY", "MARKETING_CONSENT_SIGNING_KEY", From ab5f47b6eee47aeb35b072a14a98fa4a79b9bcfb Mon Sep 17 00:00:00 2001 From: Norberia <30403707+norberia@users.noreply.github.com> Date: Thu, 10 Sep 2026 17:55:40 +0800 Subject: [PATCH 2/2] test: allow removed Langfuse variable in regression coverage --- apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts b/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts index 2750ffaa..a143eb33 100644 --- a/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts +++ b/apps/ui/src/features/deploy/task/runner.github-ai-proxy.test.ts @@ -398,6 +398,7 @@ describe("deployment AI Proxy credentials", () => { }); it("does not forward the removed Langfuse host variable", () => { + /* eslint-disable turbo/no-undeclared-env-vars -- Regression test intentionally sets the removed variable, which must stay out of turbo.json. */ const previous = process.env.LANGFUSE_HOST; try { process.env.LANGFUSE_HOST = "https://old-langfuse.example.com"; @@ -412,6 +413,7 @@ describe("deployment AI Proxy credentials", () => { process.env.LANGFUSE_HOST = previous; } } + /* eslint-enable turbo/no-undeclared-env-vars */ }); it("uses GITHUB_DEPLOY_OPENAI_* when both are set", () => {