@librechat/agents 3.1.90 → 3.1.92

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 (94) 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 +48 -14
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/instrumentation.cjs +2 -7
  6. package/dist/cjs/instrumentation.cjs.map +1 -1
  7. package/dist/cjs/langfuse.cjs +285 -0
  8. package/dist/cjs/langfuse.cjs.map +1 -0
  9. package/dist/cjs/main.cjs +25 -0
  10. package/dist/cjs/main.cjs.map +1 -1
  11. package/dist/cjs/run.cjs +75 -44
  12. package/dist/cjs/run.cjs.map +1 -1
  13. package/dist/cjs/stream.cjs +10 -3
  14. package/dist/cjs/stream.cjs.map +1 -1
  15. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs +380 -0
  16. package/dist/cjs/tools/cloudflare/CloudflareBridgeRuntime.cjs.map +1 -0
  17. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +997 -0
  18. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -0
  19. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +575 -0
  20. package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -0
  21. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs +165 -0
  22. package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -0
  23. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +17 -5
  24. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  25. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +110 -6
  26. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  27. package/dist/cjs/utils/callbacks.cjs +27 -0
  28. package/dist/cjs/utils/callbacks.cjs.map +1 -0
  29. package/dist/esm/agents/AgentContext.mjs +9 -5
  30. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  31. package/dist/esm/graphs/Graph.mjs +48 -14
  32. package/dist/esm/graphs/Graph.mjs.map +1 -1
  33. package/dist/esm/instrumentation.mjs +2 -7
  34. package/dist/esm/instrumentation.mjs.map +1 -1
  35. package/dist/esm/langfuse.mjs +275 -0
  36. package/dist/esm/langfuse.mjs.map +1 -0
  37. package/dist/esm/main.mjs +5 -1
  38. package/dist/esm/main.mjs.map +1 -1
  39. package/dist/esm/run.mjs +75 -44
  40. package/dist/esm/run.mjs.map +1 -1
  41. package/dist/esm/stream.mjs +10 -3
  42. package/dist/esm/stream.mjs.map +1 -1
  43. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs +378 -0
  44. package/dist/esm/tools/cloudflare/CloudflareBridgeRuntime.mjs.map +1 -0
  45. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +994 -0
  46. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -0
  47. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +566 -0
  48. package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -0
  49. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs +155 -0
  50. package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -0
  51. package/dist/esm/tools/local/LocalExecutionEngine.mjs +17 -6
  52. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  53. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +111 -7
  54. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  55. package/dist/esm/utils/callbacks.mjs +24 -0
  56. package/dist/esm/utils/callbacks.mjs.map +1 -0
  57. package/dist/types/agents/AgentContext.d.ts +4 -1
  58. package/dist/types/graphs/Graph.d.ts +6 -5
  59. package/dist/types/index.d.ts +1 -0
  60. package/dist/types/langfuse.d.ts +57 -0
  61. package/dist/types/tools/cloudflare/CloudflareBridgeRuntime.d.ts +23 -0
  62. package/dist/types/tools/cloudflare/CloudflareProgrammaticToolCalling.d.ts +4 -0
  63. package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +21 -0
  64. package/dist/types/tools/cloudflare/CloudflareSandboxTools.d.ts +22 -0
  65. package/dist/types/tools/cloudflare/index.d.ts +4 -0
  66. package/dist/types/tools/local/LocalExecutionEngine.d.ts +1 -0
  67. package/dist/types/types/graph.d.ts +8 -0
  68. package/dist/types/types/run.d.ts +2 -2
  69. package/dist/types/types/tools.d.ts +118 -2
  70. package/dist/types/utils/callbacks.d.ts +5 -0
  71. package/package.json +4 -4
  72. package/src/__tests__/stream.eagerEventExecution.test.ts +66 -0
  73. package/src/agents/AgentContext.ts +13 -3
  74. package/src/graphs/Graph.ts +57 -16
  75. package/src/index.ts +1 -0
  76. package/src/instrumentation.ts +2 -7
  77. package/src/langfuse.ts +441 -0
  78. package/src/run.ts +105 -59
  79. package/src/specs/langfuse-callbacks.test.ts +75 -0
  80. package/src/specs/langfuse-config.test.ts +114 -0
  81. package/src/specs/langfuse-metadata.test.ts +19 -1
  82. package/src/stream.ts +13 -3
  83. package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +537 -0
  84. package/src/tools/cloudflare/CloudflareBridgeRuntime.ts +480 -0
  85. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +1162 -0
  86. package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +744 -0
  87. package/src/tools/cloudflare/CloudflareSandboxTools.ts +225 -0
  88. package/src/tools/cloudflare/index.ts +4 -0
  89. package/src/tools/local/LocalExecutionEngine.ts +20 -4
  90. package/src/tools/local/resolveLocalExecutionTools.ts +169 -7
  91. package/src/types/graph.ts +9 -0
  92. package/src/types/run.ts +2 -7
  93. package/src/types/tools.ts +141 -2
  94. package/src/utils/callbacks.ts +39 -0
