@langchain/core 0.1.27-rc.1 → 0.1.27-rc.2

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.
@@ -12,6 +12,7 @@ const stream_js_1 = require("../utils/stream.cjs");
12
12
  const config_js_1 = require("./config.cjs");
13
13
  const async_caller_js_1 = require("../utils/async_caller.cjs");
14
14
  const root_listener_js_1 = require("../tracers/root_listener.cjs");
15
+ const utils_js_1 = require("./utils.cjs");
15
16
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
17
  function _coerceToDict(value, defaultKey) {
17
18
  return value &&
@@ -353,21 +354,28 @@ class Runnable extends serializable_js_1.Serializable {
353
354
  * @param streamOptions
354
355
  */
355
356
  async *streamLog(input, options, streamOptions) {
356
- const stream = new log_stream_js_1.LogStreamCallbackHandler({
357
+ const logStreamCallbackHandler = new log_stream_js_1.LogStreamCallbackHandler({
357
358
  ...streamOptions,
358
359
  autoClose: false,
360
+ _schemaFormat: "original",
359
361
  });
360
362
  const config = (0, config_js_1.ensureConfig)(options);
363
+ yield* this._streamLog(input, logStreamCallbackHandler, config);
364
+ }
365
+ async *_streamLog(input, logStreamCallbackHandler, config) {
361
366
  const { callbacks } = config;
362
367
  if (callbacks === undefined) {
363
- config.callbacks = [stream];
368
+ // eslint-disable-next-line no-param-reassign
369
+ config.callbacks = [logStreamCallbackHandler];
364
370
  }
365
371
  else if (Array.isArray(callbacks)) {
366
- config.callbacks = callbacks.concat([stream]);
372
+ // eslint-disable-next-line no-param-reassign
373
+ config.callbacks = callbacks.concat([logStreamCallbackHandler]);
367
374
  }
368
375
  else {
369
376
  const copiedCallbacks = callbacks.copy();
370
- copiedCallbacks.inheritableHandlers.push(stream);
377
+ copiedCallbacks.inheritableHandlers.push(logStreamCallbackHandler);
378
+ // eslint-disable-next-line no-param-reassign
371
379
  config.callbacks = copiedCallbacks;
372
380
  }
373
381
  const runnableStreamPromise = this.stream(input, config);
@@ -384,16 +392,16 @@ class Runnable extends serializable_js_1.Serializable {
384
392
  },
385
393
  ],
386
394
  });
387
- await stream.writer.write(patch);
395
+ await logStreamCallbackHandler.writer.write(patch);
388
396
  }
389
397
  }
390
398
  finally {
391
- await stream.writer.close();
399
+ await logStreamCallbackHandler.writer.close();
392
400
  }
393
401
  }
394
402
  const runnableStreamConsumePromise = consumeRunnableStream();
395
403
  try {
396
- for await (const log of stream) {
404
+ for await (const log of logStreamCallbackHandler) {
397
405
  yield log;
398
406
  }
399
407
  }
@@ -401,6 +409,187 @@ class Runnable extends serializable_js_1.Serializable {
401
409
  await runnableStreamConsumePromise;
402
410
  }
403
411
  }
412
+ /**
413
+ * Generate a stream of events emitted by the internal steps of the runnable.
414
+ *
415
+ * Use to create an iterator over StreamEvents that provide real-time information
416
+ * about the progress of the runnable, including StreamEvents from intermediate
417
+ * results.
418
+ *
419
+ * A StreamEvent is a dictionary with the following schema:
420
+ *
421
+ * - `event`: string - Event names are of the format: on_[runnable_type]_(start|stream|end).
422
+ * - `name`: string - The name of the runnable that generated the event.
423
+ * - `run_id`: string - Randomly generated ID associated with the given execution of
424
+ * the runnable that emitted the event. A child runnable that gets invoked as part of the execution of a
425
+ * parent runnable is assigned its own unique ID.
426
+ * - `tags`: string[] - The tags of the runnable that generated the event.
427
+ * - `metadata`: Record<string, any> - The metadata of the runnable that generated the event.
428
+ * - `data`: Record<string, any>
429
+ *
430
+ * Below is a table that illustrates some events that might be emitted by various
431
+ * chains. Metadata fields have been omitted from the table for brevity.
432
+ * Chain definitions have been included after the table.
433
+ *
434
+ * | event | name | chunk | input | output |
435
+ * |----------------------|------------------|------------------------------------|-----------------------------------------------|-------------------------------------------------|
436
+ * | on_llm_start | [model name] | | {'input': 'hello'} | |
437
+ * | on_llm_stream | [model name] | 'Hello' OR AIMessageChunk("hello") | | |
438
+ * | on_llm_end | [model name] | | 'Hello human!' |
439
+ * | on_chain_start | format_docs | | | |
440
+ * | on_chain_stream | format_docs | "hello world!, goodbye world!" | | |
441
+ * | on_chain_end | format_docs | | [Document(...)] | "hello world!, goodbye world!" |
442
+ * | on_tool_start | some_tool | | {"x": 1, "y": "2"} | |
443
+ * | on_tool_stream | some_tool | {"x": 1, "y": "2"} | | |
444
+ * | on_tool_end | some_tool | | | {"x": 1, "y": "2"} |
445
+ * | on_retriever_start | [retriever name] | | {"query": "hello"} | |
446
+ * | on_retriever_chunk | [retriever name] | {documents: [...]} | | |
447
+ * | on_retriever_end | [retriever name] | | {"query": "hello"} | {documents: [...]} |
448
+ * | on_prompt_start | [template_name] | | {"question": "hello"} | |
449
+ * | on_prompt_end | [template_name] | | {"question": "hello"} | ChatPromptValue(messages: [SystemMessage, ...]) |
450
+ */
451
+ async *streamEvents(input, options, streamOptions) {
452
+ if (options.version !== "v1") {
453
+ throw new Error(`Only version "v1" of the events schema is currently supported.`);
454
+ }
455
+ let runLog;
456
+ let hasEncounteredStartEvent = false;
457
+ const config = (0, config_js_1.ensureConfig)(options);
458
+ const rootTags = config.tags ?? [];
459
+ const rootMetadata = config.metadata ?? {};
460
+ const rootName = config.runName ?? this.getName();
461
+ const logStreamCallbackHandler = new log_stream_js_1.LogStreamCallbackHandler({
462
+ ...streamOptions,
463
+ autoClose: false,
464
+ _schemaFormat: "streaming_events",
465
+ });
466
+ const rootEventFilter = new utils_js_1._RootEventFilter({
467
+ ...streamOptions,
468
+ });
469
+ const logStream = this._streamLog(input, logStreamCallbackHandler, config);
470
+ for await (const log of logStream) {
471
+ if (!runLog) {
472
+ runLog = log_stream_js_1.RunLog.fromRunLogPatch(log);
473
+ }
474
+ else {
475
+ runLog = runLog.concat(log);
476
+ }
477
+ if (runLog.state === undefined) {
478
+ throw new Error(`Internal error: "streamEvents" state is missing. Please open a bug report.`);
479
+ }
480
+ // Yield the start event for the root runnable if it hasn't been seen.
481
+ // The root run is never filtered out
482
+ if (!hasEncounteredStartEvent) {
483
+ hasEncounteredStartEvent = true;
484
+ const state = { ...runLog.state };
485
+ const event = {
486
+ run_id: state.id,
487
+ event: `on_${state.type}_start`,
488
+ name: rootName,
489
+ tags: rootTags,
490
+ metadata: rootMetadata,
491
+ data: {
492
+ input,
493
+ },
494
+ };
495
+ if (rootEventFilter.includeEvent(event, state.type)) {
496
+ yield event;
497
+ }
498
+ }
499
+ const paths = log.ops
500
+ .filter((op) => op.path.startsWith("/logs/"))
501
+ .map((op) => op.path.split("/")[2]);
502
+ const dedupedPaths = [...new Set(paths)];
503
+ for (const path of dedupedPaths) {
504
+ let eventType;
505
+ let data = {};
506
+ const logEntry = runLog.state.logs[path];
507
+ if (logEntry.end_time === undefined) {
508
+ if (logEntry.streamed_output.length > 0) {
509
+ eventType = "stream";
510
+ }
511
+ else {
512
+ eventType = "start";
513
+ }
514
+ }
515
+ else {
516
+ eventType = "end";
517
+ }
518
+ if (eventType === "start") {
519
+ // Include the inputs with the start event if they are available.
520
+ // Usually they will NOT be available for components that operate
521
+ // on streams, since those components stream the input and
522
+ // don't know its final value until the end of the stream.
523
+ if (logEntry.inputs !== undefined) {
524
+ data.input = logEntry.inputs;
525
+ }
526
+ }
527
+ else if (eventType === "end") {
528
+ if (logEntry.inputs !== undefined) {
529
+ data.input = logEntry.inputs;
530
+ }
531
+ data.output = logEntry.final_output;
532
+ }
533
+ else if (eventType === "stream") {
534
+ const chunkCount = logEntry.streamed_output.length;
535
+ if (chunkCount !== 1) {
536
+ throw new Error(`Expected exactly one chunk of streamed output, got ${chunkCount} instead. Encountered in: "${logEntry.name}"`);
537
+ }
538
+ data = { chunk: logEntry.streamed_output[0] };
539
+ // Clean up the stream, we don't need it anymore.
540
+ // And this avoids duplicates as well!
541
+ logEntry.streamed_output = [];
542
+ }
543
+ yield {
544
+ event: `on_${logEntry.type}_${eventType}`,
545
+ name: logEntry.name,
546
+ run_id: logEntry.id,
547
+ tags: logEntry.tags,
548
+ metadata: logEntry.metadata,
549
+ data,
550
+ };
551
+ }
552
+ // Finally, we take care of the streaming output from the root chain
553
+ // if there is any.
554
+ const { state } = runLog;
555
+ if (state.streamed_output.length > 0) {
556
+ const chunkCount = state.streamed_output.length;
557
+ if (chunkCount !== 1) {
558
+ throw new Error(`Expected exactly one chunk of streamed output, got ${chunkCount} instead. Encountered in: "${state.name}"`);
559
+ }
560
+ const data = { chunk: state.streamed_output[0] };
561
+ // Clean up the stream, we don't need it anymore.
562
+ state.streamed_output = [];
563
+ const event = {
564
+ event: `on_${state.type}_stream`,
565
+ run_id: state.id,
566
+ tags: rootTags,
567
+ metadata: rootMetadata,
568
+ name: rootName,
569
+ data,
570
+ };
571
+ if (rootEventFilter.includeEvent(event, state.type)) {
572
+ yield event;
573
+ }
574
+ }
575
+ }
576
+ const state = runLog?.state;
577
+ if (state !== undefined) {
578
+ // Finally, yield the end event for the root runnable.
579
+ const event = {
580
+ event: `on_${state.type}_end`,
581
+ name: rootName,
582
+ run_id: state.id,
583
+ tags: rootTags,
584
+ metadata: rootMetadata,
585
+ data: {
586
+ output: state.final_output,
587
+ },
588
+ };
589
+ if (rootEventFilter.includeEvent(event, state.type))
590
+ yield event;
591
+ }
592
+ }
404
593
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
405
594
  static isRunnable(thing) {
406
595
  return thing ? thing.lc_runnable : false;
@@ -540,6 +729,12 @@ class RunnableBinding extends Runnable {
540
729
  generator, options) {
541
730
  yield* this.bound.transform(generator, await this._mergeConfig(options, this.kwargs));
542
731
  }
732
+ async *streamEvents(input, options, streamOptions) {
733
+ yield* this.bound.streamEvents(input, {
734
+ ...(await this._mergeConfig(options, this.kwargs)),
735
+ version: options.version,
736
+ }, streamOptions);
737
+ }
543
738
  static isRunnableBinding(
544
739
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
545
740
  thing
@@ -1,5 +1,5 @@
1
1
  import { CallbackManagerForChainRun } from "../callbacks/manager.js";
2
- import { LogStreamCallbackHandlerInput, RunLogPatch } from "../tracers/log_stream.js";
2
+ import { LogStreamCallbackHandler, LogStreamCallbackHandlerInput, RunLogPatch, StreamEvent } from "../tracers/log_stream.js";
3
3
  import { Serializable } from "../load/serializable.js";
4
4
  import { IterableReadableStream, type IterableReadableStreamInterface } from "../utils/stream.js";
5
5
  import { RunnableConfig } from "./config.js";
@@ -176,6 +176,49 @@ export declare abstract class Runnable<RunInput = any, RunOutput = any, CallOpti
176
176
  * @param streamOptions
177
177
  */
178
178
  streamLog(input: RunInput, options?: Partial<CallOptions>, streamOptions?: Omit<LogStreamCallbackHandlerInput, "autoClose">): AsyncGenerator<RunLogPatch>;
179
+ protected _streamLog(input: RunInput, logStreamCallbackHandler: LogStreamCallbackHandler, config: Partial<CallOptions>): AsyncGenerator<RunLogPatch>;
180
+ /**
181
+ * Generate a stream of events emitted by the internal steps of the runnable.
182
+ *
183
+ * Use to create an iterator over StreamEvents that provide real-time information
184
+ * about the progress of the runnable, including StreamEvents from intermediate
185
+ * results.
186
+ *
187
+ * A StreamEvent is a dictionary with the following schema:
188
+ *
189
+ * - `event`: string - Event names are of the format: on_[runnable_type]_(start|stream|end).
190
+ * - `name`: string - The name of the runnable that generated the event.
191
+ * - `run_id`: string - Randomly generated ID associated with the given execution of
192
+ * the runnable that emitted the event. A child runnable that gets invoked as part of the execution of a
193
+ * parent runnable is assigned its own unique ID.
194
+ * - `tags`: string[] - The tags of the runnable that generated the event.
195
+ * - `metadata`: Record<string, any> - The metadata of the runnable that generated the event.
196
+ * - `data`: Record<string, any>
197
+ *
198
+ * Below is a table that illustrates some events that might be emitted by various
199
+ * chains. Metadata fields have been omitted from the table for brevity.
200
+ * Chain definitions have been included after the table.
201
+ *
202
+ * | event | name | chunk | input | output |
203
+ * |----------------------|------------------|------------------------------------|-----------------------------------------------|-------------------------------------------------|
204
+ * | on_llm_start | [model name] | | {'input': 'hello'} | |
205
+ * | on_llm_stream | [model name] | 'Hello' OR AIMessageChunk("hello") | | |
206
+ * | on_llm_end | [model name] | | 'Hello human!' |
207
+ * | on_chain_start | format_docs | | | |
208
+ * | on_chain_stream | format_docs | "hello world!, goodbye world!" | | |
209
+ * | on_chain_end | format_docs | | [Document(...)] | "hello world!, goodbye world!" |
210
+ * | on_tool_start | some_tool | | {"x": 1, "y": "2"} | |
211
+ * | on_tool_stream | some_tool | {"x": 1, "y": "2"} | | |
212
+ * | on_tool_end | some_tool | | | {"x": 1, "y": "2"} |
213
+ * | on_retriever_start | [retriever name] | | {"query": "hello"} | |
214
+ * | on_retriever_chunk | [retriever name] | {documents: [...]} | | |
215
+ * | on_retriever_end | [retriever name] | | {"query": "hello"} | {documents: [...]} |
216
+ * | on_prompt_start | [template_name] | | {"question": "hello"} | |
217
+ * | on_prompt_end | [template_name] | | {"question": "hello"} | ChatPromptValue(messages: [SystemMessage, ...]) |
218
+ */
219
+ streamEvents(input: RunInput, options: Partial<CallOptions> & {
220
+ version: "v1";
221
+ }, streamOptions?: Omit<LogStreamCallbackHandlerInput, "autoClose">): AsyncGenerator<StreamEvent>;
179
222
  static isRunnable(thing: any): thing is Runnable;
180
223
  /**
181
224
  * Bind lifecycle listeners to a Runnable, returning a new Runnable.
@@ -231,6 +274,9 @@ export declare class RunnableBinding<RunInput, RunOutput, CallOptions extends Ru
231
274
  _streamIterator(input: RunInput, options?: Partial<CallOptions> | undefined): AsyncGenerator<Awaited<RunOutput>, void, unknown>;
232
275
  stream(input: RunInput, options?: Partial<CallOptions> | undefined): Promise<IterableReadableStream<RunOutput>>;
233
276
  transform(generator: AsyncGenerator<RunInput>, options: Partial<CallOptions>): AsyncGenerator<RunOutput>;
277
+ streamEvents(input: RunInput, options: Partial<CallOptions> & {
278
+ version: "v1";
279
+ }, streamOptions?: Omit<LogStreamCallbackHandlerInput, "autoClose">): AsyncGenerator<StreamEvent>;
234
280
  static isRunnableBinding(thing: any): thing is RunnableBinding<any, any, any>;
235
281
  /**
236
282
  * Bind lifecycle listeners to a Runnable, returning a new Runnable.
@@ -1,11 +1,12 @@
1
1
  import pRetry from "p-retry";
2
2
  import { CallbackManager, } from "../callbacks/manager.js";
3
- import { LogStreamCallbackHandler, RunLogPatch, } from "../tracers/log_stream.js";
3
+ import { LogStreamCallbackHandler, RunLog, RunLogPatch, } from "../tracers/log_stream.js";
4
4
  import { Serializable } from "../load/serializable.js";
5
5
  import { IterableReadableStream, concat, atee, pipeGeneratorWithSetup, AsyncGeneratorWithSetup, } from "../utils/stream.js";
6
6
  import { DEFAULT_RECURSION_LIMIT, ensureConfig, getCallbackManagerForConfig, mergeConfigs, patchConfig, } from "./config.js";
7
7
  import { AsyncCaller } from "../utils/async_caller.js";
8
8
  import { RootListenersTracer } from "../tracers/root_listener.js";
9
+ import { _RootEventFilter } from "./utils.js";
9
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
11
  export function _coerceToDict(value, defaultKey) {
11
12
  return value &&
@@ -346,21 +347,28 @@ export class Runnable extends Serializable {
346
347
  * @param streamOptions
347
348
  */
348
349
  async *streamLog(input, options, streamOptions) {
349
- const stream = new LogStreamCallbackHandler({
350
+ const logStreamCallbackHandler = new LogStreamCallbackHandler({
350
351
  ...streamOptions,
351
352
  autoClose: false,
353
+ _schemaFormat: "original",
352
354
  });
353
355
  const config = ensureConfig(options);
356
+ yield* this._streamLog(input, logStreamCallbackHandler, config);
357
+ }
358
+ async *_streamLog(input, logStreamCallbackHandler, config) {
354
359
  const { callbacks } = config;
355
360
  if (callbacks === undefined) {
356
- config.callbacks = [stream];
361
+ // eslint-disable-next-line no-param-reassign
362
+ config.callbacks = [logStreamCallbackHandler];
357
363
  }
358
364
  else if (Array.isArray(callbacks)) {
359
- config.callbacks = callbacks.concat([stream]);
365
+ // eslint-disable-next-line no-param-reassign
366
+ config.callbacks = callbacks.concat([logStreamCallbackHandler]);
360
367
  }
361
368
  else {
362
369
  const copiedCallbacks = callbacks.copy();
363
- copiedCallbacks.inheritableHandlers.push(stream);
370
+ copiedCallbacks.inheritableHandlers.push(logStreamCallbackHandler);
371
+ // eslint-disable-next-line no-param-reassign
364
372
  config.callbacks = copiedCallbacks;
365
373
  }
366
374
  const runnableStreamPromise = this.stream(input, config);
@@ -377,16 +385,16 @@ export class Runnable extends Serializable {
377
385
  },
378
386
  ],
379
387
  });
380
- await stream.writer.write(patch);
388
+ await logStreamCallbackHandler.writer.write(patch);
381
389
  }
382
390
  }
