From 2ceac211e7f839933b27992f889d23a3b01f1732 Mon Sep 17 00:00:00 2001 From: vbarda Date: Wed, 24 Jul 2024 17:25:21 -0400 Subject: [PATCH 1/2] docs: add how to for graph factory + update cli --- docs/docs/cloud/deployment/graph_rebuild.md | 87 +++++++++++++++++++++ docs/docs/cloud/deployment/setup.md | 2 +- docs/docs/cloud/reference/cli.md | 4 +- docs/mkdocs.yml | 1 + 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 docs/docs/cloud/deployment/graph_rebuild.md diff --git a/docs/docs/cloud/deployment/graph_rebuild.md b/docs/docs/cloud/deployment/graph_rebuild.md new file mode 100644 index 000000000..d700b0822 --- /dev/null +++ b/docs/docs/cloud/deployment/graph_rebuild.md @@ -0,0 +1,87 @@ +# Rebuild Graph at Runtime + +You might need to rebuild your graph with a different configuration for a new run. This guide shows how you can do this. + +## Prerequisites + +Make sure to check out [this how-to guide](./setup.md) on setting up your app for deployment first. + +## Define graphs + +Let's say you have an app with a simple graph that calls an LLM and returns the response to the user. The app file directory looks like the following: + +``` +my-app/ +|-- requirements.txt +|-- .env +|-- openai_agent.py # code for your graph +``` + +where the graph is defined in `openai_agent.py`. + +### No rebuild + +In the standard LangGraph API configuration, the server uses the compiled graph instance that's defined at the top level of `openai_agent.py`, which looks like the following: + +```python +from langchain_openai import ChatOpenAI +from langgraph.graph import END, MessageGraph + +model = ChatOpenAI(temperature=0) + +graph_workflow = MessageGraph() + +graph_workflow.add_node("agent", model) +graph_workflow.add_edge("agent", END) +graph_workflow.set_entry_point("agent") + +agent = graph_workflow.compile() +``` + +To make the server aware of your graph, you need to specify a path to the variable that contains the `CompiledStateGraph` instance in your LangGraph API configuration (`langgraph.json`), e.g.: + +``` +{ + "dependencies": ["."], + "graphs": { + "openai_agent": "./openai_agent.py:agent", + }, + "env": "./.env" +} +``` + +### Rebuild + +To make your graph rebuild on each new run with custom configuration, you need to rewrite `openai_agent.py` to instead provide a _function_ that takes a config and returns a graph (or compiled graph) instance as follows: + +```python +from langchain_openai import ChatOpenAI +from langgraph.graph import END, MessageGraph +from langchain_core.runnables import RunnableConfig + +model = ChatOpenAI(temperature=0) + +def make_graph(config: RunnableConfig) + graph_workflow = MessageGraph() + + graph_workflow.add_node("agent", model) + graph_workflow.add_edge("agent", END) + graph_workflow.set_entry_point("agent") + + agent = graph_workflow.compile() + return agent +``` + +Finally, you need to specify the path to your graph-making function (`make_graph`) in `langgraph.json`: + +``` +{ + "dependencies": ["."], + "graphs": { + "openai_agent": "./openai_agent.py:make_graph", + }, + "env": "./.env" +} +``` + +See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file) \ No newline at end of file diff --git a/docs/docs/cloud/deployment/setup.md b/docs/docs/cloud/deployment/setup.md index 0ab7900d0..1aa7da3f8 100644 --- a/docs/docs/cloud/deployment/setup.md +++ b/docs/docs/cloud/deployment/setup.md @@ -88,7 +88,7 @@ agent = graph_workflow.compile() ``` !!! warning "Assign `CompiledGraph` to Variable" - The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module. + The build process for LangGraph Cloud requires that the `CompiledGraph` object be assigned to a variable at the top-level of a Python module (alternatively, you can provide [a function that creates a graph](./graph_rebuild.md)). Example file directory: ``` diff --git a/docs/docs/cloud/reference/cli.md b/docs/docs/cloud/reference/cli.md index 5e77ad113..ee481ea20 100644 --- a/docs/docs/cloud/reference/cli.md +++ b/docs/docs/cloud/reference/cli.md @@ -13,7 +13,7 @@ The LangGraph CLI requires a JSON configuration file with the following keys: | Key | Description | | --- | ----------- | | `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. | -| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph is defined. Example: `./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.graph.CompiledGraph`. | +| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: | | `env` | Path to `.env` file or a mapping from environment variable to its value. | | `python_version` | `3.11` or `3.12`. Defaults to `3.11`. | | `pip_config_file`| Path to `pip` config file. | @@ -49,7 +49,7 @@ Example: "." ], "graphs": { - "my_graph_id": "./your_package/your_file.py:variable" + "my_graph_id": "./your_package/your_file.py:make_graph" }, "env": { "OPENAI_API_KEY": "secret-key" diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 083661975..a3bfb34e7 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -192,6 +192,7 @@ nav: - Deployment: - Setup App: "cloud/deployment/setup.md" - Setup App (pyproject.toml): "cloud/deployment/setup_pyproject.md" + - Rebuild Graph at Runtime: "cloud/deployment/graph_rebuild.md" - Test App Locally: "cloud/deployment/test_locally.md" - Deploy to Cloud: "cloud/deployment/cloud.md" - Self-Host: "cloud/deployment/self_hosted.md" From 190372e137bf0494a0619dec6e7616dfedfd77cc Mon Sep 17 00:00:00 2001 From: vbarda Date: Wed, 24 Jul 2024 21:22:19 -0400 Subject: [PATCH 2/2] update --- docs/docs/cloud/deployment/graph_rebuild.md | 69 +++++++++++++++++++-- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/docs/docs/cloud/deployment/graph_rebuild.md b/docs/docs/cloud/deployment/graph_rebuild.md index d700b0822..c7853b30a 100644 --- a/docs/docs/cloud/deployment/graph_rebuild.md +++ b/docs/docs/cloud/deployment/graph_rebuild.md @@ -1,6 +1,9 @@ # Rebuild Graph at Runtime -You might need to rebuild your graph with a different configuration for a new run. This guide shows how you can do this. +You might need to rebuild your graph with a different configuration for a new run. For example, you might need to use a different graph state or graph structure depending on the config. This guide shows how you can do this. + +!!! note "Note" + In most cases, customizing behavior based on the config should be handled by a single graph where each node can read a config and change its behavior based on it ## Prerequisites @@ -52,24 +55,80 @@ To make the server aware of your graph, you need to specify a path to the variab ### Rebuild -To make your graph rebuild on each new run with custom configuration, you need to rewrite `openai_agent.py` to instead provide a _function_ that takes a config and returns a graph (or compiled graph) instance as follows: +To make your graph rebuild on each new run with custom configuration, you need to rewrite `openai_agent.py` to instead provide a _function_ that takes a config and returns a graph (or compiled graph) instance. Let's say we want to return our existing graph for user ID '1', and a tool-calling agent for other users. We can modify `openai_agent.py` as follows: ```python +from typing import Annotated, TypedDict from langchain_openai import ChatOpenAI from langgraph.graph import END, MessageGraph +from langgraph.graph.state import StateGraph +from langgraph.graph.message import add_messages +from langgraph.prebuilt import ToolNode +from langchain_core.tools import tool +from langchain_core.messages import BaseMessage from langchain_core.runnables import RunnableConfig + +class State(TypedDict): + messages: Annotated[list[BaseMessage], add_messages] + + model = ChatOpenAI(temperature=0) -def make_graph(config: RunnableConfig) - graph_workflow = MessageGraph() +def make_default_graph(): + """Make a simple LLM agent""" + graph_workflow = StateGraph(State) + def call_model(state): + return {"messages": [model.invoke(state["messages"])]} - graph_workflow.add_node("agent", model) + graph_workflow.add_node("agent", call_model) graph_workflow.add_edge("agent", END) graph_workflow.set_entry_point("agent") agent = graph_workflow.compile() return agent + + +def make_alternative_graph(): + """Make a tool-calling agent""" + + @tool + def add(a: float, b: float): + """Adds two numbers.""" + return a + b + + tool_node = ToolNode([add]) + model_with_tools = model.bind_tools([add]) + def call_model(state): + return {"messages": [model_with_tools.invoke(state["messages"])]} + + def should_continue(state: State): + if state["messages"][-1].tool_calls: + return "tools" + else: + return END + + graph_workflow = StateGraph(State) + + graph_workflow.add_node("agent", call_model) + graph_workflow.add_node("tools", tool_node) + graph_workflow.add_edge("tools", "agent") + graph_workflow.set_entry_point("agent") + graph_workflow.add_conditional_edges("agent", should_continue) + + agent = graph_workflow.compile() + return agent + + +# this is the graph making function that will decide which graph to +# build based on the provided config +def make_graph(config: RunnableConfig): + user_id = config.get("configurable", {}).get("user_id") + # route to different graph state / structure based on the user ID + if user_id == "1": + return make_default_graph() + else: + return make_alternative_graph() ``` Finally, you need to specify the path to your graph-making function (`make_graph`) in `langgraph.json`: