Skip to content

Conversation

@lukeocodes
Copy link
Contributor

Summary

This PR fixes the WebSocket clients (Listen V1 and V2) to properly handle parameters that accept both single strings and arrays of strings, as defined in the AsyncAPI and OpenAPI specifications.

Problem

When passing arrays to WebSocket connect methods, they were incorrectly URL-encoded as a single stringified array:

keyterm=%5B%27term1%27%2C%20%27term2%27%5D  ❌ Wrong - causes HTTP 400

This caused the API to reject requests with multiple keyterms, keywords, tags, etc.

Solution

Arrays are now properly expanded into multiple query parameters:

keyterm=term1&keyterm=term2  ✅ Correct

The fix adds runtime type checking to detect arrays and iterates over each element, adding them as separate query parameters. This matches the behavior of the REST API clients and aligns with the OpenAPI spec which defines these parameters with style: form and explode: true.

Fixed Parameters

Listen V2 (/v2/listen)

  • keyterm: now accepts string | string[]
  • tag: now accepts string | string[]

Listen V1 (/v1/listen)

  • keyterm: now accepts string | string[]
  • keywords: now accepts string | string[]
  • extra: now accepts string | string[]
  • redact: now accepts string | string[]
  • replace: now accepts string | string[]
  • search: now accepts string | string[]
  • tag: now accepts string | string[]

Changes Made

  1. Updated client method signatures to accept typing.Union[str, typing.Sequence[str]] for affected parameters
  2. Added runtime type checking to detect and properly handle arrays:
    if keyterm is not None:
        if isinstance(keyterm, (list, tuple)):
            for term in keyterm:
                query_params = query_params.add("keyterm", str(term))
        else:
            query_params = query_params.add("keyterm", keyterm)
  3. Updated type definitions to reflect proper union types
  4. Added documentation for array parameter support in method docstrings

Usage Examples

Before (Broken)

from deepgram import DeepgramClient

client = DeepgramClient(api_key="YOUR_KEY")
terms = ["term1", "term2", "term3"]

# This would fail with HTTP 400
with client.listen.v2.connect(
    model="flux-general-en",
    keyterm=terms  # ❌ Incorrectly encoded
) as connection:
    pass

After (Fixed)

from deepgram import DeepgramClient

client = DeepgramClient(api_key="YOUR_KEY")

# Single string (works as before)
with client.listen.v2.connect(
    model="flux-general-en",
    keyterm="Snuffleupagus"
) as connection:
    pass

# Array of strings (now works!)
with client.listen.v2.connect(
    model="flux-general-en",
    keyterm=["Snuffleupagus", "BigBird", "OscarTheGrouch"],
    tag=["production", "client-xyz"]
) as connection:
    pass

# Listen V1 with multiple array parameters
with client.listen.v1.connect(
    model="nova-3",
    keyterm=["term1", "term2"],
    keywords=["speech:2", "recognition:2"],
    search=["hello", "world"],
    tag=["dev", "test"]
) as connection:
    pass

Backward Compatibility

Fully backward compatible: Existing code that passes single strings continues to work exactly as before. This only adds new functionality for arrays.

Testing

Tested with:

  • Single string values (existing behavior)
  • Arrays of strings (new functionality)
  • Mixed usage across multiple parameters

The generated query strings now match the expected format:

keyterm=term1&keyterm=term2&keyterm=term3&tag=production&tag=staging

Modified Files

  • src/deepgram/listen/v1/client.py - Updated V1Client.connect() and AsyncV1Client.connect()
  • src/deepgram/listen/v2/client.py - Updated V2Client.connect() and AsyncV2Client.connect()
  • src/deepgram/types/listen_v1keyterm.py - Type definition
  • src/deepgram/types/listen_v1keywords.py - Type definition
  • src/deepgram/types/listen_v1extra.py - Type definition
  • src/deepgram/types/listen_v1redact.py - Type definition
  • src/deepgram/types/listen_v1replace.py - Type definition
  • src/deepgram/types/listen_v1search.py - Type definition
  • src/deepgram/types/listen_v1tag.py - Type definition
  • src/deepgram/types/listen_v2tag.py - Type definition

Closes

Fixes #648
Fixes #629
Related to #616

Update WebSocket clients to properly handle parameters that accept both
single strings and arrays of strings. Arrays are now correctly expanded
into multiple query parameters (e.g., keyterm=term1&keyterm=term2) instead
of being URL-encoded as a single stringified array.

Fixed parameters in Listen V2:
- keyterm: now accepts string | string[]
- tag: now accepts string | string[]

Fixed parameters in Listen V1:
- keyterm: now accepts string | string[]
- keywords: now accepts string | string[]
- extra: now accepts string | string[]
- redact: now accepts string | string[]
- replace: now accepts string | string[]
- search: now accepts string | string[]
- tag: now accepts string | string[]