@@ -0,0 +1,75 @@
1
+ import { CallbackManager } from '@langchain/core/callbacks/manager';
2
+ import { HumanMessage } from '@langchain/core/messages';
3
+ import { Providers } from '@/common';
4
+ import { Run } from '@/run';
5
+ import type * as t from '@/types';
6
+
7
+ const mockSpan = {
8
+ end: jest.fn(),
9
+ setAttributes: jest.fn(),
10
+ setStatus: jest.fn(),
11
+ };
12
+ const mockStartSpan = jest.fn(() => mockSpan);
13
+ const mockForceFlush = jest.fn();
14
+ const mockShutdown = jest.fn();
15
+
16
+ jest.mock('@langfuse/otel', () => ({
17
+ LangfuseSpanProcessor: jest.fn().mockImplementation(() => ({})),
18
+ isDefaultExportSpan: jest.fn(() => false),
19
+ }));
20
+
21
+ jest.mock('@opentelemetry/sdk-trace-base', () => ({
22
+ BasicTracerProvider: jest.fn().mockImplementation(() => ({
23
+ forceFlush: mockForceFlush,
24
+ getTracer: jest.fn(() => ({
25
+ startSpan: mockStartSpan,
26
+ })),
27
+ shutdown: mockShutdown,
28
+ })),
29
+ }));
30
+
31
+ describe('Langfuse callback composition', () => {
32
+ beforeEach(() => {
33
+ jest.clearAllMocks();
34
+ });
35
+
36
+ it('runs explicit per-agent tracing when callbacks is a CallbackManager', async () => {
37
+ const manager = CallbackManager.fromHandlers({
38
+ handleCustomEvent: async (): Promise<void> => undefined,
39
+ });
40
+ const run = await Run.create<t.IState>({
41
+ runId: 'test-langfuse-callback-manager',
42
+ graphConfig: {
43
+ type: 'standard',
44
+ agents: [
45
+ {
46
+ agentId: 'agent_abc123',
47
+ name: 'DWAINE',
48
+ provider: Providers.OPENAI,
49
+ clientOptions: { model: 'gpt-4' },
50
+ tools: [],
51
+ langfuse: {
52
+ enabled: true,
53
+ publicKey: 'pk-test',
54
+ secretKey: 'sk-test',
55
+ },
56
+ },
57
+ ],
58
+ },
59
+ skipCleanup: true,
60
+ });
61
+
62
+ run.Graph?.overrideTestModel(['hello']);
63
+
64
+ const config = {
65
+ callbacks: manager,
66
+ configurable: { thread_id: 'thread-1', user_id: 'user-1' },
67
+ streamMode: 'values' as const,
68
+ version: 'v2' as const,
69
+ };
70
+
71
+ await run.processStream({ messages: [new HumanMessage('hello')] }, config);
72
+
73
+ expect(mockStartSpan).toHaveBeenCalled();
74
+ });
75
+ });
@@ -0,0 +1,114 @@
1
+ import { LangfuseSpanProcessor } from '@langfuse/otel';
2
+ import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
3
+ import { HumanMessage } from '@langchain/core/messages';
4
+ import type { Serialized } from '@langchain/core/load/serializable';
5
+ import { createLangfuseHandler } from '@/langfuse';
6
+
7
+ const mockSpan = {
8
+ end: jest.fn(),
9
+ setAttributes: jest.fn(),
10
+ setStatus: jest.fn(),
11
+ };
12
+ const mockStartSpan = jest.fn(() => mockSpan);
13
+ const mockGetTracer = jest.fn(() => ({
14
+ startSpan: mockStartSpan,
15
+ }));
16
+
17
+ jest.mock('@langfuse/otel', () => ({
18
+ LangfuseSpanProcessor: jest.fn().mockImplementation(() => ({})),
19
+ isDefaultExportSpan: jest.fn(() => false),
20
+ }));
21
+
22
+ jest.mock('@opentelemetry/sdk-trace-base', () => ({
23
+ BasicTracerProvider: jest.fn().mockImplementation(() => ({
24
+ forceFlush: jest.fn(),
25
+ getTracer: mockGetTracer,
26
+ shutdown: jest.fn(),
27
+ })),
28
+ }));
29
+
30
+ describe('createLangfuseHandler', () => {
31
+ beforeEach(() => {
32
+ jest.clearAllMocks();
33
+ });
34
+
35
+ it('creates a handler when keys are provided and baseUrl is omitted', () => {
36
+ const handler = createLangfuseHandler({
37
+ langfuse: {
38
+ enabled: true,
39
+ publicKey: 'pk-test',
40
+ secretKey: 'sk-test',
41
+ },
42
+ });
43
+
44
+ expect(handler).toBeDefined();
45
+ expect(LangfuseSpanProcessor).toHaveBeenCalledWith(
46
+ expect.objectContaining({
47
+ publicKey: 'pk-test',
48
+ secretKey: 'sk-test',
49
+ exportMode: 'immediate',
50
+ })
51
+ );
52
+ expect(
53
+ (LangfuseSpanProcessor as jest.Mock).mock.calls[0][0].baseUrl
54
+ ).toBeUndefined();
55
+ expect(BasicTracerProvider).toHaveBeenCalledTimes(1);
56
+ });
57
+
58
+ it('starts per-agent spans with v5 trace attributes', async () => {
59
+ const handler = createLangfuseHandler({
60
+ langfuse: {
61
+ enabled: true,
62
+ publicKey: 'pk-test',
63
+ secretKey: 'sk-test',
64
+ },
65
+ userId: 'user-1',
66
+ sessionId: 'thread-1',
67
+ traceMetadata: {
68
+ messageId: 'message-1',
69
+ agentId: 'agent-1',
70
+ agentName: 'DWAINE',
71
+ },
72
+ tags: ['librechat', 'agent'],
73
+ });
74
+
75
+ await handler?.handleChatModelStart(
76
+ {
77
+ id: ['langchain', 'chat_models', 'ChatOpenAI'],
78
+ kwargs: { model: 'gpt-4o' },
79
+ } as unknown as Serialized,
80
+ [[new HumanMessage('hello')]],
81
+ 'run-1'
82
+ );
83
+
84
+ expect(mockGetTracer).toHaveBeenCalledWith('langfuse-sdk');
85
+ expect(mockStartSpan).toHaveBeenCalledWith(
86
+ 'gpt-4o',
87
+ expect.objectContaining({
88
+ attributes: expect.objectContaining({
89
+ 'langfuse.trace.name': 'LibreChat Agent: DWAINE',
90
+ 'langfuse.trace.metadata.agentId': 'agent-1',
91
+ 'langfuse.trace.metadata.messageId': 'message-1',
92
+ 'langfuse.observation.model.name': 'gpt-4o',
93
+ 'langfuse.observation.type': 'generation',
94
+ 'user.id': 'user-1',
95
+ 'session.id': 'thread-1',
96
+ 'langfuse.trace.tags': ['librechat', 'agent'],
97
+ }),
98
+ })
99
+ );
100
+ });
101
+
102
+ it('does not create a handler when a required key is missing', () => {
103
+ const handler = createLangfuseHandler({
104
+ langfuse: {
105
+ enabled: true,
106
+ publicKey: 'pk-test',
107
+ },
108
+ });
109
+
110
+ expect(handler).toBeUndefined();
111
+ expect(LangfuseSpanProcessor).not.toHaveBeenCalled();
112
+ expect(BasicTracerProvider).not.toHaveBeenCalled();
113
+ });
114
+ });
@@ -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) {