From 85c89ce475128829f255f4ac5be35332fa8a572a Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Wed, 30 Oct 2024 13:37:48 -0700 Subject: [PATCH 1/8] Add Inference --- eng/Packages.Data.props | 1 + .../src/Azure.AI.Projects.csproj | 5 + .../src/Custom/AIProjectClient.cs | 22 +++ .../Custom/Connection/ConnectionsClient.cs | 30 ++++ .../src/Custom/Inference/InferenceClient.cs | 139 ++++++++++++++++++ .../{ => Agent}/Sample_Agent_Basics.cs | 0 .../{ => Agent}/Sample_Agent_Functions.cs | 0 .../{ => Agent}/Sample_Agent_Streaming.cs | 0 .../{ => Agent}/Samples_Agent_FileSearch.cs | 0 .../Inference/Sample_ChatCompletions.cs | 21 +++ 10 files changed, 218 insertions(+) create mode 100644 sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs rename sdk/ai/Azure.AI.Projects/tests/Samples/{ => Agent}/Sample_Agent_Basics.cs (100%) rename sdk/ai/Azure.AI.Projects/tests/Samples/{ => Agent}/Sample_Agent_Functions.cs (100%) rename sdk/ai/Azure.AI.Projects/tests/Samples/{ => Agent}/Sample_Agent_Streaming.cs (100%) rename sdk/ai/Azure.AI.Projects/tests/Samples/{ => Agent}/Samples_Agent_FileSearch.cs (100%) create mode 100644 sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props index 2898d8ec5886e..53ea299967134 100644 --- a/eng/Packages.Data.props +++ b/eng/Packages.Data.props @@ -129,6 +129,7 @@ + diff --git a/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj b/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj index 9ee32c0284579..c086183aceebe 100644 --- a/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj +++ b/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj @@ -15,6 +15,7 @@ + @@ -29,4 +30,8 @@ + + + + diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs index 054c841b1a654..4b2274efdef6d 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs @@ -3,6 +3,7 @@ using System; using Azure.Core; +using Azure.Core.Pipeline; namespace Azure.AI.Projects { @@ -36,5 +37,26 @@ public AIProjectClient(string connectionString, TokenCredential credential, AIPr options) { } + + internal AIProjectClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, TokenCredential tokenCredential, Uri endpoint, string subscriptionId, string resourceGroupName, string projectName) + { + ClientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + _tokenCredential = tokenCredential; + _endpoint = endpoint; + _subscriptionId = subscriptionId; + _resourceGroupName = resourceGroupName; + _projectName = projectName; + } + + /// Initializes a new instance of EvaluationsClient. + /// The API version to use for this operation. + /// is null. + public virtual InferenceClient GetInferenceClient(string apiVersion = "2024-07-01-preview") + { + Argument.AssertNotNull(apiVersion, nameof(apiVersion)); + + return new InferenceClient(ClientDiagnostics, _pipeline, _tokenCredential, _endpoint, _subscriptionId, _resourceGroupName, _projectName, apiVersion); + } } } diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs index 45ba561ad9d0f..5ae09a2b35d2a 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs @@ -4,6 +4,8 @@ #nullable disable using System; +using System.Threading.Tasks; +using System.Threading; using Azure.Core; using Azure.Core.Pipeline; @@ -79,5 +81,33 @@ public ConnectionsClient(Uri endpoint, string subscriptionId, string resourceGro _projectName = projectName; _apiVersion = options.Version; } + + /// List the details of all the connections (not including their credentials). + /// Category of the workspace connection. + /// Indicates whether to list datastores. Service default: do not list datastores. + /// Target of the workspace connection. + /// The cancellation token to use. + internal virtual async Task> GetConnectionAsync(ConnectionType? category = null, bool? includeAll = null, string target = null, CancellationToken cancellationToken = default) + { + ConnectionsListResponse connections = await GetConnectionsAsync(category, includeAll, target, cancellationToken).ConfigureAwait(false); + ConnectionsListSecretsResponse secret = connections.Value[0]; + + Response secretResponse = await GetConnectionAsync(secret.Name).ConfigureAwait(false); + return secretResponse; + } + + /// Get the details of a single connection. + /// Category of the workspace connection. + /// Indicates whether to list datastores. Service default: do not list datastores. + /// Target of the workspace connection. + /// The cancellation token to use. + internal virtual Response GetConnection(ConnectionType? category = null, bool? includeAll = null, string target = null, CancellationToken cancellationToken = default) + { + ConnectionsListResponse connections = GetConnections(category, includeAll, target, cancellationToken); + ConnectionsListSecretsResponse secret = connections.Value[0]; + + Response secretResponse = GetConnection(secret.Name); + return secretResponse; + } } } diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs new file mode 100644 index 0000000000000..2cadd4202ec75 --- /dev/null +++ b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable disable + +using System; +using Azure.AI.Inference; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.AI.Projects +{ + /// The Inference sub-client. + public partial class InferenceClient + { + private static readonly string[] AuthorizationScopes = new string[] { "https://management.azure.com/.default" }; + private readonly TokenCredential _tokenCredential; + private readonly HttpPipeline _pipeline; + private readonly Uri _endpoint; + private readonly string _subscriptionId; + private readonly string _resourceGroupName; + private readonly string _projectName; + private readonly string _apiVersion; + + /// The ClientDiagnostics is used to provide tracing support for the client library. + internal ClientDiagnostics ClientDiagnostics { get; } + + /// The HTTP pipeline for sending and receiving REST requests and responses. + public virtual HttpPipeline Pipeline => _pipeline; + + /// The ClientDiagnostics is used to provide tracing support for the client library. + internal AIProjectClient AIProjectClient => new AIProjectClient(ClientDiagnostics, _pipeline, _tokenCredential, _endpoint, _subscriptionId, _resourceGroupName, _projectName); + + /// Initializes a new instance of ConnectionsClient for mocking. + protected InferenceClient() + { + } + + /// Initializes a new instance of InferenceClient. + /// The Azure AI Studio project connection string, in the form `endpoint;subscription_id;resource_group_name;project_name`. + /// A credential used to authenticate to an Azure Service. + /// is null. + /// + public InferenceClient(string connectionString, TokenCredential credential) : this(connectionString, credential, new AIProjectClientOptions()) + { + } + + /// + /// Initializes a new instance of InferenceClient. + /// + /// The Azure AI Studio project connection string, in the form `endpoint;subscription_id;resource_group_name;project_name`. + /// A credential used to authenticate to an Azure Service. + /// The options for configuring the client. + /// is null. + /// is an empty string. + public InferenceClient(string connectionString, TokenCredential credential, AIProjectClientOptions options) + : this(new Uri(ClientHelper.ParseConnectionString(connectionString, "endpoint")), + ClientHelper.ParseConnectionString(connectionString, "subscriptionId"), + ClientHelper.ParseConnectionString(connectionString, "resourceGroupName"), + ClientHelper.ParseConnectionString(connectionString, "projectName"), + credential, + options) + { + } + + /// Initializes a new instance of InferenceClient. + /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. + /// The Azure subscription ID. + /// The name of the Azure Resource Group. + /// The Azure AI Studio project name. + /// A credential used to authenticate to an Azure Service. + /// , , , or is null. + /// , or is an empty string, and was expected to be non-empty. + public InferenceClient(Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, TokenCredential credential) : this(endpoint, subscriptionId, resourceGroupName, projectName, credential, new AIProjectClientOptions()) + { + } + + /// Initializes a new instance of InferenceClient. + /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. + /// The Azure subscription ID. + /// The name of the Azure Resource Group. + /// The Azure AI Studio project name. + /// A credential used to authenticate to an Azure Service. + /// The options for configuring the client. + /// , , , or is null. + /// , or is an empty string, and was expected to be non-empty. + public InferenceClient(Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, TokenCredential credential, AIProjectClientOptions options) + { + Argument.AssertNotNull(endpoint, nameof(endpoint)); + Argument.AssertNotNullOrEmpty(subscriptionId, nameof(subscriptionId)); + Argument.AssertNotNullOrEmpty(resourceGroupName, nameof(resourceGroupName)); + Argument.AssertNotNullOrEmpty(projectName, nameof(projectName)); + Argument.AssertNotNull(credential, nameof(credential)); + options ??= new AIProjectClientOptions(); + + ClientDiagnostics = new ClientDiagnostics(options, true); + _tokenCredential = credential; + _pipeline = HttpPipelineBuilder.Build(options, Array.Empty(), new HttpPipelinePolicy[] { new BearerTokenAuthenticationPolicy(_tokenCredential, AuthorizationScopes) }, new ResponseClassifier()); + _endpoint = endpoint; + _subscriptionId = subscriptionId; + _resourceGroupName = resourceGroupName; + _projectName = projectName; + _apiVersion = options.Version; + } + + /// Initializes a new instance of InferenceClient. + /// The handler for diagnostic messaging in the client. + /// The HTTP pipeline for sending and receiving REST requests and responses. + /// The token credential to copy. + /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. + /// The Azure subscription ID. + /// The name of the Azure Resource Group. + /// The Azure AI Studio project name. + /// The API version to use for this operation. + internal InferenceClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, TokenCredential tokenCredential, Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, string apiVersion) + { + ClientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + _tokenCredential = tokenCredential; + _endpoint = endpoint; + _subscriptionId = subscriptionId; + _resourceGroupName = resourceGroupName; + _projectName = projectName; + _apiVersion = apiVersion; + } + + // Initializes a new instance of Inference's ChatCompletionsClient. + public virtual ChatCompletionsClient GetChatCompletionsClient() + { + ConnectionsListSecretsResponse secret = AIProjectClient.GetConnectionsClient().GetConnection(); + + // Get the URI and Key from the secret + var endpoint = new Uri("uri"); + var credential = new AzureKeyCredential("key"); + + return new ChatCompletionsClient(endpoint, credential, new AzureAIInferenceClientOptions()); + } + } +} diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Sample_Agent_Basics.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Basics.cs similarity index 100% rename from sdk/ai/Azure.AI.Projects/tests/Samples/Sample_Agent_Basics.cs rename to sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Basics.cs diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Sample_Agent_Functions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs similarity index 100% rename from sdk/ai/Azure.AI.Projects/tests/Samples/Sample_Agent_Functions.cs rename to sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Sample_Agent_Streaming.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Streaming.cs similarity index 100% rename from sdk/ai/Azure.AI.Projects/tests/Samples/Sample_Agent_Streaming.cs rename to sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Streaming.cs diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Samples_Agent_FileSearch.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs similarity index 100% rename from sdk/ai/Azure.AI.Projects/tests/Samples/Samples_Agent_FileSearch.cs rename to sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs new file mode 100644 index 0000000000000..2514b456f2322 --- /dev/null +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable disable + +using System; +using Azure.Identity; +using NUnit.Framework; + +namespace Azure.AI.Projects.Tests; +public class Sample_ChatCompletions +{ + [Test] + public void InferenceChatCompletions() + { + var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); + + var test = client.GetChatCompletionsClient(); + } +} From 88319e8527a9ffe67f77053a3c3ef2fc1f143296 Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Wed, 30 Oct 2024 13:41:54 -0700 Subject: [PATCH 2/8] Update --- sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj | 4 ---- .../tests/Samples/Inference/Sample_ChatCompletions.cs | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj b/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj index c086183aceebe..f933d8b2ac100 100644 --- a/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj +++ b/sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj @@ -30,8 +30,4 @@ - - - - diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs index 2514b456f2322..11118d87ae406 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using Azure.AI.Inference; using Azure.Identity; using NUnit.Framework; @@ -16,6 +17,8 @@ public void InferenceChatCompletions() var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); - var test = client.GetChatCompletionsClient(); + ChatCompletionsClient test = client.GetChatCompletionsClient(); + + // Call ChatCompletionsClient Operations } } From 6c8aed668676d9d1b59929b500a9eda3fab9fa44 Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Wed, 30 Oct 2024 13:43:20 -0700 Subject: [PATCH 3/8] Fix code --- .../Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs index 2cadd4202ec75..6bf608dc2e048 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs @@ -127,7 +127,7 @@ internal InferenceClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipel // Initializes a new instance of Inference's ChatCompletionsClient. public virtual ChatCompletionsClient GetChatCompletionsClient() { - ConnectionsListSecretsResponse secret = AIProjectClient.GetConnectionsClient().GetConnection(); + ConnectionsListSecretsResponse secret = AIProjectClient.GetConnectionsClient().GetConnection(ConnectionType.Serverless, true); // Get the URI and Key from the secret var endpoint = new Uri("uri"); From 84c57bc679b956173b0e5df37d511f3f0b769ab7 Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Mon, 4 Nov 2024 13:45:33 -0800 Subject: [PATCH 4/8] Fix inference and add samples --- .../api/Azure.AI.Projects.netstandard2.0.cs | 12 ++ .../Custom/Connection/ConnectionsClient.cs | 133 ++++++++++++++++-- .../src/Custom/Inference/InferenceClient.cs | 56 +++++++- .../src/Generated/ConnectionsClient.cs | 98 ------------- .../Inference/Sample_ChatCompletions.cs | 16 ++- .../Samples/Inference/Sample_Embeddings.cs | 33 +++++ 6 files changed, 230 insertions(+), 118 deletions(-) create mode 100644 sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs diff --git a/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs b/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs index a1ae3e9755789..bc07a636b4023 100644 --- a/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs +++ b/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs @@ -459,6 +459,7 @@ public AIProjectClient(System.Uri endpoint, string subscriptionId, string resour public virtual Azure.AI.Projects.AgentsClient GetAgentsClient(string apiVersion = "2024-07-01-preview") { throw null; } public virtual Azure.AI.Projects.ConnectionsClient GetConnectionsClient(string apiVersion = "2024-07-01-preview") { throw null; } public virtual Azure.AI.Projects.EvaluationsClient GetEvaluationsClient(string apiVersion = "2024-07-01-preview") { throw null; } + public virtual Azure.AI.Projects.InferenceClient GetInferenceClient(string apiVersion = "2024-07-01-preview") { throw null; } } public partial class AIProjectClientOptions : Azure.Core.ClientOptions { @@ -937,6 +938,17 @@ public IndexResource(string indexConnectionId, string indexName) { } string System.ClientModel.Primitives.IPersistableModel.GetFormatFromOptions(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; } System.BinaryData System.ClientModel.Primitives.IPersistableModel.Write(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; } } + public partial class InferenceClient + { + protected InferenceClient() { } + public InferenceClient(string connectionString, Azure.Core.TokenCredential credential) { } + public InferenceClient(string connectionString, Azure.Core.TokenCredential credential, Azure.AI.Projects.AIProjectClientOptions options) { } + public InferenceClient(System.Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, Azure.Core.TokenCredential credential) { } + public InferenceClient(System.Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, Azure.Core.TokenCredential credential, Azure.AI.Projects.AIProjectClientOptions options) { } + public virtual Azure.Core.Pipeline.HttpPipeline Pipeline { get { throw null; } } + public virtual Azure.AI.Inference.ChatCompletionsClient GetChatCompletionsClient() { throw null; } + public virtual Azure.AI.Inference.EmbeddingsClient GetEmbeddingsClient() { throw null; } + } public abstract partial class InputData : System.ClientModel.Primitives.IJsonModel, System.ClientModel.Primitives.IPersistableModel { protected InputData() { } diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs index 5ae09a2b35d2a..03eb1a00dda16 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/Connection/ConnectionsClient.cs @@ -75,39 +75,152 @@ public ConnectionsClient(Uri endpoint, string subscriptionId, string resourceGro ClientDiagnostics = new ClientDiagnostics(options, true); _tokenCredential = credential; _pipeline = HttpPipelineBuilder.Build(options, Array.Empty(), new HttpPipelinePolicy[] { new BearerTokenAuthenticationPolicy(_tokenCredential, AuthorizationScopes) }, new ResponseClassifier()); - _endpoint = endpoint; + _endpoint = new Uri("https://management.azure.com"); _subscriptionId = subscriptionId; _resourceGroupName = resourceGroupName; _projectName = projectName; _apiVersion = options.Version; } + /// Initializes a new instance of ConnectionsClient. + /// The handler for diagnostic messaging in the client. + /// The HTTP pipeline for sending and receiving REST requests and responses. + /// The token credential to copy. + /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. + /// The Azure subscription ID. + /// The name of the Azure Resource Group. + /// The Azure AI Studio project name. + /// The API version to use for this operation. + internal ConnectionsClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, TokenCredential tokenCredential, Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, string apiVersion) + { + ClientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + _tokenCredential = tokenCredential; + _endpoint = new Uri("https://management.azure.com"); + _subscriptionId = subscriptionId; + _resourceGroupName = resourceGroupName; + _projectName = projectName; + _apiVersion = apiVersion; + } + /// List the details of all the connections (not including their credentials). /// Category of the workspace connection. + /// /// Indicates whether to list datastores. Service default: do not list datastores. /// Target of the workspace connection. /// The cancellation token to use. - internal virtual async Task> GetConnectionAsync(ConnectionType? category = null, bool? includeAll = null, string target = null, CancellationToken cancellationToken = default) + internal virtual async Task> GetDefaultConnectionAsync(ConnectionType category, bool? withCredential = null, bool? includeAll = null, string target = null, CancellationToken cancellationToken = default) { ConnectionsListResponse connections = await GetConnectionsAsync(category, includeAll, target, cancellationToken).ConfigureAwait(false); - ConnectionsListSecretsResponse secret = connections.Value[0]; - Response secretResponse = await GetConnectionAsync(secret.Name).ConfigureAwait(false); - return secretResponse; + if (connections?.Value == null || connections.Value.Count == 0) + { + throw new InvalidOperationException("No connections found for the specified parameters."); + } + + var secret = connections.Value[0]; + return withCredential.GetValueOrDefault() + ? await GetSecretsAsync(secret.Name, "ignored").ConfigureAwait(false) + : await GetConnectionAsync(secret.Name).ConfigureAwait(false); } /// Get the details of a single connection. /// Category of the workspace connection. + /// /// Indicates whether to list datastores. Service default: do not list datastores. /// Target of the workspace connection. /// The cancellation token to use. - internal virtual Response GetConnection(ConnectionType? category = null, bool? includeAll = null, string target = null, CancellationToken cancellationToken = default) + internal virtual Response GetDefaultConnection(ConnectionType category, bool? withCredential = null, bool? includeAll = null, string target = null, CancellationToken cancellationToken = default) { - ConnectionsListResponse connections = GetConnections(category, includeAll, target, cancellationToken); - ConnectionsListSecretsResponse secret = connections.Value[0]; + ConnectionsListResponse connections = GetConnections(category, includeAll, target, cancellationToken); - Response secretResponse = GetConnection(secret.Name); - return secretResponse; + if (connections?.Value == null || connections.Value.Count == 0) + { + throw new InvalidOperationException("No connections found for the specified parameters."); + } + + var secret = connections.Value[0]; + return withCredential.GetValueOrDefault() + ? GetSecrets(secret.Name, "ignored") + : GetConnection(secret.Name); + } + + // CUSTOM: Fixed the request URI by removing "/agents/v1.0" + internal HttpMessage CreateGetConnectionsRequest(string category, bool? includeAll, string target, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier200); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendRaw("/subscriptions/", false); + uri.AppendRaw(_subscriptionId, true); + uri.AppendRaw("/resourceGroups/", false); + uri.AppendRaw(_resourceGroupName, true); + uri.AppendRaw("/providers/Microsoft.MachineLearningServices/workspaces/", false); + uri.AppendRaw(_projectName, true); + uri.AppendPath("/connections", false); + uri.AppendQuery("api-version", _apiVersion, true); + if (category != null) + { + uri.AppendQuery("category", category, true); + } + if (includeAll != null) + { + uri.AppendQuery("includeAll", includeAll.Value, true); + } + if (target != null) + { + uri.AppendQuery("target", target, true); + } + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + internal HttpMessage CreateGetConnectionRequest(string connectionName, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier200); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendRaw("/subscriptions/", false); + uri.AppendRaw(_subscriptionId, true); + uri.AppendRaw("/resourceGroups/", false); + uri.AppendRaw(_resourceGroupName, true); + uri.AppendRaw("/providers/Microsoft.MachineLearningServices/workspaces/", false); + uri.AppendRaw(_projectName, true); + uri.AppendPath("/connections/", false); + uri.AppendPath(connectionName, true); + uri.AppendQuery("api-version", _apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + internal HttpMessage CreateGetSecretsRequest(string connectionName, RequestContent content, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier200); + var request = message.Request; + request.Method = RequestMethod.Post; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendRaw("/subscriptions/", false); + uri.AppendRaw(_subscriptionId, true); + uri.AppendRaw("/resourceGroups/", false); + uri.AppendRaw(_resourceGroupName, true); + uri.AppendRaw("/providers/Microsoft.MachineLearningServices/workspaces/", false); + uri.AppendRaw(_projectName, true); + uri.AppendPath("/connections/", false); + uri.AppendPath(connectionName, true); + uri.AppendPath("/listsecrets", false); + uri.AppendQuery("api-version", _apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("Content-Type", "application/json"); + request.Content = content; + return message; } } } diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs index 6bf608dc2e048..4c10ae3a6bd0c 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs @@ -124,16 +124,58 @@ internal InferenceClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipel _apiVersion = apiVersion; } - // Initializes a new instance of Inference's ChatCompletionsClient. + /// Initializes a new instance of Inference's ChatCompletionsClient. public virtual ChatCompletionsClient GetChatCompletionsClient() { - ConnectionsListSecretsResponse secret = AIProjectClient.GetConnectionsClient().GetConnection(ConnectionType.Serverless, true); - - // Get the URI and Key from the secret - var endpoint = new Uri("uri"); - var credential = new AzureKeyCredential("key"); + var connectionsClient = AIProjectClient.GetConnectionsClient(); + ConnectionsListSecretsResponse connectionSecret = connectionsClient.GetDefaultConnection(ConnectionType.Serverless, true); + + if (connectionSecret.Properties is ConnectionPropertiesApiKeyAuth apiKeyAuthProperties) + { + if (string.IsNullOrWhiteSpace(apiKeyAuthProperties.Target)) + { + throw new ArgumentException("The API key authentication target URI is missing or invalid."); + } + + if (!Uri.TryCreate(apiKeyAuthProperties.Target, UriKind.Absolute, out var endpoint)) + { + throw new UriFormatException("Invalid URI format in API key authentication target."); + } + + var credential = new AzureKeyCredential(apiKeyAuthProperties.Credentials.Key); + return new ChatCompletionsClient(endpoint, credential, new AzureAIInferenceClientOptions()); + } + else + { + throw new ArgumentException("Cannot connect with Inference! Ensure valid ConnectionPropertiesApiKeyAuth."); + } + } - return new ChatCompletionsClient(endpoint, credential, new AzureAIInferenceClientOptions()); + /// Initializes a new instance of Inference's EmbeddingsClient. + public virtual EmbeddingsClient GetEmbeddingsClient() + { + var connectionsClient = AIProjectClient.GetConnectionsClient(); + ConnectionsListSecretsResponse connectionSecret = connectionsClient.GetDefaultConnection(ConnectionType.Serverless, true); + + if (connectionSecret.Properties is ConnectionPropertiesApiKeyAuth apiKeyAuthProperties) + { + if (string.IsNullOrWhiteSpace(apiKeyAuthProperties.Target)) + { + throw new ArgumentException("The API key authentication target URI is missing or invalid."); + } + + if (!Uri.TryCreate(apiKeyAuthProperties.Target, UriKind.Absolute, out var endpoint)) + { + throw new UriFormatException("Invalid URI format in API key authentication target."); + } + + var credential = new AzureKeyCredential(apiKeyAuthProperties.Credentials.Key); + return new EmbeddingsClient(endpoint, credential, new AzureAIInferenceClientOptions()); + } + else + { + throw new ArgumentException("Cannot connect with Inference! Ensure valid ConnectionPropertiesApiKeyAuth."); + } } } } diff --git a/sdk/ai/Azure.AI.Projects/src/Generated/ConnectionsClient.cs b/sdk/ai/Azure.AI.Projects/src/Generated/ConnectionsClient.cs index b3edbeeb2c3e4..87f6a2931d580 100644 --- a/sdk/ai/Azure.AI.Projects/src/Generated/ConnectionsClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Generated/ConnectionsClient.cs @@ -38,27 +38,6 @@ protected ConnectionsClient() { } - /// Initializes a new instance of ConnectionsClient. - /// The handler for diagnostic messaging in the client. - /// The HTTP pipeline for sending and receiving REST requests and responses. - /// The token credential to copy. - /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. - /// The Azure subscription ID. - /// The name of the Azure Resource Group. - /// The Azure AI Studio project name. - /// The API version to use for this operation. - internal ConnectionsClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, TokenCredential tokenCredential, Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, string apiVersion) - { - ClientDiagnostics = clientDiagnostics; - _pipeline = pipeline; - _tokenCredential = tokenCredential; - _endpoint = endpoint; - _subscriptionId = subscriptionId; - _resourceGroupName = resourceGroupName; - _projectName = projectName; - _apiVersion = apiVersion; - } - /// List the details of all the connections (not including their credentials). /// Category of the workspace connection. /// Indicates whether to list datastores. Service default: do not list datastores. @@ -379,83 +358,6 @@ internal virtual Response GetSecrets(string connectionName, RequestContent conte } } - internal HttpMessage CreateGetConnectionsRequest(string category, bool? includeAll, string target, RequestContext context) - { - var message = _pipeline.CreateMessage(context, ResponseClassifier200); - var request = message.Request; - request.Method = RequestMethod.Get; - var uri = new RawRequestUriBuilder(); - uri.Reset(_endpoint); - uri.AppendRaw("/agents/v1.0/subscriptions/", false); - uri.AppendRaw(_subscriptionId, true); - uri.AppendRaw("/resourceGroups/", false); - uri.AppendRaw(_resourceGroupName, true); - uri.AppendRaw("/providers/Microsoft.MachineLearningServices/workspaces/", false); - uri.AppendRaw(_projectName, true); - uri.AppendPath("/connections", false); - uri.AppendQuery("api-version", _apiVersion, true); - if (category != null) - { - uri.AppendQuery("category", category, true); - } - if (includeAll != null) - { - uri.AppendQuery("includeAll", includeAll.Value, true); - } - if (target != null) - { - uri.AppendQuery("target", target, true); - } - request.Uri = uri; - request.Headers.Add("Accept", "application/json"); - return message; - } - - internal HttpMessage CreateGetConnectionRequest(string connectionName, RequestContext context) - { - var message = _pipeline.CreateMessage(context, ResponseClassifier200); - var request = message.Request; - request.Method = RequestMethod.Get; - var uri = new RawRequestUriBuilder(); - uri.Reset(_endpoint); - uri.AppendRaw("/agents/v1.0/subscriptions/", false); - uri.AppendRaw(_subscriptionId, true); - uri.AppendRaw("/resourceGroups/", false); - uri.AppendRaw(_resourceGroupName, true); - uri.AppendRaw("/providers/Microsoft.MachineLearningServices/workspaces/", false); - uri.AppendRaw(_projectName, true); - uri.AppendPath("/connections/", false); - uri.AppendPath(connectionName, true); - uri.AppendQuery("api-version", _apiVersion, true); - request.Uri = uri; - request.Headers.Add("Accept", "application/json"); - return message; - } - - internal HttpMessage CreateGetSecretsRequest(string connectionName, RequestContent content, RequestContext context) - { - var message = _pipeline.CreateMessage(context, ResponseClassifier200); - var request = message.Request; - request.Method = RequestMethod.Post; - var uri = new RawRequestUriBuilder(); - uri.Reset(_endpoint); - uri.AppendRaw("/agents/v1.0/subscriptions/", false); - uri.AppendRaw(_subscriptionId, true); - uri.AppendRaw("/resourceGroups/", false); - uri.AppendRaw(_resourceGroupName, true); - uri.AppendRaw("/providers/Microsoft.MachineLearningServices/workspaces/", false); - uri.AppendRaw(_projectName, true); - uri.AppendPath("/connections/", false); - uri.AppendPath(connectionName, true); - uri.AppendPath("/listsecrets", false); - uri.AppendQuery("api-version", _apiVersion, true); - request.Uri = uri; - request.Headers.Add("Accept", "application/json"); - request.Headers.Add("Content-Type", "application/json"); - request.Content = content; - return message; - } - private static RequestContext DefaultRequestContext = new RequestContext(); internal static RequestContext FromCancellationToken(CancellationToken cancellationToken = default) { diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs index 11118d87ae406..074e1f963c913 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs @@ -12,13 +12,23 @@ namespace Azure.AI.Projects.Tests; public class Sample_ChatCompletions { [Test] - public void InferenceChatCompletions() + public void ChatCompletions() { var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); - ChatCompletionsClient test = client.GetChatCompletionsClient(); + ChatCompletionsClient chatClient = client.GetChatCompletionsClient(); - // Call ChatCompletionsClient Operations + var requestOptions = new ChatCompletionsOptions() + { + Messages = + { + new ChatRequestSystemMessage("You are a helpful assistant."), + new ChatRequestUserMessage("How many feet are in a mile?"), + }, + }; + + Response response = chatClient.Complete(requestOptions); + Console.WriteLine(response.Value.Content); } } diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs new file mode 100644 index 0000000000000..4a7a63db5c495 --- /dev/null +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure.AI.Inference; +using Azure.Identity; +using NUnit.Framework; + +namespace Azure.AI.Projects.Tests; +public class Sample_Embeddings +{ + [Test] + public void BasicEmbedding() + { + var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); + + EmbeddingsClient embeddingsClient = client.GetEmbeddingsClient(); + + var input = new List { "King", "Queen", "Jack", "Page" }; + var requestOptions = new EmbeddingsOptions(input); + + Response response = embeddingsClient.Embed(requestOptions); + foreach (EmbeddingItem item in response.Value.Data) + { + List embedding = item.Embedding.ToObjectFromJson>(); + Console.WriteLine($"Index: {item.Index}, Embedding: <{string.Join(", ", embedding)}>"); + } + } +} From ce7b8a3904bb520b2819ef25f7628a8d4aa7c228 Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Mon, 4 Nov 2024 15:45:54 -0800 Subject: [PATCH 5/8] Update samples --- .../tests/AIProjectsTestEnvironment.cs | 12 ++++++++++++ .../tests/Samples/Agent/Sample_Agent_Basics.cs | 9 ++++++--- .../tests/Samples/Agent/Sample_Agent_Functions.cs | 5 +++-- .../tests/Samples/Agent/Sample_Agent_Streaming.cs | 5 +++-- .../tests/Samples/Agent/Samples_Agent_FileSearch.cs | 5 +++-- .../Samples/Inference/Sample_ChatCompletions.cs | 5 +++-- .../tests/Samples/Inference/Sample_Embeddings.cs | 6 ++++-- 7 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 sdk/ai/Azure.AI.Projects/tests/AIProjectsTestEnvironment.cs diff --git a/sdk/ai/Azure.AI.Projects/tests/AIProjectsTestEnvironment.cs b/sdk/ai/Azure.AI.Projects/tests/AIProjectsTestEnvironment.cs new file mode 100644 index 0000000000000..e0afc53144cc9 --- /dev/null +++ b/sdk/ai/Azure.AI.Projects/tests/AIProjectsTestEnvironment.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core.TestFramework; + +namespace Azure.AI.Projects.Tests +{ + public class AIProjectsTestEnvironment : TestEnvironment + { + public string AzureAICONNECTIONSTRING => GetRecordedVariable("AZURE_AI_CONNECTION_STRING"); + } +} diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Basics.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Basics.cs index 8ea58a440f8cb..748bfd6f5c8b6 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Basics.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Basics.cs @@ -6,19 +6,22 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Azure.Core; -using Azure.Identity; +using Azure.Core.TestFramework; using NUnit.Framework; namespace Azure.AI.Projects.Tests; -public partial class Sample_Agent_Basics +public partial class Sample_Agent_Basics : SamplesBase { [Test] public async Task BasicExample() { #region Snippet:OverviewCreateClient +#if SNIPPET var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); +#else + var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; +#endif AgentsClient client = new AgentsClient(connectionString, new DefaultAzureCredential()); #endregion diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs index 3a602d488bd13..a08764c4f11a5 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs @@ -7,17 +7,18 @@ using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; +using Azure.Core.TestFramework; using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; -public partial class Sample_Agent_Functions +public partial class Sample_Agent_Functions : SamplesBase { [Test] public async Task FunctionCallingExample() { - var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; AgentsClient client = new AgentsClient(connectionString, new DefaultAzureCredential()); #region Snippet:FunctionsDefineFunctionTools diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Streaming.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Streaming.cs index b7a99dd102e22..2271d2b2a88b4 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Streaming.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Streaming.cs @@ -4,17 +4,18 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Azure.Core.TestFramework; using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests { - public class Sample_Agent_Streaming + public class Sample_Agent_Streaming : SamplesBase { [Test] public async Task Streaming() { - var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; AgentsClient client = new AgentsClient(connectionString, new DefaultAzureCredential()); Response agentResponse = await client.CreateAgentAsync( diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs index 1ccc7cce56c64..715a474c95ae4 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs @@ -7,17 +7,18 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using Azure.Core.TestFramework; using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; -public partial class Sample_Agent_FileSearch +public partial class Sample_Agent_FileSearch : SamplesBase { [Test] public async Task FilesSearchExample() { - var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; AgentsClient client = new AgentsClient(connectionString, new DefaultAzureCredential()); #region Snippet:UploadAgentFilesToUse diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs index 074e1f963c913..b486bc519a511 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs @@ -5,16 +5,17 @@ using System; using Azure.AI.Inference; +using Azure.Core.TestFramework; using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; -public class Sample_ChatCompletions +public class Sample_ChatCompletions : SamplesBase { [Test] public void ChatCompletions() { - var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); ChatCompletionsClient chatClient = client.GetChatCompletionsClient(); diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs index 4a7a63db5c495..ce4471a8809f3 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs @@ -6,16 +6,18 @@ using System; using System.Collections.Generic; using Azure.AI.Inference; +using Azure.Core.TestFramework; using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; -public class Sample_Embeddings +public class Sample_Embeddings : SamplesBase { [Test] + [Ignore("Model deployment needed to run this sample")] public void BasicEmbedding() { - var connectionString = Environment.GetEnvironmentVariable("AZURE_AI_CONNECTION_STRING"); + var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); EmbeddingsClient embeddingsClient = client.GetEmbeddingsClient(); From cc5431101f0567345e91738543df4a2d15c772bc Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Tue, 5 Nov 2024 13:23:37 -0800 Subject: [PATCH 6/8] Feedback --- sdk/ai/Azure.AI.Projects/README.md | 4 +- .../api/Azure.AI.Projects.netstandard2.0.cs | 14 +- .../src/Custom/AIProjectClient.cs | 50 +++-- .../src/Custom/Inference/InferenceClient.cs | 181 ------------------ .../Samples/Agent/Sample_Agent_Functions.cs | 1 - .../Samples/Agent/Samples_Agent_FileSearch.cs | 1 - .../Inference/Sample_ChatCompletions.cs | 6 +- .../Samples/Inference/Sample_Embeddings.cs | 6 +- 8 files changed, 43 insertions(+), 220 deletions(-) delete mode 100644 sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs diff --git a/sdk/ai/Azure.AI.Projects/README.md b/sdk/ai/Azure.AI.Projects/README.md index ce9ba7c50bff4..f8eb838a5a7bd 100644 --- a/sdk/ai/Azure.AI.Projects/README.md +++ b/sdk/ai/Azure.AI.Projects/README.md @@ -1,4 +1,4 @@ -# Azure.AI.Client client library for .NET +# Azure AI Projects client library for .NET The Azure AI Assistants client library for .NET is an adaptation of OpenAI's REST APIs that provides an idiomatic interface and rich integration with the rest of the Azure SDK ecosystem. It will connect to Azure AI resources endpoint. @@ -20,7 +20,7 @@ To use Assistants capabilities, you'll need to use an Azure AI resource, you mus Install the client library for .NET with [NuGet](https://www.nuget.org/ ): ```dotnetcli -dotnet add package Azure.AI.Project --prerelease +dotnet add package Azure.AI.Projects --prerelease ``` ### Authenticate the client diff --git a/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs b/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs index bc07a636b4023..108f572050074 100644 --- a/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs +++ b/sdk/ai/Azure.AI.Projects/api/Azure.AI.Projects.netstandard2.0.cs @@ -457,9 +457,10 @@ public AIProjectClient(System.Uri endpoint, string subscriptionId, string resour public AIProjectClient(System.Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, Azure.Core.TokenCredential credential, Azure.AI.Projects.AIProjectClientOptions options) { } public virtual Azure.Core.Pipeline.HttpPipeline Pipeline { get { throw null; } } public virtual Azure.AI.Projects.AgentsClient GetAgentsClient(string apiVersion = "2024-07-01-preview") { throw null; } + public virtual Azure.AI.Inference.ChatCompletionsClient GetChatCompletionsClient() { throw null; } public virtual Azure.AI.Projects.ConnectionsClient GetConnectionsClient(string apiVersion = "2024-07-01-preview") { throw null; } + public virtual Azure.AI.Inference.EmbeddingsClient GetEmbeddingsClient() { throw null; } public virtual Azure.AI.Projects.EvaluationsClient GetEvaluationsClient(string apiVersion = "2024-07-01-preview") { throw null; } - public virtual Azure.AI.Projects.InferenceClient GetInferenceClient(string apiVersion = "2024-07-01-preview") { throw null; } } public partial class AIProjectClientOptions : Azure.Core.ClientOptions { @@ -938,17 +939,6 @@ public IndexResource(string indexConnectionId, string indexName) { } string System.ClientModel.Primitives.IPersistableModel.GetFormatFromOptions(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; } System.BinaryData System.ClientModel.Primitives.IPersistableModel.Write(System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; } } - public partial class InferenceClient - { - protected InferenceClient() { } - public InferenceClient(string connectionString, Azure.Core.TokenCredential credential) { } - public InferenceClient(string connectionString, Azure.Core.TokenCredential credential, Azure.AI.Projects.AIProjectClientOptions options) { } - public InferenceClient(System.Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, Azure.Core.TokenCredential credential) { } - public InferenceClient(System.Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, Azure.Core.TokenCredential credential, Azure.AI.Projects.AIProjectClientOptions options) { } - public virtual Azure.Core.Pipeline.HttpPipeline Pipeline { get { throw null; } } - public virtual Azure.AI.Inference.ChatCompletionsClient GetChatCompletionsClient() { throw null; } - public virtual Azure.AI.Inference.EmbeddingsClient GetEmbeddingsClient() { throw null; } - } public abstract partial class InputData : System.ClientModel.Primitives.IJsonModel, System.ClientModel.Primitives.IPersistableModel { protected InputData() { } diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs index 4b2274efdef6d..52154bdad0ea8 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs @@ -2,8 +2,8 @@ // Licensed under the MIT License. using System; +using Azure.AI.Inference; using Azure.Core; -using Azure.Core.Pipeline; namespace Azure.AI.Projects { @@ -38,25 +38,45 @@ public AIProjectClient(string connectionString, TokenCredential credential, AIPr { } - internal AIProjectClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, TokenCredential tokenCredential, Uri endpoint, string subscriptionId, string resourceGroupName, string projectName) + /// Initializes a new instance of Inference's ChatCompletionsClient. + public virtual ChatCompletionsClient GetChatCompletionsClient() { - ClientDiagnostics = clientDiagnostics; - _pipeline = pipeline; - _tokenCredential = tokenCredential; - _endpoint = endpoint; - _subscriptionId = subscriptionId; - _resourceGroupName = resourceGroupName; - _projectName = projectName; + return InitializeInferenceClient((endpoint, credential) => + new ChatCompletionsClient(endpoint, credential, new AzureAIInferenceClientOptions())); } - /// Initializes a new instance of EvaluationsClient. - /// The API version to use for this operation. - /// is null. - public virtual InferenceClient GetInferenceClient(string apiVersion = "2024-07-01-preview") + /// Initializes a new instance of Inference's EmbeddingsClient. + public virtual EmbeddingsClient GetEmbeddingsClient() { - Argument.AssertNotNull(apiVersion, nameof(apiVersion)); + return InitializeInferenceClient((endpoint, credential) => + new EmbeddingsClient(endpoint, credential, new AzureAIInferenceClientOptions())); + } + + /// Initializes a new instance of Inference client. + private T InitializeInferenceClient(Func clientFactory) + { + var connectionsClient = GetConnectionsClient(); + ConnectionsListSecretsResponse connectionSecret = connectionsClient.GetDefaultConnection(ConnectionType.Serverless, true); + + if (connectionSecret.Properties is ConnectionPropertiesApiKeyAuth apiKeyAuthProperties) + { + if (string.IsNullOrWhiteSpace(apiKeyAuthProperties.Target)) + { + throw new ArgumentException("The API key authentication target URI is missing or invalid."); + } + + if (!Uri.TryCreate(apiKeyAuthProperties.Target, UriKind.Absolute, out var endpoint)) + { + throw new UriFormatException("Invalid URI format in API key authentication target."); + } - return new InferenceClient(ClientDiagnostics, _pipeline, _tokenCredential, _endpoint, _subscriptionId, _resourceGroupName, _projectName, apiVersion); + var credential = new AzureKeyCredential(apiKeyAuthProperties.Credentials.Key); + return clientFactory(endpoint, credential); + } + else + { + throw new ArgumentException("Cannot connect with Inference! Ensure valid ConnectionPropertiesApiKeyAuth."); + } } } } diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs deleted file mode 100644 index 4c10ae3a6bd0c..0000000000000 --- a/sdk/ai/Azure.AI.Projects/src/Custom/Inference/InferenceClient.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#nullable disable - -using System; -using Azure.AI.Inference; -using Azure.Core; -using Azure.Core.Pipeline; - -namespace Azure.AI.Projects -{ - /// The Inference sub-client. - public partial class InferenceClient - { - private static readonly string[] AuthorizationScopes = new string[] { "https://management.azure.com/.default" }; - private readonly TokenCredential _tokenCredential; - private readonly HttpPipeline _pipeline; - private readonly Uri _endpoint; - private readonly string _subscriptionId; - private readonly string _resourceGroupName; - private readonly string _projectName; - private readonly string _apiVersion; - - /// The ClientDiagnostics is used to provide tracing support for the client library. - internal ClientDiagnostics ClientDiagnostics { get; } - - /// The HTTP pipeline for sending and receiving REST requests and responses. - public virtual HttpPipeline Pipeline => _pipeline; - - /// The ClientDiagnostics is used to provide tracing support for the client library. - internal AIProjectClient AIProjectClient => new AIProjectClient(ClientDiagnostics, _pipeline, _tokenCredential, _endpoint, _subscriptionId, _resourceGroupName, _projectName); - - /// Initializes a new instance of ConnectionsClient for mocking. - protected InferenceClient() - { - } - - /// Initializes a new instance of InferenceClient. - /// The Azure AI Studio project connection string, in the form `endpoint;subscription_id;resource_group_name;project_name`. - /// A credential used to authenticate to an Azure Service. - /// is null. - /// - public InferenceClient(string connectionString, TokenCredential credential) : this(connectionString, credential, new AIProjectClientOptions()) - { - } - - /// - /// Initializes a new instance of InferenceClient. - /// - /// The Azure AI Studio project connection string, in the form `endpoint;subscription_id;resource_group_name;project_name`. - /// A credential used to authenticate to an Azure Service. - /// The options for configuring the client. - /// is null. - /// is an empty string. - public InferenceClient(string connectionString, TokenCredential credential, AIProjectClientOptions options) - : this(new Uri(ClientHelper.ParseConnectionString(connectionString, "endpoint")), - ClientHelper.ParseConnectionString(connectionString, "subscriptionId"), - ClientHelper.ParseConnectionString(connectionString, "resourceGroupName"), - ClientHelper.ParseConnectionString(connectionString, "projectName"), - credential, - options) - { - } - - /// Initializes a new instance of InferenceClient. - /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. - /// The Azure subscription ID. - /// The name of the Azure Resource Group. - /// The Azure AI Studio project name. - /// A credential used to authenticate to an Azure Service. - /// , , , or is null. - /// , or is an empty string, and was expected to be non-empty. - public InferenceClient(Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, TokenCredential credential) : this(endpoint, subscriptionId, resourceGroupName, projectName, credential, new AIProjectClientOptions()) - { - } - - /// Initializes a new instance of InferenceClient. - /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. - /// The Azure subscription ID. - /// The name of the Azure Resource Group. - /// The Azure AI Studio project name. - /// A credential used to authenticate to an Azure Service. - /// The options for configuring the client. - /// , , , or is null. - /// , or is an empty string, and was expected to be non-empty. - public InferenceClient(Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, TokenCredential credential, AIProjectClientOptions options) - { - Argument.AssertNotNull(endpoint, nameof(endpoint)); - Argument.AssertNotNullOrEmpty(subscriptionId, nameof(subscriptionId)); - Argument.AssertNotNullOrEmpty(resourceGroupName, nameof(resourceGroupName)); - Argument.AssertNotNullOrEmpty(projectName, nameof(projectName)); - Argument.AssertNotNull(credential, nameof(credential)); - options ??= new AIProjectClientOptions(); - - ClientDiagnostics = new ClientDiagnostics(options, true); - _tokenCredential = credential; - _pipeline = HttpPipelineBuilder.Build(options, Array.Empty(), new HttpPipelinePolicy[] { new BearerTokenAuthenticationPolicy(_tokenCredential, AuthorizationScopes) }, new ResponseClassifier()); - _endpoint = endpoint; - _subscriptionId = subscriptionId; - _resourceGroupName = resourceGroupName; - _projectName = projectName; - _apiVersion = options.Version; - } - - /// Initializes a new instance of InferenceClient. - /// The handler for diagnostic messaging in the client. - /// The HTTP pipeline for sending and receiving REST requests and responses. - /// The token credential to copy. - /// The Azure AI Studio project endpoint, in the form `https://<azure-region>.api.azureml.ms` or `https://<private-link-guid>.<azure-region>.api.azureml.ms`, where <azure-region> is the Azure region where the project is deployed (e.g. westus) and <private-link-guid> is the GUID of the Enterprise private link. - /// The Azure subscription ID. - /// The name of the Azure Resource Group. - /// The Azure AI Studio project name. - /// The API version to use for this operation. - internal InferenceClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, TokenCredential tokenCredential, Uri endpoint, string subscriptionId, string resourceGroupName, string projectName, string apiVersion) - { - ClientDiagnostics = clientDiagnostics; - _pipeline = pipeline; - _tokenCredential = tokenCredential; - _endpoint = endpoint; - _subscriptionId = subscriptionId; - _resourceGroupName = resourceGroupName; - _projectName = projectName; - _apiVersion = apiVersion; - } - - /// Initializes a new instance of Inference's ChatCompletionsClient. - public virtual ChatCompletionsClient GetChatCompletionsClient() - { - var connectionsClient = AIProjectClient.GetConnectionsClient(); - ConnectionsListSecretsResponse connectionSecret = connectionsClient.GetDefaultConnection(ConnectionType.Serverless, true); - - if (connectionSecret.Properties is ConnectionPropertiesApiKeyAuth apiKeyAuthProperties) - { - if (string.IsNullOrWhiteSpace(apiKeyAuthProperties.Target)) - { - throw new ArgumentException("The API key authentication target URI is missing or invalid."); - } - - if (!Uri.TryCreate(apiKeyAuthProperties.Target, UriKind.Absolute, out var endpoint)) - { - throw new UriFormatException("Invalid URI format in API key authentication target."); - } - - var credential = new AzureKeyCredential(apiKeyAuthProperties.Credentials.Key); - return new ChatCompletionsClient(endpoint, credential, new AzureAIInferenceClientOptions()); - } - else - { - throw new ArgumentException("Cannot connect with Inference! Ensure valid ConnectionPropertiesApiKeyAuth."); - } - } - - /// Initializes a new instance of Inference's EmbeddingsClient. - public virtual EmbeddingsClient GetEmbeddingsClient() - { - var connectionsClient = AIProjectClient.GetConnectionsClient(); - ConnectionsListSecretsResponse connectionSecret = connectionsClient.GetDefaultConnection(ConnectionType.Serverless, true); - - if (connectionSecret.Properties is ConnectionPropertiesApiKeyAuth apiKeyAuthProperties) - { - if (string.IsNullOrWhiteSpace(apiKeyAuthProperties.Target)) - { - throw new ArgumentException("The API key authentication target URI is missing or invalid."); - } - - if (!Uri.TryCreate(apiKeyAuthProperties.Target, UriKind.Absolute, out var endpoint)) - { - throw new UriFormatException("Invalid URI format in API key authentication target."); - } - - var credential = new AzureKeyCredential(apiKeyAuthProperties.Credentials.Key); - return new EmbeddingsClient(endpoint, credential, new AzureAIInferenceClientOptions()); - } - else - { - throw new ArgumentException("Cannot connect with Inference! Ensure valid ConnectionPropertiesApiKeyAuth."); - } - } - } -} diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs index a08764c4f11a5..6e1eca5a44dd7 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Sample_Agent_Functions.cs @@ -8,7 +8,6 @@ using System.Text.Json; using System.Threading.Tasks; using Azure.Core.TestFramework; -using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs index 715a474c95ae4..3be0b228536bd 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Agent/Samples_Agent_FileSearch.cs @@ -8,7 +8,6 @@ using System.IO; using System.Threading.Tasks; using Azure.Core.TestFramework; -using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs index b486bc519a511..9b1e94569a87b 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_ChatCompletions.cs @@ -6,19 +6,17 @@ using System; using Azure.AI.Inference; using Azure.Core.TestFramework; -using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; + public class Sample_ChatCompletions : SamplesBase { [Test] public void ChatCompletions() { var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; - InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); - - ChatCompletionsClient chatClient = client.GetChatCompletionsClient(); + ChatCompletionsClient chatClient = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetChatCompletionsClient(); var requestOptions = new ChatCompletionsOptions() { diff --git a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs index ce4471a8809f3..c6e512de4c1c7 100644 --- a/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs +++ b/sdk/ai/Azure.AI.Projects/tests/Samples/Inference/Sample_Embeddings.cs @@ -7,10 +7,10 @@ using System.Collections.Generic; using Azure.AI.Inference; using Azure.Core.TestFramework; -using Azure.Identity; using NUnit.Framework; namespace Azure.AI.Projects.Tests; + public class Sample_Embeddings : SamplesBase { [Test] @@ -18,9 +18,7 @@ public class Sample_Embeddings : SamplesBase public void BasicEmbedding() { var connectionString = TestEnvironment.AzureAICONNECTIONSTRING; - InferenceClient client = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetInferenceClient(); - - EmbeddingsClient embeddingsClient = client.GetEmbeddingsClient(); + EmbeddingsClient embeddingsClient = new AIProjectClient(connectionString, new DefaultAzureCredential()).GetEmbeddingsClient(); var input = new List { "King", "Queen", "Jack", "Page" }; var requestOptions = new EmbeddingsOptions(input); From 5fdb267369d77abfa181bdd8e9547d73af75d97a Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Tue, 5 Nov 2024 13:26:55 -0800 Subject: [PATCH 7/8] Readme update --- sdk/ai/Azure.AI.Projects/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/ai/Azure.AI.Projects/README.md b/sdk/ai/Azure.AI.Projects/README.md index f8eb838a5a7bd..dffe873ba05d7 100644 --- a/sdk/ai/Azure.AI.Projects/README.md +++ b/sdk/ai/Azure.AI.Projects/README.md @@ -1,6 +1,6 @@ # Azure AI Projects client library for .NET -The Azure AI Assistants client library for .NET is an adaptation of OpenAI's REST APIs that provides an idiomatic interface +TODO: [Update README] The Azure AI Assistants client library for .NET is an adaptation of OpenAI's REST APIs that provides an idiomatic interface and rich integration with the rest of the Azure SDK ecosystem. It will connect to Azure AI resources endpoint. Use this library to: From 59c4b99c7508543aa8922f01efcfbce62c50ff52 Mon Sep 17 00:00:00 2001 From: ShivangiReja Date: Tue, 5 Nov 2024 14:00:46 -0800 Subject: [PATCH 8/8] Feedback --- sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs b/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs index 52154bdad0ea8..ed8dfa92f3060 100644 --- a/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs +++ b/sdk/ai/Azure.AI.Projects/src/Custom/AIProjectClient.cs @@ -38,17 +38,20 @@ public AIProjectClient(string connectionString, TokenCredential credential, AIPr { } + private ChatCompletionsClient _chatCompletionsClient; + private EmbeddingsClient _embeddingsClient; + /// Initializes a new instance of Inference's ChatCompletionsClient. public virtual ChatCompletionsClient GetChatCompletionsClient() { - return InitializeInferenceClient((endpoint, credential) => + return _chatCompletionsClient ??= InitializeInferenceClient((endpoint, credential) => new ChatCompletionsClient(endpoint, credential, new AzureAIInferenceClientOptions())); } /// Initializes a new instance of Inference's EmbeddingsClient. public virtual EmbeddingsClient GetEmbeddingsClient() { - return InitializeInferenceClient((endpoint, credential) => + return _embeddingsClient ??= InitializeInferenceClient((endpoint, credential) => new EmbeddingsClient(endpoint, credential, new AzureAIInferenceClientOptions())); }