From df95c5305ec31ab71963749676b84b39ba56fca1 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Apr 2024 14:09:58 -0700 Subject: [PATCH 01/32] Add Azure client --- .../@azure-tools/typespec-csharp/OpenAI.sln | 40 ++++++++----------- .../app/DemoApp/DemoApp.csproj | 10 +++++ .../typespec-csharp/app/DemoApp/Program.cs | 2 + .../azoai/AzureOpenAI/AzureOpenAI.csproj | 13 ++++++ .../azoai/AzureOpenAI/AzureOpenAIClient.cs | 8 ++++ 5 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj create mode 100644 tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln b/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln index 5d1edd8c3..60d92a3a4 100644 --- a/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln +++ b/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln @@ -1,10 +1,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34701.34 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAI", "src\OpenAI.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI", "src\OpenAI.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAI.Tests", "tests\OpenAI.Tests.csproj", "{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI.Tests", "tests\OpenAI.Tests.csproj", "{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoApp", "app\DemoApp\DemoApp.csproj", "{79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureOpenAI", "azoai\AzureOpenAI\AzureOpenAI.csproj", "{FB61EB83-7171-48F4-9423-E64B99BDAA5A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -12,26 +16,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Release|Any CPU.Build.0 = Release|Any CPU - {8E9A77AC-792A-4432-8320-ACFD46730401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8E9A77AC-792A-4432-8320-ACFD46730401}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8E9A77AC-792A-4432-8320-ACFD46730401}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8E9A77AC-792A-4432-8320-ACFD46730401}.Release|Any CPU.Build.0 = Release|Any CPU - {A4241C1F-A53D-474C-9E4E-075054407E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4241C1F-A53D-474C-9E4E-075054407E74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4241C1F-A53D-474C-9E4E-075054407E74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4241C1F-A53D-474C-9E4E-075054407E74}.Release|Any CPU.Build.0 = Release|Any CPU - {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Release|Any CPU.Build.0 = Release|Any CPU - {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Release|Any CPU.Build.0 = Release|Any CPU {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.Build.0 = Debug|Any CPU {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -40,6 +24,14 @@ Global {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.Build.0 = Release|Any CPU + {79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}.Release|Any CPU.Build.0 = Release|Any CPU + {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj new file mode 100644 index 000000000..74abf5c97 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs new file mode 100644 index 000000000..3751555cb --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj new file mode 100644 index 000000000..8fe75562b --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs new file mode 100644 index 000000000..181f249e6 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs @@ -0,0 +1,8 @@ +using OpenAI; + +namespace AzureOpenAI; + +public class AzureOpenAIClient : OpenAIClient +{ + +} From 60575d9e54602d1323f87427a9b82f01d1d13f1d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Apr 2024 16:09:03 -0700 Subject: [PATCH 02/32] updates --- .../app/DemoApp/DemoApp.csproj | 4 ++ .../typespec-csharp/app/DemoApp/Program.cs | 41 ++++++++++++++++++- .../azoai/AzureOpenAI/AzureOpenAIClient.cs | 6 ++- .../AzureOpenAI/AzureOpenAIClientOptions.cs | 7 ++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj index 74abf5c97..48462fbc6 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index 3751555cb..288731362 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -1,2 +1,41 @@ -// See https://aka.ms/new-console-template for more information +using OpenAI; +using OpenAI.Models; +using System.ClientModel; + Console.WriteLine("Hello, World!"); + +//Uri endpoint = new("https://annelo-openai-01.openai.azure.com/"); +//string apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!; + +string apiKey = Environment.GetEnvironmentVariable("OPENAI_KEY")!; + +OpenAIClient client = new OpenAIClient(new ApiKeyCredential(apiKey)); +Chat chatClient = client.GetChatClient(); + +List messages = new List(); +var message = new +{ + role = "user", + content = "running over the same old ground" +}; +messages.Add(BinaryData.FromObjectAsJson(message)); + +CreateChatCompletionRequest request = new CreateChatCompletionRequest(messages, CreateChatCompletionRequestModel.Gpt35Turbo); + +ClientResult result = chatClient.CreateChatCompletion(request); + +//AzureOpenAIClient client = new AzureOpenAIClient(endpoint, new ApiKeyCredential(apiKey)); +//Chat chatClient = client.GetChatClient("gpt-35-turbo-instruct"); + +// TODO: Here. What is the convenience model story? +// Should the Azure client return Response? +//ClientResult result = await chatClient.CompleteChatAsync() + +// TODO: How do we reconcile returning Response from Azure client protocol methods +// and ClientResult from unbranded client protocol methods? + +// TODO: MRW story - input model and output models too. + +//Client + +//OpenAIClient client = new() \ No newline at end of file diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs index 181f249e6..8bba2e9e7 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs @@ -1,8 +1,12 @@ using OpenAI; +using System.ClientModel; namespace AzureOpenAI; public class AzureOpenAIClient : OpenAIClient { - + public AzureOpenAIClient(Uri endpoint, ApiKeyCredential credential, AzureOpenAIClientOptions? options = default) + : base(endpoint, credential, options) + { + } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs new file mode 100644 index 000000000..70ba5197d --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs @@ -0,0 +1,7 @@ +using OpenAI; + +namespace AzureOpenAI; + +public class AzureOpenAIClientOptions : OpenAIClientOptions +{ +} From 45b680aab02431f05031d42cf3134c488dc29f5d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Apr 2024 16:23:45 -0700 Subject: [PATCH 03/32] updates --- .../azoai/AzureOpenAI/AzureChatClient.cs | 42 ++++ .../azoai/AzureOpenAI/AzureOpenAIClient.cs | 5 + .../Generated/Internal/ClientUriBuilder.cs | 210 ++++++++++++++++++ .../typespec-csharp/src/Generated/Chat.cs | 6 +- .../src/Generated/OpenAIClient.cs | 3 + 5 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs new file mode 100644 index 000000000..a78d37699 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -0,0 +1,42 @@ +using OpenAI; +using System.ClientModel; +using System.ClientModel.Primitives; + +namespace AzureOpenAI; + +public class AzureChatClient : Chat +{ + internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, Uri endpoint) + : base(pipeline, credential, endpoint) + { + } + + protected override PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) + { + var message = Pipeline.CreateMessage(); + message.ResponseClassifier = PipelineMessageClassifier200; + + var request = message.Request; + request.Method = "POST"; + + var uri = new ClientUriBuilder(); + uri.Reset(Endpoint); + uri.AppendPath("/chat/completions", false); + request.Uri = uri.ToUri(); + + request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Content-Type", "application/json"); + + request.Content = content; + + if (context != null) + { + message.Apply(context); + } + + return message; + } + + private static PipelineMessageClassifier? _pipelineMessageClassifier200; + private static PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs index 8bba2e9e7..5f8175a8e 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs @@ -9,4 +9,9 @@ public AzureOpenAIClient(Uri endpoint, ApiKeyCredential credential, AzureOpenAIC : base(endpoint, credential, options) { } + + public override Chat GetChatClient() + { + return new AzureChatClient(Pipeline, KeyCredential, Endpoint); + } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs new file mode 100644 index 000000000..b24daf525 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs @@ -0,0 +1,210 @@ +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenAI +{ + internal class ClientUriBuilder + { + private UriBuilder _uriBuilder; + private StringBuilder _pathBuilder; + private StringBuilder _queryBuilder; + private const char PathSeparator = '/'; + + public ClientUriBuilder() + { + } + + private UriBuilder UriBuilder => _uriBuilder ??= new UriBuilder(); + + private StringBuilder PathBuilder => _pathBuilder ??= new StringBuilder(UriBuilder.Path); + + private StringBuilder QueryBuilder => _queryBuilder ??= new StringBuilder(UriBuilder.Query); + + public void Reset(Uri uri) + { + _uriBuilder = new UriBuilder(uri); + _pathBuilder = new StringBuilder(UriBuilder.Path); + _queryBuilder = new StringBuilder(UriBuilder.Query); + } + + public void AppendPath(string value, bool escape) + { + Argument.AssertNotNullOrWhiteSpace(value, nameof(value)); + + if (escape) + { + value = Uri.EscapeDataString(value); + } + + if (value[0] == PathSeparator) + { + value = value.Substring(1); + } + + PathBuilder.Append(value); + UriBuilder.Path = PathBuilder.ToString(); + } + + public void AppendPath(bool value, bool escape = false) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendPath(float value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendPath(double value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendPath(int value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendPath(byte[] value, string format, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } + + public void AppendPath(IEnumerable value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendPath(DateTimeOffset value, string format, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } + + public void AppendPath(TimeSpan value, string format, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } + + public void AppendPath(Guid value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendPath(long value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, string value, bool escape) + { + Argument.AssertNotNullOrWhiteSpace(name, nameof(name)); + Argument.AssertNotNullOrWhiteSpace(value, nameof(value)); + + if (QueryBuilder.Length == 0) + { + QueryBuilder.Append('?'); + } + else + { + QueryBuilder.Append('&'); + } + + if (escape) + { + value = Uri.EscapeDataString(value); + } + + QueryBuilder.Append(name); + QueryBuilder.Append('='); + QueryBuilder.Append(value); + } + + public void AppendQuery(string name, bool value, bool escape = false) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, float value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, DateTimeOffset value, string format, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } + + public void AppendQuery(string name, TimeSpan value, string format, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } + + public void AppendQuery(string name, double value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, decimal value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, int value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, long value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, TimeSpan value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQuery(string name, byte[] value, string format, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } + + public void AppendQuery(string name, Guid value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQueryDelimited(string name, IEnumerable value, string delimiter, bool escape = true) + { + var stringValues = value.Select(v => ModelSerializationExtensions.TypeFormatters.ConvertToString(v)); + AppendQuery(name, string.Join(delimiter, stringValues), escape); + } + + public void AppendQueryDelimited(string name, IEnumerable value, string delimiter, string format, bool escape = true) + { + var stringValues = value.Select(v => ModelSerializationExtensions.TypeFormatters.ConvertToString(v, format)); + AppendQuery(name, string.Join(delimiter, stringValues), escape); + } + + public Uri ToUri() + { + if (_pathBuilder != null) + { + UriBuilder.Path = _pathBuilder.ToString(); + } + + if (_queryBuilder != null) + { + UriBuilder.Query = _queryBuilder.ToString(); + } + + return UriBuilder.Uri; + } + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs index b42111af1..92df1745c 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs @@ -21,6 +21,8 @@ public partial class Chat private readonly ClientPipeline _pipeline; private readonly Uri _endpoint; + protected Uri Endpoint => _endpoint; + /// The HTTP pipeline for sending and receiving REST requests and responses. public virtual ClientPipeline Pipeline => _pipeline; @@ -33,7 +35,7 @@ protected Chat() /// The HTTP pipeline for sending and receiving REST requests and responses. /// The key credential to copy. /// OpenAI Endpoint. - internal Chat(ClientPipeline pipeline, ApiKeyCredential keyCredential, Uri endpoint) + protected internal Chat(ClientPipeline pipeline, ApiKeyCredential keyCredential, Uri endpoint) { _pipeline = pipeline; _keyCredential = keyCredential; @@ -124,7 +126,7 @@ public virtual ClientResult CreateChatCompletion(BinaryContent content, RequestO return ClientResult.FromResponse(_pipeline.ProcessMessage(message, context)); } - internal PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) + protected virtual PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) { var message = _pipeline.CreateMessage(); if (context != null) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs index c56780241..ed5d58e32 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs @@ -13,6 +13,9 @@ namespace OpenAI /// The OpenAI service client. public partial class OpenAIClient { + protected ApiKeyCredential KeyCredential => _keyCredential; + protected Uri Endpoint => _endpoint; + private const string AuthorizationHeader = "Authorization"; private readonly ApiKeyCredential _keyCredential; private const string AuthorizationApiKeyPrefix = "Bearer"; From 51e2eecc012b4b9b2da19a742a155a8774a09a53 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Apr 2024 16:55:37 -0700 Subject: [PATCH 04/32] beginning work on extension methods --- .../azoai/AzureOpenAI/AzureOpenAI.csproj | 17 +- .../Generated/Internal/Argument.cs | 125 ++++++ .../Internal/ChangeTrackingDictionary.cs | 163 ++++++++ .../Generated/Internal/ChangeTrackingList.cs | 149 +++++++ .../Generated/Internal/ClientUriBuilder.cs | 301 +++++++------- .../Internal/ModelSerializationExtensions.cs | 388 ++++++++++++++++++ .../Generated/Internal/Optional.cs | 47 +++ ...hatExtensionConfiguration.Serialization.cs | 125 ++++++ .../Models/AzureChatExtensionConfiguration.cs | 76 ++++ .../Models/AzureChatExtensionType.cs | 63 +++ .../AzureCreateChatCompletionRequest.cs | 18 + .../Models/AzureModelExtensions.cs | 21 + ...hatExtensionConfiguration.Serialization.cs | 138 +++++++ .../AzureSearchChatExtensionConfiguration.cs | 50 +++ ...chChatExtensionParameters.Serialization.cs | 290 +++++++++++++ .../AzureSearchChatExtensionParameters.cs | 146 +++++++ ...hatExtensionConfiguration.Serialization.cs | 135 ++++++ .../UnknownAzureChatExtensionConfiguration.cs | 30 ++ .../Models/CreateChatCompletionRequest.cs | 4 + 19 files changed, 2127 insertions(+), 159 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Argument.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingDictionary.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingList.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ModelSerializationExtensions.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Optional.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionType.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj index 8fe75562b..467bc0f2a 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj @@ -1,13 +1,14 @@ - - net6.0 - enable - enable - + + net6.0 + enable + enable + latest + - - - + + + diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Argument.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Argument.cs new file mode 100644 index 000000000..b39f2be32 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Argument.cs @@ -0,0 +1,125 @@ +// + +#nullable disable + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace AzureOpenAI; + +internal static class Argument +{ + public static void AssertNotNull(T value, string name) + { + if (value is null) + { + throw new ArgumentNullException(name); + } + } + + public static void AssertNotNull(T? value, string name) + where T : struct + { + if (!value.HasValue) + { + throw new ArgumentNullException(name); + } + } + + public static void AssertNotNullOrEmpty(IEnumerable value, string name) + { + if (value is null) + { + throw new ArgumentNullException(name); + } + if (value is ICollection collectionOfT && collectionOfT.Count == 0) + { + throw new ArgumentException("Value cannot be an empty collection.", name); + } + if (value is ICollection collection && collection.Count == 0) + { + throw new ArgumentException("Value cannot be an empty collection.", name); + } + using IEnumerator e = value.GetEnumerator(); + if (!e.MoveNext()) + { + throw new ArgumentException("Value cannot be an empty collection.", name); + } + } + + public static void AssertNotNullOrEmpty(string value, string name) + { + if (value is null) + { + throw new ArgumentNullException(name); + } + if (value.Length == 0) + { + throw new ArgumentException("Value cannot be an empty string.", name); + } + } + + public static void AssertNotNullOrWhiteSpace(string value, string name) + { + if (value is null) + { + throw new ArgumentNullException(name); + } + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("Value cannot be empty or contain only white-space characters.", name); + } + } + + public static void AssertNotDefault(ref T value, string name) + where T : struct, IEquatable + { + if (value.Equals(default)) + { + throw new ArgumentException("Value cannot be empty.", name); + } + } + + public static void AssertInRange(T value, T minimum, T maximum, string name) + where T : notnull, IComparable + { + if (minimum.CompareTo(value) > 0) + { + throw new ArgumentOutOfRangeException(name, "Value is less than the minimum allowed."); + } + if (maximum.CompareTo(value) < 0) + { + throw new ArgumentOutOfRangeException(name, "Value is greater than the maximum allowed."); + } + } + + public static void AssertEnumDefined(Type enumType, object value, string name) + { + if (!Enum.IsDefined(enumType, value)) + { + throw new ArgumentException($"Value not defined for {enumType.FullName}.", name); + } + } + + public static T CheckNotNull(T value, string name) + where T : class + { + AssertNotNull(value, name); + return value; + } + + public static string CheckNotNullOrEmpty(string value, string name) + { + AssertNotNullOrEmpty(value, name); + return value; + } + + public static void AssertNull(T value, string name, string message = null) + { + if (value != null) + { + throw new ArgumentException(message ?? "Value must be null.", name); + } + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingDictionary.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingDictionary.cs new file mode 100644 index 000000000..b054e615b --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingDictionary.cs @@ -0,0 +1,163 @@ +// + +#nullable disable + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace AzureOpenAI; + +internal class ChangeTrackingDictionary : IDictionary, IReadOnlyDictionary where TKey : notnull +{ + private IDictionary _innerDictionary; + + public ChangeTrackingDictionary() + { + } + + public ChangeTrackingDictionary(IDictionary dictionary) + { + if (dictionary == null) + { + return; + } + _innerDictionary = new Dictionary(dictionary); + } + + public ChangeTrackingDictionary(IReadOnlyDictionary dictionary) + { + if (dictionary == null) + { + return; + } + _innerDictionary = new Dictionary(); + foreach (var pair in dictionary) + { + _innerDictionary.Add(pair); + } + } + + public bool IsUndefined => _innerDictionary == null; + + public int Count => IsUndefined ? 0 : EnsureDictionary().Count; + + public bool IsReadOnly => IsUndefined ? false : EnsureDictionary().IsReadOnly; + + public ICollection Keys => IsUndefined ? Array.Empty() : EnsureDictionary().Keys; + + public ICollection Values => IsUndefined ? Array.Empty() : EnsureDictionary().Values; + + public TValue this[TKey key] + { + get + { + if (IsUndefined) + { + throw new KeyNotFoundException(nameof(key)); + } + return EnsureDictionary()[key]; + } + set + { + EnsureDictionary()[key] = value; + } + } + + IEnumerable IReadOnlyDictionary.Keys => Keys; + + IEnumerable IReadOnlyDictionary.Values => Values; + + public IEnumerator> GetEnumerator() + { + if (IsUndefined) + { + IEnumerator> enumerateEmpty() + { + yield break; + } + return enumerateEmpty(); + } + return EnsureDictionary().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(KeyValuePair item) + { + EnsureDictionary().Add(item); + } + + public void Clear() + { + EnsureDictionary().Clear(); + } + + public bool Contains(KeyValuePair item) + { + if (IsUndefined) + { + return false; + } + return EnsureDictionary().Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int index) + { + if (IsUndefined) + { + return; + } + EnsureDictionary().CopyTo(array, index); + } + + public bool Remove(KeyValuePair item) + { + if (IsUndefined) + { + return false; + } + return EnsureDictionary().Remove(item); + } + + public void Add(TKey key, TValue value) + { + EnsureDictionary().Add(key, value); + } + + public bool ContainsKey(TKey key) + { + if (IsUndefined) + { + return false; + } + return EnsureDictionary().ContainsKey(key); + } + + public bool Remove(TKey key) + { + if (IsUndefined) + { + return false; + } + return EnsureDictionary().Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + if (IsUndefined) + { + value = default; + return false; + } + return EnsureDictionary().TryGetValue(key, out value); + } + + public IDictionary EnsureDictionary() + { + return _innerDictionary ??= new Dictionary(); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingList.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingList.cs new file mode 100644 index 000000000..a2ff13738 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ChangeTrackingList.cs @@ -0,0 +1,149 @@ +// + +#nullable disable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace AzureOpenAI; + +internal class ChangeTrackingList : IList, IReadOnlyList +{ + private IList _innerList; + + public ChangeTrackingList() + { + } + + public ChangeTrackingList(IList innerList) + { + if (innerList != null) + { + _innerList = innerList; + } + } + + public ChangeTrackingList(IReadOnlyList innerList) + { + if (innerList != null) + { + _innerList = innerList.ToList(); + } + } + + public bool IsUndefined => _innerList == null; + + public int Count => IsUndefined ? 0 : EnsureList().Count; + + public bool IsReadOnly => IsUndefined ? false : EnsureList().IsReadOnly; + + public T this[int index] + { + get + { + if (IsUndefined) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return EnsureList()[index]; + } + set + { + if (IsUndefined) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + EnsureList()[index] = value; + } + } + + public void Reset() + { + _innerList = null; + } + + public IEnumerator GetEnumerator() + { + if (IsUndefined) + { + IEnumerator enumerateEmpty() + { + yield break; + } + return enumerateEmpty(); + } + return EnsureList().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(T item) + { + EnsureList().Add(item); + } + + public void Clear() + { + EnsureList().Clear(); + } + + public bool Contains(T item) + { + if (IsUndefined) + { + return false; + } + return EnsureList().Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (IsUndefined) + { + return; + } + EnsureList().CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + if (IsUndefined) + { + return false; + } + return EnsureList().Remove(item); + } + + public int IndexOf(T item) + { + if (IsUndefined) + { + return -1; + } + return EnsureList().IndexOf(item); + } + + public void Insert(int index, T item) + { + EnsureList().Insert(index, item); + } + + public void RemoveAt(int index) + { + if (IsUndefined) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + EnsureList().RemoveAt(index); + } + + public IList EnsureList() + { + return _innerList ??= new List(); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs index b24daf525..782d0be04 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientUriBuilder.cs @@ -7,204 +7,203 @@ using System.Linq; using System.Text; -namespace OpenAI +namespace AzureOpenAI; + +internal class ClientUriBuilder { - internal class ClientUriBuilder + private UriBuilder _uriBuilder; + private StringBuilder _pathBuilder; + private StringBuilder _queryBuilder; + private const char PathSeparator = '/'; + + public ClientUriBuilder() { - private UriBuilder _uriBuilder; - private StringBuilder _pathBuilder; - private StringBuilder _queryBuilder; - private const char PathSeparator = '/'; + } - public ClientUriBuilder() - { - } + private UriBuilder UriBuilder => _uriBuilder ??= new UriBuilder(); + + private StringBuilder PathBuilder => _pathBuilder ??= new StringBuilder(UriBuilder.Path); - private UriBuilder UriBuilder => _uriBuilder ??= new UriBuilder(); + private StringBuilder QueryBuilder => _queryBuilder ??= new StringBuilder(UriBuilder.Query); - private StringBuilder PathBuilder => _pathBuilder ??= new StringBuilder(UriBuilder.Path); + public void Reset(Uri uri) + { + _uriBuilder = new UriBuilder(uri); + _pathBuilder = new StringBuilder(UriBuilder.Path); + _queryBuilder = new StringBuilder(UriBuilder.Query); + } - private StringBuilder QueryBuilder => _queryBuilder ??= new StringBuilder(UriBuilder.Query); + public void AppendPath(string value, bool escape) + { + Argument.AssertNotNullOrWhiteSpace(value, nameof(value)); - public void Reset(Uri uri) + if (escape) { - _uriBuilder = new UriBuilder(uri); - _pathBuilder = new StringBuilder(UriBuilder.Path); - _queryBuilder = new StringBuilder(UriBuilder.Query); + value = Uri.EscapeDataString(value); } - public void AppendPath(string value, bool escape) + if (value[0] == PathSeparator) { - Argument.AssertNotNullOrWhiteSpace(value, nameof(value)); + value = value.Substring(1); + } - if (escape) - { - value = Uri.EscapeDataString(value); - } + PathBuilder.Append(value); + UriBuilder.Path = PathBuilder.ToString(); + } - if (value[0] == PathSeparator) - { - value = value.Substring(1); - } + public void AppendPath(bool value, bool escape = false) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - PathBuilder.Append(value); - UriBuilder.Path = PathBuilder.ToString(); - } + public void AppendPath(float value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendPath(bool value, bool escape = false) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendPath(double value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendPath(float value, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendPath(int value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendPath(double value, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendPath(byte[] value, string format, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } - public void AppendPath(int value, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendPath(IEnumerable value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendPath(byte[] value, string format, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); - } + public void AppendPath(DateTimeOffset value, string format, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } - public void AppendPath(IEnumerable value, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendPath(TimeSpan value, string format, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } - public void AppendPath(DateTimeOffset value, string format, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); - } + public void AppendPath(Guid value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendPath(TimeSpan value, string format, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); - } + public void AppendPath(long value, bool escape = true) + { + AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendPath(Guid value, bool escape = true) - { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, string value, bool escape) + { + Argument.AssertNotNullOrWhiteSpace(name, nameof(name)); + Argument.AssertNotNullOrWhiteSpace(value, nameof(value)); - public void AppendPath(long value, bool escape = true) + if (QueryBuilder.Length == 0) { - AppendPath(ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + QueryBuilder.Append('?'); } - - public void AppendQuery(string name, string value, bool escape) + else { - Argument.AssertNotNullOrWhiteSpace(name, nameof(name)); - Argument.AssertNotNullOrWhiteSpace(value, nameof(value)); - - if (QueryBuilder.Length == 0) - { - QueryBuilder.Append('?'); - } - else - { - QueryBuilder.Append('&'); - } - - if (escape) - { - value = Uri.EscapeDataString(value); - } - - QueryBuilder.Append(name); - QueryBuilder.Append('='); - QueryBuilder.Append(value); + QueryBuilder.Append('&'); } - public void AppendQuery(string name, bool value, bool escape = false) + if (escape) { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + value = Uri.EscapeDataString(value); } - public void AppendQuery(string name, float value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + QueryBuilder.Append(name); + QueryBuilder.Append('='); + QueryBuilder.Append(value); + } - public void AppendQuery(string name, DateTimeOffset value, string format, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); - } + public void AppendQuery(string name, bool value, bool escape = false) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQuery(string name, TimeSpan value, string format, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); - } + public void AppendQuery(string name, float value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQuery(string name, double value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, DateTimeOffset value, string format, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } - public void AppendQuery(string name, decimal value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, TimeSpan value, string format, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } - public void AppendQuery(string name, int value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, double value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQuery(string name, long value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, decimal value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQuery(string name, TimeSpan value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, int value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQuery(string name, byte[] value, string format, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); - } + public void AppendQuery(string name, long value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQuery(string name, Guid value, bool escape = true) - { - AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); - } + public void AppendQuery(string name, TimeSpan value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } - public void AppendQueryDelimited(string name, IEnumerable value, string delimiter, bool escape = true) - { - var stringValues = value.Select(v => ModelSerializationExtensions.TypeFormatters.ConvertToString(v)); - AppendQuery(name, string.Join(delimiter, stringValues), escape); - } + public void AppendQuery(string name, byte[] value, string format, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value, format), escape); + } - public void AppendQueryDelimited(string name, IEnumerable value, string delimiter, string format, bool escape = true) + public void AppendQuery(string name, Guid value, bool escape = true) + { + AppendQuery(name, ModelSerializationExtensions.TypeFormatters.ConvertToString(value), escape); + } + + public void AppendQueryDelimited(string name, IEnumerable value, string delimiter, bool escape = true) + { + var stringValues = value.Select(v => ModelSerializationExtensions.TypeFormatters.ConvertToString(v)); + AppendQuery(name, string.Join(delimiter, stringValues), escape); + } + + public void AppendQueryDelimited(string name, IEnumerable value, string delimiter, string format, bool escape = true) + { + var stringValues = value.Select(v => ModelSerializationExtensions.TypeFormatters.ConvertToString(v, format)); + AppendQuery(name, string.Join(delimiter, stringValues), escape); + } + + public Uri ToUri() + { + if (_pathBuilder != null) { - var stringValues = value.Select(v => ModelSerializationExtensions.TypeFormatters.ConvertToString(v, format)); - AppendQuery(name, string.Join(delimiter, stringValues), escape); + UriBuilder.Path = _pathBuilder.ToString(); } - public Uri ToUri() + if (_queryBuilder != null) { - if (_pathBuilder != null) - { - UriBuilder.Path = _pathBuilder.ToString(); - } - - if (_queryBuilder != null) - { - UriBuilder.Query = _queryBuilder.ToString(); - } - - return UriBuilder.Uri; + UriBuilder.Query = _queryBuilder.ToString(); } + + return UriBuilder.Uri; } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ModelSerializationExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ModelSerializationExtensions.cs new file mode 100644 index 000000000..e4edf1684 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ModelSerializationExtensions.cs @@ -0,0 +1,388 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text.Json; +using System.Xml; + +namespace AzureOpenAI; + +internal static class ModelSerializationExtensions +{ + public static object GetObject(this JsonElement element) + { + switch (element.ValueKind) + { + case JsonValueKind.String: + return element.GetString(); + case JsonValueKind.Number: + if (element.TryGetInt32(out int intValue)) + { + return intValue; + } + if (element.TryGetInt64(out long longValue)) + { + return longValue; + } + return element.GetDouble(); + case JsonValueKind.True: + return true; + case JsonValueKind.False: + return false; + case JsonValueKind.Undefined: + case JsonValueKind.Null: + return null; + case JsonValueKind.Object: + var dictionary = new Dictionary(); + foreach (var jsonProperty in element.EnumerateObject()) + { + dictionary.Add(jsonProperty.Name, jsonProperty.Value.GetObject()); + } + return dictionary; + case JsonValueKind.Array: + var list = new List(); + foreach (var item in element.EnumerateArray()) + { + list.Add(item.GetObject()); + } + return list.ToArray(); + default: + throw new NotSupportedException($"Not supported value kind {element.ValueKind}"); + } + } + + public static byte[] GetBytesFromBase64(this JsonElement element, string format) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + + return format switch + { + "U" => TypeFormatters.FromBase64UrlString(element.GetRequiredString()), + "D" => element.GetBytesFromBase64(), + _ => throw new ArgumentException($"Format is not supported: '{format}'", nameof(format)) + }; + } + + public static DateTimeOffset GetDateTimeOffset(this JsonElement element, string format) => format switch + { + "U" when element.ValueKind == JsonValueKind.Number => DateTimeOffset.FromUnixTimeSeconds(element.GetInt64()), + _ => TypeFormatters.ParseDateTimeOffset(element.GetString(), format) + }; + + public static TimeSpan GetTimeSpan(this JsonElement element, string format) => TypeFormatters.ParseTimeSpan(element.GetString(), format); + + public static char GetChar(this JsonElement element) + { + if (element.ValueKind == JsonValueKind.String) + { + var text = element.GetString(); + if (text == null || text.Length != 1) + { + throw new NotSupportedException($"Cannot convert \"{text}\" to a char"); + } + return text[0]; + } + else + { + throw new NotSupportedException($"Cannot convert {element.ValueKind} to a char"); + } + } + + [Conditional("DEBUG")] + public static void ThrowNonNullablePropertyIsNull(this JsonProperty property) + { + throw new JsonException($"A property '{property.Name}' defined as non-nullable but received as null from the service. This exception only happens in DEBUG builds of the library and would be ignored in the release build"); + } + + public static string GetRequiredString(this JsonElement element) + { + var value = element.GetString(); + if (value == null) + { + throw new InvalidOperationException($"The requested operation requires an element of type 'String', but the target element has type '{element.ValueKind}'."); + } + return value; + } + + public static void WriteStringValue(this Utf8JsonWriter writer, DateTimeOffset value, string format) + { + writer.WriteStringValue(TypeFormatters.ToString(value, format)); + } + + public static void WriteStringValue(this Utf8JsonWriter writer, DateTime value, string format) + { + writer.WriteStringValue(TypeFormatters.ToString(value, format)); + } + + public static void WriteStringValue(this Utf8JsonWriter writer, TimeSpan value, string format) + { + writer.WriteStringValue(TypeFormatters.ToString(value, format)); + } + + public static void WriteStringValue(this Utf8JsonWriter writer, char value) + { + writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); + } + + public static void WriteBase64StringValue(this Utf8JsonWriter writer, byte[] value, string format) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + switch (format) + { + case "U": + writer.WriteStringValue(TypeFormatters.ToBase64UrlString(value)); + break; + case "D": + writer.WriteBase64StringValue(value); + break; + default: + throw new ArgumentException($"Format is not supported: '{format}'", nameof(format)); + } + } + + public static void WriteNumberValue(this Utf8JsonWriter writer, DateTimeOffset value, string format) + { + if (format != "U") + { + throw new ArgumentOutOfRangeException(nameof(format), "Only 'U' format is supported when writing a DateTimeOffset as a Number."); + } + writer.WriteNumberValue(value.ToUnixTimeSeconds()); + } + + public static void WriteObjectValue(this Utf8JsonWriter writer, object value, ModelReaderWriterOptions options = null) + { + switch (value) + { + case null: + writer.WriteNullValue(); + break; + case IJsonModel jsonModel: + jsonModel.Write(writer, options ?? new ModelReaderWriterOptions("W")); + break; + case byte[] bytes: + writer.WriteBase64StringValue(bytes); + break; + case BinaryData bytes0: + writer.WriteBase64StringValue(bytes0); + break; + case JsonElement json: + json.WriteTo(writer); + break; + case int i: + writer.WriteNumberValue(i); + break; + case decimal d: + writer.WriteNumberValue(d); + break; + case double d0: + if (double.IsNaN(d0)) + { + writer.WriteStringValue("NaN"); + } + else + { + writer.WriteNumberValue(d0); + } + break; + case float f: + writer.WriteNumberValue(f); + break; + case long l: + writer.WriteNumberValue(l); + break; + case string s: + writer.WriteStringValue(s); + break; + case bool b: + writer.WriteBooleanValue(b); + break; + case Guid g: + writer.WriteStringValue(g); + break; + case DateTimeOffset dateTimeOffset: + writer.WriteStringValue(dateTimeOffset, "O"); + break; + case DateTime dateTime: + writer.WriteStringValue(dateTime, "O"); + break; + case IEnumerable> enumerable: + writer.WriteStartObject(); + foreach (var pair in enumerable) + { + writer.WritePropertyName(pair.Key); + writer.WriteObjectValue(pair.Value, options); + } + writer.WriteEndObject(); + break; + case IEnumerable objectEnumerable: + writer.WriteStartArray(); + foreach (var item in objectEnumerable) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + break; + case TimeSpan timeSpan: + writer.WriteStringValue(timeSpan, "P"); + break; + default: + throw new NotSupportedException($"Not supported type {value.GetType()}"); + } + } + + public static void WriteObjectValue(this Utf8JsonWriter writer, object value, ModelReaderWriterOptions options = null) + { + writer.WriteObjectValue(value, options); + } + + internal static class TypeFormatters + { + private const string RoundtripZFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ"; + public const string DefaultNumberFormat = "G"; + + public static string ToString(bool value) => value ? "true" : "false"; + + public static string ToString(DateTime value, string format) => value.Kind switch + { + DateTimeKind.Utc => ToString((DateTimeOffset)value, format), + _ => throw new NotSupportedException($"DateTime {value} has a Kind of {value.Kind}. Azure SDK requires it to be UTC. You can call DateTime.SpecifyKind to change Kind property value to DateTimeKind.Utc.") + }; + + public static string ToString(DateTimeOffset value, string format) => format switch + { + "D" => value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), + "U" => value.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), + "O" => value.ToUniversalTime().ToString(RoundtripZFormat, CultureInfo.InvariantCulture), + "o" => value.ToUniversalTime().ToString(RoundtripZFormat, CultureInfo.InvariantCulture), + "R" => value.ToString("r", CultureInfo.InvariantCulture), + _ => value.ToString(format, CultureInfo.InvariantCulture) + }; + + public static string ToString(TimeSpan value, string format) => format switch + { + "P" => XmlConvert.ToString(value), + _ => value.ToString(format, CultureInfo.InvariantCulture) + }; + + public static string ToString(byte[] value, string format) => format switch + { + "U" => ToBase64UrlString(value), + "D" => Convert.ToBase64String(value), + _ => throw new ArgumentException($"Format is not supported: '{format}'", nameof(format)) + }; + + public static string ToBase64UrlString(byte[] value) + { + int numWholeOrPartialInputBlocks = checked(value.Length + 2) / 3; + int size = checked(numWholeOrPartialInputBlocks * 4); + char[] output = new char[size]; + + int numBase64Chars = Convert.ToBase64CharArray(value, 0, value.Length, output, 0); + + int i = 0; + for (; i < numBase64Chars; i++) + { + char ch = output[i]; + if (ch == '+') + { + output[i] = '-'; + } + else + { + if (ch == '/') + { + output[i] = '_'; + } + else + { + if (ch == '=') + { + break; + } + } + } + } + + return new string(output, 0, i); + } + + public static byte[] FromBase64UrlString(string value) + { + int paddingCharsToAdd = (value.Length % 4) switch + { + 0 => 0, + 2 => 2, + 3 => 1, + _ => throw new InvalidOperationException("Malformed input") + }; + char[] output = new char[(value.Length + paddingCharsToAdd)]; + int i = 0; + for (; i < value.Length; i++) + { + char ch = value[i]; + if (ch == '-') + { + output[i] = '+'; + } + else + { + if (ch == '_') + { + output[i] = '/'; + } + else + { + output[i] = ch; + } + } + } + + for (; i < output.Length; i++) + { + output[i] = '='; + } + + return Convert.FromBase64CharArray(output, 0, output.Length); + } + + public static DateTimeOffset ParseDateTimeOffset(string value, string format) => format switch + { + "U" => DateTimeOffset.FromUnixTimeSeconds(long.Parse(value, CultureInfo.InvariantCulture)), + _ => DateTimeOffset.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal) + }; + + public static TimeSpan ParseTimeSpan(string value, string format) => format switch + { + "P" => XmlConvert.ToTimeSpan(value), + _ => TimeSpan.ParseExact(value, format, CultureInfo.InvariantCulture) + }; + + public static string ConvertToString(object value, string format = null) => value switch + { + null => "null", + string s => s, + bool b => ToString(b), + int or float or double or long or decimal => ((IFormattable)value).ToString(DefaultNumberFormat, CultureInfo.InvariantCulture), + byte[] b0 when format != null => ToString(b0, format), + IEnumerable s0 => string.Join(",", s0), + DateTimeOffset dateTime when format != null => ToString(dateTime, format), + TimeSpan timeSpan when format != null => ToString(timeSpan, format), + TimeSpan timeSpan0 => XmlConvert.ToString(timeSpan0), + Guid guid => guid.ToString(), + BinaryData binaryData => ConvertToString(binaryData.ToArray(), format), + _ => value.ToString() + }; + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Optional.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Optional.cs new file mode 100644 index 000000000..465c25d9c --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/Optional.cs @@ -0,0 +1,47 @@ +// + +#nullable disable + +using System.Collections.Generic; +using System.Text.Json; + +namespace AzureOpenAI; + +internal static class Optional +{ + public static bool IsCollectionDefined(IEnumerable collection) + { + return !(collection is ChangeTrackingList changeTrackingList && changeTrackingList.IsUndefined); + } + + public static bool IsCollectionDefined(IDictionary collection) + { + return !(collection is ChangeTrackingDictionary changeTrackingDictionary && changeTrackingDictionary.IsUndefined); + } + + public static bool IsCollectionDefined(IReadOnlyDictionary collection) + { + return !(collection is ChangeTrackingDictionary changeTrackingDictionary && changeTrackingDictionary.IsUndefined); + } + + public static bool IsDefined(T? value) + where T : struct + { + return value.HasValue; + } + + public static bool IsDefined(object value) + { + return value != null; + } + + public static bool IsDefined(JsonElement value) + { + return value.ValueKind != JsonValueKind.Undefined; + } + + public static bool IsDefined(string value) + { + return value != null; + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.Serialization.cs new file mode 100644 index 000000000..31340a9c3 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.Serialization.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +[PersistableModelProxy(typeof(UnknownAzureChatExtensionConfiguration))] +public partial class AzureChatExtensionConfiguration : IJsonModel +{ + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{format}' format."); + } + + writer.WriteStartObject(); + writer.WritePropertyName("type"u8); + writer.WriteStringValue(Type.ToString()); + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + writer.WriteEndObject(); + } + + AzureChatExtensionConfiguration IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + } + + internal static AzureChatExtensionConfiguration DeserializeAzureChatExtensionConfiguration(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + if (element.TryGetProperty("type", out JsonElement discriminator)) + { + switch (discriminator.GetString()) + { + //case "azure_cosmos_db": return AzureCosmosDBChatExtensionConfiguration.DeserializeAzureCosmosDBChatExtensionConfiguration(element, options); + //case "azure_ml_index": return AzureMachineLearningIndexChatExtensionConfiguration.DeserializeAzureMachineLearningIndexChatExtensionConfiguration(element, options); + case "azure_search": return AzureSearchChatExtensionConfiguration.DeserializeAzureSearchChatExtensionConfiguration(element, options); + //case "elasticsearch": return ElasticsearchChatExtensionConfiguration.DeserializeElasticsearchChatExtensionConfiguration(element, options); + //case "pinecone": return PineconeChatExtensionConfiguration.DeserializePineconeChatExtensionConfiguration(element, options); + } + } + return UnknownAzureChatExtensionConfiguration.DeserializeUnknownAzureChatExtensionConfiguration(element, options); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{options.Format}' format."); + } + } + + AzureChatExtensionConfiguration IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + ///// Deserializes the model from a raw response. + ///// The response to deserialize the model from. + //internal static AzureChatExtensionConfiguration FromResponse(Response response) + //{ + // using var document = JsonDocument.Parse(response.Content); + // return DeserializeAzureChatExtensionConfiguration(document.RootElement); + //} + + ///// Convert into a Utf8JsonRequestContent. + //internal virtual RequestContent ToRequestContent() + //{ + // var content = new Utf8JsonRequestContent(); + // content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + // return content; + //} +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs new file mode 100644 index 000000000..f4099f671 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace AzureOpenAI.Models; + +/// +/// A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat +/// completions request that should use Azure OpenAI chat extensions to augment the response behavior. +/// The use of this configuration is compatible only with Azure OpenAI. +/// Please note is the base class. According to the scenario, a derived class of the base class might need to be assigned here, or this property needs to be casted to one of the possible derived classes. +/// The available derived classes include , , , and . +/// +public abstract partial class AzureChatExtensionConfiguration +{ + /// + /// Keeps track of any properties unknown to the library. + /// + /// To assign an object to the value of this property use . + /// + /// + /// To assign an already formatted json string to this property use . + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromObjectAsJson("foo") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromString("\"foo\"") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromObjectAsJson(new { key = "value" }) + /// Creates a payload of { "key": "value" }. + /// + /// + /// BinaryData.FromString("{\"key\": \"value\"}") + /// Creates a payload of { "key": "value" }. + /// + /// + /// + /// + private protected IDictionary _serializedAdditionalRawData; + + /// Initializes a new instance of . + protected AzureChatExtensionConfiguration() + { + } + + /// Initializes a new instance of . + /// + /// The label for the type of an Azure chat extension. This typically corresponds to a matching Azure resource. + /// Azure chat extensions are only compatible with Azure OpenAI. + /// + /// Keeps track of any properties unknown to the library. + internal AzureChatExtensionConfiguration(AzureChatExtensionType type, IDictionary serializedAdditionalRawData) + { + Type = type; + _serializedAdditionalRawData = serializedAdditionalRawData; + } + + /// + /// The label for the type of an Azure chat extension. This typically corresponds to a matching Azure resource. + /// Azure chat extensions are only compatible with Azure OpenAI. + /// + internal AzureChatExtensionType Type { get; set; } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionType.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionType.cs new file mode 100644 index 000000000..7f0b3bb95 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionType.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace AzureOpenAI.Models; + +/// +/// A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat +/// completions request that should use Azure OpenAI chat extensions to augment the response behavior. +/// The use of this configuration is compatible only with Azure OpenAI. +/// +internal readonly partial struct AzureChatExtensionType : IEquatable +{ + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public AzureChatExtensionType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string AzureSearchValue = "azure_search"; + private const string AzureMachineLearningIndexValue = "azure_ml_index"; + private const string AzureCosmosDBValue = "azure_cosmos_db"; + private const string ElasticsearchValue = "elasticsearch"; + private const string PineconeValue = "pinecone"; + + /// Represents the use of Azure AI Search as an Azure OpenAI chat extension. + public static AzureChatExtensionType AzureSearch { get; } = new AzureChatExtensionType(AzureSearchValue); + /// Represents the use of Azure Machine Learning index as an Azure OpenAI chat extension. + public static AzureChatExtensionType AzureMachineLearningIndex { get; } = new AzureChatExtensionType(AzureMachineLearningIndexValue); + /// Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension. + public static AzureChatExtensionType AzureCosmosDB { get; } = new AzureChatExtensionType(AzureCosmosDBValue); + /// Represents the use of Elasticsearch® index as an Azure OpenAI chat extension. + public static AzureChatExtensionType Elasticsearch { get; } = new AzureChatExtensionType(ElasticsearchValue); + /// Represents the use of Pinecone index as an Azure OpenAI chat extension. + public static AzureChatExtensionType Pinecone { get; } = new AzureChatExtensionType(PineconeValue); + /// Determines if two values are the same. + public static bool operator ==(AzureChatExtensionType left, AzureChatExtensionType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(AzureChatExtensionType left, AzureChatExtensionType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator AzureChatExtensionType(string value) => new AzureChatExtensionType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is AzureChatExtensionType other && Equals(other); + /// + public bool Equals(AzureChatExtensionType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs new file mode 100644 index 000000000..fedde4a54 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs @@ -0,0 +1,18 @@ +using OpenAI.Models; + +namespace AzureOpenAI.Models; + +internal class AzureCreateChatCompletionRequest : CreateChatCompletionRequest +{ + // _serializedAdditionalRawData + private readonly Dictionary _azureProperties; + internal Dictionary AzureProperties => _azureProperties; + + public AzureCreateChatCompletionRequest(IEnumerable messages, CreateChatCompletionRequestModel model) + : base(messages, model) + { + _azureProperties = new Dictionary(); + } + + // TODO: Add data_sources field +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs new file mode 100644 index 000000000..1aa0a9571 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs @@ -0,0 +1,21 @@ +using OpenAI.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AzureOpenAI.Models; + +internal static class AzureModelExtensions +{ + public static List? GetDataSources(this CreateChatCompletionRequest request) + { + if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) + { + return null; + } + + // TODO: Do something real + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.Serialization.cs new file mode 100644 index 000000000..43ea89117 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.Serialization.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +public partial class AzureSearchChatExtensionConfiguration : IJsonModel +{ + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureSearchChatExtensionConfiguration)} does not support writing '{format}' format."); + } + + writer.WriteStartObject(); + writer.WritePropertyName("parameters"u8); + writer.WriteObjectValue(Parameters, options); + writer.WritePropertyName("type"u8); + writer.WriteStringValue(Type.ToString()); + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + writer.WriteEndObject(); + } + + AzureSearchChatExtensionConfiguration IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureSearchChatExtensionConfiguration)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureSearchChatExtensionConfiguration(document.RootElement, options); + } + + internal static AzureSearchChatExtensionConfiguration DeserializeAzureSearchChatExtensionConfiguration(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + AzureSearchChatExtensionParameters parameters = default; + AzureChatExtensionType type = default; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("parameters"u8)) + { + parameters = AzureSearchChatExtensionParameters.DeserializeAzureSearchChatExtensionParameters(property.Value, options); + continue; + } + if (property.NameEquals("type"u8)) + { + type = new AzureChatExtensionType(property.Value.GetString()); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + serializedAdditionalRawData = rawDataDictionary; + return new AzureSearchChatExtensionConfiguration(type, serializedAdditionalRawData, parameters); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureSearchChatExtensionConfiguration)} does not support writing '{options.Format}' format."); + } + } + + AzureSearchChatExtensionConfiguration IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureSearchChatExtensionConfiguration(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureSearchChatExtensionConfiguration)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + ///// Deserializes the model from a raw response. + ///// The response to deserialize the model from. + //internal static new AzureSearchChatExtensionConfiguration FromResponse(Response response) + //{ + // using var document = JsonDocument.Parse(response.Content); + // return DeserializeAzureSearchChatExtensionConfiguration(document.RootElement); + //} + + ///// Convert into a Utf8JsonRequestContent. + //internal override BinaryContent ToBinaryContent() + //{ + // var content = new Utf8JsonRequestContent(); + // content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + // return content; + //} +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.cs new file mode 100644 index 000000000..d8a7e655f --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using OpenAI; +using System; +using System.Collections.Generic; + +namespace AzureOpenAI.Models; + +/// +/// A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat +/// extension. +/// +public partial class AzureSearchChatExtensionConfiguration : AzureChatExtensionConfiguration +{ + /// Initializes a new instance of . + /// The parameters to use when configuring Azure Search. + /// is null. + public AzureSearchChatExtensionConfiguration(AzureSearchChatExtensionParameters parameters) + { + Argument.AssertNotNull(parameters, nameof(parameters)); + + Type = AzureChatExtensionType.AzureSearch; + Parameters = parameters; + } + + /// Initializes a new instance of . + /// + /// The label for the type of an Azure chat extension. This typically corresponds to a matching Azure resource. + /// Azure chat extensions are only compatible with Azure OpenAI. + /// + /// Keeps track of any properties unknown to the library. + /// The parameters to use when configuring Azure Search. + internal AzureSearchChatExtensionConfiguration(AzureChatExtensionType type, IDictionary serializedAdditionalRawData, AzureSearchChatExtensionParameters parameters) : base(type, serializedAdditionalRawData) + { + Parameters = parameters; + } + + /// Initializes a new instance of for deserialization. + internal AzureSearchChatExtensionConfiguration() + { + } + + /// The parameters to use when configuring Azure Search. + public AzureSearchChatExtensionParameters Parameters { get; } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs new file mode 100644 index 000000000..638f8ea53 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs @@ -0,0 +1,290 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +public partial class AzureSearchChatExtensionParameters : IJsonModel +{ + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModel)this).Write(writer, new ModelReaderWriterOptions("W")); + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureSearchChatExtensionParameters)} does not support writing '{format}' format."); + } + + writer.WriteStartObject(); + if (Optional.IsDefined(Authentication)) + { + writer.WritePropertyName("authentication"u8); + writer.WriteObjectValue(Authentication, options); + } + if (Optional.IsDefined(DocumentCount)) + { + writer.WritePropertyName("top_n_documents"u8); + writer.WriteNumberValue(DocumentCount.Value); + } + if (Optional.IsDefined(ShouldRestrictResultScope)) + { + writer.WritePropertyName("in_scope"u8); + writer.WriteBooleanValue(ShouldRestrictResultScope.Value); + } + if (Optional.IsDefined(Strictness)) + { + writer.WritePropertyName("strictness"u8); + writer.WriteNumberValue(Strictness.Value); + } + if (Optional.IsDefined(RoleInformation)) + { + writer.WritePropertyName("role_information"u8); + writer.WriteStringValue(RoleInformation); + } + writer.WritePropertyName("endpoint"u8); + writer.WriteStringValue(SearchEndpoint.AbsoluteUri); + writer.WritePropertyName("index_name"u8); + writer.WriteStringValue(IndexName); + if (Optional.IsDefined(FieldMappingOptions)) + { + writer.WritePropertyName("fields_mapping"u8); + writer.WriteObjectValue(FieldMappingOptions, options); + } + if (Optional.IsDefined(QueryType)) + { + writer.WritePropertyName("query_type"u8); + writer.WriteStringValue(QueryType.Value.ToString()); + } + if (Optional.IsDefined(SemanticConfiguration)) + { + writer.WritePropertyName("semantic_configuration"u8); + writer.WriteStringValue(SemanticConfiguration); + } + if (Optional.IsDefined(Filter)) + { + writer.WritePropertyName("filter"u8); + writer.WriteStringValue(Filter); + } + if (Optional.IsDefined(EmbeddingDependency)) + { + writer.WritePropertyName("embedding_dependency"u8); + writer.WriteObjectValue(EmbeddingDependency, options); + } + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + writer.WriteEndObject(); + } + + AzureSearchChatExtensionParameters IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureSearchChatExtensionParameters)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureSearchChatExtensionParameters(document.RootElement, options); + } + + internal static AzureSearchChatExtensionParameters DeserializeAzureSearchChatExtensionParameters(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + OnYourDataAuthenticationOptions authentication = default; + int? topNDocuments = default; + bool? inScope = default; + int? strictness = default; + string roleInformation = default; + Uri endpoint = default; + string indexName = default; + AzureSearchIndexFieldMappingOptions fieldsMapping = default; + AzureSearchQueryType? queryType = default; + string semanticConfiguration = default; + string filter = default; + OnYourDataVectorizationSource embeddingDependency = default; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("authentication"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + authentication = OnYourDataAuthenticationOptions.DeserializeOnYourDataAuthenticationOptions(property.Value, options); + continue; + } + if (property.NameEquals("top_n_documents"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + topNDocuments = property.Value.GetInt32(); + continue; + } + if (property.NameEquals("in_scope"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + inScope = property.Value.GetBoolean(); + continue; + } + if (property.NameEquals("strictness"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + strictness = property.Value.GetInt32(); + continue; + } + if (property.NameEquals("role_information"u8)) + { + roleInformation = property.Value.GetString(); + continue; + } + if (property.NameEquals("endpoint"u8)) + { + endpoint = new Uri(property.Value.GetString()); + continue; + } + if (property.NameEquals("index_name"u8)) + { + indexName = property.Value.GetString(); + continue; + } + if (property.NameEquals("fields_mapping"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + fieldsMapping = AzureSearchIndexFieldMappingOptions.DeserializeAzureSearchIndexFieldMappingOptions(property.Value, options); + continue; + } + if (property.NameEquals("query_type"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + queryType = new AzureSearchQueryType(property.Value.GetString()); + continue; + } + if (property.NameEquals("semantic_configuration"u8)) + { + semanticConfiguration = property.Value.GetString(); + continue; + } + if (property.NameEquals("filter"u8)) + { + filter = property.Value.GetString(); + continue; + } + if (property.NameEquals("embedding_dependency"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + embeddingDependency = OnYourDataVectorizationSource.DeserializeOnYourDataVectorizationSource(property.Value, options); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + serializedAdditionalRawData = rawDataDictionary; + return new AzureSearchChatExtensionParameters( + authentication, + topNDocuments, + inScope, + strictness, + roleInformation, + endpoint, + indexName, + fieldsMapping, + queryType, + semanticConfiguration, + filter, + embeddingDependency, + serializedAdditionalRawData); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureSearchChatExtensionParameters)} does not support writing '{options.Format}' format."); + } + } + + AzureSearchChatExtensionParameters IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureSearchChatExtensionParameters(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureSearchChatExtensionParameters)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// Deserializes the model from a raw response. + /// The response to deserialize the model from. + internal static AzureSearchChatExtensionParameters FromResponse(Response response) + { + using var document = JsonDocument.Parse(response.Content); + return DeserializeAzureSearchChatExtensionParameters(document.RootElement); + } + + /// Convert into a Utf8JsonRequestContent. + internal virtual RequestContent ToRequestContent() + { + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + return content; + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.cs new file mode 100644 index 000000000..a7548a0da --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace AzureOpenAI.Models; + +/// Parameters for Azure Cognitive Search when used as an Azure OpenAI chat extension. The supported authentication types are APIKey, SystemAssignedManagedIdentity and UserAssignedManagedIdentity. +public partial class AzureSearchChatExtensionParameters +{ + /// + /// Keeps track of any properties unknown to the library. + /// + /// To assign an object to the value of this property use . + /// + /// + /// To assign an already formatted json string to this property use . + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromObjectAsJson("foo") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromString("\"foo\"") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromObjectAsJson(new { key = "value" }) + /// Creates a payload of { "key": "value" }. + /// + /// + /// BinaryData.FromString("{\"key\": \"value\"}") + /// Creates a payload of { "key": "value" }. + /// + /// + /// + /// + private IDictionary _serializedAdditionalRawData; + + /// Initializes a new instance of . + /// The absolute endpoint path for the Azure Cognitive Search resource to use. + /// The name of the index to use as available in the referenced Azure Cognitive Search resource. + /// or is null. + public AzureSearchChatExtensionParameters(Uri searchEndpoint, string indexName) + { + Argument.AssertNotNull(searchEndpoint, nameof(searchEndpoint)); + Argument.AssertNotNull(indexName, nameof(indexName)); + + SearchEndpoint = searchEndpoint; + IndexName = indexName; + } + + /// Initializes a new instance of . + /// + /// The authentication method to use when accessing the defined data source. + /// Each data source type supports a specific set of available authentication methods; please see the documentation of + /// the data source for supported mechanisms. + /// If not otherwise provided, On Your Data will attempt to use System Managed Identity (default credential) + /// authentication. + /// Please note is the base class. According to the scenario, a derived class of the base class might need to be assigned here, or this property needs to be casted to one of the possible derived classes. + /// The available derived classes include , , , , , and . + /// + /// The configured top number of documents to feature for the configured query. + /// Whether queries should be restricted to use of indexed data. + /// The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer. + /// Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit. + /// The absolute endpoint path for the Azure Cognitive Search resource to use. + /// The name of the index to use as available in the referenced Azure Cognitive Search resource. + /// Customized field mapping behavior to use when interacting with the search index. + /// The query type to use with Azure Cognitive Search. + /// The additional semantic configuration for the query. + /// Search filter. + /// + /// The embedding dependency for vector search. + /// Please note is the base class. According to the scenario, a derived class of the base class might need to be assigned here, or this property needs to be casted to one of the possible derived classes. + /// The available derived classes include , and . + /// + /// Keeps track of any properties unknown to the library. + //internal AzureSearchChatExtensionParameters(OnYourDataAuthenticationOptions authentication, int? documentCount, bool? shouldRestrictResultScope, int? strictness, string roleInformation, Uri searchEndpoint, string indexName, AzureSearchIndexFieldMappingOptions fieldMappingOptions, AzureSearchQueryType? queryType, string semanticConfiguration, string filter, OnYourDataVectorizationSource embeddingDependency, IDictionary serializedAdditionalRawData) + internal AzureSearchChatExtensionParameters(int? documentCount, bool? shouldRestrictResultScope, int? strictness, string roleInformation, Uri searchEndpoint, string indexName, string semanticConfiguration, string filter, IDictionary serializedAdditionalRawData) + { + //Authentication = authentication; + DocumentCount = documentCount; + ShouldRestrictResultScope = shouldRestrictResultScope; + Strictness = strictness; + RoleInformation = roleInformation; + SearchEndpoint = searchEndpoint; + IndexName = indexName; + //FieldMappingOptions = fieldMappingOptions; + //QueryType = queryType; + SemanticConfiguration = semanticConfiguration; + Filter = filter; + //EmbeddingDependency = embeddingDependency; + _serializedAdditionalRawData = serializedAdditionalRawData; + } + + /// Initializes a new instance of for deserialization. + internal AzureSearchChatExtensionParameters() + { + } + + /// + /// The authentication method to use when accessing the defined data source. + /// Each data source type supports a specific set of available authentication methods; please see the documentation of + /// the data source for supported mechanisms. + /// If not otherwise provided, On Your Data will attempt to use System Managed Identity (default credential) + /// authentication. + /// Please note is the base class. According to the scenario, a derived class of the base class might need to be assigned here, or this property needs to be casted to one of the possible derived classes. + /// The available derived classes include , , , , , and . + /// + //public OnYourDataAuthenticationOptions Authentication { get; set; } + /// The configured top number of documents to feature for the configured query. + public int? DocumentCount { get; set; } + /// Whether queries should be restricted to use of indexed data. + public bool? ShouldRestrictResultScope { get; set; } + /// The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer. + public int? Strictness { get; set; } + /// Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit. + public string RoleInformation { get; set; } + /// The absolute endpoint path for the Azure Cognitive Search resource to use. + public Uri SearchEndpoint { get; } + /// The name of the index to use as available in the referenced Azure Cognitive Search resource. + public string IndexName { get; } + /// Customized field mapping behavior to use when interacting with the search index. + //public AzureSearchIndexFieldMappingOptions FieldMappingOptions { get; set; } + ///// The query type to use with Azure Cognitive Search. + //public AzureSearchQueryType? QueryType { get; set; } + /// The additional semantic configuration for the query. + public string SemanticConfiguration { get; set; } + /// Search filter. + public string Filter { get; set; } + ///// + ///// The embedding dependency for vector search. + ///// Please note is the base class. According to the scenario, a derived class of the base class might need to be assigned here, or this property needs to be casted to one of the possible derived classes. + ///// The available derived classes include , and . + ///// + //public OnYourDataVectorizationSource EmbeddingDependency { get; set; } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs new file mode 100644 index 000000000..24b3daa6c --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.AI.OpenAI +{ + internal partial class UnknownAzureChatExtensionConfiguration : IUtf8JsonSerializable, IJsonModel + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModel)this).Write(writer, new ModelReaderWriterOptions("W")); + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{format}' format."); + } + + writer.WriteStartObject(); + writer.WritePropertyName("type"u8); + writer.WriteStringValue(Type.ToString()); + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + writer.WriteEndObject(); + } + + AzureChatExtensionConfiguration IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + } + + internal static UnknownAzureChatExtensionConfiguration DeserializeUnknownAzureChatExtensionConfiguration(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + AzureChatExtensionType type = "Unknown"; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("type"u8)) + { + type = new AzureChatExtensionType(property.Value.GetString()); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + serializedAdditionalRawData = rawDataDictionary; + return new UnknownAzureChatExtensionConfiguration(type, serializedAdditionalRawData); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{options.Format}' format."); + } + } + + AzureChatExtensionConfiguration IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// Deserializes the model from a raw response. + /// The response to deserialize the model from. + internal static new UnknownAzureChatExtensionConfiguration FromResponse(Response response) + { + using var document = JsonDocument.Parse(response.Content); + return DeserializeUnknownAzureChatExtensionConfiguration(document.RootElement); + } + + /// Convert into a Utf8JsonRequestContent. + internal override RequestContent ToRequestContent() + { + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + return content; + } + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.cs new file mode 100644 index 000000000..5d9b43d3b --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace AzureOpenAI.Models; + +/// Unknown version of AzureChatExtensionConfiguration. +internal partial class UnknownAzureChatExtensionConfiguration : AzureChatExtensionConfiguration +{ + /// Initializes a new instance of . + /// + /// The label for the type of an Azure chat extension. This typically corresponds to a matching Azure resource. + /// Azure chat extensions are only compatible with Azure OpenAI. + /// + /// Keeps track of any properties unknown to the library. + internal UnknownAzureChatExtensionConfiguration(AzureChatExtensionType type, IDictionary serializedAdditionalRawData) : base(type, serializedAdditionalRawData) + { + } + + /// Initializes a new instance of for deserialization. + internal UnknownAzureChatExtensionConfiguration() + { + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index 29676d50a..e276d9992 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; namespace OpenAI.Models @@ -43,6 +44,9 @@ public partial class CreateChatCompletionRequest /// private IDictionary _serializedAdditionalRawData; + [EditorBrowsable(EditorBrowsableState.Never)] + public IDictionary AzureProperties => _serializedAdditionalRawData; + /// Initializes a new instance of . /// /// A list of messages comprising the conversation so far. From 0ac5efe688a70728f875a256d73df8b4f46fee43 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Apr 2024 17:02:24 -0700 Subject: [PATCH 05/32] WIP --- .../Models/AzureModelExtensions.cs | 28 ++- ...chChatExtensionParameters.Serialization.cs | 158 ++++++++------- ...hatExtensionConfiguration.Serialization.cs | 182 +++++++++--------- 3 files changed, 187 insertions(+), 181 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs index 1aa0a9571..c10fff4ea 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs @@ -1,21 +1,35 @@ using OpenAI.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Text.Json; namespace AzureOpenAI.Models; internal static class AzureModelExtensions { - public static List? GetDataSources(this CreateChatCompletionRequest request) + // TODO: return type for collection? + public static IList? GetDataSources(this CreateChatCompletionRequest request) { if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) { return null; } - // TODO: Do something real + // TODO: Notice that the generator would create this using + // the same serialization/deserialization code they would typically + // put into a model. + + if (property.NameEquals("data_sources"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(AzureChatExtensionConfiguration.DeserializeAzureChatExtensionConfiguration(item, options)); + } + dataSources = array; + continue; + } } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs index 638f8ea53..c7c2a1a6a 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs @@ -12,8 +12,6 @@ namespace AzureOpenAI.Models; public partial class AzureSearchChatExtensionParameters : IJsonModel { - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModel)this).Write(writer, new ModelReaderWriterOptions("W")); - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; @@ -23,11 +21,11 @@ void IJsonModel.Write(Utf8JsonWriter writer, } writer.WriteStartObject(); - if (Optional.IsDefined(Authentication)) - { - writer.WritePropertyName("authentication"u8); - writer.WriteObjectValue(Authentication, options); - } + //if (Optional.IsDefined(Authentication)) + //{ + // writer.WritePropertyName("authentication"u8); + // writer.WriteObjectValue(Authentication, options); + //} if (Optional.IsDefined(DocumentCount)) { writer.WritePropertyName("top_n_documents"u8); @@ -52,16 +50,16 @@ void IJsonModel.Write(Utf8JsonWriter writer, writer.WriteStringValue(SearchEndpoint.AbsoluteUri); writer.WritePropertyName("index_name"u8); writer.WriteStringValue(IndexName); - if (Optional.IsDefined(FieldMappingOptions)) - { - writer.WritePropertyName("fields_mapping"u8); - writer.WriteObjectValue(FieldMappingOptions, options); - } - if (Optional.IsDefined(QueryType)) - { - writer.WritePropertyName("query_type"u8); - writer.WriteStringValue(QueryType.Value.ToString()); - } + //if (Optional.IsDefined(FieldMappingOptions)) + //{ + // writer.WritePropertyName("fields_mapping"u8); + // writer.WriteObjectValue(FieldMappingOptions, options); + //} + //if (Optional.IsDefined(QueryType)) + //{ + // writer.WritePropertyName("query_type"u8); + // writer.WriteStringValue(QueryType.Value.ToString()); + //} if (Optional.IsDefined(SemanticConfiguration)) { writer.WritePropertyName("semantic_configuration"u8); @@ -72,11 +70,11 @@ void IJsonModel.Write(Utf8JsonWriter writer, writer.WritePropertyName("filter"u8); writer.WriteStringValue(Filter); } - if (Optional.IsDefined(EmbeddingDependency)) - { - writer.WritePropertyName("embedding_dependency"u8); - writer.WriteObjectValue(EmbeddingDependency, options); - } + //if (Optional.IsDefined(EmbeddingDependency)) + //{ + // writer.WritePropertyName("embedding_dependency"u8); + // writer.WriteObjectValue(EmbeddingDependency, options); + //} if (options.Format != "W" && _serializedAdditionalRawData != null) { foreach (var item in _serializedAdditionalRawData) @@ -115,31 +113,31 @@ internal static AzureSearchChatExtensionParameters DeserializeAzureSearchChatExt { return null; } - OnYourDataAuthenticationOptions authentication = default; + //OnYourDataAuthenticationOptions authentication = default; int? topNDocuments = default; bool? inScope = default; int? strictness = default; string roleInformation = default; Uri endpoint = default; string indexName = default; - AzureSearchIndexFieldMappingOptions fieldsMapping = default; - AzureSearchQueryType? queryType = default; + //AzureSearchIndexFieldMappingOptions fieldsMapping = default; + //AzureSearchQueryType? queryType = default; string semanticConfiguration = default; string filter = default; - OnYourDataVectorizationSource embeddingDependency = default; + //OnYourDataVectorizationSource embeddingDependency = default; IDictionary serializedAdditionalRawData = default; Dictionary rawDataDictionary = new Dictionary(); foreach (var property in element.EnumerateObject()) { - if (property.NameEquals("authentication"u8)) - { - if (property.Value.ValueKind == JsonValueKind.Null) - { - continue; - } - authentication = OnYourDataAuthenticationOptions.DeserializeOnYourDataAuthenticationOptions(property.Value, options); - continue; - } + //if (property.NameEquals("authentication"u8)) + //{ + // if (property.Value.ValueKind == JsonValueKind.Null) + // { + // continue; + // } + // authentication = OnYourDataAuthenticationOptions.DeserializeOnYourDataAuthenticationOptions(property.Value, options); + // continue; + //} if (property.NameEquals("top_n_documents"u8)) { if (property.Value.ValueKind == JsonValueKind.Null) @@ -182,24 +180,24 @@ internal static AzureSearchChatExtensionParameters DeserializeAzureSearchChatExt indexName = property.Value.GetString(); continue; } - if (property.NameEquals("fields_mapping"u8)) - { - if (property.Value.ValueKind == JsonValueKind.Null) - { - continue; - } - fieldsMapping = AzureSearchIndexFieldMappingOptions.DeserializeAzureSearchIndexFieldMappingOptions(property.Value, options); - continue; - } - if (property.NameEquals("query_type"u8)) - { - if (property.Value.ValueKind == JsonValueKind.Null) - { - continue; - } - queryType = new AzureSearchQueryType(property.Value.GetString()); - continue; - } + //if (property.NameEquals("fields_mapping"u8)) + //{ + // if (property.Value.ValueKind == JsonValueKind.Null) + // { + // continue; + // } + // fieldsMapping = AzureSearchIndexFieldMappingOptions.DeserializeAzureSearchIndexFieldMappingOptions(property.Value, options); + // continue; + //} + //if (property.NameEquals("query_type"u8)) + //{ + // if (property.Value.ValueKind == JsonValueKind.Null) + // { + // continue; + // } + // queryType = new AzureSearchQueryType(property.Value.GetString()); + // continue; + //} if (property.NameEquals("semantic_configuration"u8)) { semanticConfiguration = property.Value.GetString(); @@ -210,15 +208,15 @@ internal static AzureSearchChatExtensionParameters DeserializeAzureSearchChatExt filter = property.Value.GetString(); continue; } - if (property.NameEquals("embedding_dependency"u8)) - { - if (property.Value.ValueKind == JsonValueKind.Null) - { - continue; - } - embeddingDependency = OnYourDataVectorizationSource.DeserializeOnYourDataVectorizationSource(property.Value, options); - continue; - } + //if (property.NameEquals("embedding_dependency"u8)) + //{ + // if (property.Value.ValueKind == JsonValueKind.Null) + // { + // continue; + // } + // embeddingDependency = OnYourDataVectorizationSource.DeserializeOnYourDataVectorizationSource(property.Value, options); + // continue; + //} if (options.Format != "W") { rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); @@ -226,18 +224,18 @@ internal static AzureSearchChatExtensionParameters DeserializeAzureSearchChatExt } serializedAdditionalRawData = rawDataDictionary; return new AzureSearchChatExtensionParameters( - authentication, + //authentication, topNDocuments, inScope, strictness, roleInformation, endpoint, indexName, - fieldsMapping, - queryType, + //fieldsMapping, + //queryType, semanticConfiguration, filter, - embeddingDependency, + //embeddingDependency, serializedAdditionalRawData); } @@ -272,19 +270,19 @@ AzureSearchChatExtensionParameters IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; - /// Deserializes the model from a raw response. - /// The response to deserialize the model from. - internal static AzureSearchChatExtensionParameters FromResponse(Response response) - { - using var document = JsonDocument.Parse(response.Content); - return DeserializeAzureSearchChatExtensionParameters(document.RootElement); - } + ///// Deserializes the model from a raw response. + ///// The response to deserialize the model from. + //internal static AzureSearchChatExtensionParameters FromResponse(Response response) + //{ + // using var document = JsonDocument.Parse(response.Content); + // return DeserializeAzureSearchChatExtensionParameters(document.RootElement); + //} - /// Convert into a Utf8JsonRequestContent. - internal virtual RequestContent ToRequestContent() - { - var content = new Utf8JsonRequestContent(); - content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); - return content; - } + ///// Convert into a Utf8JsonRequestContent. + //internal virtual RequestContent ToRequestContent() + //{ + // var content = new Utf8JsonRequestContent(); + // content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + // return content; + //} } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs index 24b3daa6c..b2f7802be 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs @@ -5,131 +5,125 @@ #nullable disable -using System; using System.ClientModel.Primitives; -using System.Collections.Generic; using System.Text.Json; -using Azure.Core; -namespace Azure.AI.OpenAI +namespace AzureOpenAI.Models; + +internal partial class UnknownAzureChatExtensionConfiguration : IJsonModel { - internal partial class UnknownAzureChatExtensionConfiguration : IUtf8JsonSerializable, IJsonModel + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) { - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModel)this).Write(writer, new ModelReaderWriterOptions("W")); - - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if (format != "J") - { - throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{format}' format."); - } + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{format}' format."); + } - writer.WriteStartObject(); - writer.WritePropertyName("type"u8); - writer.WriteStringValue(Type.ToString()); - if (options.Format != "W" && _serializedAdditionalRawData != null) + writer.WriteStartObject(); + writer.WritePropertyName("type"u8); + writer.WriteStringValue(Type.ToString()); + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) { - foreach (var item in _serializedAdditionalRawData) - { - writer.WritePropertyName(item.Key); + writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); + writer.WriteRawValue(item.Value); #else - using (JsonDocument document = JsonDocument.Parse(item.Value)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } -#endif + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); } +#endif } - writer.WriteEndObject(); } + writer.WriteEndObject(); + } - AzureChatExtensionConfiguration IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + AzureChatExtensionConfiguration IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if (format != "J") - { - throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{format}' format."); - } - - using JsonDocument document = JsonDocument.ParseValue(ref reader); - return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{format}' format."); } - internal static UnknownAzureChatExtensionConfiguration DeserializeUnknownAzureChatExtensionConfiguration(JsonElement element, ModelReaderWriterOptions options = null) - { - options ??= new ModelReaderWriterOptions("W"); + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + } - if (element.ValueKind == JsonValueKind.Null) - { - return null; - } - AzureChatExtensionType type = "Unknown"; - IDictionary serializedAdditionalRawData = default; - Dictionary rawDataDictionary = new Dictionary(); - foreach (var property in element.EnumerateObject()) - { - if (property.NameEquals("type"u8)) - { - type = new AzureChatExtensionType(property.Value.GetString()); - continue; - } - if (options.Format != "W") - { - rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); - } - } - serializedAdditionalRawData = rawDataDictionary; - return new UnknownAzureChatExtensionConfiguration(type, serializedAdditionalRawData); - } + internal static UnknownAzureChatExtensionConfiguration DeserializeUnknownAzureChatExtensionConfiguration(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + if (element.ValueKind == JsonValueKind.Null) { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - return ModelReaderWriter.Write(this, options); - default: - throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{options.Format}' format."); - } + return null; } - - AzureChatExtensionConfiguration IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + AzureChatExtensionType type = "Unknown"; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) + if (property.NameEquals("type"u8)) { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{options.Format}' format."); + type = new AzureChatExtensionType(property.Value.GetString()); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); } } + serializedAdditionalRawData = rawDataDictionary; + return new UnknownAzureChatExtensionConfiguration(type, serializedAdditionalRawData); + } - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - /// Deserializes the model from a raw response. - /// The response to deserialize the model from. - internal static new UnknownAzureChatExtensionConfiguration FromResponse(Response response) + switch (format) { - using var document = JsonDocument.Parse(response.Content); - return DeserializeUnknownAzureChatExtensionConfiguration(document.RootElement); + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support writing '{options.Format}' format."); } + } - /// Convert into a Utf8JsonRequestContent. - internal override RequestContent ToRequestContent() + AzureChatExtensionConfiguration IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) { - var content = new Utf8JsonRequestContent(); - content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); - return content; + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureChatExtensionConfiguration(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureChatExtensionConfiguration)} does not support reading '{options.Format}' format."); } } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + ///// Deserializes the model from a raw response. + ///// The response to deserialize the model from. + //internal static new UnknownAzureChatExtensionConfiguration FromResponse(Response response) + //{ + // using var document = JsonDocument.Parse(response.Content); + // return DeserializeUnknownAzureChatExtensionConfiguration(document.RootElement); + //} + + ///// Convert into a Utf8JsonRequestContent. + //internal override RequestContent ToRequestContent() + //{ + // var content = new Utf8JsonRequestContent(); + // content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + // return content; + //} } From 8ca386fe810d2018a32a3e9cad5e0de2b8b16f8d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 9 Apr 2024 09:49:58 -0700 Subject: [PATCH 06/32] Initial implementation of settable extension property --- .../azoai/AzureOpenAI/AzureChatClient.cs | 9 ++ .../Models/AzureChatExtensionConfiguration.cs | 5 ++ .../Models/AzureModelExtensions.cs | 88 ++++++++++++++----- .../Models/CreateChatCompletionRequest.cs | 3 +- 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index a78d37699..58914df21 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -11,6 +11,15 @@ internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, U { } + // TODO: Show how this would differ for this case. Do we still need OperationName and the + // remapping policy? + // 1. Version parameter + // 2. Auth key is different - does that show up here or in the client? + // 3. DeploymentId in path + // + // Note: Model content is already serialized by the time we get here. Nothing + // content-related should happen in this method. If we can show that, do we need + // to make these methods protected virtual? protected override PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) { var message = Pipeline.CreateMessage(); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs index f4099f671..ea73126d4 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs @@ -49,6 +49,11 @@ public abstract partial class AzureChatExtensionConfiguration /// /// /// + + // TODO: Could this be IDictionary> or even + // IDictionary> in some cases. What would that do? + // + // Note: No, because you don't have the T. private protected IDictionary _serializedAdditionalRawData; /// Initializes a new instance of . diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs index c10fff4ea..1a4e2fedf 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs @@ -1,35 +1,79 @@ using OpenAI.Models; -using System.Text.Json; +using System.Diagnostics; namespace AzureOpenAI.Models; internal static class AzureModelExtensions { - // TODO: return type for collection? - public static IList? GetDataSources(this CreateChatCompletionRequest request) + public static void SetAzureDataSource(this CreateChatCompletionRequest request, AzureChatExtensionConfiguration dataSource) { - if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) - { - return null; - } + Argument.AssertNotNull(dataSource, nameof(dataSource)); + + // TODO: we can do it slow via MRW.Write to BinaryData; + // can we do it fast via MRW.Write to Utf8JsonWriter? + + // TODO: Does it matter to pass MRW Options here? + //BinaryData serializedValue = ModelReaderWriter.Write(dataSource); + + // Note: One observation here is - working in the "BinaryData space" is + // different depending on input/output models. We could make it all + // strongly-typed for input, but we'd have a lot of properties to [EBN]. + + // If the BinaryData represents JSON, then we need to mutate the JSON + // to add a collection, and this is expensive. + + // We could use MutableJsonDocument 😮, JsonNode, etc. + // We could keep separate property bags for input and output types, i.e. + // for different scenarios. + // We could do something like PipelineMessage and hold an ArrayBackedPropertyBag + + // For now: Let's have serialized raw data and strongly-typed additional properties. + // This lets us wait to serialize them until we can do it in an optimal fashion. - // TODO: Notice that the generator would create this using - // the same serialization/deserialization code they would typically - // put into a model. - if (property.NameEquals("data_sources"u8)) + // Add the list if it doesn't already exist + IList dataSources; + + if (request.AdditionalTypedProperties.TryGetValue("data_sources", out object? value)) + { + Debug.Assert(value is IList); + + dataSources = (value as IList)!; + } + else { - if (property.Value.ValueKind == JsonValueKind.Null) - { - continue; - } - List array = new List(); - foreach (var item in property.Value.EnumerateArray()) - { - array.Add(AzureChatExtensionConfiguration.DeserializeAzureChatExtensionConfiguration(item, options)); - } - dataSources = array; - continue; + dataSources = []; + request.AdditionalTypedProperties.Add("data_sources", dataSources); } + + dataSources.Add(dataSource); } + + //// TODO: return type for collection? + //public static IList? GetDataSources(this CreateChatCompletionRequest request) + //{ + // if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) + // { + // return null; + // } + + // // TODO: Notice that the generator would create this using + // // the same serialization/deserialization code they would typically + // // put into a model. + + // if (property.NameEquals("data_sources"u8)) + // { + // if (property.Value.ValueKind == JsonValueKind.Null) + // { + // continue; + // } + // List array = new List(); + // foreach (var item in property.Value.EnumerateArray()) + // { + // array.Add(AzureChatExtensionConfiguration.DeserializeAzureChatExtensionConfiguration(item, options)); + // } + // dataSources = array; + // continue; + // } + //} } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index e276d9992..e1644afad 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -44,8 +44,9 @@ public partial class CreateChatCompletionRequest /// private IDictionary _serializedAdditionalRawData; + private IDictionary _additionalTypedProperties; [EditorBrowsable(EditorBrowsableState.Never)] - public IDictionary AzureProperties => _serializedAdditionalRawData; + public IDictionary AdditionalTypedProperties => _additionalTypedProperties; /// Initializes a new instance of . /// From 443c709fa97753d4cce2462415f5d98e4ab2bfbd Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 9 Apr 2024 10:31:05 -0700 Subject: [PATCH 07/32] functional prototype for inputs; with lots of open questions --- .../app/DemoApp/DemoApp.csproj | 1 + .../typespec-csharp/app/DemoApp/Program.cs | 7 +++ .../Internal/PersistableModelList.cs | 57 +++++++++++++++++++ .../Models/AzureModelExtensions.cs | 9 ++- ...eateChatCompletionRequest.Serialization.cs | 25 +++++++- .../Models/CreateChatCompletionRequest.cs | 2 +- 6 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj index 48462fbc6..487fd868e 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj @@ -8,6 +8,7 @@ + diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index 288731362..e8cf112f3 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -2,6 +2,8 @@ using OpenAI.Models; using System.ClientModel; +using AzureOpenAI.Models; + Console.WriteLine("Hello, World!"); //Uri endpoint = new("https://annelo-openai-01.openai.azure.com/"); @@ -20,7 +22,12 @@ }; messages.Add(BinaryData.FromObjectAsJson(message)); +// Add Azure property +AzureSearchChatExtensionParameters searchParams = new(new Uri("https://azure.search.com"), "MySearchIndex"); +AzureSearchChatExtensionConfiguration dataSource = new(searchParams); + CreateChatCompletionRequest request = new CreateChatCompletionRequest(messages, CreateChatCompletionRequestModel.Gpt35Turbo); +request.SetAzureDataSource(dataSource); ClientResult result = chatClient.CreateChatCompletion(request); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs new file mode 100644 index 000000000..c97e68095 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs @@ -0,0 +1,57 @@ +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI; + +internal class PersistableModelList : List>, IPersistableModel> +{ + public PersistableModelList Create(BinaryData data, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + public string GetFormatFromOptions(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + public BinaryData Write(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } +} + +internal class JsonModelList : List>, IJsonModel> +{ + public JsonModelList Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + public JsonModelList Create(BinaryData data, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + public string GetFormatFromOptions(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + public void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartArray(); + + foreach (IJsonModel item in this) + { + item.Write(writer, options); + } + + writer.WriteEndArray(); + } + + public BinaryData Write(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs index 1a4e2fedf..df6259c58 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs @@ -3,7 +3,7 @@ namespace AzureOpenAI.Models; -internal static class AzureModelExtensions +public static class AzureModelExtensions { public static void SetAzureDataSource(this CreateChatCompletionRequest request, AzureChatExtensionConfiguration dataSource) { @@ -30,15 +30,14 @@ public static void SetAzureDataSource(this CreateChatCompletionRequest request, // For now: Let's have serialized raw data and strongly-typed additional properties. // This lets us wait to serialize them until we can do it in an optimal fashion. - // Add the list if it doesn't already exist - IList dataSources; + JsonModelList dataSources; if (request.AdditionalTypedProperties.TryGetValue("data_sources", out object? value)) { - Debug.Assert(value is IList); + Debug.Assert(value is JsonModelList); - dataSources = (value as IList)!; + dataSources = (value as JsonModelList)!; } else { diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index 9f3ea44b8..2eafcd7da 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -259,7 +259,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); + writer.WriteRawValue(item.Value); #else using (JsonDocument document = JsonDocument.Parse(item.Value)) { @@ -268,6 +268,29 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR #endif } } + + // Note: we were holding strongly-typed values for added Azure properties + // We have to serialize them separately here. + // TODO: Does the format matter here? It seems like we want to write them + // in every case. + if (_additionalTypedProperties != null) + { + foreach (var property in _additionalTypedProperties) + { + // TODO: it might be nice to have generated collections that + // implement IPersistableModel, IJsonModel + if (property.Value is not IJsonModel model) + { + // TODO: how do we validate this on the input side? + throw new InvalidOperationException($"invalid typed property, type is '{property.Value.GetType()}'"); + } + + writer.WritePropertyName(property.Key); + + // Note: what if it's just a primitive, i.e. a string or int, what do we do? + model.Write(writer, options); + } + } writer.WriteEndObject(); } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index e1644afad..9d1ffee21 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -46,7 +46,7 @@ public partial class CreateChatCompletionRequest private IDictionary _additionalTypedProperties; [EditorBrowsable(EditorBrowsableState.Never)] - public IDictionary AdditionalTypedProperties => _additionalTypedProperties; + public IDictionary AdditionalTypedProperties => _additionalTypedProperties ??= new Dictionary(); /// Initializes a new instance of . /// From 43b4afe6cceedac8f0f54b5ea62fa121baa0d87f Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 10 Apr 2024 13:19:55 -0700 Subject: [PATCH 08/32] updates --- .../@azure-tools/typespec-csharp/app/DemoApp/Program.cs | 9 --------- .../azoai/AzureOpenAI/Models/AzureModelExtensions.cs | 6 ++++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index e8cf112f3..b87d682d1 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -34,15 +34,6 @@ //AzureOpenAIClient client = new AzureOpenAIClient(endpoint, new ApiKeyCredential(apiKey)); //Chat chatClient = client.GetChatClient("gpt-35-turbo-instruct"); -// TODO: Here. What is the convenience model story? -// Should the Azure client return Response? //ClientResult result = await chatClient.CompleteChatAsync() -// TODO: How do we reconcile returning Response from Azure client protocol methods -// and ClientResult from unbranded client protocol methods? - // TODO: MRW story - input model and output models too. - -//Client - -//OpenAIClient client = new() \ No newline at end of file diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs index df6259c58..4061d9e3e 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs @@ -1,10 +1,16 @@ using OpenAI.Models; +using System.ClientModel; using System.Diagnostics; namespace AzureOpenAI.Models; public static class AzureModelExtensions { + public static BinaryContent ToBinaryBody(this CreateChatCompletionRequest request) + { + throw new NotImplementedException(); + } + public static void SetAzureDataSource(this CreateChatCompletionRequest request, AzureChatExtensionConfiguration dataSource) { Argument.AssertNotNull(dataSource, nameof(dataSource)); From 99f8265b512e626e21306f8d6a5abd2b511c2c70 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 10 Apr 2024 15:42:43 -0700 Subject: [PATCH 09/32] starting the model-inheritance design exploration --- .../azoai/AzureOpenAI/AzureChatClient.cs | 6 + .../Models/AzureModelExtensions.cs | 156 +++++++++--------- 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index 58914df21..20ace0473 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -1,4 +1,5 @@ using OpenAI; +using OpenAI.Models; using System.ClientModel; using System.ClientModel.Primitives; @@ -11,6 +12,11 @@ internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, U { } + public Task> CreateChatCompletionAsync(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) + { + return base.CreateChatCompletionAsync(createChatCompletionRequest, cancellationToken); + } + // TODO: Show how this would differ for this case. Do we still need OperationName and the // remapping policy? // 1. Version parameter diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs index 4061d9e3e..e68d21f11 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs @@ -4,81 +4,81 @@ namespace AzureOpenAI.Models; -public static class AzureModelExtensions -{ - public static BinaryContent ToBinaryBody(this CreateChatCompletionRequest request) - { - throw new NotImplementedException(); - } - - public static void SetAzureDataSource(this CreateChatCompletionRequest request, AzureChatExtensionConfiguration dataSource) - { - Argument.AssertNotNull(dataSource, nameof(dataSource)); - - // TODO: we can do it slow via MRW.Write to BinaryData; - // can we do it fast via MRW.Write to Utf8JsonWriter? - - // TODO: Does it matter to pass MRW Options here? - //BinaryData serializedValue = ModelReaderWriter.Write(dataSource); - - // Note: One observation here is - working in the "BinaryData space" is - // different depending on input/output models. We could make it all - // strongly-typed for input, but we'd have a lot of properties to [EBN]. - - // If the BinaryData represents JSON, then we need to mutate the JSON - // to add a collection, and this is expensive. - - // We could use MutableJsonDocument 😮, JsonNode, etc. - // We could keep separate property bags for input and output types, i.e. - // for different scenarios. - // We could do something like PipelineMessage and hold an ArrayBackedPropertyBag - - // For now: Let's have serialized raw data and strongly-typed additional properties. - // This lets us wait to serialize them until we can do it in an optimal fashion. - - // Add the list if it doesn't already exist - JsonModelList dataSources; - - if (request.AdditionalTypedProperties.TryGetValue("data_sources", out object? value)) - { - Debug.Assert(value is JsonModelList); - - dataSources = (value as JsonModelList)!; - } - else - { - dataSources = []; - request.AdditionalTypedProperties.Add("data_sources", dataSources); - } - - dataSources.Add(dataSource); - } - - //// TODO: return type for collection? - //public static IList? GetDataSources(this CreateChatCompletionRequest request) - //{ - // if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) - // { - // return null; - // } - - // // TODO: Notice that the generator would create this using - // // the same serialization/deserialization code they would typically - // // put into a model. - - // if (property.NameEquals("data_sources"u8)) - // { - // if (property.Value.ValueKind == JsonValueKind.Null) - // { - // continue; - // } - // List array = new List(); - // foreach (var item in property.Value.EnumerateArray()) - // { - // array.Add(AzureChatExtensionConfiguration.DeserializeAzureChatExtensionConfiguration(item, options)); - // } - // dataSources = array; - // continue; - // } - //} -} +//public static class AzureModelExtensions +//{ +// public static BinaryContent ToBinaryBody(this CreateChatCompletionRequest request) +// { +// throw new NotImplementedException(); +// } + +// public static void SetAzureDataSource(this CreateChatCompletionRequest request, AzureChatExtensionConfiguration dataSource) +// { +// Argument.AssertNotNull(dataSource, nameof(dataSource)); + +// // TODO: we can do it slow via MRW.Write to BinaryData; +// // can we do it fast via MRW.Write to Utf8JsonWriter? + +// // TODO: Does it matter to pass MRW Options here? +// //BinaryData serializedValue = ModelReaderWriter.Write(dataSource); + +// // Note: One observation here is - working in the "BinaryData space" is +// // different depending on input/output models. We could make it all +// // strongly-typed for input, but we'd have a lot of properties to [EBN]. + +// // If the BinaryData represents JSON, then we need to mutate the JSON +// // to add a collection, and this is expensive. + +// // We could use MutableJsonDocument 😮, JsonNode, etc. +// // We could keep separate property bags for input and output types, i.e. +// // for different scenarios. +// // We could do something like PipelineMessage and hold an ArrayBackedPropertyBag + +// // For now: Let's have serialized raw data and strongly-typed additional properties. +// // This lets us wait to serialize them until we can do it in an optimal fashion. + +// // Add the list if it doesn't already exist +// JsonModelList dataSources; + +// if (request.AdditionalTypedProperties.TryGetValue("data_sources", out object? value)) +// { +// Debug.Assert(value is JsonModelList); + +// dataSources = (value as JsonModelList)!; +// } +// else +// { +// dataSources = []; +// request.AdditionalTypedProperties.Add("data_sources", dataSources); +// } + +// dataSources.Add(dataSource); +// } + +// //// TODO: return type for collection? +// //public static IList? GetDataSources(this CreateChatCompletionRequest request) +// //{ +// // if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) +// // { +// // return null; +// // } + +// // // TODO: Notice that the generator would create this using +// // // the same serialization/deserialization code they would typically +// // // put into a model. + +// // if (property.NameEquals("data_sources"u8)) +// // { +// // if (property.Value.ValueKind == JsonValueKind.Null) +// // { +// // continue; +// // } +// // List array = new List(); +// // foreach (var item in property.Value.EnumerateArray()) +// // { +// // array.Add(AzureChatExtensionConfiguration.DeserializeAzureChatExtensionConfiguration(item, options)); +// // } +// // dataSources = array; +// // continue; +// // } +// //} +//} From 9140b1dfa41161159b8cf5f05b56e4d754d8ff0d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 16 Apr 2024 17:50:09 -0700 Subject: [PATCH 10/32] update to dictionary approach --- .../app/DemoApp/DemoApp.csproj | 1 + .../DemoApp/Mocks/MockPipelineTransport.cs | 196 ++++++++++++++++++ .../app/DemoApp/Mocks/MockRequestHeaders.cs | 55 +++++ .../app/DemoApp/Mocks/ObservablePolicy.cs | 70 +++++++ .../typespec-csharp/app/DemoApp/Program.cs | 191 ++++++++++++++--- .../azoai/AzureOpenAI/AzureChatClient.cs | 85 ++++++-- .../azoai/AzureOpenAI/AzureOpenAI.csproj | 2 +- .../azoai/AzureOpenAI/AzureOpenAIClient.cs | 7 +- .../AzureOpenAI/AzureOpenAIClientOptions.cs | 6 + .../azoai/AzureOpenAI/ChatClientExtensions.cs | 29 +++ .../Internal/ClientPipelineExtensions.cs | 49 +++++ .../Internal/PersistableModelList.cs | 6 +- .../AzureCreateChatCompletionRequest.cs | 18 -- .../Models/AzureModelExtensions.cs | 84 -------- ...hatExtensionConfiguration.Serialization.cs | 0 .../AzureChatExtensionConfiguration.cs | 0 .../{ => Input}/AzureChatExtensionType.cs | 0 ...hatExtensionConfiguration.Serialization.cs | 0 .../AzureSearchChatExtensionConfiguration.cs | 0 ...chChatExtensionParameters.Serialization.cs | 0 .../AzureSearchChatExtensionParameters.cs | 0 .../CreateChatCompletionRequestExtensions.cs | 47 +++++ ...hatExtensionConfiguration.Serialization.cs | 0 .../UnknownAzureChatExtensionConfiguration.cs | 0 ...ataSourceResponseCitation.Serialization.cs | 179 ++++++++++++++++ ...ChatExtensionDataSourceResponseCitation.cs | 94 +++++++++ ...tExtensionsMessageContext.Serialization.cs | 157 ++++++++++++++ .../AzureChatExtensionsMessageContext.cs | 83 ++++++++ ...ChatCompletionResponseMessageExtensions.cs | 17 ++ .../typespec-csharp/src/Generated/Chat.cs | 2 +- ...eateChatCompletionRequest.Serialization.cs | 50 ++--- .../Models/CreateChatCompletionRequest.cs | 9 +- 32 files changed, 1259 insertions(+), 178 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockPipelineTransport.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockRequestHeaders.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/ObservablePolicy.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/ChatClientExtensions.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientPipelineExtensions.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureChatExtensionConfiguration.Serialization.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureChatExtensionConfiguration.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureChatExtensionType.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureSearchChatExtensionConfiguration.Serialization.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureSearchChatExtensionConfiguration.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureSearchChatExtensionParameters.Serialization.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/AzureSearchChatExtensionParameters.cs (100%) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/UnknownAzureChatExtensionConfiguration.Serialization.cs (100%) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/{ => Input}/UnknownAzureChatExtensionConfiguration.cs (100%) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj index 487fd868e..4e4604be7 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/DemoApp.csproj @@ -5,6 +5,7 @@ net6.0 enable enable + latest diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockPipelineTransport.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockPipelineTransport.cs new file mode 100644 index 000000000..1258c9d8a --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockPipelineTransport.cs @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.ClientModel.Primitives; + +namespace ClientModel.Tests.Mocks; + +public class MockPipelineTransport : PipelineTransport +{ + private readonly Func _responseFactory; + private int _retryCount; + + public string Id { get; } + + public Action? OnSendingRequest { get; set; } + public Action? OnReceivedResponse { get; set; } + + public MockPipelineTransport(string id, params int[] codes) + : this(id, i => (codes[i], BinaryData.FromString(string.Empty))) + { + } + + public MockPipelineTransport(string id, Func responseFactory) + { + Id = id; + _responseFactory = responseFactory; + } + + protected override PipelineMessage CreateMessageCore() + { + return new RetriableTransportMessage(); + } + + protected override void ProcessCore(PipelineMessage message) + { + try + { + Stamp(message, "Transport"); + + OnSendingRequest?.Invoke(_retryCount, message); + + if (message is RetriableTransportMessage transportMessage) + { + (int status, BinaryData? content) = _responseFactory(_retryCount); + transportMessage.SetResponse(status, content); + } + + OnReceivedResponse?.Invoke(_retryCount, message); + } + finally + { + _retryCount++; + } + } + + protected override ValueTask ProcessCoreAsync(PipelineMessage message) + { + try + { + Stamp(message, "Transport"); + + OnSendingRequest?.Invoke(_retryCount, message); + + if (message is RetriableTransportMessage transportMessage) + { + (int status, BinaryData? content) = _responseFactory(_retryCount); + transportMessage.SetResponse(status, content); + } + + OnReceivedResponse?.Invoke(_retryCount, message); + } + finally + { + _retryCount++; + } + + return new ValueTask(); + } + + private void Stamp(PipelineMessage message, string prefix) + { + List values; + + if (message.TryGetProperty(typeof(ObservablePolicy), out object? prop) && + prop is List list) + { + values = list; + } + else + { + values = new List(); + message.SetProperty(typeof(ObservablePolicy), values); + } + + values.Add($"{prefix}:{Id}"); + } + + private class RetriableTransportMessage : PipelineMessage + { + public RetriableTransportMessage() : this(new TransportRequest()) + { + } + + protected internal RetriableTransportMessage(PipelineRequest request) : base(request) + { + } + + public void SetResponse(int status, BinaryData? content) + { + Response = new RetriableTransportResponse(status, content); + } + } + + private class TransportRequest : PipelineRequest + { + private Uri? _uri; + private readonly PipelineRequestHeaders _headers; + private string _method; + private BinaryContent? _content; + + public TransportRequest() + { + _headers = new MockRequestHeaders(); + _uri = new Uri("https://www.example.com"); + _method = "GET"; + } + + public override void Dispose() { } + + protected override BinaryContent? ContentCore + { + get => _content; + set => _content = value; + } + + protected override PipelineRequestHeaders HeadersCore + => _headers; + + protected override string MethodCore + { + get => _method; + set => _method = value; + } + + protected override Uri? UriCore + { + get => _uri; + set => _uri = value; + } + } + + private class RetriableTransportResponse : PipelineResponse + { + private Stream? _contentStream; + private BinaryData _content; + + public RetriableTransportResponse(int status, BinaryData? content) + { + Status = status; + ContentStream = content?.ToStream(); + _content = content ?? BinaryData.FromString(string.Empty); + } + + public override int Status { get; } + + public override string ReasonPhrase => throw new NotImplementedException(); + + public override Stream? ContentStream + { + get => _contentStream; + set => _contentStream = value; + } + + public override BinaryData Content => _content; + + protected override PipelineResponseHeaders HeadersCore + => throw new NotImplementedException(); + + public override void Dispose() { } + + public override BinaryData BufferContent(CancellationToken cancellationToken = default) + { + return _content = _contentStream == null ? + BinaryData.FromString(string.Empty) : + BinaryData.FromStream(_contentStream); + } + + public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) + { + return new(_content = _contentStream == null ? + BinaryData.FromString(string.Empty) : + BinaryData.FromStream(_contentStream)); + } + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockRequestHeaders.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockRequestHeaders.cs new file mode 100644 index 000000000..045e3a27e --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/MockRequestHeaders.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; + +namespace ClientModel.Tests.Mocks; + +public class MockRequestHeaders : PipelineRequestHeaders +{ + private readonly Dictionary _headers; + + public MockRequestHeaders() + { + _headers = new Dictionary(); + } + + public override void Add(string name, string value) + { + if (_headers.ContainsKey(name)) + { + _headers[name] += string.Concat(",", value); + } + else + { + _headers[name] = value; + } + } + + public override IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + public override bool Remove(string name) + { + return _headers.Remove(name); + } + + public override void Set(string name, string value) + { + _headers[name] = value; + } + + public override bool TryGetValue(string name, out string? value) + { + return _headers.TryGetValue(name, out value); + } + + public override bool TryGetValues(string name, out IEnumerable? values) + { + throw new NotImplementedException(); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/ObservablePolicy.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/ObservablePolicy.cs new file mode 100644 index 000000000..d70ea3b44 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Mocks/ObservablePolicy.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ClientModel.Tests.Mocks; + +public class ObservablePolicy : PipelinePolicy +{ + public string Id { get; } + protected bool IsLastPolicy { get; set; } = false; + + public ObservablePolicy(string id) + { + Id = id; + } + + public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) + { + Stamp(message, "Request"); + + if (!IsLastPolicy) + { + ProcessNext(message, pipeline, currentIndex); + } + + Stamp(message, "Response"); + } + + public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) + { + Stamp(message, "Request"); + + if (!IsLastPolicy) + { + await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false); + } + + Stamp(message, "Response"); + } + + private void Stamp(PipelineMessage message, string prefix) + { + List values; + + if (message.TryGetProperty(typeof(ObservablePolicy), out object? prop) && + prop is List list) + { + values = list; + } + else + { + values = new List(); + message.SetProperty(typeof(ObservablePolicy), values); + } + + values.Add($"{prefix}:{Id}"); + } + + public static List GetData(PipelineMessage message) + { + message.TryGetProperty(typeof(ObservablePolicy), out object? prop); + + return prop is List list ? list : new List(); + } + + public override string ToString() => $"ObservablePolicy:{Id}"; +} diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index b87d682d1..ca0c149cd 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -1,39 +1,182 @@ -using OpenAI; +using AzureOpenAI; +using AzureOpenAI.Models; +using ClientModel.Tests.Mocks; +using OpenAI; using OpenAI.Models; using System.ClientModel; -using AzureOpenAI.Models; - Console.WriteLine("Hello, World!"); -//Uri endpoint = new("https://annelo-openai-01.openai.azure.com/"); -//string apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!; +//CallUnbrandedService(); +CallAzureService(); + +void CallUnbrandedService() +{ + // + string apiKey = Environment.GetEnvironmentVariable("OPENAI_KEY")!; + + OpenAIClient client = new(new Uri("https://www.mock-oai.com"), new ApiKeyCredential(apiKey), GetUnbrandedClientOptions()); + // + + Chat chatClient = client.GetChatClient(); -string apiKey = Environment.GetEnvironmentVariable("OPENAI_KEY")!; + List messages = new(); + var message = new + { + role = "user", + content = "running over the same old ground" + }; + messages.Add(BinaryData.FromObjectAsJson(message)); -OpenAIClient client = new OpenAIClient(new ApiKeyCredential(apiKey)); -Chat chatClient = client.GetChatClient(); + CreateChatCompletionRequest request = new(messages, CreateChatCompletionRequestModel.Gpt35Turbo); -List messages = new List(); -var message = new + ClientResult result = chatClient.CreateChatCompletion(request); + CreateChatCompletionResponse completion = result.Value; + + // TODO: Do something with output +} + +OpenAIClientOptions GetUnbrandedClientOptions() +{ + OpenAIClientOptions options = new() + { + Transport = new MockPipelineTransport("Transport", i => (200, BinaryData.FromString( + """ + { + "id": "chatcmpl-7R1nGnsXO8n4oi9UPz2f3UHdgAYMn", + "created": 1686676106, + "choices": [ + { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": "Ahoy matey! So ye be wantin' to care for a fine squawkin' parrot, eh? Well, shiver me timbers, let ol' Cap'n Assistant share some wisdom with ye! Here be the steps to keepin' yer parrot happy 'n healthy:\n\n1. Secure a sturdy cage: Yer parrot be needin' a comfortable place to lay anchor! Be sure ye get a sturdy cage, at least double the size of the bird's wingspan, with enough space to spread their wings, yarrrr!\n\n2. Perches 'n toys: Aye, parrots need perches of different sizes, shapes, 'n textures to keep their feet healthy. Also, a few toys be helpin' to keep them entertained 'n their minds stimulated, arrrh!\n\n3. Proper grub: Feed yer feathered friend a balanced diet of high-quality pellets, fruits, 'n veggies to keep 'em strong 'n healthy. Give 'em fresh water every day, or ye’ll have a scurvy bird on yer hands!\n\n4. Cleanliness: Swab their cage deck! Clean their cage on a regular basis: fresh water 'n food daily, the floor every couple of days, 'n a thorough scrubbing ev'ry few weeks, so the bird be livin' in a tidy haven, arrhh!\n\n5. Socialize 'n train: Parrots be a sociable lot, arrr! Exercise 'n interact with 'em daily to create a bond 'n maintain their mental 'n physical health. Train 'em with positive reinforcement, treat 'em kindly, yarrr!\n\n6. Proper rest: Yer parrot be needin' ’bout 10-12 hours o' sleep each night. Cover their cage 'n let them slumber in a dim, quiet quarter for a proper night's rest, ye scallywag!\n\n7. Keep a weather eye open for illness: Birds be hidin' their ailments, arrr! Be watchful for signs of sickness, such as lethargy, loss of appetite, puffin' up, or change in droppings, and make haste to a vet if need be.\n\n8. Provide fresh air 'n avoid toxins: Parrots be sensitive to draft and pollutants. Keep yer quarters well ventilated, but no drafts, arrr! Be mindful of toxins like Teflon fumes, candles, or air fresheners.\n\nSo there ye have it, me hearty! With proper care 'n commitment, yer parrot will be squawkin' \"Yo-ho-ho\" for many years to come! Good luck, sailor, and may the wind be at yer back!" + }, + "logprobs": null + } + ], + "usage": { + "completion_tokens": 557, + "prompt_tokens": 33, + "total_tokens": 590 + } + } + """))) + }; + + return options; +} + +void CallAzureService() { - role = "user", - content = "running over the same old ground" -}; -messages.Add(BinaryData.FromObjectAsJson(message)); + // + Uri endpoint = new("https://annelo-openai-01.openai.azure.com/"); + string apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!; + + AzureOpenAIClient client = new(endpoint, new ApiKeyCredential(apiKey), GetAzureClientOptions()); + // -// Add Azure property -AzureSearchChatExtensionParameters searchParams = new(new Uri("https://azure.search.com"), "MySearchIndex"); -AzureSearchChatExtensionConfiguration dataSource = new(searchParams); + Chat chatClient = client.GetChatClient(); -CreateChatCompletionRequest request = new CreateChatCompletionRequest(messages, CreateChatCompletionRequestModel.Gpt35Turbo); -request.SetAzureDataSource(dataSource); + List messages = new(); + var message = new + { + role = "user", + content = "running over the same old ground" + }; + messages.Add(BinaryData.FromObjectAsJson(message)); -ClientResult result = chatClient.CreateChatCompletion(request); + CreateChatCompletionRequest request = new(messages, CreateChatCompletionRequestModel.Gpt35Turbo); -//AzureOpenAIClient client = new AzureOpenAIClient(endpoint, new ApiKeyCredential(apiKey)); -//Chat chatClient = client.GetChatClient("gpt-35-turbo-instruct"); + // + // Add Azure input property via extension methods + request.GetDataSources().Add(GetAzureSearchDataSource()); -//ClientResult result = await chatClient.CompleteChatAsync() + // -// TODO: MRW story - input model and output models too. + ClientResult result = chatClient.CreateChatCompletion(request); + CreateChatCompletionResponse completion = result.Value; + + // + // Use Azure output property via extension methods + AzureChatExtensionsMessageContext? azureContext = completion.Choices[0].Message.GetAzureExtensionsContext(); + + if (azureContext is not null && azureContext.Citations.Count > 0) + { + int i = 0; + foreach (var citation in azureContext.Citations) + { + Console.WriteLine($"Citation {i++}: Content={citation.Content}, Uri={citation.Url}, Title={citation.Title}"); + } + } + // + + // TODO: illustrate calls to protocol method overloads. +} + +AzureSearchChatExtensionConfiguration GetAzureSearchDataSource() +{ + AzureSearchChatExtensionParameters searchParams = new(new Uri("https://azure.search.com"), "MySearchIndex"); + return new(searchParams); +} + +AzureOpenAIClientOptions GetAzureClientOptions() +{ + MockPipelineTransport mockTransport = new("Transport", i => (200, BinaryData.FromString( + """ + { + "id": "chatcmpl-7R1nGnsXO8n4oi9UPz2f3UHdgAYMn", + "created": 1686676106, + "choices": [ + { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": "Ahoy matey! So ye be wantin' to care for a fine squawkin' parrot, eh? Well, shiver me timbers, let ol' Cap'n Assistant share some wisdom with ye! Here be the steps to keepin' yer parrot happy 'n healthy:\n\n1. Secure a sturdy cage: Yer parrot be needin' a comfortable place to lay anchor! Be sure ye get a sturdy cage, at least double the size of the bird's wingspan, with enough space to spread their wings, yarrrr!\n\n2. Perches 'n toys: Aye, parrots need perches of different sizes, shapes, 'n textures to keep their feet healthy. Also, a few toys be helpin' to keep them entertained 'n their minds stimulated, arrrh!\n\n3. Proper grub: Feed yer feathered friend a balanced diet of high-quality pellets, fruits, 'n veggies to keep 'em strong 'n healthy. Give 'em fresh water every day, or ye’ll have a scurvy bird on yer hands!\n\n4. Cleanliness: Swab their cage deck! Clean their cage on a regular basis: fresh water 'n food daily, the floor every couple of days, 'n a thorough scrubbing ev'ry few weeks, so the bird be livin' in a tidy haven, arrhh!\n\n5. Socialize 'n train: Parrots be a sociable lot, arrr! Exercise 'n interact with 'em daily to create a bond 'n maintain their mental 'n physical health. Train 'em with positive reinforcement, treat 'em kindly, yarrr!\n\n6. Proper rest: Yer parrot be needin' ’bout 10-12 hours o' sleep each night. Cover their cage 'n let them slumber in a dim, quiet quarter for a proper night's rest, ye scallywag!\n\n7. Keep a weather eye open for illness: Birds be hidin' their ailments, arrr! Be watchful for signs of sickness, such as lethargy, loss of appetite, puffin' up, or change in droppings, and make haste to a vet if need be.\n\n8. Provide fresh air 'n avoid toxins: Parrots be sensitive to draft and pollutants. Keep yer quarters well ventilated, but no drafts, arrr! Be mindful of toxins like Teflon fumes, candles, or air fresheners.\n\nSo there ye have it, me hearty! With proper care 'n commitment, yer parrot will be squawkin' \"Yo-ho-ho\" for many years to come! Good luck, sailor, and may the wind be at yer back!", + "context": { + "citations": [ + { + "content": "Content of the citation", + "url": "https://www.example.com", + "title": "Title of the citation", + "filepath": "path/to/file", + "chunk_id": "chunk-id" + } + ] + } + }, + "logprobs": null + } + ], + "usage": { + "completion_tokens": 557, + "prompt_tokens": 33, + "total_tokens": 590 + } + } + """))); + + mockTransport.OnSendingRequest = (i, m) => + { + Console.WriteLine("Request:"); + Console.WriteLine($" Uri='{m.Request.Uri}'"); + Console.WriteLine($" Content='{WriteAsString(m.Request.Content!)}'"); + }; + + AzureOpenAIClientOptions options = new() + { + //Transport = mockTransport + }; + + return options; +} + +string WriteAsString(BinaryContent content) +{ + MemoryStream stream = new(); + content.WriteTo(stream); + stream.Position = 0; + return BinaryData.FromStream(stream).ToString(); +} \ No newline at end of file diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index 20ace0473..c81b2420c 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -5,28 +5,82 @@ namespace AzureOpenAI; -public class AzureChatClient : Chat +internal class AzureChatClient : Chat { - internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, Uri endpoint) + private readonly string _apiVersion; + + internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, Uri endpoint, string apiVersion) : base(pipeline, credential, endpoint) { + _apiVersion = apiVersion; + } + + public override async Task> CreateChatCompletionAsync(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); + + using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); + + ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); + + PipelineResponse response = result.GetRawResponse(); + + // TODO: handle null case + CreateChatCompletionResponse value = ModelReaderWriter.Read(response.Content)!; + return ClientResult.FromValue(value, response); + } + + public override ClientResult CreateChatCompletion(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); + + using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); + + ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); + + PipelineResponse response = result.GetRawResponse(); + + // TODO: handle null case + CreateChatCompletionResponse value = ModelReaderWriter.Read(response.Content)!; + return ClientResult.FromValue(value, response); + } + + public override Task CreateChatCompletionAsync(BinaryContent content, RequestOptions context = null) + { + // Note, that we can later remap the values from the 3rd party client format to the + // Azure client format, but this has a perf cost and it's an improvement that can + // come later. + throw new InvalidOperationException("Improperly formatted content -- Azure service requires different format. " + + $"Please consult the REST API documentation and call the '{nameof(CreateChatCompletion)}' method overload that " + + "takes a 'model' parameter."); } - public Task> CreateChatCompletionAsync(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) + public override ClientResult CreateChatCompletion(BinaryContent content, RequestOptions context = null) { - return base.CreateChatCompletionAsync(createChatCompletionRequest, cancellationToken); + throw new InvalidOperationException("Improperly formatted content -- Azure service requires different format. " + + $"Please consult the REST API documentation and call the '{nameof(CreateChatCompletion)}' method overload that " + + "takes a 'model' parameter."); + } + + public ClientResult CreateChatCompletion(string model, BinaryContent content, RequestOptions context = null) + { + Argument.AssertNotNull(model, nameof(model)); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateCreateChatCompletionRequest(model, content, context); + return ClientResult.FromResponse(Pipeline.ProcessMessage(message, context)); + } + + public async Task CreateChatCompletionAsync(string model, BinaryContent content, RequestOptions context = null) + { + Argument.AssertNotNull(model, nameof(model)); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateCreateChatCompletionRequest(model, content, context); + return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false)); } - // TODO: Show how this would differ for this case. Do we still need OperationName and the - // remapping policy? - // 1. Version parameter - // 2. Auth key is different - does that show up here or in the client? - // 3. DeploymentId in path - // - // Note: Model content is already serialized by the time we get here. Nothing - // content-related should happen in this method. If we can show that, do we need - // to make these methods protected virtual? - protected override PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) + private PipelineMessage CreateCreateChatCompletionRequest(string model, BinaryContent content, RequestOptions context) { var message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -36,7 +90,10 @@ protected override PipelineMessage CreateCreateChatCompletionRequest(BinaryConte var uri = new ClientUriBuilder(); uri.Reset(Endpoint); + uri.AppendPath("/openai/deployments/", false); + uri.AppendPath(model, false); uri.AppendPath("/chat/completions", false); + uri.AppendQuery("api-version", _apiVersion, true); request.Uri = uri.ToUri(); request.Headers.Set("Accept", "application/json"); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj index 467bc0f2a..460feb8c7 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAI.csproj @@ -1,7 +1,7 @@ - net6.0 + netstandard2.0 enable enable latest diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs index 5f8175a8e..092653950 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs @@ -5,13 +5,18 @@ namespace AzureOpenAI; public class AzureOpenAIClient : OpenAIClient { + private readonly string _apiVersion; + public AzureOpenAIClient(Uri endpoint, ApiKeyCredential credential, AzureOpenAIClientOptions? options = default) : base(endpoint, credential, options) { + options ??= new AzureOpenAIClientOptions(); + + _apiVersion = options.ApiVersion; } public override Chat GetChatClient() { - return new AzureChatClient(Pipeline, KeyCredential, Endpoint); + return new AzureChatClient(Pipeline, KeyCredential, Endpoint, _apiVersion); } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs index 70ba5197d..23cc45ae3 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClientOptions.cs @@ -4,4 +4,10 @@ namespace AzureOpenAI; public class AzureOpenAIClientOptions : OpenAIClientOptions { + public AzureOpenAIClientOptions(string? version = default) + { + ApiVersion = version ?? "1.0"; + } + + public string ApiVersion { get; set; } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/ChatClientExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/ChatClientExtensions.cs new file mode 100644 index 000000000..0bef56f06 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/ChatClientExtensions.cs @@ -0,0 +1,29 @@ +using OpenAI; +using System.ClientModel; +using System.ClientModel.Primitives; + +namespace AzureOpenAI; + +public static class ChatClientExtensions +{ + // Expose overloads of protocol methods as extensions to the base client + public static ClientResult CreateChatCompletion(this Chat client, string model, BinaryContent content, RequestOptions options = default) + { + if (client is not AzureChatClient azureClient) + { + throw new NotSupportedException("Cannot call CreateChatCompletion with 'model' parameter when not using the Azure OpeAI client."); + } + + return azureClient.CreateChatCompletion(model, content, options); + } + + public static async Task CreateChatCompletionAsync(this Chat client, string model, BinaryContent content, RequestOptions options = default) + { + if (client is not AzureChatClient azureClient) + { + throw new NotSupportedException("Cannot call CreateChatCompletionAsync with 'model' parameter when not using the Azure OpeAI client."); + } + + return await azureClient.CreateChatCompletionAsync(model, content, options).ConfigureAwait(false); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientPipelineExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientPipelineExtensions.cs new file mode 100644 index 000000000..356acda94 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/ClientPipelineExtensions.cs @@ -0,0 +1,49 @@ +// + +#nullable disable + +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Threading; +using System.Threading.Tasks; + +namespace OpenAI +{ + internal static class ClientPipelineExtensions + { + public static async ValueTask ProcessMessageAsync(this ClientPipeline pipeline, PipelineMessage message, RequestOptions requestContext, CancellationToken cancellationToken = default) + { + await pipeline.SendAsync(message).ConfigureAwait(false); + + if (message.Response == null) + { + throw new InvalidOperationException("Failed to receive Result."); + } + + if (!message.Response.IsError || requestContext?.ErrorOptions == ClientErrorBehaviors.NoThrow) + { + return message.Response; + } + + throw new ClientResultException(message.Response); + } + + public static PipelineResponse ProcessMessage(this ClientPipeline pipeline, PipelineMessage message, RequestOptions requestContext, CancellationToken cancellationToken = default) + { + pipeline.Send(message); + + if (message.Response == null) + { + throw new InvalidOperationException("Failed to receive Result."); + } + + if (!message.Response.IsError || requestContext?.ErrorOptions == ClientErrorBehaviors.NoThrow) + { + return message.Response; + } + + throw new ClientResultException(message.Response); + } + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs index c97e68095..b12307872 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs @@ -3,7 +3,8 @@ namespace AzureOpenAI; -internal class PersistableModelList : List>, IPersistableModel> +internal class PersistableModelList : List, IPersistableModel> + where TModel : IPersistableModel { public PersistableModelList Create(BinaryData data, ModelReaderWriterOptions options) { @@ -21,7 +22,8 @@ public BinaryData Write(ModelReaderWriterOptions options) } } -internal class JsonModelList : List>, IJsonModel> +internal class JsonModelList : List, IJsonModel> + where TModel : IJsonModel { public JsonModelList Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) { diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs deleted file mode 100644 index fedde4a54..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureCreateChatCompletionRequest.cs +++ /dev/null @@ -1,18 +0,0 @@ -using OpenAI.Models; - -namespace AzureOpenAI.Models; - -internal class AzureCreateChatCompletionRequest : CreateChatCompletionRequest -{ - // _serializedAdditionalRawData - private readonly Dictionary _azureProperties; - internal Dictionary AzureProperties => _azureProperties; - - public AzureCreateChatCompletionRequest(IEnumerable messages, CreateChatCompletionRequestModel model) - : base(messages, model) - { - _azureProperties = new Dictionary(); - } - - // TODO: Add data_sources field -} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs deleted file mode 100644 index e68d21f11..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureModelExtensions.cs +++ /dev/null @@ -1,84 +0,0 @@ -using OpenAI.Models; -using System.ClientModel; -using System.Diagnostics; - -namespace AzureOpenAI.Models; - -//public static class AzureModelExtensions -//{ -// public static BinaryContent ToBinaryBody(this CreateChatCompletionRequest request) -// { -// throw new NotImplementedException(); -// } - -// public static void SetAzureDataSource(this CreateChatCompletionRequest request, AzureChatExtensionConfiguration dataSource) -// { -// Argument.AssertNotNull(dataSource, nameof(dataSource)); - -// // TODO: we can do it slow via MRW.Write to BinaryData; -// // can we do it fast via MRW.Write to Utf8JsonWriter? - -// // TODO: Does it matter to pass MRW Options here? -// //BinaryData serializedValue = ModelReaderWriter.Write(dataSource); - -// // Note: One observation here is - working in the "BinaryData space" is -// // different depending on input/output models. We could make it all -// // strongly-typed for input, but we'd have a lot of properties to [EBN]. - -// // If the BinaryData represents JSON, then we need to mutate the JSON -// // to add a collection, and this is expensive. - -// // We could use MutableJsonDocument 😮, JsonNode, etc. -// // We could keep separate property bags for input and output types, i.e. -// // for different scenarios. -// // We could do something like PipelineMessage and hold an ArrayBackedPropertyBag - -// // For now: Let's have serialized raw data and strongly-typed additional properties. -// // This lets us wait to serialize them until we can do it in an optimal fashion. - -// // Add the list if it doesn't already exist -// JsonModelList dataSources; - -// if (request.AdditionalTypedProperties.TryGetValue("data_sources", out object? value)) -// { -// Debug.Assert(value is JsonModelList); - -// dataSources = (value as JsonModelList)!; -// } -// else -// { -// dataSources = []; -// request.AdditionalTypedProperties.Add("data_sources", dataSources); -// } - -// dataSources.Add(dataSource); -// } - -// //// TODO: return type for collection? -// //public static IList? GetDataSources(this CreateChatCompletionRequest request) -// //{ -// // if (!request.AzureProperties.TryGetValue("data_sources", out BinaryData? value)) -// // { -// // return null; -// // } - -// // // TODO: Notice that the generator would create this using -// // // the same serialization/deserialization code they would typically -// // // put into a model. - -// // if (property.NameEquals("data_sources"u8)) -// // { -// // if (property.Value.ValueKind == JsonValueKind.Null) -// // { -// // continue; -// // } -// // List array = new List(); -// // foreach (var item in property.Value.EnumerateArray()) -// // { -// // array.Add(AzureChatExtensionConfiguration.DeserializeAzureChatExtensionConfiguration(item, options)); -// // } -// // dataSources = array; -// // continue; -// // } -// //} -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.Serialization.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.Serialization.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.Serialization.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionConfiguration.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionType.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionType.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureChatExtensionType.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionType.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionConfiguration.Serialization.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.Serialization.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionConfiguration.Serialization.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionConfiguration.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionConfiguration.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionConfiguration.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionParameters.Serialization.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.Serialization.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionParameters.Serialization.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionParameters.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/AzureSearchChatExtensionParameters.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureSearchChatExtensionParameters.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs new file mode 100644 index 000000000..16fdc5e1c --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -0,0 +1,47 @@ +using OpenAI.Models; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace AzureOpenAI.Models; + +public static class CreateChatCompletionRequestExtensions +{ + // Input model property + // Note: on the input-side, we are unable to type-check that we're using an Azure + // model, because the end-user created an instance of an unbranded model. We'll + // later convert this to an Azure model that can serialize to the Azure format, + // but for now, we need to stash the values in a collection of objects that we'll + // use to populate the Azure model properties later. + public static IList GetDataSources(this CreateChatCompletionRequest request) + { + // TODO: How can we validate that this is being called in the right context, + // e.g. user is using an Azure client instance and not an unbranded one? + + // TODO: What is the interplay of SerializedAdditionalRawData and + // AdditionalTypedProperties? i.e. in a round-trip scenario, if we + // wanted to mutate a SeriazliedAdditionalRawData, do we deserialize it + // and stick it in AdditionalTypedProperties (are these "deserialized + // additional properties, e.g.?). + + // TODO: what is our concurrency story for models in unbranded clients? + // Are we happy with taking the Azure client stance of "models don't need + // to be thread-safe because they are rarely shared between threads"? + JsonModelList dataSources; + + if (request.SerializedAdditionalRawData.TryGetValue("data_sources", out object? value)) + { + Debug.Assert(value is JsonModelList); + + dataSources = (value as JsonModelList)!; + } + else + { + dataSources = []; + request.SerializedAdditionalRawData.Add("data_sources", dataSources); + } + + return dataSources; + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/UnknownAzureChatExtensionConfiguration.Serialization.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.Serialization.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/UnknownAzureChatExtensionConfiguration.Serialization.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/UnknownAzureChatExtensionConfiguration.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/UnknownAzureChatExtensionConfiguration.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/UnknownAzureChatExtensionConfiguration.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.Serialization.cs new file mode 100644 index 000000000..11773544c --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.Serialization.cs @@ -0,0 +1,179 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +public partial class AzureChatExtensionDataSourceResponseCitation : IJsonModel +{ + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionDataSourceResponseCitation)} does not support writing '{format}' format."); + } + + writer.WriteStartObject(); + writer.WritePropertyName("content"u8); + writer.WriteStringValue(Content); + if (Optional.IsDefined(Title)) + { + writer.WritePropertyName("title"u8); + writer.WriteStringValue(Title); + } + if (Optional.IsDefined(Url)) + { + writer.WritePropertyName("url"u8); + writer.WriteStringValue(Url); + } + if (Optional.IsDefined(Filepath)) + { + writer.WritePropertyName("filepath"u8); + writer.WriteStringValue(Filepath); + } + if (Optional.IsDefined(ChunkId)) + { + writer.WritePropertyName("chunk_id"u8); + writer.WriteStringValue(ChunkId); + } + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + writer.WriteEndObject(); + } + + AzureChatExtensionDataSourceResponseCitation IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionDataSourceResponseCitation)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureChatExtensionDataSourceResponseCitation(document.RootElement, options); + } + + internal static AzureChatExtensionDataSourceResponseCitation DeserializeAzureChatExtensionDataSourceResponseCitation(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string content = default; + string title = default; + string url = default; + string filepath = default; + string chunkId = default; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("content"u8)) + { + content = property.Value.GetString(); + continue; + } + if (property.NameEquals("title"u8)) + { + title = property.Value.GetString(); + continue; + } + if (property.NameEquals("url"u8)) + { + url = property.Value.GetString(); + continue; + } + if (property.NameEquals("filepath"u8)) + { + filepath = property.Value.GetString(); + continue; + } + if (property.NameEquals("chunk_id"u8)) + { + chunkId = property.Value.GetString(); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + serializedAdditionalRawData = rawDataDictionary; + return new AzureChatExtensionDataSourceResponseCitation( + content, + title, + url, + filepath, + chunkId, + serializedAdditionalRawData); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureChatExtensionDataSourceResponseCitation)} does not support writing '{options.Format}' format."); + } + } + + AzureChatExtensionDataSourceResponseCitation IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureChatExtensionDataSourceResponseCitation(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureChatExtensionDataSourceResponseCitation)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + ///// Deserializes the model from a raw response. + ///// The response to deserialize the model from. + //internal static AzureChatExtensionDataSourceResponseCitation FromResponse(Response response) + //{ + // using var document = JsonDocument.Parse(response.Content); + // return DeserializeAzureChatExtensionDataSourceResponseCitation(document.RootElement); + //} + + ///// Convert into a Utf8JsonRequestContent. + //internal virtual RequestContent ToRequestContent() + //{ + // var content = new Utf8JsonRequestContent(); + // content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + // return content; + //} +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.cs new file mode 100644 index 000000000..4c4e65481 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionDataSourceResponseCitation.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace AzureOpenAI.Models; + +/// +/// A single instance of additional context information available when Azure OpenAI chat extensions are involved +/// in the generation of a corresponding chat completions response. This context information is only populated when +/// using an Azure OpenAI request configured to use a matching extension. +/// +public partial class AzureChatExtensionDataSourceResponseCitation +{ + /// + /// Keeps track of any properties unknown to the library. + /// + /// To assign an object to the value of this property use . + /// + /// + /// To assign an already formatted json string to this property use . + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromObjectAsJson("foo") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromString("\"foo\"") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromObjectAsJson(new { key = "value" }) + /// Creates a payload of { "key": "value" }. + /// + /// + /// BinaryData.FromString("{\"key\": \"value\"}") + /// Creates a payload of { "key": "value" }. + /// + /// + /// + /// + private IDictionary _serializedAdditionalRawData; + + /// Initializes a new instance of . + /// The content of the citation. + /// is null. + internal AzureChatExtensionDataSourceResponseCitation(string content) + { + Argument.AssertNotNull(content, nameof(content)); + + Content = content; + } + + /// Initializes a new instance of . + /// The content of the citation. + /// The title of the citation. + /// The URL of the citation. + /// The file path of the citation. + /// The chunk ID of the citation. + /// Keeps track of any properties unknown to the library. + internal AzureChatExtensionDataSourceResponseCitation(string content, string title, string url, string filepath, string chunkId, IDictionary serializedAdditionalRawData) + { + Content = content; + Title = title; + Url = url; + Filepath = filepath; + ChunkId = chunkId; + _serializedAdditionalRawData = serializedAdditionalRawData; + } + + /// Initializes a new instance of for deserialization. + internal AzureChatExtensionDataSourceResponseCitation() + { + } + + /// The content of the citation. + public string Content { get; } + /// The title of the citation. + public string Title { get; } + /// The URL of the citation. + public string Url { get; } + /// The file path of the citation. + public string Filepath { get; } + /// The chunk ID of the citation. + public string ChunkId { get; } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.Serialization.cs new file mode 100644 index 000000000..b5367eb5f --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.Serialization.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +public partial class AzureChatExtensionsMessageContext : IJsonModel +{ + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionsMessageContext)} does not support writing '{format}' format."); + } + + writer.WriteStartObject(); + if (Optional.IsCollectionDefined(Citations)) + { + writer.WritePropertyName("citations"u8); + writer.WriteStartArray(); + foreach (var item in Citations) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(Intent)) + { + writer.WritePropertyName("intent"u8); + writer.WriteStringValue(Intent); + } + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + writer.WriteEndObject(); + } + + AzureChatExtensionsMessageContext IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(AzureChatExtensionsMessageContext)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureChatExtensionsMessageContext(document.RootElement, options); + } + + internal static AzureChatExtensionsMessageContext DeserializeAzureChatExtensionsMessageContext(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + IReadOnlyList citations = default; + string intent = default; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("citations"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(AzureChatExtensionDataSourceResponseCitation.DeserializeAzureChatExtensionDataSourceResponseCitation(item, options)); + } + citations = array; + continue; + } + if (property.NameEquals("intent"u8)) + { + intent = property.Value.GetString(); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + serializedAdditionalRawData = rawDataDictionary; + return new AzureChatExtensionsMessageContext(citations ?? new ChangeTrackingList(), intent, serializedAdditionalRawData); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(AzureChatExtensionsMessageContext)} does not support writing '{options.Format}' format."); + } + } + + AzureChatExtensionsMessageContext IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureChatExtensionsMessageContext(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(AzureChatExtensionsMessageContext)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + ///// Deserializes the model from a raw response. + ///// The response to deserialize the model from. + //internal static AzureChatExtensionsMessageContext FromResponse(Response response) + //{ + // using var document = JsonDocument.Parse(response.Content); + // return DeserializeAzureChatExtensionsMessageContext(document.RootElement); + //} + + ///// Convert into a Utf8JsonRequestContent. + //internal virtual RequestContent ToRequestContent() + //{ + // var content = new Utf8JsonRequestContent(); + // content.JsonWriter.WriteObjectValue(this, new ModelReaderWriterOptions("W")); + // return content; + //} +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.cs new file mode 100644 index 000000000..ac4e3c880 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatExtensionsMessageContext.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace AzureOpenAI.Models; + +/// +/// A representation of the additional context information available when Azure OpenAI chat extensions are involved +/// in the generation of a corresponding chat completions response. This context information is only populated when +/// using an Azure OpenAI request configured to use a matching extension. +/// +public partial class AzureChatExtensionsMessageContext +{ + /// + /// Keeps track of any properties unknown to the library. + /// + /// To assign an object to the value of this property use . + /// + /// + /// To assign an already formatted json string to this property use . + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromObjectAsJson("foo") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromString("\"foo\"") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromObjectAsJson(new { key = "value" }) + /// Creates a payload of { "key": "value" }. + /// + /// + /// BinaryData.FromString("{\"key\": \"value\"}") + /// Creates a payload of { "key": "value" }. + /// + /// + /// + /// + private IDictionary _serializedAdditionalRawData; + + /// Initializes a new instance of . + internal AzureChatExtensionsMessageContext() + { + Citations = new ChangeTrackingList(); + } + + /// Initializes a new instance of . + /// + /// The contextual information associated with the Azure chat extensions used for a chat completions request. + /// These messages describe the data source retrievals, plugin invocations, and other intermediate steps taken in the + /// course of generating a chat completions response that was augmented by capabilities from Azure OpenAI chat + /// extensions. + /// + /// The detected intent from the chat history, used to pass to the next turn to carry over the context. + /// Keeps track of any properties unknown to the library. + internal AzureChatExtensionsMessageContext(IReadOnlyList citations, string intent, IDictionary serializedAdditionalRawData) + { + Citations = citations; + Intent = intent; + _serializedAdditionalRawData = serializedAdditionalRawData; + } + + /// + /// The contextual information associated with the Azure chat extensions used for a chat completions request. + /// These messages describe the data source retrievals, plugin invocations, and other intermediate steps taken in the + /// course of generating a chat completions response that was augmented by capabilities from Azure OpenAI chat + /// extensions. + /// + public IReadOnlyList Citations { get; } + /// The detected intent from the chat history, used to pass to the next turn to carry over the context. + public string Intent { get; } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs new file mode 100644 index 000000000..6b3d7af11 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs @@ -0,0 +1,17 @@ +using OpenAI.Models; + +namespace AzureOpenAI.Models; + +public static class ChatCompletionResponseMessageExtensions +{ + // Output property + public static AzureChatExtensionsMessageContext? GetAzureExtensionsContext(this ChatCompletionResponseMessage message) + { + // TODO: How to throw if used incorrectly? + + // TODO: retrieve from dictionary + //return azureMessage.AzureExtensionsContext; + + throw new NotImplementedException(); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs index 92df1745c..e8c197777 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs @@ -126,7 +126,7 @@ public virtual ClientResult CreateChatCompletion(BinaryContent content, RequestO return ClientResult.FromResponse(_pipeline.ProcessMessage(message, context)); } - protected virtual PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) + private PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) { var message = _pipeline.CreateMessage(); if (context != null) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index 2eafcd7da..55f59d851 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -257,40 +257,34 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { foreach (var item in _serializedAdditionalRawData) { - writer.WritePropertyName(item.Key); + // TODO: revisit for "dictionary approach" + if (item.Value is BinaryData serializedValue) + { + writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); + writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(item.Value)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } + using (JsonDocument document = JsonDocument.Parse(serializedValue)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } #endif - } - } - - // Note: we were holding strongly-typed values for added Azure properties - // We have to serialize them separately here. - // TODO: Does the format matter here? It seems like we want to write them - // in every case. - if (_additionalTypedProperties != null) - { - foreach (var property in _additionalTypedProperties) - { - // TODO: it might be nice to have generated collections that - // implement IPersistableModel, IJsonModel - if (property.Value is not IJsonModel model) - { - // TODO: how do we validate this on the input side? - throw new InvalidOperationException($"invalid typed property, type is '{property.Value.GetType()}'"); } - writer.WritePropertyName(property.Key); + // Note: we were holding strongly-typed values for added Azure properties + // We have to serialize them separately here. + // TODO: Does the format matter here? It seems like we want to write them + // in every case. + else if (item.Value is IJsonModel model) + { + writer.WritePropertyName(item.Key); - // Note: what if it's just a primitive, i.e. a string or int, what do we do? - model.Write(writer, options); + // Note: what if it's just a primitive, i.e. a string or int, what do we do? + model.Write(writer, options); + } } } + writer.WriteEndObject(); } @@ -334,8 +328,8 @@ internal static CreateChatCompletionRequest DeserializeCreateChatCompletionReque string user = default; BinaryData functionCall = default; IList functions = default; - IDictionary serializedAdditionalRawData = default; - Dictionary additionalPropertiesDictionary = new Dictionary(); + IDictionary serializedAdditionalRawData = default; + Dictionary additionalPropertiesDictionary = new Dictionary(); foreach (var property in element.EnumerateObject()) { if (property.NameEquals("messages"u8)) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index 9d1ffee21..49f9cfefe 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -42,11 +42,10 @@ public partial class CreateChatCompletionRequest /// /// /// - private IDictionary _serializedAdditionalRawData; - - private IDictionary _additionalTypedProperties; + private IDictionary _serializedAdditionalRawData; [EditorBrowsable(EditorBrowsableState.Never)] - public IDictionary AdditionalTypedProperties => _additionalTypedProperties ??= new Dictionary(); + // TODO: lazily instantiate; also, add it to all models. + public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); /// Initializes a new instance of . /// @@ -192,7 +191,7 @@ public CreateChatCompletionRequest(IEnumerable messages, CreateChatC /// A list of functions the model may generate JSON inputs for. /// /// Keeps track of any properties unknown to the library. - internal CreateChatCompletionRequest(IList messages, CreateChatCompletionRequestModel model, double? frequencyPenalty, IDictionary logitBias, bool? logprobs, long? topLogprobs, long? maxTokens, long? n, double? presencePenalty, CreateChatCompletionRequestResponseFormat responseFormat, long? seed, BinaryData stop, bool? stream, double? temperature, double? topP, IList tools, BinaryData toolChoice, string user, BinaryData functionCall, IList functions, IDictionary serializedAdditionalRawData) + internal CreateChatCompletionRequest(IList messages, CreateChatCompletionRequestModel model, double? frequencyPenalty, IDictionary logitBias, bool? logprobs, long? topLogprobs, long? maxTokens, long? n, double? presencePenalty, CreateChatCompletionRequestResponseFormat responseFormat, long? seed, BinaryData stop, bool? stream, double? temperature, double? topP, IList tools, BinaryData toolChoice, string user, BinaryData functionCall, IList functions, IDictionary serializedAdditionalRawData) { Messages = messages; Model = model; From fa9fbffffc44ae887ea35921b2fac4e4a6a9c55d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 17 Apr 2024 16:10:07 -0700 Subject: [PATCH 11/32] nits --- .../typespec-csharp/app/DemoApp/Program.cs | 71 ++++++++++--------- ...hatExtensionConfiguration.Serialization.cs | 6 +- .../Input/AzureChatExtensionConfiguration.cs | 2 +- .../CreateChatCompletionRequestExtensions.cs | 9 +-- ...eateChatCompletionRequest.Serialization.cs | 6 +- 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index ca0c149cd..ad9a9ae15 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -7,15 +7,15 @@ Console.WriteLine("Hello, World!"); -//CallUnbrandedService(); +//CallThirdPartyService(); CallAzureService(); -void CallUnbrandedService() +void CallThirdPartyService() { // string apiKey = Environment.GetEnvironmentVariable("OPENAI_KEY")!; - OpenAIClient client = new(new Uri("https://www.mock-oai.com"), new ApiKeyCredential(apiKey), GetUnbrandedClientOptions()); + OpenAIClient client = new(new Uri("https://www.mock-oai.com"), new ApiKeyCredential(apiKey), GetThirdPartyClientOptions()); // Chat chatClient = client.GetChatClient(); @@ -36,38 +36,6 @@ void CallUnbrandedService() // TODO: Do something with output } -OpenAIClientOptions GetUnbrandedClientOptions() -{ - OpenAIClientOptions options = new() - { - Transport = new MockPipelineTransport("Transport", i => (200, BinaryData.FromString( - """ - { - "id": "chatcmpl-7R1nGnsXO8n4oi9UPz2f3UHdgAYMn", - "created": 1686676106, - "choices": [ - { - "index": 0, - "finish_reason": "stop", - "message": { - "role": "assistant", - "content": "Ahoy matey! So ye be wantin' to care for a fine squawkin' parrot, eh? Well, shiver me timbers, let ol' Cap'n Assistant share some wisdom with ye! Here be the steps to keepin' yer parrot happy 'n healthy:\n\n1. Secure a sturdy cage: Yer parrot be needin' a comfortable place to lay anchor! Be sure ye get a sturdy cage, at least double the size of the bird's wingspan, with enough space to spread their wings, yarrrr!\n\n2. Perches 'n toys: Aye, parrots need perches of different sizes, shapes, 'n textures to keep their feet healthy. Also, a few toys be helpin' to keep them entertained 'n their minds stimulated, arrrh!\n\n3. Proper grub: Feed yer feathered friend a balanced diet of high-quality pellets, fruits, 'n veggies to keep 'em strong 'n healthy. Give 'em fresh water every day, or ye’ll have a scurvy bird on yer hands!\n\n4. Cleanliness: Swab their cage deck! Clean their cage on a regular basis: fresh water 'n food daily, the floor every couple of days, 'n a thorough scrubbing ev'ry few weeks, so the bird be livin' in a tidy haven, arrhh!\n\n5. Socialize 'n train: Parrots be a sociable lot, arrr! Exercise 'n interact with 'em daily to create a bond 'n maintain their mental 'n physical health. Train 'em with positive reinforcement, treat 'em kindly, yarrr!\n\n6. Proper rest: Yer parrot be needin' ’bout 10-12 hours o' sleep each night. Cover their cage 'n let them slumber in a dim, quiet quarter for a proper night's rest, ye scallywag!\n\n7. Keep a weather eye open for illness: Birds be hidin' their ailments, arrr! Be watchful for signs of sickness, such as lethargy, loss of appetite, puffin' up, or change in droppings, and make haste to a vet if need be.\n\n8. Provide fresh air 'n avoid toxins: Parrots be sensitive to draft and pollutants. Keep yer quarters well ventilated, but no drafts, arrr! Be mindful of toxins like Teflon fumes, candles, or air fresheners.\n\nSo there ye have it, me hearty! With proper care 'n commitment, yer parrot will be squawkin' \"Yo-ho-ho\" for many years to come! Good luck, sailor, and may the wind be at yer back!" - }, - "logprobs": null - } - ], - "usage": { - "completion_tokens": 557, - "prompt_tokens": 33, - "total_tokens": 590 - } - } - """))) - }; - - return options; -} - void CallAzureService() { // @@ -121,6 +89,39 @@ AzureSearchChatExtensionConfiguration GetAzureSearchDataSource() return new(searchParams); } +OpenAIClientOptions GetThirdPartyClientOptions() +{ + OpenAIClientOptions options = new() + { + Transport = new MockPipelineTransport("Transport", i => (200, BinaryData.FromString( + """ + { + "id": "chatcmpl-7R1nGnsXO8n4oi9UPz2f3UHdgAYMn", + "created": 1686676106, + "choices": [ + { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": "Ahoy matey! So ye be wantin' to care for a fine squawkin' parrot, eh? Well, shiver me timbers, let ol' Cap'n Assistant share some wisdom with ye! Here be the steps to keepin' yer parrot happy 'n healthy:\n\n1. Secure a sturdy cage: Yer parrot be needin' a comfortable place to lay anchor! Be sure ye get a sturdy cage, at least double the size of the bird's wingspan, with enough space to spread their wings, yarrrr!\n\n2. Perches 'n toys: Aye, parrots need perches of different sizes, shapes, 'n textures to keep their feet healthy. Also, a few toys be helpin' to keep them entertained 'n their minds stimulated, arrrh!\n\n3. Proper grub: Feed yer feathered friend a balanced diet of high-quality pellets, fruits, 'n veggies to keep 'em strong 'n healthy. Give 'em fresh water every day, or ye’ll have a scurvy bird on yer hands!\n\n4. Cleanliness: Swab their cage deck! Clean their cage on a regular basis: fresh water 'n food daily, the floor every couple of days, 'n a thorough scrubbing ev'ry few weeks, so the bird be livin' in a tidy haven, arrhh!\n\n5. Socialize 'n train: Parrots be a sociable lot, arrr! Exercise 'n interact with 'em daily to create a bond 'n maintain their mental 'n physical health. Train 'em with positive reinforcement, treat 'em kindly, yarrr!\n\n6. Proper rest: Yer parrot be needin' ’bout 10-12 hours o' sleep each night. Cover their cage 'n let them slumber in a dim, quiet quarter for a proper night's rest, ye scallywag!\n\n7. Keep a weather eye open for illness: Birds be hidin' their ailments, arrr! Be watchful for signs of sickness, such as lethargy, loss of appetite, puffin' up, or change in droppings, and make haste to a vet if need be.\n\n8. Provide fresh air 'n avoid toxins: Parrots be sensitive to draft and pollutants. Keep yer quarters well ventilated, but no drafts, arrr! Be mindful of toxins like Teflon fumes, candles, or air fresheners.\n\nSo there ye have it, me hearty! With proper care 'n commitment, yer parrot will be squawkin' \"Yo-ho-ho\" for many years to come! Good luck, sailor, and may the wind be at yer back!" + }, + "logprobs": null + } + ], + "usage": { + "completion_tokens": 557, + "prompt_tokens": 33, + "total_tokens": 590 + } + } + """))) + }; + + return options; +} + + AzureOpenAIClientOptions GetAzureClientOptions() { MockPipelineTransport mockTransport = new("Transport", i => (200, BinaryData.FromString( diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.Serialization.cs index 31340a9c3..ad595cb27 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.Serialization.cs @@ -30,7 +30,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mo { writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); + writer.WriteRawValue(item.Value); #else using (JsonDocument document = JsonDocument.Parse(item.Value)) { @@ -69,8 +69,8 @@ internal static AzureChatExtensionConfiguration DeserializeAzureChatExtensionCon //case "azure_cosmos_db": return AzureCosmosDBChatExtensionConfiguration.DeserializeAzureCosmosDBChatExtensionConfiguration(element, options); //case "azure_ml_index": return AzureMachineLearningIndexChatExtensionConfiguration.DeserializeAzureMachineLearningIndexChatExtensionConfiguration(element, options); case "azure_search": return AzureSearchChatExtensionConfiguration.DeserializeAzureSearchChatExtensionConfiguration(element, options); - //case "elasticsearch": return ElasticsearchChatExtensionConfiguration.DeserializeElasticsearchChatExtensionConfiguration(element, options); - //case "pinecone": return PineconeChatExtensionConfiguration.DeserializePineconeChatExtensionConfiguration(element, options); + //case "elasticsearch": return ElasticsearchChatExtensionConfiguration.DeserializeElasticsearchChatExtensionConfiguration(element, options); + //case "pinecone": return PineconeChatExtensionConfiguration.DeserializePineconeChatExtensionConfiguration(element, options); } } return UnknownAzureChatExtensionConfiguration.DeserializeUnknownAzureChatExtensionConfiguration(element, options); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.cs index ea73126d4..807b35cd4 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatExtensionConfiguration.cs @@ -49,7 +49,7 @@ public abstract partial class AzureChatExtensionConfiguration /// /// /// - + // TODO: Could this be IDictionary> or even // IDictionary> in some cases. What would that do? // diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 16fdc5e1c..30607bb4b 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -1,8 +1,5 @@ using OpenAI.Models; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Text; namespace AzureOpenAI.Models; @@ -10,14 +7,14 @@ public static class CreateChatCompletionRequestExtensions { // Input model property // Note: on the input-side, we are unable to type-check that we're using an Azure - // model, because the end-user created an instance of an unbranded model. We'll + // model, because the end-user created an instance of a third-party model. We'll // later convert this to an Azure model that can serialize to the Azure format, // but for now, we need to stash the values in a collection of objects that we'll // use to populate the Azure model properties later. public static IList GetDataSources(this CreateChatCompletionRequest request) { // TODO: How can we validate that this is being called in the right context, - // e.g. user is using an Azure client instance and not an unbranded one? + // e.g. user is using an Azure client instance and not a third-party one? // TODO: What is the interplay of SerializedAdditionalRawData and // AdditionalTypedProperties? i.e. in a round-trip scenario, if we @@ -25,7 +22,7 @@ public static IList GetDataSources(this CreateC // and stick it in AdditionalTypedProperties (are these "deserialized // additional properties, e.g.?). - // TODO: what is our concurrency story for models in unbranded clients? + // TODO: what is our concurrency story for models in third-party clients? // Are we happy with taking the Azure client stance of "models don't need // to be thread-safe because they are rarely shared between threads"? JsonModelList dataSources; diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index 55f59d851..c7c7adc0d 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -31,7 +31,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR continue; } #if NET6_0_OR_GREATER - writer.WriteRawValue(item); + writer.WriteRawValue(item); #else using (JsonDocument document = JsonDocument.Parse(item)) { @@ -235,7 +235,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { writer.WritePropertyName("function_call"u8); #if NET6_0_OR_GREATER - writer.WriteRawValue(FunctionCall); + writer.WriteRawValue(FunctionCall); #else using (JsonDocument document = JsonDocument.Parse(FunctionCall)) { @@ -262,7 +262,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(serializedValue); + writer.WriteRawValue(serializedValue); #else using (JsonDocument document = JsonDocument.Parse(serializedValue)) { From 9699f6353e90cb2db6f6bd372c3ba27474561413 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 17 Apr 2024 18:19:37 -0700 Subject: [PATCH 12/32] Updates to JsonModelList, input extension --- .../typespec-csharp/app/DemoApp/Program.cs | 5 ++ .../Generated/Internal/JsonModelList.cs | 89 +++++++++++++++++++ .../Internal/PersistableModelList.cs | 59 ------------ .../CreateChatCompletionRequestExtensions.cs | 15 ---- 4 files changed, 94 insertions(+), 74 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/JsonModelList.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index ad9a9ae15..03c87d1a5 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -58,9 +58,14 @@ void CallAzureService() CreateChatCompletionRequest request = new(messages, CreateChatCompletionRequestModel.Gpt35Turbo); // + // Add Azure input property via extension methods request.GetDataSources().Add(GetAzureSearchDataSource()); + // + // request.DataSources.Add(GetAzureSearchDataSource()); + // + // ClientResult result = chatClient.CreateChatCompletion(request); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/JsonModelList.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/JsonModelList.cs new file mode 100644 index 000000000..eac0fd6c8 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/JsonModelList.cs @@ -0,0 +1,89 @@ +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI; + +internal class JsonModelList : List, IJsonModel> + where TModel : IJsonModel +{ + public JsonModelList Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel>)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(JsonModelList)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeJsonModelList(document.RootElement, options); + } + + public JsonModelList Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel>)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeJsonModelList(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(JsonModelList)} does not support reading '{options.Format}' format."); + } + } + + internal static JsonModelList DeserializeJsonModelList(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= new ModelReaderWriterOptions("W"); + + if (element.ValueKind != JsonValueKind.Array) + { + throw new InvalidOperationException("Cannot deserialize JsonModelList from JSON that is not an array."); + } + + JsonModelList list = []; + + foreach (JsonElement item in element.EnumerateArray()) + { + // TODO: Make efficient + TModel? value = ModelReaderWriter.Read(BinaryData.FromString(item.ToString()), options) ?? + throw new InvalidOperationException("Failed to deserialized array element."); + list.Add(value); + } + + return list; + } + + public string GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + public void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel>)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(JsonModelList)} does not support writing '{format}' format."); + } + + writer.WriteStartArray(); + + foreach (IJsonModel item in this) + { + item.Write(writer, options); + } + + writer.WriteEndArray(); + } + + public BinaryData Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel>)this).GetFormatFromOptions(options) : options.Format; + + return format switch + { + "J" => ModelReaderWriter.Write(this, options), + _ => throw new FormatException($"The model {nameof(JsonModelList)} does not support writing '{options.Format}' format."), + }; + } +} \ No newline at end of file diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs deleted file mode 100644 index b12307872..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Generated/Internal/PersistableModelList.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.ClientModel.Primitives; -using System.Text.Json; - -namespace AzureOpenAI; - -internal class PersistableModelList : List, IPersistableModel> - where TModel : IPersistableModel -{ - public PersistableModelList Create(BinaryData data, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - public string GetFormatFromOptions(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - public BinaryData Write(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } -} - -internal class JsonModelList : List, IJsonModel> - where TModel : IJsonModel -{ - public JsonModelList Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - public JsonModelList Create(BinaryData data, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - public string GetFormatFromOptions(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - public void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) - { - writer.WriteStartArray(); - - foreach (IJsonModel item in this) - { - item.Write(writer, options); - } - - writer.WriteEndArray(); - } - - public BinaryData Write(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 30607bb4b..1ac45234c 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -5,26 +5,11 @@ namespace AzureOpenAI.Models; public static class CreateChatCompletionRequestExtensions { - // Input model property - // Note: on the input-side, we are unable to type-check that we're using an Azure - // model, because the end-user created an instance of a third-party model. We'll - // later convert this to an Azure model that can serialize to the Azure format, - // but for now, we need to stash the values in a collection of objects that we'll - // use to populate the Azure model properties later. public static IList GetDataSources(this CreateChatCompletionRequest request) { // TODO: How can we validate that this is being called in the right context, // e.g. user is using an Azure client instance and not a third-party one? - // TODO: What is the interplay of SerializedAdditionalRawData and - // AdditionalTypedProperties? i.e. in a round-trip scenario, if we - // wanted to mutate a SeriazliedAdditionalRawData, do we deserialize it - // and stick it in AdditionalTypedProperties (are these "deserialized - // additional properties, e.g.?). - - // TODO: what is our concurrency story for models in third-party clients? - // Are we happy with taking the Azure client stance of "models don't need - // to be thread-safe because they are rarely shared between threads"? JsonModelList dataSources; if (request.SerializedAdditionalRawData.TryGetValue("data_sources", out object? value)) From 0829d5c9d2692c423fad1fa0848f284f36a501ae Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 17 Apr 2024 18:25:15 -0700 Subject: [PATCH 13/32] add 'emitted' subtype of BinaryData --- .../src/Generated/Internal/SerializedModelData.cs | 12 ++++++++++++ .../ChatCompletionResponseMessage.Serialization.cs | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs new file mode 100644 index 000000000..3fc65e2f1 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenAI; + +internal class SerializedModelData : BinaryData +{ + public SerializedModelData(string value) : base(value) + { + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs index 6096b65c4..ff2217bfd 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs @@ -53,7 +53,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mode { writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); + writer.WriteRawValue(item.Value); #else using (JsonDocument document = JsonDocument.Parse(item.Value)) { @@ -133,7 +133,7 @@ internal static ChatCompletionResponseMessage DeserializeChatCompletionResponseM } if (options.Format != "W") { - additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + additionalPropertiesDictionary.Add(property.Name, new SerializedModelData(property.Value.GetRawText())); } } serializedAdditionalRawData = additionalPropertiesDictionary; From d6dcaca8266d0c79c842ead0589eee898b79600c Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 17 Apr 2024 20:45:10 -0700 Subject: [PATCH 14/32] update 3rd-party model serialization method --- ...eateChatCompletionRequest.Serialization.cs | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index c7c7adc0d..8e30cf0ee 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -155,7 +155,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { writer.WritePropertyName("stop"u8); #if NET6_0_OR_GREATER - writer.WriteRawValue(Stop); + writer.WriteRawValue(Stop); #else using (JsonDocument document = JsonDocument.Parse(Stop)) { @@ -257,30 +257,31 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { foreach (var item in _serializedAdditionalRawData) { - // TODO: revisit for "dictionary approach" - if (item.Value is BinaryData serializedValue) + writer.WritePropertyName(item.Key); + + switch (item.Value) { - writer.WritePropertyName(item.Key); + case SerializedModelData serializedValue: #if NET6_0_OR_GREATER - writer.WriteRawValue(serializedValue); + writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } + using (JsonDocument document = JsonDocument.Parse(serializedValue)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } #endif - } + break; - // Note: we were holding strongly-typed values for added Azure properties - // We have to serialize them separately here. - // TODO: Does the format matter here? It seems like we want to write them - // in every case. - else if (item.Value is IJsonModel model) - { - writer.WritePropertyName(item.Key); + // If it's not a SerializedModelData that one of our models + // stored as additionalRawData, it's an un-serialized value. + // Serialize it now. + case IJsonModel model: + model.Write(writer, options); + break; - // Note: what if it's just a primitive, i.e. a string or int, what do we do? - model.Write(writer, options); + default: + JsonSerializer.Serialize(writer, item.Value); + break; } } } From 8ab3c85e384002af0d136f2e47fd770c3b1ca2d2 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 17 Apr 2024 21:09:04 -0700 Subject: [PATCH 15/32] add Azure response subtypes --- .../azoai/AzureOpenAI/AzureChatClient.cs | 19 ++---- ...CompletionResponseMessage.Serialization.cs | 54 ++++++++++++++++ .../AzureChatCompletionResponseMessage.cs | 12 ++++ ...ateChatCompletionResponse.Serialization.cs | 62 +++++++++++++++++++ .../AzureCreateChatCompletionResponse.cs | 7 +++ ...tCompletionResponseChoice.Serialization.cs | 58 +++++++++++++++++ ...AzureCreateChatCompletionResponseChoice.cs | 7 +++ .../Models/ChatCompletionResponseMessage.cs | 2 +- .../Models/CreateChatCompletionResponse.cs | 2 +- .../CreateChatCompletionResponseChoice.cs | 2 +- 10 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index c81b2420c..fc4bca05b 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -1,4 +1,5 @@ -using OpenAI; +using AzureOpenAI.Models; +using OpenAI; using OpenAI.Models; using System.ClientModel; using System.ClientModel.Primitives; @@ -20,14 +21,8 @@ public override async Task> CreateCha Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); - ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); - - PipelineResponse response = result.GetRawResponse(); - - // TODO: handle null case - CreateChatCompletionResponse value = ModelReaderWriter.Read(response.Content)!; - return ClientResult.FromValue(value, response); + return ClientResult.FromValue(AzureCreateChatCompletionResponse.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } public override ClientResult CreateChatCompletion(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) @@ -35,14 +30,8 @@ public override ClientResult CreateChatCompletion( Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); - ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); - - PipelineResponse response = result.GetRawResponse(); - - // TODO: handle null case - CreateChatCompletionResponse value = ModelReaderWriter.Read(response.Content)!; - return ClientResult.FromValue(value, response); + return ClientResult.FromValue(AzureCreateChatCompletionResponse.FromResponse(result.GetRawResponse()), result.GetRawResponse()); } public override Task CreateChatCompletionAsync(BinaryContent content, RequestOptions context = null) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs new file mode 100644 index 000000000..a25be8714 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs @@ -0,0 +1,54 @@ +using OpenAI.Models; +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +internal partial class AzureChatCompletionResponseMessage : IJsonModel +{ + AzureChatCompletionResponseMessage IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); + } + + AzureChatCompletionResponseMessage IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{options.Format}' format."); + } + } + + private AzureChatCompletionResponseMessage DeserializeAzureChatCompletionResponseMessage(JsonElement rootElement, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + => "J"; + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs new file mode 100644 index 000000000..bf362a0a9 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs @@ -0,0 +1,12 @@ +using OpenAI.Models; + +namespace AzureOpenAI.Models; + +internal partial class AzureChatCompletionResponseMessage : ChatCompletionResponseMessage +{ + /// + /// If Azure OpenAI chat extensions are configured, this array represents the incremental steps performed by those + /// extensions while processing the chat completions request. + /// + public AzureChatExtensionsMessageContext? AzureExtensionsContext { get; } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs new file mode 100644 index 000000000..2c2c93652 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs @@ -0,0 +1,62 @@ +using OpenAI.Models; +using System.ClientModel.Primitives; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +internal partial class AzureCreateChatCompletionResponse : IJsonModel +{ + AzureCreateChatCompletionResponse IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); + } + + AzureCreateChatCompletionResponse IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{options.Format}' format."); + } + } + + internal static AzureCreateChatCompletionResponse DeserializeAzureCreateChatCompletionResponse(JsonElement element, ModelReaderWriterOptions options = null) + { + throw new NotImplementedException(); + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + => "J"; + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + /// Deserializes the model from a raw response. + /// The result to deserialize the model from. + internal static CreateChatCompletionResponse FromResponse(PipelineResponse response) + { + using var document = JsonDocument.Parse(response.Content); + return DeserializeAzureCreateChatCompletionResponse(document.RootElement); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs new file mode 100644 index 000000000..d1f6530a3 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs @@ -0,0 +1,7 @@ +using OpenAI.Models; + +namespace AzureOpenAI.Models; + +internal partial class AzureCreateChatCompletionResponse : CreateChatCompletionResponse +{ +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs new file mode 100644 index 000000000..7b44704ea --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs @@ -0,0 +1,58 @@ +using OpenAI.Models; +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; + +namespace AzureOpenAI.Models; + +internal partial class AzureCreateChatCompletionResponseChoice : IJsonModel +{ + AzureCreateChatCompletionResponseChoice IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); + } + + + AzureCreateChatCompletionResponseChoice IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{options.Format}' format."); + } + } + + private AzureCreateChatCompletionResponseChoice DeserializeAzureCreateCompletionResponseChoice(JsonElement rootElement, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + => "J"; + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs new file mode 100644 index 000000000..5fe8d8bc4 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs @@ -0,0 +1,7 @@ +using OpenAI.Models; + +namespace AzureOpenAI.Models; + +internal partial class AzureCreateChatCompletionResponseChoice : CreateChatCompletionResponseChoice +{ +} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs index d4e71edb8..233bcdaf7 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs @@ -66,7 +66,7 @@ internal ChatCompletionResponseMessage(string content, IReadOnlyList Initializes a new instance of for deserialization. - internal ChatCompletionResponseMessage() + protected ChatCompletionResponseMessage() { } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs index 811da2977..1885bf508 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs @@ -88,7 +88,7 @@ internal CreateChatCompletionResponse(string id, IReadOnlyList Initializes a new instance of for deserialization. - internal CreateChatCompletionResponse() + public CreateChatCompletionResponse() { } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs index d9579d2bd..cf7af0590 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs @@ -86,7 +86,7 @@ internal CreateChatCompletionResponseChoice(CreateChatCompletionResponseChoiceFi } /// Initializes a new instance of for deserialization. - internal CreateChatCompletionResponseChoice() + public CreateChatCompletionResponseChoice() { } From f10b1bba9c57175fe0592e46247ea23d3781f376 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 17 Apr 2024 21:20:03 -0700 Subject: [PATCH 16/32] updates --- .../Output/ChatCompletionResponseMessageExtensions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs index 6b3d7af11..d638a57ef 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs @@ -7,11 +7,11 @@ public static class ChatCompletionResponseMessageExtensions // Output property public static AzureChatExtensionsMessageContext? GetAzureExtensionsContext(this ChatCompletionResponseMessage message) { - // TODO: How to throw if used incorrectly? + if (message is not AzureChatCompletionResponseMessage azureMessage) + { + throw new NotSupportedException("Cannot get AzureExtensionsContext when not using the Azure OpeAI client."); + } - // TODO: retrieve from dictionary - //return azureMessage.AzureExtensionsContext; - - throw new NotImplementedException(); + return azureMessage.AzureExtensionsContext; } } From 09e5bc66df74aab1f6bdfa477fcab9456c29fdbf Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Thu, 18 Apr 2024 15:17:42 -0700 Subject: [PATCH 17/32] use dictionary for output model --- ...CompletionResponseMessage.Serialization.cs | 108 +++++++++--------- .../AzureChatCompletionResponseMessage.cs | 20 ++-- ...ChatCompletionResponseMessageExtensions.cs | 24 +++- .../Models/ChatCompletionResponseMessage.cs | 6 +- 4 files changed, 90 insertions(+), 68 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs index a25be8714..0de7e4aac 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs @@ -1,54 +1,54 @@ -using OpenAI.Models; -using System.ClientModel.Primitives; -using System.Text.Json; - -namespace AzureOpenAI.Models; - -internal partial class AzureChatCompletionResponseMessage : IJsonModel -{ - AzureChatCompletionResponseMessage IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if (format != "J") - { - throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{format}' format."); - } - - using JsonDocument document = JsonDocument.ParseValue(ref reader); - return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); - } - - AzureChatCompletionResponseMessage IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{options.Format}' format."); - } - } - - private AzureChatCompletionResponseMessage DeserializeAzureChatCompletionResponseMessage(JsonElement rootElement, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) - => "J"; - - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } -} +//using OpenAI.Models; +//using System.ClientModel.Primitives; +//using System.Text.Json; + +//namespace AzureOpenAI.Models; + +//internal partial class AzureChatCompletionResponseMessage : IJsonModel +//{ +// AzureChatCompletionResponseMessage IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) +// { +// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; +// if (format != "J") +// { +// throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{format}' format."); +// } + +// using JsonDocument document = JsonDocument.ParseValue(ref reader); +// return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); +// } + +// AzureChatCompletionResponseMessage IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) +// { +// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + +// switch (format) +// { +// case "J": +// { +// using JsonDocument document = JsonDocument.Parse(data); +// return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); +// } +// default: +// throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{options.Format}' format."); +// } +// } + +// private AzureChatCompletionResponseMessage DeserializeAzureChatCompletionResponseMessage(JsonElement rootElement, ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } + +// string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) +// => "J"; + +// void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } + +// BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs index bf362a0a9..d557c4e8e 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs @@ -1,12 +1,12 @@ -using OpenAI.Models; +//using OpenAI.Models; -namespace AzureOpenAI.Models; +//namespace AzureOpenAI.Models; -internal partial class AzureChatCompletionResponseMessage : ChatCompletionResponseMessage -{ - /// - /// If Azure OpenAI chat extensions are configured, this array represents the incremental steps performed by those - /// extensions while processing the chat completions request. - /// - public AzureChatExtensionsMessageContext? AzureExtensionsContext { get; } -} +//internal partial class AzureChatCompletionResponseMessage : ChatCompletionResponseMessage +//{ +// /// +// /// If Azure OpenAI chat extensions are configured, this array represents the incremental steps performed by those +// /// extensions while processing the chat completions request. +// /// +// public AzureChatExtensionsMessageContext? AzureExtensionsContext { get; } +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs index d638a57ef..aa73bb838 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs @@ -1,4 +1,5 @@ using OpenAI.Models; +using System.ClientModel.Primitives; namespace AzureOpenAI.Models; @@ -7,11 +8,28 @@ public static class ChatCompletionResponseMessageExtensions // Output property public static AzureChatExtensionsMessageContext? GetAzureExtensionsContext(this ChatCompletionResponseMessage message) { - if (message is not AzureChatCompletionResponseMessage azureMessage) + if (!message.SerializedAdditionalRawData.TryGetValue("context", out object? value)) { - throw new NotSupportedException("Cannot get AzureExtensionsContext when not using the Azure OpeAI client."); + return null; } - return azureMessage.AzureExtensionsContext; + // It's either deserialized already or not. Find out now. + // TODO: this works for OpenAI scenarios today, but we need to find a + // way to handle cases where BinaryData is a valid type for extended + // properties. + if (value is BinaryData serializedValue) + { + // Qn: when can Read return null? + var deserializedValue = ModelReaderWriter.Read(serializedValue)!; + message.SerializedAdditionalRawData["context"] = deserializedValue; + return deserializedValue; + } + + if (value is not AzureChatExtensionsMessageContext context) + { + throw new InvalidOperationException($"'context' value is unexpected type: '{value.GetType()}'."); + } + + return context; } } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs index 233bcdaf7..118e9403a 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; namespace OpenAI.Models { @@ -40,7 +41,10 @@ public partial class ChatCompletionResponseMessage /// /// /// - private IDictionary _serializedAdditionalRawData; + private IDictionary _serializedAdditionalRawData; + [EditorBrowsable(EditorBrowsableState.Never)] + // TODO: lazily instantiate; also, add it to all models. + public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); /// Initializes a new instance of . /// The contents of the message. From 5d6b7b25b64b3f844d1ebefad4f96ce3b6529f64 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Thu, 18 Apr 2024 17:07:18 -0700 Subject: [PATCH 18/32] functional e2e, using 'AW' approach - this doesn't work --- .../typespec-csharp/app/DemoApp/Program.cs | 2 +- .../azoai/AzureOpenAI/AzureChatClient.cs | 17 ++- ...ateChatCompletionResponse.Serialization.cs | 124 +++++++++--------- .../AzureCreateChatCompletionResponse.cs | 10 +- ...tCompletionResponseChoice.Serialization.cs | 116 ++++++++-------- ...AzureCreateChatCompletionResponseChoice.cs | 10 +- ...CompletionResponseMessage.Serialization.cs | 31 ++++- .../Models/ChatCompletionResponseMessage.cs | 8 +- ...eateChatCompletionRequest.Serialization.cs | 2 +- 9 files changed, 171 insertions(+), 149 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index 03c87d1a5..2114d9016 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -173,7 +173,7 @@ AzureOpenAIClientOptions GetAzureClientOptions() AzureOpenAIClientOptions options = new() { - //Transport = mockTransport + Transport = mockTransport }; return options; diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index fc4bca05b..dca6205a4 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -1,5 +1,4 @@ -using AzureOpenAI.Models; -using OpenAI; +using OpenAI; using OpenAI.Models; using System.ClientModel; using System.ClientModel.Primitives; @@ -10,6 +9,10 @@ internal class AzureChatClient : Chat { private readonly string _apiVersion; + // Needed to workaround the fact that generated models don't write + // additional values when format is "W". + private readonly ModelReaderWriterOptions _modelOptions = new("AW"); + internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, Uri endpoint, string apiVersion) : base(pipeline, credential, endpoint) { @@ -20,18 +23,20 @@ public override async Task> CreateCha { Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); - using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); + using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, _modelOptions); ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); - return ClientResult.FromValue(AzureCreateChatCompletionResponse.FromResponse(result.GetRawResponse()), result.GetRawResponse()); + var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; + return ClientResult.FromValue(value, result.GetRawResponse()); } public override ClientResult CreateChatCompletion(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) { Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); - using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); + using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, _modelOptions); ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); - return ClientResult.FromValue(AzureCreateChatCompletionResponse.FromResponse(result.GetRawResponse()), result.GetRawResponse()); + var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; + return ClientResult.FromValue(value, result.GetRawResponse()); } public override Task CreateChatCompletionAsync(BinaryContent content, RequestOptions context = null) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs index 2c2c93652..36a944dfa 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs @@ -1,62 +1,62 @@ -using OpenAI.Models; -using System.ClientModel.Primitives; -using System.Text.Json; - -namespace AzureOpenAI.Models; - -internal partial class AzureCreateChatCompletionResponse : IJsonModel -{ - AzureCreateChatCompletionResponse IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if (format != "J") - { - throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{format}' format."); - } - - using JsonDocument document = JsonDocument.ParseValue(ref reader); - return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); - } - - AzureCreateChatCompletionResponse IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{options.Format}' format."); - } - } - - internal static AzureCreateChatCompletionResponse DeserializeAzureCreateChatCompletionResponse(JsonElement element, ModelReaderWriterOptions options = null) - { - throw new NotImplementedException(); - } - - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) - => "J"; - - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - /// Deserializes the model from a raw response. - /// The result to deserialize the model from. - internal static CreateChatCompletionResponse FromResponse(PipelineResponse response) - { - using var document = JsonDocument.Parse(response.Content); - return DeserializeAzureCreateChatCompletionResponse(document.RootElement); - } -} +//using OpenAI.Models; +//using System.ClientModel.Primitives; +//using System.Text.Json; + +//namespace AzureOpenAI.Models; + +//internal partial class AzureCreateChatCompletionResponse : IJsonModel +//{ +// AzureCreateChatCompletionResponse IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) +// { +// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; +// if (format != "J") +// { +// throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{format}' format."); +// } + +// using JsonDocument document = JsonDocument.ParseValue(ref reader); +// return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); +// } + +// AzureCreateChatCompletionResponse IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) +// { +// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + +// switch (format) +// { +// case "J": +// { +// using JsonDocument document = JsonDocument.Parse(data); +// return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); +// } +// default: +// throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{options.Format}' format."); +// } +// } + +// internal static AzureCreateChatCompletionResponse DeserializeAzureCreateChatCompletionResponse(JsonElement element, ModelReaderWriterOptions options = null) +// { +// throw new NotImplementedException(); +// } + +// string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) +// => "J"; + +// void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } + +// BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } + +// /// Deserializes the model from a raw response. +// /// The result to deserialize the model from. +// internal static CreateChatCompletionResponse FromResponse(PipelineResponse response) +// { +// using var document = JsonDocument.Parse(response.Content); +// return DeserializeAzureCreateChatCompletionResponse(document.RootElement); +// } +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs index d1f6530a3..b66d8ac69 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs @@ -1,7 +1,7 @@ -using OpenAI.Models; +//using OpenAI.Models; -namespace AzureOpenAI.Models; +//namespace AzureOpenAI.Models; -internal partial class AzureCreateChatCompletionResponse : CreateChatCompletionResponse -{ -} +//internal partial class AzureCreateChatCompletionResponse : CreateChatCompletionResponse +//{ +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs index 7b44704ea..3696fae9d 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs @@ -1,58 +1,58 @@ -using OpenAI.Models; -using System; -using System.ClientModel.Primitives; -using System.Collections.Generic; -using System.Text; -using System.Text.Json; - -namespace AzureOpenAI.Models; - -internal partial class AzureCreateChatCompletionResponseChoice : IJsonModel -{ - AzureCreateChatCompletionResponseChoice IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if (format != "J") - { - throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{format}' format."); - } - - using JsonDocument document = JsonDocument.ParseValue(ref reader); - return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); - } - - - AzureCreateChatCompletionResponseChoice IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{options.Format}' format."); - } - } - - private AzureCreateChatCompletionResponseChoice DeserializeAzureCreateCompletionResponseChoice(JsonElement rootElement, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) - => "J"; - - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } - - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - throw new NotImplementedException(); - } -} +//using OpenAI.Models; +//using System; +//using System.ClientModel.Primitives; +//using System.Collections.Generic; +//using System.Text; +//using System.Text.Json; + +//namespace AzureOpenAI.Models; + +//internal partial class AzureCreateChatCompletionResponseChoice : IJsonModel +//{ +// AzureCreateChatCompletionResponseChoice IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) +// { +// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; +// if (format != "J") +// { +// throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{format}' format."); +// } + +// using JsonDocument document = JsonDocument.ParseValue(ref reader); +// return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); +// } + + +// AzureCreateChatCompletionResponseChoice IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) +// { +// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + +// switch (format) +// { +// case "J": +// { +// using JsonDocument document = JsonDocument.Parse(data); +// return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); +// } +// default: +// throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{options.Format}' format."); +// } +// } + +// private AzureCreateChatCompletionResponseChoice DeserializeAzureCreateCompletionResponseChoice(JsonElement rootElement, ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } + +// string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) +// => "J"; + +// void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } + +// BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs index 5fe8d8bc4..2c0bc0347 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs @@ -1,7 +1,7 @@ -using OpenAI.Models; +//using OpenAI.Models; -namespace AzureOpenAI.Models; +//namespace AzureOpenAI.Models; -internal partial class AzureCreateChatCompletionResponseChoice : CreateChatCompletionResponseChoice -{ -} +//internal partial class AzureCreateChatCompletionResponseChoice : CreateChatCompletionResponseChoice +//{ +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs index ff2217bfd..7c466690d 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs @@ -52,14 +52,31 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mode foreach (var item in _serializedAdditionalRawData) { writer.WritePropertyName(item.Key); + + switch (item.Value) + { + case SerializedModelData serializedValue: #if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); + writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(item.Value)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } + using (JsonDocument document = JsonDocument.Parse(serializedValue)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } #endif + break; + + // If it's not a SerializedModelData that one of our models + // stored as additionalRawData, it's an un-serialized value. + // Serialize it now. + case IJsonModel model: + model.Write(writer, options); + break; + + default: + JsonSerializer.Serialize(writer, item.Value); + break; + } } } writer.WriteEndObject(); @@ -89,8 +106,8 @@ internal static ChatCompletionResponseMessage DeserializeChatCompletionResponseM IReadOnlyList toolCalls = default; ChatCompletionResponseMessageRole role = default; ChatCompletionResponseMessageFunctionCall functionCall = default; - IDictionary serializedAdditionalRawData = default; - Dictionary additionalPropertiesDictionary = new Dictionary(); + IDictionary serializedAdditionalRawData = default; + Dictionary additionalPropertiesDictionary = new Dictionary(); foreach (var property in element.EnumerateObject()) { if (property.NameEquals("content"u8)) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs index 118e9403a..7798ef4ab 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs @@ -41,9 +41,9 @@ public partial class ChatCompletionResponseMessage /// /// /// - private IDictionary _serializedAdditionalRawData; + private IDictionary? _serializedAdditionalRawData; + [EditorBrowsable(EditorBrowsableState.Never)] - // TODO: lazily instantiate; also, add it to all models. public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); /// Initializes a new instance of . @@ -60,7 +60,7 @@ internal ChatCompletionResponseMessage(string content) /// The role of the author of this message. /// Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model. /// Keeps track of any properties unknown to the library. - internal ChatCompletionResponseMessage(string content, IReadOnlyList toolCalls, ChatCompletionResponseMessageRole role, ChatCompletionResponseMessageFunctionCall functionCall, IDictionary serializedAdditionalRawData) + internal ChatCompletionResponseMessage(string content, IReadOnlyList toolCalls, ChatCompletionResponseMessageRole role, ChatCompletionResponseMessageFunctionCall functionCall, IDictionary serializedAdditionalRawData) { Content = content; ToolCalls = toolCalls; @@ -70,7 +70,7 @@ internal ChatCompletionResponseMessage(string content, IReadOnlyList Initializes a new instance of for deserialization. - protected ChatCompletionResponseMessage() + private ChatCompletionResponseMessage() { } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index 8e30cf0ee..cae7f368b 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -541,7 +541,7 @@ internal static CreateChatCompletionRequest DeserializeCreateChatCompletionReque } if (options.Format != "W") { - additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + additionalPropertiesDictionary.Add(property.Name, new SerializedModelData(property.Value.GetRawText())); } } serializedAdditionalRawData = additionalPropertiesDictionary; From 56639be83cfba1486e1c4784ea7b5b1a0b42a0dd Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Thu, 18 Apr 2024 17:20:02 -0700 Subject: [PATCH 19/32] make e2e work by separating which types of values are written from dictionary --- .../azoai/AzureOpenAI/AzureChatClient.cs | 8 +-- ...CompletionResponseMessage.Serialization.cs | 54 ---------------- .../AzureChatCompletionResponseMessage.cs | 12 ---- ...ateChatCompletionResponse.Serialization.cs | 62 ------------------- .../AzureCreateChatCompletionResponse.cs | 7 --- ...tCompletionResponseChoice.Serialization.cs | 58 ----------------- ...AzureCreateChatCompletionResponseChoice.cs | 7 --- ...CompletionResponseMessage.Serialization.cs | 43 ++++++------- ...eateChatCompletionRequest.Serialization.cs | 44 ++++++------- .../Models/CreateChatCompletionRequest.cs | 2 +- 10 files changed, 47 insertions(+), 250 deletions(-) delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index dca6205a4..50cd1cbe0 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -9,10 +9,6 @@ internal class AzureChatClient : Chat { private readonly string _apiVersion; - // Needed to workaround the fact that generated models don't write - // additional values when format is "W". - private readonly ModelReaderWriterOptions _modelOptions = new("AW"); - internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, Uri endpoint, string apiVersion) : base(pipeline, credential, endpoint) { @@ -23,7 +19,7 @@ public override async Task> CreateCha { Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); - using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, _modelOptions); + using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; return ClientResult.FromValue(value, result.GetRawResponse()); @@ -33,7 +29,7 @@ public override ClientResult CreateChatCompletion( { Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); - using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, _modelOptions); + using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; return ClientResult.FromValue(value, result.GetRawResponse()); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs deleted file mode 100644 index 0de7e4aac..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.Serialization.cs +++ /dev/null @@ -1,54 +0,0 @@ -//using OpenAI.Models; -//using System.ClientModel.Primitives; -//using System.Text.Json; - -//namespace AzureOpenAI.Models; - -//internal partial class AzureChatCompletionResponseMessage : IJsonModel -//{ -// AzureChatCompletionResponseMessage IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) -// { -// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; -// if (format != "J") -// { -// throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{format}' format."); -// } - -// using JsonDocument document = JsonDocument.ParseValue(ref reader); -// return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); -// } - -// AzureChatCompletionResponseMessage IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) -// { -// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - -// switch (format) -// { -// case "J": -// { -// using JsonDocument document = JsonDocument.Parse(data); -// return DeserializeAzureChatCompletionResponseMessage(document.RootElement, options); -// } -// default: -// throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{options.Format}' format."); -// } -// } - -// private AzureChatCompletionResponseMessage DeserializeAzureChatCompletionResponseMessage(JsonElement rootElement, ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } - -// string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) -// => "J"; - -// void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } - -// BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs deleted file mode 100644 index d557c4e8e..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -//using OpenAI.Models; - -//namespace AzureOpenAI.Models; - -//internal partial class AzureChatCompletionResponseMessage : ChatCompletionResponseMessage -//{ -// /// -// /// If Azure OpenAI chat extensions are configured, this array represents the incremental steps performed by those -// /// extensions while processing the chat completions request. -// /// -// public AzureChatExtensionsMessageContext? AzureExtensionsContext { get; } -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs deleted file mode 100644 index 36a944dfa..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.Serialization.cs +++ /dev/null @@ -1,62 +0,0 @@ -//using OpenAI.Models; -//using System.ClientModel.Primitives; -//using System.Text.Json; - -//namespace AzureOpenAI.Models; - -//internal partial class AzureCreateChatCompletionResponse : IJsonModel -//{ -// AzureCreateChatCompletionResponse IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) -// { -// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; -// if (format != "J") -// { -// throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{format}' format."); -// } - -// using JsonDocument document = JsonDocument.ParseValue(ref reader); -// return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); -// } - -// AzureCreateChatCompletionResponse IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) -// { -// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - -// switch (format) -// { -// case "J": -// { -// using JsonDocument document = JsonDocument.Parse(data); -// return DeserializeAzureCreateChatCompletionResponse(document.RootElement, options); -// } -// default: -// throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{options.Format}' format."); -// } -// } - -// internal static AzureCreateChatCompletionResponse DeserializeAzureCreateChatCompletionResponse(JsonElement element, ModelReaderWriterOptions options = null) -// { -// throw new NotImplementedException(); -// } - -// string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) -// => "J"; - -// void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } - -// BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } - -// /// Deserializes the model from a raw response. -// /// The result to deserialize the model from. -// internal static CreateChatCompletionResponse FromResponse(PipelineResponse response) -// { -// using var document = JsonDocument.Parse(response.Content); -// return DeserializeAzureCreateChatCompletionResponse(document.RootElement); -// } -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs deleted file mode 100644 index b66d8ac69..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -//using OpenAI.Models; - -//namespace AzureOpenAI.Models; - -//internal partial class AzureCreateChatCompletionResponse : CreateChatCompletionResponse -//{ -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs deleted file mode 100644 index 3696fae9d..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.Serialization.cs +++ /dev/null @@ -1,58 +0,0 @@ -//using OpenAI.Models; -//using System; -//using System.ClientModel.Primitives; -//using System.Collections.Generic; -//using System.Text; -//using System.Text.Json; - -//namespace AzureOpenAI.Models; - -//internal partial class AzureCreateChatCompletionResponseChoice : IJsonModel -//{ -// AzureCreateChatCompletionResponseChoice IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) -// { -// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; -// if (format != "J") -// { -// throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{format}' format."); -// } - -// using JsonDocument document = JsonDocument.ParseValue(ref reader); -// return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); -// } - - -// AzureCreateChatCompletionResponseChoice IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) -// { -// var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - -// switch (format) -// { -// case "J": -// { -// using JsonDocument document = JsonDocument.Parse(data); -// return DeserializeAzureCreateCompletionResponseChoice(document.RootElement, options); -// } -// default: -// throw new FormatException($"The model {nameof(CreateCompletionResponseChoice)} does not support reading '{options.Format}' format."); -// } -// } - -// private AzureCreateChatCompletionResponseChoice DeserializeAzureCreateCompletionResponseChoice(JsonElement rootElement, ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } - -// string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) -// => "J"; - -// void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } - -// BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs deleted file mode 100644 index 2c0bc0347..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponseChoice.cs +++ /dev/null @@ -1,7 +0,0 @@ -//using OpenAI.Models; - -//namespace AzureOpenAI.Models; - -//internal partial class AzureCreateChatCompletionResponseChoice : CreateChatCompletionResponseChoice -//{ -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs index 7c466690d..734ef6953 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs @@ -51,31 +51,32 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mode { foreach (var item in _serializedAdditionalRawData) { - writer.WritePropertyName(item.Key); - - switch (item.Value) + if (item.Value is SerializedModelData serializedValue) { - case SerializedModelData serializedValue: + + writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(serializedValue); + writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } + using (JsonDocument document = JsonDocument.Parse(serializedValue)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } #endif - break; - - // If it's not a SerializedModelData that one of our models - // stored as additionalRawData, it's an un-serialized value. - // Serialize it now. - case IJsonModel model: - model.Write(writer, options); - break; - - default: - JsonSerializer.Serialize(writer, item.Value); - break; + } + } + } + if (_serializedAdditionalRawData != null) + { + // If it's not a SerializedModelData that one of our models + // stored as additionalRawData, it's an un-serialized value. + // Serialize it now. + foreach (var item in _serializedAdditionalRawData) + { + if (item.Value is not SerializedModelData) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value); } } } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index cae7f368b..ac36840e2 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -257,35 +257,35 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { foreach (var item in _serializedAdditionalRawData) { - writer.WritePropertyName(item.Key); - - switch (item.Value) + if (item.Value is SerializedModelData serializedValue) { - case SerializedModelData serializedValue: + + writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER - writer.WriteRawValue(serializedValue); + writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } + using (JsonDocument document = JsonDocument.Parse(serializedValue)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } #endif - break; - - // If it's not a SerializedModelData that one of our models - // stored as additionalRawData, it's an un-serialized value. - // Serialize it now. - case IJsonModel model: - model.Write(writer, options); - break; - - default: - JsonSerializer.Serialize(writer, item.Value); - break; } } } - + if (_serializedAdditionalRawData != null) + { + // If it's not a SerializedModelData that one of our models + // stored as additionalRawData, it's an un-serialized value. + // Serialize it now. + foreach (var item in _serializedAdditionalRawData) + { + if (item.Value is not SerializedModelData) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value); + } + } + } writer.WriteEndObject(); } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index 49f9cfefe..879387dba 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -44,7 +44,7 @@ public partial class CreateChatCompletionRequest /// private IDictionary _serializedAdditionalRawData; [EditorBrowsable(EditorBrowsableState.Never)] - // TODO: lazily instantiate; also, add it to all models. + // TODO: merge with "additional properties" feature? public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); /// Initializes a new instance of . From b2ac716ca48ed5e2c033520eca13a58a58dff9e0 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Fri, 19 Apr 2024 18:21:17 -0700 Subject: [PATCH 20/32] WIP - trying Mads's suggestion --- .../typespec-csharp/app/DemoApp/Program.cs | 2 +- .../azoai/AzureOpenAI/AzureChatClient.cs | 7 +- .../CreateChatCompletionRequestExtensions.cs | 74 +++++++++++++++++-- ...eateChatCompletionRequest.Serialization.cs | 25 +++++-- .../Models/CreateChatCompletionRequest.cs | 4 +- 5 files changed, 93 insertions(+), 19 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index 2114d9016..2a13c817d 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -40,7 +40,7 @@ void CallAzureService() { // Uri endpoint = new("https://annelo-openai-01.openai.azure.com/"); - string apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!; + string apiKey = "Fake"; // Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!; AzureOpenAIClient client = new(endpoint, new ApiKeyCredential(apiKey), GetAzureClientOptions()); // diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index 50cd1cbe0..77059b7e2 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -1,4 +1,5 @@ -using OpenAI; +using AzureOpenAI.Models; +using OpenAI; using OpenAI.Models; using System.ClientModel; using System.ClientModel.Primitives; @@ -19,7 +20,7 @@ public override async Task> CreateCha { Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); - using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); + using BinaryContent content = createChatCompletionRequest.ToBinaryContent(); ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; return ClientResult.FromValue(value, result.GetRawResponse()); @@ -29,7 +30,7 @@ public override ClientResult CreateChatCompletion( { Argument.AssertNotNull(createChatCompletionRequest, nameof(createChatCompletionRequest)); - using BinaryContent content = BinaryContent.Create(createChatCompletionRequest, new ModelReaderWriterOptions("W")); + using BinaryContent content = createChatCompletionRequest.ToBinaryContent(); ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; return ClientResult.FromValue(value, result.GetRawResponse()); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 1ac45234c..238b210e9 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -1,29 +1,87 @@ using OpenAI.Models; +using System.ClientModel; +using System.ClientModel.Primitives; using System.Diagnostics; +using System.Text.Json; namespace AzureOpenAI.Models; public static class CreateChatCompletionRequestExtensions { + private static readonly Dictionary> _dataSources = new(); + public static IList GetDataSources(this CreateChatCompletionRequest request) { - // TODO: How can we validate that this is being called in the right context, - // e.g. user is using an Azure client instance and not a third-party one? + // Note: no need to validate in this scenario because base client won't serialize + // result. It would be a courtesy, otherwise this will fail silently, but won't + // leak data to the wrong service. JsonModelList dataSources; - if (request.SerializedAdditionalRawData.TryGetValue("data_sources", out object? value)) + if (_dataSources.TryGetValue(request, out JsonModelList? value)) { - Debug.Assert(value is JsonModelList); - - dataSources = (value as JsonModelList)!; + Debug.Assert(value != null); + dataSources = value!; } else { - dataSources = []; - request.SerializedAdditionalRawData.Add("data_sources", dataSources); + dataSources = new JsonModelList(); + _dataSources[request] = dataSources; } return dataSources; } + + internal static BinaryContent ToBinaryContent(this CreateChatCompletionRequest request) + { + _dataSources.TryGetValue(request, out JsonModelList? dataSources); + return BinaryContent.Create(new AzureChatCompletionRequest(request, dataSources)); + } + + internal class AzureChatCompletionRequest : IJsonModel + { + private readonly CreateChatCompletionRequest _request; + private readonly JsonModelList? _dataSources; + + public AzureChatCompletionRequest(CreateChatCompletionRequest request, JsonModelList? dataSources) + { + _request = request; + _dataSources = dataSources; + } + + AzureChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + AzureChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + throw new NotImplementedException(); + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + => "J"; + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + ((IJsonModel)_request).Write(writer, new ModelReaderWriterOptions("W*")); + if (_dataSources is not null) + { + ((IJsonModel)_dataSources).Write(writer, new ModelReaderWriterOptions("W")); + } + writer.WriteEndObject(); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + return format switch + { + "J" => ModelReaderWriter.Write(this, options), + _ => throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{options.Format}' format."), + }; + } + } } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index ac36840e2..da2ad1152 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -14,13 +14,17 @@ public partial class CreateChatCompletionRequest : IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - if (format != "J") + var format = (options.Format == "W" || options.Format == "W*") ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J" && format != "J*") { throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{format}' format."); } - writer.WriteStartObject(); + if (format == "J") + { + // don't write for "W*" + writer.WriteStartObject(); + } writer.WritePropertyName("messages"u8); writer.WriteStartArray(); foreach (var item in Messages) @@ -286,7 +290,11 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR } } } - writer.WriteEndObject(); + if (format == "J") + { + // don't write for "W*" + writer.WriteEndObject(); + } } CreateChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) @@ -598,7 +606,14 @@ CreateChatCompletionRequest IPersistableModel.Creat } } - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + { + return options.Format switch + { + "W*" => "J*", + _ => "J" + }; + } /// Deserializes the model from a raw response. /// The result to deserialize the model from. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index 879387dba..cb72e1ba8 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -43,9 +43,9 @@ public partial class CreateChatCompletionRequest /// /// private IDictionary _serializedAdditionalRawData; - [EditorBrowsable(EditorBrowsableState.Never)] + //[EditorBrowsable(EditorBrowsableState.Never)] // TODO: merge with "additional properties" feature? - public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); + //public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); /// Initializes a new instance of . /// From 04c02557d6784fcfd73a6e41b0905f1039f2cff7 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 16:37:26 -0700 Subject: [PATCH 21/32] use local ClientModel; remove BinaryData subtype; update input extensions --- .../@azure-tools/typespec-csharp/OpenAI.sln | 10 ++- .../CreateChatCompletionRequestExtensions.cs | 54 +++++++++----- .../Generated/Internal/SerializedModelData.cs | 12 ---- ...CompletionResponseMessage.Serialization.cs | 31 +++----- ...eateChatCompletionRequest.Serialization.cs | 70 +++++-------------- .../Models/CreateChatCompletionRequest.cs | 6 +- .../typespec-csharp/src/OpenAI.csproj | 5 +- 7 files changed, 76 insertions(+), 112 deletions(-) delete mode 100644 tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln b/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln index 60d92a3a4..e46a7dedf 100644 --- a/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln +++ b/tsp-output/@azure-tools/typespec-csharp/OpenAI.sln @@ -6,9 +6,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI", "src\OpenAI.csproj EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAI.Tests", "tests\OpenAI.Tests.csproj", "{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoApp", "app\DemoApp\DemoApp.csproj", "{79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoApp", "app\DemoApp\DemoApp.csproj", "{79A5AD41-2A38-4771-93FE-0B3BCC6DB61A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureOpenAI", "azoai\AzureOpenAI\AzureOpenAI.csproj", "{FB61EB83-7171-48F4-9423-E64B99BDAA5A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureOpenAI", "azoai\AzureOpenAI\AzureOpenAI.csproj", "{FB61EB83-7171-48F4-9423-E64B99BDAA5A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ClientModel", "..\..\..\..\azure-sdk-for-net\sdk\core\System.ClientModel\src\System.ClientModel.csproj", "{8364F37B-2B3D-4CAA-8F20-5B808E19C6CE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -32,6 +34,10 @@ Global {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB61EB83-7171-48F4-9423-E64B99BDAA5A}.Release|Any CPU.Build.0 = Release|Any CPU + {8364F37B-2B3D-4CAA-8F20-5B808E19C6CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8364F37B-2B3D-4CAA-8F20-5B808E19C6CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8364F37B-2B3D-4CAA-8F20-5B808E19C6CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8364F37B-2B3D-4CAA-8F20-5B808E19C6CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 238b210e9..37b27a92a 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -8,34 +8,38 @@ namespace AzureOpenAI.Models; public static class CreateChatCompletionRequestExtensions { - private static readonly Dictionary> _dataSources = new(); - public static IList GetDataSources(this CreateChatCompletionRequest request) { - // Note: no need to validate in this scenario because base client won't serialize - // result. It would be a courtesy, otherwise this will fail silently, but won't - // leak data to the wrong service. + // TODO: How can we validate that this is being called in the right context, + // e.g. user is using an Azure client instance and not a third-party one? + // Or does it matter? + + if (request is not IJsonModel model) + { + throw new InvalidOperationException("TODO"); + } JsonModelList dataSources; - if (_dataSources.TryGetValue(request, out JsonModelList? value)) + if (model.AdditionalProperties.TryGetValue("data_sources", out object? value)) { - Debug.Assert(value != null); - dataSources = value!; + Debug.Assert(value is JsonModelList); + + dataSources = (value as JsonModelList)!; } else { - dataSources = new JsonModelList(); - _dataSources[request] = dataSources; + dataSources = []; + model.AdditionalProperties.Add("data_sources", dataSources); } return dataSources; } - internal static BinaryContent ToBinaryContent(this CreateChatCompletionRequest request) + public static BinaryContent ToBinaryContent(this CreateChatCompletionRequest request) { - _dataSources.TryGetValue(request, out JsonModelList? dataSources); - return BinaryContent.Create(new AzureChatCompletionRequest(request, dataSources)); + AzureChatCompletionRequest azureRequest = new(request); + return BinaryContent.Create(azureRequest, new ModelReaderWriterOptions("W")); } internal class AzureChatCompletionRequest : IJsonModel @@ -43,20 +47,31 @@ internal class AzureChatCompletionRequest : IJsonModel? _dataSources; - public AzureChatCompletionRequest(CreateChatCompletionRequest request, JsonModelList? dataSources) + public AzureChatCompletionRequest(CreateChatCompletionRequest request) { _request = request; - _dataSources = dataSources; + + if (request is not IJsonModel model) + { + throw new InvalidOperationException("TODO"); + } + + if (model.AdditionalProperties.TryGetValue("data_sources", out object? value)) + { + Debug.Assert(value is JsonModelList); + + _dataSources = (value as JsonModelList)!; + } } AzureChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) { - throw new NotImplementedException(); + throw new NotSupportedException("Not supported for input types."); } AzureChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) { - throw new NotImplementedException(); + throw new NotSupportedException("Not supported for input types."); } string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) @@ -65,11 +80,16 @@ string IPersistableModel.GetFormatFromOptions(ModelR void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) { writer.WriteStartObject(); + + // Note that we write the base model differently in this context ((IJsonModel)_request).Write(writer, new ModelReaderWriterOptions("W*")); + + // Write the additional Azure properties if (_dataSources is not null) { ((IJsonModel)_dataSources).Write(writer, new ModelReaderWriterOptions("W")); } + writer.WriteEndObject(); } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs deleted file mode 100644 index 3fc65e2f1..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Internal/SerializedModelData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenAI; - -internal class SerializedModelData : BinaryData -{ - public SerializedModelData(string value) : base(value) - { - } -} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs index 734ef6953..8f205f1ab 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs @@ -51,35 +51,24 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mode { foreach (var item in _serializedAdditionalRawData) { - if (item.Value is SerializedModelData serializedValue) + // Skip non-serialized items + if (item.Value is not BinaryData serializedValue) { + continue; + } - writer.WritePropertyName(item.Key); + writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } -#endif - } - } - } - if (_serializedAdditionalRawData != null) - { - // If it's not a SerializedModelData that one of our models - // stored as additionalRawData, it's an un-serialized value. - // Serialize it now. - foreach (var item in _serializedAdditionalRawData) - { - if (item.Value is not SerializedModelData) + using (JsonDocument document = JsonDocument.Parse(serializedValue)) { - writer.WritePropertyName(item.Key); - writer.WriteObjectValue(item.Value); + JsonSerializer.Serialize(writer, document.RootElement); } +#endif } } + writer.WriteEndObject(); } @@ -151,7 +140,7 @@ internal static ChatCompletionResponseMessage DeserializeChatCompletionResponseM } if (options.Format != "W") { - additionalPropertiesDictionary.Add(property.Name, new SerializedModelData(property.Value.GetRawText())); + additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); } } serializedAdditionalRawData = additionalPropertiesDictionary; diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index da2ad1152..62f91ddf0 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -10,10 +10,12 @@ namespace OpenAI.Models { - public partial class CreateChatCompletionRequest : IJsonModel + public partial class CreateChatCompletionRequest : JsonModel { - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) { + // Note: support format "W*" that doesn't write start/end object + // to enable extending write routine. var format = (options.Format == "W" || options.Format == "W*") ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J" && format != "J*") { @@ -25,6 +27,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR // don't write for "W*" writer.WriteStartObject(); } + writer.WritePropertyName("messages"u8); writer.WriteStartArray(); foreach (var item in Messages) @@ -261,33 +264,21 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR { foreach (var item in _serializedAdditionalRawData) { - if (item.Value is SerializedModelData serializedValue) + // Skip non-serialized items + if (item.Value is not BinaryData serializedValue) { + continue; + } - writer.WritePropertyName(item.Key); + writer.WritePropertyName(item.Key); #if NET6_0_OR_GREATER writer.WriteRawValue(serializedValue); #else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } -#endif - } - } - } - if (_serializedAdditionalRawData != null) - { - // If it's not a SerializedModelData that one of our models - // stored as additionalRawData, it's an un-serialized value. - // Serialize it now. - foreach (var item in _serializedAdditionalRawData) - { - if (item.Value is not SerializedModelData) + using (JsonDocument document = JsonDocument.Parse(serializedValue)) { - writer.WritePropertyName(item.Key); - writer.WriteObjectValue(item.Value); + JsonSerializer.Serialize(writer, document.RootElement); } +#endif } } if (format == "J") @@ -297,7 +288,7 @@ void IJsonModel.Write(Utf8JsonWriter writer, ModelR } } - CreateChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + protected override CreateChatCompletionRequest CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -549,7 +540,7 @@ internal static CreateChatCompletionRequest DeserializeCreateChatCompletionReque } if (options.Format != "W") { - additionalPropertiesDictionary.Add(property.Name, new SerializedModelData(property.Value.GetRawText())); + additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); } } serializedAdditionalRawData = additionalPropertiesDictionary; @@ -577,36 +568,7 @@ internal static CreateChatCompletionRequest DeserializeCreateChatCompletionReque serializedAdditionalRawData); } - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - return ModelReaderWriter.Write(this, options); - default: - throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{options.Format}' format."); - } - } - - CreateChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeCreateChatCompletionRequest(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support reading '{options.Format}' format."); - } - } - - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + protected override string GetFormatFromOptionsCore(ModelReaderWriterOptions options) { return options.Format switch { diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index cb72e1ba8..a40240bb5 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; namespace OpenAI.Models @@ -43,10 +42,7 @@ public partial class CreateChatCompletionRequest /// /// private IDictionary _serializedAdditionalRawData; - //[EditorBrowsable(EditorBrowsableState.Never)] - // TODO: merge with "additional properties" feature? - //public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); - + /// Initializes a new instance of . /// /// A list of messages comprising the conversation so far. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/OpenAI.csproj b/tsp-output/@azure-tools/typespec-csharp/src/OpenAI.csproj index 95e032e4e..edd6c0e06 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/OpenAI.csproj +++ b/tsp-output/@azure-tools/typespec-csharp/src/OpenAI.csproj @@ -10,7 +10,10 @@ - + + + + From 065287085d2a133593f4cffb5bc7f361a6e274d0 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 16:46:33 -0700 Subject: [PATCH 22/32] rework input model wrapper type --- .../typespec-csharp/app/DemoApp/Program.cs | 3 + .../Input/AzureChatCompletionRequest.cs | 71 +++++++++++++++++++ .../CreateChatCompletionRequestExtensions.cs | 69 +----------------- 3 files changed, 77 insertions(+), 66 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs index 2a13c817d..d71844c34 100644 --- a/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs +++ b/tsp-output/@azure-tools/typespec-csharp/app/DemoApp/Program.cs @@ -69,6 +69,9 @@ void CallAzureService() // ClientResult result = chatClient.CreateChatCompletion(request); + + Console.WriteLine("**"); + CreateChatCompletionResponse completion = result.Value; // diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs new file mode 100644 index 000000000..22538ede4 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs @@ -0,0 +1,71 @@ +using OpenAI.Models; +using System.ClientModel.Primitives; +using System.Diagnostics; +using System.Text.Json; + +namespace AzureOpenAI.Models.Input +{ + internal class AzureCreateChatCompletionRequest : IJsonModel + { + private readonly CreateChatCompletionRequest _request; + private readonly JsonModelList? _dataSources; + + public AzureCreateChatCompletionRequest(CreateChatCompletionRequest request) + { + _request = request; + + if (request is not IJsonModel model) + { + throw new InvalidOperationException("TODO"); + } + + if (model.AdditionalProperties.TryGetValue("data_sources", out object? value)) + { + Debug.Assert(value is JsonModelList); + + _dataSources = (value as JsonModelList)!; + } + } + + AzureCreateChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + throw new NotSupportedException("Not supported for input types."); + } + + AzureCreateChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + throw new NotSupportedException("Not supported for input types."); + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + => "J"; + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + + // Note that we write the base model differently in this context + ((IJsonModel)_request).Write(writer, new ModelReaderWriterOptions("W*")); + + // Write the additional Azure properties + if (_dataSources is not null) + { + writer.WritePropertyName("data_sources"); + ((IJsonModel)_dataSources).Write(writer, new ModelReaderWriterOptions("W")); + } + + writer.WriteEndObject(); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + return format switch + { + "J" => ModelReaderWriter.Write(this, options), + _ => throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{options.Format}' format."), + }; + } + } +} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 37b27a92a..09d24022c 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -1,8 +1,8 @@ -using OpenAI.Models; +using AzureOpenAI.Models.Input; +using OpenAI.Models; using System.ClientModel; using System.ClientModel.Primitives; using System.Diagnostics; -using System.Text.Json; namespace AzureOpenAI.Models; @@ -38,70 +38,7 @@ public static IList GetDataSources(this CreateC public static BinaryContent ToBinaryContent(this CreateChatCompletionRequest request) { - AzureChatCompletionRequest azureRequest = new(request); + AzureCreateChatCompletionRequest azureRequest = new(request); return BinaryContent.Create(azureRequest, new ModelReaderWriterOptions("W")); } - - internal class AzureChatCompletionRequest : IJsonModel - { - private readonly CreateChatCompletionRequest _request; - private readonly JsonModelList? _dataSources; - - public AzureChatCompletionRequest(CreateChatCompletionRequest request) - { - _request = request; - - if (request is not IJsonModel model) - { - throw new InvalidOperationException("TODO"); - } - - if (model.AdditionalProperties.TryGetValue("data_sources", out object? value)) - { - Debug.Assert(value is JsonModelList); - - _dataSources = (value as JsonModelList)!; - } - } - - AzureChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) - { - throw new NotSupportedException("Not supported for input types."); - } - - AzureChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - throw new NotSupportedException("Not supported for input types."); - } - - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) - => "J"; - - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) - { - writer.WriteStartObject(); - - // Note that we write the base model differently in this context - ((IJsonModel)_request).Write(writer, new ModelReaderWriterOptions("W*")); - - // Write the additional Azure properties - if (_dataSources is not null) - { - ((IJsonModel)_dataSources).Write(writer, new ModelReaderWriterOptions("W")); - } - - writer.WriteEndObject(); - } - - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - return format switch - { - "J" => ModelReaderWriter.Write(this, options), - _ => throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{options.Format}' format."), - }; - } - } } From 24124c98860672d7281a96278e7ac33679c2ae29 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 17:06:38 -0700 Subject: [PATCH 23/32] handle output models; no change really --- .../azoai/AzureOpenAI/AzureChatClient.cs | 10 ++ .../Input/AzureChatCompletionRequest.cs | 93 +++++++++---------- .../AzureChatCompletionResponseMessage.cs | 38 ++++++++ .../AzureCreateChatCompletionResponse.cs | 25 +++++ ...ChatCompletionResponseMessageExtensions.cs | 5 +- 5 files changed, 121 insertions(+), 50 deletions(-) create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs create mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index 77059b7e2..ab4f20d93 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -22,7 +22,12 @@ public override async Task> CreateCha using BinaryContent content = createChatCompletionRequest.ToBinaryContent(); ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); + var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; + + //// Note: need to pass special options to read additional properties + //var value = ModelReaderWriter.Read(result.GetRawResponse().Content, new ModelReaderWriterOptions("W*"))!; + return ClientResult.FromValue(value, result.GetRawResponse()); } @@ -32,7 +37,12 @@ public override ClientResult CreateChatCompletion( using BinaryContent content = createChatCompletionRequest.ToBinaryContent(); ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); + var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; + + //// Note: need to pass special options to read additional properties + //var value = ModelReaderWriter.Read(result.GetRawResponse().Content, new ModelReaderWriterOptions("W*"))!; + return ClientResult.FromValue(value, result.GetRawResponse()); } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs index 22538ede4..928c8177d 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs @@ -3,69 +3,68 @@ using System.Diagnostics; using System.Text.Json; -namespace AzureOpenAI.Models.Input +namespace AzureOpenAI.Models; + +internal class AzureCreateChatCompletionRequest : IJsonModel { - internal class AzureCreateChatCompletionRequest : IJsonModel + private readonly CreateChatCompletionRequest _request; + private readonly JsonModelList? _dataSources; + + public AzureCreateChatCompletionRequest(CreateChatCompletionRequest request) { - private readonly CreateChatCompletionRequest _request; - private readonly JsonModelList? _dataSources; + _request = request; - public AzureCreateChatCompletionRequest(CreateChatCompletionRequest request) + if (request is not IJsonModel model) { - _request = request; - - if (request is not IJsonModel model) - { - throw new InvalidOperationException("TODO"); - } - - if (model.AdditionalProperties.TryGetValue("data_sources", out object? value)) - { - Debug.Assert(value is JsonModelList); - - _dataSources = (value as JsonModelList)!; - } + throw new InvalidOperationException("TODO"); } - AzureCreateChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + if (model.AdditionalProperties.TryGetValue("data_sources", out object? value)) { - throw new NotSupportedException("Not supported for input types."); - } + Debug.Assert(value is JsonModelList); - AzureCreateChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - throw new NotSupportedException("Not supported for input types."); + _dataSources = (value as JsonModelList)!; } + } - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) - => "J"; + AzureCreateChatCompletionRequest IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + throw new NotSupportedException("Not supported for input types."); + } - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) - { - writer.WriteStartObject(); + AzureCreateChatCompletionRequest IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + throw new NotSupportedException("Not supported for input types."); + } - // Note that we write the base model differently in this context - ((IJsonModel)_request).Write(writer, new ModelReaderWriterOptions("W*")); + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) + => "J"; - // Write the additional Azure properties - if (_dataSources is not null) - { - writer.WritePropertyName("data_sources"); - ((IJsonModel)_dataSources).Write(writer, new ModelReaderWriterOptions("W")); - } + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); - writer.WriteEndObject(); - } + // Note that we write the base model differently in this context + ((IJsonModel)_request).Write(writer, new ModelReaderWriterOptions("W*")); - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + // Write the additional Azure properties + if (_dataSources is not null) { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - return format switch - { - "J" => ModelReaderWriter.Write(this, options), - _ => throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{options.Format}' format."), - }; + writer.WritePropertyName("data_sources"); + ((IJsonModel)_dataSources).Write(writer, new ModelReaderWriterOptions("W")); } + + writer.WriteEndObject(); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + return format switch + { + "J" => ModelReaderWriter.Write(this, options), + _ => throw new FormatException($"The model {nameof(CreateChatCompletionRequest)} does not support writing '{options.Format}' format."), + }; } } diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs new file mode 100644 index 000000000..e14ae5291 --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs @@ -0,0 +1,38 @@ +//using OpenAI.Models; +//using System.ClientModel.Primitives; +//using System.Diagnostics; +//using System.Text.Json; + +//namespace AzureOpenAI.Models; + +//internal class AzureChatCompletionResponseMessage : JsonModel +//{ +// private readonly ChatCompletionResponseMessage _message; +// private readonly AzureChatExtensionsMessageContext? _azureContext; + +// public AzureChatCompletionResponseMessage(ChatCompletionResponseMessage message) +// { +// _message = message; + +// if (message is not IJsonModel model) +// { +// throw new InvalidOperationException("TODO"); +// } + +// if (model.AdditionalProperties.TryGetValue("context", out object? value)) +// { +// Debug.Assert(value is AzureChatExtensionsMessageContext); + +// _azureContext = value as AzureChatExtensionsMessageContext; +// } +// } + +// protected override AzureChatCompletionResponseMessage CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) +// { +// } + +// protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) +// { +// throw new NotSupportedException("TODO"); +// } +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs new file mode 100644 index 000000000..b02e040cb --- /dev/null +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs @@ -0,0 +1,25 @@ +//using System; +//using System.ClientModel.Primitives; +//using System.Collections.Generic; +//using System.Text; +//using System.Text.Json; + +//namespace AzureOpenAI.Models; + +//internal class AzureCreateChatCompletionResponse : JsonModel +//{ +// public AzureCreateChatCompletionResponse() +// { + +// } + +// protected override AzureCreateChatCompletionResponse CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) +// { + +// } + +// protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) +// { +// throw new NotImplementedException(); +// } +//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs index aa73bb838..76753cc2c 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs @@ -14,9 +14,8 @@ public static class ChatCompletionResponseMessageExtensions } // It's either deserialized already or not. Find out now. - // TODO: this works for OpenAI scenarios today, but we need to find a - // way to handle cases where BinaryData is a valid type for extended - // properties. + // This should work for all cases because we know the contract regarding + // BinaryData values. if (value is BinaryData serializedValue) { // Qn: when can Read return null? From 687a88aef2591b8261039986419da91c8b4b4c16 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 17:08:23 -0700 Subject: [PATCH 24/32] nits --- .../Models/Input/CreateChatCompletionRequestExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 09d24022c..08215cad0 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -1,5 +1,4 @@ -using AzureOpenAI.Models.Input; -using OpenAI.Models; +using OpenAI.Models; using System.ClientModel; using System.ClientModel.Primitives; using System.Diagnostics; From 8226cf8d3c91d93ec1b2fb01580afd6dafb2dd59 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 17:10:27 -0700 Subject: [PATCH 25/32] nits --- .../azoai/AzureOpenAI/AzureChatClient.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index ab4f20d93..77059b7e2 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -22,12 +22,7 @@ public override async Task> CreateCha using BinaryContent content = createChatCompletionRequest.ToBinaryContent(); ClientResult result = await CreateChatCompletionAsync(createChatCompletionRequest.Model.ToString(), content, context: default).ConfigureAwait(false); - var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; - - //// Note: need to pass special options to read additional properties - //var value = ModelReaderWriter.Read(result.GetRawResponse().Content, new ModelReaderWriterOptions("W*"))!; - return ClientResult.FromValue(value, result.GetRawResponse()); } @@ -37,12 +32,7 @@ public override ClientResult CreateChatCompletion( using BinaryContent content = createChatCompletionRequest.ToBinaryContent(); ClientResult result = CreateChatCompletion(createChatCompletionRequest.Model.ToString(), content, context: default); - var value = ModelReaderWriter.Read(result.GetRawResponse().Content)!; - - //// Note: need to pass special options to read additional properties - //var value = ModelReaderWriter.Read(result.GetRawResponse().Content, new ModelReaderWriterOptions("W*"))!; - return ClientResult.FromValue(value, result.GetRawResponse()); } From f629e007143a2381dade3f16078214eec23c9880 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 17:18:31 -0700 Subject: [PATCH 26/32] updates --- .../Output/ChatCompletionResponseMessageExtensions.cs | 9 +++++++-- .../Generated/Models/ChatCompletionResponseMessage.cs | 6 +----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs index 76753cc2c..2c62b0eaa 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/ChatCompletionResponseMessageExtensions.cs @@ -8,7 +8,12 @@ public static class ChatCompletionResponseMessageExtensions // Output property public static AzureChatExtensionsMessageContext? GetAzureExtensionsContext(this ChatCompletionResponseMessage message) { - if (!message.SerializedAdditionalRawData.TryGetValue("context", out object? value)) + if (message is not IJsonModel model) + { + throw new InvalidOperationException("TODO"); + } + + if (!model.AdditionalProperties.TryGetValue("context", out object? value)) { return null; } @@ -20,7 +25,7 @@ public static class ChatCompletionResponseMessageExtensions { // Qn: when can Read return null? var deserializedValue = ModelReaderWriter.Read(serializedValue)!; - message.SerializedAdditionalRawData["context"] = deserializedValue; + model.AdditionalProperties["context"] = deserializedValue; return deserializedValue; } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs index 7798ef4ab..49b221690 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; namespace OpenAI.Models { @@ -43,9 +42,6 @@ public partial class ChatCompletionResponseMessage /// private IDictionary? _serializedAdditionalRawData; - [EditorBrowsable(EditorBrowsableState.Never)] - public IDictionary SerializedAdditionalRawData => _serializedAdditionalRawData ??= new Dictionary(); - /// Initializes a new instance of . /// The contents of the message. internal ChatCompletionResponseMessage(string content) @@ -70,7 +66,7 @@ internal ChatCompletionResponseMessage(string content, IReadOnlyList Initializes a new instance of for deserialization. - private ChatCompletionResponseMessage() + internal ChatCompletionResponseMessage() { } From d97c28c48ba43390c1c2fdc1b36d75b768d32b82 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 17:24:18 -0700 Subject: [PATCH 27/32] nits and updates --- tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs | 2 +- .../Models/ChatCompletionResponseMessage.Serialization.cs | 1 - .../Models/CreateChatCompletionRequest.Serialization.cs | 4 ++-- .../src/Generated/Models/CreateChatCompletionRequest.cs | 2 +- .../src/Generated/Models/CreateChatCompletionResponse.cs | 2 +- .../Generated/Models/CreateChatCompletionResponseChoice.cs | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs index e8c197777..84d746c82 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs @@ -126,7 +126,7 @@ public virtual ClientResult CreateChatCompletion(BinaryContent content, RequestO return ClientResult.FromResponse(_pipeline.ProcessMessage(message, context)); } - private PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) + internal PipelineMessage CreateCreateChatCompletionRequest(BinaryContent content, RequestOptions context) { var message = _pipeline.CreateMessage(); if (context != null) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs index 8f205f1ab..dd5942457 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs @@ -68,7 +68,6 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mode #endif } } - writer.WriteEndObject(); } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index 62f91ddf0..0fb02dd9d 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -162,7 +162,7 @@ protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOption { writer.WritePropertyName("stop"u8); #if NET6_0_OR_GREATER - writer.WriteRawValue(Stop); + writer.WriteRawValue(Stop); #else using (JsonDocument document = JsonDocument.Parse(Stop)) { @@ -225,7 +225,7 @@ protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOption { writer.WritePropertyName("tool_choice"u8); #if NET6_0_OR_GREATER - writer.WriteRawValue(ToolChoice); + writer.WriteRawValue(ToolChoice); #else using (JsonDocument document = JsonDocument.Parse(ToolChoice)) { diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index a40240bb5..cc16ec008 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -42,7 +42,7 @@ public partial class CreateChatCompletionRequest /// /// private IDictionary _serializedAdditionalRawData; - + /// Initializes a new instance of . /// /// A list of messages comprising the conversation so far. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs index 1885bf508..811da2977 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs @@ -88,7 +88,7 @@ internal CreateChatCompletionResponse(string id, IReadOnlyList Initializes a new instance of for deserialization. - public CreateChatCompletionResponse() + internal CreateChatCompletionResponse() { } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs index cf7af0590..d9579d2bd 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.cs @@ -86,7 +86,7 @@ internal CreateChatCompletionResponseChoice(CreateChatCompletionResponseChoiceFi } /// Initializes a new instance of for deserialization. - public CreateChatCompletionResponseChoice() + internal CreateChatCompletionResponseChoice() { } From 6f2b486a75e6c768b51efa71e24f274949f5e853 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 18:08:04 -0700 Subject: [PATCH 28/32] rework things a bit --- .../AzureChatCompletionResponseMessage.cs | 38 -------- .../AzureCreateChatCompletionResponse.cs | 25 ------ ...CompletionResponseMessage.Serialization.cs | 90 ++++--------------- .../Models/ChatCompletionResponseMessage.cs | 49 +--------- ...eateChatCompletionRequest.Serialization.cs | 21 +---- .../Models/CreateChatCompletionRequest.cs | 33 ------- ...ateChatCompletionResponse.Serialization.cs | 73 +++------------ .../Models/CreateChatCompletionResponse.cs | 36 +------- ...tCompletionResponseChoice.Serialization.cs | 3 +- .../src/Generated/OpenAIModelFactory.cs | 3 +- 10 files changed, 38 insertions(+), 333 deletions(-) delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs delete mode 100644 tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs deleted file mode 100644 index e14ae5291..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureChatCompletionResponseMessage.cs +++ /dev/null @@ -1,38 +0,0 @@ -//using OpenAI.Models; -//using System.ClientModel.Primitives; -//using System.Diagnostics; -//using System.Text.Json; - -//namespace AzureOpenAI.Models; - -//internal class AzureChatCompletionResponseMessage : JsonModel -//{ -// private readonly ChatCompletionResponseMessage _message; -// private readonly AzureChatExtensionsMessageContext? _azureContext; - -// public AzureChatCompletionResponseMessage(ChatCompletionResponseMessage message) -// { -// _message = message; - -// if (message is not IJsonModel model) -// { -// throw new InvalidOperationException("TODO"); -// } - -// if (model.AdditionalProperties.TryGetValue("context", out object? value)) -// { -// Debug.Assert(value is AzureChatExtensionsMessageContext); - -// _azureContext = value as AzureChatExtensionsMessageContext; -// } -// } - -// protected override AzureChatCompletionResponseMessage CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) -// { -// } - -// protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) -// { -// throw new NotSupportedException("TODO"); -// } -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs deleted file mode 100644 index b02e040cb..000000000 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Output/AzureCreateChatCompletionResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -//using System; -//using System.ClientModel.Primitives; -//using System.Collections.Generic; -//using System.Text; -//using System.Text.Json; - -//namespace AzureOpenAI.Models; - -//internal class AzureCreateChatCompletionResponse : JsonModel -//{ -// public AzureCreateChatCompletionResponse() -// { - -// } - -// protected override AzureCreateChatCompletionResponse CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) -// { - -// } - -// protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) -// { -// throw new NotImplementedException(); -// } -//} diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs index dd5942457..308ae7367 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.Serialization.cs @@ -10,9 +10,9 @@ namespace OpenAI.Models { - public partial class ChatCompletionResponseMessage : IJsonModel + public partial class ChatCompletionResponseMessage : JsonModel { - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -47,31 +47,14 @@ void IJsonModel.Write(Utf8JsonWriter writer, Mode writer.WritePropertyName("function_call"u8); writer.WriteObjectValue(FunctionCall, options); } - if (options.Format != "W" && _serializedAdditionalRawData != null) + if (options.Format != "W") { - foreach (var item in _serializedAdditionalRawData) - { - // Skip non-serialized items - if (item.Value is not BinaryData serializedValue) - { - continue; - } - - writer.WritePropertyName(item.Key); -#if NET6_0_OR_GREATER - writer.WriteRawValue(serializedValue); -#else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } -#endif - } + WriteUnknownProperties(writer, options); } writer.WriteEndObject(); } - ChatCompletionResponseMessage IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + protected override ChatCompletionResponseMessage CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -79,34 +62,25 @@ ChatCompletionResponseMessage IJsonModel.Create(r throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{format}' format."); } + // TODO: use Reader APIs using JsonDocument document = JsonDocument.ParseValue(ref reader); - return DeserializeChatCompletionResponseMessage(document.RootElement, options); - } - - internal static ChatCompletionResponseMessage DeserializeChatCompletionResponseMessage(JsonElement element, ModelReaderWriterOptions options = null) - { + JsonElement element = document.RootElement; options ??= new ModelReaderWriterOptions("W"); if (element.ValueKind == JsonValueKind.Null) { return null; } - string content = default; - IReadOnlyList toolCalls = default; - ChatCompletionResponseMessageRole role = default; - ChatCompletionResponseMessageFunctionCall functionCall = default; - IDictionary serializedAdditionalRawData = default; - Dictionary additionalPropertiesDictionary = new Dictionary(); foreach (var property in element.EnumerateObject()) { if (property.NameEquals("content"u8)) { if (property.Value.ValueKind == JsonValueKind.Null) { - content = null; + Content = null; continue; } - content = property.Value.GetString(); + Content = property.Value.GetString(); continue; } if (property.NameEquals("tool_calls"u8)) @@ -120,12 +94,12 @@ internal static ChatCompletionResponseMessage DeserializeChatCompletionResponseM { array.Add(ChatCompletionMessageToolCall.DeserializeChatCompletionMessageToolCall(item, options)); } - toolCalls = array; + ToolCalls = array; continue; } if (property.NameEquals("role"u8)) { - role = new ChatCompletionResponseMessageRole(property.Value.GetString()); + Role = new ChatCompletionResponseMessageRole(property.Value.GetString()); continue; } if (property.NameEquals("function_call"u8)) @@ -134,55 +108,23 @@ internal static ChatCompletionResponseMessage DeserializeChatCompletionResponseM { continue; } - functionCall = ChatCompletionResponseMessageFunctionCall.DeserializeChatCompletionResponseMessageFunctionCall(property.Value, options); + FunctionCall = ChatCompletionResponseMessageFunctionCall.DeserializeChatCompletionResponseMessageFunctionCall(property.Value, options); continue; } if (options.Format != "W") { - additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + // TODO: use Reader APIs + ((IJsonModel)this).AdditionalProperties.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); } } - serializedAdditionalRawData = additionalPropertiesDictionary; - return new ChatCompletionResponseMessage(content, toolCalls ?? new ChangeTrackingList(), role, functionCall, serializedAdditionalRawData); - } - - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - return ModelReaderWriter.Write(this, options); - default: - throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support writing '{options.Format}' format."); - } - } - - ChatCompletionResponseMessage IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeChatCompletionResponseMessage(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(ChatCompletionResponseMessage)} does not support reading '{options.Format}' format."); - } + return this; } - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; - /// Deserializes the model from a raw response. /// The result to deserialize the model from. internal static ChatCompletionResponseMessage FromResponse(PipelineResponse response) { - using var document = JsonDocument.Parse(response.Content); - return DeserializeChatCompletionResponseMessage(document.RootElement); + return ModelReaderWriter.Read(response.Content); } /// Convert into a Utf8JsonRequestBody. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs index 49b221690..b6bb2cb11 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/ChatCompletionResponseMessage.cs @@ -10,46 +10,6 @@ namespace OpenAI.Models /// The ChatCompletionResponseMessage. public partial class ChatCompletionResponseMessage { - /// - /// Keeps track of any properties unknown to the library. - /// - /// To assign an object to the value of this property use . - /// - /// - /// To assign an already formatted json string to this property use . - /// - /// - /// Examples: - /// - /// - /// BinaryData.FromObjectAsJson("foo") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromString("\"foo\"") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromObjectAsJson(new { key = "value" }) - /// Creates a payload of { "key": "value" }. - /// - /// - /// BinaryData.FromString("{\"key\": \"value\"}") - /// Creates a payload of { "key": "value" }. - /// - /// - /// - /// - private IDictionary? _serializedAdditionalRawData; - - /// Initializes a new instance of . - /// The contents of the message. - internal ChatCompletionResponseMessage(string content) - { - Content = content; - ToolCalls = new ChangeTrackingList(); - } - /// Initializes a new instance of . /// The contents of the message. /// @@ -62,7 +22,6 @@ internal ChatCompletionResponseMessage(string content, IReadOnlyList Initializes a new instance of for deserialization. @@ -71,13 +30,13 @@ internal ChatCompletionResponseMessage() } /// The contents of the message. - public string Content { get; } + public string Content { get; private set; } /// Gets the tool calls. - public IReadOnlyList ToolCalls { get; } + public IReadOnlyList ToolCalls { get; private set; } /// The role of the author of this message. - public ChatCompletionResponseMessageRole Role { get; } = ChatCompletionResponseMessageRole.Assistant; + public ChatCompletionResponseMessageRole Role { get; private set; } = ChatCompletionResponseMessageRole.Assistant; /// Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model. - public ChatCompletionResponseMessageFunctionCall FunctionCall { get; } + public ChatCompletionResponseMessageFunctionCall FunctionCall { get; private set; } } } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs index 0fb02dd9d..0d8348203 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.Serialization.cs @@ -260,26 +260,9 @@ protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOption } writer.WriteEndArray(); } - if (options.Format != "W" && _serializedAdditionalRawData != null) + if (options.Format != "W") { - foreach (var item in _serializedAdditionalRawData) - { - // Skip non-serialized items - if (item.Value is not BinaryData serializedValue) - { - continue; - } - - writer.WritePropertyName(item.Key); -#if NET6_0_OR_GREATER - writer.WriteRawValue(serializedValue); -#else - using (JsonDocument document = JsonDocument.Parse(serializedValue)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } -#endif - } + WriteUnknownProperties(writer, options); } if (format == "J") { diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs index cc16ec008..4f23bf189 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionRequest.cs @@ -11,38 +11,6 @@ namespace OpenAI.Models /// The CreateChatCompletionRequest. public partial class CreateChatCompletionRequest { - /// - /// Keeps track of any properties unknown to the library. - /// - /// To assign an object to the value of this property use . - /// - /// - /// To assign an already formatted json string to this property use . - /// - /// - /// Examples: - /// - /// - /// BinaryData.FromObjectAsJson("foo") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromString("\"foo\"") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromObjectAsJson(new { key = "value" }) - /// Creates a payload of { "key": "value" }. - /// - /// - /// BinaryData.FromString("{\"key\": \"value\"}") - /// Creates a payload of { "key": "value" }. - /// - /// - /// - /// - private IDictionary _serializedAdditionalRawData; - /// Initializes a new instance of . /// /// A list of messages comprising the conversation so far. @@ -209,7 +177,6 @@ internal CreateChatCompletionRequest(IList messages, CreateChatCompl User = user; FunctionCall = functionCall; Functions = functions; - _serializedAdditionalRawData = serializedAdditionalRawData; } /// Initializes a new instance of for deserialization. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs index c2a9b2e99..77d7f46ab 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs @@ -10,9 +10,9 @@ namespace OpenAI.Models { - public partial class CreateChatCompletionResponse : IJsonModel + public partial class CreateChatCompletionResponse : JsonModel { - void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -46,25 +46,14 @@ void IJsonModel.Write(Utf8JsonWriter writer, Model writer.WritePropertyName("usage"u8); writer.WriteObjectValue(Usage, options); } - if (options.Format != "W" && _serializedAdditionalRawData != null) + if (options.Format != "W") { - foreach (var item in _serializedAdditionalRawData) - { - writer.WritePropertyName(item.Key); -#if NET6_0_OR_GREATER - writer.WriteRawValue(item.Value); -#else - using (JsonDocument document = JsonDocument.Parse(item.Value)) - { - JsonSerializer.Serialize(writer, document.RootElement); - } -#endif - } + WriteUnknownProperties(writer, options); } writer.WriteEndObject(); } - CreateChatCompletionResponse IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + protected override CreateChatCompletionResponse CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -72,12 +61,9 @@ CreateChatCompletionResponse IJsonModel.Create(ref throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{format}' format."); } + // TODO: use Reader APIs using JsonDocument document = JsonDocument.ParseValue(ref reader); - return DeserializeCreateChatCompletionResponse(document.RootElement, options); - } - - internal static CreateChatCompletionResponse DeserializeCreateChatCompletionResponse(JsonElement element, ModelReaderWriterOptions options = null) - { + JsonElement element = document.RootElement; options ??= new ModelReaderWriterOptions("W"); if (element.ValueKind == JsonValueKind.Null) @@ -91,8 +77,6 @@ internal static CreateChatCompletionResponse DeserializeCreateChatCompletionResp string systemFingerprint = default; CreateChatCompletionResponseObject @object = default; CompletionUsage usage = default; - IDictionary serializedAdditionalRawData = default; - Dictionary additionalPropertiesDictionary = new Dictionary(); foreach (var property in element.EnumerateObject()) { if (property.NameEquals("id"u8)) @@ -141,10 +125,10 @@ internal static CreateChatCompletionResponse DeserializeCreateChatCompletionResp } if (options.Format != "W") { - additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + // TODO: use Reader APIs + ((IJsonModel)this).AdditionalProperties.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); } } - serializedAdditionalRawData = additionalPropertiesDictionary; return new CreateChatCompletionResponse( id, choices, @@ -152,47 +136,14 @@ internal static CreateChatCompletionResponse DeserializeCreateChatCompletionResp model, systemFingerprint, @object, - usage, - serializedAdditionalRawData); + usage); } - - BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - return ModelReaderWriter.Write(this, options); - default: - throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support writing '{options.Format}' format."); - } - } - - CreateChatCompletionResponse IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) - { - var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; - - switch (format) - { - case "J": - { - using JsonDocument document = JsonDocument.Parse(data); - return DeserializeCreateChatCompletionResponse(document.RootElement, options); - } - default: - throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{options.Format}' format."); - } - } - - string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; - + /// Deserializes the model from a raw response. /// The result to deserialize the model from. internal static CreateChatCompletionResponse FromResponse(PipelineResponse response) { - using var document = JsonDocument.Parse(response.Content); - return DeserializeCreateChatCompletionResponse(document.RootElement); + return ModelReaderWriter.Read(response.Content); } /// Convert into a Utf8JsonRequestBody. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs index 811da2977..87dd6aabe 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs @@ -11,38 +11,6 @@ namespace OpenAI.Models /// Represents a chat completion response returned by model, based on the provided input. public partial class CreateChatCompletionResponse { - /// - /// Keeps track of any properties unknown to the library. - /// - /// To assign an object to the value of this property use . - /// - /// - /// To assign an already formatted json string to this property use . - /// - /// - /// Examples: - /// - /// - /// BinaryData.FromObjectAsJson("foo") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromString("\"foo\"") - /// Creates a payload of "foo". - /// - /// - /// BinaryData.FromObjectAsJson(new { key = "value" }) - /// Creates a payload of { "key": "value" }. - /// - /// - /// BinaryData.FromString("{\"key\": \"value\"}") - /// Creates a payload of { "key": "value" }. - /// - /// - /// - /// - private IDictionary _serializedAdditionalRawData; - /// Initializes a new instance of . /// A unique identifier for the chat completion. /// A list of chat completion choices. Can be more than one if `n` is greater than 1. @@ -74,8 +42,7 @@ internal CreateChatCompletionResponse(string id, IEnumerable /// The object type, which is always `chat.completion`. /// - /// Keeps track of any properties unknown to the library. - internal CreateChatCompletionResponse(string id, IReadOnlyList choices, DateTimeOffset created, string model, string systemFingerprint, CreateChatCompletionResponseObject @object, CompletionUsage usage, IDictionary serializedAdditionalRawData) + internal CreateChatCompletionResponse(string id, IReadOnlyList choices, DateTimeOffset created, string model, string systemFingerprint, CreateChatCompletionResponseObject @object, CompletionUsage usage) { Id = id; Choices = choices; @@ -84,7 +51,6 @@ internal CreateChatCompletionResponse(string id, IReadOnlyList Initializes a new instance of for deserialization. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.Serialization.cs index a07ad941d..a994c21a7 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponseChoice.Serialization.cs @@ -94,7 +94,8 @@ internal static CreateChatCompletionResponseChoice DeserializeCreateChatCompleti } if (property.NameEquals("message"u8)) { - message = ChatCompletionResponseMessage.DeserializeChatCompletionResponseMessage(property.Value, options); + // TODO: use reader APIs instead of going through BinaryData + message = ModelReaderWriter.Read(BinaryData.FromString(property.Value.ToString()), options); continue; } if (property.NameEquals("logprobs"u8)) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs index f1ac128e2..28dae4a0f 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs @@ -580,8 +580,7 @@ public static CreateChatCompletionResponse CreateChatCompletionResponse(string i model, systemFingerprint, @object, - usage, - serializedAdditionalRawData: null); + usage); } /// Initializes a new instance of . From b124eff407621c4ecde9ea07f99acc26b362fb2e Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 22 Apr 2024 18:12:39 -0700 Subject: [PATCH 29/32] revert unneeded changes --- ...ateChatCompletionResponse.Serialization.cs | 73 ++++++++++++++++--- .../Models/CreateChatCompletionResponse.cs | 36 ++++++++- .../src/Generated/OpenAIModelFactory.cs | 3 +- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs index 77d7f46ab..c2a9b2e99 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.Serialization.cs @@ -10,9 +10,9 @@ namespace OpenAI.Models { - public partial class CreateChatCompletionResponse : JsonModel + public partial class CreateChatCompletionResponse : IJsonModel { - protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -46,14 +46,25 @@ protected override void WriteCore(Utf8JsonWriter writer, ModelReaderWriterOption writer.WritePropertyName("usage"u8); writer.WriteObjectValue(Usage, options); } - if (options.Format != "W") + if (options.Format != "W" && _serializedAdditionalRawData != null) { - WriteUnknownProperties(writer, options); + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } } writer.WriteEndObject(); } - protected override CreateChatCompletionResponse CreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + CreateChatCompletionResponse IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) { var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; if (format != "J") @@ -61,9 +72,12 @@ protected override CreateChatCompletionResponse CreateCore(ref Utf8JsonReader re throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{format}' format."); } - // TODO: use Reader APIs using JsonDocument document = JsonDocument.ParseValue(ref reader); - JsonElement element = document.RootElement; + return DeserializeCreateChatCompletionResponse(document.RootElement, options); + } + + internal static CreateChatCompletionResponse DeserializeCreateChatCompletionResponse(JsonElement element, ModelReaderWriterOptions options = null) + { options ??= new ModelReaderWriterOptions("W"); if (element.ValueKind == JsonValueKind.Null) @@ -77,6 +91,8 @@ protected override CreateChatCompletionResponse CreateCore(ref Utf8JsonReader re string systemFingerprint = default; CreateChatCompletionResponseObject @object = default; CompletionUsage usage = default; + IDictionary serializedAdditionalRawData = default; + Dictionary additionalPropertiesDictionary = new Dictionary(); foreach (var property in element.EnumerateObject()) { if (property.NameEquals("id"u8)) @@ -125,10 +141,10 @@ protected override CreateChatCompletionResponse CreateCore(ref Utf8JsonReader re } if (options.Format != "W") { - // TODO: use Reader APIs - ((IJsonModel)this).AdditionalProperties.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + additionalPropertiesDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); } } + serializedAdditionalRawData = additionalPropertiesDictionary; return new CreateChatCompletionResponse( id, choices, @@ -136,14 +152,47 @@ protected override CreateChatCompletionResponse CreateCore(ref Utf8JsonReader re model, systemFingerprint, @object, - usage); + usage, + serializedAdditionalRawData); } - + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options); + default: + throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support writing '{options.Format}' format."); + } + } + + CreateChatCompletionResponse IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data); + return DeserializeCreateChatCompletionResponse(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(CreateChatCompletionResponse)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + /// Deserializes the model from a raw response. /// The result to deserialize the model from. internal static CreateChatCompletionResponse FromResponse(PipelineResponse response) { - return ModelReaderWriter.Read(response.Content); + using var document = JsonDocument.Parse(response.Content); + return DeserializeCreateChatCompletionResponse(document.RootElement); } /// Convert into a Utf8JsonRequestBody. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs index 87dd6aabe..811da2977 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Models/CreateChatCompletionResponse.cs @@ -11,6 +11,38 @@ namespace OpenAI.Models /// Represents a chat completion response returned by model, based on the provided input. public partial class CreateChatCompletionResponse { + /// + /// Keeps track of any properties unknown to the library. + /// + /// To assign an object to the value of this property use . + /// + /// + /// To assign an already formatted json string to this property use . + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromObjectAsJson("foo") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromString("\"foo\"") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromObjectAsJson(new { key = "value" }) + /// Creates a payload of { "key": "value" }. + /// + /// + /// BinaryData.FromString("{\"key\": \"value\"}") + /// Creates a payload of { "key": "value" }. + /// + /// + /// + /// + private IDictionary _serializedAdditionalRawData; + /// Initializes a new instance of . /// A unique identifier for the chat completion. /// A list of chat completion choices. Can be more than one if `n` is greater than 1. @@ -42,7 +74,8 @@ internal CreateChatCompletionResponse(string id, IEnumerable /// The object type, which is always `chat.completion`. /// - internal CreateChatCompletionResponse(string id, IReadOnlyList choices, DateTimeOffset created, string model, string systemFingerprint, CreateChatCompletionResponseObject @object, CompletionUsage usage) + /// Keeps track of any properties unknown to the library. + internal CreateChatCompletionResponse(string id, IReadOnlyList choices, DateTimeOffset created, string model, string systemFingerprint, CreateChatCompletionResponseObject @object, CompletionUsage usage, IDictionary serializedAdditionalRawData) { Id = id; Choices = choices; @@ -51,6 +84,7 @@ internal CreateChatCompletionResponse(string id, IReadOnlyList Initializes a new instance of for deserialization. diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs index 28dae4a0f..f1ac128e2 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIModelFactory.cs @@ -580,7 +580,8 @@ public static CreateChatCompletionResponse CreateChatCompletionResponse(string i model, systemFingerprint, @object, - usage); + usage, + serializedAdditionalRawData: null); } /// Initializes a new instance of . From 4cf6fa6aaf1f0bc1a655acfbf918c8d84d528253 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 23 Apr 2024 09:42:58 -0700 Subject: [PATCH 30/32] update clients --- .../typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs | 4 +++- .../typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs | 6 +++++- .../@azure-tools/typespec-csharp/src/Generated/Chat.cs | 2 -- .../typespec-csharp/src/Generated/OpenAIClient.cs | 3 --- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs index 77059b7e2..fe8ff22be 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureChatClient.cs @@ -9,11 +9,13 @@ namespace AzureOpenAI; internal class AzureChatClient : Chat { private readonly string _apiVersion; + private readonly Uri _endpoint; internal AzureChatClient(ClientPipeline pipeline, ApiKeyCredential credential, Uri endpoint, string apiVersion) : base(pipeline, credential, endpoint) { _apiVersion = apiVersion; + _endpoint = endpoint; } public override async Task> CreateChatCompletionAsync(CreateChatCompletionRequest createChatCompletionRequest, CancellationToken cancellationToken = default) @@ -80,7 +82,7 @@ private PipelineMessage CreateCreateChatCompletionRequest(string model, BinaryCo request.Method = "POST"; var uri = new ClientUriBuilder(); - uri.Reset(Endpoint); + uri.Reset(_endpoint); uri.AppendPath("/openai/deployments/", false); uri.AppendPath(model, false); uri.AppendPath("/chat/completions", false); diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs index 092653950..81ea55bb2 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/AzureOpenAIClient.cs @@ -6,6 +6,8 @@ namespace AzureOpenAI; public class AzureOpenAIClient : OpenAIClient { private readonly string _apiVersion; + private readonly ApiKeyCredential _apiKeyCredential; + private readonly Uri _endpoint; public AzureOpenAIClient(Uri endpoint, ApiKeyCredential credential, AzureOpenAIClientOptions? options = default) : base(endpoint, credential, options) @@ -13,10 +15,12 @@ public AzureOpenAIClient(Uri endpoint, ApiKeyCredential credential, AzureOpenAIC options ??= new AzureOpenAIClientOptions(); _apiVersion = options.ApiVersion; + _endpoint = endpoint; + _apiKeyCredential = credential; } public override Chat GetChatClient() { - return new AzureChatClient(Pipeline, KeyCredential, Endpoint, _apiVersion); + return new AzureChatClient(Pipeline, _apiKeyCredential, _endpoint, _apiVersion); } } diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs index 84d746c82..f142108fc 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/Chat.cs @@ -21,8 +21,6 @@ public partial class Chat private readonly ClientPipeline _pipeline; private readonly Uri _endpoint; - protected Uri Endpoint => _endpoint; - /// The HTTP pipeline for sending and receiving REST requests and responses. public virtual ClientPipeline Pipeline => _pipeline; diff --git a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs index ed5d58e32..c56780241 100644 --- a/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs +++ b/tsp-output/@azure-tools/typespec-csharp/src/Generated/OpenAIClient.cs @@ -13,9 +13,6 @@ namespace OpenAI /// The OpenAI service client. public partial class OpenAIClient { - protected ApiKeyCredential KeyCredential => _keyCredential; - protected Uri Endpoint => _endpoint; - private const string AuthorizationHeader = "Authorization"; private readonly ApiKeyCredential _keyCredential; private const string AuthorizationApiKeyPrefix = "Bearer"; From 6ae64d3b80d366c8894364788b73117b91171a13 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 23 Apr 2024 10:26:57 -0700 Subject: [PATCH 31/32] rename file --- ...atCompletionRequest.cs => AzureCreateChatCompletionRequest.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/{AzureChatCompletionRequest.cs => AzureCreateChatCompletionRequest.cs} (100%) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureCreateChatCompletionRequest.cs similarity index 100% rename from tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureChatCompletionRequest.cs rename to tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/AzureCreateChatCompletionRequest.cs From dd32b09b1cd204baadddf71674e5dc22fb0583ba Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 23 Apr 2024 16:22:55 -0700 Subject: [PATCH 32/32] nits --- .../Models/Input/CreateChatCompletionRequestExtensions.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs index 08215cad0..d56e2a5a3 100644 --- a/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs +++ b/tsp-output/@azure-tools/typespec-csharp/azoai/AzureOpenAI/Models/Input/CreateChatCompletionRequestExtensions.cs @@ -9,10 +9,6 @@ public static class CreateChatCompletionRequestExtensions { public static IList GetDataSources(this CreateChatCompletionRequest request) { - // TODO: How can we validate that this is being called in the right context, - // e.g. user is using an Azure client instance and not a third-party one? - // Or does it matter? - if (request is not IJsonModel model) { throw new InvalidOperationException("TODO");