Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,20 @@
<Description>Semantic Kernel connectors for Azure OpenAI. Contains clients for text generation, chat completion, embedding and DALL-E text to image.</Description>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Core\ClientCore.TextToAudio.cs" />
<Compile Remove="Services\AzureOpenAITextToAudioService.cs" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="SemanticKernel.Connectors.AzureOpenAI.UnitTests" />
</ItemGroup>

<ItemGroup>
<None Include="Core\ClientCore.TextToAudio.cs" />
<None Include="Services\AzureOpenAITextToAudioService.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" VersionOverride="2.0.0-beta.2" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.ClientModel;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using OpenAI.Audio;

namespace Microsoft.SemanticKernel.Connectors.AzureOpenAI;

/// <summary>
/// Base class for AI clients that provides common functionality for interacting with Azure OpenAI services.
/// </summary>
internal partial class ClientCore
{
/// <summary>
/// Generates an image with the provided configuration.
/// </summary>
/// <param name="prompt">Prompt to generate the image</param>
/// <param name="executionSettings">Text to Audio execution settings for the prompt</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>Url of the generated image</returns>
internal async Task<IReadOnlyList<AudioContent>> GetAudioContentsAsync(
string prompt,
PromptExecutionSettings? executionSettings,
CancellationToken cancellationToken)
{
Verify.NotNullOrWhiteSpace(prompt);

OpenAITextToAudioExecutionSettings? audioExecutionSettings = OpenAITextToAudioExecutionSettings.FromExecutionSettings(executionSettings);
var (responseFormat, mimeType) = GetGeneratedSpeechFormatAndMimeType(audioExecutionSettings?.ResponseFormat);
SpeechGenerationOptions options = new()
{
ResponseFormat = responseFormat,
Speed = audioExecutionSettings?.Speed,
};

ClientResult<BinaryData> response = await RunRequestAsync(() => this.Client.GetAudioClient(this.ModelId).GenerateSpeechFromTextAsync(prompt, GetGeneratedSpeechVoice(audioExecutionSettings?.Voice), options, cancellationToken)).ConfigureAwait(false);

return [new AudioContent(response.Value.ToArray(), mimeType)];
}

private static GeneratedSpeechVoice GetGeneratedSpeechVoice(string? voice)
=> voice?.ToUpperInvariant() switch
{
"ALLOY" => GeneratedSpeechVoice.Alloy,
"ECHO" => GeneratedSpeechVoice.Echo,
"FABLE" => GeneratedSpeechVoice.Fable,
"ONYX" => GeneratedSpeechVoice.Onyx,
"NOVA" => GeneratedSpeechVoice.Nova,
"SHIMMER" => GeneratedSpeechVoice.Shimmer,
_ => throw new NotSupportedException($"The voice '{voice}' is not supported."),
};

private static (GeneratedSpeechFormat Format, string MimeType) GetGeneratedSpeechFormatAndMimeType(string? format)
=> format?.ToUpperInvariant() switch
{
"WAV" => (GeneratedSpeechFormat.Wav, "audio/wav"),
"MP3" => (GeneratedSpeechFormat.Mp3, "audio/mpeg"),
"OPUS" => (GeneratedSpeechFormat.Opus, "audio/opus"),
"FLAC" => (GeneratedSpeechFormat.Flac, "audio/flac"),
"AAC" => (GeneratedSpeechFormat.Aac, "audio/aac"),
"PCM" => (GeneratedSpeechFormat.Pcm, "audio/l16"),
_ => throw new NotSupportedException($"The format '{format}' is not supported.")
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.Services;
using Microsoft.SemanticKernel.TextToAudio;

namespace Microsoft.SemanticKernel.Connectors.AzureOpenAI;

/// <summary>
/// Azure OpenAI text-to-audio service.
/// </summary>
[Experimental("SKEXP0001")]
public sealed class AzureOpenAITextToAudioService : ITextToAudioService
{
/// <summary>
/// Azure OpenAI text-to-audio client for HTTP operations.
/// </summary>
private readonly AzureOpenAITextToAudioClient _client;

/// <inheritdoc/>
public IReadOnlyDictionary<string, object?> Attributes => this._client.Attributes;

/// <summary>
/// Gets the key used to store the deployment name in the <see cref="IAIService.Attributes"/> dictionary.
/// </summary>
public static string DeploymentNameKey => "DeploymentName";

/// <summary>
/// Creates an instance of the <see cref="AzureOpenAITextToAudioService"/> connector with API key auth.
/// </summary>
/// <param name="deploymentName">Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="apiKey">Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="modelId">Azure OpenAI model id, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource</param>
/// <param name="httpClient">Custom <see cref="HttpClient"/> for HTTP requests.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use for logging. If null, no logging will be performed.</param>
public AzureOpenAITextToAudioService(
string deploymentName,
string endpoint,
string apiKey,
string? modelId = null,
HttpClient? httpClient = null,
ILoggerFactory? loggerFactory = null)
{
this._client = new(deploymentName, endpoint, apiKey, modelId, httpClient, loggerFactory?.CreateLogger(typeof(AzureOpenAITextToAudioService)));

this._client.AddAttribute(DeploymentNameKey, deploymentName);
this._client.AddAttribute(AIServiceExtensions.ModelIdKey, modelId);
}

/// <inheritdoc/>
public Task<IReadOnlyList<AudioContent>> GetAudioContentsAsync(
string text,
PromptExecutionSettings? executionSettings = null,
Kernel? kernel = null,
CancellationToken cancellationToken = default)
=> this._client.GetAudioContentsAsync(text, executionSettings, cancellationToken);
}