diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 8a1ec4a..e07c9d7 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -7,6 +7,7 @@ on: permissions: contents: read + id-token: write pull-requests: write issues: write diff --git a/app/Dockerfile b/app/Dockerfile index 5f744b7..2d6bf7c 100755 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -1,8 +1,8 @@ -# TODO: pin digest — 运行: +# Reproducible-build note: operators may pin this mutable base tag by digest: # docker pull pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime # docker inspect pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime --format='{{index .RepoDigests 0}}' -# 然后改为:FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime@sha256: -# 不 pin digest 的话 mutable tag 可能导致构建不可重现(对应评审 CD-H6 / BP-M6)。 +# Then replace the FROM line with: +# FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime@sha256: FROM pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime ENV DEBIAN_FRONTEND=noninteractive diff --git a/app/config.py b/app/config.py index 0beedf9..7d3fe7f 100644 --- a/app/config.py +++ b/app/config.py @@ -9,7 +9,7 @@ from pathlib import Path -APP_VERSION = "0.7.6" +APP_VERSION = "0.8.4" def _env_float(name: str, default: float) -> float: diff --git a/doc/changelog.en.md b/doc/changelog.en.md index e384a9b..5257938 100644 --- a/doc/changelog.en.md +++ b/doc/changelog.en.md @@ -4,6 +4,10 @@ ## Unreleased +No unreleased changes. + +## 0.8.4 — Rust kernel foundation and release gates (2026-06-10) + ### Features - Added an optional `artifacts` manifest to completed results. The manifest diff --git a/doc/changelog.zh.md b/doc/changelog.zh.md index 1b99559..c95227f 100644 --- a/doc/changelog.zh.md +++ b/doc/changelog.zh.md @@ -4,6 +4,10 @@ ## Unreleased +暂无未发布变更。 + +## 0.8.4 — Rust kernel 基础能力与发布门禁 (2026-06-10) + ### 功能 - 新增完成态结果的可选 `artifacts` manifest。该字段只列出当前结果相关 artifact diff --git a/doc/configuration.en.md b/doc/configuration.en.md index ba1bb17..5b144b9 100644 --- a/doc/configuration.en.md +++ b/doc/configuration.en.md @@ -2,7 +2,7 @@ [简体中文](./configuration.zh.md) | **English** -This is the public configuration index for VoScript v0.7.6. It covers the +This is the public configuration index for VoScript v0.8.4. It covers the environment variables that the current code reads, the per-request override semantics of `POST /api/transcribe`, and internal defaults that are documented for operators but are not stable public knobs yet. Do not assume a Whisper, @@ -40,7 +40,7 @@ parameters yet. | `MODEL_IDLE_TIMEOUT_SEC` | `180` | GPU model idle-unload timeout, defaulting to 180 seconds (3 minutes). Set `0` to disable idle unload and keep models resident. When enabled, loaded models are released only after the serialized GPU runtime has been idle for this many seconds; on the next reload, ASR, diarization, and embedding each choose the visible CUDA device with the most free memory during their own lazy load. | | `RUST_KERNEL_MODE` | `off` | Optional Rust-backed provider/kernel mode. `off` keeps Python implementations; `required` makes selected Rust-backed paths import and run successfully or fail closed. The current selected paths are voiceprint scoring, result post-processing, and artifact manifest helper contracts; CI / Docker packaging still validates the Rust extension directly when the runtime default is off. | -`MODELS_DIR` and `LANGUAGE` are defined in the config module, but v0.7.6's main +`MODELS_DIR` and `LANGUAGE` are defined in the config module, but v0.8.4's main HTTP transcription path does not use them as stable public tuning knobs: Whisper local checkpoint lookup still expects `/models/faster-whisper-`, and default language should be controlled with the request `language` field or @@ -97,7 +97,7 @@ cache is incomplete. Current internal ASR defaults are `beam_size=5`, `vad_filter=True`, `vad_parameters.min_silence_duration_ms=500`, and `condition_on_previous_text=False`. -These do not have env or API fields in v0.7.6. Do not configure nonexistent +These do not have env or API fields in v0.8.4. Do not configure nonexistent variables such as `WHISPER_BEAM_SIZE`, `WHISPER_COMPUTE_TYPE`, or `WHISPER_VAD_*`. ## Denoising @@ -109,7 +109,7 @@ variables such as `WHISPER_BEAM_SIZE`, `WHISPER_COMPUTE_TYPE`, or `WHISPER_VAD_* | API `denoise_model` | omitted | Omitted means inherit `DENOISE_MODEL`; explicit `none` disables denoising for this job only. | | API `snr_threshold` | omitted | Omitted means inherit `DENOISE_SNR_THRESHOLD`; explicit values override the DeepFilterNet SNR gate for this job only. | -v0.7.6 defaults to `DENOISE_MODEL=none` for clean meeting-recorder audio. Enable +v0.8.4 defaults to `DENOISE_MODEL=none` for clean meeting-recorder audio. Enable `deepfilternet` or `noisereduce` only for noisy environments, either per job or as a service default. If you need clean recordings to be skipped automatically, use `deepfilternet`; `noisereduce` runs whenever it is selected. @@ -171,7 +171,7 @@ Cohort lifecycle: files to build and save a cohort. - After each enroll / update, the background `cohort-rebuild` thread wakes every 60 seconds and rebuilds after the latest enrollment is at least 30 seconds old. -- v0.7.6 protects larger loaded or persisted cohorts during automatic rebuilds: +- v0.8.4 protects larger loaded or persisted cohorts during automatic rebuilds: clearing transcription results, having only a few embeddings, or having fewer source embeddings than the current cohort will not shrink the cohort automatically. - `POST /api/voiceprints/rebuild-cohort` is an explicit manual rebuild and uses @@ -206,6 +206,14 @@ New fields are added under the optional-field principle. Clients should ignore unknown fields and tolerate missing `words`, `alignment`, `artifacts`, and `warning`. +## v0.8.4 Validation Wording + +v0.8.4 has internal live validation covering the optional Rust kernel foundation, +selected voiceprint scoring, result post-processing, artifact/status helper +contracts, Docker packaging smoke, and public release-scan gates. Public +documentation records only these behavior categories, not real task names, +sample names, job IDs, speaker IDs, hosts, logs, or paths. + ## v0.7.6 Validation Wording v0.7.6 has internal live validation covering `/healthz` availability during GPU diff --git a/doc/configuration.zh.md b/doc/configuration.zh.md index f955c98..05bd2e5 100644 --- a/doc/configuration.zh.md +++ b/doc/configuration.zh.md @@ -2,7 +2,7 @@ **简体中文** | [English](./configuration.en.md) -本文是 VoScript v0.7.6 的公开配置索引,覆盖当前代码已经读取并生效的 +本文是 VoScript v0.8.4 的公开配置索引,覆盖当前代码已经读取并生效的 环境变量、`POST /api/transcribe` 的请求级覆盖语义,以及还没有暴露为稳定 配置项的内部默认值。没有在本文列出的 Whisper / diarization / AS-norm 变量, 不要假定已经可用。 @@ -38,7 +38,7 @@ | `MODEL_IDLE_TIMEOUT_SEC` | `180` | GPU 模型空闲卸载超时,默认 180 秒(3 分钟)。设为 `0` 可关闭空闲卸载并保持模型常驻。开启后,只有串行 GPU 运行时空闲达到该秒数才释放已加载模型;下一次 reload 时 ASR、diarization 和 embedding 会在各自 lazy load 时分别选择当前可见 CUDA 中空闲显存最多的设备。 | | `RUST_KERNEL_MODE` | `off` | 可选 Rust-backed provider/kernel 路径开关。`off` 保持 Python 实现;`required` 要求被选择的 Rust-backed 路径可导入并执行,缺失或调用失败时 fail closed。当前被选择的路径是声纹计分、结果后处理和 artifact manifest helper contract;默认关闭时,CI / Docker packaging 仍会直接验证 Rust 扩展。 | -`MODELS_DIR` 和 `LANGUAGE` 在配置模块里有定义,但 v0.7.6 的主 HTTP 转写路径 +`MODELS_DIR` 和 `LANGUAGE` 在配置模块里有定义,但 v0.8.4 的主 HTTP 转写路径 没有把它们作为稳定公开调参入口使用:Whisper 本地 checkpoint 查找仍使用 `/models/faster-whisper-`,语言默认请通过请求字段 `language` 控制或留空自动检测。 @@ -90,7 +90,7 @@ Hugging Face snapshot,缓存不完整时再走 Hub。 当前内部 ASR 默认值:`beam_size=5`、`vad_filter=True`、 `vad_parameters.min_silence_duration_ms=500`、`condition_on_previous_text=False`。 -这些值在 v0.7.6 还没有对应 env 或 API 字段;不要写 `WHISPER_BEAM_SIZE`、 +这些值在 v0.8.4 还没有对应 env 或 API 字段;不要写 `WHISPER_BEAM_SIZE`、 `WHISPER_COMPUTE_TYPE`、`WHISPER_VAD_*` 之类未实现配置。 ## 降噪 @@ -102,7 +102,7 @@ Hugging Face snapshot,缓存不完整时再走 Hub。 | API `denoise_model` | 省略 | 省略表示继承 `DENOISE_MODEL`;显式传 `none` 表示只对本次任务关闭降噪。 | | API `snr_threshold` | 省略 | 省略表示继承 `DENOISE_SNR_THRESHOLD`;显式传值只覆盖本次任务的 DeepFilterNet SNR gate。 | -v0.7.6 默认面向干净会议录音,因此 `DENOISE_MODEL=none`。只有噪声环境才建议按任务 +v0.8.4 默认面向干净会议录音,因此 `DENOISE_MODEL=none`。只有噪声环境才建议按任务 或服务级启用 `deepfilternet` / `noisereduce`。如需“干净录音自动跳过”,请选择 `deepfilternet`;`noisereduce` 一旦被选择就会运行。 @@ -160,7 +160,7 @@ cohort 生命周期: - 否则扫描持久化转写结果和 `emb_*.npy` 构建并保存 cohort。 - 每次 enroll / update 后,后台 `cohort-rebuild` 线程每 60 秒检查一次,在最近一次 enroll 至少过去 30 秒后自动重建。 -- v0.7.6 的后台自动重建会保护更大的已加载或已持久化 cohort:清空转写结果、 +- v0.8.4 的后台自动重建会保护更大的已加载或已持久化 cohort:清空转写结果、 只有少量 embedding,或源数量少于现有 cohort 时,不会自动缩小 cohort。 - `POST /api/voiceprints/rebuild-cohort` 是显式手动重建,仍按当前可用 embedding 立即生成新 cohort。 @@ -188,6 +188,13 @@ cohort 生命周期: 新增字段按可选字段原则扩展;客户端应忽略不认识的字段,并容忍 `words` / `alignment` / `artifacts` / `warning` 缺失。 +## v0.8.4 验证口径 + +v0.8.4 已用 internal live validation 覆盖:可选 Rust kernel 基础能力、已选择的 +声纹计分、结果后处理、artifact/status helper contract、Docker packaging smoke +以及 public release scan gate。公开文档只记录行为类别,不发布真实任务名、 +样本名、job id、speaker id、主机、日志或路径。 + ## v0.7.6 验证口径 v0.7.6 已用 internal live validation 覆盖:GPU cleanup 期间 `/healthz` 仍保持可用、 diff --git a/doc/security.en.md b/doc/security.en.md index 3331141..32b5175 100644 --- a/doc/security.en.md +++ b/doc/security.en.md @@ -22,7 +22,7 @@ Treat the service as if it were an internal database. ## Built-in hardening (on by default) -As of 0.7.6 the following protections are in place out of the box: +As of 0.8.4 the following protections are in place out of the box: 1. **Container runs as a non-root user.** The Dockerfile creates an `app` user (uid/gid 1000 by default, overridable via `APP_UID`/ diff --git a/doc/security.zh.md b/doc/security.zh.md index 85d18f4..e3e633b 100644 --- a/doc/security.zh.md +++ b/doc/security.zh.md @@ -19,7 +19,7 @@ ## 内置的硬化(默认启用) -当前版本(0.7.6)默认开启以下保护: +当前版本(0.8.4)默认开启以下保护: 1. **容器以非 root 用户运行**。Dockerfile 创建 `app` 用户(uid/gid 1000, 可通过 `APP_UID`/`APP_GID` 覆盖),`USER app`。即使服务代码被 RCE, diff --git a/tests/unit/test_main_lifespan.py b/tests/unit/test_main_lifespan.py index 7346c60..d754861 100644 --- a/tests/unit/test_main_lifespan.py +++ b/tests/unit/test_main_lifespan.py @@ -54,8 +54,10 @@ def test_rebuild_thread_alive_during_lifespan(app_client): assert thread.daemon, "cohort-rebuild thread must be a daemon thread" -def test_openapi_version_reports_076(app_client): - assert app_client.app.version == "0.7.6" +def test_openapi_version_matches_config(app_client): + from app.config import APP_VERSION + + assert app_client.app.version == APP_VERSION def test_rebuild_thread_survives_tick_exception(app_client, monkeypatch):