647 lines
15 KiB
C#
647 lines
15 KiB
C#
using BS.Shared;
|
|
using BS.Shared.DataContracts.Feature.AI;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using BeWo.ViewModel.ListViewModel;
|
|
using System.Windows.Input;
|
|
using BS.Shared.Core;
|
|
using DevExpress.Mvvm;
|
|
using BeWo.ServiceProxy;
|
|
using BS.Shared.Extensions;
|
|
using System.Diagnostics;
|
|
using DevExpress.XtraCharts.Native;
|
|
using static DevExpress.Utils.Drawing.Helpers.NativeMethods;
|
|
using System.Security.Cryptography;
|
|
using BeWo.ViewModel.Controls.AI;
|
|
using DevExpress.Charts.Native;
|
|
|
|
namespace BeWo.ViewModel.View.AI
|
|
{
|
|
public class AiConversationChatViewModel : DialogViewModel
|
|
{
|
|
private bool _IsSettingsOpen;
|
|
private bool _IsCloneOpen;
|
|
private bool _IsSending;
|
|
|
|
private string _MessageInput;
|
|
|
|
private AiModelDC _SelectedModel;
|
|
private AiConfigVM _Config;
|
|
|
|
private AiPromptbausteinPromptVM _SavePromptVM;
|
|
|
|
public AiConversationChatViewModel()
|
|
{
|
|
Models = new ObservableSortCollection<AiModelDC>();
|
|
|
|
SendNewMessageCommand = new DelegateCommand(SendNewMessage, () => ListVM.SelectedVM is object && !string.IsNullOrWhiteSpace(MessageInput) && !IsSending);
|
|
OpenNewConversationCommand = new DelegateCommand(OpenNewConversation, () => CanSendNewMessage && ListVM.SelectedVM is object);
|
|
StartConversationWithMessageCommand = new DelegateCommand(StartConversationWithMessage, () => CanSendNewMessage && !string.IsNullOrWhiteSpace(MessageInput) && !IsSending);
|
|
|
|
ReloadConfigCommand = new DelegateCommand(ReloadConfig);
|
|
ReloadConversationsCommand = new DelegateCommand(ReloadConversations);
|
|
ReloadModelsCommand = new DelegateCommand(ReloadModels);
|
|
|
|
OpenSettingCommand = new DelegateCommand(OpenSetting, () => BeWoApp.AppSettings?.ModuleAiSettingsEnabled ?? false);
|
|
RenameCommand = new DelegateCommand(Rename, () => ListVM.SelectedVM is object);
|
|
OpenCloneCommand = new DelegateCommand(OpenClone, () => ListVM.SelectedVM is object && BeWoAppInfo.IsInAiDeveloperMode);
|
|
CloneCommand = new DelegateCommand(Clone);
|
|
|
|
AskDeleteCommand = new DelegateCommand(AskDelete, () => ListVM.SelectedVM is object);
|
|
|
|
SavePromptCommand = new DelegateCommand<AiConversationMessageVM>(SavePrompt, vm => BeWoApp.HasLoggedOnUserRight(UserRightType.AiModulePromptbausteineEdit));
|
|
AcceptSavePromptCommand = new DelegateCommand(AcceptSavePrompt, () => !string.IsNullOrWhiteSpace(SavePromptVM?.Title));
|
|
CancelSavePromptCommand = new DelegateCommand(CancelSavePrompt);
|
|
|
|
ListVM = new AiConversationListVM();
|
|
ListVM.SelectedVMChanged += (s, e) => FirePropertyChanged(nameof(IsNewConversationVisible));
|
|
|
|
AssistentCommands = new List<AiMenuItemTemplate>();
|
|
}
|
|
|
|
public event EventHandler SendNewMessageSuccess;
|
|
|
|
public ICommand SendNewMessageCommand { get; set; }
|
|
public ICommand OpenNewConversationCommand { get; set; }
|
|
public ICommand StartConversationWithMessageCommand { get; set; }
|
|
public ICommand AskDeleteCommand { get; set; }
|
|
public ICommand ReloadConfigCommand { get; set; }
|
|
public ICommand ReloadConversationsCommand { get; set; }
|
|
public ICommand ReloadModelsCommand { get; set; }
|
|
|
|
public ICommand OpenSettingCommand { get; set; }
|
|
public ICommand RenameCommand { get; set; }
|
|
public ICommand OpenCloneCommand { get; set; }
|
|
public ICommand CloneCommand { get; set; }
|
|
|
|
public ICommand SavePromptCommand { get; set; }
|
|
public ICommand AcceptSavePromptCommand { get; set; }
|
|
public ICommand CancelSavePromptCommand { get; set; }
|
|
|
|
public AiActionType ActionType { get; set; }
|
|
public AiViewContextDC ViewContext { get; set; }
|
|
|
|
public bool CanSendNewMessage { get; set; }
|
|
public AiConversationListVM ListVM { get; set; }
|
|
public ObservableCollection<AiModelDC> Models { get; set; }
|
|
|
|
public AiModelDC SelectedModel
|
|
{
|
|
get { return _SelectedModel; }
|
|
set
|
|
{
|
|
if (_SelectedModel == value)
|
|
return;
|
|
|
|
_SelectedModel = value;
|
|
FirePropertyChanged(nameof(SelectedModel));
|
|
}
|
|
}
|
|
public AiConfigVM Config
|
|
{
|
|
get { return _Config; }
|
|
set
|
|
{
|
|
if (Config == value)
|
|
return;
|
|
|
|
_Config = value;
|
|
FirePropertyChanged(nameof(Config));
|
|
}
|
|
}
|
|
|
|
public string MessageInput
|
|
{
|
|
get { return _MessageInput; }
|
|
set
|
|
{
|
|
if (MessageInput == value)
|
|
return;
|
|
|
|
_MessageInput = value;
|
|
FirePropertyChanged(nameof(MessageInput));
|
|
}
|
|
}
|
|
public AbstractBaseVM InputItem { get; set; }
|
|
|
|
public List<AiMenuItemTemplate> AssistentCommands { get; protected set; }
|
|
|
|
public bool IsSettingsOpen
|
|
{
|
|
get => _IsSettingsOpen;
|
|
set
|
|
{
|
|
_IsSettingsOpen = value;
|
|
FirePropertyChanged(nameof(IsSettingsOpen));
|
|
}
|
|
}
|
|
public bool IsCloneOpen
|
|
{
|
|
get => _IsCloneOpen;
|
|
set
|
|
{
|
|
_IsCloneOpen = value;
|
|
FirePropertyChanged(nameof(IsCloneOpen));
|
|
}
|
|
}
|
|
public bool IsSending
|
|
{
|
|
get => _IsSending;
|
|
set
|
|
{
|
|
_IsSending = value;
|
|
FirePropertyChanged(nameof(IsSending));
|
|
}
|
|
}
|
|
public bool IsNewConversationVisible => ListVM.SelectedVM is null;
|
|
|
|
public AiPromptbausteinPromptVM SavePromptVM
|
|
{
|
|
get => _SavePromptVM;
|
|
set
|
|
{
|
|
if (SetProperty(ref _SavePromptVM, value, nameof(SavePromptVM)))
|
|
{
|
|
FirePropertyChanged(nameof(IsSavePromptOpen));
|
|
}
|
|
}
|
|
}
|
|
public bool IsSavePromptOpen
|
|
{
|
|
get => SavePromptVM is object;
|
|
set
|
|
{
|
|
if (!value)
|
|
SavePromptVM = null;
|
|
}
|
|
}
|
|
|
|
private void OpenNewConversation()
|
|
{
|
|
ListVM.SelectedVM = null;
|
|
}
|
|
private void StartConversationWithMessage()
|
|
{
|
|
StartSending();
|
|
|
|
try
|
|
{
|
|
var message = MessageInput;
|
|
|
|
var conv = new AiConversationDC();
|
|
|
|
conv.Created = DateTime.Now;
|
|
conv.Updated = DateTime.Now;
|
|
conv.ActionType = ActionType;
|
|
conv.Messages = new List<AiConversationMessageDC>()
|
|
{
|
|
new AiConversationMessageDC()
|
|
{
|
|
Message = message,
|
|
Role = AiConversationMessageRole.User
|
|
}
|
|
};
|
|
|
|
var vm = InsertConverstion(conv);
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(
|
|
x => x.CreateAiConversationWithMessage(conv, ViewContext, message),
|
|
dc => UpdateConverstion(vm, dc),
|
|
e =>
|
|
{
|
|
EndSending();
|
|
ListVM.VMList.Remove(vm);
|
|
}
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EndSending();
|
|
}
|
|
}
|
|
|
|
private void SendNewMessage()
|
|
{
|
|
StartSending();
|
|
|
|
try
|
|
{
|
|
var conv = ListVM.SelectedVM;
|
|
|
|
var msg_dc = new AiConversationMessageDC();
|
|
|
|
msg_dc.Message = MessageInput;
|
|
msg_dc.Role = BS.Shared.AiConversationMessageRole.User;
|
|
msg_dc.Created = DateTime.Now;
|
|
|
|
var msg_vm = new AiConversationMessageVM(msg_dc);
|
|
|
|
conv.Messages.VMList.Add(msg_vm);
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(
|
|
x => x.SendNewMessage(conv.DataContract.Oid.Value, MessageInput),
|
|
(messages) =>
|
|
{
|
|
if (messages.Count != 2)
|
|
throw new InvalidOperationException("message count: " + messages.Count);
|
|
|
|
msg_vm.DataContract = messages[0];
|
|
|
|
conv.Messages.VMList.Add(new AiConversationMessageVM(messages[1]));
|
|
|
|
FinishSending();
|
|
},
|
|
(exception) =>
|
|
{
|
|
conv.Messages.VMList.Remove(msg_vm);
|
|
|
|
EndSending();
|
|
}
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EndSending();
|
|
}
|
|
}
|
|
private void StartSending()
|
|
{
|
|
if (IsSending)
|
|
throw new InvalidOperationException("Sendet bereits");
|
|
|
|
IsSending = true;
|
|
IsWaiting = true;
|
|
}
|
|
private void EndSending()
|
|
{
|
|
if (!IsSending)
|
|
throw new InvalidOperationException("Sendet nicht");
|
|
|
|
IsSending = false;
|
|
IsWaiting = false;
|
|
}
|
|
|
|
private AiConversationVM InsertConverstion(AiConversationDC dc)
|
|
{
|
|
var vm = new AiConversationVM(dc);
|
|
ListVM.VMList.Insert(0, vm);
|
|
ListVM.SelectedVM = vm;
|
|
|
|
return vm;
|
|
}
|
|
|
|
private void UpdateConverstion(AiConversationVM vm, AiConversationDC dc)
|
|
{
|
|
vm.UpdateByDataContract(dc);
|
|
|
|
FinishSending();
|
|
}
|
|
private void FinishSending()
|
|
{
|
|
MessageInput = string.Empty;
|
|
|
|
EndSending();
|
|
|
|
SendNewMessageSuccess?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void ReloadConfig()
|
|
{
|
|
var config = ServiceFacade.DoAiEnhancedServiceSnyc(x => x.GetAiConfig());
|
|
|
|
if (Config is null)
|
|
Config = new AiConfigVM(config);
|
|
else
|
|
Config.Update(config);
|
|
|
|
//ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.GetAiConfig(), (x) =>
|
|
//{
|
|
// if (Config is null)
|
|
// Config = new AiConfigVM(x);
|
|
// else
|
|
// Config.Update(x);
|
|
//});
|
|
}
|
|
public void UpdateConfig()
|
|
{
|
|
if (!Config.IsDirty)
|
|
return;
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.UpdateAiConfig(Config.CommitToDataContract()), (x) =>
|
|
{
|
|
Config.Update(x);
|
|
});
|
|
}
|
|
|
|
private void ReloadModels()
|
|
{
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.GetAiModels(), (x) =>
|
|
{
|
|
Models.MakeEqualTo(x);
|
|
|
|
if (Config.SelectedModel is null && Models.Any())
|
|
Config.SelectedModel = Models.First();
|
|
});
|
|
}
|
|
private void ReloadConversations()
|
|
{
|
|
var ui_context = ActionType;
|
|
var reference = ViewContext.Primary;
|
|
|
|
var list = VMFactory.CreateAiConversationListVM(ui_context, reference);
|
|
|
|
ListVM.VMList.MakeEqualTo(list.VMList);
|
|
}
|
|
|
|
private void OpenSetting()
|
|
{
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.GetAiModels(), (x) =>
|
|
{
|
|
Models.MakeEqualTo(x);
|
|
IsSettingsOpen = true;
|
|
});
|
|
}
|
|
private void Rename()
|
|
{
|
|
if (ListVM.SelectedVM is AiConversationVM vm)
|
|
{
|
|
vm.IsEditing = !vm.IsEditing;
|
|
}
|
|
}
|
|
private void OpenClone()
|
|
{
|
|
foreach (var message in ListVM.SelectedVM.Messages.VMList)
|
|
{
|
|
message.SetIsChecked(true);
|
|
}
|
|
|
|
IsCloneOpen = true;
|
|
}
|
|
private void Clone()
|
|
{
|
|
var conv = ListVM.SelectedVM;
|
|
|
|
AiConversationMessageVM last = null;
|
|
foreach (var msg in conv.Messages.VMList)
|
|
{
|
|
if (!msg.IsChecked)
|
|
break;
|
|
|
|
last = msg;
|
|
}
|
|
|
|
var conv_oid = conv.DataContract.Oid.Value;
|
|
var msg_oid = last?.DataContract.Oid;
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.CloneAiConversation(conv_oid, msg_oid), dc =>
|
|
{
|
|
InsertConverstion(dc);
|
|
CloseClone();
|
|
});
|
|
}
|
|
private void CloseClone()
|
|
{
|
|
IsCloneOpen = false;
|
|
}
|
|
|
|
public void AskDelete()
|
|
{
|
|
var selected = ListVM.SelectedVM;
|
|
|
|
var info = selected.Displayname;
|
|
|
|
var oid = selected.DataContract.Oid;
|
|
|
|
if (BeWoAppInfo.IsInAiDeveloperMode)
|
|
{
|
|
info += $":{oid}";
|
|
}
|
|
|
|
var msg = $"Wollen Sie die ausgewählte Konversation ({info}) löschen?";
|
|
|
|
if (MessageBox.Show(msg, "Konversation löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning) ==
|
|
MessageBoxResult.Yes)
|
|
{
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.DeleteAiConversation(oid.Value), () =>
|
|
{
|
|
ListVM.VMList.Remove(selected);
|
|
CommandManager.InvalidateRequerySuggested();
|
|
//SelectedVM = VMList.FirstOrDefault();
|
|
});
|
|
}
|
|
}
|
|
public void SavePrompt(AiConversationMessageVM message)
|
|
{
|
|
var dc = new AiPromptbausteinPromptDC();
|
|
|
|
dc.Creator = BeWoApp.CompactLoggedOnEmployee;
|
|
dc.Prompt = message.Message;
|
|
dc.CanEdit = true;
|
|
dc.ActionType = AiActionType.ServiceRecordConversation;
|
|
|
|
SavePromptVM = new AiPromptbausteinPromptVM(dc);
|
|
}
|
|
public void AcceptSavePrompt()
|
|
{
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.InsertAiPromptbausteinPrompt(SavePromptVM.CommitToDataContract()), cb => SavePromptVM = null, exc => SavePromptVM = null);
|
|
}
|
|
public void CancelSavePrompt()
|
|
{
|
|
SavePromptVM = null;
|
|
}
|
|
|
|
public void ImportAssistentCommand(AiMenuItemTemplate origin)
|
|
{
|
|
var origin_command = origin.Command;
|
|
|
|
var new_command = new DelegateCommand<AiConversationMessageVM>(x =>
|
|
{
|
|
origin_command.Execute(x);
|
|
Close();
|
|
});
|
|
|
|
var imported_menu_item = new AiMenuItemTemplate()
|
|
{
|
|
Command = new_command,
|
|
Icon = origin.Icon,
|
|
Name = origin.Name
|
|
};
|
|
|
|
AssistentCommands.Add(imported_menu_item);
|
|
}
|
|
|
|
internal void Start()
|
|
{
|
|
if (InputItem is null)
|
|
return;
|
|
|
|
if (InputItem is AiPromptbausteinPromptVM prompt)
|
|
{
|
|
StartPrompt(prompt);
|
|
}
|
|
else if (InputItem is AiPromptbausteinRoutineVM routine)
|
|
{
|
|
StartRoutine(routine);
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(InputItem));
|
|
}
|
|
|
|
InputItem = null;
|
|
}
|
|
|
|
private void StartPrompt(AiPromptbausteinPromptVM prompt)
|
|
{
|
|
StartSending();
|
|
|
|
try
|
|
{
|
|
var conv = new AiConversationDC();
|
|
|
|
conv.Created = DateTime.Now;
|
|
conv.Updated = DateTime.Now;
|
|
conv.ActionType = ActionType;
|
|
|
|
var vm = InsertConverstion(conv);
|
|
|
|
Action<Exception> error = (e) =>
|
|
{
|
|
EndSending();
|
|
ListVM.VMList.Remove(vm);
|
|
ServiceFacade.EndManuelWaiting();
|
|
};
|
|
|
|
ServiceFacade.StartManuelWaiting();
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.CreateAiConversation(conv, ViewContext), cb =>
|
|
{
|
|
vm.UpdateByDataContract(cb);
|
|
|
|
var msg = prompt.Prompt;
|
|
|
|
var msg_dc = new AiConversationMessageDC();
|
|
|
|
msg_dc.Message = msg;
|
|
msg_dc.Role = BS.Shared.AiConversationMessageRole.User;
|
|
msg_dc.Created = DateTime.Now;
|
|
|
|
var msg_vm = new AiConversationMessageVM(msg_dc);
|
|
|
|
vm.Messages.VMList.Add(msg_vm);
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.ExecuteAiConversationWithPrompt(cb.Oid.Value, ViewContext, prompt.DataContract.Oid.Value),
|
|
cb2 =>
|
|
{
|
|
FinishSending();
|
|
|
|
ServiceFacade.EndManuelWaiting();
|
|
|
|
vm.Messages.VMList.Remove(msg_vm);
|
|
|
|
vm.AddMessages(cb2);
|
|
}, error);
|
|
},
|
|
error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EndSending();
|
|
}
|
|
}
|
|
|
|
private void StartRoutine(AiPromptbausteinRoutineVM routine)
|
|
{
|
|
StartSending();
|
|
|
|
try
|
|
{
|
|
var conv = new AiConversationDC();
|
|
|
|
conv.Created = DateTime.Now;
|
|
conv.Updated = DateTime.Now;
|
|
conv.ActionType = ActionType;
|
|
|
|
var vm = InsertConverstion(conv);
|
|
|
|
Action<Exception> error = (e) =>
|
|
{
|
|
EndSending();
|
|
ListVM.VMList.Remove(vm);
|
|
ServiceFacade.EndManuelWaiting();
|
|
};
|
|
|
|
ServiceFacade.StartManuelWaiting();
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.CreateAiConversation(conv, ViewContext), cb =>
|
|
{
|
|
vm.UpdateByDataContract(cb);
|
|
ExecuteChain(routine, 0, error);
|
|
}, error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EndSending();
|
|
}
|
|
}
|
|
|
|
private void ExecuteChain(AiPromptbausteinRoutineVM routine, int step, Action<Exception> error)
|
|
{
|
|
if (step >= routine.Steps.Count)
|
|
{
|
|
FinishSending();
|
|
|
|
ServiceFacade.EndManuelWaiting();
|
|
|
|
return;
|
|
}
|
|
|
|
var conv = ListVM.SelectedVM;
|
|
|
|
var custom_viewContext = new AiViewContextDC
|
|
{
|
|
References = ViewContext.References,
|
|
Primary = ViewContext.Primary
|
|
};
|
|
|
|
if (step > 0 && routine.RoutineType == AiRoutineType.Chain)
|
|
{
|
|
custom_viewContext.Secondary = conv.Messages.VMList.Last().Message;
|
|
}
|
|
else
|
|
{
|
|
custom_viewContext.Secondary = ViewContext.Secondary;
|
|
}
|
|
|
|
var msg = routine.Steps.VMList[step].PromptReference.Prompt;
|
|
|
|
var msg_dc = new AiConversationMessageDC
|
|
{
|
|
Message = msg,
|
|
Role = AiConversationMessageRole.User,
|
|
Created = DateTime.Now
|
|
};
|
|
|
|
var msg_vm = new AiConversationMessageVM(msg_dc);
|
|
|
|
conv.Messages.VMList.Add(msg_vm);
|
|
|
|
ServiceFacade.DoAiEnhancedServiceAsnyc(
|
|
x => x.ExecuteAiConversationWithRoutine(conv.Oid, custom_viewContext, routine.DataContract.Oid.Value, step),
|
|
cb =>
|
|
{
|
|
conv.Messages.VMList.Remove(msg_vm);
|
|
|
|
conv.AddMessages(cb);
|
|
|
|
ExecuteChain(routine, step + 1, error);
|
|
}, error);
|
|
}
|
|
}
|
|
}
|