-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Issue 264 - Using Shared WebSocket Class Implementation #468
Conversation
WalkthroughThe changes involve enhancements to the Changes
Possibly related PRs
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Unit Tests passed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 32
🧹 Outside diff range and nitpick comments (22)
deepgram/clients/common/v1/websocket_events.py (2)
8-13
: LGTM: Class definition and docstring are well-structured.The
WebSocketEvents
class is correctly defined as a subclass ofStrEnum
, and the docstring provides a clear description of the class purpose.Consider removing the unnecessary empty line (line 9) before the class definition to improve code consistency.
15-19
: LGTM: Enum members are well-defined.The enum members cover the basic events expected in WebSocket communication, and the use of string values matching the member names is consistent and follows good practices for string enums.
Consider adding a "Message" event if it's relevant to the Deepgram API. This would typically represent received messages from the WebSocket connection. For example:
Message: str = "Message"This addition would make the enum more comprehensive for typical WebSocket interactions.
deepgram/clients/common/v1/__init__.py (1)
13-16
: LGTM! Improved client architecture with abstract base classes.The addition of abstract client classes (
AbstractAsyncRestClient
,AbstractSyncRestClient
,AbstractAsyncWebSocketClient
,AbstractSyncWebSocketClient
) is an excellent enhancement. This change aligns perfectly with the PR objective of organizing API support in a more reusable manner and will likely contribute significantly to reducing code sprawl.The separation of sync/async and REST/WebSocket clients indicates a well-structured design that should improve maintainability and extensibility.
Consider grouping related imports together for better readability. For example:
from .abstract_async_rest import AbstractAsyncRestClient from .abstract_sync_rest import AbstractSyncRestClient from .abstract_async_websocket import AbstractAsyncWebSocketClient from .abstract_sync_websocket import AbstractSyncWebSocketClientThis minor reorganization could make the imports easier to scan and understand at a glance.
deepgram/clients/common/v1/helpers.py (2)
11-32
: LGTM: Well-implemented function with proper error handling.The
append_query_params
function is well-implemented, handling various data types and edge cases correctly. It follows Python best practices and includes appropriate type hinting.Consider adding a type hint for the return value to improve code clarity:
-def append_query_params(url: str, params: Optional[Dict] = None): +def append_query_params(url: str, params: Optional[Dict] = None) -> str:
36-53
: LGTM: Well-implemented WebSocket URL conversion function.The
convert_to_websocket_url
function effectively converts regular URLs to WebSocket URLs, handling both HTTP and HTTPS protocols correctly.Consider the following improvements:
- Add a return type hint:
-def convert_to_websocket_url(base_url: str, endpoint: str): +def convert_to_websocket_url(base_url: str, endpoint: str) -> str:
- Use
str.startswith()
instead ofin
for more precise protocol checking:- if "http://" in base_url: + if base_url.startswith("http://"):
- Simplify the URL scheme replacement using
str.replace()
:- if re.match(r"^https?://", base_url, re.IGNORECASE): - if base_url.startswith("http://"): - use_ssl = False # Override to false if http:// is found - base_url = base_url.replace("https://", "").replace("http://", "") + if base_url.startswith(("http://", "https://")): + use_ssl = base_url.startswith("https://") + base_url = base_url.replace("http://", "").replace("https://", "")These changes will improve code clarity and efficiency.
deepgram/clients/common/__init__.py (1)
12-13
: LGTM: Abstract REST client imports look goodThe addition of
AbstractAsyncRestClient
andAbstractSyncRestClient
aligns with the PR objective of organizing the Agent API support in a more reusable manner. These abstract classes will provide a solid foundation for both asynchronous and synchronous REST API interactions.Consider combining these imports with the previous import statement for consistency:
from .v1 import ( DeepgramError, DeepgramTypeError, DeepgramApiError, DeepgramUnknownApiError, AbstractAsyncRestClient, AbstractSyncRestClient, )deepgram/clients/common/v1/errors.py (2)
40-58
: LGTM! Consider adding type hints for better code clarity.The
DeepgramApiError
class is well-implemented and follows best practices for custom exceptions. The docstring provides clear explanations, and the__str__
method offers a useful string representation.Consider adding type hints to the
__init__
method for improved code clarity:- def __init__(self, message: str, status: str, original_error=None): + def __init__(self, message: str, status: str, original_error: Optional[Dict[str, Any]] = None):This change would require adding the following import at the top of the file:
from typing import Dict, Any, Optional
Line range hint
1-77
: Consider standardizing error handling across all exception classes.While the new exception classes (
DeepgramApiError
andDeepgramUnknownApiError
) provide more detailed error information, it might be beneficial to standardize the error handling approach across all exception classes in this file for better consistency and maintainability.Consider the following suggestions:
Add a
status
attribute toDeepgramError
andDeepgramTypeError
classes, similar to the new classes. This would allow for consistent error reporting across all Deepgram-related exceptions.Standardize the
__str__
method across all classes to include the status (if applicable) and maintain a consistent format.Consider creating a base
DeepgramBaseError
class that implements common functionality, and have all other exception classes inherit from it. This would reduce code duplication and ensure consistency.Here's a basic example of how this could look:
class DeepgramBaseError(Exception): def __init__(self, message: str, status: Optional[str] = None): super().__init__(message) self.name = self.__class__.__name__ self.message = message self.status = status def __str__(self): return f"{self.name}: {self.message}" + (f" (Status: {self.status})" if self.status else "") class DeepgramError(DeepgramBaseError): pass class DeepgramTypeError(DeepgramBaseError): pass class DeepgramApiError(DeepgramBaseError): def __init__(self, message: str, status: str, original_error: Optional[Dict[str, Any]] = None): super().__init__(message, status) self.original_error = original_error class DeepgramUnknownApiError(DeepgramBaseError): passThis structure would provide a more consistent and maintainable approach to error handling across the Deepgram SDK.
examples/text-to-speech/websocket/complete/main.py (1)
Line range hint
1-124
: Summary of changes and suggestions for improvementThe changes in this file aim to improve the user experience by reducing repetitive warning messages. However, there are a few areas that could be further improved:
- Replace the global variable usage with a more encapsulated approach, such as using class attributes or instance variables.
- Provide clear explanations for the commented-out configuration options, either in the code comments or in the documentation.
- Consider adding more comprehensive error handling and logging throughout the example to make it more robust and easier to debug.
These improvements would make the example more maintainable, easier to understand, and more aligned with best practices in Python development.
deepgram/audio/speaker/speaker.py (2)
29-44
: LGTM: Improved type safety for class attributesThe changes to make
_stream
,_output_device_index
,_thread
,_receiver_thread
, and_loop
explicitly optional and initialized as None improve type safety and provide a clear initial state for these attributes. This aligns with best practices for type hinting in Python.Consider adding type hints to the other class attributes (
_audio
,_chunk
,_rate
,_channels
,_queue
,_exit
) for consistency and to further improve type safety throughout the class.
222-232
: Improved WebSocket exception handlingThe addition of specific exception handling for WebSocket-related errors (
ConnectionClosedOK
,ConnectionClosed
, andWebSocketException
) improves the robustness of the_start_asyncio_receiver
method. This change provides more granular logging for different connection issues, which will aid in debugging and monitoring the connection state.Consider replacing the broad
except Exception
clause at the end of the method with more specific exception types that you expect to encounter. This will make the error handling more precise and easier to maintain.deepgram/clients/common/v1/abstract_async_rest.py (1)
12-13
: LGTM! Consider using absolute imports for better maintainability.The changes to the import statements are correct and don't affect the functionality of the
AbstractAsyncRestClient
class. The consolidation of error imports improves code readability.Consider using absolute imports instead of relative imports for
DeepgramClientOptions
. This can make the code more maintainable and less prone to errors if the file structure changes in the future. For example:from deepgram.options import DeepgramClientOptionsexamples/text-to-speech/websocket/async_complete/main.py (1)
25-25
: Useasyncio.get_running_loop()
instead ofasyncio.get_event_loop()
In asynchronous functions,
asyncio.get_running_loop()
is preferred overasyncio.get_event_loop()
to get the currently running event loop.Suggested change:
-loop = asyncio.get_event_loop() +loop = asyncio.get_running_loop()deepgram/clients/common/v1/abstract_async_websocket.py (1)
2-2
: Typographical error in license statement.In line 2, the phrase "governed by a MIT license" should use "an" instead of "a" because "MIT" starts with a vowel sound.
Apply this diff to correct the typo:
-# Use of this source code is governed by a MIT license that can be found in the LICENSE file. +# Use of this source code is governed by an MIT license that can be found in the LICENSE file.deepgram/clients/listen/v1/websocket/client.py (3)
248-248
: Simplify event emission by using the enum directlyWhen emitting events, wrapping the event enum within itself is redundant. Use the enum member directly for clarity.
Apply this diff to simplify event emission:
- LiveTranscriptionEvents(LiveTranscriptionEvents.Open), + LiveTranscriptionEvents.Open,Repeat this change for all similar occurrences in the
match
statement cases.
205-205
: Remove duplicate logging statementsThe debug log
self._logger.debug("callback handlers for: %s", event)
is executed twice consecutively without any state change, which is redundant.Remove one of the duplicate log statements:
self._logger.debug("callback handlers for: %s", event) - self._logger.debug("callback handlers for: %s", event)
Also applies to: 212-212
509-511
: Unused method_close_message
The
_close_message
method is defined but not called anywhere in the class. This could indicate dead code.Verify whether this method is needed:
- If it is intended to be used, ensure it is called appropriately.
- If not needed, consider removing it to clean up the codebase.
deepgram/clients/listen/v1/websocket/async_client.py (1)
510-511
: Potential Issue with_close_message
Return ValueThe
_close_message
method returns the result ofawait self.send(...)
. Ensure that this method's return value is appropriately handled wherever_close_message
is called, and consider documenting what the return value represents.deepgram/clients/speak/v1/websocket/client.py (2)
236-253
: Redundant debug logging in_emit
methodMultiple debug logs before and after iterating through threads may clutter the log output without providing significant value.
Consider condensing or removing some of the debug statements to streamline logging output.
Line range hint
513-551
: Improve exception handling insend_raw
methodUsing
except Exception as e
may catch unexpected exceptions and make debugging challenging.Catch specific exceptions that
super().send(msg)
might raise, such as network-related exceptions.- except Exception as e: # pylint: disable=broad-except + except WebSocketException as e:Ensure relevant exceptions are imported and appropriately handled.
deepgram/clients/speak/v1/websocket/async_client.py (2)
Line range hint
363-379
: Avoid catching broad exceptions in_process_text
Catching all exceptions can mask underlying issues. It's advisable to catch specific exceptions to enhance error handling and make debugging easier.
Line range hint
433-454
: Avoid catching broad exceptions in_flush
Using a broad
except Exception
block may suppress critical errors. Consider handling specific exceptions to improve reliability and maintainability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (30)
- deepgram/audio/speaker/speaker.py (4 hunks)
- deepgram/clients/init.py (1 hunks)
- deepgram/clients/analyze/v1/async_client.py (1 hunks)
- deepgram/clients/analyze/v1/client.py (1 hunks)
- deepgram/clients/common/init.py (1 hunks)
- deepgram/clients/common/v1/init.py (1 hunks)
- deepgram/clients/common/v1/abstract_async_rest.py (1 hunks)
- deepgram/clients/common/v1/abstract_async_websocket.py (1 hunks)
- deepgram/clients/common/v1/abstract_sync_rest.py (1 hunks)
- deepgram/clients/common/v1/abstract_sync_websocket.py (1 hunks)
- deepgram/clients/common/v1/errors.py (1 hunks)
- deepgram/clients/common/v1/helpers.py (1 hunks)
- deepgram/clients/common/v1/websocket_events.py (1 hunks)
- deepgram/clients/errors.py (0 hunks)
- deepgram/clients/helpers.py (0 hunks)
- deepgram/clients/listen/v1/rest/async_client.py (1 hunks)
- deepgram/clients/listen/v1/rest/client.py (1 hunks)
- deepgram/clients/listen/v1/websocket/async_client.py (11 hunks)
- deepgram/clients/listen/v1/websocket/client.py (11 hunks)
- deepgram/clients/listen/v1/websocket/response.py (0 hunks)
- deepgram/clients/manage/v1/async_client.py (1 hunks)
- deepgram/clients/manage/v1/client.py (1 hunks)
- deepgram/clients/selfhosted/v1/async_client.py (1 hunks)
- deepgram/clients/selfhosted/v1/client.py (1 hunks)
- deepgram/clients/speak/v1/rest/async_client.py (1 hunks)
- deepgram/clients/speak/v1/rest/client.py (1 hunks)
- deepgram/clients/speak/v1/websocket/async_client.py (21 hunks)
- deepgram/clients/speak/v1/websocket/client.py (18 hunks)
- examples/text-to-speech/websocket/async_complete/main.py (4 hunks)
- examples/text-to-speech/websocket/complete/main.py (2 hunks)
💤 Files not reviewed due to no reviewable changes (3)
- deepgram/clients/errors.py
- deepgram/clients/helpers.py
- deepgram/clients/listen/v1/websocket/response.py
✅ Files skipped from review due to trivial changes (3)
- deepgram/clients/init.py
- deepgram/clients/manage/v1/async_client.py
- deepgram/clients/manage/v1/client.py
🔇 Additional comments (53)
deepgram/clients/common/v1/websocket_events.py (2)
1-3
: LGTM: Copyright and license information is complete.The copyright notice, license information, and SPDX identifier are present and up to date. This follows best practices for open-source software.
1-19
: Summary: New WebSocketEvents class aligns well with PR objectives.This new file introduces a well-structured
WebSocketEvents
class that enumerates possible events from the Deepgram API. The implementation follows good practices and provides a clear structure for handling WebSocket events.The introduction of this class aligns well with the PR objectives of enhancing support for the Agent API in a more organized and reusable manner. It should contribute to reducing code sprawl within the deepgram-python-sdk by providing a centralized definition of WebSocket events.
Consider the suggestions made in the review comments to further improve the implementation:
- Potentially use the standard library's
enum
module if the minimum Python version allows it.- Add a "Message" event if relevant to the Deepgram API.
Overall, this addition is a positive step towards improving the SDK's structure and maintainability.
deepgram/clients/common/v1/__init__.py (2)
7-12
: LGTM! Enhanced error handling for API operations.The addition of
DeepgramApiError
andDeepgramUnknownApiError
to the imports from the.errors
module is a positive change. This enhancement aligns well with the PR objective of improving API support and will likely contribute to more robust error handling in API operations.
7-16
: Summary: Excellent enhancements to error handling and client architecture.The changes in this file significantly improve the module's capabilities by introducing new error types and abstract client classes. These additions align perfectly with the PR objectives of enhancing API support and reducing code sprawl.
The new error classes (
DeepgramApiError
andDeepgramUnknownApiError
) will allow for more precise error handling in API operations. The abstract client classes (AbstractAsyncRestClient
,AbstractSyncRestClient
,AbstractAsyncWebSocketClient
,AbstractSyncWebSocketClient
) provide a solid foundation for a more organized and reusable client architecture.These changes are likely to have a positive impact on the overall structure and maintainability of the deepgram-python-sdk. Great work on implementing these improvements!
deepgram/clients/common/v1/helpers.py (2)
1-9
: LGTM: File header and imports are correct and well-organized.The copyright notice, license information, and imports are appropriate for the file's content. The code follows good practices for organizing imports and maintaining readability.
1-53
: Overall, excellent implementation of helper functions.This new file successfully introduces reusable utility functions for URL manipulation, specifically for appending query parameters and converting URLs to WebSocket format. These functions will likely contribute to reducing code sprawl within the deepgram-python-sdk, aligning well with the PR objectives.
The implementation is clean, well-documented, and follows Python best practices. With the suggested minor improvements, this file will be a valuable addition to the project.
deepgram/clients/common/__init__.py (3)
5-10
: LGTM: Error classes import looks goodThe addition of
DeepgramApiError
andDeepgramUnknownApiError
to the import statement is consistent with existing error classes and aligns with the PR objective of enhancing API support. This change improves error handling capabilities for users of the SDK.
5-15
: Overall, the changes in this file look good and align with the PR objectivesThe additions to the
__init__.py
file enhance the SDK's public API by introducing new error classes and abstract client classes for both REST and WebSocket interactions. These changes support the goal of improving Agent API support in a more organized and reusable manner.A few minor suggestions were made to improve import style consistency. Additionally, a verification step was requested to ensure the new WebSocket classes are being utilized in the examples as mentioned in the PR description.
Great job on enhancing the SDK's capabilities while maintaining a clean and organized structure!
14-15
: LGTM: Abstract WebSocket client imports look goodThe addition of
AbstractAsyncWebSocketClient
andAbstractSyncWebSocketClient
aligns with the PR objective of organizing the Agent API support in a more reusable manner, specifically for WebSocket functionality. These abstract classes will provide a solid foundation for both asynchronous and synchronous WebSocket interactions.Consider combining these imports with the previous import statement for consistency:
from .v1 import ( DeepgramError, DeepgramTypeError, DeepgramApiError, DeepgramUnknownApiError, AbstractAsyncRestClient, AbstractSyncRestClient, AbstractAsyncWebSocketClient, AbstractSyncWebSocketClient, )The PR mentions that all Speech-to-Text and Text-to-Speech examples using WebSocket functionality were tested. Let's verify the usage of these new WebSocket classes:
deepgram/clients/selfhosted/v1/async_client.py (2)
Line range hint
15-165
: Confirmed: Rest of the file unaffected by import change.I've reviewed the entire file, and I can confirm that the change in the import statement doesn't affect any other part of the file. The
AsyncSelfHostedClient
class still correctly inherits fromAbstractAsyncRestClient
, and all methods and their implementations remain unchanged.
12-12
: LGTM! Verify the new import structure.The change from a specific import to a common module import looks good. This could be part of a refactoring effort to centralize common classes, which can improve code organization and reusability.
To ensure this change doesn't introduce any issues, please run the following script to verify the new import structure:
These tests will help ensure that:
AbstractAsyncRestClient
is properly defined in the common module.- There are no remaining imports from the old location.
- There are no potential circular imports introduced by this change.
deepgram/clients/speak/v1/rest/client.py (1)
16-17
: Improved import organizationThe simplification of import statements enhances code organization and readability. This change aligns well with the PR objective of improving support for the Agent API in a more organized manner.
deepgram/audio/speaker/speaker.py (5)
12-13
: LGTM: WebSocket import addedThe addition of the
websockets
import aligns with the PR objectives to enhance support for the Agent API using a shared WebSocket class implementation.
282-301
: LGTM: Improved thread management and resource cleanupThe changes in the
finish
method enhance the stability of the thread management logic:
- Checking for the existence of
_thread
and_receiver_thread
before joining them prevents potential runtime errors.- Setting
_stream
to None after closing it ensures the attribute is in a known state.These improvements make the cleanup process more robust and less prone to errors.
Line range hint
1-324
: LGTM: Well-structured and organized implementationThe changes made to this file are targeted and improve specific areas without disrupting the overall organization of the code. The enhancements align well with the PR objectives to support the Agent API in a more organized and reusable manner. The
Speaker
class remains cohesive and follows a logical flow from initialization to audio playback and cleanup.
Line range hint
1-324
: Overall assessment: Solid improvements with minor suggestionsThe changes made to the
Speaker
class in this file significantly enhance its robustness and type safety. The improvements in WebSocket exception handling, thread management, and resource cleanup align well with the PR objectives to support the Agent API in a more organized and reusable manner.Key improvements:
- Enhanced type safety for class attributes
- Improved WebSocket exception handling
- More robust thread management and resource cleanup
Suggestions for further improvement:
- Add type hints to remaining class attributes for consistency
- Replace broad exception handling with more specific exceptions where possible
- Implement additional security measures for WebSocket connections
Overall, these changes represent a solid step forward in the development of the deepgram-python-sdk.
Line range hint
1-324
: Consider additional security measures for WebSocket connectionsWhile the current implementation looks solid, it's important to ensure that the WebSocket connections are properly secured, especially when dealing with audio data. Consider the following security measures:
- Implement proper authentication and authorization for WebSocket connections.
- Use secure WebSocket connections (wss://) in production environments.
- Validate and sanitize any data received through the WebSocket before processing it.
- Implement rate limiting to prevent potential DoS attacks.
To verify the security of the WebSocket implementation, you can run the following script:
This script will help identify if secure WebSocket protocols are being used and if there's any authentication mechanism in place for WebSocket connections.
deepgram/clients/common/v1/abstract_sync_rest.py (2)
13-13
: LGTM: Improved import statement organization.The consolidation of error class imports into a single line improves code readability and reduces code sprawl. This change aligns well with the PR objectives.
12-13
: Verify changes against PR objectives.The modifications to the import statements align with the PR objectives of enhancing support for the Agent API in a more organized manner and reducing code sprawl. However, it's important to ensure that these changes are part of a broader restructuring effort.
To confirm the extent of the restructuring and its impact on other parts of the codebase, please run the following script:
This script will help us understand if the changes in this file are part of a consistent pattern across the codebase, ensuring that the PR objectives are fully met.
deepgram/clients/analyze/v1/client.py (1)
12-13
: Improved import organizationThe changes to the import statements simplify the import paths, which aligns well with the PR objective of enhancing support for the Agent API in a more organized manner. This modification improves code maintainability and readability without affecting the functionality.
deepgram/clients/analyze/v1/async_client.py (1)
12-13
: LGTM! Verify import paths in the codebase.The consolidation of imports from the
...common
module simplifies the import structure. This change doesn't affect the functionality but may indicate a reorganization of the project's module structure.To ensure consistency across the codebase, run the following script to check for any remaining occurrences of the old import paths:
✅ Verification successful
Import paths successfully consolidated. No outdated imports found in the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for old import paths in the codebase # Search for old AbstractAsyncRestClient import echo "Checking for old AbstractAsyncRestClient import:" rg --type python "from \.\.\.abstract_async_client import AbstractAsyncRestClient" # Search for old DeepgramError and DeepgramTypeError imports echo "Checking for old DeepgramError and DeepgramTypeError imports:" rg --type python "from \.\.\.common\.v1\.errors import DeepgramError, DeepgramTypeError"Length of output: 477
Script:
#!/bin/bash # Description: Check for old import paths in the codebase # Search for old AbstractAsyncRestClient import echo "Checking for old AbstractAsyncRestClient import:" rg -g "*.py" "from \.\.\.abstract_async_client import AbstractAsyncRestClient" # Search for old DeepgramError and DeepgramTypeError imports echo "Checking for old DeepgramError and DeepgramTypeError imports:" rg -g "*.py" "from \.\.\.common\.v1\.errors import DeepgramError, DeepgramTypeError"Length of output: 407
deepgram/clients/listen/v1/rest/client.py (1)
12-13
: LGTM: Import statements consolidated.The import statements have been simplified by consolidating them under the
....common
module. This change improves code organization and maintainability.Let's verify the usage of these imported classes throughout the file:
✅ Verification successful
Verified: Import statements usage confirmed.
The consolidated imports are correctly utilized throughout the
client.py
file, ensuring proper functionality and maintainability.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify the usage of AbstractSyncRestClient, DeepgramError, and DeepgramTypeError echo "Checking usage of AbstractSyncRestClient:" rg "class ListenRESTClient\(AbstractSyncRestClient\)" deepgram/clients/listen/v1/rest/client.py echo "\nChecking usage of DeepgramError:" rg "raise DeepgramError" deepgram/clients/listen/v1/rest/client.py echo "\nChecking usage of DeepgramTypeError:" rg "raise DeepgramTypeError" deepgram/clients/listen/v1/rest/client.pyLength of output: 1114
deepgram/clients/listen/v1/rest/async_client.py (3)
12-13
: Improved import structure enhances code organization.The changes to the import statements are a positive step towards better code organization. By importing from a common module (
....common
) instead of using relative imports, this modification:
- Enhances code reusability across the project.
- Improves maintainability by centralizing common classes.
- Aligns with the PR objective of organizing the Agent API support more effectively.
These changes contribute to reducing code sprawl within the deepgram-python-sdk, as mentioned in the PR objectives.
12-13
: Changes align with PR objectives and suggest broader refactoring.The modifications to the import statements in this file align well with the PR objectives of enhancing support for the Agent API in a more organized and reusable manner. By moving common classes to a shared module, this change contributes to reducing code sprawl within the deepgram-python-sdk.
To ensure consistency across the project:
- Check if similar import changes have been applied to other relevant files in the SDK.
- Verify that the common module (
....common
) is properly structured and contains all necessary shared classes.#!/bin/bash # Check for similar import patterns across the SDK rg --type python -e "from ....common import AbstractAsyncRestClient, DeepgramError, DeepgramTypeError" deepgram/ # Verify the structure of the common module ls -R deepgram/clients/common/
Line range hint
26-26
: Verify unchanged functionality of AsyncListenRESTClient.The changes to the import statements don't affect the functionality of the AsyncListenRESTClient class. The class still correctly inherits from AbstractAsyncRestClient, and the usage of DeepgramError and DeepgramTypeError remains unchanged throughout the methods.
To ensure that the import changes haven't inadvertently affected any functionality, please run the following verification steps:
- Execute all existing unit tests related to this file.
- Manually test the AsyncListenRESTClient class with a sample audio file to confirm that transcription still works as expected.
examples/text-to-speech/websocket/async_complete/main.py (1)
19-20
: 🛠️ Refactor suggestionAvoid using global variables for
warning_notice
Using global variables can make the code harder to understand and maintain. Consider encapsulating
warning_notice
within a class or using closures to maintain state.⛔ Skipped due to learnings
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#434 File: examples/text-to-speech/websocket/async_complete/main.py:68-79 Timestamp: 2024-08-07T19:24:31.383Z Learning: When the user indicates that a piece of code is only an example, consider suggesting the inclusion of best practices, such as error handling, to promote good coding habits.
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#434 File: examples/text-to-speech/websocket/async_complete/main.py:21-27 Timestamp: 2024-08-07T19:24:37.020Z Learning: When the user indicates that a piece of code is only an example, consider suggesting the inclusion of best practices, such as error handling, to promote good coding habits.
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#434 File: examples/text-to-speech/websocket/async_complete/main.py:32-58 Timestamp: 2024-08-07T19:24:27.278Z Learning: When the user indicates that a piece of code is only an example, acknowledge their comment and note the context for future reviews without insisting on changes unless they significantly enhance the clarity or functionality of the example.
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#434 File: examples/text-to-speech/websocket/async_complete/main.py:90-93 Timestamp: 2024-08-07T19:24:46.887Z Learning: When the user indicates that a piece of code is only an example, acknowledge their comment and note the context for future reviews without insisting on changes unless they significantly enhance the clarity or functionality of the example.
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#434 File: examples/text-to-speech/websocket/async_interactive/main.py:0-0 Timestamp: 2024-08-06T20:02:34.130Z Learning: When the user indicates that a piece of code is only an example, consider suggesting the inclusion of best practices, such as error handling, to promote good coding habits.
deepgram/clients/listen/v1/websocket/client.py (3)
35-37
: Inheritance fromAbstractSyncWebSocketClient
The
ListenWebSocketClient
class now inherits fromAbstractSyncWebSocketClient
, promoting code reuse and reducing redundancy. This change streamlines the client implementation.
Line range hint
525-542
: Ensure all threads are properly managed upon finishingThe
finish
method attempts to join several threads. Ensure that all threads started by the client are accounted for and properly terminated to prevent resource leaks.Review the thread management logic to confirm that:
- Threads are started with appropriate names for identification.
- All threads are joined or terminated when the client finishes.
- There are no potential race conditions or deadlocks during thread shutdown.
240-325
:⚠️ Potential issueVerify compatibility of
match
statements with Python versionThe
match
statement used for pattern matching requires Python 3.10 or newer. Ensure that the project's minimum Python version supports this feature to avoid runtime errors.Run the following script to check the required Python version:
If backward compatibility is needed, consider refactoring the
match
statement to useif-elif-else
constructs.deepgram/clients/listen/v1/websocket/async_client.py (4)
35-37
: Enhance Code Reusability by ExtendingAbstractAsyncWebSocketClient
The
AsyncListenWebSocketClient
class now extendsAbstractAsyncWebSocketClient
, promoting code reuse and reducing redundancy by leveraging common WebSocket client functionality.
85-86
: Properly Initialize the Base ClassThe constructor now calls
super().__init__(self._config, self._endpoint)
, ensuring that the parent class is correctly initialized. This is essential for the proper functioning of inherited methods and attributes.
134-146
: Delegate Connection Logic to Parent Class instart
MethodBy calling
super().start(...)
in thestart
method, the connection setup and error handling are centralized in the parent class, enhancing maintainability and reducing code duplication.
510-511
: Ensure Availability ofsend
MethodThe
_close_message
method callsawait self.send(...)
, but thesend
method has been removed from this class. Ensure that thesend
method is available in the parent classAbstractAsyncWebSocketClient
.Run the following script to verify that the
send
method is implemented in the class hierarchy:deepgram/clients/speak/v1/websocket/client.py (10)
8-8
: Import necessary typing objectsThe addition of
Callable
to the imports is appropriate, ensuring that callable types can be correctly annotated throughout the code.
15-16
: Inherit fromAbstractSyncWebSocketClient
and importDeepgramError
The import of
AbstractSyncWebSocketClient
andDeepgramError
sets up the foundation for proper inheritance and custom exception handling.
38-40
: Updated class inheritance for code reuseThe
SpeakWSClient
class now inherits fromAbstractSyncWebSocketClient
, promoting code reuse and consistency across WebSocket clients.
69-71
: Validate theconfig
parameter in the constructorThe check for
config is None
and raising aDeepgramError
ensures that the class is initialized with the necessary configuration, preventing potential issues later on.
119-121
: Call parent constructor with appropriate argumentsCalling
super().__init__(self._config, self._endpoint)
ensures that the base class is properly initialized, maintaining the integrity of the inheritance structure.
168-171
: Delegate listening to theSpeaker
instance if availableBy delegating listening to
self._speaker
, the code efficiently manages audio playback without the need for a separate listening thread.
Line range hint
255-384
: Usingmatch
statements improves message processingThe implementation of
match
statements in_process_text
provides clear and concise handling of different message types, leveraging Python 3.10 features.
386-398
: Efficient handling of binary messages in_process_binary
The
_process_binary
method correctly processes binary data and emits theAudioData
event, ensuring binary messages are handled appropriately.
Line range hint
600-630
: Ensure graceful shutdown infinish
methodProperly stopping threads and cleaning up resources in the
finish
method prevents resource leaks and ensures the client shuts down gracefully.
649-649
: Provide class alias for backward compatibilityDefining
SpeakWebSocketClient
as an alias forSpeakWSClient
maintains backward compatibility and eases the transition for existing codebases.deepgram/clients/speak/v1/websocket/async_client.py (10)
8-8
: Appropriate addition of type hintsThe added imports from the
typing
module enhance code readability and maintainability by providing explicit type hints.
15-16
: Necessary imports for subclassing and error handlingImporting
AbstractAsyncWebSocketClient
andDeepgramError
is appropriate for extending the base functionality and consistent error management.
38-40
: Refactoring class to inherit fromAbstractAsyncWebSocketClient
Refactoring
AsyncSpeakWSClient
to inherit fromAbstractAsyncWebSocketClient
centralizes WebSocket functionalities, promoting code reuse and maintainability.
68-69
: Improved initialization with config validationAdding a check for a
None
configuration and raising aDeepgramError
enhances robustness by ensuring that the client is initialized with valid settings.
97-101
: Detailed logging of speaker settingsLogging the
rate
,channels
, anddevice_index
provides valuable debugging information, aiding in the troubleshooting of speaker initialization issues.
116-118
: Correct invocation of the parent class constructorCalling
super().__init__(self._config, self._endpoint)
ensures that the base class is properly initialized, maintaining the integrity of the class hierarchy.
165-169
: Delegating listening to the speaker instanceUtilizing
super().delegate_listening(self._speaker)
correctly delegates the listening responsibilities to the speaker, aligning with the refactored architecture.
Line range hint
258-359
: Verify compatibility ofmatch
statements with Python versionsThe use of
match
statements requires Python 3.10 or newer. Ensure that the codebase and deployment environments support Python 3.10+. If support for older versions is needed, consider replacingmatch
withif-elif
constructs.
Line range hint
605-646
: Proper exception handling infinish
methodThe
finish
method appropriately handlesasyncio.CancelledError
andasyncio.TimeoutError
, ensuring graceful shutdown and resource cleanup.
668-668
: Creation of class alias for backward compatibilityCreating
AsyncSpeakWebSocketClient
as an alias toAsyncSpeakWSClient
maintains backward compatibility and eases the transition for existing codebases.
b2b07b9
to
f5f24b7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (5)
deepgram/clients/listen/v1/websocket/client.py (2)
Line range hint
461-461
: Remove call to non-existent method_signal_exit
The method
_signal_exit()
is called, but it has been removed from the class. This will result in anAttributeError
at runtime.Consider updating the code to properly handle exit without calling
_signal_exit()
. If necessary, implement the exit logic within_flush
or recreate the_signal_exit
method.
Line range hint
1-576
: Overall improvements with minor issues to addressThe refactoring of
ListenWebSocketClient
to inherit fromAbstractSyncWebSocketClient
is a significant improvement that should reduce code duplication and improve maintainability. The changes to methods likestart
,finish
, and the addition of_process_text
and_close_message
are well-implemented.However, there are a few issues to address:
- Correct the class name in log messages (lines 145-146).
- Handle JSON decoding errors more specifically in
_process_text
.- Either implement or remove the
_process_binary
method.- Remove or replace calls to the non-existent
_signal_exit
method in error handling blocks.After addressing these issues, the code will be more robust and consistent.
deepgram/clients/speak/v1/websocket/client.py (3)
Line range hint
255-373
: Improved message processing with better structureThe
_process_text
method has been significantly improved:
- Separation of text and binary processing enhances clarity and maintainability.
- Use of match-case statement improves readability for different response types.
- Improved logging provides better diagnostics.
However, there's still room for improvement in exception handling:
Consider catching more specific exceptions instead of using a broad
except Exception
. This will make it easier to debug and handle different error scenarios appropriately. For example:try: # ... existing code ... except json.JSONDecodeError as e: self._logger.error("Invalid JSON in message: %s", e) # Handle JSON parsing error except KeyError as e: self._logger.error("Missing expected key in message: %s", e) # Handle missing key error except Exception as e: self._logger.error("Unexpected error in _process_text: %s", e) # Handle other unexpected errorsThis approach will provide more specific error handling and logging.
Line range hint
399-457
: Enhanced flush method with improved error handlingThe
_flush
method has been improved with better error handling and the use of_exit_event
for graceful termination. These changes enhance the robustness of the method.However, similar to the
_process_text
method, there's room for improvement in exception handling:Consider catching more specific exceptions instead of using a broad
except Exception
. This will provide better error diagnostics and handling. For example:try: # ... existing code ... except ValueError as e: self._logger.error("Invalid value in _flush: %s", e) # Handle value error except RuntimeError as e: self._logger.error("Runtime error in _flush: %s", e) # Handle runtime error except Exception as e: self._logger.error("Unexpected error in _flush: %s", e) # Handle other unexpected errorsThis approach will allow for more targeted error handling and logging.
473-481
: Method signature aligned with abstract base classThe
send
method signature has been updated to acceptUnion[str, bytes]
, aligning with the abstract base class. This change addresses the learning from previous reviews about maintaining consistency with abstract base classes to satisfy linters like pylint.However, the implementation doesn't actually handle bytes data. To make this more explicit:
Consider updating the type hint to be more specific about what the method actually handles:
def send(self, data: str) -> bool: """ Sends text data. Does not support bytes data. """ return self.send_text(data)This makes it clear that the method only supports string input, despite the more general signature required by the abstract base class.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (30)
- deepgram/audio/speaker/speaker.py (4 hunks)
- deepgram/clients/init.py (1 hunks)
- deepgram/clients/analyze/v1/async_client.py (1 hunks)
- deepgram/clients/analyze/v1/client.py (1 hunks)
- deepgram/clients/common/init.py (1 hunks)
- deepgram/clients/common/v1/init.py (1 hunks)
- deepgram/clients/common/v1/abstract_async_rest.py (1 hunks)
- deepgram/clients/common/v1/abstract_async_websocket.py (1 hunks)
- deepgram/clients/common/v1/abstract_sync_rest.py (1 hunks)
- deepgram/clients/common/v1/abstract_sync_websocket.py (1 hunks)
- deepgram/clients/common/v1/errors.py (1 hunks)
- deepgram/clients/common/v1/helpers.py (1 hunks)
- deepgram/clients/common/v1/websocket_events.py (1 hunks)
- deepgram/clients/errors.py (0 hunks)
- deepgram/clients/helpers.py (0 hunks)
- deepgram/clients/listen/v1/rest/async_client.py (1 hunks)
- deepgram/clients/listen/v1/rest/client.py (1 hunks)
- deepgram/clients/listen/v1/websocket/async_client.py (11 hunks)
- deepgram/clients/listen/v1/websocket/client.py (11 hunks)
- deepgram/clients/listen/v1/websocket/response.py (0 hunks)
- deepgram/clients/manage/v1/async_client.py (1 hunks)
- deepgram/clients/manage/v1/client.py (1 hunks)
- deepgram/clients/selfhosted/v1/async_client.py (1 hunks)
- deepgram/clients/selfhosted/v1/client.py (1 hunks)
- deepgram/clients/speak/v1/rest/async_client.py (1 hunks)
- deepgram/clients/speak/v1/rest/client.py (1 hunks)
- deepgram/clients/speak/v1/websocket/async_client.py (21 hunks)
- deepgram/clients/speak/v1/websocket/client.py (18 hunks)
- examples/text-to-speech/websocket/async_complete/main.py (4 hunks)
- examples/text-to-speech/websocket/complete/main.py (2 hunks)
💤 Files not reviewed due to no reviewable changes (3)
- deepgram/clients/errors.py
- deepgram/clients/helpers.py
- deepgram/clients/listen/v1/websocket/response.py
🚧 Files skipped from review as they are similar to previous changes (25)
- deepgram/audio/speaker/speaker.py
- deepgram/clients/init.py
- deepgram/clients/analyze/v1/async_client.py
- deepgram/clients/analyze/v1/client.py
- deepgram/clients/common/init.py
- deepgram/clients/common/v1/init.py
- deepgram/clients/common/v1/abstract_async_rest.py
- deepgram/clients/common/v1/abstract_async_websocket.py
- deepgram/clients/common/v1/abstract_sync_rest.py
- deepgram/clients/common/v1/abstract_sync_websocket.py
- deepgram/clients/common/v1/errors.py
- deepgram/clients/common/v1/helpers.py
- deepgram/clients/common/v1/websocket_events.py
- deepgram/clients/listen/v1/rest/async_client.py
- deepgram/clients/listen/v1/rest/client.py
- deepgram/clients/listen/v1/websocket/async_client.py
- deepgram/clients/manage/v1/async_client.py
- deepgram/clients/manage/v1/client.py
- deepgram/clients/selfhosted/v1/async_client.py
- deepgram/clients/selfhosted/v1/client.py
- deepgram/clients/speak/v1/rest/async_client.py
- deepgram/clients/speak/v1/rest/client.py
- deepgram/clients/speak/v1/websocket/async_client.py
- examples/text-to-speech/websocket/async_complete/main.py
- examples/text-to-speech/websocket/complete/main.py
🧰 Additional context used
📓 Learnings (1)
deepgram/clients/speak/v1/websocket/client.py (2)
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#468 File: deepgram/clients/speak/v1/websocket/client.py:477-481 Timestamp: 2024-09-27T15:17:25.060Z Learning: When maintaining consistency with abstract base classes and satisfying linters like pylint, it's acceptable to keep method signatures like `send(self, data: Union[str, bytes])` even if the method doesn't handle `bytes`.
Learnt from: dvonthenen PR: deepgram/deepgram-python-sdk#468 File: deepgram/clients/speak/v1/websocket/async_client.py:475-483 Timestamp: 2024-09-27T15:17:06.865Z Learning: When overriding abstract methods like `send` that accept `Union[bytes, str]`, even if the subclass only supports `str`, the method signature should remain the same to satisfy linters like pylint and maintain consistency with the abstract base class.
🔇 Additional comments (14)
deepgram/clients/listen/v1/websocket/client.py (7)
7-7
: Improved class structure and inheritanceThe
ListenWebSocketClient
now inherits fromAbstractSyncWebSocketClient
, which is a good refactoring decision. This change likely reduces code duplication and improves maintainability by centralizing common WebSocket functionality in the parent class.Also applies to: 14-15, 35-37
509-511
: New method for closing the streamThe
_close_message
method is a good addition for explicitly closing the stream. It sends a properly formatted JSON message to indicate the stream should be closed.
519-522
: Improved finish method with parent class callThe
finish
method now correctly calls the parent class'sfinish
method before performing its own cleanup. This ensures that any necessary actions in the parent class are executed. The error handling for the parent method call is also appropriate.
224-354
:⚠️ Potential issueHandle potential JSON decoding errors
When parsing the incoming message with
json.loads(message)
, there's a risk of ajson.JSONDecodeError
if the message is not valid JSON. Currently, this exception is caught by the generalexcept Exception
block, but it's better practice to catch specific exceptions.Consider adding a specific exception handler for JSON decoding:
try: data = json.loads(message) except json.JSONDecodeError as e: self._logger.error("Failed to decode JSON message: %s", e) # Optionally emit an error event or handle the invalid message return
358-360
:⚠️ Potential issueImplement
_process_binary
or remove itThe
_process_binary
method raisesNotImplementedError
, indicating that binary messages are not expected. If binary data will not be processed, consider removing this method. Otherwise, implement the necessary logic to handle binary messages properly.Decide whether to support binary messages:
- If supporting, implement the method.
- If not, and if the superclass does not require it, safely remove the method.
398-398
:⚠️ Potential issueRemove call to non-existent method
_signal_exit
The method
_signal_exit()
is called, but it has been removed from the class. This will result in anAttributeError
at runtime.Consider updating the code to properly handle exit without calling
_signal_exit()
. If necessary, implement the exit logic within_keep_alive
or recreate the_signal_exit
method.
135-147
:⚠️ Potential issueCorrect the class name in log messages
The log messages reference
AsyncListenWebSocketClient
, but the class isListenWebSocketClient
. This could cause confusion during debugging.Apply this diff to correct the class name:
- self._logger.error("AsyncListenWebSocketClient.start failed") - self._logger.debug("AsyncListenWebSocketClient.start LEAVE") + self._logger.error("ListenWebSocketClient.start failed") + self._logger.debug("ListenWebSocketClient.start LEAVE")Likely invalid or redundant comment.
deepgram/clients/speak/v1/websocket/client.py (7)
8-8
: Improved class structure through inheritanceThe changes to the import statements and class definition enhance the overall structure of the
SpeakWSClient
. By inheriting fromAbstractSyncWebSocketClient
, the class can leverage common functionality and maintain a consistent interface. This is a good practice for code reuse and maintainability.Also applies to: 15-16, 38-40
69-71
: Improved initialization and error handlingThe changes to the
__init__
method enhance error handling and leverage the parent class's functionality. The addition of theconfig
check prevents potential issues, and the flexible_speaker
initialization allows for better customization.Minor suggestion: Consider making the error message for the
DeepgramError
more specific, e.g., "Config cannot be None".Also applies to: 101-104, 119-120
168-189
: Improved start method with better error handlingThe changes to the
start
method significantly improve its structure and functionality:
- Delegation to the speaker (if present) enhances separation of concerns.
- Leveraging the parent class's
start
method reduces code duplication.- Improved error handling and logging provide better diagnostics.
These changes make the method more robust and easier to maintain.
Also applies to: 214-219
386-397
: Good addition of binary data processingThe new
_process_binary
method is a good addition that improves the separation of concerns between text and binary data processing. It's implemented in a simple and straightforward manner, emitting an AudioData event with the received binary data.
537-550
: Improved send_raw method with better error handlingThe changes to the
send_raw
method enhance its robustness:
- Improved error handling with specific exception catching.
- Better logging for debugging purposes.
- Utilization of the parent class's
send
method, which is a good practice for code reuse.These improvements make the method more reliable and easier to debug.
Line range hint
600-630
: Enhanced finish method with improved cleanupThe changes to the
finish
method significantly improve the cleanup process:
- Utilization of the parent class's
finish
method, which is a good practice for maintaining the inheritance chain.- Improved thread management, ensuring that all created threads (speaker and flush) are properly stopped and joined.
- Better logging for debugging purposes.
These improvements ensure a more thorough and reliable cleanup process when closing the WebSocket connection.
649-649
: Good addition of backwards-compatible aliasThe addition of
SpeakWebSocketClient = SpeakWSClient
at the end of the file is a good practice. It provides backwards compatibility if the class was previously referred to asSpeakWebSocketClient
in other parts of the codebase or by users of the library. This helps maintain API stability while allowing for the new, more concise class name.
f5f24b7
to
e0ad551
Compare
e0ad551
to
4c9c639
Compare
Proposed changes
Addresses: #264
This is to make way to support Agent API in a much cleaner and reusable way. It should drastically decrease code spawl.
Tested all Speech-to-Text and Text-to-Speech examples. Basically, anything that uses a WebSocket.
Also, unit tests passed further demonstrating that users should be unaware of this internal change.
Types of changes
What types of changes does your code introduce to the community Python SDK?
Put an
x
in the boxes that applyChecklist
Put an
x
in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.Further comments
NA
Summary by CodeRabbit
New Features
AbstractAsyncWebSocketClient
andAbstractSyncWebSocketClient
).DeepgramApiError
andDeepgramUnknownApiError
.Improvements
Bug Fixes