diff --git a/.github/workflows/test-online-cpu-docker.yml b/.github/workflows/test-online-cpu-docker.yml new file mode 100644 index 000000000..c4b90605e --- /dev/null +++ b/.github/workflows/test-online-cpu-docker.yml @@ -0,0 +1,60 @@ +name: Test online CPU Docker image + +on: + pull_request: + paths: + - ".github/workflows/test-online-cpu-docker.yml" + - "runtime/dockerfile/Dockerfile.online.cpu" + - "runtime/dockerfile/Dockerfile.online.cpu.dockerignore" + - "runtime/dockerfile/online-cpu-entrypoint.sh" + - "runtime/docs/SDK_advanced_guide_online.md" + - "runtime/docs/SDK_advanced_guide_online_zh.md" + - "runtime/onnxruntime/**" + - "runtime/websocket/**" + - "tests/test_online_cpu_dockerfile.py" + push: + branches: [main] + paths: + - ".github/workflows/test-online-cpu-docker.yml" + - "runtime/dockerfile/**" + - "runtime/onnxruntime/**" + - "runtime/websocket/**" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: online-cpu-docker-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-amd64: + runs-on: ubuntu-24.04 + timeout-minutes: 120 + steps: + - uses: actions/checkout@v4 + + - name: Validate entrypoint + run: bash -n runtime/dockerfile/online-cpu-entrypoint.sh + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build source image + uses: docker/build-push-action@v6 + with: + context: . + file: runtime/dockerfile/Dockerfile.online.cpu + platforms: linux/amd64 + load: true + tags: funasr-online-cpu:test + cache-from: type=gha,scope=online-cpu-amd64 + cache-to: type=gha,mode=max,scope=online-cpu-amd64 + + - name: Smoke test linked server + run: | + docker run --rm \ + --entrypoint /workspace/FunASR/runtime/websocket/build/bin/funasr-wss-server-2pass \ + funasr-online-cpu:test \ + --help diff --git a/runtime/dockerfile/Dockerfile.online.cpu b/runtime/dockerfile/Dockerfile.online.cpu new file mode 100644 index 000000000..4f6cf8a5e --- /dev/null +++ b/runtime/dockerfile/Dockerfile.online.cpu @@ -0,0 +1,38 @@ +# syntax=docker/dockerfile:1.7 + +ARG BASE_IMAGE=registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.13@sha256:2a54c20f6b9e2fd253b24abd5c083545751485bcc26e038ed12eab3380c86d51 +FROM ${BASE_IMAGE} + +ARG TARGETARCH + +WORKDIR /workspace +RUN rm -rf /workspace/FunASR +COPY . /workspace/FunASR + +WORKDIR /workspace/FunASR +RUN pip install --no-cache-dir -e ./ && \ + case "${TARGETARCH}" in \ + amd64) \ + onnxruntime_dir=/workspace/onnxruntime-linux-x64-1.14.0; \ + ffmpeg_dir=/workspace/ffmpeg-master-latest-linux64-gpl-shared \ + ;; \ + arm64) \ + onnxruntime_dir=/workspace/onnxruntime-linux-aarch64-1.17.1; \ + ffmpeg_dir=/workspace/ffmpeg-master-latest-linuxarm64-gpl-shared \ + ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 2 ;; \ + esac && \ + cmake -S runtime/websocket -B runtime/websocket/build \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_PORTAUDIO=OFF \ + -DONNXRUNTIME_DIR="${onnxruntime_dir}" \ + -DFFMPEG_DIR="${ffmpeg_dir}" && \ + cmake --build runtime/websocket/build --parallel "$(nproc)" + +COPY runtime/dockerfile/online-cpu-entrypoint.sh /usr/local/bin/online-cpu-entrypoint.sh +RUN chmod 0755 /usr/local/bin/online-cpu-entrypoint.sh + +WORKDIR /workspace/FunASR/runtime +EXPOSE 10095 +VOLUME ["/workspace/models"] +ENTRYPOINT ["/usr/local/bin/online-cpu-entrypoint.sh"] diff --git a/runtime/dockerfile/Dockerfile.online.cpu.dockerignore b/runtime/dockerfile/Dockerfile.online.cpu.dockerignore new file mode 100644 index 000000000..ef1c0cfeb --- /dev/null +++ b/runtime/dockerfile/Dockerfile.online.cpu.dockerignore @@ -0,0 +1,8 @@ +.git +.github +.mcp-tasks +**/.mcp-tasks +**/__pycache__ +**/.pytest_cache +**/*.pyc +web-pages diff --git a/runtime/dockerfile/online-cpu-entrypoint.sh b/runtime/dockerfile/online-cpu-entrypoint.sh new file mode 100755 index 000000000..6d1a95e7d --- /dev/null +++ b/runtime/dockerfile/online-cpu-entrypoint.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -gt 0 && "$1" != -* ]]; then + exec "$@" +fi + +model_root="${FUNASR_DOWNLOAD_MODEL_DIR:-/workspace/models}" +hotword_file="${FUNASR_HOTWORD_FILE:-${model_root}/hotwords.txt}" +mkdir -p "${model_root}" +touch "${hotword_file}" + +decoder_threads="${FUNASR_DECODER_THREAD_NUM:-$(nproc)}" +io_threads="${FUNASR_IO_THREAD_NUM:-$(( (decoder_threads + 15) / 16 ))}" + +server=/workspace/FunASR/runtime/websocket/build/bin/funasr-wss-server-2pass + +exec "${server}" \ + --download-model-dir "${model_root}" \ + --model-dir "${FUNASR_MODEL_DIR:-damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx}" \ + --online-model-dir "${FUNASR_ONLINE_MODEL_DIR:-damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx}" \ + --vad-dir "${FUNASR_VAD_DIR:-damo/speech_fsmn_vad_zh-cn-16k-common-onnx}" \ + --punc-dir "${FUNASR_PUNC_DIR:-damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx}" \ + --itn-dir "${FUNASR_ITN_DIR:-thuduj12/fst_itn_zh}" \ + --lm-dir "${FUNASR_LM_DIR:-damo/speech_ngram_lm_zh-cn-ai-wesp-fst}" \ + --decoder-thread-num "${decoder_threads}" \ + --model-thread-num "${FUNASR_MODEL_THREAD_NUM:-1}" \ + --io-thread-num "${io_threads}" \ + --port "${FUNASR_PORT:-10095}" \ + --certfile "" \ + --keyfile "" \ + --hotword "${hotword_file}" \ + "$@" diff --git a/runtime/docs/SDK_advanced_guide_online.md b/runtime/docs/SDK_advanced_guide_online.md index 28b0b3aae..b4bac726b 100644 --- a/runtime/docs/SDK_advanced_guide_online.md +++ b/runtime/docs/SDK_advanced_guide_online.md @@ -37,6 +37,33 @@ mkdir -p ./funasr-runtime-resources/models sudo docker run -p 10096:10095 -it --privileged=true -v $PWD/funasr-runtime-resources/models:/workspace/models registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.13 ``` +### Build the online CPU image from source + +The public 0.1.13 image above is the supported prebuilt release. To rebuild the +online CPU server from the current FunASR checkout, use the repository-owned +`runtime/dockerfile/Dockerfile.online.cpu`: + +```shell +git clone https://github.com/modelscope/FunASR.git +cd FunASR +docker build \ + -f runtime/dockerfile/Dockerfile.online.cpu \ + -t funasr-online-cpu:local . +mkdir -p ./funasr-runtime-resources/models +docker run --rm -p 10096:10095 \ + -v $PWD/funasr-runtime-resources/models:/workspace/models \ + funasr-online-cpu:local +``` + +This Dockerfile pins the public 0.1.13 multi-architecture image by manifest +digest as its compiler/runtime toolchain, copies the current checkout, and +rebuilds `funasr-wss-server-2pass`. The resulting container starts the server +directly on port 10095 without the prebuilt image's interactive daemon wrapper. +Model IDs, thread counts, and the container port can be overridden with the +`FUNASR_MODEL_DIR`, `FUNASR_ONLINE_MODEL_DIR`, `FUNASR_VAD_DIR`, +`FUNASR_PUNC_DIR`, `FUNASR_DECODER_THREAD_NUM`, and `FUNASR_PORT` +environment variables. + ### Launching the Server After Docker is launched, start the funasr-wss-server-2pass service program: diff --git a/runtime/docs/SDK_advanced_guide_online_zh.md b/runtime/docs/SDK_advanced_guide_online_zh.md index c16a63f6a..1e4ff0dfc 100644 --- a/runtime/docs/SDK_advanced_guide_online_zh.md +++ b/runtime/docs/SDK_advanced_guide_online_zh.md @@ -48,6 +48,30 @@ sudo docker run -p 10096:10095 -it --privileged=true \ registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.13 ``` +### 从源码构建 online CPU 镜像 + +上面的 0.1.13 镜像是公开预构建版本。如果需要基于当前 FunASR 源码自行构建, +请使用仓库内的 `runtime/dockerfile/Dockerfile.online.cpu`: + +```shell +git clone https://github.com/modelscope/FunASR.git +cd FunASR +docker build \ + -f runtime/dockerfile/Dockerfile.online.cpu \ + -t funasr-online-cpu:local . +mkdir -p ./funasr-runtime-resources/models +docker run --rm -p 10096:10095 \ + -v $PWD/funasr-runtime-resources/models:/workspace/models \ + funasr-online-cpu:local +``` + +该 Dockerfile 将公开的 0.1.13 多架构镜像按 manifest digest 固定为编译与运行 +工具链,然后复制当前 checkout 并重新编译 `funasr-wss-server-2pass`。构建出的 +容器会直接在 10095 端口启动服务,不依赖预构建镜像中的交互式 daemon 包装。 +可通过 `FUNASR_MODEL_DIR`、`FUNASR_ONLINE_MODEL_DIR`、 +`FUNASR_VAD_DIR`、`FUNASR_PUNC_DIR`、`FUNASR_DECODER_THREAD_NUM` +和 `FUNASR_PORT` 环境变量覆盖模型、线程数及容器端口。 + ### 服务端启动 docker启动之后,启动 funasr-wss-server-2pass服务程序: @@ -166,4 +190,4 @@ kill -9 PID [基于FST的中文ITN](https://www.modelscope.cn/models/thuduj12/fst_itn_zh/summary) 如果,您希望部署您finetune后的模型(例如10epoch.pb),需要手动将模型重命名为model.pb,并将原modelscope中模型model.pb替换掉,将路径指定为`model_dir`即可。 - +- diff --git a/runtime/docs/docker_online_cpu_zh_lists b/runtime/docs/docker_online_cpu_zh_lists index 4cb5ca045..22785bc01 100644 --- a/runtime/docs/docker_online_cpu_zh_lists +++ b/runtime/docs/docker_online_cpu_zh_lists @@ -1,4 +1,7 @@ DOCKER: + funasr-runtime-sdk-online-cpu-0.1.13 + funasr-runtime-sdk-online-cpu-0.1.12 + funasr-runtime-sdk-online-cpu-0.1.11 funasr-runtime-sdk-online-cpu-0.1.10 funasr-runtime-sdk-online-cpu-0.1.9 funasr-runtime-sdk-online-cpu-0.1.8 diff --git a/tests/test_online_cpu_dockerfile.py b/tests/test_online_cpu_dockerfile.py new file mode 100644 index 000000000..efd1a4ab9 --- /dev/null +++ b/tests/test_online_cpu_dockerfile.py @@ -0,0 +1,92 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +DOCKER_DIR = ROOT / "runtime" / "dockerfile" + + +def test_online_cpu_dockerfile_rebuilds_current_checkout_from_pinned_base(): + dockerfile = (DOCKER_DIR / "Dockerfile.online.cpu").read_text() + + required = [ + "funasr-runtime-sdk-online-cpu-0.1.13@sha256:2a54c20f", + "COPY . /workspace/FunASR", + "pip install --no-cache-dir -e ./", + "cmake -S runtime/websocket -B runtime/websocket/build", + "-DONNXRUNTIME_DIR=", + "-DFFMPEG_DIR=", + "online-cpu-entrypoint.sh", + "EXPOSE 10095", + ] + for marker in required: + assert marker in dockerfile + + +def test_online_cpu_entrypoint_execs_server_and_supports_overrides(): + entrypoint = (DOCKER_DIR / "online-cpu-entrypoint.sh").read_text() + + required = [ + "set -euo pipefail", + "FUNASR_MODEL_DIR", + "FUNASR_ONLINE_MODEL_DIR", + "FUNASR_VAD_DIR", + "FUNASR_PUNC_DIR", + "FUNASR_PORT", + "exec", + "funasr-wss-server-2pass", + '"$@"', + ] + for marker in required: + assert marker in entrypoint + + +def test_online_cpu_guides_document_source_build_and_image_boundary(): + guides = [ + ROOT / "runtime" / "docs" / "SDK_advanced_guide_online.md", + ROOT / "runtime" / "docs" / "SDK_advanced_guide_online_zh.md", + ] + + for guide in guides: + text = guide.read_text() + assert "Dockerfile.online.cpu" in text + assert "docker build" in text + assert "0.1.13" in text + assert "10095" in text + + +def test_online_cpu_docker_workflow_builds_and_smokes_the_server(): + workflow = ( + ROOT / ".github" / "workflows" / "test-online-cpu-docker.yml" + ).read_text() + + required = [ + "runtime/dockerfile/Dockerfile.online.cpu", + "timeout-minutes: 120", + "docker/setup-buildx-action@v3", + "docker/build-push-action@v6", + "platforms: linux/amd64", + "load: true", + "cache-from: type=gha,scope=online-cpu-amd64", + "cache-to: type=gha,mode=max,scope=online-cpu-amd64", + "funasr-wss-server-2pass", + "--help", + "bash -n runtime/dockerfile/online-cpu-entrypoint.sh", + ] + for marker in required: + assert marker in workflow + + +def test_online_cpu_fallback_manifest_lists_current_public_images(): + manifest = ( + ROOT / "runtime" / "docs" / "docker_online_cpu_zh_lists" + ).read_text() + images = [ + line.strip() + for line in manifest.splitlines() + if line.strip().startswith("funasr-runtime-sdk-online-cpu-") + ] + + assert images[:2] == [ + "funasr-runtime-sdk-online-cpu-0.1.13", + "funasr-runtime-sdk-online-cpu-0.1.12", + ]