Repository: kizuna-org/akari-poc
Pull request #83: feat(webrtcvad): add WebRTC Voice Activity Detector module and related classes
Author: login: shiron-dev
Created: 2025-05-02T05:44:14Z | Merged: 2025-05-02T07:03:44Z


- Introduced _WebRTCVadModule for voice activity detection.
- Added _WebRTCVadParams and _WebRTCVadMode for configuration.
- Updated AkariDataSet to include boolean output for speech detection.
- Added webrtcvad dependency to the project.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **新機能**
  - WebRTCベースの音声アクティビティ検出（VAD）モジュールを追加し、音声データから発話区間を判定できるようになりました。
  - 音声入力モジュールのコールバック機能が拡張され、複数のコールバックを連鎖的に処理可能になりました。

- **改善**
  - データセット管理にブール型データのサポートを追加しました。
  - 音声入力モジュールのストリーミング機能が有効化されました。

- **依存関係**
  - 新たに webrtcvad と setuptools ライブラリが導入されました。
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/akari/data.py b/akari/data.py
index dbfe962..31888d9 100644
--- a/akari/data.py
+++ b/akari/data.py
@@ -50,6 +50,7 @@ class _AkariDataSet:
     def __init__(self) -> None:
         self.text: _AkariDataSetType[str] | None = None
         self.audio: _AkariDataSetType[bytes] | None = None
+        self.bool: _AkariDataSetType[bool] | None = None
         self.allData: Any | None = None
 
 
diff --git a/main.py b/main.py
index 1fa4552..afdff28 100644
--- a/main.py
+++ b/main.py
@@ -11,7 +11,7 @@
 import akari
 import modules
 import sample
-from modules import audio, azure_openai, gemini
+from modules import audio, azure_openai, gemini, webrtcvad
 
 dotenv.load_dotenv()
 
@@ -65,48 +65,49 @@ def list_audio_devices() -> None:
         gemini.LLMModule: gemini.LLMModule(akariRouter, akariLogger),
         audio.SpeakerModule: audio.SpeakerModule(akariRouter, akariLogger),
         audio.MicModule: audio.MicModule(akariRouter, akariLogger),
+        webrtcvad.WebRTCVadModule: webrtcvad.WebRTCVadModule(akariRouter, akariLogger),
     }
 )
 
-akariRouter.callModule(
-    moduleType=modules.RootModule,
-    data=akari.AkariData(),
-    params=sample.SampleModule,
-    streaming=False,
-)
+# akariRouter.callModule(
+#     moduleType=modules.RootModule,
+#     data=akari.AkariData(),
+#     params=sample.SampleModule,
+#     streaming=False,
+# )
 
-akariRouter.callModule(
-    moduleType=azure_openai.LLMModule,
-    data=akari.AkariData(),
-    params=azure_openai.LLMModuleParams(
-        model="gpt-4o-mini",
-        messages=[
-            {"role": "user", "content": "Hello, Akari!"},
-            {"role": "system", "content": "You are a helpful assistant."},
-        ],
-        temperature=0.7,
-        max_tokens=150,
-        top_p=1.0,
-        frequency_penalty=0.0,
-        presence_penalty=0.0,
-        stream=True,
-    ),
-    streaming=False,
-    callback=modules.PrintModule,
-)
+# akariRouter.callModule(
+#     moduleType=azure_openai.LLMModule,
+#     data=akari.AkariData(),
+#     params=azure_openai.LLMModuleParams(
+#         model="gpt-4o-mini",
+#         messages=[
+#             {"role": "user", "content": "Hello, Akari!"},
+#             {"role": "system", "content": "You are a helpful assistant."},
+#         ],
+#         temperature=0.7,
+#         max_tokens=150,
+#         top_p=1.0,
+#         frequency_penalty=0.0,
+#         presence_penalty=0.0,
+#         stream=True,
+#     ),
+#     streaming=False,
+#     callback=modules.PrintModule,
+# )
 
 
-data = akariRouter.callModule(
-    moduleType=gemini.LLMModule,
-    data=akari.AkariData(),
-    params=gemini.LLMModuleParams(
-        model="gemini-2.0-flash",
-        messages=[
-            Content(role="user", parts=[Part.from_text("Hello, Akari!")]),
-        ],
-    ),
-    streaming=False,
-)
+# data = akariRouter.callModule(
+#     moduleType=gemini.LLMModule,
+#     data=akari.AkariData(),
+#     params=gemini.LLMModuleParams(
+#         model="gemini-2.0-flash",
+#         messages=[
+#             Content(role="user", parts=[Part.from_text("Hello, Akari!")]),
+#         ],
+#     ),
+#     streaming=False,
+# )
 
 
 # data = akari.AkariData()
@@ -156,14 +157,15 @@ def list_audio_devices() -> None:
 # )
 
 
-# akariRouter.callModule(
-#     moduleType=audio.MicModule,
-#     data=akari.AkariData(),
-#     params=audio.MicModuleParams(
-#         streamDurationMilliseconds=1000,
-#         destructionMilliseconds=5000,
-#         callbackParams=audio.SpeakerModuleParams(),
-#     ),
-#     streaming=False,
-#     callback=audio.SpeakerModule,
-# )
+akariRouter.callModule(
+    moduleType=audio.MicModule,
+    data=akari.AkariData(),
+    params=audio.MicModuleParams(
+        streamDurationMilliseconds=1000,
+        destructionMilliseconds=5000,
+        callbackParams=webrtcvad.WebRTCVadParams(),
+        callback_callback=modules.PrintModule,
+    ),
+    streaming=False,
+    callback=webrtcvad.WebRTCVadModule,
+)
diff --git a/modules/audio/mic.py b/modules/audio/mic.py
index b56a947..ce8e981 100644
--- a/modules/audio/mic.py
+++ b/modules/audio/mic.py
@@ -27,6 +27,7 @@ class _MicModuleParams:
     streamDurationMilliseconds: int = 5 * 100
     destructionMilliseconds: int = 5 * 100
     callbackParams: Any | None = None
+    callback_callback: AkariModuleType | None = None
 
 
 class _MicModule(AkariModule):
@@ -75,6 +76,7 @@ def call_module_in_thread() -> None:
                                 data=data,
                                 params=params.callbackParams,
                                 streaming=True,
+                                callback=params.callback_callback,
                             )
 
                         thread = threading.Thread(target=call_module_in_thread)
diff --git a/modules/webrtcvad/__init__.py b/modules/webrtcvad/__init__.py
new file mode 100644
index 0000000..f138532
--- /dev/null
+++ b/modules/webrtcvad/__init__.py
@@ -0,0 +1,5 @@
+from .vad import _WebRTCVadMode as WebRTCVadMode
+from .vad import _WebRTCVadModule as WebRTCVadModule
+from .vad import _WebRTCVadParams as WebRTCVadParams
+
+__all__ = ["WebRTCVadModule", "WebRTCVadMode", "WebRTCVadParams"]
diff --git a/modules/webrtcvad/vad.py b/modules/webrtcvad/vad.py
new file mode 100644
index 0000000..983bee1
--- /dev/null
+++ b/modules/webrtcvad/vad.py
@@ -0,0 +1,80 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any, Literal
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleParams,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    VERY_SENSITIVE = 0
+    SENSITIVE = 1
+    LOW_SENSITIVE = 2
+    STRICT = 3
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
+    sample_rate: Literal[8000, 16000, 32000, 48000] = 16000
+    frame_duration_ms: Literal[10, 20, 30] = 30
+    callback_params: AkariModuleParams | None = None
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self._vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariData:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
+
+        self._vad.set_mode(params.mode.value)
+
+        buffer = io.BytesIO(audio.main)
+        frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * 2)
+
+        if len(audio.main) < frame_size_bytes:
+            raise ValueError(
+                f"Audio data is too short. Expected at least {frame_size_bytes} bytes, but got {len(audio.main)} bytes."
+            )
+
+        buffer.seek(-frame_size_bytes, io.SEEK_END)
+        audio_data = buffer.read(frame_size_bytes)
+
+        try:
+            is_speech = self._vad.is_speech(audio_data, params.sample_rate)
+        except Exception as e:
+            raise ValueError(f"Error processing audio data with WebRTC VAD: {e}") from e
+
+        dataset = AkariDataSet()
+        dataset.bool = AkariDataSetType(is_speech)
+        data.add(dataset)
+
+        if is_speech and callback:
+            data = self._router.callModule(callback, data, params.callback_params, True, None)
+
+        return data
diff --git a/poetry.lock b/poetry.lock
index 430f3d2..d21142a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1734,6 +1734,27 @@ files = [
 [package.dependencies]
 pyasn1 = ">=0.1.3"
 
+[[package]]
+name = "setuptools"
+version = "80.1.0"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+    {file = "setuptools-80.1.0-py3-none-any.whl", hash = "sha256:ea0e7655c05b74819f82e76e11a85b31779fee7c4969e82f72bab0664e8317e4"},
+    {file = "setuptools-80.1.0.tar.gz", hash = "sha256:2e308396e1d83de287ada2c2fd6e64286008fe6aca5008e0b6a8cb0e2c86eedd"},
+]
+
+[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""]
+core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
+enabler = ["pytest-enabler (>=2.2)"]
+test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
+type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"]
+
 [[package]]
 name = "shapely"
 version = "2.1.0"
