diff --git a/src/oss/langgraph/graph-api.mdx b/src/oss/langgraph/graph-api.mdx index fd682fecc3..21f6d674a0 100644 --- a/src/oss/langgraph/graph-api.mdx +++ b/src/oss/langgraph/graph-api.mdx @@ -281,6 +281,76 @@ There are two subtle and important points to note here: 2. We initialize the graph with `StateGraph({ state: OverallState, input: InputState, output: OutputState })`. How can we write to `PrivateState` in `node2`? 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. ::: +:::python + +**Private channels are not redacted when streaming.** + +Input, output, and private schemas constrain what each node _reads_ (its input schema) and what `invoke` _returns_ (the output schema). They do **not** hide channels from `stream`. + +When you stream with `stream_mode="values"`, the graph emits **all** of its state channels by default — including private ones — because values streaming defaults to the full set of state channels rather than the output schema. This is why a private channel like `bar` is hidden by `invoke` but visible while streaming: + +```python +for chunk in graph.stream({"user_input": "My"}, stream_mode="values"): + print(chunk) +# {'user_input': 'My'} +# {'user_input': 'My', 'foo': 'My name'} +# {'user_input': 'My', 'foo': 'My name', 'bar': 'My name is'} # <-- private channel +# {'user_input': 'My', 'foo': 'My name', 'bar': 'My name is', 'graph_output': 'My name is Lance'} +``` + +To restrict the streamed values to a specific set of channels (e.g. only the output schema), pass `output_keys`: + +```python +for chunk in graph.stream( + {"user_input": "My"}, + stream_mode="values", + output_keys=["graph_output"], # [!code highlight] +): + print(chunk) +# {'graph_output': 'My name is Lance'} +``` + +If you only need the channels a node actually produced each step (rather than the full accumulated state), use `stream_mode="updates"` instead. + +::: + +:::js + +**Private channels are not redacted when streaming.** + +Input, output, and private schemas constrain what each node _reads_ (its input schema) and what `invoke` _returns_ (the output schema). They do **not** hide channels from `stream`. + +When you stream with `streamMode: "values"`, the graph emits **all** of its state channels by default — including private ones — because values streaming defaults to the full set of state channels rather than the output schema. This is why a private channel like `bar` is hidden by `invoke` but visible while streaming: + +```typescript +for await (const chunk of await graph.stream( + { userInput: "My" }, + { streamMode: "values" } +)) { + console.log(chunk); +} +// { userInput: 'My' } +// { userInput: 'My', foo: 'My name' } +// { userInput: 'My', foo: 'My name', bar: 'My name is' } // <-- private channel +// { userInput: 'My', foo: 'My name', bar: 'My name is', graphOutput: 'My name is Lance' } +``` + +To restrict the streamed values to a specific set of channels (e.g. only the output schema), pass `outputKeys`: + +```typescript +for await (const chunk of await graph.stream( + { userInput: "My" }, + { streamMode: "values", outputKeys: ["graphOutput"] } // [!code highlight] +)) { + console.log(chunk); +} +// { graphOutput: 'My name is Lance' } +``` + +If you only need the channels a node actually produced each step (rather than the full accumulated state), use `streamMode: "updates"` instead. + +::: + ### Reducers Reducers are key to understanding how updates from nodes are applied to the `State`. Each key in the `State` has its own independent reducer function. If no reducer function is explicitly specified then it is assumed that all updates to that key should override it. There are a few different types of reducers, starting with the default type of reducer: