🪢 fix: disable central fanout media uploads for langfuse (#14642)

* fix(langfuse): disable central fanout media uploads

* test(langfuse): cover fanout media policy in run config

* chore(deps): bump agents for Langfuse media policy

* chore(deps): bump agents to 3.3.13

* fix(langfuse): gate central fanout media uploads
This commit is contained in:
Ravi Kumar L 2026-08-05 23:23:54 +02:00 committed by GitHub
parent 4cec1a675f
commit 7775f25b0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 57 additions and 7 deletions

View file

@ -164,6 +164,9 @@ NODE_MAX_OLD_SPACE_SIZE=6144
# See otel/langfuse-fanout/README.md.
# LANGFUSE_FANOUT_ENABLED=false
# LANGFUSE_FANOUT_COLLECTOR_URL=http://langfuse-fanout-collector:4318
# App-side switch: set true to tell the Langfuse SDK not to create media uploads
# for central/fallback collector traces. Tenant-routed media uploads are unchanged.
# LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED=false
# Emergency switch: unset/false defaults enabled; set true to keep central fanout export but skip tenant trace/score export.
# LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED=false
# Langfuse Cloud base URL options: https://cloud.langfuse.com (EU),

View file

@ -46,7 +46,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "^4.3.3",
"@librechat/agents": "^3.3.12",
"@librechat/agents": "^3.3.13",
"@librechat/api": "*",
"@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7",

10
package-lock.json generated
View file

@ -61,7 +61,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "^4.3.3",
"@librechat/agents": "^3.3.12",
"@librechat/agents": "^3.3.13",
"@librechat/api": "*",
"@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7",
@ -10616,9 +10616,9 @@
}
},
"node_modules/@librechat/agents": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.3.12.tgz",
"integrity": "sha512-kTB50KMlEXR3jMLmGMZzzSkne019k57Wy3Fv0dGX7I4G/oxJMgqZsw70KqiAMYokNOQnc1DoWGmIcZLgJYcewA==",
"version": "3.3.13",
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.3.13.tgz",
"integrity": "sha512-NOaUCvMaq82ftzZr/Z/3KL1LzfKO0YRHibhFigcx22KxrmeioyJ32w4RnrBsYGhLx0gDrI4nWlW6Sk0ZAH/YBQ==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.115.0",
@ -42707,7 +42707,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "^4.3.3",
"@librechat/agents": "^3.3.12",
"@librechat/agents": "^3.3.13",
"@librechat/data-schemas": "*",
"@modelcontextprotocol/sdk": "^1.29.0",
"@opentelemetry/api": "^1.9.0",

View file

@ -107,7 +107,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "^4.3.3",
"@librechat/agents": "^3.3.12",
"@librechat/agents": "^3.3.13",
"@librechat/data-schemas": "*",
"@modelcontextprotocol/sdk": "^1.29.0",
"@opentelemetry/api": "^1.9.0",

View file

@ -222,6 +222,7 @@ beforeEach(() => {
delete process.env.LANGFUSE_HOST;
delete process.env.LANGFUSE_FANOUT_ENABLED;
delete process.env.LANGFUSE_FANOUT_COLLECTOR_URL;
delete process.env.LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED;
delete process.env.LANGFUSE_FANOUT_TENANT_DESTINATIONS;
delete process.env.LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED;
delete process.env.LANGFUSE_TRACING_ENABLED;

View file

@ -12,6 +12,7 @@ const envKeys = [
'LANGFUSE_BASEURL',
'LANGFUSE_FANOUT_ENABLED',
'LANGFUSE_FANOUT_COLLECTOR_URL',
'LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED',
'LANGFUSE_FANOUT_TENANT_DESTINATIONS',
'LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED',
'LANGFUSE_TRACING_ENABLED',
@ -184,6 +185,7 @@ describe('buildLangfuseConfig', () => {
'librechat.langfuse.destination': 'eu',
},
});
expect(config).not.toHaveProperty('mediaUploadEnabled');
});
it('fails closed to central-only export when tenant secret decryption fails', async () => {
@ -438,4 +440,39 @@ describe('buildLangfuseConfig', () => {
tags: ['tenant:tenant-1'],
});
});
it.each(['true', '1', 'yes', 'on'])(
'disables central collector media uploads when LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED is %s',
async (value) => {
process.env.LANGFUSE_FANOUT_ENABLED = 'true';
process.env.LANGFUSE_FANOUT_COLLECTOR_URL = 'http://collector-from-env:4318';
process.env.LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED = value;
const { buildLangfuseConfig } = await import('./config');
expect(buildLangfuseConfig({ tenantId: 'tenant-1' })).toEqual({
deterministicTraceId: true,
baseUrl: 'http://collector-from-env:4318',
mediaUploadEnabled: false,
metadata: { 'librechat.tenant.id': 'tenant-1' },
tags: ['tenant:tenant-1'],
});
},
);
it.each(['false', '0', 'no', 'off', ''])(
'keeps central collector media uploads at the SDK default when LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED is %s',
async (value) => {
process.env.LANGFUSE_FANOUT_ENABLED = 'true';
process.env.LANGFUSE_FANOUT_COLLECTOR_URL = 'http://collector-from-env:4318';
process.env.LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED = value;
const { buildLangfuseConfig } = await import('./config');
expect(buildLangfuseConfig({ tenantId: 'tenant-1' })).toEqual({
deterministicTraceId: true,
baseUrl: 'http://collector-from-env:4318',
metadata: { 'librechat.tenant.id': 'tenant-1' },
tags: ['tenant:tenant-1'],
});
},
);
});

View file

@ -2,6 +2,7 @@ import type { AppConfig } from '@librechat/data-schemas';
import type { RunConfig } from '@librechat/agents';
import {
hasLangfuseEnvCredentials,
isLangfuseCentralMediaUploadDisabled,
isLangfuseFanoutEnabled,
isLangfuseTenantExportEnabled,
isLangfuseTraceSampled,
@ -16,6 +17,7 @@ import { traceIdForMessage } from './trace';
type LangfuseRunConfig = NonNullable<RunConfig['langfuse']>;
type LangfuseRunConfigWithTraceAttributes = LangfuseRunConfig & {
librechatTraceAttributes?: Record<string, string | number | boolean | null | undefined>;
mediaUploadEnabled?: boolean;
};
type LangfuseTenantDestination = NonNullable<ReturnType<typeof resolveLangfuseTenantDestination>>;
type LangfuseExportPlan =
@ -226,6 +228,9 @@ export function buildLangfuseConfig({
break;
case 'fanoutCollector':
langfuse.baseUrl = exportPlan.collectorUrl;
if (isLangfuseCentralMediaUploadDisabled()) {
langfuse.mediaUploadEnabled = false;
}
break;
case 'disabled':
langfuse.enabled = false;

View file

@ -8,6 +8,10 @@ export function isLangfuseTenantExportEnabled(): boolean {
return !isTrueEnv(process.env.LANGFUSE_FANOUT_TENANT_EXPORT_DISABLED);
}
export function isLangfuseCentralMediaUploadDisabled(): boolean {
return isTrueEnv(process.env.LANGFUSE_FANOUT_CENTRAL_MEDIA_UPLOAD_DISABLED);
}
export function isLangfuseFanoutEnabled(): boolean {
return (
isTrueEnv(process.env.LANGFUSE_FANOUT_ENABLED) &&