Skip to content

Adding InyaAI plugin#4512

Open
Gnani-AI-Mintlify wants to merge 6 commits intolivekit:mainfrom
Gnani-AI-Mintlify:main
Open

Adding InyaAI plugin#4512
Gnani-AI-Mintlify wants to merge 6 commits intolivekit:mainfrom
Gnani-AI-Mintlify:main

Conversation

@Gnani-AI-Mintlify
Copy link

@Gnani-AI-Mintlify Gnani-AI-Mintlify commented Jan 13, 2026

We have integrated Gnani's Speech-to-Text with LiveKit Agents. The service provides low-latency, high-accuracy transcription for Indian languages and accents, supporting English (Indian), Hindi, Tamil, Telugu, and more. It offers secure API key–based authentication and real-time transcription optimized for conversational and voice-based applications.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Gnani Speech-to-Text plugin supporting multiple Indian languages including English, Hindi, Gujarati, Tamil, Kannada, Telugu, Marathi, Bengali, Malayalam, and Punjabi, with code-switching capability.
    • Configuration via environment variables or direct parameter initialization.
  • Documentation

    • Added comprehensive plugin documentation with installation, configuration, usage examples, API details, and limitations.
  • Tests

    • Added extensive test suite covering initialization, credential validation, recognition, error handling, and language support.

✏️ Tip: You can customize this high-level summary in your review settings.

@CLAassistant
Copy link

CLAassistant commented Jan 13, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

This PR adds a new Gnani speech-to-text plugin for LiveKit agents, including plugin infrastructure, STT implementation with API integration, comprehensive test coverage, documentation, and packaging configuration.

Changes

Cohort / File(s) Summary
Plugin Package Structure
livekit-plugins/livekit-plugins-gnani/livekit/__init__.py, livekit-plugins/livekit-plugins-gnani/livekit/plugins/__init__.py
Namespace package initialization using pkgutil.extend_path() to enable cross-directory package sharing.
Core Plugin Implementation
livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/__init__.py, livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/stt.py, livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/log.py, livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/models.py, livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/version.py
STT implementation with Gnani API integration, credential management, audio processing (WAV conversion), multipart form submission, response parsing, comprehensive error handling, language support via Literal type, and module logging. Plugin auto-registers on import.
Packaging & Metadata
livekit-plugins/livekit-plugins-gnani/pyproject.toml, livekit-plugins/livekit-plugins-gnani/setup.py, livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/py.typed
Project configuration with hatchling and setuptools, version resolution, dependencies (livekit-agents, aiohttp), and PEP 561 typing marker.
Documentation
livekit-plugins/livekit-plugins-gnani/README.md
User-facing documentation covering installation, features, multilingual support (10\+ language codes), configuration, usage examples, API endpoint details, limitations, and license.
Build & Test Infrastructure
CONTRIBUTING.md, tests/docker-compose.yml, tests/test_plugin_gnani_stt.py
Updated mypy configuration to include gnani plugin; Docker environment setup with Gnani credentials and host mapping; comprehensive test suite with 18\+ test methods covering initialization, credential validation, recognition flow, error handling, and HTTP session management.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/Test
    participant STT as Gnani STT Plugin
    participant Audio as Audio Processor
    participant HTTP as HTTP Client
    participant API as Gnani API
    
    Client->>STT: recognize(audio_buffer, language?)
    STT->>Audio: collect frames & convert to WAV
    Audio-->>STT: wav_bytes
    STT->>STT: build multipart form<br/>(language_code, audio_file)
    STT->>HTTP: POST with auth headers<br/>(api_key, org_id, user_id, request_id)
    HTTP->>API: HTTP POST request
    API-->>HTTP: JSON response<br/>(success, transcript)
    HTTP-->>STT: response body
    STT->>STT: validate response<br/>parse transcript
    STT->>STT: create SpeechEvent<br/>(FINAL_TRANSCRIPT, SpeechData)
    STT-->>Client: SpeechEvent
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • davidzhao
  • tinalenguyen
  • longcw

