Skip to content

Conversation

@dqbd
Copy link
Collaborator

@dqbd dqbd commented Sep 8, 2025

Sample:

import { interrupt, writer } from "@langchain/langgraph";

const builder = new StateGraph(
  z.object({
    messages: z.custom<BaseMessage[]>().register(registry, MessagesZodMeta),
    innerReason: z.string(),
  }),
  {
    input: z.object({
      messages: z.custom<BaseMessage[]>().register(registry, MessagesZodMeta),
    }),

    // Allow specifying types for interrupts and writes
    writer: writer<{ custom: string }>,
    interrupt: interrupt<{ reason: string }, { messages: string[] }>,

    // Allow optionally specifying all nodes upfront.
    // This is needed to properly type `goto` commands and removes
    // friction when dynamically constructing graphs.
    nodes: ["hello", "what"],
  }
);

const node: typeof builder.Node = async (_, runtime) => {
  const result = runtime.interrupt?.({ reason: "hello" });
  if (result != null) {
    runtime.writer?.({ custom: "hello" });
    return { messages: result.messages };
  }
  return {};
};

builder.addNode("hello", node);
builder.addEdge("__start__", "hello");

const graph = builder.compile();

const invoke = await graph.invoke({ messages: "input" });
if (graph.isInterrupted(invoke)) {
  // ... invoke[INTERRUPT].value == { reason: string }
}

This PR also removes undocumented typedNode helper, which has been superseded by this PR

@changeset-bot
Copy link

changeset-bot bot commented Sep 8, 2025

🦋 Changeset detected

Latest commit: 0053c17

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

store?: BaseStore;

writer?: (chunk: unknown) => void;
writer?: IsEqual<WriterType, unknown> extends true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there cases where writer is not defined? Wonder if we can remove the optionality.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writer is currently defined only when custom stream mode is requested

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's not possible to type. In this case, I would advocate to remove the optionality afterall and throw a meaningful error at runtime, because the optionality doesn't really provide any value IMHO.

? (chunk: unknown) => void
: WriterType;

interrupt?: IsEqual<InterruptType, unknown> extends true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to infer the existence of interrupt based on whether a checkpointer parameter was provided?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I'd like to support creating the builder separately, that would entail making checkpointer a constructor parameter.

An alternative I think can be always providing the function in runtime but throwing an error in runtime if no checkpointer is passed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we throw an error already that is meaningful enough.

}

export interface Runtime<ContextType = Record<string, unknown>> {
export interface Runtime<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is Runtime different from LangGraphRunnableConfig? Both seem to have very similar types. Maybe we can do:

export interface Runtime<
  ContextType = Record<string, unknown>,
  InterruptType = unknown,
  WriterType = unknown
> extends LangGraphRunnableConfig<ContextType, InterruptType, WriterType> {
  signal?: AbortSignal;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime is a pruned version of LangGraphRunnableConfig, so ideally it should be in reverse.

@dqbd dqbd marked this pull request as ready for review September 8, 2025 17:14
@dqbd dqbd enabled auto-merge (squash) September 8, 2025 17:16
@dqbd dqbd disabled auto-merge September 8, 2025 17:17
@dqbd dqbd merged commit 2311efc into v1 Sep 8, 2025
12 checks passed
@dqbd dqbd deleted the dqbd/state-graph-builder-encapsulation branch September 8, 2025 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants