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

Azure OpenAI: add capabilities up through 2023-07-01-preview #37539

Merged
merged 16 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
143 changes: 139 additions & 4 deletions sdk/openai/Azure.AI.OpenAI/api/Azure.AI.OpenAI.netstandard2.0.cs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@
<Compile Include="$(AzureCoreSharedSources)AzureResourceProviderNamespaceAttribute.cs" LinkBase="Shared" />
</ItemGroup>

<ItemGroup>
<None Include="..\tsp-location.yaml" Link="tsp-location.yaml" />
</ItemGroup>
</Project>
186 changes: 178 additions & 8 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/AzureOpenAIModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,199 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Azure.Core.Pipeline;

namespace Azure.AI.OpenAI
{
/// <summary> Model factory for models. </summary>
public static partial class AzureOpenAIModelFactory
{
/// <summary>
/// Initializes a new instance of ImageLocation for testing and mocking.
/// </summary>
/// <param name="url">The URL to represent as a download source for an image</param>
/// <param name="pipeline">
/// An optional HttpPipeline to associate with the ImageLocation, needed for the
/// GetStream() convenience method to function.
/// </param>
/// <returns>A new instance of ImageLocation</returns>
/// <exception cref="ArgumentNullException">Thrown if url is null</exception>
public static ImageLocation ImageLocation(Uri url, HttpPipeline pipeline = default)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url));
}

return new ImageLocation(url)
{
ClientPipeline = pipeline,
};
}

/// <summary>
/// Initializes a new instance of ImageGenerations for tests and mocking.
/// </summary>
/// <param name="created">The timestamp to use for image generation origination</param>
/// <param name="data">The collection of ImageLocations to associate with the result</param>
/// <returns>A new instance of ImageGenerations</returns>
public static ImageGenerations ImageGenerations(
DateTimeOffset created,
IEnumerable<ImageLocation> data)
{
return new ImageGenerations(created, data);
}

/// <summary>
/// Initializes a new instance of ContentFilterResult for tests and mocking.
/// </summary>
/// <param name="severity"> The severity level associated with this filter result </param>
/// <param name="isFiltered"> Whether or not the severity level for this filter result should block content </param>
/// <returns> A new instance of ContentFilterResult </returns>
public static ContentFilterResult ContentFilterResult(
ContentFilterSeverity severity = default,
bool isFiltered = default)
{
return new ContentFilterResult(severity, isFiltered);
}

/// <summary>
/// Initializes a new instance of ContentFilterResults for tests and mocking.
/// </summary>
/// <param name="sexualFilterResult"> The content filter result for the 'sexual' category </param>
/// <param name="violenceFilterResult"> The content filter result for the 'violence' category </param>
/// <param name="hateFilterResult"> The content filter result for the 'hate' category </param>
/// <param name="selfHarmFilterResult"> The content filter result for the 'selfHarm' category </param>
/// <returns> A new instance of ContentFilterResults </returns>
public static ContentFilterResults ContentFilterResults(
ContentFilterResult sexualFilterResult = null,
ContentFilterResult violenceFilterResult = null,
ContentFilterResult hateFilterResult = null,
ContentFilterResult selfHarmFilterResult = null)
{
return new ContentFilterResults(sexualFilterResult, violenceFilterResult, hateFilterResult, selfHarmFilterResult);
}
/// <summary>
/// Initializes a new instance of PromptFilterResult for tests and mocking.
/// </summary>
/// <param name="index"> The index of the prompt to associate this filter result with </param>
/// <param name="contentFilterResults"> The content filter results associated with the matching prompt index </param>
/// <returns> A new instance of PromptFilterResult </returns>
public static PromptFilterResult PromptFilterResult(
int index = default,
ContentFilterResults contentFilterResults = null)
{
return new PromptFilterResult(index, contentFilterResults);
}

/// <summary> Initializes a new instance of Choice. </summary>
/// <param name="text"> The generated text for a given completions prompt. </param>
/// <param name="index"> The ordered index associated with this completions choice. </param>
/// <param name="contentFilterResults"> The filter category results associated with this completions choice </param>
/// <param name="logProbabilityModel"> The log probabilities model for tokens associated with this completions choice. </param>
/// <param name="finishReason"> Reason for finishing. </param>
/// <exception cref="ArgumentNullException"> <paramref name="text"/> is null. </exception>
/// <returns> A new <see cref="OpenAI.Choice"/> instance for mocking. </returns>
public static Choice Choice(string text = null, int index = default, CompletionsLogProbabilityModel logProbabilityModel = null, CompletionsFinishReason finishReason = default)
public static Choice Choice(
string text = null,
int index = default,
ContentFilterResults contentFilterResults = null,
CompletionsLogProbabilityModel logProbabilityModel = null,
CompletionsFinishReason finishReason = default)
{
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
// Custom code: remove inappropriate null check of nullable CompletionsLogProbabilityModel
return new Choice(text, index, contentFilterResults, logProbabilityModel, finishReason);
}

/// <summary> Initializes a new instance of ChatChoice. </summary>
/// <param name="message"> The chat message associated with this chat completions choice </param>
/// <param name="index"> The ordered index associated with this chat completions choice. </param>
/// <param name="finishReason"> The reason that this chat completions choice completed its generated. </param>
/// <param name="deltaMessage"> For streamed choices, the internal representation of a 'delta' payload </param>
/// <param name="contentFilterResults"> The category annotations for this chat choice's content filtering </param>
/// <returns> A new <see cref="OpenAI.ChatChoice"/> instance for mocking. </returns>
public static ChatChoice ChatChoice(
ChatMessage message = null,
int index = default,
CompletionsFinishReason finishReason = default,
ChatMessage deltaMessage = null,
ContentFilterResults contentFilterResults = null)
{
return new ChatChoice(message, index, finishReason, deltaMessage, contentFilterResults);
}

/// <summary> Initializes a new instance of ChatCompletions. </summary>
/// <param name="id"> A unique identifier associated with this chat completions response. </param>
/// <param name="created"> The first timestamp associated with generation activity for this completions response. </param>
/// <param name="choices"> The collection of completions choices associated with this completions response. </param>
/// <param name="promptFilterResults"> The filter annotations for this chat completions messages input </param>
/// <param name="usage"> Usage information for tokens processed and generated as part of this completions operation. </param>
/// <returns> A new <see cref="OpenAI.ChatCompletions"/> instance for mocking. </returns>
public static ChatCompletions ChatCompletions(
string id = null,
DateTimeOffset created = default(DateTimeOffset),
IReadOnlyList<ChatChoice> choices = null,
IReadOnlyList<PromptFilterResult> promptFilterResults = null,
CompletionsUsage usage = null)
{
choices ??= new List<ChatChoice>();
usage ??= AIOpenAIModelFactory.CompletionsUsage();

long constrainedUnixTimeInSec = Math.Max(
Math.Min(created.ToUnixTimeSeconds(), int.MaxValue),
int.MinValue);

return new Choice(text, index, logProbabilityModel, finishReason);
return new ChatCompletions(
id ?? string.Empty,
created,
choices,
promptFilterResults,
usage);
}

/// <summary>
/// Initializes a new instance of StreamingChoice for tests and mocking.
/// </summary>
/// <param name="originalBaseChoice"> An underlying Choice for this streaming representation </param>
/// <returns> A new instance of StreamingChoice </returns>
public static StreamingChoice StreamingChoice(Choice originalBaseChoice = null)
{
return new StreamingChoice(originalBaseChoice);
}

/// <summary>
/// Initializes a new instance of StreamingCompletions for tests and mocking.
/// </summary>
/// <param name="baseCompletions"> The non-streaming completions to base this streaming representation on </param>
/// <param name="streamingChoices"> The streaming choices associated with this streaming completions </param>
/// <returns> A new instance of StreamingCompletions </returns>
public static StreamingCompletions StreamingCompletions(
Completions baseCompletions = null,
List<StreamingChoice> streamingChoices = null)
{
return new StreamingCompletions(baseCompletions, streamingChoices);
}

/// <summary>
/// Initializes a new instance of StreamingChatChoice for tests and mocking.
/// </summary>
/// <param name="originalBaseChoice"> An underlying ChatChoice for this streaming representation </param>
/// <returns> A new instance of StreamingChatChoice </returns>
public static StreamingChatChoice StreamingChatChoice(ChatChoice originalBaseChoice = null)
{
return new StreamingChatChoice(originalBaseChoice);
}

/// <summary>
/// Initializes a new instance of StreamingChatCompletions for tests and mocking.
/// </summary>
/// <param name="baseChatCompletions"> The non-streaming completions to base this streaming representation on </param>
/// <param name="streamingChatChoices"> The streaming choices associated with this streaming chat completions </param>
/// <returns> A new instance of StreamingChatCompletions </returns>
public static StreamingChatCompletions StreamingChatCompletions(
ChatCompletions baseChatCompletions = null,
List<StreamingChatChoice> streamingChatChoices = null)
{
return new StreamingChatCompletions(baseChatCompletions, streamingChatChoices);
}
}
}
21 changes: 0 additions & 21 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/ChatCompletions.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#nullable disable

using System;
using System.Collections.Generic;
using System.Text.Json;
using Azure;
Expand All @@ -25,6 +26,38 @@ void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)
writer.WriteObjectValue(item);
}
writer.WriteEndArray();
if (Optional.IsDefined(Functions) && Functions.Count > 0)
{
writer.WritePropertyName("functions"u8);
writer.WriteStartArray();
foreach (var item in Functions)
{
if (item.IsPredefined)
{
throw new ArgumentException(
@"Predefined function definitions such as 'auto' and 'none' cannot be provided as
custom functions. These should only be used to constrain the FunctionCall option.");
}
writer.WriteObjectValue(item);
}
writer.WriteEndArray();
}
if (Optional.IsDefined(FunctionCall))
{
writer.WritePropertyName("function_call");

if (FunctionCall.IsPredefined)
{
writer.WriteStringValue(FunctionCall.Name);
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("name");
writer.WriteStringValue(FunctionCall.Name);
writer.WriteEndObject();
}
}
if (Optional.IsDefined(MaxTokens))
{
if (MaxTokens != null)
Expand Down
27 changes: 27 additions & 0 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/ChatCompletionsOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ public partial class ChatCompletionsOptions
/// <inheritdoc cref="CompletionsOptions.User"/>
public string User { get; set; }

/// <summary> A list of functions the model may generate JSON inputs for. </summary>
public IList<FunctionDefinition> Functions { get; set; }

/// <summary>
/// Controls how the model will use provided Functions.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>
/// Providing a custom <see cref="FunctionDefinition"/> will request that the model limit its
/// completions to function calls for that function.
/// </item>
/// <item>
/// <see cref="FunctionDefinition.Auto"/> represents the default behavior and will allow the model
/// to freely select between issuing a stanard completions response or a call to any provided
/// function.
/// </item>
/// <item>
/// <see cref="FunctionDefinition.None"/> will request that the model only issue standard
/// completions responses, irrespective of provided functions. Note that the function definitions
/// provided may still influence the completions content.
/// </item>
/// </list>
/// </remarks>
public FunctionDefinition FunctionCall { get; set; }

internal IDictionary<string, int> InternalStringKeyedTokenSelectionBiases { get; }

internal string InternalNonAzureModelName { get; set; }
Expand All @@ -64,6 +90,7 @@ public ChatCompletionsOptions(IEnumerable<ChatMessage> messages)
Messages = messages.ToList();
TokenSelectionBiases = new ChangeTrackingDictionary<int, int>();
StopSequences = new ChangeTrackingList<string>();
Functions = new ChangeTrackingList<FunctionDefinition>();
}

/// <inheritdoc cref="ChatCompletionsOptions(IEnumerable{ChatMessage})"/>
Expand Down
10 changes: 7 additions & 3 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/ChatMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ namespace Azure.AI.OpenAI
/// <summary> A single, role-attributed message within a chat completion interaction. </summary>
public partial class ChatMessage
{
/// <summary> Initializes a new instance of ChatMessage. </summary>
public ChatMessage()
{
// Note: this constructor is custom purely to facilitate it having public visibility.
}

/// <summary> Initializes a new instance of ChatMessage. </summary>
/// <param name="role"> The role associated with this message payload. </param>
/// <param name="content"> The text associated with this message payload. </param>
public ChatMessage(ChatRole role, string content)
public ChatMessage(ChatRole role)
{
// Note: this constructor is custom purely to facilitate it having public visibility.
Role = role;
Content = content;
}
}
}
14 changes: 0 additions & 14 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/ChatRole.cs

This file was deleted.

21 changes: 0 additions & 21 deletions sdk/openai/Azure.AI.OpenAI/src/Custom/Completions.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ namespace Azure.AI.OpenAI
/// <summary> Representation of the manner in which a completions response concluded. </summary>
public readonly partial struct CompletionsFinishReason
{
private const string StoppedValue = "stop";
private const string TokenLimitReachedValue = "length";
private const string ContentFilteredValue = "content_filter";

/// <summary> Initializes a new instance of <see cref="CompletionsFinishReason"/>. </summary>
public CompletionsFinishReason(string value)
{
Expand Down
Loading