Files
BeWoPlaner/BeWo/ViewModel/View/AI/AiConversationChatViewModel.cs

672 lines
16 KiB
C#
Raw Permalink Normal View History

2025-11-24 12:20:26 +01:00
using BS.Shared;
2025-05-14 10:05:15 +02:00
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;
2026-03-20 14:16:43 +01:00
using System.Diagnostics;
2026-05-27 15:34:53 +02:00
using DevExpress.XtraCharts.Native;
using static DevExpress.Utils.Drawing.Helpers.NativeMethods;
using System.Security.Cryptography;
2026-05-29 12:26:37 +02:00
using BeWo.ViewModel.Controls.AI;
2026-06-12 15:44:39 +02:00
using DevExpress.Charts.Native;
2025-05-14 10:05:15 +02:00
2025-11-05 11:49:53 +01:00
namespace BeWo.ViewModel.View.AI
2025-05-14 10:05:15 +02:00
{
2025-12-30 12:35:43 +01:00
public class AiConversationChatViewModel : DialogViewModel
2025-05-14 10:05:15 +02:00
{
private bool _IsSettingsOpen;
private bool _IsCloneOpen;
private bool _IsSending;
private string _MessageInput;
private AiModelDC _SelectedModel;
private AiConfigVM _Config;
2026-03-16 14:08:38 +01:00
private AiPromptbausteinPromptVM _SavePromptVM;
2025-05-14 10:05:15 +02:00
public AiConversationChatViewModel()
{
Models = new ObservableSortCollection<AiModelDC>();
SendNewMessageCommand = new DelegateCommand(SendNewMessage, () => ListVM.SelectedVM is object && !string.IsNullOrWhiteSpace(MessageInput) && !IsSending);
2026-06-15 11:22:06 +02:00
OpenNewConversationCommand = new DelegateCommand(OpenNewConversation, () => CanSendNewMessage && ListVM.SelectedVM is object);
2026-02-20 11:41:26 +01:00
StartConversationWithMessageCommand = new DelegateCommand(StartConversationWithMessage, () => CanSendNewMessage && !string.IsNullOrWhiteSpace(MessageInput) && !IsSending);
2025-05-14 10:05:15 +02:00
ReloadConfigCommand = new DelegateCommand(ReloadConfig);
ReloadConversationsCommand = new DelegateCommand(ReloadConversations);
ReloadModelsCommand = new DelegateCommand(ReloadModels);
OpenSettingCommand = new DelegateCommand(OpenSetting, () => BeWoApp.AppSettings?.ModuleAiSettingsEnabled ?? false);
2026-06-12 15:44:39 +02:00
RenameCommand = new DelegateCommand(Rename, () => ListVM.SelectedVM is object);
OpenCloneCommand = new DelegateCommand(OpenClone, () => ListVM.SelectedVM is object && BeWoAppInfo.IsInAiDeveloperMode);
2025-05-14 10:05:15 +02:00
CloneCommand = new DelegateCommand(Clone);
2026-06-12 15:44:39 +02:00
AskDeleteCommand = new DelegateCommand(AskDelete, () => ListVM.SelectedVM is object);
2025-05-14 10:05:15 +02:00
2026-03-16 14:08:38 +01:00
SavePromptCommand = new DelegateCommand<AiConversationMessageVM>(SavePrompt, vm => BeWoApp.HasLoggedOnUserRight(UserRightType.AiModulePromptbausteineEdit));
2026-03-20 14:16:43 +01:00
AcceptSavePromptCommand = new DelegateCommand(AcceptSavePrompt, () => !string.IsNullOrWhiteSpace(SavePromptVM?.Title));
2026-03-16 14:08:38 +01:00
CancelSavePromptCommand = new DelegateCommand(CancelSavePrompt);
2025-05-14 10:05:15 +02:00
ListVM = new AiConversationListVM();
ListVM.SelectedVMChanged += (s, e) => FirePropertyChanged(nameof(IsNewConversationVisible));
2026-05-29 12:26:37 +02:00
AssistentCommands = new List<AiMenuItemTemplate>();
2025-05-14 10:05:15 +02:00
}
public event EventHandler SendNewMessageSuccess;
public ICommand SendNewMessageCommand { get; set; }
2026-06-15 11:22:06 +02:00
public ICommand OpenNewConversationCommand { get; set; }
2025-05-14 10:05:15 +02:00
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; }
2025-07-08 16:30:58 +02:00
public ICommand RenameCommand { get; set; }
2025-05-14 10:05:15 +02:00
public ICommand OpenCloneCommand { get; set; }
public ICommand CloneCommand { get; set; }
2026-03-16 14:08:38 +01:00
public ICommand SavePromptCommand { get; set; }
public ICommand AcceptSavePromptCommand { get; set; }
public ICommand CancelSavePromptCommand { get; set; }
2026-05-29 12:26:37 +02:00
public AiActionType ActionType { get; set; }
public AiViewContextDC ViewContext { get; set; }
2026-02-19 14:32:37 +01:00
public bool CanSendNewMessage { get; set; }
2025-05-14 10:05:15 +02:00
public AiConversationListVM ListVM { get; set; }
public ObservableCollection<AiModelDC> Models { get; set; }
2026-02-19 14:32:37 +01:00
2025-05-14 10:05:15 +02:00
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));
}
}
2026-06-12 15:44:39 +02:00
public AbstractBaseVM InputItem { get; set; }
2025-05-14 10:05:15 +02:00
2026-05-29 12:26:37 +02:00
public List<AiMenuItemTemplate> AssistentCommands { get; protected set; }
2025-05-14 10:05:15 +02:00
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;
2026-03-16 14:08:38 +01:00
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;
}
}
2026-06-15 11:22:06 +02:00
private void OpenNewConversation()
{
ListVM.SelectedVM = null;
}
2025-05-14 10:05:15 +02:00
private void StartConversationWithMessage()
{
StartSending();
try
{
var message = MessageInput;
var conv = new AiConversationDC();
conv.Created = DateTime.Now;
conv.Updated = DateTime.Now;
2026-05-26 12:30:29 +02:00
conv.ActionType = ActionType;
2025-05-14 10:05:15 +02:00
conv.Messages = new List<AiConversationMessageDC>()
{
new AiConversationMessageDC()
{
Message = message,
Role = AiConversationMessageRole.User
}
};
var vm = InsertConverstion(conv);
2026-06-12 15:44:39 +02:00
ServiceFacade.DoAiEnhancedServiceAsnyc(
2026-05-27 15:34:53 +02:00
x => x.CreateAiConversationWithMessage(conv, ViewContext, message),
2025-05-14 10:05:15 +02:00
dc => UpdateConverstion(vm, dc),
2026-03-16 14:08:38 +01:00
e =>
{
2025-07-04 14:27:08 +02:00
EndSending();
ListVM.VMList.Remove(vm);
}
);
2025-05-14 10:05:15 +02:00
}
catch (Exception ex)
{
EndSending();
}
}
2026-05-27 15:34:53 +02:00
2025-05-14 10:05:15 +02:00
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]));
2026-06-22 11:49:59 +02:00
conv.Updated = DateTime.Now;
2026-05-27 15:34:53 +02:00
FinishSending();
2025-05-14 10:05:15 +02:00
},
(exception) =>
{
conv.Messages.VMList.Remove(msg_vm);
EndSending();
}
);
}
catch (Exception ex)
{
EndSending();
}
}
private void StartSending()
{
if (IsSending)
throw new InvalidOperationException("Sendet bereits");
IsSending = true;
2026-05-27 15:34:53 +02:00
IsWaiting = true;
2025-05-14 10:05:15 +02:00
}
private void EndSending()
{
if (!IsSending)
throw new InvalidOperationException("Sendet nicht");
IsSending = false;
2026-05-27 15:34:53 +02:00
IsWaiting = false;
2025-05-14 10:05:15 +02:00
}
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);
2026-05-27 15:34:53 +02:00
FinishSending();
}
private void FinishSending()
{
2025-05-14 10:05:15 +02:00
MessageInput = string.Empty;
2026-06-23 11:32:47 +02:00
ReloadConversations(ListVM.VMList);
2025-05-14 10:05:15 +02:00
EndSending();
SendNewMessageSuccess?.Invoke(this, EventArgs.Empty);
}
private void ReloadConfig()
{
2026-02-09 13:19:57 +01:00
var config = ServiceFacade.DoAiEnhancedServiceSnyc(x => x.GetAiConfig());
2026-03-16 14:08:38 +01:00
if (Config is null)
Config = new AiConfigVM(config);
else
Config.Update(config);
2026-02-09 13:19:57 +01:00
//ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.GetAiConfig(), (x) =>
//{
// if (Config is null)
// Config = new AiConfigVM(x);
// else
// Config.Update(x);
//});
2025-05-14 10:05:15 +02:00
}
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()
{
2026-05-26 12:30:29 +02:00
var ui_context = ActionType;
2026-05-27 15:34:53 +02:00
var reference = ViewContext.Primary;
2025-05-14 10:05:15 +02:00
2026-02-09 13:19:57 +01:00
var list = VMFactory.CreateAiConversationListVM(ui_context, reference);
2026-06-23 11:32:47 +02:00
ReloadConversations(list.VMList);
}
private void ReloadConversations(IEnumerable<AiConversationVM> aiConversations)
{
if(aiConversations is null)
{
throw new ArgumentOutOfRangeException(nameof(aiConversations));
}
var sorted = aiConversations.OrderByDescending(vm => vm.Updated).ToList();
var t0 = ListVM.SelectedVM;
ListVM.VMList.UpdateBy(sorted);
2026-06-23 11:34:10 +02:00
if(ListVM.VMList.Contains(t0))
ListVM.SelectedVM = t0;
2026-06-23 11:32:47 +02:00
ListVM.VMList.ResetBindings();
2025-05-14 10:05:15 +02:00
}
private void OpenSetting()
{
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.GetAiModels(), (x) =>
{
Models.MakeEqualTo(x);
IsSettingsOpen = true;
});
}
2025-07-08 16:30:58 +02:00
private void Rename()
{
2026-03-16 14:08:38 +01:00
if (ListVM.SelectedVM is AiConversationVM vm)
2025-07-08 16:30:58 +02:00
{
vm.IsEditing = !vm.IsEditing;
}
}
2025-05-14 10:05:15 +02:00
private void OpenClone()
{
foreach (var message in ListVM.SelectedVM.Messages.VMList)
{
2025-07-09 15:30:54 +02:00
message.SetIsChecked(true);
2025-05-14 10:05:15 +02:00
}
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;
2025-07-03 14:39:22 +02:00
var info = selected.Displayname;
2025-05-14 10:05:15 +02:00
2025-07-03 14:39:22 +02:00
var oid = selected.DataContract.Oid;
2025-08-20 12:10:53 +02:00
if (BeWoAppInfo.IsInAiDeveloperMode)
2025-07-03 14:39:22 +02:00
{
info += $":{oid}";
}
var msg = $"Wollen Sie die ausgewählte Konversation ({info}) löschen?";
2025-05-14 10:05:15 +02:00
if (MessageBox.Show(msg, "Konversation löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning) ==
MessageBoxResult.Yes)
{
2026-03-16 14:08:38 +01:00
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.DeleteAiConversation(oid.Value), () =>
{
2025-05-14 10:05:15 +02:00
ListVM.VMList.Remove(selected);
CommandManager.InvalidateRequerySuggested();
//SelectedVM = VMList.FirstOrDefault();
});
}
}
2026-03-16 14:08:38 +01:00
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()
{
2026-05-12 11:47:04 +02:00
ServiceFacade.DoAiEnhancedServiceAsnyc(x => x.InsertAiPromptbausteinPrompt(SavePromptVM.CommitToDataContract()), cb => SavePromptVM = null, exc => SavePromptVM = null);
2026-03-16 14:08:38 +01:00
}
public void CancelSavePrompt()
{
SavePromptVM = null;
}
2026-05-29 12:26:37 +02:00
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);
}
2026-06-12 15:44:39 +02:00
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 =>
{
2026-06-23 11:32:47 +02:00
vm.Updated = DateTime.Now;
2026-06-12 15:44:39 +02:00
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)
{
2026-06-23 11:32:47 +02:00
var conv = ListVM.SelectedVM;
2026-06-12 15:44:39 +02:00
if (step >= routine.Steps.Count)
{
2026-06-23 11:32:47 +02:00
conv.Updated = DateTime.Now;
2026-06-12 15:44:39 +02:00
FinishSending();
ServiceFacade.EndManuelWaiting();
return;
}
2026-06-15 12:06:03 +02:00
var custom_viewContext = new AiViewContextDC
{
References = ViewContext.References,
Primary = ViewContext.Primary
};
2026-06-12 15:44:39 +02:00
2026-06-15 12:06:03 +02:00
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
};
2026-06-12 15:44:39 +02:00
var msg_vm = new AiConversationMessageVM(msg_dc);
conv.Messages.VMList.Add(msg_vm);
ServiceFacade.DoAiEnhancedServiceAsnyc(
2026-06-15 12:06:03 +02:00
x => x.ExecuteAiConversationWithRoutine(conv.Oid, custom_viewContext, routine.DataContract.Oid.Value, step),
2026-06-12 15:44:39 +02:00
cb =>
{
conv.Messages.VMList.Remove(msg_vm);
conv.AddMessages(cb);
ExecuteChain(routine, step + 1, error);
}, error);
}
2025-05-14 10:05:15 +02:00
}
}