@langchain/core 0.2.14 → 0.2.16

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.
Files changed (111) hide show
  1. package/README.md +1 -1
  2. package/caches.cjs +1 -1
  3. package/caches.d.cts +1 -1
  4. package/caches.d.ts +1 -1
  5. package/caches.js +1 -1
  6. package/callbacks/dispatch/web.cjs +1 -0
  7. package/callbacks/dispatch/web.d.cts +1 -0
  8. package/callbacks/dispatch/web.d.ts +1 -0
  9. package/callbacks/dispatch/web.js +1 -0
  10. package/callbacks/dispatch.cjs +1 -0
  11. package/callbacks/dispatch.d.cts +1 -0
  12. package/callbacks/dispatch.d.ts +1 -0
  13. package/callbacks/dispatch.js +1 -0
  14. package/dist/{caches.cjs → caches/base.cjs} +6 -6
  15. package/dist/{caches.d.ts → caches/base.d.ts} +7 -7
  16. package/dist/{caches.js → caches/base.js} +6 -6
  17. package/dist/caches/tests/in_memory_cache.test.d.ts +1 -0
  18. package/dist/caches/tests/in_memory_cache.test.js +33 -0
  19. package/dist/callbacks/base.cjs +8 -0
  20. package/dist/callbacks/base.d.ts +16 -10
  21. package/dist/callbacks/base.js +8 -0
  22. package/dist/callbacks/dispatch/index.cjs +49 -0
  23. package/dist/callbacks/dispatch/index.d.ts +35 -0
  24. package/dist/callbacks/dispatch/index.js +45 -0
  25. package/dist/callbacks/dispatch/web.cjs +61 -0
  26. package/dist/callbacks/dispatch/web.d.ts +32 -0
  27. package/dist/callbacks/dispatch/web.js +57 -0
  28. package/dist/callbacks/manager.cjs +32 -5
  29. package/dist/callbacks/manager.d.ts +5 -2
  30. package/dist/callbacks/manager.js +31 -5
  31. package/dist/language_models/base.cjs +4 -4
  32. package/dist/language_models/base.d.ts +2 -2
  33. package/dist/language_models/base.js +1 -1
  34. package/dist/language_models/chat_models.d.ts +4 -4
  35. package/dist/language_models/llms.d.ts +1 -1
  36. package/dist/language_models/tests/chat_models.test.js +33 -0
  37. package/dist/load/import_map.cjs +2 -2
  38. package/dist/load/import_map.d.ts +2 -2
  39. package/dist/load/import_map.js +2 -2
  40. package/dist/messages/ai.cjs +2 -0
  41. package/dist/messages/ai.js +2 -0
  42. package/dist/messages/base.cjs +45 -5
  43. package/dist/messages/base.d.ts +1 -0
  44. package/dist/messages/base.js +43 -4
  45. package/dist/messages/index.d.ts +1 -1
  46. package/dist/messages/tests/base_message.test.js +134 -2
  47. package/dist/messages/tests/message_utils.test.js +54 -2
  48. package/dist/messages/tool.cjs +31 -0
  49. package/dist/messages/tool.d.ts +27 -0
  50. package/dist/messages/tool.js +32 -1
  51. package/dist/messages/transformers.cjs +1 -0
  52. package/dist/messages/transformers.js +1 -0
  53. package/dist/messages/utils.cjs +5 -1
  54. package/dist/messages/utils.js +5 -1
  55. package/dist/output_parsers/openai_tools/json_output_tools_parsers.cjs +2 -0
  56. package/dist/output_parsers/openai_tools/json_output_tools_parsers.js +2 -0
  57. package/dist/runnables/base.cjs +89 -16
  58. package/dist/runnables/base.d.ts +51 -0
  59. package/dist/runnables/base.js +86 -15
  60. package/dist/runnables/config.cjs +30 -8
  61. package/dist/runnables/config.js +30 -8
  62. package/dist/runnables/history.cjs +1 -1
  63. package/dist/runnables/history.d.ts +1 -3
  64. package/dist/runnables/history.js +1 -1
  65. package/dist/runnables/index.cjs +2 -1
  66. package/dist/runnables/index.d.ts +1 -1
  67. package/dist/runnables/index.js +1 -1
  68. package/dist/runnables/tests/runnable.test.js +3 -3
  69. package/dist/runnables/tests/runnable_stream_events.test.js +1 -1
  70. package/dist/runnables/tests/runnable_stream_events_v2.test.js +124 -19
  71. package/dist/runnables/tests/runnable_tools.test.d.ts +1 -0
  72. package/dist/runnables/tests/runnable_tools.test.js +111 -0
  73. package/dist/singletons/tests/async_local_storage.test.js +38 -4
  74. package/dist/{tools.cjs → tools/index.cjs} +130 -14
  75. package/dist/{tools.d.ts → tools/index.d.ts} +69 -31
  76. package/dist/{tools.js → tools/index.js} +130 -14
  77. package/dist/tools/tests/tools.test.d.ts +1 -0
  78. package/dist/tools/tests/tools.test.js +74 -0
  79. package/dist/tracers/base.cjs +1 -0
  80. package/dist/tracers/base.d.ts +1 -1
  81. package/dist/tracers/base.js +1 -0
  82. package/dist/tracers/event_stream.cjs +15 -0
  83. package/dist/tracers/event_stream.d.ts +1 -0
  84. package/dist/tracers/event_stream.js +15 -0
  85. package/dist/tracers/initialize.cjs +2 -0
  86. package/dist/tracers/initialize.d.ts +2 -0
  87. package/dist/tracers/initialize.js +2 -0
  88. package/dist/types/zod.cjs +2 -0
  89. package/dist/types/zod.d.ts +2 -0
  90. package/dist/types/zod.js +1 -0
  91. package/dist/utils/callbacks.cjs +17 -0
  92. package/dist/utils/callbacks.d.ts +1 -0
  93. package/dist/utils/callbacks.js +13 -0
  94. package/dist/utils/function_calling.cjs +38 -10
  95. package/dist/utils/function_calling.d.ts +32 -11
  96. package/dist/utils/function_calling.js +36 -9
  97. package/dist/utils/testing/index.cjs +10 -3
  98. package/dist/utils/testing/index.d.ts +1 -1
  99. package/dist/utils/testing/index.js +9 -2
  100. package/package.json +28 -14
  101. package/tools.cjs +1 -1
  102. package/tools.d.cts +1 -1
  103. package/tools.d.ts +1 -1
  104. package/tools.js +1 -1
  105. package/dist/utils/beta_warning.cjs +0 -11
  106. package/dist/utils/beta_warning.d.ts +0 -5
  107. package/dist/utils/beta_warning.js +0 -7
  108. package/utils/beta_warning.cjs +0 -1
  109. package/utils/beta_warning.d.cts +0 -1
  110. package/utils/beta_warning.d.ts +0 -1
  111. package/utils/beta_warning.js +0 -1
