ci(container): publish SenseVoice runtime to GHCR

Signed-off-by: LauraGPT <18321252+LauraGPT@users.noreply.github.com>
This commit is contained in:
LauraGPT committed 2026-08-30 04:56:45 +00:00
1 parent 6991744856
commit cdb9fe2806
6 files changed
+189 -6

No files matched your search

@@ -0,0 +1,78 @@
name: SenseVoice container
on:
pull_request:
paths:
- Dockerfile
- requirements.txt
- api.py
- model.py
- .github/workflows/sensevoice-container.yml
- tests/test_container_contract.py
push:
branches:
- main
tags:
- "v*"
paths:
- Dockerfile
- requirements.txt
- api.py
- model.py
- .github/workflows/sensevoice-container.yml
- tests/test_container_contract.py
workflow_dispatch:
permissions:
contents: read
packages: write
concurrency:
group: sensevoice-container-${{ github.ref }}
cancel-in-progress: true
jobs:
contract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate container contract
run: python -m unittest tests.test_container_contract
build:
needs: contract
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate image metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/sensevoice
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=sha
- name: Build and publish
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true
+14 -5
View File
@@ -1,20 +1,26 @@
# ======================================================
# FunASR SenseVoiceSmall Inference Server
# ======================================================
FROM pytorch/pytorch:2.3.1-cuda12.1-cudnn8-runtime
FROM pytorch/pytorch:2.12.1-cuda12.6-cudnn9-runtime@sha256:79c5599719e0b1afdb56ac2d14588b530283752d7ae6ec3c36e18ec9deb8b229
LABEL org.opencontainers.image.source="https://github.com/QwenAudio/SenseVoice" \
org.opencontainers.image.description="SenseVoiceSmall FastAPI inference server" \
org.opencontainers.image.licenses="Apache-2.0"
# Install system dependencies
RUN apt-get update && apt-get install -y \
ffmpeg libsndfile1 git && \
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg libsndfile1 git python3-venv && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN ls -la /app
RUN python -m venv --system-site-packages /opt/sensevoice-venv
ENV PATH=/opt/sensevoice-venv/bin:$PATH
# Copy only requirements first
COPY requirements.txt /app/
# Install dependencies (cached if requirements.txt didn't change)
RUN pip install --no-cache-dir -r requirements.txt
RUN python -m pip install --no-cache-dir -r requirements.txt
# Now copy the rest of your code
COPY . /app
@@ -34,5 +40,8 @@ ENV MODELSCOPE_CACHE=/models
# Create model cache directory (helps reuse between restarts)
RUN mkdir -p /models
HEALTHCHECK --interval=30s --timeout=5s --start-period=5m --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:50000/', timeout=3)" || exit 1
# Start FastAPI app
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "50000"]
+15 -1
View File
@@ -303,6 +303,20 @@ pip3 install -e ./
SenseVoice can be built and run using Docker to simplify setup, ensure reproducibility, and support both CPU and GPU inference.
### Official image
The official `linux/amd64` runtime image is published to GitHub Container Registry. Model weights are not embedded in the image; they are downloaded into the mounted `/models` cache on first start.
```bash
docker pull ghcr.io/qwenaudio/sensevoice:latest
docker run --rm --gpus all \
-p 50000:50000 \
-v sensevoice-models:/models \
ghcr.io/qwenaudio/sensevoice:latest
```
Open `http://127.0.0.1:50000/docs` after the container becomes healthy. Release tags and immutable `sha-<commit>` tags are available in the [container package](https://github.com/QwenAudio/SenseVoice/pkgs/container/sensevoice).
### Build with Docker
```bash
docker build -t sensevoice .
@@ -314,7 +328,7 @@ docker run --gpus all -p 50000:50000 sensevoice
```
### Run (CPU-only)
```bash
docker run -e SENSEVOICE_DEVICE=cpu -p 50000:50000 sensevoice
docker run --rm -e SENSEVOICE_DEVICE=cpu -p 50000:50000 -v sensevoice-models:/models ghcr.io/qwenaudio/sensevoice:latest
```
### Docker Compose
Docker Compose provides an easier way to run SenseVoice with persistent model caching, networking etc.
+14
View File
@@ -246,6 +246,20 @@ export SENSEVOICE_DEVICE=cuda:0
fastapi run --port 50000
```
### 公式 Docker イメージ
公式の `linux/amd64` ランタイムイメージは GitHub Container Registry で公開されます。モデル重みはイメージに含まれず、初回起動時にマウントした `/models` キャッシュへダウンロードされます。
```bash
docker pull ghcr.io/qwenaudio/sensevoice:latest
docker run --rm --gpus all \
-p 50000:50000 \
-v sensevoice-models:/models \
ghcr.io/qwenaudio/sensevoice:latest
```
コンテナが healthy になったら `http://127.0.0.1:50000/docs` を開いてください。リリースタグと不変の `sha-<commit>` タグは[コンテナパッケージ](https://github.com/QwenAudio/SenseVoice/pkgs/container/sensevoice)で確認できます。CPU モードでは `-e SENSEVOICE_DEVICE=cpu` を追加し、`--gpus all` を削除します。
## 微調整
### トレーニング環境のインストール
+14
View File
@@ -274,6 +274,20 @@ export SENSEVOICE_DEVICE=cuda:0
fastapi run --port 50000
```
### Docker 官方镜像
官方 `linux/amd64` 运行时镜像发布在 GitHub Container Registry。镜像不内置模型权重;首次启动会把权重下载到挂载的 `/models` 缓存。
```bash
docker pull ghcr.io/qwenaudio/sensevoice:latest
docker run --rm --gpus all \
-p 50000:50000 \
-v sensevoice-models:/models \
ghcr.io/qwenaudio/sensevoice:latest
```
容器健康后访问 `http://127.0.0.1:50000/docs`。版本 tag 和不可变的 `sha-<commit>` tag 见 [容器包页面](https://github.com/QwenAudio/SenseVoice/pkgs/container/sensevoice)。CPU 模式增加 `-e SENSEVOICE_DEVICE=cpu` 并移除 `--gpus all`。
## 微调
### 安装训练环境
+54
View File
@@ -0,0 +1,54 @@
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
class ContainerContractTest(unittest.TestCase):
def test_container_base_matches_repository_torch_floor(self):
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
self.assertIn(
"pytorch/pytorch:2.12.1-cuda12.6-cudnn9-runtime@sha256:",
dockerfile,
)
self.assertNotIn("pytorch/pytorch:2.3.1", dockerfile)
def test_container_installs_dependencies_in_an_isolated_venv(self):
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
self.assertIn(
"python -m venv --system-site-packages /opt/sensevoice-venv",
dockerfile,
)
self.assertIn("ENV PATH=/opt/sensevoice-venv/bin:$PATH", dockerfile)
self.assertNotIn("--break-system-packages", dockerfile)
def test_container_workflow_builds_prs_and_only_pushes_trusted_refs(self):
workflow = (
ROOT / ".github" / "workflows" / "sensevoice-container.yml"
).read_text(encoding="utf-8")
self.assertIn("pull_request:", workflow)
self.assertIn("packages: write", workflow)
self.assertIn("platforms: linux/amd64", workflow)
self.assertIn("push: ${{ github.event_name != 'pull_request' }}", workflow)
self.assertIn(
"ghcr.io/${{ github.repository_owner }}/sensevoice",
workflow,
)
def test_readme_uses_public_ghcr_image_and_real_service_port(self):
readme = (ROOT / "README.md").read_text(encoding="utf-8")
self.assertIn("ghcr.io/qwenaudio/sensevoice", readme.lower())
self.assertIn("-p 50000:50000", readme)
self.assertNotIn(
"registry.cn-hangzhou.aliyuncs.com/funasr/sensevoice",
readme,
)
if __name__ == "__main__":
unittest.main()