Skip to content

feat: add funasr STT plugin (local FunASR / SenseVoice)#606

Open
LauraGPT wants to merge 6 commits into
GetStream:mainfrom
LauraGPT:add-funasr-stt-plugin
Open

feat: add funasr STT plugin (local FunASR / SenseVoice)#606
LauraGPT wants to merge 6 commits into
GetStream:mainfrom
LauraGPT:add-funasr-stt-plugin

Conversation

@LauraGPT

@LauraGPT LauraGPT commented Jun 22, 2026

Copy link
Copy Markdown

Adds a funasr STT plugin: a local, self-hosted speech-to-text backend powered by FunASR (SenseVoice / Fun-ASR-Nano / Paraformer). It is strong on Chinese, Cantonese, Japanese, Korean, and more; it runs on CPU or CUDA and requires no API key.

What it does

  • Implements STT(stt.STT, Warmable[Optional[AutoModel]]) with buffered offline transcription through the public Vision Agents audio path.
  • Loads a FunASR AutoModel during warmup; model inference runs off the event loop and emits final TranscriptResponse items with SenseVoice tags removed.
  • Defaults to iic/SenseVoiceSmall; callers can select FunAudioLLM/Fun-ASR-Nano-2512, Paraformer, or another FunASR-compatible model.

Repository integration

  • Registers plugins/funasr as a uv workspace member and source.
  • Adds the installable vision-agents[funasr] extra and locks the complete dependency graph.
  • Surfaces FunASR in the root README STT integration list.
  • Builds a wheel containing only the plugin package plus its metadata, and a matching source distribution.

Validation

Verified on exact head 8f71dc851d2c80f2c0a91608f33a9557a5904939, rebased onto 019edac7726987b1c5c18580e06b59d59387409c:

  • uv sync --all-extras --dev --frozen: 452 packages resolved; every workspace package, including vision-agents-plugins-funasr, built successfully.
  • FunASR unit tests: 4 passed against the real getstream.PcmData implementation.
  • ruff check . and ruff format --check .: passed across 536 files.
  • dev.py validate-extras: passed.
  • mypy: 120 core and 232 plugin source files passed.
  • Dependency resolution passed for Python 3.10 and 3.14.
  • Wheel and sdist build passed; wheel metadata contains funasr>=1.1.0 and vision-agents and contains only the FunASR package files.
  • Real CPU smoke through STT.warmup() -> process_audio() -> public output stream succeeded with iic/SenseVoiceSmall; the sample produced 开饭时间早上9点至下午5点。, language zh, with SenseVoice tags removed.
  • Broad non-integration plugin suite: 483 passed, 7 skipped. Its 9 Anthropic setup errors were reproduced 9/9 on a detached current-main environment and come from blockbuster rejecting Anthropic's credential-file os.stat; no FunASR test failed.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 265d41f4-47c6-4f00-b423-bd1ec7aa3cb7

📥 Commits

Reviewing files that changed from the base of the PR and between 8f71dc8 and 6f42e16.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • agents-core/pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • agents-core/pyproject.toml

📝 Walkthrough

Walkthrough

Adds a new plugins/funasr package for offline FunASR speech-to-text. It includes package and workspace configuration, an STT implementation with warmup, buffering, transcription, and shutdown behavior, public export wiring, tests for defaults and error paths, a runnable example, and README documentation.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
plugins/funasr/tests/test_funasr_stt.py (1)

5-25: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Organize tests in a test class.

Per path instructions, unit tests for a class should be kept in a test class. Wrap test_stt_construct() and test_stt_custom_params() in a TestSTT class.

Proposed refactor
 def test_stt_construct():
