@langchain/core 0.2.15 → 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 (90) hide show
  1. package/caches.cjs +1 -1
  2. package/caches.d.cts +1 -1
  3. package/caches.d.ts +1 -1
  4. package/caches.js +1 -1
  5. package/callbacks/dispatch/web.cjs +1 -0
  6. package/callbacks/dispatch/web.d.cts +1 -0
  7. package/callbacks/dispatch/web.d.ts +1 -0
  8. package/callbacks/dispatch/web.js +1 -0
  9. package/callbacks/dispatch.cjs +1 -0
  10. package/callbacks/dispatch.d.cts +1 -0
  11. package/callbacks/dispatch.d.ts +1 -0
  12. package/callbacks/dispatch.js +1 -0
  13. package/dist/{caches.cjs → caches/base.cjs} +6 -6
  14. package/dist/{caches.d.ts → caches/base.d.ts} +7 -7
  15. package/dist/{caches.js → caches/base.js} +6 -6
  16. package/dist/caches/tests/in_memory_cache.test.d.ts +1 -0
  17. package/dist/caches/tests/in_memory_cache.test.js +33 -0
  18. package/dist/callbacks/base.cjs +8 -0
  19. package/dist/callbacks/base.d.ts +16 -10
  20. package/dist/callbacks/base.js +8 -0
  21. package/dist/callbacks/dispatch/index.cjs +49 -0
  22. package/dist/callbacks/dispatch/index.d.ts +35 -0
  23. package/dist/callbacks/dispatch/index.js +45 -0
  24. package/dist/callbacks/dispatch/web.cjs +61 -0
  25. package/dist/callbacks/dispatch/web.d.ts +32 -0
  26. package/dist/callbacks/dispatch/web.js +57 -0
  27. package/dist/callbacks/manager.cjs +20 -0
  28. package/dist/callbacks/manager.d.ts +2 -1
  29. package/dist/callbacks/manager.js +20 -0
  30. package/dist/language_models/base.cjs +4 -4
  31. package/dist/language_models/base.d.ts +2 -2
  32. package/dist/language_models/base.js +1 -1
  33. package/dist/language_models/chat_models.d.ts +4 -4
  34. package/dist/language_models/llms.d.ts +1 -1
  35. package/dist/language_models/tests/chat_models.test.js +33 -0
  36. package/dist/load/import_map.cjs +2 -2
  37. package/dist/load/import_map.d.ts +2 -2
  38. package/dist/load/import_map.js +2 -2
  39. package/dist/messages/ai.cjs +2 -0
  40. package/dist/messages/ai.js +2 -0
  41. package/dist/messages/base.cjs +45 -5
  42. package/dist/messages/base.d.ts +1 -0
  43. package/dist/messages/base.js +43 -4
  44. package/dist/messages/index.d.ts +1 -1
  45. package/dist/messages/tests/base_message.test.js +134 -2
  46. package/dist/messages/tests/message_utils.test.js +54 -2
  47. package/dist/messages/tool.cjs +31 -0
  48. package/dist/messages/tool.d.ts +27 -0
  49. package/dist/messages/tool.js +32 -1
  50. package/dist/messages/transformers.cjs +1 -0
  51. package/dist/messages/transformers.js +1 -0
  52. package/dist/messages/utils.cjs +5 -1
  53. package/dist/messages/utils.js +5 -1
  54. package/dist/output_parsers/openai_tools/json_output_tools_parsers.cjs +2 -0
  55. package/dist/output_parsers/openai_tools/json_output_tools_parsers.js +2 -0
  56. package/dist/runnables/base.cjs +73 -1
  57. package/dist/runnables/base.d.ts +49 -0
  58. package/dist/runnables/base.js +70 -0
  59. package/dist/runnables/index.cjs +2 -1
  60. package/dist/runnables/index.d.ts +1 -1
  61. package/dist/runnables/index.js +1 -1
  62. package/dist/runnables/tests/runnable_stream_events.test.js +1 -1
  63. package/dist/runnables/tests/runnable_stream_events_v2.test.js +106 -1
  64. package/dist/runnables/tests/runnable_tools.test.d.ts +1 -0
  65. package/dist/runnables/tests/runnable_tools.test.js +111 -0
  66. package/dist/{tools.cjs → tools/index.cjs} +130 -14
  67. package/dist/{tools.d.ts → tools/index.d.ts} +69 -31
  68. package/dist/{tools.js → tools/index.js} +130 -14
  69. package/dist/tools/tests/tools.test.d.ts +1 -0
  70. package/dist/tools/tests/tools.test.js +74 -0
  71. package/dist/tracers/base.cjs +1 -0
  72. package/dist/tracers/base.d.ts +1 -1
  73. package/dist/tracers/base.js +1 -0
  74. package/dist/tracers/event_stream.cjs +15 -0
  75. package/dist/tracers/event_stream.d.ts +1 -0
  76. package/dist/tracers/event_stream.js +15 -0
  77. package/dist/types/zod.cjs +2 -0
  78. package/dist/types/zod.d.ts +2 -0
  79. package/dist/types/zod.js +1 -0
  80. package/dist/utils/function_calling.cjs +38 -10
  81. package/dist/utils/function_calling.d.ts +32 -11
  82. package/dist/utils/function_calling.js +36 -9
  83. package/dist/utils/testing/index.cjs +10 -3
  84. package/dist/utils/testing/index.d.ts +1 -1
  85. package/dist/utils/testing/index.js +9 -2
  86. package/package.json +28 -1
  87. package/tools.cjs +1 -1
  88. package/tools.d.cts +1 -1
  89. package/tools.d.ts +1 -1
  90. package/tools.js +1 -1
@@ -93,7 +93,7 @@ export declare class CallbackManagerForChainRun extends BaseRunManager implement
93
93
  export declare class CallbackManagerForToolRun extends BaseRunManager implements BaseCallbackManagerMethods {
94
94
  getChild(tag?: string): CallbackManager;
95
95
  handleToolError(err: Error | unknown): Promise<void>;
96
- handleToolEnd(output: string): Promise<void>;
96
+ handleToolEnd(output: any): Promise<void>;
97
97
  }
98
98
  /**
99
99
  * @example
@@ -141,6 +141,7 @@ export declare class CallbackManager extends BaseCallbackManager implements Base
141
141
  handleChainStart(chain: Serialized, inputs: ChainValues, runId?: string, runType?: string | undefined, _tags?: string[] | undefined, _metadata?: Record<string, unknown> | undefined, runName?: string | undefined): Promise<CallbackManagerForChainRun>;
142
142
  handleToolStart(tool: Serialized, input: string, runId?: string, _parentRunId?: string | undefined, _tags?: string[] | undefined, _metadata?: Record<string, unknown> | undefined, runName?: string | undefined): Promise<CallbackManagerForToolRun>;
143
143
  handleRetrieverStart(retriever: Serialized, query: string, runId?: string, _parentRunId?: string | undefined, _tags?: string[] | undefined, _metadata?: Record<string, unknown> | undefined, runName?: string | undefined): Promise<CallbackManagerForRetrieverRun>;
144
+ handleCustomEvent?(eventName: string, data: any, runId: string, _tags?: string[], _metadata?: Record<string, any>): Promise<any>;
144
145
  addHandler(handler: BaseCallbackHandler, inherit?: boolean): void;
145
146
  removeHandler(handler: BaseCallbackHandler): void;
146
147
  setHandlers(handlers: BaseCallbackHandler[], inherit?: boolean): void;
@@ -299,6 +299,7 @@ export class CallbackManagerForToolRun extends BaseRunManager {
299
299
  }
300
300
  }, handler.awaitHandlers)));
301
301
  }
302
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
302
303
  async handleToolEnd(output) {
303
304
  await Promise.all(this.handlers.map((handler) => consumeCallback(async () => {
304
305
  if (!handler.ignoreAgent) {
@@ -496,6 +497,25 @@ export class CallbackManager extends BaseCallbackManager {
496
497
  }, handler.awaitHandlers)));
497
498
  return new CallbackManagerForRetrieverRun(runId, this.handlers, this.inheritableHandlers, this.tags, this.inheritableTags, this.metadata, this.inheritableMetadata, this._parentRunId);
498
499
  }
500
+ async handleCustomEvent(eventName,
501
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
502
+ data, runId, _tags,
503
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
504
+ _metadata) {
505
+ await Promise.all(this.handlers.map((handler) => consumeCallback(async () => {
506
+ if (!handler.ignoreCustomEvent) {
507
+ try {
508
+ await handler.handleCustomEvent?.(eventName, data, runId, this.tags, this.metadata);
509
+ }
510
+ catch (err) {
511
+ console.error(`Error in handler ${handler.constructor.name}, handleCustomEvent: ${err}`);
512
+ if (handler.raiseError) {
513
+ throw err;
514
+ }
515
+ }
516
+ }
517
+ }, handler.awaitHandlers)));
518
+ }
499
519
  addHandler(handler, inherit = true) {
500
520
  this.handlers.push(handler);
501
521
  if (inherit) {
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseLanguageModel = exports.BaseLangChain = exports.calculateMaxTokens = exports.isOpenAITool = exports.getModelContextSize = exports.getEmbeddingContextSize = exports.getModelNameForTiktoken = void 0;
4
- const caches_js_1 = require("../caches.cjs");
4
+ const base_js_1 = require("../caches/base.cjs");
5
5
  const prompt_values_js_1 = require("../prompt_values.cjs");
6
6
  const utils_js_1 = require("../messages/utils.cjs");
7
7
  const async_caller_js_1 = require("../utils/async_caller.cjs");
8
8
  const tiktoken_js_1 = require("../utils/tiktoken.cjs");
9
- const base_js_1 = require("../runnables/base.cjs");
9
+ const base_js_2 = require("../runnables/base.cjs");
10
10
  // https://www.npmjs.com/package/js-tiktoken
11
11
  const getModelNameForTiktoken = (modelName) => {
12
12
  if (modelName.startsWith("gpt-3.5-turbo-16k")) {
@@ -102,7 +102,7 @@ const getVerbosity = () => false;
102
102
  /**
103
103
  * Base class for language models, chains, tools.
104
104
  */
