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`_ -[![Tests](https://img.shields.io/badge/tests-69%20passed-darkgreen)]() -[![Coverage](https://img.shields.io/badge/coverage-52%25-tan)]() +[![Tests](https://img.shields.io/badge/tests-69-darkgreen)]() +[![Coverage](https://img.shields.io/badge/coverage-54%25-tan)]() [![Try on Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Try%20on-Spaces-blue)](https://huggingface.co/spaces/Remsky/Kokoro-TTS-Zero) -[![Kokoro](https://img.shields.io/badge/kokoro-v0.7.9::31a2b63-BB5420)](https://github.com/hexgrad/kokoro) -[![Misaki](https://img.shields.io/badge/misaki-v0.7.9::ebc76c2-B8860B)](https://github.com/hexgrad/misaki) +[![Kokoro](https://img.shields.io/badge/kokoro-0.9.2-BB5420)](https://github.com/hexgrad/kokoro) +[![Misaki](https://img.shields.io/badge/misaki-0.9.3-B8860B)](https://github.com/hexgrad/misaki) [![Tested at Model Commit](https://img.shields.io/badge/last--tested--model--commit-1.0::9901c2b-blue)](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 [![Helm Chart](https://img.shields.io/badge/Helm%20Chart-black?style=flat&logo=helm&logoColor=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Setup-Kubernetes) [![DigitalOcean](https://img.shields.io/badge/DigitalOcean-black?style=flat&logo=digitalocean&logoColor=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-DigitalOcean) [![SillyTavern](https://img.shields.io/badge/SillyTavern-black?style=flat&color=red)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-SillyTavern) [![OpenWebUI](https://img.shields.io/badge/OpenWebUI-black?style=flat&color=white)](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'![Kokoro](https://img.shields.io/badge/kokoro-{dep_info["kokoro"]["version"]}::{dep_info["kokoro"]["commit"]}-BB5420)', + 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'![Misaki](https://img.shields.io/badge/misaki-{dep_info["misaki"]["version"]}::{dep_info["misaki"]["commit"]}-B8860B)', + 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