Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/community_growth_20k.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down Expand Up @@ -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. |
Expand Down
78 changes: 30 additions & 48 deletions scripts/check_funasr_website_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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"(?<![\w/])openai/sensevoice(?![\w./+-])"),
(
"http://127.0.0.1:8000/v1",
r"(?<![\w/])http://127\.0\.0\.1:8000/v1(?![\w/?#.:+-])",
),
("FunASR 官方插件 0.1.1", r"FunASR 官方插件 0\.1\.1(?![\w.+-])"),
("最大 25 MB 音频上传", r"最大 (?<!\d)25 MB 音频上传"),
),
Expand All @@ -75,17 +78,22 @@ class StaticAssetContract:
"https://github.com/0xShug0/audio.cpp/blob/1778b23a5f6a4951c788e4bb0e7baa04f20012a2/docs/models/fun_asr_nano.md",
"https://github.com/RVC-Boss/GPT-SoVITS/pull/2824",
),
forbidden=("16K+",),
forbidden=("16K+", "custom_openai", "openai/FunAudioLLM/SenseVoiceSmall"),
),
f"{BASE_URL}/en/ecosystem.html": PageContract(
required=(
"36K+",
"37K+",
"/en/donors.html",
"LiteLLM",
"custom_openai",
"54.3K stars",
),
visible_required=("api_base",),
visible_patterns=(
("openai/sensevoice", r"(?<![\w/])openai/sensevoice(?![\w./+-])"),
(
"http://127.0.0.1:8000/v1",
r"(?<![\w/])http://127\.0\.0\.1:8000/v1(?![\w/?#.:+-])",
),
("FunASR plugin 0.1.1", r"FunASR plugin 0\.1\.1(?![\w.+-])"),
("25 MB uploads", r"(?<!\d)25 MB uploads"),
),
Expand All @@ -97,7 +105,7 @@ class StaticAssetContract:
"https://github.com/0xShug0/audio.cpp/blob/1778b23a5f6a4951c788e4bb0e7baa04f20012a2/docs/models/fun_asr_nano.md",
"https://github.com/RVC-Boss/GPT-SoVITS/pull/2824",
),
forbidden=("16K+",),
forbidden=("16K+", "custom_openai", "openai/FunAudioLLM/SenseVoiceSmall"),
),
f"{BASE_URL}/donors.html": PageContract(
required=(
Expand Down Expand Up @@ -444,9 +452,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}
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"):
Expand All @@ -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"])
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -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]:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down
Loading
Loading