Poem

🐰 Hop, hop, the Gnani files arise,
New plugins bloom beneath the skies,
With voices transcribed, tests in place,
LiveKit agents run with grace!
Credentials tucked, APIs aligned—
A STT marvel, finely designed! 🎙️✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Adding InyaAI plugin' is misleading; the actual implementation adds a Gnani plugin (branded as InyaAI), but all code references and module names use 'gnani' not 'InyaAI'. Clarify the title to reference 'Gnani' or 'gnani' to match the actual implementation, e.g., 'Add Gnani STT plugin' or 'Add livekit-plugins-gnani package'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f503ab7 and 2c65ad1.

📒 Files selected for processing (3)
  • CONTRIBUTING.md
  • tests/docker-compose.yml
  • tests/test_plugin_gnani_stt.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • CONTRIBUTING.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings

Files:

  • tests/test_plugin_gnani_stt.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: unit-tests
🔇 Additional comments (8)
tests/docker-compose.yml (2)

63-65: LGTM — Gnani env passthrough is wired for tests.


86-86: Host override for api.vachana.ai looks consistent with toxiproxy routing.

tests/test_plugin_gnani_stt.py (6)

1-41: Nice fixture setup and deterministic audio buffer.


50-105: Good coverage for initialization, credential validation, and option updates.


106-190: Confirm aiohttp async context-manager mocks are awaitable.
If STT.recognize uses async with session.post(...), the mocked context manager needs an async __aenter__ to avoid await errors.

✅ Example adjustment (apply similarly to other aiohttp mocks)
 with patch.object(aiohttp.ClientSession, "post") as mock_post:
-    mock_post.return_value.__aenter__.return_value = mock_response
+    mock_ctx = AsyncMock()
+    mock_ctx.__aenter__.return_value = mock_response
+    mock_ctx.__aexit__.return_value = None
+    mock_post.return_value = mock_ctx

192-239: Capabilities, speech-event creation, and supported-language coverage look solid.


241-251: LGTM for custom base URL and session reuse validation.


43-45: No action needed. pytest-asyncio is configured with asyncio_mode = "auto" in pyproject.toml, which enables automatic detection and execution of async tests without requiring explicit @pytest.mark.asyncio markers. The async test method will execute correctly as written.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@CONTRIBUTING.md`:
- Around line 95-96: Update the incorrect plugin module name
`livekit.plugins.inyaai` to `livekit.plugins.gnani` wherever referenced (e.g.,
the two lines in CONTRIBUTING.md and the import in
tests/test_plugin_inyaai_stt.py); change the import path in the test from `from
livekit.plugins.inyaai...` to `from livekit.plugins.gnani...` and update any
test identifiers or docstrings that reference "inyaai" so they match the gnani
package naming.

In `@tests/test_plugin_inyaai_stt.py`:
- Around line 146-161: The test test_recognize_api_error should expect the HTTP
error exception raised by STT.recognize, not the network error; change the
assertion to expect APIStatusError (the exception raised for non-200 responses
in livekit.plugins.gnani.stt.STT.recognize) instead of APIConnectionError so the
test matches the implementation that throws APIStatusError for status != 200.
🧹 Nitpick comments (6)
livekit-plugins/livekit-plugins-gnani/README.md (1)

48-71: Basic usage example has incomplete imports.

The example references VoiceAssistant, silero, and tts without showing their imports. This could confuse users trying to copy the example.

📝 Suggested fix for imports
 ```python
 from livekit.agents import AutoSubscribe, JobContext, WorkerOptions, cli, llm
+from livekit.agents.pipeline import VoiceAssistant
 from livekit.plugins import gnani
+from livekit.plugins import silero
+from livekit.plugins import openai as tts
tests/test_plugin_inyaai_stt.py (1)

146-175: Move APIConnectionError import to module level.

APIConnectionError is imported multiple times inside individual test methods (lines 148, 167, 181). Moving it to the top-level imports improves readability and follows Python best practices.

♻️ Suggested fix

Add to the imports at the top of the file:

 from livekit import rtc
 from livekit.agents import stt
+from livekit.agents import APIConnectionError
 from livekit.agents.stt import SpeechData, SpeechEventType
 from livekit.plugins.inyaai import STT

Then remove the local imports from each test method.

livekit-plugins/livekit-plugins-gnani/pyproject.toml (1)

25-37: Consider adding Python 3.13 classifier.

The classifiers list Python 3.9 through 3.12. If Python 3.13 is supported (which is likely given >=3.9.0 requirement), consider adding the classifier for discoverability on PyPI.

📝 Suggested addition
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Programming Language :: Python :: 3 :: Only",
livekit-plugins/livekit-plugins-gnani/setup.py (1)

31-32: Use context manager for file reading.

The file handle for README.md is not explicitly closed. While Python's garbage collector will eventually close it, using a context manager is the recommended practice.

♻️ Suggested fix
+with open("README.md", encoding="utf-8") as readme_file:
+    long_description = readme_file.read()
+
 setup(
     name="livekit-plugins-gnani",
     version=about["__version__"],
     description="Agent Framework plugin for Gnani (Vachana) Speech-to-Text API.",
-    long_description=open("README.md").read(),
+    long_description=long_description,
     long_description_content_type="text/markdown",
livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/stt.py (2)

109-112: Guard against closed aiohttp sessions.
If a provided ClientSession is already closed, the current logic will reuse it and fail later with a less actionable error. Consider checking .closed and recreating (or raising explicitly).

♻️ Proposed fix
-    def _ensure_session(self) -> aiohttp.ClientSession:
-        if not self._session:
-            self._session = utils.http_context.http_session()
-        return self._session
+    def _ensure_session(self) -> aiohttp.ClientSession:
+        if self._session is None or self._session.closed:
+            self._session = utils.http_context.http_session()
+        return self._session

122-222: Normalize language before adding to form data.
If GnaniLanguages is a plain Enum, FormData may serialize the enum name rather than the language code. Coercing to .value when present avoids incorrect payloads.

♻️ Proposed fix
-            form_data.add_field("language_code", target_language)
+            language_code = (
+                target_language.value if hasattr(target_language, "value") else target_language
+            )
+            form_data.add_field("language_code", language_code)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffee15c and d221467.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • CONTRIBUTING.md
  • livekit-plugins/livekit-plugins-gnani/README.md
  • livekit-plugins/livekit-plugins-gnani/livekit/__init__.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/__init__.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/__init__.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/log.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/models.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/py.typed
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/stt.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/version.py
  • livekit-plugins/livekit-plugins-gnani/pyproject.toml
  • livekit-plugins/livekit-plugins-gnani/setup.py
  • tests/docker-compose.yml
  • tests/test_plugin_inyaai_stt.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings

Files:

  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/__init__.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/models.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/version.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/stt.py
  • tests/test_plugin_inyaai_stt.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/log.py
  • livekit-plugins/livekit-plugins-gnani/livekit/__init__.py
  • livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/__init__.py
  • livekit-plugins/livekit-plugins-gnani/setup.py
🧠 Learnings (1)
📚 Learning: 2026-01-16T07:44:56.353Z
Learnt from: CR
Repo: livekit/agents PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T07:44:56.353Z
Learning: Follow the Plugin System pattern where plugins in livekit-plugins/ are separate packages registered via the Plugin base class

Applied to files:

  • CONTRIBUTING.md
  • livekit-plugins/livekit-plugins-gnani/livekit/__init__.py
🧬 Code graph analysis (2)
tests/test_plugin_inyaai_stt.py (4)
livekit-agents/livekit/agents/stt/stt.py (3)
  • SpeechData (53-61)
  • SpeechEventType (32-49)
  • SpeechEvent (70-74)
tests/conftest.py (1)
  • job_process (19-22)
livekit-agents/livekit/agents/_exceptions.py (1)
  • APIConnectionError (84-88)
livekit-agents/livekit/agents/utils/http_context.py (1)
  • http_session (40-51)
livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/__init__.py (2)
livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/stt.py (1)
  • STT (46-242)
livekit-agents/livekit/agents/plugin.py (2)
  • Plugin (13-56)
  • register_plugin (31-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: type-check (3.9)
  • GitHub Check: type-check (3.13)
  • GitHub Check: unit-tests
🔇 Additional comments (24)
livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/py.typed (1)

1-1: LGTM!

Correct PEP 561 marker file for indicating type hint support in the package.

tests/docker-compose.yml (2)

63-65: LGTM!

Environment variables for InyaAI credentials follow the established pattern used by other plugins in this compose file.


86-86: LGTM!

The extra_hosts mapping for api.vachana.ai correctly routes the Gnani API through toxiproxy for integration testing, consistent with other service mappings.

livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/log.py (1)

1-17: LGTM!

Standard logging setup following the established pattern in other LiveKit plugins. The logger name correctly matches the module path.

livekit-plugins/livekit-plugins-gnani/livekit/__init__.py (1)

1-2: LGTM!

Correct namespace package setup using pkgutil.extend_path, allowing the livekit namespace to be shared across multiple plugin packages. This follows the established plugin system pattern. Based on learnings, plugins in livekit-plugins/ are separate packages, and this namespace approach supports that architecture.

livekit-plugins/livekit-plugins-gnani/livekit/plugins/__init__.py (1)

1-2: LGTM!

Correct namespace package initialization for livekit.plugins, consistent with the parent namespace setup.

livekit-plugins/livekit-plugins-gnani/README.md (1)

1-135: LGTM!

The documentation is comprehensive, covering installation, features, supported languages, configuration options, and usage examples. The API details and limitations sections are particularly helpful for users evaluating this plugin.

tests/test_plugin_inyaai_stt.py (3)

26-41: LGTM!

The audio_buffer fixture correctly creates a 1-second audio frame with proper 16-bit PCM parameters (16kHz, mono). This provides a realistic test fixture for STT recognition tests.


106-125: LGTM!

The recognition success test properly mocks the API response and verifies all expected fields of the SpeechEvent including type, alternatives count, text, language, and confidence.


227-245: LGTM!

Good coverage of all supported languages including the code-switching variant (en-IN,hi-IN). This ensures the STT accepts the full range of documented language options.

livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/version.py (1)

1-15: LGTM!

Simple and clean version module following standard Python packaging conventions.

livekit-plugins/livekit-plugins-gnani/pyproject.toml (1)

38-41: LGTM!

Dependencies are appropriately specified with minimum versions, allowing flexibility for users while ensuring compatibility with the required features.

livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/models.py (1)

17-29: LGTM!

The GnaniLanguages type alias provides good type safety and IDE auto-completion for supported language codes. The inline comments documenting each language code are helpful.

livekit-plugins/livekit-plugins-gnani/setup.py (2)

22-25: Version loading pattern is acceptable.

The exec() pattern for loading version from a separate file is a common setuptools idiom that avoids import-time side effects. This is fine for packaging purposes.


37-42: LGTM!

The namespace package discovery and dependencies are correctly configured, matching the pyproject.toml specification for consistency.

livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/__init__.py (4)

15-26: Clear module contract and exports.
Docstring and __all__ make the public surface explicit and tidy.


34-36: Plugin initialization wiring looks good.
Constructor forwards name/version/package/logger cleanly.


39-39: Confirm import-time registration happens on the main thread.
Plugin.register_plugin raises outside the main thread; please verify this module is only imported on the main thread or registration is deferred to a controlled startup path.


41-48: Doc cleanup for unexported symbols is fine.
__pdoc__ filtering aligns with the explicit __all__.

livekit-plugins/livekit-plugins-gnani/livekit/plugins/gnani/stt.py (5)

41-43: Options dataclass is clean and minimal.


46-100: Credential validation and initialization look solid.
Clear env fallbacks and explicit errors.


101-107: Explicit model/provider identifiers are good.


114-120: Option updates are straightforward.


223-242: SpeechEvent assembly is clear.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants