From bebea9f1f733d72c0b52cb92e5a4c4597c1297b4 Mon Sep 17 00:00:00 2001 From: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com> Date: Tue, 20 Feb 2024 09:28:29 -0800 Subject: [PATCH] still complicated --- examples/lats/img/tree.png | Bin 0 -> 32699 bytes examples/lats/lats.ipynb | 686 +++++++++++++++++++++++-------------- 2 files changed, 432 insertions(+), 254 deletions(-) create mode 100644 examples/lats/img/tree.png diff --git a/examples/lats/img/tree.png b/examples/lats/img/tree.png new file mode 100644 index 0000000000000000000000000000000000000000..6b3dd0c0240cc173d777222ea9dea32603249755 GIT binary patch literal 32699 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 give value of action V\n", - "4. Update global state with the next step from this rollout\n", - "#### Reflect\n", - "For all terminal states (final responses), \n", - "1. Get the reward\n", - "2. Assign `c` = reflection IFF reward isn't a 'sucess'\n", - "#### Select\n", - "(from (Upper Confidence bounds applied to Trees))\n", - "1. next action is the one that maximizes the value of V + w sqrt(ln (counts(prev state) )/counts(current state))\n", - "\n", - "\n", - "#### \"Backgpropagate\"\n", - "\n", - "```\n", - "V(s) = (V_{old}(s)(N(s) - 1)+r) / N(s)\n", - "```\n", - "Nodes in the path are equivalent to the trajectories.\n", - "\n", - "Then after the backpropagation, we pick the " + "It has four main steps:\n", + "1. Expand and simulate: select the \"best\" 5 potential actions to take and execute them in parallel.\n", + "2. Reflect + Evaluate: observe the outcomes of these actions and score the decisions based on reflection (and possibly external feedback)\n", + "3. Backpropagate: update the scores of the root trajectories based on the outcomes.\n", + "4. Select: pick the best next actions based on the aggreate rewards from step (2). Either respond (if a solution is found or the max search depth is reached) or continue searching." ] }, { @@ -62,7 +42,7 @@ { "cell_type": "code", "execution_count": 1, - "id": "5f43795e-353d-4a35-8dc9-c867b0d18568", + "id": "a177ecc9-0c96-460f-9b39-9c1ce54754f1", "metadata": {}, "outputs": [], "source": [ @@ -92,18 +72,25 @@ "source": [ "## Graph State\n", "\n", - "LATS is based on a (greedy) Monte-Carlo tree search. For each step, it picks N candidates, scores them, and then adds them to the tree. In future iterations, it picks nodes with the highest upper confidence bound, which is a fancy " + "LATS is based on a (greedy) Monte-Carlo tree search. For each search steps, it picks the node with the highest \"upper confidence bound\", which is a metric that balances exploitation (highest average reward) and exploration (lowest visits). Starting from that node, it generates N (5 in this case) new candidate actions to take, and adds them to the tree. It stops searching either when it has generated a valid solution OR when it has reached the maximum number of rollouts (search tree depth).\n", + "\n", + "![Tree Diagram](./img/tree.png)\n", + "\n", + "Our LangGraph state will be composed of two items:\n", + "1. The root of the search tree\n", + "2. The user input" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 10, "id": "54c6f319-3966-4f66-aa7b-50e249189111", "metadata": {}, "outputs": [], "source": [ "from __future__ import annotations\n", "\n", + "import math\n", "from typing import List, Optional\n", "\n", "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage\n", @@ -122,9 +109,11 @@ " self.value = 0\n", " self.visits = 0\n", " self.reflection = reflection\n", + " self.depth = parent.depth + 1 if parent is not None else 1\n", " self._is_solved = reflection.found_solution if reflection else False\n", " if self._is_solved:\n", " self._mark_tree_as_solved()\n", + " self.backpropagate(reflection.normalized_score)\n", "\n", " def __repr__(self) -> str:\n", " return (\n", @@ -138,6 +127,10 @@ " return self._is_solved\n", "\n", " @property\n", + " def is_terminal(self):\n", + " return not self.children\n", + "\n", + " @property\n", " def best_child(self):\n", " \"\"\"Select the child with the highest UCT to search next.\"\"\"\n", " if not self.children:\n", @@ -149,13 +142,13 @@ " \"\"\"Return the child with the highest value.\"\"\"\n", " if not self.children:\n", " return None\n", - " return max(self.children, key=lambda child: child.value)\n", + " return max(self.children, key=lambda child: int(child.is_solved) * child.value)\n", "\n", " @property\n", - " def depth_below(self) -> int:\n", + " def height(self) -> int:\n", " \"\"\"Check for how far we've rolled out the tree.\"\"\"\n", " if self.children:\n", - " return 1 + max([child.depth_below for child in self.children])\n", + " return 1 + max([child.height for child in self.children])\n", " return 1\n", "\n", " def upper_confidence_bound(self, exploration_weight=1.0):\n", @@ -170,28 +163,48 @@ " exploration_term = math.sqrt(math.log(self.parent.visits) / self.visits)\n", " return average_reward + exploration_weight * exploration_term\n", "\n", - " def update_reward(self, reward: float):\n", + " def backpropagate(self, reward: float):\n", " \"\"\"Update the score of this node and its parents.\"\"\"\n", - " self.visits += 1\n", - " self.value += reward\n", - " parent = self.parent\n", - " while parent:\n", - " parent.value += reward\n", - " parent = parent.parent\n", + " node = self\n", + " while node:\n", + " node.visits += 1\n", + " node.value = (node.value * (node.visits - 1) + reward) / node.visits\n", + " node = node.parent\n", "\n", - " def get_messages(self) -> List[BaseMessage]:\n", + " def get_messages(self, include_reflections: bool = True):\n", + " if include_reflections:\n", + " return self.messages + [self.reflection.as_message()]\n", + " return self.messages\n", + "\n", + " def get_trajectory(self, include_reflections: bool = True) -> List[BaseMessage]:\n", " \"\"\"Get messages representing this search branch.\"\"\"\n", " messages = []\n", - " parent = self.parent\n", - " while parent:\n", + " node = self\n", + " while node:\n", " messages.extend(\n", - " [\n", - " HumanMessage(content=self.reflection),\n", - " self.messages,\n", - " ]\n", + " node.get_messages(include_reflections=include_reflections)[::-1]\n", " )\n", + " node = node.parent\n", + " # Reverse the final back-tracked trajectory to return in the correct order\n", " return messages[::-1] # root solution, reflection, child 1, ...\n", "\n", + " def get_best_solution(self):\n", + " \"\"\"Return the best solution from within the current sub-tree.\"\"\"\n", + " all_nodes = [self]\n", + " nodes = deque()\n", + " nodes.append(self)\n", + " while nodes:\n", + " node = nodes.popleft()\n", + " all_nodes.extend(node.children)\n", + " for n in node.children:\n", + " nodes.append(n)\n", + " best_node = max(\n", + " all_nodes,\n", + " # We filter out all non-terminal, non-solution trajectories\n", + " key=lambda node: int(node.is_terminal and node.is_solved) * node.value,\n", + " )\n", + " return best_node\n", + "\n", " def _mark_tree_as_solved(self):\n", " parent = self.parent\n", " while parent:\n", @@ -199,9 +212,19 @@ " parent = parent.parent" ] }, + { + "cell_type": "markdown", + "id": "cdd3111f-b860-471f-8784-1d5e3783910d", + "metadata": {}, + "source": [ + "#### The graph state itself\n", + "\n", + "The main component is the tree, represented by the root node." + ] + }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 11, "id": "e10c94ba-9daa-4899-97ce-4f28428c2c38", "metadata": {}, "outputs": [], @@ -212,9 +235,35 @@ "class TreeState(TypedDict):\n", " # The full tree\n", " root: Node\n", - " # The rolled-out steps so far\n", - " input: str\n", - " solution: AIMessage" + " # The original input\n", + " input: str" + ] + }, + { + "cell_type": "markdown", + "id": "2e8ddf25-d040-4e1f-87bd-5837ff105845", + "metadata": {}, + "source": [ + "## Define Language Agent\n", + "\n", + "Our agent will have three primary LLM-powered processes:\n", + "1. Reflect: score the action based on the tool response.\n", + "2. Initial response: to create the root node and start the search.\n", + "3. Expand: generate 5 candidate \"next steps\" from the best spot in the current tree\n", + "\n", + "For more \"Grounded\" tool applications (such as code synthesis), you could integrate code execution into the reflection/reward step. This type of external feedback is very useful (though adds complexity to an already complicated example notebook)." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "48738896-42ac-47eb-b482-0d4d4dd86c87", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_openai import ChatOpenAI\n", + "\n", + "llm = ChatOpenAI(model=\"gpt-3.5-turbo\")" ] }, { @@ -222,12 +271,14 @@ "id": "5d460856-e26d-4430-910e-0aac58563612", "metadata": {}, "source": [ - "## Tools" + "#### Tools\n", + "\n", + "For our example, we will give the language agent a search engine." ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 13, "id": "55c2aff3-f454-43da-8f45-1a3d46523cd5", "metadata": {}, "outputs": [], @@ -244,51 +295,107 @@ }, { "cell_type": "markdown", - "id": "2e8ddf25-d040-4e1f-87bd-5837ff105845", + "id": "1c611f1e-74b4-4157-997c-face8ad409a4", "metadata": {}, "source": [ - "## Define Agent" + "### Reflection\n", + "\n", + "The reflection chain will score agent outputs based on the decision and the tool responses.\n", + "We will call this within the other two nodes." ] }, { "cell_type": "code", - "execution_count": 30, - "id": "e524c626-d533-4c20-a696-902fdee493ec", + "execution_count": 14, + "id": "ddfd1750-c265-4b29-b505-83b1c5e2d30e", "metadata": {}, "outputs": [], "source": [ - "# from collections import defaultdict\n", - "# from typing import List\n", - "\n", + "from langchain.chains import create_structured_output_runnable\n", "from langchain.output_parsers.openai_tools import (\n", " JsonOutputToolsParser,\n", " PydanticToolsParser,\n", - ")" + ")\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_core.runnables import chain as as_runnable\n", + "\n", + "\n", + "class Reflection(BaseModel):\n", + " reflections: str = Field(\n", + " description=\"The critique and reflections on the sufficiency, superfluency,\"\n", + " \" and general quality of the response\"\n", + " )\n", + " score: int = Field(\n", + " description=\"Score from 0-10 on the quality of the candidate response.\",\n", + " gte=0,\n", + " lte=10,\n", + " )\n", + " found_solution: bool = Field(\n", + " description=\"Whether the response has fully solved the question or task.\"\n", + " )\n", + "\n", + " def as_message(self):\n", + " return HumanMessage(\n", + " content=f\"Reasoning: {self.reflections}\\nScore: {self.score}\"\n", + " )\n", + "\n", + " @property\n", + " def normalized_score(self) -> float:\n", + " return self.score / 10.0\n", + "\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"Reflect and grade the assistant response to the user question below.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"candidate\"),\n", + " ]\n", + ")\n", + "\n", + "reflection_llm_chain = (\n", + " prompt\n", + " | llm.bind_tools(tools=[Reflection], tool_choice=\"Reflection\").with_config(\n", + " run_name=\"Reflection\"\n", + " )\n", + " | PydanticToolsParser(tools=[Reflection])\n", + ")\n", + "\n", + "\n", + "@as_runnable\n", + "def reflection_chain(inputs) -> Reflection:\n", + " tool_choices = reflection_llm_chain.invoke(inputs)\n", + " reflection = tool_choices[0]\n", + " if not isinstance(inputs[\"candidate\"][-1], AIMessage):\n", + " reflection.found_solution = False\n", + " return reflection" ] }, { "cell_type": "markdown", - "id": "aae256cc-1f62-4a39-908a-4d8c8d059a14", + "id": "4e47dfb2-4ab3-4a31-b117-f07786b357cb", "metadata": {}, "source": [ - "#### Generate the initial candidate" + "### Initial Response\n", + "\n", + "We start with a single root node, generated by this first step. It responds to the user input either with a tool invocation or a response." ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 15, "id": "72fc5363-f0f3-4362-8499-14eb583bd75b", "metadata": {}, "outputs": [], "source": [ - "import datetime\n", "from typing import List\n", "\n", "from langchain_core.prompt_values import ChatPromptValue\n", - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", "from langchain_core.pydantic_v1 import BaseModel, Field, ValidationError\n", "from langchain_core.runnables import RunnableConfig\n", - "from langchain_openai import ChatOpenAI\n", "\n", "prompt_template = ChatPromptTemplate.from_messages(\n", " [\n", @@ -301,24 +408,28 @@ " ]\n", ")\n", "\n", - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", "\n", - "initial_answer_chain = prompt_template | llm.bind_tools(tools=tools)" + "initial_answer_chain = prompt_template | llm.bind_tools(tools=tools).with_config(\n", + " run_name=\"GenerateInitialCandidate\"\n", + ")\n", + "\n", + "\n", + "parser = JsonOutputToolsParser(return_id=True)" ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 16, "id": "7207f913-a6db-4ef9-a98d-ecb8612b23d5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_qmSe7jJJ3yIcjNqqJHSPlGWk', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]})" + "AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_APBQsd15wnSNPhyghCvFrNC8', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]})" ] }, - "execution_count": 72, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -332,15 +443,59 @@ }, { "cell_type": "markdown", - "id": "34452e88-e33a-474c-9623-075d1f434dda", + "id": "7a7d34a6-cee0-4321-989a-963ca4b2caeb", "metadata": {}, "source": [ - "#### Generate N candidates" + "#### Starting Node\n", + "\n", + "We will package up the candidate generation and reflection in a single node of our graph. This is represented by the following function:" ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 17, + "id": "5b6b173c-78f5-4ae1-80b3-28c80e68f5c5", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "\n", + "# Define the node we will add to the graph\n", + "def generate_initial_response(state: TreeState) -> dict:\n", + " \"\"\"Generate the initial candidate response.\"\"\"\n", + " res = initial_answer_chain.invoke({\"input\": state[\"input\"]})\n", + " parsed = parser.invoke(res)\n", + " tool_responses = tool_executor.batch(\n", + " [ToolInvocation(tool=r[\"type\"], tool_input=r[\"args\"]) for r in parsed]\n", + " )\n", + " output_messages = [res] + [\n", + " ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n", + " for resp, tool_call in zip(tool_responses, parsed)\n", + " ]\n", + " reflection = reflection_chain.invoke(\n", + " {\"input\": state[\"input\"], \"candidate\": output_messages}\n", + " )\n", + " root = Node(output_messages, reflection=reflection)\n", + " return {\n", + " **state,\n", + " \"root\": root,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "34452e88-e33a-474c-9623-075d1f434dda", + "metadata": {}, + "source": [ + "### Candidate Generation\n", + "\n", + "The following code prompts the same LLM to generate N additional candidates to check." + ] + }, + { + "cell_type": "code", + "execution_count": 18, "id": "550bff9a-86aa-43ad-ad98-506e97c122d2", "metadata": {}, "outputs": [], @@ -353,7 +508,11 @@ " n = config[\"configurable\"].get(\"N\", 5)\n", " bound_kwargs = llm.bind_tools(tools=tools).kwargs\n", " chat_result = llm.generate(\n", - " [messages.to_messages()], n=n, callbacks=config[\"callbacks\"], **bound_kwargs\n", + " [messages.to_messages()],\n", + " n=n,\n", + " callbacks=config[\"callbacks\"],\n", + " run_name=\"GenerateCandidates\",\n", + " **bound_kwargs\n", " )\n", " return [gen.message for gen in chat_result.generations[0]]\n", "\n", @@ -363,180 +522,130 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 19, "id": "e368e61f-8150-4fd6-b3fd-208d1f0ddc9c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_5DMq9O6BIden7lLraFH0NuYZ', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_5DMq9O6BIden7lLraFH0NuYZ', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_5DMq9O6BIden7lLraFH0NuYZ', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_5DMq9O6BIden7lLraFH0NuYZ', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_5DMq9O6BIden7lLraFH0NuYZ', 'function': {'arguments': '{\"query\":\"lithium pollution research report\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]})]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "res = expansion_chain.invoke({\"input\": \"Write a research report on lithium pollution.\"})" + "res = expansion_chain.invoke({\"input\": \"Write a research report on lithium pollution.\"})\n", + "res" ] }, { "cell_type": "markdown", - "id": "a0c841ae-4c1d-467f-bdf8-3d39ce4f7794", + "id": "88ecf775-29ed-4ebd-8297-d1aa3cda3f9b", "metadata": {}, "source": [ - "#### Reflect on outputs" + "#### Candidate generation node\n", + "\n", + "We will package the candidate generation and reflection steps in the following \"expand\" node.\n", + "We do all the operations as a batch process to speed up execution." ] }, { "cell_type": "code", - "execution_count": 60, - "id": "a6374041-c91b-44eb-b1a2-530d1579b2bc", + "execution_count": 20, + "id": "d32af859-53e8-46be-8182-7d522be31f54", "metadata": {}, "outputs": [], "source": [ - "from langchain.chains import create_structured_output_runnable\n", - "\n", - "\n", - "class Reflection(BaseModel):\n", - " reflections: str = Field(\n", - " description=\"The critique and reflections on the sufficiency, superfluency,\"\n", - " \" and general quality of the response\"\n", - " )\n", - " score: int = Field(\n", - " description=\"Score from 1-10 on the quality of the candidate response.\"\n", - " )\n", - " found_solution: bool = Field(\n", - " description=\"Whether the response has fully solved the question or task.\"\n", - " )\n", - "\n", - "\n", - "prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"Reflect and grade the assistant response to the user question below.\",\n", - " ),\n", - " (\"user\", \"{input}\"),\n", - " MessagesPlaceholder(variable_name=\"candidate\"),\n", - " (\n", - " \"system\",\n", - " \"Reflect on the assistant response above, critique, and score the response.\",\n", - " ),\n", - " ]\n", - ")\n", - "reflection_chain = create_structured_output_runnable(\n", - " output_schema=Reflection, llm=llm, prompt=prompt\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "id": "c01ae663-56fe-40fc-bac2-29f0bc7765a5", - "metadata": {}, - "outputs": [], - "source": [ - "# n = 5 # number of generated actions (beam width)\n", - "# w = 1 # Exploration weight.\n", - "# L = 5 # Depth limit / max rollout\n", - "# K = 3 # Number of rollouts" - ] - }, - { - "cell_type": "markdown", - "id": "0c3bc054-5cfb-4ab8-b1c5-c9d5669ed16d", - "metadata": {}, - "source": [ - "## Define Graph\n", - "\n", - "Now we may construct the actual graph.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "id": "47b43e08-ee04-4121-a294-fbbd5a4e3995", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "import operator\n", - "from collections import deque\n", - "\n", - "from langgraph.graph import StateGraph\n", - "\n", - "parser = JsonOutputToolsParser(return_id=True)\n", - "\n", - "\n", - "def start(state: TreeState) -> dict:\n", - " res = initial_answer_chain.invoke({\"input\": state[\"input\"]})\n", - " parsed = parser.invoke(res)\n", - " tool_responses = tool_executor.batch(\n", - " [ToolInvocation(tool=r[\"type\"], tool_input=r[\"args\"]) for r in parsed]\n", - " )\n", - " output_messages = [res] + [\n", - " ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n", - " for resp, tool_call in zip(tool_responses, parsed)\n", - " ]\n", - " print(output_messages)\n", - " reflection = reflection_chain.invoke(\n", - " {\"input\": state[\"input\"], \"candidate\": output_messages}\n", - " )\n", - " root = Node(output_messages, reflection=reflection)\n", - " return {\n", - " **state,\n", - " \"root\": root,\n", - " }\n", + "from collections import defaultdict, deque\n", "\n", "\n", "def expand(state: TreeState, config: RunnableConfig) -> dict:\n", + " \"\"\"Starting from the \"best\" node in the tree, generate N candidates for the next step.\"\"\"\n", " root = state[\"root\"]\n", " best_candidate: Node = root.best_child if root.children else root\n", - " messages = best_candidate.get_messages()\n", + " messages = best_candidate.get_trajectory()\n", " # Generate N candidates from the single child candidate\n", " new_candidates = expansion_chain.invoke(\n", " {\"input\": state[\"input\"], \"messages\": messages}, config\n", " )\n", " parsed = parser.batch(new_candidates)\n", + " flattened = [\n", + " (i, tool_call)\n", + " for i, tool_calls in enumerate(parsed)\n", + " for tool_call in tool_calls\n", + " ]\n", " tool_responses = tool_executor.batch(\n", - " [ToolInvocation(tool=r[\"type\"], tool_input=r[\"args\"]) for r in res]\n", + " [\n", + " ToolInvocation(tool=tool_call[\"type\"], tool_input=tool_call[\"args\"])\n", + " for _, tool_call in flattened\n", + " ]\n", " )\n", + " collected_responses = defaultdict(list)\n", + " for (i, tool_call), resp in zip(flattened, tool_responses):\n", + " collected_responses[i].append(\n", + " ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n", + " )\n", + " output_messages = []\n", + " for i, candidate in enumerate(new_candidates):\n", + " output_messages.append([candidate] + collected_responses[i])\n", "\n", " # Reflect on each candidate\n", " # For tasks with external validation, you'd add that here.\n", " reflections = reflection_chain.batch(\n", - " [{\"input\": state[\"input\"], \"candidate\": [msg]} for msg in new_candidates],\n", + " [{\"input\": state[\"input\"], \"candidate\": msges} for msges in output_messages],\n", " config,\n", " )\n", " # Grow tree\n", " child_nodes = [\n", " Node(cand, parent=best_candidate, reflection=reflection)\n", - " for cand, reflection in zip(new_candidates, reflections)\n", + " for cand, reflection in zip(output_messages, reflections)\n", " ]\n", " best_candidate.children.extend(child_nodes)\n", " # We have already extended the tree directly, so we just return the state\n", - " return state\n", + " return state" + ] + }, + { + "cell_type": "markdown", + "id": "84bad5da-645d-4c6a-83dd-8c852f21f622", + "metadata": {}, + "source": [ + "## Create Graph\n", "\n", - "\n", - "def select_solution(state: TreeState):\n", - " all_nodes = [state[\"root\"]]\n", - " nodes = deque()\n", - " nodes.append(state[\"root\"])\n", - " while nodes:\n", - " node = nodes.popleft()\n", - " all_nodes.extend(node.children)\n", - " for n in node.children:\n", - " nodes.append(n)\n", - " # TODO: Diff between value and reward?\n", - " best_node = max(all_nodes, key=lambda node: node.value)\n", - " return {**state, \"solution\": best_node.solution}\n", + "With those two nodes defined, we are ready to define the graph. After each agent step, we have the option of finishing." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "8aec0f20-f978-4df0-8900-e3a1f0544f6d", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import END, StateGraph\n", "\n", "\n", "def should_loop(state: TreeState):\n", + " \"\"\"Determine whether to continue the tree search.\"\"\"\n", " root = state[\"root\"]\n", " if root.is_solved:\n", - " return \"select_solution\"\n", - " if root.depth_below > 5:\n", - " return \"select_solution\"\n", + " return END\n", + " if root.height > 5:\n", + " return END\n", " return \"expand\"\n", "\n", "\n", "builder = StateGraph(TreeState)\n", - "builder.add_node(\"start\", start)\n", + "builder.add_node(\"start\", generate_initial_response)\n", "builder.add_node(\"expand\", expand)\n", - "builder.add_node(\"select_solution\", select_solution)\n", "builder.set_entry_point(\"start\")\n", "\n", "\n", @@ -551,13 +660,20 @@ " should_loop,\n", ")\n", "\n", - "builder.set_finish_point(\"select_solution\")\n", "graph = builder.compile()" ] }, + { + "cell_type": "markdown", + "id": "1383d69c-1d90-43f5-987e-c7fc4c3a24f8", + "metadata": {}, + "source": [ + "## Invoke" + ] + }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 22, "id": "92392fb3-8431-4649-9e78-2cc160e96ec1", "metadata": {}, "outputs": [ @@ -565,69 +681,131 @@ "name": "stdout", "output_type": "stream", "text": [ - "[AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_w2aqRiOdWwXbxytaNcQRKeBU', 'function': {'arguments': '{\"query\":\"49ers vs Chiefs current score\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}), ToolMessage(content='[{\"url\": \"https://www.cbssports.com/nfl/news/2024-super-bowl-chiefs-vs-49ers-score-patrick-mahomes-leads-ot-comeback-as-k-c-wins-back-to-back-titles/live/\", \"content\": \"The championship-winning drive, which included a fourth-and-1 scramble from Mahomes and a clutch 7-yard catch from tight end Travis Kelce, was a must-score for K.C. The NFL\\'s new playoff overtime rules -- both teams are guaranteed at least one possession in the extra period -- were in effect for the first time, and the Chiefs needed to answer the Niners\\' field goal.\\\\n Held out of the end zone until that point, Kansas City grabbed its first lead of the game at 13-10.\\\\nJennings\\' touchdown receiving (followed by a missed extra point) concluded a 75-yard drive that put the Niners back on top, 16-13, as the wideout joined former Philadelphia Eagles quarterback Nick Foles as the only players to throw and catch a touchdown in a Super Bowl.\\\\n He spread the ball around -- eight pass-catchers had at least two receptions -- slowly but surely overcoming a threatening 49ers defense that knocked him off his spot consistently in the first half.\\\\nMahomes, with his third Super Bowl MVP, now sits alongside Tom Brady (five) and Joe Montana (three) atop the mountain while becoming just the third player to win the award back-to-back, joining Bart Starr (I-II) and Terry Bradshaw (XIII-XIV).\\\\n The muffed punt that bounced off of cornerback Darrell Luter Jr.\\'s ankle was also the big break that the Chiefs needed as they scored on the very next play to take the lead for the first time in the game. College Pick\\'em\\\\nA Daily SportsLine Betting Podcast\\\\nNFL Playoff Time!\\\\n2024 Super Bowl, Chiefs vs. 49ers score: Patrick Mahomes leads OT comeback as K.C. wins back-to-back titles\\\\nCall it a dynasty; the Chiefs are the first team to win consecutive Super Bowls since 2003-04\\\\nThe Kansas City Chiefs are Super Bowl champions, again.\"}, {\"url\": \"https://www.nbcnews.com/news/sports/live-blog/super-bowl-chiefs-49ers-score-live-updates-rcna136833\", \"content\": \"Profile\\\\nSections\\\\ntv\\\\nFeatured\\\\nMore From NBC\\\\nFollow NBC News\\\\nnews Alerts\\\\nThere are no new alerts at this time\\\\nSuper Bowl LVIII\\\\nSuper Bowl 2024 live updates: 49ers vs. Chiefs how to watch, kickoff time, pregame, Taylor Swift arrival\\\\nEverything you need to know about Super Bowl 58:\\\\nJason Abbruzzese\\\\nAnd we\\'re under way. \\\\u201cDespite the 12-hour flight and 17-hour time difference, the Embassy can confidently Speak Now to say that if she departs Tokyo in the evening after her concert, she should comfortably arrive in Las Vegas before the Super Bowl begins.\\\\u201d\\\\nNBC News\\\\nFootball fans across the country were celebrating after they were surprised with tickets to the Super Bowl in Las Vegas. Mahomes warms up\\\\nNBC News\\\\nICYMI: A person tried to climb the Vegas Sphere (and got arrested)\\\\nSaba Hamedy\\\\nLas Vegas police confirmed last Wednesday that a man was arrested after he climbed a structure on the 200 block of Sands Avenue, where the Sphere is located.\\\\n President Biden is skipping a Super Bowl interview\\\\nMonica Alba\\\\nJonathan Allen\\\\nFor the second year in a row, President Joe Biden is passing on the opportunity to sit down for a Super Bowl interview that could reach millions of Americans on Sunday \\\\u2014 a move his advisers say is part of their larger communication strategy.\\\\n Can the Niners tie the Patriots and Steelers?\\\\nClaire Cardona\\\\nAllan Smith\\\\nThe Kansas City Chiefs, the reigning Super Bowl champs, could go two in a row if they beat the San Francisco 49ers.\\\\n\"}, {\"url\": \"https://www.bbc.co.uk/sport/american-football/live/ceqj69d5y8yt\", \"content\": \"Super Bowl 2024 LIVE: Kansas City Chiefs v San Francisco 49ers - live score updates, start time, half-time show, Taylor Swift\"}, {\"url\": \"https://www.sportingnews.com/us/nfl/news/super-bowl-2024-live-score-49ers-chiefs-results-highlights/0c440aa7145b809ed174d8ff\", \"content\": \"Super Bowl start time\\\\nSuper Bowl 58 between the Chiefs and 49ers is set to kick off at 6:30 p.m. ET (3:30 p.m. local time) in Las Vegas, Nev.\\\\n6:30 p.m. ET has become the standard start time for Super Bowls and is preceded by performances of \\\\\"Lift Every Voice and Sing,\\\\\" \\\\\"America the Beautiful,\\\\\" and \\\\\"The Star-Spangled Banner. Brock Purdy drops a dime to Chris Conley on 3rd & 9 \\\\ud83c\\\\udfaf\\\\n\\\\ud83d\\\\udcfa: #SBLVIII on CBS\\\\n\\\\ud83d\\\\udcf1: Stream on #NFLPlus https://t.co/dClcEDViWl pic.twitter.com/Oa10d7khdl\\\\n7:10 p.m. \\\\u2014 The 49ers are relying heavily on McCaffrey, who takes the ball on each of the first three snaps on this drive. \\\\ud83d\\\\udcfa: #SBLVIII on CBS\\\\n\\\\ud83d\\\\udcf1: Stream on #NFLPlus https://t.co/dClcEDViWl pic.twitter.com/yUc00MtP84\\\\n7:24 p.m. \\\\u2014 The 49ers force a 3rd & 1, but Rashee Rice is easily able to pick up the first and one or two more yards.\\\\n49ers 3, Chiefs 0\\\\n7:19 p.m. MORE SUPER BOWL 58:\\\\n\\\\u2022\\\\u00a0 Inside Taylor Swift and Travis Kelce\\'s whirlwind dating timeline\\\\n\\\\u2022. Ranking the thriving Mike and Kyle Shanahan coaching tree\\\\n\\\\u2022. Tracking every Super Bowl 58 commercial\\\\nWhat channel is the Super Bowl on?\\\\nSuper Bowl 58 will be broadcast nationally on CBS. PURDY TO JENNINGS TO CMC FOR SIX \\\\ud83d\\\\udd25\\\\n\\\\ud83d\\\\udcfa: #SBLVIII on CBS\\\\n\\\\ud83d\\\\udcf1: Stream on #NFLPlus https://t.co/dClcEDViWl pic.twitter.com/ktiTXIiHzS\\\\n7:47 p.m. \\\\u2014 L\\'Jarius Sneed, who was on the other side of this two weeks ago, gets hit with an unsportsmanlike conduct penalty and and gives the 49ers their 11th first down.\"}, {\"url\": \"https://www.msn.com/en-us/sports/nfl/super-bowl-2024-live-score-49ers-vs-chiefs-updates-highlights-results-from-las-vegas/ar-BB1i7cim\", \"content\": \"7:02 p.m. \\\\u2014 Pacheco starts the Chiefs\\' second drive with a nice run for a first down run. 6:58 p.m. \\\\u2014 Kyle Shanahan plays it ultra-conservative and only gets the 49ers some extra field ...\"}]', tool_call_id='call_w2aqRiOdWwXbxytaNcQRKeBU')]\n" - ] - }, - { - "ename": "BadRequestError", - "evalue": "Error code: 400 - {'error': {'message': \"Invalid parameter: 'tool_calls' cannot be used when 'functions' are present. Please use 'tools' instead of 'functions'.\", 'type': 'invalid_request_error', 'param': 'messages.[2].tool_calls', 'code': None}}", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mBadRequestError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[77], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[43mgraph\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minput\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mWhat\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43ms the score of the 49\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mrs chiefs game?\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langgraph/pregel/__init__.py:579\u001b[0m, in \u001b[0;36mPregel.invoke\u001b[0;34m(self, input, config, output_keys, input_keys, **kwargs)\u001b[0m\n\u001b[1;32m 569\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 570\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 571\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 576\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 577\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 578\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--> 579\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 580\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 581\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 582\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 583\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 584\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 585\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 586\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 587\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m latest\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langgraph/pregel/__init__.py:615\u001b[0m, in \u001b[0;36mPregel.transform\u001b[0;34m(self, input, config, output_keys, input_keys, **kwargs)\u001b[0m\n\u001b[1;32m 606\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtransform\u001b[39m(\n\u001b[1;32m 607\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 608\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 613\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 614\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--> 615\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 616\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 617\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 618\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 619\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 620\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 621\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 622\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 623\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:1497\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 1495\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1496\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m-> 1497\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 1498\u001b[0m \u001b[38;5;28;01myield\u001b[39;00m chunk\n\u001b[1;32m 1499\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m final_output_supported:\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langgraph/pregel/__init__.py:355\u001b[0m, in \u001b[0;36mPregel._transform\u001b[0;34m(self, input, run_manager, config, input_keys, output_keys, interrupt)\u001b[0m\n\u001b[1;32m 348\u001b[0m done, inflight \u001b[38;5;241m=\u001b[39m concurrent\u001b[38;5;241m.\u001b[39mfutures\u001b[38;5;241m.\u001b[39mwait(\n\u001b[1;32m 349\u001b[0m futures,\n\u001b[1;32m 350\u001b[0m return_when\u001b[38;5;241m=\u001b[39mconcurrent\u001b[38;5;241m.\u001b[39mfutures\u001b[38;5;241m.\u001b[39mFIRST_EXCEPTION,\n\u001b[1;32m 351\u001b[0m timeout\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstep_timeout,\n\u001b[1;32m 352\u001b[0m )\n\u001b[1;32m 354\u001b[0m \u001b[38;5;66;03m# interrupt on failure or timeout\u001b[39;00m\n\u001b[0;32m--> 355\u001b[0m \u001b[43m_interrupt_or_proceed\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdone\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minflight\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstep\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 357\u001b[0m \u001b[38;5;66;03m# apply writes to channels\u001b[39;00m\n\u001b[1;32m 358\u001b[0m _apply_writes(\n\u001b[1;32m 359\u001b[0m checkpoint, channels, pending_writes, config, step \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m 360\u001b[0m )\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langgraph/pregel/__init__.py:698\u001b[0m, in \u001b[0;36m_interrupt_or_proceed\u001b[0;34m(done, inflight, step)\u001b[0m\n\u001b[1;32m 696\u001b[0m inflight\u001b[38;5;241m.\u001b[39mpop()\u001b[38;5;241m.\u001b[39mcancel()\n\u001b[1;32m 697\u001b[0m \u001b[38;5;66;03m# raise the exception\u001b[39;00m\n\u001b[0;32m--> 698\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exc\n\u001b[1;32m 699\u001b[0m \u001b[38;5;66;03m# TODO this is where retry of an entire step would happen\u001b[39;00m\n\u001b[1;32m 701\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m inflight:\n\u001b[1;32m 702\u001b[0m \u001b[38;5;66;03m# if we got here means we timed out\u001b[39;00m\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/concurrent/futures/thread.py:58\u001b[0m, in \u001b[0;36m_WorkItem.run\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 55\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[1;32m 57\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 58\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 59\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 exc:\n\u001b[1;32m 60\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfuture\u001b[38;5;241m.\u001b[39mset_exception(exc)\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:4064\u001b[0m, in \u001b[0;36mRunnableBindingBase.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 4058\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 4059\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 4060\u001b[0m \u001b[38;5;28minput\u001b[39m: Input,\n\u001b[1;32m 4061\u001b[0m config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 4062\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Optional[Any],\n\u001b[1;32m 4063\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Output:\n\u001b[0;32m-> 4064\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbound\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 4065\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4066\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_merge_configs\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4067\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\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\u001b[43m,\u001b[49m\n\u001b[1;32m 4068\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:2053\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 2051\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2052\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-> 2053\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 2054\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2055\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 2056\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2057\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 2058\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2059\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2060\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 2061\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/langchain/libs/core/langchain_core/runnables/base.py:3507\u001b[0m, in \u001b[0;36mRunnableLambda.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 3505\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Invoke this runnable synchronously.\"\"\"\u001b[39;00m\n\u001b[1;32m 3506\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m-> 3507\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3508\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_invoke\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3509\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3510\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3511\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 3512\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3513\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 3514\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[1;32m 3515\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot invoke a coroutine function synchronously.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 3516\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUse `ainvoke` instead.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 3517\u001b[0m )\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:1246\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[0;34m(self, func, input, config, run_type, **kwargs)\u001b[0m\n\u001b[1;32m 1242\u001b[0m context \u001b[38;5;241m=\u001b[39m copy_context()\n\u001b[1;32m 1243\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(var_child_runnable_config\u001b[38;5;241m.\u001b[39mset, child_config)\n\u001b[1;32m 1244\u001b[0m output \u001b[38;5;241m=\u001b[39m cast(\n\u001b[1;32m 1245\u001b[0m Output,\n\u001b[0;32m-> 1246\u001b[0m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1247\u001b[0m \u001b[43m \u001b[49m\u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1248\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 1249\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 1250\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1251\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1252\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 1253\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 1254\u001b[0m )\n\u001b[1;32m 1255\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\u001b[1;32m 1256\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/config.py:326\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m 325\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 326\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\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", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:3383\u001b[0m, in \u001b[0;36mRunnableLambda._invoke\u001b[0;34m(self, input, run_manager, config, **kwargs)\u001b[0m\n\u001b[1;32m 3381\u001b[0m output \u001b[38;5;241m=\u001b[39m chunk\n\u001b[1;32m 3382\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 3383\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3384\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m 3385\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3386\u001b[0m \u001b[38;5;66;03m# If the output is a runnable, invoke it\u001b[39;00m\n\u001b[1;32m 3387\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(output, Runnable):\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/config.py:326\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m 325\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 326\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\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", - "Cell \u001b[0;32mIn[76], line 21\u001b[0m, in \u001b[0;36mstart\u001b[0;34m(state)\u001b[0m\n\u001b[1;32m 16\u001b[0m output_messages \u001b[38;5;241m=\u001b[39m [res] \u001b[38;5;241m+\u001b[39m [\n\u001b[1;32m 17\u001b[0m ToolMessage(content\u001b[38;5;241m=\u001b[39mjson\u001b[38;5;241m.\u001b[39mdumps(resp), tool_call_id\u001b[38;5;241m=\u001b[39mtool_call[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mid\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m resp, tool_call \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(tool_responses, parsed)\n\u001b[1;32m 19\u001b[0m ]\n\u001b[1;32m 20\u001b[0m \u001b[38;5;28mprint\u001b[39m(output_messages)\n\u001b[0;32m---> 21\u001b[0m reflection \u001b[38;5;241m=\u001b[39m \u001b[43mreflection_chain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 22\u001b[0m \u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minput\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstate\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minput\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcandidate\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43moutput_messages\u001b[49m\u001b[43m}\u001b[49m\n\u001b[1;32m 23\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 24\u001b[0m root \u001b[38;5;241m=\u001b[39m Node(output_messages, reflection\u001b[38;5;241m=\u001b[39mreflection)\n\u001b[1;32m 25\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[1;32m 26\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mstate,\n\u001b[1;32m 27\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mroot\u001b[39m\u001b[38;5;124m\"\u001b[39m: root,\n\u001b[1;32m 28\u001b[0m }\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/runnables/base.py:2053\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 2051\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2052\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-> 2053\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 2054\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2055\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 2056\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2057\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 2058\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2059\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2060\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 2061\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/langchain/libs/core/langchain_core/runnables/base.py:4064\u001b[0m, in \u001b[0;36mRunnableBindingBase.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 4058\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 4059\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 4060\u001b[0m \u001b[38;5;28minput\u001b[39m: Input,\n\u001b[1;32m 4061\u001b[0m config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 4062\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Optional[Any],\n\u001b[1;32m 4063\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Output:\n\u001b[0;32m-> 4064\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbound\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 4065\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4066\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_merge_configs\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4067\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\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\u001b[43m,\u001b[49m\n\u001b[1;32m 4068\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/language_models/chat_models.py:166\u001b[0m, in \u001b[0;36mBaseChatModel.invoke\u001b[0;34m(self, input, config, stop, **kwargs)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 156\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28minput\u001b[39m: LanguageModelInput,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 162\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m BaseMessage:\n\u001b[1;32m 163\u001b[0m config \u001b[38;5;241m=\u001b[39m ensure_config(config)\n\u001b[1;32m 164\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m cast(\n\u001b[1;32m 165\u001b[0m ChatGeneration,\n\u001b[0;32m--> 166\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgenerate_prompt\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 167\u001b[0m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_convert_input\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 168\u001b[0m \u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 169\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcallbacks\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 170\u001b[0m \u001b[43m \u001b[49m\u001b[43mtags\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtags\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 171\u001b[0m \u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmetadata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 172\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrun_name\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 173\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 174\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mgenerations[\u001b[38;5;241m0\u001b[39m][\u001b[38;5;241m0\u001b[39m],\n\u001b[1;32m 175\u001b[0m )\u001b[38;5;241m.\u001b[39mmessage\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/language_models/chat_models.py:544\u001b[0m, in \u001b[0;36mBaseChatModel.generate_prompt\u001b[0;34m(self, prompts, stop, callbacks, **kwargs)\u001b[0m\n\u001b[1;32m 536\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mgenerate_prompt\u001b[39m(\n\u001b[1;32m 537\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 538\u001b[0m prompts: List[PromptValue],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 542\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m LLMResult:\n\u001b[1;32m 543\u001b[0m prompt_messages \u001b[38;5;241m=\u001b[39m [p\u001b[38;5;241m.\u001b[39mto_messages() \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m prompts]\n\u001b[0;32m--> 544\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgenerate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprompt_messages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcallbacks\u001b[49m\u001b[43m,\u001b[49m\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", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/language_models/chat_models.py:408\u001b[0m, in \u001b[0;36mBaseChatModel.generate\u001b[0;34m(self, messages, stop, callbacks, tags, metadata, run_name, **kwargs)\u001b[0m\n\u001b[1;32m 406\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_managers:\n\u001b[1;32m 407\u001b[0m run_managers[i]\u001b[38;5;241m.\u001b[39mon_llm_error(e, response\u001b[38;5;241m=\u001b[39mLLMResult(generations\u001b[38;5;241m=\u001b[39m[]))\n\u001b[0;32m--> 408\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[1;32m 409\u001b[0m flattened_outputs \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 410\u001b[0m LLMResult(generations\u001b[38;5;241m=\u001b[39m[res\u001b[38;5;241m.\u001b[39mgenerations], llm_output\u001b[38;5;241m=\u001b[39mres\u001b[38;5;241m.\u001b[39mllm_output)\n\u001b[1;32m 411\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results\n\u001b[1;32m 412\u001b[0m ]\n\u001b[1;32m 413\u001b[0m llm_output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_combine_llm_outputs([res\u001b[38;5;241m.\u001b[39mllm_output \u001b[38;5;28;01mfor\u001b[39;00m res \u001b[38;5;129;01min\u001b[39;00m results])\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/language_models/chat_models.py:398\u001b[0m, in \u001b[0;36mBaseChatModel.generate\u001b[0;34m(self, messages, stop, callbacks, tags, metadata, run_name, **kwargs)\u001b[0m\n\u001b[1;32m 395\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(messages):\n\u001b[1;32m 396\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 397\u001b[0m results\u001b[38;5;241m.\u001b[39mappend(\n\u001b[0;32m--> 398\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_generate_with_cache\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 399\u001b[0m \u001b[43m \u001b[49m\u001b[43mm\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 400\u001b[0m \u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 401\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_managers\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mrun_managers\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m 402\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 403\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 404\u001b[0m )\n\u001b[1;32m 405\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\u001b[1;32m 406\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_managers:\n", - "File \u001b[0;32m~/code/lc/langchain/libs/core/langchain_core/language_models/chat_models.py:577\u001b[0m, in \u001b[0;36mBaseChatModel._generate_with_cache\u001b[0;34m(self, messages, stop, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 573\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 574\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAsked to cache, but no cache found at `langchain.cache`.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 575\u001b[0m )\n\u001b[1;32m 576\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m new_arg_supported:\n\u001b[0;32m--> 577\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_generate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 578\u001b[0m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m 579\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 580\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 581\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_generate(messages, stop\u001b[38;5;241m=\u001b[39mstop, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_openai/chat_models/base.py:451\u001b[0m, in \u001b[0;36mChatOpenAI._generate\u001b[0;34m(self, messages, stop, run_manager, stream, **kwargs)\u001b[0m\n\u001b[1;32m 445\u001b[0m message_dicts, params \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_create_message_dicts(messages, stop)\n\u001b[1;32m 446\u001b[0m params \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 447\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams,\n\u001b[1;32m 448\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream\u001b[39m\u001b[38;5;124m\"\u001b[39m: stream} \u001b[38;5;28;01mif\u001b[39;00m stream \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m {}),\n\u001b[1;32m 449\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 450\u001b[0m }\n\u001b[0;32m--> 451\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessages\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmessage_dicts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_create_chat_result(response)\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/openai/_utils/_utils.py:271\u001b[0m, in \u001b[0;36mrequired_args..inner..wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 269\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMissing required argument: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mquote(missing[\u001b[38;5;241m0\u001b[39m])\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 270\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[0;32m--> 271\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\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", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/openai/resources/chat/completions.py:659\u001b[0m, in \u001b[0;36mCompletions.create\u001b[0;34m(self, messages, model, frequency_penalty, function_call, functions, logit_bias, logprobs, max_tokens, n, presence_penalty, response_format, seed, stop, stream, temperature, tool_choice, tools, top_logprobs, top_p, user, extra_headers, extra_query, extra_body, timeout)\u001b[0m\n\u001b[1;32m 608\u001b[0m \u001b[38;5;129m@required_args\u001b[39m([\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m], [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmodel\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mstream\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 609\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcreate\u001b[39m(\n\u001b[1;32m 610\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 657\u001b[0m timeout: \u001b[38;5;28mfloat\u001b[39m \u001b[38;5;241m|\u001b[39m httpx\u001b[38;5;241m.\u001b[39mTimeout \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m|\u001b[39m NotGiven \u001b[38;5;241m=\u001b[39m NOT_GIVEN,\n\u001b[1;32m 658\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ChatCompletion \u001b[38;5;241m|\u001b[39m Stream[ChatCompletionChunk]:\n\u001b[0;32m--> 659\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_post\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 660\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m/chat/completions\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 661\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmaybe_transform\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 662\u001b[0m \u001b[43m \u001b[49m\u001b[43m{\u001b[49m\n\u001b[1;32m 663\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 664\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmodel\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 665\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfrequency_penalty\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfrequency_penalty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 666\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfunction_call\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunction_call\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 667\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfunctions\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunctions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 668\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mlogit_bias\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mlogit_bias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 669\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mlogprobs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mlogprobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 670\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmax_tokens\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_tokens\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 671\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mn\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 672\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpresence_penalty\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpresence_penalty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 673\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mresponse_format\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 674\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseed\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 675\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstop\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 676\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstream\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 677\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtemperature\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 678\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtool_choice\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtool_choice\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 679\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtools\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 680\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtop_logprobs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_logprobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 681\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtop_p\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_p\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 682\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43muser\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43muser\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 683\u001b[0m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 684\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompletion_create_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCompletionCreateParams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 685\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 686\u001b[0m \u001b[43m \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmake_request_options\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 687\u001b[0m \u001b[43m \u001b[49m\u001b[43mextra_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_headers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mextra_query\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_query\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mextra_body\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_body\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\n\u001b[1;32m 688\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 689\u001b[0m \u001b[43m \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mChatCompletion\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 690\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m 691\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mStream\u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatCompletionChunk\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 692\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/openai/_base_client.py:1180\u001b[0m, in \u001b[0;36mSyncAPIClient.post\u001b[0;34m(self, path, cast_to, body, options, files, stream, stream_cls)\u001b[0m\n\u001b[1;32m 1166\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mpost\u001b[39m(\n\u001b[1;32m 1167\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 1168\u001b[0m path: \u001b[38;5;28mstr\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1175\u001b[0m stream_cls: \u001b[38;5;28mtype\u001b[39m[_StreamT] \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 1176\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ResponseT \u001b[38;5;241m|\u001b[39m _StreamT:\n\u001b[1;32m 1177\u001b[0m opts \u001b[38;5;241m=\u001b[39m FinalRequestOptions\u001b[38;5;241m.\u001b[39mconstruct(\n\u001b[1;32m 1178\u001b[0m method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpost\u001b[39m\u001b[38;5;124m\"\u001b[39m, url\u001b[38;5;241m=\u001b[39mpath, json_data\u001b[38;5;241m=\u001b[39mbody, files\u001b[38;5;241m=\u001b[39mto_httpx_files(files), \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions\n\u001b[1;32m 1179\u001b[0m )\n\u001b[0;32m-> 1180\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m cast(ResponseT, \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mopts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m)\u001b[49m)\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/openai/_base_client.py:869\u001b[0m, in \u001b[0;36mSyncAPIClient.request\u001b[0;34m(self, cast_to, options, remaining_retries, stream, stream_cls)\u001b[0m\n\u001b[1;32m 860\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrequest\u001b[39m(\n\u001b[1;32m 861\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 862\u001b[0m cast_to: Type[ResponseT],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 867\u001b[0m stream_cls: \u001b[38;5;28mtype\u001b[39m[_StreamT] \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 868\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ResponseT \u001b[38;5;241m|\u001b[39m _StreamT:\n\u001b[0;32m--> 869\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 870\u001b[0m \u001b[43m \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 871\u001b[0m \u001b[43m \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 872\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 873\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 874\u001b[0m \u001b[43m \u001b[49m\u001b[43mremaining_retries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mremaining_retries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 875\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/openai/_base_client.py:960\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[0;34m(self, cast_to, options, remaining_retries, stream, stream_cls)\u001b[0m\n\u001b[1;32m 957\u001b[0m err\u001b[38;5;241m.\u001b[39mresponse\u001b[38;5;241m.\u001b[39mread()\n\u001b[1;32m 959\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mRe-raising status error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m--> 960\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_make_status_error_from_response(err\u001b[38;5;241m.\u001b[39mresponse) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 962\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_process_response(\n\u001b[1;32m 963\u001b[0m cast_to\u001b[38;5;241m=\u001b[39mcast_to,\n\u001b[1;32m 964\u001b[0m options\u001b[38;5;241m=\u001b[39moptions,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 967\u001b[0m stream_cls\u001b[38;5;241m=\u001b[39mstream_cls,\n\u001b[1;32m 968\u001b[0m )\n", - "\u001b[0;31mBadRequestError\u001b[0m: Error code: 400 - {'error': {'message': \"Invalid parameter: 'tool_calls' cannot be used when 'functions' are present. Please use 'tools' instead of 'functions'.\", 'type': 'invalid_request_error', 'param': 'messages.[2].tool_calls', 'code': None}}" + "start\n", + "rolled out: 1\n", + "---\n", + "expand\n", + "rolled out: 2\n", + "---\n", + "expand\n", + "rolled out: 3\n", + "---\n", + "__end__\n", + "rolled out: 3\n", + "---\n" ] } ], "source": [ - "res = graph.invoke({\"input\": \"What's the score of the 49'rs chiefs game?\"})" - ] - }, - { - "cell_type": "raw", - "id": "582c3a7f-4076-4f39-8ba5-8a17df5a4ee9", - "metadata": {}, - "source": [ - "from datasets import load_dataset\n", - "\n", - "dataset = load_dataset(\"deepmind/code_contests\", split=\"valid\")\n", - "dataset[\"validation\"]" + "question = \"Generate a table with the average size and weight, as well as the oldest recorded instance for each of the top 5 most common birds.\"\n", + "for step in graph.stream({\"input\": question}):\n", + " step_name, step_state = next(iter(step.items()))\n", + " print(step_name)\n", + " print(\"rolled out: \", step_state[\"root\"].height)\n", + " print(\"---\")" ] }, { "cell_type": "code", - "execution_count": null, - "id": "ed5d4701-8a0e-4070-aa86-3aed0e62e708", + "execution_count": 25, + "id": "37a9e785-9909-4b56-b9be-da484e3711e1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The search results have provided detailed information on the average size and weight, as well as the oldest recorded instance for each of the top 5 most common birds: Northern Cardinal, Dark-eyed Junco, Mourning Dove, Downy Woodpecker, and House Finch. Now, I will compile this information into a table format for easy reference. Let's create the table with the average size and weight, as well as the oldest recorded instance for each of these birds.\n", + "Here is the table with the average size and weight, as well as the oldest recorded instance for each of the top 5 most common birds:\n", + "\n", + "| Bird Species | Average Size | Average Weight | Oldest Recorded Instance |\n", + "|---------------------|--------------------|------------------|--------------------------|\n", + "| Northern Cardinal | 21.5 cm (male), 21.25 cm (female) | 42-48 g | 15 years and 9 months |\n", + "| Dark-eyed Junco | 14-16 cm | 18-30 g | At least 11 years, 4 months old |\n", + "| Mourning Dove | 22.5-36 cm | 96-170 g | 19 years |\n", + "| Downy Woodpecker | 14-18 cm | 20-33 g | At least 11 years |\n", + "| House Finch | 13-14 cm | 16-27 g | 8-11 years |\n", + "\n", + "This table summarizes the average size and weight, as well as the oldest recorded instance for each of the top 5 most common birds.\n" + ] + } + ], + "source": [ + "solution_node = step[\"__end__\"][\"root\"].get_best_solution()\n", + "best_trajectory = solution_node.get_trajectory(include_reflections=False)\n", + "print(best_trajectory[-1].content)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "1e084037-42e7-4f8e-962d-aaa3f04ab54c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "start\n", + "rolled out: 1\n", + "---\n", + "expand\n", + "rolled out: 2\n", + "---\n", + "expand\n", + "rolled out: 3\n", + "---\n", + "__end__\n", + "rolled out: 3\n", + "---\n" + ] + } + ], + "source": [ + "question = \"Write out magnus carlson series of moves in his game against Alireza Firouzja and propose an alternate strategy\"\n", + "for step in graph.stream({\"input\": question}):\n", + " step_name, step_state = next(iter(step.items()))\n", + " print(step_name)\n", + " print(\"rolled out: \", step_state[\"root\"].height)\n", + " print(\"---\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "d403c1c8-b26b-4d79-87b1-d2d16c1a7673", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "In the game between Magnus Carlsen and Alireza Firouzja, Magnus Carlsen started with the move C4, and Alireza countered with H6. To propose an alternate strategy for Magnus Carlsen, focusing on positional play, creating strong pawn structures, and leveraging his endgame skills could be highly effective. Magnus could aim to control the center, develop his pieces harmoniously, and look for opportunities to gradually improve his position. By maintaining a solid pawn structure and maneuvering his pieces strategically, Magnus could aim to outmaneuver his opponent in the later stages of the game, utilizing his renowned endgame skills to secure a favorable outcome.\n" + ] + } + ], + "source": [ + "solution_node = step[\"__end__\"][\"root\"].get_best_solution()\n", + "best_trajectory = solution_node.get_trajectory(include_reflections=False)\n", + "print(best_trajectory[-1].content)" + ] + }, + { + "cell_type": "markdown", + "id": "f1b5140d-f51e-4032-8bc8-d7153252e3bf", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "Congrats on implementing LATS! This is a technique that can be reasonably ast and effective at solving complex reasoning tasks. A few notes that you probably observed above:\n", + "1. While effective , the tree rollout can take additional compute time. If you wanted to include this in a production app, you'd either want to ensure that intermediate steps are streamed (so the user sees the thinking process/has access to intermediate results) or use it for fine-tuning data to improve the single-shot accuracy and avoid long rollouts.\n", + "2. The candidate selection process is only as good as the reward you generate. Here we are using self-reflection exclusively, but if you have an external source of feedback (such as code test execution), that should be incorporated in the locations mentioned above." + ] + }, + { + "cell_type": "markdown", + "id": "6130dff9-4753-4556-a39e-330ac65ba9c6", "metadata": {}, - "outputs": [], "source": [] } ],