From 421d2553d7711b13c9ed6b0ce5b3d6892d29e347 Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Fri, 23 Aug 2024 17:48:51 -0700 Subject: [PATCH] Update docstrings, improve API refs (#369) --- docs/docs/concepts/low_level.md | 5 - libs/checkpoint/src/base.ts | 2 + libs/langgraph/src/graph/annotation.ts | 99 +++++++++++++++++-- libs/langgraph/src/graph/graph.ts | 2 + libs/langgraph/src/graph/message.ts | 6 ++ .../src/graph/messages_annotation.ts | 5 + libs/langgraph/src/graph/state.ts | 62 ++++++++++++ libs/langgraph/src/prebuilt/agent_executor.ts | 2 + .../src/prebuilt/chat_agent_executor.ts | 2 + tsconfig.json | 2 + 10 files changed, 173 insertions(+), 14 deletions(-) diff --git a/docs/docs/concepts/low_level.md b/docs/docs/concepts/low_level.md index 762644bf..4904f12e 100644 --- a/docs/docs/concepts/low_level.md +++ b/docs/docs/concepts/low_level.md @@ -22,11 +22,6 @@ A super-step can be considered a single iteration over the graph nodes. Nodes th The `StateGraph` class is the main graph class to uses. This is parameterized by a user defined `State` object. (defined using the `Annotation` object and passed as the first argument) - -### MessageGraph - -The `MessageGraph` class is a special type of graph. The `State` of a `MessageGraph` is ONLY a list of messages. This class is rarely used except for chatbots, as most applications require the `State` to be more complex than a list of messages. - ### Compiling your graph To build your graph, you first define the [state](#state), you then add [nodes](#nodes) and [edges](#edges), and then you compile it. What exactly is compiling your graph and why is it needed? diff --git a/libs/checkpoint/src/base.ts b/libs/checkpoint/src/base.ts index 27cc8824..c7de5c00 100644 --- a/libs/checkpoint/src/base.ts +++ b/libs/checkpoint/src/base.ts @@ -72,6 +72,7 @@ export function deepCopy(obj: T): T { return newObj as T; } +/** @hidden */ export function emptyCheckpoint(): Checkpoint { return { v: 1, @@ -84,6 +85,7 @@ export function emptyCheckpoint(): Checkpoint { }; } +/** @hidden */ export function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint { return { v: checkpoint.v, diff --git a/libs/langgraph/src/graph/annotation.ts b/libs/langgraph/src/graph/annotation.ts index 2ae507de..f2f8b396 100644 --- a/libs/langgraph/src/graph/annotation.ts +++ b/libs/langgraph/src/graph/annotation.ts @@ -46,6 +46,19 @@ export type NodeType = RunnableLike< UpdateType >; +/** @ignore */ +export interface AnnotationFunction { + (): LastValue; + ( + annotation: SingleReducer + ): BinaryOperatorAggregate; + Root: (sd: S) => AnnotationRoot; +} + +/** + * Should not be instantiated directly. See {@link Annotation}. + * @internal + */ export class AnnotationRoot { lc_graph_name = "AnnotationRoot"; @@ -62,14 +75,81 @@ export class AnnotationRoot { } } -// TODO: Add docstring -export function Annotation(): LastValue; - -export function Annotation( - annotation: SingleReducer -): BinaryOperatorAggregate; - -export function Annotation( +/** + * Helper that instantiates channels within a StateGraph state. + * + * Can be used as a field in an {@link Annotation.Root} wrapper in one of two ways: + * 1. **Directly**: Creates a channel that stores the most recent value returned from a node. + * 2. **With a reducer**: Creates a channel that applies the reducer on a node's return value. + * + * @example + * ```ts + * import { StateGraph, Annotation } from "@langchain/langgraph"; + * + * // Define a state with a single string key named "currentOutput" + * const SimpleAnnotation = Annotation.Root({ + * currentOutput: Annotation, + * }); + * + * const graphBuilder = new StateGraph(SimpleAnnotation); + * + * // A node in the graph that returns an object with a "currentOutput" key + * // replaces the value in the state. You can get the state type as shown below: + * const myNode = (state: typeof SimpleAnnotation.State) => { + * return { + * currentOutput: "some_new_value", + * }; + * } + * + * const graph = graphBuilder + * .addNode("myNode", myNode) + * ... + * .compile(); + * ``` + * + * @example + * ```ts + * import { type BaseMessage, AIMessage } from "@langchain/core/messages"; + * import { StateGraph, Annotation } from "@langchain/langgraph"; + * + * // Define a state with a single key named "messages" that will + * // combine a returned BaseMessage or arrays of BaseMessages + * const AnnotationWithReducer = Annotation.Root({ + * messages: Annotation({ + * // Different types are allowed for updates + * reducer: (left: BaseMessage[], right: BaseMessage | BaseMessage[]) => { + * if (Array.isArray(right)) { + * return left.concat(right); + * } + * return left.concat([right]); + * }, + * default: () => [], + * }), + * }); + * + * const graphBuilder = new StateGraph(AnnotationWithReducer); + * + * // A node in the graph that returns an object with a "messages" key + * // will update the state by combining the existing value with the returned one. + * const myNode = (state: typeof AnnotationWithReducer.State) => { + * return { + * messages: [new AIMessage("Some new response")], + * }; + * }; + * + * const graph = graphBuilder + * .addNode("myNode", myNode) + * ... + * .compile(); + * ``` + * @namespace + * @property Root + * Helper function that instantiates a StateGraph state. See {@link Annotation} for usage. + */ +export const Annotation: AnnotationFunction = function < + ValueType, + UpdateType = ValueType +>( annotation?: SingleReducer ): BaseChannel { if (annotation) { @@ -78,7 +158,8 @@ export function Annotation( // @ts-expect-error - Annotation without reducer return new LastValue(); } -} +} as AnnotationFunction; + Annotation.Root = (sd: S) => new AnnotationRoot(sd); export function getChannel( diff --git a/libs/langgraph/src/graph/graph.ts b/libs/langgraph/src/graph/graph.ts index e9884801..f3701f1b 100644 --- a/libs/langgraph/src/graph/graph.ts +++ b/libs/langgraph/src/graph/graph.ts @@ -27,7 +27,9 @@ import { import { RunnableCallable } from "../utils.js"; import { InvalidUpdateError } from "../errors.js"; +/** Special reserved node name denoting the start of a graph. */ export const START = "__start__"; +/** Special reserved node name denoting the end of a graph. */ export const END = "__end__"; export interface BranchOptions { diff --git a/libs/langgraph/src/graph/message.ts b/libs/langgraph/src/graph/message.ts index 48fc423a..bdc97df4 100644 --- a/libs/langgraph/src/graph/message.ts +++ b/libs/langgraph/src/graph/message.ts @@ -11,6 +11,11 @@ type Messages = | BaseMessage | BaseMessageLike; +/** + * Prebuilt reducer that combines returned messages. + * Can handle standard messages and special modifiers like {@link RemoveMessage} + * instances. + */ export function messagesStateReducer( left: BaseMessage[], right: Messages @@ -59,6 +64,7 @@ export function messagesStateReducer( return merged.filter((m) => !idsToRemove.has(m.id)); } +/** @ignore */ export class MessageGraph extends StateGraph< BaseMessage[], BaseMessage[], diff --git a/libs/langgraph/src/graph/messages_annotation.ts b/libs/langgraph/src/graph/messages_annotation.ts index 9f299365..361b4746 100644 --- a/libs/langgraph/src/graph/messages_annotation.ts +++ b/libs/langgraph/src/graph/messages_annotation.ts @@ -4,6 +4,11 @@ import { BaseMessage } from "@langchain/core/messages"; import { Annotation } from "./annotation.js"; import { messagesStateReducer } from "./message.js"; +/** + * Prebuilt state annotation that combines returned messages. + * Can handle standard messages and special modifiers like {@link RemoveMessage} + * instances. + */ export const MessagesAnnotation = Annotation.Root({ messages: Annotation({ reducer: messagesStateReducer, diff --git a/libs/langgraph/src/graph/state.ts b/libs/langgraph/src/graph/state.ts index 6a055b34..218c434a 100644 --- a/libs/langgraph/src/graph/state.ts +++ b/libs/langgraph/src/graph/state.ts @@ -44,6 +44,68 @@ export interface StateGraphArgs { : ChannelReducers<{ __root__: Channels }>; } +/** + * A graph whose nodes communicate by reading and writing to a shared state. + * Each node takes a defined `State` as input and returns a `Partial`. + * + * Each state key can optionally be annotated with a reducer function that + * will be used to aggregate the values of that key received from multiple nodes. + * The signature of a reducer function is (left: Value, right: UpdateValue) => Value. + * + * See {@link Annotation} for more on defining state. + * + * After adding nodes and edges to your graph, you must call `.compile()` on it before + * you can use it. + * + * @example + * ```ts + * import { + * type BaseMessage, + * AIMessage, + * HumanMessage, + * } from "@langchain/core/messages"; + * import { StateGraph, Annotation } from "@langchain/langgraph"; + * + * // Define a state with a single key named "messages" that will + * // combine a returned BaseMessage or arrays of BaseMessages + * const StateAnnotation = Annotation.Root({ + * sentiment: Annotation, + * messages: Annotation({ + * reducer: (left: BaseMessage[], right: BaseMessage | BaseMessage[]) => { + * if (Array.isArray(right)) { + * return left.concat(right); + * } + * return left.concat([right]); + * }, + * default: () => [], + * }), + * }); + * + * const graphBuilder = new StateGraph(StateAnnotation); + * + * // A node in the graph that returns an object with a "messages" key + * // will update the state by combining the existing value with the returned one. + * const myNode = (state: typeof StateAnnotation.State) => { + * return { + * messages: [new AIMessage("Some new response")], + * sentiment: "positive", + * }; + * }; + * + * const graph = graphBuilder + * .addNode("myNode", myNode) + * .addEdge("__start__", "myNode") + * .addEdge("myNode", "__end__") + * .compile(); + * + * await graph.invoke({ messages: [new HumanMessage("how are you?")] }); + * + * // { + * // messages: [HumanMessage("how are you?"), AIMessage("Some new response")], + * // sentiment: "positive", + * // } + * ``` + */ export class StateGraph< SD extends StateDefinition | unknown, S = SD extends StateDefinition ? StateType : SD, diff --git a/libs/langgraph/src/prebuilt/agent_executor.ts b/libs/langgraph/src/prebuilt/agent_executor.ts index 498ad84b..21611833 100644 --- a/libs/langgraph/src/prebuilt/agent_executor.ts +++ b/libs/langgraph/src/prebuilt/agent_executor.ts @@ -11,6 +11,7 @@ interface Step { observation: unknown; } +/** @ignore */ export interface AgentExecutorState { agentOutcome?: AgentAction | AgentFinish; steps: Array; @@ -18,6 +19,7 @@ export interface AgentExecutorState { chatHistory?: BaseMessage[]; } +/** @ignore */ export function createAgentExecutor({ agentRunnable, tools, diff --git a/libs/langgraph/src/prebuilt/chat_agent_executor.ts b/libs/langgraph/src/prebuilt/chat_agent_executor.ts index b77866c9..b4fd4c35 100644 --- a/libs/langgraph/src/prebuilt/chat_agent_executor.ts +++ b/libs/langgraph/src/prebuilt/chat_agent_executor.ts @@ -15,8 +15,10 @@ import { } from "../graph/state.js"; import { END, START } from "../graph/index.js"; +/** @ignore */ export type FunctionCallingExecutorState = { messages: Array }; +/** @ignore */ export function createFunctionCallingExecutor({ model, tools, diff --git a/tsconfig.json b/tsconfig.json index dc8bbecb..97d793ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,8 @@ }, "include": [ "libs/*/src/**/*", + "libs/*/*.d.ts", + "libs/langgraph/*.d.ts", "libs/langgraph/*.d.ts", ], "exclude": [