Mate-Engine/Assets/LLMUnity/Runtime/LLMUtils.cs
shinyflvre 39a7b41d7d 1.4.0 Pre Release
**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!
2025-04-08 19:16:19 +02:00

204 lines
6.5 KiB
C#

/// @file
/// @brief File implementing LLM helper code.
using System;
using System.Collections.Generic;
namespace LLMUnity
{
/// @ingroup utils
/// <summary>
/// Class implementing a basic LLM Exception
/// </summary>
public class LLMException : Exception
{
public int ErrorCode { get; private set; }
public LLMException(string message, int errorCode) : base(message)
{
ErrorCode = errorCode;
}
}
/// @ingroup utils
/// <summary>
/// Class implementing a basic LLM Destroy Exception
/// </summary>
public class DestroyException : Exception {}
/// @ingroup utils
/// <summary>
/// Class representing a LORA asset
/// </summary>
public class LoraAsset
{
public string assetPath;
public string fullPath;
public float weight;
public LoraAsset(string path, float weight = 1)
{
assetPath = LLM.GetLLMManagerAsset(path);
fullPath = RuntimePath(path);
this.weight = weight;
}
public static string RuntimePath(string path)
{
return LLMUnitySetup.GetFullPath(LLM.GetLLMManagerAssetRuntime(path));
}
}
/// @ingroup utils
/// <summary>
/// Class representing the LORA manager allowing to convert and retrieve LORA assets to string (for serialisation)
/// </summary>
public class LoraManager
{
List<LoraAsset> loras = new List<LoraAsset>();
public string delimiter = ",";
/// <summary>
/// Clears the LORA assets
/// </summary>
public void Clear()
{
loras.Clear();
}
/// <summary>
/// Searches for a LORA based on the path
/// </summary>
/// <param name="path">LORA path</param>
/// <returns>LORA index</returns>
public int IndexOf(string path)
{
string fullPath = LoraAsset.RuntimePath(path);
for (int i = 0; i < loras.Count; i++)
{
LoraAsset lora = loras[i];
if (lora.assetPath == path || lora.fullPath == fullPath) return i;
}
return -1;
}
/// <summary>
/// Checks if the provided LORA based on a path exists already in the LORA manager
/// </summary>
/// <param name="path">LORA path</param>
/// <returns>whether the LORA manager contains the LORA</returns>
public bool Contains(string path)
{
return IndexOf(path) != -1;
}
/// <summary>
/// Adds a LORA with the defined weight
/// </summary>
/// <param name="path">LORA path</param>
/// <param name="weight">LORA weight</param>
public void Add(string path, float weight = 1)
{
if (Contains(path)) return;
loras.Add(new LoraAsset(path, weight));
}
/// <summary>
/// Removes a LORA based on its path
/// </summary>
/// <param name="path">LORA path</param>
public void Remove(string path)
{
int index = IndexOf(path);
if (index != -1) loras.RemoveAt(index);
}
/// <summary>
/// Modifies the weight of a LORA
/// </summary>
/// <param name="path">LORA path</param>
/// <param name="weight">LORA weight</param>
public void SetWeight(string path, float weight)
{
int index = IndexOf(path);
if (index == -1)
{
LLMUnitySetup.LogError($"LoRA {path} not loaded with the LLM");
return;
}
loras[index].weight = weight;
}
/// <summary>
/// Converts strings with the lora paths and weights to entries in the LORA manager
/// </summary>
/// <param name="loraString">lora paths</param>
/// <param name="loraWeightsString">lora weights</param>
public void FromStrings(string loraString, string loraWeightsString)
{
if (string.IsNullOrEmpty(loraString) && string.IsNullOrEmpty(loraWeightsString))
{
Clear();
return;
}
try
{
List<string> loraStringArr = new List<string>(loraString.Split(delimiter));
List<string> loraWeightsStringArr = new List<string>(loraWeightsString.Split(delimiter));
if (loraStringArr.Count != loraWeightsStringArr.Count) throw new Exception($"LoRAs number ({loraString}) doesn't match the number of weights ({loraWeightsString})");
List<LoraAsset> lorasNew = new List<LoraAsset>();
for (int i = 0; i < loraStringArr.Count; i++) lorasNew.Add(new LoraAsset(loraStringArr[i].Trim(), float.Parse(loraWeightsStringArr[i])));
loras = lorasNew;
}
catch (Exception e)
{
LLMUnitySetup.LogError($"Loras not set: {e.Message}");
}
}
/// <summary>
/// Converts the entries of the LORA manager to strings with the lora paths and weights
/// </summary>
/// <returns>strings with the lora paths and weights</returns>
public (string, string) ToStrings()
{
string loraString = "";
string loraWeightsString = "";
for (int i = 0; i < loras.Count; i++)
{
if (i > 0)
{
loraString += delimiter;
loraWeightsString += delimiter;
}
loraString += loras[i].assetPath;
loraWeightsString += loras[i].weight;
}
return (loraString, loraWeightsString);
}
/// <summary>
/// Gets the weights of the LORAs in the manager
/// </summary>
/// <returns>LORA weights</returns>
public float[] GetWeights()
{
float[] weights = new float[loras.Count];
for (int i = 0; i < loras.Count; i++) weights[i] = loras[i].weight;
return weights;
}
/// <summary>
/// Gets the paths of the LORAs in the manager
/// </summary>
/// <returns>LORA paths</returns>
public string[] GetLoras()
{
string[] loraPaths = new string[loras.Count];
for (int i = 0; i < loras.Count; i++) loraPaths[i] = loras[i].assetPath;
return loraPaths;
}
}
/// \endcond
}