new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

LangChain · LangGraph · all subjects

nodes

32 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Tool binding with bindTools in JavaScript

In JavaScript, nodes can bind tools to a model using the .bindTools method. This allows the model to decide whether to call the tool when processing the state.

Tool binding with bind_tools in Python

In Python, nodes can bind tools to a model using the .bind_tools method. This allows the model to decide whether to call the tool when processing the state.

Pattern: Keep node functions tolerant of unknown keys

Keep node functions tolerant of unknown keys. TypedDict ignores extra keys at runtime, so leftover state from an older code version will not raise unless a node explicitly reads a missing key.

Node status values

The discovery snapshot's status property can be one of: 'pending', 'running', 'complete', or 'error'. This status is used directly to determine node state in the UI.

Graph execution pattern for product UX

Graph execution turns graph structure into product UX by exposing the same checkpoints, node names, state keys, and stream metadata that LangGraph uses internally. Instead of treating a run as a single assistant response, you expose each step and its status to users, making the agent's pipeline visible.

Node card collapsible behavior with status

Node cards should auto-open when the node status is 'running' and auto-collapse when the node status transitions to 'complete'. This allows users to focus on the currently active step in long pipelines.

Dynamic pipeline handling with conditional nodes

Not all graphs have a fixed set of nodes; some pipelines add or skip nodes based on input. Skipped nodes will not appear in stream.subgraphs. Only render cards for nodes that are discovered during the current thread execution, avoiding empty placeholder cards for conditionally skipped steps.

Graph execution use cases

Graph execution cards work well for multi-step pipelines where visibility matters, such as research pipelines (classify → gather sources → analyze → synthesize), content generation (outline → draft → fact-check → edit → publish), data processing (ingest → validate → transform → aggregate → export), code generation (understand requirements → plan → write → review → test), and decision workflows (gather context → evaluate → score → recommend).

Backend and frontend contract for node mapping

The mapping between node names and UI cards forms a contract between the graph backend and the frontend. Backend authors can add, rename, or reorder nodes intentionally, while frontend authors decide how each state key should be visualized: as a status badge, markdown panel, table, chart, trace view, or approval card. This decoupling allows flexibility in graph design.

Research pipeline example node structure

A research pipeline example includes four nodes: Classify (categorize the user's query), Research (gather relevant information), Analyze (draw conclusions from the research), and Synthesize (produce a final, polished response). Each node writes its output to a specific key in the graph's state.

Graph nodes and sequential/parallel execution

A LangGraph graph is composed of named nodes that execute in sequence or in parallel. Each node is responsible for a specific task, such as classify, research, analyze, or synthesize. Every node writes its output to a specific key in the graph's state.

SubgraphDiscoverySnapshot properties

Each SubgraphDiscoverySnapshot carries the node name (accessed via node.nodeName) and current status. The status field reports one of four values: 'pending', 'running', 'complete', or 'error'.

Task execution example with result()

Example showing synchronous task execution using result(): ```python @entrypoint(checkpointer=checkpointer) def my_workflow(some_input: int) -> int: future = slow_computation(some_input) return future.result() # Wait for the result synchronously ``` For async execution: ```python @entrypoint(checkpointer=checkpointer) async def my_workflow(some_input: int) -> int: return await slow_computation(some_input) # Await result asynchronously ```

Task serialization requirement

The outputs of tasks must be JSON-serializable to support checkpointing.

Task definition and characteristics

A task represents a discrete unit of work such as an API call or data processing step. It has two key characteristics: Asynchronous Execution - tasks are designed to be executed asynchronously allowing multiple operations to run concurrently without blocking; Checkpointing - task results are saved to a checkpoint enabling resumption of the workflow from the last saved state. Tasks are defined using the @task decorator (Python) or task function (JavaScript) which wraps a regular function.

Task execution context and invocation

Tasks can only be called from within an entrypoint, another task, or a state graph node. Tasks cannot be called directly from the main application code. When you call a task it returns immediately with a future object (Python) or a Promise (JavaScript). To obtain the result of a task you can either wait for it synchronously using result() or await it asynchronously using await.

When to use tasks in workflows

Tasks are useful in the following scenarios: Checkpointing - when you need to save the result of a long-running operation to a checkpoint so you don't need to recompute it when resuming the workflow; Human-in-the-loop - if building a workflow that requires human intervention you MUST use tasks to encapsulate any randomness (e.g. API calls) to ensure the workflow can be resumed correctly; Parallel Execution - for I/O-bound tasks enabling parallel execution allowing multiple operations to run concurrently without blocking; Observability - wrapping operations in tasks provides a way to track workflow progress and monitor individual operations using LangSmith; Retryable Work - when work needs to be retried to handle failures or inconsistencies tasks provide a way to encapsulate and manage retry logic.

Dedicated nodes for SQL agent steps enforce tool-call behavior

Implementing SQL agent steps in dedicated nodes (for listing DB tables, getting schema, generating queries, checking queries) allows forcing tool-calls when needed and customizing the prompts associated with each step. This provides better control than relying on system prompts in prebuilt agents.

Node types in LangGraph

LLM steps use an LLM to understand, analyze, generate text, or make reasoning decisions. Data steps retrieve information from external sources. Action steps perform external actions. User input steps need human intervention.

Command object for node routing decisions

Nodes handle their own routing using Command objects. Command takes update (state changes) and goto (next node name). Type hints like Command[Literal["node1", "node2"]] declare where node can route. This keeps control flow explicit and traceable.

Node decision routing based on classification

In classify_intent node: if billing or critical urgency, goto human_review; if question or feature, goto search_documentation; if bug, goto bug_tracking; else goto draft_response. Routing decisions happen inside nodes using Command, not graph structure.

Node isolation for external services and different failure modes

Separate nodes for Doc Search and Bug Track isolate external API calls. If search service fails, it doesn't affect LLM calls. Different services have different retry strategies. Separate nodes let you configure retry_policy independently. For example, search_documentation gets retry_policy with max_attempts=3, other nodes get different policies.

Intermediate visibility from separate classification node

Having classify_intent as separate node lets you inspect what LLM decided before taking action. Valuable for debugging and monitoring—you can see exactly when and why agent routes to human review, without re-running LLM on failures in later nodes.

Structured output from LLM for typed responses

Use llm.with_structured_output(EmailClassification) to create structured LLM that returns typed dictionary directly. Python: structured_llm = llm.with_structured_output(EmailClassification); classification = structured_llm.invoke(prompt). JavaScript: const structuredLlm = llm.withStructuredOutput(EmailClassificationSchema); const classification = await structuredLlm.invoke(prompt).

Trade-off: combining nodes vs. separate nodes for resilience

Combining read_email and classify_intent into one node would lose ability to inspect raw email before classification and would repeat both operations on any node failure. Separate nodes have observability/debugging benefits worth the trade-off. Application should choose granularity based on specific needs.

Nodes in Python accept state, config, and runtime

In LangGraph, nodes are Python functions (synchronous or asynchronous) that accept: state (the state of the graph), config (a RunnableConfig object containing configuration information like thread_id and tracing information like tags), and runtime (a Runtime object containing runtime context and other information like store, stream_writer, execution_info, server_info, heartbeat for idle timeout refresh, and control for graceful shutdown).

Nodes in JavaScript accept state and config

In LangGraph, nodes are typically functions (sync or async) that accept: state (the state of the graph) and config (a RunnableConfig object containing configuration information like thread_id and tracing information like tags). You can add nodes to a graph using the addNode method. For better type safety, use the GraphNode type utility or State.Node to type your node functions.

Adding nodes to a graph

Nodes are added to a graph using the add_node method in Python or addNode in JavaScript. If you add a node to a graph without specifying a name, it will be given a default name equivalent to the function name.

Node re-execution and idempotency

When you compile with a checkpointer, LangGraph saves checkpoints at super-step boundaries, not mid-function inside a node. If execution stops and later resumes (after an interrupt or retry), the affected node runs again from the start of its function, meaning code and side effects before the pause run again. Design node logic so re-execution does not corrupt state. If a node inserts a database row, running it twice should not create duplicate rows unless intentional. Use idempotency keys, upserts, or read-before-write checks.

Tasks in nodes for checkpointing

If a node contains multiple operations, you can implement each operation as a task instead of splitting the logic across multiple nodes. Task results are checkpointed when the graph uses a checkpointer, so resuming a thread can skip completed task work inside the node.

Node caching based on input

LangGraph supports caching of tasks/nodes based on the input to the node. To use caching: specify a cache when compiling a graph (or specifying an entrypoint), and specify a cache policy for nodes. Each cache policy supports key_func (used to generate a cache key based on input to a node, defaults to hash of input with pickle) and ttl (time to live for cache in seconds; if not specified, cache never expires).

GraphNode type definition

A GraphNode is typed as GraphNode<typeof State> and is a function that takes state as input and returns a partial state object (the updates to apply). Node functions are used with addNode() to define the computation at each node in the graph.

Give your agent this brain