@@ -1895,7 +1916,21 @@ h2 = ["h2 (>=4,<5)"]
 socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
 zstd = ["zstandard (>=0.18.0)"]
 
+[[package]]
+name = "webrtcvad"
+version = "2.0.10"
+description = "Python interface to the Google WebRTC Voice Activity Detector (VAD)"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+    {file = "webrtcvad-2.0.10.tar.gz", hash = "sha256:f1bed2fb25b63fb7b1a55d64090c993c9c9167b28485ae0bcdd81cf6ede96aea"},
+]
+
+[package.extras]
+dev = ["check-manifest", "memory_profiler", "nose", "psutil", "unittest2", "zest.releaser"]
+
 [metadata]
 lock-version = "2.1"
 python-versions = "^3.13.3"
-content-hash = "c307f245ee14ec7e0d5edceea9f0194089d28547c9ccdcf26d96b4c71e55791c"
+content-hash = "4f8d0ecd1127432eb726ed84258c3d636d7a3a74948e74e3ee067c0f321b5efa"
diff --git a/pyproject.toml b/pyproject.toml
index c7b5050..383ade4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,6 +14,8 @@ pyaudio = "^0.2.14"
 audioop-lts = "^0.2.1"
 google-cloud-aiplatform = "^1.90.0"
 google-auth = "^2.39.0"
+webrtcvad = "^2.0.10"
+setuptools = "^80.1.0"
 
 [tool.poetry.group.dev.dependencies]
 pytest = "8.3.5"


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR introduces a WebRTC Voice Activity Detection module to process audio data and produce boolean speech detection results.
- Added _WebRTCVadModule with streaming functionality.
- Introduced _WebRTCVadParams and _WebRTCVadMode for configuration.
- Updated AkariDataSet to include a boolean output for speech detection.

### Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

<details>
<summary>Show a summary per file</summary>

| File | Description |
| ---- | ----------- |
| pyproject.toml | Added dependency for webrtcvad. |
| modules/webrtcvad/vad.py | Implemented WebRTC VAD through a new module. |
| modules/webrtcvad/__init__.py | Re-exported the new VAD types and module. |
| main.py | Included the new WebRTC VAD module in the router configuration. |
| akari/data.py | Updated data structure to support boolean output. |
</details>



<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**modules/webrtcvad/vad.py:60**
* [nitpick] Consider renaming 'bool' to a more descriptive name like 'is_speech' to enhance clarity and prevent any conflict with the built-in 'bool' type.
```
dataset.bool = AkariDataSetType(is_speech)
```
**akari/data.py:53**
* [nitpick] Renaming the attribute 'bool' to a more descriptive name such as 'is_speech' would improve readability and avoid confusion with Python's built-in 'bool' type.
```
self.bool: _AkariDataSetType[bool] | None = None
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T05:44:38Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>main.py (1)</summary><blockquote>

`99-111`: **既存機能のコメントアウトは意図的な変更と思われますが、影響範囲を確認してください**

こちらも同様に、既存の機能（Gemini LLMモジュール）がコメントアウトされています。前述のコメントと同様の懸念があります。

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>akari/data.py (1)</summary><blockquote>

`53-53`: **boolean型データセットの追加は適切ですが、ドキュメントが不足しています**

`bool: _AkariDataSetType[bool] | None = None`の追加は型ヒントの観点から適切に実装されています。ただし、この属性が何のために使用されるのか、どのような値を期待しているのかを示すdocstringやコメントが追加されていないため、可読性と保守性の観点から改善の余地があります。

また、この変更によって既存のコード（特にこのクラスを使用する部分）への影響がないか確認してください。

```diff
 class _AkariDataSet:
     def __init__(self) -> None:
         self.text: _AkariDataSetType[str] | None = None
         self.audio: _AkariDataSetType[bytes] | None = None
-        self.bool: _AkariDataSetType[bool] | None = None
+        # 音声アクティビティ検出（VAD）結果を格納するための属性
+        self.bool: _AkariDataSetType[bool] | None = None
         self.allData: Any | None = None
```

</blockquote></details>
<details>
<summary>modules/webrtcvad/__init__.py (1)</summary><blockquote>

`1-5`: **再エクスポートの実装は適切ですが、docstringが不足しています**

内部クラス（`_`プレフィックス付き）をパブリックAPIとして適切に再エクスポートしており、`__all__`リストの使用も正しいです。ただし、モジュールレベルのdocstringが不足しているため、このモジュールの目的や使用方法がわかりにくくなっています。

特に、WebRTC VADモジュールがどのように使用されるべきか、どのようなパラメータが有効か、どのようなデータを返すのかなど、基本的な使用方法を説明するドキュメントを追加することを推奨します。

```diff
+"""
+WebRTC Voice Activity Detection (VAD) モジュール
+
+このモジュールは音声ストリームから発話区間を検出するための機能を提供します。
+WebRTCVadModuleはAkariのモジュールシステムに統合され、音声データから発話の有無を
+boolean値として返します。
+
+使用例:
+    akariRouter.callModule(
+        moduleType=webrtcvad.WebRTCVadModule,
+        data=audioData,
+        params=webrtcvad.WebRTCVadParams(
+            mode=webrtcvad.WebRTCVadMode.NORMAL,
+            sample_rate=16000,
+            frame_duration_ms=30
+        ),
+        streaming=True
+    )
+"""
+
 from .vad import _WebRTCVadMode as WebRTCVadMode
 from .vad import _WebRTCVadModule as WebRTCVadModule
 from .vad import _WebRTCVadParams as WebRTCVadParams

 __all__ = ["WebRTCVadModule", "WebRTCVadMode", "WebRTCVadParams"]
```

</blockquote></details>
<details>
<summary>main.py (1)</summary><blockquote>

`72-97`: **既存機能のコメントアウトは意図的な変更と思われますが、影響範囲を確認してください**

複数のモジュール呼び出しがコメントアウトされていますが、これは意図的な変更と思われます。おそらく、WebRTC VADモジュールのテストや開発に集中するために、一時的に他の機能を無効化しているのでしょう。

ただし、このような変更はプロダクション環境では問題を引き起こす可能性があります。最終的なPRではこれらのコメントアウトを元に戻すか、または明示的に削除するかを検討してください。

また、コメントを追加して、なぜこれらの呼び出しをコメントアウトしたのかを説明することで、将来の開発者が変更の意図を理解しやすくなります。

```diff
-# akariRouter.callModule(
-#     moduleType=modules.RootModule,
-#     data=akari.AkariData(),
-#     params=sample.SampleModule,
-#     streaming=False,
-# )
+# NOTE: 音声処理とVADモジュールのテストに集中するため、他のモジュール呼び出しは一時的に無効化しています
+# akariRouter.callModule(
+#     moduleType=modules.RootModule,
+#     data=akari.AkariData(),
+#     params=sample.SampleModule,
+#     streaming=False,
+# )
```

</blockquote></details>
<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

`1-7`: **不要なインポートが含まれています**

`typing.Any`と`pyaudio`がインポートされていますが、コード内で使用されていません。未使用のインポートは削除するのがベストプラクティスです。

```diff
import dataclasses
import io
from enum import Enum
-from typing import Any

-import pyaudio
import webrtcvad
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

</details>

</details>

---

`34-42`: **VADモードが初期化されていません**

`webrtcvad.Vad()`インスタンスを作成していますが、モードが設定されていません。各呼び出しではなく初期化時にVADモードを設定することで、パフォーマンスが向上する可能性があります。また、初期化パラメータをクラスに渡すことも検討すべきです。

