Add the demo (#163)

This commit is contained in:
Gabriel de Marmiesse
2026-04-20 15:55:33 +02:00
committed by GitHub
parent c9ebef4d1a
commit 5304da453b
12 changed files with 169 additions and 29 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ weights_path: hf://kyutai/pocket-tts/languages/french_24l/model.safetensors@3959
weights_path_without_voice_cloning: hf://kyutai/pocket-tts-without-voice-cloning/languages/french_24l/model.safetensors@d29db7978e464fb90cb3359ee0c69a273b9142cc
remove_semicolons: true
model_recommended_frames_after_eos: 12
model_recommended_frames_after_eos: 8
flow_lm:
insert_bos_before_voice: true
+1
View File
@@ -3,6 +3,7 @@
weights_path: hf://kyutai/pocket-tts/languages/german/model.safetensors@39592ff23c9ef80098bb74895d104c26275fe2c9
weights_path_without_voice_cloning: hf://kyutai/pocket-tts-without-voice-cloning/languages/german/model.safetensors@d29db7978e464fb90cb3359ee0c69a273b9142cc
remove_semicolons: true
flow_lm:
insert_bos_before_voice: true
+1
View File
@@ -3,6 +3,7 @@
weights_path: hf://kyutai/pocket-tts/languages/german_24l/model.safetensors@39592ff23c9ef80098bb74895d104c26275fe2c9
weights_path_without_voice_cloning: hf://kyutai/pocket-tts-without-voice-cloning/languages/german_24l/model.safetensors@d29db7978e464fb90cb3359ee0c69a273b9142cc
remove_semicolons: true
flow_lm:
insert_bos_before_voice: true
+40
View File
@@ -7,3 +7,43 @@ DEFAULT_EOS_THRESHOLD = -4.0
DEFAULT_FRAMES_AFTER_EOS = None
# TODO: make this dynamic since english_2026-04 supports bigger chunks
MAX_TOKEN_PER_CHUNK = 50
DEFAULT_TEXT_FOR_LANGUAGE = {
"english": (
"Hello world. I am Kyutai's Pocket TTS. "
"I'm fast enough to run on small CPUs. "
"I hope you'll like me."
),
"french": (
"Bonjour le monde. Je suis le TTS de poche de Kyutai. "
"Je suis assez rapide pour fonctionner sur de petits CPU. "
"J'espère que vous m'aimerez."
),
"german": (
"Hallo Welt. Ich bin Kyutais Pocket TTS. "
"Ich bin schnell genug, um auf kleinen CPUs zu laufen. "
"Ich hoffe, du wirst mich mögen."
),
"portuguese": (
"Olá mundo. Eu sou o Pocket TTS da Kyutai. "
"Sou rápido o suficiente para rodar em CPUs pequenas. "
"Espero que você goste de mim."
),
"italian": (
"Ciao mondo. Sono il Pocket TTS di Kyutai. "
"Sono abbastanza veloce da funzionare su piccole CPU. "
"Spero che ti piacerò."
),
"spanish": (
"Hola mundo. Soy el Pocket TTS de Kyutai. "
"Soy lo suficientemente rápido para funcionar en pequeñas CPU. "
"Espero que te guste."
),
}
def get_default_text_for_language(language: str | None) -> str:
for key, text in DEFAULT_TEXT_FOR_LANGUAGE.items():
if language is not None and key in language:
return text
return DEFAULT_TEXT_FOR_LANGUAGE[DEFAULT_LANGUAGE]
+13 -6
View File
@@ -11,7 +11,7 @@ import typer
import uvicorn
from fastapi import FastAPI, File, Form, HTTPException, UploadFile
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.responses import HTMLResponse, StreamingResponse
from typing_extensions import Annotated
from pocket_tts.data.audio import stream_audio_chunks
@@ -23,6 +23,7 @@ from pocket_tts.default_parameters import (
DEFAULT_NOISE_CLAMP,
DEFAULT_TEMPERATURE,
MAX_TOKEN_PER_CHUNK,
get_default_text_for_language,
)
from pocket_tts.models.tts_model import TTSModel, export_model_state
from pocket_tts.utils.logging_utils import enable_logging
@@ -58,11 +59,17 @@ web_app.add_middleware(
)
@web_app.get("/")
@web_app.get("/", response_class=HTMLResponse)
async def root():
"""Serve the frontend."""
static_path = Path(__file__).parent / "static" / "index.html"
return FileResponse(static_path)
content = static_path.read_text()
# Replace the placeholder with the actual default text prompt
print(str(tts_model.origin))
content = content.replace(
"DEFAULT_TEXT_PROMPT", get_default_text_for_language(str(tts_model.origin))
)
return content
@web_app.get("/health")
@@ -214,9 +221,7 @@ def serve(
@cli_app.command()
def generate(
text: Annotated[
str, typer.Option(help="Text to generate")
] = "Hello world. I am Kyutai's Pocket TTS. I'm fast enough to run on small CPUs. I hope you'll like me.",
text: Annotated[str, typer.Option(help="Text to generate")] = None,
voice: Annotated[
str, typer.Option(help="Path to audio conditioning file (voice to clone)")
] = DEFAULT_AUDIO_PROMPT,
@@ -268,6 +273,8 @@ def generate(
"""Generate speech using Kyutai Pocket TTS."""
log_level = logging.ERROR if quiet else logging.INFO
with enable_logging("pocket_tts", log_level):
if text is None:
text = get_default_text_for_language(language)
if text == "-":
# Read text from stdin
text = sys.stdin.read()
+4
View File
@@ -293,6 +293,10 @@ class TTSModel(nn.Module):
if config is None and language is None:
language = DEFAULT_LANGUAGE
if language is not None:
if language == "french":
raise ValueError(
"The french model is not ready yet, please use 'french_24l' instead."
)
config = CONFIGS_DIR / f"{language}.yaml"
config = Path(config)
if config.suffix not in (".yaml", ".yml"):
+5 -3
View File
@@ -28,7 +28,7 @@
class="w-full border rounded p-2"
placeholder="Enter your text here..."
rows="4"
>Hello world. I am Kyutai's Pocket TTS. I'm fast enough to run on small CPUs. I hope you'll like me.</textarea>
>DEFAULT_TEXT_PROMPT</textarea>
</div>
<div class="bg-white p-4 rounded-lg shadow">
@@ -314,7 +314,9 @@
formData.append('voice_url', voiceUrl);
}
const response = await fetch('/tts', {
const ttsEndpoint = `${window.location.pathname.replace(/\/+$/, '')}/tts`;
const response = await fetch(ttsEndpoint, {
method: 'POST',
body: formData
});
@@ -392,4 +394,4 @@
});
</script>
</body>
</html>
</html>