diff --git a/docs/community_growth_20k.md b/docs/community_growth_20k.md index 84363b13d..78dceefaa 100644 --- a/docs/community_growth_20k.md +++ b/docs/community_growth_20k.md @@ -361,7 +361,7 @@ Paste the Markdown output into launch notes, weekly community updates, or releas - [x] Keep recent Python GitHub release pages linked to runtime assets. On 2026-07-23, `v1.3.19` through `v1.3.26` were verified to include a `Runtime downloads` section pointing to `runtime-llamacpp-v0.1.8`, including Linux Vulkan, Windows CUDA, all eight prebuilt llama.cpp / GGUF assets, and the matching PyPI install command. The `scripts/update_release_runtime_downloads.py` guard and `tests/test_release_runtime_downloads.py` cover future tag releases. - [x] Update PyPI release description if needed. On 2026-07-23, the live PyPI `funasr 1.3.26` page was audited: its long description already comes from `README.md` and includes the current deployment-routing markers `OpenAI-compatible`, `/v1/audio/transcriptions`, `vllm`, `runtime-llamacpp-v0.1.8`, `GGUF`, and `funasr==1.3.26`. The package summary was prepared for the next PyPI publish by updating `setup.py` to `OpenAI-compatible speech recognition toolkit with WebSocket streaming, vLLM acceleration, and llama.cpp/GGUF edge runtime`, plus discovery keywords for `openai-compatible`, `websocket`, `vllm`, `gguf`, and `llama-cpp`; the currently published PyPI metadata remains `1.3.26` until the next weekly release upload. - [ ] Update Hugging Face organization/model cards. The `FunAudioLLM/Fun-ASR-MLT-Nano-2512` README update is complete: HF PR [#5](https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-2512/discussions/5) merged commit `3dd802bfb8a05dadc440c9d9fc84ecfc1e15c0ff`, and a 2026-07-23 live Hub download confirmed `main` now includes `python -m pip install -U "funasr>=1.3.26"` before optional demo-repo setup. Prepared README patches for `FunAudioLLM/Fun-ASR-Nano-2512` and `FunAudioLLM/SenseVoiceSmall` would add the same `funasr>=1.3.26` install floor, but the current `langgz` token still cannot write those model cards: direct upload fails with `403 Forbidden: pass create_pr=1`, and the Hub's suggested `--create-pr` path also fails at `preupload/main?create_pr=1` with `403 Forbidden: Authorization error`. Retry after model-card preupload/write permission is granted on those two repos. -- [ ] Update ModelScope model cards and demos. +- [ ] Update ModelScope model cards and demos. On 2026-07-23, `scripts/sync_modelscope_model_cards.py` was added to generate and optionally upload current ModelScope README updates for `FunAudioLLM/Fun-ASR-Nano-2512` (`README.md`, `README_zh.md`) and `iic/SenseVoiceSmall` (`README.md`). The prepared cards add `python -m pip install -U "funasr>=1.3.26" modelscope` and `torch.cuda.is_available()` CUDA-wheel verification guidance, matching the clean H100 quickstart evidence. Prepared files are under `/cpfs_speech/user/zhifu.gzf/.cache/funasr-ops/modelscope-card-sync-20260723/`. A live upload attempt with the existing 32-byte `.modelscope/credentials/session` failed: SDK uploads returned HTTP 400 from the ModelScope LFS batch API, and CLI upload reported `No credentials found`; the remaining blocker is a real ModelScope access token from `https://modelscope.cn/my/myaccesstoken`. Do not mark complete until those three ModelScope README files are uploaded and re-downloaded from the live model cards. - [x] Update homepage hero and docs entry points. On 2026-07-23, the live homepage entry points were audited and added to the public website contract. [`https://www.funasr.com/`](https://www.funasr.com/) visibly leads with the Chinese industrial speech-recognition hero, links `/donors.html`, and exposes the OpenAI-compatible `/v1/audio/transcriptions` and `vLLM` deployment path. [`https://www.funasr.com/en/`](https://www.funasr.com/en/) exposes `Industrial Speech Recognition`, `OpenAI-compatible`, `/v1/audio/transcriptions`, and `vLLM`. `scripts/check_funasr_website_static.py` now patrols both homepages plus the ecosystem, donor, CLI tutorial, llama.cpp landing, and llama.cpp blog pages; the live check prints `funasr.com static page contract passed for 11 pages`. ### Week 4: Distribution and feedback diff --git a/scripts/sync_modelscope_model_cards.py b/scripts/sync_modelscope_model_cards.py new file mode 100755 index 000000000..a7e216ac0 --- /dev/null +++ b/scripts/sync_modelscope_model_cards.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +"""Prepare and optionally upload current ModelScope model-card guidance. + +The script keeps high-traffic ModelScope cards aligned with the FunASR GitHub +README and PyPI release guidance. By default it writes patched README files to a +local output directory. Use --upload with a ModelScope access token to publish +only those README files back to their model repositories. +""" + +from __future__ import annotations + +import argparse +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable + + +DEFAULT_SOURCE_ROOT = Path( + "/cpfs_speech/user/zhifu.gzf/.cache/funasr-ops/modelscope-card-audit-20260723/models" +) +DEFAULT_OUTPUT_ROOT = Path( + "/cpfs_speech/user/zhifu.gzf/.cache/funasr-ops/modelscope-card-sync-20260723" +) + + +@dataclass(frozen=True) +class CardPatch: + repo_id: str + source: Path + path_in_repo: str + replacements: tuple[tuple[str, str], ...] + markers: tuple[str, ...] + + def output_path(self, output_root: Path) -> Path: + return output_root / self.repo_id.replace("/", "__") / self.path_in_repo + + +def _patches(source_root: Path) -> tuple[CardPatch, ...]: + nano_en_env_old = """# Environment Setup 🐍 + +```shell +git clone https://github.com/FunAudioLLM/Fun-ASR.git +cd Fun-ASR +pip install -r requirements.txt +```""" + nano_en_env_new = """# Environment Setup 🐍 + +Use FunASR v1.3.26 or newer for the current ModelScope hub path, OpenAI-compatible server, and vLLM fallback fixes: + +```shell +python -m pip install -U "funasr>=1.3.26" modelscope +``` + +For GPU inference, install the PyTorch / torchaudio CUDA wheels that match your driver from [pytorch.org](https://pytorch.org/get-started/locally/) and verify CUDA before using `device="cuda"`: + +```python +import torch +print(torch.__version__, torch.version.cuda, torch.cuda.is_available()) +``` + +Optional demo repository setup: + +```shell +git clone https://github.com/FunAudioLLM/Fun-ASR.git +cd Fun-ASR +pip install -r requirements.txt +```""" + nano_zh_env_old = """# 环境安装 🐍 + +```shell +git clone https://github.com/FunAudioLLM/Fun-ASR.git +cd Fun-ASR +pip install -r requirements.txt +```""" + nano_zh_env_new = """# 环境安装 🐍 + +推荐使用 FunASR v1.3.26 或更新版本,以获得当前 ModelScope hub 路径、OpenAI 兼容服务和 vLLM fallback 修复: + +```shell +python -m pip install -U "funasr>=1.3.26" modelscope +``` + +GPU 推理前,请按 [PyTorch 官网](https://pytorch.org/get-started/locally/)安装与你驱动匹配的 PyTorch / torchaudio CUDA wheel,并先确认 CUDA 可见: + +```python +import torch +print(torch.__version__, torch.version.cuda, torch.cuda.is_available()) +``` + +如需运行 demo 仓库,可再执行: + +```shell +git clone https://github.com/FunAudioLLM/Fun-ASR.git +cd Fun-ASR +pip install -r requirements.txt +```""" + sensevoice_env_old = """推理之前,请务必更新funasr与modelscope版本 + +```shell +pip install -U funasr modelscope +```""" + sensevoice_env_new = """推理之前,请务必更新 funasr 与 modelscope 版本。推荐使用 FunASR v1.3.26 或更新版本,以获得当前命令行、OpenAI 兼容服务、字幕输出和 ModelScope hub 路径修复: + +```shell +python -m pip install -U "funasr>=1.3.26" modelscope +``` + +GPU 推理前,请按 [PyTorch 官网](https://pytorch.org/get-started/locally/)安装与你驱动匹配的 PyTorch / torchaudio CUDA wheel,并先确认 CUDA 可见: + +```python +import torch +print(torch.__version__, torch.version.cuda, torch.cuda.is_available()) +```""" + return ( + CardPatch( + repo_id="FunAudioLLM/Fun-ASR-Nano-2512", + source=source_root / "FunAudioLLM--Fun-ASR-Nano-2512/snapshots/master/README.md", + path_in_repo="README.md", + replacements=( + (nano_en_env_old, nano_en_env_new), + ( + "## Quick start via ModelScope\n```shell\npip install -U funasr modelscope\n```", + "## Quick start via ModelScope\n```shell\npython -m pip install -U \"funasr>=1.3.26\" modelscope\n```", + ), + ), + markers=("funasr>=1.3.26", "torch.cuda.is_available()", "Quick start via ModelScope"), + ), + CardPatch( + repo_id="FunAudioLLM/Fun-ASR-Nano-2512", + source=source_root / "FunAudioLLM--Fun-ASR-Nano-2512/snapshots/master/README_zh.md", + path_in_repo="README_zh.md", + replacements=((nano_zh_env_old, nano_zh_env_new),), + markers=("funasr>=1.3.26", "torch.cuda.is_available()", "环境安装"), + ), + CardPatch( + repo_id="iic/SenseVoiceSmall", + source=source_root / "iic--SenseVoiceSmall/snapshots/master/README.md", + path_in_repo="README.md", + replacements=( + (sensevoice_env_old, sensevoice_env_new), + ( + "#安装ModelScope\npip install modelscope", + "# 安装 ModelScope 与 FunASR\npython -m pip install -U \"funasr>=1.3.26\" modelscope", + ), + ), + markers=("funasr>=1.3.26", "torch.cuda.is_available()", "OpenAI 兼容"), + ), + ) + + +def prepare_cards(source_root: Path, output_root: Path) -> list[CardPatch]: + prepared: list[CardPatch] = [] + for patch in _patches(source_root): + if not patch.source.exists(): + raise FileNotFoundError(f"missing source card: {patch.source}") + text = patch.source.read_text() + for old, new in patch.replacements: + if old not in text: + raise ValueError(f"expected text not found in {patch.source}: {old[:80]!r}") + text = text.replace(old, new) + missing = [marker for marker in patch.markers if marker not in text] + if missing: + raise ValueError(f"prepared {patch.repo_id}:{patch.path_in_repo} missing {missing}") + destination = patch.output_path(output_root) + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(text) + prepared.append(patch) + return prepared + + +def upload_cards(prepared: Iterable[CardPatch], output_root: Path, token: str) -> None: + try: + from modelscope.hub.api import HubApi + except ImportError as exc: # pragma: no cover - depends on ops environment + raise SystemExit("Install modelscope before using --upload") from exc + + api = HubApi() + for patch in prepared: + local_path = patch.output_path(output_root) + print(f"uploading {patch.repo_id}:{patch.path_in_repo}") + api.upload_file( + repo_id=patch.repo_id, + path_or_fileobj=str(local_path), + path_in_repo=patch.path_in_repo, + repo_type="model", + token=token, + commit_message="Update FunASR quickstart guidance", + commit_description=( + "Sync model-card install guidance to funasr>=1.3.26 and add " + "CUDA wheel verification notes." + ), + revision="master", + disable_tqdm=True, + ) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source-root", type=Path, default=DEFAULT_SOURCE_ROOT) + parser.add_argument("--output-root", type=Path, default=DEFAULT_OUTPUT_ROOT) + parser.add_argument("--upload", action="store_true", help="upload prepared README files to ModelScope") + parser.add_argument("--token-file", type=Path, help="ModelScope access token file for --upload") + parser.add_argument("--token", help="ModelScope access token for --upload") + return parser.parse_args() + + +def main() -> int: + args = parse_args() + prepared = prepare_cards(args.source_root, args.output_root) + for patch in prepared: + print(f"prepared {patch.repo_id}:{patch.path_in_repo} -> {patch.output_path(args.output_root)}") + if args.upload: + token = args.token or (args.token_file.read_text().strip() if args.token_file else "") + if not token: + raise SystemExit("--upload requires --token or --token-file") + upload_cards(prepared, args.output_root, token) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_growth_plan_doc.py b/tests/test_growth_plan_doc.py index 165d0cd10..6db39833d 100644 --- a/tests/test_growth_plan_doc.py +++ b/tests/test_growth_plan_doc.py @@ -208,6 +208,28 @@ def test_growth_plan_records_sharp_github_release_focus(): assert "[ ] Create a GitHub release focused on one sharp value proposition" not in text +def test_growth_plan_records_modelscope_card_sync_blocker(): + text = PLAN.read_text() + + required_markers = [ + "[ ] Update ModelScope model cards and demos", + "`scripts/sync_modelscope_model_cards.py`", + "`FunAudioLLM/Fun-ASR-Nano-2512`", + "`README.md`, `README_zh.md`", + "`iic/SenseVoiceSmall`", + "`python -m pip install -U \"funasr>=1.3.26\" modelscope`", + "`torch.cuda.is_available()`", + "modelscope-card-sync-20260723", + "HTTP 400 from the ModelScope LFS batch API", + "`No credentials found`", + "https://modelscope.cn/my/myaccesstoken", + ] + for marker in required_markers: + assert marker in text + + assert (ROOT / "scripts" / "sync_modelscope_model_cards.py").exists() + + def test_growth_plan_records_pypi_description_audit(): text = PLAN.read_text()