diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/.editorconfig b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/.editorconfig index c62e3deaa..4b6145b1b 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/.editorconfig +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/.editorconfig @@ -7,3 +7,9 @@ dotnet_diagnostic.VSTHRD111.severity = none # Use .ConfigureAwait(bool) # CA1859: Use concrete types when possible for improved performance dotnet_diagnostic.CA1859.severity = none + +# CS0618: Type or member is obsolete +dotnet_diagnostic.CS0618.severity = none + +# IDE0090: Use 'new(...)' +dotnet_diagnostic.IDE0090.severity = none diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs index f006bd1cb..b1c64ff6e 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs @@ -8,7 +8,7 @@ using Moq; using System.Reflection; using Microsoft.Teams.AI.AI.Planners; -using Azure.AI.OpenAI.Assistants; +using OpenAI.Assistants; namespace Microsoft.Teams.AI.Tests.AITests { @@ -99,7 +99,7 @@ public async Task Test_BeginTaskAsync_Assistant_WaitForPreviousRun() testClient.RemainingMessages.Enqueue("welcome"); AssistantThread thread = await testClient.CreateThreadAsync(new(), CancellationToken.None); - await testClient.CreateRunAsync(thread.Id, AssistantsModelFactory.CreateRunOptions(), CancellationToken.None); + await testClient.CreateRunAsync(thread.Id, "", OpenAIModelFactory.CreateRunOptions(), CancellationToken.None); turnState.ThreadId = thread.Id; // Act @@ -219,8 +219,7 @@ public async Task Test_ContinueTaskAsync_Assistant_RequiresAction() var aiOptions = new AIOptions(planner); var ai = new AI(aiOptions); - var functionToolCall = AssistantsModelFactory.RequiredFunctionToolCall("test-tool-id", "test-action", "{}"); - var requiredAction = AssistantsModelFactory.SubmitToolOutputsAction(new List{ functionToolCall }); + var requiredAction = OpenAIModelFactory.CreateRequiredAction("test-tool-id", "test-action", "{}"); testClient.RemainingActions.Enqueue(requiredAction); testClient.RemainingRunStatus.Enqueue("requires_action"); @@ -267,8 +266,7 @@ public async Task Test_ContinueTaskAsync_Assistant_IgnoreRedundantAction() var aiOptions = new AIOptions(planner); var ai = new AI(aiOptions); - var functionToolCall = AssistantsModelFactory.RequiredFunctionToolCall("test-tool-id", "test-action", "{}"); - var requiredAction = AssistantsModelFactory.SubmitToolOutputsAction(new List { functionToolCall }); + var requiredAction = OpenAIModelFactory.CreateRequiredAction("test-tool-id", "test-action", "{}"); testClient.RemainingActions.Enqueue(requiredAction); testClient.RemainingRunStatus.Enqueue("requires_action"); @@ -316,9 +314,9 @@ public async Task Test_ContinueTaskAsync_Assistant_MultipleMessages() var ai = new AI(aiOptions); testClient.RemainingRunStatus.Enqueue("completed"); - testClient.RemainingMessages.Enqueue("message 2"); - testClient.RemainingMessages.Enqueue("message 1"); testClient.RemainingMessages.Enqueue("welcome"); + testClient.RemainingMessages.Enqueue("message 1"); + testClient.RemainingMessages.Enqueue("message 2"); // Act var plan = await planner.ContinueTaskAsync(turnContextMock.Object, turnState, ai, CancellationToken.None); diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/OAuthBotAuthenticationTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/OAuthBotAuthenticationTests.cs index 81b167a66..51ab1db18 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/OAuthBotAuthenticationTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/OAuthBotAuthenticationTests.cs @@ -44,7 +44,8 @@ public async void Test_CreateOAuthCard_WithSSOEnabled() var turnState = await TurnStateConfig.GetTurnStateWithConversationStateAsync(turnContext); var app = new TestApplication(new() { Adapter = testAdapter }); - var authSettings = new OAuthSettings() { + var authSettings = new OAuthSettings() + { ConnectionName = "connectionName", Title = "title", Text = "text", @@ -131,7 +132,7 @@ public async void Test_VerifyStateRouteSelector_ReturnsTrue() } [Fact] - public async void Test_VerifyStateRouteSelector_IncorrectActivity_ReturnsFalse () + public async void Test_VerifyStateRouteSelector_IncorrectActivity_ReturnsFalse() { // Arrange var testAdapter = new SimpleAdapter(); diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MeetingsTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MeetingsTests.cs index e8184b44f..ada88dd30 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MeetingsTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MeetingsTests.cs @@ -3,7 +3,6 @@ using Microsoft.Bot.Schema; using Microsoft.Teams.AI.State; using Microsoft.Teams.AI.Tests.TestUtils; -using Record = Microsoft.Teams.AI.State.Record; namespace Microsoft.Teams.AI.Tests.Application { diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MessageExtensionsTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MessageExtensionsTests.cs index b8b30ed3d..83920ce3f 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MessageExtensionsTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/MessageExtensionsTests.cs @@ -7,7 +7,6 @@ using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Record = Microsoft.Teams.AI.State.Record; namespace Microsoft.Teams.AI.Tests.Application { diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/TaskModulesTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/TaskModulesTests.cs index 1be57f65c..c2309bb22 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/TaskModulesTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/TaskModulesTests.cs @@ -6,7 +6,6 @@ using Microsoft.Teams.AI.Tests.TestUtils; using Moq; using Newtonsoft.Json.Linq; -using Record = Microsoft.Teams.AI.State.Record; namespace Microsoft.Teams.AI.Tests.Application { diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/IntegrationTests/OpenAIModelTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/IntegrationTests/OpenAIModelTests.cs index 4c6a3eb86..3242f8fbc 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/IntegrationTests/OpenAIModelTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/IntegrationTests/OpenAIModelTests.cs @@ -5,11 +5,11 @@ using Moq; using System.Reflection; using Xunit.Abstractions; -using Microsoft.Extensions.Logging; using Microsoft.Bot.Schema; using Microsoft.Teams.AI.AI.Tokenizers; using Microsoft.Teams.AI.AI.Prompts; using Microsoft.Teams.AI.AI.Prompts.Sections; +using Microsoft.Extensions.Logging; namespace Microsoft.Teams.AI.Tests.IntegrationTests { @@ -17,7 +17,9 @@ public sealed class OpenAIModelTests { private readonly IConfigurationRoot _configuration; private readonly RedirectOutput _output; +#pragma warning disable IDE0052 // Remove unread private members private readonly ILoggerFactory _loggerFactory; +#pragma warning restore IDE0052 // Remove unread private members public OpenAIModelTests(ITestOutputHelper output) { diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/OpenAIModelFactory.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/OpenAIModelFactory.cs new file mode 100644 index 000000000..09cd6ba2d --- /dev/null +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/OpenAIModelFactory.cs @@ -0,0 +1,129 @@ +using OpenAI.Assistants; +using System.ClientModel; +using System.ClientModel.Primitives; + +namespace Microsoft.Teams.AI.Tests.TestUtils +{ + internal sealed class OpenAIModelFactory + { + public static RunCreationOptions CreateRunOptions() + { + return new RunCreationOptions(); + } + + public static RequiredAction CreateRequiredAction(string toolCallId, string functionName, string functionArguments) + { + return new TestRequiredAction(toolCallId, functionName, functionArguments); + } + + public static Assistant CreateAssistant() + { + return ModelReaderWriter.Read(BinaryData.FromString(@$"{{ + ""id"": ""{Guid.NewGuid()}"", + ""object"": ""assistant"", + ""created_at"": {DateTime.Now.Second} + }}"))!; + } + + public static AssistantThread CreateAssistantThread(string guid, DateTimeOffset offset) + { + return ModelReaderWriter.Read(BinaryData.FromString(@$"{{ + ""id"": ""{guid}"", + ""created_at"": {offset.Second} + }}"))!; + } + + public static ThreadMessage CreateThreadMessage(string threadId, string message) + { + var json = @$"{{ + ""id"": ""{Guid.NewGuid()}"", + ""thread_id"": ""{threadId}"", + ""created_at"": {DateTime.Now.Second}, + ""content"": [ + {{ + ""type"": ""text"", + ""text"": {{ + ""value"": ""{message}"", + ""annotations"": [] + }} + }} + ] + }}"; + return ModelReaderWriter.Read(BinaryData.FromString(json))!; + } + + public static ThreadRun CreateThreadRun(string threadId, string runStatus, string? runId = null, IList requiredActions = null!) + { + var raJson = "{}"; + if (requiredActions != null && requiredActions.Count > 0) + { + var toolCalls = requiredActions.Select((requiredAction) => + { + var ra = (TestRequiredAction)requiredAction; + return $@"{{ + ""id"": ""{ra.ToolCallId}"", + ""type"": ""function"", + ""function"": {{ + ""name"": ""{ra.FunctionName}"", + ""arguments"": ""{ra.FunctionArguments}"" + }} + }}"; + }); + + raJson = $@"{{ + ""type"": ""submit_tool_outputs"", + ""submit_tool_outputs"": {{ + ""tool_calls"": [ + {string.Join(",", toolCalls)} + ] + }} + }} + "; + } + + return ModelReaderWriter.Read(BinaryData.FromString(@$"{{ + ""id"": ""{runId ?? Guid.NewGuid().ToString()}"", + ""thread_id"": ""{threadId}"", + ""created_at"": {DateTime.Now.Second}, + ""status"": ""{runStatus}"", + ""required_action"": {raJson} + }}"))!; + } + } + + internal sealed class TestRequiredAction : RequiredAction + { + public new string FunctionName; + + public new string FunctionArguments; + + public new string ToolCallId; + + public TestRequiredAction(string toolCallId, string functionName, string functionArguments) + { + this.FunctionName = functionName; + this.FunctionArguments = functionArguments; + this.ToolCallId = toolCallId; + } + } + + internal sealed class TestAsyncPageableCollection : AsyncPageableCollection where T : class + { + public List Items; + + internal PipelineResponse _pipelineResponse; + + public TestAsyncPageableCollection(List items, PipelineResponse response) + { + Items = items; + _pipelineResponse = response; + } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public override async IAsyncEnumerable> AsPages(string? continuationToken = null, int? pageSizeHint = null) +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + yield return ResultPage.Create(Items, null, _pipelineResponse); + } + } +} diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/TestAssistantsOpenAIClient.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/TestAssistantsOpenAIClient.cs index 6d40ee779..7cab001fd 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/TestAssistantsOpenAIClient.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/TestUtils/TestAssistantsOpenAIClient.cs @@ -1,16 +1,19 @@ -using Azure; -using Azure.AI.OpenAI.Assistants; +using OpenAI.Assistants; using Moq; +using System.ClientModel; +using System.ClientModel.Primitives; +using OpenAI; +#pragma warning disable OPENAI001 namespace Microsoft.Teams.AI.Tests.TestUtils { - internal sealed class TestAssistantsOpenAIClient : AssistantsClient + internal sealed class TestAssistantsOpenAIClient : AssistantClient { public TestAssistantsOpenAIClient() { } - public Assistant Assistant { get; set; } = AssistantsModelFactory.Assistant(); + public Assistant Assistant { get; set; } = OpenAIModelFactory.CreateAssistant(); public List Threads { get; set; } = new(); @@ -34,53 +37,32 @@ public void Reset() RemainingMessages.Clear(); } - public override Task> GetAssistantAsync(string assistantId, CancellationToken cancellationToken = default) - { - return Task.FromResult(Response.FromValue(Assistant, Mock.Of())); - } + //public override Task> GetAssistantAsync(string assistantId, CancellationToken cancellationToken = default) + //{ + // return Task.FromResult(Response.FromValue(Assistant, Mock.Of())); + //} - public override Task> CreateThreadAsync(AssistantThreadCreationOptions options, CancellationToken cancellationToken = default) + public override Task> CreateThreadAsync(ThreadCreationOptions options, CancellationToken cancellationToken = default) { - AssistantThread newThread = AssistantsModelFactory.AssistantThread(Guid.NewGuid().ToString(), DateTimeOffset.Now); + AssistantThread newThread = OpenAIModelFactory.CreateAssistantThread(Guid.NewGuid().ToString(), DateTimeOffset.Now); Threads.Add(newThread); List messages = new(); - var annotations = new List() { AssistantsModelFactory.MessageTextAnnotation() }; - foreach (var m in options.Messages) - { - ThreadMessage newMessage = AssistantsModelFactory.ThreadMessage( - Guid.NewGuid().ToString(), - DateTimeOffset.Now, - newThread.Id, - MessageRole.User, - new List() { AssistantsModelFactory.MessageTextContent(m.Content, annotations) }, - Assistant.Id, - null, - m.FileIds, - null - ); - messages.Add(newMessage); - } - Messages.Add(newThread.Id, messages); Runs.Add(newThread.Id, new List()); - return Task.FromResult(Response.FromValue(newThread, Mock.Of())); + return Task.FromResult(ClientResult.FromValue(newThread, Mock.Of())); } - public override Task> CreateMessageAsync(string threadId, MessageRole role, string content, IEnumerable? fileIds = null, IDictionary? metadata = null, CancellationToken cancellationToken = default) + public override Task> CreateMessageAsync(string threadId, MessageRole role, IEnumerable content, MessageCreationOptions options = null!, CancellationToken cancellationToken = default) { - var annotations = new List() { AssistantsModelFactory.MessageTextAnnotation() }; - ThreadMessage newMessage = AssistantsModelFactory.ThreadMessage( - Guid.NewGuid().ToString(), - DateTimeOffset.Now, - threadId, - role, - new List { AssistantsModelFactory.MessageTextContent(content, annotations) }, - Assistant.Id, - null, - fileIds, - null - ); + ThreadMessage newMessage = _CreateMessage(threadId, content.First().Text); + + return Task.FromResult(ClientResult.FromValue(newMessage, Mock.Of())); + } + + private ThreadMessage _CreateMessage(string threadId, string message) + { + ThreadMessage newMessage = OpenAIModelFactory.CreateThreadMessage(threadId, message); if (Messages.ContainsKey(threadId)) { @@ -91,46 +73,30 @@ public override Task> CreateMessageAsync(string threadId Messages.Add(threadId, new() { newMessage }); }; - return Task.FromResult(Response.FromValue(newMessage, Mock.Of())); + return newMessage; } - public override async Task>> GetMessagesAsync(string threadId, int? limit = null, ListSortOrder? order = null, string? after = null, string? before = null, CancellationToken cancellationToken = default) + public override AsyncPageableCollection GetMessagesAsync(string threadId, ListOrder? resultOrder = null, CancellationToken cancellationToken = default) { while (RemainingMessages.Count > 0) { string nextMessage = RemainingMessages.Dequeue(); - await CreateMessageAsync(threadId, MessageRole.User, nextMessage); + _CreateMessage(threadId, nextMessage); } - string lastMessageId = before!; - int i = Messages[threadId].FindIndex(m => m.Id == lastMessageId); - int remainingItems = Messages[threadId].Count() - (i + 1); - var filteredMessages = Messages[threadId].GetRange(i + 1, remainingItems); + // Sorted by oldest first + List messages = Messages[threadId].ToList(); + if (resultOrder != null && resultOrder.Value == ListOrder.NewestFirst) + { + messages.Reverse(); + } - var p = AssistantsModelFactory.PageableList(filteredMessages, null, threadId, false); - return Response.FromValue(p, Mock.Of()); + return new TestAsyncPageableCollection(messages, Mock.Of()); } - public override Task> CreateRunAsync(string threadId, CreateRunOptions createRunOptions, CancellationToken cancellationToken = default) + public override Task> CreateRunAsync(string threadId, string assistantId, RunCreationOptions createRunOptions, CancellationToken cancellationToken = default) { - RequiredAction? remainingActions = null; - - if (RemainingActions.Count > 0) - { - remainingActions = RemainingActions.Dequeue(); - } - - var newRun = AssistantsModelFactory.ThreadRun( - Guid.NewGuid().ToString(), - threadId, - createRunOptions.AssistantId, - RunStatus.InProgress, - remainingActions, - null, - createRunOptions.OverrideModelName, - createRunOptions.OverrideInstructions, - createRunOptions.OverrideTools - ); + var newRun = OpenAIModelFactory.CreateThreadRun(threadId, "in_progress"); if (Runs.ContainsKey(threadId)) { @@ -141,63 +107,76 @@ public override Task> CreateRunAsync(string threadId, Create Runs.Add(threadId, new() { newRun }); } - return Task.FromResult(Response.FromValue(newRun, Mock.Of())); + return Task.FromResult(ClientResult.FromValue(newRun, Mock.Of())); } - public override Task> GetRunAsync(string threadId, string runId, CancellationToken cancellationToken = default) + public override Task> GetRunAsync(string threadId, string runId, CancellationToken cancellationToken = default) + { + ThreadRun? run = _GetRun(threadId, runId); + + + return Task.FromResult(ClientResult.FromValue(run, Mock.Of()))!; + } + + private ThreadRun? _GetRun(string threadId, string runId) { if (Runs[threadId].Count() == 0) { - return Task.FromResult(Response.FromValue(null!, Mock.Of())); + return null; } - RunStatus runStatus = RemainingRunStatus.Dequeue(); + string runStatus = RemainingRunStatus.Dequeue(); int i = Runs[threadId].FindIndex(r => string.Equals(r.Id, runId)); ThreadRun run = Runs[threadId][i]; + List ras = new(); + if (runStatus == "requires_action") + { + while (RemainingActions.Count > 0) + { + ras.Add(RemainingActions.Dequeue()); + } + } + // Updates the runStatus. This cannot be done in the original run object. - ThreadRun runWithUpdatedStatus = AssistantsModelFactory.ThreadRun( - run.Id, + ThreadRun runWithUpdatedStatus = OpenAIModelFactory.CreateThreadRun( run.ThreadId, - run.AssistantId, runStatus, - run.RequiredAction, - null, - run.Model, - run.Instructions, - run.Tools + run.Id, + ras ); Runs[threadId][i] = runWithUpdatedStatus; - return Task.FromResult(Response.FromValue(runWithUpdatedStatus, Mock.Of())); + return runWithUpdatedStatus; } - public override async Task>> GetRunsAsync(string threadId, int? limit = null, ListSortOrder? order = null, string? after = null, string? before = null, CancellationToken cancellationToken = default) + public override AsyncPageableCollection GetRunsAsync(string threadId, ListOrder? resultOrder = null, CancellationToken cancellationToken = default) { - PageableList response; + AsyncPageableCollection response; // AssistantsPlanner only needs the get the latest. if (Runs[threadId].Count() == 0) { - response = AssistantsModelFactory.PageableList(new List(), "null", null, false); - return Response.FromValue(response, Mock.Of()); + response = new TestAsyncPageableCollection(new List(), Mock.Of()); + return response; } int lastIndex = Runs[threadId].Count() - 1; ThreadRun run = Runs[threadId][lastIndex]; - ThreadRun runWithUpdatedStatus = await GetRunAsync(threadId, run.Id); + ThreadRun runWithUpdatedStatus = _GetRun(threadId, run.Id)!; - response = AssistantsModelFactory.PageableList(new List() { runWithUpdatedStatus }, "null", null, false); - return Response.FromValue(response, Mock.Of()); + response = new TestAsyncPageableCollection(new List() { runWithUpdatedStatus }, Mock.Of()); + return response; } - public override async Task> SubmitToolOutputsToRunAsync(string threadId, string runId, IEnumerable toolOutputs, CancellationToken cancellationToken = default) + public override Task> SubmitToolOutputsToRunAsync(string threadId, string runId, IEnumerable toolOutputs, CancellationToken cancellationToken = default) { - ThreadRun runWithUpdatedStatus = await GetRunAsync(threadId, runId); + ThreadRun runWithUpdatedStatus = _GetRun(threadId, runId)!; var response = runWithUpdatedStatus; - return Response.FromValue(response, Mock.Of()); + return Task.FromResult(ClientResult.FromValue(response, Mock.Of())); } } } +#pragma warning restore OPENAI001 diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Embeddings/OpenAIEmbeddings.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Embeddings/OpenAIEmbeddings.cs index 2c5483e46..63e2b7b93 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Embeddings/OpenAIEmbeddings.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Embeddings/OpenAIEmbeddings.cs @@ -41,7 +41,6 @@ public OpenAIEmbeddings(OpenAIEmbeddingsOptions options, ILoggerFactory? loggerF options.RetryPolicy = options.RetryPolicy ?? new List { TimeSpan.FromMilliseconds(2000), TimeSpan.FromMilliseconds(5000) }; _logger = loggerFactory == null ? NullLogger.Instance : loggerFactory.CreateLogger(); - OpenAIEmbeddingsOptions embeddingsOptions = (OpenAIEmbeddingsOptions)_options; OpenAIClientOptions openAIClientOptions = new() { RetryPolicy = new SequentialDelayRetryPolicy(options.RetryPolicy!, options.RetryPolicy!.Count) diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs index 87670b5d5..1aedb8fa4 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs @@ -1,13 +1,17 @@ -using Azure.AI.OpenAI.Assistants; +using Azure.AI.OpenAI; using Microsoft.Bot.Builder; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Teams.AI.Exceptions; using Microsoft.Teams.AI.State; using Microsoft.Teams.AI.Utilities; +using OpenAI; +using OpenAI.Assistants; +using System.ClientModel; using System.Runtime.CompilerServices; using System.Text.Json; +#pragma warning disable OPENAI001 // Assistants API is currently in beta and is subject to change. #pragma warning disable IDE0130 // Namespace does not match folder structure [assembly: InternalsVisibleTo("Microsoft.Teams.AI.Tests")] @@ -23,8 +27,12 @@ public class AssistantsPlanner : IPlanner private static readonly TimeSpan DEFAULT_POLLING_INTERVAL = TimeSpan.FromSeconds(1); private readonly AssistantsPlannerOptions _options; - private readonly AssistantsClient _client; + private readonly AssistantClient _client; + + // TODO: Write trace logs +#pragma warning disable IDE0052 // Remove unread private members private readonly ILogger _logger; +#pragma warning restore IDE0052 // Remove unread private members /// /// Create new AssistantsPlanner. @@ -52,18 +60,20 @@ public AssistantsPlanner(AssistantsPlannerOptions options, ILoggerFactory? logge /// /// OpenAI or Azure OpenAI API key. /// Definition of the assistant to create. + /// The underlying LLM model. /// Azure OpenAI API Endpoint. /// A cancellation token that can be used by other objects /// or threads to receive notice of cancellation. /// The created assistant. - public static async Task CreateAssistantAsync(string apiKey, AssistantCreationOptions request, string? endpoint = null, CancellationToken cancellationToken = default) + public static async Task CreateAssistantAsync(string apiKey, AssistantCreationOptions request, string model, string? endpoint = null, CancellationToken cancellationToken = default) { Verify.ParamNotNull(apiKey); Verify.ParamNotNull(request); + Verify.ParamNotNull(model); - AssistantsClient client = _CreateClient(apiKey, endpoint); + AssistantClient client = _CreateClient(apiKey, endpoint); - return await client.CreateAssistantAsync(request, cancellationToken); + return await client.CreateAssistantAsync(model, request, cancellationToken); } /// @@ -141,14 +151,15 @@ private async Task _BlockOnInProgressRunsAsync(string threadId, CancellationToke // Loop until the last run is completed while (true) { - PageableList? runs = await _client.GetRunsAsync(threadId, null, null, null, null, cancellationToken); + AsyncPageableCollection? runs = _client.GetRunsAsync(threadId, ListOrder.NewestFirst, cancellationToken); - if (runs == null || runs.Count() == 0) + if (runs == null) { return; } - ThreadRun? run = runs.ElementAt(0); + // TODO: Confirm pointer is on the first object. + ThreadRun? run = runs.GetAsyncEnumerator().Current; if (run == null || _IsRunCompleted(run)) { return; @@ -162,9 +173,9 @@ private async Task _BlockOnInProgressRunsAsync(string threadId, CancellationToke private async Task _GeneratePlanFromMessagesAsync(string threadId, string lastMessageId, CancellationToken cancellationToken) { // Find the new messages - PageableList messages = await _client.GetMessagesAsync(threadId, null, null, null, lastMessageId, cancellationToken); + AsyncPageableCollection messages = _client.GetMessagesAsync(threadId, ListOrder.NewestFirst, cancellationToken); List newMessages = new(); - foreach (ThreadMessage message in messages) + await foreach (ThreadMessage message in messages) { if (string.Equals(message.Id, lastMessageId)) { @@ -183,35 +194,30 @@ private async Task _GeneratePlanFromMessagesAsync(string threadId, string Plan plan = new(); foreach (ThreadMessage message in newMessages) { - foreach (MessageContent content in message.ContentItems) + foreach (MessageContent content in message.Content) { - if (content is MessageTextContent textMessage) + if (content.Text != null) { - plan.Commands.Add(new PredictedSayCommand(textMessage.Text ?? string.Empty)); + plan.Commands.Add(new PredictedSayCommand(content.Text ?? string.Empty)); } } } return plan; } - private Plan _GeneratePlanFromTools(TState state, SubmitToolOutputsAction submitToolOutputsAction) + private Plan _GeneratePlanFromTools(TState state, IReadOnlyList requiredActions) { Plan plan = new(); Dictionary toolMap = new(); - foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls) + foreach (RequiredAction requiredAction in requiredActions) { - RequiredFunctionToolCall? functionToolCall = toolCall as RequiredFunctionToolCall; - if (functionToolCall == null) - { - return plan; - } - + // Currently `RequiredAction` is only for a function tool call // TODO: Potential bug if assistant predicts same tool twice. - toolMap[functionToolCall!.Name] = toolCall.Id; + toolMap[requiredAction.FunctionName] = requiredAction.ToolCallId; plan.Commands.Add(new PredictedDoCommand ( - functionToolCall.Name, - JsonSerializer.Deserialize>(functionToolCall.Arguments) + requiredAction.FunctionName, + JsonSerializer.Deserialize>(requiredAction.FunctionArguments) ?? new Dictionary() )); } @@ -226,11 +232,8 @@ private async Task _SubmitActionResultsAsync(TState state, CancellationTok Dictionary toolMap = state.SubmitToolMap; foreach (KeyValuePair requiredAction in toolMap) { - toolOutputs.Add(new() - { - ToolCallId = requiredAction.Value, - Output = state.Temp!.ActionOutputs.ContainsKey(requiredAction.Key) ? state.Temp!.ActionOutputs[requiredAction.Key] : string.Empty - }); + ToolOutput toolOutput = new(requiredAction.Value, state.Temp!.ActionOutputs.ContainsKey(requiredAction.Key) ? state.Temp!.ActionOutputs[requiredAction.Key] : string.Empty); + toolOutputs.Add(toolOutput); } // Submit the tool outputs @@ -241,16 +244,14 @@ private async Task _SubmitActionResultsAsync(TState state, CancellationTok if (result.Status == RunStatus.RequiresAction) { - SubmitToolOutputsAction? submitToolOutputs = result.RequiredAction as SubmitToolOutputsAction; - - if (submitToolOutputs == null) + if (result.RequiredActions.Count == 0) { return new Plan(); } state.SubmitToolOutputs = true; - return _GeneratePlanFromTools(state, submitToolOutputs); + return _GeneratePlanFromTools(state, result.RequiredActions); } else if (result.Status == RunStatus.Completed) { @@ -277,10 +278,11 @@ private async Task _SubmitUserInputAsync(TState state, CancellationToken c string threadId = await _EnsureThreadCreatedAsync(state, cancellationToken); // Add the users input to the thread - ThreadMessage message = await _client.CreateMessageAsync(threadId, "user", state.Temp?.Input ?? string.Empty, null, null, cancellationToken); + List messages = new() { state.Temp?.Input ?? string.Empty }; + ThreadMessage message = await _client.CreateMessageAsync(threadId, MessageRole.User, messages, null, cancellationToken); // Create a new run - ThreadRun run = await _client.CreateRunAsync(threadId, new(_options.AssistantId), cancellationToken); + ThreadRun run = await _client.CreateRunAsync(threadId, _options.AssistantId, null, cancellationToken); // Update state and wait for the run to complete state.ThreadId = threadId; @@ -290,16 +292,14 @@ private async Task _SubmitUserInputAsync(TState state, CancellationToken c if (result.Status == RunStatus.RequiresAction) { - SubmitToolOutputsAction? submitToolOutputs = result.RequiredAction as SubmitToolOutputsAction; - - if (submitToolOutputs == null) + if (result.RequiredActions.Count == 0) { return new Plan(); } state.SubmitToolOutputs = true; - return _GeneratePlanFromTools(state, submitToolOutputs); + return _GeneratePlanFromTools(state, result.RequiredActions); } else if (result.Status == RunStatus.Completed) { @@ -320,20 +320,22 @@ private async Task _SubmitUserInputAsync(TState state, CancellationToken c } } - internal static AssistantsClient _CreateClient(string apiKey, string? endpoint = null) + internal static AssistantClient _CreateClient(string apiKey, string? endpoint = null) { Verify.ParamNotNull(apiKey); if (endpoint != null) { // Azure OpenAI - return new AssistantsClient(new Uri(endpoint), new Azure.AzureKeyCredential(apiKey)); + AzureOpenAIClient azureOpenAI = new(new Uri(endpoint), new ApiKeyCredential(apiKey)); + return azureOpenAI.GetAssistantClient(); } else { // OpenAI - return new AssistantsClient(apiKey); + return new AssistantClient(apiKey); } } } } +#pragma warning restore OPENAI001 diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Microsoft.Teams.AI.csproj b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Microsoft.Teams.AI.csproj index cea86555c..66d591992 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Microsoft.Teams.AI.csproj +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Microsoft.Teams.AI.csproj @@ -1,58 +1,57 @@ - - - - netstandard2.0 - latest - enable - enable - Microsoft.Teams.AI - Microsoft Teams AI SDK - 1.5.0 - Microsoft - Microsoft - © Microsoft Corporation. All rights reserved. - SDK focused on building AI based applications for Microsoft Teams. - README.md - https://github.com/microsoft/teams-ai - git - True - - - - - https://github.com/microsoft/teams-ai - https://github-production-user-asset-6210df.s3.amazonaws.com/14900841/240368384-972a9a1b-679a-4725-bfc0-a1e76151a78a.png - MIT - true - bots;ai;teams - - NU5125 - true - true - - - - - - - - - - - - - - - - - - - - - - - - + + + + netstandard2.0 + latest + enable + enable + Microsoft.Teams.AI + Microsoft Teams AI SDK + 1.5.0 + Microsoft + Microsoft + © Microsoft Corporation. All rights reserved. + SDK focused on building AI based applications for Microsoft Teams. + README.md + https://github.com/microsoft/teams-ai + git + True + + + + + https://github.com/microsoft/teams-ai + https://github-production-user-asset-6210df.s3.amazonaws.com/14900841/240368384-972a9a1b-679a-4725-bfc0-a1e76151a78a.png + MIT + true + bots;ai;teams + + NU5125 + true + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/06.assistants.a.mathBot/.editorconfig b/dotnet/samples/06.assistants.a.mathBot/.editorconfig new file mode 100644 index 000000000..875356ee6 --- /dev/null +++ b/dotnet/samples/06.assistants.a.mathBot/.editorconfig @@ -0,0 +1,232 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/dotnet/samples/06.assistants.a.mathBot/MathBot.csproj b/dotnet/samples/06.assistants.a.mathBot/MathBot.csproj index a557c0933..3eef0e49f 100644 --- a/dotnet/samples/06.assistants.a.mathBot/MathBot.csproj +++ b/dotnet/samples/06.assistants.a.mathBot/MathBot.csproj @@ -15,6 +15,7 @@ + diff --git a/dotnet/samples/06.assistants.a.mathBot/MathBot.sln b/dotnet/samples/06.assistants.a.mathBot/MathBot.sln index 010a2b8bd..7e734faa4 100644 --- a/dotnet/samples/06.assistants.a.mathBot/MathBot.sln +++ b/dotnet/samples/06.assistants.a.mathBot/MathBot.sln @@ -1,10 +1,14 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34004.107 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MathBot", "MathBot.csproj", "{F52EC4EB-8F19-4907-8654-1A24D88C5F82}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FC7A5238-0425-4C28-AB22-984DE44A0E15}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/dotnet/samples/06.assistants.a.mathBot/Program.cs b/dotnet/samples/06.assistants.a.mathBot/Program.cs index 4f7896672..5da03db9e 100644 --- a/dotnet/samples/06.assistants.a.mathBot/Program.cs +++ b/dotnet/samples/06.assistants.a.mathBot/Program.cs @@ -7,7 +7,7 @@ using Microsoft.Teams.AI.AI.Planners; using MathBot; -using Azure.AI.OpenAI.Assistants; +using OpenAI.Assistants; var builder = WebApplication.CreateBuilder(args); @@ -46,14 +46,14 @@ if (string.IsNullOrEmpty(assistantId)) { Console.WriteLine("No Assistant ID configured, creating new Assistant..."); - AssistantCreationOptions assistantCreateParams = new("gpt-4") + AssistantCreationOptions assistantCreateParams = new() { Name = "Math Tutor", Instructions = "You are a personal math tutor. Write and run code to answer math questions." }; assistantCreateParams.Tools.Add(new CodeInterpreterToolDefinition()); - string newAssistantId = AssistantsPlanner.CreateAssistantAsync(apiKey, assistantCreateParams, endpoint).Result.Id; + string newAssistantId = AssistantsPlanner.CreateAssistantAsync(apiKey, assistantCreateParams, "gpt-4", endpoint).Result.Id; Console.WriteLine($"Created a new assistant with an ID of: {newAssistantId}"); Console.WriteLine("Copy and save above ID, and set `OpenAI:AssistantId` in appsettings.Development.json."); Console.WriteLine("Press any key to exit."); diff --git a/dotnet/samples/06.assistants.b.orderBot/.editorconfig b/dotnet/samples/06.assistants.b.orderBot/.editorconfig new file mode 100644 index 000000000..4ee1489ee --- /dev/null +++ b/dotnet/samples/06.assistants.b.orderBot/.editorconfig @@ -0,0 +1,237 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +[*.{cs,vb}] +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion \ No newline at end of file diff --git a/dotnet/samples/06.assistants.b.orderBot/OrderBot.csproj b/dotnet/samples/06.assistants.b.orderBot/OrderBot.csproj index de22d5b89..98a917002 100644 --- a/dotnet/samples/06.assistants.b.orderBot/OrderBot.csproj +++ b/dotnet/samples/06.assistants.b.orderBot/OrderBot.csproj @@ -14,6 +14,8 @@ + + diff --git a/dotnet/samples/06.assistants.b.orderBot/Program.cs b/dotnet/samples/06.assistants.b.orderBot/Program.cs index c5cfad248..d8a3f602a 100644 --- a/dotnet/samples/06.assistants.b.orderBot/Program.cs +++ b/dotnet/samples/06.assistants.b.orderBot/Program.cs @@ -7,7 +7,7 @@ using Microsoft.Teams.AI.AI.Planners; using OrderBot; using OrderBot.Models; -using Azure.AI.OpenAI.Assistants; +using OpenAI.Assistants; var builder = WebApplication.CreateBuilder(args); @@ -45,7 +45,7 @@ // Missing Assistant ID, create new Assistant if (string.IsNullOrEmpty(assistantId)) { - AssistantCreationOptions assistantCreationOptions = new("gpt-4") + AssistantCreationOptions assistantCreationOptions = new() { Name = "Order Bot", Instructions = string.Join("\n", new[] @@ -59,7 +59,7 @@ assistantCreationOptions.Tools.Add(new FunctionToolDefinition("place_order", "Creates or updates a food order.", new BinaryData(OrderParameters.GetSchema()))); - string newAssistantId = AssistantsPlanner.CreateAssistantAsync(apiKey, assistantCreationOptions, endpoint).Result.Id; + string newAssistantId = AssistantsPlanner.CreateAssistantAsync(apiKey, assistantCreationOptions, "gpt-4", endpoint).Result.Id; Console.WriteLine($"Created a new assistant with an ID of: {newAssistantId}"); Console.WriteLine("Copy and save above ID, and set `OpenAI:AssistantId` in appsettings.Development.json.");