Skip to content
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 Deadlock, Bumped Interface on WS Clients, Consolidated WS Code #345

Merged

Conversation

dvonthenen
Copy link
Contributor

@dvonthenen dvonthenen commented Oct 26, 2024

Proposed changes

Addresses: #344, #308

It was discovered that the threading model/functions used were such that different OS threads were being created for each thread (ie, Send, Receive, KeepAlive, for example) and also that the locking mechanism to keep exclusivity on the send functions would not work on systems where the logical (not physical) CPU count was low (ie, 2 Logical CPUs up to 4ish) leading to a deadlock state.

Identified the issue has been there since the very first PR for v4 (Note1, Note2). Most CPUs these days have 6-8 or more cores which is why this probably wasn't reported. My guess is that this manifested itself by using a VM with 2 CPUs because modern systems have 6+ cores these days, but in virtual machines, you can (and there are reasons to) have 2 vCPUs. The threading and locking model was changed to be in-line with .NET Async best practices, thus correcting the problem.

Since this was a breaking API change, this PR includes:

  • a refactor of the WS client code to have a single code base where other clients extend from that functionality (Python and Go are already like this)
  • because the locking model changed, some of the function interfaces had to utilize the await keyword when calling, thus breaking the interface
  • since we had to break the interface with this change for websocket clients, I preserved the existing v1 clients for those that never had issues (because they had 6+ Logical CPUs) and introduced a v2 for Speak and Listen WebSocket Clients.
  • this also introduced new functions on the root factory to create client methods always to use the most recent version of the client AND provide methods to create older versions of the client (if they didn't want to new Client directly)
  • this in turn, introduced two new examples to prove that you can still use the older v1 clients without changing your code

Other significant notes:

  • Unit tests function as expected.
  • WebSocket examples updated to use the v2 client and work as expected.
  • Two new examples for Listen and Speak were introduced for the v1 clients and they work as expected. These examples are located at:
    • tests/edge_cases/stt_v1_client_example
    • tests/edge_cases/tts_v1_client_example
  • since the "old" v1 interface is there, user's application will still compile and work as expected giving individuals time to migrate to this new interface in their own time

Types of changes

What types of changes does your code introduce to the community .NET SDK?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update or tests (if none of the other choices apply)

Checklist

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.

  • I have read the CONTRIBUTING doc
  • I have lint'ed all of my code using repo standards
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

NA

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced new projects for speech-to-text (stt_v1_client_example) and text-to-speech (tts_v1_client_example) functionalities.
    • Enhanced test coverage for various client methods, including AnalyzeClient, ManageClient, and SpeakClient, ensuring robust error handling and validation.
    • Added new response types for WebSocket interactions, including OpenResponse, CloseResponse, and ErrorResponse, improving event handling.
  • Bug Fixes

    • Improved error handling in callback verification methods across multiple client implementations.
  • Deprecations

    • Marked several interfaces and classes as obsolete, guiding users towards newer versions for improved functionality.
  • Documentation

    • Updated documentation comments for clarity and to reflect recent changes in functionality across various classes and methods.

Copy link
Contributor

coderabbitai bot commented Oct 26, 2024

Warning

Rate limit exceeded

@dvonthenen has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 2 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Files that changed from the base of the PR and between f42b51d and 22518be.

Walkthrough

The pull request introduces significant updates to the Deepgram solution, adding two new projects: stt_v1_client_example and tts_v1_client_example, which are integrated into the solution structure. Additionally, various files across the Deepgram namespace have been modified to enhance functionality, including the introduction of new interfaces and classes, updates to existing methods, and improvements in error handling and logging. Notably, there are changes to the WebSocket client implementations, as well as the addition of metadata handling for audio processing.

Changes

File Path Change Summary
Deepgram.Dev.sln Added projects: stt_v1_client_example, tts_v1_client_example. Updated project references in GlobalSection(ProjectConfigurationPlatforms) and GlobalSection(NestedProjects).
Deepgram.Tests/Fakes/ConcreteRestClient.cs Added class ConcreteRestClient with a constructor accepting apiKey and deepgramClientOptions.
Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs Removed import for Deepgram.Models.Exceptions.v1.
Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs Added using directive for Deepgram.Abstractions.v1.
Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs Updated namespaces: removed NSubstitute, added Deepgram.Abstractions.v1.
Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs Added using directive for Deepgram.Abstractions.v1.
Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs Added using directive for Deepgram.Abstractions.v1. Updated tests for CallBack property handling.
Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs Added using directive for Deepgram.Abstractions.v1.
Deepgram.Tests/UnitTests/UtilitiesTests/RequestContentUtilTests.cs Updated import from Deepgram.Abstractions to Deepgram.Abstractions.v1.
Deepgram/Abstractions/v1/AbstractRestClient.cs Added abstract class AbstractRestClient with multiple async methods for REST API interactions.
Deepgram/Abstractions/v1/Constants.cs Updated namespace from Deepgram.Abstractions to Deepgram.Abstractions.v1.
Deepgram/Abstractions/v1/LocalFileWithMetadata.cs Added class LocalFileWithMetadata for file and metadata handling.
Deepgram/Abstractions/v1/NoopSchema.cs Updated namespace from Deepgram.Abstractions to Deepgram.Abstractions.v1.
Deepgram/Abstractions/v1/Utilities.cs Updated namespace from Deepgram.Abstractions to Deepgram.Abstractions.v1.
Deepgram/Abstractions/v2/AbstractRestClient.cs Rewritten class AbstractRestClient with improved logging and error handling.
Deepgram/Abstractions/v2/AbstractWebSocketClient.cs Added abstract class for managing WebSocket connections with event handling.
Deepgram/Abstractions/v2/Constants.cs Added static class for constants related to REST and WebSocket communication.
Deepgram/Abstractions/v2/LocalFileWithMetadata.cs Updated LocalFileWithMetadata class with enhanced documentation.
Deepgram/Abstractions/v2/NoopSchema.cs Added new file for placeholder functionality.
Deepgram/Abstractions/v2/Utilities.cs Added utility functions for HTTP request handling.
Deepgram/Clients/Analyze/v1/Client.cs Updated constructor to include httpId.
Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs Added new interface for audio and text analysis methods.
Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs Added new interface for REST client interaction with transcription services.
Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs Added interface for managing WebSocket connections.
Deepgram/Clients/Interfaces/v2/ISelfHostedClient.cs Added interface for managing credentials in a self-hosted environment.
Deepgram/Clients/Interfaces/v2/ISpeakClient.cs Added deprecated interface for Speak API.
Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs Added new interface for Speak API interactions.
Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs Added interface for Speak API WebSocket interactions.
Deepgram/Models/Common/v2/WebSocket/CloseResponse.cs Added record for WebSocket close event responses.
Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs Added record for WebSocket error responses.
Deepgram/Models/Common/v2/WebSocket/OpenResponse.cs Added record for WebSocket open event responses.
Deepgram/Models/Common/v2/WebSocket/UnhandledResponse.cs Added record for unhandled WebSocket responses.
Deepgram/Models/Listen/v2/WebSocket/Channel.cs Added record for audio channel information.
Deepgram/Models/Listen/v2/WebSocket/Hit.cs Added record for audio processing hits.
Deepgram/Models/Listen/v2/WebSocket/MetadataResponse.cs Added record for metadata responses.
Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs Added record for results of WebSocket interactions.
Deepgram/Models/Speak/v2/WebSocket/AudioResponse.cs Added record for audio responses, implementing IDisposable.
Deepgram/Models/Speak/v2/WebSocket/ControlMessage.cs Added class for control messages in WebSocket communication.
Deepgram/Models/Speak/v2/WebSocket/ErrorResponse.cs Added record for Speak API error responses.
Deepgram/Models/Speak/v2/WebSocket/FlushedResponse.cs Added record for flushed responses in WebSocket context.
Deepgram/Models/Speak/v2/WebSocket/MetadataResponse.cs Added record for Speak API metadata responses.
Deepgram/Models/Speak/v2/WebSocket/OpenResponse.cs Added record for Speak API open event responses.
Deepgram/Models/Speak/v2/WebSocket/SpeakSchema.cs Added schema class for audio processing parameters.
Deepgram/GlobalUsings.cs Removed several global using directives.
Deepgram/ListenWebSocketClient.cs Updated namespace import to version 2 for WebSocket client.
Deepgram/Models/Listen/v2/WebSocket/ListenType.cs Added enum for different WebSocket event types.
Deepgram/Models/Listen/v2/WebSocket/LiveSchema.cs Added class for live audio processing configurations.

Possibly related PRs

Suggested reviewers

  • lukeocodes
  • naomi-lgbt
  • jpvajda

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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

🛑 Comments failed to post (107)
Deepgram/Clients/Interfaces/v2/IResponseEvent.cs (2)

7-8: ⚠️ Potential issue

Remove 'I' prefix from class name.

The 'I' prefix is a C# naming convention specifically for interfaces. Since this is a class, it should be renamed to ResponseEvent<T> to follow C# naming conventions.

-public class IResponseEvent<T>
+public class ResponseEvent<T>
📝 Committable suggestion

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

public class ResponseEvent<T>
{

7-10: 🛠️ Refactor suggestion

Consider making the class immutable for thread safety.

Given that this class is part of the v2 API designed to fix threading issues, consider making it immutable to ensure thread safety. This can be achieved by:

  1. Making the property get-only
  2. Initializing the response through the constructor
 public class IResponseEvent<T>
 {
-    public T? Response { get; set; }
+    public T? Response { get; }
+
+    public ResponseEvent(T? response)
+    {
+        Response = response;
+    }
 }

Committable suggestion was skipped due to low confidence.

Deepgram/Clients/Speak/v2/WebSocket/ResponseEvent.cs (1)

7-10: 🛠️ Refactor suggestion

Consider making the Response property read-only.

The current implementation allows the Response property to be modified after the event is created, which could lead to unexpected behavior in event handlers. Consider making it read-only to ensure immutability:

-public T? Response { get; set; } = response;
+public T? Response { get; } = response;

This change would:

  1. Prevent accidental modifications of the response after event creation
  2. Better align with the event-driven programming pattern where event args are typically immutable
  3. Make the class thread-safe by design
📝 Committable suggestion

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

public class ResponseEvent<T>(T? response) : EventArgs
{
    public T? Response { get; } = response;
}
Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (1)

12-14: ⚠️ Potential issue

Add property documentation and implement proper resource management.

The properties need documentation and the Content property should be properly managed to prevent memory leaks.

Apply these improvements:

+    /// <summary>
+    /// Gets or sets the metadata associated with the file content.
+    /// </summary>
-    public Dictionary<string, string> Metadata { get; set; }
+    public Dictionary<string, string> Metadata { get; set; } = new();

+    /// <summary>
+    /// Gets or sets the file content as a MemoryStream.
+    /// The caller is responsible for disposing of this stream when no longer needed.
+    /// </summary>
+    /// <remarks>
+    /// This property should be properly disposed of to prevent memory leaks.
+    /// </remarks>
     public MemoryStream Content { get; set; }

+    /// <summary>
+    /// Releases the resources used by the Content stream.
+    /// </summary>
+    public void Dispose()
+    {
+        Content?.Dispose();
+    }

Consider implementing the IDisposable pattern to ensure proper resource cleanup:

-public class LocalFileWithMetadata
+public class LocalFileWithMetadata : IDisposable

Committable suggestion was skipped due to low confidence.

Deepgram/Clients/Interfaces/v2/ISpeakClient.cs (1)

7-16: ⚠️ Potential issue

Fix XML documentation format.

The documentation block uses incorrect comment format. XML documentation comments should use /// for each line and proper XML tags.

Apply this diff to fix the documentation:

-/// <summary>
-// *********** WARNING ***********
-// This class provides the ISpeakClient implementation for the Deepgram API
-//
-// Deprecated: This class is deprecated. Use the ISpeakRESTClient interface instead.
-// This will be removed in a future release.
-//
-// This package is frozen and no new functionality will be added.
-// *********** WARNING ***********
-/// </summary>
+/// <summary>
+/// *********** WARNING ***********
+/// This class provides the ISpeakClient implementation for the Deepgram API
+/// 
+/// Deprecated: This class is deprecated. Use the ISpeakRESTClient interface instead.
+/// This will be removed in a future release.
+/// 
+/// This package is frozen and no new functionality will be added.
+/// *********** WARNING ***********
+/// </summary>
📝 Committable suggestion

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

/// <summary>
/// *********** WARNING ***********
/// This class provides the ISpeakClient implementation for the Deepgram API
/// 
/// Deprecated: This class is deprecated. Use the ISpeakRESTClient interface instead.
/// This will be removed in a future release.
/// 
/// This package is frozen and no new functionality will be added.
/// *********** WARNING ***********
/// </summary>
Deepgram/Models/Speak/v2/WebSocket/ControlMessage.cs (2)

9-14: ⚠️ Potential issue

Improve property design and documentation.

Several issues with the Type property:

  1. XML documentation incorrectly describes it as "Text of the words to speak"
  2. Property is nullable despite being set in constructor
  3. Consider using an enum for control message types

Apply this diff:

     /// <summary>
-    /// Text of the words to speak
+    /// Gets or sets the type of control message.
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("type")]
-    public string? Type { get; set; } = text;
+    public string Type { get; set; } = text;

Consider creating an enum for control message types:

public enum ControlMessageType
{
    Start,
    Stop,
    // Add other control message types
}

16-22: ⚠️ Potential issue

Add missing using directives and improve error handling.

The ToString implementation has several issues:

  1. Missing using directives for JsonSerializer and Regex
  2. Undocumented exceptions
  3. Unclear necessity for Regex.Unescape

Add these using directives at the top of the file:

+using System.Text.RegularExpressions;
+using System.Text.Json;

Update the method documentation:

     /// <summary>
     /// Override ToString method to serialize the object
     /// </summary>
+    /// <returns>A JSON string representation of the control message.</returns>
+    /// <exception cref="JsonException">Thrown when serialization fails.</exception>
     public override string ToString()
     {
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
     }
📝 Committable suggestion

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

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    /// <returns>A JSON string representation of the control message.</returns>
    /// <exception cref="JsonException">Thrown when serialization fails.</exception>
    public override string ToString()
    {
        return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
    }
Deepgram/Models/Speak/v2/WebSocket/AudioResponse.cs (3)

9-11: ⚠️ Potential issue

Fix incorrect XML documentation.

The XML documentation describes this as an "Open event type" which appears to be incorrect. This property represents the audio response type.

-    /// Open event type.
+    /// The type of speak response, defaults to Audio.
📝 Committable suggestion

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

    /// <summary>
    /// The type of speak response, defaults to Audio.
    /// </summary>

17-20: 🛠️ Refactor suggestion

Enhance Stream property implementation for thread safety and resource management.

Consider implementing the following improvements:

  1. Add thread-safe access to prevent potential race conditions during WebSocket operations
  2. Ensure proper disposal of existing stream when setting a new value
-    public MemoryStream? Stream { get; set; }
+    private MemoryStream? _stream;
+    public MemoryStream? Stream
+    {
+        get => _stream;
+        set
+        {
+            lock (this)
+            {
+                _stream?.Dispose();
+                _stream = value;
+            }
+        }
+    }
📝 Committable suggestion

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

    /// <summary>
    /// A stream of the audio file
    /// </summary>
    private MemoryStream? _stream;
    public MemoryStream? Stream
    {
        get => _stream;
        set
        {
            lock (this)
            {
                _stream?.Dispose();
                _stream = value;
            }
        }
    }

25-28: 🛠️ Refactor suggestion

Implement the complete dispose pattern.

Since this is a public disposable type that could be inherited, it should either be sealed or implement the full dispose pattern to ensure proper cleanup in derived classes.

Option 1 (Preferred) - Seal the record:

-public record AudioResponse : IDisposable
+public sealed record AudioResponse : IDisposable

Option 2 - Implement full dispose pattern:

-    public void Dispose()
-    {
-        Stream?.Dispose();
-    }
+    private bool _disposed;
+
+    protected virtual void Dispose(bool disposing)
+    {
+        if (!_disposed)
+        {
+            if (disposing)
+            {
+                Stream?.Dispose();
+            }
+            _disposed = true;
+        }
+    }
+
+    public void Dispose()
+    {
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }

Committable suggestion was skipped due to low confidence.

Deepgram/Models/Listen/v2/WebSocket/Search.cs (1)

23-29: ⚠️ Potential issue

Add null check and improve documentation for ToString method.

The current implementation could be more robust and better documented.

     /// <summary>
-    /// Override ToString method to serialize the object
+    /// Converts the Search object to its JSON string representation.
     /// </summary>
+    /// <returns>A JSON-formatted string representing the Search object.
+    /// Null values are omitted from the output.</returns>
+    /// <exception cref="JsonException">Thrown when serialization fails.</exception>
     public override string ToString()
     {
+        var options = JsonSerializeOptions.DefaultOptions 
+            ?? throw new InvalidOperationException("DefaultOptions not initialized");
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        return Regex.Unescape(JsonSerializer.Serialize(this, options));
     }
📝 Committable suggestion

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

    /// <summary>
    /// Converts the Search object to its JSON string representation.
    /// </summary>
    /// <returns>A JSON-formatted string representing the Search object.
    /// Null values are omitted from the output.</returns>
    /// <exception cref="JsonException">Thrown when serialization fails.</exception>
    public override string ToString()
    {
        var options = JsonSerializeOptions.DefaultOptions 
            ?? throw new InvalidOperationException("DefaultOptions not initialized");
        return Regex.Unescape(JsonSerializer.Serialize(this, options));
    }
Deepgram/Models/Listen/v2/WebSocket/Average.cs (2)

23-29: ⚠️ Potential issue

Add missing using statements and consider null handling.

The ToString implementation needs the following improvements:

  1. Add required using statements
  2. Consider null handling to avoid potential serialization issues

Apply these changes:

+using System.Text.RegularExpressions;
+using System.Text.Json;

 public override string ToString()
 {
-    return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+    try
+    {
+        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+    }
+    catch (JsonException)
+    {
+        return string.Empty;
+    }
 }
📝 Committable suggestion

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

    using System.Text.RegularExpressions;
    using System.Text.Json;

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    public override string ToString()
    {
        try
        {
            return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
        }
        catch (JsonException)
        {
            return string.Empty;
        }
    }

7-21: 🛠️ Refactor suggestion

Consider enhancing type safety and documentation.

While the implementation is solid, consider these improvements:

  1. Use an enum for Sentiment to restrict values to "Positive", "Negative", or "Neutral"
  2. Add range validation for SentimentScore if there are expected bounds
  3. Enhance XML comments with value ranges and examples

Here's a suggested implementation:

+public enum SentimentType
+{
+    Positive,
+    Negative,
+    Neutral
+}

 public record Average
 {
     /// <summary>
-    /// Sentiment: Positive, Negative, or Neutral.
+    /// The sentiment classification result.
+    /// </summary>
+    /// <example>
+    /// Positive, Negative, or Neutral
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("sentiment")]
-    public string? Sentiment { get; set; }
+    public SentimentType? Sentiment { get; set; }

     /// <summary>
-    /// Sentiment score.
+    /// A numerical score indicating the strength of the sentiment.
+    /// Typically ranges from -1.0 (most negative) to 1.0 (most positive).
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("sentiment_score")]
     public double? SentimentScore { get; set; }

Committable suggestion was skipped due to low confidence.

Deepgram/Abstractions/v2/WebSocketMessage.cs (2)

9-12: ⚠️ Potential issue

Add null check for message parameter.

The constructor should validate that the message parameter is not null to prevent NullReferenceException.

 public WebSocketMessage(byte[] message, WebSocketMessageType type)
     : this(message, type, Constants.UseArrayLengthForSend)
 {
+    ArgumentNullException.ThrowIfNull(message);
 }

Committable suggestion was skipped due to low confidence.


14-25: ⚠️ Potential issue

Improve parameter validation and code clarity.

The constructor needs several improvements for robustness and maintainability:

  1. Add null check for message parameter
  2. Simplify the length validation logic
  3. Make the valid length range more explicit
 public WebSocketMessage(byte[] message, WebSocketMessageType type, int length)
 {
+    ArgumentNullException.ThrowIfNull(message);
+    
-    if (length != Constants.UseArrayLengthForSend && length <= message.Length && length > 0)
+    int messageLength = length == Constants.UseArrayLengthForSend 
+        ? message.Length 
+        : ValidateAndGetLength(length, message.Length);
+
+    Message = new ArraySegment<byte>(message, 0, messageLength);
+    MessageType = type;
 }
+
+private static int ValidateAndGetLength(int requestedLength, int maxLength)
+{
+    if (requestedLength <= 0)
+        throw new ArgumentOutOfRangeException(nameof(requestedLength), "Length must be positive.");
+    if (requestedLength > maxLength)
+        throw new ArgumentOutOfRangeException(nameof(requestedLength), $"Length cannot exceed message length of {maxLength}.");
+    return requestedLength;
 }
📝 Committable suggestion

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

    public WebSocketMessage(byte[] message, WebSocketMessageType type, int length)
    {
        ArgumentNullException.ThrowIfNull(message);
        
        int messageLength = length == Constants.UseArrayLengthForSend 
            ? message.Length 
            : ValidateAndGetLength(length, message.Length);

        Message = new ArraySegment<byte>(message, 0, messageLength);
        MessageType = type;
    }

    private static int ValidateAndGetLength(int requestedLength, int maxLength)
    {
        if (requestedLength <= 0)
            throw new ArgumentOutOfRangeException(nameof(requestedLength), "Length must be positive.");
        if (requestedLength > maxLength)
            throw new ArgumentOutOfRangeException(nameof(requestedLength), $"Length cannot exceed message length of {maxLength}.");
        return requestedLength;
    }
Deepgram/Models/Speak/v2/WebSocket/FlushedResponse.cs (2)

24-30: ⚠️ Potential issue

Review the ToString implementation for potential issues.

The current implementation has several concerns:

  1. Using Regex.Unescape on JSON might corrupt valid JSON strings
  2. Missing using statement for System.Text.RegularExpressions
  3. No error handling for serialization failures
+using System.Text.RegularExpressions;

 public record FlushedResponse
 {
     // ... other members ...

     /// <summary>
-    /// Override ToString method to serialize the object
+    /// Converts the response to its JSON string representation.
+    /// </summary>
+    /// <returns>A JSON string representation of the response.</returns>
+    /// <exception cref="JsonException">Thrown when serialization fails.</exception>
     /// </summary>
     public override string ToString()
     {
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
     }
📝 Committable suggestion

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

    /// <summary>
    /// Converts the response to its JSON string representation.
    /// </summary>
    /// <returns>A JSON string representation of the response.</returns>
    /// <exception cref="JsonException">Thrown when serialization fails.</exception>
    public override string ToString()
    {
        return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
    }

7-15: 🛠️ Refactor suggestion

Consider enhancing the record definition and Type property.

  1. Mark the record as sealed for better performance since it's not designed for inheritance.
  2. Enhance the XML documentation to better describe the purpose of this response type.
  3. Since the Type is always SpeakType.Flushed, consider making it non-nullable.
-public record FlushedResponse
+/// <summary>
+/// Represents a response indicating that all pending audio data has been flushed in the WebSocket stream.
+/// This response is sent when the server has processed and transmitted all queued audio data.
+/// </summary>
+public sealed record FlushedResponse
 {
     /// <summary>
-    /// Flush event type.
+    /// The type of the speak event. Always set to SpeakType.Flushed for this response.
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("type")]
     [JsonConverter(typeof(JsonStringEnumConverter))]
-    public SpeakType? Type { get; set; } = SpeakType.Flushed;
+    public SpeakType Type { get; init; } = SpeakType.Flushed;
📝 Committable suggestion

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

/// <summary>
/// Represents a response indicating that all pending audio data has been flushed in the WebSocket stream.
/// This response is sent when the server has processed and transmitted all queued audio data.
/// </summary>
public sealed record FlushedResponse
{
    /// <summary>
    /// The type of the speak event. Always set to SpeakType.Flushed for this response.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    [JsonPropertyName("type")]
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public SpeakType Type { get; init; } = SpeakType.Flushed;
Deepgram/Models/Listen/v2/WebSocket/Channel.cs (1)

23-29: ⚠️ Potential issue

Simplify ToString implementation and add null check

The current implementation has several potential issues:

  1. Regex.Unescape is unnecessary for JSON serialization and could potentially corrupt valid JSON strings
  2. Missing using directive for System.Text.RegularExpressions
  3. No null check on the serialization result
+using System.Text.RegularExpressions;

 public override string ToString()
 {
-    return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+    var json = JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
+    return json ?? string.Empty;
 }
📝 Committable suggestion

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

    using System.Text.RegularExpressions;

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    public override string ToString()
    {
        var json = JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
        return json ?? string.Empty;
    }
Deepgram/Models/Common/v2/WebSocket/OpenResponse.cs (2)

17-28: 🛠️ Refactor suggestion

Consider using record's built-in copy constructor instead of explicit Copy method.

Since this is a record type, C# provides built-in copy construction with the with expression. This would be more idiomatic and less error-prone.

Consider this alternative approach:

-    /// <summary>
-    /// Copy method to copy the object
-    /// </summary>
-    public void Copy(OpenResponse other)
-    {
-        if (other == null)
-        {
-            return;
-        }
-
-        Type = other.Type;
-    }
+    /// <summary>
+    /// Creates a new instance with the specified type.
+    /// </summary>
+    /// <param name="type">The WebSocket event type.</param>
+    /// <returns>A new instance of OpenResponse with the specified type.</returns>
+    public OpenResponse WithType(WebSocketType? type) => this with { Type = type };

Usage example:

var newResponse = existingResponse.WithType(WebSocketType.Open);

30-36: ⚠️ Potential issue

Add missing using statements for JsonSerializer and Regex.

The code references JsonSerializer, JsonSerializeOptions, and Regex but the required using statements are missing.

Add these using statements at the top of the file:

+using System.Text.RegularExpressions;
+using System.Text.Json;
+using System.Text.Json.Serialization;

Committable suggestion was skipped due to low confidence.

Deepgram/Models/Listen/v2/WebSocket/SpeechStartedResponse.cs (2)

7-16: 🛠️ Refactor suggestion

Consider making Type property non-nullable

Since this record specifically represents a speech started event, and Type has a default value of ListenType.SpeechStarted, consider making it non-nullable. This would better express the intent and reduce unnecessary complexity.

-    public ListenType? Type { get; set; } = ListenType.SpeechStarted;
+    public ListenType Type { get; init; } = ListenType.SpeechStarted;

Also consider using init instead of set since this is a record type, promoting immutability.

📝 Committable suggestion

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

public record SpeechStartedResponse
{
    /// <summary>
    /// SpeechStarted event type.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("type")]
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public ListenType Type { get; init; } = ListenType.SpeechStarted;


7-38: 🛠️ Refactor suggestion

Ensure thread safety in WebSocket context

Given that this record is used in a WebSocket context and the PR aims to fix deadlock issues:

  1. Consider making all properties immutable using init instead of set
  2. Add the readonly modifier to the record declaration
-public record SpeechStartedResponse
+public readonly record SpeechStartedResponse
 {
-    public ListenType? Type { get; set; } = ListenType.SpeechStarted;
+    public ListenType Type { get; init; } = ListenType.SpeechStarted;
 
-    public int[]? Channel { get; set; }
+    public IReadOnlyList<int>? ChannelIndices { get; init; }
 
-    public decimal? Timestamp { get; set; }
+    public decimal? Timestamp { get; init; }
📝 Committable suggestion

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

public readonly record SpeechStartedResponse
{
    /// <summary>
    /// SpeechStarted event type.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    [JsonPropertyName("type")]
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public ListenType Type { get; init; } = ListenType.SpeechStarted;

    /// <summary>
    /// Channel index information
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    [JsonPropertyName("channel_index")]
    public IReadOnlyList<int>? ChannelIndices { get; init; }

    /// <summary>
    /// Timestamp of the event.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    [JsonPropertyName("timestamp")]
    public decimal? Timestamp { get; init; }

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    public override string ToString()
    {
        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
    }
}
Deepgram/Models/Listen/v2/WebSocket/UtteranceEndResponse.cs (1)

31-37: ⚠️ Potential issue

Add null checks and error handling to ToString implementation.

The current implementation might throw exceptions if JsonSerializeOptions.DefaultOptions is null, and the use of Regex.Unescape might be unnecessary depending on the serialization options.

Consider adding null checks and error handling:

     public override string ToString()
     {
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        try
+        {
+            var options = JsonSerializeOptions.DefaultOptions ?? new JsonSerializerOptions();
+            return JsonSerializer.Serialize(this, options);
+        }
+        catch (JsonException ex)
+        {
+            return $"{{\"error\": \"Failed to serialize {nameof(UtteranceEndResponse)}: {ex.Message}\"}}";
+        }
     }
📝 Committable suggestion

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

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    public override string ToString()
    {
        try
        {
            var options = JsonSerializeOptions.DefaultOptions ?? new JsonSerializerOptions();
            return JsonSerializer.Serialize(this, options);
        }
        catch (JsonException ex)
        {
            return $"{{\"error\": \"Failed to serialize {nameof(UtteranceEndResponse)}: {ex.Message}\"}}";
        }
    }
Deepgram/Models/Common/v2/WebSocket/UnhandledResponse.cs (4)

7-14: ⚠️ Potential issue

Remove default empty string from nullable Raw property.

The default empty string value contradicts the nullable design and prevents JsonIgnoreCondition.WhenWritingNull from working as expected. It's better to let it default to null to maintain consistency with the nullable design.

-    public string? Raw { get; set; } = "";
+    public string? Raw { get; set; }
📝 Committable suggestion

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

public record UnhandledResponse 
{
    /// <summary>
    /// Raw JSON
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("raw")]
    public string? Raw { get; set; }

24-36: 🛠️ Refactor suggestion

Consider removing custom Copy method in favor of record's built-in copy constructor.

C# records already provide built-in copy functionality through the with expression. If custom copying is needed, consider these improvements:

  1. Add XML documentation for the parameter
  2. Add null validation for individual property values
  3. Return the modified instance for method chaining

Alternative approach (Recommended):

-    public void Copy(UnhandledResponse other)
-    {
-        if (other == null)
-        {
-            return;
-        }
-
-        Raw = other.Raw;
-        Type = other.Type;
-    }

// Usage: var copy = original with { Raw = "new value" };

Committable suggestion was skipped due to low confidence.


38-44: ⚠️ Potential issue

Enhance ToString method robustness.

The current implementation could be improved to handle edge cases and errors:

     public override string ToString()
     {
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        try
+        {
+            var json = JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
+            return string.IsNullOrEmpty(json) ? string.Empty : Regex.Unescape(json);
+        }
+        catch (JsonException ex)
+        {
+            return $"{{\"error\": \"Failed to serialize: {ex.Message}\"}}";
+        }
     }
📝 Committable suggestion

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

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    public override string ToString()
    {
        try
        {
            var json = JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
            return string.IsNullOrEmpty(json) ? string.Empty : Regex.Unescape(json);
        }
        catch (JsonException ex)
        {
            return $"{{\"error\": \"Failed to serialize: {ex.Message}\"}}";
        }
    }

16-22: ⚠️ Potential issue

Resolve conflict between nullable Type and default value.

The property is marked nullable but has a default value, making the nullable annotation redundant and preventing JsonIgnoreCondition.WhenWritingNull from working. Choose one of these approaches:

Option 1 (Recommended) - If Type should always have a value:

-    public WebSocketType? Type { get; set; } = WebSocketType.Unhandled;
+    public WebSocketType Type { get; set; } = WebSocketType.Unhandled;

Option 2 - If Type should be nullable:

-    public WebSocketType? Type { get; set; } = WebSocketType.Unhandled;
+    public WebSocketType? Type { get; set; }
📝 Committable suggestion

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

    /// <summary>
    /// Unhandled event type.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("type")]
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public WebSocketType Type { get; set; } = WebSocketType.Unhandled;
```

Option 2:
```suggestion
    /// <summary>
    /// Unhandled event type.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("type")]
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public WebSocketType? Type { get; set; }
Deepgram/Models/Listen/v2/WebSocket/Hit.cs (2)

38-44: 🛠️ Refactor suggestion

Improve ToString implementation robustness.

The current implementation has a few potential issues:

  1. Unnecessary use of Regex.Unescape as JsonSerializer already handles escaping
  2. Missing exception handling for serialization errors
  3. No XML documentation for the return value
     /// <summary>
-    /// Override ToString method to serialize the object
+    /// Converts the Hit object to its JSON string representation.
     /// </summary>
+    /// <returns>A JSON string representation of the Hit object, or an error message if serialization fails.</returns>
     public override string ToString()
     {
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        try
+        {
+            return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
+        }
+        catch (JsonException ex)
+        {
+            return $"Error serializing Hit: {ex.Message}";
+        }
     }
📝 Committable suggestion

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

    /// <summary>
    /// Converts the Hit object to its JSON string representation.
    /// </summary>
    /// <returns>A JSON string representation of the Hit object, or an error message if serialization fails.</returns>
    public override string ToString()
    {
        try
        {
            return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
        }
        catch (JsonException ex)
        {
            return $"Error serializing Hit: {ex.Message}";
        }
    }

7-37: 🛠️ Refactor suggestion

Consider adding property validation.

The Start and End properties represent time offsets but lack validation to ensure Start is less than End. Consider implementing validation either through a constructor or property setters.

 public record Hit
 {
+    private decimal? start;
+    private decimal? end;
+
     // ... other properties ...
 
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("end")]
-    public decimal? End { get; set; }
+    public decimal? End
+    {
+        get => end;
+        set
+        {
+            if (start.HasValue && value.HasValue && value < start)
+            {
+                throw new ArgumentException("End time cannot be less than Start time");
+            }
+            end = value;
+        }
+    }
 
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("start")]
-    public decimal? Start { get; set; }
+    public decimal? Start
+    {
+        get => start;
+        set
+        {
+            if (end.HasValue && value.HasValue && value > end)
+            {
+                throw new ArgumentException("Start time cannot be greater than End time");
+            }
+            start = value;
+        }
+    }
📝 Committable suggestion

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

public record Hit
{
    private decimal? start;
    private decimal? end;

    /// <summary>
    /// Value between 0 and 1 that indicates the model's relative confidence in this hit.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("confidence")]
    public double? Confidence { get; set; }


    /// <summary>
    /// Offset in seconds from the start of the audio to where the hit ends.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("end")]
    public decimal? End
    {
        get => end;
        set
        {
            if (start.HasValue && value.HasValue && value < start)
            {
                throw new ArgumentException("End time cannot be less than Start time");
            }
            end = value;
        }
    }

    /// <summary>
    /// Transcript that corresponds to the time between start and end.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("snippet")]
    public string? Snippet { get; set; }

    /// <summary>
    /// Offset in seconds from the start of the audio to where the hit occurs.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("start")]
    public decimal? Start
    {
        get => start;
        set
        {
            if (end.HasValue && value.HasValue && value > end)
            {
                throw new ArgumentException("Start time cannot be greater than End time");
            }
            start = value;
        }
    }
Deepgram/Models/Listen/v2/WebSocket/Alternative.cs (2)

5-5: ⚠️ Potential issue

Add missing using directives.

The following imports are required but missing:

  • System.Text.Json for JsonSerializer
  • System.Text.RegularExpressions for Regex

Add these imports at the beginning of the file:

+using System.Text.Json;
+using System.Text.RegularExpressions;

namespace Deepgram.Models.Listen.v2.WebSocket;
📝 Committable suggestion

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

using System.Text.Json;
using System.Text.RegularExpressions;

namespace Deepgram.Models.Listen.v2.WebSocket;

19-20: 🛠️ Refactor suggestion

Consider adding validation for Confidence property.

The confidence value should be between 0 and 1 as per the documentation, but there's no validation to ensure this constraint.

Consider adding a property setter validation:

     [JsonPropertyName("confidence")]
-    public double? Confidence { get; set; }
+    public double? Confidence
+    {
+        get => _confidence;
+        set
+        {
+            if (value.HasValue && (value.Value < 0 || value.Value > 1))
+            {
+                throw new ArgumentOutOfRangeException(nameof(value), "Confidence must be between 0 and 1");
+            }
+            _confidence = value;
+        }
+    }
+    private double? _confidence;
📝 Committable suggestion

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

    [JsonPropertyName("confidence")]
    public double? Confidence
    {
        get => _confidence;
        set
        {
            if (value.HasValue && (value.Value < 0 || value.Value > 1))
            {
                throw new ArgumentOutOfRangeException(nameof(value), "Confidence must be between 0 and 1");
            }
            _confidence = value;
        }
    }
    private double? _confidence;
Deepgram/Models/Listen/v2/WebSocket/Metadata.cs (4)

23-28: ⚠️ Potential issue

Fix the XML documentation reference for ModelInfo.

The XML documentation refers to <see cref="ModelInfo"/>, but ModelInfo appears to be a property type rather than a separate type that can be referenced.

-    /// IReadonlyDictionary of <see cref="ModelInfo"/>
+    /// Model information containing details about the model used
📝 Committable suggestion

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

    /// <summary>
    /// Model information containing details about the model used
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("model_info")]
    public ModelInfo? ModelInfo { get; set; }

42-45: ⚠️ Potential issue

Improve ToString implementation robustness.

The current implementation has several potential issues:

  1. Unnecessary use of Regex.Unescape which could cause issues with special characters
  2. No error handling for serialization failures

Consider this safer implementation:

-    public override string ToString()
-    {
-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
-    }
+    public override string ToString()
+    {
+        try
+        {
+            return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
+        }
+        catch (JsonException ex)
+        {
+            return $"{{\"error\": \"Failed to serialize metadata: {ex.Message}\"}}";
+        }
+    }
📝 Committable suggestion

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

    public override string ToString()
    {
        try
        {
            return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
        }
        catch (JsonException ex)
        {
            return $"{{\"error\": \"Failed to serialize metadata: {ex.Message}\"}}";
        }
    }

7-8: 🛠️ Refactor suggestion

Consider making the record immutable for better encapsulation.

Since this is a data transfer object representing metadata, it should be immutable. Consider using init-only properties or constructor initialization.

-public record MetaData
+public sealed record MetaData
{
-    public string? RequestId { get; set; }
+    public string? RequestId { get; init; }

Committable suggestion was skipped due to low confidence.


1-46: 💡 Codebase verification

Based on the gathered information, I can now provide the final response:

MetaData class integration appears incomplete in v2 WebSocket implementation

The verification reveals some concerns:

  • The v2 WebSocket client implementation directory (Deepgram/Clients/Live/v2/) is missing
  • While the MetaData class is properly defined and used in ResultResponse v2, the corresponding client implementation is not yet available
  • Test coverage shows commented-out tests for metadata handling
🔗 Analysis chain

Verify integration with WebSocket client implementation.

Since this is part of the v2 WebSocket implementation addressing deadlock issues, let's verify its integration with the WebSocket client.


Let me gather more specific information about the integration.


Let me check one more aspect to verify the complete integration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of the MetaData class in WebSocket clients
echo "Searching for MetaData usage in WebSocket clients..."
rg -l "MetaData" --type cs

# Search for specific patterns of metadata handling in WebSocket implementations
echo "Checking metadata handling patterns..."
ast-grep --pattern 'new MetaData { $$ }'

Length of output: 602


Script:

#!/bin/bash
# Check how MetaData is used in ResultResponse and Client implementations
echo "Checking ResultResponse v2 implementation..."
rg -A 10 "MetaData" Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs

echo "\nChecking WebSocket client implementation..."
rg -A 10 "MetaData" Deepgram/Clients/Live/v1/Client.cs

# Look for any potential deadlock-related fixes in v2
echo "\nChecking for async/await patterns in v2..."
rg -A 5 "async|await" Deepgram/Models/Listen/v2/WebSocket/

Length of output: 1686


Script:

#!/bin/bash
# Check for the v2 WebSocket client implementation
echo "Searching for v2 WebSocket client..."
fd -e cs . Deepgram/Clients/Live/v2/

# Check for any test coverage of the MetaData class in v2
echo "\nChecking test coverage..."
rg -A 10 "MetaData" Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs

# Look for any configuration or setup related to MetaData in v2
echo "\nChecking for metadata configuration..."
rg -A 5 "Configure|Setup.*Meta" Deepgram/Models/Listen/v2/

Length of output: 1663

Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (1)

7-37: 🛠️ Refactor suggestion

Consider improving property defaults and documentation.

  1. The default empty strings ("") for nullable properties could be misleading. Consider using null as the default since these properties are nullable and have JsonIgnoreCondition.WhenWritingNull.
  2. The XML documentation could be more descriptive to help API consumers.

Here's the suggested improvement:

 public record ErrorResponse
 {
     /// <summary>
-    /// Error Description
+    /// A detailed description of the error that occurred during WebSocket communication.
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("description")]
-    public string? Description { get; set; } = "";
+    public string? Description { get; set; } = null;

     /// <summary>
-    /// Error Message
+    /// A brief message describing the error condition.
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("message")]
-    public string? Message { get; set; } = "";
+    public string? Message { get; set; } = null;

     /// <summary>
-    /// Error Variant
+    /// Specifies the variant or category of the error for more specific error handling.
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("variant")]
-    public string? Variant { get; set; } = "";
+    public string? Variant { get; set; } = null;
📝 Committable suggestion

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

public record ErrorResponse
{
    /// <summary>
    /// A detailed description of the error that occurred during WebSocket communication.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("description")]
    public string? Description { get; set; } = null;

    /// <summary>
    /// A brief message describing the error condition.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("message")]
    public string? Message { get; set; } = null;

    /// <summary>
    /// Specifies the variant or category of the error for more specific error handling.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("variant")]
    public string? Variant { get; set; } = null;

    /// <summary>
    /// Error event type.
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("type")]
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public WebSocketType? Type { get; set; } = WebSocketType.Error;
Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (1)

9-14: ⚠️ Potential issue

Fix interface documentation.

The XML documentation appears to be copied from a class implementation:

  1. It incorrectly states "Implements version 1" when this is a v2 interface
  2. Contains constructor parameter documentation which isn't applicable to an interface

Apply this diff to fix the documentation:

 /// <summary>
-/// Implements version 1 of the Speak Client.
+/// Interface for version 2 of the Speak REST Client.
 /// </summary>
-/// <param name="apiKey">Required DeepgramApiKey</param>
-/// <param name="deepgramClientOptions"><see cref="DeepgramHttpClientOptions"/> for HttpClient Configuration</param>
📝 Committable suggestion

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

/// <summary>
/// Interface for version 2 of the Speak REST Client.
/// </summary>
public interface ISpeakRESTClient
Deepgram/Models/Speak/v2/WebSocket/SpeakSchema.cs (1)

31-39: 🛠️ Refactor suggestion

Use enum instead of string for Encoding property.

Since there's a fixed set of supported encodings (referenced as AudioEncoding), consider using an enum instead of string to ensure type safety and prevent invalid values.

Convert to enum-based property:

+    public enum AudioEncoding
+    {
+        [JsonPropertyName("linear16")]
+        Linear16,
+        [JsonPropertyName("mulaw")]
+        MuLaw,
+        // Add other supported encodings
+    }

     /// <summary>
     /// Encoding allows you to specify the expected encoding of your submitted audio.
     /// <see href="https://developers.deepgram.com/docs/encoding">
-    /// supported encodings <see cref="AudioEncoding"/>
+    /// Supported encodings are defined in <see cref="AudioEncoding"/>
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("encoding")]
-    public string? Encoding { get; set; }
+    public AudioEncoding? Encoding { get; set; }

Committable suggestion was skipped due to low confidence.

Deepgram/Models/Listen/v2/WebSocket/Word.cs (3)

30-35: ⚠️ Potential issue

Add validation for the Confidence property.

The confidence value should be between 0 and 1, but there's no validation to ensure this constraint.

Consider adding a validation method or using a custom JsonConverter:

private double? confidence;

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("confidence")]
public double? Confidence
{
    get => confidence;
    init
    {
        if (value.HasValue && (value.Value < 0 || value.Value > 1))
        {
            throw new ArgumentOutOfRangeException(nameof(value), "Confidence must be between 0 and 1");
        }
        confidence = value;
    }
}

7-8: 🛠️ Refactor suggestion

Consider making properties immutable for the record type.

The record type is typically used for immutable data. Consider changing the properties to init-only setters.

-public record Word
+public sealed record Word

Committable suggestion was skipped due to low confidence.


58-64: 🛠️ Refactor suggestion

Optimize ToString implementation.

The current implementation has a few issues:

  1. Missing using statement for System.Text.RegularExpressions
  2. Regex.Unescape might be unnecessary and could affect performance
+using System.Text.RegularExpressions;

 public override string ToString()
 {
-    return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+    return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
 }

Committable suggestion was skipped due to low confidence.

Deepgram/Abstractions/v2/Utilities.cs (3)

39-45: ⚠️ Potential issue

Add stream validation and proper resource handling

The method needs additional validation and safety checks:

  1. Missing null check for the stream parameter
  2. No validation that the stream is readable and seekable
  3. Potential resource leak if StreamContent creation fails
 internal static HttpContent CreateStreamPayload(Stream body)
 {
+    ArgumentNullException.ThrowIfNull(body);
+    if (!body.CanRead || !body.CanSeek)
+    {
+        throw new ArgumentException("Stream must be readable and seekable", nameof(body));
+    }
+
     body.Seek(0, SeekOrigin.Begin);
-    HttpContent httpContent = new StreamContent(body);
-    httpContent.Headers.Add("Content-Length", body.Length.ToString());
-    return httpContent;
+    try
+    {
+        var httpContent = new StreamContent(body);
+        httpContent.Headers.Add("Content-Length", body.Length.ToString());
+        return httpContent;
+    }
+    catch
+    {
+        body.Dispose();
+        throw;
+    }
 }
