Promptbausteine Ownsoft

This commit is contained in:
2026-01-16 13:33:59 +01:00
parent df4de8ba1e
commit b5eee2c495
20 changed files with 209 additions and 58 deletions

View File

@@ -319,33 +319,40 @@
Selected="Tabitem_Selected" />
</dxa:AccordionItem>
<dxa:AccordionItem Foreground="{StaticResource TabItemTextBrush}" Header="{markup:Translate AiTitle}">
<dxa:AccordionItem
<!--<dxa:AccordionItem
Name="TabitemAiPromptbausteine"
Foreground="Black"
Header="{markup:Translate AiPromptbausteine}"
IsEnabled="False"
Selected="Tabitem_Selected" />
<dxa:AccordionItem
Name="TabitemAiPromptbausteineTree"
Foreground="Black"
Header="{markup:Translate AiPromptbausteine2}"
Selected="Tabitem_Selected" />
IsEnabled="False"
Selected="Tabitem_Selected" />-->
<dxa:AccordionItem
Name="TabitemAiPromptbausteineComplexTree"
Name="TabitemAiPromptbausteineComplexTree3"
Foreground="Black"
Header="{markup:Translate AiPromptbausteine3}" >
Header="{markup:Translate Prompt}">
<dxa:AccordionItem
Name="TabitemAiPromptbausteineComplexTree"
Foreground="Black"
Header="{markup:Translate Zeiterfassung}">
<dxa:AccordionItem
Name="TabitemAiPromptbausteineComplexTree1"
Foreground="Black"
Header="{markup:Translate AiPromptbausteineServiceConversation}"
Selected="Tabitem_Selected" />
Name="TabitemAiPromptbausteineComplexTree1"
Foreground="Black"
Header="{markup:Translate Chat}"
Selected="Tabitem_Selected" />
<dxa:AccordionItem
Name="TabitemAiPromptbausteineComplexTree2"
Foreground="Black"
Header="{markup:Translate AiPromptbausteineServiceDocumentation}"
Selected="Tabitem_Selected" />
Name="TabitemAiPromptbausteineComplexTree2"
Foreground="Black"
Header="{markup:Translate Dokumentation}"
Selected="Tabitem_Selected" />
</dxa:AccordionItem>
</dxa:AccordionItem>
</dxa:AccordionItem>
<dxa:AccordionItem Foreground="{StaticResource TabItemTextBrush}" Header="Sonstiges">
<dxa:AccordionItem Foreground="{StaticResource TabItemTextBrush}" Header="Sonstiges">
<dxa:AccordionItem
Name="TabitemAbsenceCategories"
Foreground="Black"

View File

@@ -569,13 +569,13 @@ namespace BeWo.View.Master
case nameof(TabitemSozioTherapie):
CheckMandatorVM(() => AddView(TabitemSozioTherapie, new SoziotherapieSettingsView(this)));
return;
case nameof(TabitemAiPromptbausteine):
VMFactory.CreateAiPromptbausteinListVMAsync(
r => this.Dispatch(() => AddView(TabitemAiPromptbausteine, new AiPromptbausteinView(r))));
return;
case nameof(TabitemAiPromptbausteineTree):
AddView(TabitemAiPromptbausteineTree, new AiPromptbausteinTreeView());
return;
//case nameof(TabitemAiPromptbausteine):
// VMFactory.CreateAiPromptbausteinListVMAsync(
// r => this.Dispatch(() => AddView(TabitemAiPromptbausteine, new AiPromptbausteinView(r))));
// return;
//case nameof(TabitemAiPr omptbausteineTree):
// AddView(TabitemAiPromptbausteineTree, new AiPromptbausteinTreeView());
// return;
case nameof(TabitemAiPromptbausteineComplexTree1):
var vm11 = new AiPromptbausteinComplexTreeViewModel(BeWoApp.MainControl.WindowService, AiActionType.ServiceRecordConversation);
AddView(TabitemAiPromptbausteineComplexTree1, new AiPromptbausteinComplexTreeView(vm11));

View File

@@ -126,7 +126,7 @@ namespace BeWo.ViewModel
public AiPromptbausteinFolderVM Parent { get; set; }
public FontWeight FontWeight => !IsNew && ParentFolderOid is null ? FontWeights.SemiBold : FontWeights.Medium;
public FontWeight FontWeight => !IsNew && Parent is null ? FontWeights.SemiBold : FontWeights.Medium;
public bool HasSubFolders => SubFolders is object && SubFolders.Count > 0;
public string Displayname
{

View File

@@ -15,7 +15,11 @@ namespace BeWo.ViewModel.ListViewModel
foreach (var vm in VMList)
{
vm.Parent = parent;
vm.ParentFolderOid = parent?.DataContract?.Oid;
if(vm.DataContract.ParentFolderOid != null)
{
vm.ParentFolderOid = parent?.DataContract?.Oid;
}
}
}
@@ -26,6 +30,9 @@ namespace BeWo.ViewModel.ListViewModel
foreach (var vm in VMList)
{
if (vm.DataContract.Oid < -1)
return false;
if (vm.SubFolders is object && vm.SubFolders.ContainsDirtyObject())
return true;

View File

@@ -183,6 +183,9 @@ namespace BeWo.ViewModel.View.Administration
{
foreach (var node in folders.VMList)
{
if (node.DataContract.Oid is long oid && oid < -1)
continue;
if (node.IsNew)
{
toadd_Folder.Add(node);
@@ -203,11 +206,13 @@ namespace BeWo.ViewModel.View.Administration
}
}
}
void DFS_Search_Delete(AiPromptbausteinFolderListVM folders, ref List<AiPromptbausteinFolderDC> todelete_Folder, ref List<AiPromptbausteinPromptDC> todelete_Prompt)
{
foreach (var node_dc in folders.DCBackup)
{
if (node_dc.Oid is long oid && oid < -1)
continue;
if (folders.VMList.FirstOrDefault(vm => vm.DataContract == node_dc) is AiPromptbausteinFolderVM node_vm)
{
if (node_vm.SubFolders.DCBackup is object && node_vm.SubFolders.DCBackup.Count > 0)

View File

@@ -3,10 +3,11 @@ using System.Collections.Generic;
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.Interface;
namespace BeWo.Data.Entities
{
public class AiPromptbausteinPrompt : BeWoEntityBase
public class AiPromptbausteinPrompt : BeWoEntityBase, IAiPromptbausteinPrompt
{
public AiPromptbausteinPrompt()
{

View File

@@ -71,7 +71,7 @@ namespace AICore.Context.SummaryParser
sb.AppendLine("#### **Zeiterfassungseinträge**");
sb.AppendLine(ToCsvString(fcs.ServiceRecords));
sb.AppendLine();
sb.AppendLine("#### **Dokumentationfelder**");
sb.AppendLine("#### **Dokumentations(text)**");
sb.AppendLine(fcs.Dokutext);
sb.AppendLine();
return sb.ToString();

View File

@@ -1,12 +1,18 @@
using BS.Shared.Exceptions;
using BS.Shared.Interface.Feature.AICore;
using System.Collections.Generic;
using System.Linq;
namespace AICore.Prompt.Core
{
public static class AiUtils
{
public static void CheckTokenSize(IEnumerable<IAiConversationMessage> messages, string modelName, int max_context_size)
{
CheckTokenSize(messages.Select(x => x.Message), modelName, max_context_size);
}
public static void CheckTokenSize(IEnumerable<string> messages, string modelName, int max_context_size)
{
var buffer = 500;
@@ -19,7 +25,7 @@ namespace AICore.Prompt.Core
var calc_tokens = prev_tokens + buffer;
var percentage = calc_tokens * 100 / max_tokens;
var percentage = (double)calc_tokens * 100 / max_tokens;
if (calc_tokens > max_tokens)
{
@@ -28,17 +34,18 @@ namespace AICore.Prompt.Core
}
}
private static int CalculateTokensByModel(IEnumerable<IAiConversationMessage> messages, string modelName)
private static int CalculateTokensByModel(IEnumerable<string> messages, string modelName)
{
var count = 0;
foreach (var message in messages)
{
count += 4;
count += message.Message.Length / 2;
count += message.Length / 2;
}
return count;
}
}
}

View File

@@ -8,13 +8,15 @@ using System.Threading.Tasks;
namespace BeWo.Service.Core
{
public static class PathHelper
public static class BeWoServerPathHelper
{
/// <summary>
/// Holt den Ordner, wo sich die systemPrompts befinden
/// </summary>
public static string GetAiSystemPromptPath()
=> Path.Combine(getRoot(), "AI", "systemPrompts");
public static string GetAiOwnsoftPromptPath()
=> Path.Combine(getRoot(), "AI", "ownsoftPrompts");
public static string GetUniqueRequestLogFile()

View File

@@ -10,13 +10,13 @@ namespace BeWo.Service.Core
{
public static class ServiceLogger
{
public static FileLogger CoreLogger { get; } = new FileLogger(PathHelper.GetServiceLogFile());
public static FileLogger WCFErrorLogger { get; } = new FileLogger(PathHelper.GetWCFErrorLogFile());
public static FileLogger JsonErrorLogger { get; } = new FileLogger(PathHelper.GetJsonErrorLogFile());
public static FileLogger CoreLogger { get; } = new FileLogger(BeWoServerPathHelper.GetServiceLogFile());
public static FileLogger WCFErrorLogger { get; } = new FileLogger(BeWoServerPathHelper.GetWCFErrorLogFile());
public static FileLogger JsonErrorLogger { get; } = new FileLogger(BeWoServerPathHelper.GetJsonErrorLogFile());
public static FileLogger GetRequestLogger()
{
var filePath = PathHelper.GetUniqueRequestLogFile();
var filePath = BeWoServerPathHelper.GetUniqueRequestLogFile();
if (filePath == null)
throw new NotImplementedException("err98462");

View File

@@ -7,7 +7,6 @@ using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Feature.AI;
using BS.Shared.DataContracts.Feature.AI.Functions.ServiceRecords;
using DevExpress.DataAccess.Native.Web;
using Newtonsoft.Json;
using RestSharp.Validation;
using System;
using System.Collections.Generic;
@@ -17,6 +16,11 @@ using System.Threading.Tasks;
using AICore.Prompt.SystemPrompts;
using BeWo.Service.Core;
using BS.Shared.Extensions.CustomInterfaces;
using AICore.Prompt.Core;
using DevExpress.XtraCharts.Native;
using System.IO;
using BS.Shared.Interface;
using System.Text.Json;
namespace BeWo.Service.Plugins
{
@@ -33,7 +37,7 @@ namespace BeWo.Service.Plugins
// Anfrage zusammensetzen
var context = AiFunctionContextLoader.LoadServiceRecordDocumentationContext(request);
var systemPromptFolderPath = PathHelper.GetAiSystemPromptPath();
var systemPromptFolderPath = BeWoServerPathHelper.GetAiSystemPromptPath();
var factory = new AiFunctionSystemPromptBuilder(systemPromptFolderPath);
var system_prompt = factory.CreateServiceRecordDocumentationSystemPrompt(context);
@@ -41,17 +45,16 @@ namespace BeWo.Service.Plugins
var prompt_baustein_ref_oid = prompt_baustein_ref.Oid;
var prompt_baustein_ref_ver = prompt_baustein_ref.Version;
var prompt_baustein = DAOFactory.GenericDAO.LoadByID<AiPromptbausteinPrompt>(prompt_baustein_ref_oid);
ServiceLogic.ConcurrencyCheck(prompt_baustein_ref_ver, prompt_baustein);
//if (prompt_baustein.IsOrdner())
// throw new Exception("prompt is folder");
//if (prompt_baustein.IsKette())
// throw new NotImplementedException();
if (prompt_baustein.Oid < 0)
throw new NotImplementedException();
IAiPromptbausteinPrompt prompt_baustein = null;
if(prompt_baustein_ref_oid > 0)
{
prompt_baustein = DAOFactory.GenericDAO.LoadByID<AiPromptbausteinPrompt>(prompt_baustein_ref_oid);
ServiceLogic.ConcurrencyCheck(prompt_baustein_ref_ver, (AiPromptbausteinPrompt)prompt_baustein);
}
else
{
prompt_baustein = findOwnsoftPrompt(AiActionType.ServiceRecordDocumentation, prompt_baustein_ref.Oid);
}
// Anfrage abschicken
var result = getResult(system_prompt, prompt_baustein.Prompt);
@@ -65,8 +68,26 @@ namespace BeWo.Service.Plugins
return response;
}
private string getResult(string system, string user_prompt)
{
var models = ServiceFacade.Ollama2ApiClient.GetAiModelle();
var model = models.Data.FirstOrDefault(x => x.ModelName == "gemma3_12b_max_context:latest");
if (model == null)
throw new NotImplementedException();
var messages = new List<string>()
{
system, user_prompt
};
if(model.Value > -1)
{
AiUtils.CheckTokenSize(messages, model.ModelName, model.Value);
}
var payload = new
{
model = "gemma3_12b_max_context:latest",
@@ -86,7 +107,7 @@ namespace BeWo.Service.Plugins
};
var logger = ServiceLogger.GetRequestLogger();
var response = ServiceFacade.Ollama2ApiClient.SendAiMessageRequest(payload, out string message, out string model, out int? duration, logger);
var response = ServiceFacade.Ollama2ApiClient.SendAiMessageRequest(payload, out string message, out string model2, out int? duration, logger);
return message;
}
@@ -95,5 +116,48 @@ namespace BeWo.Service.Plugins
{
//throw new NotImplementedException();
}
private AiPromptbausteinPromptDC findOwnsoftPrompt(AiActionType aiAction, long prompt_oid)
{
var root = getOwnsoftFolder(aiAction);
return findOwnsoftPrompt_rec(root, prompt_oid);
}
private AiPromptbausteinPromptDC findOwnsoftPrompt_rec(AiPromptbausteinFolderDC dc, long prompt_oid)
{
foreach (var subprompts in dc.SubPrompts)
{
if (subprompts.Oid == prompt_oid)
return subprompts;
}
foreach (var subfolder in dc.SubFolders)
{
var search = findOwnsoftPrompt_rec(subfolder, prompt_oid);
if (search != null)
return search;
}
return null;
}
private AiPromptbausteinFolderDC getOwnsoftFolder(AiActionType aiActionType)
{
var folder_path = BeWoServerPathHelper.GetAiOwnsoftPromptPath();
var file_name = $"{aiActionType.ToString().ToLower()}.json";
var file_path = Path.Combine(folder_path, file_name);
if (!File.Exists(file_path))
return null;
var json = File.ReadAllText(file_path, Encoding.UTF8);
var ownsoft_folder = JsonSerializer.Deserialize<AiPromptbausteinFolderDC>(json);
return ownsoft_folder;
}
}
}

View File

@@ -9,8 +9,12 @@ using BS.Shared.DataContracts.Feature.AI;
using BS.Shared.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.Json;
using System.Xml;
namespace BeWo.Service.Plugins
{
@@ -144,13 +148,12 @@ namespace BeWo.Service.Plugins
aiPromptbausteinListe.Add(aiPromptbaustein);
}
public IEnumerable<AiPromptbausteinFolderDC> GetAiPromptbausteinFolder(AiActionType aiActionType)
{
var own_bib = PredefinedAiPromptbausteinFolders.CreateOwnBibNode(aiActionType);
var shared_bib = PredefinedAiPromptbausteinFolders.CreateSharedBibNode(aiActionType);
var public_bib = PredefinedAiPromptbausteinFolders.CreatePublicBibNode(aiActionType);
var ownsoft_bib = GetOwnsoftFolder(aiActionType);
var own = DAOFactory.SearchDAO.GetOwnActiveRootAiPromptbausteineFolders(aiActionType);
var own_dcs = MapperFactory.AiPromptbausteinFolder.MapToNewDCs(own);
@@ -163,9 +166,14 @@ namespace BeWo.Service.Plugins
own_bib.SubFolders.AddRange(own_dcs);
return new List<AiPromptbausteinFolderDC>() {
var result = new List<AiPromptbausteinFolderDC>() {
own_bib, shared_bib, public_bib
};
if(ownsoft_bib is object)
result.Add(ownsoft_bib);
return result;
}
private void initOwnFolder(AiPromptbausteinFolderDC folder)
{
@@ -249,5 +257,21 @@ namespace BeWo.Service.Plugins
DAOFactory.GenericDAO.SetActivationType(originale, BS.Shared.ActivationTypeId.Deleted);
}
private AiPromptbausteinFolderDC GetOwnsoftFolder(AiActionType aiActionType)
{
var folder_path = BeWoServerPathHelper.GetAiOwnsoftPromptPath();
var file_name = $"{aiActionType.ToString().ToLower()}.json";
var file_path = Path.Combine(folder_path, file_name);
if (!File.Exists(file_path))
return null;
var json = File.ReadAllText(file_path, Encoding.UTF8);
var ownsoft_folder = JsonSerializer.Deserialize<AiPromptbausteinFolderDC>(json);
return ownsoft_folder;
}
}
}

View File

@@ -272,7 +272,7 @@ namespace BeWo.Service.Plugins
{
var current_user = UserRightHelper.GetLoggedInUserWithOid();
var systemPromptFolderPath = PathHelper.GetAiSystemPromptPath();
var systemPromptFolderPath = BeWoServerPathHelper.GetAiSystemPromptPath();
var factory = new AiConversationSystemPromptBuilder(systemPromptFolderPath);
var context_loaded = AiContextLoader.LoadContext(uicontext, context_reference);

View File

@@ -230,7 +230,7 @@
<Compile Include="Core\ExcelDownload.cs" />
<Compile Include="Core\FileAttachmentUtils.cs" />
<Compile Include="Core\MergedConfig.cs" />
<Compile Include="Core\PathHelper.cs" />
<Compile Include="Core\BeWoServerPathHelper.cs" />
<Compile Include="Core\ServiceLogger.cs" />
<Compile Include="Core\ServiceLogic.cs" />
<Compile Include="Core\SettingsLogic.cs" />

View File

@@ -77,7 +77,7 @@ namespace BeWo.Service.ServiceImplementations
};
var str = JsonConvert.SerializeObject(log, Formatting.Indented);
File.WriteAllText(PathHelper.GetApiServiceTestFile(), str, Encoding.UTF8);
File.WriteAllText(BeWoServerPathHelper.GetApiServiceTestFile(), str, Encoding.UTF8);
return ApiResponse<string>.SuccessResponse("Hello World!");
}

View File

@@ -99,13 +99,30 @@ namespace BS.Shared.DataContracts
};
}
private static AiPromptbausteinFolderDC CreateExampleNode(AiActionType aiActionType)
public static AiPromptbausteinFolderDC CreateOwnsoftBibNode(AiActionType aiActionType)
{
return new AiPromptbausteinFolderDC()
{
ActionType = aiActionType,
Description = AiPromptbausteineTexts.OwnsoftBibNodeDescription,
Oid = -4,
ParentFolderOid = null,
SubFolders = new List<AiPromptbausteinFolderDC>(),
SubPrompts = new List<AiPromptbausteinPromptDC>(),
Title = AiPromptbausteineTexts.OwnsoftBibNodeDescription,
Version = 1,
IsExpanded = true,
IsNewFeature = false
};
}
public static AiPromptbausteinFolderDC CreateExampleNode(AiActionType aiActionType)
{
return new AiPromptbausteinFolderDC()
{
ActionType = aiActionType,
Description = "Beispielbeschreibung",
Oid = -4,
Oid = -100,
ParentFolderOid = null,
SubFolders = new List<AiPromptbausteinFolderDC>(),
SubPrompts = new List<AiPromptbausteinPromptDC>(),
@@ -117,11 +134,11 @@ namespace BS.Shared.DataContracts
};
}
private static AiPromptbausteinPromptDC CreateExamplePrompt(AiActionType aiActionType)
public static AiPromptbausteinPromptDC CreateExamplePrompt(AiActionType aiActionType)
{
return new AiPromptbausteinPromptDC()
{
Oid = -5,
Oid = -101,
Description = "Beispielbeschreibung",
Title = "Beispieltitel",
Prompt = "Beispielprompt",

View File

@@ -1,11 +1,12 @@
using BS.Shared.DataContracts.Compact;
using BS.Shared.Interface;
using System;
using System.Runtime.Serialization;
namespace BS.Shared.DataContracts
{
[DataContract]
public class AiPromptbausteinPromptDC : BeWoDataContract
public class AiPromptbausteinPromptDC : BeWoDataContract, IAiPromptbausteinPrompt
{
[DataMember] public string Title { get; set; }
[DataMember] public string Description { get; set; }

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BS.Shared.Interface
{
public interface IAiPromptbausteinPrompt
{
string Prompt { get; set; }
}
}

View File

@@ -422,6 +422,7 @@
<Compile Include="Interface\Abstract\IAddressable.cs" />
<Compile Include="Interface\Feature\AICore\IAiConversationMessage.cs" />
<Compile Include="Interface\IAiPromptbaustein.cs" />
<Compile Include="Interface\IAiPromptbausteinPrompt.cs" />
<Compile Include="Interface\IApiErrorExtractor.cs" />
<Compile Include="Interface\IContact.cs" />
<Compile Include="Interface\IFileStorageDAO.cs" />

View File

@@ -14,5 +14,7 @@ namespace BS.Shared.Text
public static string SharedBibNodeDescription => "Hier sehen Sie Prompts, welche von Ihren Kollegin";
public static string PublicBibNodeTitle => "Öffentlich";
public static string PublicBibNodeDescription => "Hier sehen Sie öffentliche Prompts";
public static string OwnsoftBibNodeTitle => "Ownsoft";
public static string OwnsoftBibNodeDescription => "Hier können Sie vordefinierte Prompts sehen";
}
}