import io import wave class StubAnthropicClient: async def complete( self, system_prompt: str, messages: list[dict], model: str = "claude-sonnet-4-6", max_tokens: int = 2048, ) -> tuple[str, dict]: usage = { "provider": "stub", "model": "stub", "input_tokens": 0, "output_tokens": 0, } if "game master" in system_prompt.lower(): return ( "Vous vous retrouvez dans une ruelle sombre de Paris. " "Une silhouette mystérieuse s'approche lentement.\n" "-----\n" "1. Suivez la silhouette dans l'obscurité\n" "2. Restez dans l'ombre et observez\n" "3. Demandez de l'aide à voix haute\n" "4. Courez vers la lumière au bout de la ruelle\n" "-----\n" "no notes" ), usage return "La Nuit Parisienne\nUne aventure mystérieuse dans les rues sombres de Paris.", usage class StubDeepLClient: def can_translate_to(self, lang: str) -> bool: return True async def translate( self, text: str, to_language: str, context: str | None = None ) -> str: return f"[STUB] {text[:120]}" class StubGeminiClient: def get_voice_by_language(self, lang: str) -> str: return "Stub" async def generate_audio(self, text: str, voice: str) -> bytes: buf = io.BytesIO() with wave.open(buf, "wb") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(24000) wf.writeframes(b"\x00" * 480) return buf.getvalue() class StubSpacyClient: def get_parts_of_speech(self, text: str, language: str) -> dict: return {"language": language, "sentences": [{"text": text, "tokens": []}]}