customerid + complexes ai chat control

This commit is contained in:
2026-02-19 14:32:37 +01:00
parent eb16a396ac
commit b724ecae4e
16 changed files with 248 additions and 56 deletions

View File

@@ -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();

View File

@@ -142,6 +142,9 @@
<Compile Include="ViewModel\View\DialogViewModel.cs" />
<Compile Include="ViewModel\View\Gkv\GkvAbrechnungDialogViewModel.cs" />
<Compile Include="ViewModel\View\WindowViewModel.cs" />
<Compile Include="View\Controls\AI\AiConversationButton.xaml.cs">
<DependentUpon>AiConversationButton.xaml</DependentUpon>
</Compile>
<Compile Include="View\Detail\AI\AiPromptbausteinActionResultView.xaml.cs">
<DependentUpon>AiPromptbausteinActionResultView.xaml</DependentUpon>
</Compile>
@@ -207,6 +210,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\Controls\AI\AiConversationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\Detail\AI\AiPromptbausteinActionResultView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@@ -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;

View File

@@ -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);

View File

@@ -0,0 +1,56 @@
<UserControl
x:Class="BeWo.View.Controls.AI.AiConversationButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:draw="clr-namespace:System.Drawing;assembly=System.Drawing"
xmlns:local="clr-namespace:BeWo.View.Controls.AI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<StackPanel
Margin="0"
VerticalAlignment="Center"
Orientation="Horizontal">
<StackPanel.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\..\..\Styles\ModernOrangeBlack.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</StackPanel.Resources>
<Button
x:Name="btnOpenAi4"
Height="30"
Margin="10,0,0,0"
Click="btnOpenAi4_Click"
Content="{StaticResource AiDesignsIconKIChatWeissOrange}"
Style="{StaticResource NavigationToolbarButtonStyle}"
ToolTip="KI Chat öffnen"
ToolTipService.ShowOnDisabled="True" />
<Button
x:Name="btnOpenAi5"
Height="30"
Margin="10,0,0,0"
Click="btnOpenAi5_Click"
Style="{StaticResource NavigationToolbarButtonStyle}"
ToolTip="KI Chat mit Promptbaustein starten"
ToolTipService.ShowOnDisabled="True">
Prompt mit KI Chat
</Button>
<Image
x:Name="imgWarning"
Height="20"
Margin="10,0,0,0"
Source="{Binding Source={x:Static draw:SystemIcons.Warning}, Converter={StaticResource IconToImageSourceConverter}, Mode=OneWay}"
Visibility="Visible">
<!-- Visibility="{Binding ElementName=btnOpenAi4, Path=Visibility}" -->
<Image.ToolTip>
Die aktuelle Ansicht beinhaltet zu viele Daten.
Es können keine neuen Chats zu dieser Ansicht erstellt werden.
</Image.ToolTip>
</Image>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -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
}
/// <summary>
/// Interaktionslogik für AIConversationButton.xaml
/// </summary>
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);
}
}
}

View File

@@ -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">
<StackPanel.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\..\..\Styles\ModernOrangeBlack.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</StackPanel.Resources>
<Button
x:Name="btnOpenAi4"
Height="30"
Margin="10,0,0,0"
Command="{Binding Path=OpenAiWindowCommand, RelativeSource={RelativeSource AncestorType={x:Type localView:BeWoView}}}"
Content="{StaticResource AiDesignsIconKIChatWeissOrange}"
Style="{StaticResource NavigationToolbarButtonStyle}"
ToolTip="KI Chat öffnen" />
<Image
Height="20"
Source="{Binding Source={x:Static draw:SystemIcons.Warning}, Converter={StaticResource IconToImageSourceConverter}, Mode=OneWay}"
Visibility="Collapsed">
<!-- Visibility="{Binding ElementName=btnOpenAi4, Path=Visibility}" -->
<Image.ToolTip>
Die aktuelle Ansicht beinhaltet zu viele Daten.
Es können keine neuen Chats zu dieser Ansicht erstellt werden.
</Image.ToolTip>
</Image>
<Button
x:Name="btnOpenAi5"
Height="30"
Margin="10,0,0,0"
Command="{Binding Path=OpenAiChatPromptWindowCommand, RelativeSource={RelativeSource AncestorType={x:Type localView:BeWoView}}}"
Style="{StaticResource NavigationToolbarButtonStyle}"
ToolTip="KI Chat mit Promptbaustein starten">
Prompt mit KI Chat
</Button>
<ai:AiConversationButton
OpenAiPromptClick="AiConversationButton_OpenAiPromptClick"
OpenAiWindowClick="AiConversationButton_OpenAiWindowClick"
State="Normal" />
</StackPanel>
<StackPanel
Grid.Column="2"

View File

@@ -73,7 +73,7 @@ namespace BeWo.View.Detail.Zeiterfassung
private TextBox _GrosseDokuTextBox;
public List<CompactWohnheimDC> AlleWohnheime { get; private set; }
public List<CompactWohnheimDC> 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<bool> canExecuteAi = () => supportConceptSelectionControl.SelectedItem.SupportConceptTreeNodeDC?.Customer is object;
Func<bool> 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<ServiceRecordVM> 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);

View File

@@ -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<ServiceRecordVM> 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<string>();
_AiActionHistory.Add(AktuellerDokutext);
_AiActionHistoryIndex = 0;
UpdateViewYearFilter();
}
public void CalculateAiButtonState()
{
}
public IEnumerable<ServiceRecordVM> CurrentVisibleVMs { get; set; }
@@ -456,6 +469,11 @@ namespace BeWo.ViewModel.ListViewModel
);
}
public void UpdateViewYearFilter()
{
}
public Dictionary<TableID, long[]> GetVisibleInformationReferences(IEnumerable<ServiceRecordVM> selected_records)
{
var current = SelectedCustomerNode;

View File

@@ -912,13 +912,15 @@ namespace BeWo.ViewModel
einheit_vm_list.VMList.Sort(WohneinheitVM.Compare);
}
public static AiConversationChatViewModel CreateAiConversationChatViewModel(AiContextType uIContext, Dictionary<TableID, long[]> bewoObjects, long? reference_oid = null)
public static AiConversationChatViewModel CreateAiConversationChatViewModel(AiContextType uIContext, Dictionary<TableID, long[]> 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;
}

View File

@@ -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<AiModelDC> Models { get; set; }
public AiModelDC SelectedModel
{
get { return _SelectedModel; }

View File

@@ -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)

View File

@@ -23,7 +23,6 @@ namespace AICore.Facade.LLM
if (modelSource != AiModelSource.ollama && modelSource != AiModelSource.ollama2)
throw new NotImplementedException("err4548975");
}
public override ApiResponse<IEnumerable<AiModelDC>> 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)

View File

@@ -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();

View File

@@ -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

View File

@@ -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<AiService2>();