Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/doxygen/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ PROJECT_NAME = "LLM for Unity"
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = v3.0.2
PROJECT_NUMBER = v3.0.3

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## v3.0.3
#### 🚀 Features

- Support Qwen 3.5 models (PR: #391)
- Upgrade LlamaLib to v2.0.4 (llama.cpp b8209) (PR: #391)
- add button to redownload LlamaLib (PR: #393)


## v3.0.2
#### 🚀 Features

Expand Down
13 changes: 3 additions & 10 deletions CHANGELOG.release.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
### 🚀 Features

- Cache LlamaLib to prevent re-downloads (PR: #386)
- Implement strategies for context overflow (chat truncation, chat summarization) (PR: #384)
- Upgrade LlamaLib to v2.0.4 (PR: #384)
- Re-introduce UI dropdown for level of debug messages (PR: #384)

### 🐛 Fixes

- Fix context overflow with caching and overflow strategies (PR: #384)
- Ensure macOS build includes the required runtime library (PR: #382)
- Fix inference for AMD GPUs using Vulkan (PR: #384)
- Support Qwen 3.5 models (PR: #391)
- Upgrade LlamaLib to v2.0.4 (llama.cpp b8209) (PR: #391)
- add button to redownload LlamaLib (PR: #393)

18 changes: 18 additions & 0 deletions Editor/LLMEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,24 @@ async Task createButtons()
}
}

public override async Task AddOtherToggles()
{
if (GUILayout.Button("Redownload libraries", GUILayout.Width(buttonWidth)))
{
bool confirmed = EditorUtility.DisplayDialog(
"Confirm Action",
"Are you sure you want to redownload the libraries?",
"Yes",
"Cancel"
);

if (confirmed)
{
await LLMUnitySetup.RedownloadLibrary();
}
}
}

async Task AddLoadButtons()
{
if (showCustomURL) await createCustomURLField();
Expand Down
7 changes: 7 additions & 0 deletions Editor/PropertyEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace LLMUnity
{
Expand Down Expand Up @@ -96,11 +97,17 @@ public virtual void AddLogo()
}
}

public virtual async Task AddOtherToggles()
{
await Task.CompletedTask;
}

public virtual void AddOptionsToggles(SerializedObject llmScriptSO)
{
AddLogo();
AddAdvancedOptionsToggle(llmScriptSO);
AddDebugModeToggle();
_ = AddOtherToggles();
Space();
}

Expand Down
85 changes: 52 additions & 33 deletions Runtime/LLMUnitySetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ public class LLMUnitySetup
{
// DON'T CHANGE! the version is autocompleted with a GitHub action
/// <summary> LLM for Unity version </summary>
public static string Version = "v3.0.2";
public static string Version = "v3.0.3";
/// <summary> LlamaLib version </summary>
public static string LlamaLibVersion = "v2.0.4";
public static string LlamaLibVersion = "v2.0.5";
/// <summary> LlamaLib release url </summary>
public static string LlamaLibReleaseURL = $"https://github.com/undreamai/LlamaLib/releases/download/{LlamaLibVersion}";
/// <summary> LlamaLib name </summary>
Expand All @@ -115,6 +115,8 @@ public class LLMUnitySetup
public static string modelDownloadPath = Path.Combine(LLMUnityStore, "models");
/// <summary> cache download path </summary>
public static string cacheDownloadPath = Path.Combine(LLMUnityStore, "cache");
public static string cacheZipPath = Path.Combine(cacheDownloadPath, Path.GetFileName(LlamaLibURL));
public static string cacheZipHashPath = cacheZipPath + ".sha256";
/// <summary> Path of file with build information for runtime </summary>
public static string LLMManagerPath = GetAssetPath("LLMManager.json");

Expand All @@ -131,8 +133,8 @@ public class LLMUnitySetup
}},
{"Medium models (up to 10B)", new(string, string, string)[]
{
("Qwen 3.5 9B", "https://huggingface.co/unsloth/Qwen3.5-9B-GGUF/resolve/main/Qwen3.5-9B-Q4_K_M.gguf", null),
("Llama 3.1 8B", "https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF/resolve/main/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf", "https://huggingface.co/meta-llama/Meta-Llama-3.1-8B/blob/main/LICENSE"),
("Qwen 3 8B", "https://huggingface.co/unsloth/Qwen3-8B-GGUF/resolve/main/Qwen3-8B-Q4_K_M.gguf", null),
("DeepSeek R1 Distill Llama 8B", "https://huggingface.co/lmstudio-community/DeepSeek-R1-Distill-Llama-8B-GGUF/resolve/main/DeepSeek-R1-Distill-Llama-8B-Q4_K_M.gguf", null),
("DeepSeek R1 Distill Qwen 7B", "https://huggingface.co/lmstudio-community/DeepSeek-R1-Distill-Qwen-7B-GGUF/resolve/main/DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf", null),
("Gemma 2 9B it", "https://huggingface.co/bartowski/gemma-2-9b-it-GGUF/resolve/main/gemma-2-9b-it-Q4_K_M.gguf", "https://ai.google.dev/gemma/terms"),
Expand All @@ -141,17 +143,17 @@ public class LLMUnitySetup
}},
{"Small models (up to 5B)", new(string, string, string)[]
{
("Qwen 3.5 4B", "https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-Q4_K_M.gguf", null),
("Llama 3.2 3B", "https://huggingface.co/hugging-quants/Llama-3.2-3B-Instruct-Q4_K_M-GGUF/resolve/main/llama-3.2-3b-instruct-q4_k_m.gguf", "https://huggingface.co/meta-llama/Llama-3.2-1B/blob/main/LICENSE.txt"),
("Gemma 3 4B", "https://huggingface.co/lmstudio-community/gemma-3-4b-it-GGUF/resolve/main/gemma-3-4b-it-Q4_K_M.gguf", "https://ai.google.dev/gemma/terms"),
("Phi 4 4B", "https://huggingface.co/bartowski/microsoft_Phi-4-mini-instruct-GGUF/resolve/main/microsoft_Phi-4-mini-instruct-Q4_K_M.gguf", null),
("Qwen 3 4B", "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q4_K_M.gguf", null),
}},
{"Tiny models (up to 2B)", new(string, string, string)[]
{
("Qwen 3.5 2B", "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/Qwen3.5-2B-Q4_K_M.gguf", null),
("Qwen 3.5 0.8B", "https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/main/Qwen3.5-0.8B-Q4_K_M.gguf", null),
("Llama 3.2 1B", "https://huggingface.co/hugging-quants/Llama-3.2-1B-Instruct-Q4_K_M-GGUF/resolve/main/llama-3.2-1b-instruct-q4_k_m.gguf", "https://huggingface.co/meta-llama/Llama-3.2-1B/blob/main/LICENSE.txt"),
("Gemma 3 1B", "https://huggingface.co/lmstudio-community/gemma-3-1b-it-GGUF/resolve/main/gemma-3-1b-it-Q4_K_M.gguf", "https://ai.google.dev/gemma/terms"),
("Qwen 3 1.7B", "https://huggingface.co/unsloth/Qwen3-1.7B-GGUF/resolve/main/Qwen3-1.7B-Q4_K_M.gguf", null),
("Qwen 3 0.6B", "https://huggingface.co/unsloth/Qwen3-0.6B-GGUF/resolve/main/Qwen3-0.6B-Q4_K_M.gguf", null),
("DeepSeek R1 Distill Qwen 1.5B", "https://huggingface.co/lmstudio-community/DeepSeek-R1-Distill-Qwen-1.5B-GGUF/resolve/main/DeepSeek-R1-Distill-Qwen-1.5B-Q4_K_M.gguf", null),
}},
{"RAG models", new(string, string, string)[]
Expand Down Expand Up @@ -195,9 +197,11 @@ public static void LogWarning(string message)

public static void LogError(string message, bool throwException = false)
{
if ((int)DebugMode > (int)DebugModeType.Error) return;
Debug.LogError(message);
foreach (Action<string> errorCallback in errorCallbacks) errorCallback(message);
if ((int)DebugMode <= (int)DebugModeType.Error || throwException)
{
Debug.LogError(message);
foreach (Action<string> errorCallback in errorCallbacks) errorCallback(message);
}
if (throwException) throw new LLMUnityException(message);
}

Expand Down Expand Up @@ -453,45 +457,45 @@ static void ExtractInsideDirectory(string zipPath, string extractPath, string pr
}
}

static async Task DownloadAndExtractInsideDirectory(string url, string path, string setupDir)
static async Task DownloadAndExtractInsideDirectory()
{
string urlName = Path.GetFileName(url);
string zipPath = Path.Combine(cacheDownloadPath, urlName);
string setupFile = Path.Combine(setupDir, urlName + ".complete");
string setupDir = Path.Combine(libraryPath, "setup");
Directory.CreateDirectory(setupDir);

string setupFile = Path.Combine(setupDir, Path.GetFileName(LlamaLibURL) + ".complete");
if (File.Exists(setupFile)) return;

Directory.CreateDirectory(cacheDownloadPath);
foreach (string existingZipPath in Directory.GetFiles(cacheDownloadPath, "*.zip"))
{
if (existingZipPath != zipPath)
if (existingZipPath != cacheZipPath)
{
Debug.Log(existingZipPath);
File.Delete(existingZipPath);
}
}

string hashurl = url + ".sha256";
string hashPath = zipPath + ".sha256";
string hash = File.Exists(hashPath)? File.ReadAllText(hashPath).Trim() : "";
string hashurl = LlamaLibURL + ".sha256";
string cacheZipNewHashPath = cacheZipHashPath + ".new";
string hash = File.Exists(cacheZipHashPath)? File.ReadAllText(cacheZipHashPath).Trim() : "";
bool same_hash = false;
try
{
new ResumingWebClient().GetURLFileSize(hashurl); // avoid showing error if url doesn't exist
await DownloadFile(hashurl, hashPath+".new", debug: false);
same_hash = File.ReadAllText(hashPath+".new").Trim() == hash;
await DownloadFile(hashurl, cacheZipNewHashPath, debug: false);
same_hash = File.ReadAllText(cacheZipNewHashPath).Trim() == hash;
} catch {}

if (!File.Exists(zipPath) || !same_hash) await DownloadFile(url, zipPath, true, null, SetLibraryProgress);
if (!File.Exists(cacheZipPath) || !same_hash) await DownloadFile(LlamaLibURL, cacheZipPath, true, null, SetLibraryProgress);

AssetDatabase.StartAssetEditing();
ExtractInsideDirectory(zipPath, path, $"{libraryName}/runtimes/");
ExtractInsideDirectory(cacheZipPath, libraryPath, $"{libraryName}/runtimes/");
CreateEmptyFile(setupFile);
AssetDatabase.StopAssetEditing();

if (File.Exists(hashPath+".new"))
if (File.Exists(cacheZipNewHashPath))
{
if (File.Exists(hashPath)) File.Delete(hashPath);
File.Move(hashPath+".new", hashPath);
if (File.Exists(cacheZipHashPath)) File.Delete(cacheZipHashPath);
File.Move(cacheZipNewHashPath, cacheZipHashPath);
}
}

Expand Down Expand Up @@ -526,26 +530,41 @@ static void DeleteEarlierVersions()
static async Task DownloadLibrary()
{
if (libraryProgress < 1) return;
libraryProgress = 0;

try
{
DeleteEarlierVersions();

string setupDir = Path.Combine(libraryPath, "setup");
Directory.CreateDirectory(setupDir);

// setup LlamaLib in StreamingAssets
await DownloadAndExtractInsideDirectory(LlamaLibURL, libraryPath, setupDir);
}
catch (Exception e)
{
LogError(e.Message);
}

for (int i=1; i<=3; i++)
{
if (i > 1) Log("Downloading LlamaLib failed, try #" + i);
libraryProgress = 0;
try
{
await DownloadAndExtractInsideDirectory();
break;
}
catch (Exception e)
{
LogError(e.Message);
}
}

libraryProgress = 1;
}

public static async Task RedownloadLibrary()
{
if (File.Exists(cacheZipPath)) File.Delete(cacheZipPath);
if (File.Exists(cacheZipHashPath)) File.Delete(cacheZipHashPath);
if (Directory.Exists(libraryPath)) Directory.Delete(libraryPath, true);
await DownloadLibrary();
}

private static void SetLibraryProgress(float progress)
{
libraryProgress = Math.Min(0.99f, progress);
Expand Down
8 changes: 4 additions & 4 deletions Tests/Editor/TestLLM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public virtual void SetParameters()
}
else
{
reply1 = "Sure! Here's a fun fact: Ants are among the most common insects, often found in human homes, and they play an important role in soil fertility by breaking down organic matter.";
reply1 = "Sure! Here's a fun fact: Ants work together to build complex structures like nests, which is a fascinating example of teamwork.";
reply2 = "Of course! Here's a fun fact: Ants are among the most common insects, often found in human homes, and they play an important role in soil fertility by breaking down organic matter.";
}
tokens1 = 20;
Expand Down Expand Up @@ -336,7 +336,7 @@ public void TestChat(string reply, string replyGT)
var commonWords = words1.Intersect(words2).Count();
var totalWords = Math.Max(words1.Length, words2.Length);

Assert.That((double)commonWords / totalWords >= 0.5);
Assert.That((double)commonWords / totalWords >= 0.25);
}

public void TestPostChat(int num)
Expand Down Expand Up @@ -566,7 +566,7 @@ public override void SetParameters()
else
{
reply1 = "Ants are known for their ability to build complex structures, such as hexagons, which is a key feature of their social behavior.";
reply2 = "Of course! Ants are so sneaky and clever that they can even build intricate nests with just a few minutes of effort—no matter where you start!";
reply2 = "Of course! Ants are so smart—well, they’re not really!";
}
}

Expand Down Expand Up @@ -617,7 +617,7 @@ public override void SetParameters()
if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
{
reply1 = "Sure! Here's a fun fact: Ants are among the most common insects, often found in human homes or gardens.";
reply2 = "Of course! Here’s a fun fact: Ants are so tiny that they can fit into the space between your fingers, but they’re actually super effective at building nests and keeping the soil alive.";
reply2 = "Of course! \"Ants are the most efficient insects on Earth—working together to build intricate nests and survive!\"";
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v3.0.2
v3.0.3
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ai.undream.llm",
"version": "3.0.2",
"version": "3.0.3",
"displayName": "LLM for Unity",
"description": "LLM for Unity allows to run and distribute Large Language Models (LLMs) in the Unity engine.",
"unity": "2022.3",
Expand Down
Loading