refactor: half way to process per session
All checks were successful
Build Worker Base and Application Images / check-base-changes (push) Successful in 7s
Build Worker Base and Application Images / build-base (push) Has been skipped
Build Worker Base and Application Images / build-docker (push) Successful in 2m52s
Build Worker Base and Application Images / deploy-stack (push) Successful in 9s

This commit is contained in:
ziesorx 2025-09-25 20:52:26 +07:00
parent 2e5316ca01
commit 34d1982e9e
12 changed files with 2771 additions and 92 deletions

View file

@ -0,0 +1,317 @@
"""
Inter-Process Communication (IPC) system for session processes.
Defines message types and protocols for main session communication.
"""
import time
from enum import Enum
from typing import Dict, Any, Optional, Union
from dataclasses import dataclass, field
import numpy as np
class MessageType(Enum):
"""Message types for IPC communication."""
# Commands: Main → Session
INITIALIZE = "initialize"
PROCESS_FRAME = "process_frame"
SET_SESSION_ID = "set_session_id"
SHUTDOWN = "shutdown"
HEALTH_CHECK = "health_check"
# Responses: Session → Main
INITIALIZED = "initialized"
DETECTION_RESULT = "detection_result"
SESSION_SET = "session_set"
SHUTDOWN_COMPLETE = "shutdown_complete"
HEALTH_RESPONSE = "health_response"
ERROR = "error"
@dataclass
class IPCMessage:
"""Base class for all IPC messages."""
type: MessageType
session_id: str
timestamp: float = field(default_factory=time.time)
message_id: str = field(default_factory=lambda: str(int(time.time() * 1000000)))
@dataclass
class InitializeCommand(IPCMessage):
"""Initialize session process with configuration."""
subscription_config: Dict[str, Any] = field(default_factory=dict)
model_config: Dict[str, Any] = field(default_factory=dict)
@dataclass
class ProcessFrameCommand(IPCMessage):
"""Process a frame through the detection pipeline."""
frame: Optional[np.ndarray] = None
display_id: str = ""
subscription_identifier: str = ""
frame_timestamp: float = 0.0
@dataclass
class SetSessionIdCommand(IPCMessage):
"""Set the session ID for the current session."""
backend_session_id: str = ""
display_id: str = ""
@dataclass
class ShutdownCommand(IPCMessage):
"""Shutdown the session process gracefully."""
@dataclass
class HealthCheckCommand(IPCMessage):
"""Check health status of session process."""
@dataclass
class InitializedResponse(IPCMessage):
"""Response indicating successful initialization."""
success: bool = False
error_message: Optional[str] = None
@dataclass
class DetectionResultResponse(IPCMessage):
"""Detection results from session process."""
detections: Dict[str, Any] = field(default_factory=dict)
processing_time: float = 0.0
phase: str = "" # "detection" or "processing"
@dataclass
class SessionSetResponse(IPCMessage):
"""Response confirming session ID was set."""
success: bool = False
backend_session_id: str = ""
@dataclass
class ShutdownCompleteResponse(IPCMessage):
"""Response confirming graceful shutdown."""
@dataclass
class HealthResponse(IPCMessage):
"""Health status response."""
status: str = "unknown" # "healthy", "degraded", "unhealthy"
memory_usage_mb: float = 0.0
cpu_percent: float = 0.0
gpu_memory_mb: Optional[float] = None
uptime_seconds: float = 0.0
processed_frames: int = 0
@dataclass
class ErrorResponse(IPCMessage):
"""Error message from session process."""
error_type: str = ""
error_message: str = ""
traceback: Optional[str] = None
# Type aliases for message unions
CommandMessage = Union[
InitializeCommand,
ProcessFrameCommand,
SetSessionIdCommand,
ShutdownCommand,
HealthCheckCommand
]
ResponseMessage = Union[
InitializedResponse,
DetectionResultResponse,
SessionSetResponse,
ShutdownCompleteResponse,
HealthResponse,
ErrorResponse
]
IPCMessageUnion = Union[CommandMessage, ResponseMessage]
class MessageSerializer:
"""Handles serialization/deserialization of IPC messages."""
@staticmethod
def serialize_message(message: IPCMessageUnion) -> Dict[str, Any]:
"""
Serialize message to dictionary for queue transport.
Args:
message: Message to serialize
Returns:
Dictionary representation of message
"""
result = {
'type': message.type.value,
'session_id': message.session_id,
'timestamp': message.timestamp,
'message_id': message.message_id,
}
# Add specific fields based on message type
if isinstance(message, InitializeCommand):
result.update({
'subscription_config': message.subscription_config,
'model_config': message.model_config
})
elif isinstance(message, ProcessFrameCommand):
result.update({
'frame': message.frame,
'display_id': message.display_id,
'subscription_identifier': message.subscription_identifier,
'frame_timestamp': message.frame_timestamp
})
elif isinstance(message, SetSessionIdCommand):
result.update({
'backend_session_id': message.backend_session_id,
'display_id': message.display_id
})
elif isinstance(message, InitializedResponse):
result.update({
'success': message.success,
'error_message': message.error_message
})
elif isinstance(message, DetectionResultResponse):
result.update({
'detections': message.detections,
'processing_time': message.processing_time,
'phase': message.phase
})
elif isinstance(message, SessionSetResponse):
result.update({
'success': message.success,
'backend_session_id': message.backend_session_id
})
elif isinstance(message, HealthResponse):
result.update({
'status': message.status,
'memory_usage_mb': message.memory_usage_mb,
'cpu_percent': message.cpu_percent,
'gpu_memory_mb': message.gpu_memory_mb,
'uptime_seconds': message.uptime_seconds,
'processed_frames': message.processed_frames
})
elif isinstance(message, ErrorResponse):
result.update({
'error_type': message.error_type,
'error_message': message.error_message,
'traceback': message.traceback
})
return result
@staticmethod
def deserialize_message(data: Dict[str, Any]) -> IPCMessageUnion:
"""
Deserialize dictionary back to message object.
Args:
data: Dictionary representation
Returns:
Deserialized message object
"""
msg_type = MessageType(data['type'])
session_id = data['session_id']
timestamp = data['timestamp']
message_id = data['message_id']
base_kwargs = {
'session_id': session_id,
'timestamp': timestamp,
'message_id': message_id
}
if msg_type == MessageType.INITIALIZE:
return InitializeCommand(
type=msg_type,
subscription_config=data['subscription_config'],
model_config=data['model_config'],
**base_kwargs
)
elif msg_type == MessageType.PROCESS_FRAME:
return ProcessFrameCommand(
type=msg_type,
frame=data['frame'],
display_id=data['display_id'],
subscription_identifier=data['subscription_identifier'],
frame_timestamp=data['frame_timestamp'],
**base_kwargs
)
elif msg_type == MessageType.SET_SESSION_ID:
return SetSessionIdCommand(
backend_session_id=data['backend_session_id'],
display_id=data['display_id'],
**base_kwargs
)
elif msg_type == MessageType.SHUTDOWN:
return ShutdownCommand(**base_kwargs)
elif msg_type == MessageType.HEALTH_CHECK:
return HealthCheckCommand(**base_kwargs)
elif msg_type == MessageType.INITIALIZED:
return InitializedResponse(
type=msg_type,
success=data['success'],
error_message=data.get('error_message'),
**base_kwargs
)
elif msg_type == MessageType.DETECTION_RESULT:
return DetectionResultResponse(
type=msg_type,
detections=data['detections'],
processing_time=data['processing_time'],
phase=data['phase'],
**base_kwargs
)
elif msg_type == MessageType.SESSION_SET:
return SessionSetResponse(
type=msg_type,
success=data['success'],
backend_session_id=data['backend_session_id'],
**base_kwargs
)
elif msg_type == MessageType.SHUTDOWN_COMPLETE:
return ShutdownCompleteResponse(type=msg_type, **base_kwargs)
elif msg_type == MessageType.HEALTH_RESPONSE:
return HealthResponse(
type=msg_type,
status=data['status'],
memory_usage_mb=data['memory_usage_mb'],
cpu_percent=data['cpu_percent'],
gpu_memory_mb=data.get('gpu_memory_mb'),
uptime_seconds=data.get('uptime_seconds', 0.0),
processed_frames=data.get('processed_frames', 0),
**base_kwargs
)
elif msg_type == MessageType.ERROR:
return ErrorResponse(
type=msg_type,
error_type=data['error_type'],
error_message=data['error_message'],
traceback=data.get('traceback'),
**base_kwargs
)
else:
raise ValueError(f"Unknown message type: {msg_type}")