diff --git a/docs/community_growth_20k.md b/docs/community_growth_20k.md index 027fa747e..ca210fb00 100644 --- a/docs/community_growth_20k.md +++ b/docs/community_growth_20k.md @@ -266,7 +266,7 @@ High-star feature requests and roadmap issues are earlier in the funnel than PRs | `deepset-ai/haystack-core-integrations#3572` FunASR-Haystack Python 3.14 installation | Removes an installation scare in Haystack's integration catalog that can prevent new users from trying FunASR | A current Windows CPython 3.14 resolver check succeeds with `funasr 1.3.14`, `umap-learn 0.5.12`, `pynndescent 0.6.0`, `numba 0.66.0`, and `llvmlite 0.48.0`; the reported `llvmlite 0.36.0` path is therefore more consistent with a stale lock or lagging package index than the current dependency graph. Diagnostic commands and requested lock/index evidence are at https://github.com/deepset-ai/haystack-core-integrations/issues/3572#issuecomment-4965365012. Do not change core packaging until the reporter provides a current failing resolution. | | `crewAIInc/crewAI#5983` FunASR for voice-enabled agents | Routes a 55k-star multi-agent framework toward a provider-neutral voice command path where FunASR/SenseVoice can be a local OpenAI-compatible transcription backend | LauraGPT revived the stale broad request with a concrete `voice.stt.*` config, multipart `/v1/audio/transcriptions` contract, mock endpoint test shape, and agent-command handoff at https://github.com/crewAIInc/crewAI/issues/5983#issuecomment-4901293351. Watch for maintainer direction before opening a code PR. | | `Significant-Gravitas/AutoGPT#13347` FunASR as an open-source STT backend | Keeps the 185k-star AutoGPT voice-input discussion anchored on a generic OpenAI-compatible transcription contract rather than a heavyweight FunASR-only dependency | LauraGPT mapped the existing hard-coded Copilot Whisper route, then opened PR `Significant-Gravitas/AutoGPT#13500` and linked it back at https://github.com/Significant-Gravitas/AutoGPT/issues/13347#issuecomment-4908286807. The PR later merged, so keep this as a completed high-visibility OpenAI-compatible STT contract win rather than an active operator item. | -| `modelscope/FunASR#3334/#3350/#3361/#3362/#3363/#3366/#3398` owned conversion and link hygiene | Protects owned website, docs, runtime, model_zoo, and first-run troubleshooting surfaces that already send qualified traffic to the four repositories | Merged on 2026-07-23 and hardened again after the donor-page and troubleshooting reviews. `scripts/check_funasr_website_static.py` now patrols 14 public pages across home, ecosystem, donor, CLI tutorial, llama.cpp landing, llama.cpp blog, and launch-story surfaces. It requires the current `35K+` ecosystem proof, visible donor copy saying donations buy servers and the `www.funasr.com` domain, LiteLLM `custom_openai` routing, `funasr >= 1.3.26` CLI guidance, and `runtime-llamacpp-v0.1.9` download paths, including the Windows Vulkan ZIP, while forbidding stale `16K+`, `1.3.10`, and `runtime-llamacpp-v0.1.1` regressions. PRs #3361-#3363 refreshed high-traffic runtime download commands, package/help metadata, model_zoo Hugging Face links, and verified core ModelScope entries from stale `alibaba-damo-academy` / `damo` paths to current `modelscope/FunASR` / `iic` pages. `modelscope/FunASR#3366` then added README-linked install and deployment FAQs at [`docs/troubleshooting.md`](./troubleshooting.md) and [`docs/troubleshooting_zh.md`](./troubleshooting_zh.md), covering `torch` / `torchaudio`, ModelScope versus Hugging Face downloads, `funasr-server` `/v1/audio/transcriptions`, `WebSocket` realtime checks, `llama.cpp` / `GGUF`, and `Deployment Help` issue details. Post-merge validation passed the static contract suite, docs link guards, FAQ guards, real public website contract runs across all 14 pages, and HTTP 200 checks for updated owned/model links. | +| `modelscope/FunASR#3334/#3350/#3361/#3362/#3363/#3366/#3398` owned conversion and link hygiene | Protects owned website, docs, runtime, model_zoo, and first-run troubleshooting surfaces that already send qualified traffic to the four repositories | Historical July 2026 record: these PRs protected 14 public pages, donor usage copy, CLI installation guidance, runtime downloads, model links, and the README-linked [English](./troubleshooting.md) / [Chinese](./troubleshooting_zh.md) troubleshooting guides. Their original static, link, FAQ, and public HTTP checks passed, but the old LiteLLM `custom_openai` assertion was incorrect for transcription. The September 29 correction requires visible `openai/sensevoice` and an explicit self-hosted `api_base`, rejects the old route and checkpoint-ID example, and updates both ecosystem source pages. Source validation is separate from publication; it does not establish that the live website has deployed the correction. | | `modelscope/FunClip#188/#189`, `QwenAudio/Fun-ASR#151`, `QwenAudio/SenseVoice#327` four-repo community templates | Reduces maintainer follow-up and makes first external contributions easier to review across the four core repositories | Merged on 2026-07-23 after the four-repo template inventory. The four core repositories now have `CONTRIBUTING.md`, issue templates, and PR templates. FunClip gained bug/feature issue templates, a PR template, CONTRIBUTING.md, and template guards; Fun-ASR gained a PR template covering Nano, MLT, streaming, vLLM, GGUF, and HF/ModelScope routing; SenseVoice gained a PR template covering ASR, emotion/event tags, API/web UI/ONNX/Docker, GGUF, and model-card routing. Post-merge validation passed the new template guards plus each repo's lightweight docs/runtime smoke tests. | | `chatchat-space/Langchain-Chatchat#5479` FunASR/SenseVoice voice input | Puts FunASR in a 38k-star Chinese RAG/Agent app where local audio upload can feed existing Chat and knowledge-base flows | LauraGPT proposed an OpenAI-compatible ASR endpoint slice, including `asr.base_url`, multipart request shape, minimal `{ "text": "..." }` response, and mocked CI endpoint at https://github.com/chatchat-space/Langchain-Chatchat/issues/5479#issuecomment-4901293565. Monitor stale handling and maintainer appetite for a first audio-upload transcription path. | | `royshil/obs-localvocal#314` SenseVoice/Paraformer engine option | Opens an OBS live-captioning path where SenseVoice/Paraformer can be evaluated through a cleaner ASR-engine boundary instead of a Whisper-only runtime | Maintainer is interested but capacity-limited until mid/late August. LauraGPT mapped current Whisper coupling points and suggested a facade-only first refactor at https://github.com/royshil/obs-localvocal/issues/314#issuecomment-4909662013; wait for a refactor branch or review request before proposing Sherpa-ONNX/FunASR runtime code. | @@ -302,7 +302,7 @@ High-star feature requests and roadmap issues are earlier in the funnel than PRs | `zts212653/clowder-ai#1083` Qwen3-ASR service unification | Puts Qwen3-ASR into a user-facing local STT service slot by making it a `whisper-stt` backend variant instead of a separate, easier-to-misconfigure service | Merged on 2026-07-13 as `704bb735cae01be4bcd4089427bc6983c99a43a3` after fixes for Rosetta hardware detection, Qwen3-ASR install/server dispatch, async backend locking, temp WAV cleanup, and stale setup docs. The upstream service slot is already discoverable from the FunASR EN/ZH community integrations pages. | | `run-llama/llama_index#21958` FunASR endpoint reader | Puts FunASR behind a LlamaIndex reader for OpenAI-compatible transcription endpoints used in RAG and agent pipelines | LauraGPT rechecked head `07a8599deaebe5e7a559d62174e7a872870c2f7e` at https://github.com/run-llama/llama_index/pull/21958#issuecomment-4905345545; author acknowledged the shared-reader pytest caveat at https://github.com/run-llama/llama_index/pull/21958#issuecomment-4905384536. Keep the endpoint contract clear and avoid forcing local `funasr` dependencies into the main package. | | `run-llama/llama_index#21996` local FunASR reader | Gives LlamaIndex users a local SenseVoice/FunASR reader for private transcription workflows | Keep optional dependencies isolated, verify the reader does not affect default installs, and watch for maintainer guidance on package extras. | -| `BerriAI/litellm-docs#610` FunASR transcription proxy docs | Puts FunASR/SenseVoice behind LiteLLM's existing OpenAI-compatible `custom_openai` audio transcription route, reaching teams that already deploy LiteLLM gateways | Rebased across the upstream `docs/audio_transcription.md` conflict on 2026-07-23; head `1a70fa02` is mergeable with CodeRabbit, Vercel, and Vercel preview comments all green. The docs now keep upstream's `mock_testing_fallbacks` deprecation note for Proxy requests, use `funasr>=1.3.26`, preserve `api_base: http://localhost:8000/v1`, and avoid installing `vllm` in the basic setup path. Local validation passed `git diff --check`, content assertions, and `npm run build`; build warnings are existing site-wide Docusaurus broken links outside `/docs/audio_transcription`. Evidence posted at https://github.com/BerriAI/litellm-docs/pull/610#issuecomment-5049681978. | +| `BerriAI/litellm-docs#610` FunASR transcription proxy docs | Routes self-hosted FunASR/SenseVoice transcription through LiteLLM's `openai/sensevoice` provider/model with an explicit backend `api_base`, reaching teams that deploy LiteLLM gateways | At 2026-09-29 02:26 UTC, signed head `64ab922e497ce0f181565e31c9b29025f48b01e6` includes 883 upstream main commits and corrects the unsupported `custom_openai` transcription provider, checkpoint-ID model name, and weak Proxy master-key example. Native LiteLLM 1.103.0 / OpenAI SDK transport checks, FunASR 1.4.16 ASGI tests with inference stubbed, and the exact-main master-key guard passed 14 focused tests; the documentation build produced 1,357 HTML pages and desktop/mobile browser checks passed. No speech model was downloaded or inferred, and a live Proxy was not booted. The PR remains open: hosted docs checks require approval with zero jobs run, and Vercel deployment is pending. The green July checks and successful preview-comment integration are not evidence of a current successful deployment. [Current PR and validation](https://github.com/BerriAI/litellm-docs/pull/610). | | `mem0ai/mem0#5571` optional FunASR transcription helper | Adds local FunASR transcription to a high-star memory layer used by agent builders | Keep FunASR optional and make examples clear about local model downloads; the current failing Vercel preview is a Mem0 team authorization gate, not a FunASR code failure, so wait for maintainer-side preview access or review. | | `Significant-Gravitas/AutoGPT#13500` configurable transcription endpoints | Opens a path from AutoGPT's high-visibility agent platform to local FunASR/SenseVoice gateways through the existing OpenAI-compatible transcription route | Merged on 2026-07-22 after the review and status gates cleared. The merged path keeps transcription provider configuration generic, so FunASR/SenseVoice can fit through an OpenAI-compatible endpoint without adding a heavyweight toolkit dependency to AutoGPT. | | `Vexa-ai/vexa#810` custom STT endpoint documentation | Makes bring-your-own OpenAI-compatible transcription servers visible in a meeting-bot docs surface, giving SenseVoiceSmall and Fun-ASR-Nano users a clean path to point Vexa at private FunASR/SenseVoice gateways | Delivered via Vexa's community-batch release `#867`, merged to `main` as `e617d839` and shipped in `v0.12.16` with `docs/docs/how-to/custom-stt.mdx`. Maintainer confirmed on 2026-07-22 that #810 is superseded by the delivered release content, so treat the closed #810 PR as completed evidence rather than an active merge-card gate. | diff --git a/scripts/check_funasr_website_static.py b/scripts/check_funasr_website_static.py index de4d43493..621bbecb4 100755 --- a/scripts/check_funasr_website_static.py +++ b/scripts/check_funasr_website_static.py @@ -16,9 +16,7 @@ BASE_URL = "https://www.funasr.com" -DONOR_PAGE_URLS = frozenset( - (f"{BASE_URL}/donors.html", f"{BASE_URL}/en/donors.html") -) +DONOR_PAGE_URLS = frozenset((f"{BASE_URL}/donors.html", f"{BASE_URL}/en/donors.html")) @dataclass(frozen=True) @@ -57,13 +55,18 @@ class StaticAssetContract: ), f"{BASE_URL}/ecosystem.html": PageContract( required=( - "36K+", + "37K+", "/donors.html", "LiteLLM", - "custom_openai", "54.3K stars", ), + visible_required=("api_base",), visible_patterns=( + ("openai/sensevoice", r"(? None tag = tag.lower() attributes = {name.lower(): value for name, value in attrs} hidden = ( - self._parent_hidden - or tag in _HIDDEN_TAGS - or self._has_hidden_attribute(attributes) + self._parent_hidden or tag in _HIDDEN_TAGS or self._has_hidden_attribute(attributes) ) if not hidden: if tag == "img" and attributes.get("src"): @@ -460,9 +466,7 @@ def handle_startendtag(self, tag: str, attrs: list[tuple[str, str | None]]) -> N tag = tag.lower() attributes = {name.lower(): value for name, value in attrs} hidden = ( - self._parent_hidden - or tag in _HIDDEN_TAGS - or self._has_hidden_attribute(attributes) + self._parent_hidden or tag in _HIDDEN_TAGS or self._has_hidden_attribute(attributes) ) if not hidden and tag == "img" and attributes.get("src"): self.images.add(attributes["src"]) @@ -541,10 +545,7 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None tag = tag.lower() attributes = {name.lower(): value for name, value in attrs} if not self._stack: - is_navigation = ( - tag == "div" - and "nav-links" in (attributes.get("class") or "").split() - ) + is_navigation = tag == "div" and "nav-links" in (attributes.get("class") or "").split() if not is_navigation: return self.found_navigation = True @@ -619,9 +620,7 @@ def validate_navigation(pages: dict[str, str]) -> list[str]: directory_links: list[DirectoryLink] = [] for link in navigation_links: target = urlparse(urljoin(url, link.href)) - is_language_toggle = ( - target.netloc == "www.funasr.com" and target.path == alternate_path - ) + is_language_toggle = target.netloc == "www.funasr.com" and target.path == alternate_path is_github_action = target.netloc == "github.com" if not is_language_toggle and not is_github_action: directory_links.append(link) @@ -633,21 +632,14 @@ def validate_navigation(pages: dict[str, str]) -> list[str]: expected_links = [link for link in navigation_links if link.href == expected] if not any(link.label in expected_labels for link in expected_links): label_description = "` or `".join(expected_labels) - failures.append( - f"{url}: `{expected}` must use visible label `{label_description}`" - ) - if expected in links and ( - not directory_links or directory_links[-1].href != expected - ): + failures.append(f"{url}: `{expected}` must use visible label `{label_description}`") + if expected in links and (not directory_links or directory_links[-1].href != expected): failures.append(f"{url}: `{expected}` must be the last directory link") if links.count(expected) > 1: - failures.append( - f"{url}: directory navigation contains duplicate `{expected}`" - ) + failures.append(f"{url}: directory navigation contains duplicate `{expected}`") if wrong_language in links: failures.append( - f"{url}: directory navigation contains wrong-language " - f"`{wrong_language}`" + f"{url}: directory navigation contains wrong-language " f"`{wrong_language}`" ) return failures @@ -723,9 +715,7 @@ def validate_assets(assets: dict[str, bytes]) -> list[str]: return failures -def _fetch_bytes( - url: str, timeout: float, retries: int, require_exact_url: bool = False -) -> bytes: +def _fetch_bytes(url: str, timeout: float, retries: int, require_exact_url: bool = False) -> bytes: last_error: Exception | None = None for attempt in range(retries + 1): try: @@ -741,17 +731,13 @@ def _fetch_bytes( raise last_error -def _fetch_url( - url: str, timeout: float, retries: int, require_exact_url: bool = False -) -> str: +def _fetch_url(url: str, timeout: float, retries: int, require_exact_url: bool = False) -> str: return _fetch_bytes( url, timeout=timeout, retries=retries, require_exact_url=require_exact_url, - ).decode( - "utf-8", errors="replace" - ) + ).decode("utf-8", errors="replace") def fetch_pages(timeout: float, retries: int = 3) -> dict[str, str]: @@ -790,9 +776,7 @@ def main(argv: list[str] | None = None) -> int: parser.add_argument("--retries", type=int, default=3) args = parser.parse_args(argv) - navigation_pages = fetch_navigation_pages( - timeout=args.timeout, retries=args.retries - ) + navigation_pages = fetch_navigation_pages(timeout=args.timeout, retries=args.retries) pages = {} for url in PAGE_CONTRACTS: if url in DONOR_PAGE_URLS: @@ -810,9 +794,7 @@ def main(argv: list[str] | None = None) -> int: all_navigation_pages.update(pages) failures = validate_pages(pages) failures.extend(validate_navigation(all_navigation_pages)) - failures.extend( - validate_assets(fetch_assets(timeout=args.timeout, retries=args.retries)) - ) + failures.extend(validate_assets(fetch_assets(timeout=args.timeout, retries=args.retries))) if failures: print("funasr.com static page contract failed:", file=sys.stderr) for failure in failures: diff --git a/tests/test_check_funasr_website_static.py b/tests/test_check_funasr_website_static.py index 0c635ec67..a99eb9e16 100644 --- a/tests/test_check_funasr_website_static.py +++ b/tests/test_check_funasr_website_static.py @@ -50,7 +50,7 @@ def test_website_contract_accepts_current_public_copy(): vLLM Acceleration """, "https://www.funasr.com/ecosystem.html": """ -
36K+
+
37K+
功德榜 LiteLLM FunClip v2.1.1 @@ -58,7 +58,7 @@ def test_website_contract_accepts_current_public_copy(): merged PR pinned guide merged Transformers fix -
custom_openai
+
openai/sensevoice api_base: http://127.0.0.1:8000/v1
54.3K stars
FunASR 官方插件 0.1.1 @@ -66,7 +66,7 @@ def test_website_contract_accepts_current_public_copy():
支持最大 25 MB 音频上传
""", "https://www.funasr.com/en/ecosystem.html": """ -
36K+
+
37K+
Thanks LiteLLM FunClip v2.1.1 @@ -74,7 +74,7 @@ def test_website_contract_accepts_current_public_copy(): merged PR pinned guide merged Transformers fix -
custom_openai
+
openai/sensevoice api_base: http://127.0.0.1:8000/v1
54.3K stars
FunASR plugin 0.1.1 @@ -277,9 +277,22 @@ def test_homepage_contract_accepts_current_product_site_build(tmp_path, monkeypa assert checker.validate_pages(pages) == [] -def test_llamacpp_comparison_contract_accepts_current_product_site_build( - tmp_path, monkeypatch -): +def test_ecosystem_contract_accepts_current_product_site_build(tmp_path, monkeypatch): + checker = _load_module() + builder = _load_product_site_builder() + builder.build(tmp_path) + routes = ("ecosystem.html", "en/ecosystem.html") + pages = { + f"https://www.funasr.com/{route}": (tmp_path / route).read_text(encoding="utf-8") + for route in routes + } + monkeypatch.setattr( + checker, "PAGE_CONTRACTS", {url: checker.PAGE_CONTRACTS[url] for url in pages} + ) + assert checker.validate_pages(pages) == [] + + +def test_llamacpp_comparison_contract_accepts_current_product_site_build(tmp_path, monkeypatch): checker = _load_module() builder = _load_product_site_builder() builder.build(tmp_path) @@ -288,9 +301,7 @@ def test_llamacpp_comparison_contract_accepts_current_product_site_build( "en/blog/funasr-llama-cpp-whisper-cpp-alternative.html", ) pages = { - f"https://www.funasr.com/{route}": (tmp_path / route).read_text( - encoding="utf-8" - ) + f"https://www.funasr.com/{route}": (tmp_path / route).read_text(encoding="utf-8") for route in routes } monkeypatch.setattr( @@ -326,7 +337,7 @@ def test_ecosystem_contract_requires_current_release_and_native_runtime(): "https://www.funasr.com/en/ecosystem.html", ): contract = checker.PAGE_CONTRACTS[url] - assert "36K+" in contract.required + assert "37K+" in contract.required assert evidence_links <= set(contract.required_links) @@ -336,10 +347,10 @@ def test_ecosystem_contract_requires_live_dify_marketplace(): pages = { url: """ -
36K+
+
37K+
Thanks LiteLLM -
custom_openai
+
openai/sensevoice api_base: http://127.0.0.1:8000/v1
54.3K stars
Dify @@ -350,8 +361,7 @@ def test_ecosystem_contract_requires_live_dify_marketplace(): assert any( url in failure - and "missing link `https://marketplace.dify.ai/plugin/langgenius/funasr`" - in failure + and "missing link `https://marketplace.dify.ai/plugin/langgenius/funasr`" in failure for failure in failures ) assert any( @@ -359,15 +369,14 @@ def test_ecosystem_contract_requires_live_dify_marketplace(): for failure in failures ) assert any( - url in failure and "visible text missing `25 MB uploads`" in failure - for failure in failures + url in failure and "visible text missing `25 MB uploads`" in failure for failure in failures ) def _valid_english_ecosystem_html(): return """ -
36K+
+
37K+
Thanks LiteLLM FunClip v2.1.1 @@ -375,7 +384,7 @@ def _valid_english_ecosystem_html(): merged PR pinned guide merged Transformers fix -
custom_openai
+
openai/sensevoice api_base: http://127.0.0.1:8000/v1
54.3K stars
FunASR plugin 0.1.1 @@ -385,6 +394,75 @@ def _valid_english_ecosystem_html(): """ +@pytest.fixture(params=["en/", ""]) +def litellm_ecosystem_page(request): + language = request.param + html = _valid_english_ecosystem_html() + if not language: + html = ( + html.replace("/en/donors.html", "/donors.html") + .replace("FunASR plugin 0.1.1", "FunASR 官方插件 0.1.1") + .replace("supports 25 MB uploads", "支持最大 25 MB 音频上传") + ) + return f"https://www.funasr.com/{language}ecosystem.html", html + + +def test_ecosystem_accepts_litellm_transcription_route(litellm_ecosystem_page): + checker = _load_module() + url, html = litellm_ecosystem_page + assert not [ + failure for failure in checker.validate_pages({url: html}) if failure.startswith(url + ":") + ] + + +@pytest.mark.parametrize("legacy", ["custom_openai", "openai/FunAudioLLM/SenseVoiceSmall"]) +def test_ecosystem_rejects_legacy_litellm_routing(litellm_ecosystem_page, legacy): + checker = _load_module() + url, html = litellm_ecosystem_page + failures = checker.validate_pages({url: html + f"{legacy}"}) + assert f"{url}: forbidden `{legacy}`" in failures + + +@pytest.mark.parametrize("suffix", ["Small", "/model", "-extra"]) +def test_ecosystem_rejects_wrong_litellm_model_alias(litellm_ecosystem_page, suffix): + checker = _load_module() + url, html = litellm_ecosystem_page + html = html.replace("openai/sensevoice", "openai/sensevoice" + suffix) + failures = checker.validate_pages({url: html}) + assert f"{url}: visible text missing `openai/sensevoice`" in failures + + +@pytest.mark.parametrize("required", ["openai/sensevoice", "api_base", "http://127.0.0.1:8000/v1"]) +def test_ecosystem_requires_visible_litellm_configuration(litellm_ecosystem_page, required): + checker = _load_module() + url, html = litellm_ecosystem_page + html = html.replace(required, f"") + failures = checker.validate_pages({url: html}) + assert f"{url}: visible text missing `{required}`" in failures + + +@pytest.mark.parametrize("suffix", ["0", "/audio/transcriptions", "?route=other"]) +def test_ecosystem_rejects_wrong_litellm_api_base(litellm_ecosystem_page, suffix): + checker = _load_module() + url, html = litellm_ecosystem_page + api_base = "http://127.0.0.1:8000/v1" + html = html.replace(api_base, api_base + suffix) + failures = checker.validate_pages({url: html}) + assert f"{url}: visible text missing `{api_base}`" in failures + + +def test_ecosystem_source_uses_working_litellm_configuration(litellm_ecosystem_page): + checker = _load_module() + url, _ = litellm_ecosystem_page + relative = url.removeprefix("https://www.funasr.com/") + source = ROOT / "web-pages" / "product-site" / "legacy" / relative + visible = checker.extract_visible_text(source.read_text()) + assert "openai/sensevoice" in visible + assert "api_base" in visible + assert "http://127.0.0.1:8000/v1" in visible + assert "custom_openai" not in visible + + def test_ecosystem_contract_rejects_longer_dify_version(): checker = _load_module() url = "https://www.funasr.com/en/ecosystem.html" @@ -393,9 +471,7 @@ def test_ecosystem_contract_rejects_longer_dify_version(): failures = checker.validate_pages(pages) - assert any( - url in failure and "FunASR plugin 0.1.1" in failure for failure in failures - ) + assert any(url in failure and "FunASR plugin 0.1.1" in failure for failure in failures) def test_ecosystem_contract_rejects_dify_version_suffixes(): @@ -408,9 +484,7 @@ def test_ecosystem_contract_rejects_dify_version_suffixes(): failures = checker.validate_pages(pages) - assert any( - url in failure and "FunASR plugin 0.1.1" in failure for failure in failures - ) + assert any(url in failure and "FunASR plugin 0.1.1" in failure for failure in failures) def test_ecosystem_contract_rejects_larger_dify_upload_limit(): @@ -475,8 +549,7 @@ def test_website_contract_includes_funclip_v210_launch(): in checker.PAGE_CONTRACTS ) assert ( - "https://www.funasr.com/img/funclip-v2-1-0-interface.jpg" - in checker.STATIC_ASSET_CONTRACTS + "https://www.funasr.com/img/funclip-v2-1-0-interface.jpg" in checker.STATIC_ASSET_CONTRACTS ) @@ -523,17 +596,14 @@ def test_funclip_v210_contract_rejects_hidden_evidence_and_wrong_routes(): for failure in failures ) assert any( - url in failure - and "missing link `https://github.com/modelscope/FunClip`" in failure + url in failure and "missing link `https://github.com/modelscope/FunClip`" in failure for failure in failures ) assert any( - url in failure and "missing link `/en/donors.html`" in failure - for failure in failures + url in failure and "missing link `/en/donors.html`" in failure for failure in failures ) assert any( - url in failure - and "missing image `/img/funclip-v2-1-0-interface.jpg`" in failure + url in failure and "missing image `/img/funclip-v2-1-0-interface.jpg`" in failure for failure in failures ) @@ -573,13 +643,11 @@ def test_funclip_v210_contract_rejects_zero_area_links(): failures = checker.validate_pages(pages) assert any( - url in failure - and "missing link `https://github.com/modelscope/FunClip`" in failure + url in failure and "missing link `https://github.com/modelscope/FunClip`" in failure for failure in failures ) assert any( - url in failure and "missing link `/en/donors.html`" in failure - for failure in failures + url in failure and "missing link `/en/donors.html`" in failure for failure in failures ) @@ -606,8 +674,7 @@ def test_v1328_contract_rejects_language_swapped_body(): failures = checker.validate_pages(pages) assert any( - url in failure - and "visible text missing `精确合并源码通过 118 项聚焦回归测试`" in failure + url in failure and "visible text missing `精确合并源码通过 118 项聚焦回归测试`" in failure for failure in failures ) @@ -640,8 +707,7 @@ def test_v1328_contract_rejects_css_test_count_and_indirect_repo_link(): for failure in failures ) assert any( - url in failure - and "missing link `https://github.com/modelscope/FunASR`" in failure + url in failure and "missing link `https://github.com/modelscope/FunASR`" in failure for failure in failures ) @@ -670,14 +736,12 @@ def test_v1328_contract_requires_visible_stop_and_sensevoice_copy(): failures = checker.validate_pages(pages) assert any( - url in failure - and "visible text missing `including STOP final decode`" in failure + url in failure and "visible text missing `including STOP final decode`" in failure for failure in failures ) assert any( url in failure - and "visible text missing `SenseVoice users receive the subtitle alignment fix`" - in failure + and "visible text missing `SenseVoice users receive the subtitle alignment fix`" in failure for failure in failures ) @@ -725,16 +789,15 @@ def test_website_contract_reports_stale_runtime_and_star_copy(): pages = {url: "" for url in checker.PAGE_CONTRACTS} pages["https://www.funasr.com/ecosystem.html"] = "16K+" - pages[ - "https://www.funasr.com/blog/funasr-llama-cpp-whisper-cpp-alternative.html" - ] = "runtime-llamacpp-v0.1.1" + pages["https://www.funasr.com/blog/funasr-llama-cpp-whisper-cpp-alternative.html"] = ( + "runtime-llamacpp-v0.1.1" + ) pages["https://www.funasr.com/llama-cpp.html"] = "runtime-llamacpp-v0.1.1" failures = checker.validate_pages(pages) assert any( - "ecosystem.html" in failure and "forbidden `16K+`" in failure - for failure in failures + "ecosystem.html" in failure and "forbidden `16K+`" in failure for failure in failures ) assert any( "funasr-llama-cpp-whisper-cpp-alternative.html" in failure @@ -771,8 +834,7 @@ def test_website_contract_requires_visible_donor_usage_copy(): failures = checker.validate_pages(pages) assert any( - "donors.html" in failure - and "visible text missing `购买和维护服务器`" in failure + "donors.html" in failure and "visible text missing `购买和维护服务器`" in failure for failure in failures ) assert any( @@ -855,8 +917,7 @@ def test_navigation_contract_requires_donors_as_last_directory_link(): assert not any("ok.html" in failure for failure in failures) assert any( - "missing.html" in failure and "missing `/en/donors.html`" in failure - for failure in failures + "missing.html" in failure and "missing `/en/donors.html`" in failure for failure in failures ) assert any( "not-last.html" in failure and "must be the last directory link" in failure @@ -867,8 +928,7 @@ def test_navigation_contract_requires_donors_as_last_directory_link(): for failure in failures ) assert any( - "wrong-language.html" in failure - and "contains wrong-language `/donors.html`" in failure + "wrong-language.html" in failure and "contains wrong-language `/donors.html`" in failure for failure in failures ) assert not any("plain.html" in failure for failure in failures) @@ -903,23 +963,19 @@ def test_navigation_contract_requires_visible_correctly_labeled_donor_link(): failures = checker.validate_navigation(pages) assert any( - "hidden.html" in failure - and "missing `/donors.html`; link must be visible" in failure + "hidden.html" in failure and "missing `/donors.html`; link must be visible" in failure for failure in failures ) assert any( - "wrong-label.html" in failure - and "must use visible label `功德榜`" in failure + "wrong-label.html" in failure and "must use visible label `功德榜`" in failure for failure in failures ) assert any( - "hidden-label.html" in failure - and "missing `/donors.html`; link must be visible" in failure + "hidden-label.html" in failure and "missing `/donors.html`; link must be visible" in failure for failure in failures ) assert any( - "empty.html" in failure - and "missing `/en/donors.html`; link must be visible" in failure + "empty.html" in failure and "missing `/en/donors.html`; link must be visible" in failure for failure in failures ) diff --git a/web-pages/product-site/content/legacy-manifest.json b/web-pages/product-site/content/legacy-manifest.json index c89cc092c..c3c5c2373 100644 --- a/web-pages/product-site/content/legacy-manifest.json +++ b/web-pages/product-site/content/legacy-manifest.json @@ -53,7 +53,7 @@ "css/index.1682178c.css": "b5f6034ce886c25773928c114e4290d1c491a3d081677b488810198efd76b4e1", "decoder.js": "f2023a28036b1ff58e5bdabbb72ea427f8faaf60e89acd78ff183c313bdb0a2c", "donors.html": "eec6b789b29c27d372925ae0ad3e13d4c6e62978b3c38c4da9e3aec129f10934", - "ecosystem.html": "de151aebb8d807b21e719d59a54766f4998945310c97bbabf2ac31dae5c0a283", + "ecosystem.html": "a3cdaf0fefdc10fd51e8f09c3239e338528f4700e0037c2eb9556d650f6faef9", "en/blog/cantonese-speech-recognition.html": "b9bd146198206f781c37f341c370a690dc83cd7163334e34b4e7f65e69af8ff2", "en/blog/chinese-speech-recognition.html": "16b68bd2a1122236c580c62275002ae2c0875ca14e32686e88d3b5ccfa9f56ef", "en/blog/fun-asr-nano-guide.html": "1746c708bd0606188de4266d8255984dd9e14634af33c54ad65a5f05b767be1b", @@ -91,7 +91,7 @@ "en/blog/voice-activity-detection-python.html": "d12f5d0f4ae07c3434f6188d12f5603b8abb9578413b79133668638d1c5e18a7", "en/blog/which-funasr-model.html": "1207180aa929a3aa51db136c1098f4edc8f07405e2b56fe033998bf996f8ec7f", "en/donors.html": "eacb7f9434b1b29e13e9b0175bb9680556ee72ab4dbd155e68f9c912eb0212b1", - "en/ecosystem.html": "81cec9464db159189ef29df884b124c795fce01ef08bfaae03d982696de13701", + "en/ecosystem.html": "38356f6bdb5cd89988304be61f13707f4df9fa84c2a191701aaee475d7587902", "en/index.html": "d86effc3bf4d218eabd465cbbe026f62c98dd50110ddd58251d9d271a64882a2", "en/llama-cpp.html": "dbfbd125ebbf07f7d923c91b122818f8b09484ce54c1e80eba66a89d22dfb5b4", "en/models.html": "9806d01ba1807c2a6a88f7232102e185426fb8458bf51e1d7a8ce872352e1fd1", diff --git a/web-pages/product-site/legacy/ecosystem.html b/web-pages/product-site/legacy/ecosystem.html index 949c7e272..6b30b9be9 100644 --- a/web-pages/product-site/legacy/ecosystem.html +++ b/web-pages/product-site/legacy/ecosystem.html @@ -276,7 +276,7 @@

AI 平台与框架

LiteLLM
54.3K stars
-
OpenAI 兼容 AI Gateway。FunASR / SenseVoice 可通过 custom_openai 作为自托管语音转写端点接入。
+
OpenAI 兼容 AI Gateway。使用 openai/sensevoice,并显式设置 api_base: http://127.0.0.1:8000/v1,可接入同机自托管的 FunASR 转写服务。sensevoice 是服务模型别名,不是 checkpoint 仓库 ID;容器部署需使用容器内可访问的服务地址。配置文档 PR 尚待上游合并。
AI GatewayOpenAI 兼容
diff --git a/web-pages/product-site/legacy/en/ecosystem.html b/web-pages/product-site/legacy/en/ecosystem.html index 5f252de3d..acf97df66 100644 --- a/web-pages/product-site/legacy/en/ecosystem.html +++ b/web-pages/product-site/legacy/en/ecosystem.html @@ -277,7 +277,7 @@

AI Platforms & Frameworks

54.3K stars
-
OpenAI-compatible AI Gateway. FunASR / SenseVoice can be routed through custom_openai as a self-hosted transcription endpoint.
+
OpenAI-compatible AI Gateway. Use openai/sensevoice with an explicit api_base: http://127.0.0.1:8000/v1 for a self-hosted FunASR transcription server on the same host. sensevoice is a server model alias, not a checkpoint repository ID; containers need a backend address reachable from inside the container. The configuration docs PR is awaiting upstream merge.
AI GatewayOpenAI-compatible