```diff
def __init__(
    self,
    router: AkariRouter,
    logger: AkariLogger,
+   params: _WebRTCVadParams = _WebRTCVadParams(),
) -> None:
    super().__init__(router, logger)
    self.vad = webrtcvad.Vad()
+   self.vad.set_mode(params.mode.value)
+   self.params = params
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between f7e5995bc990f5d2b97992a22180f526ccf1f8e5 and fbb9b429f0d91403e4c317634c3b40ff3766fb3d.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `poetry.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `akari/data.py` (1 hunks)
* `main.py` (3 hunks)
* `modules/webrtcvad/__init__.py` (1 hunks)
* `modules/webrtcvad/vad.py` (1 hunks)
* `pyproject.toml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。

レビューでは、以下の観点に合致していない点を重点的に指摘します。

- 型ヒント（type hints）を適切に使用している...</summary>

> `**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
> レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。
> 
> レビューでは、以下の観点に合致していない点を重点的に指摘します。
> 
> - 型ヒント（type hints）を適切に使用しているか（PEP 484に準拠）。
> - 非同期処理においては、async/awaitを正しく使用し、asyncioやaiohttp等の非同期ライブラリが適切に使われているか。
> - 型推論と明示的な型指定のバランスが適切か。
> - コレクション（リスト・辞書・集合など）に型指定がされており、例えばList[str], Dict[str, Any]などが活用されているか。
> - 抽象化・設計が適切で、関数・クラス・モジュールが過度に肥大化していないか（単一責任原則などを守っているか）。
> - 例外処理が漏れなく実装され、try/except節の使い方やカスタム例外の設計が適切か。
> - 外部ライブラリの使用において型定義（stubファイルやtypes-パッケージ）を活用しているか。
> - テストコードの書き方において、テストが冪等であり、setup_methodやteardown_methodによるリセット処理が適切か。
> - 予期しない型のエラーや例外を回避できる堅牢な実装となっているか。
> - パフォーマンスに著しい問題がないか（計算量、不要なオブジェクト生成、メモリリークの懸念など）。
> - 冪等性が求められる関数や処理でそれが保証されているか。
> - ライブラリのインポートが明示的かつ必要最小限で、循環参照の問題がないか。
> - コードの可読性・保守性が高く、コメントやdocstringが適切に記述されているか。
> - コード修正におけるデグレードのリスクがないか、機能や性能に影響を与えていないかを確認する。

- `akari/data.py`
- `modules/webrtcvad/__init__.py`
- `main.py`
- `modules/webrtcvad/vad.py`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>modules/webrtcvad/__init__.py (1)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (3)</summary>

* `_WebRTCVadMode` (20-24)
* `_WebRTCVadModule` (34-62)
* `_WebRTCVadParams` (28-31)

</details>

</blockquote></details>
<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

<details>
<summary>tests/test_serial_module.py (1)</summary>

* `router` (21-22)

</details>
<details>
<summary>akari/data.py (2)</summary>

* `last` (10-13)
* `last` (69-72)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>modules/webrtcvad/vad.py</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>pyproject.toml (1)</summary>

`17-17`: <details>
<summary>❓ Verification inconclusive</summary>

**依存関係の追加は適切ですが、型定義の確認が必要です**

`webrtcvad`ライブラリの追加自体は問題ありませんが、このライブラリに対する型定義（stubファイルまたはtypes-パッケージ）が存在するか確認してください。存在しない場合、mypy実行時に型エラーが発生する可能性があります。

また、バージョン指定 `^2.0.10` はセマンティックバージョニングに従って2.x.yのバージョンなら利用可能という指定になっており、これは適切です。

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there's a types package for webrtcvad
pip search types-webrtcvad 2>/dev/null || echo "No types package found"
# Check if the package itself contains type hints
pip show webrtcvad 2>/dev/null
```

Length of output: 104

---



</details>

**webrtcvadの型定義が未確認です**

`pyproject.toml` に追加されている `webrtcvad = "^2.0.10"` には、現状 PyPI 上に `types-webrtcvad` パッケージが存在せず、ライブラリ本体にも型ヒントが含まれていない可能性があります。

• webrtcvad をローカル環境にインストールし、サイトパッケージ内やソースに `.pyi` スタブや PEP 561 対応があるか確認してください。  
• 型定義が見当たらない場合は、プロジェクトにスタブファイルを追加するか、`mypy` 設定で該当モジュールを除外（`ignore` 設定や `MYPYPATH` の指定）してください。

以上、ご対応をお願いします。

</details>
<details>
<summary>main.py (2)</summary>

`14-14`: **インポートの追加は適切です**

`webrtcvad`モジュールのインポートは適切に行われています。

---

`68-68`: <details>
<summary>❓ Verification inconclusive</summary>

**モジュールの登録は適切ですが、初期化パラメータの検討が必要かもしれません**

`webrtcvad.WebRTCVadModule`のインスタンスが適切に`akariRouter`に登録されています。ただし、他のモジュールと異なり、特定のクライアントやリソースを初期化パラメータとして渡していません。

WebRTCVadModuleが追加のクライアント設定や初期化パラメータを必要としないことを確認してください。将来的に設定が必要になった場合は、他のモジュールと同様の方法で初期化パラメータを渡せるようにすることを検討してください。

---

🏁 Script executed:

```shell
#!/bin/bash
# webrtcvad.Vad の初期化オプションを調査
python -c "import webrtcvad; help(webrtcvad.Vad.__init__)" 2>/dev/null || echo "Failed to get help information"
```

Length of output: 131

---

追加確認：WebRTCVadModule の定義とコンストラクタ引数を調査します。以下のスクリプトを実行してください。

---

🏁 Script executed:

```shell
#!/bin/bash
# WebRTCVadModule の定義と __init__ メソッドを表示
file=$(grep -R -l "class WebRTCVadModule" .)
echo "Found in: $file"
sed -n '1,200p' "$file"
grep -n "def __init__" "$file"
```

Length of output: 203

---



</details>

WebRTCVadModule の定義がリポジトリ内で見つかりませんでした。  
実装ファイルのパスと `__init__` メソッドのシグネチャをご確認のうえ、初期化パラメータが必要かどうか確認してください。

</details>
<details>
<summary>modules/webrtcvad/vad.py (4)</summary>

`20-24`: **Enum実装は適切です**

`_WebRTCVadMode`の定義は明確で、WebRTC VADライブラリの想定値に適切に対応しています。前置アンダースコアによる内部使用の意図も明確です。

---

`27-31`: **パラメータクラスの設計は良好です**

`_WebRTCVadParams`データクラスは適切な型ヒントと合理的なデフォルト値を持っています。音声処理に一般的な値（16000Hzのサンプルレート、30msのフレーム持続時間）を設定しています。

---

`43-44`: **callメソッドの実装は適切です**

ストリーミング用に設計されたモジュールなので、`call`メソッドを無効化し、明確なエラーメッセージを表示する方法は正しいアプローチです。

---

`46-52`: _🛠️ Refactor suggestion_

**戻り値の型定義が必要です**

戻り値の型定義が適切に行われていますが、関数内のバリデーションはさらに強化できます。WebRTC VADはサポートされている特定のサンプルレートとフレーム持続時間のみをサポートしているため、パラメータの検証を追加することをお勧めします。

```diff
def stream_call(
    self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
) -> AkariDataSet:
+   # WebRTC VADがサポートするサンプルレートを検証
+   if params.sample_rate not in [8000, 16000, 32000, 48000]:
+       raise ValueError(f"サンプルレート {params.sample_rate} はサポートされていません。8000, 16000, 32000, または 48000 を使用してください。")
+   
+   # WebRTC VADがサポートするフレーム持続時間を検証
+   if params.frame_duration_ms not in [10, 20, 30]:
+       raise ValueError(f"フレーム持続時間 {params.frame_duration_ms} はサポートされていません。10, 20, または 30 を使用してください。")
+   
+   # VADモードを設定
+   self.vad.set_mode(params.mode.value)

    audio = data.last().audio
    if audio is None:
        raise ValueError("Audio data is missing or empty.")
```





> Likely an incorrect or invalid review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T05:49:41Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

`53-57`: **バッファサイズの検証が必要です**

オーディオバッファからフレームを読み取る際、バッファサイズが必要なフレームサイズよりも小さい場合のエラーハンドリングが欠けています。これにより、短すぎるオーディオデータが入力された場合にランタイムエラーが発生する可能性があります。


以前のレビューでも指摘されていた通り、バッファサイズの検証を追加してください：

```diff
buffer = io.BytesIO(audio.main)
frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
+# バッファサイズがフレームサイズ以上であることを確認
+if len(audio.main) < frame_size:
+   raise ValueError(f"オーディオデータが不足しています。必要なサイズ: {frame_size} バイト、実際のサイズ: {len(audio.main)} バイト")

