From 987315bf51737c79bbd01bc6b3fd9cd22ed6df0d Mon Sep 17 00:00:00 2001 From: Krystian Moskal Date: Tue, 25 Nov 2025 16:19:16 +0100 Subject: [PATCH] Fix interactive responses while using .WithFiles() method --- .../Services/Abstract/ILLMService.cs | 1 + .../Services/LLMService/AnthropicService.cs | 4 +-- .../Services/LLMService/DeepSeekService.cs | 3 +- .../Services/LLMService/GeminiService.cs | 3 +- .../Services/LLMService/GroqCloudService.cs | 1 + .../Services/LLMService/LLMService.cs | 17 +++------- .../LLMService/OpenAiCompatibleService.cs | 31 +++++++++++++++---- .../Steps/Commands/AnswerCommandHandler.cs | 6 ++-- .../Steps/Commands/FetchCommandHandler.cs | 6 ++-- 9 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/MaIN.Services/Services/Abstract/ILLMService.cs b/src/MaIN.Services/Services/Abstract/ILLMService.cs index 1004d689..1547c43c 100644 --- a/src/MaIN.Services/Services/Abstract/ILLMService.cs +++ b/src/MaIN.Services/Services/Abstract/ILLMService.cs @@ -32,6 +32,7 @@ public interface ILLMService /// Task AskMemory(Chat chat, ChatMemoryOptions memoryOptions, + ChatRequestOptions requestOptions, CancellationToken cancellationToken = default); /// diff --git a/src/MaIN.Services/Services/LLMService/AnthropicService.cs b/src/MaIN.Services/Services/LLMService/AnthropicService.cs index d8ca83b3..e7ec783d 100644 --- a/src/MaIN.Services/Services/LLMService/AnthropicService.cs +++ b/src/MaIN.Services/Services/LLMService/AnthropicService.cs @@ -71,7 +71,7 @@ private void ValidateApiKey() if (HasFiles(lastMessage)) { var result = ChatHelper.ExtractMemoryOptions(lastMessage); - var memoryResult = await AskMemory(chat, result, cancellationToken); + var memoryResult = await AskMemory(chat, result, options, cancellationToken); resultBuilder.Append(memoryResult!.Message.Content); lastMessage.MarkProcessed(); UpdateSessionCache(chat.Id, resultBuilder.ToString(), options.CreateSession); @@ -531,7 +531,7 @@ private List BuildAnthropicMessages(List conversation) return messages; } - public async Task AskMemory(Chat chat, ChatMemoryOptions memoryOptions, CancellationToken cancellationToken = default) + public async Task AskMemory(Chat chat, ChatMemoryOptions memoryOptions, ChatRequestOptions requestOptions, CancellationToken cancellationToken = default) { throw new NotSupportedException("Embeddings are not supported by the Anthropic. Document reading requires embedding support."); } diff --git a/src/MaIN.Services/Services/LLMService/DeepSeekService.cs b/src/MaIN.Services/Services/LLMService/DeepSeekService.cs index 0c71f22c..f01fabf4 100644 --- a/src/MaIN.Services/Services/LLMService/DeepSeekService.cs +++ b/src/MaIN.Services/Services/LLMService/DeepSeekService.cs @@ -48,6 +48,7 @@ protected override void ValidateApiKey() public override async Task AskMemory( Chat chat, ChatMemoryOptions memoryOptions, + ChatRequestOptions requestOptions, CancellationToken cancellationToken = default) { var lastMsg = chat.Messages.Last(); @@ -63,7 +64,7 @@ protected override void ValidateApiKey() chat.Messages.Last().Files = []; var result = await Send(chat, new ChatRequestOptions(), cancellationToken); chat.Messages.Last().Content = lastMsg.Content; - return result; + return await CreateChatResult(chat, result.Message.Content, [], requestOptions); } private string ComposeMessage(Message lastMsg, string[] filePaths) diff --git a/src/MaIN.Services/Services/LLMService/GeminiService.cs b/src/MaIN.Services/Services/LLMService/GeminiService.cs index 786993c8..9449b630 100644 --- a/src/MaIN.Services/Services/LLMService/GeminiService.cs +++ b/src/MaIN.Services/Services/LLMService/GeminiService.cs @@ -70,6 +70,7 @@ protected override void ValidateApiKey() public override async Task AskMemory( Chat chat, ChatMemoryOptions memoryOptions, + ChatRequestOptions requestOptions, CancellationToken cancellationToken = default) { if (!chat.Messages.Any()) @@ -91,7 +92,7 @@ protected override void ValidateApiKey() var retrievedContext = await kernel.AskAsync(userQuery, cancellationToken: cancellationToken); chat.Messages.Last().MarkProcessed(); await kernel.DeleteIndexAsync(cancellationToken: cancellationToken); - return CreateChatResult(chat, retrievedContext.Result, []); + return await CreateChatResult(chat, retrievedContext.Result, [], requestOptions); } } diff --git a/src/MaIN.Services/Services/LLMService/GroqCloudService.cs b/src/MaIN.Services/Services/LLMService/GroqCloudService.cs index 724f2518..1907adb6 100644 --- a/src/MaIN.Services/Services/LLMService/GroqCloudService.cs +++ b/src/MaIN.Services/Services/LLMService/GroqCloudService.cs @@ -42,6 +42,7 @@ protected override void ValidateApiKey() public override async Task AskMemory( Chat chat, ChatMemoryOptions memoryOptions, + ChatRequestOptions requestOptions, CancellationToken cancellationToken = default) { var lastMsg = chat.Messages.Last(); diff --git a/src/MaIN.Services/Services/LLMService/LLMService.cs b/src/MaIN.Services/Services/LLMService/LLMService.cs index adfdbe80..41fadea5 100644 --- a/src/MaIN.Services/Services/LLMService/LLMService.cs +++ b/src/MaIN.Services/Services/LLMService/LLMService.cs @@ -58,7 +58,7 @@ public LLMService( if (ChatHelper.HasFiles(lastMsg)) { var memoryOptions = ChatHelper.ExtractMemoryOptions(lastMsg); - return await AskMemory(chat, memoryOptions, cancellationToken); + return await AskMemory(chat, memoryOptions, requestOptions, cancellationToken); } var model = KnownModels.GetModel(chat.Model); @@ -90,6 +90,7 @@ public Task CleanSessionCache(string? id) public async Task AskMemory( Chat chat, ChatMemoryOptions memoryOptions, + ChatRequestOptions requestOptions, CancellationToken cancellationToken = default) { var model = KnownModels.GetModel(chat.Model); @@ -127,18 +128,8 @@ public Task CleanSessionCache(string? id) memory.generator._embedder._weights.Dispose(); memory.generator.Dispose(); - return new ChatResult - { - Done = true, - CreatedAt = DateTime.Now, - Model = chat.Model, - Message = new Message - { - Content = memoryService.CleanResponseText(result.Result), - Role = nameof(AuthorRole.Assistant), - Type = MessageType.LocalLLM, - } - }; + var tokens = await ProcessChatRequest(chat, model, userMessage, requestOptions, cancellationToken); + return await CreateChatResult(chat, tokens, requestOptions); } private async Task> ProcessChatRequest( diff --git a/src/MaIN.Services/Services/LLMService/OpenAiCompatibleService.cs b/src/MaIN.Services/Services/LLMService/OpenAiCompatibleService.cs index 8357a860..e8a41a0d 100644 --- a/src/MaIN.Services/Services/LLMService/OpenAiCompatibleService.cs +++ b/src/MaIN.Services/Services/LLMService/OpenAiCompatibleService.cs @@ -68,7 +68,7 @@ public abstract class OpenAiCompatibleService( if (HasFiles(lastMessage)) { var result = ChatHelper.ExtractMemoryOptions(lastMessage); - var memoryResult = await AskMemory(chat, result, cancellationToken); + var memoryResult = await AskMemory(chat, result, options, cancellationToken); resultBuilder.Append(memoryResult!.Message.Content); lastMessage.MarkProcessed(); UpdateSessionCache(chat.Id, resultBuilder.ToString(), options.CreateSession); @@ -80,7 +80,7 @@ public abstract class OpenAiCompatibleService( Type = TokenType.FullAnswer }); } - return CreateChatResult(chat, resultBuilder.ToString(), tokens); + return await CreateChatResult(chat, resultBuilder.ToString(), tokens, options); } if (chat.ToolsConfiguration?.Tools != null && chat.ToolsConfiguration.Tools.Any()) @@ -128,7 +128,7 @@ await _notificationService.DispatchNotification( lastMessage.MarkProcessed(); UpdateSessionCache(chat.Id, resultBuilder.ToString(), options.CreateSession); - return CreateChatResult(chat, resultBuilder.ToString(), tokens); + return await CreateChatResult(chat, resultBuilder.ToString(), tokens, options); } private async Task ProcessWithToolsAsync( @@ -279,7 +279,7 @@ await _notificationService.DispatchNotification( chat.Messages.Last().MarkProcessed(); UpdateSessionCache(chat.Id, resultBuilder.ToString(), options.CreateSession); - return CreateChatResult(chat, resultBuilder.ToString(), tokens); + return await CreateChatResult(chat, resultBuilder.ToString(), tokens, options); } private async Task?> ProcessStreamingChatWithToolsAsync( @@ -438,6 +438,7 @@ await _notificationService.DispatchNotification( public virtual async Task AskMemory( Chat chat, ChatMemoryOptions memoryOptions, + ChatRequestOptions requestOptions, CancellationToken cancellationToken = default) { if (!chat.Messages.Any()) @@ -458,7 +459,7 @@ await _notificationService.DispatchNotification( var retrievedContext = await kernel.AskAsync(userQuery, cancellationToken: cancellationToken); await kernel.DeleteIndexAsync(cancellationToken: cancellationToken); - return CreateChatResult(chat, retrievedContext.Result, []); + return await CreateChatResult(chat, retrievedContext.Result, [], requestOptions); } public virtual async Task GetCurrentModels() @@ -714,8 +715,19 @@ internal static void MergeMessages(List conversation, List } } - protected static ChatResult CreateChatResult(Chat chat, string content, List tokens) + protected async Task CreateChatResult(Chat chat, string content, List tokens, ChatRequestOptions requestOptions) { + var responseText = string.Concat(tokens.Select(x => x.Text)); + + if (requestOptions.InteractiveUpdates) + { + await SendNotification(chat.Id, new LLMTokenValue + { + Type = TokenType.FullAnswer, + Text = responseText + }, true); + } + return new ChatResult { Done = true, @@ -730,6 +742,13 @@ protected static ChatResult CreateChatResult(Chat chat, string content, List BuildMessagesArray(List conversation, Chat chat, ImageType imageType) { diff --git a/src/MaIN.Services/Services/Steps/Commands/AnswerCommandHandler.cs b/src/MaIN.Services/Services/Steps/Commands/AnswerCommandHandler.cs index 75d097a3..6d5a0dcc 100644 --- a/src/MaIN.Services/Services/Steps/Commands/AnswerCommandHandler.cs +++ b/src/MaIN.Services/Services/Steps/Commands/AnswerCommandHandler.cs @@ -35,8 +35,8 @@ public class AnswerCommandHandler( switch (command.KnowledgeUsage) { case KnowledgeUsage.UseMemory: - result = await llmService.AskMemory(command.Chat, - new ChatMemoryOptions { Memory = command.Chat.Memory }); + result = await llmService.AskMemory(command.Chat, new ChatMemoryOptions { Memory = command.Chat.Memory }, + new ChatRequestOptions()); return result!.Message; case KnowledgeUsage.UseKnowledge: var isKnowledgeNeeded = await ShouldUseKnowledge(command.Knowledge, command.Chat); @@ -138,7 +138,7 @@ await notificationService.DispatchNotification(NotificationMessageBuilder.Create return result.Message; } - var knowledgeResult = await llmService.AskMemory(chat, memoryOptions); + var knowledgeResult = await llmService.AskMemory(chat, memoryOptions, new ChatRequestOptions()); chat.Messages.Last().Content = originalContent; return knowledgeResult?.Message; } diff --git a/src/MaIN.Services/Services/Steps/Commands/FetchCommandHandler.cs b/src/MaIN.Services/Services/Steps/Commands/FetchCommandHandler.cs index 9e494145..33737672 100644 --- a/src/MaIN.Services/Services/Steps/Commands/FetchCommandHandler.cs +++ b/src/MaIN.Services/Services/Steps/Commands/FetchCommandHandler.cs @@ -94,7 +94,7 @@ private async Task HandleFileSource(FetchCommand command, Dictionary HandleWebSource(FetchCommand command, Dictionary ProcessJsonResponse(Message response, FetchCommand c var result = await llmServiceFactory.CreateService(command.Chat.Backend ?? settings.BackendType).AskMemory(command.MemoryChat!, new ChatMemoryOptions { TextData = chunks - }); + }, new ChatRequestOptions()); result!.Message.Role = command.ResponseType == FetchResponseType.AS_System ? "System" : "Assistant"; var newMessage = result!.Message;