@librechat/agents 3.1.99 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,13 @@ var langchain = require('@langfuse/langchain');
5
5
  var misc = require('./utils/misc.cjs');
6
6
 
7
7
  const TRACE_METADATA_MAX_LENGTH = 200;
8
+ const LANGFUSE_FORCE_FLUSH_ON_DISPOSE = 'LANGFUSE_FORCE_FLUSH_ON_DISPOSE';
9
+ function parseBooleanEnv(value) {
10
+ if (value == null) {
11
+ return false;
12
+ }
13
+ return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
14
+ }
8
15
  function hasLangfuseConfigCredentials(langfuse) {
9
16
  return (langfuse != null &&
10
17
  misc.isPresent(langfuse.publicKey) &&
@@ -70,7 +77,8 @@ function isLangfuseCallbackHandler(value) {
70
77
  return value instanceof langchain.CallbackHandler;
71
78
  }
72
79
  async function disposeLangfuseHandler(value) {
73
- if (value == null) {
80
+ if (value == null ||
81
+ !parseBooleanEnv(process.env[LANGFUSE_FORCE_FLUSH_ON_DISPOSE])) {
74
82
  return;
75
83
  }
76
84
  const provider = tracing.getLangfuseTracerProvider();
@@ -1 +1 @@
1
- {"version":3,"file":"langfuse.cjs","sources":["../../src/langfuse.ts"],"sourcesContent":["import { getLangfuseTracerProvider } from '@langfuse/tracing';\nimport { CallbackHandler } from '@langfuse/langchain';\nimport type * as t from '@/types';\nimport { isPresent } from '@/utils/misc';\n\nconst TRACE_METADATA_MAX_LENGTH = 200;\n\nexport type LangfuseTraceMetadata = Record<string, string>;\n\ntype LangfuseHandlerParams = {\n userId?: string;\n sessionId?: string;\n traceMetadata?: LangfuseTraceMetadata;\n tags?: string[];\n};\n\ntype AgentLangfuseHandlerParams = LangfuseHandlerParams & {\n langfuse?: t.LangfuseConfig;\n};\n\ntype FlushableTracerProvider = {\n forceFlush?: () => Promise<void> | void;\n};\n\nfunction hasLangfuseTracingConfig(langfuse?: t.LangfuseConfig): boolean {\n return (\n langfuse?.toolNodeTracing != null || langfuse?.toolOutputTracing != null\n );\n}\n\nexport function hasLangfuseConfigCredentials(\n langfuse?: t.LangfuseConfig\n): langfuse is t.LangfuseConfig & {\n publicKey: string;\n secretKey: string;\n} {\n return (\n langfuse != null &&\n isPresent(langfuse.publicKey) &&\n isPresent(langfuse.secretKey)\n );\n}\n\nfunction hasLangfuseConfigBaseUrl(langfuse?: t.LangfuseConfig): boolean {\n return isPresent(langfuse?.baseUrl);\n}\n\nexport function isExplicitLangfuseConfig(\n langfuse?: t.LangfuseConfig\n): boolean {\n return (\n langfuse?.enabled != null ||\n isPresent(langfuse?.publicKey) ||\n isPresent(langfuse?.secretKey) ||\n isPresent(langfuse?.baseUrl) ||\n hasLangfuseTracingConfig(langfuse)\n );\n}\n\nfunction createTraceMetadata(\n metadata: Record<string, unknown>\n): LangfuseTraceMetadata {\n const traceMetadata: LangfuseTraceMetadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (value == null) {\n continue;\n }\n const stringValue = typeof value === 'string' ? value : String(value);\n if (\n stringValue.trim() === '' ||\n stringValue.length > TRACE_METADATA_MAX_LENGTH\n ) {\n continue;\n }\n traceMetadata[key] = stringValue;\n }\n return traceMetadata;\n}\n\nexport function createLangfuseTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n}: {\n messageId?: unknown;\n parentMessageId?: unknown;\n agentId?: unknown;\n agentName?: unknown;\n}): LangfuseTraceMetadata {\n return createTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n });\n}\n\nexport function getLangfuseTraceName(\n traceMetadata?: LangfuseTraceMetadata,\n fallback: string = 'LibreChat Agent'\n): string {\n const agentName = traceMetadata?.agentName;\n return isPresent(agentName) ? `${fallback}: ${agentName}` : fallback;\n}\n\nexport function hasLangfuseEnvConfig(): boolean {\n return hasLangfuseEnvCredentials();\n}\n\nexport function hasLangfuseEnvCredentials(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nexport function shouldCreateLangfuseHandler(\n langfuse?: t.LangfuseConfig\n): boolean {\n if (langfuse?.enabled === false) {\n return false;\n }\n return (\n hasLangfuseEnvConfig() ||\n hasLangfuseConfigCredentials(langfuse) ||\n (hasLangfuseConfigBaseUrl(langfuse) && hasLangfuseEnvCredentials())\n );\n}\n\nexport function createLegacyLangfuseHandler(\n params: LangfuseHandlerParams\n): CallbackHandler {\n return new CallbackHandler(params);\n}\n\nexport function createLangfuseHandler({\n langfuse,\n userId,\n sessionId,\n traceMetadata,\n tags,\n}: AgentLangfuseHandlerParams): CallbackHandler | undefined {\n if (!shouldCreateLangfuseHandler(langfuse)) {\n return undefined;\n }\n return new CallbackHandler({\n userId,\n sessionId,\n traceMetadata,\n tags,\n });\n}\n\nexport function hasExplicitLangfuseConfig(\n contexts: Iterable<{ langfuse?: t.LangfuseConfig }>\n): boolean {\n for (const context of contexts) {\n if (isExplicitLangfuseConfig(context.langfuse)) {\n return true;\n }\n }\n return false;\n}\n\nexport function isLangfuseCallbackHandler(value: unknown): boolean {\n return value instanceof CallbackHandler;\n}\n\nexport async function disposeLangfuseHandler(value: unknown): Promise<void> {\n if (value == null) {\n return;\n }\n const provider = getLangfuseTracerProvider() as FlushableTracerProvider;\n await provider.forceFlush?.();\n}\n"],"names":["isPresent","CallbackHandler","getLangfuseTracerProvider"],"mappings":";;;;;;AAKA,MAAM,yBAAyB,GAAG,GAAG;AAyB/B,SAAU,4BAA4B,CAC1C,QAA2B,EAAA;IAK3B,QACE,QAAQ,IAAI,IAAI;AAChB,QAAAA,cAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC7B,QAAAA,cAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;AAEjC;AAEA,SAAS,wBAAwB,CAAC,QAA2B,EAAA;AAC3D,IAAA,OAAOA,cAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;AACrC;AAcA,SAAS,mBAAmB,CAC1B,QAAiC,EAAA;IAEjC,MAAM,aAAa,GAA0B,EAAE;AAC/C,IAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AACnD,QAAA,IAAI,KAAK,IAAI,IAAI,EAAE;YACjB;QACF;AACA,QAAA,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;AACrE,QAAA,IACE,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE;AACzB,YAAA,WAAW,CAAC,MAAM,GAAG,yBAAyB,EAC9C;YACA;QACF;AACA,QAAA,aAAa,CAAC,GAAG,CAAC,GAAG,WAAW;IAClC;AACA,IAAA,OAAO,aAAa;AACtB;AAEM,SAAU,2BAA2B,CAAC,EAC1C,SAAS,EACT,eAAe,EACf,OAAO,EACP,SAAS,GAMV,EAAA;AACC,IAAA,OAAO,mBAAmB,CAAC;QACzB,SAAS;QACT,eAAe;QACf,OAAO;QACP,SAAS;AACV,KAAA,CAAC;AACJ;SAEgB,oBAAoB,CAClC,aAAqC,EACrC,WAAmB,iBAAiB,EAAA;AAEpC,IAAA,MAAM,SAAS,GAAG,aAAa,EAAE,SAAS;AAC1C,IAAA,OAAOA,cAAS,CAAC,SAAS,CAAC,GAAG,CAAA,EAAG,QAAQ,CAAA,EAAA,EAAK,SAAS,CAAA,CAAE,GAAG,QAAQ;AACtE;SAEgB,oBAAoB,GAAA;IAClC,OAAO,yBAAyB,EAAE;AACpC;SAEgB,yBAAyB,GAAA;IACvC,QACEA,cAAS,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAC1CA,cAAS,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AAE9C;AAEM,SAAU,2BAA2B,CACzC,QAA2B,EAAA;AAE3B,IAAA,IAAI,QAAQ,EAAE,OAAO,KAAK,KAAK,EAAE;AAC/B,QAAA,OAAO,KAAK;IACd;IACA,QACE,oBAAoB,EAAE;QACtB,4BAA4B,CAAC,QAAQ,CAAC;SACrC,wBAAwB,CAAC,QAAQ,CAAC,IAAI,yBAAyB,EAAE,CAAC;AAEvE;AAQM,SAAU,qBAAqB,CAAC,EACpC,QAAQ,EACR,MAAM,EACN,SAAS,EACT,aAAa,EACb,IAAI,GACuB,EAAA;AAC3B,IAAA,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC,EAAE;AAC1C,QAAA,OAAO,SAAS;IAClB;IACA,OAAO,IAAIC,yBAAe,CAAC;QACzB,MAAM;QACN,SAAS;QACT,aAAa;QACb,IAAI;AACL,KAAA,CAAC;AACJ;AAaM,SAAU,yBAAyB,CAAC,KAAc,EAAA;IACtD,OAAO,KAAK,YAAYA,yBAAe;AACzC;AAEO,eAAe,sBAAsB,CAAC,KAAc,EAAA;AACzD,IAAA,IAAI,KAAK,IAAI,IAAI,EAAE;QACjB;IACF;AACA,IAAA,MAAM,QAAQ,GAAGC,iCAAyB,EAA6B;AACvE,IAAA,MAAM,QAAQ,CAAC,UAAU,IAAI;AAC/B;;;;;;;;;;;;"}
1
+ {"version":3,"file":"langfuse.cjs","sources":["../../src/langfuse.ts"],"sourcesContent":["import { getLangfuseTracerProvider } from '@langfuse/tracing';\nimport { CallbackHandler } from '@langfuse/langchain';\nimport type * as t from '@/types';\nimport { isPresent } from '@/utils/misc';\n\nconst TRACE_METADATA_MAX_LENGTH = 200;\nconst LANGFUSE_FORCE_FLUSH_ON_DISPOSE = 'LANGFUSE_FORCE_FLUSH_ON_DISPOSE';\n\nexport type LangfuseTraceMetadata = Record<string, string>;\n\ntype LangfuseHandlerParams = {\n userId?: string;\n sessionId?: string;\n traceMetadata?: LangfuseTraceMetadata;\n tags?: string[];\n};\n\ntype AgentLangfuseHandlerParams = LangfuseHandlerParams & {\n langfuse?: t.LangfuseConfig;\n};\n\ntype FlushableTracerProvider = {\n forceFlush?: () => Promise<void> | void;\n};\n\nfunction parseBooleanEnv(value?: string): boolean {\n if (value == null) {\n return false;\n }\n return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());\n}\n\nfunction hasLangfuseTracingConfig(langfuse?: t.LangfuseConfig): boolean {\n return (\n langfuse?.toolNodeTracing != null || langfuse?.toolOutputTracing != null\n );\n}\n\nexport function hasLangfuseConfigCredentials(\n langfuse?: t.LangfuseConfig\n): langfuse is t.LangfuseConfig & {\n publicKey: string;\n secretKey: string;\n} {\n return (\n langfuse != null &&\n isPresent(langfuse.publicKey) &&\n isPresent(langfuse.secretKey)\n );\n}\n\nfunction hasLangfuseConfigBaseUrl(langfuse?: t.LangfuseConfig): boolean {\n return isPresent(langfuse?.baseUrl);\n}\n\nexport function isExplicitLangfuseConfig(langfuse?: t.LangfuseConfig): boolean {\n return (\n langfuse?.enabled != null ||\n isPresent(langfuse?.publicKey) ||\n isPresent(langfuse?.secretKey) ||\n isPresent(langfuse?.baseUrl) ||\n hasLangfuseTracingConfig(langfuse)\n );\n}\n\nfunction createTraceMetadata(\n metadata: Record<string, unknown>\n): LangfuseTraceMetadata {\n const traceMetadata: LangfuseTraceMetadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (value == null) {\n continue;\n }\n const stringValue = typeof value === 'string' ? value : String(value);\n if (\n stringValue.trim() === '' ||\n stringValue.length > TRACE_METADATA_MAX_LENGTH\n ) {\n continue;\n }\n traceMetadata[key] = stringValue;\n }\n return traceMetadata;\n}\n\nexport function createLangfuseTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n}: {\n messageId?: unknown;\n parentMessageId?: unknown;\n agentId?: unknown;\n agentName?: unknown;\n}): LangfuseTraceMetadata {\n return createTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n });\n}\n\nexport function getLangfuseTraceName(\n traceMetadata?: LangfuseTraceMetadata,\n fallback: string = 'LibreChat Agent'\n): string {\n const agentName = traceMetadata?.agentName;\n return isPresent(agentName) ? `${fallback}: ${agentName}` : fallback;\n}\n\nexport function hasLangfuseEnvConfig(): boolean {\n return hasLangfuseEnvCredentials();\n}\n\nexport function hasLangfuseEnvCredentials(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nexport function shouldCreateLangfuseHandler(\n langfuse?: t.LangfuseConfig\n): boolean {\n if (langfuse?.enabled === false) {\n return false;\n }\n return (\n hasLangfuseEnvConfig() ||\n hasLangfuseConfigCredentials(langfuse) ||\n (hasLangfuseConfigBaseUrl(langfuse) && hasLangfuseEnvCredentials())\n );\n}\n\nexport function createLegacyLangfuseHandler(\n params: LangfuseHandlerParams\n): CallbackHandler {\n return new CallbackHandler(params);\n}\n\nexport function createLangfuseHandler({\n langfuse,\n userId,\n sessionId,\n traceMetadata,\n tags,\n}: AgentLangfuseHandlerParams): CallbackHandler | undefined {\n if (!shouldCreateLangfuseHandler(langfuse)) {\n return undefined;\n }\n return new CallbackHandler({\n userId,\n sessionId,\n traceMetadata,\n tags,\n });\n}\n\nexport function hasExplicitLangfuseConfig(\n contexts: Iterable<{ langfuse?: t.LangfuseConfig }>\n): boolean {\n for (const context of contexts) {\n if (isExplicitLangfuseConfig(context.langfuse)) {\n return true;\n }\n }\n return false;\n}\n\nexport function isLangfuseCallbackHandler(value: unknown): boolean {\n return value instanceof CallbackHandler;\n}\n\nexport async function disposeLangfuseHandler(value: unknown): Promise<void> {\n if (\n value == null ||\n !parseBooleanEnv(process.env[LANGFUSE_FORCE_FLUSH_ON_DISPOSE])\n ) {\n return;\n }\n const provider = getLangfuseTracerProvider() as FlushableTracerProvider;\n await provider.forceFlush?.();\n}\n"],"names":["isPresent","CallbackHandler","getLangfuseTracerProvider"],"mappings":"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"}
@@ -3,6 +3,13 @@ import { CallbackHandler } from '@langfuse/langchain';
3
3
  import { isPresent } from './utils/misc.mjs';
4
4
 
5
5
  const TRACE_METADATA_MAX_LENGTH = 200;
6
+ const LANGFUSE_FORCE_FLUSH_ON_DISPOSE = 'LANGFUSE_FORCE_FLUSH_ON_DISPOSE';
7
+ function parseBooleanEnv(value) {
8
+ if (value == null) {
9
+ return false;
10
+ }
11
+ return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
12
+ }
6
13
  function hasLangfuseConfigCredentials(langfuse) {
7
14
  return (langfuse != null &&
8
15
  isPresent(langfuse.publicKey) &&
@@ -68,7 +75,8 @@ function isLangfuseCallbackHandler(value) {
68
75
  return value instanceof CallbackHandler;
69
76
  }
70
77
  async function disposeLangfuseHandler(value) {
71
- if (value == null) {
78
+ if (value == null ||
79
+ !parseBooleanEnv(process.env[LANGFUSE_FORCE_FLUSH_ON_DISPOSE])) {
72
80
  return;
73
81
  }
74
82
  const provider = getLangfuseTracerProvider();
@@ -1 +1 @@
1
- {"version":3,"file":"langfuse.mjs","sources":["../../src/langfuse.ts"],"sourcesContent":["import { getLangfuseTracerProvider } from '@langfuse/tracing';\nimport { CallbackHandler } from '@langfuse/langchain';\nimport type * as t from '@/types';\nimport { isPresent } from '@/utils/misc';\n\nconst TRACE_METADATA_MAX_LENGTH = 200;\n\nexport type LangfuseTraceMetadata = Record<string, string>;\n\ntype LangfuseHandlerParams = {\n userId?: string;\n sessionId?: string;\n traceMetadata?: LangfuseTraceMetadata;\n tags?: string[];\n};\n\ntype AgentLangfuseHandlerParams = LangfuseHandlerParams & {\n langfuse?: t.LangfuseConfig;\n};\n\ntype FlushableTracerProvider = {\n forceFlush?: () => Promise<void> | void;\n};\n\nfunction hasLangfuseTracingConfig(langfuse?: t.LangfuseConfig): boolean {\n return (\n langfuse?.toolNodeTracing != null || langfuse?.toolOutputTracing != null\n );\n}\n\nexport function hasLangfuseConfigCredentials(\n langfuse?: t.LangfuseConfig\n): langfuse is t.LangfuseConfig & {\n publicKey: string;\n secretKey: string;\n} {\n return (\n langfuse != null &&\n isPresent(langfuse.publicKey) &&\n isPresent(langfuse.secretKey)\n );\n}\n\nfunction hasLangfuseConfigBaseUrl(langfuse?: t.LangfuseConfig): boolean {\n return isPresent(langfuse?.baseUrl);\n}\n\nexport function isExplicitLangfuseConfig(\n langfuse?: t.LangfuseConfig\n): boolean {\n return (\n langfuse?.enabled != null ||\n isPresent(langfuse?.publicKey) ||\n isPresent(langfuse?.secretKey) ||\n isPresent(langfuse?.baseUrl) ||\n hasLangfuseTracingConfig(langfuse)\n );\n}\n\nfunction createTraceMetadata(\n metadata: Record<string, unknown>\n): LangfuseTraceMetadata {\n const traceMetadata: LangfuseTraceMetadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (value == null) {\n continue;\n }\n const stringValue = typeof value === 'string' ? value : String(value);\n if (\n stringValue.trim() === '' ||\n stringValue.length > TRACE_METADATA_MAX_LENGTH\n ) {\n continue;\n }\n traceMetadata[key] = stringValue;\n }\n return traceMetadata;\n}\n\nexport function createLangfuseTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n}: {\n messageId?: unknown;\n parentMessageId?: unknown;\n agentId?: unknown;\n agentName?: unknown;\n}): LangfuseTraceMetadata {\n return createTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n });\n}\n\nexport function getLangfuseTraceName(\n traceMetadata?: LangfuseTraceMetadata,\n fallback: string = 'LibreChat Agent'\n): string {\n const agentName = traceMetadata?.agentName;\n return isPresent(agentName) ? `${fallback}: ${agentName}` : fallback;\n}\n\nexport function hasLangfuseEnvConfig(): boolean {\n return hasLangfuseEnvCredentials();\n}\n\nexport function hasLangfuseEnvCredentials(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nexport function shouldCreateLangfuseHandler(\n langfuse?: t.LangfuseConfig\n): boolean {\n if (langfuse?.enabled === false) {\n return false;\n }\n return (\n hasLangfuseEnvConfig() ||\n hasLangfuseConfigCredentials(langfuse) ||\n (hasLangfuseConfigBaseUrl(langfuse) && hasLangfuseEnvCredentials())\n );\n}\n\nexport function createLegacyLangfuseHandler(\n params: LangfuseHandlerParams\n): CallbackHandler {\n return new CallbackHandler(params);\n}\n\nexport function createLangfuseHandler({\n langfuse,\n userId,\n sessionId,\n traceMetadata,\n tags,\n}: AgentLangfuseHandlerParams): CallbackHandler | undefined {\n if (!shouldCreateLangfuseHandler(langfuse)) {\n return undefined;\n }\n return new CallbackHandler({\n userId,\n sessionId,\n traceMetadata,\n tags,\n });\n}\n\nexport function hasExplicitLangfuseConfig(\n contexts: Iterable<{ langfuse?: t.LangfuseConfig }>\n): boolean {\n for (const context of contexts) {\n if (isExplicitLangfuseConfig(context.langfuse)) {\n return true;\n }\n }\n return false;\n}\n\nexport function isLangfuseCallbackHandler(value: unknown): boolean {\n return value instanceof CallbackHandler;\n}\n\nexport async function disposeLangfuseHandler(value: unknown): Promise<void> {\n if (value == null) {\n return;\n }\n const provider = getLangfuseTracerProvider() as FlushableTracerProvider;\n await provider.forceFlush?.();\n}\n"],"names":[],"mappings":";;;;AAKA,MAAM,yBAAyB,GAAG,GAAG;AAyB/B,SAAU,4BAA4B,CAC1C,QAA2B,EAAA;IAK3B,QACE,QAAQ,IAAI,IAAI;AAChB,QAAA,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC7B,QAAA,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;AAEjC;AAEA,SAAS,wBAAwB,CAAC,QAA2B,EAAA;AAC3D,IAAA,OAAO,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;AACrC;AAcA,SAAS,mBAAmB,CAC1B,QAAiC,EAAA;IAEjC,MAAM,aAAa,GAA0B,EAAE;AAC/C,IAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AACnD,QAAA,IAAI,KAAK,IAAI,IAAI,EAAE;YACjB;QACF;AACA,QAAA,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;AACrE,QAAA,IACE,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE;AACzB,YAAA,WAAW,CAAC,MAAM,GAAG,yBAAyB,EAC9C;YACA;QACF;AACA,QAAA,aAAa,CAAC,GAAG,CAAC,GAAG,WAAW;IAClC;AACA,IAAA,OAAO,aAAa;AACtB;AAEM,SAAU,2BAA2B,CAAC,EAC1C,SAAS,EACT,eAAe,EACf,OAAO,EACP,SAAS,GAMV,EAAA;AACC,IAAA,OAAO,mBAAmB,CAAC;QACzB,SAAS;QACT,eAAe;QACf,OAAO;QACP,SAAS;AACV,KAAA,CAAC;AACJ;SAEgB,oBAAoB,CAClC,aAAqC,EACrC,WAAmB,iBAAiB,EAAA;AAEpC,IAAA,MAAM,SAAS,GAAG,aAAa,EAAE,SAAS;AAC1C,IAAA,OAAO,SAAS,CAAC,SAAS,CAAC,GAAG,CAAA,EAAG,QAAQ,CAAA,EAAA,EAAK,SAAS,CAAA,CAAE,GAAG,QAAQ;AACtE;SAEgB,oBAAoB,GAAA;IAClC,OAAO,yBAAyB,EAAE;AACpC;SAEgB,yBAAyB,GAAA;IACvC,QACE,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAC1C,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AAE9C;AAEM,SAAU,2BAA2B,CACzC,QAA2B,EAAA;AAE3B,IAAA,IAAI,QAAQ,EAAE,OAAO,KAAK,KAAK,EAAE;AAC/B,QAAA,OAAO,KAAK;IACd;IACA,QACE,oBAAoB,EAAE;QACtB,4BAA4B,CAAC,QAAQ,CAAC;SACrC,wBAAwB,CAAC,QAAQ,CAAC,IAAI,yBAAyB,EAAE,CAAC;AAEvE;AAQM,SAAU,qBAAqB,CAAC,EACpC,QAAQ,EACR,MAAM,EACN,SAAS,EACT,aAAa,EACb,IAAI,GACuB,EAAA;AAC3B,IAAA,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC,EAAE;AAC1C,QAAA,OAAO,SAAS;IAClB;IACA,OAAO,IAAI,eAAe,CAAC;QACzB,MAAM;QACN,SAAS;QACT,aAAa;QACb,IAAI;AACL,KAAA,CAAC;AACJ;AAaM,SAAU,yBAAyB,CAAC,KAAc,EAAA;IACtD,OAAO,KAAK,YAAY,eAAe;AACzC;AAEO,eAAe,sBAAsB,CAAC,KAAc,EAAA;AACzD,IAAA,IAAI,KAAK,IAAI,IAAI,EAAE;QACjB;IACF;AACA,IAAA,MAAM,QAAQ,GAAG,yBAAyB,EAA6B;AACvE,IAAA,MAAM,QAAQ,CAAC,UAAU,IAAI;AAC/B;;;;"}
1
+ {"version":3,"file":"langfuse.mjs","sources":["../../src/langfuse.ts"],"sourcesContent":["import { getLangfuseTracerProvider } from '@langfuse/tracing';\nimport { CallbackHandler } from '@langfuse/langchain';\nimport type * as t from '@/types';\nimport { isPresent } from '@/utils/misc';\n\nconst TRACE_METADATA_MAX_LENGTH = 200;\nconst LANGFUSE_FORCE_FLUSH_ON_DISPOSE = 'LANGFUSE_FORCE_FLUSH_ON_DISPOSE';\n\nexport type LangfuseTraceMetadata = Record<string, string>;\n\ntype LangfuseHandlerParams = {\n userId?: string;\n sessionId?: string;\n traceMetadata?: LangfuseTraceMetadata;\n tags?: string[];\n};\n\ntype AgentLangfuseHandlerParams = LangfuseHandlerParams & {\n langfuse?: t.LangfuseConfig;\n};\n\ntype FlushableTracerProvider = {\n forceFlush?: () => Promise<void> | void;\n};\n\nfunction parseBooleanEnv(value?: string): boolean {\n if (value == null) {\n return false;\n }\n return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());\n}\n\nfunction hasLangfuseTracingConfig(langfuse?: t.LangfuseConfig): boolean {\n return (\n langfuse?.toolNodeTracing != null || langfuse?.toolOutputTracing != null\n );\n}\n\nexport function hasLangfuseConfigCredentials(\n langfuse?: t.LangfuseConfig\n): langfuse is t.LangfuseConfig & {\n publicKey: string;\n secretKey: string;\n} {\n return (\n langfuse != null &&\n isPresent(langfuse.publicKey) &&\n isPresent(langfuse.secretKey)\n );\n}\n\nfunction hasLangfuseConfigBaseUrl(langfuse?: t.LangfuseConfig): boolean {\n return isPresent(langfuse?.baseUrl);\n}\n\nexport function isExplicitLangfuseConfig(langfuse?: t.LangfuseConfig): boolean {\n return (\n langfuse?.enabled != null ||\n isPresent(langfuse?.publicKey) ||\n isPresent(langfuse?.secretKey) ||\n isPresent(langfuse?.baseUrl) ||\n hasLangfuseTracingConfig(langfuse)\n );\n}\n\nfunction createTraceMetadata(\n metadata: Record<string, unknown>\n): LangfuseTraceMetadata {\n const traceMetadata: LangfuseTraceMetadata = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (value == null) {\n continue;\n }\n const stringValue = typeof value === 'string' ? value : String(value);\n if (\n stringValue.trim() === '' ||\n stringValue.length > TRACE_METADATA_MAX_LENGTH\n ) {\n continue;\n }\n traceMetadata[key] = stringValue;\n }\n return traceMetadata;\n}\n\nexport function createLangfuseTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n}: {\n messageId?: unknown;\n parentMessageId?: unknown;\n agentId?: unknown;\n agentName?: unknown;\n}): LangfuseTraceMetadata {\n return createTraceMetadata({\n messageId,\n parentMessageId,\n agentId,\n agentName,\n });\n}\n\nexport function getLangfuseTraceName(\n traceMetadata?: LangfuseTraceMetadata,\n fallback: string = 'LibreChat Agent'\n): string {\n const agentName = traceMetadata?.agentName;\n return isPresent(agentName) ? `${fallback}: ${agentName}` : fallback;\n}\n\nexport function hasLangfuseEnvConfig(): boolean {\n return hasLangfuseEnvCredentials();\n}\n\nexport function hasLangfuseEnvCredentials(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nexport function shouldCreateLangfuseHandler(\n langfuse?: t.LangfuseConfig\n): boolean {\n if (langfuse?.enabled === false) {\n return false;\n }\n return (\n hasLangfuseEnvConfig() ||\n hasLangfuseConfigCredentials(langfuse) ||\n (hasLangfuseConfigBaseUrl(langfuse) && hasLangfuseEnvCredentials())\n );\n}\n\nexport function createLegacyLangfuseHandler(\n params: LangfuseHandlerParams\n): CallbackHandler {\n return new CallbackHandler(params);\n}\n\nexport function createLangfuseHandler({\n langfuse,\n userId,\n sessionId,\n traceMetadata,\n tags,\n}: AgentLangfuseHandlerParams): CallbackHandler | undefined {\n if (!shouldCreateLangfuseHandler(langfuse)) {\n return undefined;\n }\n return new CallbackHandler({\n userId,\n sessionId,\n traceMetadata,\n tags,\n });\n}\n\nexport function hasExplicitLangfuseConfig(\n contexts: Iterable<{ langfuse?: t.LangfuseConfig }>\n): boolean {\n for (const context of contexts) {\n if (isExplicitLangfuseConfig(context.langfuse)) {\n return true;\n }\n }\n return false;\n}\n\nexport function isLangfuseCallbackHandler(value: unknown): boolean {\n return value instanceof CallbackHandler;\n}\n\nexport async function disposeLangfuseHandler(value: unknown): Promise<void> {\n if (\n value == null ||\n !parseBooleanEnv(process.env[LANGFUSE_FORCE_FLUSH_ON_DISPOSE])\n ) {\n return;\n }\n const provider = getLangfuseTracerProvider() as FlushableTracerProvider;\n await provider.forceFlush?.();\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.1.99",
3
+ "version": "3.2.00",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
package/src/langfuse.ts CHANGED
@@ -4,6 +4,7 @@ import type * as t from '@/types';
4
4
  import { isPresent } from '@/utils/misc';
5
5
 
6
6
  const TRACE_METADATA_MAX_LENGTH = 200;
7
+ const LANGFUSE_FORCE_FLUSH_ON_DISPOSE = 'LANGFUSE_FORCE_FLUSH_ON_DISPOSE';
7
8
 
8
9
  export type LangfuseTraceMetadata = Record<string, string>;
9
10
 
@@ -22,6 +23,13 @@ type FlushableTracerProvider = {
22
23
  forceFlush?: () => Promise<void> | void;
23
24
  };
24
25
 
26
+ function parseBooleanEnv(value?: string): boolean {
27
+ if (value == null) {
28
+ return false;
29
+ }
30
+ return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
31
+ }
32
+
25
33
  function hasLangfuseTracingConfig(langfuse?: t.LangfuseConfig): boolean {
26
34
  return (
27
35
  langfuse?.toolNodeTracing != null || langfuse?.toolOutputTracing != null
@@ -45,9 +53,7 @@ function hasLangfuseConfigBaseUrl(langfuse?: t.LangfuseConfig): boolean {
45
53
  return isPresent(langfuse?.baseUrl);
46
54
  }
47
55
 
48
- export function isExplicitLangfuseConfig(
49
- langfuse?: t.LangfuseConfig
50
- ): boolean {
56
+ export function isExplicitLangfuseConfig(langfuse?: t.LangfuseConfig): boolean {
51
57
  return (
52
58
  langfuse?.enabled != null ||
53
59
  isPresent(langfuse?.publicKey) ||
@@ -168,7 +174,10 @@ export function isLangfuseCallbackHandler(value: unknown): boolean {
168
174
  }
169
175
 
170
176
  export async function disposeLangfuseHandler(value: unknown): Promise<void> {
171
- if (value == null) {
177
+ if (
178
+ value == null ||
179
+ !parseBooleanEnv(process.env[LANGFUSE_FORCE_FLUSH_ON_DISPOSE])
180
+ ) {
172
181
  return;
173
182
  }
174
183
  const provider = getLangfuseTracerProvider() as FlushableTracerProvider;
@@ -45,6 +45,7 @@ jest.mock('@opentelemetry/sdk-trace-base', () => ({
45
45
  describe('Langfuse callback composition', () => {
46
46
  beforeEach(() => {
47
47
  jest.clearAllMocks();
48
+ delete process.env.LANGFUSE_FORCE_FLUSH_ON_DISPOSE;
48
49
  });
49
50
 
50
51
  it('runs explicit per-agent tracing when callbacks is a CallbackManager', async () => {
@@ -85,7 +86,7 @@ describe('Langfuse callback composition', () => {
85
86
  await run.processStream({ messages: [new HumanMessage('hello')] }, config);
86
87
 
87
88
  expect(mockStartActiveSpan).toHaveBeenCalled();
88
- expect(mockForceFlush).toHaveBeenCalled();
89
+ expect(mockForceFlush).not.toHaveBeenCalled();
89
90
  });
90
91
 
91
92
  it('attaches Langfuse callbacks for direct graph invocations', async () => {
@@ -1,14 +1,23 @@
1
1
  import { CallbackHandler } from '@langfuse/langchain';
2
2
  import {
3
3
  createLangfuseHandler,
4
+ disposeLangfuseHandler,
4
5
  hasLangfuseConfigCredentials,
5
6
  shouldCreateLangfuseHandler,
6
7
  } from '@/langfuse';
7
8
 
9
+ const mockForceFlush = jest.fn();
10
+
8
11
  jest.mock('@langfuse/langchain', () => ({
9
12
  CallbackHandler: jest.fn().mockImplementation((params) => ({ params })),
10
13
  }));
11
14
 
15
+ jest.mock('@langfuse/tracing', () => ({
16
+ getLangfuseTracerProvider: jest.fn(() => ({
17
+ forceFlush: mockForceFlush,
18
+ })),
19
+ }));
20
+
12
21
  const MockedCallbackHandler = CallbackHandler as jest.MockedClass<
13
22
  typeof CallbackHandler
14
23
  >;
@@ -23,6 +32,7 @@ describe('createLangfuseHandler', () => {
23
32
  delete process.env.LANGFUSE_SECRET_KEY;
24
33
  delete process.env.LANGFUSE_BASE_URL;
25
34
  delete process.env.LANGFUSE_BASEURL;
35
+ delete process.env.LANGFUSE_FORCE_FLUSH_ON_DISPOSE;
26
36
  });
27
37
 
28
38
  afterEach(() => {
@@ -147,4 +157,17 @@ describe('createLangfuseHandler', () => {
147
157
  })
148
158
  ).toBe(true);
149
159
  });
160
+
161
+ it('does not flush the shared Langfuse provider during per-chat cleanup', async () => {
162
+ await expect(disposeLangfuseHandler({})).resolves.toBeUndefined();
163
+ expect(mockForceFlush).not.toHaveBeenCalled();
164
+ });
165
+
166
+ it('force flushes during cleanup when explicitly enabled', async () => {
167
+ process.env.LANGFUSE_FORCE_FLUSH_ON_DISPOSE = 'true';
168
+
169
+ await expect(disposeLangfuseHandler({})).resolves.toBeUndefined();
170
+
171
+ expect(mockForceFlush).toHaveBeenCalledTimes(1);
172
+ });
150
173
  });