feat: keep reasoning in history with item ids
This commit is contained in:
@@ -11,6 +11,7 @@ ELLIPSIS = "…"
|
|||||||
STATUS_RUNNING = "▸"
|
STATUS_RUNNING = "▸"
|
||||||
STATUS_DONE = "✓"
|
STATUS_DONE = "✓"
|
||||||
HEADER_SEP = " · "
|
HEADER_SEP = " · "
|
||||||
|
HARD_BREAK = " \n"
|
||||||
|
|
||||||
MAX_CMD_LEN = 40
|
MAX_CMD_LEN = 40
|
||||||
MAX_REASON_LEN = 80
|
MAX_REASON_LEN = 80
|
||||||
@@ -94,6 +95,12 @@ def _format_tool_call(server: str, tool: str) -> str:
|
|||||||
return name or "tool"
|
return name or "tool"
|
||||||
|
|
||||||
|
|
||||||
|
def _with_id(item_id: Optional[str], line: str) -> str:
|
||||||
|
if item_id:
|
||||||
|
return f"[{item_id}] {line}"
|
||||||
|
return f"[?] {line}"
|
||||||
|
|
||||||
|
|
||||||
def _truncate_output(text: str, max_lines: int = 20, max_chars: int = 4000) -> str:
|
def _truncate_output(text: str, max_lines: int = 20, max_chars: int = 4000) -> str:
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
return ""
|
||||||
@@ -146,6 +153,10 @@ def _set_current_action(state: ExecRenderState, item_id: Optional[str], line: st
|
|||||||
|
|
||||||
def _complete_action(state: ExecRenderState, item_id: Optional[str], line: str) -> bool:
|
def _complete_action(state: ExecRenderState, item_id: Optional[str], line: str) -> bool:
|
||||||
changed = False
|
changed = False
|
||||||
|
if state.current_reasoning:
|
||||||
|
if not state.recent_actions or state.recent_actions[-1] != state.current_reasoning:
|
||||||
|
state.recent_actions.append(state.current_reasoning)
|
||||||
|
changed = True
|
||||||
if line:
|
if line:
|
||||||
state.recent_actions.append(line)
|
state.recent_actions.append(line)
|
||||||
changed = True
|
changed = True
|
||||||
@@ -154,6 +165,8 @@ def _complete_action(state: ExecRenderState, item_id: Optional[str], line: str)
|
|||||||
state.current_action_id = None
|
state.current_action_id = None
|
||||||
state.current_reasoning = None
|
state.current_reasoning = None
|
||||||
changed = True
|
changed = True
|
||||||
|
if not item_id and state.current_action_id is None:
|
||||||
|
state.current_reasoning = None
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
|
|
||||||
@@ -258,47 +271,48 @@ class ExecProgressRenderer:
|
|||||||
if itype == "reasoning":
|
if itype == "reasoning":
|
||||||
reasoning = _format_reasoning(item.get("text", ""))
|
reasoning = _format_reasoning(item.get("text", ""))
|
||||||
if reasoning:
|
if reasoning:
|
||||||
|
reasoning_line = _with_id(item_id, reasoning)
|
||||||
if self.state.current_action and not self.state.current_reasoning:
|
if self.state.current_action and not self.state.current_reasoning:
|
||||||
self.state.current_reasoning = reasoning
|
self.state.current_reasoning = reasoning_line
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
self.state.pending_reasoning = reasoning
|
self.state.pending_reasoning = reasoning_line
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
if itype == "command_execution":
|
if itype == "command_execution":
|
||||||
command = _format_command(item.get("command", ""))
|
command = _format_command(item.get("command", ""))
|
||||||
if etype == "item.started":
|
if etype == "item.started":
|
||||||
line = f"{STATUS_RUNNING} running: {command}"
|
line = _with_id(item_id, f"{STATUS_RUNNING} running: {command}")
|
||||||
changed = _set_current_action(self.state, item_id, line) or changed
|
changed = _set_current_action(self.state, item_id, line) or changed
|
||||||
elif etype == "item.completed":
|
elif etype == "item.completed":
|
||||||
exit_code = item.get("exit_code")
|
exit_code = item.get("exit_code")
|
||||||
exit_part = f" (exit {exit_code})" if exit_code is not None else ""
|
exit_part = f" (exit {exit_code})" if exit_code is not None else ""
|
||||||
line = f"{STATUS_DONE} ran: {command}{exit_part}"
|
line = _with_id(item_id, f"{STATUS_DONE} ran: {command}{exit_part}")
|
||||||
changed = _complete_action(self.state, item_id, line) or changed
|
changed = _complete_action(self.state, item_id, line) or changed
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
if itype == "mcp_tool_call":
|
if itype == "mcp_tool_call":
|
||||||
name = _format_tool_call(item.get("server", ""), item.get("tool", ""))
|
name = _format_tool_call(item.get("server", ""), item.get("tool", ""))
|
||||||
if etype == "item.started":
|
if etype == "item.started":
|
||||||
line = f"{STATUS_RUNNING} tool: {name}"
|
line = _with_id(item_id, f"{STATUS_RUNNING} tool: {name}")
|
||||||
changed = _set_current_action(self.state, item_id, line) or changed
|
changed = _set_current_action(self.state, item_id, line) or changed
|
||||||
elif etype == "item.completed":
|
elif etype == "item.completed":
|
||||||
line = f"{STATUS_DONE} tool: {name}"
|
line = _with_id(item_id, f"{STATUS_DONE} tool: {name}")
|
||||||
changed = _complete_action(self.state, item_id, line) or changed
|
changed = _complete_action(self.state, item_id, line) or changed
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
if itype == "web_search" and etype == "item.completed":
|
if itype == "web_search" and etype == "item.completed":
|
||||||
query = _format_query(item.get("query", ""))
|
query = _format_query(item.get("query", ""))
|
||||||
line = f"{STATUS_DONE} searched: {query}"
|
line = _with_id(item_id, f"{STATUS_DONE} searched: {query}")
|
||||||
return _complete_action(self.state, item_id, line) or changed
|
return _complete_action(self.state, item_id, line) or changed
|
||||||
|
|
||||||
if itype == "file_change" and etype == "item.completed":
|
if itype == "file_change" and etype == "item.completed":
|
||||||
line = f"{STATUS_DONE} {_format_file_change(item.get('changes', []))}"
|
line = _with_id(item_id, f"{STATUS_DONE} {_format_file_change(item.get('changes', []))}")
|
||||||
return _complete_action(self.state, item_id, line) or changed
|
return _complete_action(self.state, item_id, line) or changed
|
||||||
|
|
||||||
if itype == "error" and etype == "item.completed":
|
if itype == "error" and etype == "item.completed":
|
||||||
warning = _truncate(item.get("message", ""), 120)
|
warning = _truncate(item.get("message", ""), 120)
|
||||||
line = f"{STATUS_DONE} warning: {warning}"
|
line = _with_id(item_id, f"{STATUS_DONE} warning: {warning}")
|
||||||
return _complete_action(self.state, item_id, line) or changed
|
return _complete_action(self.state, item_id, line) or changed
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
@@ -350,4 +364,4 @@ class ExecProgressRenderer:
|
|||||||
def _assemble(header: str, lines: list[str]) -> str:
|
def _assemble(header: str, lines: list[str]) -> str:
|
||||||
if not lines:
|
if not lines:
|
||||||
return header
|
return header
|
||||||
return header + "\n\n" + "\n".join(lines)
|
return header + "\n\n" + HARD_BREAK.join(lines)
|
||||||
|
|||||||
Reference in New Issue
Block a user