Execute a single plan step using the LLM for tool call generation. When a model_manager is available, the LLM generates the tool call from the step description and tool schemas, then the tool is executed. If the tool fails, the error is fed back to the LLM for retry.
(
self,
step: dict[str, Any],
var_context: dict[str, Any],
)
| 337 | return self._tool_catalog |
| 338 | |
| 339 | async def _execute_step( |
| 340 | self, |
| 341 | step: dict[str, Any], |
| 342 | var_context: dict[str, Any], |
| 343 | ) -> StepResult: |
| 344 | """Execute a single plan step using the LLM for tool call generation. |
| 345 | |
| 346 | When a model_manager is available, the LLM generates the tool call |
| 347 | from the step description and tool schemas, then the tool is executed. |
| 348 | If the tool fails, the error is fed back to the LLM for retry. |
| 349 | |
| 350 | Falls back to static arg execution when no model_manager is provided. |
| 351 | """ |
| 352 | step_index = step.get("index", "?") |
| 353 | step_title = step.get("title", "Untitled") |
| 354 | tool_calls = step.get("tool_calls", []) |
| 355 | hint_tool = tool_calls[0]["name"] if tool_calls else step.get("tool", "none") |
| 356 | hint_args = ( |
| 357 | tool_calls[0].get("args", {}) if tool_calls else step.get("args", {}) |
| 358 | ) |
| 359 | |
| 360 | if self._on_step_start: |
| 361 | self._on_step_start(step_index, step_title, hint_tool) |
| 362 | |
| 363 | start_time = time.perf_counter() |
| 364 | |
| 365 | # LLM-driven execution (agentic loop with retry) |
| 366 | if self._model_manager: |
| 367 | logger.info( |
| 368 | "Step %s: using agentic LLM execution (model_manager=%s)", |
| 369 | step_index, |
| 370 | type(self._model_manager).__name__, |
| 371 | ) |
| 372 | step_result = await self._execute_step_with_llm( |
| 373 | step, var_context, step_index, step_title, hint_tool, hint_args |
| 374 | ) |
| 375 | else: |
| 376 | logger.info( |
| 377 | "Step %s: using static arg execution (no model_manager)", step_index |
| 378 | ) |
| 379 | step_result = await self._execute_step_static( |
| 380 | step, var_context, step_index, step_title, hint_tool, hint_args |
| 381 | ) |
| 382 | |
| 383 | step_result.duration = time.perf_counter() - start_time |
| 384 | |
| 385 | if self._on_step_complete: |
| 386 | self._on_step_complete(step_result) |
| 387 | |
| 388 | return step_result |
| 389 | |
| 390 | async def _execute_step_static( |
| 391 | self, |
no test coverage detected