Skip to content

M.E.AI.Abstractions - Speech to Text Abstraction - #5838

Merged
stephentoub merged 32 commits into
dotnet:mainfrom
rogerbarreto:audio-transcription-abstraction
Apr 2, 2025
Merged

M.E.AI.Abstractions - Speech to Text Abstraction#5838
stephentoub merged 32 commits into
dotnet:mainfrom
rogerbarreto:audio-transcription-abstraction

Conversation

@rogerbarreto

@rogerbarretorogerbarreto commented Feb 3, 2025

Copy link
Copy Markdown
Contributor

ADR - Introducing Speech To Text Abstraction

Problem Statement

The project requires the ability to transcribe and translate speech audios to text. The project is a proof of concept to validate the ISpeechToTextClient abstraction against different transcription and translation APIs providing a consistent interface for the project to use.

Note

The names used for the proposed abstractions below are open and can be changed at any time given a bigger consensus.

Considered Options

Option 1: Generic Multi Modality Abstraction IModelClient<TInput, TOutput> (Discarded)

This option would have provided a generic abstraction for all models, including audio transcription. However, this would have made the abstraction too generic and brought up some questioning during the meeting:

Usability Concerns:

The generic interface could make the API less intuitive and harder to use, as users would not be guided towards the specific options they need. 1

  • Naming and Clarity:

    Generic names like "complete streaming" do not convey the specific functionality, making it difficult for users to understand what the method does. Specific names like "transcribe" or "generate song" would be clearer. 2

  • Implementation Complexity:

    Implementing a generic interface would still require concrete implementations for each permutation of input and output types, which could be complex and cumbersome. 3

  • Specific Use Cases:

    Different services have specific requirements and optimizations for their modalities, which may not be effectively captured by a generic interface. 4

  • Future Proofing vs. Practicality:

    While a generic interface aims to be future-proof, it may not be practical for current needs and could lead to an explosion of permutations that are not all relevant. 5

  • Separation of Streaming and Non-Streaming:

    There was a concern about separating streaming and non-streaming interfaces, as it could complicate the API further. 6

Option 2: Speech to Text Abstraction ISpeechToTextClient (Preferred)

This option would provide a specific abstraction for audio transcription and audio translations, which would be more intuitive and easier to use. The specific interface would allow for better optimization and customization for each service.

Initially was thought on having different interfaces one for streaming and another for non-streaming api, but after some discussion, it was decided to have a single interface similar to what we have in IChatClient.

Note

Further modality abstractions will mostly follow this as a standard moving forward.

publicinterfaceISpeechToTextClient:IDisposable{Task<SpeechToTextResponse>GetTextAsync(StreamaudioSpeechStream,SpeechToTextOptions?options=null,CancellationTokencancellationToken=default);IAsyncEnumerable<SpeechToTextResponseUpdate>GetStreamingTextAsync(StreamaudioSpeechStream,SpeechToTextOptions?options=null,CancellationTokencancellationToken=default);}

Inputs:

  • Stream audioSpeechStream, allows for streaming audio data contents to the service.

    This API enables usage of large audio files or real-time transcription (without having to load the full file in-memory) and can easily be extended to support different audio input types like a single DataContent or a Stream instance.

    Supporting scenarios like:

    • Single in-memory data of audio. Non up-streaming audio
    • One audio streamed in multiple audio content chunks - Real-time Transcription
    • Single or multiple audio uri (referenced) audioContents - Batch Transcription

    DataContent type input extension

    // Non-Streaming APIpublicstaticTask<SpeechToTextResponse>GetTextAsync(thisISpeechToTextClientclient,DataContentaudioSpeechContent,SpeechToTextOptions?options=null,CancellationTokencancellationToken=default);// Streaming APIpublicstaticIAsyncEnumerable<SpeechToTextResponseUpdate>GetStreamingTextAsync(thisISpeechToTextClientclient,DataContentaudioSpeechContent,SpeechToTextOptions?options=null,CancellationTokencancellationToken=default);
  • SpeechToTextOptions, analogous to existing ChatOptions it allows providing additional options on both Streaming and Non-Streaming APIs for the service, such as language, model, or other parameters.

    publicclassSpeechToTextOptions{/// <summary>Gets or sets the model ID for the speech to text.</summary>publicstring?ModelId{get;set;}/// <summary>Gets or sets the language of source speech.</summary>publicstring?SpeechLanguage{get;set;}/// <summary>Gets or sets the language for the target generated text.</summary>publicstring?TextLanguage{get;set;}/// <summary>Gets or sets the sample rate of the speech input audio.</summary>publicint?SpeechSampleRate{get;set;}/// <summary>Gets or sets any additional properties associated with the options.</summary>publicAdditionalPropertiesDictionary?AdditionalProperties{get;set;}/// <summary>Produces a clone of the current <see cref="SpeechToTextOptions"/> instance.</summary>/// <returns>A clone of the current <see cref="SpeechToTextOptions"/> instance.</returns>publicvirtualSpeechToTextOptionsClone();}
    • ModelId is a unique identifier for the model to use for transcription.

    • SpeechLanguage is the language of the audio content.

    • SpeechSampleRate is the sample rate of the audio content. Real-time speech to text generation requires a specific sample rate.

Outputs:

  • SpeechToTextResponse, For non-streaming API analogous to existing ChatResponse it provides the text generated result and additional information about the speech response.

    publicclassSpeechToTextResponse{[JsonConstructor]publicSpeechToTextResponse();publicSpeechToTextResponse(IList<AIContent>contents);publicSpeechToTextResponse(string?content);/// <summary>Gets or sets the ID of the generated text response.</summary>publicstring?ResponseId{get;set;}/// <summary>Gets or sets the model ID using in the creation of the speech to text.</summary>publicstring?ModelId{get;set;}/// <summary>Gets or sets the start time of the text segment associated with this response in relation to the full audio speech length.</summary>publicTimeSpan?StartTime{get;set;}/// <summary>Gets or sets the end time of the text segment associated with this response in relation to the full audio speech length.</summary>publicTimeSpan?EndTime{get;set;}/// <summary>Gets or sets the raw representation of the speech to text completion from an underlying implementation.</summary>[JsonIgnore]publicobject?RawRepresentation{get;set;}/// <summary>Gets or sets any additional properties associated with the speech to text completion.</summary>publicAdditionalPropertiesDictionary?AdditionalProperties{get;set;}/// <summary>Gets or sets the generated content items.</summary>[AllowNull]publicIList<AIContent>Contents{get;set;}/// <summary>Gets the text of this speech to text response.</summary>[JsonIgnore]publicstringText=>Contents?.ConcatText()??string.Empty;}
    • ResponseId is a unique identifier for the response.

    • ModelId is a unique identifier for the model used for transcription.

    • StartTime and EndTime represents both Timestamps from where the text started and ended relative to the speech audio length.

      i.e: Audio starts with instrumental music for the first 30 seconds before any speech, the transcription should start from 30 seconds forward, same for the end time.

Note

TimeSpan is used to represent the time stamps as it is more intuitive and easier to work with, some services give the time in milliseconds, ticks or other formats.

  • SpeechToTextResponseUpdate, For streaming API, analogous to existing ChatResponseUpdate it provides the speech to text result as multiple chunks of updates, that represents the content generated as well as any important information about the processing.

    publicclassSpeechToTextResponseUpdate{[JsonConstructor]publicSpeechToTextResponseUpdate();publicSpeechToTextResponseUpdate(IList<AIContent>contents);publicSpeechToTextResponseUpdate(string?content);/// <summary>Gets or sets the kind of the generated text update.</summary>publicSpeechToTextResponseUpdateKindKind{get;set;}=SpeechToTextResponseUpdateKind.TextUpdating;/// <summary>Gets or sets the ID of the generated text response of which this update is a part.</summary>publicstring?ResponseId{get;set;}/// <summary>Gets or sets the start time of the text segment associated with this update in relation to the full audio speech length.</summary>publicTimeSpan?StartTime{get;set;}/// <summary>Gets or sets the end time of the text segment associated with this update in relation to the full audio speech length.</summary>publicTimeSpan?EndTime{get;set;}/// <summary>Gets or sets the model ID using in the creation of the speech to text of which this update is a part.</summary>publicstring?ModelId{get;set;}/// <summary>Gets or sets the raw representation of the generated text update from an underlying implementation.</summary>[JsonIgnore]publicobject?RawRepresentation{get;set;}/// <summary>Gets or sets additional properties for the update.</summary>publicAdditionalPropertiesDictionary?AdditionalProperties{get;set;}/// <summary>Gets the text of this speech to text response.</summary>[JsonIgnore]publicstringText=>Contents?.ConcatText()??string.Empty;/// <summary>Gets or sets the generated content items.</summary>[AllowNull]publicIList<AIContent>Contents{get;set;}}
    • ResponseId is a unique identifier for the speech to text response.

    • StartTime and EndTime for the given transcribed chunk represents the timestamp where it starts and ends relative to the audio length.

      i.e: Audio starts with instrumental music for the first 30 seconds before any speech, the transcription chunk will flush with the StartTime from 30 seconds forward until the last word of the chunk which will represent the end time.

Note

TimeSpan is used to represent the time stamps as it is more intuitive and easier to work with, some services give the time in milliseconds, ticks or other formats.

    • Contents is a list of AIContent objects that represent the transcription result. Most use cases will have one TextContent object that can be retrieved from the Text property similarly as a Text in ChatMessage.

    • Kind is a struct similarly to ChatRole

      [JsonConverter(typeof(Converter))]publicreadonlystructSpeechToTextResponseUpdateKind:IEquatable<SpeechToTextResponseUpdateKind>{/// <summary>Gets when the generated text session is opened.</summary>publicstaticSpeechToTextResponseUpdateKindSessionOpen{get;}=new("sessionopen");/// <summary>Gets when a non-blocking error occurs during speech to text updates.</summary>publicstaticSpeechToTextResponseUpdateKindError{get;}=new("error");/// <summary>Gets when the text update is in progress, without waiting for silence.</summary>publicstaticSpeechToTextResponseUpdateKindTextUpdating{get;}=new("textupdating");/// <summary>Gets when the text was generated after small period of silence.</summary>publicstaticSpeechToTextResponseUpdateKindTextUpdated{get;}=new("textupdated");/// <summary>Gets when the generated text session is closed.</summary>publicstaticSpeechToTextResponseUpdateKindSessionClose{get;}=new("sessionclose");// Similar implementation to ChatRole}

      General Update Kinds:

      • SessionOpen - When the transcription session is open.

      • TextUpdating - When the speech to text is in progress, without waiting for silence. (Preferably for UI updates)

        Different apis used different names for this, ie:

        • AssemblyAI: PartialTranscriptReceived
        • Whisper.net: SegmentData
        • Azure AI Speech: RecognizingSpeech
      • TextUpdated - When a speech to text block is complete after a small period of silence.

        Different API names for this, ie:

        • AssemblyAI: FinalTranscriptReceived
        • Whisper.net: N/A (Not supported by the internal API)
        • Azure AI Speech: RecognizedSpeech
      • SessionClose - When the transcription session is closed.

      • Error - When an error occurs during the speech to text process.

        Errors during the streaming can happen, and normally won't block the ongoing process, but can provide more detailed information about the error. For this reason instead of throwing an exception, the error can be provided as part of the ongoing streaming using a dedicated content ErrorContent.

        publicclassErrorContent:AIContent{/// <summary>The error message.</summary>privatestring_message;/// <summary>Initializes a new instance of the <see cref="ErrorContent"/> class with the specified message.</summary>/// <param name="message">The message to store in this content.</param>[JsonConstructor]publicErrorContent(stringmessage){_message=Throw.IfNull(message);}/// <summary>Gets or sets the error message.</summary>publicstringMessage{get=>_message;set=>_message=Throw.IfNull(value);}/// <summary>Gets or sets the error code.</summary>publicstring?ErrorCode{get;set;}/// <summary>Gets or sets the error details.</summary>publicstring?Details{get;set;}}

      Specific API categories:

@rogerbarretorogerbarreto changed the title M.E.AI - Audio transcription abstraction (WIP) - Missing UT + ITM.E.AI.Abstractions - Audio transcription abstraction (WIP) - Missing UT + ITFeb 3, 2025
@rogerbarreto

Copy link
Copy Markdown
ContributorAuthor

@dotnet-policy-service agree company="Microsoft"

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️Found issues‼️

ProjectCoverage TypeExpectedActual
Microsoft.Extensions.AI.AbstractionsLine8369.66 🔻
Microsoft.Extensions.AI.AbstractionsBranch8366.2 🔻
Microsoft.Gen.MetadataExtractorLine9857.35 🔻
Microsoft.Gen.MetadataExtractorBranch9862.5 🔻
Microsoft.Extensions.AI.OllamaLine8078.25 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

ProjectExpectedActual
Microsoft.Extensions.AI8889
Microsoft.Extensions.AI.OpenAI7778

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=938431&view=codecoverage-tab

@RussKieRussKie added the area-ai Microsoft.Extensions.AI libraries label Feb 4, 2025
@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️Found issues‼️

ProjectCoverage TypeExpectedActual
Microsoft.Extensions.Caching.HybridLine8682.77 🔻
Microsoft.Extensions.AI.AbstractionsLine8381.95 🔻
Microsoft.Extensions.AI.AbstractionsBranch8373.8 🔻
Microsoft.Gen.MetadataExtractorLine9857.35 🔻
Microsoft.Gen.MetadataExtractorBranch9862.5 🔻
Microsoft.Extensions.AI.OllamaLine8078.25 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

ProjectExpectedActual
Microsoft.Extensions.AI.OpenAI7778
Microsoft.Extensions.AI8889

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=942860&view=codecoverage-tab

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️Found issues‼️

ProjectCoverage TypeExpectedActual
Microsoft.Extensions.AI.OllamaLine8078.11 🔻
Microsoft.Extensions.Caching.HybridLine8682.92 🔻
Microsoft.Extensions.AI.OpenAILine7774.23 🔻
Microsoft.Extensions.AI.OpenAIBranch7763.08 🔻
Microsoft.Extensions.AI.AbstractionsLine8381.36 🔻
Microsoft.Extensions.AI.AbstractionsBranch8374.51 🔻
Microsoft.Gen.MetadataExtractorLine9857.35 🔻
Microsoft.Gen.MetadataExtractorBranch9862.5 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

ProjectExpectedActual
Microsoft.Extensions.AI.AzureAIInference9192
Microsoft.Extensions.AI8889

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=945523&view=codecoverage-tab

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️Found issues‼️

ProjectCoverage TypeExpectedActual
Microsoft.Extensions.AI.AbstractionsBranch8381.05 🔻
Microsoft.Extensions.Caching.HybridLine8682.77 🔻
Microsoft.Extensions.AI.OllamaLine8078.11 🔻
Microsoft.Extensions.AI.OpenAIBranch7770.56 🔻
Microsoft.Extensions.AILine8880.31 🔻
Microsoft.Extensions.AIBranch8887.64 🔻
Microsoft.Gen.MetadataExtractorLine9857.35 🔻
Microsoft.Gen.MetadataExtractorBranch9862.5 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

ProjectExpectedActual
Microsoft.Extensions.AI.AzureAIInference9192

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=945918&view=codecoverage-tab

@luisquintanilla

Copy link
Copy Markdown
Contributor

cc: @Swimburger for visibility. Feedback is appreciated. Thanks!

@rogerbarretorogerbarreto changed the title M.E.AI.Abstractions - Audio transcription abstraction (WIP) - Missing UT + ITM.E.AI.Abstractions - Speech to Text Abstraction (WIP) - Missing UT + ITFeb 23, 2025
@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️Found issues‼️

ProjectCoverage TypeExpectedActual
Microsoft.Extensions.AI.AbstractionsBranch8278.82 🔻
Microsoft.Extensions.AILine8979.8 🔻
Microsoft.Extensions.AIBranch8986.67 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

ProjectExpectedActual
Microsoft.Gen.MetadataExtractor5770

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=960384&view=codecoverage-tab

@rogerbarreto
rogerbarreto marked this pull request as ready for review February 26, 2025 09:48
@rogerbarreto
rogerbarreto requested review from a team as code ownersFebruary 26, 2025 09:48
@rogerbarretorogerbarreto changed the title M.E.AI.Abstractions - Speech to Text Abstraction (WIP) - Missing UT + ITM.E.AI.Abstractions - Speech to Text AbstractionFeb 26, 2025
Comment threadsrc/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ErrorContent.cs Outdated
Comment threadsrc/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ErrorContent.cs Outdated
Comment threadsrc/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs Outdated
Comment threadsrc/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAISpeechToTextClient.cs Outdated
Comment threadsrc/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAISpeechToTextClient.cs Outdated
Comment threadsrc/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAISpeechToTextClient.cs Outdated

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice. Thanks!

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-aiMicrosoft.Extensions.AI libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@rogerbarreto@dotnet-comment-bot@luisquintanilla@stephentoub@SteveSandersonMS@RussKie