@visibe.ai/node 0.1.19 → 0.1.21

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.
@@ -31,6 +31,16 @@ class LangChainCallback {
31
31
  nextSpanId() {
32
32
  return `step_${++this.stepCounter}`;
33
33
  }
34
+ // Walk up the run parent chain to find the nearest agent name.
35
+ // Falls back to this.agentName if no ancestor has an associated agent.
36
+ _findAgentName(runId) {
37
+ if (!runId)
38
+ return this.agentName;
39
+ const name = this.runIdToAgentName.get(runId);
40
+ if (name)
41
+ return name;
42
+ return this._findAgentName(this.runIdToParent.get(runId));
43
+ }
34
44
  constructor(options) {
35
45
  // Required by @langchain/core v1+ for proper callback registration.
36
46
  // Without `name`, ensureHandler() wraps via fromMethods() which drops prototype methods.
@@ -41,10 +51,17 @@ class LangChainCallback {
41
51
  this.raiseError = false;
42
52
  // Maps LangChain runId → our spanId so we can set parent_span_id.
43
53
  this.runIdToSpanId = new Map();
54
+ // Tracks parent run IDs so we can walk up the chain hierarchy.
55
+ this.runIdToParent = new Map();
56
+ // Maps runId → agent name for chains that emitted an agent_start span.
57
+ // Used by _findAgentName() to resolve the correct agent for llm_call / tool_call spans.
58
+ this.runIdToAgentName = new Map();
44
59
  // Pending LLM calls: runId → { startMs, model, inputText }
45
60
  this.pendingLLMCalls = new Map();
46
61
  this.pendingToolCalls = new Map();
47
62
  this.stepCounter = 0;
63
+ // Set to false in subclasses that emit their own agent_start spans (e.g. LangGraphCallback).
64
+ this._emitRootAgentStart = true;
48
65
  // Token / call / cost accumulators — read by patchCompiledStateGraph / patchRunnableSequence.
49
66
  this.totalInputTokens = 0;
50
67
  this.totalOutputTokens = 0;
@@ -101,7 +118,7 @@ class LangChainCallback {
101
118
  const span = this.visibe.buildLLMSpan({
102
119
  spanId,
103
120
  parentSpanId,
104
- agentName: this.agentName,
121
+ agentName: this._findAgentName(parentRunId),
105
122
  model,
106
123
  status: 'success',
107
124
  inputTokens,
@@ -144,7 +161,7 @@ class LangChainCallback {
144
161
  spanId,
145
162
  parentSpanId,
146
163
  toolName: pending?.toolName ?? 'tool',
147
- agentName: this.agentName,
164
+ agentName: this._findAgentName(parentRunId),
148
165
  status: 'success',
149
166
  durationMs: pending ? Date.now() - pending.startMs : 0,
150
167
  inputText: pending?.inputText ?? '',
@@ -173,6 +190,18 @@ class LangChainCallback {
173
190
  if (!this.runIdToSpanId.has(runId)) {
174
191
  this.runIdToSpanId.set(runId, this.nextSpanId());
175
192
  }
193
+ if (_parentRunId) {
194
+ this.runIdToParent.set(runId, _parentRunId);
195
+ }
196
+ // Emit agent_start for the root chain so LLM spans have a real parent and
197
+ // the agents breakdown is populated. Subclasses that manage their own
198
+ // agent_start spans (LangGraphCallback) set _emitRootAgentStart = false.
199
+ if (this._emitRootAgentStart && !_parentRunId) {
200
+ const spanId = this.runIdToSpanId.get(runId);
201
+ const agentName = _name ?? _chain?.name ?? this.agentName;
202
+ this.runIdToAgentName.set(runId, agentName);
203
+ this.visibe.batcher.add(this.traceId, this.visibe.buildAgentStartSpan({ spanId, agentName }));
204
+ }
176
205
  }
177
206
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
207
  async handleChainEnd(_outputs, _runId) { }
@@ -12,6 +12,9 @@ class LangGraphCallback extends langchain_1.LangChainCallback {
12
12
  constructor(options) {
13
13
  super(options);
14
14
  this.nodeNames = new Set(options.nodeNames ?? []);
15
+ // LangGraphCallback emits its own agent_start spans per node — suppress the
16
+ // base-class root-chain emission to avoid a spurious 'LangGraph' agent span.
17
+ this._emitRootAgentStart = false;
15
18
  }
16
19
  // Override handleChainStart to emit agent_start spans for LangGraph nodes.
17
20
  //
@@ -28,6 +31,8 @@ class LangGraphCallback extends langchain_1.LangChainCallback {
28
31
  // Use the spanId already assigned by super for this runId.
29
32
  const spanId = this.runIdToSpanId.get(runId) ?? this.nextSpanId();
30
33
  this.runIdToSpanId.set(runId, spanId);
34
+ // Register agent name so _findAgentName() resolves it for child llm_call / tool_call spans.
35
+ this.runIdToAgentName.set(runId, nodeName);
31
36
  this.visibe.batcher.add(this.traceId, this.visibe.buildAgentStartSpan({
32
37
  spanId,
33
38
  agentName: nodeName,
@@ -27,6 +27,16 @@ export class LangChainCallback {
27
27
  nextSpanId() {
28
28
  return `step_${++this.stepCounter}`;
29
29
  }
30
+ // Walk up the run parent chain to find the nearest agent name.
31
+ // Falls back to this.agentName if no ancestor has an associated agent.
32
+ _findAgentName(runId) {
33
+ if (!runId)
34
+ return this.agentName;
35
+ const name = this.runIdToAgentName.get(runId);
36
+ if (name)
37
+ return name;
38
+ return this._findAgentName(this.runIdToParent.get(runId));
39
+ }
30
40
  constructor(options) {
31
41
  // Required by @langchain/core v1+ for proper callback registration.
32
42
  // Without `name`, ensureHandler() wraps via fromMethods() which drops prototype methods.
@@ -37,10 +47,17 @@ export class LangChainCallback {
37
47
  this.raiseError = false;
38
48
  // Maps LangChain runId → our spanId so we can set parent_span_id.
39
49
  this.runIdToSpanId = new Map();
50
+ // Tracks parent run IDs so we can walk up the chain hierarchy.
51
+ this.runIdToParent = new Map();
52
+ // Maps runId → agent name for chains that emitted an agent_start span.
53
+ // Used by _findAgentName() to resolve the correct agent for llm_call / tool_call spans.
54
+ this.runIdToAgentName = new Map();
40
55
  // Pending LLM calls: runId → { startMs, model, inputText }
41
56
  this.pendingLLMCalls = new Map();
42
57
  this.pendingToolCalls = new Map();
43
58
  this.stepCounter = 0;
59
+ // Set to false in subclasses that emit their own agent_start spans (e.g. LangGraphCallback).
60
+ this._emitRootAgentStart = true;
44
61
  // Token / call / cost accumulators — read by patchCompiledStateGraph / patchRunnableSequence.
45
62
  this.totalInputTokens = 0;
46
63
  this.totalOutputTokens = 0;
@@ -97,7 +114,7 @@ export class LangChainCallback {
97
114
  const span = this.visibe.buildLLMSpan({
98
115
  spanId,
99
116
  parentSpanId,
100
- agentName: this.agentName,
117
+ agentName: this._findAgentName(parentRunId),
101
118
  model,
102
119
  status: 'success',
103
120
  inputTokens,
@@ -140,7 +157,7 @@ export class LangChainCallback {
140
157
  spanId,
141
158
  parentSpanId,
142
159
  toolName: pending?.toolName ?? 'tool',
143
- agentName: this.agentName,
160
+ agentName: this._findAgentName(parentRunId),
144
161
  status: 'success',
145
162
  durationMs: pending ? Date.now() - pending.startMs : 0,
146
163
  inputText: pending?.inputText ?? '',
@@ -169,6 +186,18 @@ export class LangChainCallback {
169
186
  if (!this.runIdToSpanId.has(runId)) {
170
187
  this.runIdToSpanId.set(runId, this.nextSpanId());
171
188
  }
189
+ if (_parentRunId) {
190
+ this.runIdToParent.set(runId, _parentRunId);
191
+ }
192
+ // Emit agent_start for the root chain so LLM spans have a real parent and
193
+ // the agents breakdown is populated. Subclasses that manage their own
194
+ // agent_start spans (LangGraphCallback) set _emitRootAgentStart = false.
195
+ if (this._emitRootAgentStart && !_parentRunId) {
196
+ const spanId = this.runIdToSpanId.get(runId);
197
+ const agentName = _name ?? _chain?.name ?? this.agentName;
198
+ this.runIdToAgentName.set(runId, agentName);
199
+ this.visibe.batcher.add(this.traceId, this.visibe.buildAgentStartSpan({ spanId, agentName }));
200
+ }
172
201
  }
173
202
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
203
  async handleChainEnd(_outputs, _runId) { }
@@ -8,6 +8,9 @@ export class LangGraphCallback extends LangChainCallback {
8
8
  constructor(options) {
9
9
  super(options);
10
10
  this.nodeNames = new Set(options.nodeNames ?? []);
11
+ // LangGraphCallback emits its own agent_start spans per node — suppress the
12
+ // base-class root-chain emission to avoid a spurious 'LangGraph' agent span.
13
+ this._emitRootAgentStart = false;
11
14
  }
12
15
  // Override handleChainStart to emit agent_start spans for LangGraph nodes.
13
16
  //
@@ -24,6 +27,8 @@ export class LangGraphCallback extends LangChainCallback {
24
27
  // Use the spanId already assigned by super for this runId.
25
28
  const spanId = this.runIdToSpanId.get(runId) ?? this.nextSpanId();
26
29
  this.runIdToSpanId.set(runId, spanId);
30
+ // Register agent name so _findAgentName() resolves it for child llm_call / tool_call spans.
31
+ this.runIdToAgentName.set(runId, nodeName);
27
32
  this.visibe.batcher.add(this.traceId, this.visibe.buildAgentStartSpan({
28
33
  spanId,
29
34
  agentName: nodeName,
@@ -10,6 +10,8 @@ export declare class LangChainCallback {
10
10
  protected readonly traceId: string;
11
11
  protected readonly agentName: string;
12
12
  protected runIdToSpanId: Map<string, string>;
13
+ protected runIdToParent: Map<string, string>;
14
+ protected runIdToAgentName: Map<string, string>;
13
15
  protected pendingLLMCalls: Map<string, {
14
16
  startMs: number;
15
17
  model?: string;
@@ -22,6 +24,8 @@ export declare class LangChainCallback {
22
24
  }>;
23
25
  protected stepCounter: number;
24
26
  protected nextSpanId(): string;
27
+ protected _emitRootAgentStart: boolean;
28
+ protected _findAgentName(runId?: string): string;
25
29
  totalInputTokens: number;
26
30
  totalOutputTokens: number;
27
31
  totalCost: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visibe.ai/node",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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",