Skip to content

fix(assemblyai): tolerate None in language and language_codes options - #8470

Open
pylrn wants to merge 1 commit into
livekit:mainfrom
pylrn:fix/assemblyai-none-language-handling
Open

pylrn wants to merge 1 commit into
livekit:mainfrom
pylrn:fix/assemblyai-none-language-handling

Conversation

@pylrn

@pylrn pylrn commented Nov 9, 2026

Copy link
Copy Markdown

Problem

  1. TTS(language=None) crashes: In TTS.__init__, language was typed NotGivenOr[str] and processed via _language_name(language) if is_given(language) else None. When a caller explicitly passed language=None (e.g., from an agent configuration dictionary or factory defaulting optional arguments to None), is_given(None) evaluated to True, passing None to _language_name which called None.strip() and raised AttributeError / ValueError: language cannot be empty. This was also inconsistent with TTS.update_options(language=None), which explicitly supports None to reset back to the server's default language.
  2. STT(language_codes=None) and STT(language_code=None) crashes: In STT.__init__, an explicit language_codes=None or language_code=None failed the _U3_PRO_MODELS check on non-U3-Pro models (because is_given(None) is True), and for U3-Pro models _normalize_language_codes attempted to iterate over None (for code in language_codes), raising TypeError: 'NoneType' object is not iterable.
  3. update_options(language_codes=None) crashes: Calling update_options(language_codes=None) on STT or SpeechStream passed None directly into _normalize_language_codes, raising TypeError: 'NoneType' object is not iterable instead of clearing steering (normalized to []), unlike "" or [].

Changes

  • TTS:
    • Type language parameter as NotGivenOr[str | None] = NOT_GIVEN in TTS.__init__.
    • Guard _language_name(language) with if is_given(language) and language is not None else None so explicit None leaves language to the server default (matching update_options(language=None)).
  • STT:
    • Update language_code and language_codes parameter types to accept None in STT.__init__, STT.update_options, and SpeechStream.update_options.
    • In _normalize_language_codes, return [] when language_codes is None (matching "" and []).
    • In STT.__init__, treat language_code and language_codes as NOT_GIVEN when empty (including None), keeping them exempt from the non-U3-Pro model gate at construction.
  • Tests:
    • Added unit test in tests/test_plugin_assemblyai_tts.py verifying TTS(language=None) leaves _opts.language as None.
    • Added unit tests in tests/test_plugin_assemblyai_stt.py verifying STT(language_codes=None), STT(language_code=None), and update_options(language_codes=None) behave as unset/cleared steering.

Testing

  • Ran uv run pytest tests/test_plugin_assemblyai_tts.py tests/test_plugin_assemblyai_stt.py (178 passed).
  • Ran uv run ruff check and uv run ruff format --check (clean).
  • Ran uv run mypy livekit-plugins/livekit-plugins-assemblyai/ (0 issues in 5 source files).

- In TTS.__init__, allow language=None without raising AttributeError/ValueError, leaving language to the server default (matching update_options(language=None)).
- In STT.__init__, treat language_code=None and language_codes=None as unset (NOT_GIVEN) rather than raising ValueError on non-U3-Pro models or TypeError in normalization.
- In STT and SpeechStream update_options, allow language_codes=None to clear language steering (normalized to []), matching [] and "".
- Add test coverage in test_plugin_assemblyai_tts.py and test_plugin_assemblyai_stt.py.
@pylrn
pylrn requested a review from a team as a code owner November 9, 2026 15:20
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Devin Review

Comment on lines +275 to 278
if is_given(language_code) and not language_code:
language_code = NOT_GIVEN
if is_given(language_codes) and not language_codes:
language_codes = NOT_GIVEN

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Unset language blocks valid steering

With language_code=None and language_codes=['es'], STT raises a mutual-exclusion error before clearing the unset value. The reverse combination fails too, so callers passing both optional fields cannot select a language.

Learn more

The constructor treats None as an unset language option, but its mutual-exclusion check runs before these two normalization lines. is_given(None) is true, so an explicitly unset field triggers the check when the other field has a real value. The same mismatch applies in either argument order. The check and the new normalization must use the same meaning of unset.

Example: STT(api_key="k", language_code=None, language_codes=["es"]) raises ValueError instead of storing ["es"] in _opts.language_codes.

Recommended fix: Normalize both empty or None language arguments to NOT_GIVEN before the mutual-exclusion check in STT.__init__; retain the error when both normalized arguments contain actual codes.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

This branch has not been deployed

No deployments
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.

2 participants