11import time
2- from typing import Dict , List , Optional
2+ from typing import Sequence
33
44from pydantic import Field , model_validator
55
6- from app .agent .toolcall import ToolCallAgent
6+ from app .agent .toolcall import CompatibleToolCallObject , ToolCallAgent
77from app .logger import logger
88from app .prompt .planning import NEXT_STEP_PROMPT , PLANNING_SYSTEM_PROMPT
99from app .schema import TOOL_CHOICE_TYPE , Message , ToolCall , ToolChoice
@@ -28,14 +28,16 @@ class PlanningAgent(ToolCallAgent):
2828 default_factory = lambda : ToolCollection (PlanningTool (), Terminate ())
2929 )
3030 tool_choices : TOOL_CHOICE_TYPE = ToolChoice .AUTO # type: ignore
31- special_tool_names : List [str ] = Field (default_factory = lambda : [Terminate ().name ])
31+ special_tool_names : list [str ] = Field (default_factory = lambda : [Terminate ().name ])
3232
33- tool_calls : List [ToolCall ] = Field (default_factory = list )
34- active_plan_id : Optional [str ] = Field (default = None )
33+ tool_calls : Sequence [ToolCall | CompatibleToolCallObject ] = Field (
34+ default_factory = list
35+ )
36+ active_plan_id : str | None = Field (default = None )
3537
3638 # Add a dictionary to track the step status for each tool call
37- step_execution_tracker : Dict [str , Dict ] = Field (default_factory = dict )
38- current_step_index : Optional [ int ] = None
39+ step_execution_tracker : dict [str , dict ] = Field (default_factory = dict )
40+ current_step_index : int | None = None
3941
4042 max_steps : int = 20
4143
@@ -113,7 +115,7 @@ async def get_plan(self) -> str:
113115 )
114116 return result .output if hasattr (result , "output" ) else str (result )
115117
116- async def run (self , request : Optional [ str ] = None ) -> str :
118+ async def run (self , request : str | None = None ) -> str :
117119 """Run the agent with an optional initial request."""
118120 if request :
119121 await self .create_initial_plan (request )
@@ -155,7 +157,7 @@ async def update_plan_status(self, tool_call_id: str) -> None:
155157 except Exception as e :
156158 logger .warning (f"Failed to update plan status: { e } " )
157159
158- async def _get_current_step_index (self ) -> Optional [ int ] :
160+ async def _get_current_step_index (self ) -> int | None :
159161 """
160162 Parse the current plan to identify the first non-completed step's index.
161163 Returns None if no active step is found.
@@ -209,19 +211,22 @@ async def create_initial_plan(self, request: str) -> None:
209211 ]
210212 self .memory .add_messages (messages )
211213 response = await self .llm .ask_tool (
212- messages = messages ,
214+ messages = list ( messages ) ,
213215 system_msgs = [Message .system_message (self .system_prompt )],
214216 tools = self .available_tools .to_params (),
215217 tool_choice = ToolChoice .AUTO ,
216218 )
219+ if not response :
220+ raise RuntimeError ("Model refused to create plan" )
221+ tool_calls = response .tool_calls or []
217222 assistant_msg = Message .from_tool_calls (
218- content = response .content , tool_calls = response . tool_calls
223+ content = response .content or "" , tool_calls = tool_calls
219224 )
220225
221226 self .memory .add_message (assistant_msg )
222227
223228 plan_created = False
224- for tool_call in response . tool_calls :
229+ for tool_call in tool_calls :
225230 if tool_call .function .name == "planning" :
226231 result = await self .execute_tool (tool_call )
227232 logger .info (
0 commit comments