From 6a2d3a54cf9adacebccfdec4ebe294974694146c Mon Sep 17 00:00:00 2001
From: remsky
Date: Fri, 4 Apr 2025 16:49:10 -0600
Subject: [PATCH] Bump version to 0.3.0 and update related configurations; add
misaki patch script and remove obsolete build workflow
---
.github/workflows/build-push.yml | 87 ------------
.github/workflows/docker-publish.yml | 102 --------------
.github/workflows/release.yml | 95 +++++++++++++
README.md | 20 ++-
VERSION | 2 +-
charts/kokoro-fastapi/Chart.yaml | 4 +-
docker/cpu/docker-compose.yml | 2 +-
docker/gpu/docker-compose.yml | 4 +-
pyproject.toml | 13 +-
readme-parts/config.yaml | 11 ++
scripts/fix_misaki.py | 45 +++++++
scripts/update_badges.py | 71 +++++-----
scripts/update_version.py | 195 +++++++++++++++++++++++++++
slim.report.json | 49 -------
start-cpu.sh | 8 ++
15 files changed, 411 insertions(+), 297 deletions(-)
delete mode 100644 .github/workflows/build-push.yml
delete mode 100644 .github/workflows/docker-publish.yml
create mode 100644 .github/workflows/release.yml
create mode 100644 readme-parts/config.yaml
create mode 100644 scripts/fix_misaki.py
create mode 100755 scripts/update_version.py
delete mode 100644 slim.report.json
diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml
deleted file mode 100644
index d2c606b..0000000
--- a/.github/workflows/build-push.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-name: Docker Build and push
-
-on:
- push:
- tags: [ 'v*.*.*' ]
- paths-ignore:
- - '**.md'
- - 'docs/**'
- workflow_dispatch:
- inputs:
- version:
- description: 'Version to build and publish (e.g. v0.2.0)'
- required: true
- type: string
-
-jobs:
- prepare-release:
- runs-on: ubuntu-latest
- outputs:
- version: ${{ steps.get-version.outputs.version }}
- is_prerelease: ${{ steps.check-prerelease.outputs.is_prerelease }}
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Get version
- id: get-version
- run: |
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
- else
- echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- fi
-
- - name: Check if prerelease
- id: check-prerelease
- run: |
- echo "is_prerelease=${{ contains(steps.get-version.outputs.version, '-pre') }}" >> $GITHUB_OUTPUT
-
- build-images:
- needs: prepare-release
- runs-on: ubuntu-latest
- permissions:
- packages: write
- env:
- DOCKER_BUILDKIT: 1
- BUILDKIT_STEP_LOG_MAX_SIZE: 10485760
- # This environment variable will override the VERSION variable in your HCL file.
- VERSION: ${{ needs.prepare-release.outputs.version }}
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Free disk space
- run: |
- echo "Listing current disk space"
- df -h
- echo "Cleaning up disk space..."
- sudo rm -rf /usr/share/dotnet
- sudo rm -rf /usr/local/lib/android
- sudo rm -rf /opt/ghc
- sudo rm -rf /opt/hostedtoolcache
- docker system prune -af
- echo "Disk space after cleanup"
- df -h
-
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
- with:
- driver-opts: |
- image=moby/buildkit:latest
- network=host
-
- - name: Log in to GitHub Container Registry
- uses: docker/login-action@v2
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build and push images
- run: |
- # No need to override VERSION via --set; the env var does the job.
- docker buildx bake --push
\ No newline at end of file
diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml
deleted file mode 100644
index d3e880e..0000000
--- a/.github/workflows/docker-publish.yml
+++ /dev/null
@@ -1,102 +0,0 @@
-name: Docker Build and Publish
-
-on:
- push:
- tags: [ 'v*.*.*' ]
- paths-ignore:
- - '**.md'
- - 'docs/**'
- workflow_dispatch:
- inputs:
- version:
- description: 'Version to release (e.g. v0.2.0)'
- required: true
- type: string
-
-jobs:
- prepare-release:
- runs-on: ubuntu-latest
- outputs:
- version: ${{ steps.get-version.outputs.version }}
- is_prerelease: ${{ steps.check-prerelease.outputs.is_prerelease }}
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Get version
- id: get-version
- run: |
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
- else
- echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- fi
-
- - name: Check if prerelease
- id: check-prerelease
- run: echo "is_prerelease=${{ contains(steps.get-version.outputs.version, '-pre') }}" >> $GITHUB_OUTPUT
-
- build-images:
- needs: prepare-release
- runs-on: ubuntu-latest
- permissions:
- packages: write
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Free disk space
- run: |
- echo "Listing current disk space"
- df -h
- echo "Cleaning up disk space..."
- sudo rm -rf /usr/share/dotnet
- sudo rm -rf /usr/local/lib/android
- sudo rm -rf /opt/ghc
- sudo rm -rf /opt/hostedtoolcache
- docker system prune -af
- echo "Disk space after cleanup"
- df -h
-
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
- with:
- driver-opts: |
- image=moby/buildkit:latest
- network=host
-
- - name: Log in to GitHub Container Registry
- uses: docker/login-action@v2
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build and push images
- env:
- DOCKER_BUILDKIT: 1
- BUILDKIT_STEP_LOG_MAX_SIZE: 10485760
- VERSION: ${{ needs.prepare-release.outputs.version }}
- run: docker buildx bake --push
-
- create-release:
- needs: [prepare-release, build-images]
- runs-on: ubuntu-latest
- permissions:
- contents: write
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Create Release
- uses: softprops/action-gh-release@v1
- with:
- tag_name: ${{ needs.prepare-release.outputs.version }}
- generate_release_notes: true
- draft: true
- prerelease: ${{ needs.prepare-release.outputs.is_prerelease }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..4554796
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,95 @@
+name: Create Release and Publish Docker Images
+
+on:
+ push:
+ branches:
+ - release # Trigger when commits are pushed to the release branch (e.g., after merging master)
+ paths-ignore:
+ - '**.md'
+ - 'docs/**'
+
+jobs:
+ prepare-release:
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.get-version.outputs.version }}
+ version_tag: ${{ steps.get-version.outputs.version_tag }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Get version from VERSION file
+ id: get-version
+ run: |
+ VERSION_PLAIN=$(cat VERSION)
+ echo "version=${VERSION_PLAIN}" >> $GITHUB_OUTPUT
+ echo "version_tag=v${VERSION_PLAIN}" >> $GITHUB_OUTPUT # Add 'v' prefix for tag
+
+ build-images:
+ needs: prepare-release
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write # Needed to push images to GHCR
+ env:
+ DOCKER_BUILDKIT: 1
+ BUILDKIT_STEP_LOG_MAX_SIZE: 10485760
+ # This environment variable will override the VERSION variable in docker-bake.hcl
+ VERSION: ${{ needs.prepare-release.outputs.version_tag }} # Use tag version (vX.Y.Z) for bake
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Free disk space # Optional: Keep if needed for large builds
+ run: |
+ echo "Listing current disk space"
+ df -h
+ echo "Cleaning up disk space..."
+ sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache
+ docker system prune -af
+ echo "Disk space after cleanup"
+ df -h
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3 # Use v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3 # Use v3
+ with:
+ driver-opts: |
+ image=moby/buildkit:latest
+ network=host
+
+ - name: Log in to GitHub Container Registry
+ uses: docker/login-action@v3 # Use v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and push images using Docker Bake
+ run: |
+ echo "Building and pushing images for version ${{ needs.prepare-release.outputs.version_tag }}"
+ # The VERSION env var above sets the tag for the bake file targets
+ docker buildx bake --push
+
+ create-release:
+ needs: [prepare-release, build-images]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # Needed to create releases
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Fetch all history for release notes generation
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2 # Use v2
+ with:
+ tag_name: ${{ needs.prepare-release.outputs.version_tag }} # Use vX.Y.Z tag
+ name: Release ${{ needs.prepare-release.outputs.version_tag }}
+ generate_release_notes: true # Auto-generate release notes
+ draft: false # Publish immediately
+ prerelease: false # Mark as a stable release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index 5318b03..ab87ff4 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,12 @@
# _`FastKoko`_
-[]()
-[]()
+[]()
+[]()
[](https://huggingface.co/spaces/Remsky/Kokoro-TTS-Zero)
-[](https://github.com/hexgrad/kokoro)
-[](https://github.com/hexgrad/misaki)
+[](https://github.com/hexgrad/kokoro)
+[](https://github.com/hexgrad/misaki)
[](https://huggingface.co/hexgrad/Kokoro-82M/commit/9901c2b79161b6e898b7ea857ae5298f47b8b0d6)
@@ -24,10 +24,6 @@ Dockerized FastAPI wrapper for [Kokoro-82M](https://huggingface.co/hexgrad/Kokor
### Integration Guides
[](https://github.com/remsky/Kokoro-FastAPI/wiki/Setup-Kubernetes) [](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-DigitalOcean) [](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-SillyTavern)
[](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-OpenWebUi)
-
-
-
-
## Get Started
@@ -41,8 +37,8 @@ Refer to the core/config.py file for a full list of variables which can be manag
# the `latest` tag can be used, but should not be considered stable as it may include `nightly` branch builds
# it may have some bonus features however, and feedback/testing is welcome
-docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:v0.2.2 # CPU, or:
-docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.2 #NVIDIA GPU
+docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:v0.3.0 # CPU, or:
+docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:v0.3.0 #NVIDIA GPU
```
@@ -66,7 +62,7 @@ docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.2 #NV
# *Note for Apple Silicon (M1/M2) users:
# The current GPU build relies on CUDA, which is not supported on Apple Silicon.
# If you are on an M1/M2/M3 Mac, please use the `docker/cpu` setup.
- # MPS (Appleās GPU acceleration) support is planned but not yet available.
+ # MPS (Apple's GPU acceleration) support is planned but not yet available.
# Models will auto-download, but if needed you can manually download:
python docker/scripts/download_model.py --output api/src/models/v1_0
@@ -139,8 +135,8 @@ with client.audio.speech.with_streaming_response.create(
-
## Features
+
OpenAI-Compatible Speech Endpoint
diff --git a/VERSION b/VERSION
index 22c08f7..0d91a54 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.2.1
+0.3.0
diff --git a/charts/kokoro-fastapi/Chart.yaml b/charts/kokoro-fastapi/Chart.yaml
index 0f79d40..ed6f675 100644
--- a/charts/kokoro-fastapi/Chart.yaml
+++ b/charts/kokoro-fastapi/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: kokoro-fastapi
description: A Helm chart for deploying the Kokoro FastAPI TTS service to Kubernetes
type: application
-version: 0.2.0
-appVersion: "0.2.0"
+version: 0.3.0
+appVersion: "0.3.0"
keywords:
- tts
diff --git a/docker/cpu/docker-compose.yml b/docker/cpu/docker-compose.yml
index ed15540..8ca8821 100644
--- a/docker/cpu/docker-compose.yml
+++ b/docker/cpu/docker-compose.yml
@@ -20,7 +20,7 @@ services:
# # Gradio UI service [Comment out everything below if you don't need it]
# gradio-ui:
- # image: ghcr.io/remsky/kokoro-fastapi-ui:v0.2.0
+ # image: ghcr.io/remsky/kokoro-fastapi-ui:v${VERSION}
# # Uncomment below (and comment out above) to build from source instead of using the released image
# build:
# context: ../../ui
diff --git a/docker/gpu/docker-compose.yml b/docker/gpu/docker-compose.yml
index 2623394..9faddd8 100644
--- a/docker/gpu/docker-compose.yml
+++ b/docker/gpu/docker-compose.yml
@@ -1,7 +1,7 @@
name: kokoro-tts-gpu
services:
kokoro-tts:
- # image: ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.0
+ # image: ghcr.io/remsky/kokoro-fastapi-gpu:v${VERSION}
build:
context: ../..
dockerfile: docker/gpu/Dockerfile
@@ -24,7 +24,7 @@ services:
# # Gradio UI service
# gradio-ui:
- # image: ghcr.io/remsky/kokoro-fastapi-ui:v0.2.0
+ # image: ghcr.io/remsky/kokoro-fastapi-ui:v${VERSION}
# # Uncomment below to build from source instead of using the released image
# # build:
# # context: ../../ui
diff --git a/pyproject.toml b/pyproject.toml
index 5e6bd9c..5d082f7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kokoro-fastapi"
-version = "0.1.4"
+version = "0.3.0"
description = "FastAPI TTS Service"
readme = "README.md"
requires-python = ">=3.10"
@@ -31,10 +31,11 @@ dependencies = [
"matplotlib>=3.10.0",
"mutagen>=1.47.0",
"psutil>=6.1.1",
- "kokoro @ git+https://github.com/hexgrad/kokoro.git@31a2b6337b8c1b1418ef68c48142328f640da938",
- 'misaki[en,ja,ko,zh] @ git+https://github.com/hexgrad/misaki.git@ebc76c21b66c5fc4866ed0ec234047177b396170',
- "spacy==3.7.2",
- "en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl",
+ "espeakng-loader==0.2.4",
+ "kokoro==0.9.2",
+ "misaki[en,ja,ko,zh]==0.9.3",
+ "spacy==3.8.5",
+ "en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl",
"inflect>=7.5.0",
"phonemizer-fork>=3.3.2",
"av>=14.2.0",
@@ -53,8 +54,8 @@ test = [
"pytest-cov==6.0.0",
"httpx==0.26.0",
"pytest-asyncio==0.25.3",
- "openai>=1.59.6",
"tomli>=2.0.1",
+ "jinja2>=3.1.6"
]
[tool.uv]
diff --git a/readme-parts/config.yaml b/readme-parts/config.yaml
new file mode 100644
index 0000000..890723a
--- /dev/null
+++ b/readme-parts/config.yaml
@@ -0,0 +1,11 @@
+# README Configuration
+# version is now automatically loaded from the VERSION file
+tests_passed: "69"
+coverage: "52"
+kokoro_version: "0.7.9"
+kokoro_commit: "31a2b63"
+misaki_version: "0.7.9"
+misaki_commit: "ebc76c2"
+model_version: "1.0"
+model_commit: "9901c2b"
+model_commit_full: "9901c2b79161b6e898b7ea857ae5298f47b8b0d6"
diff --git a/scripts/fix_misaki.py b/scripts/fix_misaki.py
new file mode 100644
index 0000000..768065d
--- /dev/null
+++ b/scripts/fix_misaki.py
@@ -0,0 +1,45 @@
+"""
+Patch for misaki package to fix the EspeakWrapper.set_data_path issue.
+"""
+
+import os
+import sys
+import importlib.util
+
+# Find the misaki package
+try:
+ import misaki
+ misaki_path = os.path.dirname(misaki.__file__)
+ print(f"Found misaki package at: {misaki_path}")
+except ImportError:
+ print("Misaki package not found. Make sure it's installed.")
+ sys.exit(1)
+
+# Path to the espeak.py file
+espeak_file = os.path.join(misaki_path, "espeak.py")
+
+if not os.path.exists(espeak_file):
+ print(f"Could not find {espeak_file}")
+ sys.exit(1)
+
+# Read the current content
+with open(espeak_file, 'r') as f:
+ content = f.read()
+
+# Check if the problematic line exists
+if "EspeakWrapper.set_data_path(espeakng_loader.get_data_path())" in content:
+ # Replace the problematic line
+ new_content = content.replace(
+ "EspeakWrapper.set_data_path(espeakng_loader.get_data_path())",
+ "# Fixed line to use data_path attribute instead of set_data_path method\n"
+ "EspeakWrapper.data_path = espeakng_loader.get_data_path()"
+ )
+
+ # Write the modified content back
+ with open(espeak_file, 'w') as f:
+ f.write(new_content)
+
+ print(f"Successfully patched {espeak_file}")
+else:
+ print(f"The problematic line was not found in {espeak_file}")
+ print("The file may have already been patched or the issue is different.")
diff --git a/scripts/update_badges.py b/scripts/update_badges.py
index dd5d6c4..27849be 100644
--- a/scripts/update_badges.py
+++ b/scripts/update_badges.py
@@ -4,40 +4,37 @@ import tomli
from pathlib import Path
def extract_dependency_info():
- """Extract version and commit hash for kokoro and misaki from pyproject.toml"""
+ """Extract version for kokoro and misaki from pyproject.toml"""
with open("pyproject.toml", "rb") as f:
pyproject = tomli.load(f)
deps = pyproject["project"]["dependencies"]
info = {}
+ kokoro_found = False
+ misaki_found = False
- # Extract kokoro info
for dep in deps:
- if dep.startswith("kokoro @"):
- # Extract version from the dependency string if available
- version_match = re.search(r"kokoro @ git\+https://github\.com/hexgrad/kokoro\.git@", dep)
- if version_match:
- # If no explicit version, use v0.7.9 as shown in the README
- version = "v0.7.9"
- commit_match = re.search(r"@([a-f0-9]{7})", dep)
- if commit_match:
- info["kokoro"] = {
- "version": version,
- "commit": commit_match.group(1)
- }
- elif dep.startswith("misaki["):
- # Extract version from the dependency string if available
- version_match = re.search(r"misaki\[.*?\] @ git\+https://github\.com/hexgrad/misaki\.git@", dep)
- if version_match:
- # If no explicit version, use v0.7.9 as shown in the README
- version = "v0.7.9"
- commit_match = re.search(r"@([a-f0-9]{7})", dep)
- if commit_match:
- info["misaki"] = {
- "version": version,
- "commit": commit_match.group(1)
- }
-
+ # Match kokoro==version
+ kokoro_match = re.match(r"^kokoro==(.+)$", dep)
+ if kokoro_match:
+ info["kokoro"] = {"version": kokoro_match.group(1)}
+ kokoro_found = True
+
+ # Match misaki[...] ==version or misaki==version
+ misaki_match = re.match(r"^misaki(?:\[.*?\])?==(.+)$", dep)
+ if misaki_match:
+ info["misaki"] = {"version": misaki_match.group(1)}
+ misaki_found = True
+
+ # Stop if both found
+ if kokoro_found and misaki_found:
+ break
+
+ if not kokoro_found:
+ raise ValueError("Kokoro version not found in pyproject.toml dependencies")
+ if not misaki_found:
+ raise ValueError("Misaki version not found in pyproject.toml dependencies")
+
return info
def run_pytest_with_coverage():
@@ -98,20 +95,24 @@ def update_readme_badges(passed_tests, coverage_percentage, dep_info):
# Update kokoro badge
if "kokoro" in dep_info:
+ # Find badge like kokoro-v0.9.2::abcdefg-BB5420 or kokoro-v0.9.2-BB5420
+ kokoro_version = dep_info["kokoro"]["version"]
content = re.sub(
- r'!\[Kokoro\]\(https://img\.shields\.io/badge/kokoro-[^)]+\)',
- f'',
+ r'(!\[Kokoro\]\(https://img\.shields\.io/badge/kokoro-)[^)-]+(-BB5420\))',
+ lambda m: f"{m.group(1)}{kokoro_version}{m.group(2)}",
content
)
# Update misaki badge
if "misaki" in dep_info:
+ # Find badge like misaki-v0.9.3::abcdefg-B8860B or misaki-v0.9.3-B8860B
+ misaki_version = dep_info["misaki"]["version"]
content = re.sub(
- r'!\[Misaki\]\(https://img\.shields\.io/badge/misaki-[^)]+\)',
- f'',
+ r'(!\[Misaki\]\(https://img\.shields\.io/badge/misaki-)[^)-]+(-B8860B\))',
+ lambda m: f"{m.group(1)}{misaki_version}{m.group(2)}",
content
)
-
+
readme_path.write_text(content)
return True
@@ -128,11 +129,11 @@ def main():
print(f"- Tests: {passed_tests} passed")
print(f"- Coverage: {coverage_percentage}%")
if "kokoro" in dep_info:
- print(f"- Kokoro: {dep_info['kokoro']['version']}::{dep_info['kokoro']['commit']}")
+ print(f"- Kokoro: {dep_info['kokoro']['version']}")
if "misaki" in dep_info:
- print(f"- Misaki: {dep_info['misaki']['version']}::{dep_info['misaki']['commit']}")
+ print(f"- Misaki: {dep_info['misaki']['version']}")
else:
print("Failed to update badges")
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/scripts/update_version.py b/scripts/update_version.py
new file mode 100755
index 0000000..ddc1856
--- /dev/null
+++ b/scripts/update_version.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python3
+"""
+Version Update Script
+
+This script reads the version from the VERSION file and updates references
+in pyproject.toml, the Helm chart, and README.md.
+"""
+
+import re
+import yaml
+from pathlib import Path
+
+# Get the project root directory
+ROOT_DIR = Path(__file__).parent.parent
+
+# --- Configuration ---
+VERSION_FILE = ROOT_DIR / "VERSION"
+PYPROJECT_FILE = ROOT_DIR / "pyproject.toml"
+HELM_CHART_FILE = ROOT_DIR / "charts" / "kokoro-fastapi" / "Chart.yaml"
+README_FILE = ROOT_DIR / "README.md"
+# --- End Configuration ---
+
+def update_pyproject(version: str):
+ """Updates the version in pyproject.toml"""
+ if not PYPROJECT_FILE.exists():
+ print(f"Skipping: {PYPROJECT_FILE} not found.")
+ return
+
+ try:
+ content = PYPROJECT_FILE.read_text()
+ # Regex to find and capture current version = "X.Y.Z" under [project]
+ pattern = r'(^\[project\]\s*(?:.*\s)*?version\s*=\s*)"([^"]+)"'
+ match = re.search(pattern, content, flags=re.MULTILINE)
+
+ if not match:
+ print(f"Warning: Version pattern not found in {PYPROJECT_FILE}")
+ return
+
+ current_version = match.group(2)
+ if current_version == version:
+ print(f"Already up-to-date: {PYPROJECT_FILE} (version {version})")
+ else:
+ # Perform replacement
+ new_content = re.sub(pattern, rf'\1"{version}"', content, count=1, flags=re.MULTILINE)
+ PYPROJECT_FILE.write_text(new_content)
+ print(f"Updated {PYPROJECT_FILE} from {current_version} to {version}")
+
+ except Exception as e:
+ print(f"Error processing {PYPROJECT_FILE}: {e}")
+
+def update_helm_chart(version: str):
+ """Updates the version and appVersion in the Helm chart"""
+ if not HELM_CHART_FILE.exists():
+ print(f"Skipping: {HELM_CHART_FILE} not found.")
+ return
+
+ try:
+ content = HELM_CHART_FILE.read_text()
+ original_content = content
+ updated_count = 0
+
+ # Update 'version:' line (unquoted)
+ # Looks for 'version:' followed by optional whitespace and the version number
+ version_pattern = r"^(version:\s*)(\S+)"
+ current_version_match = re.search(version_pattern, content, flags=re.MULTILINE)
+ if current_version_match and current_version_match.group(2) != version:
+ content = re.sub(version_pattern, rf"\g<1>{version}", content, count=1, flags=re.MULTILINE)
+ print(f"Updating 'version' in {HELM_CHART_FILE} from {current_version_match.group(2)} to {version}")
+ updated_count += 1
+ elif current_version_match:
+ print(f"Already up-to-date: 'version' in {HELM_CHART_FILE} is {version}")
+ else:
+ print(f"Warning: 'version:' pattern not found in {HELM_CHART_FILE}")
+
+
+ # Update 'appVersion:' line (quoted or unquoted)
+ # Looks for 'appVersion:' followed by optional whitespace, optional quote, the version, optional quote
+ app_version_pattern = r"^(appVersion:\s*)(\"?)([^\"\s]+)(\"?)"
+ current_app_version_match = re.search(app_version_pattern, content, flags=re.MULTILINE)
+
+ if current_app_version_match:
+ leading_whitespace = current_app_version_match.group(1) # e.g., "appVersion: "
+ opening_quote = current_app_version_match.group(2) # e.g., '"' or ''
+ current_app_ver = current_app_version_match.group(3) # e.g., '0.2.0'
+ closing_quote = current_app_version_match.group(4) # e.g., '"' or ''
+
+ # Check if quotes were consistent (both present or both absent)
+ if opening_quote != closing_quote:
+ print(f"Warning: Inconsistent quotes found for appVersion in {HELM_CHART_FILE}. Skipping update for this line.")
+ elif current_app_ver == version and opening_quote == '"': # Check if already correct *and* quoted
+ print(f"Already up-to-date: 'appVersion' in {HELM_CHART_FILE} is \"{version}\"")
+ else:
+ # Always replace with the quoted version
+ replacement = f'{leading_whitespace}"{version}"' # Ensure quotes
+ original_display = f"{opening_quote}{current_app_ver}{closing_quote}" # How it looked before
+ target_display = f'"{version}"' # How it should look
+
+ # Only report update if the displayed value actually changes
+ if original_display != target_display:
+ content = re.sub(app_version_pattern, replacement, content, count=1, flags=re.MULTILINE)
+ print(f"Updating 'appVersion' in {HELM_CHART_FILE} from {original_display} to {target_display}")
+ updated_count += 1
+ else:
+ # It matches the target version but might need quoting fixed silently if we didn't update
+ # Or it was already correct. Check if content changed. If not, report up-to-date.
+ if not (content != original_content and updated_count > 0): # Avoid double message if version also changed
+ print(f"Already up-to-date: 'appVersion' in {HELM_CHART_FILE} is {target_display}")
+
+
+ else:
+ print(f"Warning: 'appVersion:' pattern not found in {HELM_CHART_FILE}")
+
+
+ # Write back only if changes were made
+ if content != original_content:
+ HELM_CHART_FILE.write_text(content)
+ # Confirmation message printed above during the specific update
+ elif updated_count == 0 and current_version_match and current_app_version_match :
+ # If no updates were made but patterns were found, confirm it's up-to-date overall
+ print(f"Already up-to-date: {HELM_CHART_FILE} (version {version})")
+
+
+ except Exception as e:
+ print(f"Error processing {HELM_CHART_FILE}: {e}")
+
+def update_readme(version_with_v: str):
+ """Updates Docker image tags in README.md"""
+ if not README_FILE.exists():
+ print(f"Skipping: {README_FILE} not found.")
+ return
+
+ try:
+ content = README_FILE.read_text()
+ # Regex to find and capture current ghcr.io/.../kokoro-fastapi-(cpu|gpu):vX.Y.Z
+ pattern = r'(ghcr\.io/remsky/kokoro-fastapi-(?:cpu|gpu)):(v\d+\.\d+\.\d+)'
+ matches = list(re.finditer(pattern, content)) # Find all occurrences
+
+ if not matches:
+ print(f"Warning: Docker image tag pattern not found in {README_FILE}")
+ else:
+ updated_needed = False
+ for match in matches:
+ current_tag = match.group(2)
+ if current_tag != version_with_v:
+ updated_needed = True
+ break # Only need one mismatch to trigger update
+
+ if updated_needed:
+ # Perform replacement on all occurrences
+ new_content = re.sub(pattern, rf'\1:{version_with_v}', content)
+ README_FILE.write_text(new_content)
+ print(f"Updated Docker image tags in {README_FILE} to {version_with_v}")
+ else:
+ print(f"Already up-to-date: Docker image tags in {README_FILE} (version {version_with_v})")
+
+ # Check for ':latest' tag usage remains the same
+ if ':latest' in content:
+ print(f"Warning: Found ':latest' tag in {README_FILE}. Consider updating manually if needed.")
+
+ except Exception as e:
+ print(f"Error processing {README_FILE}: {e}")
+
+
+def main():
+ # Read the version from the VERSION file
+ if not VERSION_FILE.exists():
+ print(f"Error: {VERSION_FILE} not found.")
+ return
+
+ try:
+ version = VERSION_FILE.read_text().strip()
+ if not re.match(r"^\d+\.\d+\.\d+$", version):
+ print(f"Error: Invalid version format '{version}' in {VERSION_FILE}. Expected X.Y.Z")
+ return
+ except Exception as e:
+ print(f"Error reading {VERSION_FILE}: {e}")
+ return
+
+ print(f"Read version: {version} from {VERSION_FILE}")
+ print("-" * 20)
+
+ # Prepare versions (with and without 'v')
+ version_plain = version
+ version_with_v = f"v{version}"
+
+ # Update files
+ update_pyproject(version_plain)
+ update_helm_chart(version_plain)
+ update_readme(version_with_v)
+
+ print("-" * 20)
+ print("Version update script finished.")
+
+if __name__ == "__main__":
+ main()
diff --git a/slim.report.json b/slim.report.json
deleted file mode 100644
index 415c381..0000000
--- a/slim.report.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "document": "doc.report.command",
- "version": "ov/command/slim/1.1",
- "engine": "linux/amd64|ALP|x.1.42.2|29e62e7836de7b1004607c51c502537ffe1969f0|2025-01-16_07:48:54AM|x",
- "containerized": false,
- "host_distro": {
- "name": "Ubuntu",
- "version": "22.04",
- "display_name": "Ubuntu 22.04.5 LTS"
- },
- "type": "slim",
- "state": "error",
- "target_reference": "kokoro-fastapi:latest",
- "system": {
- "type": "",
- "release": "",
- "distro": {
- "name": "",
- "version": "",
- "display_name": ""
- }
- },
- "source_image": {
- "identity": {
- "id": ""
- },
- "size": 0,
- "size_human": "",
- "create_time": "",
- "architecture": "",
- "container_entry": {
- "exe_path": ""
- }
- },
- "minified_image_size": 0,
- "minified_image_size_human": "",
- "minified_image": "",
- "minified_image_id": "",
- "minified_image_digest": "",
- "minified_image_has_data": false,
- "minified_by": 0,
- "artifact_location": "",
- "container_report_name": "",
- "seccomp_profile_name": "",
- "apparmor_profile_name": "",
- "image_stack": null,
- "image_created": false,
- "image_build_engine": ""
-}
diff --git a/start-cpu.sh b/start-cpu.sh
index 4438ff9..98fae6d 100755
--- a/start-cpu.sh
+++ b/start-cpu.sh
@@ -10,9 +10,17 @@ export PYTHONPATH=$PROJECT_ROOT:$PROJECT_ROOT/api
export MODEL_DIR=src/models
export VOICES_DIR=src/voices/v1_0
export WEB_PLAYER_PATH=$PROJECT_ROOT/web
+# Set the espeak-ng data path to your location
+export ESPEAK_DATA_PATH=/usr/lib/x86_64-linux-gnu/espeak-ng-data
# Run FastAPI with CPU extras using uv run
# Note: espeak may still require manual installation,
uv pip install -e ".[cpu]"
uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
+
+# Apply the misaki patch to fix possible EspeakWrapper issue in older versions
+# echo "Applying misaki patch..."
+# python scripts/fix_misaki.py
+
+# Start the server
uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880