@cloudbase/agent-adapter-langgraph 0.0.11 → 0.0.13

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/index.mjs CHANGED
@@ -58,6 +58,7 @@ function convertJsonSchemaToZodSchema(jsonSchema, required) {
58
58
  }
59
59
 
60
60
  // src/agent.ts
61
+ import { noopLogger } from "@cloudbase/agent-shared";
61
62
  var ClientPropertiesAnnotation = Annotation.Root({
62
63
  tools: Annotation
63
64
  });
@@ -69,6 +70,8 @@ var LanggraphAgent = class extends AbstractAgent {
69
70
  constructor(agentConfig) {
70
71
  super(agentConfig);
71
72
  this.compiledWorkflow = agentConfig.compiledWorkflow;
73
+ const baseLogger = agentConfig.logger ?? noopLogger;
74
+ this.logger = baseLogger.child?.({ component: "langgraph-agent" }) ?? baseLogger;
72
75
  }
73
76
  run(input) {
74
77
  return new Observable((subscriber) => {
@@ -77,12 +80,29 @@ var LanggraphAgent = class extends AbstractAgent {
77
80
  }
78
81
  async _run(subscriber, input) {
79
82
  const { messages, runId, threadId } = input;
80
- subscriber.next({
83
+ const logger = this.logger.child?.({ runId, threadId }) ?? this.logger;
84
+ logger.info?.("Run started");
85
+ const runStartedEvent = {
81
86
  type: EventType.RUN_STARTED,
82
87
  threadId,
83
88
  runId
84
- });
85
- const streamEventInput = input.forwardedProps?.resume ? new Command({
89
+ };
90
+ logger.trace?.({ aguiEvent: runStartedEvent }, "Emitting AGUI event");
91
+ subscriber.next(runStartedEvent);
92
+ const isResume = !!input.forwardedProps?.resume;
93
+ const lastUserMessage = messages.filter((m) => m.role === "user").pop();
94
+ logger.debug?.(
95
+ {
96
+ isResume,
97
+ messageCount: messages.length,
98
+ toolCount: input.tools?.length ?? 0,
99
+ tools: input.tools?.map((t) => t.name),
100
+ lastUserMessage: typeof lastUserMessage?.content === "string" ? lastUserMessage.content.slice(0, 200) : void 0
101
+ },
102
+ "Preparing stream input"
103
+ );
104
+ logger.trace?.({ messages, tools: input.tools }, "Full input messages");
105
+ const streamEventInput = isResume ? new Command({
86
106
  resume: JSON.stringify(input.forwardedProps?.resume?.payload)
87
107
  }) : {
88
108
  messages: aguiMessagesToLangChain(messages),
@@ -102,6 +122,7 @@ var LanggraphAgent = class extends AbstractAgent {
102
122
  thread_id: threadId
103
123
  }
104
124
  });
125
+ logger.debug?.("Stream created, starting event processing");
105
126
  const chatModelRuns = [];
106
127
  const handledToolCallIds = /* @__PURE__ */ new Set();
107
128
  for (const msg of messages) {
@@ -109,202 +130,375 @@ var LanggraphAgent = class extends AbstractAgent {
109
130
  handledToolCallIds.add(msg.toolCallId);
110
131
  }
111
132
  }
133
+ if (handledToolCallIds.size > 0) {
134
+ logger.debug?.(
135
+ { count: handledToolCallIds.size },
136
+ "Pre-populated handled tool call IDs from input messages"
137
+ );
138
+ }
112
139
  let interrupt;
113
140
  let currentToolCall = null;
114
- for await (const event of stream) {
115
- if (event.event.startsWith("ChannelWrite<")) {
116
- continue;
117
- }
118
- if (event.event === "on_chat_model_start") {
119
- chatModelRuns.push({ runId: event.run_id });
120
- continue;
121
- }
122
- if (event.event === "on_chat_model_stream") {
123
- let chatModelRun = chatModelRuns.find(
124
- (run) => run.runId === event.run_id
141
+ let eventCount = 0;
142
+ let toolCallCount = 0;
143
+ let textChunkCount = 0;
144
+ try {
145
+ for await (const event of stream) {
146
+ eventCount++;
147
+ logger.trace?.(
148
+ { eventType: event.event, eventCount, langGraphEvent: event },
149
+ "Processing stream event"
125
150
  );
126
- if (!chatModelRun) {
127
- subscriber.next({
128
- type: EventType.RUN_ERROR,
129
- message: `Received a message from an unknown chat model run. Run Id: ${event.run_id}`
130
- });
151
+ if (event.event.startsWith("ChannelWrite<")) {
131
152
  continue;
132
153
  }
133
- const chunkId = event.data.chunk.id;
134
- if (!chatModelRun.messageId) {
135
- chatModelRun.messageId = chunkId;
136
- subscriber.next({
137
- messageId: chunkId,
138
- type: EventType.TEXT_MESSAGE_START,
139
- role: "assistant"
140
- });
141
- } else if (chatModelRun.messageId !== chunkId) {
142
- subscriber.next({
143
- type: EventType.RUN_ERROR,
144
- message: `Received a message of unknown message id from current run. Run Id: ${event.run_id} Message Id from current run: ${chatModelRun.messageId} Message Id from received message: ${chunkId}`
145
- });
154
+ if (event.event === "on_chat_model_start") {
155
+ logger.debug?.(
156
+ { chatModelRunId: event.run_id },
157
+ "Chat model started"
158
+ );
159
+ chatModelRuns.push({ runId: event.run_id });
146
160
  continue;
147
161
  }
148
- if (Array.isArray(event.data.chunk?.tool_call_chunks) && event.data.chunk?.tool_call_chunks?.length > 0) {
149
- const parentMessageId = chatModelRun.messageId;
150
- event.data.chunk.tool_call_chunks.map((x) => ({
151
- ...x,
152
- args: typeof x.args === "string" ? x.args : x.args ? JSON.stringify(x.args) : ""
153
- })).forEach((toolCall) => {
154
- if (currentToolCall) {
155
- if (toolCall.id && currentToolCall.id !== toolCall.id) {
156
- subscriber.next({
157
- toolCallId: currentToolCall.id,
158
- type: EventType.TOOL_CALL_END
159
- });
160
- if (toolCall.name && toolCall.id) {
161
- currentToolCall = toolCall;
162
- subscriber.next({
162
+ if (event.event === "on_chat_model_stream") {
163
+ const chatModelRun = chatModelRuns.find(
164
+ (run) => run.runId === event.run_id
165
+ );
166
+ if (!chatModelRun) {
167
+ logger.warn?.(
168
+ { chatModelRunId: event.run_id },
169
+ "Received message from unknown chat model run"
170
+ );
171
+ subscriber.next({
172
+ type: EventType.RUN_ERROR,
173
+ message: `Received a message from an unknown chat model run. Run Id: ${event.run_id}`
174
+ });
175
+ continue;
176
+ }
177
+ const chunkId = event.data.chunk.id;
178
+ if (!chatModelRun.messageId) {
179
+ chatModelRun.messageId = chunkId;
180
+ const textStartEvent = {
181
+ messageId: chunkId,
182
+ type: EventType.TEXT_MESSAGE_START,
183
+ role: "assistant"
184
+ };
185
+ logger.debug?.({ messageId: chunkId }, "Text message started");
186
+ logger.trace?.(
187
+ { aguiEvent: textStartEvent },
188
+ "Emitting AGUI event"
189
+ );
190
+ subscriber.next(textStartEvent);
191
+ } else if (chatModelRun.messageId !== chunkId) {
192
+ logger.warn?.(
193
+ {
194
+ expectedMessageId: chatModelRun.messageId,
195
+ receivedMessageId: chunkId,
196
+ chatModelRunId: event.run_id
197
+ },
198
+ "Received message with unexpected ID"
199
+ );
200
+ subscriber.next({
201
+ type: EventType.RUN_ERROR,
202
+ message: `Received a message of unknown message id from current run. Run Id: ${event.run_id} Message Id from current run: ${chatModelRun.messageId} Message Id from received message: ${chunkId}`
203
+ });
204
+ continue;
205
+ }
206
+ if (Array.isArray(event.data.chunk?.tool_call_chunks) && event.data.chunk?.tool_call_chunks?.length > 0) {
207
+ const parentMessageId = chatModelRun.messageId;
208
+ event.data.chunk.tool_call_chunks.map((x) => ({
209
+ ...x,
210
+ args: typeof x.args === "string" ? x.args : x.args ? JSON.stringify(x.args) : ""
211
+ })).forEach((toolCall) => {
212
+ if (currentToolCall) {
213
+ if (toolCall.id && currentToolCall.id !== toolCall.id) {
214
+ const toolEndEvent = {
163
215
  toolCallId: currentToolCall.id,
164
- toolCallName: currentToolCall.name,
165
- parentMessageId,
166
- type: EventType.TOOL_CALL_START
167
- });
168
- if (currentToolCall.args) {
169
- subscriber.next({
216
+ type: EventType.TOOL_CALL_END
217
+ };
218
+ logger.debug?.(
219
+ {
220
+ toolCallId: currentToolCall.id,
221
+ toolCallName: currentToolCall.name
222
+ },
223
+ "Tool call ended"
224
+ );
225
+ logger.trace?.(
226
+ { aguiEvent: toolEndEvent },
227
+ "Emitting AGUI event"
228
+ );
229
+ subscriber.next(toolEndEvent);
230
+ if (toolCall.name && toolCall.id) {
231
+ currentToolCall = toolCall;
232
+ toolCallCount++;
233
+ const toolStartEvent = {
170
234
  toolCallId: currentToolCall.id,
171
- delta: currentToolCall.args,
235
+ toolCallName: currentToolCall.name,
236
+ parentMessageId,
237
+ type: EventType.TOOL_CALL_START
238
+ };
239
+ logger.debug?.(
240
+ {
241
+ toolCallId: toolCall.id,
242
+ toolCallName: toolCall.name
243
+ },
244
+ "Tool call started"
245
+ );
246
+ logger.trace?.(
247
+ { aguiEvent: toolStartEvent },
248
+ "Emitting AGUI event"
249
+ );
250
+ subscriber.next(toolStartEvent);
251
+ if (currentToolCall.args) {
252
+ const toolArgsEvent = {
253
+ toolCallId: currentToolCall.id,
254
+ delta: currentToolCall.args,
255
+ type: EventType.TOOL_CALL_ARGS
256
+ };
257
+ logger.trace?.(
258
+ { aguiEvent: toolArgsEvent },
259
+ "Emitting AGUI event"
260
+ );
261
+ subscriber.next(toolArgsEvent);
262
+ if (isValidJson(currentToolCall.args)) {
263
+ const toolEndEvent2 = {
264
+ toolCallId: currentToolCall.id,
265
+ type: EventType.TOOL_CALL_END
266
+ };
267
+ logger.debug?.(
268
+ { toolCallId: currentToolCall.id },
269
+ "Tool call ended (args complete)"
270
+ );
271
+ logger.trace?.(
272
+ { aguiEvent: toolEndEvent2 },
273
+ "Emitting AGUI event"
274
+ );
275
+ subscriber.next(toolEndEvent2);
276
+ currentToolCall = null;
277
+ }
278
+ }
279
+ }
280
+ } else {
281
+ if (toolCall.args) {
282
+ currentToolCall.args += toolCall.args;
283
+ const toolArgsEvent = {
284
+ toolCallId: currentToolCall.id,
285
+ delta: toolCall.args,
172
286
  type: EventType.TOOL_CALL_ARGS
173
- });
287
+ };
288
+ logger.trace?.(
289
+ { aguiEvent: toolArgsEvent },
290
+ "Emitting AGUI event"
291
+ );
292
+ subscriber.next(toolArgsEvent);
174
293
  if (isValidJson(currentToolCall.args)) {
175
- subscriber.next({
294
+ const toolEndEvent = {
176
295
  toolCallId: currentToolCall.id,
177
296
  type: EventType.TOOL_CALL_END
178
- });
297
+ };
298
+ logger.debug?.(
299
+ { toolCallId: currentToolCall.id },
300
+ "Tool call ended (args complete)"
301
+ );
302
+ logger.trace?.(
303
+ { aguiEvent: toolEndEvent },
304
+ "Emitting AGUI event"
305
+ );
306
+ subscriber.next(toolEndEvent);
179
307
  currentToolCall = null;
180
308
  }
181
309
  }
182
310
  }
183
311
  } else {
184
- if (toolCall.args) {
185
- currentToolCall.args += toolCall.args;
186
- subscriber.next({
187
- toolCallId: currentToolCall.id,
188
- delta: toolCall.args,
189
- type: EventType.TOOL_CALL_ARGS
190
- });
191
- if (isValidJson(currentToolCall.args)) {
192
- subscriber.next({
193
- toolCallId: currentToolCall.id,
194
- type: EventType.TOOL_CALL_END
195
- });
196
- currentToolCall = null;
197
- }
198
- }
199
- }
200
- } else {
201
- if (toolCall.name && toolCall.id) {
202
- currentToolCall = toolCall;
203
- subscriber.next({
204
- toolCallId: toolCall.id,
205
- toolCallName: toolCall.name,
206
- parentMessageId,
207
- type: EventType.TOOL_CALL_START
208
- });
209
- if (toolCall.args) {
210
- subscriber.next({
312
+ if (toolCall.name && toolCall.id) {
313
+ currentToolCall = toolCall;
314
+ toolCallCount++;
315
+ const toolStartEvent = {
211
316
  toolCallId: toolCall.id,
212
- delta: toolCall.args,
213
- type: EventType.TOOL_CALL_ARGS
214
- });
215
- if (isValidJson(toolCall.args)) {
216
- subscriber.next({
317
+ toolCallName: toolCall.name,
318
+ parentMessageId,
319
+ type: EventType.TOOL_CALL_START
320
+ };
321
+ logger.debug?.(
322
+ { toolCallId: toolCall.id, toolCallName: toolCall.name },
323
+ "Tool call started"
324
+ );
325
+ logger.trace?.(
326
+ { aguiEvent: toolStartEvent },
327
+ "Emitting AGUI event"
328
+ );
329
+ subscriber.next(toolStartEvent);
330
+ if (toolCall.args) {
331
+ const toolArgsEvent = {
217
332
  toolCallId: toolCall.id,
218
- type: EventType.TOOL_CALL_END
219
- });
220
- currentToolCall = null;
333
+ delta: toolCall.args,
334
+ type: EventType.TOOL_CALL_ARGS
335
+ };
336
+ logger.trace?.(
337
+ { aguiEvent: toolArgsEvent },
338
+ "Emitting AGUI event"
339
+ );
340
+ subscriber.next(toolArgsEvent);
341
+ if (isValidJson(toolCall.args)) {
342
+ const toolEndEvent = {
343
+ toolCallId: toolCall.id,
344
+ type: EventType.TOOL_CALL_END
345
+ };
346
+ logger.debug?.(
347
+ { toolCallId: toolCall.id },
348
+ "Tool call ended (args complete)"
349
+ );
350
+ logger.trace?.(
351
+ { aguiEvent: toolEndEvent },
352
+ "Emitting AGUI event"
353
+ );
354
+ subscriber.next(toolEndEvent);
355
+ currentToolCall = null;
356
+ }
221
357
  }
222
358
  }
223
359
  }
224
- }
225
- });
226
- }
227
- const delta = event.data.chunk.content;
228
- if (typeof delta === "string" && delta) {
229
- subscriber.next({
230
- messageId: chatModelRun.messageId,
231
- type: EventType.TEXT_MESSAGE_CONTENT,
232
- delta
233
- });
360
+ });
361
+ }
362
+ const delta = event.data.chunk.content;
363
+ if (typeof delta === "string" && delta) {
364
+ textChunkCount++;
365
+ const textContentEvent = {
366
+ messageId: chatModelRun.messageId,
367
+ type: EventType.TEXT_MESSAGE_CONTENT,
368
+ delta
369
+ };
370
+ logger.trace?.(
371
+ { aguiEvent: textContentEvent },
372
+ "Emitting AGUI event"
373
+ );
374
+ subscriber.next(textContentEvent);
375
+ }
376
+ continue;
234
377
  }
235
- continue;
236
- }
237
- if (event.event === "on_chat_model_end") {
238
- const chatModelRun = chatModelRuns.find(
239
- (run) => run.runId === event.run_id
240
- );
241
- if (!chatModelRun) {
242
- subscriber.next({
243
- type: EventType.RUN_ERROR,
244
- message: `Received a on_chat_model_end event from an unknown chat model run. Run Id: ${event.run_id}`
245
- });
378
+ if (event.event === "on_chat_model_end") {
379
+ const chatModelRun = chatModelRuns.find(
380
+ (run) => run.runId === event.run_id
381
+ );
382
+ if (!chatModelRun) {
383
+ logger.warn?.(
384
+ { chatModelRunId: event.run_id },
385
+ "Received on_chat_model_end from unknown run"
386
+ );
387
+ subscriber.next({
388
+ type: EventType.RUN_ERROR,
389
+ message: `Received a on_chat_model_end event from an unknown chat model run. Run Id: ${event.run_id}`
390
+ });
391
+ continue;
392
+ }
393
+ const textEndEvent = {
394
+ type: EventType.TEXT_MESSAGE_END,
395
+ messageId: chatModelRun.messageId
396
+ };
397
+ logger.debug?.(
398
+ { messageId: chatModelRun.messageId },
399
+ "Text message ended"
400
+ );
401
+ logger.trace?.({ aguiEvent: textEndEvent }, "Emitting AGUI event");
402
+ subscriber.next(textEndEvent);
246
403
  continue;
247
404
  }
248
- subscriber.next({
249
- type: EventType.TEXT_MESSAGE_END,
250
- messageId: chatModelRun.messageId
251
- });
252
- continue;
253
- }
254
- if (event.event === "on_tool_end") {
255
- const toolMessage = event.data.output;
256
- if (toolMessage && toolMessage.tool_call_id) {
257
- if (!handledToolCallIds.has(toolMessage.tool_call_id)) {
258
- if (!toolMessage.id) {
259
- toolMessage.id = crypto.randomUUID();
260
- if (toolMessage.lc_kwargs) {
261
- toolMessage.lc_kwargs.id = toolMessage.id;
405
+ if (event.event === "on_tool_end") {
406
+ const toolMessage = event.data.output;
407
+ if (toolMessage && toolMessage.tool_call_id) {
408
+ if (!handledToolCallIds.has(toolMessage.tool_call_id)) {
409
+ if (!toolMessage.id) {
410
+ toolMessage.id = crypto.randomUUID();
411
+ if (toolMessage.lc_kwargs) {
412
+ toolMessage.lc_kwargs.id = toolMessage.id;
413
+ }
262
414
  }
415
+ const toolResultEvent = {
416
+ toolCallId: toolMessage.tool_call_id,
417
+ type: EventType.TOOL_CALL_RESULT,
418
+ content: typeof toolMessage.content === "string" ? toolMessage.content : JSON.stringify(toolMessage.content),
419
+ messageId: toolMessage.id
420
+ };
421
+ logger.debug?.(
422
+ {
423
+ toolCallId: toolMessage.tool_call_id,
424
+ messageId: toolMessage.id
425
+ },
426
+ "Tool call result received"
427
+ );
428
+ logger.trace?.(
429
+ { aguiEvent: toolResultEvent },
430
+ "Emitting AGUI event"
431
+ );
432
+ subscriber.next(toolResultEvent);
433
+ handledToolCallIds.add(toolMessage.tool_call_id);
434
+ } else {
435
+ logger.trace?.(
436
+ { toolCallId: toolMessage.tool_call_id },
437
+ "Skipping duplicate tool call result"
438
+ );
263
439
  }
264
- subscriber.next({
265
- toolCallId: toolMessage.tool_call_id,
266
- type: EventType.TOOL_CALL_RESULT,
267
- content: typeof toolMessage.content === "string" ? toolMessage.content : JSON.stringify(toolMessage.content),
268
- messageId: toolMessage.id
269
- });
270
- handledToolCallIds.add(toolMessage.tool_call_id);
271
440
  }
441
+ continue;
442
+ }
443
+ if (event.event === "on_chain_stream" && event.data.chunk?.__interrupt__ && Array.isArray(event.data.chunk.__interrupt__) && event.data.chunk.__interrupt__.length > 0) {
444
+ const rawInterrupt = event.data.chunk.__interrupt__[0];
445
+ logger.debug?.(
446
+ { interruptId: rawInterrupt.id },
447
+ "Interrupt received"
448
+ );
449
+ interrupt = {
450
+ id: rawInterrupt.id,
451
+ // TODO: replace with actual reason
452
+ reason: "agent requested interrupt",
453
+ payload: rawInterrupt.value
454
+ };
272
455
  }
273
- continue;
274
456
  }
275
- if (event.event === "on_chain_stream" && event.data.chunk?.__interrupt__ && Array.isArray(event.data.chunk.__interrupt__) && event.data.chunk.__interrupt__.length > 0) {
276
- const rawInterrupt = event.data.chunk.__interrupt__[0];
277
- interrupt = {
278
- id: rawInterrupt.id,
279
- // TODO: replace with actual reason
280
- reason: "agent requested interrupt",
281
- payload: rawInterrupt.value
457
+ const stats = { eventCount, toolCallCount, textChunkCount };
458
+ if (interrupt) {
459
+ const runFinishedEvent = {
460
+ type: EventType.RUN_FINISHED,
461
+ threadId,
462
+ runId,
463
+ outcome: "interrupt",
464
+ interrupt
282
465
  };
466
+ logger.info?.(
467
+ { outcome: "interrupt", interruptId: interrupt.id, ...stats },
468
+ "Run finished with interrupt"
469
+ );
470
+ logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
471
+ subscriber.next(runFinishedEvent);
472
+ } else {
473
+ const runFinishedEvent = {
474
+ type: EventType.RUN_FINISHED,
475
+ threadId,
476
+ runId
477
+ };
478
+ logger.info?.({ outcome: "complete", ...stats }, "Run finished");
479
+ logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
480
+ subscriber.next(runFinishedEvent);
283
481
  }
284
- }
285
- if (interrupt) {
286
- subscriber.next({
287
- type: EventType.RUN_FINISHED,
288
- threadId,
289
- runId,
290
- outcome: "interrupt",
291
- interrupt
292
- });
293
- } else {
482
+ } catch (error) {
483
+ logger.error?.(
484
+ { err: error, eventCount, toolCallCount, textChunkCount },
485
+ "Error during stream processing"
486
+ );
294
487
  subscriber.next({
295
- type: EventType.RUN_FINISHED,
296
- threadId,
297
- runId
488
+ type: EventType.RUN_ERROR,
489
+ message: error instanceof Error ? error.message : String(error)
298
490
  });
299
491
  }
300
492
  subscriber.complete();
301
493
  }
302
494
  clone() {
303
495
  const workflow = this.compiledWorkflow;
496
+ const logger = this.logger;
304
497
  this.compiledWorkflow = void 0;
305
498
  const cloned = super.clone();
306
499
  this.compiledWorkflow = workflow;
307
500
  cloned.compiledWorkflow = workflow;
501
+ cloned.logger = logger;
308
502
  return cloned;
309
503
  }
310
504
  };
@@ -356,8 +550,7 @@ function aguiMessagesToLangChain(messages) {
356
550
  id: message.id
357
551
  };
358
552
  default:
359
- console.error(`Message role ${message.role} is not implemented`);
360
- throw new Error("message role is not supported.");
553
+ throw new Error(`Message role ${message.role} is not supported.`);
361
554
  }
362
555
  });
363
556
  }
@@ -1052,11 +1245,16 @@ var TDAIStore = class extends BaseStore {
1052
1245
  }
1053
1246
  }
1054
1247
  };
1248
+
1249
+ // src/index.ts
1250
+ import { noopLogger as noopLogger2, createConsoleLogger } from "@cloudbase/agent-shared";
1055
1251
  export {
1056
1252
  ClientPropertiesAnnotation,
1057
1253
  ClientStateAnnotation,
1058
1254
  LanggraphAgent,
1059
1255
  TDAISaver,
1060
- TDAIStore
1256
+ TDAIStore,
1257
+ createConsoleLogger,
1258
+ noopLogger2 as noopLogger
1061
1259
  };
1062
1260
  //# sourceMappingURL=index.mjs.map