Changes:
- Updated client method signatures to accept Union[str, Sequence[str]]
- Added runtime type checking to detect and iterate over arrays
- Updated type definitions to reflect proper union types
- Added documentation for array parameter support

This fix ensures consistency with the AsyncAPI/OpenAPI specs and matches
the behavior of the REST API clients.

Fixes #648
Fixes #629
Related to #616
Copilot AI review requested due to automatic review settings January 29, 2026 20:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug in the WebSocket clients (Listen V1 and V2) where array parameters were incorrectly URL-encoded as stringified arrays instead of being expanded into multiple query parameters. The fix ensures arrays are properly handled according to the OpenAPI specification (style: form, explode: true).

Changes:

  • Updated type definitions for affected parameters to accept Union[str, Sequence[str]]
  • Added runtime type checking to detect arrays and iterate over elements
  • Updated method signatures and docstrings to reflect array support

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/deepgram/types/listen_v2tag.py Updated type definition to support arrays
src/deepgram/types/listen_v1tag.py Updated type definition to support arrays
src/deepgram/types/listen_v1search.py Updated type definition to support arrays
src/deepgram/types/listen_v1replace.py Updated type definition to support arrays
src/deepgram/types/listen_v1redact.py Updated type definition to support arrays while preserving literals
src/deepgram/types/listen_v1keywords.py Updated type definition to support arrays
src/deepgram/types/listen_v1keyterm.py Updated type definition to support arrays
src/deepgram/types/listen_v1extra.py Updated type definition to support arrays
src/deepgram/listen/v2/client.py Added array handling logic and updated signatures for V2Client and AsyncV2Client
src/deepgram/listen/v1/client.py Added array handling logic and updated signatures for V1Client and AsyncV1Client

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 5 to 8
ListenV1Redact = typing.Union[
typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"], typing.Any
typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"],
typing.Sequence[str],
]
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The type definition should be Optional to maintain consistency with other parameters. The Union should include None, or the entire Union should be wrapped with Optional, as redact is an optional parameter based on the function signature in client.py where it's defined as redact: typing.Optional[typing.Union[str, typing.Sequence[str]]].

Copilot uses AI. Check for mistakes.

ListenV1Redact = typing.Union[
typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"], typing.Any
typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"],
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The type definition should include str as a standalone option in the Union to match the other parameter types and allow any string value (not just the literal values or sequences). The current definition only allows literal strings or sequences, which doesn't align with the Union[str, Sequence[str]] pattern used for other parameters.

Suggested change
typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"],
typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"],
str,

Copilot uses AI. Check for mistakes.
- Wrap Union in Optional to match function signature
- Add str as standalone option in Union to allow any string value
- Maintains Literal types for common values (autocomplete support)

Addresses Copilot AI review comments
@lukeocodes
Copy link
Contributor Author

Thanks for the review, Copilot! 🤖

I've addressed both comments regarding ListenV1Redact:

Fixed in commit d835960:

  1. ✅ Wrapped the Union in Optional to match the function signature consistency
  2. ✅ Added str as a standalone option in the Union to allow any string value

New type definition:

ListenV1Redact = typing.Optional[
    typing.Union[
        typing.Literal["true", "false", "pci", "numbers", "aggressive_numbers", "ssn"],
        str,
        typing.Sequence[str],
    ]
]

This now properly supports:

  • Common literal values (with IDE autocomplete)
  • Any custom string value
  • Arrays of strings
  • None (optional parameter)

Update test to reflect new type definition:
- Changed from Optional[Any] to Optional[Union[str, Sequence[str]]]
- Updated test name and docstring for clarity
- Usage tests remain unchanged and already cover the correct behavior
@lukeocodes
Copy link
Contributor Author

✅ All Tests Passing!

Fixed the test failures with two additional commits:

Commit d835960: Fixed ListenV1Redact type definition

  • Wrapped Union in Optional to match function signature
  • Added str as standalone option to allow any string value
  • Maintains Literal types for common values (autocomplete support)

Commit 6d6d6f8: Updated test assertion

  • Updated test_listen_v1tag_is_optional_union to match new type definition
  • Changed from Optional[Any] to Optional[Union[str, Sequence[str]]]

Test Results:

  • ✅ All compile checks passing (Python 3.8-3.13)
  • ✅ All test suites passing (Python 3.8-3.13)
  • ✅ Title check passing

The PR is now ready for review!

@lukeocodes lukeocodes merged commit 38cc1e5 into main Jan 29, 2026
13 checks passed
@lukeocodes lukeocodes deleted the fix/websocket-array-parameters branch January 29, 2026 21:21
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.

[Bug]: Multiple keyterm support for Listen v1 and v2 is broken in sdk v5.3 [Bug]: Issue in sending multiple Keyterms

3 participants