mirror of
https://github.com/shinyflvre/Mate-Engine.git
synced 2026-07-17 21:18:29 +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!
700 lines
26 KiB
C#
700 lines
26 KiB
C#
using NUnit.Framework;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using LLMUnity;
|
|
using System;
|
|
using UnityEngine.TestTools;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace LLMUnityTests
|
|
{
|
|
public abstract class TestSearchable<T> where T : Searchable
|
|
{
|
|
protected string weather = "how is the weather today?";
|
|
protected string raining = "is it raining?";
|
|
protected string sometext = "something completely sometext";
|
|
protected float weatherRainingDiff = 0.2073563f;
|
|
protected float weatherSometextDiff = 0.4837922f;
|
|
|
|
protected string modelNameLLManager;
|
|
protected GameObject gameObject;
|
|
protected LLM llm;
|
|
public T search;
|
|
protected Exception error = null;
|
|
|
|
public TestSearchable()
|
|
{
|
|
Task task = Init();
|
|
task.Wait();
|
|
}
|
|
|
|
public virtual async Task Init()
|
|
{
|
|
await DownloadModels();
|
|
gameObject = new GameObject();
|
|
gameObject.SetActive(false);
|
|
llm = CreateLLM();
|
|
search = CreateSearch();
|
|
gameObject.SetActive(true);
|
|
}
|
|
|
|
public virtual LLM CreateLLM()
|
|
{
|
|
LLM llm = gameObject.AddComponent<LLM>();
|
|
llm.SetModel(modelNameLLManager);
|
|
llm.parallelPrompts = 1;
|
|
return llm;
|
|
}
|
|
|
|
public abstract T CreateSearch();
|
|
|
|
public virtual async Task DownloadModels()
|
|
{
|
|
modelNameLLManager = await LLMManager.DownloadModel(GetModelUrl());
|
|
}
|
|
|
|
protected virtual string GetModelUrl()
|
|
{
|
|
return "https://huggingface.co/CompendiumLabs/bge-small-en-v1.5-gguf/resolve/main/bge-small-en-v1.5-f16.gguf";
|
|
}
|
|
|
|
public static bool ApproxEqual(float x1, float x2)
|
|
{
|
|
float tolerance = (Application.platform == RuntimePlatform.OSXPlayer) ? 0.001f : 0.0001f;
|
|
return Mathf.Abs(x1 - x2) < tolerance;
|
|
}
|
|
|
|
[UnityTest]
|
|
public virtual IEnumerator RunTests()
|
|
{
|
|
Task task = RunTestsTask();
|
|
while (!task.IsCompleted) yield return null;
|
|
if (error != null)
|
|
{
|
|
Debug.LogError(error.ToString());
|
|
throw (error);
|
|
}
|
|
}
|
|
|
|
public virtual async Task RunTestsTask()
|
|
{
|
|
error = null;
|
|
try
|
|
{
|
|
await Tests();
|
|
llm.OnDestroy();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
error = e;
|
|
}
|
|
}
|
|
|
|
public virtual async Task Tests()
|
|
{
|
|
await TestAdd();
|
|
await TestSearch();
|
|
await TestIncrementalSearch();
|
|
await TestSaveLoad();
|
|
}
|
|
|
|
public virtual async Task TestAdd()
|
|
{
|
|
void CheckCount(int[] nums)
|
|
{
|
|
int sum = 0;
|
|
for (int i = 0; i < nums.Length; i++)
|
|
{
|
|
Assert.That(search.Count(i.ToString()) == nums[i]);
|
|
sum += nums[i];
|
|
}
|
|
Assert.That(search.Count() == sum);
|
|
}
|
|
|
|
int key, num;
|
|
key = await search.Add(weather);
|
|
Assert.That(key == 0);
|
|
Assert.That(search.Get(key) == weather);
|
|
Assert.That(search.Count() == 1);
|
|
search.Remove(key);
|
|
Assert.That(search.Count() == 0);
|
|
|
|
key = await search.Add(weather);
|
|
Assert.That(key == 1);
|
|
key = await search.Add(raining);
|
|
Assert.That(key == 2);
|
|
key = await search.Add(sometext);
|
|
Assert.That(key == 3);
|
|
Assert.That(search.Count() == 3);
|
|
search.Clear();
|
|
Assert.That(search.Count() == 0);
|
|
|
|
key = await search.Add(weather, "0");
|
|
Assert.That(key == 0);
|
|
key = await search.Add(raining, "0");
|
|
Assert.That(key == 1);
|
|
key = await search.Add(weather, "1");
|
|
Assert.That(key == 2);
|
|
key = await search.Add(sometext, "1");
|
|
Assert.That(key == 3);
|
|
key = await search.Add(sometext, "2");
|
|
Assert.That(key == 4);
|
|
CheckCount(new int[] {2, 2, 1});
|
|
num = search.Remove(weather, "0");
|
|
Assert.That(num == 1);
|
|
CheckCount(new int[] {1, 2, 1});
|
|
num = search.Remove(weather, "1");
|
|
Assert.That(num == 1);
|
|
CheckCount(new int[] {1, 1, 1});
|
|
num = search.Remove(weather, "0");
|
|
Assert.That(num == 0);
|
|
CheckCount(new int[] {1, 1, 1});
|
|
num = search.Remove(raining, "0");
|
|
Assert.That(num == 1);
|
|
CheckCount(new int[] {0, 1, 1});
|
|
num = search.Remove(sometext, "1");
|
|
Assert.That(num == 1);
|
|
CheckCount(new int[] {0, 0, 1});
|
|
num = search.Remove(sometext, "2");
|
|
Assert.That(num == 1);
|
|
CheckCount(new int[] {0, 0, 0});
|
|
|
|
search.Clear();
|
|
Assert.That(search.Count() == 0);
|
|
}
|
|
|
|
public virtual async Task TestSearch()
|
|
{
|
|
string[] results;
|
|
float[] distances;
|
|
|
|
(results, distances) = await search.Search(weather, 1);
|
|
Assert.That(results.Length == 0);
|
|
Assert.That(distances.Length == 0);
|
|
|
|
await search.Add(weather);
|
|
await search.Add(raining);
|
|
await search.Add(sometext);
|
|
|
|
(results, distances) = await search.Search(weather, 2);
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], raining);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherRainingDiff));
|
|
|
|
(results, distances) = await search.Search(raining, 2);
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], weather);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherRainingDiff));
|
|
|
|
search.Clear();
|
|
|
|
await search.Add(weather, "0");
|
|
await search.Add(raining, "1");
|
|
await search.Add(sometext, "0");
|
|
await search.Add(sometext, "1");
|
|
|
|
(results, distances) = await search.Search(weather, 2, "0");
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
|
|
(results, distances) = await search.Search(weather, 2, "0");
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
|
|
(results, distances) = await search.Search(weather, 2, "1");
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
|
|
(results, distances) = await search.Search(weather, 3, "1");
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
search.Clear();
|
|
}
|
|
|
|
public async Task TestIncrementalSearch()
|
|
{
|
|
string[] results;
|
|
float[] distances;
|
|
bool completed;
|
|
|
|
int searchKey = await search.IncrementalSearch(weather);
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 1);
|
|
Assert.That(searchKey == 0);
|
|
Assert.That(results.Length == 0);
|
|
Assert.That(distances.Length == 0);
|
|
Assert.That(completed);
|
|
search.Clear();
|
|
|
|
await search.Add(weather);
|
|
await search.Add(raining);
|
|
await search.Add(sometext);
|
|
|
|
searchKey = await search.IncrementalSearch(weather);
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 1);
|
|
Assert.That(searchKey == 0);
|
|
Assert.That(results.Length == 1);
|
|
Assert.That(distances.Length == 1);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(!completed);
|
|
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 2);
|
|
Assert.That(results.Length == 2);
|
|
Assert.That(distances.Length == 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], weatherRainingDiff));
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
Assert.That(completed);
|
|
|
|
searchKey = await search.IncrementalSearch(weather);
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 2);
|
|
Assert.That(searchKey == 1);
|
|
Assert.That(results.Length == 2);
|
|
Assert.That(distances.Length == 2);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], raining);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherRainingDiff));
|
|
Assert.That(!completed);
|
|
|
|
search.IncrementalSearchComplete(searchKey);
|
|
search.Clear();
|
|
|
|
await search.Add(weather, "0");
|
|
await search.Add(raining, "1");
|
|
await search.Add(sometext, "0");
|
|
await search.Add(sometext, "1");
|
|
|
|
searchKey = await search.IncrementalSearch(weather, "0");
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 2);
|
|
Assert.That(searchKey == 0);
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
Assert.That(completed);
|
|
|
|
searchKey = await search.IncrementalSearch(weather, "0");
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 2);
|
|
Assert.That(searchKey == 1);
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
Assert.That(completed);
|
|
|
|
searchKey = await search.IncrementalSearch(weather, "1");
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 1);
|
|
Assert.That(searchKey == 2);
|
|
Assert.AreEqual(results.Length, 1);
|
|
Assert.AreEqual(distances.Length, 1);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.That(!completed);
|
|
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 1);
|
|
Assert.AreEqual(results.Length, 1);
|
|
Assert.AreEqual(distances.Length, 1);
|
|
Assert.AreEqual(results[0], sometext);
|
|
Assert.That(ApproxEqual(distances[0], weatherSometextDiff));
|
|
Assert.That(completed);
|
|
|
|
searchKey = await search.IncrementalSearch(weather, "1");
|
|
(results, distances, completed) = search.IncrementalFetch(searchKey, 3);
|
|
Assert.That(searchKey == 3);
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
Assert.That(completed);
|
|
search.Clear();
|
|
}
|
|
|
|
public virtual async Task TestSaveLoad()
|
|
{
|
|
string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
|
string[] results;
|
|
float[] distances;
|
|
|
|
await search.Add(weather);
|
|
await search.Add(raining);
|
|
await search.Add(sometext);
|
|
search.Save(path);
|
|
|
|
search.Clear();
|
|
await search.Load(path);
|
|
File.Delete(path);
|
|
|
|
Assert.That(search.Count() == 3);
|
|
Assert.That(search.Get(0) == weather);
|
|
Assert.That(search.Get(1) == raining);
|
|
Assert.That(search.Get(2) == sometext);
|
|
|
|
(results, distances) = await search.Search(raining, 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], weather);
|
|
Assert.That(ApproxEqual(distances[0], 0));
|
|
Assert.That(ApproxEqual(distances[1], weatherRainingDiff));
|
|
|
|
search.Clear();
|
|
|
|
await search.Add(weather, "0");
|
|
await search.Add(raining, "1");
|
|
await search.Add(sometext, "0");
|
|
await search.Add(sometext, "1");
|
|
search.Save(path);
|
|
|
|
search.Clear();
|
|
await search.Load(path);
|
|
File.Delete(path);
|
|
|
|
Assert.That(search.Count() == 4);
|
|
Assert.That(search.Count("0") == 2);
|
|
Assert.That(search.Count("1") == 2);
|
|
Assert.That(search.Get(0) == weather);
|
|
Assert.That(search.Get(1) == raining);
|
|
Assert.That(search.Get(2) == sometext);
|
|
Assert.That(search.Get(3) == sometext);
|
|
|
|
(results, distances) = await search.Search(raining, 2, "0");
|
|
Assert.AreEqual(results[0], weather);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], weatherRainingDiff));
|
|
|
|
search.Clear();
|
|
}
|
|
}
|
|
|
|
public abstract class TestSearchMethod<T> : TestSearchable<T> where T : SearchMethod
|
|
{
|
|
public override T CreateSearch()
|
|
{
|
|
T search = gameObject.AddComponent<T>();
|
|
search.SetLLM(llm);
|
|
return search;
|
|
}
|
|
|
|
public override async Task Tests()
|
|
{
|
|
await base.Tests();
|
|
await TestEncode();
|
|
await TestSimilarity();
|
|
await TestSearchFromList();
|
|
}
|
|
|
|
public async Task TestEncode()
|
|
{
|
|
float[] encoding = await search.Encode(weather);
|
|
Assert.That(ApproxEqual(encoding[0], -0.02910374f));
|
|
Assert.That(ApproxEqual(encoding[383], 0.01764517f));
|
|
}
|
|
|
|
public async Task TestSimilarity()
|
|
{
|
|
float[] sentence1 = await search.Encode(weather);
|
|
float[] sentence2 = await search.Encode(raining);
|
|
float similarity = SimpleSearch.DotProduct(sentence1, sentence2);
|
|
float distance = SimpleSearch.InverseDotProduct(sentence1, sentence2);
|
|
Assert.That(ApproxEqual(similarity, 1 - weatherRainingDiff));
|
|
Assert.That(ApproxEqual(distance, weatherRainingDiff));
|
|
}
|
|
|
|
public async Task TestSearchFromList()
|
|
{
|
|
(string[] results, float[] distances) = await search.SearchFromList(weather, new string[] {sometext, raining});
|
|
Assert.AreEqual(results.Length, 2);
|
|
Assert.AreEqual(distances.Length, 2);
|
|
Assert.AreEqual(results[0], raining);
|
|
Assert.AreEqual(results[1], sometext);
|
|
Assert.That(ApproxEqual(distances[0], weatherRainingDiff));
|
|
Assert.That(ApproxEqual(distances[1], weatherSometextDiff));
|
|
}
|
|
}
|
|
|
|
public class TestSimpleSearch : TestSearchMethod<SimpleSearch> {}
|
|
|
|
public class TestDBSearch : TestSearchMethod<DBSearch> {}
|
|
|
|
public abstract class TestSplitter<T> : TestSearchable<T> where T : Chunking
|
|
{
|
|
public override T CreateSearch()
|
|
{
|
|
T search = gameObject.AddComponent<T>();
|
|
DBSearch searchMethod = gameObject.AddComponent<DBSearch>();
|
|
searchMethod.SetLLM(llm);
|
|
search.SetSearch(searchMethod);
|
|
return search;
|
|
}
|
|
|
|
public static (string, List<(int, int)>) GenerateText(int length)
|
|
{
|
|
System.Random random = new System.Random();
|
|
char[] characters = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
|
|
|
|
char[] generatedText = new char[length];
|
|
List<(int, int)> indices = new List<(int, int)>();
|
|
bool delimited = false;
|
|
bool seenChar = false;
|
|
int preI = 0;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
int charType = random.Next(0, 20);
|
|
if (charType == 0)
|
|
{
|
|
generatedText[i] = SentenceSplitter.DefaultDelimiters[random.Next(SentenceSplitter.DefaultDelimiters.Length)];
|
|
delimited = seenChar && true;
|
|
}
|
|
else if (charType < 3)
|
|
{
|
|
generatedText[i] = ' ';
|
|
}
|
|
else
|
|
{
|
|
generatedText[i] = characters[random.Next(characters.Length)];
|
|
if (delimited)
|
|
{
|
|
indices.Add((preI, i - 1));
|
|
preI = i;
|
|
delimited = false;
|
|
}
|
|
seenChar = true;
|
|
}
|
|
}
|
|
indices.Add((preI, length - 1));
|
|
return (new string(generatedText), indices);
|
|
}
|
|
|
|
public override async Task Tests()
|
|
{
|
|
await base.Tests();
|
|
await TestProperSplit();
|
|
}
|
|
|
|
public async Task TestProperSplit()
|
|
{
|
|
for (int length = 50; length <= 500; length += 50)
|
|
{
|
|
(string randomText, _) = GenerateText(length);
|
|
List<(int, int)> indices = await search.Split(randomText);
|
|
int currIndex = 0;
|
|
foreach ((int startIndex, int endIndex) in indices)
|
|
{
|
|
Assert.AreEqual(currIndex, startIndex);
|
|
currIndex = endIndex + 1;
|
|
}
|
|
Assert.AreEqual(currIndex, length);
|
|
int key = await search.Add(randomText);
|
|
Assert.AreEqual(search.Get(key), randomText);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TestTokenSplitter : TestSplitter<TokenSplitter> {}
|
|
|
|
public class TestWordSplitter : TestSplitter<WordSplitter>
|
|
{
|
|
public override async Task Tests()
|
|
{
|
|
await base.Tests();
|
|
await TestSplit();
|
|
}
|
|
|
|
public async Task TestSplit()
|
|
{
|
|
System.Random random = new System.Random();
|
|
char[] characters = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
|
|
char[] boundary = " .,!".ToCharArray();
|
|
int w = 0;
|
|
for (int numSplits = 0; numSplits < 10; numSplits++)
|
|
{
|
|
List<string> splits = new List<string> {};
|
|
for (int splitNr = 0; splitNr < numSplits; splitNr++)
|
|
{
|
|
int numWords = search.numWords;
|
|
if (splitNr == numSplits - 1) numWords -= random.Next(search.numWords);
|
|
|
|
string split = "";
|
|
for (int wi = 0; wi < search.numWords; wi++)
|
|
{
|
|
split += "w" + characters[w++ % characters.Length] + boundary[random.Next(boundary.Length)];
|
|
}
|
|
splits.Add(split.TrimEnd());
|
|
}
|
|
|
|
string text = String.Join(" ", splits);
|
|
List<(int, int)> indices = await search.Split(text);
|
|
for (int i = 0; i < indices.Count; i++)
|
|
{
|
|
(int startIndex, int endIndex) = indices[i];
|
|
string splitPred = text.Substring(startIndex, endIndex - startIndex + 1);
|
|
if (i != indices.Count - 1) splitPred = splitPred.Substring(0, splitPred.Length - 1);
|
|
Assert.AreEqual(splitPred, splits[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TestSentenceSplitter : TestSplitter<SentenceSplitter>
|
|
{
|
|
public override async Task Tests()
|
|
{
|
|
await base.Tests();
|
|
await TestSplit();
|
|
}
|
|
|
|
public async Task TestSplit()
|
|
{
|
|
async Task<string[]> SplitSentences(string text)
|
|
{
|
|
List<(int, int)> indices = await search.Split(text);
|
|
List<string> sentences = new List<string>();
|
|
foreach ((int startIndex, int endIndex) in indices) sentences.Add(text.Substring(startIndex, endIndex - startIndex + 1));
|
|
return sentences.ToArray();
|
|
}
|
|
|
|
string[] sentences = new string[]
|
|
{
|
|
"hi.",
|
|
"how are you today?",
|
|
"the weather is nice!",
|
|
"perfect"
|
|
};
|
|
string text;
|
|
string[] sentencesBack, sentencesGT;
|
|
int key;
|
|
|
|
sentencesGT = (string[])sentences.Clone();
|
|
text = String.Join("", sentencesGT);
|
|
sentencesBack = await SplitSentences(text);
|
|
Assert.AreEqual(sentencesBack, sentencesGT);
|
|
key = await search.Add(text);
|
|
Assert.AreEqual(search.Get(key), text);
|
|
|
|
sentencesGT = (string[])sentences.Clone();
|
|
sentencesGT[0] = " " + sentencesGT[0] + " ";
|
|
sentencesGT[1] += " ; ";
|
|
sentencesGT[2] += ".... ";
|
|
sentencesGT[3] += " ?";
|
|
text = String.Join("", sentencesGT);
|
|
sentencesBack = await SplitSentences(text);
|
|
Assert.AreEqual(sentencesBack, sentencesGT);
|
|
key = await search.Add(text);
|
|
Assert.AreEqual(search.Get(key), text);
|
|
|
|
for (int length = 10; length <= 100; length += 10)
|
|
{
|
|
(string randomText, List<(int, int)> indicesGT) = GenerateText(length);
|
|
List<(int, int)> indices = await search.Split(randomText);
|
|
Assert.AreEqual(indices.Count, indicesGT.Count);
|
|
Assert.AreEqual(indices, indicesGT);
|
|
key = await search.Add(randomText);
|
|
Assert.AreEqual(search.Get(key), randomText);
|
|
}
|
|
|
|
search.Clear();
|
|
}
|
|
}
|
|
|
|
public abstract class TestRAG : TestSearchable<RAG>
|
|
{
|
|
public override RAG CreateSearch()
|
|
{
|
|
RAG rag = gameObject.AddComponent<RAG>();
|
|
rag.Init(GetSearchMethod(), GetChunkingMethod(), llm);
|
|
return rag;
|
|
}
|
|
|
|
public abstract SearchMethods GetSearchMethod();
|
|
public abstract ChunkingMethods GetChunkingMethod();
|
|
}
|
|
|
|
public class TestRAG_SimpleSearch_NoChunking : TestRAG
|
|
{
|
|
public override SearchMethods GetSearchMethod() { return SearchMethods.SimpleSearch; }
|
|
public override ChunkingMethods GetChunkingMethod() { return ChunkingMethods.NoChunking; }
|
|
}
|
|
|
|
public class TestRAG_DBSearch_NoChunking : TestRAG
|
|
{
|
|
public override SearchMethods GetSearchMethod() { return SearchMethods.DBSearch; }
|
|
public override ChunkingMethods GetChunkingMethod() { return ChunkingMethods.NoChunking; }
|
|
}
|
|
|
|
public class TestRAG_SimpleSearch_WordSplitter : TestRAG
|
|
{
|
|
public override SearchMethods GetSearchMethod() { return SearchMethods.SimpleSearch; }
|
|
public override ChunkingMethods GetChunkingMethod() { return ChunkingMethods.TokenSplitter; }
|
|
}
|
|
|
|
public class TestRAG_DBSearch_TokenSplitter : TestRAG
|
|
{
|
|
public override SearchMethods GetSearchMethod() { return SearchMethods.DBSearch; }
|
|
public override ChunkingMethods GetChunkingMethod() { return ChunkingMethods.TokenSplitter; }
|
|
}
|
|
|
|
public abstract class TestRAG_Chunking : TestRAG
|
|
{
|
|
public override async Task TestSearch()
|
|
{
|
|
await base.TestSearch();
|
|
|
|
string[] results;
|
|
float[] distances;
|
|
|
|
await search.Add(weather + raining);
|
|
await search.Add(sometext);
|
|
|
|
search.ReturnChunks(false);
|
|
(results, distances) = await search.Search(weather, 1);
|
|
Assert.That(results.Length == 1);
|
|
Assert.That(distances.Length == 1);
|
|
Assert.AreEqual(results[0], weather + raining);
|
|
|
|
search.ReturnChunks(true);
|
|
(results, distances) = await search.Search(weather, 1);
|
|
Assert.That(results.Length == 1);
|
|
Assert.That(distances.Length == 1);
|
|
Assert.AreEqual(results[0], weather);
|
|
search.Clear();
|
|
}
|
|
}
|
|
|
|
public class TestRAG_DBSearch_SentenceSplitter : TestRAG_Chunking
|
|
{
|
|
public override SearchMethods GetSearchMethod() { return SearchMethods.DBSearch; }
|
|
public override ChunkingMethods GetChunkingMethod() { return ChunkingMethods.SentenceSplitter; }
|
|
}
|
|
|
|
public class TestRAG_SimpleSearch_SentenceSplitter : TestRAG_Chunking
|
|
{
|
|
public override SearchMethods GetSearchMethod() { return SearchMethods.SimpleSearch; }
|
|
public override ChunkingMethods GetChunkingMethod() { return ChunkingMethods.SentenceSplitter; }
|
|
}
|
|
}
|