Skip to content

Commit

Permalink
Merge pull request #17 from projecte-aina/tests
Browse files Browse the repository at this point in the history
Refactor the api based on mvc structure
  • Loading branch information
PaulNdrei authored Apr 29, 2024
2 parents e6ef699 + 4ca11a8 commit 47c4a8b
Show file tree
Hide file tree
Showing 34 changed files with 1,023 additions and 552 deletions.
60 changes: 60 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Python Unit Tests


on:
workflow_dispatch:
pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3

- name: Set up Python
# This is the version of the action for setting up Python, not the Python version.
uses: actions/setup-python@v5
with:
# Semantic version range syntax or exact version of a Python version
python-version: '3.10.12'
# Optional - x64 or x86 architecture, defaults to x64
architecture: 'x64'

- name: Display Python version
run: python -c "import sys; print(sys.version)"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest httpx pytest-repeat
pip install -r requirements.txt
- name: install apt dependecies
run: |
sudo apt-get update
sudo apt-get install -y build-essential autoconf automake libtool pkg-config git wget cmake
sudo rm -rf /var/lib/apt/lists/*
- name: install espeak-ng
run: |
git clone -b dev-ca https://github.com/projecte-aina/espeak-ng
pip install --upgrade pip
cd espeak-ng && sudo ./autogen.sh && sudo ./configure --prefix=/usr && sudo make && sudo make install
- name: install lingua-franca
run: pip install git+https://github.com/MycroftAI/lingua-franca.git@5bfd75fe5996fd364102a0eec3f714c9ddc9275c

- name: install model
run: |
wget -q http://share.laklak.eu/model_vits_ca/best_model_8khz.pth -P ./models/vits_ca/
mv ./models/vits_ca/best_model_8khz.pth ./models/vits_ca/best_model.pth
- name: Run tests
run: pytest



2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ RUN wget -q http://share.laklak.eu/model_vits_ca/best_model_8khz.pth -P /app/mod
RUN mv /app/models/vits_ca/best_model_8khz.pth /app/models/vits_ca/best_model.pth
COPY . .

ENTRYPOINT python server/server.py --speech_speed ${SPEECH_SPEED} --mp_workers ${MP_WORKERS} --use_cuda ${USE_CUDA} --use_mp ${USE_MP}
ENTRYPOINT python main.py --speech_speed ${SPEECH_SPEED} --mp_workers ${MP_WORKERS} --use_cuda ${USE_CUDA} --use_mp ${USE_MP}
35 changes: 35 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM python:3.10.12-slim
# RUN apt-get update && apt-get install -y --no-install-recommends wget gcc g++ make python3 python3-dev python3-pip python3-venv python3-wheel espeak espeak-ng libsndfile1-dev && rm -rf /var/lib/apt/lists/*

# Install required packages for building eSpeak and general utilities
RUN apt-get update && apt-get install -y \
build-essential \
autoconf \
automake \
libtool \
pkg-config \
git \
wget \
cmake \
&& rm -rf /var/lib/apt/lists/*

RUN git clone -b dev-ca https://github.com/projecte-aina/espeak-ng

RUN pip install --upgrade pip && \
cd espeak-ng && \
./autogen.sh && \
./configure --prefix=/usr && \
make && \
make install

RUN pip install git+https://github.com/MycroftAI/lingua-franca.git@5bfd75fe5996fd364102a0eec3f714c9ddc9275c

WORKDIR /app
# RUN wget -q http://share.laklak.eu/model_vits_ca/best_model_8khz.pth -P /app/models/vits_ca/
# RUN mv /app/models/vits_ca/best_model_8khz.pth /app/models/vits_ca/best_model.pth

COPY ./requirements.txt /app
RUN python -m pip install --upgrade pip
RUN python -m pip install --no-cache-dir -r requirements.txt

ENTRYPOINT python main.py --speech_speed ${SPEECH_SPEED} --mp_workers ${MP_WORKERS} --use_cuda ${USE_CUDA} --use_mp ${USE_MP} --show_details True --reload
34 changes: 34 additions & 0 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM python:3.10.12-slim
# RUN apt-get update && apt-get install -y --no-install-recommends wget gcc g++ make python3 python3-dev python3-pip python3-venv python3-wheel espeak espeak-ng libsndfile1-dev && rm -rf /var/lib/apt/lists/*

# Install required packages for building eSpeak and general utilities
RUN apt-get update && apt-get install -y \
build-essential \
autoconf \
automake \
libtool \
pkg-config \
git \
wget \
cmake \
&& rm -rf /var/lib/apt/lists/*

RUN git clone -b dev-ca https://github.com/projecte-aina/espeak-ng

RUN pip install --upgrade pip && \
cd espeak-ng && \
./autogen.sh && \
./configure --prefix=/usr && \
make && \
make install

RUN pip install git+https://github.com/MycroftAI/lingua-franca.git@5bfd75fe5996fd364102a0eec3f714c9ddc9275c

WORKDIR /app

COPY ./requirements.txt /app
RUN python -m pip install --upgrade pip
RUN python -m pip install --no-cache-dir -r requirements.txt
RUN pip install pytest httpx pydub pytest-repeat

ENTRYPOINT pytest
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
deploy:
docker compose --env-file .env up -d --build
deploy-d:
docker compose --env-file .env up --build
deploy-gpu:
docker compose -f docker-compose-gpu.yml --env-file .env up -d --build
dev:
docker compose -f docker-compose-dev.yml up --build
tests:
docker compose -f docker-compose-test.yml up --build
undeploy:
docker compose down
stop:
docker compose stop
docker compose stop


act-run-tests:
gh act -j test -W '.github/workflows/tests.yml'
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ docker run --shm-size=1gb -p 8080:8000 tts-api
The default entrypoint puts the web interface to `http://0.0.0.0:8080/`.


## Developer Mode
You can run this api with docker with reload mode that will let you watch you local changes on api.<br>
To run in dev mode run the following command.

```bash
make dev
```

> [!NOTE]
> The model **best_model.pth** is requiered, you have to download by yourself.
```bash
wget -q http://share.laklak.eu/model_vits_ca/best_model_8khz.pth -P models/vits_ca/
```
```bash
mv models/vits_ca/best_model_8khz.pth models/vits_ca/best_model.pth
```

## REST API Endpoints

Expand Down
18 changes: 18 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3.9'
services:
server:
build:
context: .
dockerfile: Dockerfile.dev
container_name: fastapi-dev
environment:
- SPEECH_SPEED=${SPEECH_SPEED-1.0}
- MP_WORKERS=${MP_WORKERS-4}
- USE_MP=False
restart: always
volumes:
- .:/app

ports:
- '8080:8000'
shm_size: ${SHM_SIZE-2gb}
14 changes: 14 additions & 0 deletions docker-compose-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '3.9'
services:
server:
build:
context: .
dockerfile: Dockerfile.test
container_name: fastapi-test
restart: always
volumes:
- .:/app

ports:
- '8080:8000'
shm_size: ${SHM_SIZE-2gb}
153 changes: 153 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
from pathlib import Path
from TTS.utils.manage import ModelManager
from lingua_franca import load_language # Lingua franca

import argparse
import uvicorn
import torch
import multiprocessing as mp
import sys
import os

from server import create_app
from server.utils.argparse import MpWorkersAction
from server.utils.utils import update_config
from server.utils.utils import update_config

# Set global paths
# Determine the current script's directory and set up paths related to the model
path = Path(__file__).parent / "server" /".models.json"
path_dir = os.path.dirname(path)

# Initialize the model manager with the aforementioned path
manager = ModelManager(path)

# Set the relative paths for the default TTS model and its associated configuration
models_path_rel = '../models/vits_ca'
model_ca = os.path.join(path_dir, models_path_rel, 'best_model.pth')
config_ca = os.path.join(path_dir, models_path_rel, 'config.json')

# Load lingua franca language
load_language('ca-es')

def create_argparser():
def convert_boolean(x):
return x.lower() in ["true", "1", "yes"]

# Create an argument parser to handle command-line arguments
# The parser setup seems incomplete and might be continued in the next section of the code.
parser = argparse.ArgumentParser()
parser.add_argument(
"--list_models",
type=convert_boolean,
nargs="?",
const=True,
default=False,
help="list available pre-trained tts and vocoder models."
)
parser.add_argument(
"--model_name",
type=str,
default="tts_models/en/ljspeech/tacotron2-DDC",
help="Name of one of the pre-trained tts models in format <language>/<dataset>/<model_name>",
)
parser.add_argument("--vocoder_name", type=str, default=None, help="name of one of the released vocoder models.")
# Args for running custom models
parser.add_argument(
"--config_path",
default=config_ca,
type=str,
help="Path to model config file."
)
parser.add_argument(
"--model_path",
type=str,
default=model_ca,
help="Path to model file.",
)
parser.add_argument(
"--vocoder_path",
type=str,
help="Path to vocoder model file. If it is not defined, model uses GL as vocoder. Please make sure that you installed vocoder library before (WaveRNN).",
default=None,
)
parser.add_argument("--vocoder_config_path", type=str, help="Path to vocoder model config file.", default=None)
parser.add_argument("--speakers_file_path", type=str, help="JSON file for multi-speaker model.", default=None)
parser.add_argument("--port", type=int, default=8000, help="port to listen on.")
parser.add_argument("--host", type=str, default="0.0.0.0", help="host ip to listen.")
parser.add_argument("--use_mp", type=convert_boolean, default=False, nargs='?', const=True, help="true to use Python multiprocessing.")
parser.add_argument("--use_cuda", type=convert_boolean, default=False, nargs='?', const=False, help="true to use CUDA.")
parser.add_argument("--mp_workers", action=MpWorkersAction ,type=int, default=mp.cpu_count(), nargs='?', const=mp.cpu_count(), help="number of CPUs used for multiprocessing")
parser.add_argument("--debug", type=convert_boolean, default=False, help="true to enable Flask debug mode.")
parser.add_argument("--show_details", type=convert_boolean, default=False, help="Generate model detail page.")
parser.add_argument("--speech_speed", type=float, default=1.0, nargs='?', const=1.0, help="Change speech speed.")
parser.add_argument("--reload", type=bool, action=argparse.BooleanOptionalAction, default=False, help="Reload on changes")
return parser


# parse the args
args = create_argparser().parse_args()
print("args =========", args)
# update in-use models to the specified released models.
model_path = None
config_path = None
speakers_file_path = None
vocoder_path = None
vocoder_config_path = None
# new_speaker_ids = None
# use_aliases = None

# CASE1: list pre-trained TTS models
if args.list_models:
manager.list_models()
sys.exit()

# CASE2: load pre-trained model paths
if args.model_name is not None and not args.model_path:
model_path, config_path, model_item = manager.download_model(args.model_name)
args.vocoder_name = model_item["default_vocoder"] if args.vocoder_name is None else args.vocoder_name

if args.vocoder_name is not None and not args.vocoder_path:
vocoder_path, vocoder_config_path, _ = manager.download_model(args.vocoder_name)

# CASE3: set custom model paths
if args.model_path is not None:
model_path = args.model_path
config_path = args.config_path
speakers_file_path = args.speakers_file_path
speaker_ids_path = os.path.join(path_dir, models_path_rel, 'speaker_ids.json')

if args.vocoder_path is not None:
vocoder_path = args.vocoder_path
vocoder_config_path = args.vocoder_config_path

# CASE4: change speaker speed
if args.speech_speed != 1.0:
update_config(config_path, args.speech_speed)


app = create_app(
model_path = model_path,
config_path = config_path,
speakers_file_path = speakers_file_path,
vocoder_path = vocoder_path,
vocoder_config_path = vocoder_config_path,
speaker_ids_path = speaker_ids_path,
speech_speed = args.speech_speed,
mp_workers = args.mp_workers,
use_cuda = args.use_cuda,
use_mp = args.use_mp,
show_details=args.show_details,
args=args
)



def main():
uvicorn.run('main:app', host=args.host, port=args.port, reload=args.reload)

if __name__ == "__main__":
torch.set_num_threads(1)
torch.set_grad_enabled(False)
mp.set_start_method("fork")
main()
Loading

0 comments on commit 47c4a8b

Please sign in to comment.