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, GermanPersonality logger = logging.getLogger(__name__) class GermanTextToSpeechService(TextToSpeechService): def __init__(self): super().__init__(language_code="de-DE") def _get_voice_config(self, gender: str, character_name: str = None) -> Dict[str, Any]: """Get German-specific voice configuration.""" tts_gender = self.gender_map.get(gender, texttospeech.SsmlVoiceGender.FEMALE) # Character-specific German voices using Chirp3-HD models character_voice_map = { "Mehmet": { "name": "de-DE-Chirp3-HD-Charon", # Male voice with slight accent "speaking_rate": 0.95, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.MALE, }, "Lisa": { "name": "de-DE-Chirp3-HD-Kore", # Young female voice "speaking_rate": 1.05, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.FEMALE, }, "Frau Schmidt": { "name": "de-DE-Chirp3-HD-Zephyr", # Formal female voice "speaking_rate": 0.9, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.FEMALE, }, "Klaus": { "name": "de-DE-Chirp3-HD-Puck", # Cheerful male voice "speaking_rate": 1.0, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.MALE, }, "Flughafen-Mitarbeiter": { "name": "de-DE-Chirp3-HD-Leda", # Professional female voice "speaking_rate": 0.95, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.FEMALE, }, "Dr. Müller": { "name": "de-DE-Chirp3-HD-Fenrir", # Professional male voice for doctor "speaking_rate": 0.9, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.MALE, } } # Generic German voices by gender using Chirp3-HD models gender_voice_fallback = { texttospeech.SsmlVoiceGender.MALE: { "name": "de-DE-Chirp3-HD-Charon", "speaking_rate": 1.0, "pitch": None, "ssml_gender": texttospeech.SsmlVoiceGender.MALE, }, texttospeech.SsmlVoiceGender.FEMALE: { "name": "de-DE-Chirp3-HD-Kore", "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 German 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 German gender fallback voice for {tts_gender}: {config_set['name']}") return config_set class GermanAIConversationService(BaseAIConversationService): def __init__(self): super().__init__(language_code="de") def get_personality_for_scenario(self, scenario: str, character_name: str = None) -> GermanPersonality: """Get German 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 GermanPersonality, CharacterType, Gender, PersonalityTone, GoalItem, HelpfulPhrase return GermanPersonality( character_type=CharacterType.GENERIC, name="Herr/Frau Müller", gender=Gender.FEMALE, tone=PersonalityTone.FRIENDLY, age_range="middle-aged", background="Helpful Berlin resident", typical_phrases=["Hallo!", "Wie geht's?", "Kann ich helfen?"], response_style="Friendly and helpful", location_context="Berlin", scenario_title="General Conversation", scenario_description="General German conversation practice", scenario_challenge="Practice basic German conversation", scenario_goal="Have a natural conversation in German", goal_items=[], helpful_phrases=[], is_helpful=True, is_talkative=True ) class GermanConversationFlowService(BaseConversationFlowService): def __init__(self): super().__init__(language_code="de-DE") self.tts_service = GermanTextToSpeechService() self.ai_service = GermanAIConversationService() def extract_scenario_from_context(self, context: str) -> str: """Extract scenario type from context string.""" logger.info(f"Extracting German scenario from context: '{context}'") context_lower = context.lower() detected_scenario = None if "spati" in context_lower or "späti" in context_lower or "convenience" in context_lower: detected_scenario = "spati" elif "wg" in context_lower or "room" in context_lower or "apartment" in context_lower: detected_scenario = "wg_viewing" elif "bürgeramt" in context_lower or "burgeramt" in context_lower or "registration" in context_lower: detected_scenario = "burgeramt" elif "biergarten" in context_lower or "beer" in context_lower or "restaurant" in context_lower: detected_scenario = "biergarten" elif "ber" in context_lower or "airport" in context_lower or "flughafen" in context_lower or "potsdamer" in context_lower: detected_scenario = "ber_airport" elif "arzt" in context_lower or "doctor" in context_lower or "medical" in context_lower: detected_scenario = "arzt" else: detected_scenario = "spati" # Default to späti logger.info(f"Detected German scenario: '{detected_scenario}'") return detected_scenario