feat: just more cleaning

This commit is contained in:
Brooklyn Nicholson 2026-04-15 14:14:01 -05:00
parent 46cef4b7fa
commit 4b4b4d47bc
24 changed files with 2852 additions and 829 deletions

View file

@ -155,7 +155,7 @@ def _strip_blocked_tools(toolsets: List[str]) -> List[str]:
return [t for t in toolsets if t not in blocked_toolset_names]
def _build_child_progress_callback(task_index: int, parent_agent, task_count: int = 1) -> Optional[callable]:
def _build_child_progress_callback(task_index: int, goal: str, parent_agent, task_count: int = 1) -> Optional[callable]:
"""Build a callback that relays child agent tool calls to the parent display.
Two display paths:
@ -173,14 +173,46 @@ def _build_child_progress_callback(task_index: int, parent_agent, task_count: in
# Show 1-indexed prefix only in batch mode (multiple tasks)
prefix = f"[{task_index + 1}] " if task_count > 1 else ""
goal_label = (goal or "").strip()
# Gateway: batch tool names, flush periodically
_BATCH_SIZE = 5
_batch: List[str] = []
def _relay(event_type: str, tool_name: str = None, preview: str = None, args=None, **kwargs):
if not parent_cb:
return
try:
parent_cb(
event_type,
tool_name,
preview,
args,
task_index=task_index,
task_count=task_count,
goal=goal_label,
**kwargs,
)
except Exception as e:
logger.debug("Parent callback failed: %s", e)
def _callback(event_type: str, tool_name: str = None, preview: str = None, args=None, **kwargs):
# event_type is one of: "tool.started", "tool.completed",
# "reasoning.available", "_thinking", "subagent_progress"
# "reasoning.available", "_thinking", "subagent.*"
if event_type == "subagent.start":
if spinner and goal_label:
short = (goal_label[:55] + "...") if len(goal_label) > 55 else goal_label
try:
spinner.print_above(f" {prefix}├─ 🔀 {short}")
except Exception as e:
logger.debug("Spinner print_above failed: %s", e)
_relay("subagent.start", preview=preview or goal_label or "", **kwargs)
return
if event_type == "subagent.complete":
_relay("subagent.complete", preview=preview, **kwargs)
return
# "_thinking" / reasoning events
if event_type in ("_thinking", "reasoning.available"):
@ -191,7 +223,7 @@ def _build_child_progress_callback(task_index: int, parent_agent, task_count: in
spinner.print_above(f" {prefix}├─ 💭 \"{short}\"")
except Exception as e:
logger.debug("Spinner print_above failed: %s", e)
# Don't relay thinking to gateway (too noisy for chat)
_relay("subagent.thinking", preview=text)
return
# tool.completed — no display needed here (spinner shows on started)
@ -212,23 +244,18 @@ def _build_child_progress_callback(task_index: int, parent_agent, task_count: in
logger.debug("Spinner print_above failed: %s", e)
if parent_cb:
_relay("subagent.tool", tool_name, preview, args)
_batch.append(tool_name or "")
if len(_batch) >= _BATCH_SIZE:
summary = ", ".join(_batch)
try:
parent_cb("subagent_progress", f"🔀 {prefix}{summary}")
except Exception as e:
logger.debug("Parent callback failed: %s", e)
_relay("subagent.progress", preview=f"🔀 {prefix}{summary}")
_batch.clear()
def _flush():
"""Flush remaining batched tool names to gateway on completion."""
if parent_cb and _batch:
summary = ", ".join(_batch)
try:
parent_cb("subagent_progress", f"🔀 {prefix}{summary}")
except Exception as e:
logger.debug("Parent callback flush failed: %s", e)
_relay("subagent.progress", preview=f"🔀 {prefix}{summary}")
_batch.clear()
_callback._flush = _flush
@ -242,6 +269,7 @@ def _build_child_agent(
toolsets: Optional[List[str]],
model: Optional[str],
max_iterations: int,
task_count: int,
parent_agent,
# Credential overrides from delegation config (provider:model resolution)
override_provider: Optional[str] = None,
@ -298,7 +326,7 @@ def _build_child_agent(
parent_api_key = parent_agent._client_kwargs.get("api_key")
# Build progress callback to relay tool calls to parent display
child_progress_cb = _build_child_progress_callback(task_index, parent_agent)
child_progress_cb = _build_child_progress_callback(task_index, goal, parent_agent, task_count)
# Each subagent gets its own iteration budget capped at max_iterations
# (configurable via delegation.max_iterations, default 50). This means
@ -469,6 +497,12 @@ def _run_single_child(
_heartbeat_thread.start()
try:
if child_progress_cb:
try:
child_progress_cb("subagent.start", preview=goal)
except Exception as e:
logger.debug("Progress callback start failed: %s", e)
result = child.run_conversation(user_message=goal)
# Flush any remaining batched progress to gateway
@ -563,11 +597,34 @@ def _run_single_child(
if status == "failed":
entry["error"] = result.get("error", "Subagent did not produce a response.")
if child_progress_cb:
try:
child_progress_cb(
"subagent.complete",
preview=summary[:160] if summary else entry.get("error", ""),
status=status,
duration_seconds=duration,
summary=summary[:500] if summary else entry.get("error", ""),
)
except Exception as e:
logger.debug("Progress callback completion failed: %s", e)
return entry
except Exception as exc:
duration = round(time.monotonic() - child_start, 2)
logging.exception(f"[subagent-{task_index}] failed")
if child_progress_cb:
try:
child_progress_cb(
"subagent.complete",
preview=str(exc),
status="failed",
duration_seconds=duration,
summary=str(exc),
)
except Exception as e:
logger.debug("Progress callback failure relay failed: %s", e)
return {
"task_index": task_index,
"status": "error",
@ -714,7 +771,7 @@ def delegate_task(
child = _build_child_agent(
task_index=i, goal=t["goal"], context=t.get("context"),
toolsets=t.get("toolsets") or toolsets, model=creds["model"],
max_iterations=effective_max_iter, parent_agent=parent_agent,
max_iterations=effective_max_iter, task_count=n_tasks, parent_agent=parent_agent,
override_provider=creds["provider"], override_base_url=creds["base_url"],
override_api_key=creds["api_key"],
override_api_mode=creds["api_mode"],