Files
roboco/roboco/models/session.py
T

144 lines
4.4 KiB
Python
Raw Normal View History

2025-12-10 02:49:54 +01:00
"""
Session Model
Sessions group messages within boundaries (time, count, content length).
They are automatically created and closed based on configuration.
"""
2025-12-11 01:05:20 +01:00
from datetime import UTC, datetime, timedelta
2025-12-10 02:49:54 +01:00
from uuid import UUID, uuid4
from pydantic import Field
from roboco.models.base import (
RobocoBase,
SessionStatus,
TimestampMixin,
)
# =============================================================================
# SUPPORTING MODELS
# =============================================================================
class SessionConfig(RobocoBase):
"""Configuration for session boundaries."""
max_time_window: timedelta | None = Field(
default=timedelta(minutes=30), description="Maximum session duration"
)
max_message_count: int | None = Field(
default=100, ge=1, description="Maximum messages per session"
)
max_content_length: int | None = Field(
default=50000, ge=1, description="Maximum total characters per session"
)
timeout_seconds: int = Field(
default=300, ge=0, description="Inactivity timeout in seconds"
)
# =============================================================================
# MAIN SESSION MODEL
# =============================================================================
class Session(TimestampMixin):
"""
A session groups messages within boundaries.
Sessions can be bounded by time, message count, or content length.
Any boundary being reached triggers session closure.
"""
# Identity
id: UUID = Field(default_factory=uuid4, description="Session ID (sesh_id)")
group_id: UUID = Field(..., description="Parent group ID")
# Boundaries
max_time_window: timedelta | None = Field(
default=timedelta(minutes=30), description="Maximum session duration"
)
max_message_count: int | None = Field(
default=100, ge=1, description="Maximum messages per session"
)
max_content_length: int | None = Field(
default=50000, ge=1, description="Maximum total characters"
)
# Timeout
timeout_seconds: int = Field(default=300, ge=0, description="Inactivity timeout")
2025-12-10 02:49:54 +01:00
# State
status: SessionStatus = Field(default=SessionStatus.ACTIVE)
# Timestamps
2025-12-12 02:45:47 +01:00
started_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
last_activity_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
2025-12-10 02:49:54 +01:00
closed_at: datetime | None = None
# Statistics
message_count: int = Field(default=0, ge=0)
total_content_length: int = Field(default=0, ge=0)
@property
def is_active(self) -> bool:
"""Check if session is still active."""
return self.status == SessionStatus.ACTIVE
@property
def duration(self) -> timedelta:
"""Get session duration."""
2025-12-11 01:05:20 +01:00
end = self.closed_at or datetime.now(UTC)
2025-12-10 02:49:54 +01:00
return end - self.started_at
def should_close(self) -> bool:
"""Check if any boundary has been reached."""
2025-12-11 01:05:20 +01:00
now = datetime.now(UTC)
2025-12-10 02:49:54 +01:00
# Check time window
if self.max_time_window and now - self.started_at >= self.max_time_window:
return True
2025-12-10 02:49:54 +01:00
# Check message count
if self.max_message_count and self.message_count >= self.max_message_count:
return True
2025-12-10 02:49:54 +01:00
# Check content length
if (
self.max_content_length
and self.total_content_length >= self.max_content_length
):
return True
2025-12-10 02:49:54 +01:00
# Check timeout
if self.timeout_seconds > 0:
inactivity = (now - self.last_activity_at).total_seconds()
if inactivity >= self.timeout_seconds:
return True
return False
def add_message(self, content_length: int) -> None:
"""Record a new message in the session."""
self.message_count += 1
self.total_content_length += content_length
2025-12-11 01:05:20 +01:00
self.last_activity_at = datetime.now(UTC)
2025-12-10 02:49:54 +01:00
def close(self, timed_out: bool = False) -> None:
"""Close the session."""
2025-12-11 01:05:20 +01:00
self.closed_at = datetime.now(UTC)
2025-12-10 02:49:54 +01:00
self.status = SessionStatus.TIMED_OUT if timed_out else SessionStatus.CLOSED
# =============================================================================
# CREATE SCHEMA
# =============================================================================
class SessionCreate(RobocoBase):
"""Schema for creating a new session."""
group_id: UUID
config: SessionConfig | None = None