mirror of
https://github.com/shinyflvre/Mate-Engine.git
synced 2026-07-28 10:23:33 +00:00
**Changelog 1.4.0 – THE LOCAL AI Update** **Disclaimer:** The AI runs *entirely locally* and is designed to be environmentally and ethically responsible. - It does **not** harm nature or exploit any artists. - It has only **1.5 billion parameters**, making it a **very small model** by modern standards. - It consumes as much GPU energy as watching a **1440p 60fps YouTube video**. - It was **not trained on artist data**; it uses personal input and public Wikipedia datasets. - It has its own personality, and I ensured it adheres to **ethical standards**. --- ### New Features - Added **Qwen 2.5 1.5B LLM** to MateEngine! - Press **Middle Mouse Button** while hovering over the pet to open the chat window. - Runs fully locally on your GPU — no internet connection required. - Only uses GPU while processing prompts. - Note: This is a **lightweight model** and won’t handle complex tasks or coding. It’s just a small, fun feature! - Added **Resize Button** – Quickly adjust app size for 1080p, 1440p, and 4K monitors. - Added **VRM Information Panel** – Displays polygon count, VRM version, and bone status: - **Perfect** – Properly exported model. - **Failure** – Model has export issues; animations may break. - Added **Options Submenu**. - Added **Volume Sliders** – Independently control pet, effects, and menu volume. - Added **Graphics Settings**. - Added **Dance Trails** – Glowing effects on the pet’s hands during dance. Toggle on/off as desired. - Improved **Music Source Selector** – You can now whitelist or add custom audio sources for dancing. - Added **MateEngine Version Info** – View the current app version from the menu. - Menu can now be opened with **M** key or **Right-Click**. - Added full support for **VRM 1.x** and **VRM 0.x** models. - Introduced **VRM Validator Tool** in Unity Editor – Great for content creators. --- ### Improvements & Fixes - Major **Performance Optimizations**. - Fixed a **GC Error** that could appear after 6–12 hours of continuous use. - Improved **Hand Tracking** – Reduced stuttering in some animations. - Added **Discord Rich Presence** integration. - Added **Glowing Halo** for Steam users as exclusive DLC!
293 lines
12 KiB
C#
293 lines
12 KiB
C#
/// @file
|
|
/// @brief File implementing the chunking functionality
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO.Compression;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LLMUnity
|
|
{
|
|
/// @ingroup rag
|
|
/// <summary>
|
|
/// Class implementing the chunking functionality
|
|
/// </summary>
|
|
[Serializable]
|
|
public abstract class Chunking : SearchPlugin
|
|
{
|
|
protected bool returnChunks = false;
|
|
protected Dictionary<string, List<int>> dataSplitToPhrases = new Dictionary<string, List<int>>();
|
|
protected Dictionary<int, int[]> phraseToSentences = new Dictionary<int, int[]>();
|
|
protected Dictionary<int, int> sentenceToPhrase = new Dictionary<int, int>();
|
|
protected Dictionary<int, int[]> hexToPhrase = new Dictionary<int, int[]>();
|
|
protected int nextKey = 0;
|
|
|
|
/// <summary>
|
|
/// Set to true to return chunks or the direct input with the Search function
|
|
/// </summary>
|
|
/// <param name="returnChunks">whether to return chunks</param>
|
|
public void ReturnChunks(bool returnChunks)
|
|
{
|
|
this.returnChunks = returnChunks;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Splits the provided phrase into chunks
|
|
/// </summary>
|
|
/// <param name="input">phrase</param>
|
|
/// <returns>List of start/end indices of the split chunks</returns>
|
|
public abstract Task<List<(int, int)>> Split(string input);
|
|
|
|
/// <summary>
|
|
/// Retrieves the phrase with the specific id
|
|
/// </summary>
|
|
/// <param name="key">phrase id</param>
|
|
/// <returns>phrase</returns>
|
|
public override string Get(int key)
|
|
{
|
|
StringBuilder phraseBuilder = new StringBuilder();
|
|
foreach (int sentenceId in phraseToSentences[key])
|
|
{
|
|
phraseBuilder.Append(search.Get(sentenceId));
|
|
}
|
|
return phraseBuilder.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a phrase to the search after splitting it into chunks.
|
|
/// </summary>
|
|
/// <param name="inputString">input phrase</param>
|
|
/// <param name="group">data group to add it to </param>
|
|
/// <returns>phrase id</returns>
|
|
public override async Task<int> Add(string inputString, string group = "")
|
|
{
|
|
int key = nextKey++;
|
|
// sentence -> phrase
|
|
List<int> sentenceIds = new List<int>();
|
|
foreach ((int startIndex, int endIndex) in await Split(inputString))
|
|
{
|
|
string sentenceText = inputString.Substring(startIndex, endIndex - startIndex + 1);
|
|
int sentenceId = await search.Add(sentenceText, group);
|
|
sentenceIds.Add(sentenceId);
|
|
|
|
sentenceToPhrase[sentenceId] = key;
|
|
}
|
|
// phrase -> sentence
|
|
phraseToSentences[key] = sentenceIds.ToArray();
|
|
|
|
// data split -> phrase
|
|
if (!dataSplitToPhrases.ContainsKey(group)) dataSplitToPhrases[group] = new List<int>(){key};
|
|
else dataSplitToPhrases[group].Add(key);
|
|
|
|
// hex -> phrase
|
|
int hash = inputString.GetHashCode();
|
|
if (!hexToPhrase.TryGetValue(hash, out int[] entries)) entries = new int[0];
|
|
List<int> matchingHash = new List<int>(entries);
|
|
matchingHash.Add(key);
|
|
|
|
hexToPhrase[hash] = matchingHash.ToArray();
|
|
return key;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a phrase and the phrase chunks from the search
|
|
/// </summary>
|
|
/// <param name="key">phrase id</param>
|
|
public override void Remove(int key)
|
|
{
|
|
if (!phraseToSentences.TryGetValue(key, out int[] sentenceIds)) return;
|
|
int hash = Get(key).GetHashCode();
|
|
|
|
// phrase -> sentence
|
|
phraseToSentences.Remove(key);
|
|
foreach (int sentenceId in sentenceIds)
|
|
{
|
|
search.Remove(sentenceId);
|
|
// sentence -> phrase
|
|
sentenceToPhrase.Remove(sentenceId);
|
|
}
|
|
|
|
// data split -> phrase
|
|
foreach (var dataSplitPhrases in dataSplitToPhrases.Values) dataSplitPhrases.Remove(key);
|
|
|
|
// hex -> phrase
|
|
if (hexToPhrase.TryGetValue(hash, out int[] phraseIds))
|
|
{
|
|
List<int> updatedIds = phraseIds.ToList();
|
|
updatedIds.Remove(key);
|
|
if (updatedIds.Count == 0) hexToPhrase.Remove(hash);
|
|
else hexToPhrase[hash] = updatedIds.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a phrase and the phrase chunks from the search.
|
|
/// </summary>
|
|
/// <param name="inputString">input phrase</param>
|
|
/// <param name="group">data group to remove it from </param>
|
|
/// <returns>number of removed phrases</returns>
|
|
public override int Remove(string inputString, string group = "")
|
|
{
|
|
int hash = inputString.GetHashCode();
|
|
if (!hexToPhrase.TryGetValue(hash, out int[] entries)) return 0;
|
|
List<int> removeIds = new List<int>();
|
|
foreach (int key in entries)
|
|
{
|
|
if (dataSplitToPhrases[group].Contains(key) && Get(key) == inputString) removeIds.Add(key);
|
|
}
|
|
foreach (int removeId in removeIds) Remove(removeId);
|
|
return removeIds.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a count of the phrases
|
|
/// </summary>
|
|
/// <returns>phrase count</returns>
|
|
public override int Count()
|
|
{
|
|
return phraseToSentences.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a count of the phrases in a specific data group
|
|
/// </summary>
|
|
/// <param name="group">data group</param>
|
|
/// <returns>phrase count</returns>
|
|
public override int Count(string group)
|
|
{
|
|
if (!dataSplitToPhrases.TryGetValue(group, out List<int> dataSplitPhrases)) return 0;
|
|
return dataSplitPhrases.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows to do search and retrieve results in batches (incremental search).
|
|
/// </summary>
|
|
/// <param name="queryString">search query</param>
|
|
/// <param name="group">data group to search in</param>
|
|
/// <returns>incremental search key</returns>
|
|
public override async Task<int> IncrementalSearch(string queryString, string group = "")
|
|
{
|
|
return await search.IncrementalSearch(queryString, group);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the most similar search results in batches (incremental search).
|
|
/// The phrase/chunk keys and distances are retrieved, as well as a parameter that dictates whether the search is exhausted.
|
|
/// The returnChunks variable defines whether to return chunks or phrases.
|
|
/// </summary>
|
|
/// <param name="fetchKey">incremental search key</param>
|
|
/// <param name="k">number of results to retrieve</param>
|
|
/// <returns>
|
|
/// A tuple containing:
|
|
/// <list type="bullet">
|
|
/// <item><description>Array of retrieved keys (`int[]`).</description></item>
|
|
/// <item><description>Array of distances for each result (`float[]`).</description></item>
|
|
/// <item><description>`bool` indicating if the search is exhausted.</description></item>
|
|
/// </list>
|
|
/// </returns>
|
|
public override ValueTuple<int[], float[], bool> IncrementalFetchKeys(int fetchKey, int k)
|
|
{
|
|
if (returnChunks)
|
|
{
|
|
return search.IncrementalFetchKeys(fetchKey, k);
|
|
}
|
|
else
|
|
{
|
|
List<int> phraseKeys = new List<int>();
|
|
List<float> distancesList = new List<float>();
|
|
bool done = false;
|
|
bool completed;
|
|
do
|
|
{
|
|
int[] resultKeys;
|
|
float[] distancesIter;
|
|
(resultKeys, distancesIter, completed) = search.IncrementalFetchKeys(fetchKey, k);
|
|
for (int i = 0; i < resultKeys.Length; i++)
|
|
{
|
|
int phraseId = sentenceToPhrase[resultKeys[i]];
|
|
if (phraseKeys.Contains(phraseId)) continue;
|
|
phraseKeys.Add(phraseId);
|
|
distancesList.Add(distancesIter[i]);
|
|
if (phraseKeys.Count() == k)
|
|
{
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
if (completed) break;
|
|
}
|
|
while (!done);
|
|
if (completed) IncrementalSearchComplete(fetchKey);
|
|
return (phraseKeys.ToArray(), distancesList.ToArray(), completed);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the most similar search results in batches (incremental search).
|
|
/// The phrases/chunks and their distances are retrieved, as well as a parameter that dictates whether the search is exhausted.
|
|
/// The returnChunks variable defines whether to return chunks or phrases.
|
|
/// </summary>
|
|
/// <param name="fetchKey">incremental search key</param>
|
|
/// <param name="k">number of results to retrieve</param>
|
|
/// <returns>
|
|
/// A tuple containing:
|
|
/// <list type="bullet">
|
|
/// <item><description>Array of retrieved phrases/chunks (`string[]`).</description></item>
|
|
/// <item><description>Array of distances for each result (`float[]`).</description></item>
|
|
/// <item><description>`bool` indicating if the search is exhausted.</description></item>
|
|
/// </list>
|
|
/// </returns>
|
|
public override ValueTuple<string[], float[], bool> IncrementalFetch(int fetchKey, int k)
|
|
{
|
|
(int[] resultKeys, float[] distances, bool completed) = IncrementalFetchKeys(fetchKey, k);
|
|
string[] results = new string[resultKeys.Length];
|
|
for (int i = 0; i < resultKeys.Length; i++)
|
|
{
|
|
if (returnChunks) results[i] = search.Get(resultKeys[i]);
|
|
else results[i] = Get(resultKeys[i]);
|
|
}
|
|
return (results, distances, completed);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes the search and clears the cached results for an incremental search
|
|
/// </summary>
|
|
/// <param name="fetchKey">incremental search key</param>
|
|
public override void IncrementalSearchComplete(int fetchKey)
|
|
{
|
|
search.IncrementalSearchComplete(fetchKey);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the object and the associated search object
|
|
/// </summary>
|
|
public override void Clear()
|
|
{
|
|
nextKey = 0;
|
|
dataSplitToPhrases.Clear();
|
|
phraseToSentences.Clear();
|
|
sentenceToPhrase.Clear();
|
|
hexToPhrase.Clear();
|
|
search.Clear();
|
|
}
|
|
|
|
protected override void SaveInternal(ZipArchive archive)
|
|
{
|
|
ArchiveSaver.Save(archive, dataSplitToPhrases, GetSavePath("dataSplitToPhrases"));
|
|
ArchiveSaver.Save(archive, phraseToSentences, GetSavePath("phraseToSentences"));
|
|
ArchiveSaver.Save(archive, sentenceToPhrase, GetSavePath("sentenceToPhrase"));
|
|
ArchiveSaver.Save(archive, hexToPhrase, GetSavePath("hexToPhrase"));
|
|
ArchiveSaver.Save(archive, nextKey, GetSavePath("nextKey"));
|
|
}
|
|
|
|
protected override void LoadInternal(ZipArchive archive)
|
|
{
|
|
dataSplitToPhrases = ArchiveSaver.Load<Dictionary<string, List<int>>>(archive, GetSavePath("dataSplitToPhrases"));
|
|
phraseToSentences = ArchiveSaver.Load<Dictionary<int, int[]>>(archive, GetSavePath("phraseToSentences"));
|
|
sentenceToPhrase = ArchiveSaver.Load<Dictionary<int, int>>(archive, GetSavePath("sentenceToPhrase"));
|
|
hexToPhrase = ArchiveSaver.Load<Dictionary<int, int[]>>(archive, GetSavePath("hexToPhrase"));
|
|
nextKey = ArchiveSaver.Load<int>(archive, GetSavePath("nextKey"));
|
|
}
|
|
}
|
|
}
|