📝 Committable suggestion

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

    internal static HttpContent CreateStreamPayload(Stream body)
    {
        ArgumentNullException.ThrowIfNull(body);
        if (!body.CanRead || !body.CanSeek)
        {
            throw new ArgumentException("Stream must be readable and seekable", nameof(body));
        }

        body.Seek(0, SeekOrigin.Begin);
        try
        {
            var httpContent = new StreamContent(body);
            httpContent.Headers.Add("Content-Length", body.Length.ToString());
            return httpContent;
        }
        catch
        {
            body.Dispose();
            throw;
        }
    }

20-31: ⚠️ Potential issue

Add parameter validation and error handling

The method needs some improvements:

  1. The documentation mentions a contentType parameter that doesn't exist
  2. Missing null check for the body parameter
  3. No exception handling for serialization failures
 /// <summary>
 /// Create the body payload of a HttpRequestMessage
 /// </summary>
 /// <typeparam name="T">Type of the body to be sent</typeparam>
 /// <param name="body">instance value for the body</param>
-/// <param name="contentType">What type of content is being sent default is : application/json</param>
 /// <returns></returns>
 internal static StringContent CreatePayload<T>(T body)
 {
+    ArgumentNullException.ThrowIfNull(body);
+    try
+    {
         var serialized = JsonSerializer.Serialize(body, _jsonSerializerOptions);
         return new(serialized, Encoding.UTF8, DEFAULT_CONTENT_TYPE);
+    }
+    catch (JsonException ex)
+    {
+        throw new ArgumentException("Failed to serialize the body", nameof(body), ex);
+    }
 }
📝 Committable suggestion

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

    /// <summary>
    /// Create the body payload of a HttpRequestMessage
    /// </summary>
    /// <typeparam name="T">Type of the body to be sent</typeparam>
    /// <param name="body">instance value for the body</param>
    /// <returns></returns>
    internal static StringContent CreatePayload<T>(T body)
    {
        ArgumentNullException.ThrowIfNull(body);
        try
        {
            var serialized = JsonSerializer.Serialize(body, _jsonSerializerOptions);
            return new(serialized, Encoding.UTF8, DEFAULT_CONTENT_TYPE);
        }
        catch (JsonException ex)
        {
            throw new ArgumentException("Failed to serialize the body", nameof(body), ex);
        }
    }

54-59: ⚠️ Potential issue

Improve error handling and implement promised null checks

The method needs several improvements:

  1. Missing null check for httpResponseMessage parameter
  2. No error handling for deserialization failures
  3. No validation of response content
  4. Documentation promises null checks but none are implemented
 internal static async Task<TResponse> DeserializeAsync<TResponse>(HttpResponseMessage httpResponseMessage)
 {
+    ArgumentNullException.ThrowIfNull(httpResponseMessage);
+
     var content = await httpResponseMessage.Content.ReadAsStringAsync();
-    var deepgramResponse = JsonSerializer.Deserialize<TResponse>(content);
-    return deepgramResponse;
+    if (string.IsNullOrEmpty(content))
+    {
+        throw new InvalidOperationException("Response content is empty");
+    }
+
+    try
+    {
+        var deepgramResponse = JsonSerializer.Deserialize<TResponse>(content, _jsonSerializerOptions);
+        return deepgramResponse ?? throw new InvalidOperationException("Deserialized response is null");
+    }
+    catch (JsonException ex)
+    {
+        throw new InvalidOperationException("Failed to deserialize response content", ex);
+    }
 }
📝 Committable suggestion

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

    internal static async Task<TResponse> DeserializeAsync<TResponse>(HttpResponseMessage httpResponseMessage)
    {
        ArgumentNullException.ThrowIfNull(httpResponseMessage);

        var content = await httpResponseMessage.Content.ReadAsStringAsync();
        if (string.IsNullOrEmpty(content))
        {
            throw new InvalidOperationException("Response content is empty");
        }

        try
        {
            var deepgramResponse = JsonSerializer.Deserialize<TResponse>(content, _jsonSerializerOptions);
            return deepgramResponse ?? throw new InvalidOperationException("Deserialized response is null");
        }
        catch (JsonException ex)
        {
            throw new InvalidOperationException("Failed to deserialize response content", ex);
        }
    }
Deepgram/Clients/Interfaces/v2/ISelfHostedClient.cs (3)

21-22: ⚠️ Potential issue

Replace CancellationTokenSource with CancellationToken

Using CancellationTokenSource in method parameters is not a common practice. Methods should accept CancellationToken instead, letting the caller manage the source.

Apply this change to all method signatures. Example for ListCredentials:

-public Task<CredentialsResponse> ListCredentials(string projectId, CancellationTokenSource? cancellationToken = default,
+public Task<CredentialsResponse> ListCredentials(string projectId, CancellationToken cancellationToken = default,
     Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null);

Also applies to: 30-31, 39-40, 48-49


46-46: ⚠️ Potential issue

Update parameter documentation to match naming

The XML documentation refers to createOnPremCredentialsSchema but the parameter is named credentialsSchema.

-/// <param name="createOnPremCredentialsSchema"><see cref="CredentialsSchema"/> for credentials to be created</param>
+/// <param name="credentialsSchema"><see cref="CredentialsSchema"/> for credentials to be created</param>
📝 Committable suggestion

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

    /// <param name="credentialsSchema"><see cref="CredentialsSchema"/> for credentials to be created</param>

9-11: ⚠️ Potential issue

Update XML documentation to match v2 versioning

The documentation mentions "version 1 of the OnPrem Client" but this interface is in the v2 namespace.

-/// Implements version 1 of the OnPrem Client.
+/// Implements version 2 of the Self-Hosted Client.
📝 Committable suggestion

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

/// <summary>
/// Implements version 2 of the Self-Hosted Client.
/// </summary>
Deepgram/Clients/Interfaces/v2/ILiveClient.cs (1)

22-72: 🛠️ Refactor suggestion

Consider alternative design to method overloading.

The current design uses method overloading which might be confusing for consumers and makes it harder to unsubscribe from specific events. Consider a more explicit approach.

Alternative design:

public interface ILiveClient : IListenWebSocketClient
{
    public Task<bool> SubscribeToOpen(EventHandler<OpenResponse> eventHandler);
    public Task<bool> SubscribeToMetadata(EventHandler<MetadataResponse> eventHandler);
    public Task<bool> SubscribeToResults(EventHandler<ResultResponse> eventHandler);
    // ... similar methods for other events
}

This approach:

  1. Makes the API more explicit and discoverable
  2. Allows for easier unsubscription implementation
  3. Reduces potential confusion about which overload to use
Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (2)

73-77: ⚠️ Potential issue

Address TODO and improve error handling implementation.

Several concerns with the current error handling approach:

  1. The TODO comment needs to be resolved before merging
  2. Using raw Exception type could lead to serialization issues
  3. Missing JSON serialization attributes unlike other properties

Consider implementing a dedicated error model:

-    // TODO: DYV is this needed???
-    /// <summary>
-    /// Error information.
-    /// </summary>
-    public Exception? Error { get; set; }
+    /// <summary>
+    /// Contains error information if the WebSocket response indicates a failure.
+    /// </summary>
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    [JsonPropertyName("error")]
+    public WebSocketError? Error { get; set; }

And create a new error model:

public record WebSocketError
{
    [JsonPropertyName("code")]
    public string Code { get; init; } = string.Empty;

    [JsonPropertyName("message")]
    public string Message { get; init; } = string.Empty;
}

82-85: ⚠️ Potential issue

Remove unnecessary Regex.Unescape call in ToString.

The Regex.Unescape call is unnecessary and could potentially cause issues since JsonSerializer already handles proper string escaping.

-        return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+        return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
📝 Committable suggestion

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

    public override string ToString()
    {
        return JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions);
    }
Deepgram/Models/Listen/v2/WebSocket/MetadataResponse.cs (3)

82-88: ⚠️ Potential issue

Add error handling to ToString implementation.

The current implementation might throw exceptions during serialization. Consider adding error handling to make it more robust.

public override string ToString()
{
-    return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
+    try
+    {
+        return Regex.Unescape(JsonSerializer.Serialize(this, 
+            JsonSerializeOptions.DefaultOptions ?? JsonSerializerOptions.Default));
+    }
+    catch (Exception ex)
+    {
+        return $"Error serializing {GetType().Name}: {ex.Message}";
+    }
}
📝 Committable suggestion

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

    /// <summary>
    /// Override ToString method to serialize the object
    /// </summary>
    public override string ToString()
    {
        try
        {
            return Regex.Unescape(JsonSerializer.Serialize(this, 
                JsonSerializeOptions.DefaultOptions ?? JsonSerializerOptions.Default));
        }
        catch (Exception ex)
        {
            return $"Error serializing {GetType().Name}: {ex.Message}";
        }
    }

73-80: ⚠️ Potential issue

Consider adding validation for Extra metadata size limit.

The documentation mentions a 2048-character limit per key-value pair in Extra metadata, but this constraint isn't enforced in the code. Consider adding validation to ensure compliance with this limit.

Here's a suggested implementation:

public Dictionary<string, string>? Extra
{
    get => _extra;
    set
    {
        if (value != null)
        {
            foreach (var kvp in value)
            {
                if (kvp.Key.Length + kvp.Value.Length > 2048)
                {
                    throw new ArgumentException($"Extra metadata key-value pair '{kvp.Key}' exceeds 2048 character limit");
                }
            }
        }
        _extra = value;
    }
}
private Dictionary<string, string>? _extra;

Committable suggestion was skipped due to low confidence.


73-80: 🛠️ Refactor suggestion

Consider using IReadOnlyDictionary for Extra property.

For consistency with the ModelInfo property and to prevent unintended modifications, consider using IReadOnlyDictionary for the Extra property as well.

-public Dictionary<string, string>? Extra { get; set; }
+public IReadOnlyDictionary<string, string>? Extra { get; set; }
📝 Committable suggestion

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

    /// <summary>
    /// Deepgram's Extra Metadata feature allows you to attach arbitrary key-value pairs to your API requests that are attached to the API response for usage in downstream processing.
    /// Extra metadata is limited to 2048 characters per key-value pair.
    /// <see href="https://developers.deepgram.com/docs/extra-metadata"/>
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("extra")]
    public IReadOnlyDictionary<string, string>? Extra { get; set; }
Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (2)

87-106: ⚠️ Potential issue

Clean up commented code and clarify method purposes.

There are several commented-out method declarations that should either be removed or implemented. Additionally, the distinction between Send, SendMessage, and the commented-out SendBinary methods is unclear.

  1. Remove or implement the commented-out methods.
  2. Consider renaming methods to better reflect their purposes:
-public void Send(byte[] data, int length = Constants.UseArrayLengthForSend);
+public void SendBinaryData(byte[] data, int length = Constants.UseArrayLengthForSend);

-public void SendMessage(byte[] data, int length = Constants.UseArrayLengthForSend);
+public void SendTextData(byte[] data, int length = Constants.UseArrayLengthForSend);

Also applies to: 119-133


9-11: ⚠️ Potential issue

Fix incorrect interface documentation.

The XML documentation incorrectly states "Implements version 1 of the Live Client" but this is a v2 interface for the Speak WebSocket client.

Update the documentation to:

/// <summary>
-/// Implements version 1 of the Live Client.
+/// Interface for version 2 of the Speak WebSocket client.
/// </summary>
📝 Committable suggestion

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

/// <summary>
/// Interface for version 2 of the Speak WebSocket client.
/// </summary>
Deepgram/Models/Listen/v2/WebSocket/LiveSchema.cs (3)

70-77: 🛠️ Refactor suggestion

Consider using enum for Encoding property.

The Encoding property references AudioEncoding in the documentation but accepts any string. Consider using an enum to constrain valid values.

public enum AudioEncoding
{
    [JsonPropertyName("linear16")]
    Linear16,
    [JsonPropertyName("mulaw")]
    MuLaw,
    // Add other supported encodings
}

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("encoding")]
public AudioEncoding? Encoding { get; set; }

53-59: ⚠️ Potential issue

Fix malformed XML documentation comment.

The XML documentation comment is missing an opening <summary> tag.

-    // <summary>
+    /// <summary>
📝 Committable suggestion

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

    /// <summary>
    /// <see href="https://developers.deepgram.com/docs/diarization">
    /// default is null, only applies if Diarize is set to true
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("diarize_version")]
    public string? DiarizeVersion { get; set; }

231-237: ⚠️ Potential issue

Property name mismatch with documentation.

The JsonPropertyName "utterance_end_ms" doesn't match the property name "UtteranceEnd". This could cause confusion. Consider renaming the property to match the API parameter name.

-    public string? UtteranceEnd { get; set; }
+    public string? UtteranceEndMs { get; set; }
📝 Committable suggestion

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

    /// <summary>
    /// Indicates how long Deepgram will wait to send a {"type": "UtteranceEnd"} message after a word has been transcribed
    /// <see href="https://developers.deepgram.com/docs/understanding-end-of-speech-detection-while-streaming"/>
    /// </summary>
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
	[JsonPropertyName("utterance_end_ms")]
    public string? UtteranceEndMs { get; set; }
Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (2)

9-14: ⚠️ Potential issue

Inconsistent interface documentation and incorrect XML comments.

The XML documentation for the interface IListenRESTClient is inconsistent and contains errors:

  • The summary states "Implements version 1 of the Analyze Client," which does not match the interface name IListenRESTClient or the version v2.
  • <param> tags are used, but interfaces do not have parameters. These tags are inappropriate in this context.

21-23: ⚠️ Potential issue

Fix improperly formatted XML documentation <see> tags.

The <see> tags in the XML documentation comments are not properly closed. They should be self-closing tags ending with a /. For example:

  • Line 21: <see cref="UrlSource"> should be <see cref="UrlSource"/>.
  • Line 53: <see cref="PreRecordedSchema"> should be <see cref="PreRecordedSchema"/>.

This issue occurs in multiple places:

  • Lines 21, 31, 39, 53, 62, 74

Please review and correct these to ensure proper XML documentation.

Also applies to: 31-33, 39-42, 53-55, 62-66, 74-77

Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs (5)

15-16: 🛠️ Refactor suggestion

Use CancellationToken instead of CancellationTokenSource in method signatures

It's recommended to accept CancellationToken parameters rather than CancellationTokenSource in asynchronous methods. This allows callers to control cancellation without exposing the token source.

Apply this diff to update the method signatures:

