feat(logs): Implement file-based offloading for scripts and tool outputs
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import threading
|
||||
import time
|
||||
import copy
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
@@ -1062,12 +1063,31 @@ class AppController:
|
||||
sys.stderr.flush()
|
||||
self.event_queue.put("response", {"text": f"ERROR: {e}", "status": "error", "role": "System"})
|
||||
|
||||
def _offload_entry_payload(self, entry: Dict[str, Any]) -> Dict[str, Any]:
|
||||
optimized = copy.deepcopy(entry)
|
||||
kind = optimized.get("kind")
|
||||
payload = optimized.get("payload", {})
|
||||
if kind == "tool_result" and "output" in payload:
|
||||
output = payload["output"]
|
||||
ref_path = session_logger.log_tool_output(output)
|
||||
if ref_path:
|
||||
filename = Path(ref_path).name
|
||||
payload["output"] = f"[REF:{filename}]"
|
||||
if kind == "tool_call" and "script" in payload:
|
||||
script = payload["script"]
|
||||
ref_path = session_logger.log_tool_call(script, "LOG_ONLY", None)
|
||||
if ref_path:
|
||||
filename = Path(ref_path).name
|
||||
payload["script"] = f"[REF:{filename}]"
|
||||
return optimized
|
||||
|
||||
def _on_ai_stream(self, text: str) -> None:
|
||||
"""Handles streaming text from the AI."""
|
||||
self.event_queue.put("response", {"text": text, "status": "streaming...", "role": "AI"})
|
||||
|
||||
def _on_comms_entry(self, entry: Dict[str, Any]) -> None:
|
||||
session_logger.log_comms(entry)
|
||||
optimized_entry = self._offload_entry_payload(entry)
|
||||
session_logger.log_comms(optimized_entry)
|
||||
entry["local_ts"] = time.time()
|
||||
kind = entry.get("kind")
|
||||
payload = entry.get("payload", {})
|
||||
@@ -1128,6 +1148,7 @@ class AppController:
|
||||
|
||||
def _on_tool_log(self, script: str, result: str) -> None:
|
||||
session_logger.log_tool_call(script, result, None)
|
||||
session_logger.log_tool_output(result)
|
||||
source_tier = ai_client.get_current_tier()
|
||||
with self._pending_tool_calls_lock:
|
||||
self._pending_tool_calls.append({"script": script, "result": result, "ts": time.time(), "source_tier": source_tier})
|
||||
|
||||
@@ -29,7 +29,9 @@ _ts: str = "" # session timestamp string e.g. "20260301_142233"
|
||||
_session_id: str = "" # YYYYMMDD_HHMMSS[_Label]
|
||||
_session_dir: Optional[Path] = None # Path to the sub-directory for this session
|
||||
_seq: int = 0 # monotonic counter for script files this session
|
||||
_output_seq: int = 0 # monotonic counter for output files this session
|
||||
_seq_lock: threading.Lock = threading.Lock()
|
||||
_output_seq_lock: threading.Lock = threading.Lock()
|
||||
|
||||
_comms_fh: Optional[TextIO] = None # file handle: logs/sessions/<session_id>/comms.log
|
||||
_tool_fh: Optional[TextIO] = None # file handle: logs/sessions/<session_id>/toolcalls.log
|
||||
@@ -44,7 +46,7 @@ def open_session(label: Optional[str] = None) -> None:
|
||||
Called once at GUI startup. Creates the log directories if needed and
|
||||
opens the log files for this session within a sub-directory.
|
||||
"""
|
||||
global _ts, _session_id, _session_dir, _comms_fh, _tool_fh, _api_fh, _cli_fh, _seq
|
||||
global _ts, _session_id, _session_dir, _comms_fh, _tool_fh, _api_fh, _cli_fh, _seq, _output_seq
|
||||
if _comms_fh is not None:
|
||||
return
|
||||
|
||||
@@ -56,9 +58,13 @@ def open_session(label: Optional[str] = None) -> None:
|
||||
|
||||
_session_dir = paths.get_logs_dir() / _session_id
|
||||
_session_dir.mkdir(parents=True, exist_ok=True)
|
||||
(_session_dir / "scripts").mkdir(exist_ok=True)
|
||||
(_session_dir / "outputs").mkdir(exist_ok=True)
|
||||
|
||||
paths.get_scripts_dir().mkdir(parents=True, exist_ok=True)
|
||||
|
||||
_seq = 0
|
||||
_output_seq = 0
|
||||
_comms_fh = open(_session_dir / "comms.log", "w", encoding="utf-8", buffering=1)
|
||||
_tool_fh = open(_session_dir / "toolcalls.log", "w", encoding="utf-8", buffering=1)
|
||||
_api_fh = open(_session_dir / "apihooks.log", "w", encoding="utf-8", buffering=1)
|
||||
@@ -132,7 +138,7 @@ def log_comms(entry: dict[str, Any]) -> None:
|
||||
def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optional[str]:
|
||||
"""
|
||||
Append a tool-call record to the toolcalls log and write the PS1 script to
|
||||
scripts/generated/. Returns the path of the written script file.
|
||||
the session's scripts directory. Returns the path of the written script file.
|
||||
"""
|
||||
global _seq
|
||||
if _tool_fh is None:
|
||||
@@ -143,8 +149,12 @@ def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optio
|
||||
seq = _seq
|
||||
|
||||
ts_entry = datetime.datetime.now().strftime("%H:%M:%S")
|
||||
ps1_name = f"{_ts}_{seq:04d}.ps1"
|
||||
ps1_path: Optional[Path] = paths.get_scripts_dir() / ps1_name
|
||||
ps1_name = f"script_{seq:04d}.ps1"
|
||||
|
||||
if _session_dir:
|
||||
ps1_path: Optional[Path] = _session_dir / "scripts" / ps1_name
|
||||
else:
|
||||
ps1_path = paths.get_scripts_dir() / f"{_ts}_{seq:04d}.ps1"
|
||||
|
||||
try:
|
||||
if ps1_path:
|
||||
@@ -167,6 +177,28 @@ def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optio
|
||||
|
||||
return str(ps1_path) if ps1_path else None
|
||||
|
||||
def log_tool_output(content: str) -> Optional[str]:
|
||||
"""
|
||||
Save tool output content to a unique file in the session's outputs directory.
|
||||
Returns the path of the written file.
|
||||
"""
|
||||
global _output_seq
|
||||
if _session_dir is None:
|
||||
return None
|
||||
|
||||
with _output_seq_lock:
|
||||
_output_seq += 1
|
||||
seq = _output_seq
|
||||
|
||||
out_name = f"output_{seq:04d}.txt"
|
||||
out_path = _session_dir / "outputs" / out_name
|
||||
|
||||
try:
|
||||
out_path.write_text(content, encoding="utf-8")
|
||||
return str(out_path)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def log_cli_call(command: str, stdin_content: Optional[str], stdout_content: Optional[str], stderr_content: Optional[str], latency: float) -> None:
|
||||
"""Log details of a CLI subprocess execution."""
|
||||
if _cli_fh is None:
|
||||
|
||||
Reference in New Issue
Block a user