From 9efb36e1eebb805be60914d251c271190a117d7e Mon Sep 17 00:00:00 2001 From: Rene Evertz Date: Tue, 5 Aug 2025 15:57:30 +0200 Subject: [PATCH] Max Context Size --- AICore/AICore.csproj | 2 +- AICore/Facade/LLM/BaseLLMApiClient.cs | 5 +-- AICore/Facade/LLM/OllamaApiClient.cs | 15 ++++--- AICore/Facade/LLM/OpenWebApiClient.cs | 15 ++++--- AICore/Prompt/Core/AiUtils.cs | 29 ++++--------- Data/Access/GenericDAO.cs | 8 +++- Data/Entities/AiModel.cs | 5 +++ Service/Plugins/AiService2.cs | 59 ++++++++------------------- 8 files changed, 58 insertions(+), 80 deletions(-) diff --git a/AICore/AICore.csproj b/AICore/AICore.csproj index 07a0c5f6a..9e5e17d49 100644 --- a/AICore/AICore.csproj +++ b/AICore/AICore.csproj @@ -113,7 +113,7 @@ - {B0D73E3D-4AE7-4024-93A6-DB1F46D7CCEE} + {b0d73e3d-4ae7-4024-93a6-db1f46d7ccee} Data diff --git a/AICore/Facade/LLM/BaseLLMApiClient.cs b/AICore/Facade/LLM/BaseLLMApiClient.cs index 39860a8aa..ecbaaf743 100644 --- a/AICore/Facade/LLM/BaseLLMApiClient.cs +++ b/AICore/Facade/LLM/BaseLLMApiClient.cs @@ -1,5 +1,4 @@ -using BeWo.Data.Entities; -using BeWo.ServiceUtils.Core; +using BeWo.ServiceUtils.Core; using BS.Shared.DataContracts.Feature.AI; using BS.Shared.DataContracts; using BS.Shared.Interface; @@ -15,7 +14,7 @@ namespace AICore.Facade.LLM { public abstract class BaseLLMApiClient { - public abstract ApiResponse> GetAiModelle(); + public abstract ApiResponse> GetAiModelle(); public abstract ApiResponse SendAiMessageRequest(dynamic payload, out string message, out string model, out int? duration); } } diff --git a/AICore/Facade/LLM/OllamaApiClient.cs b/AICore/Facade/LLM/OllamaApiClient.cs index 4fae59a9e..bb125d54d 100644 --- a/AICore/Facade/LLM/OllamaApiClient.cs +++ b/AICore/Facade/LLM/OllamaApiClient.cs @@ -1,5 +1,4 @@ -using BeWo.Data.Entities; -using BS.Shared.DataContracts; +using BS.Shared.DataContracts; using BS.Shared.DataContracts.Feature.AI; using BS.Shared.Extensions; using BS.Shared.Interface; @@ -28,7 +27,7 @@ namespace AICore.Facade.LLM _ErrorExtractor = new DefaultErrorExtractor(); } - public override ApiResponse> GetAiModelle() + public override ApiResponse> GetAiModelle() { var models = new List(); @@ -39,7 +38,11 @@ namespace AICore.Facade.LLM new { name = "", - model = "" + model = "", + details = new + { + max_context_size = 0 + } } } @@ -52,9 +55,9 @@ namespace AICore.Facade.LLM throw new NotImplementedException(); } - var data = response.Data.models.Select(x => new AiModel(x.name, 0)); + var data = response.Data.models.Select(x => new AiModelDC(x.name, x.details.max_context_size)); - return ApiResponse>.SuccessResponse(data); + return ApiResponse>.SuccessResponse(data); } public override ApiResponse SendAiMessageRequest(dynamic payload, out string message, out string model, out int? duration) diff --git a/AICore/Facade/LLM/OpenWebApiClient.cs b/AICore/Facade/LLM/OpenWebApiClient.cs index 7fb04b7a9..fa817ccac 100644 --- a/AICore/Facade/LLM/OpenWebApiClient.cs +++ b/AICore/Facade/LLM/OpenWebApiClient.cs @@ -1,5 +1,4 @@ -using BeWo.Data.Entities; -using BeWo.Server.ApiFacade.Core; +using BeWo.Server.ApiFacade.Core; using BS.Shared.DataContracts; using BS.Shared.DataContracts.Feature.AI; using BS.Shared.Extensions; @@ -28,7 +27,7 @@ namespace AICore.Facade.LLM _ErrorExtractor = new DefaultErrorExtractor(); } - public override ApiResponse> GetAiModelle() + public override ApiResponse> GetAiModelle() { var models = new List(); @@ -39,7 +38,11 @@ namespace AICore.Facade.LLM new { id = "", - name = "" + name = "", + details = new + { + max_context_size = 0 + } } } @@ -52,9 +55,9 @@ namespace AICore.Facade.LLM throw new NotImplementedException(); } - var data = response.Data.data.Select(x => new AiModel(x.name, 0)); + var data = response.Data.data.Select(x => new AiModelDC(x.name, x.details.max_context_size)); - return ApiResponse>.SuccessResponse(data); + return ApiResponse>.SuccessResponse(data); } public override ApiResponse SendAiMessageRequest(dynamic payload, out string message, out string model, out int? duration) diff --git a/AICore/Prompt/Core/AiUtils.cs b/AICore/Prompt/Core/AiUtils.cs index 7d371bd84..7c949fccf 100644 --- a/AICore/Prompt/Core/AiUtils.cs +++ b/AICore/Prompt/Core/AiUtils.cs @@ -1,17 +1,19 @@ using BeWo.Data.Entities; using BS.Shared.Exceptions; -using System; using System.Collections.Generic; namespace AICore.Prompt.Core { public static class AiUtils { - public static string CheckTokenSize(IList messages, string modelName) + public static void CheckTokenSize(IList messages, string modelName, int max_context_size) { var buffer = 500; - var max_tokens = GetMaxTokensByModel(modelName); + if (max_context_size < 0) + return; + + var max_tokens = max_context_size; var prev_tokens = CalculateTokensByModel(messages, modelName); @@ -20,27 +22,10 @@ namespace AICore.Prompt.Core var percentage = calc_tokens * 100 / max_tokens; if (calc_tokens > max_tokens) - return $"Windowsize exceeded: ({prev_tokens}+{buffer})/{max_tokens} ({percentage}%)"; - - return null; - } - - private static int GetMaxTokensByModel(string modelName) - { - switch (modelName) { - case "llama3.2:latest": return 128000; - case "gemma3:12b": return 128000; - case "gemma3:4b": return 128000; - case "gemma3_12b-max-context:latest": return 128000; - case "gemma3_4b-max-context:latest": return 128000; - case "mistral:latest": return 32000; - case "phi4:latest": return 16000; - default: - break; + var msg = $"Windowsize exceeded: ({prev_tokens}+{buffer})/{max_tokens} ({percentage}%)"; + throw new BeWoNotImplementedException(msg); } - - throw BeWoNotImplementedException.CreateFromInvalidObject(modelName); } private static int CalculateTokensByModel(IList messages, string modelName) diff --git a/Data/Access/GenericDAO.cs b/Data/Access/GenericDAO.cs index b0592d3d5..59b57650e 100644 --- a/Data/Access/GenericDAO.cs +++ b/Data/Access/GenericDAO.cs @@ -10,6 +10,7 @@ using BS.Shared.DataContracts.Light; using NHibernate; using NHibernate.Criterion; using NHibernate.Proxy; +using NHibernate.Linq; namespace BeWo.Data.Access { @@ -250,7 +251,12 @@ namespace BeWo.Data.Access this.Session.Lock(entity, LockMode.None); } - public virtual void SetActivationType(BeWoEntityBase pEntity, ActivationTypeId activationType) + public virtual void SetActivationType(IEnumerable pEntities, ActivationTypeId activationType) + { + pEntities.ForEach(x => SetActivationType(x, activationType)); + } + + public virtual void SetActivationType(BeWoEntityBase pEntity, ActivationTypeId activationType) { if (pEntity.SystemEntryID == null) { diff --git a/Data/Entities/AiModel.cs b/Data/Entities/AiModel.cs index 51e6d1f7d..ebb3b0547 100644 --- a/Data/Entities/AiModel.cs +++ b/Data/Entities/AiModel.cs @@ -1,9 +1,12 @@ using BS.Shared; using BS.Shared.DataContracts.Feature.AI; using System; +using System.Diagnostics; +using System.Xml.Linq; namespace BeWo.Data.Entities { + [DebuggerDisplay("{ModelName}:{Value}")] public class AiModel : BeWoEntityBase { public AiModel() @@ -20,6 +23,8 @@ namespace BeWo.Data.Entities public virtual string ModelName { get; set; } public virtual int Value { get; set; } + public override int GetHashCode() => (ModelName, Value).GetHashCode(); + public virtual int CompareTo(AiModel other) { if (other == null) return 1; diff --git a/Service/Plugins/AiService2.cs b/Service/Plugins/AiService2.cs index 8fd055329..b7a4fc456 100644 --- a/Service/Plugins/AiService2.cs +++ b/Service/Plugins/AiService2.cs @@ -225,51 +225,26 @@ namespace BeWo.Service.Plugins { var models = getAiModels(); - if (models == null || !models.Any()) - return null; + var model = models.FirstOrDefault(x => x.ModelName.Contains("llama3.2")) + ?? models.FirstOrDefault(x => x.ModelName.Contains("llama")) + ?? models.FirstOrDefault(); - var llama = models.Where(x => x.ModelName.Contains("llama3.2")); - - if (llama.Any()) - return llama.First(); - - return models.First(); + return model; } private IEnumerable getAiModels() { - var old_models = DAOFactory.GenericDAO.GetAll(); - var new_models = ServiceFacade.ActiveAiClient.GetAiModelle().GetResponseData(); - var unknown_models = new_models.ToList(); + var current_models = DAOFactory.GenericDAO.GetAll(); + var current_models_deactives = current_models.Where(x => x.IsActive != ActivationTypeId.Active); + var request_models_dc = ServiceFacade.ActiveAiClient.GetAiModelle().GetResponseData(); + var request_models = MapperFactory.AiModel.MapToNewEntities(request_models_dc); - int i = 0; - foreach (var old_model in old_models) - { - bool found = false; - foreach (var new_model in new_models) - { - if (old_model.ModelName == new_model.ModelName) - { - if (old_model.IsActive != BS.Shared.ActivationTypeId.Active) - DAOFactory.GenericDAO.SetActivationType(old_model, BS.Shared.ActivationTypeId.Active); + var toadd = request_models.Except(current_models); + var toremove = current_models.Except(request_models); + var toreadd = current_models_deactives.Intersect(toadd); - found = true; - var index = unknown_models.IndexOf(model => model.Equals(new_model)); - unknown_models.RemoveAt(index); - break; - } - } - - if (!found && old_model.IsActive == BS.Shared.ActivationTypeId.Active) - DAOFactory.GenericDAO.Deactivate(old_model); - } - - if (unknown_models.Count != 0) - { - foreach (var unknown_model in unknown_models) - { - DAOFactory.GenericDAO.Insert(unknown_model); - } - } + DAOFactory.GenericDAO.Insert(toadd); + DAOFactory.GenericDAO.Deactivate(toremove); + DAOFactory.GenericDAO.SetActivationType(toreadd, ActivationTypeId.Active); var models = DAOFactory.GenericDAO.GetAllActive(); @@ -312,6 +287,8 @@ namespace BeWo.Service.Plugins // Hole Config if (config == null) config = GetAiConfig(); + + var configModel = config.SelectedModel; var configModelName = config.SelectedModel.ModelName; // Build Payload @@ -334,9 +311,9 @@ namespace BeWo.Service.Plugins conv.Messages.Add(user_msg); // Check Token Size - if (AiUtils.CheckTokenSize(conv.Messages, configModelName) is string error) + if (-1 < configModel.Value) { - throw new BeWoNotImplementedException(error); + AiUtils.CheckTokenSize(conv.Messages, configModelName, configModel.Value); } // Update DB Entity