@librechat/agents 3.1.90 → 3.1.91

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.
Files changed (80) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +9 -5
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +46 -14
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/langfuse.cjs +234 -0
  6. package/dist/cjs/langfuse.cjs.map +1 -0
  7. package/dist/cjs/main.cjs +25 -0
  8. package/dist/cjs/main.cjs.map +1 -1
  9. package/dist/cjs/run.cjs +44 -27
  10. package/dist/cjs/run.cjs.map +1 -1
  11. package/dist/cjs/stream.cjs +10 -3
  12. package/dist/cjs/stream.cjs.map +1 -1
  13. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs +380 -0
  14. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs.map +1 -0
  15. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +997 -0
  16. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -0
  17. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +575 -0
  18. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -0
  19. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs +165 -0
  20. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -0
  21. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +17 -5
  22. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  23. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +110 -6
  24. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  25. package/dist/esm/agents/AgentContext.mjs +9 -5
  26. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  27. package/dist/esm/graphs/Graph.mjs +46 -14
  28. package/dist/esm/graphs/Graph.mjs.map +1 -1
  29. package/dist/esm/langfuse.mjs +226 -0
  30. package/dist/esm/langfuse.mjs.map +1 -0
  31. package/dist/esm/main.mjs +5 -1
  32. package/dist/esm/main.mjs.map +1 -1
  33. package/dist/esm/run.mjs +44 -27
  34. package/dist/esm/run.mjs.map +1 -1
  35. package/dist/esm/stream.mjs +10 -3
  36. package/dist/esm/stream.mjs.map +1 -1
  37. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs +378 -0
  38. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs.map +1 -0
  39. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +994 -0
  40. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -0
  41. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +566 -0
  42. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -0
  43. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs +155 -0
  44. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -0
  45. package/dist/esm/tools/local/LocalExecutionEngine.mjs +17 -6
  46. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  47. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +111 -7
  48. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  49. package/dist/types/agents/AgentContext.d.ts +4 -1
  50. package/dist/types/graphs/Graph.d.ts +6 -5
  51. package/dist/types/index.d.ts +1 -0
  52. package/dist/types/langfuse.d.ts +48 -0
  53. package/dist/types/tools/cloudflare/CloudflareBridgeRuntime.d.ts +23 -0
  54. package/dist/types/tools/cloudflare/CloudflareProgrammaticToolCalling.d.ts +4 -0
  55. package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +21 -0
  56. package/dist/types/tools/cloudflare/CloudflareSandboxTools.d.ts +22 -0
  57. package/dist/types/tools/cloudflare/index.d.ts +4 -0
  58. package/dist/types/tools/local/LocalExecutionEngine.d.ts +1 -0
  59. package/dist/types/types/graph.d.ts +8 -0
  60. package/dist/types/types/tools.d.ts +118 -2
  61. package/package.json +4 -4
  62. package/src/__tests__/stream.eagerEventExecution.test.ts +66 -0
  63. package/src/agents/AgentContext.ts +13 -3
  64. package/src/graphs/Graph.ts +53 -16
  65. package/src/index.ts +1 -0
  66. package/src/langfuse.ts +358 -0
  67. package/src/run.ts +60 -38
  68. package/src/specs/langfuse-config.test.ts +57 -0
  69. package/src/specs/langfuse-metadata.test.ts +19 -1
  70. package/src/stream.ts +13 -3
  71. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +537 -0
  72. package/src/tools/cloudflare/CloudflareBridgeRuntime.ts +480 -0
  73. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +1162 -0
  74. package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +744 -0
  75. package/src/tools/cloudflare/CloudflareSandboxTools.ts +225 -0
  76. package/src/tools/cloudflare/index.ts +4 -0
  77. package/src/tools/local/LocalExecutionEngine.ts +20 -4
  78. package/src/tools/local/resolveLocalExecutionTools.ts +169 -7
  79. package/src/types/graph.ts +9 -0
  80. package/src/types/tools.ts +141 -2
package/src/run.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  // src/run.ts
2
2
  import './instrumentation';
3
- import { CallbackHandler } from '@langfuse/langchain';
4
3
  import { PromptTemplate } from '@langchain/core/prompts';
5
4
  import { RunnableLambda } from '@langchain/core/runnables';
6
5
  import { AzureChatOpenAI, ChatOpenAI } from '@langchain/openai';
@@ -31,7 +30,14 @@ import { initializeModel } from '@/llm/init';
31
30
  import { HandlerRegistry } from '@/events';
32
31
  import { executeHooks } from '@/hooks';
33
32
  import { isOpenAILike } from '@/utils/llm';
34
- import { isPresent } from '@/utils/misc';
33
+ import {
34
+ createLegacyLangfuseHandler,
35
+ createLangfuseHandler,
36
+ disposeLangfuseHandler,
37
+ hasExplicitLangfuseConfig,
38
+ hasLangfuseEnvConfig,
39
+ isLangfuseCallbackHandler,
40
+ } from '@/langfuse';
35
41
  import type { HookRegistry } from '@/hooks';
36
42
 
37
43
  export const defaultOmitOptions = new Set([
@@ -607,9 +613,8 @@ export class Run<_T extends t.BaseGraphState> {
607
613
  .concat(customHandler);
608
614
 
609
615
  if (
610
- isPresent(process.env.LANGFUSE_SECRET_KEY) &&
611
- isPresent(process.env.LANGFUSE_PUBLIC_KEY) &&
612
- isPresent(process.env.LANGFUSE_BASE_URL)
616
+ hasLangfuseEnvConfig() &&
617
+ !hasExplicitLangfuseConfig(this.Graph.agentContexts.values())
613
618
  ) {
614
619
  const userId = config.configurable?.user_id;
615
620
  const sessionId = config.configurable?.thread_id;
@@ -621,7 +626,7 @@ export class Run<_T extends t.BaseGraphState> {
621
626
  parentMessageId: config.configurable?.requestBody?.parentMessageId,
622
627
  agentName: primaryContext?.name,
623
628
  };
624
- const handler = new CallbackHandler({
629
+ const handler = createLegacyLangfuseHandler({
625
630
  userId,
626
631
  sessionId,
627
632
  traceMetadata,
@@ -1134,12 +1139,8 @@ export class Run<_T extends t.BaseGraphState> {
1134
1139
  titleMethod = TitleMethod.COMPLETION,
1135
1140
  titlePromptTemplate,
1136
1141
  }: t.RunTitleOptions): Promise<{ language?: string; title?: string }> {
1137
- if (
1138
- chainOptions != null &&
1139
- isPresent(process.env.LANGFUSE_SECRET_KEY) &&
1140
- isPresent(process.env.LANGFUSE_PUBLIC_KEY) &&
1141
- isPresent(process.env.LANGFUSE_BASE_URL)
1142
- ) {
1142
+ let titleLangfuseHandler: unknown;
1143
+ if (chainOptions != null) {
1143
1144
  const userId = chainOptions.configurable?.user_id;
1144
1145
  const sessionId = chainOptions.configurable?.thread_id;
1145
1146
  const titleContext = this.Graph?.agentContexts.get(
@@ -1149,14 +1150,31 @@ export class Run<_T extends t.BaseGraphState> {
1149
1150
  messageId: 'title-' + this.id,
1150
1151
  agentName: titleContext?.name,
1151
1152
  };
1152
- const handler = new CallbackHandler({
1153
- userId,
1154
- sessionId,
1155
- traceMetadata,
1156
- });
1157
- chainOptions.callbacks = (
1158
- (chainOptions.callbacks as t.ProvidedCallbacks) ?? []
1159
- ).concat([handler]);
1153
+ const hasExplicitLangfuse =
1154
+ this.Graph != null &&
1155
+ hasExplicitLangfuseConfig(this.Graph.agentContexts.values());
1156
+ if (titleContext?.langfuse != null) {
1157
+ titleLangfuseHandler = createLangfuseHandler({
1158
+ langfuse: titleContext.langfuse,
1159
+ userId,
1160
+ sessionId,
1161
+ traceMetadata,
1162
+ });
1163
+ } else if (hasLangfuseEnvConfig() && !hasExplicitLangfuse) {
1164
+ titleLangfuseHandler = createLegacyLangfuseHandler({
1165
+ userId,
1166
+ sessionId,
1167
+ traceMetadata,
1168
+ });
1169
+ }
1170
+
1171
+ if (titleLangfuseHandler != null) {
1172
+ chainOptions.callbacks = (
1173
+ (chainOptions.callbacks as t.ProvidedCallbacks) ?? []
1174
+ ).concat([
1175
+ titleLangfuseHandler as NonNullable<t.ProvidedCallbacks>[number],
1176
+ ]);
1177
+ }
1160
1178
  }
1161
1179
 
1162
1180
  const convoTemplate = PromptTemplate.fromTemplate(
@@ -1221,24 +1239,28 @@ export class Run<_T extends t.BaseGraphState> {
1221
1239
  });
1222
1240
 
1223
1241
  try {
1224
- return await fullChain.invoke(
1225
- { input: inputText, output: response },
1226
- invokeConfig
1227
- );
1228
- } catch (_e) {
1229
- // Fallback: strip callbacks to avoid EventStream tracer errors in certain environments
1230
- // But preserve langfuse handler if it exists
1231
- const langfuseHandler = (
1232
- invokeConfig.callbacks as t.ProvidedCallbacks
1233
- )?.find((cb) => cb instanceof CallbackHandler);
1234
- const { callbacks: _cb, ...rest } = invokeConfig;
1235
- const safeConfig = Object.assign({}, rest, {
1236
- callbacks: langfuseHandler ? [langfuseHandler] : [],
1237
- });
1238
- return await fullChain.invoke(
1239
- { input: inputText, output: response },
1240
- safeConfig as Partial<RunnableConfig>
1241
- );
1242
+ try {
1243
+ return await fullChain.invoke(
1244
+ { input: inputText, output: response },
1245
+ invokeConfig
1246
+ );
1247
+ } catch (_e) {
1248
+ // Fallback: strip callbacks to avoid EventStream tracer errors in certain environments
1249
+ // but preserve Langfuse tracing if it exists.
1250
+ const langfuseHandler = (
1251
+ invokeConfig.callbacks as t.ProvidedCallbacks
1252
+ )?.find(isLangfuseCallbackHandler);
1253
+ const { callbacks: _cb, ...rest } = invokeConfig;
1254
+ const safeConfig = Object.assign({}, rest, {
1255
+ callbacks: langfuseHandler ? [langfuseHandler] : [],
1256
+ });
1257
+ return await fullChain.invoke(
1258
+ { input: inputText, output: response },
1259
+ safeConfig as Partial<RunnableConfig>
1260
+ );
1261
+ }
1262
+ } finally {
1263
+ await disposeLangfuseHandler(titleLangfuseHandler);
1242
1264
  }
1243
1265
  }
1244
1266
  }
@@ -0,0 +1,57 @@
1
+ import { LangfuseSpanProcessor } from '@langfuse/otel';
2
+ import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
3
+ import { createLangfuseHandler } from '@/langfuse';
4
+
5
+ jest.mock('@langfuse/otel', () => ({
6
+ LangfuseSpanProcessor: jest.fn().mockImplementation(() => ({})),
7
+ }));
8
+
9
+ jest.mock('@opentelemetry/sdk-trace-base', () => ({
10
+ BasicTracerProvider: jest.fn().mockImplementation(() => ({
11
+ forceFlush: jest.fn(),
12
+ getTracer: jest.fn(),
13
+ shutdown: jest.fn(),
14
+ })),
15
+ }));
16
+
17
+ describe('createLangfuseHandler', () => {
18
+ beforeEach(() => {
19
+ jest.clearAllMocks();
20
+ });
21
+
22
+ it('creates a handler when keys are provided and baseUrl is omitted', () => {
23
+ const handler = createLangfuseHandler({
24
+ langfuse: {
25
+ enabled: true,
26
+ publicKey: 'pk-test',
27
+ secretKey: 'sk-test',
28
+ },
29
+ });
30
+
31
+ expect(handler).toBeDefined();
32
+ expect(LangfuseSpanProcessor).toHaveBeenCalledWith(
33
+ expect.objectContaining({
34
+ publicKey: 'pk-test',
35
+ secretKey: 'sk-test',
36
+ exportMode: 'immediate',
37
+ })
38
+ );
39
+ expect(
40
+ (LangfuseSpanProcessor as jest.Mock).mock.calls[0][0].baseUrl
41
+ ).toBeUndefined();
42
+ expect(BasicTracerProvider).toHaveBeenCalledTimes(1);
43
+ });
44
+
45
+ it('does not create a handler when a required key is missing', () => {
46
+ const handler = createLangfuseHandler({
47
+ langfuse: {
48
+ enabled: true,
49
+ publicKey: 'pk-test',
50
+ },
51
+ });
52
+
53
+ expect(handler).toBeUndefined();
54
+ expect(LangfuseSpanProcessor).not.toHaveBeenCalled();
55
+ expect(BasicTracerProvider).not.toHaveBeenCalled();
56
+ });
57
+ });
@@ -10,7 +10,10 @@ const MockedCallbackHandler = CallbackHandler as jest.MockedClass<
10
10
  typeof CallbackHandler
11
11
  >;
12
12
 
13
- async function createTestRun(agentName?: string): Promise<Run<never>> {
13
+ async function createTestRun(
14
+ agentName?: string,
15
+ agentOverrides: Record<string, unknown> = {}
16
+ ): Promise<Run<never>> {
14
17
  const run = await Run.create({
15
18
  runId: 'test-run-id',
16
19
  graphConfig: {
@@ -22,6 +25,7 @@ async function createTestRun(agentName?: string): Promise<Run<never>> {
22
25
  provider: Providers.OPENAI,
23
26
  clientOptions: { model: 'gpt-4' },
24
27
  tools: [],
28
+ ...agentOverrides,
25
29
  },
26
30
  ],
27
31
  },
@@ -88,4 +92,18 @@ describe('Langfuse trace metadata includes agentName', () => {
88
92
 
89
93
  expect(MockedCallbackHandler).not.toHaveBeenCalled();
90
94
  });
95
+
96
+ it('does not create the legacy CallbackHandler when explicit agent config is supplied', async () => {
97
+ const run = await createTestRun('DWAINE', {
98
+ langfuse: {
99
+ enabled: false,
100
+ },
101
+ });
102
+ await run.processStream(
103
+ { messages: [] },
104
+ { configurable: { thread_id: 't1', user_id: 'u1' }, version: 'v2' }
105
+ );
106
+
107
+ expect(MockedCallbackHandler).not.toHaveBeenCalled();
108
+ });
91
109
  });
package/src/stream.ts CHANGED
@@ -136,10 +136,19 @@ function isDirectGraphTool(
136
136
  }
137
137
 
138
138
  function isDirectLocalTool(name: string, graph: StandardGraph): boolean {
139
- if (graph.toolExecution?.engine !== 'local') {
139
+ const toolExecution = graph.toolExecution;
140
+ const engine = toolExecution?.engine;
141
+ if (
142
+ toolExecution == null ||
143
+ (engine !== 'local' && engine !== 'cloudflare-sandbox')
144
+ ) {
140
145
  return false;
141
146
  }
142
- if (graph.toolExecution.local?.includeCodingTools === false) {
147
+ const includeCodingTools =
148
+ engine === 'cloudflare-sandbox'
149
+ ? toolExecution.cloudflare?.includeCodingTools
150
+ : toolExecution.local?.includeCodingTools;
151
+ if (includeCodingTools === false) {
143
152
  return CODE_EXECUTION_TOOLS.has(name);
144
153
  }
145
154
  return LOCAL_CODING_BUNDLE_NAME_SET.has(name);
@@ -270,7 +279,8 @@ function hasPotentialDirectToolInStreamContext(args: {
270
279
  agentContext?: AgentContext;
271
280
  }): boolean {
272
281
  const { graph, agentContext } = args;
273
- if (graph.toolExecution?.engine === 'local') {
282
+ const engine = graph.toolExecution?.engine;
283
+ if (engine === 'local' || engine === 'cloudflare-sandbox') {
274
284
  return true;
275
285
  }
276
286
  if ((agentContext?.graphTools?.length ?? 0) > 0) {