buffer.seek(-frame_size, io.SEEK_END)
audio_data = buffer.read(frame_size)
```

---

`58-62`: **VAD処理での例外ハンドリングが必要です**

`vad.is_speech`メソッドの呼び出しに対する例外ハンドリングが実装されていません。WebRTC VADライブラリからの例外を適切に処理することで、コードの堅牢性が向上します。


以前のレビューでも指摘されていた通り、try-except文を追加してエラーハンドリングを強化してください：

```diff
-is_speech = self.vad.is_speech(audio_data, params.sample_rate)
+try:
+   is_speech = self.vad.is_speech(audio_data, params.sample_rate)
+except Exception as e:
+   self.logger.error(f"VAD処理中にエラーが発生しました: {str(e)}")
+   raise ValueError(f"音声分析中にエラーが発生しました: {str(e)}")

dataset = AkariDataSet()
dataset.bool = AkariDataSetType(is_speech)
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

`4-4`: **未使用のインポートを削除してください**

コードの中で使用されていないインポートがあります：
- `typing.Any`（4行目）
- `pyaudio`（6行目）

未使用のインポートを削除することで、コードの可読性が向上し、将来的な混乱を防ぐことができます。


```diff
-import dataclasses
-import io
-from enum import Enum
-from typing import Any
+import dataclasses
+import io
+from enum import Enum

-import pyaudio
import webrtcvad

from akari import (
```


Also applies to: 6-6

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

</details>

</details>

---

`34-62`: **ロガーの活用が不足しています**

`AkariLogger`がモジュールに注入されていますが、現在のコードではロギングが行われていません。エラーケースだけでなく、モジュールの処理開始や完了、重要な処理ステップなどでもログを出力することで、デバッグやモニタリングが容易になります。


ロギング処理を追加することをお勧めします：

```diff
def stream_call(
    self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
) -> AkariDataSet:
+   self.logger.debug("WebRTC VAD処理を開始します")
    audio = data.last().audio
    if audio is None:
+       self.logger.error("オーディオデータが欠落しています")
        raise ValueError("Audio data is missing or empty.")
    
    # VADモードを設定
    self.vad.set_mode(params.mode.value)
+   self.logger.debug(f"VADモードを設定しました: {params.mode.name}")
    
    buffer = io.BytesIO(audio.main)
    frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
    # バッファサイズの検証
    # ...
    
    buffer.seek(-frame_size, io.SEEK_END)
    audio_data = buffer.read(frame_size)
+   self.logger.debug(f"オーディオフレームを抽出しました: {len(audio_data)}バイト")
    
    try:
        is_speech = self.vad.is_speech(audio_data, params.sample_rate)
+       self.logger.debug(f"VAD結果: {'音声あり' if is_speech else '音声なし'}")
    except Exception as e:
        # エラーロギングは既に追加されています
        # ...
    
    dataset = AkariDataSet()
    dataset.bool = AkariDataSetType(is_speech)
+   self.logger.debug("WebRTC VAD処理を完了しました")
    
    return dataset
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fbb9b429f0d91403e4c317634c3b40ff3766fb3d and 2921b5fc11d2a7dbcca26afadfa268410e977862.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `modules/webrtcvad/vad.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。

レビューでは、以下の観点に合致していない点を重点的に指摘します。

- 型ヒント（type hints）を適切に使用している...</summary>

> `**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
> レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。
> 
> レビューでは、以下の観点に合致していない点を重点的に指摘します。
> 
> - 型ヒント（type hints）を適切に使用しているか（PEP 484に準拠）。
> - 非同期処理においては、async/awaitを正しく使用し、asyncioやaiohttp等の非同期ライブラリが適切に使われているか。
> - 型推論と明示的な型指定のバランスが適切か。
> - コレクション（リスト・辞書・集合など）に型指定がされており、例えばList[str], Dict[str, Any]などが活用されているか。
> - 抽象化・設計が適切で、関数・クラス・モジュールが過度に肥大化していないか（単一責任原則などを守っているか）。
> - 例外処理が漏れなく実装され、try/except節の使い方やカスタム例外の設計が適切か。
> - 外部ライブラリの使用において型定義（stubファイルやtypes-パッケージ）を活用しているか。
> - テストコードの書き方において、テストが冪等であり、setup_methodやteardown_methodによるリセット処理が適切か。
> - 予期しない型のエラーや例外を回避できる堅牢な実装となっているか。
> - パフォーマンスに著しい問題がないか（計算量、不要なオブジェクト生成、メモリリークの懸念など）。
> - 冪等性が求められる関数や処理でそれが保証されているか。
> - ライブラリのインポートが明示的かつ必要最小限で、循環参照の問題がないか。
> - コードの可読性・保守性が高く、コメントやdocstringが適切に記述されているか。
> - コード修正におけるデグレードのリスクがないか、機能や性能に影響を与えていないかを確認する。

- `modules/webrtcvad/vad.py`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

<details>
<summary>tests/test_serial_module.py (1)</summary>

* `router` (21-22)

</details>
<details>
<summary>akari/data.py (2)</summary>

* `last` (10-13)
* `last` (69-72)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>modules/webrtcvad/vad.py</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T05:54:10Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

`4-4`: **未使用のインポートを削除する必要があります**

`typing.Any`と`pyaudio`がインポートされていますが、コード内で使用されていません。使用していないインポートは削除して、コードをクリーンに保つことをお勧めします。

```diff
- import pyaudio
- from typing import Any, Literal
+ from typing import Literal
```




Also applies to: 6-6

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

</details>

</details>

---

`67-70`: **例外処理のベストプラクティスに従った修正が必要です**

例外処理は追加されていますが、元の例外情報を保持するためには、Pythonのベストプラクティスに従って`raise ... from e`構文を使用するべきです。

```diff
try:
    is_speech = self._vad.is_speech(audio_data, params.sample_rate)
except Exception as e:
-    raise ValueError(f"Error processing audio data: {e}")
+    raise ValueError(f"Error processing audio data: {e}") from e
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

70-70: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2921b5fc11d2a7dbcca26afadfa268410e977862 and 9e04321cc68c74287797a19e1fa7861a10668ecc.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `modules/webrtcvad/vad.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。

レビューでは、以下の観点に合致していない点を重点的に指摘します。

- 型ヒント（type hints）を適切に使用している...</summary>

> `**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
> レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。
> 
> レビューでは、以下の観点に合致していない点を重点的に指摘します。
> 
> - 型ヒント（type hints）を適切に使用しているか（PEP 484に準拠）。
> - 非同期処理においては、async/awaitを正しく使用し、asyncioやaiohttp等の非同期ライブラリが適切に使われているか。
> - 型推論と明示的な型指定のバランスが適切か。
> - コレクション（リスト・辞書・集合など）に型指定がされており、例えばList[str], Dict[str, Any]などが活用されているか。
> - 抽象化・設計が適切で、関数・クラス・モジュールが過度に肥大化していないか（単一責任原則などを守っているか）。
> - 例外処理が漏れなく実装され、try/except節の使い方やカスタム例外の設計が適切か。
> - 外部ライブラリの使用において型定義（stubファイルやtypes-パッケージ）を活用しているか。
> - テストコードの書き方において、テストが冪等であり、setup_methodやteardown_methodによるリセット処理が適切か。
> - 予期しない型のエラーや例外を回避できる堅牢な実装となっているか。
> - パフォーマンスに著しい問題がないか（計算量、不要なオブジェクト生成、メモリリークの懸念など）。
> - 冪等性が求められる関数や処理でそれが保証されているか。
> - ライブラリのインポートが明示的かつ必要最小限で、循環参照の問題がないか。
> - コードの可読性・保守性が高く、コメントやdocstringが適切に記述されているか。
> - コード修正におけるデグレードのリスクがないか、機能や性能に影響を与えていないかを確認する。

- `modules/webrtcvad/vad.py`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>modules/webrtcvad/vad.py (3)</summary><blockquote>

<details>
<summary>tests/test_serial_module.py (1)</summary>

* `router` (21-22)

</details>
<details>
<summary>akari/data.py (3)</summary>

* `last` (10-13)
* `last` (69-72)
* `add` (61-62)

</details>
<details>
<summary>akari/router.py (1)</summary>

* `callModule` (21-60)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>modules/webrtcvad/vad.py</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

---

70-70: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (5)</summary>

`21-25`: **適切なVADモードの列挙型定義です**

WebRTC VADの感度レベルを明確に定義した列挙型の実装は良いです。タイプセーフな方法でVADの感度を指定できるようになっています。

---

`28-34`: **パラメータの型制約が適切に実装されています**

`Literal`型を使用して`sample_rate`と`frame_duration_ms`の有効な値を制限していることは素晴らしい実装です。これにより、WebRTC VADライブラリがサポートする値のみを受け入れることが型チェック時に保証されます。

---

`45-46`: **同期メソッドの無効化は適切です**

VADモジュールがストリーミング専用であることを明示するために`call`メソッドで`NotImplementedError`を発生させる実装は明確で適切です。

---

`51-62`: **オーディオデータの検証とモード設定が適切に実装されています**

オーディオデータの存在確認、VADモードの設定、およびフレームサイズに対するオーディオデータ長のバリデーションが適切に実装されています。特に、以前のレビューで指摘されていたバッファサイズの検証が追加されており、安全な実装になっています。

---

`72-79`: **データセットの処理とコールバック呼び出しの実装は適切です**

検出結果を`AkariDataSet`に格納し、条件付きでコールバックを呼び出す実装は、Akariフレームワークの設計に適合しています。特に音声が検出された場合のみコールバックが呼び出される条件分岐は効率的です。

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T06:04:23Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

`1-8`: **未使用のインポートを削除してください**

静的解析ツールが指摘しているように、以下のインポートは使用されていません：
- `typing.Any` (4行目)
- `pyaudio` (6行目)


```diff
-import dataclasses
-import io
-from enum import Enum
-from typing import Any, Literal
+import dataclasses
+import io
+from enum import Enum
+from typing import Literal

-import pyaudio
+import webrtcvad
-import webrtcvad
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

</details>

</details>

---

`58-58`: **フレームサイズ計算に音声フォーマットの考慮が必要**

現在のフレームサイズ計算は16ビット音声（2バイト/サンプル）を前提としています。しかし、異なる音声フォーマットが使用される可能性があります。


音声フォーマットから動的にバイト数を取得するロジックを追加することを検討してください：

```diff
-frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * 2)
+# サンプルあたりのバイト数（16ビット音声の場合は2）
+bytes_per_sample = 2  # 将来的には音声フォーマットから取得するように変更可能
+frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * bytes_per_sample)
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9e04321cc68c74287797a19e1fa7861a10668ecc and 3498d4ae3aca4a4e0770e1eb57b5b32447012cf9.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `poetry.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `main.py` (3 hunks)
* `modules/audio/mic.py` (2 hunks)
* `modules/webrtcvad/vad.py` (1 hunks)
* `pyproject.toml` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* pyproject.toml
* main.py

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。

レビューでは、以下の観点に合致していない点を重点的に指摘します。

- 型ヒント（type hints）を適切に使用している...</summary>

> `**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
> レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。
> 
> レビューでは、以下の観点に合致していない点を重点的に指摘します。
> 
> - 型ヒント（type hints）を適切に使用しているか（PEP 484に準拠）。
> - 非同期処理においては、async/awaitを正しく使用し、asyncioやaiohttp等の非同期ライブラリが適切に使われているか。
> - 型推論と明示的な型指定のバランスが適切か。
> - コレクション（リスト・辞書・集合など）に型指定がされており、例えばList[str], Dict[str, Any]などが活用されているか。
> - 抽象化・設計が適切で、関数・クラス・モジュールが過度に肥大化していないか（単一責任原則などを守っているか）。
> - 例外処理が漏れなく実装され、try/except節の使い方やカスタム例外の設計が適切か。
> - 外部ライブラリの使用において型定義（stubファイルやtypes-パッケージ）を活用しているか。
> - テストコードの書き方において、テストが冪等であり、setup_methodやteardown_methodによるリセット処理が適切か。
> - 予期しない型のエラーや例外を回避できる堅牢な実装となっているか。
> - パフォーマンスに著しい問題がないか（計算量、不要なオブジェクト生成、メモリリークの懸念など）。
> - 冪等性が求められる関数や処理でそれが保証されているか。
> - ライブラリのインポートが明示的かつ必要最小限で、循環参照の問題がないか。
> - コードの可読性・保守性が高く、コメントやdocstringが適切に記述されているか。
> - コード修正におけるデグレードのリスクがないか、機能や性能に影響を与えていないかを確認する。

- `modules/audio/mic.py`
- `modules/webrtcvad/vad.py`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>modules/webrtcvad/vad.py (4)</summary><blockquote>

<details>
<summary>tests/test_serial_module.py (1)</summary>

* `router` (21-22)

</details>
<details>
<summary>modules/audio/mic.py (1)</summary>

* `call` (37-94)

</details>
<details>
<summary>akari/data.py (3)</summary>

* `last` (10-13)
* `last` (69-72)
* `add` (61-62)

</details>
<details>
<summary>akari/router.py (1)</summary>

* `callModule` (21-60)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>modules/webrtcvad/vad.py</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

---

71-71: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (9)</summary><blockquote>

<details>
<summary>modules/audio/mic.py (2)</summary>

`30-30`: **コールバックチェーンをサポートするパラメータが適切に追加されています**

この変更では`_MicModuleParams`データクラスに`callback_callback`パラメータが追加され、コールバックのチェーン化が可能になりました。これにより、マイクモジュールがWebRTC VADモジュールを呼び出し、さらにそのモジュールが別のモジュール（例：PrintModule）を呼び出すという連鎖的な処理が実現できます。型アノテーション`AkariModuleType | None`も適切です。

---

`79-79`: **コールバックチェーンの実装が完了しています**

`call_module_in_thread`関数内で、新しく追加された`callback_callback`パラメータが正しく`self._router.callModule`に渡されています。これによりコールバックの連鎖が実現され、マイクから取得した音声データがWebRTC VADモジュールで処理された後、さらに別のモジュールで処理されるという流れが確立されています。

</details>
<details>
<summary>modules/webrtcvad/vad.py (7)</summary>

`21-25`: **VADモードの列挙型が適切に定義されています**

WebRTC VADの感度設定を表す列挙型が明確に定義されており、各モードの名前も直感的です。この実装はWebRTC VADのAPIと整合しており、0（最も感度が高い）から3（最も厳格）の範囲の値を適切に使用しています。

---

`28-33`: **パラメータクラスの型制約が優れています**

`_WebRTCVadParams`データクラスでは、`sample_rate`と`frame_duration_ms`に`Literal`型を使用して有効な値を制限している点が優れています。これによりWebRTC VADが要求する特定の値のみを受け付け、実行時エラーを防止できます。

---

`74-75`: **boolean型データセットの使用が適切です**

検出結果を`AkariDataSet`の`bool`属性に格納し、その結果を`data`に追加する実装は適切です。これにより、音声検出結果がデータセットのチェーン内で保持され、後続の処理で利用可能になります。

---

`77-78`: **音声検出時のコールバック処理が効果的に実装されています**

音声が検出された場合のみコールバックを実行する条件分岐が効果的に実装されています。これにより、無音区間で不要な処理が発生することを防ぎ、システムリソースを節約できます。

---

`43-43`: **VADインスタンスの属性名にアンダースコアを使用しているのは良い習慣です**

`self._vad`のように内部属性名にアンダースコアを使用することで、これがクラスの内部実装詳細であることを明示しています。この命名規則はPythonの慣例に従っており、適切です。

---

`45-46`: **非同期処理モデルの明確な分離は優れた設計です**

同期的な`call`メソッドを無効化し、ユーザーに`stream_call`メソッドの使用を促すアプローチは適切です。これにより、このモジュールが主にストリーミングコンテキストで使用されることを明確に示しています。

---

`60-63`: **オーディオデータのサイズチェックは重要なエラー防止策です**

フレームサイズよりも小さいオーディオデータをバリデーションするコードは、以前のレビューコメントを適切に反映しています。これにより、バッファ操作時の潜在的なエラーを回避できます。

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T06:52:47Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (1)</summary><blockquote>

`51-53`: _🛠️ Refactor suggestion_

**オーディオデータの検証をさらに強化できます**

現在のコードでは`audio`が`None`かどうかをチェックしていますが、`audio.main`が適切な型（bytes）であるかの検証が行われていません。



以下の修正を提案します：

```diff
audio = data.last().audio
if audio is None:
    raise ValueError("Audio data is missing or empty.")
+if not hasattr(audio, 'main') or not isinstance(audio.main, bytes):
+    raise ValueError("Audio data is invalid or not in the expected format.")
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (2)</summary><blockquote>

`1-8`: **未使用のインポートがあります**

静的解析ツールが検出したように、以下の2つのインポートが使用されていません：
1. `typing.Any`
2. `pyaudio`

未使用のインポートは必要ない依存関係を作り出し、コードの可読性を下げる可能性があります。


以下の修正を提案します：

```diff
-import dataclasses
-import io
-from enum import Enum
-from typing import Any, Literal
-
-import pyaudio
-import webrtcvad
+import dataclasses
+import io
+from enum import Enum
+from typing import Literal
+
+import webrtcvad
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

</details>

</details>

---

`77-78`: **コールバック処理の改善の余地があります**

現在のコードでは、音声が検出されてコールバックが提供されている場合、`_router.callModule`を呼び出しますが、最後の引数に`None`を渡しています。これは現在のコールバックチェーンを終了させる可能性があります。


モジュール間の一貫性を保つために、現在のコールバックを次のコールバックとして渡すことを検討してください：

```diff
if is_speech and callback:
-    data = self._router.callModule(callback, data, params.callback_params, True, None)
+    data = self._router.callModule(callback, data, params.callback_params, True, None)  # コールバックチェーンを終了
```

もしコールバックチェーンを継続させたい場合は、以下のように修正することを検討してください：

```diff
if is_speech and callback:
-    data = self._router.callModule(callback, data, params.callback_params, True, None)
+    # 現在のコールバックを次のモジュールに渡す（オプション）
+    next_callback = None  # 必要に応じてここを変更
+    data = self._router.callModule(callback, data, params.callback_params, True, next_callback)
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3498d4ae3aca4a4e0770e1eb57b5b32447012cf9 and f1374019eff908b6e8d5945d45f606b3d70e2473.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `modules/webrtcvad/vad.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。

レビューでは、以下の観点に合致していない点を重点的に指摘します。

- 型ヒント（type hints）を適切に使用している...</summary>

> `**/*.py`: Pythonプロジェクトにおける熟練の専門家・開発者としてPull Requestをレビューします。
> レビューコメントをする際は、まず結論を簡潔に述べ、その後に指摘の根拠と改善案を順序立てて論理的に記述します。
> 
> レビューでは、以下の観点に合致していない点を重点的に指摘します。
> 
> - 型ヒント（type hints）を適切に使用しているか（PEP 484に準拠）。
> - 非同期処理においては、async/awaitを正しく使用し、asyncioやaiohttp等の非同期ライブラリが適切に使われているか。
> - 型推論と明示的な型指定のバランスが適切か。
> - コレクション（リスト・辞書・集合など）に型指定がされており、例えばList[str], Dict[str, Any]などが活用されているか。
> - 抽象化・設計が適切で、関数・クラス・モジュールが過度に肥大化していないか（単一責任原則などを守っているか）。
> - 例外処理が漏れなく実装され、try/except節の使い方やカスタム例外の設計が適切か。
> - 外部ライブラリの使用において型定義（stubファイルやtypes-パッケージ）を活用しているか。
> - テストコードの書き方において、テストが冪等であり、setup_methodやteardown_methodによるリセット処理が適切か。
> - 予期しない型のエラーや例外を回避できる堅牢な実装となっているか。
> - パフォーマンスに著しい問題がないか（計算量、不要なオブジェクト生成、メモリリークの懸念など）。
> - 冪等性が求められる関数や処理でそれが保証されているか。
> - ライブラリのインポートが明示的かつ必要最小限で、循環参照の問題がないか。
> - コードの可読性・保守性が高く、コメントやdocstringが適切に記述されているか。
> - コード修正におけるデグレードのリスクがないか、機能や性能に影響を与えていないかを確認する。

- `modules/webrtcvad/vad.py`

</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>modules/webrtcvad/vad.py (4)</summary><blockquote>

<details>
<summary>tests/test_serial_module.py (1)</summary>

* `router` (21-22)

</details>
<details>
<summary>modules/audio/mic.py (1)</summary>

* `call` (37-94)

</details>
<details>
<summary>akari/data.py (3)</summary>

* `last` (10-13)
* `last` (69-72)
* `add` (61-62)

</details>
<details>
<summary>akari/router.py (1)</summary>

* `callModule` (21-60)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>modules/webrtcvad/vad.py</summary>

4-4: `typing.Any` imported but unused

Remove unused import: `typing.Any`

(F401)

---

6-6: `pyaudio` imported but unused

Remove unused import: `pyaudio`

(F401)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>modules/webrtcvad/vad.py (5)</summary>

`21-25`: **VadModeの列挙型定義は適切です**

WebRTC VADの感度モードを列挙型として適切に定義されています。各値はWebRTC VADライブラリの仕様に合わせて適切に設定されています。

---

`28-33`: **パラメータクラスの型制約は優れた実装です**

`Literal`型を使用してサンプルレートとフレーム持続時間を制限しており、WebRTC VADがサポートする値のみを許可する実装は優れています。これにより、無効な値が渡された場合に型チェック時にエラーを検出できます。

---

`45-46`: **適切なメソッド制約が実装されています**

`call`メソッドの実装を制限し、代わりに`stream_call`メソッドの使用を促す設計は適切です。これによりユーザーに明確な使用方法が示されています。

---

`60-63`: **適切なデータ長検証が実装されています**

オーディオデータの長さが必要なフレームサイズより短い場合のエラーハンドリングが適切に実装されています。これにより、実行時エラーを防ぐことができます。

---

`68-71`: **例外処理が適切に実装されています**

