@visibe.ai/node 0.1.21 → 0.1.22

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.
package/dist/cjs/index.js CHANGED
@@ -190,12 +190,25 @@ async function patchFramework(framework, client) {
190
190
  }
191
191
  case 'langchain': {
192
192
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
193
- const { patchRunnableSequence } = await Promise.resolve().then(() => __importStar(require('./integrations/langchain')));
193
+ const { patchRunnableSequence, patchAgentExecutor } = await Promise.resolve().then(() => __importStar(require('./integrations/langchain')));
194
194
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
195
195
  const lcModule = await Promise.resolve().then(() => __importStar(require('@langchain/core/runnables')));
196
- const result = patchRunnableSequence(lcModule, client);
197
- if (typeof result === 'function')
198
- _lcRestore = result;
196
+ const r1 = patchRunnableSequence(lcModule, client);
197
+ // Also patch AgentExecutor (langchain/agents) if installed — AgentExecutor is NOT a
198
+ // RunnableSequence so without this patch each agent loop iteration creates a separate trace.
199
+ let r2;
200
+ try {
201
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
+ const agentsModule = await Promise.resolve().then(() => __importStar(require('langchain/agents')));
203
+ r2 = patchAgentExecutor(agentsModule, client);
204
+ }
205
+ catch { /* langchain package not installed — skip */ }
206
+ _lcRestore = () => {
207
+ if (typeof r1 === 'function')
208
+ r1();
209
+ if (typeof r2 === 'function')
210
+ r2();
211
+ };
199
212
  break;
200
213
  }
201
214
  case 'vercel_ai': {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LangChainCallback = exports.LANGGRAPH_INTERNAL_NODES = exports.activeLangChainStorage = void 0;
4
4
  exports.patchRunnableSequence = patchRunnableSequence;
5
+ exports.patchAgentExecutor = patchAgentExecutor;
5
6
  const node_async_hooks_1 = require("node:async_hooks");
6
7
  const node_crypto_1 = require("node:crypto");
7
8
  const utils_1 = require("../utils");
@@ -312,6 +313,109 @@ function patchRunnableSequence(lcModule, visibe) {
312
313
  };
313
314
  }
314
315
  // ---------------------------------------------------------------------------
316
+ // patchAgentExecutor — patches AgentExecutor from `langchain/agents` so the
317
+ // entire multi-step agent loop is captured as ONE trace.
318
+ //
319
+ // AgentExecutor is NOT a RunnableSequence — it extends Runnable directly and
320
+ // calls the inner agent RunnableSequence in a loop. Without this patch each
321
+ // iteration fires patchRunnableSequence and creates a separate trace.
322
+ // ---------------------------------------------------------------------------
323
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
324
+ function patchAgentExecutor(agentsModule, visibe) {
325
+ const AgentExecutor = agentsModule?.AgentExecutor;
326
+ if (!AgentExecutor)
327
+ return () => { };
328
+ const originalInvoke = AgentExecutor.prototype.invoke;
329
+ const originalStream = AgentExecutor.prototype.stream;
330
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
331
+ AgentExecutor.prototype.invoke = async function (input, config) {
332
+ if (exports.activeLangChainStorage.getStore() !== undefined) {
333
+ return originalInvoke.call(this, input, config);
334
+ }
335
+ const traceId = (0, node_crypto_1.randomUUID)();
336
+ const startedAt = new Date().toISOString();
337
+ const startMs = Date.now();
338
+ const agentName = this.name ?? 'langchain';
339
+ await visibe.apiClient.createTrace({
340
+ trace_id: traceId,
341
+ name: agentName,
342
+ framework: 'langchain',
343
+ started_at: startedAt,
344
+ ...(visibe.sessionId ? { session_id: visibe.sessionId } : {}),
345
+ });
346
+ const cb = new LangChainCallback({ visibe, traceId, agentName });
347
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
348
+ let result;
349
+ let status = 'completed';
350
+ try {
351
+ result = await exports.activeLangChainStorage.run(cb, () => originalInvoke.call(this, input, _mergeCallbacks(config, cb)));
352
+ }
353
+ catch (err) {
354
+ status = 'failed';
355
+ throw err;
356
+ }
357
+ finally {
358
+ visibe.batcher.flush();
359
+ await visibe.apiClient.completeTrace(traceId, {
360
+ status,
361
+ ended_at: new Date().toISOString(),
362
+ duration_ms: Date.now() - startMs,
363
+ llm_call_count: cb.llmCallCount,
364
+ total_cost: cb.totalCost,
365
+ total_tokens: cb.totalInputTokens + cb.totalOutputTokens,
366
+ total_input_tokens: cb.totalInputTokens,
367
+ total_output_tokens: cb.totalOutputTokens,
368
+ });
369
+ }
370
+ return result;
371
+ };
372
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
373
+ AgentExecutor.prototype.stream = async function* (input, config) {
374
+ if (exports.activeLangChainStorage.getStore() !== undefined) {
375
+ yield* (await originalStream.call(this, input, config));
376
+ return;
377
+ }
378
+ const traceId = (0, node_crypto_1.randomUUID)();
379
+ const startedAt = new Date().toISOString();
380
+ const startMs = Date.now();
381
+ const agentName = this.name ?? 'langchain';
382
+ await visibe.apiClient.createTrace({
383
+ trace_id: traceId,
384
+ name: agentName,
385
+ framework: 'langchain',
386
+ started_at: startedAt,
387
+ ...(visibe.sessionId ? { session_id: visibe.sessionId } : {}),
388
+ });
389
+ const cb = new LangChainCallback({ visibe, traceId, agentName });
390
+ let status = 'completed';
391
+ try {
392
+ const gen = await exports.activeLangChainStorage.run(cb, () => originalStream.call(this, input, _mergeCallbacks(config, cb)));
393
+ yield* gen;
394
+ }
395
+ catch (err) {
396
+ status = 'failed';
397
+ throw err;
398
+ }
399
+ finally {
400
+ visibe.batcher.flush();
401
+ await visibe.apiClient.completeTrace(traceId, {
402
+ status,
403
+ ended_at: new Date().toISOString(),
404
+ duration_ms: Date.now() - startMs,
405
+ llm_call_count: cb.llmCallCount,
406
+ total_cost: cb.totalCost,
407
+ total_tokens: cb.totalInputTokens + cb.totalOutputTokens,
408
+ total_input_tokens: cb.totalInputTokens,
409
+ total_output_tokens: cb.totalOutputTokens,
410
+ });
411
+ }
412
+ };
413
+ return () => {
414
+ AgentExecutor.prototype.invoke = originalInvoke;
415
+ AgentExecutor.prototype.stream = originalStream;
416
+ };
417
+ }
418
+ // ---------------------------------------------------------------------------
315
419
  // Private helpers
316
420
  // ---------------------------------------------------------------------------
317
421
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
package/dist/esm/index.js CHANGED
@@ -151,12 +151,25 @@ async function patchFramework(framework, client) {
151
151
  }
152
152
  case 'langchain': {
153
153
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
- const { patchRunnableSequence } = await import('./integrations/langchain.js');
154
+ const { patchRunnableSequence, patchAgentExecutor } = await import('./integrations/langchain.js');
155
155
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
156
  const lcModule = await import('@langchain/core/runnables');
157
- const result = patchRunnableSequence(lcModule, client);
158
- if (typeof result === 'function')
159
- _lcRestore = result;
157
+ const r1 = patchRunnableSequence(lcModule, client);
158
+ // Also patch AgentExecutor (langchain/agents) if installed — AgentExecutor is NOT a
159
+ // RunnableSequence so without this patch each agent loop iteration creates a separate trace.
160
+ let r2;
161
+ try {
162
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
+ const agentsModule = await import('langchain/agents');
164
+ r2 = patchAgentExecutor(agentsModule, client);
165
+ }
166
+ catch { /* langchain package not installed — skip */ }
167
+ _lcRestore = () => {
168
+ if (typeof r1 === 'function')
169
+ r1();
170
+ if (typeof r2 === 'function')
171
+ r2();
172
+ };
160
173
  break;
161
174
  }
162
175
  case 'vercel_ai': {
@@ -307,6 +307,109 @@ export function patchRunnableSequence(lcModule, visibe) {
307
307
  };
308
308
  }
309
309
  // ---------------------------------------------------------------------------
