Skip to content

Commit

Permalink
Merge branch 'main' into isaac/runtutorialnotebooks
Browse files Browse the repository at this point in the history
  • Loading branch information
isahers1 authored Sep 20, 2024
2 parents 704fd41 + b9fe384 commit 8d54240
Show file tree
Hide file tree
Showing 61 changed files with 1,133 additions and 520 deletions.
58 changes: 46 additions & 12 deletions docs/docs/concepts/low_level.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,62 @@ By default, the graph will have the same input and output schemas. If you want t

#### Multiple schemas

Typically, all graph nodes communicate with a single schema. This means that they will read and write to the same state channels. But, there are cases where we may want a bit more control over this:
Typically, all graph nodes communicate with a single schema. This means that they will read and write to the same state channels. But, there are cases where we want more control over this:

* Internal nodes may pass information that is not required in the graph's input / output.
* Internal nodes can pass information that is not required in the graph's input / output.
* We may also want to use different input / output schemas for the graph. The output might, for example, only contain a single relevant output key.

It is possible to have nodes write to private state channels inside the graph for internal node communication. We can simply define a private schema and use a type hint -- e.g., `state: PrivateState` as shown below -- to specify it as the node input schema. See [this notebook](../how-tos/pass_private_state.ipynb) for more detail.
It is possible to have nodes write to private state channels inside the graph for internal node communication. We can simply define a private schema, `PrivateState`. See [this notebook](../how-tos/pass_private_state.ipynb) for more detail.

It is also possible to define explicit input and output schemas for a graph. In these cases, we define an "internal" schema that contains *all* keys relevant to graph operations. But, we also define `input` and `output` schemas that are sub-sets of the "internal" schema to constrain the input and output of the graph. See [this notebook](../how-tos/input_output_schema.ipynb) for more detail.

Let's look at an example:

```python
class OverallState(TypedDict):
foo: int
class InputState(TypedDict):
user_input: str

class PrivateState(TypedDict):
baz: int
class OutputState(TypedDict):
graph_output: str

def node_1(state: OverallState) -> PrivateState:
...
class OverallState(TypedDict):
foo: str
user_input: str
graph_output: str

def node_2(state: PrivateState) -> OverallState:
...
class PrivateState(TypedDict):
bar: str

def node_1(state: InputState) -> OverallState:
# Write to OverallState
return {"foo": state["user_input"] + " name"}

def node_2(state: OverallState) -> PrivateState:
# Read from OverallState, write to PrivateState
return {"bar": state["foo"] + " is"}

def node_3(state: PrivateState) -> OutputState:
# Read from PrivateState, write to OutputState
return {"graph_output": state["bar"] + " Lance"}

builder = StateGraph(OverallState,input=InputState,output=OutputState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
builder.add_edge("node_2", "node_3")
builder.add_edge("node_3", END)

graph = builder.compile()
graph.invoke({"user_input":"My"})
{'graph_output': 'My name is Lance'}
```
There are two subtle and important points to note here:

It is also possible to define explicit input and output schemas for a graph. In these cases, we define an "internal" schema that contains *all* keys relevant to graph operations. But, we also define `input` and `output` schemas that are sub-sets of the "internal" schema to constrain the input and output of the graph. See [this notebook](../how-tos/input_output_schema.ipynb) for more detail.
1. We pass `state: InputState` as the input schema to `node_1`. But, we write out to `foo`, a channel in `OverallState`. How can we write out to a state channel that is not included in the input schema? This is because a node *can write to any state channel in the graph state.* The graph state is the union of of the state channels defined at initialization, which includes `OverallState` and the filters `InputState` and `OutputState`.

2. We initialize the graph with `StateGraph(OverallState,input=InputState,output=OutputState)`. So, how can we write to `PrivateState` in `node_2`? How does the graph gain access to this schema if it was not passed in the `StateGraph` initialization? We can do this because *nodes can also declare additional state channels* as long as the state schema definition exists. In this case, the `PrivateState` schema is defined, so we can add `bar` as a new state channel in the graph and write to it.

### Reducers

Expand Down
1 change: 1 addition & 0 deletions docs/docs/how-tos/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ These guides show how to use different streaming modes.
- [How to add node retries](node-retries.ipynb)
- [How to force function calling agent to structure output](react-agent-structured-output.ipynb)
- [How to pass custom LangSmith run ID for graph runs](run-id-langsmith.ipynb)
- [How to return state before hitting recursion limit](return-when-recursion-limit-hits.ipynb)

## Prebuilt ReAct Agent

Expand Down
4 changes: 3 additions & 1 deletion docs/docs/how-tos/input_output_schema.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"\n",
"By default, `StateGraph` takes in a single schema and all nodes are expected to communicate with that schema. However, it is also possible to define explicit input and output schemas for a graph. Often, in these cases, we define an \"internal\" schema that contains all keys relevant to graph operations. But, we use specific input and output schemas to filter what's permitted when invoking and what's returned. We use type hints below to, for example, show that the output of `answer_node` will be filtered to `OutputState`. In addition, we define each node's input schema (e.g., as state: `OverallState` for `answer_node`).\n",
"\n",
"In this notebook we'll walk through an example of this. At a high level, in order to do this you simply have to pass in `input=..., output=...` when defining the graph. Let's see an example below!\n",
"In this notebook we'll walk through an example of this. At a high level, in order to do this you simply have to pass in `input=..., output=...` when defining the graph. See the conceptual docs [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#multiple-schemas) for more details.\n",
"\n",
"Let's look at an example!\n",
"\n",
"## Setup\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/how-tos/pass_private_state.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"3. Retrieves documents for that generated query\n",
"4. Generates a final answer based on those documents\n",
"\n",
"We will have a separate node for each step. We will only have the `question` and `answer` on the overall state. However, we will need separate states for the `search_query` and the `documents` - we will pass these as private state keys.\n",
"We will have a separate node for each step. We will only have the `question` and `answer` on the overall state. However, we will need separate states for the `search_query` and the `documents` - we will pass these as private state keys. See the conceptual docs [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#multiple-schemas) for more details.\n",
"\n",
"Let's look at an example!\n",
"\n",
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/how-tos/recursion-limit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"\n",
"You can set the graph recursion limit when invoking or streaming the graph. The recursion limit sets the number of supersteps that the graph is allowed to execute before it raises an error. Read more about the concept of recursion limits [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#recursion-limit). Let's see an example of this in a simple graph with parallel branches to better understand exactly how the recursion limit works.\n",
"\n",
"If you want to see an example of how you can return the last value of your state instead of receiving a recursion limit error form your graph, read [this how-to](https://langchain-ai.github.io/langgraph/how-tos/return-when-recursion-limit-hits/).\n",
"\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages"
Expand Down
Loading

0 comments on commit 8d54240

Please sign in to comment.