Merge pull request #2695 from langchain-ai/nc/10dec/multistep-plan

lib: Add unit test for multistep planner graph
This commit is contained in:
Nuno Campos
2024-12-10 10:54:37 -08:00
committed by GitHub
2 changed files with 122 additions and 0 deletions
+61
View File
@@ -14944,3 +14944,64 @@ def test_command_with_static_breakpoints() -> None:
graph.invoke({"foo": "abc"}, config)
result = graph.invoke(Command(resume="node1"), config)
assert result == {"foo": "abc|node-1|node-2"}
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_SYNC)
def test_multistep_plan(request: pytest.FixtureRequest, checkpointer_name: str):
from langchain_core.messages import AnyMessage
checkpointer = request.getfixturevalue(f"checkpointer_{checkpointer_name}")
class State(TypedDict, total=False):
plan: list[Union[str, list[str]]]
messages: Annotated[list[AnyMessage], add_messages]
def planner(state: State):
if state.get("plan") is None:
# create plan somehow
plan = ["step1", ["step2", "step3"], "step4"]
# pick the first step to execute next
first_step, *plan = plan
# put the rest of plan in state
return Command(goto=first_step, update={"plan": plan})
elif state["plan"]:
# go to the next step of the plan
next_step, *next_plan = state["plan"]
return Command(goto=next_step, update={"plan": next_plan})
else:
# the end of the plan
pass
def step1(state: State):
return Command(goto="planner", update={"messages": [("human", "step1")]})
def step2(state: State):
return Command(goto="planner", update={"messages": [("human", "step2")]})
def step3(state: State):
return Command(goto="planner", update={"messages": [("human", "step3")]})
def step4(state: State):
return Command(goto="planner", update={"messages": [("human", "step4")]})
builder = StateGraph(State)
builder.add_node(planner)
builder.add_node(step1)
builder.add_node(step2)
builder.add_node(step3)
builder.add_node(step4)
builder.add_edge(START, "planner")
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "1"}}
assert graph.invoke({"messages": [("human", "start")]}, config) == {
"messages": [
_AnyIdHumanMessage(content="start"),
_AnyIdHumanMessage(content="step1"),
_AnyIdHumanMessage(content="step2"),
_AnyIdHumanMessage(content="step3"),
_AnyIdHumanMessage(content="step4"),
],
"plan": [],
}
+61
View File
@@ -13199,3 +13199,64 @@ async def test_interrupt_loop(checkpointer_name: str):
] == [
{"node": {"age": 19}},
]
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
async def test_multistep_plan(checkpointer_name: str):
from langchain_core.messages import AnyMessage
class State(TypedDict, total=False):
plan: list[Union[str, list[str]]]
messages: Annotated[list[AnyMessage], add_messages]
def planner(state: State):
if state.get("plan") is None:
# create plan somehow
plan = ["step1", ["step2", "step3"], "step4"]
# pick the first step to execute next
first_step, *plan = plan
# put the rest of plan in state
return Command(goto=first_step, update={"plan": plan})
elif state["plan"]:
# go to the next step of the plan
next_step, *next_plan = state["plan"]
return Command(goto=next_step, update={"plan": next_plan})
else:
# the end of the plan
pass
def step1(state: State):
return Command(goto="planner", update={"messages": [("human", "step1")]})
def step2(state: State):
return Command(goto="planner", update={"messages": [("human", "step2")]})
def step3(state: State):
return Command(goto="planner", update={"messages": [("human", "step3")]})
def step4(state: State):
return Command(goto="planner", update={"messages": [("human", "step4")]})
builder = StateGraph(State)
builder.add_node(planner)
builder.add_node(step1)
builder.add_node(step2)
builder.add_node(step3)
builder.add_node(step4)
builder.add_edge(START, "planner")
async with awith_checkpointer(checkpointer_name) as checkpointer:
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "1"}}
assert await graph.ainvoke({"messages": [("human", "start")]}, config) == {
"messages": [
_AnyIdHumanMessage(content="start"),
_AnyIdHumanMessage(content="step1"),
_AnyIdHumanMessage(content="step2"),
_AnyIdHumanMessage(content="step3"),
_AnyIdHumanMessage(content="step4"),
],
"plan": [],
}