router.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { Runnable } from "./base.js";
  2. import { ensureConfig } from "./config.js";
  3. /**
  4. * A runnable that routes to a set of runnables based on Input['key'].
  5. * Returns the output of the selected runnable.
  6. * @example
  7. * ```typescript
  8. * import { RouterRunnable, RunnableLambda } from "@langchain/core/runnables";
  9. *
  10. * const router = new RouterRunnable({
  11. * runnables: {
  12. * toUpperCase: RunnableLambda.from((text: string) => text.toUpperCase()),
  13. * reverseText: RunnableLambda.from((text: string) =>
  14. * text.split("").reverse().join("")
  15. * ),
  16. * },
  17. * });
  18. *
  19. * // Invoke the 'reverseText' runnable
  20. * const result1 = router.invoke({ key: "reverseText", input: "Hello World" });
  21. *
  22. * // "dlroW olleH"
  23. *
  24. * // Invoke the 'toUpperCase' runnable
  25. * const result2 = router.invoke({ key: "toUpperCase", input: "Hello World" });
  26. *
  27. * // "HELLO WORLD"
  28. * ```
  29. */
  30. export class RouterRunnable extends Runnable {
  31. static lc_name() {
  32. return "RouterRunnable";
  33. }
  34. constructor(fields) {
  35. super(fields);
  36. Object.defineProperty(this, "lc_namespace", {
  37. enumerable: true,
  38. configurable: true,
  39. writable: true,
  40. value: ["langchain_core", "runnables"]
  41. });
  42. Object.defineProperty(this, "lc_serializable", {
  43. enumerable: true,
  44. configurable: true,
  45. writable: true,
  46. value: true
  47. });
  48. Object.defineProperty(this, "runnables", {
  49. enumerable: true,
  50. configurable: true,
  51. writable: true,
  52. value: void 0
  53. });
  54. this.runnables = fields.runnables;
  55. }
  56. async invoke(input, options) {
  57. const { key, input: actualInput } = input;
  58. const runnable = this.runnables[key];
  59. if (runnable === undefined) {
  60. throw new Error(`No runnable associated with key "${key}".`);
  61. }
  62. return runnable.invoke(actualInput, ensureConfig(options));
  63. }
  64. async batch(inputs, options, batchOptions) {
  65. const keys = inputs.map((input) => input.key);
  66. const actualInputs = inputs.map((input) => input.input);
  67. const missingKey = keys.find((key) => this.runnables[key] === undefined);
  68. if (missingKey !== undefined) {
  69. throw new Error(`One or more keys do not have a corresponding runnable.`);
  70. }
  71. const runnables = keys.map((key) => this.runnables[key]);
  72. const optionsList = this._getOptionsList(options ?? {}, inputs.length);
  73. const maxConcurrency = optionsList[0]?.maxConcurrency ?? batchOptions?.maxConcurrency;
  74. const batchSize = maxConcurrency && maxConcurrency > 0 ? maxConcurrency : inputs.length;
  75. const batchResults = [];
  76. for (let i = 0; i < actualInputs.length; i += batchSize) {
  77. const batchPromises = actualInputs
  78. .slice(i, i + batchSize)
  79. .map((actualInput, i) => runnables[i].invoke(actualInput, optionsList[i]));
  80. const batchResult = await Promise.all(batchPromises);
  81. batchResults.push(batchResult);
  82. }
  83. return batchResults.flat();
  84. }
  85. async stream(input, options) {
  86. const { key, input: actualInput } = input;
  87. const runnable = this.runnables[key];
  88. if (runnable === undefined) {
  89. throw new Error(`No runnable associated with key "${key}".`);
  90. }
  91. return runnable.stream(actualInput, options);
  92. }
  93. }