58 lines
2.8 KiB
Python
58 lines
2.8 KiB
Python
"""
|
|
Tests for :func:`omnigent.runtime.tool_output.cap_tool_output` — the canonical
|
|
size cap applied to every ``function_call_output`` producer's ``output`` field.
|
|
Each assertion is chosen so the corresponding production breakage turns it red.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from omnigent.runtime.tool_output import MAX_TOOL_OUTPUT_BYTES, cap_tool_output
|
|
|
|
_TRUNCATION_MARKER = "[output truncated by omnigent:"
|
|
|
|
|
|
def test_cap_tool_output_passes_through_when_within_cap() -> None:
|
|
"""A tool result at or under the cap is returned unchanged (same object)."""
|
|
small = "hello world"
|
|
# `is` (not just ==) proves no re-encode/copy on the common path.
|
|
assert cap_tool_output(small) is small
|
|
# Exactly at the cap is still within bounds — the check is `<=`, so a
|
|
# full-size-but-not-over result must hit the same no-copy passthrough
|
|
# (`is`), not fall into the truncation branch (which builds a new string).
|
|
at_cap = "x" * MAX_TOOL_OUTPUT_BYTES
|
|
assert cap_tool_output(at_cap) is at_cap
|
|
assert _TRUNCATION_MARKER not in cap_tool_output(at_cap)
|
|
|
|
|
|
def test_cap_tool_output_truncates_when_over_cap() -> None:
|
|
"""An over-cap result is truncated to the cap plus a byte-accurate notice."""
|
|
over = 50_000
|
|
big = "x" * (MAX_TOOL_OUTPUT_BYTES + over)
|
|
capped = cap_tool_output(big)
|
|
# The kept prefix is exactly the first MAX_TOOL_OUTPUT_BYTES bytes...
|
|
assert capped.startswith("x" * MAX_TOOL_OUTPUT_BYTES)
|
|
# ...followed by the notice naming how many bytes were dropped. If `over`
|
|
# is reported wrong, the byte arithmetic in cap_tool_output regressed.
|
|
assert _TRUNCATION_MARKER in capped
|
|
assert f"{over} of {MAX_TOOL_OUTPUT_BYTES + over} bytes omitted" in capped
|
|
# The whole capped string stays bounded: kept bytes + the short notice,
|
|
# never the multi-MB original. A failure here means truncation didn't fire.
|
|
assert len(capped.encode("utf-8")) < MAX_TOOL_OUTPUT_BYTES + over
|
|
|
|
|
|
def test_cap_tool_output_truncates_on_a_character_boundary() -> None:
|
|
"""Truncation never splits a multibyte UTF-8 char (no decode error / U+FFFD)."""
|
|
# "€" is 3 UTF-8 bytes. Sizing the string so the byte cap lands mid-char
|
|
# forces the boundary logic: cap+1 chars => 3*(cap+1) bytes, well over cap,
|
|
# and MAX_TOOL_OUTPUT_BYTES is not a multiple of 3, so the cap splits a char.
|
|
euros = "€" * (MAX_TOOL_OUTPUT_BYTES + 1)
|
|
capped = cap_tool_output(euros)
|
|
# decode("utf-8") on the kept prefix would have raised / inserted U+FFFD if
|
|
# a partial char survived; assert clean euros only before the notice.
|
|
kept = capped.split("\n\n" + _TRUNCATION_MARKER)[0]
|
|
assert "�" not in kept
|
|
assert set(kept) == {"€"}
|
|
# The kept prefix is the largest whole-char run within the byte cap.
|
|
assert len(kept.encode("utf-8")) <= MAX_TOOL_OUTPUT_BYTES
|
|
assert len(kept.encode("utf-8")) > MAX_TOOL_OUTPUT_BYTES - 3
|