-    """The plugin constructs and exposes the expected defaults without loading a model."""
+class TestSTT:
+    def test_construct(self):
+        """The plugin constructs and exposes the expected defaults without loading a model."""
-    stt = funasr.STT()
-    assert stt.provider_name == "funasr"
-    assert stt.model_id == "iic/SenseVoiceSmall"
-    assert stt.language == "auto"
-    assert stt.device == "cpu"
-    assert stt.use_itn is True
-
-
-def test_stt_custom_params():
-    stt = funasr.STT(
-        model="FunAudioLLM/Fun-ASR-Nano-2512",
-        language="zh",
-        device="cuda",
-        use_itn=False,
-    )
-    assert stt.model_id == "FunAudioLLM/Fun-ASR-Nano-2512"
-    assert stt.language == "zh"
-    assert stt.device == "cuda"
-    assert stt.use_itn is False
+        stt = funasr.STT()
+        assert stt.provider_name == "funasr"
+        assert stt.model_id == "iic/SenseVoiceSmall"
+        assert stt.language == "auto"
+        assert stt.device == "cpu"
+        assert stt.use_itn is True
+
+    def test_custom_params(self):
+        stt = funasr.STT(
+            model="FunAudioLLM/Fun-ASR-Nano-2512",
+            language="zh",
+            device="cuda",
+            use_itn=False,
+        )
+        assert stt.model_id == "FunAudioLLM/Fun-ASR-Nano-2512"
+        assert stt.language == "zh"
+        assert stt.device == "cuda"
+        assert stt.use_itn is False

Source: Path instructions

plugins/funasr/example/funasr_example.py (1)

9-10: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add return type annotation to build_stt().

Per coding guidelines, use type annotations everywhere. The function should declare its return type.

Proposed fix
-def build_stt():
-    return funasr.STT(model="iic/SenseVoiceSmall", language="auto", device="cpu")
+def build_stt() -> funasr.STT:
+    return funasr.STT(model="iic/SenseVoiceSmall", language="auto", device="cpu")

Source: Coding guidelines

plugins/funasr/vision_agents/plugins/funasr/stt.py (1)

41-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add explicit return type annotations on public/private methods.

__init__, process_audio, and _process_buffer should declare -> None for consistent typing in this codebase.

Proposed fix
-    def __init__(
+    def __init__(
         self,
         model: str = "iic/SenseVoiceSmall",
         language: str = "auto",
         device: str = "cpu",
         use_itn: bool = True,
         client: Optional[AutoModel] = None,
-    ):
+    ) -> None:
-    async def process_audio(
+    async def process_audio(
         self,
         pcm_data: PcmData,
         participant: Participant,
-    ):
+    ) -> None:
-    async def _process_buffer(self, participant: Participant):
+    async def _process_buffer(self, participant: Participant) -> None:

As per coding guidelines, "Use type annotations everywhere."

Also applies to: 96-100, 142-143

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59d3e3e7-aa2a-4f96-8367-acb8e3eae5b7

📥 Commits

Reviewing files that changed from the base of the PR and between f98c82a and 21b8744.

📒 Files selected for processing (8)
  • plugins/funasr/README.md
  • plugins/funasr/example/__init__.py
  • plugins/funasr/example/funasr_example.py
  • plugins/funasr/py.typed
  • plugins/funasr/pyproject.toml
  • plugins/funasr/tests/test_funasr_stt.py
  • plugins/funasr/vision_agents/plugins/funasr/__init__.py
  • plugins/funasr/vision_agents/plugins/funasr/stt.py

Comment thread plugins/funasr/pyproject.toml Outdated
Comment thread plugins/funasr/vision_agents/plugins/funasr/stt.py Outdated
Comment thread plugins/funasr/vision_agents/plugins/funasr/stt.py Outdated
@LauraGPT
LauraGPT force-pushed the add-funasr-stt-plugin branch from 21b8744 to e05f50b Compare June 22, 2026 23:40
@LauraGPT

Copy link
Copy Markdown
Author

