"""工具输出处理原语(从 tools/base.py 析出,2026-07-23——base 只留 Tool 基类与路径边界)。"""
from __future__ import annotations


def compact_tool_output(
    text: str,
    *,
    max_chars: int = 8_000,
    head_chars: int = 4_000,
    tail_chars: int = 2_000,
) -> str:
    """压缩长工具输出,保留头尾和截断说明。"""
    if len(text) <= max_chars:
        return text
    head_chars = max(0, min(head_chars, max_chars))
    tail_chars = max(0, min(tail_chars, max_chars - head_chars))
    removed = len(text) - head_chars - tail_chars
    return (
        text[:head_chars]
        + f"\n[... truncated, {removed} chars omitted ...]\n"
        + (text[-tail_chars:] if tail_chars else "")
    )


_TIMEOUT_HINT = (
    "若是批量循环(检索 / PDF 抽取 / 下载等):① 据上面已完成的部分**续跑剩下的**,别整批重跑;"
    "② 每完成一项就落盘 / 打印进度,超时也不丢;③ 大批量 / 大下载改用 background=true 后台跑,"
    "再用 check_process 取结果;④ 单次别塞太多项。"
)


def format_timeout_result(stdout: str, stderr: str, timeout_s: int) -> str:
    """run_python / shell 超时结果:带上超时前已捕获的部分输出 + 续跑提示。

    进程被 kill 前跑出的 stdout/stderr 本已捕获(subprocess 标准行为:TimeoutExpired
    带 .stdout/.stderr、docker 路径 kill 后 communicate() 续读),旧实现直接丢弃只回一句
    超时 → 模型看不到「跑到哪了」、整批重来(工具失败面板 #8:批量检索 / PDF 抽取 / 下载
    超时反复重跑,累计最多)。这里把部分输出一并返回,让模型据此续跑未完成的。"""
    parts = []
    if stdout and stdout.strip():
        parts.append(f"[stdout]\n{stdout.rstrip()}")
    if stderr and stderr.strip():
        parts.append(f"[stderr]\n{stderr.rstrip()}")
    parts.append(
        f"[Error] command timed out after {timeout_s}s(进程已被杀,上方为超时前的部分输出)。"
    )
    parts.append(_TIMEOUT_HINT)
    return "\n".join(parts)
