diff --git a/Host/Web.config b/Host/Web.config index 480e8f19a..7e12ffae7 100644 --- a/Host/Web.config +++ b/Host/Web.config @@ -310,6 +310,8 @@ + + diff --git a/Service/Plugins/AiService2.cs b/Service/Plugins/AiService2.cs new file mode 100644 index 000000000..b1e08228c --- /dev/null +++ b/Service/Plugins/AiService2.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Net; +using System.ServiceModel; +using System.Text; +using BeWo.Data.Access; +using BeWo.Data.Entities; +using BeWo.Data.Security; +using BeWo.Service.AI; +using BeWo.Service.Core; +using BeWo.Service.DCEntityMapper; +using BeWo.Service.Invoicing; +using BeWo.Service.ServiceContracts; +using BeWo.Service.ServiceProxy; +using BS.Shared; +using BS.Shared.Core; +using BS.Shared.DataContracts; +using BS.Shared.DataContracts.Compact; +using BS.Shared.DataContracts.Feature.AI; +using BS.Shared.Extensions; +using Microsoft.ML.Tokenizers; +using Newtonsoft.Json; + +namespace BeWo.Service.Plugins +{ + public class AiService2 + { + // Was wird alles benötigt: + // - WebConfig + // - DBs + + // ======================[ Config ]====================== + public AiConfigDC GetAiConfig() + { + var configs = DAOFactory.GenericDAO.GetAllActive(); + + var config = configs.FirstOrDefault(); + + if (config == null) + { + config = getDefaultAiConfig(); + + DAOFactory.GenericDAO.Insert(config); + } + + var config_dc = MapperFactory.AiConfig.MapToNewDC(config); + + return config_dc; + } + public AiConfigDC UpdateAiConfig(AiConfigDC toUpdate) + { + if (toUpdate?.Oid is null) + throw new InvalidOperationException(); + + var oid = toUpdate.Oid.Value; + var entity = DAOFactory.GenericDAO.LoadByID(oid); + var update = MapperFactory.AiConfig.MergeWithEntity(toUpdate, entity); + + DAOFactory.GenericDAO.Update(update); + + var dc = MapperFactory.AiConfig.MapToNewDC(update); + + return dc; + } + + // ======================[ Models ]====================== + public IEnumerable GetAiModels() + { + var models = getAiModels(); + + return MapperFactory.AiModel.MapToNewDCs(models); + } + + + // ======================[ Conversations ]====================== + public IEnumerable GetAiConversations(int uicontext, long? oid) + { + // SearchDAO + var current_user = UserRightHelper.GetLoggedInUserWithOid(); + var convs = DAOFactory.GenericDAO.GetAllActive(); + + var filter = new List(); + foreach (var conv in convs) + { + if (conv.UIContext != uicontext) + continue; + + if (conv.ApplicationUserOid != current_user.Oid) + continue; + + if (oid is long ref_oid) + { + var main_ref_obj = conv.ContextBeWoObjects?.FirstOrDefault(); + + // ServiceRecord + if (uicontext == 19) + main_ref_obj = conv.ContextBeWoObjects.FirstOrDefault(x => x.Tid == TableID.SupportConcept); + + if (main_ref_obj?.Oid != ref_oid) + continue; + } + + filter.Add(conv); + } + + var dcs = MapperFactory.AiConversation.MapToNewDCs(filter); + + return dcs; + } + + public AiConversationDC CreateAiConversation(AiConversationDC conversation, long modell_oid) + { + var conv = createAiConversation(conversation.UIContext, conversation.ContextBeWoObjects, modell_oid); + var conv_dc = MapperFactory.AiConversation.MapToNewDC(conv); + return conv_dc; + } + public AiConversationDC CreateAiConversationWithMessage(AiConversationDC conversation, long modell_oid, string message) + { + var conv = createAiConversation(conversation.UIContext, conversation.ContextBeWoObjects, modell_oid); + + var new_messages = sendNewMessage(conv, message); + + var conv_dc = MapperFactory.AiConversation.MapToNewDC(conv); + + return conv_dc; + } + public AiConversationDC CloneAiConversation(long conversation_oid, long? last_message_oid) + { + var parent_conv = DAOFactory.GenericDAO.GetByID(conversation_oid); + + if (parent_conv is null) + throw new InvalidOperationException("conv_oid unknown"); + + var clone_conv = new AiConversation(); + + clone_conv.Created = DateTime.Now; + clone_conv.ContextBeWoObjects = parent_conv.ContextBeWoObjects.ToList(); + clone_conv.Displayname = "Klon von " + parent_conv.Displayname; + clone_conv.Messages = new List(); + clone_conv.Modell = parent_conv.Modell; + clone_conv.UIContext = parent_conv.UIContext; + clone_conv.Updated = DateTime.Now; + clone_conv.ApplicationUserOid = parent_conv.ApplicationUserOid; + + if (last_message_oid is null) + last_message_oid = parent_conv.Messages[0].Oid; + + foreach (var msg in parent_conv.Messages) + { + var clone_msg = new AiConversationMessage(); + + clone_msg.Created = msg.Created; + clone_msg.Message = msg.Message; + clone_msg.Role = msg.Role; + clone_msg.Duration = msg.Duration; + clone_msg.ModelName = msg.ModelName; + + clone_conv.Messages.Add(clone_msg); + clone_msg.AiConversation = clone_conv; + + if (msg.Oid == last_message_oid) + break; + } + + DAOFactory.GenericDAO.Insert(clone_conv); + + var dc = MapperFactory.AiConversation.MapToNewDC(clone_conv); + + return dc; + } + + public long UpdateAiConversationDisplayname(long oid, long version, string displayname) + { + var conv = DAOFactory.GenericDAO.GetByID(oid); + + ServiceLogic.ConcurrencyCheck(version, conv); + + conv.Displayname = displayname; + + DAOFactory.GenericDAO.Update(conv); + + return conv.Version.Value; + } + + public void DeleteAiConversation(long conversation_oid) + { + var conversation = DAOFactory.GenericDAO.LoadByID(conversation_oid); + + DAOFactory.GenericDAO.SetActivationType(conversation, ActivationTypeId.Deleted); + } + + // ======================[ Conversationmessages ]====================== + public AiConversationMessageDC[] SendNewMessage(long conversation, string message) + { + var conv = DAOFactory.GenericDAO.LoadByID(conversation); + + return sendNewMessage(conv, message); + } + + + // ======================[ private ]====================== + private AiConfig getDefaultAiConfig() + { + var config = new AiConfig(); + + config.Temperature = 1; + config.Top_P = 1; + config.Max_Gen_Len = 1000; + config.SelectedModel = getDefaultAiModel(); + + return config; + } + private AiModel getDefaultAiModel() + { + var models = getAiModels(); + + if (models == null || !models.Any()) + return null; + + var llama = models.Where(x => x.ModelName.Contains("llama3.2")); + + if (llama.Any()) + return llama.First(); + + return models.First(); + } + private IEnumerable getAiModels() + { + var old_models = DAOFactory.GenericDAO.GetAll(); + var new_models = ServiceFacade.OpenWebApiClient.GetAiModelle().GetResponseData(); + var unknown_models = new_models.ToList(); + + foreach (var old_model in old_models) + { + bool found = false; + foreach (var new_model in new_models) + { + if (old_model.ModelName == new_model.ModelName) + { + if (old_model.IsActive != BS.Shared.ActivationTypeId.Active) + DAOFactory.GenericDAO.SetActivationType(old_model, BS.Shared.ActivationTypeId.Active); + + found = true; + unknown_models.Remove(new_model); + break; + } + } + + if (!found) + DAOFactory.GenericDAO.Deactivate(old_model); + } + + if (unknown_models.Count != 0) + { + foreach (var unknown_model in unknown_models) + { + DAOFactory.GenericDAO.Insert(unknown_model); + } + } + + var models = DAOFactory.GenericDAO.GetAllActive(); + + return models; + } + + private AiConversation createAiConversation(int uicontext, Dictionary context, long modell_oid) + { + var current_user = UserRightHelper.GetLoggedInUserWithOid(); + + var system_prompt = AiPromptFactory.CreateSystemInstructions(uicontext, context); + + var conv = new AiConversation(); + conv.Created = DateTime.Now; + conv.ContextBeWoObjects = MapperFactory.AiConversation.Parse(context); + conv.Displayname = "Neue Unterhaltung"; + conv.Messages = new List(); + conv.Modell = DAOFactory.GenericDAO.LoadByID(modell_oid); + conv.UIContext = uicontext; + conv.Updated = DateTime.Now; + conv.ApplicationUserOid = current_user.Oid; + + var msg = new AiConversationMessage(); + msg.Message = system_prompt; + msg.AiConversation = conv; + msg.Role = AiConversationMessageRole.System; + msg.Created = DateTime.Now; + + conv.Messages.Add(msg); + + DAOFactory.GenericDAO.Insert(conv); + + return conv; + } + + private AiConversationMessageDC[] sendNewMessage(AiConversation conv, string message) + { + // Hole Config + var config = GetAiConfig(); + var configModelName = config.SelectedModel.ModelName; + + // Build Payload + var payload = new + { + model = configModelName, + messages = conv.Messages.Select(m => new { role = m.Role.ToString().ToLower(), content = m.Message }) + }; + + // Check Token Size + checkTokenSize(conv.Messages, configModelName); + + // Updated + conv.Updated = DateTime.Now; + + // Füge Message hinzu + var user_msg = new AiConversationMessage(); + user_msg.Message = message; + user_msg.AiConversation = conv; + user_msg.Role = BS.Shared.AiConversationMessageRole.User; + user_msg.Created = DateTime.Now; + + conv.Messages.Add(user_msg); + + // Update DB Entity + // DAOFactory.GenericDAO.Update(conv); + + var conv_dc = MapperFactory.AiConversation.MapToNewDC(conv); + var user_msg_dc = MapperFactory.AiConversationMessage.MapToNewDC(user_msg); + + // Send Request, wait for Response + var dt_before_request = DateTime.Now; + var response = ServiceFacade.OpenWebApiClient.SendAiMessageRequest(payload, out var message2, out var model, out var duration); + + if (!response.Success) + { + var json = JsonConvert.SerializeObject(response.Copy()); + File.WriteAllText(MergedConfig.GetSetting("AiServiceLogFilePath"), json); + } + + var result = response.GetResponseData(); + + // Antwort + var assi_msg = new AiConversationMessage(); + assi_msg.Message = message2; + assi_msg.AiConversation = conv; + assi_msg.Role = BS.Shared.AiConversationMessageRole.Assistent; + assi_msg.Created = dt_before_request; + assi_msg.Duration = duration ?? 0; + assi_msg.ModelName = model; + + conv.Messages.Add(assi_msg); + + // Update conv. + DAOFactory.GenericDAO.Update(conv); + + var assi_msg_dc = MapperFactory.AiConversationMessage.MapToNewDC(assi_msg); + + return new AiConversationMessageDC[] { user_msg_dc, assi_msg_dc }; + } + + private void checkTokenSize(IList messages, string configModelName) + { + Tokenizer tokenizer = TiktokenTokenizer.CreateForModel(configModelName); + + int tokens = countChatTokens(messages, tokenizer); + } + + private int countChatTokens(IList messages, Tokenizer tokenizer) + { + int tokensPerMessage = 4; // OpenAI Overhead + int tokensPerReply = 2; + + int total = 0; + + foreach (var msg in messages) + { + total += tokensPerMessage; + total += tokenizer.CountTokens(msg.Role.ToString()); + total += tokenizer.CountTokens(msg.Message); + } + + return total + tokensPerReply; + } + } +} \ No newline at end of file diff --git a/Service/Service.csproj b/Service/Service.csproj index 409e26c61..592076b4a 100644 --- a/Service/Service.csproj +++ b/Service/Service.csproj @@ -457,6 +457,7 @@ + diff --git a/Service/ServiceImplementations/AiEnhancedServiceImp.cs b/Service/ServiceImplementations/AiEnhancedServiceImp.cs index 9aad7bf69..f84df5952 100644 --- a/Service/ServiceImplementations/AiEnhancedServiceImp.cs +++ b/Service/ServiceImplementations/AiEnhancedServiceImp.cs @@ -5,6 +5,7 @@ using BeWo.Data.Security; using BeWo.Service.AI; using BeWo.Service.Core; using BeWo.Service.DCEntityMapper; +using BeWo.Service.Plugins; using BeWo.Service.ServiceContracts; using BeWo.Service.ServiceProxy; using BS.Shared; @@ -22,6 +23,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.ExceptionServices; +using System.Runtime.Remoting.Metadata.W3cXsd2001; using System.Security.Cryptography; using System.ServiceModel; using System.ServiceModel.Description; @@ -35,325 +37,58 @@ namespace BeWo.Service.ServiceImplementations [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)] public class AiEnhancedServiceImp : IAiEnhancedService { - // Was wird alles benötigt: - // - WebConfig - // - DBs - - // ======================[ Config ]====================== public AiConfigDC GetAiConfig() { - var configs = DAOFactory.GenericDAO.GetAllActive(); - - var config = configs.FirstOrDefault(); - - if(config == null) - { - config = getDefaultAiConfig(); - - DAOFactory.GenericDAO.Insert(config); - } - - var config_dc = MapperFactory.AiConfig.MapToNewDC(config); - - return config_dc; + var p = PluginLoader.FindClass(); + return p.GetAiConfig(); } public AiConfigDC UpdateAiConfig(AiConfigDC toUpdate) { - if (toUpdate?.Oid is null) - throw new InvalidOperationException(); - - var oid = toUpdate.Oid.Value; - var entity = DAOFactory.GenericDAO.LoadByID(oid); - var update = MapperFactory.AiConfig.MergeWithEntity(toUpdate, entity); - - DAOFactory.GenericDAO.Update(update); - - var dc = MapperFactory.AiConfig.MapToNewDC(update); - - return dc; + var p = PluginLoader.FindClass(); + return p.UpdateAiConfig(toUpdate); } - // ======================[ Models ]====================== public IEnumerable GetAiModels() { - var models = getAiModels(); - - return MapperFactory.AiModel.MapToNewDCs(models); + var p = PluginLoader.FindClass(); + return p.GetAiModels(); } - - - // ======================[ Conversations ]====================== public IEnumerable GetAiConversations(int uicontext, long? oid) { - // SearchDAO - var current_user = UserRightHelper.GetLoggedInUserWithOid(); - var convs = DAOFactory.GenericDAO.GetAllActive(); - - var filter = new List(); - foreach (var conv in convs) - { - if (conv.UIContext != uicontext) - continue; - - if (conv.ApplicationUserOid != current_user.Oid) - continue; - - if (oid is long ref_oid) - { - var main_ref_obj = conv.ContextBeWoObjects?.FirstOrDefault(); - - // ServiceRecord - if (uicontext == 19) - main_ref_obj = conv.ContextBeWoObjects.FirstOrDefault(x => x.Tid == TableID.SupportConcept); - - if (main_ref_obj?.Oid != ref_oid) - continue; - } - - filter.Add(conv); - } - - var dcs = MapperFactory.AiConversation.MapToNewDCs(filter); - - return dcs; + var p = PluginLoader.FindClass(); + return p.GetAiConversations(uicontext, oid); } public AiConversationDC CreateAiConversation(AiConversationDC conversation, long modell_oid) { - var conv = CreateAiConversation(conversation.UIContext, conversation.ContextBeWoObjects, modell_oid); - var conv_dc = MapperFactory.AiConversation.MapToNewDC(conv); - return conv_dc; + var p = PluginLoader.FindClass(); + return p.CreateAiConversation(conversation, modell_oid); } public AiConversationDC CreateAiConversationWithMessage(AiConversationDC conversation, long modell_oid, string message) { - var conv = CreateAiConversation(conversation.UIContext, conversation.ContextBeWoObjects, modell_oid); - - var new_messages = SendNewMessage(conv, message); - - var conv_dc = MapperFactory.AiConversation.MapToNewDC(conv); - - return conv_dc; + var p = PluginLoader.FindClass(); + return p.CreateAiConversationWithMessage(conversation, modell_oid, message); } public AiConversationDC CloneAiConversation(long conversation_oid, long? last_message_oid) { - var parent_conv = DAOFactory.GenericDAO.GetByID(conversation_oid); - - if (parent_conv is null) - throw new InvalidOperationException("conv_oid unknown"); - - var clone_conv = new AiConversation(); - - clone_conv.Created = DateTime.Now; - clone_conv.ContextBeWoObjects = parent_conv.ContextBeWoObjects.ToList(); - clone_conv.Displayname = "Klon von " + parent_conv.Displayname; - clone_conv.Messages = new List(); - clone_conv.Modell = parent_conv.Modell; - clone_conv.UIContext = parent_conv.UIContext; - clone_conv.Updated = DateTime.Now; - clone_conv.ApplicationUserOid = parent_conv.ApplicationUserOid; - - if (last_message_oid is null) - last_message_oid = parent_conv.Messages[0].Oid; - - foreach (var msg in parent_conv.Messages) - { - var clone_msg = new AiConversationMessage(); - - clone_msg.Created = msg.Created; - clone_msg.Message = msg.Message; - clone_msg.Role = msg.Role; - clone_msg.Duration = msg.Duration; - clone_msg.ModelName = msg.ModelName; - - clone_conv.Messages.Add(clone_msg); - clone_msg.AiConversation = clone_conv; - - if (msg.Oid == last_message_oid) - break; - } - - DAOFactory.GenericDAO.Insert(clone_conv); - - var dc = MapperFactory.AiConversation.MapToNewDC(clone_conv); - - return dc; + var p = PluginLoader.FindClass(); + return p.CloneAiConversation(conversation_oid, last_message_oid); } - public long UpdateAiConversationDisplayname(long oid, long version, string displayname) { - var conv = DAOFactory.GenericDAO.GetByID(oid); - - ServiceLogic.ConcurrencyCheck(version, conv); - - conv.Displayname = displayname; - - DAOFactory.GenericDAO.Update(conv); - - return conv.Version.Value; + var p = PluginLoader.FindClass(); + return p.UpdateAiConversationDisplayname(oid, version, displayname); } - public void DeleteAiConversation(long conversation_oid) { - var conversation = DAOFactory.GenericDAO.LoadByID(conversation_oid); - - DAOFactory.GenericDAO.SetActivationType(conversation, ActivationTypeId.Deleted); + var p = PluginLoader.FindClass(); + p.DeleteAiConversation(conversation_oid); } - // ======================[ Conversationmessages ]====================== public AiConversationMessageDC[] SendNewMessage(long conversation, string message) { - var conv = DAOFactory.GenericDAO.LoadByID(conversation); - - return SendNewMessage(conv, message); - } - - - // ======================[ private ]====================== - private AiConfig getDefaultAiConfig() - { - var config = new AiConfig(); - - config.Temperature = 1; - config.Top_P = 1; - config.Max_Gen_Len = 1000; - config.SelectedModel = getDefaultAiModel(); - - return config; - } - private AiModel getDefaultAiModel() - { - var models = getAiModels(); - - if (models == null || !models.Any()) - return null; - - var llama = models.Where(x => x.ModelName.Contains("llama3.2")); - - if(llama.Any()) - return llama.First(); - - return models.First(); - } - private IEnumerable getAiModels() - { - var old_models = DAOFactory.GenericDAO.GetAll(); - var new_models = ServiceFacade.OpenWebApiClient.GetAiModelle().GetResponseData(); - var unknown_models = new_models.ToList(); - - foreach (var old_model in old_models) - { - bool found = false; - foreach (var new_model in new_models) - { - if (old_model.ModelName == new_model.ModelName) - { - if (old_model.IsActive != BS.Shared.ActivationTypeId.Active) - DAOFactory.GenericDAO.SetActivationType(old_model, BS.Shared.ActivationTypeId.Active); - - found = true; - unknown_models.Remove(new_model); - break; - } - } - - if (!found) - DAOFactory.GenericDAO.Deactivate(old_model); - } - - if (unknown_models.Count != 0) - { - foreach (var unknown_model in unknown_models) - { - DAOFactory.GenericDAO.Insert(unknown_model); - } - } - - var models = DAOFactory.GenericDAO.GetAllActive(); - - return models; - } - - private AiConversation CreateAiConversation(int uicontext, Dictionary context, long modell_oid) - { - var current_user = UserRightHelper.GetLoggedInUserWithOid(); - - var system_prompt = AiPromptFactory.CreateSystemInstructions(uicontext, context); - - var conv = new AiConversation(); - conv.Created = DateTime.Now; - conv.ContextBeWoObjects = MapperFactory.AiConversation.Parse(context); - conv.Displayname = "Neue Unterhaltung"; - conv.Messages = new List(); - conv.Modell = DAOFactory.GenericDAO.LoadByID(modell_oid); - conv.UIContext = uicontext; - conv.Updated = DateTime.Now; - conv.ApplicationUserOid = current_user.Oid; - - var msg = new AiConversationMessage(); - msg.Message = system_prompt; - msg.AiConversation = conv; - msg.Role = AiConversationMessageRole.System; - msg.Created = DateTime.Now; - - conv.Messages.Add(msg); - - DAOFactory.GenericDAO.Insert(conv); - - return conv; - } - - private AiConversationMessageDC[] SendNewMessage(AiConversation conv, string message) - { - // Updated - conv.Updated = DateTime.Now; - - // Füge Message hinzu - var user_msg = new AiConversationMessage(); - user_msg.Message = message; - user_msg.AiConversation = conv; - user_msg.Role = BS.Shared.AiConversationMessageRole.User; - user_msg.Created = DateTime.Now; - - conv.Messages.Add(user_msg); - - // Update DB Entity - // DAOFactory.GenericDAO.Update(conv); - - var conv_dc = MapperFactory.AiConversation.MapToNewDC(conv); - var user_msg_dc = MapperFactory.AiConversationMessage.MapToNewDC(user_msg); - - // Hole Config - var config = GetAiConfig(); - - // Send Request, wait for Response - var dt_before_request = DateTime.Now; - var response = ServiceFacade.OpenWebApiClient.SendAiMessageRequest(conv_dc, config, out var message2, out var model, out var duration); - - if (!response.Success) - { - var json = JsonConvert.SerializeObject(response.Copy()); - File.WriteAllText(MergedConfig.GetSetting("AiServiceLogFilePath"), json); - } - - var result = response.GetResponseData(); - - // Antwort - var assi_msg = new AiConversationMessage(); - assi_msg.Message = message2; - assi_msg.AiConversation = conv; - assi_msg.Role = BS.Shared.AiConversationMessageRole.Assistent; - assi_msg.Created = dt_before_request; - assi_msg.Duration = duration ?? 0; - assi_msg.ModelName = model; - - conv.Messages.Add(assi_msg); - - // Update conv. - DAOFactory.GenericDAO.Update(conv); - - var assi_msg_dc = MapperFactory.AiConversationMessage.MapToNewDC(assi_msg); - - return new AiConversationMessageDC[] { user_msg_dc, assi_msg_dc }; + var p = PluginLoader.FindClass(); + return p.SendNewMessage(conversation, message); } } } diff --git a/Service/ServiceProxy/OpenWebApiClient.cs b/Service/ServiceProxy/OpenWebApiClient.cs index ee5923648..9fad4453d 100644 --- a/Service/ServiceProxy/OpenWebApiClient.cs +++ b/Service/ServiceProxy/OpenWebApiClient.cs @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Net; using System.Text; +using System.Text.Json.Nodes; using System.Threading.Tasks; using static DevExpress.Xpo.Helpers.AssociatedCollectionCriteriaHelper; @@ -62,7 +63,7 @@ namespace BeWo.Service.ServiceProxy return ApiResponse>.SuccessResponse(data); } - public ApiResponse SendAiMessageRequest(AiConversationDC conversation, AiConfigDC config, out string message, out string model, out int? duration) + public ApiResponse SendAiMessageRequest(dynamic payload, out string message, out string model, out int? duration) { message = null; duration = null; @@ -70,12 +71,6 @@ namespace BeWo.Service.ServiceProxy var sub_url = "api/chat/completions"; - var payload = new - { - model = config.SelectedModel.ModelName, - messages = conversation.Messages.Select(m => new { role = m.Role.ToString().ToLower(), content = m.Message }) - }; - var response_format = new { id = string.Empty, @@ -101,6 +96,17 @@ namespace BeWo.Service.ServiceProxy _PostClientFacade.JsonObject = payload; + if (MergedConfig.GetSetting("AiServiceLastRequestEnabled") is string str && bool.TryParse(str, out bool b) && b) + { + var path = MergedConfig.GetSetting("AiServiceLastRequestFilePath"); + if (Directory.GetParent(path) == null) + throw new ArgumentException("AiServiceLastRequestFilePath ungültig"); + + var payload_ser = JsonConvert.SerializeObject(_PostClientFacade.JsonObject, Formatting.Indented); + + File.WriteAllText(path, payload_ser); + } + var response = _PostClientFacade.GetAnonymousTypeAsync(sub_url, response_format, _ErrorExtractor).GetAwaiter().GetResult(); if (!response.Success)