Thanks for the review! Addressed two of the three:

  • Tests organized in a class — wrapped the unit tests in a TestSTT class (per the repo's test conventions). Still passing.
  • Executor cleanupclose() now uses try/finally so the ThreadPoolExecutor is always shut down even if super().close() raises.

On the other two, I intentionally mirrored the existing fast_whisper plugin for consistency: the sdist include = ["/vision_agents"] (the leading slash is root-anchored in Hatch, same as every sibling plugin) and the broad except Exception in the audio path (it logs via logger.exception and keeps the stream resilient to a single bad chunk). Happy to change either if you'd prefer them to differ from fast_whisper.

@LauraGPT

Copy link
Copy Markdown
Author

Updated the branch to address the remaining review threads:

  • changed the FunASR sdist include path to vision_agents
  • narrowed FunASR audio buffering/transcription exception handling so unexpected errors surface instead of being swallowed
  • added regression coverage for unexpected buffer and transcription errors

Local verification:

  • targeted FunASR tests: 4 passed
  • ruff check plugins/funasr
  • python -m py_compile plugins/funasr/vision_agents/plugins/funasr/stt.py plugins/funasr/tests/test_funasr_stt.py
  • git diff --check
  • TOML parse check for root and FunASR plugin pyproject.toml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
plugins/funasr/tests/test_funasr_stt.py (1)

81-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid patching private methods in this test.

Lines 91-94 replace stt._transcribe and call stt._process_buffer() directly, which makes the assertion path-dependent and violates this repo’s test guidance. Use a fake client.generate() that raises AssertionError and drive the failure through process_audio() instead, so the regression stays covered through public behavior.

Sources: Coding guidelines, Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7e11fa38-82b7-4d3b-ac91-678198d8afd3

📥 Commits

Reviewing files that changed from the base of the PR and between e05f50b and d724a6a.

📒 Files selected for processing (3)
  • plugins/funasr/pyproject.toml
  • plugins/funasr/tests/test_funasr_stt.py
  • plugins/funasr/vision_agents/plugins/funasr/stt.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/funasr/pyproject.toml
  • plugins/funasr/vision_agents/plugins/funasr/stt.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 291ce366-675b-4a1d-a8b6-ebe943a2fb36

📥 Commits

Reviewing files that changed from the base of the PR and between d724a6a and 1fd171e.

📒 Files selected for processing (1)
  • plugins/funasr/tests/test_funasr_stt.py

Comment thread plugins/funasr/tests/test_funasr_stt.py Outdated
@LauraGPT

LauraGPT commented Jul 7, 2026

Copy link
Copy Markdown
Author

Fresh FunASR/SenseVoice-side validation recheck (2026-07-07 UTC):

  • PR is open, non-draft, and mergeable; mergeable_state=blocked is currently a review gate. Current head is 4cc511be.
  • Thread-aware review audit shows 4 total CodeRabbit review threads and 0 current unresolved threads; the earlier package include path, exception handling, executor cleanup, and test cleanup comments are now outdated.
  • GitHub checks visible on the head are green: labeler and CodeRabbit are both successful.
  • Local focused validation in a fresh checkout:
    • git diff --check origin/main...HEAD passed after fetching the merge base.
    • plugins/funasr/pyproject.toml parses, project name is vision-agents-plugins-funasr, and the sdist include is vision_agents (no leading slash).
    • python3 -m py_compile passed for the FunASR plugin, example, and tests.
    • Focused plugin tests passed with minimal local stubs for external runtime deps (funasr, getstream, and vision_agents.core) because my bare validation checkout does not install those packages: PYTHONPATH=<stub>:plugins/funasr python3 -m pytest -c /dev/null --confcutdir=plugins/funasr/tests plugins/funasr/tests/test_funasr_stt.py -q -> 4 passed.

From the FunASR plugin side this looks ready for human maintainer review.

@LauraGPT

Copy link
Copy Markdown
Author

Synced this PR with current main; exact head is 6f42e16, and GitHub compare shows 0 commits behind.

Thread-aware review audit still shows all 4 prior CodeRabbit threads resolved/outdated. The only visible completed GitHub check on the new head, labeler, is green; no failed checks are reported.

Fresh validation on the synced head with the repository lock environment:

  • uv run python -m pytest plugins/funasr/tests -q -> 4/4 passed
  • uv run ruff check plugins/funasr agents-core/vision_agents
  • uv run ruff format --check plugins/funasr
  • uv run python -m py_compile plugins/funasr/vision_agents/plugins/funasr/stt.py plugins/funasr/tests/test_funasr_stt.py
  • git diff --check

The PR remains non-draft and blocked only by maintainer review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant