mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 15:37:55 +08:00
core/tools: token-ify per-stream caps and resume memory remainders
This commit is contained in:
@@ -8,24 +8,46 @@ from plyngent.agent import ToolTag, tool
|
||||
from plyngent.agent.budget import DEFAULT_TOOL_RESULT_MAX_CHARS
|
||||
from plyngent.tools.file.read import line_range_label, read_raw_text
|
||||
from plyngent.tools.net.fetch import fetch
|
||||
from plyngent.tools.truncate_token import decode_truncate_token, truncate_with_token
|
||||
from plyngent.tools.truncate_token import (
|
||||
decode_truncate_token,
|
||||
get_remainder,
|
||||
truncate_with_token,
|
||||
)
|
||||
|
||||
|
||||
@tool(tags=ToolTag.LOCAL | ToolTag.INSTANCE_STATE)
|
||||
async def get_truncated(token: str, *, max_chars: int = DEFAULT_TOOL_RESULT_MAX_CHARS) -> str:
|
||||
"""Fetch the next chunk of a truncated result using its ``TRUNCATE_TOKEN``.
|
||||
"""Fetch the next chunk of a truncated result using its ``truncate_token``.
|
||||
|
||||
Pass the token shown at the end of a truncated ``read_file``, ``fetch``, or
|
||||
earlier ``get_truncated`` result to keep reading without re-requesting the
|
||||
whole source or raising limits. Truncated output carries a fresh token, so
|
||||
chunks chain indefinitely. File chunks start with a 1-based ``L{begin}-{end}``
|
||||
line range like ``read_file``.
|
||||
Pass the token shown at the end of a truncated ``read_file``, ``fetch``,
|
||||
``run_command``, or earlier ``get_truncated`` result to keep reading without
|
||||
re-requesting the whole source or raising limits. Truncated output carries a
|
||||
fresh token, so chunks chain indefinitely. File chunks start with a 1-based
|
||||
``L{begin}-{end}`` line range like ``read_file``; memory chunks (generic
|
||||
tool output) are served from a short-lived in-memory store that is forgotten
|
||||
when the agent exits.
|
||||
"""
|
||||
parsed = decode_truncate_token(token)
|
||||
if parsed is None:
|
||||
return "error: invalid truncate token"
|
||||
if parsed.kind == "http":
|
||||
return await fetch.handler(parsed.location, offset=parsed.offset, max_chars=parsed.limit)
|
||||
if parsed.kind == "memory":
|
||||
remainder = get_remainder(parsed.location)
|
||||
if remainder is None:
|
||||
return "error: truncate token expired (no longer in memory); re-run the tool"
|
||||
start = parsed.offset
|
||||
segment = remainder[start : start + max_chars]
|
||||
chunk, _ = truncate_with_token(
|
||||
segment,
|
||||
max_chars,
|
||||
kind="memory",
|
||||
location=parsed.location,
|
||||
offset=start,
|
||||
limit=max_chars,
|
||||
total_len=len(remainder),
|
||||
)
|
||||
return chunk
|
||||
text, err = read_raw_text(parsed.location)
|
||||
if err:
|
||||
return err
|
||||
@@ -41,6 +63,6 @@ async def get_truncated(token: str, *, max_chars: int = DEFAULT_TOOL_RESULT_MAX_
|
||||
limit=max_chars,
|
||||
total_len=len(text),
|
||||
)
|
||||
content_len = len(chunk.split("\n...[", 1)[0]) if "\n...[" in chunk else len(chunk)
|
||||
content_len = len(chunk.split("\n[Truncated", 1)[0]) if "\n[Truncated" in chunk else len(chunk)
|
||||
header = line_range_label(text, start, start + content_len)
|
||||
return f"{header}\n{chunk}"
|
||||
|
||||
@@ -22,9 +22,19 @@ DEFAULT_MAX_BATCH_STEPS = 20
|
||||
|
||||
|
||||
def truncate_output(text: str, label: str, max_chars: int = DEFAULT_MAX_OUTPUT_CHARS) -> str:
|
||||
if len(text) <= max_chars:
|
||||
"""Cap a per-stream output block; embed a truncate token when cut.
|
||||
|
||||
``label`` (stdout/stderr/batch) is kept for call-site clarity but the
|
||||
marker itself is the uniform ``[Truncated (N chars max.; M omitted).
|
||||
truncate_token=...]`` (no label), matching every other truncation site.
|
||||
Returns ``text`` unchanged when it fits.
|
||||
"""
|
||||
del label
|
||||
if max_chars < 1 or len(text) <= max_chars:
|
||||
return text
|
||||
return text[:max_chars] + f"\n...[{label} truncated]"
|
||||
from plyngent.tools.truncate_token import truncate_generic
|
||||
|
||||
return truncate_generic(text, max_chars)
|
||||
|
||||
|
||||
def format_command_result(
|
||||
|
||||
Reference in New Issue
Block a user