diff --git a/examples/llm-compiler/LLMCompiler.ipynb b/examples/llm-compiler/LLMCompiler.ipynb index e77331bda..24d9fcc52 100644 --- a/examples/llm-compiler/LLMCompiler.ipynb +++ b/examples/llm-compiler/LLMCompiler.ipynb @@ -222,11 +222,11 @@ " llm: BaseChatModel, tools: Sequence[BaseTool], base_prompt: ChatPromptTemplate\n", "):\n", " tool_descriptions = \"\\n\".join(\n", - " f\"{i}. {tool.description}\\n\" for i, tool in enumerate(tools)\n", + " f\"{i+1}. {tool.description}\\n\" for i, tool in enumerate(tools) # +1 to offset the 0 starting index, we want it count normally from 1.\n", " )\n", " planner_prompt = base_prompt.partial(\n", " replan=\"\",\n", - " num_tools=len(tools),\n", + " num_tools=len(tools)+1, # Add one because we're adding the join() tool at the end.\n", " tool_descriptions=tool_descriptions,\n", " )\n", " replanner_prompt = base_prompt.partial(\n", @@ -236,7 +236,7 @@ " ' - When starting the Current Plan, you should start with \"Thought\" that outlines the strategy for the next plan.\\n'\n", " \" - In the Current Plan, you should NEVER repeat the actions that are already executed in the Previous Plan.\\n\"\n", " \" - You must continue the task index from the end of the previous one. Do not repeat task indices.\",\n", - " num_tools=len(tools),\n", + " num_tools=len(tools)+1,\n", " tool_descriptions=tool_descriptions,\n", " )\n", "\n", @@ -338,6 +338,7 @@ "source": [ "from typing import Any, Union, Iterable, List, Tuple, Dict\n", "from typing_extensions import TypedDict\n", + "import re\n", "\n", "from langchain_core.runnables import (\n", " chain as as_runnable,\n", @@ -391,13 +392,20 @@ "\n", "\n", "def _resolve_arg(arg: Union[str, Any], observations: Dict[int, Any]):\n", - " if isinstance(arg, str) and arg.startswith(\"$\"):\n", - " try:\n", - " stripped = arg[1:].replace(\".output\", \"\").strip(\"{}\")\n", - " idx = int(stripped)\n", - " except Exception:\n", - " return str(arg)\n", - " return str(observations[idx])\n", + " # $1 or ${1} -> 1\n", + " ID_PATTERN = r\"\\$\\{?(\\d+)\\}?\"\n", + "\n", + " def replace_match(match):\n", + " # If the string is ${123}, match.group(0) is ${123}, and match.group(1) is 123.\n", + "\n", + " # Return the match group, in this case the index, from the string. This is the index\n", + " # number we get back.\n", + " idx = int(match.group(1))\n", + " return str(observations.get(idx, match.group(0)))\n", + "\n", + " # For dependencies on other tasks\n", + " if isinstance(arg, str):\n", + " return re.sub(ID_PATTERN, replace_match, arg)\n", " elif isinstance(arg, list):\n", " return [_resolve_arg(a, observations) for a in arg]\n", " else:\n", @@ -440,6 +448,7 @@ " # adjust to do a proper topological sort (not-stream)\n", " # or use a more complicated data structure\n", " tasks = scheduler_input[\"tasks\"]\n", + " args_for_tasks = {}\n", " messages = scheduler_input[\"messages\"]\n", " # If we are re-planning, we may have calls that depend on previous\n", " # plans. Start with those.\n", @@ -456,6 +465,7 @@ " task_names[task[\"idx\"]] = (\n", " task[\"tool\"] if isinstance(task[\"tool\"], str) else task[\"tool\"].name\n", " )\n", + " args_for_tasks[task[\"idx\"]] = (task[\"args\"])\n", " if (\n", " # Depends on other tasks\n", " deps\n", @@ -477,12 +487,12 @@ " wait(futures)\n", " # Convert observations to new tool messages to add to the state\n", " new_observations = {\n", - " k: (task_names[k], observations[k])\n", + " k: (task_names[k], args_for_tasks[k], observations[k])\n", " for k in sorted(observations.keys() - originals)\n", " }\n", " tool_messages = [\n", - " FunctionMessage(name=name, content=str(obs), additional_kwargs={\"idx\": k})\n", - " for k, (name, obs) in new_observations.items()\n", + " FunctionMessage(name=name, content=str(obs), additional_kwargs={\"idx\": k, 'args':task_args})\n", + " for k, (name, task_args, obs) in new_observations.items()\n", " ]\n", " return tool_messages" ] @@ -501,7 +511,11 @@ "def plan_and_schedule(messages: List[BaseMessage], config):\n", " tasks = planner.stream(messages, config)\n", " # Begin executing the planner immediately\n", - " tasks = itertools.chain([next(tasks)], tasks)\n", + " try:\n", + " tasks = itertools.chain([next(tasks)], tasks)\n", + " except StopIteration:\n", + " # Handle the case where tasks is empty.\n", + " tasks = iter([])\n", " scheduled_tasks = schedule_tasks.invoke(\n", " {\n", " \"messages\": messages,\n",