from dataclasses import dataclass
from enum import Enum
from typing import Generic, TypeVar, List, Optional
from typing_extensions import TypeAlias
MessageTypeT = TypeVar("MessageTypeT", bound="MessageType")
class MessageType(Enum):
pass
class CtlMessageType(MessageType):
Listen = "listen"
Stop = "stop"
class SttMessageType(MessageType):
Start = "start"
Utterance = "utterance"
Reset = "reset"
Commit = "commit"
Stop = "stop"
class LlmMessageType(MessageType):
FeedbackPos = "positive"
FeedbackNeg = "negative"
Start = "start"
Token = "token"
Utterance = "utterance"
End = "end"
Stop = "stop"
@dataclass(frozen=True)
class Message(Generic[MessageTypeT]):
msg: MessageTypeT
data: Optional[str] = None
def __str__(self) -> str:
if self.data is None:
return f"{self.msg.__class__.__name__}.{self.msg.name}"
else:
return f"{self.msg.__class__.__name__}.{self.msg.name}: {self.data.strip()}"
CtlMessage: TypeAlias = Message[CtlMessageType]
SttMessage: TypeAlias = Message[SttMessageType]
LlmMessage: TypeAlias = Message[LlmMessageType]
class ConfigError(Exception):
pass
class ConfigValueError(ConfigError):
def __str__(self) -> str:
return f"Invalid configuration value: {super().__str__()}"
class ModuleError(Exception):
def __init__(self, module: str, msg: str) -> None:
super().__init__(f"{module.lstrip('_')}: {msg}")
class ModelError(ModuleError):
def __init__(self, module: str, model: Optional[str], msg: str) -> None:
super().__init__(f"{module}[{model}]" if model is not None else module, msg)
class ModelNotFoundError(ModelError):
def __init__(self, module: str, model: Optional[str], msg: Optional[str], options: Optional[List[str]]) -> None:
parts: List[str] = []
if model is None:
parts.append("No model specified.")
else:
parts.append("Model not found.")
if msg is not None:
parts.append(msg)
if options is None:
parts.append("Not checked for options.")
elif options:
parts.append(f"Available options: {', '.join(options)}")
super().__init__(module, model, " ".join(parts))