Fix runtime imports

This commit is contained in:
James Ketr 2025-09-04 16:23:42 -07:00
parent b5d2605d99
commit 66c2d2e524
5 changed files with 110 additions and 26 deletions

View File

@ -13,9 +13,22 @@ import threading
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
# Import shared models # Import shared models
import sys try:
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) # Try relative import first (when running as part of the package)
from shared.models import NamePasswordRecord from ...shared.models import NamePasswordRecord
except ImportError:
try:
# Try absolute import (when running directly)
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from shared.models import NamePasswordRecord
except ImportError:
# Fallback: create minimal model for testing
from pydantic import BaseModel
class NamePasswordRecord(BaseModel):
name: str
password: str
from logger import logger from logger import logger
@ -150,10 +163,14 @@ class AuthManager:
def validate_integrity(self) -> list[str]: def validate_integrity(self) -> list[str]:
"""Validate auth data integrity and return list of issues""" """Validate auth data integrity and return list of issues"""
issues : list[str] = [] issues = []
with self.lock: with self.lock:
for name, record in self.name_passwords.items(): for name, record in self.name_passwords.items():
if not isinstance(record, dict):
issues.append(f"Name '{name}' has invalid record type: {type(record)}")
continue
if "salt" not in record or "hash" not in record: if "salt" not in record or "hash" not in record:
issues.append(f"Name '{name}' missing salt or hash") issues.append(f"Name '{name}' missing salt or hash")
continue continue

View File

@ -12,19 +12,38 @@ import threading
from typing import Dict, List, Optional, TYPE_CHECKING from typing import Dict, List, Optional, TYPE_CHECKING
# Import shared models # Import shared models
import sys # Import shared models
import os try:
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) # Try relative import first (when running as part of the package)
from shared.models import ChatMessageModel, ParticipantModel from ...shared.models import ChatMessageModel, ParticipantModel
except ImportError:
try:
# Try absolute import (when running directly)
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from shared.models import ChatMessageModel, ParticipantModel
except ImportError:
# Fallback: create minimal models for testing
from pydantic import BaseModel
from typing import Optional
class ChatMessageModel(BaseModel):
id: str
author: str
message: str
timestamp: float
class ParticipantModel(BaseModel):
id: str
name: str
from logger import logger from logger import logger
# Use try/except for importing events to handle both relative and absolute imports # Use try/except for importing events to handle both relative and absolute imports
try: try:
from ..models.events import event_bus, ChatMessageSent from ..models.events import event_bus, ChatMessageSent, SessionDisconnected, SessionLeftLobby
except ImportError: except ImportError:
try: try:
from models.events import event_bus, ChatMessageSent from models.events import event_bus, ChatMessageSent, SessionDisconnected, SessionLeftLobby
except ImportError: except ImportError:
# Create dummy event system for standalone testing # Create dummy event system for standalone testing
class DummyEventBus: class DummyEventBus:
@ -35,6 +54,12 @@ except ImportError:
class ChatMessageSent: class ChatMessageSent:
pass pass
class SessionDisconnected:
pass
class SessionLeftLobby:
pass
if TYPE_CHECKING: if TYPE_CHECKING:
from .session_manager import Session from .session_manager import Session
@ -225,12 +250,10 @@ class LobbyManager:
# Subscribe to session events - handle import errors gracefully # Subscribe to session events - handle import errors gracefully
try: try:
from ..models.events import SessionDisconnected, SessionLeftLobby
event_bus.subscribe(SessionDisconnected, self) event_bus.subscribe(SessionDisconnected, self)
event_bus.subscribe(SessionLeftLobby, self) event_bus.subscribe(SessionLeftLobby, self)
except ImportError: except ImportError:
try: try:
from models.events import SessionDisconnected, SessionLeftLobby
event_bus.subscribe(SessionDisconnected, self) event_bus.subscribe(SessionDisconnected, self)
event_bus.subscribe(SessionLeftLobby, self) event_bus.subscribe(SessionLeftLobby, self)
except (ImportError, AttributeError): except (ImportError, AttributeError):
@ -239,7 +262,6 @@ class LobbyManager:
async def handle(self, event): async def handle(self, event):
"""Handle events from the event bus""" """Handle events from the event bus"""
from ..models.events import SessionDisconnected, SessionLeftLobby
if isinstance(event, SessionDisconnected): if isinstance(event, SessionDisconnected):
await self._handle_session_disconnected(event) await self._handle_session_disconnected(event)

View File

@ -2,7 +2,17 @@
Session management for the AI Voice Bot server. Session management for the AI Voice Bot server.
This module handles session lifecycle, persistence, and cleanup operations. This module handles session lifecycle, persistence, and cleanup operations.
Extracted from main.py to improve maintainability and separation of concerns. Extracted from m await lobby.add await lobby.removeSession(self)
# Publish event
await event_bus.publish(SessionLeftLobby(
session_id=self.id,
lobby_id=lobby.id,(self)
# Publish event
await event_bus.publish(SessionJoinedLobby(
session_id=self.id,
lobby_id=lobby.id,to improve maintainability and separation of concerns.
""" """
from __future__ import annotations from __future__ import annotations
@ -17,24 +27,53 @@ from fastapi import WebSocket
from pydantic import ValidationError from pydantic import ValidationError
# Import shared models # Import shared models
import sys try:
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) # Try relative import first (when running as part of the package)
from shared.models import SessionSaved, LobbySaved, SessionsPayload, NamePasswordRecord from ...shared.models import SessionSaved, LobbySaved, SessionsPayload, NamePasswordRecord
except ImportError:
try:
# Try absolute import (when running directly)
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from shared.models import SessionSaved, LobbySaved, SessionsPayload, NamePasswordRecord
except ImportError:
# Fallback: create minimal models for testing
from pydantic import BaseModel
class SessionSaved(BaseModel):
id: str
name: str = ""
protected: bool = False
is_bot: bool = False
has_media: bool = True
bot_run_id: Optional[str] = None
lobbies: List[str] = []
class LobbySaved(BaseModel):
name: str
private: bool = False
class SessionsPayload(BaseModel):
sessions: List[SessionSaved]
class NamePasswordRecord(BaseModel):
name: str
password: str
from logger import logger from logger import logger
# Use try/except for importing events to handle both relative and absolute imports # Use try/except for importing events to handle both relative and absolute imports
try: try:
from ..models.events import event_bus, SessionDisconnected from ..models.events import event_bus, SessionDisconnected, UserNameChanged, SessionJoinedLobby, SessionLeftLobby
except ImportError: except ImportError:
try: try:
from models.events import event_bus, SessionDisconnected from models.events import event_bus, SessionDisconnected, UserNameChanged, SessionJoinedLobby, SessionLeftLobby
except ImportError: except ImportError:
# Create dummy event system for standalone testing # Create dummy event system for standalone testing
class DummyEventBus: class DummyEventBus:
async def publish(self, event): pass async def publish(self, event): pass
event_bus = DummyEventBus() event_bus = DummyEventBus()
class SessionDisconnected: pass class SessionDisconnected: pass
class UserNameChanged: pass
class SessionJoinedLobby: pass
class SessionLeftLobby: pass
class SessionConfig: class SessionConfig:
@ -91,7 +130,6 @@ class Session:
lobby_ids = [lobby.id for lobby in self.lobbies] lobby_ids = [lobby.id for lobby in self.lobbies]
# Publish name change event (don't await here to avoid blocking) # Publish name change event (don't await here to avoid blocking)
from ..models.events import UserNameChanged
asyncio.create_task(event_bus.publish(UserNameChanged( asyncio.create_task(event_bus.publish(UserNameChanged(
session_id=self.id, session_id=self.id,
old_name=old_name, old_name=old_name,
@ -118,7 +156,6 @@ class Session:
await lobby.addSession(self) await lobby.addSession(self)
# Publish join event # Publish join event
from ..models.events import SessionJoinedLobby
await event_bus.publish(SessionJoinedLobby( await event_bus.publish(SessionJoinedLobby(
session_id=self.id, session_id=self.id,
lobby_id=lobby.id, lobby_id=lobby.id,
@ -136,7 +173,6 @@ class Session:
await lobby.removeSession(self) await lobby.removeSession(self)
# Publish leave event # Publish leave event
from ..models.events import SessionLeftLobby
await event_bus.publish(SessionLeftLobby( await event_bus.publish(SessionLeftLobby(
session_id=self.id, session_id=self.id,
lobby_id=lobby.id, lobby_id=lobby.id,

View File

@ -11,9 +11,16 @@ from logger import logger
from .message_handlers import MessageRouter from .message_handlers import MessageRouter
if TYPE_CHECKING: if TYPE_CHECKING:
from ..core.session_manager import Session, SessionManager # Use absolute imports to avoid relative import issues
from ..core.lobby_manager import Lobby, LobbyManager try:
from ..core.auth_manager import AuthManager from core.session_manager import Session, SessionManager
from core.lobby_manager import Lobby, LobbyManager
from core.auth_manager import AuthManager
except ImportError:
# Fallback for when running from different directory structure
from ..core.session_manager import Session, SessionManager
from ..core.lobby_manager import Lobby, LobbyManager
from ..core.auth_manager import AuthManager
class WebSocketConnectionManager: class WebSocketConnectionManager:

View File

@ -290,7 +290,9 @@ class MessageRouter:
try: try:
await self._handlers[message_type].handle(session, lobby, data, websocket, managers) await self._handlers[message_type].handle(session, lobby, data, websocket, managers)
except Exception as e: except Exception as e:
import traceback
logger.error(f"Error handling message type {message_type}: {e}") logger.error(f"Error handling message type {message_type}: {e}")
logger.error(f"Full traceback: {traceback.format_exc()}")
await websocket.send_json({ await websocket.send_json({
"type": "error", "type": "error",
"data": {"error": f"Internal error handling {message_type}"} "data": {"error": f"Internal error handling {message_type}"}