Seven sites hand-rolled `float(headers.get("Retry-After"))` (anon_auth,
shared_metrics_sender, gemini_native_adapter, extract_api_error_context,
nous_rate_guard, skills_hub_github, skills_hub_clawhub x2) and silently
dropped RFC 7231 HTTP-date values that the conversation loop already honours
via agent/retry_utils.py::parse_retry_after_seconds. They now call it; per-site
caps/floors stay at the call site.
The free-text "resets in / quotaResetDelay / retry after N s" regexes lived in
two tables (agent_runtime_helpers vs credential_pool) whose "resets in"
grammars diverged: the pool accepted only integer `Nhr Nmin` while the error
context accepted h/hr/hours + m/min/minutes + s/seconds with decimals. One table
(agent/retry_utils.py::RETRY_DELAY_PATTERNS / reset_delay_from_message) using
the wider grammar, so a pooled credential's cooldown and the UI's reset time
now agree.
33 lines
1.4 KiB
Python
33 lines
1.4 KiB
Python
"""Head/tail truncation for oversized tool output (terminal, execute_code, MCP results).
|
|
|
|
One algorithm and one notice wording: 40% head (errors surface early) / 60% tail (the most
|
|
recent lines matter most) around a single ``... [<LABEL> TRUNCATED - N <unit> omitted out of
|
|
T total] ...`` marker, so downstream code that recognises the marker sees one shape. Limits
|
|
live in ``tools/tool_output_limits.py``; line-snapped, path-bearing footers (web/browser,
|
|
read_file pagination) are different products and stay separate.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
HEAD_RATIO = 0.4
|
|
|
|
|
|
def truncation_notice(omitted: int, total: int, *, label: str = "OUTPUT", unit: str = "chars") -> str:
|
|
return f"\n\n... [{label} TRUNCATED - {omitted:,} {unit} omitted out of {total:,} total] ...\n\n"
|
|
|
|
|
|
def head_tail_split(budget: int) -> tuple[int, int]:
|
|
"""``(head, tail)`` character budgets for ``budget`` total."""
|
|
head = int(budget * HEAD_RATIO)
|
|
return head, budget - head
|
|
|
|
|
|
def truncate_head_tail(text: str, max_chars: int, *, label: str = "OUTPUT") -> str:
|
|
"""``text`` unchanged when it fits ``max_chars``; otherwise head + notice + tail (the kept
|
|
text is exactly ``max_chars`` long, the notice rides on top)."""
|
|
if len(text) <= max_chars:
|
|
return text
|
|
head, tail = head_tail_split(max_chars)
|
|
omitted = len(text) - head - tail
|
|
return text[:head] + truncation_notice(omitted, len(text), label=label) + text[-tail:]
|