@langchain/langgraph 0.2.44 → 0.2.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,7 +21,7 @@ LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [A
21
21
 
22
22
  ### Why use LangGraph?
23
23
 
24
- LangGraph powers production-grade agents, trusted by Linkedin, Uber, Klarna, GitLab, and many more. LangGraph provides fine-grained control over both the flow and state of your agent applications. It implements a central [persistence layer](https://langchain-ai.github.io/langgraphjs/concepts/persistence/), enabling features that are common to most agent architectures:
24
+ LangGraph powers [production-grade agents](https://www.langchain.com/built-with-langgraph), trusted by Linkedin, Uber, Klarna, GitLab, and many more. LangGraph provides fine-grained control over both the flow and state of your agent applications. It implements a central [persistence layer](https://langchain-ai.github.io/langgraphjs/concepts/persistence/), enabling features that are common to most agent architectures:
25
25
 
26
26
  - **Memory**: LangGraph persists arbitrary aspects of your application's state,
27
27
  supporting memory of conversations and other updates within and across user
@@ -358,6 +358,10 @@ Then we define one normal and one conditional edge. Conditional edge means that
358
358
  * [API Reference](https://langchain-ai.github.io/langgraphjs/reference/): Review important classes and methods, simple examples of how to use the graph and checkpointing APIs, higher-level prebuilt components and more.
359
359
  * [LangGraph Platform](https://langchain-ai.github.io/langgraphjs/concepts/#langgraph-platform): LangGraph Platform is a commercial solution for deploying agentic applications in production, built on the open-source LangGraph framework.
360
360
 
361
+ ## Resources
362
+
363
+ * [Built with LangGraph](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship powerful, production-ready AI applications.
364
+
361
365
  ## Contributing
362
366
 
363
367
  For more information on how to contribute, see [here](https://github.com/langchain-ai/langgraphjs/blob/main/CONTRIBUTING.md).
@@ -6,7 +6,6 @@ const last_value_js_1 = require("../channels/last_value.cjs");
6
6
  const base_js_1 = require("../managed/base.cjs");
7
7
  /**
8
8
  * Should not be instantiated directly. See {@link Annotation}.
9
- * @internal
10
9
  */
11
10
  class AnnotationRoot {
12
11
  constructor(s) {
@@ -33,7 +33,6 @@ export interface AnnotationFunction {
33
33
  }
34
34
  /**
35
35
  * Should not be instantiated directly. See {@link Annotation}.
36
- * @internal
37
36
  */
38
37
  export declare class AnnotationRoot<SD extends StateDefinition> {
39
38
  lc_graph_name: string;
@@ -3,7 +3,6 @@ import { LastValue } from "../channels/last_value.js";
3
3
  import { isConfiguredManagedValue, } from "../managed/base.js";
4
4
  /**
5
5
  * Should not be instantiated directly. See {@link Annotation}.
6
- * @internal
7
6
  */
8
7
  export class AnnotationRoot {
9
8
  constructor(s) {
@@ -220,7 +220,7 @@ class Graph {
220
220
  this.warnIfCompiled("Setting a finish point of a graph that has already been compiled. This will not be reflected in the compiled graph.");
221
221
  return this.addEdge(key, constants_js_1.END);
222
222
  }
223
- compile({ checkpointer, interruptBefore, interruptAfter, } = {}) {
223
+ compile({ checkpointer, interruptBefore, interruptAfter, name, } = {}) {
224
224
  // validate the graph
225
225
  this.validate([
226
226
  ...(Array.isArray(interruptBefore) ? interruptBefore : []),
@@ -242,6 +242,7 @@ class Graph {
242
242
  outputChannels: constants_js_1.END,
243
243
  streamChannels: [],
244
244
  streamMode: "values",
245
+ name,
245
246
  });
246
247
  // attach nodes, edges and branches
247
248
  for (const [key, node] of Object.entries(this.nodes)) {
@@ -54,10 +54,11 @@ export declare class Graph<N extends string = typeof END, RunInput = any, RunOut
54
54
  * @deprecated use `addEdge(key, END)` instead
55
55
  */
56
56
  setFinishPoint(key: N): this;
57
- compile({ checkpointer, interruptBefore, interruptAfter, }?: {
57
+ compile({ checkpointer, interruptBefore, interruptAfter, name, }?: {
58
58
  checkpointer?: BaseCheckpointSaver | false;
59
59
  interruptBefore?: N[] | All;
60
60
  interruptAfter?: N[] | All;
61
+ name?: string;
61
62
  }): CompiledGraph<N>;
62
63
  validate(interrupt?: string[]): void;
63
64
  }
@@ -216,7 +216,7 @@ export class Graph {
216
216
  this.warnIfCompiled("Setting a finish point of a graph that has already been compiled. This will not be reflected in the compiled graph.");
217
217
  return this.addEdge(key, END);
218
218
  }
219
- compile({ checkpointer, interruptBefore, interruptAfter, } = {}) {
219
+ compile({ checkpointer, interruptBefore, interruptAfter, name, } = {}) {
220
220
  // validate the graph
221
221
  this.validate([
222
222
  ...(Array.isArray(interruptBefore) ? interruptBefore : []),
@@ -238,6 +238,7 @@ export class Graph {
238
238
  outputChannels: END,
239
239
  streamChannels: [],
240
240
  streamMode: "values",
241
+ name,
241
242
  });
242
243
  // attach nodes, edges and branches
243
244
  for (const [key, node] of Object.entries(this.nodes)) {
@@ -29,16 +29,17 @@ function messagesStateReducer(left, right) {
29
29
  }
30
30
  }
31
31
  // merge
32
- const leftIdxById = new Map(leftMessages.map((m, i) => [m.id, i]));
33
32
  const merged = [...leftMessages];
33
+ const mergedById = new Map(merged.map((m, i) => [m.id, i]));
34
34
  const idsToRemove = new Set();
35
35
  for (const m of rightMessages) {
36
- const existingIdx = leftIdxById.get(m.id);
36
+ const existingIdx = mergedById.get(m.id);
37
37
  if (existingIdx !== undefined) {
38
38
  if (m._getType() === "remove") {
39
39
  idsToRemove.add(m.id);
40
40
  }
41
41
  else {
42
+ idsToRemove.delete(m.id);
42
43
  merged[existingIdx] = m;
43
44
  }
44
45
  }
@@ -46,6 +47,7 @@ function messagesStateReducer(left, right) {
46
47
  if (m._getType() === "remove") {
47
48
  throw new Error(`Attempting to delete a message with an ID that doesn't exist ('${m.id}')`);
48
49
  }
50
+ mergedById.set(m.id, merged.length);
49
51
  merged.push(m);
50
52
  }
51
53
  }
@@ -26,16 +26,17 @@ export function messagesStateReducer(left, right) {
26
26
  }
27
27
  }
28
28
  // merge
29
- const leftIdxById = new Map(leftMessages.map((m, i) => [m.id, i]));
30
29
  const merged = [...leftMessages];
30
+ const mergedById = new Map(merged.map((m, i) => [m.id, i]));
31
31
  const idsToRemove = new Set();
32
32
  for (const m of rightMessages) {
33
- const existingIdx = leftIdxById.get(m.id);
33
+ const existingIdx = mergedById.get(m.id);
34
34
  if (existingIdx !== undefined) {
35
35
  if (m._getType() === "remove") {
36
36
  idsToRemove.add(m.id);
37
37
  }
38
38
  else {
39
+ idsToRemove.delete(m.id);
39
40
  merged[existingIdx] = m;
40
41
  }
41
42
  }
@@ -43,6 +44,7 @@ export function messagesStateReducer(left, right) {
43
44
  if (m._getType() === "remove") {
44
45
  throw new Error(`Attempting to delete a message with an ID that doesn't exist ('${m.id}')`);
45
46
  }
47
+ mergedById.set(m.id, merged.length);
46
48
  merged.push(m);
47
49
  }
48
50
  }
@@ -270,7 +270,7 @@ class StateGraph extends graph_js_1.Graph {
270
270
  this.waitingEdges.add([startKey, endKey]);
271
271
  return this;
272
272
  }
273
- compile({ checkpointer, store, interruptBefore, interruptAfter, } = {}) {
273
+ compile({ checkpointer, store, interruptBefore, interruptAfter, name, } = {}) {
274
274
  // validate the graph
275
275
  this.validate([
276
276
  ...(Array.isArray(interruptBefore) ? interruptBefore : []),
@@ -298,6 +298,7 @@ class StateGraph extends graph_js_1.Graph {
298
298
  streamChannels,
299
299
  streamMode: "updates",
300
300
  store,
301
+ name,
301
302
  });
302
303
  // attach nodes, edges and branches
303
304
  compiled.attachNode(constants_js_1.START);
@@ -117,11 +117,12 @@ export declare class StateGraph<SD extends StateDefinition | unknown, S = SD ext
117
117
  _addSchema(stateDefinition: StateDefinition): void;
118
118
  addNode<K extends string, NodeInput = S>(key: K, action: RunnableLike<NodeInput, U extends object ? U & Record<string, any> : U, LangGraphRunnableConfig<StateType<C>>>, options?: StateGraphAddNodeOptions): StateGraph<SD, S, U, N | K, I, O, C>;
119
119
  addEdge(startKey: typeof START | N | N[], endKey: N | typeof END): this;
120
- compile({ checkpointer, store, interruptBefore, interruptAfter, }?: {
120
+ compile({ checkpointer, store, interruptBefore, interruptAfter, name, }?: {
121
121
  checkpointer?: BaseCheckpointSaver | false;
122
122
  store?: BaseStore;
123
123
  interruptBefore?: N[] | All;
124
124
  interruptAfter?: N[] | All;
125
+ name?: string;
125
126
  }): CompiledStateGraph<S, U, N, I, O, C>;
126
127
  }
127
128
  /**
@@ -267,7 +267,7 @@ export class StateGraph extends Graph {
267
267
  this.waitingEdges.add([startKey, endKey]);
268
268
  return this;
269
269
  }
270
- compile({ checkpointer, store, interruptBefore, interruptAfter, } = {}) {
270
+ compile({ checkpointer, store, interruptBefore, interruptAfter, name, } = {}) {
271
271
  // validate the graph
272
272
  this.validate([
273
273
  ...(Array.isArray(interruptBefore) ? interruptBefore : []),
@@ -295,6 +295,7 @@ export class StateGraph extends Graph {
295
295
  streamChannels,
296
296
  streamMode: "updates",
297
297
  store,
298
+ name,
298
299
  });
299
300
  // attach nodes, edges and branches
300
301
  compiled.attachNode(START);
@@ -8,7 +8,7 @@ const tool_node_js_1 = require("./tool_node.cjs");
8
8
  const annotation_js_1 = require("../graph/annotation.cjs");
9
9
  const message_js_1 = require("../graph/message.cjs");
10
10
  const constants_js_1 = require("../constants.cjs");
11
- function _convertMessageModifierToStateModifier(messageModifier) {
11
+ function _convertMessageModifierToPrompt(messageModifier) {
12
12
  // Handle string or SystemMessage
13
13
  if (typeof messageModifier === "string" ||
14
14
  ((0, messages_1.isBaseMessage)(messageModifier) && messageModifier._getType() === "system")) {
@@ -24,48 +24,99 @@ function _convertMessageModifierToStateModifier(messageModifier) {
24
24
  }
25
25
  throw new Error(`Unexpected type for messageModifier: ${typeof messageModifier}`);
26
26
  }
27
- function _getStateModifierRunnable(stateModifier) {
28
- let stateModifierRunnable;
29
- if (stateModifier == null) {
30
- stateModifierRunnable = runnables_1.RunnableLambda.from((state) => state.messages).withConfig({ runName: "state_modifier" });
31
- }
32
- else if (typeof stateModifier === "string") {
33
- const systemMessage = new messages_1.SystemMessage(stateModifier);
34
- stateModifierRunnable = runnables_1.RunnableLambda.from((state) => {
27
+ const PROMPT_RUNNABLE_NAME = "prompt";
28
+ function _getPromptRunnable(prompt) {
29
+ let promptRunnable;
30
+ if (prompt == null) {
31
+ promptRunnable = runnables_1.RunnableLambda.from((state) => state.messages).withConfig({ runName: PROMPT_RUNNABLE_NAME });
32
+ }
33
+ else if (typeof prompt === "string") {
34
+ const systemMessage = new messages_1.SystemMessage(prompt);
35
+ promptRunnable = runnables_1.RunnableLambda.from((state) => {
35
36
  return [systemMessage, ...(state.messages ?? [])];
36
- }).withConfig({ runName: "state_modifier" });
37
- }
38
- else if ((0, messages_1.isBaseMessage)(stateModifier) &&
39
- stateModifier._getType() === "system") {
40
- stateModifierRunnable = runnables_1.RunnableLambda.from((state) => [
41
- stateModifier,
42
- ...state.messages,
43
- ]).withConfig({ runName: "state_modifier" });
44
- }
45
- else if (typeof stateModifier === "function") {
46
- stateModifierRunnable = runnables_1.RunnableLambda.from(stateModifier).withConfig({
47
- runName: "state_modifier",
37
+ }).withConfig({ runName: PROMPT_RUNNABLE_NAME });
38
+ }
39
+ else if ((0, messages_1.isBaseMessage)(prompt) && prompt._getType() === "system") {
40
+ promptRunnable = runnables_1.RunnableLambda.from((state) => [prompt, ...state.messages]).withConfig({ runName: PROMPT_RUNNABLE_NAME });
41
+ }
42
+ else if (typeof prompt === "function") {
43
+ promptRunnable = runnables_1.RunnableLambda.from(prompt).withConfig({
44
+ runName: PROMPT_RUNNABLE_NAME,
48
45
  });
49
46
  }
50
- else if (runnables_1.Runnable.isRunnable(stateModifier)) {
51
- stateModifierRunnable = stateModifier;
47
+ else if (runnables_1.Runnable.isRunnable(prompt)) {
48
+ promptRunnable = prompt;
52
49
  }
53
50
  else {
54
- throw new Error(`Got unexpected type for 'stateModifier': ${typeof stateModifier}`);
51
+ throw new Error(`Got unexpected type for 'prompt': ${typeof prompt}`);
52
+ }
53
+ return promptRunnable;
54
+ }
55
+ function _getPrompt(prompt, stateModifier, messageModifier) {
56
+ // Check if multiple modifiers exist
57
+ const definedCount = [prompt, stateModifier, messageModifier].filter((x) => x != null).length;
58
+ if (definedCount > 1) {
59
+ throw new Error("Expected only one of prompt, stateModifier, or messageModifier, got multiple values");
60
+ }
61
+ let finalPrompt = prompt;
62
+ if (stateModifier != null) {
63
+ finalPrompt = stateModifier;
64
+ }
65
+ else if (messageModifier != null) {
66
+ finalPrompt = _convertMessageModifierToPrompt(messageModifier);
67
+ }
68
+ return _getPromptRunnable(finalPrompt);
69
+ }
70
+ function _shouldBindTools(llm, tools) {
71
+ if (!runnables_1.Runnable.isRunnable(llm) || !("kwargs" in llm)) {
72
+ return true;
73
+ }
74
+ if (!llm.kwargs ||
75
+ typeof llm.kwargs !== "object" ||
76
+ !("tools" in llm.kwargs)) {
77
+ return true;
55
78
  }
56
- return stateModifierRunnable;
79
+ const boundTools = llm.kwargs.tools;
80
+ if (tools.length !== boundTools.length) {
81
+ throw new Error("Number of tools in the model.bindTools() and tools passed to createReactAgent must match");
82
+ }
83
+ const toolNames = new Set(tools.map((tool) => tool.name));
84
+ const boundToolNames = new Set();
85
+ for (const boundTool of boundTools) {
86
+ let boundToolName;
87
+ // OpenAI-style tool
88
+ if ("type" in boundTool && boundTool.type === "function") {
89
+ boundToolName = boundTool.function.name;
90
+ }
91
+ // Anthropic-style tool
92
+ else if ("name" in boundTool) {
93
+ boundToolName = boundTool.name;
94
+ }
95
+ // unknown tool type so we'll ignore it
96
+ else {
97
+ continue;
98
+ }
99
+ boundToolNames.add(boundToolName);
100
+ }
101
+ const missingTools = [...toolNames].filter((x) => !boundToolNames.has(x));
102
+ if (missingTools.length > 0) {
103
+ throw new Error(`Missing tools '${missingTools}' in the model.bindTools().` +
104
+ `Tools in the model.bindTools() must match the tools passed to createReactAgent.`);
105
+ }
106
+ return false;
57
107
  }
58
- function _getModelPreprocessingRunnable(stateModifier, messageModifier) {
59
- // Check if both modifiers exist
60
- if (stateModifier != null && messageModifier != null) {
61
- throw new Error("Expected value for either stateModifier or messageModifier, got values for both");
108
+ function _getModel(llm) {
109
+ // Get the underlying model from a RunnableBinding or return the model itself
110
+ let model = llm;
111
+ if (runnables_1.Runnable.isRunnable(llm) && "bound" in llm) {
112
+ model = llm.bound;
62
113
  }
63
- // Convert message modifier to state modifier if necessary
64
- if (stateModifier == null && messageModifier != null) {
65
- // eslint-disable-next-line no-param-reassign
66
- stateModifier = _convertMessageModifierToStateModifier(messageModifier);
114
+ if (!("invoke" in model &&
115
+ typeof model.invoke === "function" &&
116
+ "_modelType" in model)) {
117
+ throw new Error(`Expected \`llm\` to be a ChatModel or RunnableBinding (e.g. llm.bind_tools(...)) with invoke() and generate() methods, got ${model.constructor.name}`);
67
118
  }
68
- return _getStateModifierRunnable(stateModifier);
119
+ return model;
69
120
  }
70
121
  const createReactAgentAnnotation = () => annotation_js_1.Annotation.Root({
71
122
  messages: (0, annotation_js_1.Annotation)({
@@ -118,7 +169,7 @@ exports.createReactAgentAnnotation = createReactAgentAnnotation;
118
169
  * ```
119
170
  */
120
171
  function createReactAgent(params) {
121
- const { llm, tools, messageModifier, stateModifier, stateSchema, checkpointSaver, checkpointer, interruptBefore, interruptAfter, store, responseFormat, } = params;
172
+ const { llm, tools, messageModifier, stateModifier, prompt, stateSchema, checkpointSaver, checkpointer, interruptBefore, interruptAfter, store, responseFormat, name, } = params;
122
173
  let toolClasses;
123
174
  if (!Array.isArray(tools)) {
124
175
  toolClasses = tools.tools;
@@ -126,13 +177,17 @@ function createReactAgent(params) {
126
177
  else {
127
178
  toolClasses = tools;
128
179
  }
129
- if (!("bindTools" in llm) || typeof llm.bindTools !== "function") {
130
- throw new Error(`llm ${llm} must define bindTools method.`);
180
+ let modelWithTools;
181
+ if (_shouldBindTools(llm, toolClasses)) {
182
+ if (!("bindTools" in llm) || typeof llm.bindTools !== "function") {
183
+ throw new Error(`llm ${llm} must define bindTools method.`);
184
+ }
185
+ modelWithTools = llm.bindTools(toolClasses);
186
+ }
187
+ else {
188
+ modelWithTools = llm;
131
189
  }
132
- const modelWithTools = llm.bindTools(toolClasses);
133
- // we're passing store here for validation
134
- const preprocessor = _getModelPreprocessingRunnable(stateModifier, messageModifier);
135
- const modelRunnable = preprocessor.pipe(modelWithTools);
190
+ const modelRunnable = _getPrompt(prompt, stateModifier, messageModifier).pipe(modelWithTools);
136
191
  // If any of the tools are configured to return_directly after running,
137
192
  // our graph needs to check if these were called
138
193
  const shouldReturnDirect = new Set(toolClasses
@@ -161,11 +216,12 @@ function createReactAgent(params) {
161
216
  "prompt" in responseFormat &&
162
217
  "schema" in responseFormat) {
163
218
  const { prompt, schema } = responseFormat;
164
- modelWithStructuredOutput = llm.withStructuredOutput(schema);
219
+ modelWithStructuredOutput = _getModel(llm).withStructuredOutput(schema);
165
220
  messages.unshift(new messages_1.SystemMessage({ content: prompt }));
166
221
  }
167
222
  else {
168
- modelWithStructuredOutput = llm.withStructuredOutput(responseFormat);
223
+ modelWithStructuredOutput =
224
+ _getModel(llm).withStructuredOutput(responseFormat);
169
225
  }
170
226
  const response = await modelWithStructuredOutput.invoke(messages, config);
171
227
  return { structuredResponse: response };
@@ -219,6 +275,7 @@ function createReactAgent(params) {
219
275
  interruptBefore,
220
276
  interruptAfter,
221
277
  store,
278
+ name,
222
279
  });
223
280
  }
224
281
  exports.createReactAgent = createReactAgent;
@@ -1,7 +1,7 @@
1
- import { BaseChatModel } from "@langchain/core/language_models/chat_models";
1
+ import { LanguageModelLike } from "@langchain/core/language_models/base";
2
2
  import { BaseMessage, BaseMessageLike, SystemMessage } from "@langchain/core/messages";
3
3
  import { Runnable, RunnableToolLike } from "@langchain/core/runnables";
4
- import { StructuredToolInterface } from "@langchain/core/tools";
4
+ import { DynamicTool, StructuredToolInterface } from "@langchain/core/tools";
5
5
  import { All, BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
6
6
  import { z } from "zod";
7
7
  import { CompiledStateGraph, AnnotationRoot } from "../graph/index.js";
@@ -19,8 +19,10 @@ export type StructuredResponseSchemaAndPrompt<StructuredResponseType> = {
19
19
  prompt: string;
20
20
  schema: z.ZodType<StructuredResponseType> | Record<string, any>;
21
21
  };
22
- export type StateModifier = SystemMessage | string | ((state: typeof MessagesAnnotation.State, config: LangGraphRunnableConfig) => BaseMessageLike[]) | ((state: typeof MessagesAnnotation.State, config: LangGraphRunnableConfig) => Promise<BaseMessageLike[]>) | Runnable;
23
- /** @deprecated Use StateModifier instead. */
22
+ export type Prompt = SystemMessage | string | ((state: typeof MessagesAnnotation.State, config: LangGraphRunnableConfig) => BaseMessageLike[]) | ((state: typeof MessagesAnnotation.State, config: LangGraphRunnableConfig) => Promise<BaseMessageLike[]>) | Runnable;
23
+ /** @deprecated Use Prompt instead. */
24
+ export type StateModifier = Prompt;
25
+ /** @deprecated Use Prompt instead. */
24
26
  export type MessageModifier = SystemMessage | string | ((messages: BaseMessage[]) => BaseMessage[]) | ((messages: BaseMessage[]) => Promise<BaseMessage[]>) | Runnable;
25
27
  export declare const createReactAgentAnnotation: <T extends Record<string, any> = Record<string, any>>() => AnnotationRoot<{
26
28
  messages: import("../web.js").BinaryOperatorAggregate<BaseMessage[], Messages>;
@@ -32,63 +34,32 @@ export declare const createReactAgentAnnotation: <T extends Record<string, any>
32
34
  }>;
33
35
  export type CreateReactAgentParams<A extends AnnotationRoot<any> = AnnotationRoot<any>, StructuredResponseType = Record<string, any>> = {
34
36
  /** The chat model that can utilize OpenAI-style tool calling. */
35
- llm: BaseChatModel;
37
+ llm: LanguageModelLike;
36
38
  /** A list of tools or a ToolNode. */
37
- tools: ToolNode | (StructuredToolInterface | RunnableToolLike)[];
39
+ tools: ToolNode | (StructuredToolInterface | DynamicTool | RunnableToolLike)[];
38
40
  /**
39
- * @deprecated
40
- * Use stateModifier instead. stateModifier works the same as
41
- * messageModifier in that it runs right before calling the chat model,
42
- * but if passed as a function, it takes the full graph state as
43
- * input whenever a tool is called rather than a list of messages.
44
- *
45
- * If a function is passed, it should return a list of messages to
46
- * pass directly to the chat model.
47
- *
48
- * @example
49
- * ```ts
50
- * import { ChatOpenAI } from "@langchain/openai";
51
- * import { MessagesAnnotation } from "@langchain/langgraph";
52
- * import { createReactAgent } from "@langchain/langgraph/prebuilt";
53
- * import { type BaseMessage, SystemMessage } from "@langchain/core/messages";
54
- *
55
- * const model = new ChatOpenAI({
56
- * model: "gpt-4o-mini",
57
- * });
58
- *
59
- * const tools = [...];
60
- *
61
- * // Deprecated style with messageModifier
62
- * const deprecated = createReactAgent({
63
- * llm,
64
- * tools,
65
- * messageModifier: async (messages: BaseMessage[]) => {
66
- * return [new SystemMessage("You are a pirate")].concat(messages);
67
- * }
68
- * });
69
- *
70
- * // New style with stateModifier
71
- * const agent = createReactAgent({
72
- * llm,
73
- * tools,
74
- * stateModifier: async (state: typeof MessagesAnnotation.State) => {
75
- * return [new SystemMessage("You are a pirate.")].concat(messages);
76
- * }
77
- * });
78
- * ```
41
+ * @deprecated Use prompt instead.
79
42
  */
80
43
  messageModifier?: MessageModifier;
81
44
  /**
82
- * An optional state modifier. This takes full graph state BEFORE the LLM is called and prepares the input to LLM.
45
+ * @deprecated Use prompt instead.
46
+ */
47
+ stateModifier?: StateModifier;
48
+ /**
49
+ * An optional prompt for the LLM. This takes full graph state BEFORE the LLM is called and prepares the input to LLM.
83
50
  *
84
51
  * Can take a few different forms:
85
52
  *
86
- * - SystemMessage: this is added to the beginning of the list of messages in state["messages"].
87
53
  * - str: This is converted to a SystemMessage and added to the beginning of the list of messages in state["messages"].
54
+ * - SystemMessage: this is added to the beginning of the list of messages in state["messages"].
88
55
  * - Function: This function should take in full graph state and the output is then passed to the language model.
89
56
  * - Runnable: This runnable should take in full graph state and the output is then passed to the language model.
57
+ *
58
+ * Note:
59
+ * Prior to `v0.2.46`, the prompt was set using `stateModifier` / `messagesModifier` parameters.
60
+ * This is now deprecated and will be removed in a future release.
90
61
  */
91
- stateModifier?: StateModifier;
62
+ prompt?: Prompt;
92
63
  stateSchema?: A;
93
64
  /** An optional checkpoint saver to persist the agent's state. */
94
65
  checkpointSaver?: BaseCheckpointSaver;
@@ -112,6 +83,7 @@ export type CreateReactAgentParams<A extends AnnotationRoot<any> = AnnotationRoo
112
83
  * The prompt will be used together with the model that is being used to generate the structured response.
113
84
  */
114
85
  responseFormat?: z.ZodType<StructuredResponseType> | StructuredResponseSchemaAndPrompt<StructuredResponseType> | Record<string, any>;
86
+ name?: string;
115
87
  };
116
88
  /**
117
89
  * Creates a StateGraph agent that relies on a chat model utilizing tool calling.
@@ -5,7 +5,7 @@ import { ToolNode } from "./tool_node.js";
5
5
  import { Annotation } from "../graph/annotation.js";
6
6
  import { messagesStateReducer } from "../graph/message.js";
7
7
  import { END, START } from "../constants.js";
8
- function _convertMessageModifierToStateModifier(messageModifier) {
8
+ function _convertMessageModifierToPrompt(messageModifier) {
9
9
  // Handle string or SystemMessage
10
10
  if (typeof messageModifier === "string" ||
11
11
  (isBaseMessage(messageModifier) && messageModifier._getType() === "system")) {
@@ -21,48 +21,99 @@ function _convertMessageModifierToStateModifier(messageModifier) {
21
21
  }
22
22
  throw new Error(`Unexpected type for messageModifier: ${typeof messageModifier}`);
23
23
  }
24
- function _getStateModifierRunnable(stateModifier) {
25
- let stateModifierRunnable;
26
- if (stateModifier == null) {
27
- stateModifierRunnable = RunnableLambda.from((state) => state.messages).withConfig({ runName: "state_modifier" });
28
- }
29
- else if (typeof stateModifier === "string") {
30
- const systemMessage = new SystemMessage(stateModifier);
31
- stateModifierRunnable = RunnableLambda.from((state) => {
24
+ const PROMPT_RUNNABLE_NAME = "prompt";
25
+ function _getPromptRunnable(prompt) {
26
+ let promptRunnable;
27
+ if (prompt == null) {
28
+ promptRunnable = RunnableLambda.from((state) => state.messages).withConfig({ runName: PROMPT_RUNNABLE_NAME });
29
+ }
30
+ else if (typeof prompt === "string") {
31
+ const systemMessage = new SystemMessage(prompt);
32
+ promptRunnable = RunnableLambda.from((state) => {
32
33
  return [systemMessage, ...(state.messages ?? [])];
33
- }).withConfig({ runName: "state_modifier" });
34
- }
35
- else if (isBaseMessage(stateModifier) &&
36
- stateModifier._getType() === "system") {
37
- stateModifierRunnable = RunnableLambda.from((state) => [
38
- stateModifier,
39
- ...state.messages,
40
- ]).withConfig({ runName: "state_modifier" });
41
- }
42
- else if (typeof stateModifier === "function") {
43
- stateModifierRunnable = RunnableLambda.from(stateModifier).withConfig({
44
- runName: "state_modifier",
34
+ }).withConfig({ runName: PROMPT_RUNNABLE_NAME });
35
+ }
36
+ else if (isBaseMessage(prompt) && prompt._getType() === "system") {
37
+ promptRunnable = RunnableLambda.from((state) => [prompt, ...state.messages]).withConfig({ runName: PROMPT_RUNNABLE_NAME });
38
+ }
39
+ else if (typeof prompt === "function") {
40
+ promptRunnable = RunnableLambda.from(prompt).withConfig({
41
+ runName: PROMPT_RUNNABLE_NAME,
45
42
  });
46
43
  }
47
- else if (Runnable.isRunnable(stateModifier)) {
48
- stateModifierRunnable = stateModifier;
44
+ else if (Runnable.isRunnable(prompt)) {
45
+ promptRunnable = prompt;
49
46
  }
50
47
  else {
51
- throw new Error(`Got unexpected type for 'stateModifier': ${typeof stateModifier}`);
48
+ throw new Error(`Got unexpected type for 'prompt': ${typeof prompt}`);
49
+ }
50
+ return promptRunnable;
51
+ }
52
+ function _getPrompt(prompt, stateModifier, messageModifier) {
53
+ // Check if multiple modifiers exist
54
+ const definedCount = [prompt, stateModifier, messageModifier].filter((x) => x != null).length;
55
+ if (definedCount > 1) {
56
+ throw new Error("Expected only one of prompt, stateModifier, or messageModifier, got multiple values");
57
+ }
58
+ let finalPrompt = prompt;
59
+ if (stateModifier != null) {
60
+ finalPrompt = stateModifier;
61
+ }
62
+ else if (messageModifier != null) {
63
+ finalPrompt = _convertMessageModifierToPrompt(messageModifier);
64
+ }
65
+ return _getPromptRunnable(finalPrompt);
66
+ }
67
+ function _shouldBindTools(llm, tools) {
68
+ if (!Runnable.isRunnable(llm) || !("kwargs" in llm)) {
69
+ return true;
70
+ }
71
+ if (!llm.kwargs ||
72
+ typeof llm.kwargs !== "object" ||
73
+ !("tools" in llm.kwargs)) {
74
+ return true;
52
75
  }
53
- return stateModifierRunnable;
76
+ const boundTools = llm.kwargs.tools;
77
+ if (tools.length !== boundTools.length) {
78
+ throw new Error("Number of tools in the model.bindTools() and tools passed to createReactAgent must match");
79
+ }
80
+ const toolNames = new Set(tools.map((tool) => tool.name));
81
+ const boundToolNames = new Set();
82
+ for (const boundTool of boundTools) {
83
+ let boundToolName;
84
+ // OpenAI-style tool
85
+ if ("type" in boundTool && boundTool.type === "function") {
86
+ boundToolName = boundTool.function.name;
87
+ }
88
+ // Anthropic-style tool
89
+ else if ("name" in boundTool) {
90
+ boundToolName = boundTool.name;
91
+ }
92
+ // unknown tool type so we'll ignore it
93
+ else {
94
+ continue;
95
+ }
96
+ boundToolNames.add(boundToolName);
97
+ }
98
+ const missingTools = [...toolNames].filter((x) => !boundToolNames.has(x));
99
+ if (missingTools.length > 0) {
100
+ throw new Error(`Missing tools '${missingTools}' in the model.bindTools().` +
101
+ `Tools in the model.bindTools() must match the tools passed to createReactAgent.`);
102
+ }
103
+ return false;
54
104
  }
55
- function _getModelPreprocessingRunnable(stateModifier, messageModifier) {
56
- // Check if both modifiers exist
57
- if (stateModifier != null && messageModifier != null) {
58
- throw new Error("Expected value for either stateModifier or messageModifier, got values for both");
105
+ function _getModel(llm) {
106
+ // Get the underlying model from a RunnableBinding or return the model itself
107
+ let model = llm;
108
+ if (Runnable.isRunnable(llm) && "bound" in llm) {
109
+ model = llm.bound;
59
110
  }
60
- // Convert message modifier to state modifier if necessary
61
- if (stateModifier == null && messageModifier != null) {
62
- // eslint-disable-next-line no-param-reassign
63
- stateModifier = _convertMessageModifierToStateModifier(messageModifier);
111
+ if (!("invoke" in model &&
112
+ typeof model.invoke === "function" &&
113
+ "_modelType" in model)) {
114
+ throw new Error(`Expected \`llm\` to be a ChatModel or RunnableBinding (e.g. llm.bind_tools(...)) with invoke() and generate() methods, got ${model.constructor.name}`);
64
115
  }
65
- return _getStateModifierRunnable(stateModifier);
116
+ return model;
66
117
  }
67
118
  export const createReactAgentAnnotation = () => Annotation.Root({
68
119
  messages: Annotation({
@@ -114,7 +165,7 @@ export const createReactAgentAnnotation = () => Annotation.Root({
114
165
  * ```
115
166
  */
116
167
  export function createReactAgent(params) {
117
- const { llm, tools, messageModifier, stateModifier, stateSchema, checkpointSaver, checkpointer, interruptBefore, interruptAfter, store, responseFormat, } = params;
168
+ const { llm, tools, messageModifier, stateModifier, prompt, stateSchema, checkpointSaver, checkpointer, interruptBefore, interruptAfter, store, responseFormat, name, } = params;
118
169
  let toolClasses;
119
170
  if (!Array.isArray(tools)) {
120
171
  toolClasses = tools.tools;
@@ -122,13 +173,17 @@ export function createReactAgent(params) {
122
173
  else {
123
174
  toolClasses = tools;
124
175
  }
125
- if (!("bindTools" in llm) || typeof llm.bindTools !== "function") {
126
- throw new Error(`llm ${llm} must define bindTools method.`);
176
+ let modelWithTools;
177
+ if (_shouldBindTools(llm, toolClasses)) {
178
+ if (!("bindTools" in llm) || typeof llm.bindTools !== "function") {
179
+ throw new Error(`llm ${llm} must define bindTools method.`);
180
+ }
181
+ modelWithTools = llm.bindTools(toolClasses);
182
+ }
183
+ else {
184
+ modelWithTools = llm;
127
185
  }
128
- const modelWithTools = llm.bindTools(toolClasses);
129
- // we're passing store here for validation
130
- const preprocessor = _getModelPreprocessingRunnable(stateModifier, messageModifier);
131
- const modelRunnable = preprocessor.pipe(modelWithTools);
186
+ const modelRunnable = _getPrompt(prompt, stateModifier, messageModifier).pipe(modelWithTools);
132
187
  // If any of the tools are configured to return_directly after running,
133
188
  // our graph needs to check if these were called
134
189
  const shouldReturnDirect = new Set(toolClasses
@@ -157,11 +212,12 @@ export function createReactAgent(params) {
157
212
  "prompt" in responseFormat &&
158
213
  "schema" in responseFormat) {
159
214
  const { prompt, schema } = responseFormat;
160
- modelWithStructuredOutput = llm.withStructuredOutput(schema);
215
+ modelWithStructuredOutput = _getModel(llm).withStructuredOutput(schema);
161
216
  messages.unshift(new SystemMessage({ content: prompt }));
162
217
  }
163
218
  else {
164
- modelWithStructuredOutput = llm.withStructuredOutput(responseFormat);
219
+ modelWithStructuredOutput =
220
+ _getModel(llm).withStructuredOutput(responseFormat);
165
221
  }
166
222
  const response = await modelWithStructuredOutput.invoke(messages, config);
167
223
  return { structuredResponse: response };
@@ -215,5 +271,6 @@ export function createReactAgent(params) {
215
271
  interruptBefore,
216
272
  interruptAfter,
217
273
  store,
274
+ name,
218
275
  });
219
276
  }
@@ -208,6 +208,7 @@ class Pregel extends runnables_1.Runnable {
208
208
  this.retryPolicy = fields.retryPolicy;
209
209
  this.config = fields.config;
210
210
  this.store = fields.store;
211
+ this.name = fields.name;
211
212
  if (this.autoValidate) {
212
213
  this.validate();
213
214
  }
@@ -204,6 +204,7 @@ export class Pregel extends Runnable {
204
204
  this.retryPolicy = fields.retryPolicy;
205
205
  this.config = fields.config;
206
206
  this.store = fields.store;
207
+ this.name = fields.name;
207
208
  if (this.autoValidate) {
208
209
  this.validate();
209
210
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StreamMessagesHandler = void 0;
4
- const uuid_1 = require("uuid");
5
4
  const base_1 = require("@langchain/core/callbacks/base");
6
5
  const messages_1 = require("@langchain/core/messages");
7
6
  const constants_js_1 = require("../constants.cjs");
@@ -47,6 +46,12 @@ class StreamMessagesHandler extends base_1.BaseCallbackHandler {
47
46
  writable: true,
48
47
  value: {}
49
48
  });
49
+ Object.defineProperty(this, "stableMessageIdMap", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: {}
54
+ });
50
55
  Object.defineProperty(this, "lc_prefer_streaming", {
51
56
  enumerable: true,
52
57
  configurable: true,
@@ -55,18 +60,26 @@ class StreamMessagesHandler extends base_1.BaseCallbackHandler {
55
60
  });
56
61
  this.streamFn = streamFn;
57
62
  }
58
- _emit(meta, message, dedupe = false) {
63
+ _emit(meta, message, runId, dedupe = false) {
59
64
  if (dedupe &&
60
65
  message.id !== undefined &&
61
66
  this.seen[message.id] !== undefined) {
62
67
  return;
63
68
  }
64
- if (message.id === undefined) {
65
- const id = (0, uuid_1.v4)();
69
+ // For instance in ChatAnthropic, the first chunk has an message ID
70
+ // but the subsequent chunks do not. To avoid clients seeing two messages
71
+ // we rename the message ID if it's being auto-set to `run-${runId}`
72
+ // (see https://github.com/langchain-ai/langchainjs/pull/6646).
73
+ let messageId = message.id;
74
+ if (messageId == null || messageId === `run-${runId}`) {
75
+ messageId = this.stableMessageIdMap[runId] ?? messageId ?? `run-${runId}`;
76
+ }
77
+ this.stableMessageIdMap[runId] ??= messageId;
78
+ if (messageId !== message.id) {
66
79
  // eslint-disable-next-line no-param-reassign
67
- message.id = id;
80
+ message.id = messageId;
68
81
  // eslint-disable-next-line no-param-reassign
69
- message.lc_kwargs.id = id;
82
+ message.lc_kwargs.id = messageId;
70
83
  }
71
84
  this.seen[message.id] = message;
72
85
  this.streamFn([meta[0], "messages", [message, meta[1]]]);
@@ -86,12 +99,10 @@ class StreamMessagesHandler extends base_1.BaseCallbackHandler {
86
99
  this.emittedChatModelRunIds[runId] = true;
87
100
  if (this.metadatas[runId] !== undefined) {
88
101
  if (isChatGenerationChunk(chunk)) {
89
- this._emit(this.metadatas[runId], chunk.message);
102
+ this._emit(this.metadatas[runId], chunk.message, runId);
90
103
  }
91
104
  else {
92
- this._emit(this.metadatas[runId], new messages_1.AIMessageChunk({
93
- content: token,
94
- }));
105
+ this._emit(this.metadatas[runId], new messages_1.AIMessageChunk({ content: token }), runId);
95
106
  }
96
107
  }
97
108
  }
@@ -100,11 +111,12 @@ class StreamMessagesHandler extends base_1.BaseCallbackHandler {
100
111
  if (!this.emittedChatModelRunIds[runId]) {
101
112
  const chatGeneration = output.generations?.[0]?.[0];
102
113
  if ((0, messages_1.isBaseMessage)(chatGeneration?.message)) {
103
- this._emit(this.metadatas[runId], chatGeneration?.message, true);
114
+ this._emit(this.metadatas[runId], chatGeneration?.message, runId, true);
104
115
  }
105
116
  delete this.emittedChatModelRunIds[runId];
106
117
  }
107
118
  delete this.metadatas[runId];
119
+ delete this.stableMessageIdMap[runId];
108
120
  }
109
121
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
122
  handleLLMError(_err, runId) {
@@ -125,24 +137,24 @@ class StreamMessagesHandler extends base_1.BaseCallbackHandler {
125
137
  delete this.metadatas[runId];
126
138
  if (metadata !== undefined) {
127
139
  if ((0, messages_1.isBaseMessage)(outputs)) {
128
- this._emit(metadata, outputs, true);
140
+ this._emit(metadata, outputs, runId, true);
129
141
  }
130
142
  else if (Array.isArray(outputs)) {
131
143
  for (const value of outputs) {
132
144
  if ((0, messages_1.isBaseMessage)(value)) {
133
- this._emit(metadata, value, true);
145
+ this._emit(metadata, value, runId, true);
134
146
  }
135
147
  }
136
148
  }
137
149
  else if (outputs != null && typeof outputs === "object") {
138
150
  for (const value of Object.values(outputs)) {
139
151
  if ((0, messages_1.isBaseMessage)(value)) {
140
- this._emit(metadata, value, true);
152
+ this._emit(metadata, value, runId, true);
141
153
  }
142
154
  else if (Array.isArray(value)) {
143
155
  for (const item of value) {
144
156
  if ((0, messages_1.isBaseMessage)(item)) {
145
- this._emit(metadata, item, true);
157
+ this._emit(metadata, item, runId, true);
146
158
  }
147
159
  }
148
160
  }
@@ -15,9 +15,10 @@ export declare class StreamMessagesHandler extends BaseCallbackHandler {
15
15
  metadatas: Record<string, Meta>;
16
16
  seen: Record<string, BaseMessage>;
17
17
  emittedChatModelRunIds: Record<string, boolean>;
18
+ stableMessageIdMap: Record<string, string>;
18
19
  lc_prefer_streaming: boolean;
19
20
  constructor(streamFn: (streamChunk: StreamChunk) => void);
20
- _emit(meta: Meta, message: BaseMessage, dedupe?: boolean): void;
21
+ _emit(meta: Meta, message: BaseMessage, runId: string, dedupe?: boolean): void;
21
22
  handleChatModelStart(_llm: Serialized, _messages: BaseMessage[][], runId: string, _parentRunId?: string, _extraParams?: Record<string, unknown>, tags?: string[], metadata?: Record<string, unknown>, name?: string): void;
22
23
  handleLLMNewToken(token: string, _idx: NewTokenIndices, runId: string, _parentRunId?: string, _tags?: string[], fields?: HandleLLMNewTokenCallbackFields): void;
23
24
  handleLLMEnd(output: LLMResult, runId: string): void;
@@ -1,4 +1,3 @@
1
- import { v4 } from "uuid";
2
1
  import { BaseCallbackHandler, } from "@langchain/core/callbacks/base";
3
2
  import { AIMessageChunk, isBaseMessage, } from "@langchain/core/messages";
4
3
  import { TAG_HIDDEN, TAG_NOSTREAM } from "../constants.js";
@@ -44,6 +43,12 @@ export class StreamMessagesHandler extends BaseCallbackHandler {
44
43
  writable: true,
45
44
  value: {}
46
45
  });
46
+ Object.defineProperty(this, "stableMessageIdMap", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: {}
51
+ });
47
52
  Object.defineProperty(this, "lc_prefer_streaming", {
48
53
  enumerable: true,
49
54
  configurable: true,
@@ -52,18 +57,26 @@ export class StreamMessagesHandler extends BaseCallbackHandler {
52
57
  });
53
58
  this.streamFn = streamFn;
54
59
  }
55
- _emit(meta, message, dedupe = false) {
60
+ _emit(meta, message, runId, dedupe = false) {
56
61
  if (dedupe &&
57
62
  message.id !== undefined &&
58
63
  this.seen[message.id] !== undefined) {
59
64
  return;
60
65
  }
61
- if (message.id === undefined) {
62
- const id = v4();
66
+ // For instance in ChatAnthropic, the first chunk has an message ID
67
+ // but the subsequent chunks do not. To avoid clients seeing two messages
68
+ // we rename the message ID if it's being auto-set to `run-${runId}`
69
+ // (see https://github.com/langchain-ai/langchainjs/pull/6646).
70
+ let messageId = message.id;
71
+ if (messageId == null || messageId === `run-${runId}`) {
72
+ messageId = this.stableMessageIdMap[runId] ?? messageId ?? `run-${runId}`;
73
+ }
74
+ this.stableMessageIdMap[runId] ??= messageId;
75
+ if (messageId !== message.id) {
63
76
  // eslint-disable-next-line no-param-reassign
64
- message.id = id;
77
+ message.id = messageId;
65
78
  // eslint-disable-next-line no-param-reassign
66
- message.lc_kwargs.id = id;
79
+ message.lc_kwargs.id = messageId;
67
80
  }
68
81
  this.seen[message.id] = message;
69
82
  this.streamFn([meta[0], "messages", [message, meta[1]]]);
@@ -83,12 +96,10 @@ export class StreamMessagesHandler extends BaseCallbackHandler {
83
96
  this.emittedChatModelRunIds[runId] = true;
84
97
  if (this.metadatas[runId] !== undefined) {
85
98
  if (isChatGenerationChunk(chunk)) {
86
- this._emit(this.metadatas[runId], chunk.message);
99
+ this._emit(this.metadatas[runId], chunk.message, runId);
87
100
  }
88
101
  else {
89
- this._emit(this.metadatas[runId], new AIMessageChunk({
90
- content: token,
91
- }));
102
+ this._emit(this.metadatas[runId], new AIMessageChunk({ content: token }), runId);
92
103
  }
93
104
  }
94
105
  }
@@ -97,11 +108,12 @@ export class StreamMessagesHandler extends BaseCallbackHandler {
97
108
  if (!this.emittedChatModelRunIds[runId]) {
98
109
  const chatGeneration = output.generations?.[0]?.[0];
99
110
  if (isBaseMessage(chatGeneration?.message)) {
100
- this._emit(this.metadatas[runId], chatGeneration?.message, true);
111
+ this._emit(this.metadatas[runId], chatGeneration?.message, runId, true);
101
112
  }
102
113
  delete this.emittedChatModelRunIds[runId];
103
114
  }
104
115
  delete this.metadatas[runId];
116
+ delete this.stableMessageIdMap[runId];
105
117
  }
106
118
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
119
  handleLLMError(_err, runId) {
@@ -122,24 +134,24 @@ export class StreamMessagesHandler extends BaseCallbackHandler {
122
134
  delete this.metadatas[runId];
123
135
  if (metadata !== undefined) {
124
136
  if (isBaseMessage(outputs)) {
125
- this._emit(metadata, outputs, true);
137
+ this._emit(metadata, outputs, runId, true);
126
138
  }
127
139
  else if (Array.isArray(outputs)) {
128
140
  for (const value of outputs) {
129
141
  if (isBaseMessage(value)) {
130
- this._emit(metadata, value, true);
142
+ this._emit(metadata, value, runId, true);
131
143
  }
132
144
  }
133
145
  }
134
146
  else if (outputs != null && typeof outputs === "object") {
135
147
  for (const value of Object.values(outputs)) {
136
148
  if (isBaseMessage(value)) {
137
- this._emit(metadata, value, true);
149
+ this._emit(metadata, value, runId, true);
138
150
  }
139
151
  else if (Array.isArray(value)) {
140
152
  for (const item of value) {
141
153
  if (isBaseMessage(item)) {
142
- this._emit(metadata, item, true);
154
+ this._emit(metadata, item, runId, true);
143
155
  }
144
156
  }
145
157
  }
@@ -41,18 +41,13 @@ const getStreamModes = (streamMode, defaultStreamMode = "updates") => {
41
41
  if (streamMode !== undefined &&
42
42
  (typeof streamMode === "string" ||
43
43
  (Array.isArray(streamMode) && streamMode.length > 0))) {
44
- if (typeof streamMode === "string") {
45
- updatedStreamModes.push(streamMode);
46
- }
47
- else {
48
- reqSingle = false;
49
- updatedStreamModes.push(...streamMode);
50
- }
44
+ reqSingle = typeof streamMode === "string";
45
+ const mapped = Array.isArray(streamMode) ? streamMode : [streamMode];
46
+ updatedStreamModes.push(...mapped);
51
47
  }
52
48
  else {
53
49
  updatedStreamModes.push(defaultStreamMode);
54
50
  }
55
- // TODO: Map messages to messages-tuple
56
51
  if (updatedStreamModes.includes("updates")) {
57
52
  reqUpdates = true;
58
53
  }
@@ -303,7 +298,11 @@ class RemoteGraph extends runnables_1.Runnable {
303
298
  ...updatedStreamModes,
304
299
  ...(streamProtocolInstance?.modes ?? new Set()),
305
300
  ]),
306
- ];
301
+ ].map((mode) => {
302
+ if (mode === "messages")
303
+ return "messages-tuple";
304
+ return mode;
305
+ });
307
306
  let command;
308
307
  let serializedInput;
309
308
  if ((0, constants_js_1.isCommand)(input)) {
@@ -38,18 +38,13 @@ const getStreamModes = (streamMode, defaultStreamMode = "updates") => {
38
38
  if (streamMode !== undefined &&
39
39
  (typeof streamMode === "string" ||
40
40
  (Array.isArray(streamMode) && streamMode.length > 0))) {
41
- if (typeof streamMode === "string") {
42
- updatedStreamModes.push(streamMode);
43
- }
44
- else {
45
- reqSingle = false;
46
- updatedStreamModes.push(...streamMode);
47
- }
41
+ reqSingle = typeof streamMode === "string";
42
+ const mapped = Array.isArray(streamMode) ? streamMode : [streamMode];
43
+ updatedStreamModes.push(...mapped);
48
44
  }
49
45
  else {
50
46
  updatedStreamModes.push(defaultStreamMode);
51
47
  }
52
- // TODO: Map messages to messages-tuple
53
48
  if (updatedStreamModes.includes("updates")) {
54
49
  reqUpdates = true;
55
50
  }
@@ -300,7 +295,11 @@ export class RemoteGraph extends Runnable {
300
295
  ...updatedStreamModes,
301
296
  ...(streamProtocolInstance?.modes ?? new Set()),
302
297
  ]),
303
- ];
298
+ ].map((mode) => {
299
+ if (mode === "messages")
300
+ return "messages-tuple";
301
+ return mode;
302
+ });
304
303
  let command;
305
304
  let serializedInput;
306
305
  if (isCommand(input)) {
package/dist/web.d.ts CHANGED
@@ -5,7 +5,7 @@ export type { Pregel } from "./pregel/index.js";
5
5
  export * from "./errors.js";
6
6
  export { BaseChannel, type BinaryOperator, BinaryOperatorAggregate, type AnyValue, type WaitForNames, type DynamicBarrierValue, type LastValue, type NamedBarrierValue, type Topic, } from "./channels/index.js";
7
7
  export type { EphemeralValue } from "./channels/ephemeral_value.js";
8
- export { type AnnotationRoot as _INTERNAL_ANNOTATION_ROOT } from "./graph/index.js";
8
+ export { type AnnotationRoot } from "./graph/index.js";
9
9
  export { type RetryPolicy } from "./pregel/utils/index.js";
10
10
  export { Send, Command, type CommandParams, isCommand, START, END, type Interrupt, } from "./constants.js";
11
11
  export { MemorySaver, type Checkpoint, type CheckpointMetadata, type CheckpointTuple, copyCheckpoint, emptyCheckpoint, BaseCheckpointSaver, type Item, type GetOperation, type SearchOperation, type PutOperation, type Operation, type OperationResults, BaseStore, AsyncBatchedStore, InMemoryStore, type NameSpacePath, type NamespaceMatchType, type MatchCondition, type ListNamespacesOperation, } from "@langchain/langgraph-checkpoint";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph",
3
- "version": "0.2.44",
3
+ "version": "0.2.46",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {