mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-31 04:09:49 +02:00
90 KiB
90 KiB
In [1]:
import json
import re
from typing import Any, Dict, List, Optional, Sequence, Union
from langchain.agents.agent import AgentOutputParser
from langchain.schema import OutputParserException
from langchain_core.tools import BaseTool
THOUGHT_PATTERN = r"Thought: ([^\n]*)"
# $1 or ${1} -> 1
ID_PATTERN = r"\$\{?(\d+)\}?"
END_OF_PLAN = "<END_OF_PLAN>"
class ActionParserFSM:
def __init__(self):
self.reset()
def reset(self):
self.state = "START"
self.task_index = ""
self.action = ""
self.comment = ""
self.bracket_count = 0
self.actions = []
def parse(self, text: str):
for char in text:
action = self.process_char(char)
if action:
yield action
action = self.save_action()
if action:
yield action
def process_char(self, char: str) -> Optional[dict]:
action = None
if self.state == "START":
if char.isdigit():
self.state = "NUMBER"
self.task_index += char
elif char == "\n":
self.reset()
elif self.state == "NUMBER":
if char == ".":
self.state = "ACTION"
elif char.isdigit():
self.task_index += char
else:
self.reset()
elif self.state == "ACTION":
if char == "{":
self.bracket_count += 1
elif char == "}":
self.bracket_count -= 1
if self.bracket_count == 0:
self.state = "COMMENT"
self.action += char
elif self.state == "COMMENT":
if char == "\n":
action = self.save_action()
self.reset()
else:
self.comment += char
return action
def save_action(self):
if self.task_index and self.action:
parsed_action = json.loads(self.action.strip())
tool_name, args = next(iter(parsed_action.items()))
return {
"task_index": int(self.task_index),
"tool_name": tool_name,
"args": args,
}
class LLMCompilerPlanParser(AgentOutputParser, extra="allow"):
"""Planning output parser."""
def __init__(self, tools: Sequence[BaseTool], **kwargs):
super().__init__(**kwargs)
self.tools = tools
def parse(self, text: str) -> list[str]:
parser = ActionParserFSM()
graph_dict = {}
for task in parser.parse(text):
idx = int(task["task_index"])
task = instantiate_task(
tools=self.tools,
idx=idx,
tool_name=task["tool_name"],
args=task["args"],
)
graph_dict[idx] = task
if task["tool"] == "join":
break
return graph_dict
### Helper functions
def default_dependency_rule(idx, args: str):
matches = re.findall(ID_PATTERN, args)
numbers = [int(match) for match in matches]
return idx in numbers
def _get_dependencies_from_graph(
idx: int, tool_name: str, args: Sequence[Any]
) -> dict[str, list[str]]:
"""Get dependencies from a graph."""
if tool_name == "join":
return list(range(1, idx))
return [i for i in range(1, idx) if default_dependency_rule(i, str(args))]
def instantiate_task(
tools: Sequence[BaseTool],
idx: int,
tool_name: str,
args: Union[dict, str, bool, None],
) -> dict:
dependencies = _get_dependencies_from_graph(idx, tool_name, args)
if tool_name == "join":
tool = "join"
else:
try:
tool = tools[[tool.name for tool in tools].index(tool_name)]
except ValueError as e:
raise OutputParserException(f"Tool {tool_name} not found.")
return dict(
tool=tool,
args=args,
dependencies=dependencies,
)In [141]:
from langchain.chat_models.base import BaseChatModel
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableBranch
from langchain_core.tools import BaseTool
END_OF_PLAN = "<END_OF_PLAN>"
JOINER_FINISH = "Finish"
JOINER_REPLAN = "Replan"
JOIN_DESCRIPTION = (
"join():\n"
" - Collects and combines results from prior actions.\n"
" - A LLM agent is called upon invoking join to either finalize the user query or wait until the plans are executed.\n"
" - join should always be the last action in the plan, and will be called in two scenarios:\n"
" (a) if the answer can be determined by gathering the outputs from tasks to generate the final response.\n"
" (b) if the answer cannot be determined in the planning phase before you execute the plans. "
)
planner_prompt_tmpl_str = (
"Given a user query, create a plan to solve it with the utmost parallelizability. "
"Each plan should comprise an action from the following {num_tools} types:\n"
"{tool_descriptions}"
f"\n{{num_toolsp1}}. {JOIN_DESCRIPTION}"
"Guidelines:\n"
" - Each action described above contains input/output types and description.\n"
" - You must strictly adhere to the input and output types for each action.\n"
" - The action descriptions contain the guidelines. You MUST strictly follow those guidelines when you use the actions.\n"
" - Each action in the plan should strictly be one of the above types.\n"
" - Provide actions ONLY in json form, with the single key being the action name and the value being its arguments. Do not write python code. \n"
" - Each action line must start with a unique ID, which is strictly increasing.\n"
" - Inputs for actions can either be constants or outputs from preceding actions. "
"In the latter case, use the format $id to denote the ID of the previous action whose output will be the input.\n"
f" - Always call join as the last action in the plan. Say '{END_OF_PLAN}' after you call join\n"
" - Ensure the plan maximizes parallelizability.\n"
" - Only use the provided action types. If a query cannot be addressed using these, invoke the join action for the next steps.\n"
" - Never introduce new actions other than the ones provided.\n\n"
"{replan}"
"{examples}"
)
def _generate_planner_prompt(
tools: Sequence[BaseTool],
example_prompt=str,
):
tool_descriptions = "\n".join(
f"{i+1}. {tool.name}: {tool.description}\n\tInput schema: {tool.args}"
for i, tool in enumerate(tools)
)
planner_prompt_template = ChatPromptTemplate.from_messages(
[("system", planner_prompt_tmpl_str), ("user", "Question: {input}{context}")]
).partial(
tool_descriptions=tool_descriptions,
examples="Here are some examples:\n\n" + example_prompt
if example_prompt
else "",
num_tools=len(tools),
num_toolsp1=len(tools) + 1,
)
return planner_prompt_template
def create_planner(
llm: BaseChatModel,
example_prompt: str,
tools: Sequence[BaseTool],
stop: Optional[list[str]] = None,
):
og_planner_prompt = _generate_planner_prompt(tools, example_prompt).partial(
replan="",
context="",
)
replanner_prompt = _generate_planner_prompt(tools, example_prompt).partial(
replan=' - You are given "Previous Plan" which is the plan that the previous agent created along with the execution results '
"(given as Observation) of each plan and a general thought (given as Thought) about the executed results."
'You MUST use these information to create the next plan under "Current Plan".\n'
' - When starting the Current Plan, you should start with "Thought" that outlines the strategy for the next plan.\n'
" - In the Current Plan, you should NEVER repeat the actions that are already executed in the Previous Plan.\n"
" - You must continue the task index from the end of the previous one. Do not repeat task indices."
)
bound_llm = llm.bind(stop=stop)
return (
RunnableBranch(
((lambda x: x.get("context") is not None), replanner_prompt),
og_planner_prompt,
)
| bound_llm
| LLMCompilerPlanParser(tools=tools)
)In [131]:
from langchain.tools import tool
from langchain_openai import ChatOpenAI
@tool
def get_user_id(first_name: str, last_name: Optional[str] = None):
"""Query the user IDs of everyone with the provided name."""
return 4
@tool
def get_scores(class_name: str, user_id: int):
"""Query the class registry for grades of the provided user ID."""
return "A+"
examples = (
"Question: What's the user ID for Johnny Drop Tables?\n"
'1. {"get_user_id": {"first_name": "Johnny", "last_name":"Drop Tables"}}\n'
f'2. {{"join": null}}{END_OF_PLAN}\n'
"###\n"
"\n"
"Question: What was Eric Zhang's score in Calc?\n"
'1. {"get_user_id": {"first_name": "Eric", "last_name":"Zhang"}}\n'
'2. {"get_scores": {"class_name": "calc", "user_id": "$1"}}\n'
f'3. {{"join": null}}{END_OF_PLAN}\n'
"###\n"
"\n"
)
planner = create_planner(
ChatOpenAI(model="gpt-3.5-turbo"),
example_prompt=examples,
tools=[get_user_id, get_scores],
)In [132]:
tasks = planner.invoke(
{"input": "What are the Calc BC grades for Sam and Will Van Damm?"}
)
tasksOut [132]:
{1: {'tool': StructuredTool(name='get_user_id', description='get_user_id(first_name: str, last_name: Optional[str] = None) - Query the user IDs of everyone with the provided name.', args_schema=<class 'pydantic.main.get_user_idSchemaSchema'>, func=<function get_user_id at 0x130eb9a80>),
'args': {'first_name': 'Sam', 'last_name': 'Van Damm'},
'dependencies': []},
2: {'tool': StructuredTool(name='get_user_id', description='get_user_id(first_name: str, last_name: Optional[str] = None) - Query the user IDs of everyone with the provided name.', args_schema=<class 'pydantic.main.get_user_idSchemaSchema'>, func=<function get_user_id at 0x130eb9a80>),
'args': {'first_name': 'Will', 'last_name': 'Van Damm'},
'dependencies': []},
3: {'tool': StructuredTool(name='get_scores', description='get_scores(class_name: str, user_id: int) - Query the class registry for grades of the provided user ID.', args_schema=<class 'pydantic.main.get_scoresSchemaSchema'>, func=<function get_scores at 0x130eb9d00>),
'args': {'class_name': 'Calc BC', 'user_id': '$1'},
'dependencies': [1]},
4: {'tool': StructuredTool(name='get_scores', description='get_scores(class_name: str, user_id: int) - Query the class registry for grades of the provided user ID.', args_schema=<class 'pydantic.main.get_scoresSchemaSchema'>, func=<function get_scores at 0x130eb9d00>),
'args': {'class_name': 'Calc BC', 'user_id': '$2'},
'dependencies': [2]},
5: {'tool': 'join', 'args': None, 'dependencies': [1, 2, 3, 4]}}In [184]:
import functools
from langchain_core.runnables import (
RunnableLambda,
RunnableParallel,
RunnablePassthrough,
)
def _sort_tasks(data):
if not data:
return []
sorted_tasks = []
# Remove tasks already completed
min_idx = min([int(k) for k in data])
data = {
int(k): {
**v,
"dependencies": [dep for dep in v["dependencies"] if dep >= min_idx],
}
for k, v in data.items()
}
while data:
no_deps = {k: v for k, v in data.items() if not v["dependencies"]}
if not no_deps:
raise ValueError("We seem to have run into a circular dependency.")
sorted_tasks.append(no_deps)
data = {
k: {
**v,
"dependencies": [d for d in v["dependencies"] if d not in no_deps],
}
for k, v in data.items()
if k not in no_deps
}
return sorted_tasks
def _resolve_arg(x: dict, arg: Union[str, Any]):
if isinstance(arg, str) and arg.startswith("$"):
try:
return x[f"task_{arg[1:]}"]
except:
if arg.endswith(".output"):
return x[f"task_{arg[1:-7]}"]
raise
else:
return arg
def _execute_task(x, task):
tool_to_use = task["tool"]
args = task["args"]
if isinstance(args, str):
resolved_args = _resolve_arg(x, args)
elif isinstance(args, dict):
resolved_args = {key: _resolve_arg(x, val) for key, val in args.items()}
else:
# This will likely fail
resolved_args = args
try:
return tool_to_use.invoke(resolved_args)
except Exception as e:
return (
f"ERROR(Failed to call tool {tool_to_use} with args {tool_to_use}."
+ f" Args resolved to {resolved_args}. Error: {repr(e)})"
)
def construct_dag(tasks):
sorted_tasks = _sort_tasks(tasks)
chain = None
for idx, task_group in enumerate(sorted_tasks):
if len(task_group) == 1 and next(iter(task_group.values()))["tool"] == "join":
# TODO: actually join the values
step = lambda x: {"join": x}
else:
# Cascade all results forward
constructor = (
RunnableParallel if chain is None else RunnablePassthrough.assign
)
task_dict = {}
for idx, task in task_group.items():
task_dict[f"task_{idx}"] = RunnableLambda(
functools.partial(_execute_task, task=task)
).with_config(run_name=f"task_{idx}")
step = constructor(**task_dict).with_config(run_name=f"TaskGroup{idx}")
if chain is None:
chain = step
else:
chain |= step
if chain is not None:
return chain | RunnablePassthrough.assign(tasks=lambda _: tasks)
return chainIn [185]:
graph = construct_dag(tasks)
graph.get_graph().print_ascii() +------------------------------+
| Parallel<task_1,task_2>Input |
+------------------------------+
*** ***
** **
** **
+-------------+ +-------------+
| Lambda(...) | | Lambda(...) |
+-------------+ +-------------+
*** ***
** **
** **
+-------------------------------+
| Parallel<task_1,task_2>Output |
+-------------------------------+
*
*
*
+------------------------------+
| Parallel<task_3,task_4>Input |
+------------------------------+
***** * *****
***** * *****
*** * ***
+-------------+ +-------------+ +-------------+
| Lambda(...) | | Lambda(...) | | Passthrough |
+-------------+***** +-------------+ *****+-------------+
***** * *****
***** * *****
*** * ***
+-------------------------------+
| Parallel<task_3,task_4>Output |
+-------------------------------+
*
*
*
+-------------------------------+
| Lambda(lambda x: {'join': x}) |
+-------------------------------+
*
*
*
+----------------------+
| Parallel<tasks>Input |
+----------------------+
*** ***
*** ***
** **
+-------------------------+ +-------------+
| Lambda(lambda _: tasks) | | Passthrough |
+-------------------------+ +-------------+
*** ***
*** ***
** **
+-----------------------+
| Parallel<tasks>Output |
+-----------------------+
In [135]:
chain = planner | construct_dagIn [136]:
example_question = "Did Aliya get a better score than Roger in Geology?"
task_results = chain.invoke({"input": example_question})
task_results["join"]Out [136]:
{'task_1': 4, 'task_2': 4, 'task_3': 'A+', 'task_4': 'A+'}In [216]:
from langchain_core.output_parsers import StrOutputParser
from typing_extensions import TypedDict
def format_task(task, idx):
tool = task["tool"]
tool_name = tool if isinstance(tool, str) else tool.name # Handle join()
return f"{idx}. {{{tool_name}: {task['args']}}}"
def format_tasks(executor_output: dict):
tasks = executor_output["tasks"]
prior_observations = executor_output.get("observations")
formatted_plan = "\n".join(format_task(task, idx) for idx, task in tasks.items())
observations = "\n".join(f"{k}: {v}" for k, v in executor_output["join"].items())
result = f"Original Plan:\n{formatted_plan}\nExecuted plan results:\n{observations}"
if prior_observations:
result += f"\nPrevious Results:\n{prior_observations}"
return result
def _parse_joiner_output(raw_answer: str) -> str:
thought, answer, is_replan = "", "", False # default values
raw_answers = raw_answer.split("\n")
for ans in raw_answers:
if ans.startswith("Action:"):
answer = ans[ans.find("(") + 1 : ans.find(")")]
is_replan = JOINER_REPLAN in ans
elif ans.startswith("Thought:"):
thought = ans.split("Thought:")[1].strip()
if is_replan:
return {"thought": thought, "context": answer}
else:
return {"thought": thought, "answer": answer}In [217]:
def create_joiner(prompt, llm):
return (
(
lambda x: {
**x["plan"],
"input": x["input"],
"context": x.get("context"),
"observations": x.get("observations"),
}
)
| RunnablePassthrough.assign(scratchpad=format_tasks)
| ChatPromptTemplate.from_messages([("system", prompt), ("user", "{input}")])
| llm
| StrOutputParser()
| _parse_joiner_output
)In [218]:
system_prompt = (
"Solve a question answering task. Here are some guidelines:\n"
" - In the Assistant Scratchpad, you will be given results of a plan you have executed to answer the user's question.\n"
" - Thought needs to reason about the question based on the Observations in 1-2 sentences.\n"
" - Ignore irrelevant action results.\n"
" - If the required information is present, give a concise but complete and helpful answer to the user's question.\n"
" - If you are unable to give a satisfactory finishing answer, replan to get the required information."
" Respond in the following format:\n\n"
"Thought: <reason about the task results and whether you have sufficient information to answer the question>\n"
"Action: <action to take>\n"
"Available actions:\n"
f" (1) {JOINER_FINISH}(the final answer to return to the user): returns the answer and finishes the task.\n"
f" (2) {JOINER_REPLAN}(the reasoning and other information that will help you plan again. Can be a line of any length): instructs why we must replan\n\n"
" Examples:\n"
"Question: How many users are currently using the new product?\n"
"...task returns the number 32,000\n"
"Thought: I find no issue with the original plan, and the results satisfy everything in the user question.\n"
f"Action: {JOINER_FINISH}(32,000 users currently use the new product)\n###\n"
"Question: How much cooler is it in NY than SF?\n"
"...task results show SF is 57 degrees fahrenheit today, and they show in NY it has a high of 32 degrees fahrenheit \n"
"Thought: I can answer by synthesizing the results.\n"
f"Action: {JOINER_FINISH}(NY is 25 degrees cooler than SF today, as it has a high of 32 degrees Fahrenheit today, whereas in SF, it is 57 degrees Fahrenheit.)\n###\n"
"Question: Are the gophers beating the rabbits??\n"
"...task returns the a score of 7 for rabbits but no other value...\n"
"Thought: I need the gophers' score to make a final decision.\n"
f"Action: {JOINER_REPLAN}(The rabbits have a score of 7, but I need the gophers' score.)"
"\nAssistant Scratchpad:\n{scratchpad}"
)In [219]:
joiner = create_joiner(system_prompt, ChatOpenAI(model="gpt-3.5-turbo"))
joiner.invoke({"plan": task_results, "input": example_question})Out [219]:
{'thought': 'The plan has been executed successfully and returned the scores for both Aliya and Roger in Geology. We can compare their scores to determine if Aliya got a better score than Roger.',
'answer': 'Aliya got an A+ in Geology, while Roger also got an A+. Therefore, they both got the same score in Geology.'}In [220]:
import getpass
import os
os.environ["TAVILY_API_KEY"] = (
os.environ.get("TAVILY_API_KEY")
if "TAVILY_API_KEY" in os.environ
else getpass.getpass("Tavily API Key:")
)
# Then fetch a credentials.json file
# https://developers.google.com/gmail/api/quickstart/python#authorize_credentials_for_a_desktop_applicationIn [221]:
from operator import add, mul, sub, truediv
from typing import Literal
from langchain_community.agent_toolkits import GmailToolkit
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.tools import tool
@tool
def calculate(
arg1: float,
arg2: float,
op: Union[Literal["+"], Literal["-"], Literal["*"], Literal["/"]],
):
"""Calculate a mathematical operation on two arguments."""
resolved_op = {"+": add, "-": sub, "*": mul, "/": truediv}
return resolved_op[op](arg1, arg2)
tools = [TavilySearchResults(max_results=1), calculate]In [224]:
from langgraph.graph import END, StateGraph
class GraphState(TypedDict):
input: str
plan: Dict
agent_output: Dict
observations: Dict
num_iterations: int
context: str
stop_reason: str
MAX_ITERATIONS = 5
workflow = StateGraph(GraphState)
# 1. Define vertices
planner = create_planner(
llm=ChatOpenAI(model="gpt-4-1106-preview"),
# Add more examples to improve reliability
example_prompt=(
"Question: What's the capital of Myanmar?\n"
'1. {"tavily_search_results_json": {"query": "Capital of Myanmar"}}\n'
f'2. {{"join": null}}{END_OF_PLAN}\n'
"###\n"
"\n"
),
tools=tools,
)
plan_and_execute = planner | construct_dag
joiner = create_joiner(system_prompt, ChatOpenAI(model="gpt-4-1106-preview"))
def _reformat_task(idx, task: Union[BaseTool, str]):
tool = task["tool"]
tool_name = tool if isinstance(tool, str) else tool.name
called = {tool_name: task["args"]}
return f"{idx}. {json.dumps(called)}"
def provide_context(state):
# Insert a context string for the re-planner.
# This could alternatively call an LLM to provide additional logic
context = state["agent_output"]["context"]
num_iterations = int(state.get("num_iterations") or 1) + 1
previous_plan = "\n".join(
[
_reformat_task(idx, task)
for idx, task in sorted(state["plan"]["tasks"].items())
]
)
context_str = (
f"\n\nPrevious Plan:\n{previous_plan}\n"
f"{context}\nYou have made {num_iterations}/{MAX_ITERATIONS} attempts thus far."
)
observations = state["observations"] or {}
for task, observation in state["plan"]["join"].items():
observations[task] = observation
return {
"context": context_str,
"num_iterations": num_iterations,
"observations": observations,
}
def add_stop_reason(state):
num_iterations = int(state.get("num_iterations") or 0)
if num_iterations >= MAX_ITERATIONS:
return {"stop_reason": "end_max_iter"}
if state["agent_output"].get("answer"):
return {"stop_reason": "answer"}
return {"stop_reason": None}
# Assign each node to a state variable to update
workflow.add_node("plan_and_execute", RunnablePassthrough.assign(plan=plan_and_execute))
workflow.add_node("join", RunnablePassthrough.assign(agent_output=joiner))
workflow.add_node("provide_context", provide_context)
workflow.add_node("provide_stop_reason", add_stop_reason)
## Define edges
workflow.add_edge("plan_and_execute", "join")
workflow.add_edge("provide_context", "plan_and_execute")
workflow.add_edge("join", "provide_stop_reason")
### This condition determines looping logic
def should_continue(state):
if state["stop_reason"] is None:
return "continue"
return "end"
workflow.add_conditional_edges(
start_key="provide_stop_reason",
# Next, we pass in the function that will determine which node is called next.
condition=should_continue,
conditional_edge_mapping={
# If it generates context, we must replan
"continue": "provide_context",
# Otherwise we finish.
"end": END,
},
)
workflow.set_entry_point("plan_and_execute")
chain = workflow.compile()In [225]:
result = chain.invoke({"input": "What's the GDP of New York?"})
print(result["agent_output"]["answer"])The GDP of New York in 2022 was about 1.56 trillion U.S. dollars.
In [227]:
result = chain.invoke(
{"input": "How much larger is the GDP of the UK than that of New York?"}
)[0;31m---------------------------------------------------------------------------[0m [0;31mJSONDecodeError[0m Traceback (most recent call last) Cell [0;32mIn[227], line 1[0m [0;32m----> 1[0m result [38;5;241m=[39m [43mchain[49m[38;5;241;43m.[39;49m[43minvoke[49m[43m([49m [1;32m 2[0m [43m [49m[43m{[49m[38;5;124;43m"[39;49m[38;5;124;43minput[39;49m[38;5;124;43m"[39;49m[43m:[49m[43m [49m[38;5;124;43m"[39;49m[38;5;124;43mHow much larger is the GDP of the UK than that of New York?[39;49m[38;5;124;43m"[39;49m[43m}[49m [1;32m 3[0m [43m)[49m File [0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:492[0m, in [0;36mPregel.invoke[0;34m(self, input, config, output_keys, input_keys, **kwargs)[0m [1;32m 482[0m [38;5;28;01mdef[39;00m [38;5;21minvoke[39m( [1;32m 483[0m [38;5;28mself[39m, [1;32m 484[0m [38;5;28minput[39m: Union[[38;5;28mdict[39m[[38;5;28mstr[39m, Any], Any], [0;32m (...)[0m [1;32m 489[0m [38;5;241m*[39m[38;5;241m*[39mkwargs: Any, [1;32m 490[0m ) [38;5;241m-[39m[38;5;241m>[39m Union[[38;5;28mdict[39m[[38;5;28mstr[39m, Any], Any]: [1;32m 491[0m latest: Union[[38;5;28mdict[39m[[38;5;28mstr[39m, Any], Any] [38;5;241m=[39m [38;5;28;01mNone[39;00m [0;32m--> 492[0m [43m [49m[38;5;28;43;01mfor[39;49;00m[43m [49m[43mchunk[49m[43m [49m[38;5;129;43;01min[39;49;00m[43m [49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mstream[49m[43m([49m [1;32m 493[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 494[0m [43m [49m[43mconfig[49m[43m,[49m [1;32m 495[0m [43m [49m[43moutput_keys[49m[38;5;241;43m=[39;49m[43moutput_keys[49m[43m [49m[38;5;28;43;01mif[39;49;00m[43m [49m[43moutput_keys[49m[43m [49m[38;5;129;43;01mis[39;49;00m[43m [49m[38;5;129;43;01mnot[39;49;00m[43m [49m[38;5;28;43;01mNone[39;49;00m[43m [49m[38;5;28;43;01melse[39;49;00m[43m [49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43moutput[49m[43m,[49m [1;32m 496[0m [43m [49m[43minput_keys[49m[38;5;241;43m=[39;49m[43minput_keys[49m[43m,[49m [1;32m 497[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m,[49m [1;32m 498[0m [43m [49m[43m)[49m[43m:[49m [1;32m 499[0m [43m [49m[43mlatest[49m[43m [49m[38;5;241;43m=[39;49m[43m [49m[43mchunk[49m [1;32m 500[0m [38;5;28;01mreturn[39;00m latest File [0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:528[0m, in [0;36mPregel.transform[0;34m(self, input, config, output_keys, input_keys, **kwargs)[0m [1;32m 519[0m [38;5;28;01mdef[39;00m [38;5;21mtransform[39m( [1;32m 520[0m [38;5;28mself[39m, [1;32m 521[0m [38;5;28minput[39m: Iterator[Union[[38;5;28mdict[39m[[38;5;28mstr[39m, Any], Any]], [0;32m (...)[0m [1;32m 526[0m [38;5;241m*[39m[38;5;241m*[39mkwargs: Any, [1;32m 527[0m ) [38;5;241m-[39m[38;5;241m>[39m Iterator[Union[[38;5;28mdict[39m[[38;5;28mstr[39m, Any], Any]]: [0;32m--> 528[0m [43m [49m[38;5;28;43;01mfor[39;49;00m[43m [49m[43mchunk[49m[43m [49m[38;5;129;43;01min[39;49;00m[43m [49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m_transform_stream_with_config[49m[43m([49m [1;32m 529[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 530[0m [43m [49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m_transform[49m[43m,[49m [1;32m 531[0m [43m [49m[43mconfig[49m[43m,[49m [1;32m 532[0m [43m [49m[43moutput_keys[49m[38;5;241;43m=[39;49m[43moutput_keys[49m[43m,[49m [1;32m 533[0m [43m [49m[43minput_keys[49m[38;5;241;43m=[39;49m[43minput_keys[49m[43m,[49m [1;32m 534[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m,[49m [1;32m 535[0m [43m [49m[43m)[49m[43m:[49m [1;32m 536[0m [43m [49m[38;5;28;43;01myield[39;49;00m[43m [49m[43mchunk[49m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:1226[0m, in [0;36mRunnable._transform_stream_with_config[0;34m(self, input, transformer, config, run_type, **kwargs)[0m [1;32m 1224[0m [38;5;28;01mtry[39;00m: [1;32m 1225[0m [38;5;28;01mwhile[39;00m [38;5;28;01mTrue[39;00m: [0;32m-> 1226[0m chunk: Output [38;5;241m=[39m context[38;5;241m.[39mrun([38;5;28mnext[39m, iterator) [38;5;66;03m# type: ignore[39;00m [1;32m 1227[0m [38;5;28;01myield[39;00m chunk [1;32m 1228[0m [38;5;28;01mif[39;00m final_output_supported: File [0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:313[0m, in [0;36mPregel._transform[0;34m(self, input, run_manager, config, input_keys, output_keys)[0m [1;32m 303[0m done, inflight [38;5;241m=[39m concurrent[38;5;241m.[39mfutures[38;5;241m.[39mwait( [1;32m 304[0m [ [1;32m 305[0m executor[38;5;241m.[39msubmit(proc[38;5;241m.[39minvoke, [38;5;28minput[39m, config) [0;32m (...)[0m [1;32m 309[0m timeout[38;5;241m=[39m[38;5;28mself[39m[38;5;241m.[39mstep_timeout, [1;32m 310[0m ) [1;32m 312[0m [38;5;66;03m# interrupt on failure or timeout[39;00m [0;32m--> 313[0m [43m_interrupt_or_proceed[49m[43m([49m[43mdone[49m[43m,[49m[43m [49m[43minflight[49m[43m,[49m[43m [49m[43mstep[49m[43m)[49m [1;32m 315[0m [38;5;66;03m# apply writes to channels[39;00m [1;32m 316[0m _apply_writes(checkpoint, channels, pending_writes, config, step [38;5;241m+[39m [38;5;241m1[39m) File [0;32m~/code/lc/langgraph/langgraph/pregel/__init__.py:611[0m, in [0;36m_interrupt_or_proceed[0;34m(done, inflight, step)[0m [1;32m 609[0m inflight[38;5;241m.[39mpop()[38;5;241m.[39mcancel() [1;32m 610[0m [38;5;66;03m# raise the exception[39;00m [0;32m--> 611[0m [38;5;28;01mraise[39;00m exc [1;32m 612[0m [38;5;66;03m# TODO this is where retry of an entire step would happen[39;00m [1;32m 614[0m [38;5;28;01mif[39;00m inflight: [1;32m 615[0m [38;5;66;03m# if we got here means we timed out[39;00m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/concurrent/futures/thread.py:58[0m, in [0;36m_WorkItem.run[0;34m(self)[0m [1;32m 55[0m [38;5;28;01mreturn[39;00m [1;32m 57[0m [38;5;28;01mtry[39;00m: [0;32m---> 58[0m result [38;5;241m=[39m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mfn[49m[43m([49m[38;5;241;43m*[39;49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43margs[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mkwargs[49m[43m)[49m [1;32m 59[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m exc: [1;32m 60[0m [38;5;28mself[39m[38;5;241m.[39mfuture[38;5;241m.[39mset_exception(exc) File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:3596[0m, in [0;36mRunnableBindingBase.invoke[0;34m(self, input, config, **kwargs)[0m [1;32m 3590[0m [38;5;28;01mdef[39;00m [38;5;21minvoke[39m( [1;32m 3591[0m [38;5;28mself[39m, [1;32m 3592[0m [38;5;28minput[39m: Input, [1;32m 3593[0m config: Optional[RunnableConfig] [38;5;241m=[39m [38;5;28;01mNone[39;00m, [1;32m 3594[0m [38;5;241m*[39m[38;5;241m*[39mkwargs: Optional[Any], [1;32m 3595[0m ) [38;5;241m-[39m[38;5;241m>[39m Output: [0;32m-> 3596[0m [38;5;28;01mreturn[39;00m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mbound[49m[38;5;241;43m.[39;49m[43minvoke[49m[43m([49m [1;32m 3597[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 3598[0m [43m [49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m_merge_configs[49m[43m([49m[43mconfig[49m[43m)[49m[43m,[49m [1;32m 3599[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43m{[49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mkwargs[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m}[49m[43m,[49m [1;32m 3600[0m [43m [49m[43m)[49m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:1774[0m, in [0;36mRunnableSequence.invoke[0;34m(self, input, config)[0m [1;32m 1772[0m [38;5;28;01mtry[39;00m: [1;32m 1773[0m [38;5;28;01mfor[39;00m i, step [38;5;129;01min[39;00m [38;5;28menumerate[39m([38;5;28mself[39m[38;5;241m.[39msteps): [0;32m-> 1774[0m [38;5;28minput[39m [38;5;241m=[39m [43mstep[49m[38;5;241;43m.[39;49m[43minvoke[49m[43m([49m [1;32m 1775[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 1776[0m [43m [49m[38;5;66;43;03m# mark each step as a child run[39;49;00m [1;32m 1777[0m [43m [49m[43mpatch_config[49m[43m([49m [1;32m 1778[0m [43m [49m[43mconfig[49m[43m,[49m[43m [49m[43mcallbacks[49m[38;5;241;43m=[39;49m[43mrun_manager[49m[38;5;241;43m.[39;49m[43mget_child[49m[43m([49m[38;5;124;43mf[39;49m[38;5;124;43m"[39;49m[38;5;124;43mseq:step:[39;49m[38;5;132;43;01m{[39;49;00m[43mi[49m[38;5;241;43m+[39;49m[38;5;241;43m1[39;49m[38;5;132;43;01m}[39;49;00m[38;5;124;43m"[39;49m[43m)[49m [1;32m 1779[0m [43m [49m[43m)[49m[43m,[49m [1;32m 1780[0m [43m [49m[43m)[49m [1;32m 1781[0m [38;5;66;03m# finish the root run[39;00m [1;32m 1782[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m e: File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/passthrough.py:415[0m, in [0;36mRunnableAssign.invoke[0;34m(self, input, config, **kwargs)[0m [1;32m 409[0m [38;5;28;01mdef[39;00m [38;5;21minvoke[39m( [1;32m 410[0m [38;5;28mself[39m, [1;32m 411[0m [38;5;28minput[39m: Dict[[38;5;28mstr[39m, Any], [1;32m 412[0m config: Optional[RunnableConfig] [38;5;241m=[39m [38;5;28;01mNone[39;00m, [1;32m 413[0m [38;5;241m*[39m[38;5;241m*[39mkwargs: Any, [1;32m 414[0m ) [38;5;241m-[39m[38;5;241m>[39m Dict[[38;5;28mstr[39m, Any]: [0;32m--> 415[0m [38;5;28;01mreturn[39;00m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m_call_with_config[49m[43m([49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m_invoke[49m[43m,[49m[43m [49m[38;5;28;43minput[39;49m[43m,[49m[43m [49m[43mconfig[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m)[49m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:975[0m, in [0;36mRunnable._call_with_config[0;34m(self, func, input, config, run_type, **kwargs)[0m [1;32m 971[0m context [38;5;241m=[39m copy_context() [1;32m 972[0m context[38;5;241m.[39mrun(var_child_runnable_config[38;5;241m.[39mset, child_config) [1;32m 973[0m output [38;5;241m=[39m cast( [1;32m 974[0m Output, [0;32m--> 975[0m [43mcontext[49m[38;5;241;43m.[39;49m[43mrun[49m[43m([49m [1;32m 976[0m [43m [49m[43mcall_func_with_variable_args[49m[43m,[49m [1;32m 977[0m [43m [49m[43mfunc[49m[43m,[49m[43m [49m[38;5;66;43;03m# type: ignore[arg-type][39;49;00m [1;32m 978[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m[43m [49m[38;5;66;43;03m# type: ignore[arg-type][39;49;00m [1;32m 979[0m [43m [49m[43mconfig[49m[43m,[49m [1;32m 980[0m [43m [49m[43mrun_manager[49m[43m,[49m [1;32m 981[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m,[49m [1;32m 982[0m [43m [49m[43m)[49m, [1;32m 983[0m ) [1;32m 984[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m e: [1;32m 985[0m run_manager[38;5;241m.[39mon_chain_error(e) File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/config.py:323[0m, in [0;36mcall_func_with_variable_args[0;34m(func, input, config, run_manager, **kwargs)[0m [1;32m 321[0m [38;5;28;01mif[39;00m run_manager [38;5;129;01mis[39;00m [38;5;129;01mnot[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m accepts_run_manager(func): [1;32m 322[0m kwargs[[38;5;124m"[39m[38;5;124mrun_manager[39m[38;5;124m"[39m] [38;5;241m=[39m run_manager [0;32m--> 323[0m [38;5;28;01mreturn[39;00m [43mfunc[49m[43m([49m[38;5;28;43minput[39;49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m)[49m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/passthrough.py:402[0m, in [0;36mRunnableAssign._invoke[0;34m(self, input, run_manager, config, **kwargs)[0m [1;32m 389[0m [38;5;28;01mdef[39;00m [38;5;21m_invoke[39m( [1;32m 390[0m [38;5;28mself[39m, [1;32m 391[0m [38;5;28minput[39m: Dict[[38;5;28mstr[39m, Any], [0;32m (...)[0m [1;32m 394[0m [38;5;241m*[39m[38;5;241m*[39mkwargs: Any, [1;32m 395[0m ) [38;5;241m-[39m[38;5;241m>[39m Dict[[38;5;28mstr[39m, Any]: [1;32m 396[0m [38;5;28;01massert[39;00m [38;5;28misinstance[39m( [1;32m 397[0m [38;5;28minput[39m, [38;5;28mdict[39m [1;32m 398[0m ), [38;5;124m"[39m[38;5;124mThe input to RunnablePassthrough.assign() must be a dict.[39m[38;5;124m"[39m [1;32m 400[0m [38;5;28;01mreturn[39;00m { [1;32m 401[0m [38;5;241m*[39m[38;5;241m*[39m[38;5;28minput[39m, [0;32m--> 402[0m [38;5;241m*[39m[38;5;241m*[39m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mmapper[49m[38;5;241;43m.[39;49m[43minvoke[49m[43m([49m [1;32m 403[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 404[0m [43m [49m[43mpatch_config[49m[43m([49m[43mconfig[49m[43m,[49m[43m [49m[43mcallbacks[49m[38;5;241;43m=[39;49m[43mrun_manager[49m[38;5;241;43m.[39;49m[43mget_child[49m[43m([49m[43m)[49m[43m)[49m[43m,[49m [1;32m 405[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m,[49m [1;32m 406[0m [43m [49m[43m)[49m, [1;32m 407[0m } File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:2339[0m, in [0;36mRunnableParallel.invoke[0;34m(self, input, config)[0m [1;32m 2326[0m [38;5;28;01mwith[39;00m get_executor_for_config(config) [38;5;28;01mas[39;00m executor: [1;32m 2327[0m futures [38;5;241m=[39m [ [1;32m 2328[0m executor[38;5;241m.[39msubmit( [1;32m 2329[0m step[38;5;241m.[39minvoke, [0;32m (...)[0m [1;32m 2337[0m [38;5;28;01mfor[39;00m key, step [38;5;129;01min[39;00m steps[38;5;241m.[39mitems() [1;32m 2338[0m ] [0;32m-> 2339[0m output [38;5;241m=[39m [43m{[49m[43mkey[49m[43m:[49m[43m [49m[43mfuture[49m[38;5;241;43m.[39;49m[43mresult[49m[43m([49m[43m)[49m[43m [49m[38;5;28;43;01mfor[39;49;00m[43m [49m[43mkey[49m[43m,[49m[43m [49m[43mfuture[49m[43m [49m[38;5;129;43;01min[39;49;00m[43m [49m[38;5;28;43mzip[39;49m[43m([49m[43msteps[49m[43m,[49m[43m [49m[43mfutures[49m[43m)[49m[43m}[49m [1;32m 2340[0m [38;5;66;03m# finish the root run[39;00m [1;32m 2341[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m e: File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:2339[0m, in [0;36m<dictcomp>[0;34m(.0)[0m [1;32m 2326[0m [38;5;28;01mwith[39;00m get_executor_for_config(config) [38;5;28;01mas[39;00m executor: [1;32m 2327[0m futures [38;5;241m=[39m [ [1;32m 2328[0m executor[38;5;241m.[39msubmit( [1;32m 2329[0m step[38;5;241m.[39minvoke, [0;32m (...)[0m [1;32m 2337[0m [38;5;28;01mfor[39;00m key, step [38;5;129;01min[39;00m steps[38;5;241m.[39mitems() [1;32m 2338[0m ] [0;32m-> 2339[0m output [38;5;241m=[39m {key: [43mfuture[49m[38;5;241;43m.[39;49m[43mresult[49m[43m([49m[43m)[49m [38;5;28;01mfor[39;00m key, future [38;5;129;01min[39;00m [38;5;28mzip[39m(steps, futures)} [1;32m 2340[0m [38;5;66;03m# finish the root run[39;00m [1;32m 2341[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m e: File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/concurrent/futures/_base.py:456[0m, in [0;36mFuture.result[0;34m(self, timeout)[0m [1;32m 454[0m [38;5;28;01mraise[39;00m CancelledError() [1;32m 455[0m [38;5;28;01melif[39;00m [38;5;28mself[39m[38;5;241m.[39m_state [38;5;241m==[39m FINISHED: [0;32m--> 456[0m [38;5;28;01mreturn[39;00m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m__get_result[49m[43m([49m[43m)[49m [1;32m 457[0m [38;5;28;01melse[39;00m: [1;32m 458[0m [38;5;28;01mraise[39;00m [38;5;167;01mTimeoutError[39;00m() File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/concurrent/futures/_base.py:401[0m, in [0;36mFuture.__get_result[0;34m(self)[0m [1;32m 399[0m [38;5;28;01mif[39;00m [38;5;28mself[39m[38;5;241m.[39m_exception: [1;32m 400[0m [38;5;28;01mtry[39;00m: [0;32m--> 401[0m [38;5;28;01mraise[39;00m [38;5;28mself[39m[38;5;241m.[39m_exception [1;32m 402[0m [38;5;28;01mfinally[39;00m: [1;32m 403[0m [38;5;66;03m# Break a reference cycle with the exception in self._exception[39;00m [1;32m 404[0m [38;5;28mself[39m [38;5;241m=[39m [38;5;28;01mNone[39;00m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/concurrent/futures/thread.py:58[0m, in [0;36m_WorkItem.run[0;34m(self)[0m [1;32m 55[0m [38;5;28;01mreturn[39;00m [1;32m 57[0m [38;5;28;01mtry[39;00m: [0;32m---> 58[0m result [38;5;241m=[39m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mfn[49m[43m([49m[38;5;241;43m*[39;49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43margs[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mkwargs[49m[43m)[49m [1;32m 59[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m exc: [1;32m 60[0m [38;5;28mself[39m[38;5;241m.[39mfuture[38;5;241m.[39mset_exception(exc) File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:1774[0m, in [0;36mRunnableSequence.invoke[0;34m(self, input, config)[0m [1;32m 1772[0m [38;5;28;01mtry[39;00m: [1;32m 1773[0m [38;5;28;01mfor[39;00m i, step [38;5;129;01min[39;00m [38;5;28menumerate[39m([38;5;28mself[39m[38;5;241m.[39msteps): [0;32m-> 1774[0m [38;5;28minput[39m [38;5;241m=[39m [43mstep[49m[38;5;241;43m.[39;49m[43minvoke[49m[43m([49m [1;32m 1775[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 1776[0m [43m [49m[38;5;66;43;03m# mark each step as a child run[39;49;00m [1;32m 1777[0m [43m [49m[43mpatch_config[49m[43m([49m [1;32m 1778[0m [43m [49m[43mconfig[49m[43m,[49m[43m [49m[43mcallbacks[49m[38;5;241;43m=[39;49m[43mrun_manager[49m[38;5;241;43m.[39;49m[43mget_child[49m[43m([49m[38;5;124;43mf[39;49m[38;5;124;43m"[39;49m[38;5;124;43mseq:step:[39;49m[38;5;132;43;01m{[39;49;00m[43mi[49m[38;5;241;43m+[39;49m[38;5;241;43m1[39;49m[38;5;132;43;01m}[39;49;00m[38;5;124;43m"[39;49m[43m)[49m [1;32m 1779[0m [43m [49m[43m)[49m[43m,[49m [1;32m 1780[0m [43m [49m[43m)[49m [1;32m 1781[0m [38;5;66;03m# finish the root run[39;00m [1;32m 1782[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m e: File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/output_parsers/base.py:167[0m, in [0;36mBaseOutputParser.invoke[0;34m(self, input, config)[0m [1;32m 163[0m [38;5;28;01mdef[39;00m [38;5;21minvoke[39m( [1;32m 164[0m [38;5;28mself[39m, [38;5;28minput[39m: Union[[38;5;28mstr[39m, BaseMessage], config: Optional[RunnableConfig] [38;5;241m=[39m [38;5;28;01mNone[39;00m [1;32m 165[0m ) [38;5;241m-[39m[38;5;241m>[39m T: [1;32m 166[0m [38;5;28;01mif[39;00m [38;5;28misinstance[39m([38;5;28minput[39m, BaseMessage): [0;32m--> 167[0m [38;5;28;01mreturn[39;00m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m_call_with_config[49m[43m([49m [1;32m 168[0m [43m [49m[38;5;28;43;01mlambda[39;49;00m[43m [49m[43minner_input[49m[43m:[49m[43m [49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mparse_result[49m[43m([49m [1;32m 169[0m [43m [49m[43m[[49m[43mChatGeneration[49m[43m([49m[43mmessage[49m[38;5;241;43m=[39;49m[43minner_input[49m[43m)[49m[43m][49m [1;32m 170[0m [43m [49m[43m)[49m[43m,[49m [1;32m 171[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 172[0m [43m [49m[43mconfig[49m[43m,[49m [1;32m 173[0m [43m [49m[43mrun_type[49m[38;5;241;43m=[39;49m[38;5;124;43m"[39;49m[38;5;124;43mparser[39;49m[38;5;124;43m"[39;49m[43m,[49m [1;32m 174[0m [43m [49m[43m)[49m [1;32m 175[0m [38;5;28;01melse[39;00m: [1;32m 176[0m [38;5;28;01mreturn[39;00m [38;5;28mself[39m[38;5;241m.[39m_call_with_config( [1;32m 177[0m [38;5;28;01mlambda[39;00m inner_input: [38;5;28mself[39m[38;5;241m.[39mparse_result([Generation(text[38;5;241m=[39minner_input)]), [1;32m 178[0m [38;5;28minput[39m, [1;32m 179[0m config, [1;32m 180[0m run_type[38;5;241m=[39m[38;5;124m"[39m[38;5;124mparser[39m[38;5;124m"[39m, [1;32m 181[0m ) File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/base.py:975[0m, in [0;36mRunnable._call_with_config[0;34m(self, func, input, config, run_type, **kwargs)[0m [1;32m 971[0m context [38;5;241m=[39m copy_context() [1;32m 972[0m context[38;5;241m.[39mrun(var_child_runnable_config[38;5;241m.[39mset, child_config) [1;32m 973[0m output [38;5;241m=[39m cast( [1;32m 974[0m Output, [0;32m--> 975[0m [43mcontext[49m[38;5;241;43m.[39;49m[43mrun[49m[43m([49m [1;32m 976[0m [43m [49m[43mcall_func_with_variable_args[49m[43m,[49m [1;32m 977[0m [43m [49m[43mfunc[49m[43m,[49m[43m [49m[38;5;66;43;03m# type: ignore[arg-type][39;49;00m [1;32m 978[0m [43m [49m[38;5;28;43minput[39;49m[43m,[49m[43m [49m[38;5;66;43;03m# type: ignore[arg-type][39;49;00m [1;32m 979[0m [43m [49m[43mconfig[49m[43m,[49m [1;32m 980[0m [43m [49m[43mrun_manager[49m[43m,[49m [1;32m 981[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m,[49m [1;32m 982[0m [43m [49m[43m)[49m, [1;32m 983[0m ) [1;32m 984[0m [38;5;28;01mexcept[39;00m [38;5;167;01mBaseException[39;00m [38;5;28;01mas[39;00m e: [1;32m 985[0m run_manager[38;5;241m.[39mon_chain_error(e) File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/runnables/config.py:323[0m, in [0;36mcall_func_with_variable_args[0;34m(func, input, config, run_manager, **kwargs)[0m [1;32m 321[0m [38;5;28;01mif[39;00m run_manager [38;5;129;01mis[39;00m [38;5;129;01mnot[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m accepts_run_manager(func): [1;32m 322[0m kwargs[[38;5;124m"[39m[38;5;124mrun_manager[39m[38;5;124m"[39m] [38;5;241m=[39m run_manager [0;32m--> 323[0m [38;5;28;01mreturn[39;00m [43mfunc[49m[43m([49m[38;5;28;43minput[39;49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m)[49m File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/output_parsers/base.py:168[0m, in [0;36mBaseOutputParser.invoke.<locals>.<lambda>[0;34m(inner_input)[0m [1;32m 163[0m [38;5;28;01mdef[39;00m [38;5;21minvoke[39m( [1;32m 164[0m [38;5;28mself[39m, [38;5;28minput[39m: Union[[38;5;28mstr[39m, BaseMessage], config: Optional[RunnableConfig] [38;5;241m=[39m [38;5;28;01mNone[39;00m [1;32m 165[0m ) [38;5;241m-[39m[38;5;241m>[39m T: [1;32m 166[0m [38;5;28;01mif[39;00m [38;5;28misinstance[39m([38;5;28minput[39m, BaseMessage): [1;32m 167[0m [38;5;28;01mreturn[39;00m [38;5;28mself[39m[38;5;241m.[39m_call_with_config( [0;32m--> 168[0m [38;5;28;01mlambda[39;00m inner_input: [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mparse_result[49m[43m([49m [1;32m 169[0m [43m [49m[43m[[49m[43mChatGeneration[49m[43m([49m[43mmessage[49m[38;5;241;43m=[39;49m[43minner_input[49m[43m)[49m[43m][49m [1;32m 170[0m [43m [49m[43m)[49m, [1;32m 171[0m [38;5;28minput[39m, [1;32m 172[0m config, [1;32m 173[0m run_type[38;5;241m=[39m[38;5;124m"[39m[38;5;124mparser[39m[38;5;124m"[39m, [1;32m 174[0m ) [1;32m 175[0m [38;5;28;01melse[39;00m: [1;32m 176[0m [38;5;28;01mreturn[39;00m [38;5;28mself[39m[38;5;241m.[39m_call_with_config( [1;32m 177[0m [38;5;28;01mlambda[39;00m inner_input: [38;5;28mself[39m[38;5;241m.[39mparse_result([Generation(text[38;5;241m=[39minner_input)]), [1;32m 178[0m [38;5;28minput[39m, [1;32m 179[0m config, [1;32m 180[0m run_type[38;5;241m=[39m[38;5;124m"[39m[38;5;124mparser[39m[38;5;124m"[39m, [1;32m 181[0m ) File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/site-packages/langchain_core/output_parsers/base.py:219[0m, in [0;36mBaseOutputParser.parse_result[0;34m(self, result, partial)[0m [1;32m 206[0m [38;5;28;01mdef[39;00m [38;5;21mparse_result[39m([38;5;28mself[39m, result: List[Generation], [38;5;241m*[39m, partial: [38;5;28mbool[39m [38;5;241m=[39m [38;5;28;01mFalse[39;00m) [38;5;241m-[39m[38;5;241m>[39m T: [1;32m 207[0m [38;5;250m [39m[38;5;124;03m"""Parse a list of candidate model Generations into a specific format.[39;00m [1;32m 208[0m [1;32m 209[0m [38;5;124;03m The return value is parsed from only the first Generation in the result, which[39;00m [0;32m (...)[0m [1;32m 217[0m [38;5;124;03m Structured output.[39;00m [1;32m 218[0m [38;5;124;03m """[39;00m [0;32m--> 219[0m [38;5;28;01mreturn[39;00m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mparse[49m[43m([49m[43mresult[49m[43m[[49m[38;5;241;43m0[39;49m[43m][49m[38;5;241;43m.[39;49m[43mtext[49m[43m)[49m Cell [0;32mIn[1], line 88[0m, in [0;36mLLMCompilerPlanParser.parse[0;34m(self, text)[0m [1;32m 86[0m parser [38;5;241m=[39m ActionParserFSM() [1;32m 87[0m graph_dict [38;5;241m=[39m {} [0;32m---> 88[0m [43m[49m[38;5;28;43;01mfor[39;49;00m[43m [49m[43mtask[49m[43m [49m[38;5;129;43;01min[39;49;00m[43m [49m[43mparser[49m[38;5;241;43m.[39;49m[43mparse[49m[43m([49m[43mtext[49m[43m)[49m[43m:[49m [1;32m 89[0m [43m [49m[43midx[49m[43m [49m[38;5;241;43m=[39;49m[43m [49m[38;5;28;43mint[39;49m[43m([49m[43mtask[49m[43m[[49m[38;5;124;43m"[39;49m[38;5;124;43mtask_index[39;49m[38;5;124;43m"[39;49m[43m][49m[43m)[49m [1;32m 91[0m [43m [49m[43mtask[49m[43m [49m[38;5;241;43m=[39;49m[43m [49m[43minstantiate_task[49m[43m([49m [1;32m 92[0m [43m [49m[43mtools[49m[38;5;241;43m=[39;49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mtools[49m[43m,[49m [1;32m 93[0m [43m [49m[43midx[49m[38;5;241;43m=[39;49m[43midx[49m[43m,[49m [1;32m 94[0m [43m [49m[43mtool_name[49m[38;5;241;43m=[39;49m[43mtask[49m[43m[[49m[38;5;124;43m"[39;49m[38;5;124;43mtool_name[39;49m[38;5;124;43m"[39;49m[43m][49m[43m,[49m [1;32m 95[0m [43m [49m[43margs[49m[38;5;241;43m=[39;49m[43mtask[49m[43m[[49m[38;5;124;43m"[39;49m[38;5;124;43margs[39;49m[38;5;124;43m"[39;49m[43m][49m[43m,[49m [1;32m 96[0m [43m [49m[43m)[49m Cell [0;32mIn[1], line 29[0m, in [0;36mActionParserFSM.parse[0;34m(self, text)[0m [1;32m 27[0m [38;5;28;01mdef[39;00m [38;5;21mparse[39m([38;5;28mself[39m, text: [38;5;28mstr[39m): [1;32m 28[0m [38;5;28;01mfor[39;00m char [38;5;129;01min[39;00m text: [0;32m---> 29[0m action [38;5;241m=[39m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mprocess_char[49m[43m([49m[43mchar[49m[43m)[49m [1;32m 30[0m [38;5;28;01mif[39;00m action: [1;32m 31[0m [38;5;28;01myield[39;00m action Cell [0;32mIn[1], line 61[0m, in [0;36mActionParserFSM.process_char[0;34m(self, char)[0m [1;32m 59[0m [38;5;28;01melif[39;00m [38;5;28mself[39m[38;5;241m.[39mstate [38;5;241m==[39m [38;5;124m"[39m[38;5;124mCOMMENT[39m[38;5;124m"[39m: [1;32m 60[0m [38;5;28;01mif[39;00m char [38;5;241m==[39m [38;5;124m"[39m[38;5;130;01m\n[39;00m[38;5;124m"[39m: [0;32m---> 61[0m action [38;5;241m=[39m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43msave_action[49m[43m([49m[43m)[49m [1;32m 62[0m [38;5;28mself[39m[38;5;241m.[39mreset() [1;32m 63[0m [38;5;28;01melse[39;00m: Cell [0;32mIn[1], line 69[0m, in [0;36mActionParserFSM.save_action[0;34m(self)[0m [1;32m 67[0m [38;5;28;01mdef[39;00m [38;5;21msave_action[39m([38;5;28mself[39m): [1;32m 68[0m [38;5;28;01mif[39;00m [38;5;28mself[39m[38;5;241m.[39mtask_index [38;5;129;01mand[39;00m [38;5;28mself[39m[38;5;241m.[39maction: [0;32m---> 69[0m parsed_action [38;5;241m=[39m [43mjson[49m[38;5;241;43m.[39;49m[43mloads[49m[43m([49m[38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43maction[49m[38;5;241;43m.[39;49m[43mstrip[49m[43m([49m[43m)[49m[43m)[49m [1;32m 70[0m tool_name, args [38;5;241m=[39m [38;5;28mnext[39m([38;5;28miter[39m(parsed_action[38;5;241m.[39mitems())) [1;32m 71[0m [38;5;28;01mreturn[39;00m { [1;32m 72[0m [38;5;124m"[39m[38;5;124mtask_index[39m[38;5;124m"[39m: [38;5;28mint[39m([38;5;28mself[39m[38;5;241m.[39mtask_index), [1;32m 73[0m [38;5;124m"[39m[38;5;124mtool_name[39m[38;5;124m"[39m: tool_name, [1;32m 74[0m [38;5;124m"[39m[38;5;124margs[39m[38;5;124m"[39m: args, [1;32m 75[0m } File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/json/__init__.py:346[0m, in [0;36mloads[0;34m(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)[0m [1;32m 341[0m s [38;5;241m=[39m s[38;5;241m.[39mdecode(detect_encoding(s), [38;5;124m'[39m[38;5;124msurrogatepass[39m[38;5;124m'[39m) [1;32m 343[0m [38;5;28;01mif[39;00m ([38;5;28mcls[39m [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m object_hook [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m [1;32m 344[0m parse_int [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m parse_float [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m [1;32m 345[0m parse_constant [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m object_pairs_hook [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m [38;5;129;01mand[39;00m [38;5;129;01mnot[39;00m kw): [0;32m--> 346[0m [38;5;28;01mreturn[39;00m [43m_default_decoder[49m[38;5;241;43m.[39;49m[43mdecode[49m[43m([49m[43ms[49m[43m)[49m [1;32m 347[0m [38;5;28;01mif[39;00m [38;5;28mcls[39m [38;5;129;01mis[39;00m [38;5;28;01mNone[39;00m: [1;32m 348[0m [38;5;28mcls[39m [38;5;241m=[39m JSONDecoder File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/json/decoder.py:337[0m, in [0;36mJSONDecoder.decode[0;34m(self, s, _w)[0m [1;32m 332[0m [38;5;28;01mdef[39;00m [38;5;21mdecode[39m([38;5;28mself[39m, s, _w[38;5;241m=[39mWHITESPACE[38;5;241m.[39mmatch): [1;32m 333[0m [38;5;250m [39m[38;5;124;03m"""Return the Python representation of ``s`` (a ``str`` instance[39;00m [1;32m 334[0m [38;5;124;03m containing a JSON document).[39;00m [1;32m 335[0m [1;32m 336[0m [38;5;124;03m """[39;00m [0;32m--> 337[0m obj, end [38;5;241m=[39m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43mraw_decode[49m[43m([49m[43ms[49m[43m,[49m[43m [49m[43midx[49m[38;5;241;43m=[39;49m[43m_w[49m[43m([49m[43ms[49m[43m,[49m[43m [49m[38;5;241;43m0[39;49m[43m)[49m[38;5;241;43m.[39;49m[43mend[49m[43m([49m[43m)[49m[43m)[49m [1;32m 338[0m end [38;5;241m=[39m _w(s, end)[38;5;241m.[39mend() [1;32m 339[0m [38;5;28;01mif[39;00m end [38;5;241m!=[39m [38;5;28mlen[39m(s): File [0;32m~/.pyenv/versions/3.11.2/lib/python3.11/json/decoder.py:355[0m, in [0;36mJSONDecoder.raw_decode[0;34m(self, s, idx)[0m [1;32m 353[0m obj, end [38;5;241m=[39m [38;5;28mself[39m[38;5;241m.[39mscan_once(s, idx) [1;32m 354[0m [38;5;28;01mexcept[39;00m [38;5;167;01mStopIteration[39;00m [38;5;28;01mas[39;00m err: [0;32m--> 355[0m [38;5;28;01mraise[39;00m JSONDecodeError([38;5;124m"[39m[38;5;124mExpecting value[39m[38;5;124m"[39m, s, err[38;5;241m.[39mvalue) [38;5;28;01mfrom[39;00m [38;5;28;01mNone[39;00m [1;32m 356[0m [38;5;28;01mreturn[39;00m obj, end [0;31mJSONDecodeError[0m: Expecting value: line 1 column 1 (char 0)
In [ ]: