This commit integrates support for pushing and pulling Docker images from a container registry and enhances the Makefile for a more flexible development workflow.
Key changes include:
- **Container Registry Configuration:** Added and variables to the Makefile and updated and targets to correctly tag and push images to the configured registry.
- **Docker Compose Registry Integration:** Modified to use the image from the registry by default instead of building locally.
- **Makefile Workflow Enhancements:**
- Introduced a target (Pulling image git.wlkns.org/stephan/audio-engine-hub:latest from registry...
docker pull git.wlkns.org/stephan/audio-engine-hub:latest) for explicitly downloading images from the registry.
- Modified the target (Starting development environment by pulling image from registry...
export IMAGE_NAME=audio-engine-hub && \
export REGISTRY=git.wlkns.org && \
export USERNAME=stephan && \
export TAG=latest && \
docker compose up -d) to use the registry image.
- Added a target (Starting development environment (local build) on port 8000...) for local development, which explicitly builds the image from source () before starting the service.
- Updated and GNU bash, Version 5.1.16(1)-release (x86_64-pc-linux-gnu)
Diese Shellkommandos sind intern definiert. Geben Sie »help« ein, um diese
Liste zu sehen. Geben Sie »help Name« ein, um die Beschreibung der Funktion
»Name« zu sehen. Geben Sie »info bash« ein, um die vollständige Dokumentation
zu sehen. Geben Sie »man -k« oder »info« ein, um detaillierte Beschreibungen
der Shellkommandos zu sehen.
Ein Stern (*) neben dem Namen kennzeichnet deaktivierte Kommandos.
Jobbezeichnung [&] history [-c] [-d Offset] [n] oder history -anrw [Dateiname] oder histor>
(( Ausdruck )) if Kommandos; then Kommandos; [ elif Kommandos; then Kommandos; ]... [ >
. Dateiname [Argumente] jobs [-lnprs] [Jobbezeichnung ...] or jobs -x Kommando [Arg]
: kill [-s Signalname | -n Signalnummer | -Signalname] pid | jobspec ... >
[ Argument... ] let Argument [Argument ...]
[[ Ausdruck ]] local [Option] Name[=Wert] ...
alias [-p] [Name[=Wert] ... ] logout [n]
bg [Jobbezeichnung ...] mapfile [-d Begrenzer] [-n Anzahl] [-O Quelle] [-s Anzahl] [-t] [-u fd]>
bind [-lpsvPSVX] [-m Tastaturtabelle] [-f Dateiname] [-q Name] [-u Name]> popd [-n] [+N | -N]
break [n] printf [-v var] Format [Argumente]
builtin [Shell-Kommando [Argument ...]] pushd [-n] [+N | -N | Verzeichnis]
caller [Ausdruck] pwd [-LP]
case Wort in [Muster [| Muster]...) Kommandos ;;]... esac read [-ers] [-a Feld] [-d Begrenzer] [-i Text] [-n Zeichenanzahl] [-N Z>
cd [-L|[-P [-e]] [-@]] [Verzeichnis] readarray [-d Begrenzer] [-n Anzahl] [-O Quelle] [-s Anzahl] [-t] >
command [-pVv] Kommando [Argument ...] readonly [-aAf] [Name[=Wert] ...] oder readonly -p
compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlis> return [n]
complete [-abcdefgjksuv] [-pr] [-DEI] [-o option] [-A action] [-G globpa> select Name [in Wortliste ... ;] do Kommandos; done
compopt [-o|+o Option] [-DEI] [Name ...] set [-abefhkmnptuvxBCHP] [-o Option] [--] [Argument ...]
continue [n] shift [n]
coproc [Name] Kommando [Umleitungen] shopt [-pqsu] [-o] [Optionsname ...]
declare [-aAfFgiIlnrtux] [-p] [Name[=Wert] …] source Dateiname [Argumente]
dirs [-clpv] [+N] [-N] suspend [-f]
disown [-h] [-ar] [Jobbezeichnung ... | pid ...] test [Ausdruck]
echo [-neE] [Argument ...] time [-p] Pipeline
enable [-a] [-dnps] [-f Dateiname] [Name ...] times
eval [Argument ...] trap [-lp] [[Argument] Signalbezeichnung ...]
exec [-cl] [-a Name] [Befehl [Argument …]] [Umleitung …] true
exit [n] type [-afptP] Name [Name ...]
export [-fn] [Name[=Wert] ...] oder export -p typeset [-aAfFgiIlnrtux] [-p] Name[=Wert] …
false ulimit [-SHabcdefiklmnpqrstuvxPT] [Grenze]
fc [-e Editor] [-lnr] [Anfang] [Ende] oder fc -s [Muster=Ersetzung] [Kom> umask [-p] [-S] [Modus]
fg [Jobbezeichnung] unalias [-a] Name [Name ...]
for Name [in Wortliste ... ] ; do Kommandos; done unset [-f] [-v] [-n] [NAME ...]
for (( Ausdruck1; Ausdruck2; Ausdruck3 )); do Kommandos; done until Kommandos; do Kommandos; done
function Name { Kommandos ; } oder Name () { Kommandos ; } variables - Namen und Bedeutung einiger Shell-Variablen
getopts optstring name [arg ...] wait [-fn] [-p var] [id ...]
hash [-lr] [-p Pfadname] [-dt] [Name ...] while Kommandos; do Kommandos; done
help [-dms] [Muster ...] { Kommandos ; } targets accordingly.
- **Session Resume Update:** Updated to document the container registry integration.
139 lines
9.7 KiB
Markdown
139 lines
9.7 KiB
Markdown
# Session Resumee - AudioEngineHub Project Refactoring
|
|
|
|
**Date:** Donnerstag, 4. Dezember 2025
|
|
|
|
**Objective:** Refactor the AudioEngineHub project to follow best practices for containerization, error handling, and project structure, based on a provided `guide.md` document.
|
|
|
|
---
|
|
|
|
### Initial Project State & Overview
|
|
|
|
The project was a modular FastAPI-based TTS server with a `TTSEngineBase` interface. Key initial findings:
|
|
* `piper` was functional.
|
|
* `styletts` and `chattts` were dummy implementations.
|
|
* `f5_tts` was implemented but inactive.
|
|
* Configuration was scattered and hardcoded.
|
|
* Error handling was basic.
|
|
* Tests (`pytest` and `unittest`) existed but were limited and inconsistent.
|
|
* No containerization strategy was in place, leading to potential dependency hell.
|
|
|
|
### Refactoring Phase 1: Robustness & Configuration
|
|
|
|
1. **Centralized Configuration:**
|
|
* Replaced hardcoded values with `pydantic-settings` for `.env` file management.
|
|
* `config.py` created (later moved to `app/config.py`).
|
|
* `IMAGE_NAME` in `Makefile` was also user-configurable.
|
|
* `requirements.txt` was updated with `pydantic-settings`.
|
|
* `docker-compose.yml` was updated to use `.env`.
|
|
|
|
2. **Configurable Engines Feature:**
|
|
* Implemented dynamic `ENGINE_REGISTRY` loading based on `ACTIVE_ENGINES` setting in `.env`.
|
|
* Allows easy activation/deactivation of TTS engines.
|
|
|
|
3. **Robust Error Handling:**
|
|
* Implemented comprehensive input validation in the `/tts` endpoint (checking engine, model, speaker existence).
|
|
* Added dependency checks (e.g., `ffmpeg`, `piper` executables) to engines, reporting `HTTP 503` for unavailable engines.
|
|
* Secured `tempfile.mktemp` usage by replacing it with `tempfile.NamedTemporaryFile`.
|
|
* Wrapped synthesis logic in `try-except` blocks to catch and propagate engine-specific errors as `HTTP 500`.
|
|
|
|
4. **Asynchronous Operations:**
|
|
* Changed `TTSEngineBase.synthesize` and `selftest` to `async`.
|
|
* Refactored all concrete engine implementations (`piper`, `f5_tts`, `styletts`, `chattts`) to use `async def` methods.
|
|
* Updated `app/main.py`'s `/tts` endpoint to be `async` and use `await` for engine calls and `asyncio.gather` for concurrent chunk synthesis.
|
|
* Wrapped blocking I/O (file ops, `ffmpeg`) and CPU-bound tasks in `asyncio.to_thread`.
|
|
* Updated `tests/test_f5_tts.py` to correctly `await` async calls.
|
|
|
|
### Refactoring Phase 2: Containerization & Workflow (Based on `guide.md`)
|
|
|
|
1. **Integrated `Makefile`:**
|
|
* Created a `Makefile` with targets for `build`, `run`, `stop`, `logs`, `shell`, `up`, `down`, `test`, `tag`, `push`, `clean`, `help`.
|
|
* Included robust shell functions for port checking (`check_traefik_ports_free`, `check_app_port_free`).
|
|
* Set `SHELL := /bin/bash` in `Makefile` to ensure correct shell interpretation.
|
|
* Ensured `PYTHONPATH=$(PWD)` is set for `make test`.
|
|
|
|
2. **Adopted Multi-Stage `Dockerfile`:**
|
|
* Implemented a multi-stage `Dockerfile` (builder/runner stages).
|
|
* `builder` stage creates a Python virtual environment and installs `requirements.txt` (including `gunicorn`).
|
|
* `runner` stage uses `python:3.11-slim`, installs runtime system dependencies (`ffmpeg`), creates an unprivileged `appuser`, and sets the production `CMD` to `gunicorn` with `uvicorn` workers.
|
|
|
|
3. **Refactored Project Structure (`app/` package):**
|
|
* Created an `app/` directory.
|
|
* Moved `main.py`, `config.py`, `engines/`, `models/`, `utils/` into `app/`.
|
|
* Created `app/__init__.py`.
|
|
* Updated all Python import paths (`from app.config import settings`, `from app.engines.piper import PiperEngine`, etc.).
|
|
* Updated internal references in engine files (e.g., `model_dir`, `voices_dir`).
|
|
|
|
4. **Refined `docker-compose.yml` with Traefik:**
|
|
* Integrated `traefik` service for dynamic reverse proxying during local development.
|
|
* Modified `app` service with Traefik `labels` and connected both services to a `web` network.
|
|
* Adjusted Docker `volumes` mounts to match the new `app/` structure (e.g., `./models:/home/appuser/app/models`).
|
|
* Updated `app` service `command` for Uvicorn hot-reloading in dev.
|
|
|
|
5. **Centralized Testing Workflow:**
|
|
* Removed `test_run.sh`.
|
|
* Integrated `make test` for running `pytest --cov=. app/ tests/`.
|
|
|
|
6. **Dedicated Documentation:**
|
|
* Created `docs/` directory.
|
|
* Moved `guide.md` to `docs/guide.md`.
|
|
|
|
### Verification & Troubleshooting
|
|
|
|
* **Tests:** All unit/integration tests (`make test`) are passing.
|
|
* **Local Run (Virtual Env):** Initial local runs (`python app/main.py`) failed due to `ModuleNotFoundError` (fixed by `python -m app.main`) and `PermissionError` (fixed by needing to mock or redirect `settings.AUDIO_CACHE_DIR` for local direct execution, but not strictly needed for successful app execution through `uvicorn`). The current approach is to verify in Docker.
|
|
* **Docker Compose:**
|
|
* Initial `make up` failures were due to an outdated `docker-compose` client (`1.29.2`) and later, `Makefile` syntax issues (fixed by setting `SHELL := /bin/bash` and fixing macros).
|
|
* `docker-compose` was eventually updated to the `docker compose` CLI plugin (v5.0.0).
|
|
* `make up` command finally succeeded in bringing up containers.
|
|
* The `curl http://localhost/health` command returned `404 page not found`. This indicates a potential routing issue with Traefik or the application not being responsive on the expected path within the container. (This is the last unresolved issue).
|
|
|
|
---
|
|
|
|
### Final Debugging & Resolution (Post-Refactoring)
|
|
|
|
After the major refactoring, the service was unable to start and was returning `404` errors. The final debugging session addressed these issues.
|
|
|
|
1. **FastAPI App Startup Fix:**
|
|
* **Problem:** The Uvicorn server was failing with `Attribute "app" not found in module "app.main"`.
|
|
* **Solution:** The `app` instance was only created inside the `if __name__ == "__main__"` block. Moved `app = create_app()` to the module's global scope in `app/main.py` so Uvicorn could find it.
|
|
|
|
2. **Traefik & Docker Compose Issues:**
|
|
* **Problem:** The initial `docker-compose.yml` included a Traefik service for reverse proxying, which was causing multiple issues (invalid container names due to missing `.env` variables, Docker client API version errors). The user also clarified that they use an existing reverse proxy and did not want a new Traefik container deployed.
|
|
* **Solution:** Removed the `traefik` service entirely from `docker-compose.yml`. The `app` service's port was exposed directly to the host.
|
|
|
|
3. **Model Loading Failure:**
|
|
* **Problem:** The `piper` engine was not loading any models, returning an empty list and causing `/tts` requests to fail with a `422 Unprocessable Entity` error.
|
|
* **Diagnosis:** The root cause was an incorrect volume mount. The `docker-compose.yml` was attempting to mount a non-existent, empty `./models` directory from the host. The actual models were located in `./app/models`.
|
|
* **Solution:**
|
|
1. Corrected the `docker-compose.yml` volume mount to point to the correct source directory: `- ./app/models:/models`.
|
|
2. Updated the `app/engines/piper.py` code to use the absolute path `/models/piper/` to look for models inside the container, making the configuration more robust and independent of the working directory.
|
|
|
|
4. **Developer Experience (DX) Improvements:**
|
|
* **Automatic Port Finding:** The `Makefile` was enhanced. The `make up` and `make run` commands now automatically find a free port starting from 8000, preventing port conflicts.
|
|
* **Health Check Command:** A `make health-check` target was added. It runs a sanity check against the deployed container's `/health` endpoint to verify that the service is up and all engines are reporting an "ok" status.
|
|
* **Documentation:**
|
|
* The `README.md` was completely rewritten to provide a clear and up-to-date guide for getting started, usage, and available `make` commands.
|
|
* A note was added to `docs/guide.md` to clarify that it describes an older, more advanced setup and to point readers to the new `README.md`.
|
|
|
|
**Final Status:** The service is now fully deployable via `make up` and passes `make health-check`. All identified startup issues and bugs have been resolved.
|
|
|
|
### Container Registry Integration
|
|
|
|
To facilitate pushing and pulling Docker images from a remote registry (e.g., `git.wlkns.org`), the `Makefile` and `docker-compose.yml` were updated:
|
|
|
|
1. **Makefile Configuration:**
|
|
* Added `REGISTRY := git.wlkns.org` and `USERNAME := stephan` variables.
|
|
* Modified the `tag` target to tag images in the format `$(REGISTRY)/$(USERNAME)/$(IMAGE_NAME):$(TAG)`.
|
|
* Modified the `push` target to depend on `build` and `tag`, then push the image to the configured registry.
|
|
|
|
2. **`docker-compose.yml` Integration:**
|
|
* Changed the `app` service definition to use `image: ${REGISTRY}/${USERNAME}/${IMAGE_NAME}:${TAG}` instead of `build: .`, making it pull from the registry by default.
|
|
|
|
3. **Makefile Workflow Enhancements:**
|
|
* Added a `pull` target (`make pull`) to explicitly download the image from the registry.
|
|
* The `up` target (`make up`) was modified to start the service using the image specified in `docker-compose.yml` (which now points to the registry).
|
|
* A new `dev-up` target (`make dev-up`) was introduced for local development, which explicitly builds the image from source (`docker compose up --build -d`) before starting the service.
|
|
* The `down` and `help` targets were updated accordingly.
|
|
|
|
This allows for flexible deployment, supporting both local development with on-demand building and production-like environments pulling from a registry.
|