using System; using System.Collections.Generic; namespace LLama { /// /// AntipromptProcessor keeps track of past tokens looking for any set Anti-Prompts /// public sealed class AntipromptProcessor { private int _longestAntiprompt; private readonly List _antiprompts = new(); private string? _string; /// /// Initializes a new instance of the class. /// /// The antiprompts. public AntipromptProcessor(IEnumerable? antiprompts = null) { if (antiprompts != null) SetAntiprompts(antiprompts); } /// /// Add an antiprompt to the collection /// /// public void AddAntiprompt(string antiprompt) { _antiprompts.Add(antiprompt); _longestAntiprompt = Math.Max(_longestAntiprompt, antiprompt.Length); } /// /// Overwrite all current antiprompts with a new set /// /// public void SetAntiprompts(IEnumerable antiprompts) { _antiprompts.Clear(); _antiprompts.AddRange(antiprompts); _longestAntiprompt = 0; foreach (var antiprompt in _antiprompts) _longestAntiprompt = Math.Max(_longestAntiprompt, antiprompt.Length); } /// /// Add some text and check if the buffer now ends with any antiprompt /// /// /// true if the text buffer ends with any antiprompt public bool Add(string text) { _string += text; // When the string gets very long (4x antiprompt length) trim it down (to 2x antiprompt length). // This trimming leaves a lot of extra characters because two sequences can be considered "equal" in unicode // even with different numbers of characters. Hopefully there are enough characters here to handle all those weird circumstances! var maxLength = Math.Max(32, _longestAntiprompt * 4); var trimLength = Math.Max(16, _longestAntiprompt * 2); if (_string.Length > maxLength) _string = _string.Substring(_string.Length - trimLength); foreach (var antiprompt in _antiprompts) if (_string.EndsWith(antiprompt, StringComparison.CurrentCulture)) return true; return false; } } }