feat: reset bot session context while retaining monitoring history

This commit is contained in:
Hyu
2026-10-10 22:35:27 +08:00
parent b8a7649ac2
commit 1c70218383
20 changed files with 408 additions and 5 deletions
@@ -0,0 +1,46 @@
"""Scoped context reset markers, independent of retained monitoring history."""
import hashlib
import json
import sqlalchemy as sa
from ...entity.persistence.runner_state import RunnerState
HOST_RESET_RUNNER = '__langbot_context_reset__'
def reset_scope(workspace, bot, conversation):
return 'host-reset:' + hashlib.sha256(json.dumps([workspace, bot, conversation]).encode()).hexdigest()
async def get_reset_generation(engine, workspace, bot, conversation):
if not all((workspace, bot, conversation)):
return None
async with engine.connect() as connection:
value = (
await connection.execute(
sa.select(RunnerState.value_json).where(
RunnerState.scope_key == reset_scope(workspace, bot, conversation),
RunnerState.state_key == 'generation',
RunnerState.runner_id == HOST_RESET_RUNNER,
)
)
).scalar_one_or_none()
return json.loads(value) if value else None
def active_history_condition(transcript):
cutoff = (
sa.select(sa.func.max(RunnerState.created_at))
.where(
RunnerState.runner_id == HOST_RESET_RUNNER,
RunnerState.state_key == 'generation',
RunnerState.workspace_id == transcript.workspace_id,
RunnerState.bot_id == transcript.bot_id,
RunnerState.conversation_id == transcript.conversation_id,
)
.correlate(transcript)
.scalar_subquery()
)
return sa.or_(cutoff.is_(None), transcript.created_at > cutoff)
@@ -191,7 +191,9 @@ class PersistentStateStore:
# Seed external.conversation_id from event.conversation_id if not set
if self._check_scope_enabled('conversation', binding) and event.conversation_id:
if 'external.conversation_id' not in snapshot['conversation']:
snapshot['conversation']['external.conversation_id'] = event.conversation_id
from .context_reset import get_reset_generation
generation = await get_reset_generation(self._db_engine, event.workspace_id, event.bot_id, event.conversation_id)
snapshot['conversation']['external.conversation_id'] = generation or event.conversation_id
return snapshot
@@ -335,6 +335,8 @@ class TranscriptStore:
thread_id: str | None,
strict_thread: bool,
) -> typing.Any:
from .context_reset import active_history_condition
query = query.where(active_history_condition(Transcript))
if bot_id is not None:
query = query.where(Transcript.bot_id == bot_id)
if workspace_id is not None: