Merge remote-tracking branch 'origin/main' into bb/gui
# Conflicts: # apps/dashboard/package-lock.json # apps/dashboard/package.json # apps/dashboard/src/components/BottomPickSheet.tsx # apps/dashboard/src/hooks/useBelowBreakpoint.ts # gateway/platforms/telegram.py # hermes_cli/gateway.py # hermes_cli/web_server.py # nix/web.nix # scripts/install.ps1 # tests/gateway/test_telegram_thread_fallback.py # tui_gateway/server.py
This commit is contained in:
@@ -378,7 +378,7 @@ class ContextCompressor(ContextEngine):
|
||||
model: str,
|
||||
context_length: int,
|
||||
base_url: str = "",
|
||||
api_key: str = "",
|
||||
api_key: Any = "",
|
||||
provider: str = "",
|
||||
api_mode: str = "",
|
||||
) -> None:
|
||||
@@ -415,6 +415,7 @@ class ContextCompressor(ContextEngine):
|
||||
config_context_length: int | None = None,
|
||||
provider: str = "",
|
||||
api_mode: str = "",
|
||||
abort_on_summary_failure: bool = False,
|
||||
):
|
||||
self.model = model
|
||||
self.base_url = base_url
|
||||
@@ -426,6 +427,11 @@ class ContextCompressor(ContextEngine):
|
||||
self.protect_last_n = protect_last_n
|
||||
self.summary_target_ratio = max(0.10, min(summary_target_ratio, 0.80))
|
||||
self.quiet_mode = quiet_mode
|
||||
# When True, summary-generation failure aborts compression entirely
|
||||
# (returns messages unchanged, sets _last_compress_aborted=True).
|
||||
# When False (default = historical behavior), insert a static
|
||||
# "summary unavailable" placeholder and drop the middle window.
|
||||
self.abort_on_summary_failure = abort_on_summary_failure
|
||||
|
||||
self.context_length = get_model_context_length(
|
||||
model, base_url=base_url, api_key=api_key,
|
||||
@@ -478,6 +484,12 @@ class ContextCompressor(ContextEngine):
|
||||
# (gateway hygiene, /compress) can surface a visible warning.
|
||||
self._last_summary_dropped_count: int = 0
|
||||
self._last_summary_fallback_used: bool = False
|
||||
# When summary generation fails we now ABORT compression entirely
|
||||
# and return the original messages unchanged instead of dropping
|
||||
# the middle window with a static placeholder. Callers inspect
|
||||
# this flag to know "compression was attempted but aborted, freeze
|
||||
# the chat until the user manually retries via /compress".
|
||||
self._last_compress_aborted: bool = False
|
||||
# When a user-configured summary model fails and we recover by
|
||||
# retrying on the main model, record the failure so gateway /
|
||||
# CLI callers can still warn the user even though compression
|
||||
@@ -1371,7 +1383,7 @@ The user has requested that this compaction PRIORITISE preserving all informatio
|
||||
# Main compression entry point
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def compress(self, messages: List[Dict[str, Any]], current_tokens: int = None, focus_topic: str = None) -> List[Dict[str, Any]]:
|
||||
def compress(self, messages: List[Dict[str, Any]], current_tokens: int = None, focus_topic: str = None, force: bool = False) -> List[Dict[str, Any]]:
|
||||
"""Compress conversation messages by summarizing middle turns.
|
||||
|
||||
Algorithm:
|
||||
@@ -1389,6 +1401,9 @@ The user has requested that this compaction PRIORITISE preserving all informatio
|
||||
provided, the summariser will prioritise preserving information
|
||||
related to this topic and be more aggressive about compressing
|
||||
everything else. Inspired by Claude Code's ``/compact``.
|
||||
force: If True, clear any active summary-failure cooldown before
|
||||
running so a manual ``/compress`` can retry immediately after
|
||||
an auto-compression abort. Auto-compress callers pass False.
|
||||
"""
|
||||
# Reset per-call summary failure state — callers inspect these fields
|
||||
# after compress() returns to decide whether to surface a warning.
|
||||
@@ -1397,6 +1412,13 @@ The user has requested that this compaction PRIORITISE preserving all informatio
|
||||
self._last_summary_error = None
|
||||
self._last_aux_model_failure_error = None
|
||||
self._last_aux_model_failure_model = None
|
||||
self._last_compress_aborted = False
|
||||
|
||||
# Manual /compress (force=True) bypasses the failure cooldown so the
|
||||
# user can retry immediately after an auto-compress abort. Without
|
||||
# this, /compress would silently no-op for 30-60s after a failure.
|
||||
if force and self._summary_failure_cooldown_until > 0.0:
|
||||
self._summary_failure_cooldown_until = 0.0
|
||||
n_messages = len(messages)
|
||||
# Only need head + 3 tail messages minimum (token budget decides the real tail size)
|
||||
_min_for_compress = self._protect_head_size(messages) + 3 + 1
|
||||
@@ -1472,6 +1494,32 @@ The user has requested that this compaction PRIORITISE preserving all informatio
|
||||
# Phase 3: Generate structured summary
|
||||
summary = self._generate_summary(turns_to_summarize, focus_topic=focus_topic)
|
||||
|
||||
# If summary generation failed, behavior splits on
|
||||
# ``abort_on_summary_failure`` (config: compression.abort_on_summary_failure):
|
||||
# True → ABORT compression entirely. Return messages unchanged
|
||||
# and set _last_compress_aborted=True so callers can warn
|
||||
# the user and stop the auto-compress retry loop.
|
||||
# False → Fall through to the legacy fallback path below: insert
|
||||
# a static "summary unavailable" placeholder and drop the
|
||||
# middle window. Records _last_summary_fallback_used /
|
||||
# _last_summary_dropped_count for gateway hygiene to
|
||||
# surface a warning.
|
||||
# Default is False (historical behavior).
|
||||
if not summary and self.abort_on_summary_failure:
|
||||
n_skipped = compress_end - compress_start
|
||||
self._last_summary_dropped_count = 0 # nothing actually dropped
|
||||
self._last_summary_fallback_used = False
|
||||
self._last_compress_aborted = True
|
||||
if not self.quiet_mode:
|
||||
logger.warning(
|
||||
"Summary generation failed — aborting compression "
|
||||
"(compression.abort_on_summary_failure=true). "
|
||||
"%d message(s) preserved unchanged. Conversation is "
|
||||
"frozen until the next /compress or /new.",
|
||||
n_skipped,
|
||||
)
|
||||
return messages
|
||||
|
||||
# Phase 4: Assemble compressed message list
|
||||
compressed = []
|
||||
for i in range(compress_start):
|
||||
@@ -1486,7 +1534,8 @@ The user has requested that this compaction PRIORITISE preserving all informatio
|
||||
)
|
||||
compressed.append(msg)
|
||||
|
||||
# If LLM summary failed, insert a static fallback so the model
|
||||
# Legacy fallback path: LLM summary failed and abort_on_summary_failure
|
||||
# is False (the default). Insert a static placeholder so the model
|
||||
# knows context was lost rather than silently dropping everything.
|
||||
if not summary:
|
||||
if not self.quiet_mode:
|
||||
|
||||
Reference in New Issue
Block a user