klausur-service (11 files): - cv_gutter_repair, ocr_pipeline_regression, upload_api - ocr_pipeline_sessions, smart_spell, nru_worksheet_generator - ocr_pipeline_overlays, mail/aggregator, zeugnis_api - cv_syllable_detect, self_rag backend-lehrer (17 files): - classroom_engine/suggestions, generators/quiz_generator - worksheets_api, llm_gateway/comparison, state_engine_api - classroom/models (→ 4 submodules), services/file_processor - alerts_agent/api/wizard+digests+routes, content_generators/pdf - classroom/routes/sessions, llm_gateway/inference - classroom_engine/analytics, auth/keycloak_auth - alerts_agent/processing/rule_engine, ai_processor/print_versions agent-core (5 files): - brain/memory_store, brain/knowledge_graph, brain/context_manager - orchestrator/supervisor, sessions/session_manager admin-lehrer (5 components): - GridOverlay, StepGridReview, DevOpsPipelineSidebar - DataFlowDiagram, sbom/wizard/page website (2 files): - DependencyMap, lehrer/abitur-archiv Other: nibis_ingestion, grid_detection_service, export-doclayout-onnx Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
181 lines
5.9 KiB
Python
181 lines
5.9 KiB
Python
"""
|
|
Session Models for Breakpilot Agents
|
|
|
|
Data classes for agent sessions, checkpoints, and state tracking.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime, timezone, timedelta
|
|
from typing import Dict, Any, Optional, List
|
|
from enum import Enum
|
|
import uuid
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SessionState(Enum):
|
|
"""Agent session states"""
|
|
ACTIVE = "active"
|
|
PAUSED = "paused"
|
|
COMPLETED = "completed"
|
|
FAILED = "failed"
|
|
|
|
|
|
@dataclass
|
|
class SessionCheckpoint:
|
|
"""Represents a checkpoint in an agent session"""
|
|
name: str
|
|
timestamp: datetime
|
|
data: Dict[str, Any]
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return {
|
|
"name": self.name,
|
|
"timestamp": self.timestamp.isoformat(),
|
|
"data": self.data
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> "SessionCheckpoint":
|
|
return cls(
|
|
name=data["name"],
|
|
timestamp=datetime.fromisoformat(data["timestamp"]),
|
|
data=data["data"]
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class AgentSession:
|
|
"""
|
|
Represents an active agent session.
|
|
|
|
Attributes:
|
|
session_id: Unique session identifier
|
|
agent_type: Type of agent (tutor, grader, quality-judge, alert, orchestrator)
|
|
user_id: Associated user ID
|
|
state: Current session state
|
|
created_at: Session creation timestamp
|
|
last_heartbeat: Last heartbeat timestamp
|
|
context: Session context data
|
|
checkpoints: List of session checkpoints for recovery
|
|
"""
|
|
session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
agent_type: str = ""
|
|
user_id: str = ""
|
|
state: SessionState = SessionState.ACTIVE
|
|
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
last_heartbeat: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
context: Dict[str, Any] = field(default_factory=dict)
|
|
checkpoints: List[SessionCheckpoint] = field(default_factory=list)
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def checkpoint(self, name: str, data: Dict[str, Any]) -> SessionCheckpoint:
|
|
"""
|
|
Creates a checkpoint for recovery.
|
|
|
|
Args:
|
|
name: Checkpoint name (e.g., "task_received", "processing_complete")
|
|
data: Checkpoint data to store
|
|
|
|
Returns:
|
|
The created checkpoint
|
|
"""
|
|
checkpoint = SessionCheckpoint(
|
|
name=name,
|
|
timestamp=datetime.now(timezone.utc),
|
|
data=data
|
|
)
|
|
self.checkpoints.append(checkpoint)
|
|
logger.debug(f"Session {self.session_id}: Checkpoint '{name}' created")
|
|
return checkpoint
|
|
|
|
def heartbeat(self) -> None:
|
|
"""Updates the heartbeat timestamp"""
|
|
self.last_heartbeat = datetime.now(timezone.utc)
|
|
|
|
def pause(self) -> None:
|
|
"""Pauses the session"""
|
|
self.state = SessionState.PAUSED
|
|
self.checkpoint("session_paused", {"previous_state": "active"})
|
|
|
|
def resume(self) -> None:
|
|
"""Resumes a paused session"""
|
|
if self.state == SessionState.PAUSED:
|
|
self.state = SessionState.ACTIVE
|
|
self.heartbeat()
|
|
self.checkpoint("session_resumed", {})
|
|
|
|
def complete(self, result: Optional[Dict[str, Any]] = None) -> None:
|
|
"""Marks the session as completed"""
|
|
self.state = SessionState.COMPLETED
|
|
self.checkpoint("session_completed", {"result": result or {}})
|
|
|
|
def fail(self, error: str, error_details: Optional[Dict[str, Any]] = None) -> None:
|
|
"""Marks the session as failed"""
|
|
self.state = SessionState.FAILED
|
|
self.checkpoint("session_failed", {
|
|
"error": error,
|
|
"details": error_details or {}
|
|
})
|
|
|
|
def get_last_checkpoint(self, name: Optional[str] = None) -> Optional[SessionCheckpoint]:
|
|
"""
|
|
Gets the last checkpoint, optionally filtered by name.
|
|
|
|
Args:
|
|
name: Optional checkpoint name to filter by
|
|
|
|
Returns:
|
|
The last matching checkpoint or None
|
|
"""
|
|
if not self.checkpoints:
|
|
return None
|
|
|
|
if name:
|
|
matching = [cp for cp in self.checkpoints if cp.name == name]
|
|
return matching[-1] if matching else None
|
|
|
|
return self.checkpoints[-1]
|
|
|
|
def get_duration(self) -> timedelta:
|
|
"""Returns the session duration"""
|
|
end_time = datetime.now(timezone.utc)
|
|
if self.state in (SessionState.COMPLETED, SessionState.FAILED):
|
|
last_cp = self.get_last_checkpoint()
|
|
if last_cp:
|
|
end_time = last_cp.timestamp
|
|
return end_time - self.created_at
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Serializes the session to a dictionary"""
|
|
return {
|
|
"session_id": self.session_id,
|
|
"agent_type": self.agent_type,
|
|
"user_id": self.user_id,
|
|
"state": self.state.value,
|
|
"created_at": self.created_at.isoformat(),
|
|
"last_heartbeat": self.last_heartbeat.isoformat(),
|
|
"context": self.context,
|
|
"checkpoints": [cp.to_dict() for cp in self.checkpoints],
|
|
"metadata": self.metadata
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> "AgentSession":
|
|
"""Deserializes a session from a dictionary"""
|
|
return cls(
|
|
session_id=data["session_id"],
|
|
agent_type=data["agent_type"],
|
|
user_id=data["user_id"],
|
|
state=SessionState(data["state"]),
|
|
created_at=datetime.fromisoformat(data["created_at"]),
|
|
last_heartbeat=datetime.fromisoformat(data["last_heartbeat"]),
|
|
context=data.get("context", {}),
|
|
checkpoints=[
|
|
SessionCheckpoint.from_dict(cp)
|
|
for cp in data.get("checkpoints", [])
|
|
],
|
|
metadata=data.get("metadata", {})
|
|
)
|