@@ -7,10 +7,12 @@ import { RunnableLambda, RunnableMap, RunnablePassthrough, RunnablePick, } from
7
7
  import { ChatPromptTemplate } from "../../prompts/chat.js";
8
8
  import { FakeChatModel, FakeLLM, FakeListChatModel, FakeRetriever, FakeStreamingLLM, } from "../../utils/testing/index.js";
9
9
  import { AIMessage, AIMessageChunk, HumanMessage, SystemMessage, } from "../../messages/index.js";
10
- import { DynamicStructuredTool, DynamicTool, tool } from "../../tools.js";
10
+ import { DynamicStructuredTool, DynamicTool, tool } from "../../tools/index.js";
11
11
  import { Document } from "../../documents/document.js";
12
12
  import { PromptTemplate } from "../../prompts/prompt.js";
13
13
  import { GenerationChunk } from "../../outputs.js";
14
+ // Import from web to avoid side-effects from AsyncLocalStorage
15
+ import { dispatchCustomEvent } from "../../callbacks/dispatch/web.js";
14
16
  function reverse(s) {
15
17
  // Reverse a string.
16
18
  return s.split("").reverse().join("");
@@ -958,7 +960,7 @@ test("Chat model that supports streaming, but is invoked, should still emit on_s
958
960
  event: "on_chat_model_start",
959
961
  name: "my_model",
960
962
  run_id: expect.any(String),
961
- tags: expect.arrayContaining(["seq:step:2", "my_model", "my_chain"]),
963
+ tags: expect.arrayContaining(["my_model", "my_chain"]),
962
964
  metadata: {
963
965
  foo: "bar",
964
966
  a: "b",
@@ -976,7 +978,7 @@ test("Chat model that supports streaming, but is invoked, should still emit on_s
976
978
  {
977
979
  event: "on_chat_model_stream",
978
980
  run_id: expect.any(String),
979
- tags: expect.arrayContaining(["my_chain", "my_model", "seq:step:2"]),
981
+ tags: expect.arrayContaining(["my_chain", "my_model"]),
980
982
  metadata: {
981
983
  a: "b",
982
984
  foo: "bar",
@@ -989,7 +991,7 @@ test("Chat model that supports streaming, but is invoked, should still emit on_s
989
991
  {
990
992
  event: "on_chat_model_stream",
991
993
  run_id: expect.any(String),
992
- tags: expect.arrayContaining(["my_chain", "my_model", "seq:step:2"]),
994
+ tags: expect.arrayContaining(["my_chain", "my_model"]),
993
995
  metadata: {
994
996
  a: "b",
995
997
  foo: "bar",
@@ -1002,7 +1004,7 @@ test("Chat model that supports streaming, but is invoked, should still emit on_s
1002
1004
  {
1003
1005
  event: "on_chat_model_stream",
1004
1006
  run_id: expect.any(String),
1005
- tags: expect.arrayContaining(["my_chain", "my_model", "seq:step:2"]),
1007
+ tags: expect.arrayContaining(["my_chain", "my_model"]),
1006
1008
  metadata: {
1007
1009
  a: "b",
1008
1010
  foo: "bar",
@@ -1015,7 +1017,7 @@ test("Chat model that supports streaming, but is invoked, should still emit on_s
1015
1017
  {
1016
1018
  event: "on_chat_model_stream",
1017
1019
  run_id: expect.any(String),
1018
- tags: expect.arrayContaining(["my_chain", "my_model", "seq:step:2"]),
1020
+ tags: expect.arrayContaining(["my_chain", "my_model"]),
1019
1021
  metadata: {
1020
1022
  a: "b",
1021
1023
  foo: "bar",
@@ -1029,7 +1031,7 @@ test("Chat model that supports streaming, but is invoked, should still emit on_s
1029
1031
  event: "on_chat_model_end",
1030
1032
  name: "my_model",
1031
1033
  run_id: expect.any(String),
1032
- tags: expect.arrayContaining(["seq:step:2", "my_model", "my_chain"]),
1034
+ tags: expect.arrayContaining(["my_model", "my_chain"]),
1033
1035
  metadata: {
1034
1036
  foo: "bar",
1035
1037
  a: "b",
@@ -1172,7 +1174,7 @@ test("Chat model that doesn't support streaming, but is invoked, should emit one
1172
1174
  event: "on_chat_model_start",
1173
1175
  name: "my_model",
1174
1176
  run_id: expect.any(String),
1175
- tags: expect.arrayContaining(["seq:step:2", "my_model", "my_chain"]),
1177
+ tags: expect.arrayContaining(["my_model", "my_chain"]),
1176
1178
  metadata: {
1177
1179
  foo: "bar",
1178
1180
  a: "b",
@@ -1190,7 +1192,7 @@ test("Chat model that doesn't support streaming, but is invoked, should emit one
1190
1192
  {
1191
1193
  event: "on_chat_model_stream",
1192
1194
  run_id: expect.any(String),
1193
- tags: expect.arrayContaining(["my_chain", "my_model", "seq:step:2"]),
1195
+ tags: expect.arrayContaining(["my_chain", "my_model"]),
1194
1196
  metadata: {
1195
1197
  a: "b",
1196
1198
  foo: "bar",
@@ -1204,7 +1206,7 @@ test("Chat model that doesn't support streaming, but is invoked, should emit one
1204
1206
  event: "on_chat_model_end",
1205
1207
  name: "my_model",
1206
1208
  run_id: expect.any(String),
1207
- tags: expect.arrayContaining(["seq:step:2", "my_model", "my_chain"]),
1209
+ tags: expect.arrayContaining(["my_model", "my_chain"]),
1208
1210
  metadata: {
1209
1211
  foo: "bar",
1210
1212
  a: "b",
@@ -1356,7 +1358,7 @@ test("LLM that supports streaming, but is invoked, should still emit on_stream e
1356
1358
  },
1357
1359
  },
1358
1360
  name: "my_model",
1359
- tags: ["seq:step:2", "my_model", "my_chain"],
1361
+ tags: ["my_model", "my_chain"],
1360
1362
  run_id: expect.any(String),
1361
1363
  metadata: {
1362
1364
  foo: "bar",
@@ -1372,7 +1374,7 @@ test("LLM that supports streaming, but is invoked, should still emit on_stream e
1372
1374
  },
1373
1375
  run_id: expect.any(String),
1374
1376
  name: "my_model",
1375
- tags: ["seq:step:2", "my_model", "my_chain"],
1377
+ tags: ["my_model", "my_chain"],
1376
1378
  metadata: {
1377
1379
  foo: "bar",
1378
1380
  a: "b",
@@ -1387,7 +1389,7 @@ test("LLM that supports streaming, but is invoked, should still emit on_stream e
1387
1389
  },
1388
1390
  run_id: expect.any(String),
1389
1391
  name: "my_model",
1390
- tags: ["seq:step:2", "my_model", "my_chain"],
1392
+ tags: ["my_model", "my_chain"],
1391
1393
  metadata: {
1392
1394
  foo: "bar",
1393
1395
  a: "b",
@@ -1402,7 +1404,7 @@ test("LLM that supports streaming, but is invoked, should still emit on_stream e
1402
1404
  },
1403
1405
  run_id: expect.any(String),
1404
1406
  name: "my_model",
1405
- tags: ["seq:step:2", "my_model", "my_chain"],
1407
+ tags: ["my_model", "my_chain"],
1406
1408
  metadata: {
1407
1409
  foo: "bar",
1408
1410
  a: "b",
@@ -1417,7 +1419,7 @@ test("LLM that supports streaming, but is invoked, should still emit on_stream e
1417
1419
  },
1418
1420
  run_id: expect.any(String),
1419
1421
  name: "my_model",
1420
- tags: ["seq:step:2", "my_model", "my_chain"],
1422
+ tags: ["my_model", "my_chain"],
1421
1423
  metadata: {
1422
1424
  foo: "bar",
1423
1425
  a: "b",
@@ -1443,7 +1445,7 @@ test("LLM that supports streaming, but is invoked, should still emit on_stream e
1443
1445
  },
1444
1446
  run_id: expect.any(String),
1445
1447
  name: "my_model",
1446
- tags: ["seq:step:2", "my_model", "my_chain"],
1448
+ tags: ["my_model", "my_chain"],
1447
1449
  metadata: {
1448
1450
  foo: "bar",
1449
1451
  a: "b",
@@ -1587,7 +1589,7 @@ test("LLM that doesn't support streaming, but is invoked, should emit one on_str
1587
1589
  },
1588
1590
  },
1589
1591
  name: "my_model",
1590
- tags: ["seq:step:2", "my_model", "my_chain"],
1592
+ tags: ["my_model", "my_chain"],
1591
1593
  run_id: expect.any(String),
1592
1594
  metadata: {
1593
1595
  foo: "bar",
@@ -1603,7 +1605,7 @@ test("LLM that doesn't support streaming, but is invoked, should emit one on_str
1603
1605
  },
1604
1606
  run_id: expect.any(String),
1605
1607
  name: "my_model",
1606
- tags: ["seq:step:2", "my_model", "my_chain"],
1608
+ tags: ["my_model", "my_chain"],
1607
1609
  metadata: {
1608
1610
  foo: "bar",
1609
1611
  a: "b",
@@ -1629,7 +1631,7 @@ test("LLM that doesn't support streaming, but is invoked, should emit one on_str
1629
1631
  },
1630
1632
  run_id: expect.any(String),
1631
1633
  name: "my_model",
1632
- tags: ["seq:step:2", "my_model", "my_chain"],
1634
+ tags: ["my_model", "my_chain"],
1633
1635
  metadata: {
1634
1636
  foo: "bar",
1635
1637
  a: "b",
@@ -1748,6 +1750,109 @@ test("Runnable streamEvents method with simple tools", async () => {
1748
1750
  },
1749
1751
  ]);
1750
1752
  });
1753
+ test("Runnable streamEvents method with a custom event", async () => {
1754
+ const lambda = RunnableLambda.from(async (params, config) => {
1755
+ await dispatchCustomEvent("testEvent", { someval: "test" }, config);
1756
+ await dispatchCustomEvent("testEvent", { someval: "test2" }, config);
1757
+ return JSON.stringify({ x: params.x, y: params.y });
1758
+ });
1759
+ const events = [];
1760
+ const eventStream = await lambda.streamEvents({ x: 1, y: "2" }, { version: "v2" });
1761
+ for await (const event of eventStream) {
1762
+ events.push(event);
1763
+ }
1764
+ expect(events).toEqual([
1765
+ {
1766
+ event: "on_chain_start",
1767
+ data: { input: { x: 1, y: "2" } },
1768
+ name: "RunnableLambda",
1769
+ tags: [],
1770
+ run_id: expect.any(String),
1771
+ metadata: {},
1772
+ },
1773
+ {
1774
+ event: "on_custom_event",
1775
+ run_id: expect.any(String),
1776
+ name: "testEvent",
1777
+ tags: [],
1778
+ metadata: {},
1779
+ data: { someval: "test" },
1780
+ },
1781
+ {
1782
+ event: "on_custom_event",
1783
+ run_id: expect.any(String),
1784
+ name: "testEvent",
1785
+ tags: [],
1786
+ metadata: {},
1787
+ data: { someval: "test2" },
1788
+ },
1789
+ {
1790
+ event: "on_chain_stream",
1791
+ run_id: expect.any(String),
1792
+ name: "RunnableLambda",
1793
+ tags: [],
1794
+ metadata: {},
1795
+ data: { chunk: '{"x":1,"y":"2"}' },
1796
+ },
1797
+ {
1798
+ event: "on_chain_end",
1799
+ data: { output: '{"x":1,"y":"2"}' },
1800
+ run_id: expect.any(String),
1801
+ name: "RunnableLambda",
1802
+ tags: [],
1803
+ metadata: {},
1804
+ },
1805
+ ]);
1806
+ });
1807
+ test("Custom event inside a custom tool", async () => {
1808
+ const customTool = tool(async (params, config) => {
1809
+ await dispatchCustomEvent("testEvent", { someval: "test" }, config);
1810
+ await dispatchCustomEvent("testEvent", { someval: "test2" }, config);
1811
+ return JSON.stringify({ x: params.x, y: params.y });
1812
+ }, {
1813
+ schema: z.object({ x: z.number(), y: z.string() }),
1814
+ name: "testtool",
1815
+ });
1816
+ const events = [];
1817
+ const eventStream = await customTool.streamEvents({ x: 1, y: "2" }, { version: "v2" });
1818
+ for await (const event of eventStream) {
1819
+ events.push(event);
1820
+ }
1821
+ expect(events).toEqual([
1822
+ {
1823
+ event: "on_tool_start",
1824
+ data: { input: { x: 1, y: "2" } },
1825
+ name: "testtool",
1826
+ tags: [],
1827
+ run_id: expect.any(String),
1828
+ metadata: {},
1829
+ },
1830
+ {
1831
+ event: "on_custom_event",
1832
+ run_id: expect.any(String),
1833
+ name: "testEvent",
1834
+ tags: [],
1835
+ metadata: {},
1836
+ data: { someval: "test" },
1837
+ },
1838
+ {
1839
+ event: "on_custom_event",
1840
+ run_id: expect.any(String),
1841
+ name: "testEvent",
1842
+ tags: [],
1843
+ metadata: {},
1844
+ data: { someval: "test2" },
1845
+ },
1846
+ {
1847
+ event: "on_tool_end",
1848
+ data: { output: '{"x":1,"y":"2"}' },
1849
+ run_id: expect.any(String),
1850
+ name: "testtool",
1851
+ tags: [],
1852
+ metadata: {},
1853
+ },
1854
+ ]);
1855
+ });
1751
1856
  test("Runnable streamEvents method with tools that return objects", async () => {
1752
1857
  const adderFunc = (_params) => {
1753
1858
  return JSON.stringify({ sum: 3 });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,111 @@
1
+ import { z } from "zod";
2
+ import { RunnableLambda, RunnableToolLike } from "../base.js";
3
+ test("Runnable asTool works", async () => {
4
+ const schema = z.object({
5
+ foo: z.string(),
6
+ });
7
+ const runnable = RunnableLambda.from((input, config) => {
8
+ return `${input.foo}${config?.configurable.foo}`;
9
+ });
10
+ const tool = runnable.asTool({
11
+ schema,
12
+ });
13
+ expect(tool).toBeInstanceOf(RunnableToolLike);
14
+ expect(tool.schema).toBe(schema);
15
+ expect(tool.name).toBe(runnable.getName());
16
+ });
17
+ test("Runnable asTool works with all populated fields", async () => {
18
+ const schema = z.object({
19
+ foo: z.string(),
20
+ });
21
+ const runnable = RunnableLambda.from((input, config) => {
22
+ return `${input.foo}${config?.configurable.foo}`;
23
+ });
24
+ const tool = runnable.asTool({
25
+ schema,
26
+ name: "test",
27
+ description: "test",
28
+ });
29
+ expect(tool).toBeInstanceOf(RunnableToolLike);
30
+ expect(tool.schema).toBe(schema);
31
+ expect(tool.description).toBe("test");
32
+ expect(tool.name).toBe("test");
33
+ });
34
+ test("Runnable asTool can invoke", async () => {
35
+ const schema = z.object({
36
+ foo: z.string(),
37
+ });
38
+ const runnable = RunnableLambda.from((input, config) => {
39
+ return `${input.foo}${config?.configurable.foo}`;
40
+ });
41
+ const tool = runnable.asTool({
42
+ schema,
43
+ });
44
+ const toolResponse = await tool.invoke({
45
+ foo: "bar",
46
+ }, {
47
+ configurable: {
48
+ foo: "bar",
49
+ },
50
+ });
51
+ expect(toolResponse).toBe("barbar");
52
+ });
53
+ test("asTool should type error with mismatched schema", async () => {
54
+ // asTool infers the type of the Zod schema from the existing runnable's RunInput generic.
55
+ // If the Zod schema does not match the RunInput, it should throw a type error.
56
+ const schema = z.object({
57
+ foo: z.string(),
58
+ });
59
+ const runnable = RunnableLambda.from((input, config) => {
60
+ return `${input.bar}${config?.configurable.foo}`;
61
+ });
62
+ runnable.asTool({
63
+ // @ts-expect-error - Should error. If this does not give a type error, the generics/typing of `asTool` is broken.
64
+ schema,
65
+ });
66
+ });
67
+ test("Create a runnable tool directly from RunnableToolLike", async () => {
68
+ const schema = z.object({
69
+ foo: z.string(),
70
+ });
71
+ const adderFunc = (_) => {
72
+ return Promise.resolve(true);
73
+ };
74
+ const tool = new RunnableToolLike({
75
+ schema,
76
+ name: "test",
77
+ description: "test",
78
+ bound: RunnableLambda.from(adderFunc),
79
+ });
80
+ const result = await tool.invoke({ foo: "bar" });
81
+ expect(result).toBe(true);
82
+ });
83
+ test("asTool can take a single string input", async () => {
84
+ const firstRunnable = RunnableLambda.from((input) => {
85
+ return `${input}a`;
86
+ });
87
+ const secondRunnable = RunnableLambda.from((input) => {
88
+ return `${input}z`;
89
+ });
90
+ const runnable = firstRunnable.pipe(secondRunnable);
91
+ const asTool = runnable.asTool({
92
+ schema: z.string(),
93
+ });
94
+ const result = await asTool.invoke("b");
95
+ expect(result).toBe("baz");
96
+ });
97
+ test("Runnable asTool uses Zod schema description if not provided", async () => {
98
+ const description = "Test schema";
99
+ const schema = z
100
+ .object({
101
+ foo: z.string(),
102
+ })
103
+ .describe(description);
104
+ const runnable = RunnableLambda.from((input, config) => {
105
+ return `${input.foo}${config?.configurable.foo}`;
106
+ });
107
+ const tool = runnable.asTool({
108
+ schema,
109
+ });
110
+ expect(tool.description).toBe(description);
111
+ });
@@ -1,8 +1,22 @@
1
1
  import { test, expect } from "@jest/globals";
2
+ import { v4 } from "uuid";
2
3
  import { AsyncLocalStorage } from "node:async_hooks";
3
4
  import { AsyncLocalStorageProviderSingleton } from "../index.js";
4
5
  import { RunnableLambda } from "../../runnables/base.js";
5
6
  import { FakeListChatModel } from "../../utils/testing/index.js";
7
+ import { getCallbackManagerForConfig } from "../../runnables/config.js";
8
+ import { BaseCallbackHandler } from "../../callbacks/base.js";
9
+ class FakeCallbackHandler extends BaseCallbackHandler {
10
+ constructor() {
11
+ super(...arguments);
12
+ Object.defineProperty(this, "name", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: `fake-${v4()}`
17
+ });
18
+ }
19
+ }
6
20
  test("Config should be automatically populated after setting global async local storage", async () => {
7
21
  const inner = RunnableLambda.from((_, config) => config);
8
22
  const outer = RunnableLambda.from(async (input) => {
@@ -32,7 +46,6 @@ test("Config should be automatically populated after setting global async local
32
46
  });
33
47
  const chunks = [];
34
48
  for await (const chunk of stream) {
35
- console.log(chunk);
36
49
  chunks.push(chunk);
37
50
  }
38
51
  expect(chunks.length).toEqual(1);
@@ -58,7 +71,6 @@ test("Config should be automatically populated after setting global async local
58
71
  });
59
72
  const chunks2 = [];
60
73
  for await (const chunk of stream2) {
61
- console.log(chunk);
62
74
  chunks2.push(chunk);
63
75
  }
64
76
  expect(chunks2.length).toEqual(1);
@@ -90,17 +102,38 @@ test("Config should be automatically populated after setting global async local
90
102
  const eventStream = await outer4.streamEvents({ hi: true }, { version: "v1" });
91
103
  const events = [];
92
104
  for await (const event of eventStream) {
93
- console.log(event);
94
105
  events.push(event);
95
106
  }
96
107
  expect(events.filter((event) => event.event === "on_llm_start").length).toEqual(1);
97
108
  });
98
109
  test("Runnable streamEvents method with streaming nested in a RunnableLambda", async () => {
99
110
  AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new AsyncLocalStorage());
111
+ const asyncLocalStorage = AsyncLocalStorageProviderSingleton.getInstance();
100
112
  const chat = new FakeListChatModel({
101
113
  responses: ["Hello"],
102
114
  });
115
+ const outerRunId = v4();
116
+ const innerRunId = v4();
117
+ const innerRunId2 = v4();
118
+ const dummyHandler = new FakeCallbackHandler();
103
119
  const myFunc = async (input) => {
120
+ const outerCallbackManager = await getCallbackManagerForConfig(asyncLocalStorage.getStore());
121
+ expect(outerCallbackManager?.getParentRunId()).toEqual(outerRunId);
122
+ const nestedLambdaWithOverriddenCallbacks = RunnableLambda.from(async (_, config) => {
123
+ expect(config?.callbacks?.handlers).toEqual([]);
124
+ });
125
+ await nestedLambdaWithOverriddenCallbacks.invoke(input, {
126
+ runId: innerRunId,
127
+ callbacks: [],
128
+ });
129
+ const nestedLambdaWithoutOverriddenCallbacks = RunnableLambda.from(async (_, config) => {
130
+ const innerCallbackManager = await getCallbackManagerForConfig(asyncLocalStorage.getStore());
131
+ expect(innerCallbackManager?.getParentRunId()).toEqual(innerRunId2);
132
+ expect(config?.callbacks?.handlers).toContain(dummyHandler);
133
+ });
134
+ await nestedLambdaWithoutOverriddenCallbacks.invoke(input, {
135
+ runId: innerRunId2,
136
+ });
104
137
  for await (const _ of await chat.stream(input)) {
105
138
  // no-op
106
139
  }
@@ -109,8 +142,9 @@ test("Runnable streamEvents method with streaming nested in a RunnableLambda", a
109
142
  const events = [];
110
143
  for await (const event of myNestedLambda.streamEvents("hello", {
111
144
  version: "v1",
145
+ runId: outerRunId,
146
+ callbacks: [dummyHandler],
112
147
  })) {
113
- console.log(event);
114
148
  events.push(event);
115
149
  }
116
150
  const chatModelStreamEvent = events.find((event) => {
@@ -2,9 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tool = exports.BaseToolkit = exports.DynamicStructuredTool = exports.DynamicTool = exports.Tool = exports.StructuredTool = exports.ToolInputParsingException = void 0;
4
4
  const zod_1 = require("zod");
5
- const manager_js_1 = require("./callbacks/manager.cjs");
6
- const base_js_1 = require("./language_models/base.cjs");
7
- const config_js_1 = require("./runnables/config.cjs");
5
+ const manager_js_1 = require("../callbacks/manager.cjs");
6
+ const base_js_1 = require("../language_models/base.cjs");
7
+ const config_js_1 = require("../runnables/config.cjs");
8
+ const tool_js_1 = require("../messages/tool.cjs");
9
+ const index_js_1 = require("../singletons/index.cjs");
8
10
  /**
9
11
  * Custom error class used to handle exceptions related to tool input parsing.
10
12
  * It extends the built-in `Error` class and adds an optional `output`
@@ -38,6 +40,22 @@ class StructuredTool extends base_js_1.BaseLangChain {
38
40
  writable: true,
39
41
  value: false
40
42
  });
43
+ /**
44
+ * The tool response format.
45
+ *
46
+ * If "content" then the output of the tool is interpreted as the contents of a
47
+ * ToolMessage. If "content_and_artifact" then the output is expected to be a
48
+ * two-tuple corresponding to the (content, artifact) of a ToolMessage.
49
+ *
50
+ * @default "content"
51
+ */
52
+ Object.defineProperty(this, "responseFormat", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: "content"
57
+ });
58
+ this.responseFormat = fields?.responseFormat ?? this.responseFormat;
41
59
  }
42
60
  /**
43
61
  * Invokes the tool with the provided input and configuration.
@@ -46,7 +64,23 @@ class StructuredTool extends base_js_1.BaseLangChain {
46
64
  * @returns A Promise that resolves with a string.
47
65
  */
48
66
  async invoke(input, config) {
49
- return this.call(input, (0, config_js_1.ensureConfig)(config));
67
+ let tool_call_id;
68
+ let toolInput;
69
+ if (_isToolCall(input)) {
70
+ tool_call_id = input.id;
71
+ toolInput = input.args;
72
+ }
73
+ else {
74
+ toolInput = input;
75
+ }
76
+ const ensuredConfig = (0, config_js_1.ensureConfig)(config);
77
+ return this.call(toolInput, {
78
+ ...ensuredConfig,
79
+ configurable: {
80
+ ...ensuredConfig.configurable,
81
+ tool_call_id,
82
+ },
83
+ });
50
84
  }
51
85
  /**
52
86
  * @deprecated Use .invoke() instead. Will be removed in 0.3.0.
@@ -81,8 +115,32 @@ class StructuredTool extends base_js_1.BaseLangChain {
81
115
  await runManager?.handleToolError(e);
82
116
  throw e;
83
117
  }
84
- await runManager?.handleToolEnd(result);
85
- return result;
118
+ let content;
119
+ let artifact;
120
+ if (this.responseFormat === "content_and_artifact") {
121
+ if (Array.isArray(result) && result.length === 2) {
122
+ [content, artifact] = result;
123
+ }
124
+ else {
125
+ throw new Error(`Tool response format is "content_and_artifact" but the output was not a two-tuple.\nResult: ${JSON.stringify(result)}`);
126
+ }
127
+ }
128
+ else {
129
+ content = result;
130
+ }
131
+ let toolCallId;
132
+ if (config && "configurable" in config) {
133
+ toolCallId = config.configurable
134
+ .tool_call_id;
135
+ }
136
+ const formattedOutput = _formatToolOutput({
137
+ content,
138
+ artifact,
139
+ toolCallId,
140
+ name: this.name,
141
+ });
142
+ await runManager?.handleToolEnd(formattedOutput);
143
+ return formattedOutput;
86
144
  }
87
145
  }
88
146
  exports.StructuredTool = StructuredTool;
@@ -158,8 +216,8 @@ class DynamicTool extends Tool {
158
216
  return super.call(arg, config);
159
217
  }
160
218
  /** @ignore */
161
- async _call(input, runManager, config) {
162
- return this.func(input, runManager, config);
219
+ async _call(input, runManager, parentConfig) {
220
+ return this.func(input, runManager, parentConfig);
163
221
  }
164
222
  }
165
223
  exports.DynamicTool = DynamicTool;
@@ -217,8 +275,8 @@ class DynamicStructuredTool extends StructuredTool {
217
275
  }
218
276
  return super.call(arg, config, tags);
219
277
  }
220
- _call(arg, runManager, config) {
221
- return this.func(arg, runManager, config);
278
+ _call(arg, runManager, parentConfig) {
279
+ return this.func(arg, runManager, parentConfig);
222
280
  }
223
281
  }
224
282
  exports.DynamicStructuredTool = DynamicStructuredTool;
@@ -236,15 +294,17 @@ exports.BaseToolkit = BaseToolkit;
236
294
  /**
237
295
  * Creates a new StructuredTool instance with the provided function, name, description, and schema.
238
296
  * @function
239
- * @template {ZodAny} RunInput The input schema for the tool.
297
+ * @template {RunInput extends ZodAny = ZodAny} RunInput The input schema for the tool. This corresponds to the input type when the tool is invoked.
298
+ * @template {RunOutput = any} RunOutput The output type for the tool. This corresponds to the output type when the tool is invoked.
299
+ * @template {FuncInput extends z.infer<RunInput> | ToolCall = z.infer<RunInput>} FuncInput The input type for the function.
240
300
  *
241
- * @param {RunnableFunc<RunInput, string>} func - The function to invoke when the tool is called.
301
+ * @param {RunnableFunc<z.infer<RunInput> | ToolCall, RunOutput>} func - The function to invoke when the tool is called.
242
302
  * @param fields - An object containing the following properties:
243
303
  * @param {string} fields.name The name of the tool.
244
304
  * @param {string | undefined} fields.description The description of the tool. Defaults to either the description on the Zod schema, or `${fields.name} tool`.
245
305
  * @param {z.ZodObject<any, any, any, any>} fields.schema The Zod schema defining the input for the tool.
246
306
  *
247
- * @returns {StructuredTool<RunInput, string>} A new StructuredTool instance.
307
+ * @returns {DynamicStructuredTool<RunInput, RunOutput>} A new StructuredTool instance.
248
308
  */
249
309
  function tool(func, fields) {
250
310
  const schema = fields.schema ??
@@ -254,7 +314,63 @@ function tool(func, fields) {
254
314
  name: fields.name,
255
315
  description,
256
316
  schema: schema,
257
- func: async (input, _runManager, config) => func(input, config),
317
+ // TODO: Consider moving into DynamicStructuredTool constructor
318
+ func: async (input, runManager, config) => {
319
+ return new Promise((resolve, reject) => {
320
+ const childConfig = (0, config_js_1.patchConfig)(config, {
321
+ callbacks: runManager?.getChild(),
322
+ });
323
+ void index_js_1.AsyncLocalStorageProviderSingleton.getInstance().run(childConfig, async () => {
324
+ try {
325
+ resolve(func(input, childConfig));
326
+ }
327
+ catch (e) {
328
+ reject(e);
329
+ }
330
+ });
331
+ });
332
+ },
333
+ responseFormat: fields.responseFormat,
258
334
  });
259
335
  }
260
336
  exports.tool = tool;
337
+ function _isToolCall(toolCall) {
338
+ return !!(toolCall &&
339
+ typeof toolCall === "object" &&
340
+ "type" in toolCall &&
341
+ toolCall.type === "tool_call");
342
+ }
343
+ function _formatToolOutput(params) {
344
+ const { content, artifact, toolCallId } = params;
345
+ if (toolCallId) {
346
+ if (typeof content === "string" ||
347
+ (Array.isArray(content) &&
348
+ content.every((item) => typeof item === "object"))) {
349
+ return new tool_js_1.ToolMessage({
350
+ content,
351
+ artifact,
352
+ tool_call_id: toolCallId,
353
+ name: params.name,
354
+ });
355
+ }
356
+ else {
357
+ return new tool_js_1.ToolMessage({
358
+ content: _stringify(content),
359
+ artifact,
360
+ tool_call_id: toolCallId,
361
+ name: params.name,
362
+ });
363
+ }
364
+ }
365
+ else {
366
+ return content;
367
+ }
368
+ }
369
+ function _stringify(content) {
370
+ try {
371
+ return JSON.stringify(content, null, 2);
372
+ }
373
+ catch (_noOp) {
374
+ return `${content}`;
375
+ }
376
+ }