feat(webui): add assistant reply fork-from-here
This commit is contained in:
committed by
Xubin Ren
parent
4a58b83acc
commit
03bca4c0a9
@@ -5,6 +5,7 @@ import os
|
||||
import re
|
||||
import shutil
|
||||
from contextlib import suppress
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -30,6 +31,14 @@ _TOOL_CALL_ECHO_RE = re.compile(r'^\s*(?:generate_image|message)\([^)]*\)\s*$')
|
||||
_SESSION_PREVIEW_MAX_CHARS = 120
|
||||
_SESSION_LIST_PREVIEW_MAX_RECORDS = 200
|
||||
_SESSION_LIST_PREVIEW_MAX_CHARS = 1_000_000
|
||||
_FORK_VOLATILE_METADATA_KEYS = {
|
||||
"goal_state",
|
||||
"pending_user_turn",
|
||||
"runtime_checkpoint",
|
||||
"thread_goal",
|
||||
"title",
|
||||
"title_user_edited",
|
||||
}
|
||||
|
||||
|
||||
def _sanitize_assistant_replay_text(content: str) -> str:
|
||||
@@ -628,6 +637,62 @@ class SessionManager:
|
||||
logger.warning("Failed to delete session file {}: {}", path, e)
|
||||
return False
|
||||
|
||||
def fork_session_before_user_index(
|
||||
self,
|
||||
source_key: str,
|
||||
target_key: str,
|
||||
before_user_index: int,
|
||||
) -> Session | None:
|
||||
"""Create *target_key* from *source_key* before a global user-message index.
|
||||
|
||||
``before_user_index`` is zero-based over user messages in the full session:
|
||||
``0`` means "before the first user message", ``1`` means "before the
|
||||
second user message", and so on. A value equal to the total user-message
|
||||
count copies the full session prefix. The target user message itself is
|
||||
not copied; the WebUI pre-fills it in the composer for editing and resend.
|
||||
"""
|
||||
if before_user_index < 0:
|
||||
return None
|
||||
source = self._cache.get(source_key) or self._load(source_key)
|
||||
if source is None:
|
||||
return None
|
||||
|
||||
copied: list[dict[str, Any]] = []
|
||||
user_index = 0
|
||||
found_target = False
|
||||
for message in source.messages:
|
||||
if message.get("role") == "user":
|
||||
if user_index == before_user_index:
|
||||
found_target = True
|
||||
break
|
||||
user_index += 1
|
||||
copied.append(deepcopy(message))
|
||||
if user_index == before_user_index:
|
||||
found_target = True
|
||||
if not found_target:
|
||||
return None
|
||||
|
||||
metadata = deepcopy(source.metadata)
|
||||
for key in _FORK_VOLATILE_METADATA_KEYS:
|
||||
metadata.pop(key, None)
|
||||
|
||||
last_consolidated = min(source.last_consolidated, len(copied))
|
||||
if source.last_consolidated > len(copied):
|
||||
metadata.pop("_last_summary", None)
|
||||
last_consolidated = 0
|
||||
|
||||
now = datetime.now()
|
||||
target = Session(
|
||||
key=target_key,
|
||||
messages=copied,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
metadata=metadata,
|
||||
last_consolidated=last_consolidated,
|
||||
)
|
||||
self.save(target, fsync=True)
|
||||
return target
|
||||
|
||||
def read_session_file(self, key: str) -> dict[str, Any] | None:
|
||||
"""Load a session from disk without caching; intended for read-only HTTP endpoints.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user