VAD処理中の例外をキャッチし、`from e`を使用して元の例外連鎖を保持する実装は適切です。これによりデバッグ時に完全なスタックトレースが利用可能になります。

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T06:58:02Z
user: login: coderabbitai[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T07:03:43Z

Review comments:
diff_hunk: @@ -0,0 +1,62 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    NORMAL = 1
+    LOW_BITRATE = 2
+    AGGRESSIVE = 3
+    VERY_AGGRESSIVE = 4
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.NORMAL
+    sample_rate: int = 16000
+    frame_duration_ms: int = 30
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self.vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariDataSet:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
+
+        buffer = io.BytesIO(audio.main)
+        frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
path: modules/webrtcvad/vad.py
user: login: Copilot
body: Consider verifying that the audio data has a length at least equal to frame_size before seeking to avoid potential runtime errors if the data is too short.
```suggestion
        frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
        if len(audio.main) < frame_size:
            raise ValueError(f"Audio data is too short. Expected at least {frame_size} bytes, but got {len(audio.main)} bytes.")
```
created_at: 2025-05-02T05:44:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -156,14 +157,14 @@ def list_audio_devices() -> None:
 # )
 
 
-# akariRouter.callModule(
-#     moduleType=audio.MicModule,
-#     data=akari.AkariData(),
-#     params=audio.MicModuleParams(
-#         streamDurationMilliseconds=1000,
-#         destructionMilliseconds=5000,
-#         callbackParams=audio.SpeakerModuleParams(),
-#     ),
-#     streaming=False,
-#     callback=audio.SpeakerModule,
-# )
+akariRouter.callModule(
+    moduleType=audio.MicModule,
+    data=akari.AkariData(),
+    params=audio.MicModuleParams(
+        streamDurationMilliseconds=1000,
+        destructionMilliseconds=5000,
+        callbackParams=audio.SpeakerModuleParams(),
+    ),
+    streaming=False,
+    callback=audio.SpeakerModule,
+)
path: main.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**音声モジュールの呼び出しが有効化されていますが、VADモジュールとの連携が不明確です**

`audio.MicModule`の呼び出しが有効化され、コールバックとして`audio.SpeakerModule`が設定されています。これは、マイクからの入力を直接スピーカーに出力するエコー処理のように見えます。

ただし、新しく追加したWebRTC VADモジュールがこの処理フローでどのように利用されるのかが不明確です。VADモジュールを音声処理パイプラインに組み込むための追加の変更が必要かもしれません。例えば：

1. `audio.MicModule`の出力を`webrtcvad.WebRTCVadModule`に渡す
2. VADの結果に基づいて条件分岐する
3. 発話が検出された場合のみ次の処理（例：STTやLLM）を実行する

このような処理フローを実装するための追加のコードが必要と思われます。

```diff
 akariRouter.callModule(
     moduleType=audio.MicModule,
     data=akari.AkariData(),
     params=audio.MicModuleParams(
         streamDurationMilliseconds=1000,
         destructionMilliseconds=5000,
-        callbackParams=audio.SpeakerModuleParams(),
+        callbackParams=webrtcvad.WebRTCVadParams(
+            mode=webrtcvad.WebRTCVadMode.NORMAL,
+            sample_rate=16000,
+            frame_duration_ms=30
+        ),
     ),
     streaming=False,
-    callback=audio.SpeakerModule,
+    callback=webrtcvad.WebRTCVadModule,
 )
+
+# VADの結果に基づいて処理を分岐する例
+def process_vad_result(data: akari.AkariData) -> None:
+    if data.last().bool and data.last().bool.main:  # 発話が検出された場合
+        # 音声をスピーカーに出力
+        akariRouter.callModule(
+            moduleType=audio.SpeakerModule,
+            data=data,
+            params=audio.SpeakerModuleParams(),
+            streaming=False,
+        )
+        
+        # 必要に応じてSTTや他の処理を実行
+        # ...
+
+# VADモジュールの結果を処理
+vad_data = akariRouter.callModule(
+    moduleType=webrtcvad.WebRTCVadModule,
+    data=audio_data,  # MicModuleから取得した音声データ
+    params=webrtcvad.WebRTCVadParams(),
+    streaming=True,
+    callback=process_vad_result,
+)
```


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T05:49:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    NORMAL = 1
+    LOW_BITRATE = 2
+    AGGRESSIVE = 3
+    VERY_AGGRESSIVE = 4
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.NORMAL
+    sample_rate: int = 16000
+    frame_duration_ms: int = 30
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self.vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariDataSet:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
+
+        buffer = io.BytesIO(audio.main)
+        frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
+        buffer.seek(-frame_size, io.SEEK_END)
+        audio_data = buffer.read(frame_size)
+
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**バッファ処理に追加のエラーハンドリングが必要です**

オーディオバッファからフレームを読み取る際、バッファサイズが必要なフレームサイズよりも小さい場合のエラーハンドリングが欠けています。データ不足のケースを処理するコードを追加してください。

```diff
buffer = io.BytesIO(audio.main)
frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
+# バッファサイズがフレームサイズ以上であることを確認
+buffer_size = len(audio.main)
+if buffer_size < frame_size:
+   raise ValueError(f"オーディオデータが不足しています。必要なサイズ: {frame_size} バイト、実際のサイズ: {buffer_size} バイト")

buffer.seek(-frame_size, io.SEEK_END)
audio_data = buffer.read(frame_size)
```




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        buffer = io.BytesIO(audio.main)
        frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
        # バッファサイズがフレームサイズ以上であることを確認
        buffer_size = len(audio.main)
        if buffer_size < frame_size:
            raise ValueError(
                f"オーディオデータが不足しています。必要なサイズ: {frame_size} バイト、実際のサイズ: {buffer_size} バイト"
            )

        buffer.seek(-frame_size, io.SEEK_END)
        audio_data = buffer.read(frame_size)
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 5d472d2 to 9e04321
created_at: 2025-05-02T05:49:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    NORMAL = 1
+    LOW_BITRATE = 2
+    AGGRESSIVE = 3
+    VERY_AGGRESSIVE = 4
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.NORMAL
+    sample_rate: int = 16000
+    frame_duration_ms: int = 30
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self.vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariDataSet:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
+
+        buffer = io.BytesIO(audio.main)
+        frame_size = int(params.sample_rate * params.frame_duration_ms / 1000)
+        buffer.seek(-frame_size, io.SEEK_END)
+        audio_data = buffer.read(frame_size)
+
+        is_speech = self.vad.is_speech(audio_data, params.sample_rate)
+        dataset = AkariDataSet()
+        dataset.bool = AkariDataSetType(is_speech)
+
+        return dataset
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**VAD処理は適切ですが、例外処理を追加してください**

VADの処理と結果のラッピングは適切に実装されていますが、`is_speech`メソッドから発生する可能性のある例外を処理するtry-except文を追加すると、より堅牢になります。

```diff
-is_speech = self.vad.is_speech(audio_data, params.sample_rate)
+try:
+   is_speech = self.vad.is_speech(audio_data, params.sample_rate)
+except Exception as e:
+   self.logger.error(f"VAD処理中にエラーが発生しました: {str(e)}")
+   raise ValueError(f"音声分析中にエラーが発生しました: {str(e)}")

dataset = AkariDataSet()
dataset.bool = AkariDataSetType(is_speech)

return dataset
```




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        try:
            is_speech = self.vad.is_speech(audio_data, params.sample_rate)
        except Exception as e:
            self.logger.error(f"VAD処理中にエラーが発生しました: {str(e)}")
            raise ValueError(f"音声分析中にエラーが発生しました: {str(e)}")

        dataset = AkariDataSet()
        dataset.bool = AkariDataSetType(is_speech)

        return dataset
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T05:49:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    VERY_SENSITIVE = 0
+    SENSITIVE = 1
+    LOW_SENSITIVE = 2
+    STRICT = 3
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
+    sample_rate: int = 16000
+    frame_duration_ms: int = 30
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self.vad = webrtcvad.Vad()
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**VADモードの設定処理が欠けています**

コンストラクタでVADインスタンスを作成していますが、パラメータとして受け取った`mode`を設定していません。`stream_call`メソッドでは`params.mode`が利用されず、デフォルトの感度設定のままになります。


`stream_call`メソッドの中でVADのモード設定を行うように修正してください：

```diff
def stream_call(
    self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
) -> AkariDataSet:
    audio = data.last().audio
    if audio is None:
        raise ValueError("Audio data is missing or empty.")
    
+   # VADモードを設定
+   self.vad.set_mode(params.mode.value)
    
    buffer = io.BytesIO(audio.main)
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def stream_call(
    self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
) -> AkariDataSet:
    audio = data.last().audio
    if audio is None:
        raise ValueError("Audio data is missing or empty.")

    # VADモードを設定
    self.vad.set_mode(params.mode.value)

    buffer = io.BytesIO(audio.main)
    # … (以降の処理はそのまま)
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 5d472d2 to 9e04321
created_at: 2025-05-02T05:54:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    VERY_SENSITIVE = 0
+    SENSITIVE = 1
+    LOW_SENSITIVE = 2
+    STRICT = 3
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
+    sample_rate: int = 16000
+    frame_duration_ms: int = 30
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**サンプルレートとフレーム持続時間のバリデーションが必要です**

WebRTC VADは特定のサンプルレートとフレーム持続時間のみをサポートしています：
- サンプルレート: 8000, 16000, 32000, 48000 Hz
- フレーム持続時間: 10, 20, 30 ms

現在のコードでは、これらの値が有効かどうかの検証を行っていないため、無効な値が渡された場合にランタイムエラーが発生する可能性があります。


パラメータのバリデーションを追加することをお勧めします：

```diff
@dataclasses.dataclass
class _WebRTCVadParams:
    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
    sample_rate: int = 16000
    frame_duration_ms: int = 30
+
+   def __post_init__(self):
+       valid_rates = [8000, 16000, 32000, 48000]
+       valid_durations = [10, 20, 30]
+       
+       if self.sample_rate not in valid_rates:
+           raise ValueError(f"サンプルレートは {valid_rates} のいずれかである必要があります。現在の値: {self.sample_rate}")
+           
+       if self.frame_duration_ms not in valid_durations:
+           raise ValueError(f"フレーム持続時間は {valid_durations} msのいずれかである必要があります。現在の値: {self.frame_duration_ms}")
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
@dataclasses.dataclass
class _WebRTCVadParams:
    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
    sample_rate: int = 16000
    frame_duration_ms: int = 30

    def __post_init__(self):
        valid_rates = [8000, 16000, 32000, 48000]
        valid_durations = [10, 20, 30]
        
        if self.sample_rate not in valid_rates:
            raise ValueError(
                f"サンプルレートは {valid_rates} のいずれかである必要があります。現在の値: {self.sample_rate}"
            )
        if self.frame_duration_ms not in valid_durations:
            raise ValueError(
                f"フレーム持続時間は {valid_durations} msのいずれかである必要があります。現在の値: {self.frame_duration_ms}"
            )
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T05:54:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,80 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any, Literal
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleParams,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    VERY_SENSITIVE = 0
+    SENSITIVE = 1
+    LOW_SENSITIVE = 2
+    STRICT = 3
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
+    sample_rate: Literal[8000, 16000, 32000, 48000] = 16000
+    frame_duration_ms: Literal[10, 20, 30] = 30
+    callback_params: AkariModuleParams | None = None
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self._vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariData:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
+
+        self._vad.set_mode(params.mode.value)
+
+        buffer = io.BytesIO(audio.main)
+        frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * 2)
+
+        if len(audio.main) < frame_size_bytes:
+            raise ValueError(
+                f"Audio data is too short. Expected at least {frame_size_bytes} bytes, but got {len(audio.main)} bytes."
+            )
+
+        buffer.seek(-frame_size_bytes, io.SEEK_END)
+        audio_data = buffer.read(frame_size_bytes)
+
+        try:
+            is_speech = self._vad.is_speech(audio_data, params.sample_rate)
+        except Exception as e:
+            raise ValueError(f"Error processing audio data with WebRTC VAD: {e}")
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**例外処理の連鎖を保持するように修正してください**

