Add collab

This commit is contained in:
William Fu-Hinthorn
2024-01-17 22:26:28 -08:00
parent 84a1915896
commit a6e3d8d29c
2 changed files with 386 additions and 227 deletions
@@ -0,0 +1,328 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a3e3ebc4-57af-4fe4-bdd3-36aff67bf276",
"metadata": {},
"source": [
"## Example 2: Agent Team Supervisor\n",
"\n",
"The prevoius example routed messages automatically based on the output of the initial researcher agent.\n",
"\n",
"We can also choose to use an LLM to orchestrate the different agents.\n",
"\n",
"Below, we will create an agent group, with an agent supervisor to help delegate tasks.\n",
"\n",
"To simplify each agent node, we will use the AgentExecutor class from LangChain."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "0d30b6f7-3bec-4d9f-af50-43dfdc81ae6c",
"metadata": {},
"outputs": [],
"source": [
"# %%capture --no-stderr\n",
"# %pip install -U langchain langchain_openai langchain_experimental langsmith pandas"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "30c2f3de-c730-4aec-85a6-af2c2f058803",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_if_undefined(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass(f\"Please provide your {var}\")\n",
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"_set_if_undefined(\"TAVILY_API_KEY\")\n",
"\n",
"# Optional, add tracing in LangSmith\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Multi-agent Collaboration\""
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f04c6778-403b-4b49-9b93-678e910d5cec",
"metadata": {},
"outputs": [],
"source": [
"from typing import List, Tuple, Union\n",
"\n",
"import matplotlib.pyplot as plt\n",
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"from langchain_core.tools import tool\n",
"\n",
"tavily_tool = TavilySearchResults(max_results=5)\n",
"\n",
"\n",
"@tool\n",
"def create_plot(\n",
" data: Union[List[float], List[int]],\n",
" labels: Union[List[str], None] = None,\n",
" title: str = \"Plot\",\n",
" xlabel: str = \"X\",\n",
" ylabel: str = \"Y\",\n",
" color: Union[str, List[str]] = \"blue\",\n",
" plot_type: str = \"bar\",\n",
") -> Tuple[plt.Figure, plt.Axes]:\n",
" \"\"\"\n",
" Generates a bar or line plot from the provided data and returns the figure and axis objects.\n",
"\n",
" :param data: A list of numerical values for the bar heights or line points.\n",
" :param labels: A list of strings for the bar or point labels. Default is None.\n",
" :param title: Title of the plot. Default is 'Plot'.\n",
" :param xlabel: Label for the X-axis. Default is 'X'.\n",
" :param ylabel: Label for the Y-axis. Default is 'Y'.\n",
" :param color: Color of the bars or line. Can be a single color or a list of colors. Default is 'blue'.\n",
" :param figsize: Size of the figure as a tuple (width, height). Default is (10, 6).\n",
" :param plot_type: Type of plot ('bar' or 'line'). Default is 'bar'.\n",
" :return: Tuple containing the figure and axes objects.\n",
" \"\"\"\n",
" if plot_type not in [\"bar\", \"line\"]:\n",
" raise ValueError(\"Invalid plot_type. Expected 'bar' or 'line'.\")\n",
"\n",
" fig, ax = plt.subplots(figsize=(10, 6))\n",
" x_positions = range(len(data))\n",
"\n",
" if labels and len(labels) == len(data):\n",
" plt.xticks(x_positions, labels)\n",
"\n",
" if plot_type == \"bar\":\n",
" ax.bar(x_positions, data, color=color)\n",
" elif plot_type == \"line\":\n",
" ax.plot(x_positions, data, color=color, marker=\"o\") # 'o' for circular markers\n",
"\n",
" ax.set_title(title)\n",
" ax.set_xlabel(xlabel)\n",
" ax.set_ylabel(ylabel)\n",
"\n",
" return fig, ax"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "6a430af7-8fce-4e66-ba9e-d940c1bc48e8",
"metadata": {},
"outputs": [],
"source": [
"import operator\n",
"from typing import Annotated, Any, Dict, List, Optional, Sequence, TypedDict\n",
"\n",
"from langchain.agents import AgentExecutor, create_openai_functions_agent\n",
"from langchain_core.messages import BaseMessage, HumanMessage\n",
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
"from langchain_core.tools import BaseTool\n",
"from langchain_experimental.tools import PythonREPLTool\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"from langgraph.graph import END, StateGraph\n",
"\n",
"\n",
"class AgentState(TypedDict):\n",
" messages: Annotated[Sequence[BaseMessage], operator.add]\n",
" next: str\n",
"\n",
"\n",
"workflow = StateGraph(AgentState)\n",
"\n",
"\n",
"def create_agent_node(name: str, llm: ChatOpenAI, tools: list, system_prompt: str):\n",
" prompt = ChatPromptTemplate.from_messages(\n",
" [\n",
" (\n",
" \"system\",\n",
" system_prompt,\n",
" ),\n",
" MessagesPlaceholder(variable_name=\"messages\"),\n",
" MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
" ]\n",
" )\n",
" agent = create_openai_functions_agent(llm, tools, prompt)\n",
" executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
"\n",
" def _update_state(ai_message) -> dict:\n",
" if isinstance(ai_message, FunctionMessage):\n",
" result = ai_message\n",
" else:\n",
" message = ai_message.dict(exclude={\"type\"})\n",
" message[\"name\"] = name\n",
" result = HumanMessage(**message)\n",
" return {\n",
" \"messages\": [result],\n",
" \"sender\": name,\n",
" }\n",
"\n",
" chain = executor | _update_state\n",
" workflow.add_node(name, chain)\n",
"\n",
"\n",
"llm = ChatOpenAI(model=\"gpt-4\")\n",
"\n",
"create_agent_node(\"Researcher\", llm, [tavily_tool], \"You are a web researcher.\")\n",
"create_agent_node(\"Chart Generator\", llm, [create_plot], \"You are a chart generator.\")\n",
"# NOTE: THIS PERFORMS ARBITRARY CODE EXECUTION. PROCEED WITH CAUTION\n",
"create_agent_node(\n",
" \"Data Analyst\",\n",
" llm,\n",
" [PythonREPLTool()],\n",
" \"You may generate safe python code to analyze data.\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "d6374825-912f-40c9-910d-afa267b401bf",
"metadata": {},
"source": [
"Almost done, now we need to create the team supervisor."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "17c108a0-6dc3-46fd-a5e6-a1fcfad5458a",
"metadata": {},
"outputs": [],
"source": [
"# So the team supervisor is an LLM node. It just picks the next t\n",
"from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n",
"\n",
"\n",
"def create_agent_supervisor(members: List[str], llm: ChatOpenAI, system_prompt: str):\n",
" options = [\"FINISH\"] + members\n",
" function_def = {\n",
" \"name\": \"route\",\n",
" \"description\": \"Select the next role.\",\n",
" \"parameters\": {\n",
" \"title\": \"routeSchema\",\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"next\": {\n",
" \"title\": \"Next\",\n",
" \"anyOf\": [\n",
" {\"enum\": options},\n",
" ],\n",
" }\n",
" },\n",
" \"required\": [\"next\"],\n",
" },\n",
" }\n",
" prompt = ChatPromptTemplate.from_messages(\n",
" [\n",
" (\"system\", system_prompt),\n",
" MessagesPlaceholder(variable_name=\"messages\"),\n",
" (\n",
" \"system\",\n",
" \"Given the conversation above, who should act next?\"\n",
" \" Or should we FINISH? Select one of: {options}\",\n",
" ),\n",
" ]\n",
" ).partial(options=str(options))\n",
" chain = (\n",
" prompt\n",
" | llm.bind_functions(functions=[function_def], function_call=\"route\")\n",
" | JsonOutputFunctionsParser()\n",
" )\n",
" workflow.add_node(\"supervisor\", chain)\n",
" conditional_map = {k: k for k in members}\n",
" conditional_map[\"FINISH\"] = END\n",
"\n",
" for member in members:\n",
" workflow.add_edge(member, \"supervisor\")\n",
" workflow.add_conditional_edges(\n",
" \"supervisor\", lambda x: x[\"next\"], conditional_map\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "14778e86-077b-4e6a-893c-400e59b0cdbf",
"metadata": {},
"outputs": [],
"source": [
"create_agent_supervisor(\n",
" [\"Researcher\", \"Chart Generator\", \"Data Analyst\"],\n",
" llm,\n",
" \"You are an agent supervisor tasked with managing work order.\"\n",
" \" Respond with only the role will optimally help us accomplish the user's task or question.\"\n",
" \" When finished, respond with FINISH.\",\n",
")\n",
"\n",
"# Finally, add entrypoint\n",
"workflow.set_entry_point(\"supervisor\")\n",
"\n",
"\n",
"def enter(text: str) -> dict:\n",
" return {\"messages\": [HumanMessage(content=text)]}\n",
"\n",
"\n",
"graph = enter | workflow.compile()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "56ba78e9-d9c1-457c-a073-d606d5d3e013",
"metadata": {},
"outputs": [
{
"ename": "InvalidUpdateError",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mInvalidUpdateError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mgraph\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mCode hello world and print it to the terminal\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:1780\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 1778\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1779\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 1780\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1781\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1782\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m 1783\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1784\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1785\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1786\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1787\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 1788\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
"File \u001b[0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:521\u001b[0m, in \u001b[0;36mPregel.invoke\u001b[0;34m(self, input, config, output_keys, input_keys, **kwargs)\u001b[0m\n\u001b[1;32m 511\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 512\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 513\u001b[0m \u001b[38;5;28minput\u001b[39m: Union[\u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any], Any],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 518\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 519\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Union[\u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any], Any]:\n\u001b[1;32m 520\u001b[0m latest: Union[\u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any], Any] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m--> 521\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 522\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 523\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 524\u001b[0m \u001b[43m \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moutput_keys\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moutput\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 525\u001b[0m \u001b[43m \u001b[49m\u001b[43minput_keys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 526\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 527\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 528\u001b[0m \u001b[43m \u001b[49m\u001b[43mlatest\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\n\u001b[1;32m 529\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m latest\n",
"File \u001b[0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:557\u001b[0m, in \u001b[0;36mPregel.transform\u001b[0;34m(self, input, config, output_keys, input_keys, **kwargs)\u001b[0m\n\u001b[1;32m 548\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtransform\u001b[39m(\n\u001b[1;32m 549\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 550\u001b[0m \u001b[38;5;28minput\u001b[39m: Iterator[Union[\u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any], Any]],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 555\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 556\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Iterator[Union[\u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any], Any]]:\n\u001b[0;32m--> 557\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_transform_stream_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 558\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 559\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_transform\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 560\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 561\u001b[0m \u001b[43m \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moutput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 562\u001b[0m \u001b[43m \u001b[49m\u001b[43minput_keys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 563\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 564\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 565\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\n",
"File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:1232\u001b[0m, in \u001b[0;36mRunnable._transform_stream_with_config\u001b[0;34m(self, input, transformer, config, run_type, **kwargs)\u001b[0m\n\u001b[1;32m 1230\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1231\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m-> 1232\u001b[0m chunk: Output \u001b[38;5;241m=\u001b[39m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43miterator\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# type: ignore\u001b[39;00m\n\u001b[1;32m 1233\u001b[0m \u001b[38;5;28;01myield\u001b[39;00m chunk\n\u001b[1;32m 1234\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m final_output_supported:\n",
"File \u001b[0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:335\u001b[0m, in \u001b[0;36mPregel._transform\u001b[0;34m(self, input, run_manager, config, input_keys, output_keys)\u001b[0m\n\u001b[1;32m 332\u001b[0m _interrupt_or_proceed(done, inflight, step)\n\u001b[1;32m 334\u001b[0m \u001b[38;5;66;03m# apply writes to channels\u001b[39;00m\n\u001b[0;32m--> 335\u001b[0m \u001b[43m_apply_writes\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcheckpoint\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mchannels\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpending_writes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstep\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 337\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdebug:\n\u001b[1;32m 338\u001b[0m print_checkpoint(step, channels)\n",
"File \u001b[0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:687\u001b[0m, in \u001b[0;36m_apply_writes\u001b[0;34m(checkpoint, channels, pending_writes, config, for_step)\u001b[0m\n\u001b[1;32m 685\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m chan, vals \u001b[38;5;129;01min\u001b[39;00m pending_writes_by_channel\u001b[38;5;241m.\u001b[39mitems():\n\u001b[1;32m 686\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m chan \u001b[38;5;129;01min\u001b[39;00m channels:\n\u001b[0;32m--> 687\u001b[0m \u001b[43mchannels\u001b[49m\u001b[43m[\u001b[49m\u001b[43mchan\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mupdate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mvals\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 688\u001b[0m checkpoint[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mchannel_versions\u001b[39m\u001b[38;5;124m\"\u001b[39m][chan] \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m 689\u001b[0m updated_channels\u001b[38;5;241m.\u001b[39madd(chan)\n",
"File \u001b[0;32m~/code/lc/langgraph/langgraph/channels/last_value.py:47\u001b[0m, in \u001b[0;36mLastValue.update\u001b[0;34m(self, values)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[1;32m 46\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(values) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[0;32m---> 47\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m InvalidUpdateError()\n\u001b[1;32m 49\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalue \u001b[38;5;241m=\u001b[39m values[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]\n",
"\u001b[0;31mInvalidUpdateError\u001b[0m: "
]
}
],
"source": [
"graph.invoke(\"Code hello world and print it to the terminal\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
File diff suppressed because one or more lines are too long