mirror of
https://github.com/QwenAudio/SenseVoice.git
synced 2026-11-10 21:32:45 +08:00
ci(container): publish SenseVoice runtime to GHCR
Signed-off-by: LauraGPT <18321252+LauraGPT@users.noreply.github.com>
This commit is contained in:
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
@@ -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"]
|
||||
@@ -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.
|
||||
|
||||
@@ -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` を削除します。
|
||||
|
||||
## 微調整
|
||||
|
||||
### トレーニング環境のインストール
|
||||
|
||||
@@ -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`。
|
||||
|
||||
## 微调
|
||||
|
||||
### 安装训练环境
|
||||
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user