ChessCommanderFlet / domain /localapi_dialog_processor.py
Danisz's picture
ChessCommander
26440d6
Raw History Blame Contribute Delete
6.34 kB
import requests
import json
import base64
from typing import Optional, Dict, List
import os
from pydantic import BaseModel, Field
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ApiError(Exception):
"""Custom exception class for API errors."""
pass
class DialogueRequestBase(BaseModel):
chess_piece_id: int = Field(..., alias="IdPieza")
language: str = Field("en", alias="Language")
empathy: float = Field(..., alias="Empathy")
def get_endpoint(self) -> str:
pass
class TextDialogueRequest(DialogueRequestBase):
text_input: str = Field(..., alias="Input")
def get_endpoint(self) -> str:
return "process_text"
class AudioDialogueRequest(DialogueRequestBase):
audio_file_path: str = Field(..., alias="AudioData")
def get_endpoint(self) -> str:
return "process_audio"
class WebcamDialogueRequest(DialogueRequestBase):
audio_file_path: str = Field(..., alias="AudioData")
images: List[str] = Field(..., alias="Images")
def get_endpoint(self) -> str:
return "process_images"
class LLMGeneration(BaseModel):
TextResponse: str
Decision: bool
class ChessPieceResponse(BaseModel):
ChessPieceLLMGeneration: LLMGeneration
AudioFile: Optional[str] = None
PieceSpeechVelocity: Optional[str] = None
class PlayerVoiceAttributes(BaseModel):
Transcription: Optional[str] = None
Language: Optional[str] = None
Accent: Optional[str] = None
SpeechVelocity: str = "unknown"
class DialogueResponseData(BaseModel):
ChessPieceResponse: ChessPieceResponse
player_voice_attributes: Optional[PlayerVoiceAttributes] = None
class DialogueResponse(BaseModel):
response: str
decision: bool
transcription: Optional[str] = None
audio_data: Optional[str] = None
player_speak_velocity: Optional[str] = None
piece_speak_velocity: Optional[str] = None
class ApiClient:
def __init__(self, base_url: str):
self.base_url = base_url
def send_request(self, endpoint: str, data: DialogueRequestBase) -> DialogueResponse:
try:
url = f"{self.base_url}/{endpoint}"
response = requests.post(url, json=data.dict(by_alias=True), headers={"Content-Type": "application/json"})
response.raise_for_status()
# print(response.json())
result = DialogueResponseData(**response.json())
chess_response = result.ChessPieceResponse
player_attributes = result.player_voice_attributes
transcription = player_attributes.Transcription if player_attributes else None
player_speak_velocity = None
piece_speak_velocity = None
if player_attributes:
player_speak_velocity = player_attributes.SpeechVelocity if player_attributes else 'unknown'
piece_speak_velocity = result.ChessPieceResponse.PieceSpeechVelocity
print("VELOCITIES!")
print(f"Piece: {piece_speak_velocity}")
print(f"Player: {player_speak_velocity}")
response_text = chess_response.ChessPieceLLMGeneration.TextResponse
decision = chess_response.ChessPieceLLMGeneration.Decision
audio_data = chess_response.AudioFile
return DialogueResponse(
response=response_text,
decision=decision,
transcription=transcription,
audio_data=audio_data,
player_speak_velocity=player_speak_velocity,
piece_speak_velocity=piece_speak_velocity
)
except requests.RequestException as e:
raise ApiError(f"API request failed: {str(e)}")
except json.JSONDecodeError:
raise ApiError("Failed to parse API response")
class DialogueProcessor:
def __init__(self, api_client: ApiClient):
self.api_client = api_client
def process_request(self, request: DialogueRequestBase) -> DialogueResponse:
try:
result = self.api_client.send_request(request.get_endpoint(), request)
return result
except ApiError as e:
return DialogueResponse(response=str(e), decision=False, transcription=None, audio_data=None)
@staticmethod
def save_audio_file(audio_data: str, filename: str):
filename = f"./assets/{filename}" # Needed to play audio files
audio_bytes = base64.b64decode(audio_data)
# Remove the file if it already exists to prevent permission issues
if os.path.exists(filename):
print("audio path exists")
os.remove(filename)
with open(filename, "wb") as audio_file:
logger.info(f"Writing file!")
audio_file.write(audio_bytes)
print(f"Saved audio to {filename}")
logger.info(f"Saved audio to {filename}")
@staticmethod
def encode_image(image_path: str) -> str:
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
class LocalApiDialogueProcessor:
def __init__(self, base_url: str = "http://localhost:49521"):
api_client = ApiClient(base_url)
self.processor = DialogueProcessor(api_client)
def process_text_input(self, chess_piece_id: int, text_input: str, language: str, empathy: float) -> DialogueResponse:
request = TextDialogueRequest(IdPieza=chess_piece_id, Input=text_input, Language=language, Empathy=empathy)
return self.processor.process_request(request)
def process_audio_input(self, chess_piece_id: int, audio_file_path: str, language: str, empathy: float) -> DialogueResponse:
request = AudioDialogueRequest(IdPieza=chess_piece_id, AudioData=audio_file_path, Language=language, Empathy=empathy)
return self.processor.process_request(request)
def process_webcam_input(self, chess_piece_id: int, audio_file_path: str, image_paths: List[str], language: str, empathy: float) -> DialogueResponse:
encoded_images = [DialogueProcessor.encode_image(img_path) for img_path in image_paths]
request = WebcamDialogueRequest(IdPieza=chess_piece_id, AudioData=audio_file_path, Images=encoded_images, Language=language, Empathy=empathy)
return self.processor.process_request(request)