105
- class BaseLangChain extends base_js_1.Runnable {
105
+ class BaseLangChain extends base_js_2.Runnable {
106
106
  get lc_attributes() {
107
107
  return {
108
108
  callbacks: undefined,
@@ -186,7 +186,7 @@ class BaseLanguageModel extends BaseLangChain {
186
186
  this.cache = params.cache;
187
187
  }
188
188
  else if (params.cache) {
189
- this.cache = caches_js_1.InMemoryCache.global();
189
+ this.cache = base_js_1.InMemoryCache.global();
190
190
  }
191
191
  else {
192
192
  this.cache = undefined;
@@ -1,6 +1,6 @@
1
1
  import type { TiktokenModel } from "js-tiktoken/lite";
2
2
  import { z } from "zod";
3
- import { type BaseCache } from "../caches.js";
3
+ import { type BaseCache } from "../caches/base.js";
4
4
  import { type BasePromptValueInterface } from "../prompt_values.js";
5
5
  import { type BaseMessage, type BaseMessageLike, type MessageContent } from "../messages/base.js";
6
6
  import { type LLMResult } from "../outputs.js";
@@ -186,7 +186,7 @@ export declare abstract class BaseLanguageModel<RunOutput = any, CallOptions ext
186
186
  * @param callOptions Call options for the model
187
187
  * @returns A unique cache key.
188
188
  */
189
- protected _getSerializedCacheKeyParametersForCall({ config, ...callOptions }: CallOptions & {
189
+ _getSerializedCacheKeyParametersForCall({ config, ...callOptions }: CallOptions & {
190
190
  config?: RunnableConfig;
191
191
  }): string;
192
192
  /**
@@ -1,4 +1,4 @@
1
- import { InMemoryCache } from "../caches.js";
1
+ import { InMemoryCache } from "../caches/base.js";
2
2
  import { StringPromptValue, ChatPromptValue, } from "../prompt_values.js";
3
3
  import { coerceMessageLikeToMessage } from "../messages/utils.js";
4
4
  import { AsyncCaller } from "../utils/async_caller.js";
@@ -5,9 +5,9 @@ import { LLMResult, ChatGenerationChunk, type ChatResult, type Generation } from
5
5
  import { BaseLanguageModel, StructuredOutputMethodOptions, ToolDefinition, type BaseLanguageModelCallOptions, type BaseLanguageModelInput, type BaseLanguageModelParams } from "./base.js";
6
6
  import { type CallbackManagerForLLMRun, type Callbacks } from "../callbacks/manager.js";
7
7
  import type { RunnableConfig } from "../runnables/config.js";
8
- import type { BaseCache } from "../caches.js";
9
- import { StructuredToolInterface } from "../tools.js";
10
- import { Runnable } from "../runnables/base.js";
8
+ import type { BaseCache } from "../caches/base.js";
9
+ import { StructuredToolInterface } from "../tools/index.js";
10
+ import { Runnable, RunnableToolLike } from "../runnables/base.js";
11
11
  /**
12
12
  * Represents a serialized chat model.
13
13
  */
@@ -69,7 +69,7 @@ export declare abstract class BaseChatModel<CallOptions extends BaseChatModelCal
69
69
  * matching the provider's specific tool schema.
70
70
  * @param kwargs Any additional parameters to bind.
71
71
  */
72
- bindTools?(tools: (StructuredToolInterface | Record<string, unknown> | ToolDefinition)[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, OutputMessageType, CallOptions>;
72
+ bindTools?(tools: (StructuredToolInterface | Record<string, unknown> | ToolDefinition | RunnableToolLike)[], kwargs?: Partial<CallOptions>): Runnable<BaseLanguageModelInput, OutputMessageType, CallOptions>;
73
73
  /**
74
74
  * Invokes the chat model with a single input.
75
75
  * @param input The input for the language model.
@@ -4,7 +4,7 @@ import { type LLMResult, type Generation, GenerationChunk } from "../outputs.js"
4
4
  import { type BaseCallbackConfig, type CallbackManagerForLLMRun, type Callbacks } from "../callbacks/manager.js";
5
5
  import { BaseLanguageModel, type BaseLanguageModelCallOptions, type BaseLanguageModelInput, type BaseLanguageModelParams } from "./base.js";
6
6
  import type { RunnableConfig } from "../runnables/config.js";
7
- import type { BaseCache } from "../caches.js";
7
+ import type { BaseCache } from "../caches/base.js";
8
8
  export type SerializedLLM = {
9
9
  _model: string;
10
10
  _type: string;
@@ -3,6 +3,8 @@ import { test } from "@jest/globals";
3
3
  import { z } from "zod";
4
4
  import { zodToJsonSchema } from "zod-to-json-schema";
5
5
  import { FakeChatModel, FakeListChatModel } from "../../utils/testing/index.js";
6
+ import { HumanMessage } from "../../messages/human.js";
7
+ import { getBufferString } from "../../messages/utils.js";
6
8
  test("Test ChatModel accepts array shorthand for messages", async () => {
7
9
  const model = new FakeChatModel({});
8
10
  const response = await model.invoke([["human", "Hello there!"]]);
@@ -169,3 +171,34 @@ test("Test ChatModel withStructuredOutput new syntax and includeRaw", async () =
169
171
  // No error
170
172
  console.log(response.parsed);
171
173
  });
174
+ test("Test ChatModel can cache complex messages", async () => {
175
+ const model = new FakeChatModel({
176
+ cache: true,
177
+ });
178
+ if (!model.cache) {
179
+ throw new Error("Cache not enabled");
180
+ }
181
+ const contentToCache = [
182
+ {
183
+ type: "text",
184
+ text: "Hello there!",
185
+ },
186
+ ];
187
+ const humanMessage = new HumanMessage({
188
+ content: contentToCache,
189
+ });
190
+ const prompt = getBufferString([humanMessage]);
191
+ const llmKey = model._getSerializedCacheKeyParametersForCall({});
192
+ // Invoke model to trigger cache update
193
+ await model.invoke([humanMessage]);
194
+ const value = await model.cache.lookup(prompt, llmKey);
195
+ expect(value).toBeDefined();
196
+ if (!value)
197
+ return;
198
+ expect(value[0].text).toEqual(JSON.stringify(contentToCache, null, 2));
199
+ expect("message" in value[0]).toBeTruthy();
200
+ if (!("message" in value[0]))
201
+ return;
202
+ const cachedMsg = value[0].message;
203
+ expect(cachedMsg.content).toEqual(JSON.stringify(contentToCache, null, 2));
204
+ });
@@ -26,7 +26,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.vectorstores = exports.utils__types = exports.utils__tiktoken = exports.utils__testing = exports.utils__stream = exports.utils__math = exports.utils__json_schema = exports.utils__json_patch = exports.utils__hash = exports.utils__function_calling = exports.utils__env = exports.utils__chunk_array = exports.utils__async_caller = exports.tracers__tracer_langchain_v1 = exports.tracers__tracer_langchain = exports.tracers__run_collector = exports.tracers__log_stream = exports.tracers__initialize = exports.tracers__console = exports.tracers__base = exports.tools = exports.stores = exports.retrievers = exports.runnables = exports.prompt_values = exports.prompts = exports.outputs = exports.output_parsers = exports.messages = exports.memory = exports.load__serializable = exports.language_models__llms = exports.language_models__chat_models = exports.language_models__base = exports.example_selectors = exports.embeddings = exports.documents = exports.chat_history = exports.callbacks__promises = exports.callbacks__manager = exports.callbacks__base = exports.caches = exports.agents = void 0;
28
28
  exports.agents = __importStar(require("../agents.cjs"));
29
- exports.caches = __importStar(require("../caches.cjs"));
29
+ exports.caches = __importStar(require("../caches/base.cjs"));
30
30
  exports.callbacks__base = __importStar(require("../callbacks/base.cjs"));
31
31
  exports.callbacks__manager = __importStar(require("../callbacks/manager.cjs"));
32
32
  exports.callbacks__promises = __importStar(require("../callbacks/promises.cjs"));
@@ -47,7 +47,7 @@ exports.prompt_values = __importStar(require("../prompt_values.cjs"));
47
47
  exports.runnables = __importStar(require("../runnables/index.cjs"));
48
48
  exports.retrievers = __importStar(require("../retrievers/index.cjs"));
49
49
  exports.stores = __importStar(require("../stores.cjs"));
50
- exports.tools = __importStar(require("../tools.cjs"));
50
+ exports.tools = __importStar(require("../tools/index.cjs"));
51
51
  exports.tracers__base = __importStar(require("../tracers/base.cjs"));
52
52
  exports.tracers__console = __importStar(require("../tracers/console.cjs"));
53
53
  exports.tracers__initialize = __importStar(require("../tracers/initialize.cjs"));
@@ -1,5 +1,5 @@
1
1
  export * as agents from "../agents.js";
2
- export * as caches from "../caches.js";
2
+ export * as caches from "../caches/base.js";
3
3
  export * as callbacks__base from "../callbacks/base.js";
4
4
  export * as callbacks__manager from "../callbacks/manager.js";
5
5
  export * as callbacks__promises from "../callbacks/promises.js";
@@ -20,7 +20,7 @@ export * as prompt_values from "../prompt_values.js";
20
20
  export * as runnables from "../runnables/index.js";
21
21
  export * as retrievers from "../retrievers/index.js";
22
22
  export * as stores from "../stores.js";
23
- export * as tools from "../tools.js";
23
+ export * as tools from "../tools/index.js";
24
24
  export * as tracers__base from "../tracers/base.js";
25
25
  export * as tracers__console from "../tracers/console.js";
26
26
  export * as tracers__initialize from "../tracers/initialize.js";
@@ -1,6 +1,6 @@
1
1
  // Auto-generated by `scripts/create-entrypoints.js`. Do not edit manually.
2
2
  export * as agents from "../agents.js";
3
- export * as caches from "../caches.js";
3
+ export * as caches from "../caches/base.js";
4
4
  export * as callbacks__base from "../callbacks/base.js";
5
5
  export * as callbacks__manager from "../callbacks/manager.js";
6
6
  export * as callbacks__promises from "../callbacks/promises.js";
@@ -21,7 +21,7 @@ export * as prompt_values from "../prompt_values.js";
21
21
  export * as runnables from "../runnables/index.js";
22
22
  export * as retrievers from "../retrievers/index.js";
23
23
  export * as stores from "../stores.js";
24
- export * as tools from "../tools.js";
24
+ export * as tools from "../tools/index.js";
25
25
  export * as tracers__base from "../tracers/base.js";
26
26
  export * as tracers__console from "../tracers/console.js";
27
27
  export * as tracers__initialize from "../tracers/initialize.js";
@@ -141,6 +141,7 @@ class AIMessageChunk extends base_js_1.BaseMessageChunk {
141
141
  name: toolCallChunk.name ?? "",
142
142
  args: parsedArgs,
143
143
  id: toolCallChunk.id,
144
+ type: "tool_call",
144
145
  });
145
146
  }
146
147
  catch (e) {
@@ -149,6 +150,7 @@ class AIMessageChunk extends base_js_1.BaseMessageChunk {
149
150
  args: toolCallChunk.args,
150
151
  id: toolCallChunk.id,
151
152
  error: "Malformed args.",
153
+ type: "invalid_tool_call",
152
154
  });
153
155
  }
154
156
  }
@@ -136,6 +136,7 @@ export class AIMessageChunk extends BaseMessageChunk {
136
136
  name: toolCallChunk.name ?? "",
137
137
  args: parsedArgs,
138
138
  id: toolCallChunk.id,
139
+ type: "tool_call",
139
140
  });
140
141
  }
141
142
  catch (e) {
@@ -144,6 +145,7 @@ export class AIMessageChunk extends BaseMessageChunk {
144
145
  args: toolCallChunk.args,
145
146
  id: toolCallChunk.id,
146
147
  error: "Malformed args.",
148
+ type: "invalid_tool_call",
147
149
  });
148
150
  }
149
151
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isBaseMessageChunk = exports.isBaseMessage = exports.BaseMessageChunk = exports._mergeLists = exports._mergeDicts = exports.isOpenAIToolCallArray = exports.BaseMessage = exports.mergeContent = void 0;
3
+ exports.isBaseMessageChunk = exports.isBaseMessage = exports.BaseMessageChunk = exports._mergeObj = exports._mergeLists = exports._mergeDicts = exports.isOpenAIToolCallArray = exports.BaseMessage = exports.mergeContent = void 0;
4
4
  const serializable_js_1 = require("../load/serializable.cjs");
5
5
  function mergeContent(firstContent, secondContent) {
6
6
  // If first content is a string
@@ -14,8 +14,10 @@ function mergeContent(firstContent, secondContent) {
14
14
  // If both are arrays
15
15
  }
16
16
  else if (Array.isArray(secondContent)) {
17
- return [...firstContent, ...secondContent];
18
- // If the first content is a list and second is a string
17
+ return (_mergeLists(firstContent, secondContent) ?? [
18
+ ...firstContent,
19
+ ...secondContent,
20
+ ]);
19
21
  }
20
22
  else {
21
23
  // Otherwise, add the second content as a new element of the list
@@ -154,9 +156,13 @@ right
154
156
  throw new Error(`field[${key}] already exists in the message chunk, but with a different type.`);
155
157
  }
156
158
  else if (typeof merged[key] === "string") {
157
- merged[key] = merged[key] + value;
159
+ if (key === "type") {
160
+ // Do not merge 'type' fields
161
+ continue;
162
+ }
163
+ merged[key] += value;
158
164
  }
159
- else if (!Array.isArray(merged[key]) && typeof merged[key] === "object") {
165
+ else if (typeof merged[key] === "object" && !Array.isArray(merged[key])) {
160
166
  merged[key] = _mergeDicts(merged[key], value);
161
167
  }
162
168
  else if (Array.isArray(merged[key])) {
@@ -194,6 +200,12 @@ function _mergeLists(left, right) {
194
200
  merged.push(item);
195
201
  }
196
202
  }
203
+ else if (typeof item === "object" &&
204
+ "text" in item &&
205
+ item.text === "") {
206
+ // No-op - skip empty text blocks
207
+ continue;
208
+ }
197
209
  else {
198
210
  merged.push(item);
199
211
  }
@@ -202,6 +214,34 @@ function _mergeLists(left, right) {
202
214
  }
203
215
  }
204
216
  exports._mergeLists = _mergeLists;
217
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
218
+ function _mergeObj(left, right) {
219
+ if (!left && !right) {
220
+ throw new Error("Cannot merge two undefined objects.");
221
+ }
222
+ if (!left || !right) {
223
+ return left || right;
224
+ }
225
+ else if (typeof left !== typeof right) {
226
+ throw new Error(`Cannot merge objects of different types.\nLeft ${typeof left}\nRight ${typeof right}`);
227
+ }
228
+ else if (typeof left === "string" && typeof right === "string") {
229
+ return (left + right);
230
+ }
231
+ else if (Array.isArray(left) && Array.isArray(right)) {
232
+ return _mergeLists(left, right);
233
+ }
234
+ else if (typeof left === "object" && typeof right === "object") {
235
+ return _mergeDicts(left, right);
236
+ }
237
+ else if (left === right) {
238
+ return left;
239
+ }
240
+ else {
241
+ throw new Error(`Can not merge objects of different types.\nLeft ${left}\nRight ${right}`);
242
+ }
243
+ }
244
+ exports._mergeObj = _mergeObj;
205
245
  /**
206
246
  * Represents a chunk of a message, which can be concatenated with other
207
247
  * message chunks. It includes a method `_merge_kwargs_dict()` for merging
@@ -130,6 +130,7 @@ export type OpenAIToolCall = ToolCall & {
130
130
  export declare function isOpenAIToolCallArray(value?: unknown): value is OpenAIToolCall[];
131
131
  export declare function _mergeDicts(left: Record<string, any>, right: Record<string, any>): Record<string, any>;
132
132
  export declare function _mergeLists(left?: any[], right?: any[]): any[] | undefined;
133
+ export declare function _mergeObj<T = any>(left: T | undefined, right: T | undefined): T;
133
134
  /**
134
135
  * Represents a chunk of a message, which can be concatenated with other
135
136
  * message chunks. It includes a method `_merge_kwargs_dict()` for merging
@@ -11,8 +11,10 @@ export function mergeContent(firstContent, secondContent) {
11
11
  // If both are arrays
12
12
  }
13
13
  else if (Array.isArray(secondContent)) {
14
- return [...firstContent, ...secondContent];
15
- // If the first content is a list and second is a string
14
+ return (_mergeLists(firstContent, secondContent) ?? [
15
+ ...firstContent,
16
+ ...secondContent,
17
+ ]);
16
18
  }
17
19
  else {
18
20
  // Otherwise, add the second content as a new element of the list
@@ -148,9 +150,13 @@ right
148
150
  throw new Error(`field[${key}] already exists in the message chunk, but with a different type.`);
149
151
  }
150
152
  else if (typeof merged[key] === "string") {
151
- merged[key] = merged[key] + value;
153
+ if (key === "type") {
154
+ // Do not merge 'type' fields
155
+ continue;
156
+ }
157
+ merged[key] += value;
152
158
  }
153
- else if (!Array.isArray(merged[key]) && typeof merged[key] === "object") {
159
+ else if (typeof merged[key] === "object" && !Array.isArray(merged[key])) {
154
160
  merged[key] = _mergeDicts(merged[key], value);
155
161
  }
156
162
  else if (Array.isArray(merged[key])) {
@@ -187,6 +193,12 @@ export function _mergeLists(left, right) {
187
193
  merged.push(item);
188
194
  }
189
195
  }
196
+ else if (typeof item === "object" &&
197
+ "text" in item &&
198
+ item.text === "") {
199
+ // No-op - skip empty text blocks
200
+ continue;
201
+ }
190
202
  else {
191
203
  merged.push(item);
192
204
  }
@@ -194,6 +206,33 @@ export function _mergeLists(left, right) {
194
206
  return merged;
195
207
  }
196
208
  }
209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
210
+ export function _mergeObj(left, right) {
211
+ if (!left && !right) {
212
+ throw new Error("Cannot merge two undefined objects.");
213
+ }
214
+ if (!left || !right) {
215
+ return left || right;
216
+ }
217
+ else if (typeof left !== typeof right) {
218
+ throw new Error(`Cannot merge objects of different types.\nLeft ${typeof left}\nRight ${typeof right}`);
219
+ }
220
+ else if (typeof left === "string" && typeof right === "string") {
221
+ return (left + right);
222
+ }
223
+ else if (Array.isArray(left) && Array.isArray(right)) {
224
+ return _mergeLists(left, right);
225
+ }
226
+ else if (typeof left === "object" && typeof right === "object") {
227
+ return _mergeDicts(left, right);
228
+ }
229
+ else if (left === right) {
230
+ return left;
231
+ }
232
+ else {
233
+ throw new Error(`Can not merge objects of different types.\nLeft ${left}\nRight ${right}`);
234
+ }
235
+ }
197
236
  /**
198
237
  * Represents a chunk of a message, which can be concatenated with other
199
238
  * message chunks. It includes a method `_merge_kwargs_dict()` for merging
@@ -6,4 +6,4 @@ export * from "./human.js";
6
6
  export * from "./system.js";
7
7
  export * from "./utils.js";
8
8
  export * from "./transformers.js";
9
- export { type ToolMessageFieldsWithToolCallId, ToolMessage, ToolMessageChunk, } from "./tool.js";
9
+ export { type ToolMessageFieldsWithToolCallId, ToolMessage, ToolMessageChunk, type InvalidToolCall, } from "./tool.js";
@@ -1,6 +1,6 @@
1
- import { test } from "@jest/globals";
1
+ import { test, describe, it, expect } from "@jest/globals";
2
2
  import { ChatPromptTemplate } from "../../prompts/chat.js";
3
- import { HumanMessage, AIMessage, ToolMessage } from "../index.js";
3
+ import { HumanMessage, AIMessage, ToolMessage, ToolMessageChunk, AIMessageChunk, } from "../index.js";
4
4
  import { load } from "../../load/index.js";
5
5
  test("Test ChatPromptTemplate can format OpenAI content image messages", async () => {
6
6
  const message = new HumanMessage({
@@ -111,3 +111,135 @@ test("Deserialisation and serialisation of messages with ID", async () => {
111
111
  expect(deserialized).toEqual(message);
112
112
  expect(deserialized.id).toBe(messageId);
113
113
  });
114
+ test("Can concat artifact (string) of ToolMessageChunk", () => {
115
+ const rawOutputOne = "Hello";
116
+ const rawOutputTwo = " world";
117
+ const chunk1 = new ToolMessageChunk({
118
+ content: "Hello",
119
+ tool_call_id: "1",
120
+ artifact: rawOutputOne,
121
+ });
122
+ const chunk2 = new ToolMessageChunk({
123
+ content: " world",
124
+ tool_call_id: "1",
125
+ artifact: rawOutputTwo,
126
+ });
127
+ const concated = chunk1.concat(chunk2);
128
+ expect(concated.artifact).toBe(`${rawOutputOne}${rawOutputTwo}`);
129
+ });
130
+ test("Can concat artifact (array) of ToolMessageChunk", () => {
131
+ const rawOutputOne = ["Hello", " world"];
132
+ const rawOutputTwo = ["!!"];
133
+ const chunk1 = new ToolMessageChunk({
134
+ content: "Hello",
135
+ tool_call_id: "1",
136
+ artifact: rawOutputOne,
137
+ });
138
+ const chunk2 = new ToolMessageChunk({
139
+ content: " world",
140
+ tool_call_id: "1",
141
+ artifact: rawOutputTwo,
142
+ });
143
+ const concated = chunk1.concat(chunk2);
144
+ expect(concated.artifact).toEqual(["Hello", " world", "!!"]);
145
+ });
146
+ test("Can concat artifact (object) of ToolMessageChunk", () => {
147
+ const rawOutputOne = {
148
+ foo: "bar",
149
+ };
150
+ const rawOutputTwo = {
151
+ bar: "baz",
152
+ };
153
+ const chunk1 = new ToolMessageChunk({
154
+ content: "Hello",
155
+ tool_call_id: "1",
156
+ artifact: rawOutputOne,
157
+ });
158
+ const chunk2 = new ToolMessageChunk({
159
+ content: " world",
160
+ tool_call_id: "1",
161
+ artifact: rawOutputTwo,
162
+ });
163
+ const concated = chunk1.concat(chunk2);
164
+ expect(concated.artifact).toEqual({
165
+ foo: "bar",
166
+ bar: "baz",
167
+ });
168
+ });
169
+ describe("Complex AIMessageChunk concat", () => {
170
+ it("concatenates content arrays of strings", () => {
171
+ expect(new AIMessageChunk({
172
+ content: [{ type: "text", text: "I am" }],
173
+ id: "ai4",
174
+ }).concat(new AIMessageChunk({ content: [{ type: "text", text: " indeed." }] }))).toEqual(new AIMessageChunk({
175
+ id: "ai4",
176
+ content: [
177
+ { type: "text", text: "I am" },
178
+ { type: "text", text: " indeed." },
179
+ ],
180
+ }));
181
+ });
182
+ it("concatenates mixed content arrays", () => {
183
+ expect(new AIMessageChunk({
184
+ content: [{ index: 0, type: "text", text: "I am" }],
185
+ }).concat(new AIMessageChunk({ content: [{ type: "text", text: " indeed." }] }))).toEqual(new AIMessageChunk({
186
+ content: [
187
+ { index: 0, type: "text", text: "I am" },
188
+ { type: "text", text: " indeed." },
189
+ ],
190
+ }));
191
+ });
192
+ it("merges content arrays with same index", () => {
193
+ expect(new AIMessageChunk({ content: [{ index: 0, text: "I am" }] }).concat(new AIMessageChunk({ content: [{ index: 0, text: " indeed." }] }))).toEqual(new AIMessageChunk({ content: [{ index: 0, text: "I am indeed." }] }));
194
+ });
195
+ it("does not merge when one chunk is missing an index", () => {
196
+ expect(new AIMessageChunk({ content: [{ index: 0, text: "I am" }] }).concat(new AIMessageChunk({ content: [{ text: " indeed." }] }))).toEqual(new AIMessageChunk({
197
+ content: [{ index: 0, text: "I am" }, { text: " indeed." }],
198
+ }));
199
+ });
200
+ it("does not create a holey array when there's a gap between indexes", () => {
201
+ expect(new AIMessageChunk({ content: [{ index: 0, text: "I am" }] }).concat(new AIMessageChunk({ content: [{ index: 2, text: " indeed." }] }))).toEqual(new AIMessageChunk({
202
+ content: [
203
+ { index: 0, text: "I am" },
204
+ { index: 2, text: " indeed." },
205
+ ],
206
+ }));
207
+ });
208
+ it("does not merge content arrays with separate indexes", () => {
209
+ expect(new AIMessageChunk({ content: [{ index: 0, text: "I am" }] }).concat(new AIMessageChunk({ content: [{ index: 1, text: " indeed." }] }))).toEqual(new AIMessageChunk({
210
+ content: [
211
+ { index: 0, text: "I am" },
212
+ { index: 1, text: " indeed." },
213
+ ],
214
+ }));
215
+ });
216
+ it("merges content arrays with same index and type", () => {
217
+ expect(new AIMessageChunk({
218
+ content: [{ index: 0, text: "I am", type: "text_block" }],
219
+ }).concat(new AIMessageChunk({
220
+ content: [{ index: 0, text: " indeed.", type: "text_block" }],
221
+ }))).toEqual(new AIMessageChunk({
222
+ content: [{ index: 0, text: "I am indeed.", type: "text_block" }],
223
+ }));
224
+ });
225
+ it("merges content arrays with same index and different types without updating type", () => {
226
+ expect(new AIMessageChunk({
227
+ content: [{ index: 0, text: "I am", type: "text_block" }],
228
+ }).concat(new AIMessageChunk({
229
+ content: [{ index: 0, text: " indeed.", type: "text_block_delta" }],
230
+ }))).toEqual(new AIMessageChunk({
231
+ content: [{ index: 0, text: "I am indeed.", type: "text_block" }],
232
+ }));
233
+ });
234
+ it("concatenates empty string content and merges other fields", () => {
235
+ expect(new AIMessageChunk({
236
+ content: [{ index: 0, type: "text", text: "I am" }],
237
+ }).concat(new AIMessageChunk({
238
+ content: [{ type: "text", text: "" }],
239
+ response_metadata: { extra: "value" },
240
+ }))).toEqual(new AIMessageChunk({
241
+ content: [{ index: 0, type: "text", text: "I am" }],
242
+ response_metadata: { extra: "value" },
243
+ }));
244
+ });
245
+ });