Fix parallel child log typing

Resolve tool-specific log objects for all parallel child jobs, removing the code_execution_tool-only special case and using native get_log_object() when available, with generic fallback.

Update parallel_tools docs to reflect the shared logging contract and add coverage for wait/ fallback behavior in parallel-tool tests.
This commit is contained in:
Alessandro 2026-07-09 17:34:48 +02:00
parent 1a6d0eb614
commit 8d79f556c6
3 changed files with 143 additions and 32 deletions

View file

@ -497,24 +497,7 @@ async def execute_tool_call(agent: "Agent", tool_name: str, tool_args: dict[str,
if tool_name == "parallel":
raise ValueError("`parallel` cannot be nested inside a parallel worker.")
tool = None
try:
import helpers.mcp_handler as mcp_helper
tool = mcp_helper.MCPConfig.get_instance().get_tool(agent, tool_name)
except ImportError:
tool = None
except Exception as exc:
PrintStyle.warning(f"Failed to initialize MCP tool '{tool_name}' for parallel job: {exc}")
if not tool:
tool = agent.get_tool(
name=tool_name,
method=None,
args=tool_args,
message=json.dumps({"tool_name": tool_name, "tool_args": tool_args}),
loop_data=agent.loop_data,
)
tool = _resolve_parallel_tool(agent, tool_name, tool_args, strict=True)
if not tool:
raise ValueError(f"Tool '{tool_name}' not found or could not be initialized.")
@ -544,6 +527,58 @@ async def execute_tool_call(agent: "Agent", tool_name: str, tool_args: dict[str,
agent.loop_data.current_tool = None
def _resolve_parallel_tool(
agent: "Agent",
tool_name: str,
tool_args: dict[str, Any],
*,
strict: bool = False,
):
message = json.dumps({"tool_name": tool_name, "tool_args": tool_args})
tool = None
try:
import helpers.mcp_handler as mcp_helper
tool = mcp_helper.MCPConfig.get_instance().get_tool(agent, tool_name)
except ImportError:
tool = None
except Exception as exc:
if strict:
raise
PrintStyle.warning(f"Failed to initialize MCP tool '{tool_name}' for parallel job: {exc}")
if not tool:
get_tool = getattr(agent, "get_tool", None)
if not callable(get_tool):
if strict:
raise ValueError(f"Tool '{tool_name}' not found or could not be initialized.")
return None
try:
tool = get_tool(
name=tool_name,
method=None,
args=tool_args,
message=message,
loop_data=getattr(agent, "loop_data", None),
)
except Exception as exc:
if strict:
raise
PrintStyle.warning(f"Failed to initialize tool '{tool_name}' for parallel job: {exc}")
tool = None
if not tool:
return None
try:
tool.args = dict(tool_args)
except Exception:
pass
return tool
async def _cancel_job(
job: ParallelJob,
*,
@ -600,18 +635,16 @@ def _log_parallel_child_started(agent: "Agent", job: ParallelJob) -> None:
)
return
if job.tool_name == "code_execution_tool":
runtime = job.tool_args.get("runtime", "unknown")
session = job.tool_args.get("session", None)
session_text = f"[{session}] " if session or session == 0 else ""
job.log_item = agent.context.log.log(
type="code_exe",
heading=f"icon://terminal {session_text}code_execution_tool - {runtime}",
content="",
kvps=job.tool_args,
id=job.log_id,
)
return
tool = _resolve_parallel_tool(agent, job.tool_name, job.tool_args)
if tool is not None:
try:
job.log_item = tool.get_log_object()
if job.log_item is not None:
return
except Exception as exc:
PrintStyle.warning(
f"Failed to derive parallel child log for {job.tool_name}: {exc}"
)
heading = f"icon://construction {agent.agent_name}: Using tool '{job.tool_name}'"
job.log_item = agent.context.log.log(

View file

@ -31,7 +31,7 @@
- Direct tool background context cleanup removes both the in-memory context and any transient chat folder left on disk.
- Parent-visible child log items are created for each wrapped call so the WebUI can inspect concurrent children separately while the wrapper result remains model-history-only.
- Child tool logs mirror normal tool-call visible args; job ids remain available through wrapper results and prompt extras rather than visible process-step args.
- Wrapped `code_execution_tool` child logs use the normal `code_exe` WebUI message type and terminal heading so they render like direct code execution calls.
- Wrapped tool child logs use each tool's native `get_log_object()` output when available, preserving special log rendering (for example: `code_execution_tool` uses `code_exe`, `wait` uses `progress`, MCP tools use `mcp`, and regular tools use `tool`).
- Job IDs are stable handles for later await, collect, or cancel operations.
- Prompt extras must stay bounded and expose only job IDs, tool names, status, and compact result/error summaries.