310
+ // patchAgentExecutor — patches AgentExecutor from `langchain/agents` so the
311
+ // entire multi-step agent loop is captured as ONE trace.
312
+ //
313
+ // AgentExecutor is NOT a RunnableSequence — it extends Runnable directly and
314
+ // calls the inner agent RunnableSequence in a loop. Without this patch each
315
+ // iteration fires patchRunnableSequence and creates a separate trace.
316
+ // ---------------------------------------------------------------------------
317
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
318
+ export function patchAgentExecutor(agentsModule, visibe) {
319
+ const AgentExecutor = agentsModule?.AgentExecutor;
320
+ if (!AgentExecutor)
321
+ return () => { };
322
+ const originalInvoke = AgentExecutor.prototype.invoke;
323
+ const originalStream = AgentExecutor.prototype.stream;
324
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
325
+ AgentExecutor.prototype.invoke = async function (input, config) {
326
+ if (activeLangChainStorage.getStore() !== undefined) {
327
+ return originalInvoke.call(this, input, config);
328
+ }
329
+ const traceId = randomUUID();
330
+ const startedAt = new Date().toISOString();
331
+ const startMs = Date.now();
332
+ const agentName = this.name ?? 'langchain';
333
+ await visibe.apiClient.createTrace({
334
+ trace_id: traceId,
335
+ name: agentName,
336
+ framework: 'langchain',
337
+ started_at: startedAt,
338
+ ...(visibe.sessionId ? { session_id: visibe.sessionId } : {}),
339
+ });
340
+ const cb = new LangChainCallback({ visibe, traceId, agentName });
341
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
342
+ let result;
343
+ let status = 'completed';
344
+ try {
345
+ result = await activeLangChainStorage.run(cb, () => originalInvoke.call(this, input, _mergeCallbacks(config, cb)));
346
+ }
347
+ catch (err) {
348
+ status = 'failed';
349
+ throw err;
350
+ }
351
+ finally {
352
+ visibe.batcher.flush();
353
+ await visibe.apiClient.completeTrace(traceId, {
354
+ status,
355
+ ended_at: new Date().toISOString(),
356
+ duration_ms: Date.now() - startMs,
357
+ llm_call_count: cb.llmCallCount,
358
+ total_cost: cb.totalCost,
359
+ total_tokens: cb.totalInputTokens + cb.totalOutputTokens,
360
+ total_input_tokens: cb.totalInputTokens,
361
+ total_output_tokens: cb.totalOutputTokens,
362
+ });
363
+ }
364
+ return result;
365
+ };
366
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
367
+ AgentExecutor.prototype.stream = async function* (input, config) {
368
+ if (activeLangChainStorage.getStore() !== undefined) {
369
+ yield* (await originalStream.call(this, input, config));
370
+ return;
371
+ }
372
+ const traceId = randomUUID();
373
+ const startedAt = new Date().toISOString();
374
+ const startMs = Date.now();
375
+ const agentName = this.name ?? 'langchain';
376
+ await visibe.apiClient.createTrace({
377
+ trace_id: traceId,
378
+ name: agentName,
379
+ framework: 'langchain',
380
+ started_at: startedAt,
381
+ ...(visibe.sessionId ? { session_id: visibe.sessionId } : {}),
382
+ });
383
+ const cb = new LangChainCallback({ visibe, traceId, agentName });
384
+ let status = 'completed';
385
+ try {
386
+ const gen = await activeLangChainStorage.run(cb, () => originalStream.call(this, input, _mergeCallbacks(config, cb)));
387
+ yield* gen;
388
+ }
389
+ catch (err) {
390
+ status = 'failed';
391
+ throw err;
392
+ }
393
+ finally {
394
+ visibe.batcher.flush();
395
+ await visibe.apiClient.completeTrace(traceId, {
396
+ status,
397
+ ended_at: new Date().toISOString(),
398
+ duration_ms: Date.now() - startMs,
399
+ llm_call_count: cb.llmCallCount,
400
+ total_cost: cb.totalCost,
401
+ total_tokens: cb.totalInputTokens + cb.totalOutputTokens,
402
+ total_input_tokens: cb.totalInputTokens,
403
+ total_output_tokens: cb.totalOutputTokens,
404
+ });
405
+ }
406
+ };
407
+ return () => {
408
+ AgentExecutor.prototype.invoke = originalInvoke;
409
+ AgentExecutor.prototype.stream = originalStream;
410
+ };
411
+ }
412
+ // ---------------------------------------------------------------------------
310
413
  // Private helpers
311
414
  // ---------------------------------------------------------------------------
312
415
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -49,3 +49,4 @@ export declare class LangChainCallback {
49
49
  _onToolSpan?: () => void;
50
50
  }
51
51
  export declare function patchRunnableSequence(lcModule: any, visibe: Visibe): () => void;
52
+ export declare function patchAgentExecutor(agentsModule: any, visibe: Visibe): () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visibe.ai/node",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "AI Agent Observability — Track OpenAI, LangChain, LangGraph, Bedrock, Vercel AI, Anthropic",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",