383
391
  finally {
384
- await stream.writer.close();
392
+ await logStreamCallbackHandler.writer.close();
385
393
  }
386
394
  }
387
395
  const runnableStreamConsumePromise = consumeRunnableStream();
388
396
  try {
389
- for await (const log of stream) {
397
+ for await (const log of logStreamCallbackHandler) {
390
398
  yield log;
391
399
  }
392
400
  }
@@ -394,6 +402,187 @@ export class Runnable extends Serializable {
394
402
  await runnableStreamConsumePromise;
395
403
  }
396
404
  }
405
+ /**
406
+ * Generate a stream of events emitted by the internal steps of the runnable.
407
+ *
408
+ * Use to create an iterator over StreamEvents that provide real-time information
409
+ * about the progress of the runnable, including StreamEvents from intermediate
410
+ * results.
411
+ *
412
+ * A StreamEvent is a dictionary with the following schema:
413
+ *
414
+ * - `event`: string - Event names are of the format: on_[runnable_type]_(start|stream|end).
415
+ * - `name`: string - The name of the runnable that generated the event.
416
+ * - `run_id`: string - Randomly generated ID associated with the given execution of
417
+ * the runnable that emitted the event. A child runnable that gets invoked as part of the execution of a
418
+ * parent runnable is assigned its own unique ID.
419
+ * - `tags`: string[] - The tags of the runnable that generated the event.
420
+ * - `metadata`: Record<string, any> - The metadata of the runnable that generated the event.
421
+ * - `data`: Record<string, any>
422
+ *
423
+ * Below is a table that illustrates some events that might be emitted by various
424
+ * chains. Metadata fields have been omitted from the table for brevity.
425
+ * Chain definitions have been included after the table.
426
+ *
427
+ * | event | name | chunk | input | output |
428
+ * |----------------------|------------------|------------------------------------|-----------------------------------------------|-------------------------------------------------|
429
+ * | on_llm_start | [model name] | | {'input': 'hello'} | |
430
+ * | on_llm_stream | [model name] | 'Hello' OR AIMessageChunk("hello") | | |
431
+ * | on_llm_end | [model name] | | 'Hello human!' |
432
+ * | on_chain_start | format_docs | | | |
433
+ * | on_chain_stream | format_docs | "hello world!, goodbye world!" | | |
434
+ * | on_chain_end | format_docs | | [Document(...)] | "hello world!, goodbye world!" |
435
+ * | on_tool_start | some_tool | | {"x": 1, "y": "2"} | |
436
+ * | on_tool_stream | some_tool | {"x": 1, "y": "2"} | | |
437
+ * | on_tool_end | some_tool | | | {"x": 1, "y": "2"} |
438
+ * | on_retriever_start | [retriever name] | | {"query": "hello"} | |
439
+ * | on_retriever_chunk | [retriever name] | {documents: [...]} | | |
440
+ * | on_retriever_end | [retriever name] | | {"query": "hello"} | {documents: [...]} |
441
+ * | on_prompt_start | [template_name] | | {"question": "hello"} | |
442
+ * | on_prompt_end | [template_name] | | {"question": "hello"} | ChatPromptValue(messages: [SystemMessage, ...]) |
443
+ */
444
+ async *streamEvents(input, options, streamOptions) {
445
+ if (options.version !== "v1") {
446
+ throw new Error(`Only version "v1" of the events schema is currently supported.`);
447
+ }
448
+ let runLog;
449
+ let hasEncounteredStartEvent = false;
450
+ const config = ensureConfig(options);
451
+ const rootTags = config.tags ?? [];
452
+ const rootMetadata = config.metadata ?? {};
453
+ const rootName = config.runName ?? this.getName();
454
+ const logStreamCallbackHandler = new LogStreamCallbackHandler({
455
+ ...streamOptions,
456
+ autoClose: false,
457
+ _schemaFormat: "streaming_events",
458
+ });
459
+ const rootEventFilter = new _RootEventFilter({
460
+ ...streamOptions,
461
+ });
462
+ const logStream = this._streamLog(input, logStreamCallbackHandler, config);
463
+ for await (const log of logStream) {
464
+ if (!runLog) {
465
+ runLog = RunLog.fromRunLogPatch(log);
466
+ }
467
+ else {
468
+ runLog = runLog.concat(log);
469
+ }
470
+ if (runLog.state === undefined) {
471
+ throw new Error(`Internal error: "streamEvents" state is missing. Please open a bug report.`);
472
+ }
473
+ // Yield the start event for the root runnable if it hasn't been seen.
474
+ // The root run is never filtered out
475
+ if (!hasEncounteredStartEvent) {
476
+ hasEncounteredStartEvent = true;
477
+ const state = { ...runLog.state };
478
+ const event = {
479
+ run_id: state.id,
480
+ event: `on_${state.type}_start`,
481
+ name: rootName,
482
+ tags: rootTags,
483
+ metadata: rootMetadata,
484
+ data: {
485
+ input,
486
+ },
487
+ };
488
+ if (rootEventFilter.includeEvent(event, state.type)) {
489
+ yield event;
490
+ }
491
+ }
492
+ const paths = log.ops
493
+ .filter((op) => op.path.startsWith("/logs/"))
494
+ .map((op) => op.path.split("/")[2]);
495
+ const dedupedPaths = [...new Set(paths)];
496
+ for (const path of dedupedPaths) {
497
+ let eventType;
498
+ let data = {};
499
+ const logEntry = runLog.state.logs[path];
500
+ if (logEntry.end_time === undefined) {
501
+ if (logEntry.streamed_output.length > 0) {
502
+ eventType = "stream";
503
+ }
504
+ else {
505
+ eventType = "start";
506
+ }
507
+ }
508
+ else {
509
+ eventType = "end";
510
+ }
511
+ if (eventType === "start") {
512
+ // Include the inputs with the start event if they are available.
513
+ // Usually they will NOT be available for components that operate
514
+ // on streams, since those components stream the input and
515
+ // don't know its final value until the end of the stream.
516
+ if (logEntry.inputs !== undefined) {
517
+ data.input = logEntry.inputs;
518
+ }
519
+ }
520
+ else if (eventType === "end") {
521
+ if (logEntry.inputs !== undefined) {
522
+ data.input = logEntry.inputs;
523
+ }
524
+ data.output = logEntry.final_output;
525
+ }
526
+ else if (eventType === "stream") {
527
+ const chunkCount = logEntry.streamed_output.length;
528
+ if (chunkCount !== 1) {
529
+ throw new Error(`Expected exactly one chunk of streamed output, got ${chunkCount} instead. Encountered in: "${logEntry.name}"`);
530
+ }
531
+ data = { chunk: logEntry.streamed_output[0] };
532
+ // Clean up the stream, we don't need it anymore.
533
+ // And this avoids duplicates as well!
534
+ logEntry.streamed_output = [];
535
+ }
536
+ yield {
537
+ event: `on_${logEntry.type}_${eventType}`,
538
+ name: logEntry.name,
539
+ run_id: logEntry.id,
540
+ tags: logEntry.tags,
541
+ metadata: logEntry.metadata,
542
+ data,
543
+ };
544
+ }
545
+ // Finally, we take care of the streaming output from the root chain
546
+ // if there is any.
547
+ const { state } = runLog;
548
+ if (state.streamed_output.length > 0) {
549
+ const chunkCount = state.streamed_output.length;
550
+ if (chunkCount !== 1) {
551
+ throw new Error(`Expected exactly one chunk of streamed output, got ${chunkCount} instead. Encountered in: "${state.name}"`);
552
+ }
553
+ const data = { chunk: state.streamed_output[0] };
554
+ // Clean up the stream, we don't need it anymore.
555
+ state.streamed_output = [];
556
+ const event = {
557
+ event: `on_${state.type}_stream`,
558
+ run_id: state.id,
559
+ tags: rootTags,
560
+ metadata: rootMetadata,
561
+ name: rootName,
562
+ data,
563
+ };
564
+ if (rootEventFilter.includeEvent(event, state.type)) {
565
+ yield event;
566
+ }
567
+ }
568
+ }
569
+ const state = runLog?.state;
570
+ if (state !== undefined) {
571
+ // Finally, yield the end event for the root runnable.
572
+ const event = {
573
+ event: `on_${state.type}_end`,
574
+ name: rootName,
575
+ run_id: state.id,
576
+ tags: rootTags,
577
+ metadata: rootMetadata,
578
+ data: {
579
+ output: state.final_output,
580
+ },
581
+ };
582
+ if (rootEventFilter.includeEvent(event, state.type))
583
+ yield event;
584
+ }
585
+ }
397
586
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
398
587
  static isRunnable(thing) {
399
588
  return thing ? thing.lc_runnable : false;
@@ -532,6 +721,12 @@ export class RunnableBinding extends Runnable {
532
721
  generator, options) {
533
722
  yield* this.bound.transform(generator, await this._mergeConfig(options, this.kwargs));
534
723
  }
724
+ async *streamEvents(input, options, streamOptions) {
725
+ yield* this.bound.streamEvents(input, {
726
+ ...(await this._mergeConfig(options, this.kwargs)),
727
+ version: options.version,
728
+ }, streamOptions);
729
+ }
535
730
  static isRunnableBinding(
536
731
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
537
732
  thing