From b724ecae4e1ced987909a1852a0ff666a70a6bbf Mon Sep 17 00:00:00 2001 From: Rene Evertz Date: Thu, 19 Feb 2026 14:32:37 +0100 Subject: [PATCH] customerid + complexes ai chat control --- AiCoreUnitTest/OllamaFacadeTest.cs | 4 +- BeWo/BeWo.csproj | 7 ++ BeWo/BeWoApp.xaml.cs | 2 +- BeWo/BeWoAppInfo.cs | 2 +- .../Controls/AI/AiConversationButton.xaml | 56 +++++++++ .../Controls/AI/AiConversationButton.xaml.cs | 115 ++++++++++++++++++ .../Zeiterfassung/ServiceRecordView2.xaml | 40 +----- .../Zeiterfassung/ServiceRecordView2.xaml.cs | 25 +++- .../ListViewModel/ServiceRecordListVM.cs | 22 +++- BeWo/ViewModel/VMFactory.cs | 4 +- .../View/AI/AiConversationChatViewModel.cs | 4 +- Data/Security/UserRightHelper.cs | 5 + .../AICore/Facade/LLM/OllamaApiClient.cs | 9 +- Service/Plugins/AiFunctionService.cs | 4 +- Service/Plugins/AiService2.cs | 3 +- .../Enhanced/AiEnhancedServiceImp.cs | 2 + 16 files changed, 248 insertions(+), 56 deletions(-) create mode 100644 BeWo/View/Controls/AI/AiConversationButton.xaml create mode 100644 BeWo/View/Controls/AI/AiConversationButton.xaml.cs diff --git a/AiCoreUnitTest/OllamaFacadeTest.cs b/AiCoreUnitTest/OllamaFacadeTest.cs index 695a7b0ba..7948d1b22 100644 --- a/AiCoreUnitTest/OllamaFacadeTest.cs +++ b/AiCoreUnitTest/OllamaFacadeTest.cs @@ -1,4 +1,5 @@ using AICore.Facade.LLM; +using BeWo.Data.Security; using BeWo.Service.Core; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; @@ -48,7 +49,8 @@ namespace AiCoreUnitTest role = "user", message = "Say 'Hi'!" } - } + }, + customerid = UserRightHelper.GetTenant() }; var logger = ServiceLogger.GetRequestLogger(); diff --git a/BeWo/BeWo.csproj b/BeWo/BeWo.csproj index b68a34656..8d80999e0 100644 --- a/BeWo/BeWo.csproj +++ b/BeWo/BeWo.csproj @@ -142,6 +142,9 @@ + + AiConversationButton.xaml + AiPromptbausteinActionResultView.xaml @@ -207,6 +210,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/BeWo/BeWoApp.xaml.cs b/BeWo/BeWoApp.xaml.cs index 095fb9697..7f42ca99d 100644 --- a/BeWo/BeWoApp.xaml.cs +++ b/BeWo/BeWoApp.xaml.cs @@ -56,7 +56,7 @@ namespace BeWo public static LoginControl LoginControl; public static MainControl MainControl; - public static string ShortVersion = "3.28"; + public static string ShortVersion = "3.29"; public static string Version => BeWoAppInfo.BeWoAppVersion.ToString(); public static int RenderTier = 0; diff --git a/BeWo/BeWoAppInfo.cs b/BeWo/BeWoAppInfo.cs index 8524b1961..9497e2a38 100644 --- a/BeWo/BeWoAppInfo.cs +++ b/BeWo/BeWoAppInfo.cs @@ -15,7 +15,7 @@ namespace BeWo { var baseVersion = new Version(BeWoApp.ShortVersion); var stage = BeWoClientReleaseStage.Sandbox; - var stageVersion = new Version("2.2"); + var stageVersion = new Version("3.0"); var feature = BeWoClientFeature.AI; BeWoAppVersion = new BeWoAppVersion(baseVersion, stage, stageVersion, feature); diff --git a/BeWo/View/Controls/AI/AiConversationButton.xaml b/BeWo/View/Controls/AI/AiConversationButton.xaml new file mode 100644 index 000000000..53c70d0d5 --- /dev/null +++ b/BeWo/View/Controls/AI/AiConversationButton.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + Die aktuelle Ansicht beinhaltet zu viele Daten. + Es können keine neuen Chats zu dieser Ansicht erstellt werden. + + + + + diff --git a/BeWo/View/Controls/AI/AiConversationButton.xaml.cs b/BeWo/View/Controls/AI/AiConversationButton.xaml.cs new file mode 100644 index 000000000..e8b0cbb10 --- /dev/null +++ b/BeWo/View/Controls/AI/AiConversationButton.xaml.cs @@ -0,0 +1,115 @@ +using BeWo.ViewModel.View.AI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace BeWo.View.Controls.AI +{ + public enum AiConversationButtonState + { + Hidden, + Normal, + Loading, + Warning, + Disabled + } + + /// + /// Interaktionslogik für AIConversationButton.xaml + /// + public partial class AiConversationButton : UserControl + { + public event RoutedEventHandler OpenAiWindowClick; + public event RoutedEventHandler OpenAiPromptClick; + + public AiConversationButton() + { + InitializeComponent(); + + UpdateState(); + } + + public AiConversationButtonState State + { + get { return (AiConversationButtonState)GetValue(StateProperty); } + set { SetValue(StateProperty, value); } + } + + public static readonly DependencyProperty StateProperty = + DependencyProperty.Register("State", typeof(AiConversationButtonState), typeof(AiConversationButton), new PropertyMetadata(AiConversationButtonState.Normal, OnStateChanged)); + + private static void OnStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var control = (AiConversationButton)d; + control.UpdateState(); + } + + public string Tooltip { get; set; } + public bool IsButtonEnabled { get; set; } + public bool IsButtonVisible { get; set; } + public bool IsWarningSignVisible { get; set; } + + private void UpdateState() + { + switch (State) + { + case AiConversationButtonState.Hidden: + Tooltip = string.Empty; + IsButtonEnabled = false; + IsButtonVisible = false; + IsWarningSignVisible = false; + break; + case AiConversationButtonState.Normal: + Tooltip = string.Empty; + IsButtonEnabled = true; + IsButtonVisible = true; + IsWarningSignVisible = false; + break; + case AiConversationButtonState.Loading: + Tooltip = "Lädt"; + IsButtonEnabled = false; + IsButtonVisible = true; + IsWarningSignVisible = false; + break; + case AiConversationButtonState.Warning: + Tooltip = string.Empty; + IsButtonEnabled = true; + IsButtonVisible = true; + IsWarningSignVisible = true; + break; + case AiConversationButtonState.Disabled: + Tooltip = string.Empty; + IsButtonEnabled = false; + IsButtonVisible = true; + IsWarningSignVisible = false; + break; + } + + btnOpenAi5.ToolTip = btnOpenAi4.ToolTip = ToolTip; + btnOpenAi5.IsEnabled = btnOpenAi4.IsEnabled = IsButtonEnabled; + btnOpenAi5.Visibility = btnOpenAi4.Visibility = IsButtonVisible ? Visibility.Visible : Visibility.Collapsed; + imgWarning.Visibility = IsWarningSignVisible ? Visibility.Visible : Visibility.Collapsed; + } + + private void btnOpenAi4_Click(object sender, RoutedEventArgs e) + { + OpenAiWindowClick?.Invoke(this, e); + } + + private void btnOpenAi5_Click(object sender, RoutedEventArgs e) + { + OpenAiPromptClick?.Invoke(this, e); + } + } +} diff --git a/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml b/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml index c2e77629f..d87ff59bf 100644 --- a/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml +++ b/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml @@ -2,6 +2,7 @@ x:Class="BeWo.View.Detail.Zeiterfassung.ServiceRecordView2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ai="clr-namespace:BeWo.View.Controls.AI" xmlns:core="clr-namespace:BS.Shared.Core;assembly=BS.Shared" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:detail="clr-namespace:BeWo.View.Detail" @@ -3314,41 +3315,10 @@ Margin="0" VerticalAlignment="Center" Orientation="Horizontal"> - - - - - - - - + AlleWohnheime { get; private set; } + public List AlleWohnheime { get; private set; } private GroupOfPeopleView _GroupOfPeopleModalPopUp; @@ -348,10 +348,9 @@ namespace BeWo.View.Detail.Zeiterfassung srGridControl.FilterChanged += SrGridControl_FilterChanged; srGridControl.ItemsSourceChanged += SrGridControl_ItemsSourceChanged; - Func canExecuteAi = () => supportConceptSelectionControl.SelectedItem.SupportConceptTreeNodeDC?.Customer is object; + Func canExecuteAi = () => ViewModel.IsAiButtonVisible; OpenAiWindowCommand = CommandFactory.GetAiViewCommand(OpenAiWindow, UserRightType.AiModuleView, canExecuteAi); OpenAiChatPromptWindowCommand = CommandFactory.GetAiViewCommand(OpenAiChatPromptWindow, UserRightType.AiModuleChatAdd, canExecuteAi); - } public ICommand OpenAiWindowCommand { get; set; } @@ -2131,6 +2130,8 @@ namespace BeWo.View.Detail.Zeiterfassung if (_AssessmentSheet != null) _AssessmentSheet.ServiceRecords = list; + + ViewModel.CalculateAiButtonState(); } //private void ColorLinesWithMarker(List list) @@ -5077,13 +5078,26 @@ namespace BeWo.View.Detail.Zeiterfassung ComboBoxTreeViewControl.TreeViewSourceList = ViewModel.PrototypeVM.GoalTree; } + + + private void AiConversationButton_OpenAiPromptClick(object sender, RoutedEventArgs e) + { + OpenAiChatPromptWindow(); + } + + private void AiConversationButton_OpenAiWindowClick(object sender, RoutedEventArgs e) + { + OpenAiWindow(); + } + private AiConversationChatViewModel getAiConversationChatViewModel() { var records = GetVisibleRecordVMs(); var context = ViewModel.GetVisibleInformationReferences(records); var oid = supportConceptSelectionControl.SelectedItem.SupportConceptTreeNodeDC.SupportConcept.SupportConceptOid; + var canSendNewMessage = true; - var vm = VMFactory.CreateAiConversationChatViewModel(AiContextType.ServiceRecord, context, oid); + var vm = VMFactory.CreateAiConversationChatViewModel(AiContextType.ServiceRecord, context, oid, canSendNewMessage); return vm; } @@ -5096,8 +5110,7 @@ namespace BeWo.View.Detail.Zeiterfassung private void OpenAiChatPromptWindow() { var vm_chat = new AiPromptbausteinSelectionComplexViewModel(AiActionType.ServiceRecordConversation); - //var vm_doku = new AiPromptbausteinSelectionViewModel(AiActionType.ServiceRecordDocumentation); - + vm_chat.PromptAccepted += PromptAccepted; MainControl.WindowService.Show(vm_chat); diff --git a/BeWo/ViewModel/ListViewModel/ServiceRecordListVM.cs b/BeWo/ViewModel/ListViewModel/ServiceRecordListVM.cs index ade1ede87..1c9f7a6f8 100644 --- a/BeWo/ViewModel/ListViewModel/ServiceRecordListVM.cs +++ b/BeWo/ViewModel/ListViewModel/ServiceRecordListVM.cs @@ -11,6 +11,7 @@ using System.Windows.Threading; using BeWo.Core; using BeWo.Core.Service; using BeWo.ServiceProxy; +using BeWo.View.Controls.AI; using BeWo.ViewModel.View.AI; using BS.Shared; using BS.Shared.Core; @@ -111,6 +112,8 @@ namespace BeWo.ViewModel.ListViewModel public ICommand UndoEditAiActionCommand { get; set; } public ICommand RedoEditAiActionCommand { get; set; } + public AiConversationButtonState AiConversationButtonState { get; set; } + public IList VisibleSerivceRecords { get; set; } public ServiceRecordListVM( @@ -179,15 +182,18 @@ namespace BeWo.ViewModel.ListViewModel } VMList.ListChanged += (s, e) => CheckCustomerAbsenceTimes(); + InitAiStuff(); + } + + private void InitAiStuff() + { var vm_doku = new AiPromptbausteinSelectionComplexViewModel(AiActionType.ServiceRecordDocumentation); - //var vm_doku = new AiPromptbausteinSelectionViewModel(AiActionType.ServiceRecordDocumentation); vm_doku.PromptAccepted += PromptAccepted; OpenAiPromptSelectionCommand = new DelegateCommand(() => { BeWoApp.MainControl.WindowService.Show(vm_doku); - //BeWoApp.MainControl.WindowService.ShowAiPromptbausteinSelectionWindow(vm_doku); }, () => !string.IsNullOrWhiteSpace(AktuellerDokutext)); UndoAiActionCommand = new DelegateCommand(UndoAiAction); @@ -211,6 +217,13 @@ namespace BeWo.ViewModel.ListViewModel _AiActionHistory = new List(); _AiActionHistory.Add(AktuellerDokutext); _AiActionHistoryIndex = 0; + + UpdateViewYearFilter(); + } + + public void CalculateAiButtonState() + { + } public IEnumerable CurrentVisibleVMs { get; set; } @@ -456,6 +469,11 @@ namespace BeWo.ViewModel.ListViewModel ); } + public void UpdateViewYearFilter() + { + + } + public Dictionary GetVisibleInformationReferences(IEnumerable selected_records) { var current = SelectedCustomerNode; diff --git a/BeWo/ViewModel/VMFactory.cs b/BeWo/ViewModel/VMFactory.cs index 216941f0a..05fcc7955 100644 --- a/BeWo/ViewModel/VMFactory.cs +++ b/BeWo/ViewModel/VMFactory.cs @@ -912,13 +912,15 @@ namespace BeWo.ViewModel einheit_vm_list.VMList.Sort(WohneinheitVM.Compare); } - public static AiConversationChatViewModel CreateAiConversationChatViewModel(AiContextType uIContext, Dictionary bewoObjects, long? reference_oid = null) + public static AiConversationChatViewModel CreateAiConversationChatViewModel(AiContextType uIContext, Dictionary bewoObjects, + long? reference_oid = null, bool canSendNewMessage = true) { var vm = new AiConversationChatViewModel(); vm.AiContext = uIContext; vm.ContextBeWoObjects = bewoObjects; vm.ReferenceObjectOid = reference_oid; + vm.CanSendNewMessage = canSendNewMessage; return vm; } diff --git a/BeWo/ViewModel/View/AI/AiConversationChatViewModel.cs b/BeWo/ViewModel/View/AI/AiConversationChatViewModel.cs index 4c4a571b7..f9457e1c0 100644 --- a/BeWo/ViewModel/View/AI/AiConversationChatViewModel.cs +++ b/BeWo/ViewModel/View/AI/AiConversationChatViewModel.cs @@ -68,11 +68,11 @@ namespace BeWo.ViewModel.View.AI public ICommand OpenCloneCommand { get; set; } public ICommand CloneCommand { get; set; } + public bool CanSendNewMessage { get; set; } public bool SendAfterLoaded { get; set; } - public AiConversationListVM ListVM { get; set; } - public ObservableCollection Models { get; set; } + public AiModelDC SelectedModel { get { return _SelectedModel; } diff --git a/Data/Security/UserRightHelper.cs b/Data/Security/UserRightHelper.cs index db2570a20..bad4e748e 100644 --- a/Data/Security/UserRightHelper.cs +++ b/Data/Security/UserRightHelper.cs @@ -14,6 +14,11 @@ namespace BeWo.Data.Security { public static readonly string BASE_VERSION = "1.0"; + public static string GetTenant() + { + return MultitenancyOperationContextExt.Current?.Tenant; + } + public static Employee GetLoggedInEmployee() { if (LoggedInUserOperationContextExt.Current != null) diff --git a/Server/Components/AICore/Facade/LLM/OllamaApiClient.cs b/Server/Components/AICore/Facade/LLM/OllamaApiClient.cs index c4a292881..8af1d83c8 100644 --- a/Server/Components/AICore/Facade/LLM/OllamaApiClient.cs +++ b/Server/Components/AICore/Facade/LLM/OllamaApiClient.cs @@ -23,7 +23,6 @@ namespace AICore.Facade.LLM if (modelSource != AiModelSource.ollama && modelSource != AiModelSource.ollama2) throw new NotImplementedException("err4548975"); - } public override ApiResponse> GetAiModelle() @@ -69,8 +68,8 @@ namespace AICore.Facade.LLM if(logger is object) { - logger.Log(LogLevel.Info, "Request:"); - logger.LogJson(LogLevel.Info, _PostClientFacade.JsonObject); + logger.Log(LogLevel.Debug, "Request:"); + logger.LogJson(LogLevel.Debug, _PostClientFacade.JsonObject); } var response_format = new @@ -96,8 +95,8 @@ namespace AICore.Facade.LLM if(logger is object) { - logger.Log(LogLevel.Info, "Response:"); - logger.LogJson(LogLevel.Info, response); + logger.Log(LogLevel.Debug, "Response:"); + logger.LogJson(LogLevel.Debug, response); } if (!response.Success) diff --git a/Service/Plugins/AiFunctionService.cs b/Service/Plugins/AiFunctionService.cs index 553ebe13a..3f69cbb48 100644 --- a/Service/Plugins/AiFunctionService.cs +++ b/Service/Plugins/AiFunctionService.cs @@ -20,6 +20,7 @@ using System.IO; using BS.Shared.Interface; using System.Text.Json; using BS.Shared.Core; +using BeWo.Data.Security; namespace BeWo.Service.Plugins { @@ -102,7 +103,8 @@ namespace BeWo.Service.Plugins role = "User", content = user_prompt }, - } + }, + customerid = UserRightHelper.GetTenant() }; var logger = ServiceLogger.GetRequestLogger(); diff --git a/Service/Plugins/AiService2.cs b/Service/Plugins/AiService2.cs index 96056cb5c..b26869fad 100644 --- a/Service/Plugins/AiService2.cs +++ b/Service/Plugins/AiService2.cs @@ -313,7 +313,8 @@ namespace BeWo.Service.Plugins var payload = new { model = configModelName, - messages = conv.Messages.Select(m => new { role = m.Role.ToString().ToLower(), content = m.Message }) + messages = conv.Messages.Select(m => new { role = m.Role.ToString().ToLower(), content = m.Message }), + customerid = UserRightHelper.GetTenant() }; // Updated diff --git a/Service/ServiceImplementations/Enhanced/AiEnhancedServiceImp.cs b/Service/ServiceImplementations/Enhanced/AiEnhancedServiceImp.cs index f10b78dfc..2642333e7 100644 --- a/Service/ServiceImplementations/Enhanced/AiEnhancedServiceImp.cs +++ b/Service/ServiceImplementations/Enhanced/AiEnhancedServiceImp.cs @@ -45,6 +45,8 @@ namespace BeWo.Service.ServiceImplementations.Enhanced public AiConversationMessageDC[] SendNewMessage(long conversation, string message) => GetAiService().SendNewMessage(conversation, message); + //public bool HasContextSize() + private AiService2 GetAiService() { var plugin = PluginLoader.FindClass();