From 13798f439c02d1cbe17ef046cc590c39d6574084 Mon Sep 17 00:00:00 2001 From: Andrei Alistar Date: Thu, 8 Aug 2024 12:43:00 +0300 Subject: [PATCH] Added the session properties model --- .../Common/Models/Chat/SessionProperties.cs | 16 +++++++++++ src/dotnet/Core/Interfaces/ICoreService.cs | 8 +++--- src/dotnet/Core/Services/CoreService.cs | 12 ++++---- .../CoreAPI/Controllers/SessionsController.cs | 12 ++++---- .../Clients/RESTClients/SessionRESTClient.cs | 10 +++---- src/dotnet/CoreClient/CoreClient.cs | 23 ++++++++------- .../CoreClient/Interfaces/ICoreClient.cs | 12 ++++---- .../Interfaces/ISessionRESTClient.cs | 8 +++--- .../Core.Client.Tests/CoreClientTests.cs | 28 ++++++++++--------- .../Services/AgentConversationTestService.cs | 6 ++-- .../Core.Tests/Services/CoreServiceTests.cs | 24 ++++++++-------- 11 files changed, 90 insertions(+), 69 deletions(-) create mode 100644 src/dotnet/Common/Models/Chat/SessionProperties.cs diff --git a/src/dotnet/Common/Models/Chat/SessionProperties.cs b/src/dotnet/Common/Models/Chat/SessionProperties.cs new file mode 100644 index 0000000000..4650ac2cf3 --- /dev/null +++ b/src/dotnet/Common/Models/Chat/SessionProperties.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace FoundationaLLM.Common.Models.Chat +{ + /// + /// The session properties object. + /// + public class SessionProperties + { + /// + /// The session name. + /// + [JsonPropertyName("session_name")] + public required string SessionName { get; set; } + } +} diff --git a/src/dotnet/Core/Interfaces/ICoreService.cs b/src/dotnet/Core/Interfaces/ICoreService.cs index c7042cc6a5..f5cc441036 100644 --- a/src/dotnet/Core/Interfaces/ICoreService.cs +++ b/src/dotnet/Core/Interfaces/ICoreService.cs @@ -26,16 +26,16 @@ public interface ICoreService /// Creates a new chat session. /// /// The instance Id. - /// The name for the chat session. - Task CreateNewChatSessionAsync(string instanceId, string sessionName); + /// The session properties. + Task CreateNewChatSessionAsync(string instanceId, SessionProperties sessionProperties); /// /// Rename the chat session from its default (eg., "New Chat") to the summary provided by OpenAI. /// /// The instance id. /// The session id to rename. - /// The new name for the chat session. - Task RenameChatSessionAsync(string instanceId, string sessionId, string sessionName); + /// The session properties. + Task RenameChatSessionAsync(string instanceId, string sessionId, SessionProperties sessionProperties); /// /// Delete a chat session and related messages. diff --git a/src/dotnet/Core/Services/CoreService.cs b/src/dotnet/Core/Services/CoreService.cs index 35a1b9be58..059afbaf83 100644 --- a/src/dotnet/Core/Services/CoreService.cs +++ b/src/dotnet/Core/Services/CoreService.cs @@ -65,13 +65,13 @@ public async Task> GetChatSessionMessagesAsync(string instanceId, } /// - public async Task CreateNewChatSessionAsync(string instanceId, string sessionName) + public async Task CreateNewChatSessionAsync(string instanceId, SessionProperties sessionProperties) { - ArgumentException.ThrowIfNullOrEmpty(sessionName); + ArgumentException.ThrowIfNullOrEmpty(sessionProperties.SessionName); Session session = new() { - Name = sessionName, + Name = sessionProperties.SessionName, Type = _sessionType, UPN = _callContext.CurrentUserIdentity?.UPN ?? throw new InvalidOperationException("Failed to retrieve the identity of the signed in user when creating a new chat session.") }; @@ -79,12 +79,12 @@ public async Task CreateNewChatSessionAsync(string instanceId, string s } /// - public async Task RenameChatSessionAsync(string instanceId, string sessionId, string sessionName) + public async Task RenameChatSessionAsync(string instanceId, string sessionId, SessionProperties sessionProperties) { ArgumentNullException.ThrowIfNull(sessionId); - ArgumentException.ThrowIfNullOrEmpty(sessionName); + ArgumentException.ThrowIfNullOrEmpty(sessionProperties.SessionName); - return await _cosmosDbService.UpdateSessionNameAsync(sessionId, sessionName); + return await _cosmosDbService.UpdateSessionNameAsync(sessionId, sessionProperties.SessionName); } /// diff --git a/src/dotnet/CoreAPI/Controllers/SessionsController.cs b/src/dotnet/CoreAPI/Controllers/SessionsController.cs index b8604819b5..b297580141 100644 --- a/src/dotnet/CoreAPI/Controllers/SessionsController.cs +++ b/src/dotnet/CoreAPI/Controllers/SessionsController.cs @@ -68,20 +68,20 @@ public async Task GetCompletionPrompt(string instanceId, strin /// Creates a new chat session. /// /// The id of the instance. - /// The name for the chat session. + /// The session properties. [HttpPost(Name = "CreateNewChatSession")] - public async Task CreateNewChatSession(string instanceId, [FromBody] string sessionName) => - await _coreService.CreateNewChatSessionAsync(instanceId, sessionName); + public async Task CreateNewChatSession(string instanceId, [FromBody] SessionProperties sessionProperties) => + await _coreService.CreateNewChatSessionAsync(instanceId, sessionProperties); /// /// Rename the chat session. /// /// The id of the instance. /// The id of the session to rename. - /// The new name for the session. + /// The session properties. [HttpPost("{sessionId}/rename", Name = "RenameChatSession")] - public async Task RenameChatSession(string instanceId, string sessionId, [FromBody] string sessionName) => - await _coreService.RenameChatSessionAsync(instanceId, sessionId, sessionName); + public async Task RenameChatSession(string instanceId, string sessionId, [FromBody] SessionProperties sessionProperties) => + await _coreService.RenameChatSessionAsync(instanceId, sessionId, sessionProperties); /// /// Delete a chat session and related messages. diff --git a/src/dotnet/CoreClient/Clients/RESTClients/SessionRESTClient.cs b/src/dotnet/CoreClient/Clients/RESTClients/SessionRESTClient.cs index 5cf7b0344a..0068c62c84 100644 --- a/src/dotnet/CoreClient/Clients/RESTClients/SessionRESTClient.cs +++ b/src/dotnet/CoreClient/Clients/RESTClients/SessionRESTClient.cs @@ -17,12 +17,12 @@ internal class SessionRESTClient( private readonly string _instanceId = instanceId ?? throw new ArgumentNullException(nameof(instanceId)); /// - public async Task CreateSessionAsync(string sessionName) + public async Task CreateSessionAsync(SessionProperties sessionProperties) { var coreClient = await GetCoreClientAsync(); var responseSession = await coreClient.PostAsync( $"instances/{_instanceId}/sessions", - JsonContent.Create(sessionName)); + JsonContent.Create(sessionProperties)); if (responseSession.IsSuccessStatusCode) { @@ -38,16 +38,16 @@ public async Task CreateSessionAsync(string sessionName) } /// - public async Task RenameChatSession(string sessionId, string sessionName) + public async Task RenameChatSession(string sessionId, SessionProperties sessionProperties) { var coreClient = await GetCoreClientAsync(); var response = await coreClient.PostAsync( $"instances/{_instanceId}/sessions/{sessionId}/rename", - JsonContent.Create(sessionName)); + JsonContent.Create(sessionProperties)); if (response.IsSuccessStatusCode) { - return sessionName; + return sessionProperties.SessionName; } throw new Exception($"Failed to rename chat session. Status code: {response.StatusCode}. Reason: {response.ReasonPhrase}"); diff --git a/src/dotnet/CoreClient/CoreClient.cs b/src/dotnet/CoreClient/CoreClient.cs index 636fff61e5..cea16f6ffe 100644 --- a/src/dotnet/CoreClient/CoreClient.cs +++ b/src/dotnet/CoreClient/CoreClient.cs @@ -54,26 +54,29 @@ public CoreClient( _coreRestClient = new CoreRESTClient(coreUri, credential, instanceId, options); /// - public async Task CreateChatSessionAsync(string sessionName) + public async Task CreateChatSessionAsync(SessionProperties sessionProperties) { - var sessionId = await _coreRestClient.Sessions.CreateSessionAsync(sessionName); + if (string.IsNullOrWhiteSpace(sessionProperties.SessionName)) + throw new ArgumentException("A session name must be provided when creating a new session."); + + var sessionId = await _coreRestClient.Sessions.CreateSessionAsync(sessionProperties); return sessionId; } /// - public async Task GetCompletionWithSessionAsync(string? sessionId, string? sessionName, + public async Task GetCompletionWithSessionAsync(string? sessionId, SessionProperties? sessionProperties, string userPrompt, string agentName) { if (string.IsNullOrWhiteSpace(sessionId)) { - if (string.IsNullOrWhiteSpace(sessionName)) + if (sessionProperties == null) { throw new ArgumentException( - "The completion request must contain a sessionName if no sessionId is provided. " + + "The completion request must contain a session name if no session Id is provided. " + "A new session will be created with the provided session name."); } - sessionId = await CreateChatSessionAsync(sessionName); + sessionId = await CreateChatSessionAsync(sessionProperties); } var orchestrationRequest = new CompletionRequest @@ -129,7 +132,7 @@ public async Task GetCompletionAsync(CompletionRequest completionReq /// public async Task AttachFileAndAskQuestionAsync(Stream fileStream, string fileName, string contentType, - string agentName, string question, bool useSession, string? sessionId, string? sessionName) + string agentName, string question, bool useSession, string? sessionId, SessionProperties? sessionProperties) { if (fileStream == null) { @@ -142,14 +145,14 @@ public async Task AttachFileAndAskQuestionAsync(Stream fileStream, s { if (string.IsNullOrWhiteSpace(sessionId)) { - if (string.IsNullOrWhiteSpace(sessionName)) + if (sessionProperties == null) { throw new ArgumentException( - "The completion request must contain a sessionName if no sessionId is provided. " + + "The completion request must contain a session name if no session Id is provided. " + "A new session will be created with the provided session name."); } - sessionId = await CreateChatSessionAsync(sessionName); + sessionId = await CreateChatSessionAsync(sessionProperties); } var orchestrationRequest = new CompletionRequest diff --git a/src/dotnet/CoreClient/Interfaces/ICoreClient.cs b/src/dotnet/CoreClient/Interfaces/ICoreClient.cs index f284493d14..7149a1d5c5 100644 --- a/src/dotnet/CoreClient/Interfaces/ICoreClient.cs +++ b/src/dotnet/CoreClient/Interfaces/ICoreClient.cs @@ -14,9 +14,9 @@ public interface ICoreClient /// /// Creates a new chat session with the specified name. /// - /// The chat session name. + /// The session properties. /// The new chat session ID. - Task CreateChatSessionAsync(string sessionName); + Task CreateChatSessionAsync(SessionProperties sessionProperties); /// /// Runs a single completion request with an agent using the Core API and a chat session. @@ -25,12 +25,12 @@ public interface ICoreClient /// /// The ID of an existing session. If null or empty, a new session /// is created first. - /// Renames the new chat session if not null or empty. + /// Optional session priperties. /// The user prompt to send to the agent. /// The name of the FoundationaLLM agent that will handle the /// completion request. /// A completion from the designated FoundationaLLM agent. - Task GetCompletionWithSessionAsync(string? sessionId, string? sessionName, + Task GetCompletionWithSessionAsync(string? sessionId, SessionProperties? sessionProperties, string userPrompt, string agentName); /// @@ -83,11 +83,11 @@ Task GetCompletionWithSessionAsync(string? sessionId, string? sessio /// false, no session is created and the sessionless orchestration flow is used. /// The ID of an existing session. If null or empty, a new session /// is created first. - /// Renames the new chat session if not null or empty. + /// Optional session properties. /// A completion from the designated FoundationaLLM agent. /// A completion from the designated FoundationaLLM agent. Task AttachFileAndAskQuestionAsync(Stream fileStream, string fileName, string contentType, - string agentName, string question, bool useSession, string? sessionId, string? sessionName); + string agentName, string question, bool useSession, string? sessionId, SessionProperties? sessionProperties); /// /// Returns the chat messages related to an existing session. diff --git a/src/dotnet/CoreClient/Interfaces/ISessionRESTClient.cs b/src/dotnet/CoreClient/Interfaces/ISessionRESTClient.cs index ec1763326a..0258c0ad3a 100644 --- a/src/dotnet/CoreClient/Interfaces/ISessionRESTClient.cs +++ b/src/dotnet/CoreClient/Interfaces/ISessionRESTClient.cs @@ -25,17 +25,17 @@ public interface ISessionRESTClient /// /// Creates a new session with the specified name. /// - /// The name for the chat session. + /// The session properties. /// Returns the new Session ID. - Task CreateSessionAsync(string sessionName); + Task CreateSessionAsync(SessionProperties sessionProperties); /// /// Renames a chat session. /// /// The chat session ID. - /// The new session name. + /// The session properties. /// - Task RenameChatSession(string sessionId, string sessionName); + Task RenameChatSession(string sessionId, SessionProperties sessionProperties); /// /// Gets a completion prompt by session ID and completion prompt ID. diff --git a/tests/dotnet/Core.Client.Tests/CoreClientTests.cs b/tests/dotnet/Core.Client.Tests/CoreClientTests.cs index a5376d95ef..22a3997901 100644 --- a/tests/dotnet/Core.Client.Tests/CoreClientTests.cs +++ b/tests/dotnet/Core.Client.Tests/CoreClientTests.cs @@ -24,16 +24,16 @@ public CoreClientTests() public async Task CreateChatSessionAsync_WithName_CreatesAndRenamesSession() { // Arrange - var sessionName = "TestSession"; + var sessionProperties = new SessionProperties() { SessionName = "TestSession" }; var sessionId = "session-id"; - _coreRestClient.Sessions.CreateSessionAsync(sessionName).Returns(Task.FromResult(sessionId)); + _coreRestClient.Sessions.CreateSessionAsync(sessionProperties).Returns(Task.FromResult(sessionId)); // Act - var result = await _coreClient.CreateChatSessionAsync(sessionName); + var result = await _coreClient.CreateChatSessionAsync(sessionProperties); // Assert Assert.Equal(sessionId, result); - await _coreRestClient.Sessions.Received(1).CreateSessionAsync(sessionName); + await _coreRestClient.Sessions.Received(1).CreateSessionAsync(sessionProperties); } [Fact] @@ -42,18 +42,18 @@ public async Task GetCompletionWithSessionAsync_WithNewSession_CreatesSessionAnd // Arrange var userPrompt = "Hello, World!"; var agentName = "TestAgent"; - var sessionName = "TestSession"; + var sessionProperties = new SessionProperties() { SessionName = "TestSession" }; var sessionId = "new-session-id"; var completion = new Completion(); - _coreRestClient.Sessions.CreateSessionAsync(sessionName).Returns(Task.FromResult(sessionId)); + _coreRestClient.Sessions.CreateSessionAsync(sessionProperties).Returns(Task.FromResult(sessionId)); _coreRestClient.Completions.GetChatCompletionAsync(Arg.Any()).Returns(Task.FromResult(completion)); // Act - var result = await _coreClient.GetCompletionWithSessionAsync(null, sessionName, userPrompt, agentName); + var result = await _coreClient.GetCompletionWithSessionAsync(null, sessionProperties, userPrompt, agentName); // Assert Assert.Equal(completion, result); - await _coreRestClient.Sessions.Received(1).CreateSessionAsync(sessionName); + await _coreRestClient.Sessions.Received(1).CreateSessionAsync(sessionProperties); await _coreRestClient.Completions.GetChatCompletionAsync(Arg.Is( r => r.SessionId == sessionId && r.AgentName == agentName && r.UserPrompt == userPrompt)); } @@ -100,21 +100,21 @@ public async Task AttachFileAndAskQuestionAsync_UsesSession_UploadsFileAndSendsS var contentType = "text/plain"; var agentName = "TestAgent"; var question = "What is this file about?"; - var sessionName = "TestSession"; + var sessionProperties = new SessionProperties() { SessionName = "TestSession" }; var sessionId = "session-id"; var objectId = "object-id"; var completion = new Completion(); _coreRestClient.Attachments.UploadAttachmentAsync(fileStream, fileName, contentType).Returns(Task.FromResult(objectId)); - _coreRestClient.Sessions.CreateSessionAsync(sessionName).Returns(Task.FromResult(sessionId)); + _coreRestClient.Sessions.CreateSessionAsync(sessionProperties).Returns(Task.FromResult(sessionId)); _coreRestClient.Completions.GetChatCompletionAsync(Arg.Any()).Returns(Task.FromResult(completion)); // Act - var result = await _coreClient.AttachFileAndAskQuestionAsync(fileStream, fileName, contentType, agentName, question, true, null, sessionName); + var result = await _coreClient.AttachFileAndAskQuestionAsync(fileStream, fileName, contentType, agentName, question, true, null, sessionProperties); // Assert Assert.Equal(completion, result); await _coreRestClient.Attachments.Received(1).UploadAttachmentAsync(fileStream, fileName, contentType); - await _coreRestClient.Sessions.Received(1).CreateSessionAsync(sessionName); + await _coreRestClient.Sessions.Received(1).CreateSessionAsync(sessionProperties); await _coreRestClient.Completions.GetChatCompletionAsync(Arg.Is( r => r.AgentName == agentName && r.SessionId == sessionId && r.UserPrompt == question && r.Attachments.Contains(objectId))); } @@ -124,7 +124,9 @@ public async Task AttachFileAndAskQuestionAsync_ThrowsException_WhenFileStreamIs { // Arrange & Act & Assert await Assert.ThrowsAsync(() => - _coreClient.AttachFileAndAskQuestionAsync(null!, "file.txt", "text/plain", "agent", "question", true, "session-id", "session-name")); + _coreClient.AttachFileAndAskQuestionAsync( + null!, "file.txt", "text/plain", "agent", "question", true, "session-id", + new SessionProperties() { SessionName = "session-name" })); } [Fact] diff --git a/tests/dotnet/Core.Examples/Services/AgentConversationTestService.cs b/tests/dotnet/Core.Examples/Services/AgentConversationTestService.cs index b88fa10ea4..13cdad8495 100644 --- a/tests/dotnet/Core.Examples/Services/AgentConversationTestService.cs +++ b/tests/dotnet/Core.Examples/Services/AgentConversationTestService.cs @@ -36,7 +36,7 @@ public async Task> RunAgentConversationWithSession( if (string.IsNullOrWhiteSpace(sessionId)) { // Create a new session since an existing ID was not provided. - sessionId = await coreClient.CreateChatSessionAsync((string?) null); + sessionId = await coreClient.CreateChatSessionAsync(new SessionProperties() { SessionName = "Test" }); sessionCreated = true; } @@ -74,7 +74,7 @@ public async Task RunAgentCompletionWithSession(string agentName, if (string.IsNullOrWhiteSpace(sessionId)) { // Create a new session since an existing ID was not provided. - sessionId = await coreClient.CreateChatSessionAsync((string?) null); + sessionId = await coreClient.CreateChatSessionAsync(new SessionProperties() { SessionName = "Test" }); sessionCreated = true; } @@ -148,7 +148,7 @@ public async Task RunAgentCompletionWithQual if (string.IsNullOrWhiteSpace(sessionId)) { // Create a new session since an existing ID was not provided. - sessionId = await coreClient.CreateChatSessionAsync((string?) null); + sessionId = await coreClient.CreateChatSessionAsync(new SessionProperties() { SessionName = "Test" }); sessionCreated = true; } diff --git a/tests/dotnet/Core.Tests/Services/CoreServiceTests.cs b/tests/dotnet/Core.Tests/Services/CoreServiceTests.cs index d361f5a003..a870443755 100644 --- a/tests/dotnet/Core.Tests/Services/CoreServiceTests.cs +++ b/tests/dotnet/Core.Tests/Services/CoreServiceTests.cs @@ -123,8 +123,8 @@ public async Task CreateNewChatSessionAsync_ShouldReturnANewChatSession() // Arrange var currentUserUPN = "testuser@example.com"; var sessionType = "Test_type"; - var sessionName = "Test_name"; - var newSession = new Session { Name = sessionName, Type = sessionType, UPN = currentUserUPN }; + var sessionProperties = new SessionProperties() { SessionName = "Test_name" }; + var newSession = new Session { Name = sessionProperties.SessionName, Type = sessionType, UPN = currentUserUPN }; // Set up mock returns _callContext.CurrentUserIdentity.Returns(new UnifiedUserIdentity { UPN = currentUserUPN }); @@ -133,13 +133,13 @@ public async Task CreateNewChatSessionAsync_ShouldReturnANewChatSession() .Returns(Task.FromResult(newSession)); // Act - var resultSession = await _testedService.CreateNewChatSessionAsync(_instanceId, sessionName); + var resultSession = await _testedService.CreateNewChatSessionAsync(_instanceId, sessionProperties); // Assert Assert.NotNull(resultSession); Assert.Equal(sessionType, resultSession.Type); Assert.Equal(currentUserUPN, resultSession.UPN); - Assert.Equal(sessionName, resultSession.Name); + Assert.Equal(sessionProperties.SessionName, resultSession.Name); } #endregion @@ -151,37 +151,37 @@ public async Task RenameChatSessionAsync_ShouldReturnTheRenamedChatSession() { // Arrange var session = new Session() { Name = "OldName" }; - var expectedName = "NewName"; + var sessionProperties = new SessionProperties() { SessionName = "NewName" }; var expectedSession = new Session() { Id = session.Id, Messages = session.Messages, - Name = expectedName, + Name = sessionProperties.SessionName, SessionId = session.SessionId, TokensUsed = session.TokensUsed, Type = session.Type, }; - _cosmosDbService.UpdateSessionNameAsync(session.Id, expectedName).Returns(expectedSession); + _cosmosDbService.UpdateSessionNameAsync(session.Id, sessionProperties.SessionName).Returns(expectedSession); // Act - var actualSession = await _testedService.RenameChatSessionAsync(_instanceId, session.Id, expectedName); + var actualSession = await _testedService.RenameChatSessionAsync(_instanceId, session.Id, sessionProperties); // Assert Assert.Equivalent(expectedSession, actualSession); - Assert.Equal(expectedName, actualSession.Name); + Assert.Equal(sessionProperties.SessionName, actualSession.Name); } [Fact] public async Task RenameChatSessionAsync_ShouldThrowExceptionWhenSessionIdIsNull() { // Arrange - var sessionName = "NewName"; + var sessionProperties = new SessionProperties() { SessionName = "NewName" }; // Assert await Assert.ThrowsAsync(async () => { - await _testedService.RenameChatSessionAsync(_instanceId, null!, sessionName); + await _testedService.RenameChatSessionAsync(_instanceId, null!, sessionProperties); }); } @@ -199,7 +199,7 @@ await Assert.ThrowsAsync(async () => await Assert.ThrowsAsync(async () => { - await _testedService.RenameChatSessionAsync(_instanceId, sessionId, string.Empty); + await _testedService.RenameChatSessionAsync(_instanceId, sessionId, new SessionProperties() { SessionName = string.Empty }); }); }