explicit_interrupt was emitted for every AuxiliaryExplicitCancellation, including the host cancelling the commit fence on timeout (gateway hygiene 30 s budget, the 600 s total ceiling while still streaming). Those are real failures, but the skipped set made them read as a user stop. Use explicit_interrupt only when the user's stop event is set; a fence cancel without it reports commit_fence_cancelled (already a failed class). The generic-verdict deferral now defers only to the current attempt's recorded class, so a snapshot restore cannot hand it the previous attempt's class. The attempt emitters move to agent/conversation_compression_telemetry.py (same logger name) so the facade stays under its line cap.
76 lines
3.8 KiB
Python
76 lines
3.8 KiB
Python
"""Content-free per-attempt compression telemetry (attempt log line + shared metric).
|
|
|
|
Sibling of ``agent/conversation_compression.py`` (the facade), which imports it at module level; this
|
|
module must never import the facade (import cycle). It logs under the facade's logger name so log
|
|
consumers keep one source.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import time
|
|
import uuid
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger("agent.conversation_compression")
|
|
|
|
|
|
# Caller-side abort verdicts that only restate an outcome the compressor already classified more precisely:
|
|
# ``no_progress`` ("the transcript came back unchanged") covers the structural no-ops
|
|
# (``no_compressible_window``, ``insufficient_messages``, ``empty_post_handoff_window``) and
|
|
# ``summary_generation_aborted`` covers the terminal summary failures (``summary_auth_failure``,
|
|
# ``summary_overload_failure``, ...). The emitter keeps the compressor's class for these so the attempt log
|
|
# says WHY, not just THAT (#131412). Every other caller label names an event the compressor cannot see
|
|
# (fence cancelled, superseded, rollback, pool saturated, ...) and still wins.
|
|
_GENERIC_ABORT_VERDICTS = frozenset({"no_progress", "summary_generation_aborted"})
|
|
|
|
|
|
def _emit_compression_attempt_telemetry(
|
|
agent: Any, *, started_at: float, commit_status: str, split_status: str, failure_class: str | None = None,
|
|
commit_started_at: float | None = None,
|
|
) -> None:
|
|
"""Emit one content-free JSON log line for a compression attempt."""
|
|
try:
|
|
compressor = agent.context_compressor
|
|
telemetry = getattr(compressor, "_last_compression_telemetry", None)
|
|
if not isinstance(telemetry, dict):
|
|
telemetry = {}
|
|
payload = dict(telemetry)
|
|
payload.setdefault("event", "compression_attempt")
|
|
payload.setdefault("attempt_id", getattr(agent, "_compression_attempt_id", "") or uuid.uuid4().hex)
|
|
payload.setdefault("session_id", getattr(agent, "session_id", "") or "")
|
|
payload.update(
|
|
total_duration_ms=int((time.monotonic() - started_at) * 1000), commit_status=commit_status,
|
|
split_status=split_status,
|
|
)
|
|
if commit_started_at is not None:
|
|
telemetry["commit_ms"] = payload["commit_ms"] = max(0, int((time.monotonic() - commit_started_at) * 1000))
|
|
# Defer only to THIS attempt's class: an abort restore can put the previous attempt's telemetry back.
|
|
_own_class = telemetry.get("attempt_id") == getattr(agent, "_compression_attempt_id", None)
|
|
if failure_class and not (failure_class in _GENERIC_ABORT_VERDICTS and _own_class and payload.get("failure_class")):
|
|
payload["failure_class"] = failure_class
|
|
payload.setdefault("chunking", False)
|
|
payload.setdefault("chunk_count", 0)
|
|
payload["fallback_used"] = bool(
|
|
payload.get("fallback_used")
|
|
or getattr(compressor, "_last_summary_fallback_used", False)
|
|
or getattr(compressor, "_last_aux_model_failure_model", None)
|
|
)
|
|
logger.info(
|
|
"context compression attempt telemetry: %s", json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
|
)
|
|
from hermes_cli.observability.shared_metrics_events import finish_compression_attempt
|
|
|
|
finish_compression_attempt(
|
|
commit_status, payload.get("failure_class"), getattr(agent.context_compressor, "context_length", None), agent=agent,
|
|
)
|
|
except Exception as exc:
|
|
logger.debug("failed to emit compression attempt telemetry: %s", exc, exc_info=True)
|
|
|
|
|
|
def _emit_aborted_attempt_telemetry(agent: Any, started_at: float, failure_class: str | None) -> None:
|
|
_emit_compression_attempt_telemetry(
|
|
agent, started_at=started_at, commit_status="aborted", split_status="aborted", failure_class=failure_class
|
|
)
|