-public Task Connect(LiveSchema options, CancellationTokenSource? cancelToken = null, Dictionary<string, string>? addons = null,
+public Task Connect(LiveSchema options, CancellationToken? cancellationToken = null, Dictionary<string, string>? addons = null,
-public Task Stop(CancellationTokenSource? cancelToken = null, bool nullByte = false);
+public Task Stop(CancellationToken? cancellationToken = null, bool nullByte = false);

Ensure to update all references to cancelToken within the implementation to use cancellationToken.

Also applies to: 18-18


92-93: ⚠️ Potential issue

Include missing parameter descriptions in XML documentation

The methods have a length parameter that is not documented in the XML comments. Please add <param> tags for the length parameter with appropriate descriptions.

Apply this diff to add parameter descriptions:

 /// <param name="data">The data to be sent over the WebSocket.</param>
+/// <param name="length">The number of bytes from the data to send. Use `Constants.UseArrayLengthForSend` to send the entire array.</param>

Repeat this change for the following methods:

  • Send
  • SendBinary
  • SendMessage
  • SendBinaryImmediately
  • SendMessageImmediately

Also applies to: 98-99, 104-105, 108-109, 112-114


2-2: ⚠️ Potential issue

Typographical correction in license text

The phrase "governed by a MIT license" should be "governed by an MIT license" for correct grammar.

Apply this diff to fix the typographical error:

-// 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.
📝 Committable suggestion

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

// Use of this source code is governed by an MIT license that can be found in the LICENSE file.

10-10: ⚠️ Potential issue

Update summary to reflect version 2

The summary states "Implements version 1 of the Live Client," but the interface is in the v2 namespace. Update the comment to accurately reflect version 2.

Apply this diff to correct the summary:

-/// Implements version 1 of the Live Client.
+/// Implements version 2 of the Live Client.
📝 Committable suggestion

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

/// Implements version 2 of the Live Client.

121-122: 🛠️ Refactor suggestion

Consider defining State as a property instead of a method

Since State represents the current state of the WebSocket and does not perform an action, it may be more appropriate to implement it as a property.

Apply this diff to change State to a property:

-public WebSocketState State();
+public WebSocketState State { get; }
📝 Committable suggestion

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

    /// <returns>Returns the connection state of the WebSocket</returns>
    public WebSocketState State { get; }
Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (5)

15-16: ⚠️ Potential issue

Fix self-closing tags in XML documentation

The <see cref="..."> tags in the XML documentation comments should be self-closed with /> to conform to XML documentation standards and prevent documentation build issues.

Apply this diff to correct the tags:

-/// <param name="source">Url to the file that is to be analyzed <see cref="UrlSource"></param>
+/// <param name="source">Url to the file that is to be analyzed <see cref="UrlSource"/></param>

-/// <param name="analyzeSchema">Options for the transcription <see cref="AnalyzeSchema"></param>
+/// <param name="analyzeSchema">Options for the transcription <see cref="AnalyzeSchema"/></param>

Also applies to: 24-25, 33-34, 42-43, 54-55, 65-66, 75-76, 85-87


18-18: 🛠️ Refactor suggestion

Consider accepting 'CancellationToken' instead of 'CancellationTokenSource'

Methods typically accept a CancellationToken parameter rather than a CancellationTokenSource. This approach allows callers to pass existing cancellation tokens and aligns with common .NET practices.

Apply this diff to update the parameters:

-public Task<SyncResponse> AnalyzeUrl(UrlSource source, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default,
+public Task<SyncResponse> AnalyzeUrl(UrlSource source, AnalyzeSchema? analyzeSchema, CancellationToken cancellationToken = default,

-public Task<SyncResponse> AnalyzeText(TextSource source, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default,
+public Task<SyncResponse> AnalyzeText(TextSource source, AnalyzeSchema? analyzeSchema, CancellationToken cancellationToken = default,

// Apply similar changes to other method signatures

Also applies to: 27-27, 35-35, 44-44, 56-56, 67-67, 78-78, 89-89


56-58: 🛠️ Refactor suggestion

Use consistent naming for 'Callback' in method names and parameters

The term 'Callback' should be consistently spelled as a single word in method names and parameter names. Update 'CallBack' to 'Callback' to follow standard C# naming conventions.

Apply this diff to rename the methods and parameters:

-public Task<AsyncResponse> AnalyzeFileCallBack(byte[] source, string? callBack, AnalyzeSchema? analyzeSchema,
+public Task<AsyncResponse> AnalyzeFileCallback(byte[] source, string? callback, AnalyzeSchema? analyzeSchema,

-public Task<AsyncResponse> AnalyzeFileCallBack(Stream source, string? callBack, AnalyzeSchema? analyzeSchema,
+public Task<AsyncResponse> AnalyzeFileCallback(Stream source, string? callback, AnalyzeSchema? analyzeSchema,

-public Task<AsyncResponse> AnalyzeUrlCallBack(UrlSource source, string? callBack, AnalyzeSchema? analyzeSchema,
+public Task<AsyncResponse> AnalyzeUrlCallback(UrlSource source, string? callback, AnalyzeSchema? analyzeSchema,

-public Task<AsyncResponse> AnalyzeTextCallBack(TextSource source, string? callBack, AnalyzeSchema? analyzeSchema,
+public Task<AsyncResponse> AnalyzeTextCallback(TextSource source, string? callback, AnalyzeSchema? analyzeSchema,

// Also update parameter names:
-/// <param name="callBack">CallBack url</param>
+/// <param name="callback">Callback URL</param>

Also applies to: 67-69, 78-80, 89-89


85-85: ⚠️ Potential issue

Correct reference in XML documentation

In the <see cref="..."/> tag, change UrlSource to TextSource to accurately reflect the parameter type.

Apply this diff:

-/// <param name="source">Text that is to be analyzed <see cref="UrlSource"/></param>
+/// <param name="source">Text that is to be analyzed <see cref="TextSource"/></param>
📝 Committable suggestion

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

    /// <param name="source">Text that is to be analyzed <see cref="TextSource"/></param>

82-82: ⚠️ Potential issue

Correct XML documentation comment syntax

The comment should use triple slashes to denote XML documentation. Change // <summary> to /// <summary>.

Apply this diff:

-// <summary>
+/// <summary>
📝 Committable suggestion

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

    /// <summary>
Deepgram/ClientFactory.cs (9)

69-81: 🛠️ Refactor suggestion

Consider returning a common interface instead of object

Returning object diminishes type safety. If possible, define a common interface that both ListenV1.Client and ListenV2.Client implement, and use it as the return type.


69-81: ⚠️ Potential issue

Unused parameter httpId in CreateListenWebSocketClient

The parameter httpId is not used within the method and should be removed to avoid confusion.

Apply this diff to remove the unused parameter:

-public static object CreateListenWebSocketClient(int version, string apiKey = "", DeepgramWsClientOptions? options = null, string? httpId = null)
+public static object CreateListenWebSocketClient(int version, string apiKey = "", DeepgramWsClientOptions? options = null)
📝 Committable suggestion

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

    public static object CreateListenWebSocketClient(int version, string apiKey = "", DeepgramWsClientOptions? options = null)
    {
        // at some point this needs to be changed to use reflection to get the type of the client
        switch(version)
        {
            case 1:
                return new ListenV1.Client(apiKey, options);
            case 2:
                return new ListenV2.Client(apiKey, options);
            default:
                throw new ArgumentException("Invalid version", nameof(version));
        }
    }

66-68: ⚠️ Potential issue

Incorrect method summary in CreateListenWebSocketClient

The XML documentation incorrectly states that this method creates an AnalyzeClient. It should reference ListenWebSocketClient.

Apply this diff to correct the summary:

-/// This method allows you to create an AnalyzeClient with a specific version of the client.
+/// This method allows you to create a ListenWebSocketClient with a specific version of the client.
📝 Committable suggestion

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

    /// <summary>
    /// This method allows you to create a ListenWebSocketClient with a specific version of the client.
    /// </summary>

229-241: ⚠️ Potential issue

Inconsistent parameter type in CreateSpeakWebSocketClient

The method CreateSpeakWebSocketClient accepts DeepgramHttpClientOptions? options, but it should accept DeepgramWsClientOptions? options to match the WebSocket client context.

Apply this diff:

-public static object CreateSpeakWebSocketClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
+public static object CreateSpeakWebSocketClient(int version, string apiKey = "", DeepgramWsClientOptions? options = null)

And update the method implementation accordingly.

Committable suggestion was skipped due to low confidence.


135-140: ⚠️ Potential issue

Return type should be more specific in CreateSelfHostedClient

Returning object diminishes type safety. Return V1.ISelfHostedClient instead to maintain consistency and avoid unnecessary casting.

Apply this diff:

-public static object CreateSelfHostedClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
+public static V1.ISelfHostedClient CreateSelfHostedClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
📝 Committable suggestion

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

    public static V1.ISelfHostedClient CreateSelfHostedClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
    {
        // Currently only a single version of the SelfHostedClient exists
        return new SelfHostedClient(apiKey, options, httpId);
    }


209-212: ⚠️ Potential issue

Return type should be more specific in CreateSpeakRESTClient

Returning object diminishes type safety. Return V1.ISpeakRESTClient instead to improve code quality.

Apply this diff:

-public static object CreateSpeakRESTClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
+public static V1.ISpeakRESTClient CreateSpeakRESTClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
📝 Committable suggestion

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

    public static V1.ISpeakRESTClient CreateSpeakRESTClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
    {
        // Currently only a single version of the SpeakRESTClient exists
        return new SpeakRESTClient(apiKey, options, httpId);

30-37: ⚠️ Potential issue

Return type should be more specific in CreateAnalyzeClient

Returning object reduces type safety and requires users to perform type casting. Since only one version exists, consider returning V1.IAnalyzeClient directly.

Apply this diff to specify the return type:

-public static object CreateAnalyzeClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
+public static V1.IAnalyzeClient CreateAnalyzeClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
📝 Committable suggestion

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

    /// <summary>
    /// This method allows you to create an AnalyzeClient with a specific version of the client.
    /// </summary>
    public static V1.IAnalyzeClient CreateAnalyzeClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
    {
        // Currently only a single version of the AnalyzeClient exists
        return new AnalyzeClient(apiKey, options, httpId);
    }

172-175: ⚠️ Potential issue

Return type should be more specific in CreateListenRESTClient

Returning object reduces type safety. Consider returning V1.IListenRESTClient directly.

Apply this diff:

-public static object CreateListenRESTClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
+public static V1.IListenRESTClient CreateListenRESTClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
📝 Committable suggestion

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

    public static V1.IListenRESTClient CreateListenRESTClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
    {
        // Currently only a single version of the ListenRESTClient exists
        return new ListenRESTClient(apiKey, options, httpId);

98-103: ⚠️ Potential issue

Return type should be more specific in CreateManageClient

Returning object reduces type safety. Consider returning V1.IManageClient directly to enhance code robustness.

Apply this diff:

-public static object CreateManageClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
+public static V1.IManageClient CreateManageClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
📝 Committable suggestion

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

    public static V1.IManageClient CreateManageClient(int version, string apiKey = "", DeepgramHttpClientOptions? options = null, string? httpId = null)
    {
        // Currently only a single version of the ManageClient exists
        return new ManageClient(apiKey, options, httpId);
    }

Deepgram/Clients/Interfaces/v2/IManageClient.cs (7)

5-5: 🛠️ Refactor suggestion

Consider updating models to match interface version

The interface IManageClient is in the v2 namespace but references models from Deepgram.Models.Manage.v1. Ensure that these models are compatible with the new interface, and consider updating or creating v2 models if necessary.


9-13: ⚠️ Potential issue

Remove incorrect XML documentation tags and fix summary

Interfaces do not have parameters, so the <param> tags on lines 12-13 are not applicable and should be removed. Additionally, the summary on line 10 incorrectly states "Implements version 1 of the Manage Client." Update it to reflect that this is version 2.


21-22: ⚠️ Potential issue

Remove 'public' access modifiers from interface methods

In C#, methods declared in interfaces cannot have access modifiers; they are implicitly public. Remove the public keyword from the method declarations on the specified lines.

Apply this diff to fix the issue:

-public Task<ProjectsResponse> GetProjects(...)
+Task<ProjectsResponse> GetProjects(...)

Also applies to: 29-30, 39-40, 47-48, 55-56, 63-64, 73-74, 81-82, 89-90, 99-100, 108-109, 117-118, 126-127, 136-137, 145-146, 154-155, 164-165, 173-174, 183-185, 193-194, 204-205, 213-214, 223-224, 231-232, 241-242, 250-251


21-22: 🛠️ Refactor suggestion

Consider encapsulating optional parameters into a request options object

The method GetProjects and other methods accept multiple optional parameters: CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null. Consider creating a RequestOptions class to encapsulate these parameters, which would simplify method signatures and improve maintainability.

Example implementation:

public class RequestOptions
{
    public CancellationTokenSource? CancellationToken { get; set; } = default;
    public Dictionary<string, string>? Addons { get; set; } = null;
    public Dictionary<string, string>? Headers { get; set; } = null;
}

// Updated method signature:
Task<ProjectsResponse> GetProjects(RequestOptions? options = null);

87-88: ⚠️ Potential issue

Incorrect parameter documentation in XML comment

On line 87, the <param> tag refers to projectId with the description "Id of Model", which is incorrect. The method GetModel does not have a projectId parameter but has a modelId parameter. Update the parameter name and description to match the method signature.


115-116: ⚠️ Potential issue

Parameter name mismatch in XML comment

The <param> tag on line 115 names the parameter createProjectKeySchema, but the method signature uses keySchema. Update the parameter name in the XML comment to keySchema to match the method signature.


181-182: ⚠️ Potential issue

Parameter name mismatch in XML comment

The <param> tag on line 181 refers to memberScopeSchema, but the method signature uses scopeSchema. Update the parameter name in the XML comment to scopeSchema to match the method signature.

Deepgram/Clients/Listen/v2/WebSocket/Client.cs (3)

101-260: 🛠️ Refactor suggestion

Consider reducing code duplication in Subscribe methods

The Subscribe methods for different event types contain similar patterns where event handlers are wrapped and passed to the base Subscribe method or added to local events. While previous learnings indicate that these methods cannot be fully abstracted due to concrete type requirements, you might still consider using generics or helper functions to reduce duplication and improve maintainability.

For example, you could introduce a generic method to handle the common wrapping logic:

private async Task<bool> Subscribe<TCommon, TSpecific>(EventHandler<TSpecific> eventHandler)
    where TSpecific : TCommon, new()
{
    EventHandler<TCommon> wrappedHandler = (sender, args) =>
    {
        var castedArgs = new TSpecific();
        castedArgs.Copy(args);
        eventHandler(sender, castedArgs);
    };

    return await base.Subscribe(wrappedHandler);
}

Then, your specific Subscribe methods can utilize this helper:

-public async Task<bool> Subscribe(EventHandler<OpenResponse> eventHandler)
-{
-    // Create a new event handler that wraps the original one
-    EventHandler<Common.OpenResponse> wrappedHandler = (sender, args) =>
-    {
-        var castedArgs = new OpenResponse();
-        castedArgs.Copy(args);
-        eventHandler(sender, castedArgs);
-    };
-
-    // Pass the new event handler to the base Subscribe method
-    return await base.Subscribe(wrappedHandler);
-}
+public async Task<bool> Subscribe(EventHandler<OpenResponse> eventHandler)
+{
+    return await Subscribe<Common.OpenResponse, OpenResponse>(eventHandler);
+}

599-599: ⚠️ Potential issue

Possible null reference exception when casting resultResponse.IsFinal

At line 599, resultResponse.IsFinal is cast to a non-nullable bool without checking if it is null. Since IsFinal is a nullable boolean, this could lead to a NullReferenceException if it is null. It's safer to check for true explicitly.

Apply this diff to fix the potential null reference:

-if ((bool)resultResponse.IsFinal)
+if (resultResponse.IsFinal == true)
📝 Committable suggestion

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

                        if (resultResponse.IsFinal == true)
🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 599-599:
Nullable value type may be null.

🪛 GitHub Check: test (7.0.x)

[warning] 599-599:
Nullable value type may be null.

🪛 GitHub Check: test (6.0.x)

[warning] 599-599:
Nullable value type may be null.

🪛 GitHub Check: build

[warning] 599-599:
Nullable value type may be null.


90-93: ⚠️ Potential issue

Handle exceptions from background tasks to prevent unobserved exceptions

In the Connect method, the background tasks ProcessKeepAlive and ProcessAutoFlush are started using Task.Run without being awaited or stored. If these tasks throw exceptions, they could lead to unobserved exceptions, potentially causing the application to crash. Consider capturing the tasks and handling any exceptions to improve robustness.

Refactor the methods to capture the tasks and handle exceptions, for example:

-void StartKeepAliveBackgroundThread() => Task.Run(() => ProcessKeepAlive());
+void StartKeepAliveBackgroundThread()
+{
+    _ = Task.Run(async () => await ProcessKeepAlive().ConfigureAwait(false))
+        .ContinueWith(t =>
+        {
+            if (t.Exception != null)
+            {
+                Log.Error("ProcessKeepAlive", $"Unhandled exception: {t.Exception}");
+            }
+        }, TaskContinuationOptions.OnlyOnFaulted);
+}

Similarly, update StartAutoFlushBackgroundThread() to handle exceptions.

Committable suggestion was skipped due to low confidence.

Deepgram/Clients/Speak/v2/WebSocket/Client.cs (5)

655-667: ⚠️ Potential issue

Avoid using async void for non-event handler methods.

The method DecrementCounter is declared as async void. Similar to InspectMessage, it's advisable to change the return type to Task for better error handling.

Apply this diff to change the method signature:

-private async void DecrementCounter()
+private async Task DecrementCounter()
 {
     await _mutexLastDatagram.WaitAsync();
     try
     {
         _flushCount -= 1;
         Log.Debug("ProcessDataReceived", $"Decrement Flush count: {_flushCount}");
     }
     finally
     {
         _mutexLastDatagram.Release();
     }
 }

Ensure that any calls to DecrementCounter are awaited appropriately.

📝 Committable suggestion

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

    private async Task DecrementCounter()
    {
        await _mutexLastDatagram.WaitAsync();
        try
        {
            _flushCount -= 1;
            Log.Debug("ProcessDataReceived", $"Decrement Flush count: {_flushCount}");
        }
        finally
        {
            _mutexLastDatagram.Release();
        }
    }

389-454: ⚠️ Potential issue

Avoid using async void for non-event handler methods.

The method ProcessAutoFlush is declared as async void. To improve exception handling and maintainability, consider changing it to return a Task.

Apply this diff to adjust the method:

-internal async void ProcessAutoFlush()
+internal async Task ProcessAutoFlush()
 {
     Log.Verbose("LiveClient.ProcessAutoFlush", "ENTER");
     // ...
 }

-void StartAutoFlushBackgroundThread() => Task.Run(() => ProcessAutoFlush());
+void StartAutoFlushBackgroundThread() => Task.Run(ProcessAutoFlush);

This change allows callers to await the method and handle any exceptions it may produce.

Committable suggestion was skipped due to low confidence.


363-367: ⚠️ Potential issue

Incorrect use of lock with SemaphoreSlim _mutexLastDatagram.

The lock statement is used on _mutexLastDatagram, which is a SemaphoreSlim. This is not recommended because SemaphoreSlim is not intended to be used with the lock keyword. To ensure proper synchronization, you should use a dedicated lock object or utilize the semaphore's asynchronous methods.

Apply this diff to use a dedicated lock object:

+        private readonly object _flushCountLock = new object();

 public override void SendMessage(byte[] data, int length = Constants.UseArrayLengthForSend)
 {
     // auto flush
     if (_deepgramClientOptions.InspectSpeakMessage())
     {
         string type = GetMessageType(data);
         Log.Debug("SendMessage", $"Inspecting Message: Sending {type}");
         switch (type)
         {
             case Constants.Flush:
-                lock (_mutexLastDatagram)
+                lock (_flushCountLock)
                 {
                     _flushCount += 1;
                     Log.Debug("SendMessage", $"Increment Flush count: {_flushCount}");
                 }
                 break;

Committable suggestion was skipped due to low confidence.


669-685: ⚠️ Potential issue

Add exception handling in GetMessageType to prevent unhandled exceptions.

The method GetMessageType parses a JSON document without handling potential exceptions. If the input is malformed or missing the expected properties, this could lead to unhandled exceptions. Implementing exception handling will improve the robustness of the code.

Apply this diff to include exception handling:

 internal string GetMessageType(byte[] msg)
 {
     // Convert the byte array to a string
     string response = Encoding.UTF8.GetString(msg);
     if (response == null)
     {
         return "";
     }

     Log.Verbose("ProcessDataReceived", $"raw response: {response}");
-    var data = JsonDocument.Parse(response);
+    JsonDocument data;
+    try
+    {
+        data = JsonDocument.Parse(response);
+    }
+    catch (JsonException ex)
+    {
+        Log.Error("GetMessageType", $"Failed to parse JSON response: {ex.Message}");
+        return "";
+    }

-    string val = data.RootElement.GetProperty("type").GetString() ?? "";
+    string val;
+    if (data.RootElement.TryGetProperty("type", out JsonElement typeElement))
+    {
+        val = typeElement.GetString() ?? "";
+    }
+    else
+    {
+        Log.Warning("GetMessageType", "Response does not contain 'type' property");
+        val = "";
+    }

     Log.Debug("ProcessDataReceived", $"Type: {val}");

     return val;
 }

This ensures that any parsing errors are logged, and the method returns gracefully without throwing exceptions.

📝 Committable suggestion

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

    internal string GetMessageType(byte[] msg)
    {
        // Convert the byte array to a string
        string response = Encoding.UTF8.GetString(msg);
        if (response == null)
        {
            return "";
        }

        Log.Verbose("ProcessDataReceived", $"raw response: {response}");
        JsonDocument data;
        try
        {
            data = JsonDocument.Parse(response);
        }
        catch (JsonException ex)
        {
            Log.Error("GetMessageType", $"Failed to parse JSON response: {ex.Message}");
            return "";
        }

        string val;
        if (data.RootElement.TryGetProperty("type", out JsonElement typeElement))
        {
            val = typeElement.GetString() ?? "";
        }
        else
        {
            Log.Warning("GetMessageType", "Response does not contain 'type' property");
            val = "";
        }

        Log.Debug("ProcessDataReceived", $"Type: {val}");

        return val;
    }

632-653: ⚠️ Potential issue

Avoid using async void for non-event handler methods.

The method InspectMessage is declared as async void. It's recommended to use async Task for methods to allow proper error handling and awaitability. Changing the return type to Task improves code reliability.

Apply this diff to change the method signature:

-private async void InspectMessage()
+private async Task InspectMessage()
 {
     Log.Verbose("InspectMessage", "ENTER");
     // ...
 }

If changing to async Task is not feasible due to method call hierarchy, consider ensuring that exceptions within the method are properly handled to prevent unobserved exceptions.

📝 Committable suggestion

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

    private async Task InspectMessage()
    {
        Log.Verbose("InspectMessage", "ENTER");

        if (_deepgramClientOptions.AutoFlushSpeakDelta > 0)
        {
            var now = DateTime.Now;
            Log.Debug("InspectMessage", $"AutoFlush last received. Time: {now}");
            await _mutexLastDatagram.WaitAsync();
            try
            {
                _lastReceived = now;
            }
            finally
            {
                _mutexLastDatagram.Release();
            }
        }

        Log.Debug("InspectMessage", "Succeeded");
        Log.Verbose("InspectMessage", "LEAVE");
    }
Deepgram/Abstractions/v2/AbstractWebSocketClient.cs (5)

86-91: ⚠️ Potential issue

Avoid logging sensitive header information

The current implementation logs header keys and values unless they contain specific substrings like "password" or "token". This approach might unintentionally log sensitive information. Consider masking header values or logging only non-sensitive headers to prevent potential exposure.

To address this, you can modify the logging to exclude header values or use a whitelist approach for headers that are safe to log.

Also applies to: 98-103


579-584: ⚠️ Potential issue

Prevent multiple disposals of resources

Both Stop and Dispose methods handle disposing of _clientWebSocket and _cancellationTokenSource. Calling Dispose multiple times or after Stop might lead to exceptions due to disposing already disposed objects. Implementing a _disposed flag can help prevent this issue.

Apply this diff to implement the _disposed flag:

+ private bool _disposed = false;

 public void Dispose()
 {
+     if (_disposed)
+     {
+         return;
+     }
+
     if (_clientWebSocket == null)
     {
         return;
     }

     // Existing disposal logic...

+     _disposed = true;
     GC.SuppressFinalize(this);
 }

Also applies to: 715-742


441-445: ⚠️ Potential issue

Handle exceptions properly in background tasks

In ProcessReceiveQueue and ProcessSendQueue, exceptions are caught and logged, but the tasks may continue running in an inconsistent state. Consider rethrowing critical exceptions or implementing additional logic to safely terminate the tasks to avoid silent failures.

Also applies to: 373-384


93-105: 🛠️ Refactor suggestion

Refactor header addition logic to avoid duplication

The code for adding headers from _deepgramClientOptions.Headers and headers is duplicated. Refactor this logic into a separate method to enhance maintainability and reduce code repetition.


494-495: ⚠️ Potential issue

Safely parse WebSocketType to prevent exceptions

Using Enum.Parse without validation may throw an exception if the type property is missing or invalid in the JSON response. Replace it with Enum.TryParse to handle parsing more safely.

Here's a suggested change:

- var val = Enum.Parse(typeof(WebSocketType), data.RootElement.GetProperty("type").GetString()!);
+ if (!Enum.TryParse<WebSocketType>(data.RootElement.GetProperty("type").GetString(), out var val))
+ {
+     Log.Warning("ProcessTextMessage", "Received unknown or invalid WebSocket type.");
+     return;
+ }
📝 Committable suggestion

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

            if (!Enum.TryParse<WebSocketType>(data.RootElement.GetProperty("type").GetString(), out var val))
            {
                Log.Warning("ProcessTextMessage", "Received unknown or invalid WebSocket type.");
                return;
            }
Deepgram/Abstractions/v1/AbstractRestClient.cs (5)

86-86: ⚠️ Potential issue

Use 'await' instead of '.Result' to prevent blocking calls

Using .Result on asynchronous methods within an async method can cause deadlocks or performance issues by blocking the thread. Replace .Result with await to properly handle the asynchronous operation.

Apply the following changes:

- var resultStr = response.Content.ReadAsStringAsync().Result;
+ var resultStr = await response.Content.ReadAsStringAsync();

This change should be applied to each occurrence in the specified lines.

Also applies to: 149-149, 346-346, 422-422, 502-502, 580-580, 648-648, 718-718


84-84: ⚠️ Potential issue

Dispose of 'HttpResponseMessage' to prevent resource leaks

The HttpResponseMessage should be disposed properly to free unmanaged resources and avoid potential memory leaks. Wrap the response handling in a using statement.

Refactor the code as follows:

- var response = await _httpClient.SendAsync(request, cancellationToken.Token);
- var resultStr = await response.Content.ReadAsStringAsync();
- if (!response.IsSuccessStatusCode)
- {
-     await ThrowException("MethodName", response, resultStr);
- }
- // Other code...

+ using (var response = await _httpClient.SendAsync(request, cancellationToken.Token))
+ {
+     var resultStr = await response.Content.ReadAsStringAsync();
+     if (!response.IsSuccessStatusCode)
+     {
+         await ThrowException("MethodName", response, resultStr);
+     }
+     // Other code...
+ }

Replace "MethodName" with the actual method name in each case.

Also applies to: 147-147, 224-224, 343-343, 420-420, 500-500, 578-578, 646-646, 716-716


566-572: ⚠️ Potential issue

Avoid logging sensitive header information

Logging header keys that may contain sensitive information like passwords or tokens can lead to security issues. Ensure that such headers are not logged.

[security]

Modify the logging condition to prevent logging sensitive headers:

if (!(tmp.Contains("password") || tmp.Contains("token") || tmp.Contains("authorization") || tmp.Contains("auth")))
{
    Log.Debug("PutAsync<S, T>", $"Add Header {header.Key}={header.Value}");
}
+ else
+ {
+     Log.Debug("PutAsync<S, T>", $"Sensitive header '{header.Key}' not logged.");
+ }
request.Headers.Add(header.Key, header.Value);
📝 Committable suggestion

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

                {
                    var tmp = header.Key.ToLower();
                    if (!(tmp.Contains("password") || tmp.Contains("token") || tmp.Contains("authorization") || tmp.Contains("auth")))
                    {
                        Log.Debug("PutAsync<S, T>", $"Add Header {header.Key}={header.Value}");
                    }
                    else
                    {
                        Log.Debug("PutAsync<S, T>", $"Sensitive header '{header.Key}' not logged.");
                    }
                    request.Headers.Add(header.Key, header.Value);

235-264: 🛠️ Refactor suggestion

Simplify header processing using a 'foreach' loop

Using a for loop with ElementAt(i) on an enumerable can lead to decreased performance and less readable code. Replace it with a foreach loop for better efficiency and clarity.

Refactor the code as follows:

- for (var i = 0; i < response.Headers.Count(); i++)
- {
-     var key = response.Headers.ElementAt(i).Key.ToLower();
-     var value = response.Headers.GetValues(key).FirstOrDefault() ?? "";
-     // Remaining code...
- }

+ foreach (var header in response.Headers)
+ {
+     var key = header.Key.ToLower();
+     var value = header.Value.FirstOrDefault() ?? "";
+     // Remaining code...
+ }

Committable suggestion was skipped due to low confidence.


748-775: ⚠️ Potential issue

Handle possible null 'errMsg' to prevent null reference exceptions

The errMsg parameter might be null, which can lead to a NullReferenceException when creating a new DeepgramException. Ensure that errMsg is not null before passing it to the exception constructor.

Modify the code to provide a default message if errMsg is null or empty:

- throw new DeepgramException(errMsg);
+ var message = string.IsNullOrEmpty(errMsg) ? "An unknown error occurred." : errMsg;
+ throw new DeepgramException(message);
📝 Committable suggestion

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

    private static async Task ThrowException(string module, HttpResponseMessage response, string errMsg)
    {
        if (errMsg == null || errMsg.Length == 0)
        {
            Log.Verbose(module, $"HTTP/REST Exception thrown");
            response.EnsureSuccessStatusCode(); // this throws the exception
        }

        Log.Verbose(module, $"Deepgram Exception: {errMsg}");
        DeepgramRESTException? resException = null;
        try
        {
            resException = await HttpRequestUtil.DeserializeAsync<DeepgramRESTException>(response);
        }
        catch (Exception ex)
        {
            Log.Verbose(module, $"DeserializeAsync Error Exception: {ex}");
        }

        if (resException != null)
        {
            Log.Verbose(module, "DeepgramRESTException thrown");
            throw resException;
        }

        Log.Verbose(module, $"Deepgram Generic Exception thrown");
        var message = string.IsNullOrEmpty(errMsg) ? "An unknown error occurred." : errMsg;
        throw new DeepgramException(message);
    }
🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: test (7.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: test (6.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: build

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs (3)

86-86: ⚠️ Potential issue

Avoid synchronous calls to asynchronous methods

In the methods GetAsync<T>, GetAsync<S, T>, PostAsync<S, T>, PostAsync<R, S, T>, PatchAsync<S, T>, PutAsync<S, T>, DeleteAsync<T>, and DeleteAsync<S, T>, the use of ReadAsStringAsync().Result on lines 86, 149, 345, 422, 502, 580, 648, and 718 blocks the asynchronous flow and can lead to deadlocks. It's recommended to await the asynchronous call to read the response content without blocking the thread.

Apply this diff to fix the issue:

- var resultStr = response.Content.ReadAsStringAsync().Result;
+ var resultStr = await response.Content.ReadAsStringAsync();

Also applies to: 149-149, 345-345, 422-422, 502-502, 580-580, 648-648, 718-718


75-80: ⚠️ Potential issue

Ensure consistent handling of sensitive headers when logging

In several methods, headers are added to requests and logged for debugging purposes (e.g., lines 75-80 in GetAsync<T>). However, sensitive information such as authorization tokens or passwords might be included in headers. While the PutAsync<S, T> method (lines 565-574) includes a check to avoid logging sensitive headers, this precaution is missing in other methods. For consistency and security, implement similar checks in all methods where headers are logged.

Apply this pattern to filter sensitive headers before logging:

foreach (var header in headers)
{
+   var headerKeyLower = header.Key.ToLower();
+   if (!(headerKeyLower.Contains("password") || headerKeyLower.Contains("token") || headerKeyLower.Contains("authorization") || headerKeyLower.Contains("auth")))
+   {
        Log.Debug("MethodName", $"Add Header {header.Key}={header.Value}");
+   }
    request.Headers.Add(header.Key, header.Value);
}

Replace "MethodName" with the appropriate method name in each case.

Also applies to: 138-143, 215-220, 334-339, 411-416, 491-496, 637-642, 707-712


774-774: ⚠️ Potential issue

Handle potential null reference for 'errMsg' in ThrowException method

At line 774, the errMsg parameter could be null, which may cause an ArgumentNullException when creating a new DeepgramException. Ensure that errMsg is not null before throwing the exception. If errMsg is null or empty, provide a default error message.

Apply this diff to handle null errMsg:

  Log.Verbose(module, $"Deepgram Generic Exception thrown");
- throw new DeepgramException(errMsg);
+ throw new DeepgramException(errMsg ?? "An unknown error occurred.");
📝 Committable suggestion

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

        throw new DeepgramException(errMsg ?? "An unknown error occurred.");
🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: test (7.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: test (6.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: build

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

@dvonthenen
Copy link
Contributor Author

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

🛑 Comments failed to post (107)

I will review this Monday and go through the comments one at a time.

@dvonthenen dvonthenen force-pushed the auto-flush-deadlock-plus-refactor branch from f63d33f to e70e0cb Compare October 28, 2024 17:12
@dvonthenen
Copy link
Contributor Author

dvonthenen commented Oct 28, 2024

Integrated CodeRabbit feedback into PR. A lot if fluff or want to minimize changes and some of it is good.

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: 65

🧹 Outside diff range and nitpick comments (42)
Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs (2)

7-7: Consider renaming the constructor parameter for clarity.

The constructor parameter text is used to initialize the Type property, but the naming suggests it contains text content rather than a control message type. Consider renaming it to type or messageType for better clarity.

-public class ControlMessage(string text)
+public class ControlMessage(string messageType)

Line range hint 10-14: Enhance documentation and add validation for control message types.

The current implementation could benefit from:

  1. Documentation listing the valid control message types
  2. Input validation to ensure only valid message types are accepted

Consider adding validation and enhancing the documentation:

     /// <summary>
     /// Gets or sets the type of control message.
+    /// Valid types include:
+    /// - "start" - Initiates the message processing
+    /// - "stop" - Terminates the message processing
     /// </summary>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("type")]
-    public string? Type { get; set; } = text;
+    public string? Type
+    {
+        get => _type;
+        set
+        {
+            if (value != null && !IsValidMessageType(value))
+            {
+                throw new ArgumentException("Invalid control message type", nameof(value));
+            }
+            _type = value;
+        }
+    }
+
+    private string? _type = messageType;
+
+    private static bool IsValidMessageType(string type)
+    {
+        return type is "start" or "stop";
+    }
Deepgram/Models/Speak/v1/WebSocket/AudioResponse.cs (1)

Line range hint 22-23: Consider adding XML documentation for the Dispose method.

While the implementation is correct, adding XML documentation for the Dispose method would maintain consistency with the rest of the codebase's documentation style.

+    /// <summary>
+    /// Releases the resources used by the AudioResponse.
+    /// </summary>
     public void Dispose()
Deepgram/Models/Listen/v2/WebSocket/Average.cs (1)

7-30: Enhance XML documentation for better IntelliSense support.

While basic documentation exists, it could be more comprehensive for better developer experience.

Consider adding more detailed XML documentation:

 /// <summary>
+/// Represents the average sentiment analysis results for the audio content.
+/// </summary>
+/// <remarks>
+/// This record encapsulates both the sentiment classification and its corresponding score,
+/// providing insights into the emotional tone of the analyzed content.
+/// </remarks>
 public record Average
 {
     /// <summary>
-    /// Sentiment: Positive, Negative, or Neutral.
+    /// Gets or sets the sentiment classification of the content.
     /// </summary>
+    /// <value>One of the following values: "Positive", "Negative", or "Neutral".</value>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("sentiment")]
     public string? Sentiment { get; set; }

     /// <summary>
-    /// Sentiment score.
+    /// Gets or sets the numerical score indicating the strength of the sentiment.
     /// </summary>
+    /// <value>A value typically ranging from -1.0 (most negative) to 1.0 (most positive).</value>
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     [JsonPropertyName("sentiment_score")]
     public double? SentimentScore { get; set; }
Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (2)

10-33: Add thread synchronization for concurrent access

Given this class is part of the WebSocket client implementation and the PR addresses threading issues, consider making the class thread-safe.

Consider these improvements:

  1. Add readonly to the dictionary field to prevent concurrent dictionary replacement
  2. Use ConcurrentDictionary if metadata needs to be modified concurrently
  3. Add thread synchronization for Content access
  4. Add checks for disposed state in all public members

Example:

+    private readonly object _lock = new object();
+    private readonly ConcurrentDictionary<string, string> _metadata;
+    private volatile bool _disposed;

     public Dictionary<string, string> Metadata
     {
-        get => _metadata;
+        get => new Dictionary<string, string>(_metadata);
     }

     public MemoryStream Content
     {
         get
         {
+            ThrowIfDisposed();
+            lock (_lock)
             {
                 return _content;
             }
         }
         set
         {
+            ThrowIfDisposed();
+            lock (_lock)
             {
                 // ... existing set logic ...
             }
         }
     }

+    private void ThrowIfDisposed()
+    {
+        if (_disposed)
+            throw new ObjectDisposedException(nameof(LocalFileWithMetadata));
+    }

7-9: Enhance XML documentation for better API understanding

The current documentation could be more comprehensive to help API consumers understand the class behavior better.

Add documentation for:

  • Thread safety guarantees
  • Exception conditions
  • Constructor behavior
  • Disposal requirements

Example additions:

 /// <summary>
 /// LocalFileWithMetadata is a class that represents a file with metadata.
 /// </summary>
+/// <remarks>
+/// This class is thread-safe and can be safely accessed from multiple threads.
+/// Instances of this class should be disposed of properly to release resources.
+/// </remarks>
 public class LocalFileWithMetadata : IDisposable
 {
+    /// <summary>
+    /// Initializes a new instance of the <see cref="LocalFileWithMetadata"/> class.
+    /// </summary>
+    /// <remarks>
+    /// Creates an empty metadata dictionary and initializes an empty content stream.
+    /// </remarks>

     /// <summary>
     /// Gets or sets the metadata associated with the file content.
     /// </summary>
+    /// <exception cref="ObjectDisposedException">
+    /// Thrown when the instance has been disposed.
+    /// </exception>

     /// <summary>
     /// Gets or sets the file content as a MemoryStream.
     /// </summary>
+    /// <exception cref="ArgumentNullException">
+    /// Thrown when attempting to set a null value.
+    /// </exception>
+    /// <exception cref="ObjectDisposedException">
+    /// Thrown when the instance has been disposed.
+    /// </exception>

Also applies to: 12-15, 17-24

Deepgram/Models/Listen/v2/WebSocket/Metadata.cs (2)

16-21: Enhance ModelUUID documentation.

The current documentation is minimal. Consider adding more context about what this UUID represents and when it's populated.

     /// <summary>
-    /// Model UUID
+    /// A unique identifier for the specific model version used in processing.
+    /// This can be useful for tracking and reproducing results with exact model versions.
     /// </summary>

23-28: Add cross-reference to ModelInfo type.

The documentation references ModelInfo but lacks a proper cross-reference link.

     /// <summary>
-    /// IReadonlyDictionary of <see cref="ModelInfo"/>
+    /// Information about the model used for processing.
+    /// See <see cref="ModelInfo"/> for detailed model properties.
     /// </summary>
Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (2)

7-37: Enhance XML documentation for better IntelliSense support.

While the basic documentation is present, it could be more descriptive to provide better context for API consumers. Consider adding:

  • Property constraints or expected formats
  • Examples of typical values
  • Impact of null values on serialization

Example enhancement for the Description property:

-    /// <summary>
-    /// Error Description
-    /// </summary>
+    /// <summary>
+    /// Detailed description of the error that occurred during WebSocket communication.
+    /// When null, this field will be omitted from JSON serialization.
+    /// </summary>
+    /// <example>
+    /// "Invalid audio format provided"
+    /// </example>

57-60: Document potential exceptions in ToString method.

The ToString implementation could throw exceptions during serialization or unescaping. Consider:

     /// <summary>
     /// Override ToString method to serialize the object
     /// </summary>
+    /// <returns>A JSON string representation of the error response</returns>
+    /// <exception cref="JsonException">Thrown when serialization fails</exception>
     public override string ToString()
     {
         return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
     }
Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (3)

14-26: Improve region naming and method documentation.

  1. The region name "NoneCallBacks" doesn't follow C# naming conventions and isn't descriptive. Consider "SynchronousMethods" instead.
  2. The ToFile method is missing XML documentation.

Consider these changes:

-    #region NoneCallBacks
+    #region Synchronous Methods
     /// <summary>
     /// Speaks a file using the provided stream
     /// </summary>
     /// <param name="source">file is the form of a stream <see cref="Stream"/></param>
     /// <param name="speakSchema">Options for the transcription <see cref="SpeakSchema"/></param>
     /// <returns><see cref="SyncResponse"/></returns>
     public Task<SyncResponse> ToStream(TextSource source...);

+    /// <summary>
+    /// Speaks a file and saves the audio to the specified file
+    /// </summary>
+    /// <param name="source">Text to convert to speech</param>
+    /// <param name="filename">Path where the audio file will be saved</param>
+    /// <param name="speakSchema">Options for the transcription <see cref="SpeakSchema"/></param>
+    /// <returns><see cref="SyncResponse"/></returns>
     public Task<SyncResponse> ToFile(TextSource source...);

28-38: Fix XML documentation and region formatting.

  1. The XML documentation for speakSchema parameter has a missing closing tag
  2. The region name has inconsistent spacing (two spaces)

Apply these fixes:

-    #region  CallBack Methods
+    #region Callback Methods
     /// <summary>
     /// Speaks a file using the provided byte array and providing a CallBack
     /// </summary>
     /// <param name="source">file is the form of a byte[]</param>  
     /// <param name="callBack">CallBack url</param>    
-    /// <param name="speakSchema">Options for the transcription<see cref="SpeakSchema"></param>
+    /// <param name="speakSchema">Options for the transcription <see cref="SpeakSchema"/></param>
     /// <returns><see cref="AsyncResponse"/></returns>
     public Task<AsyncResponse> StreamCallBack(TextSource source...);

1-39: Well-structured interface design with proper async patterns.

The interface design aligns well with the PR objectives of improving the threading model:

  • Clear separation between synchronous and asynchronous operations
  • Consistent parameter patterns across methods
  • Proper use of Task-based async pattern
  • Flexible options through optional parameters

The structure will support the intended v2 improvements for handling deadlocks mentioned in the PR objectives.

Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (2)

7-8: Add XML documentation for the record type.

Please add XML documentation to describe this record's purpose and its relationship to v1, as this is part of the v2 breaking changes mentioned in the PR objectives.

+/// <summary>
+/// Represents the response structure for WebSocket communication in v2 clients.
+/// This is part of the v2 implementation that addresses threading and deadlock issues.
+/// </summary>
 public record ResultResponse

12-14: Fix inconsistent indentation in JSON attributes.

The JSON attributes use inconsistent indentation (mix of tabs and spaces). Please standardize the indentation for better maintainability.

     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
-	[JsonPropertyName("channel")]
+    [JsonPropertyName("channel")]

Also applies to: 19-21, 26-28, 33-35, 40-42, 47-49, 54-56, 61-63, 68-71

Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (2)

18-20: Fix XML documentation inconsistencies.

The parameter documentation has inconsistent usage of <see> tags. Some tags are not properly closed, and some parameters use plain text instead of <see>.

Example fix for the AnalyzeUrl method:

- <param name="source">Url to the file that is to be analyzed <see cref="UrlSource"></param>
+ <param name="source">Url to the file that is to be analyzed <see cref="UrlSource"/></param>

Also applies to: 27-29, 35-37, 44-46


12-94: Consider enhancing interface design and documentation.

Suggestions for improvement:

  1. Add input validation attributes (e.g., [NotNull]) for required parameters
  2. Document the differences between sync and callback methods in the interface summary
  3. Add remarks about the relationship between v1 and v2 interfaces, explaining the breaking changes

Would you like me to provide examples of these enhancements?

Deepgram/ClientFactory.cs (1)

Line range hint 1-245: Consider documenting thread safety guarantees.

Given that this PR addresses deadlock issues, it would be valuable to:

  1. Document the thread safety guarantees of both v1 and v2 clients
  2. Add XML documentation comments explaining the differences in threading models
  3. Consider adding runtime checks or diagnostics to help users identify potential threading issues

Add XML documentation to the factory methods explaining the threading model differences:

/// <summary>
/// Creates a new ListenWebSocketClient using the latest version (v2).
/// </summary>
/// <remarks>
/// V2 clients use an asynchronous programming model that prevents deadlocks
/// on systems with low CPU counts. This is the recommended version for all
/// new implementations.
/// </remarks>
🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (7.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (6.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: build

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

Deepgram/Abstractions/v2/AbstractRestClient.cs (2)

15-15: Add missing internal keyword for field visibility.

The field declaration is missing the explicit internal keyword, though it's marked in the comment.

-    internal HttpClient _httpClient;
+    internal readonly HttpClient _httpClient;

41-41: Fix typo in debug logging.

The debug log statement has a typo in the variable name.

-        Log.Debug("AbstractRestClient", $"options: {options.OnPrem}");
+        Log.Debug("AbstractRestClient", $"OnPrem: {options.OnPrem}");
Deepgram/Clients/Speak/v1/WebSocket/Client.cs (6)

14-23: Enhance deprecation notice with migration guidance.

While the deprecation notice is clear, consider enhancing it with specific migration instructions and a link to the migration guide to help users transition to v2 smoothly.

 // *********** WARNING ***********
 // Implements version 1 of the Speak WebSocket Client
 //
 // Deprecated: This class is deprecated. Use the `v2` of the client instead.
+// For migration instructions, see: <link-to-migration-guide>
+// Key differences in v2:
+// - Improved threading model to prevent deadlocks
+// - Better async/await support
+// - Enhanced error handling
 // This will be removed in a future release.
 //
 // This class is frozen and no new functionality will be added.
 // *********** WARNING ***********

Line range hint 28-31: Consider consolidating synchronization primitives.

Multiple SemaphoreSlim instances increase complexity and potential for deadlocks. Consider using a single AsyncLock or SemaphoreSlim with async/await pattern.

-    private readonly SemaphoreSlim _mutexSubscribe = new SemaphoreSlim(1, 1);
-    private readonly SemaphoreSlim _mutexSend = new SemaphoreSlim(1, 1);
-    private readonly SemaphoreSlim _mutexLastDatagram = new SemaphoreSlim(1, 1);
+    private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1, 1);

Line range hint 392-394: Replace Task.Factory.StartNew with Task.Run for background operations.

Using Task.Factory.StartNew with LongRunning option can create too many OS threads. Consider using Task.Run which better integrates with the thread pool.

-        void StartSenderBackgroundThread() => _ = Task.Factory.StartNew(
-            _ => ProcessSendQueue(),
-                TaskCreationOptions.LongRunning);
+        void StartSenderBackgroundThread() => _ = Task.Run(ProcessSendQueue);

-        void StartReceiverBackgroundThread() => _ = Task.Factory.StartNew(
-                _ => ProcessReceiveQueue(),
-                TaskCreationOptions.LongRunning);
+        void StartReceiverBackgroundThread() => _ = Task.Run(ProcessReceiveQueue);

-        void StartAutoFlushBackgroundThread() => _ = Task.Factory.StartNew(
-                _ => ProcessAutoFlush(),
-                TaskCreationOptions.LongRunning);
+        void StartAutoFlushBackgroundThread() => _ = Task.Run(ProcessAutoFlush);

Also applies to: 397-399, 402-404


Line range hint 526-569: Improve resource cleanup in ProcessReceiveQueue.

The MemoryStream usage could be simplified with using declarations, and the buffer array could be reused across iterations.

-                using (var ms = new MemoryStream())
+                var buffer = new byte[Constants.BufferSize];
+                var segment = new ArraySegment<byte>(buffer);
+                await using var ms = new MemoryStream();
                 {
                     do
                     {
-                        result = await _clientWebSocket.ReceiveAsync(buffer, _cancellationTokenSource.Token);
+                        result = await _clientWebSocket.ReceiveAsync(segment, _cancellationTokenSource.Token);

Line range hint 405-432: Enhance error handling in ProcessSendQueue.

The catch blocks could be more specific and provide better error context. Consider adding specific exception types and including queue state information in logs.

-        catch (Exception ex)
+        catch (WebSocketException ex)
         {
-            Log.Error("ProcessSendQueue", $"{ex.GetType()} thrown {ex.Message}");
+            Log.Error("ProcessSendQueue", $"WebSocket error during send: {ex.Message}, WebSocketError: {ex.WebSocketErrorCode}");
             Log.Verbose("ProcessSendQueue", $"Excepton: {ex}");
             Log.Verbose("SpeakWSClient.ProcessSendQueue", "LEAVE");
+            throw;
+        }
+        catch (InvalidOperationException ex)
+        {
+            Log.Error("ProcessSendQueue", $"Invalid operation during send: {ex.Message}");
+            Log.Verbose("ProcessSendQueue", $"Exception: {ex}");
+            throw;
         }

Line range hint 571-700: Modernize message type handling with pattern matching.

The switch statement for message types could be simplified using C# pattern matching, making the code more maintainable.

-                switch (val)
-                {
-                    case SpeakType.Open:
-                        var openResponse = data.Deserialize<OpenResponse>();
+                var response = val switch
+                {
+                    SpeakType.Open => HandleResponse<OpenResponse>(data, _openReceived),
+                    SpeakType.Metadata => HandleResponse<MetadataResponse>(data, _metadataReceived),
+                    SpeakType.Flushed => HandleResponse<FlushedResponse>(data, _flushedReceived),
+                    // ... other cases
+                    _ => HandleUnhandledResponse(response)
+                };

+                if (response != null)
+                {
+                    await InvokeEventHandlerAsync(response);
+                }

Consider adding a helper method for response handling:

private T? HandleResponse<T>(JsonDocument data, EventHandler<T>? handler) where T : class
{
    if (handler == null)
    {
        Log.Debug("ProcessDataReceived", $"{typeof(T).Name} has no listeners");
        return null;
    }
    
    var response = data.Deserialize<T>();
    if (response == null)
    {
        Log.Warning("ProcessDataReceived", $"{typeof(T).Name} is invalid");
        return null;
    }
    
    return response;
}
Deepgram/Clients/Listen/v1/WebSocket/Client.cs (4)

Line range hint 27-31: Consider revising the threading model to prevent potential deadlocks.

The current implementation uses multiple SemaphoreSlim instances which could contribute to deadlock scenarios. Consider:

  1. Using a single synchronization object where possible
  2. Implementing async/await patterns throughout instead of explicit synchronization

This aligns with the PR's objective of addressing deadlock issues. Consider this pattern:

-    private readonly SemaphoreSlim _mutexSubscribe = new SemaphoreSlim(1, 1);
-    private readonly SemaphoreSlim _mutexSend = new SemaphoreSlim(1, 1);
-    private readonly SemaphoreSlim _mutexLastDatagram = new SemaphoreSlim(1, 1);
+    private readonly AsyncLock _asyncLock = new AsyncLock();

Line range hint 392-407: Modernize task creation pattern.

The current use of Task.Factory.StartNew with TaskCreationOptions.LongRunning for all background tasks is not optimal. Consider:

  1. Using Task.Run for standard background operations
  2. Only use TaskCreationOptions.LongRunning for truly long-running operations

Example refactor:

-        void StartSenderBackgroundThread() => _ = Task.Factory.StartNew(
-            _ => ProcessSendQueue(),
-                TaskCreationOptions.LongRunning);
+        void StartSenderBackgroundThread() => _ = Task.Run(
+            async () => await ProcessSendQueue(),
+            _cancellationTokenSource.Token);

Line range hint 1018-1047: Enhance resource cleanup implementation.

The current Dispose pattern implementation could be improved:

  1. Consider implementing the full dispose pattern with a protected virtual Dispose(bool)
  2. Use using statements for disposable resources
  3. Add null checks before disposal

Consider this implementation:

+    private bool _disposed;
+
     public void Dispose()
     {
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }
+
+    protected virtual void Dispose(bool disposing)
+    {
+        if (_disposed) return;
+        if (disposing)
+        {
             if (_cancellationTokenSource != null)
             {
                 if (!_cancellationTokenSource.Token.IsCancellationRequested)
                 {
                     _cancellationTokenSource.Cancel();
                 }
                 _cancellationTokenSource.Dispose();
                 _cancellationTokenSource = null;
             }
 
             if (_sendChannel != null)
             {
                 _sendChannel.Writer.Complete();
             }
 
             if (_clientWebSocket != null)
             {
                 _clientWebSocket.Dispose();
                 _clientWebSocket = null;
             }
         }
+        _disposed = true;
     }

Line range hint 947-971: Simplify event invocation pattern.

The current parallel event invocation pattern might be unnecessarily complex and could lead to unexpected behavior:

  1. Event handlers typically don't need parallel execution
  2. Order of execution might be important for subscribers

Consider a simpler implementation:

-    private void InvokeParallel<T>(EventHandler<T> eventHandler, T e)
-    {
-        if (eventHandler != null)
-        {
-            try
-            {
-                Parallel.ForEach(
-                    eventHandler.GetInvocationList().Cast<EventHandler<T>>(),
-                    (handler) =>
-                        handler(null, e));
-            }
-            catch (AggregateException ae)
-            {
-                Log.Error("InvokeParallel", $"AggregateException occurred in one or more event handlers: {ae}");
-            }
-            catch (Exception ex)
-            {
-                Log.Error("InvokeParallel", $"Exception occurred in event handler: {ex}");
-            }
-        }
-    }
+    private void InvokeEvent<T>(EventHandler<T> eventHandler, T e)
+    {
+        var handler = eventHandler;
+        if (handler != null)
+        {
+            try
+            {
+                handler(null, e);
+            }
+            catch (Exception ex)
+            {
+                Log.Error("InvokeEvent", $"Exception in event handler: {ex}");
+            }
+        }
+    }
Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (2)

37-37: Correct typo in XML documentation comment.

There's a typo in the <param> tag for source: "streasm" should be "stream".

Apply this diff to correct the typo:

-/// <param name="source">file is the form of a streasm <see cref="Stream"/></param>
+/// <param name="source">File in the form of a stream <see cref="Stream"/></param>

22-22: Remove redundant 'public' modifier in interface method declarations.

In C#, members of an interface are implicitly public. Including the 'public' modifier is unnecessary and can be omitted for cleaner code.

Apply this diff to remove the redundant 'public' modifier:

-public Task<SyncResponse> TranscribeUrl(UrlSource source, PreRecordedSchema? prerecordedSchema,
+Task<SyncResponse> TranscribeUrl(UrlSource source, PreRecordedSchema? prerecordedSchema,

Repeat this change for all method declarations within the interface.

Deepgram/Clients/Interfaces/v2/IManageClient.cs (4)

16-16: Improve clarity in the summary comment

Change "Gets projects associated to ApiKey" to "Gets projects associated with the API key" for better grammatical correctness.


24-24: Enhance consistency and clarity in the summary comment

Update the comment to "Gets the project associated with the project ID" and ensure "ID" is consistently capitalized throughout the comments.


142-142: Standardize parameter description capitalization

Capitalize "email" to "Email" for consistency with other parameter descriptions.

Apply this diff:

-/// <param name="email">email of the invite to be removed</param>
+/// <param name="email">Email of the invite to be removed</param>

180-180: Improve comment formatting and capitalization

Add a space before <see...> and capitalize the sentence: "Updates scope options for the member. <see cref="MemberScopeSchema"/>"

Apply this diff:

-/// <param name="memberScopeSchema">updates scope options for member<see cref="MemberScopeSchema"/></param>
+/// <param name="memberScopeSchema">Updates scope options for the member. <see cref="MemberScopeSchema"/></param>
Deepgram/Abstractions/v2/AbstractWebSocketClient.cs (2)

176-184: Simplify event handler subscription in Subscribe methods

In the Subscribe methods for event handlers (OpenResponse, CloseResponse, UnhandledResponse, ErrorResponse), you're adding handlers using a lambda expression that directly calls the provided eventHandler. This can be simplified by subscribing the eventHandler directly, improving readability.

Apply this diff to simplify the event subscription:

 try
 {
-    _openReceived += (sender, e) => eventHandler(sender, e);
+    _openReceived += eventHandler;
 }
 finally
 {
     _mutexSubscribe.Release();
 }

 // Similarly update other Subscribe methods:

 try
 {
-    _closeReceived += (sender, e) => eventHandler(sender, e);
+    _closeReceived += eventHandler;
 }
 // ...

 try
 {
-    _unhandledReceived += (sender, e) => eventHandler(sender, e);
+    _unhandledReceived += eventHandler;
 }
 // ...

 try
 {
-    _errorReceived += (sender, e) => eventHandler(sender, e);
+    _errorReceived += eventHandler;
 }
 // ...

Also applies to: 195-204, 213-222, 231-240


585-590: Ambiguous return value when client is already disposed in Stop method

In the Stop method, if _clientWebSocket is null (indicating the client has already been disposed), the method logs this information and returns true. This might confuse the caller into thinking that the stop operation was performed successfully, even though no action was taken.

Consider returning false to indicate that the client was already stopped, or adjust the logic to more accurately reflect the operation's outcome.

 if (_clientWebSocket == null)
 {
     Log.Information("Stop", "Client has already been disposed");
     Log.Verbose("AbstractWebSocketClient.Stop", "LEAVE");
-    return true;
+    return false;
 }
Deepgram/Abstractions/v1/AbstractRestClient.cs (2)

109-109: Typographical Errors in Log Messages: Correct 'Excepton' to 'Exception'

The log messages contain a typo where "Exception" is misspelled as "Excepton".

Please correct the typo in the following lines:

- Log.Verbose("GetAsync<T>", $"Excepton: {ex}");
+ Log.Verbose("GetAsync<T>", $"Exception: {ex}");

Apply this change to the following lines:

  • Line 109 in GetAsync<T>
  • Line 173 in GetAsync<S, T>
  • Line 295 in PostRetrieveLocalFileAsync<R, S, T>
  • Line 369 in PostAsync<S, T>
  • Line 447 in PostAsync<R, S, T>
  • Line 527 in PatchAsync<S, T>
  • Line 605 in PutAsync<S, T>
  • Line 672 in DeleteAsync<T>
  • Line 743 in DeleteAsync<S, T>

Also applies to: 173-173, 295-295, 369-369, 447-447, 527-527, 605-605, 672-672, 743-743


35-36: Unused Assignment of _httpClient

At line 35, _httpClient is assigned from HttpClientFactory.Create(httpId), and immediately reassigned at line 36 with HttpClientFactory.ConfigureDeepgram(_httpClient, options). However, ConfigureDeepgram might not require the previously created _httpClient.

Consider simplifying the assignment to:

- _httpClient = HttpClientFactory.Create(httpId);
- _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, options);
+ _httpClient = HttpClientFactory.ConfigureDeepgram(HttpClientFactory.Create(httpId), options);

Or adjust based on the methods' implementations to eliminate any unnecessary steps.

Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (2)

7-7: Consider renaming the namespace 'v2' to 'V2' to follow .NET naming conventions

According to .NET naming conventions, namespace names should use PascalCase. Changing v2 to V2 would improve consistency and readability.


147-151: Rename 'State' method to 'GetState' for clarity

The method State() could be renamed to GetState() to better indicate that it retrieves the current state of the WebSocket.

Apply this change:

-public WebSocketState State();
+public WebSocketState GetState();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f63d33f and e70e0cb.

📒 Files selected for processing (82)
  • Deepgram.Dev.sln (3 hunks)
  • Deepgram.Tests/Fakes/ConcreteRestClient.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs (0 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/UtilitiesTests/RequestContentUtilTests.cs (1 hunks)
  • Deepgram/Abstractions/LocalFileWithMetadata.cs (0 hunks)
  • Deepgram/Abstractions/v1/AbstractRestClient.cs (1 hunks)
  • Deepgram/Abstractions/v1/Constants.cs (1 hunks)
  • Deepgram/Abstractions/v1/LocalFileWithMetadata.cs (1 hunks)
  • Deepgram/Abstractions/v1/NoopSchema.cs (1 hunks)
  • Deepgram/Abstractions/v1/Utilities.cs (1 hunks)
  • Deepgram/Abstractions/v2/AbstractRestClient.cs (1 hunks)
  • Deepgram/Abstractions/v2/AbstractWebSocketClient.cs (1 hunks)
  • Deepgram/Abstractions/v2/Constants.cs (1 hunks)
  • Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (1 hunks)
  • Deepgram/Abstractions/v2/NoopSchema.cs (1 hunks)
  • Deepgram/Abstractions/v2/Utilities.cs (1 hunks)
  • Deepgram/Abstractions/v2/WebSocketMessage.cs (1 hunks)
  • Deepgram/ClientFactory.cs (10 hunks)
  • Deepgram/Clients/Analyze/v1/Client.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v1/ISpeakClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v1/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/Constants.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ILiveClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IManageClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IOnPremClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IPreRecordedClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISelfHostedClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISpeakClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Listen/v1/REST/Client.cs (1 hunks)
  • Deepgram/Clients/Listen/v1/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/Constants.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/UriSegments.cs (1 hunks)
  • Deepgram/Clients/Manage/v1/Client.cs (1 hunks)
  • Deepgram/Clients/SelfHosted/v1/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v1/REST/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v1/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/Constants.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/UriSegments.cs (1 hunks)
  • Deepgram/GlobalUsings.cs (0 hunks)
  • Deepgram/ListenWebSocketClient.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/CloseResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/OpenResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/UnhandledResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/WebSocketType.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Alternative.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Average.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Channel.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/CloseResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ErrorResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Hit.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ListenType.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/LiveSchema.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Metadata.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/MetadataResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ModelInfo.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/OpenResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Search.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/SpeechStartedResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/UnhandledResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/UtteranceEndResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Word.cs (1 hunks)
  • Deepgram/Models/Speak/v1/WebSocket/AudioResponse.cs (1 hunks)
  • Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs (1 hunks)
  • Deepgram/Models/Speak/v2/WebSocket/AudioResponse.cs (1 hunks)
⛔ Files not processed due to max files limit (24)
  • Deepgram/Models/Speak/v2/WebSocket/ClearedResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/CloseResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/ControlMessage.cs
  • Deepgram/Models/Speak/v2/WebSocket/ErrorResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/FlushedResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/MetadataResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/OpenResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/SpeakSchema.cs
  • Deepgram/Models/Speak/v2/WebSocket/SpeakType.cs
  • Deepgram/Models/Speak/v2/WebSocket/TextSource.cs
  • Deepgram/Models/Speak/v2/WebSocket/UnhandledResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/WarningResponse.cs
  • Deepgram/SpeakWebSocketClient.cs
  • examples/speech-to-text/websocket/file/Program.cs
  • examples/speech-to-text/websocket/http/Program.cs
  • examples/speech-to-text/websocket/microphone/Program.cs
  • examples/text-to-speech/websocket/simple/Program.cs
  • tests/edge_cases/keepalive/Program.cs
  • tests/edge_cases/reconnect_same_object/Program.cs
  • tests/edge_cases/stt_v1_client_example/Program.cs
  • tests/edge_cases/stt_v1_client_example/Streaming.csproj
  • tests/edge_cases/tts_v1_client_example/Program.cs
  • tests/edge_cases/tts_v1_client_example/Speak.csproj
  • tests/expected_failures/websocket/exercise_timeout/Program.cs
💤 Files with no reviewable changes (3)
  • Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs
  • Deepgram/Abstractions/LocalFileWithMetadata.cs
  • Deepgram/GlobalUsings.cs
✅ Files skipped from review due to trivial changes (2)
  • Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs
  • Deepgram/Clients/Interfaces/v2/ResponseEvent.cs
🚧 Files skipped from review as they are similar to previous changes (51)
  • Deepgram.Tests/Fakes/ConcreteRestClient.cs
  • Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs
  • Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs
  • Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs
  • Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs
  • Deepgram.Tests/UnitTests/UtilitiesTests/RequestContentUtilTests.cs
  • Deepgram/Abstractions/v1/Constants.cs
  • Deepgram/Abstractions/v1/NoopSchema.cs
  • Deepgram/Abstractions/v1/Utilities.cs
  • Deepgram/Abstractions/v2/Constants.cs
  • Deepgram/Abstractions/v2/NoopSchema.cs
  • Deepgram/Abstractions/v2/Utilities.cs
  • Deepgram/Abstractions/v2/WebSocketMessage.cs
  • Deepgram/Clients/Analyze/v1/Client.cs
  • Deepgram/Clients/Interfaces/v2/Constants.cs
  • Deepgram/Clients/Interfaces/v2/ILiveClient.cs
  • Deepgram/Clients/Interfaces/v2/IOnPremClient.cs
  • Deepgram/Clients/Interfaces/v2/IPreRecordedClient.cs
  • Deepgram/Clients/Interfaces/v2/ISelfHostedClient.cs
  • Deepgram/Clients/Interfaces/v2/ISpeakClient.cs
  • Deepgram/Clients/Listen/v1/REST/Client.cs
  • Deepgram/Clients/Listen/v2/WebSocket/Constants.cs
  • Deepgram/Clients/Listen/v2/WebSocket/ResponseEvent.cs
  • Deepgram/Clients/Listen/v2/WebSocket/UriSegments.cs
  • Deepgram/Clients/Manage/v1/Client.cs
  • Deepgram/Clients/SelfHosted/v1/Client.cs
  • Deepgram/Clients/Speak/v1/REST/Client.cs
  • Deepgram/Clients/Speak/v2/WebSocket/Constants.cs
  • Deepgram/Clients/Speak/v2/WebSocket/ResponseEvent.cs
  • Deepgram/Clients/Speak/v2/WebSocket/UriSegments.cs
  • Deepgram/ListenWebSocketClient.cs
  • Deepgram/Models/Common/v2/WebSocket/CloseResponse.cs
  • Deepgram/Models/Common/v2/WebSocket/OpenResponse.cs
  • Deepgram/Models/Common/v2/WebSocket/UnhandledResponse.cs
  • Deepgram/Models/Common/v2/WebSocket/WebSocketType.cs
  • Deepgram/Models/Listen/v2/WebSocket/Alternative.cs
  • Deepgram/Models/Listen/v2/WebSocket/Channel.cs
  • Deepgram/Models/Listen/v2/WebSocket/CloseResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/ErrorResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/Hit.cs
  • Deepgram/Models/Listen/v2/WebSocket/ListenType.cs
  • Deepgram/Models/Listen/v2/WebSocket/LiveSchema.cs
  • Deepgram/Models/Listen/v2/WebSocket/MetadataResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/ModelInfo.cs
  • Deepgram/Models/Listen/v2/WebSocket/OpenResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/Search.cs
  • Deepgram/Models/Listen/v2/WebSocket/SpeechStartedResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/UnhandledResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/UtteranceEndResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/Word.cs
  • Deepgram/Models/Speak/v2/WebSocket/AudioResponse.cs
🧰 Additional context used
📓 Learnings (4)
Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (2)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/v1/Client.cs:0-0
Timestamp: 2024-10-09T02:19:46.086Z
Learning: The event subscription methods in the Deepgram .NET SDK cannot be abstracted into a common method due to concrete type requirements for each event handler.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/v1/Client.cs:0-0
Timestamp: 2024-08-08T19:34:11.728Z
Learning: The event subscription methods in the Deepgram .NET SDK cannot be abstracted into a common method due to concrete type requirements for each event handler.
Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs (2)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs:7-10
Timestamp: 2024-09-18T17:03:19.489Z
Learning: In this codebase, using primary constructors in classes (e.g., `public class ClassName(parameters)`) is valid syntax. Do not flag this as a syntax error in future reviews.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs:7-10
Timestamp: 2024-10-09T02:19:46.087Z
Learning: In this codebase, using primary constructors in classes (e.g., `public class ClassName(parameters)`) is valid syntax. Do not flag this as a syntax error in future reviews.
Deepgram/Clients/Speak/v2/WebSocket/Client.cs (2)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/v1/Client.cs:0-0
Timestamp: 2024-10-09T02:19:46.086Z
Learning: The event subscription methods in the Deepgram .NET SDK cannot be abstracted into a common method due to concrete type requirements for each event handler.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/v1/Client.cs:0-0
Timestamp: 2024-08-08T19:34:11.728Z
Learning: The event subscription methods in the Deepgram .NET SDK cannot be abstracted into a common method due to concrete type requirements for each event handler.
Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs (3)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs:7-8
Timestamp: 2024-10-09T02:19:48.728Z
Learning: Primary constructor syntax in class declarations, such as `public class ClassName(Type parameter)`, is valid starting from C# 12.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs:7-8
Timestamp: 2024-10-09T02:19:46.086Z
Learning: Primary constructor syntax in class declarations, such as `public class ClassName(Type parameter)`, is valid starting from C# 12.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs:7-8
Timestamp: 2024-09-18T17:03:37.231Z
Learning: Primary constructor syntax in class declarations, such as `public class ClassName(Type parameter)`, is valid starting from C# 12.
🪛 GitHub Check: test (8.0.x)
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 609-609:
Nullable value type may be null.

🪛 GitHub Check: test (7.0.x)
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 609-609:
Nullable value type may be null.

🪛 GitHub Check: test (6.0.x)
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 609-609:
Nullable value type may be null.

🪛 GitHub Check: build
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 609-609:
Nullable value type may be null.

🔇 Additional comments (27)
Deepgram/Clients/Interfaces/v1/ResponseEvent.cs (2)

11-14: LGTM! Clean constructor implementation.

The constructor properly initializes the immutable Response property and handles nullable types correctly.


7-7: Verify the impact of interface to class conversion.

The change from IResponseEvent<T> to concrete ResponseEvent<T> with an immutable Response property is a good design choice that aligns with thread-safety goals. However, this is a breaking change that requires verification of existing implementations.

Also applies to: 9-9

✅ Verification successful

Let me analyze the search results further to verify the impact of interface changes.


Based on the previous search results, I can now generate a final response:

Interface to class conversion is properly implemented across the codebase

The conversion from IResponseEvent<T> to concrete ResponseEvent<T> has been consistently implemented across all relevant files in the codebase. The search results show:

  • No remaining references to the old IResponseEvent interface
  • Consistent implementation of ResponseEvent<T> in all v1 and v2 interface and client files
  • Proper inheritance hierarchy maintained (e.g., Live/v1/ResponseEvent.cs inheriting from WS.ResponseEvent<T>)
  • All implementations follow the same pattern with nullable Response property
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all implementations and usages of IResponseEvent to ensure they're updated
# Test 1: Find old interface implementations
rg "IResponseEvent" -l

# Test 2: Find places where ResponseEvent is used
rg "ResponseEvent<" -A 3

Length of output: 2012


Script:

#!/bin/bash
# Search for any remaining interface references or implementations
rg "interface.*IResponseEvent" -A 2

# Search for any event handler registrations or usages
rg "EventHandler.*ResponseEvent" -A 2

Length of output: 77

Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs (1)

7-14: LGTM! Improved thread safety through immutability.

The changes to ResponseEvent<T> enhance thread safety and encapsulation by:

  • Making the Response property read-only
  • Ensuring immutability after construction
  • Maintaining proper nullability constraints

This aligns well with the PR's goal of addressing threading issues in the WebSocket implementation.

Deepgram/Clients/Interfaces/v1/ISpeakClient.cs (2)

8-15: LGTM! Clear and well-structured deprecation notice.

The warning block effectively communicates the deprecation status and migration path to developers.


Line range hint 16-19: Verify the interface inheritance and usage.

The interface is properly marked as obsolete while maintaining backward compatibility through inheritance from ISpeakRESTClient. Let's verify the implementation across the codebase.

✅ Verification successful

Let me gather more information about the implementation and usage to ensure a complete verification.


Interface inheritance and usage verification completed

The verification confirms proper implementation of the deprecation strategy:

  • Both v1 and v2 versions of ISpeakClient are correctly marked as obsolete and inherit from their respective ISpeakRESTClient
  • The concrete implementation exists in SpeakClient class
  • Factory methods support both interfaces:
    • CreateSpeakClient returns ISpeakClient (deprecated)
    • CreateSpeakRESTClient returns ISpeakRESTClient (new)

The codebase maintains backward compatibility while properly guiding users to the new interface through deprecation notices.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the interface usage and inheritance structure

# Test 1: Find all implementations of ISpeakClient
echo "=== ISpeakClient implementations ==="
ast-grep --pattern 'class $_ : $$$ISpeakClient$$$'

# Test 2: Check for direct usage of ISpeakClient in method signatures or variable declarations
echo -e "\n=== ISpeakClient usage ==="
rg "ISpeakClient\s+\w+" --type csharp

# Test 3: Verify ISpeakRESTClient exists and its structure
echo -e "\n=== ISpeakRESTClient interface ==="
fd "ISpeakRESTClient.cs" --exec cat

Length of output: 4808


Script:

#!/bin/bash
# Find the actual implementation of ISpeakClient and ISpeakRESTClient
echo "=== Concrete implementations ==="
rg "class.*: .*ISpeakRESTClient" -A 5 --type csharp

# Check for any dependency injection or service registration
echo -e "\n=== DI Registration ==="
rg "AddScoped|AddTransient|AddSingleton.*ISpeakClient|ISpeakRESTClient" --type csharp

# Check for factory method implementation
echo -e "\n=== Factory Implementation ==="
rg "CreateSpeakClient" -A 10 --type csharp

Length of output: 2309

Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs (1)

Line range hint 19-22: LGTM: ToString implementation looks good.

The implementation correctly uses JsonSerializer with proper escaping.

Deepgram/Models/Speak/v1/WebSocket/AudioResponse.cs (2)

10-10: LGTM! Documentation improvement is clear and accurate.

The updated documentation for the Type property better describes its purpose and default value.


7-7: LGTM! Proper implementation of resource management.

The implementation of IDisposable and the Dispose method follows best practices for managing the MemoryStream resource:

  • Null-conditional operator prevents null reference exceptions
  • Stream disposal is handled correctly

Let's verify that this is the only place where MemoryStream is used in the WebSocket implementation:

Also applies to: 19-28

✅ Verification successful

MemoryStream usage requires additional resource management checks

The review comment's approval needs revision as there are multiple MemoryStream usages across WebSocket implementations that require verification:

  • AudioResponse.cs (v1 and v2) properly implements IDisposable for the Stream property
  • However, several WebSocket clients use MemoryStream in their processing methods:
    • AbstractWebSocketClient.cs uses MemoryStream in ProcessReceiveQueue
    • Client.cs in both Listen and Speak (v1/v2) implementations inherit and use MemoryStream
    • Some implementations use MemoryStream without clear disposal patterns

Let's verify the disposal patterns in these implementations:


MemoryStream resource management is properly implemented

After thorough verification, the MemoryStream usage across WebSocket implementations follows proper resource management patterns:

  1. AudioResponse (v1/v2) correctly implements IDisposable for the Stream property
  2. All WebSocket clients properly use using statements for MemoryStream instances in their processing methods:
    • AbstractWebSocketClient.cs uses using (var ms = new MemoryStream())
    • Both Listen and Speak clients (v1/v2) inherit from AbstractWebSocketClient or implement proper using blocks
    • All client classes implement IDisposable for cleanup of their resources

The original review comment's approval is correct, as proper resource management patterns are consistently applied across all WebSocket-related MemoryStream usages in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other MemoryStream usages in WebSocket implementations
rg -l "MemoryStream" | grep -i "websocket"

Length of output: 382


Script:

#!/bin/bash
# Let's check the MemoryStream usage in these files to understand the resource management
for file in $(rg -l "MemoryStream" | grep -i "websocket"); do
    echo "=== $file ==="
    rg "MemoryStream" -A 5 -B 5 "$file"
    echo -e "\n"
done

Length of output: 6594


Script:

#!/bin/bash
# Check for using statements and disposal patterns
for file in $(rg -l "MemoryStream" | grep -i "websocket"); do
    echo "=== $file ==="
    # Look for using statements with MemoryStream
    rg -A 10 -B 2 "using.*MemoryStream|IDisposable" "$file"
    echo -e "\n"
done

Length of output: 6078

Deepgram/Models/Listen/v2/WebSocket/Metadata.cs (1)

1-6: LGTM! File header is well-structured.

The copyright notice, license information, and namespace declaration are properly formatted and complete.

Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (2)

1-6: LGTM! Well-structured file header with proper licensing.

The file header includes appropriate copyright notice and license information.


5-5: Verify consistent usage of the new v2 error response model.

As this is part of the v2 namespace, we should ensure all WebSocket error handling in v2 uses this model consistently.

✅ Verification successful

The previous commands failed due to incorrect glob pattern and language specification. Let's try with simpler patterns to understand the codebase structure and error handling usage.


Error response model is consistently used across v2 WebSocket implementations

The verification shows that the v2 error response model is properly implemented and consistently used:

  • The base ErrorResponse in Common.v2.WebSocket namespace is inherited by both Listen and Speak v2 implementations
  • All v2 WebSocket clients (Listen/v2/WebSocket/Client.cs and Speak/v2/WebSocket/Client.cs) properly handle error responses using the common model
  • The error handling pattern is consistent across the v2 implementations with proper event subscription and error propagation
  • The interfaces (IListenWebSocketClient, ISpeakWebSocketClient) enforce consistent error handling contracts

The error model is well-integrated into the v2 WebSocket architecture and follows a consistent pattern across different components.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of ErrorResponse class in v2 namespace
rg -t cs "ErrorResponse" --glob "Deepgram/**/*v2*.cs"

# Search for error handling patterns that might not use this model
ast-grep --pattern 'catch ($exception) { $$$' --lang c-sharp

Length of output: 380


Script:

#!/bin/bash
# Search for ErrorResponse usage with correct glob pattern and showing context
rg -t cs "ErrorResponse" -C 2

# Search for WebSocket error handling patterns
rg -t cs "catch.*WebSocket" -C 2

# List all v2 related C# files to understand the scope
fd ".cs$" . | grep -i "v2"

Length of output: 22179

Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (2)

1-8: LGTM!

The file header contains appropriate copyright notice and license information, and the namespace follows the versioning convention.


9-12: ⚠️ Potential issue

Clarify the interface's usage status and purpose.

The comment "Not currently being used" seems inconsistent with the PR objectives, which indicate this is part of a major v2 refactor for WebSocket clients. If this interface is intended for future use, the documentation should explain its purpose and implementation timeline.

Let's verify the interface usage:

Consider replacing the current documentation with:

-/// <summary>
-/// Not currently being used
-/// </summary>
+/// <summary>
+/// Defines the contract for v2 of the Speak REST client, providing synchronous and asynchronous
+/// methods for text-to-speech conversion using Deepgram's API.
+/// </summary>
Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (1)

73-77: ⚠️ Potential issue

Review Error property implementation.

The Error property needs attention:

  1. The TODO comment needs to be addressed before merging
  2. Consider creating a dedicated error DTO instead of using Exception directly
  3. Missing JSON serialization attributes unlike other properties

Let's check if and how this Error property is used:

Consider this implementation instead:

-    // TODO: DYV is this needed???
     /// <summary>
-    /// Error information.
+    /// Contains error details when the WebSocket operation fails.
     /// </summary>
-    public Exception? Error { get; set; }
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    [JsonPropertyName("error")]
+    public ErrorResponse? Error { get; set; }

Where ErrorResponse would be:

public record ErrorResponse
{
    [JsonPropertyName("message")]
    public string Message { get; init; } = string.Empty;

    [JsonPropertyName("code")]
    public string Code { get; init; } = string.Empty;
}
Deepgram/ClientFactory.cs (4)

6-12: LGTM! Clean namespace organization with version separation.

The namespace imports are well-organized using aliases, providing clear separation between v1 and v2 interfaces. This structure aligns well with the versioning strategy.


Line range hint 38-55: LGTM! Well-documented deprecation process.

The deprecation handling follows best practices with:

  • Clear warning messages
  • Proper use of the Obsolete attribute
  • Guidance for transitioning to new versions
🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (7.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (6.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: build

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


223-245: 🛠️ Refactor suggestion

Apply consistent versioning pattern across WebSocket clients.

The SpeakWebSocketClient implementation has the same versioning pattern as ListenWebSocketClient. Consider applying the same refactoring to maintain consistency.

Apply the same pattern suggested for ListenWebSocketClient to maintain consistency across the codebase.

🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

🪛 GitHub Check: test (7.0.x)

[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

🪛 GitHub Check: test (6.0.x)

[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

🪛 GitHub Check: build

[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'


77-77: Verify handling of obsolete client warnings.

The static analysis warnings about obsolete clients are expected as part of the versioning strategy. However, ensure that the deprecation timeline is documented in the migration guide.

Also applies to: 239-239

✅ Verification successful

Obsolete client warnings are properly documented with clear migration paths

The codebase shows a well-structured deprecation strategy:

  • All v1 clients are properly marked with [Obsolete] attributes that specify the new client to use
  • Deprecation notices include clear migration paths (e.g., "Please use CreateListenWebSocketClient instead")
  • Runtime warnings are implemented to notify users about deprecation during execution
  • The README.md includes a "Backwards Compatibility" section explaining the support policy for older versions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for migration guide documentation
rg -l "migration|upgrade|deprecation" docs/

Length of output: 91


Script:

#!/bin/bash
# Let's first check if there are any documentation files in the repository
fd -t f "README|MIGRATION|UPGRADE|CHANGELOG" -i

# Also search for any obsolete/deprecated attributes or comments in the codebase
rg -i "obsolete|deprecated|deprecation" -A 2

Length of output: 51118


Script:

#!/bin/bash
# Let's check the README files for any migration/upgrade documentation
cat Deepgram/README.md README.md examples/README.md

Length of output: 40764

🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (7.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (6.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: build

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

Deepgram.Dev.sln (4)

172-175: LGTM: Project structure aligns with PR objectives.

The addition of stt_v1_client_example and tts_v1_client_example folders under edge_cases provides a clear separation for v1 client examples, which aligns with the PR's goal of maintaining backward compatibility while introducing v2 clients.


176-179: LGTM: Project references are properly configured.

The new projects are correctly referenced:

  • Streaming.csproj for STT v1 client example
  • Speak.csproj for TTS v1 client example

322-329: LGTM: Build configurations are complete.

Both projects are properly configured with Debug and Release configurations, including their respective build and active configuration settings.


411-414: LGTM: Project nesting maintains solution organization.

The new projects are correctly nested under the edge_cases folder, maintaining the solution's organizational structure:

  • stt_v1_client_example{0BF29CA2-1CD6-4FF0-BC7B-B33C6B41E9A1}
  • tts_v1_client_example{5CEEB2F0-F284-4BB3-8999-6E91151C0C06}
✅ Verification successful

Project structure verification confirms correct organization

The verification confirms that both projects exist at their expected paths under the edge_cases folder:

  • tests/edge_cases/stt_v1_client_example/Streaming.csproj
  • tests/edge_cases/tts_v1_client_example/Speak.csproj

This matches the solution file's nesting structure, validating that the project organization is correctly maintained.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify project structure matches solution organization
# Test: Check if the projects exist at the expected paths
fd -t f "Streaming.csproj" "tests/edge_cases/stt_v1_client_example"
fd -t f "Speak.csproj" "tests/edge_cases/tts_v1_client_example"

Length of output: 235

Deepgram/Abstractions/v2/AbstractRestClient.cs (1)

777-781: LGTM!

The URI helper method is simple and correctly implements the URI construction logic.

Deepgram/Clients/Listen/v1/WebSocket/Client.cs (1)

14-23: LGTM! Well-documented deprecation notice.

The deprecation is properly implemented with both XML comments and the [Obsolete] attribute, providing clear guidance to users about migrating to v2.

Deepgram/Clients/Interfaces/v2/IManageClient.cs (2)

5-5: Consider updating the model namespace to match the interface version

The interface is in the v2 namespace but imports models from v1 with using Deepgram.Models.Manage.v1;. Ensure this is intentional and consider updating to Deepgram.Models.Manage.v2 if appropriate.


175-175: Fix typo in the comment

Correct "Update the scopes fot the member" to "Update the scopes for the member".

[typo]

Apply this diff:

-/// Update the scopes fot the member
+/// Update the scopes for the member
Deepgram/Abstractions/v1/AbstractRestClient.cs (1)

31-37: Potential Issue with Options Initialization

In the constructor, if options is null, it's initialized with new DeepgramHttpClientOptions(apiKey). However, if apiKey is also null, this could lead to unintended behavior or exceptions when the HttpClient is configured.

Please verify that apiKey is not null when initializing DeepgramHttpClientOptions. If apiKey can be null, consider handling this case appropriately or enforcing that an API key must be provided.

@dvonthenen dvonthenen force-pushed the auto-flush-deadlock-plus-refactor branch from 020385e to f42b51d Compare October 28, 2024 19:24
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: 15

🧹 Outside diff range and nitpick comments (21)
Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (2)

7-11: Enhance class documentation.

Consider adding more detailed XML documentation to explain:

  • The purpose and typical usage of this class
  • The responsibility for disposing of resources
  • Example usage with the using statement
 /// <summary>
-/// LocalFileWithMetadata is a class that represents a file with metadata.
+/// Represents a file content in memory along with associated metadata.
+/// This class implements IDisposable to ensure proper cleanup of memory resources.
 /// </summary>
+/// <remarks>
+/// Use this class with a 'using' statement to ensure proper resource cleanup:
+/// <code>
+/// using var file = new LocalFileWithMetadata();
+/// file.Content = new MemoryStream(bytes);
+/// file.Metadata["key"] = "value";
+/// </code>
+/// </remarks>

12-15: Enhance Metadata property documentation.

Consider adding more detailed XML documentation to explain:

  • The expected key-value pairs
  • Whether null values are allowed
  • Whether the dictionary can be null
 /// <summary>
-/// Gets or sets the metadata associated with the file content.
+/// Gets or sets a dictionary of string key-value pairs representing metadata associated with the file content.
+/// If not initialized, this property may return null.
 /// </summary>
+/// <remarks>
+/// Common metadata keys might include:
+/// - "content-type": The MIME type of the content
+/// - "filename": Original name of the file
+/// - "timestamp": When the content was created or modified
+/// </remarks>
Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (2)

7-37: Consider adding nullable reference annotations.

While the properties are marked as nullable with ?, consider adding #nullable enable directive at the file level to enforce null-safety throughout the class. This aligns with modern C# best practices for null safety.

+#nullable enable

namespace Deepgram.Models.Common.v2.WebSocket;

7-61: Good alignment with v2 WebSocket architecture.

This error response model aligns well with the PR's objectives of improving the WebSocket implementation. The record-based immutable design is particularly suitable for async operations, helping prevent thread-safety issues mentioned in the PR objectives.

Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (3)

15-22: Complete the XML documentation for ToStream method.

The current documentation is missing details for:

  • cancellationToken
  • addons
  • headers

Also, consider documenting potential exceptions that might be thrown.

Here's a suggested documentation improvement:

 /// <summary>
-/// Speaks a file using the provided stream
+/// Converts text to speech using the provided stream
 /// </summary>
 /// <param name="source">file is the form of a stream <see cref="Stream"/></param>
 /// <param name="speakSchema">Options for the transcription <see cref="SpeakSchema"/></param>
+/// <param name="cancellationToken">Token to cancel the operation</param>
+/// <param name="addons">Additional parameters for the request</param>
+/// <param name="headers">Custom headers for the request</param>
 /// <returns><see cref="SyncResponse"/></returns>
+/// <exception cref="ArgumentNullException">Thrown when source is null</exception>

24-25: Add XML documentation for ToFile method.

The ToFile method is missing XML documentation completely. This is important for maintaining API documentation consistency.

Add documentation following this template:

/// <summary>
/// Converts text to speech and saves the result to a file
/// </summary>
/// <param name="source">Text content to convert</param>
/// <param name="filename">Target file path for the audio output</param>
/// <param name="speakSchema">Options for the transcription <see cref="SpeakSchema"/></param>
/// <param name="cancellationToken">Token to cancel the operation</param>
/// <param name="addons">Additional parameters for the request</param>
/// <param name="headers">Custom headers for the request</param>
/// <returns><see cref="SyncResponse"/></returns>
/// <exception cref="ArgumentNullException">Thrown when source or filename is null</exception>
/// <exception cref="IOException">Thrown when file operations fail</exception>

12-39: Consider architectural improvements for better API design.

A few suggestions to enhance the interface design:

  1. Consider using the builder pattern for the optional parameters (SpeakSchema, addons, headers) to improve API usability
  2. Document the TextSource type and consider making it more specific to text-to-speech use cases
  3. Consider adding overloads for common use cases to simplify client code

Example of a builder pattern approach:

public interface ISpeakRequestBuilder
{
    ISpeakRequestBuilder WithSchema(SpeakSchema schema);
    ISpeakRequestBuilder WithAddons(Dictionary<string, string> addons);
    ISpeakRequestBuilder WithHeaders(Dictionary<string, string> headers);
    Task<SyncResponse> ToStreamAsync(TextSource source, CancellationToken cancellationToken = default);
    Task<SyncResponse> ToFileAsync(TextSource source, string filename, CancellationToken cancellationToken = default);
    Task<AsyncResponse> WithCallbackAsync(TextSource source, string callback, CancellationToken cancellationToken = default);
}
Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (3)

77-77: Add JSON serialization attributes for consistency.

The Error property is missing JSON serialization attributes that are present on all other properties. Consider adding them for consistency:

    /// <summary>
    /// Error information.
    /// </summary>
+   [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+   [JsonPropertyName("error")]
    public Exception? Error { get; set; }

4-5: Add missing using directives.

Add the following using directives for types used in the class:

// SPDX-License-Identifier: MIT

+using System.Text.Json;
+using System.Text.RegularExpressions;
+
namespace Deepgram.Models.Listen.v2.WebSocket;

51-53: Clarify SpeechFinal property documentation.

The current documentation "Is the result a partial result" seems contradictory to the property name SpeechFinal. Consider updating it to better reflect its purpose:

    /// <summary>
-   /// Is the result a partial result.
+   /// Indicates whether the speech processing for this segment is complete.
    /// </summary>
Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (3)

37-37: Fix typo in parameter documentation.

There's a typo in the source parameter documentation.

-/// <param name="source">file is the form of a streasm <see cref="Stream"/></param>
+/// <param name="source">File in the form of a stream <see cref="Stream"/></param>

22-23: Document the purpose of optional parameters.

The addons and headers dictionary parameters lack documentation explaining their purpose. Consider adding XML documentation for these parameters.

Add documentation for the optional parameters:

+/// <param name="cancellationToken">Optional token for canceling the operation</param>
+/// <param name="addons">Optional dictionary for additional configuration parameters</param>
+/// <param name="headers">Optional dictionary for custom HTTP headers</param>

Also applies to: 31-32, 40-42


46-53: Enhance callback-related documentation.

The callback functionality would benefit from more detailed documentation:

  1. Explain what happens when the callback URL is null
  2. Document the relationship between callbacks and AsyncResponse
  3. Add remarks about callback URL requirements/format

Add additional documentation to clarify callback behavior:

 /// <summary>
 /// Transcribes a file using the provided byte array and providing a CallBack
 /// </summary>
+/// <remarks>
+/// When a callback URL is provided, the transcription results will be sent to the specified endpoint upon completion.
+/// The callback URL must be a valid HTTPS endpoint accessible from the Deepgram servers.
+/// If the callback URL is null, the transcription will still be processed but no results will be sent.
+/// </remarks>

Also applies to: 57-64, 68-75

Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (1)

92-92: Improve method parameter formatting for readability.

Consider breaking down long method signatures into multiple lines for better readability, consistent with other methods in the interface.

-    public Task<AsyncResponse> AnalyzeTextCallBack(TextSource source, string? callBack, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null);
+    public Task<AsyncResponse> AnalyzeTextCallBack(TextSource source, string? callBack, AnalyzeSchema? analyzeSchema,
+        CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null,
+        Dictionary<string, string>? headers = null);
Deepgram/ClientFactory.cs (1)

33-37: Consider enhancing documentation for single-version clients.

The version parameter is accepted but effectively ignored for single-version clients. Consider adding XML documentation to clarify:

  • Currently supported versions
  • Future versioning plans
  • Expected behavior when version parameter is provided

Example documentation addition:

 /// <summary>
 /// This method allows you to create an AnalyzeClient with a specific version of the client.
 /// </summary>
+/// <remarks>
+/// Currently, only version 1 is supported. The version parameter is reserved for future use.
+/// All version inputs will return the v1 implementation.
+/// </remarks>

Also applies to: 100-103, 137-141, 174-177, 211-214

Deepgram/Clients/Listen/v2/WebSocket/Client.cs (2)

67-77: Consider adding error handling for background thread initialization.

The background thread initialization for KeepAlive and AutoFlush could fail silently. While the implementation is functional, it would be beneficial to add error handling and status reporting.

 if (_deepgramClientOptions.KeepAlive)
 {
     Log.Debug("Connect", "Starting KeepAlive Thread...");
-    StartKeepAliveBackgroundThread();
+    try 
+    {
+        StartKeepAliveBackgroundThread();
+        Log.Debug("Connect", "KeepAlive Thread started successfully");
+    }
+    catch (Exception ex)
+    {
+        Log.Error("Connect", $"Failed to start KeepAlive thread: {ex.Message}");
+        throw;
+    }
 }

601-605: Simplify empty transcript check.

The current check for empty transcript can be simplified using string.IsNullOrEmpty.

-if (resultResponse.Channel.Alternatives.Count == 0 || sentence == "") {
+if (resultResponse.Channel.Alternatives.Count == 0 || string.IsNullOrEmpty(sentence)) {
Deepgram.Dev.sln (1)

411-414: LGTM: Clear separation of v1 client examples.

The nested project structure provides clear organization and separation of v1 client examples. This organization will help developers:

  1. Easily identify v1 client implementations
  2. Distinguish between v1 and v2 clients
  3. Find appropriate examples for their use case

Consider adding README files in both example directories to:

  • Explain when to use v1 vs v2 clients
  • Document known limitations of v1 clients
  • Guide users on migrating to v2 clients when appropriate
Deepgram/Abstractions/v1/AbstractRestClient.cs (1)

35-36: Add null check for HttpClient after factory creation.

The HttpClient creation should be validated to ensure the factory methods succeeded.

- _httpClient = HttpClientFactory.Create(httpId);
- _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, options);
+ _httpClient = HttpClientFactory.Create(httpId) ?? throw new InvalidOperationException("Failed to create HttpClient");
+ _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, options) ?? throw new InvalidOperationException("Failed to configure HttpClient");
Deepgram/Abstractions/v2/AbstractRestClient.cs (2)

109-109: Correct the typo 'Excepton' to 'Exception' in log messages

The log messages contain a typo: 'Excepton' should be 'Exception' at the following lines: 109, 173, 296, 370, 447, 528, 605, 673, and 743.

Apply this diff to correct the typos:

- Log.Verbose("GetAsync<T>", $"Excepton: {ex}");
+ Log.Verbose("GetAsync<T>", $"Exception: {ex}");

- Log.Verbose("GetAsync<S, T>", $"Excepton: {ex}");
+ Log.Verbose("GetAsync<S, T>", $"Exception: {ex}");

- Log.Verbose("PostRetrieveLocalFileAsync<R, S, T>", $"Excepton: {ex}");
+ Log.Verbose("PostRetrieveLocalFileAsync<R, S, T>", $"Exception: {ex}");

- Log.Verbose("PostAsync<S, T>", $"Excepton: {ex}");
+ Log.Verbose("PostAsync<S, T>", $"Exception: {ex}");

- Log.Verbose("PostAsync<R, S, T>", $"Excepton: {ex}");
+ Log.Verbose("PostAsync<R, S, T>", $"Exception: {ex}");

- Log.Verbose("PatchAsync<S, T>", $"Excepton: {ex}");
+ Log.Verbose("PatchAsync<S, T>", $"Exception: {ex}");

- Log.Verbose("PutAsync<S, T>", $"Excepton: {ex}");
+ Log.Verbose("PutAsync<S, T>", $"Exception: {ex}");

- Log.Verbose("DeleteAsync<T>", $"Excepton: {ex}");
+ Log.Verbose("DeleteAsync<T>", $"Exception: {ex}");

- Log.Verbose("DeleteAsync<S, T>", $"Excepton: {ex}");
+ Log.Verbose("DeleteAsync<S, T>", $"Exception: {ex}");

Also applies to: 173-173, 296-296, 370-370, 447-447, 528-528, 605-605, 673-673, 743-743


380-380: Correct the method name in log message

The log message at line 380 has an incorrect method name. It should be PostAsync<R, S, T> instead of PostAsync<S, T>.

Apply this diff to correct the method name:

- Log.Debug("PostAsync<S, T>", $"uriSegment: {uriSegment}");
+ Log.Debug("PostAsync<R, S, T>", $"uriSegment: {uriSegment}");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e70e0cb and f42b51d.

📒 Files selected for processing (82)
  • Deepgram.Dev.sln (3 hunks)
  • Deepgram.Tests/Fakes/ConcreteRestClient.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs (0 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs (1 hunks)
  • Deepgram.Tests/UnitTests/UtilitiesTests/RequestContentUtilTests.cs (1 hunks)
  • Deepgram/Abstractions/LocalFileWithMetadata.cs (0 hunks)
  • Deepgram/Abstractions/v1/AbstractRestClient.cs (1 hunks)
  • Deepgram/Abstractions/v1/Constants.cs (1 hunks)
  • Deepgram/Abstractions/v1/LocalFileWithMetadata.cs (1 hunks)
  • Deepgram/Abstractions/v1/NoopSchema.cs (1 hunks)
  • Deepgram/Abstractions/v1/Utilities.cs (1 hunks)
  • Deepgram/Abstractions/v2/AbstractRestClient.cs (1 hunks)
  • Deepgram/Abstractions/v2/AbstractWebSocketClient.cs (1 hunks)
  • Deepgram/Abstractions/v2/Constants.cs (1 hunks)
  • Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (1 hunks)
  • Deepgram/Abstractions/v2/NoopSchema.cs (1 hunks)
  • Deepgram/Abstractions/v2/Utilities.cs (1 hunks)
  • Deepgram/Abstractions/v2/WebSocketMessage.cs (1 hunks)
  • Deepgram/ClientFactory.cs (10 hunks)
  • Deepgram/Clients/Analyze/v1/Client.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v1/ISpeakClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v1/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/Constants.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ILiveClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IManageClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IOnPremClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/IPreRecordedClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISelfHostedClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISpeakClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (1 hunks)
  • Deepgram/Clients/Interfaces/v2/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Listen/v1/REST/Client.cs (1 hunks)
  • Deepgram/Clients/Listen/v1/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/Constants.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Listen/v2/WebSocket/UriSegments.cs (1 hunks)
  • Deepgram/Clients/Manage/v1/Client.cs (1 hunks)
  • Deepgram/Clients/SelfHosted/v1/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v1/REST/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v1/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/Client.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/Constants.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/ResponseEvent.cs (1 hunks)
  • Deepgram/Clients/Speak/v2/WebSocket/UriSegments.cs (1 hunks)
  • Deepgram/GlobalUsings.cs (0 hunks)
  • Deepgram/ListenWebSocketClient.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/CloseResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/OpenResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/UnhandledResponse.cs (1 hunks)
  • Deepgram/Models/Common/v2/WebSocket/WebSocketType.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Alternative.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Average.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Channel.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/CloseResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ErrorResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Hit.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ListenType.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/LiveSchema.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Metadata.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/MetadataResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ModelInfo.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/OpenResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Search.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/SpeechStartedResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/UnhandledResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/UtteranceEndResponse.cs (1 hunks)
  • Deepgram/Models/Listen/v2/WebSocket/Word.cs (1 hunks)
  • Deepgram/Models/Speak/v1/WebSocket/AudioResponse.cs (1 hunks)
  • Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs (1 hunks)
  • Deepgram/Models/Speak/v2/WebSocket/AudioResponse.cs (1 hunks)
⛔ Files not processed due to max files limit (24)
  • Deepgram/Models/Speak/v2/WebSocket/ClearedResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/CloseResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/ControlMessage.cs
  • Deepgram/Models/Speak/v2/WebSocket/ErrorResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/FlushedResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/MetadataResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/OpenResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/SpeakSchema.cs
  • Deepgram/Models/Speak/v2/WebSocket/SpeakType.cs
  • Deepgram/Models/Speak/v2/WebSocket/TextSource.cs
  • Deepgram/Models/Speak/v2/WebSocket/UnhandledResponse.cs
  • Deepgram/Models/Speak/v2/WebSocket/WarningResponse.cs
  • Deepgram/SpeakWebSocketClient.cs
  • examples/speech-to-text/websocket/file/Program.cs
  • examples/speech-to-text/websocket/http/Program.cs
  • examples/speech-to-text/websocket/microphone/Program.cs
  • examples/text-to-speech/websocket/simple/Program.cs
  • tests/edge_cases/keepalive/Program.cs
  • tests/edge_cases/reconnect_same_object/Program.cs
  • tests/edge_cases/stt_v1_client_example/Program.cs
  • tests/edge_cases/stt_v1_client_example/Streaming.csproj
  • tests/edge_cases/tts_v1_client_example/Program.cs
  • tests/edge_cases/tts_v1_client_example/Speak.csproj
  • tests/expected_failures/websocket/exercise_timeout/Program.cs
💤 Files with no reviewable changes (3)
  • Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs
  • Deepgram/Abstractions/LocalFileWithMetadata.cs
  • Deepgram/GlobalUsings.cs
✅ Files skipped from review due to trivial changes (5)
  • Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs
  • Deepgram/Abstractions/v1/Constants.cs
  • Deepgram/Clients/Interfaces/v2/Constants.cs
  • Deepgram/Models/Listen/v2/WebSocket/Channel.cs
  • Deepgram/Models/Listen/v2/WebSocket/ErrorResponse.cs
🚧 Files skipped from review as they are similar to previous changes (60)
  • Deepgram.Tests/Fakes/ConcreteRestClient.cs
  • Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs
  • Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs
  • Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs
  • Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs
  • Deepgram.Tests/UnitTests/UtilitiesTests/RequestContentUtilTests.cs
  • Deepgram/Abstractions/v1/LocalFileWithMetadata.cs
  • Deepgram/Abstractions/v1/NoopSchema.cs
  • Deepgram/Abstractions/v1/Utilities.cs
  • Deepgram/Abstractions/v2/AbstractWebSocketClient.cs
  • Deepgram/Abstractions/v2/Constants.cs
  • Deepgram/Abstractions/v2/NoopSchema.cs
  • Deepgram/Abstractions/v2/Utilities.cs
  • Deepgram/Abstractions/v2/WebSocketMessage.cs
  • Deepgram/Clients/Analyze/v1/Client.cs
  • Deepgram/Clients/Interfaces/v1/ISpeakClient.cs
  • Deepgram/Clients/Interfaces/v1/ResponseEvent.cs
  • Deepgram/Clients/Interfaces/v2/ILiveClient.cs
  • Deepgram/Clients/Interfaces/v2/IOnPremClient.cs
  • Deepgram/Clients/Interfaces/v2/IPreRecordedClient.cs
  • Deepgram/Clients/Interfaces/v2/ISelfHostedClient.cs
  • Deepgram/Clients/Interfaces/v2/ISpeakClient.cs
  • Deepgram/Clients/Interfaces/v2/ResponseEvent.cs
  • Deepgram/Clients/Listen/v1/REST/Client.cs
  • Deepgram/Clients/Listen/v1/WebSocket/Client.cs
  • Deepgram/Clients/Listen/v2/WebSocket/Constants.cs
  • Deepgram/Clients/Listen/v2/WebSocket/ResponseEvent.cs
  • Deepgram/Clients/Listen/v2/WebSocket/UriSegments.cs
  • Deepgram/Clients/Manage/v1/Client.cs
  • Deepgram/Clients/SelfHosted/v1/Client.cs
  • Deepgram/Clients/Speak/v1/REST/Client.cs
  • Deepgram/Clients/Speak/v1/WebSocket/Client.cs
  • Deepgram/Clients/Speak/v1/WebSocket/ResponseEvent.cs
  • Deepgram/Clients/Speak/v2/WebSocket/Client.cs
  • Deepgram/Clients/Speak/v2/WebSocket/Constants.cs
  • Deepgram/Clients/Speak/v2/WebSocket/ResponseEvent.cs
  • Deepgram/Clients/Speak/v2/WebSocket/UriSegments.cs
  • Deepgram/ListenWebSocketClient.cs
  • Deepgram/Models/Common/v2/WebSocket/CloseResponse.cs
  • Deepgram/Models/Common/v2/WebSocket/OpenResponse.cs
  • Deepgram/Models/Common/v2/WebSocket/UnhandledResponse.cs
  • Deepgram/Models/Common/v2/WebSocket/WebSocketType.cs
  • Deepgram/Models/Listen/v2/WebSocket/Alternative.cs
  • Deepgram/Models/Listen/v2/WebSocket/Average.cs
  • Deepgram/Models/Listen/v2/WebSocket/CloseResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/Hit.cs
  • Deepgram/Models/Listen/v2/WebSocket/ListenType.cs
  • Deepgram/Models/Listen/v2/WebSocket/LiveSchema.cs
  • Deepgram/Models/Listen/v2/WebSocket/Metadata.cs
  • Deepgram/Models/Listen/v2/WebSocket/MetadataResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/ModelInfo.cs
  • Deepgram/Models/Listen/v2/WebSocket/OpenResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/Search.cs
  • Deepgram/Models/Listen/v2/WebSocket/SpeechStartedResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/UnhandledResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/UtteranceEndResponse.cs
  • Deepgram/Models/Listen/v2/WebSocket/Word.cs
  • Deepgram/Models/Speak/v1/WebSocket/AudioResponse.cs
  • Deepgram/Models/Speak/v1/WebSocket/ControlMessage.cs
  • Deepgram/Models/Speak/v2/WebSocket/AudioResponse.cs
🧰 Additional context used
📓 Learnings (10)
Deepgram/Abstractions/v1/AbstractRestClient.cs (1)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Abstractions/v2/AbstractRestClient.cs:392-407
Timestamp: 2024-10-28T19:02:12.199Z
Learning: In the `Deepgram/Abstractions/v2/AbstractRestClient.cs` file, the user prefers to keep the existing stream handling logic in the `PostAsync<R, S, T>` method.
Deepgram/Abstractions/v2/AbstractRestClient.cs (1)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Abstractions/v2/AbstractRestClient.cs:392-407
Timestamp: 2024-10-28T19:02:12.199Z
Learning: In the `Deepgram/Abstractions/v2/AbstractRestClient.cs` file, the user prefers to keep the existing stream handling logic in the `PostAsync<R, S, T>` method.
Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (2)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Abstractions/v2/LocalFileWithMetadata.cs:10-11
Timestamp: 2024-10-28T18:05:46.981Z
Learning: In the `Deepgram/Abstractions/v2/LocalFileWithMetadata.cs` file, for the `LocalFileWithMetadata` class, the user prefers to defer implementing the full `IDisposable` pattern, considering the current implementation sufficient, and can be changed later without breaking interfaces.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Abstractions/v1/LocalFileWithMetadata.cs:15-15
Timestamp: 2024-10-28T18:26:15.761Z
Learning: In the `Deepgram/Abstractions/v1/LocalFileWithMetadata.cs` file, for the `LocalFileWithMetadata` class, the user chooses to keep the `Metadata` property as-is, without initializing it or adding null checks at this time.
Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (1)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs:21-22
Timestamp: 2024-10-28T18:07:22.077Z
Learning: In this project, methods intentionally use `CancellationTokenSource` instead of `CancellationToken` in their method signatures.
Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (2)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs:9-11
Timestamp: 2024-10-28T19:01:20.963Z
Learning: When an interface in `Deepgram.Clients.Interfaces.v2`, such as `IListenRESTClient`, is intentionally left unused for future versions, it's acceptable to include a summary comment stating "Not currently being used."
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs:23-23
Timestamp: 2024-10-28T19:00:16.518Z
Learning: In this codebase, methods intentionally accept `CancellationTokenSource` parameters to implement per function timeouts, so do not suggest replacing them with `CancellationToken`.
Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs (2)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs:77-77
Timestamp: 2024-10-28T19:04:15.779Z
Learning: In `IListenWebSocketClient`, the methods `SendKeepAlive`, `SendFinalize`, and `SendClose` send messages on an internal queue and will error or be discarded if the connection is no longer valid, so adding `CancellationToken` parameters is not necessary.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs:25-70
Timestamp: 2024-10-28T18:14:28.097Z
Learning: In the file `Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs`, for the `Subscribe` methods, when the parameter `eventHandler` is self-explanatory, it's acceptable to omit descriptions in the XML documentation. Therefore, I'll avoid flagging missing parameter descriptions for `eventHandler` in `Subscribe` methods.
Deepgram/Clients/Interfaces/v2/IManageClient.cs (1)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IManageClient.cs:10-10
Timestamp: 2024-10-28T18:28:01.526Z
Learning: In the file `Deepgram/Clients/Interfaces/v2/IManageClient.cs`, the `IManageClient` interface is intentionally kept, even though it is not currently used. The comment "Not currently being used" remains to indicate that it may be utilized in the future.
Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (5)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs:106-107
Timestamp: 2024-10-28T18:22:59.455Z
Learning: In `Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs`, commented-out methods related to binary send, such as `SpeakWithStream`, `SendBinary`, and `SendBinaryImmediately`, are intentionally left in the code to indicate that binary send is not currently supported.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs:85-85
Timestamp: 2024-10-28T18:23:10.963Z
Learning: In the `ISpeakWebSocketClient` interface, the method `SpeakWithText(string data)` is intentionally named and should not be renamed.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/v1/Client.cs:0-0
Timestamp: 2024-10-09T02:19:46.086Z
Learning: The event subscription methods in the Deepgram .NET SDK cannot be abstracted into a common method due to concrete type requirements for each event handler.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#316
File: Deepgram/Clients/Speak/v1/WebSocket/v1/Client.cs:0-0
Timestamp: 2024-08-08T19:34:11.728Z
Learning: The event subscription methods in the Deepgram .NET SDK cannot be abstracted into a common method due to concrete type requirements for each event handler.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs:25-70
Timestamp: 2024-10-28T18:14:28.097Z
Learning: In the file `Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs`, for the `Subscribe` methods, when the parameter `eventHandler` is self-explanatory, it's acceptable to omit descriptions in the XML documentation. Therefore, I'll avoid flagging missing parameter descriptions for `eventHandler` in `Subscribe` methods.
Deepgram/Clients/Listen/v2/WebSocket/Client.cs (1)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Listen/v2/WebSocket/Client.cs:609-609
Timestamp: 2024-10-28T19:08:03.818Z
Learning: In the file `Deepgram/Clients/Listen/v2/WebSocket/Client.cs`, the direct casting of `resultResponse.IsFinal` to `bool` without null checking is acceptable and preferred.
Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs (1)
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Models/Listen/v2/WebSocket/ResultResponse.cs:79-85
Timestamp: 2024-10-28T18:10:11.373Z
Learning: In the Deepgram .NET SDK, when implementing `ToString()` methods in model classes (e.g., `ResultResponse.cs` in `Deepgram/Models/Listen/v2/WebSocket`), it's acceptable to let exceptions bubble up when these methods are primarily used in debug messages.
🪛 GitHub Check: test (8.0.x)
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 775-775:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 461-461:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

🪛 GitHub Check: test (7.0.x)
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 775-775:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 461-461:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

🪛 GitHub Check: test (6.0.x)
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 775-775:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 461-461:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

🪛 GitHub Check: build
Deepgram/Abstractions/v1/AbstractRestClient.cs

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Abstractions/v2/AbstractRestClient.cs

[warning] 775-775:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/ClientFactory.cs

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


[warning] 239-239:
'Client' is obsolete: 'Please use Deepgram.Clients.Speak.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs

[warning] 461-461:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

🔇 Additional comments (23)
Deepgram/Abstractions/v2/LocalFileWithMetadata.cs (3)

1-5: LGTM! Well-documented file header.

The copyright notice and license information are properly included.


26-32: Verify disposal safety in concurrent scenarios.

The current disposal implementation might not be thread-safe if Dispose is called while other threads are still accessing the Content. Consider documenting any assumptions about concurrent access during disposal.


17-24: Verify thread safety requirements.

The Content property might be accessed from multiple threads when used with WebSocket clients. Consider documenting any thread-safety guarantees or limitations.

Deepgram/Models/Common/v2/WebSocket/ErrorResponse.cs (2)

1-5: LGTM! Proper license and copyright headers.

The file includes appropriate copyright notice and license information.


38-53: Previous review comment about null checking is still applicable.

Deepgram/Clients/Interfaces/v2/ISpeakRESTClient.cs (1)

9-12: Clarify interface usage status and improve documentation.

The comment "Not currently being used" is concerning as this interface is part of the v2 implementation mentioned in the PR objectives. If this interface is intended for use, please:

  1. Remove or update this comment
  2. Add comprehensive documentation explaining the interface's purpose, usage, and how it differs from v1

Let's verify if this interface is actually used:

Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs (1)

12-14: Verify interface integration with WebSocket changes.

This v2 interface is part of a larger refactor involving WebSocket client changes. Please ensure:

  1. The REST client implementation aligns with the new threading model mentioned in the PR objectives
  2. The factory methods for v2 clients properly expose this interface
Deepgram/Clients/Interfaces/v2/IAnalyzeClient.cs (3)

9-11: Previous comment about misleading documentation is still applicable.


12-94: Interface design looks good!

The interface provides a well-structured API for both synchronous and asynchronous analysis operations. The consistent parameter patterns and clear separation between callback and non-callback methods make it easy to use and extend.


5-7: Verify v1 model usage in v2 interface.

The interface is in the v2 namespace but uses models from v1 (Deepgram.Models.Analyze.v1). Please verify if this is intentional or if corresponding v2 models should be created for consistency.

✅ Verification successful

Using v1 models in v2 interfaces appears to be the established pattern

The verification shows this is consistent with the codebase's architecture - all v2 interfaces (ISelfHostedClient, IListenRESTClient, ILiveClient, etc.) use v1 models from their respective domains. This appears to be an intentional design choice where v2 represents interface/client changes while retaining v1 models.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other v2 interfaces using v1 models
rg -l "namespace.*\.v2" | xargs rg "using.*\.v1"

Length of output: 1079

Deepgram/ClientFactory.cs (3)

6-12: LGTM! Clean namespace organization.

The version-specific imports are well-organized using clear aliases, making the distinction between v1 and v2 implementations explicit.


Line range hint 42-50: LGTM! Well-structured deprecation notices.

The deprecation handling follows best practices with:

  • Clear warning blocks in documentation
  • Proper use of Obsolete attributes
  • Explicit migration guidance

Also applies to: 180-190

🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (7.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (6.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: build

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'


69-83: Consider consolidating version handling logic.

While the current implementation works, there are opportunities to reduce duplication:

  1. Version handling logic is duplicated between Listen and Speak clients
  2. Deprecation logging messages are repeated
  3. TODO comments about reflection suggest future improvements

However, I understand from past discussions that these implementation details are being kept as is for now.

Let's verify the version handling is consistent across the codebase:

Also applies to: 231-244

🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (7.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: test (6.0.x)

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

🪛 GitHub Check: build

[warning] 77-77:
'Client' is obsolete: 'Please use Deepgram.Clients.Listen.v2.WebSocket instead'

Deepgram/Clients/Listen/v2/WebSocket/Client.cs (3)

19-20: LGTM: Thread-safe timestamp tracking implementation.

The implementation correctly uses SemaphoreSlim for thread-safe access to the _lastReceived timestamp, which is essential for the auto-flush functionality.


609-609: Direct boolean cast is acceptable.

The direct casting of resultResponse.IsFinal to bool without null checking is acceptable as per the learnings from the previous review.


573-579: LGTM: Robust URI construction.

The GetUri method effectively handles parameter serialization and URL encoding, making good use of LINQ for property filtering.

Deepgram.Dev.sln (2)

172-179: LGTM: Project structure follows solution conventions.

The new v1 client example projects are appropriately placed under the edge_cases directory, which aligns with the PR's goal of maintaining backward compatibility for users who don't experience deadlock issues.


322-329: LGTM: Project configurations are properly set up.

Both Debug and Release configurations are correctly defined for the new projects, maintaining consistency with the existing solution structure.

Deepgram/Abstractions/v1/AbstractRestClient.cs (3)

1-11: LGTM: Clean namespace organization.

The namespace structure properly isolates v1 implementation, aligning with the PR's objective of maintaining backward compatibility while introducing v2 clients.


86-86: Avoid synchronous .Result calls to prevent deadlocks.

The synchronous ReadAsStringAsync().Result calls throughout the HTTP methods can lead to deadlocks.

Also applies to: 149-149, 345-345, 422-422, 502-502, 580-580, 648-648, 718-718


748-775: LGTM: Robust exception handling with proper null checks.

The exception handling is well-structured:

  1. Early return for null/empty error messages via EnsureSuccessStatusCode()
  2. Attempt to deserialize as DeepgramRESTException
  3. Fallback to generic DeepgramException

The static analysis warning about possible null errMsg can be safely ignored as the null check at line 750 ensures errMsg is non-null when reaching line 774.

🧰 Tools
🪛 GitHub Check: test (8.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: test (7.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: test (6.0.x)

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

🪛 GitHub Check: build

[warning] 774-774:
Possible null reference argument for parameter 'errMsg' in 'DeepgramException.DeepgramException(string errMsg)'.

Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs (1)

15-16: 🛠️ Refactor suggestion

Consider using CancellationToken instead of CancellationTokenSource in method signatures

In the methods Connect and Stop, the parameters are of type CancellationTokenSource? cancelToken = null. It's standard practice in .NET to accept a CancellationToken rather than a CancellationTokenSource in asynchronous method signatures. This aligns with the Task-based Asynchronous Pattern and allows callers to use existing cancellation tokens more effectively.

Apply the following changes to update the method signatures:

-public Task<bool> Connect(LiveSchema options, CancellationTokenSource? cancelToken = null, Dictionary<string, string>? addons = null,
-    Dictionary<string, string>? headers = null);
+public Task<bool> Connect(LiveSchema options, CancellationToken cancellationToken = default, Dictionary<string, string>? addons = null,
+    Dictionary<string, string>? headers = null);

-public Task<bool> Stop(CancellationTokenSource? cancelToken = null, bool nullByte = false);
+public Task<bool> Stop(CancellationToken cancellationToken = default, bool nullByte = false);

Also applies to: 18-18

⛔ Skipped due to learnings
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenWebSocketClient.cs:77-77
Timestamp: 2024-10-28T19:04:15.779Z
Learning: In `IListenWebSocketClient`, the methods `SendKeepAlive`, `SendFinalize`, and `SendClose` send messages on an internal queue and will error or be discarded if the connection is no longer valid, so adding `CancellationToken` parameters is not necessary.
Learnt from: dvonthenen
PR: deepgram/deepgram-dotnet-sdk#345
File: Deepgram/Clients/Interfaces/v2/IListenRESTClient.cs:23-23
Timestamp: 2024-10-28T19:00:16.518Z
Learning: In this codebase, methods intentionally accept `CancellationTokenSource` parameters to implement per function timeouts, so do not suggest replacing them with `CancellationToken`.
Deepgram/Clients/Interfaces/v2/ISpeakWebSocketClient.cs (1)

102-107: Maintain consistency in commented-out code

The methods SpeakWithStream, SendBinary, and SendBinaryImmediately are intentionally commented out to indicate that binary send is not currently supported, as per previous learnings. Ensure that the commenting style is consistent across these methods.

Also applies to: 120-125, 133-137

Deepgram/Clients/Listen/v2/WebSocket/Client.cs Outdated Show resolved Hide resolved
Deepgram/Clients/Interfaces/v2/IManageClient.cs Outdated Show resolved Hide resolved
Deepgram/Clients/Interfaces/v2/IManageClient.cs Outdated Show resolved Hide resolved
@dvonthenen dvonthenen force-pushed the auto-flush-deadlock-plus-refactor branch from df52ddd to 22518be Compare October 28, 2024 19:48
@dvonthenen dvonthenen merged commit ba0f717 into deepgram:main Nov 1, 2024
5 checks passed
@dvonthenen dvonthenen deleted the auto-flush-deadlock-plus-refactor branch November 1, 2024 14:56
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