例外をキャッチして再スローする際、`from e`を使用して元の例外連鎖を保持するべきです。これによりデバッグ時に完全なスタックトレースが利用可能になります。


```diff
try:
    is_speech = self._vad.is_speech(audio_data, params.sample_rate)
except Exception as e:
-    raise ValueError(f"Error processing audio data with WebRTC VAD: {e}")
+    raise ValueError(f"Error processing audio data with WebRTC VAD: {e}") from e
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        try:
            is_speech = self._vad.is_speech(audio_data, params.sample_rate)
        except Exception as e:
            raise ValueError(f"Error processing audio data with WebRTC VAD: {e}") from e
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

71-71: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T06:52:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,80 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any, Literal
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleParams,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    VERY_SENSITIVE = 0
+    SENSITIVE = 1
+    LOW_SENSITIVE = 2
+    STRICT = 3
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
+    sample_rate: Literal[8000, 16000, 32000, 48000] = 16000
+    frame_duration_ms: Literal[10, 20, 30] = 30
+    callback_params: AkariModuleParams | None = None
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self._vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariData:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**オーディオデータの存在チェック改善の余地があります**

現在のコードでは`audio`が`None`かどうかをチェックしていますが、`audio.main`が適切な型（bytes）であるかの検証が行われていません。


型チェックを追加して堅牢性を高めることを推奨します：

```diff
audio = data.last().audio
if audio is None:
    raise ValueError("Audio data is missing or empty.")
+if not hasattr(audio, 'main') or not isinstance(audio.main, bytes):
+    raise ValueError("Audio data is invalid or not in the expected format.")
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        audio = data.last().audio
        if audio is None:
            raise ValueError("Audio data is missing or empty.")
        if not hasattr(audio, 'main') or not isinstance(audio.main, bytes):
            raise ValueError("Audio data is invalid or not in the expected format.")
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T06:52:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,80 @@
+import dataclasses
+import io
+from enum import Enum
+from typing import Any, Literal
+
+import pyaudio
+import webrtcvad
+
+from akari import (
+    AkariData,
+    AkariDataSet,
+    AkariDataSetType,
+    AkariLogger,
+    AkariModule,
+    AkariModuleParams,
+    AkariModuleType,
+    AkariRouter,
+)
+
+
+class _WebRTCVadMode(Enum):
+    VERY_SENSITIVE = 0
+    SENSITIVE = 1
+    LOW_SENSITIVE = 2
+    STRICT = 3
+
+
+@dataclasses.dataclass
+class _WebRTCVadParams:
+    mode: _WebRTCVadMode = _WebRTCVadMode.VERY_SENSITIVE
+    sample_rate: Literal[8000, 16000, 32000, 48000] = 16000
+    frame_duration_ms: Literal[10, 20, 30] = 30
+    callback_params: AkariModuleParams | None = None
+
+
+class _WebRTCVadModule(AkariModule):
+    def __init__(
+        self,
+        router: AkariRouter,
+        logger: AkariLogger,
+    ) -> None:
+        super().__init__(router, logger)
+        self._vad = webrtcvad.Vad()
+
+    def call(self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None) -> AkariDataSet:
+        raise NotImplementedError("WebRTCVadModule does not support call method. Use stream_call instead.")
+
+    def stream_call(
+        self, data: AkariData, params: _WebRTCVadParams, callback: AkariModuleType | None = None
+    ) -> AkariData:
+        audio = data.last().audio
+        if audio is None:
+            raise ValueError("Audio data is missing or empty.")
+
+        self._vad.set_mode(params.mode.value)
+
+        buffer = io.BytesIO(audio.main)
+        frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * 2)
path: modules/webrtcvad/vad.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**フレームサイズ計算の仮定を明示してください**

現在のコードでは、フレームサイズを計算する際に `* 2` を使用していますが、これは16ビットオーディオ（サンプルあたり2バイト）を仮定しています。この仮定は明示的にドキュメント化するか、設定可能にすべきです。


以下の修正を提案します：

```diff
buffer = io.BytesIO(audio.main)
-frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * 2)
+# 16ビットオーディオ（サンプルあたり2バイト）を仮定
+bytes_per_sample = 2
+frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * bytes_per_sample)
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        buffer = io.BytesIO(audio.main)
        # 16ビットオーディオ（サンプルあたり2バイト）を仮定
        bytes_per_sample = 2
        frame_size_bytes = int(params.sample_rate * params.frame_duration_ms / 1000 * bytes_per_sample)
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T06:58:01Z
author_association: CONTRIBUTOR

PR comments:
user: login: linear[bot]
created_at: 2025-05-02T05:44:17Z
body: <p><a href="https://linear.app/kizuna-org/issue/AKARI-38/web-vad-moduleを作る">AKARI-38 Web VAD Moduleを作る</a></p>
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-02T05:49:37Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

この変更では、WebRTC Voice Activity Detection（VAD）機能をAkariフレームワークに統合する新しいモジュールが追加されました。`modules/webrtcvad`ディレクトリにVADモジュールが実装され、`main.py`でこのモジュールがルーターに登録されました。また、`akari/data.py`の`_AkariDataSet`クラスにboolean型データセット用属性が追加され、`pyproject.toml`には`webrtcvad`ライブラリが依存関係として追加されました。既存のモジュール呼び出しの一部はコメントアウトされ、音声入力モジュールのみが有効化されています。さらに、`modules/audio/mic.py`のマイクモジュールに新たなコールバックチェーン用のパラメータが追加されました。

## Changes

| ファイル/グループ                      | 変更内容の概要                                                                                                   |
|--------------------------------------|----------------------------------------------------------------------------------------------------------------|
| akari/data.py                        | `_AkariDataSet`クラスに`bool: _AkariDataSetType[bool] | None`属性を追加。                                        |
| main.py                             | `webrtcvad`のインポートを追加し、`WebRTCVadModule`をモジュール辞書に登録。複数の`callModule`呼び出しをコメントアウトし、`audio.MicModule`のみ有効化。マイクモジュールのコールバック設定をWebRTCVad用に変更し、`callback_callback`を追加。 |
| modules/webrtcvad/__init__.py        | `vad`モジュールから`WebRTCVadMode`、`WebRTCVadModule`、`WebRTCVadParams`をエイリアス付きで再エクスポート。       |
| modules/webrtcvad/vad.py             | WebRTC VAD機能を持つ`_WebRTCVadModule`クラス、VADモード列挙体、パラメータ用データクラスを新規追加。              |
| pyproject.toml                      | 依存関係に`webrtcvad = "^2.0.10"`と`setuptools = "^80.1.0"`を追加。                                           |
| modules/audio/mic.py                 | `_MicModuleParams`に`callback_callback`フィールドを追加。`_MicModule.call`で内部呼び出しのコールバックに`callback_callback`を渡すよう変更。 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant MicModule
    participant WebRTCVadModule
    participant PrintModule

    User->>MicModule: 音声入力開始
    MicModule->>WebRTCVadModule: 音声データを送信（stream_call）
    WebRTCVadModule->>WebRTCVadModule: VAD判定処理
    WebRTCVadModule-->>MicModule: 音声区間判定結果（bool型データセット）
    alt 音声検出時
        WebRTCVadModule->>PrintModule: コールバック呼び出し
    end
```

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=kizuna-org/akari-poc&utm_content=83):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR