street-lingo/backend/languages/indonesian/services.py

136 lines
5.6 KiB
Python

import logging
from typing import Dict, Any
from google.cloud import texttospeech
from core.speech_service import TextToSpeechService, BaseAIConversationService, BaseConversationFlowService
from .models import SCENARIO_PERSONALITIES, Personality
logger = logging.getLogger(__name__)
class IndonesianTextToSpeechService(TextToSpeechService):
def __init__(self):
super().__init__(language_code="id-ID")
def _get_voice_config(self, gender: str, character_name: str = None) -> Dict[str, Any]:
"""Get Indonesian-specific voice configuration."""
tts_gender = self.gender_map.get(gender, texttospeech.SsmlVoiceGender.FEMALE)
character_voice_map = {
"Pak Budi": {
"name": "id-ID-Chirp3-HD-Charon",
"speaking_rate": 0.95,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.MALE,
},
"Ibu Sari": {
"name": "id-ID-Chirp3-HD-Kore",
"speaking_rate": 1.0,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.FEMALE,
},
"Mbak Sari": {
"name": "id-ID-Chirp3-HD-Zephyr",
"speaking_rate": 1.1,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.FEMALE,
},
"Adik Kasir": {
"name": "id-ID-Chirp3-HD-Aoede",
"speaking_rate": 1.05,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.FEMALE,
},
"Tetangga Ali": {
"name": "id-ID-Chirp3-HD-Puck",
"speaking_rate": 1.05,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.MALE,
}
}
gender_voice_fallback = {
texttospeech.SsmlVoiceGender.MALE: {
"name": "id-ID-Chirp3-HD-Fenrir",
"speaking_rate": 1.0,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.MALE,
},
texttospeech.SsmlVoiceGender.FEMALE: {
"name": "id-ID-Chirp3-HD-Leda",
"speaking_rate": 1.0,
"pitch": None,
"ssml_gender": texttospeech.SsmlVoiceGender.FEMALE,
}
}
if character_name and character_name in character_voice_map:
config_set = character_voice_map[character_name]
logger.info(f"Using character-specific voice for '{character_name}': {config_set['name']}")
return config_set
config_set = gender_voice_fallback.get(tts_gender, gender_voice_fallback[texttospeech.SsmlVoiceGender.FEMALE])
logger.info(f"Using gender fallback voice for {tts_gender}: {config_set['name']}")
return config_set
class IndonesianAIConversationService(BaseAIConversationService):
def __init__(self):
super().__init__(language_code="id")
def get_personality_for_scenario(self, scenario: str, character_name: str = None) -> Personality:
"""Get Indonesian personality based on scenario and character name."""
if scenario in SCENARIO_PERSONALITIES:
personalities = SCENARIO_PERSONALITIES[scenario]
if character_name and character_name in personalities:
return personalities[character_name]
else:
return list(personalities.values())[0]
# Return default personality if scenario not found
from .models import Personality, CharacterType, Gender, PersonalityTone, GoalItem, HelpfulPhrase
return Personality(
character_type=CharacterType.GENERIC,
name="Pak/Bu",
gender=Gender.FEMALE,
tone=PersonalityTone.FRIENDLY,
age_range="middle-aged",
background="Helpful Indonesian person",
typical_phrases=["Halo!", "Apa kabar?", "Bisa saya bantu?"],
response_style="Friendly and helpful",
location_context="Indonesia",
scenario_title="General Conversation",
scenario_description="General Indonesian conversation practice",
scenario_challenge="Practice basic Indonesian conversation",
scenario_goal="Have a natural conversation",
goal_items=[],
helpful_phrases=[],
language_code="id",
country_code="Indonesia",
is_helpful=True,
is_talkative=True
)
class IndonesianConversationFlowService(BaseConversationFlowService):
def __init__(self):
super().__init__(language_code="id-ID")
self.tts_service = IndonesianTextToSpeechService()
self.ai_service = IndonesianAIConversationService()
def extract_scenario_from_context(self, context: str) -> str:
"""Extract scenario type from context string."""
logger.info(f"Extracting scenario from context: '{context}'")
context_lower = context.lower()
detected_scenario = None
if "coffee_shop" in context_lower or "coffee" in context_lower:
detected_scenario = "coffee_shop"
elif "warung" in context_lower or "nasi goreng" in context_lower:
detected_scenario = "warung"
elif "ojek" in context_lower or "mall" in context_lower:
detected_scenario = "ojek"
elif "alfamart" in context_lower or "indomie" in context_lower:
detected_scenario = "alfamart"
else:
detected_scenario = "warung" # Default to warung
logger.info(f"Detected scenario: '{detected_scenario}'")
return detected_scenario