| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 |
- """Pure conversation-session identity tracking.
- :class:`SessionTracker` mints one id for the first observed utterance and rolls
- over to a new id once the quiet gap between consecutive utterances exceeds the
- configured threshold. It has no filesystem, config, or daemon dependencies: the
- caller supplies the clock (``now``) and may inject the id factory, which keeps
- the behavior deterministic and easy to test.
- """
- import threading
- from datetime import datetime
- from typing import Callable
- def _default_id_factory(now: float) -> str:
- """Format an epoch timestamp as a second-resolution session id."""
- return datetime.fromtimestamp(now).strftime("%Y-%m-%d-%H-%M-%S")
- class SessionTracker:
- """Assigns a stable session id and rolls over after a quiet gap.
- Args:
- gap_s: Seconds of silence between consecutive observations that starts
- a new session. Values ``<= 0`` disable rollover after the first id.
- id_factory: Builds the id from an epoch timestamp. Defaults to the
- local-time ``YYYY-MM-DD-HH-MM-SS`` format; tests inject a counter.
- """
- def __init__(self, gap_s: float, id_factory: Callable[[float], str] | None = None):
- self._gap_s = gap_s
- self._id_factory = id_factory or _default_id_factory
- self._lock = threading.Lock()
- self._current_id: str | None = None
- self._last: float = 0.0
- def observe(self, now: float) -> str:
- """Return the session id for an observation at epoch ``now``.
- Mints on the first call, and mints a new id when ``now - last > gap_s``
- (only when ``gap_s > 0``); otherwise returns the current id. The
- observation time is remembered so the gap is measured between
- consecutive observations. Thread-safe.
- """
- with self._lock:
- if self._current_id is None or (
- self._gap_s > 0 and now - self._last > self._gap_s
- ):
- self._current_id = self._id_factory(now)
- self._last = now
- return self._current_id
- @property
- def current_id(self) -> str | None:
- """The current session id, or ``None`` before the first observation."""
- with self._lock:
- return self._current_id
|