Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions api/gateway_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
unregister_stream_owner,
update_active_run,
)
from api.helpers import _redact_text, redact_session_data
from api.helpers import _redact_text
from api.models import clear_process_wakeup_pause, get_session, merge_session_messages_append_only
from api.run_journal import RunJournalWriter, bound_run_journal_snapshot_args

Expand Down Expand Up @@ -737,7 +737,7 @@ def _settle_gateway_terminal_error(session_id, stream_id, workspace, model, mode
_classify_provider_error,
_materialize_pending_user_turn_before_error,
_provider_error_payload,
_session_payload_with_full_messages,
_best_effort_terminal_session_payload,
_snapshot_and_append_partial_on_error,
_terminal_turn_duration,
)
Expand Down Expand Up @@ -788,9 +788,9 @@ def _settle_gateway_terminal_error(session_id, stream_id, workspace, model, mode
terminal_session_persisted = True
except Exception:
logger.debug("Failed to persist gateway terminal error settlement", exc_info=True)
error_payload["session"] = redact_session_data(
_session_payload_with_full_messages(session, tool_calls=[])
)
terminal_session_payload = _best_effort_terminal_session_payload(session)
if terminal_session_payload is not None:
error_payload["session"] = terminal_session_payload
error_payload["session_id"] = session.session_id
error_payload["terminal_session_persisted"] = terminal_session_persisted
if terminal_session_persisted:
Expand Down Expand Up @@ -1351,9 +1351,12 @@ def _restore_cancelled_success_writeback():
session_id,
goal_exc,
)
from api.streaming import _session_payload_with_full_messages
gateway_session_payload = _session_payload_with_full_messages(s, tool_calls=[])
put_gateway_event("done", {"session": redact_session_data(gateway_session_payload), "usage": usage})
from api.streaming import _best_effort_terminal_session_payload
gateway_done_payload = {"usage": usage}
gateway_session_payload = _best_effort_terminal_session_payload(s)
if gateway_session_payload is not None:
gateway_done_payload["session"] = gateway_session_payload
put_gateway_event("done", gateway_done_payload)
put_gateway_event("stream_end", {"session_id": session_id})
except urllib.error.HTTPError as exc:
try:
Expand Down
44 changes: 1 addition & 43 deletions api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
)
from api.gateway_restart import restart_active_profile_gateway
from api.shares import create_or_refresh_share, load_share, revoke_share
from api.session_ops import _messages_for_limited_payload

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -8772,7 +8773,6 @@ def _message_window_for_display(messages, msg_limit=None, msg_before=None, expan
return window, start_idx


_LIMITED_TOOL_CONTENT_MAX_CHARS = 4096
# Server-side ceiling on the ?msg_limit= tail-window size. A client could
# otherwise request msg_limit=1000000 and force the server to assemble and
# serialize an unbounded message payload (the frontend's own pagination grows
Expand Down Expand Up @@ -8841,48 +8841,6 @@ def _state_db_backstop_limit_for_display(session, msg_before) -> int | None:
return None if has_boundary_prefix else _STATE_DB_DISPLAY_ROW_BACKSTOP


_LIMITED_TOOL_CONTENT_NOTICE = (
"\n\n[Tool output truncated in paginated session response; "
"load the full transcript to inspect the complete result.]"
)


def _tool_message_for_limited_payload(message):
"""Return a bounded copy of large hidden tool-result rows for paginated loads."""
if not isinstance(message, dict) or str(message.get("role") or "").lower() != "tool":
return message
content = message.get("content")
if content in (None, ""):
return message
if isinstance(content, str):
text = content
else:
try:
text = json.dumps(content, ensure_ascii=False, default=str)
except Exception:
text = str(content)
if len(text) <= _LIMITED_TOOL_CONTENT_MAX_CHARS:
return message
clipped = dict(message)
preview = text[:_LIMITED_TOOL_CONTENT_MAX_CHARS] + _LIMITED_TOOL_CONTENT_NOTICE
if isinstance(content, str):
clipped["content"] = preview
elif isinstance(content, list):
clipped["content"] = [{"type": "text", "text": preview}]
elif isinstance(content, dict):
clipped["content"] = {"_truncated": True, "preview": preview}
else:
clipped["content"] = preview
clipped["_content_truncated"] = True
clipped["_content_original_chars"] = len(text)
return clipped


def _messages_for_limited_payload(messages) -> list:
"""Bound hidden tool-result payloads before sending a msg_limit response."""
return [_tool_message_for_limited_payload(msg) for msg in list(messages or [])]


def _limited_webui_messages_for_display(session, state_db_messages) -> list:
"""Return the display sidecar plus only necessary state.db rows for msg_limit.

Expand Down
41 changes: 41 additions & 0 deletions api/session_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,47 @@
logger = logging.getLogger(__name__)

AUTO_TITLE_LABELS = {'untitled', 'new chat'}
_LIMITED_TOOL_CONTENT_MAX_CHARS = 4096
_LIMITED_TOOL_CONTENT_NOTICE = (
"\n\n[Tool output truncated in paginated session response; "
"load the full transcript to inspect the complete result.]"
)


def _tool_message_for_limited_payload(message):
"""Return a bounded copy of a large hidden tool-result row."""
if not isinstance(message, dict) or str(message.get("role") or "").lower() != "tool":
return message
content = message.get("content")
if content in (None, ""):
return message
if isinstance(content, str):
text = content
else:
try:
text = json.dumps(content, ensure_ascii=False, default=str)
except Exception:
text = str(content)
if len(text) <= _LIMITED_TOOL_CONTENT_MAX_CHARS:
return message
clipped = dict(message)
preview = text[:_LIMITED_TOOL_CONTENT_MAX_CHARS] + _LIMITED_TOOL_CONTENT_NOTICE
if isinstance(content, str):
clipped["content"] = preview
elif isinstance(content, list):
clipped["content"] = [{"type": "text", "text": preview}]
elif isinstance(content, dict):
clipped["content"] = {"_truncated": True, "preview": preview}
else:
clipped["content"] = preview
clipped["_content_truncated"] = True
clipped["_content_original_chars"] = len(text)
return clipped


def _messages_for_limited_payload(messages) -> list:
"""Bound hidden tool-result rows before a limited session payload is sent."""
return [_tool_message_for_limited_payload(message) for message in list(messages or [])]


def _live_active_stream_id(session) -> str | None:
Expand Down
Loading
Loading