diff --git a/Migration.md b/Migration.md new file mode 100644 index 00000000..bc32248b --- /dev/null +++ b/Migration.md @@ -0,0 +1,200 @@ +# **LLMUnity Migration Guide: Upgrading to the New Architecture** + +This guide helps you migrate to LLMUnity v3 from v2. + +The LLM backend, [LlamaLib](https://github.com/undreamai/LlamaLib), has been completely rewritten and most of the LLM functionality in Unity has now been ported to the backend.
+At the same time, LlamaLib has been implemented as a clean, high-level library that can been used on its own by C++ and C# developers.
+Special care has been taken to provide better support for future llama.cpp versions, streamlining more the upgrade process. + +## **Breaking Changes - Graphical Interface** +In terms of graphical interface in Unity, there have been almost no changes: +- The LLMCharacter class has been renamed to LLMAgent +- The chat templates have been removed from the LLM model management (LLM GameObjects) +- "Use extras" has been renamed to "Use cuBLAS" (LLM GameObjects) +- Grammars can be directly edited (LLMAgent GameObjects) + +## **Breaking Changes - Scripting** + +### **1\. LLM Server (LLM.cs)** + +#### **API Changes** + +**Setting Model:** + +``` c# +// OLD +llm.SetModel("path/to/model.gguf"); + +// NEW +llm.model = "path/to/model.gguf"; +``` + +**Setting Chat Template:** + +Setting the chat template is not needed anymore, it is handled by LlamaLib and loads the LLM-supported template automatically. + +``` c# +// OLD +llm.SetTemplate("chatml"); + +// NEW +// Template is auto-detected from model_ +``` + +### **2\. Chat Agent (LLMCharacter → LLMAgent)** + +LLMCharacter has been moved to a new class LLMAgent.
+**Note:** LLMCharacter still exists as a deprecated wrapper for backward compatibility, but will be removed in future versions. + +#### **Property changes** + +| **Old Property** | **New Property** | **Migration** | +| --- | --- | --- | +| playerName | userRole | Direct rename | +| AIName | assistantRole | Direct rename | +| prompt | systemPrompt | Direct rename | +| chat | chat | Now a property with getter/setter | +| saveCache | Removed | Cache management removed | + + +#### **API Changes** + +**Clear History:** +``` c# +// OLD +character.ClearChat(); + +// NEW +await agent.ClearHistory(); +``` + +**Setting / Changing the System Prompt:** +- Without clearing the history + +``` c# +// OLD +character.SetPrompt("You are a helpful assistant", clearChat: false); + +// NEW +agent.systemPrompt = "You are a helpful assistant"; +``` + +- Clearing the history +``` c# +// OLD +character.SetPrompt("You are a helpful assistant", clearChat: true); + +// NEW +agent.systemPrompt = "You are a helpful assistant"; +await agent.ClearHistory(); +``` + +**Managing History:** + +``` c# +character.AddPlayerMessage("Hello"); +character.AddAIMessage("Hi there"); + +// NEW +await agent.AddUserMessage("Hello"); +await agent.AddAssistantMessage("Hi there"); +``` + +**Saving/Loading:** + +``` c# +// OLD +await character.Save("chat_history"); +await character.Load("chat_history"); + +// NEW +await agent.SaveHistory(); +await agent.LoadHistory(); +``` + +The history path is directly the `character.save` property. + +**Grammar:** + +Setting the grammar directly: +``` c# +// OLD +llmClient.grammarString = "your grammar here"; + +// NEW +llmClient.grammar = "your grammar here"; +``` + +Loading a grammar file: +``` c# +// OLD +await character.SetGrammar("path/to/grammar.gbnf"); +await character.SetJSONGrammar("path/to/schema.json"); + +// NEW +llmClient.LoadGrammar("path/to/grammar.gbnf"); +llmClient.LoadGrammar("path/to/schema.json"); +``` + +### **Reasoning Mode (New Feature)** + +``` c# +// NEW - Enable "thinking" mode of the LLM +llm.reasoning = true; +// Or +llm.SetReasoning(true); +``` + +## **Migration Steps** + +### **Step 1: Update Class Inheritance** + +``` c# +// Change LLMCharacter to LLMAgent +public class MyNPC : LLMAgent // was LLMCharacter +{ +// Your code_ +} +``` + +### **Step 2: Update Property Names** + +``` c# +// In LLMAgent +agent.userRole = "Player"; // was character.playerName +agent.assistantRole = "AI"; // was character.AIName +agent.systemPrompt = "..."; // was character.prompt +``` + +### **Step 3: Update Method Calls** + +``` c# +// History management +await agent.ClearHistory(); // was character.ClearChat() +await agent.AddUserMessage("Hi"); // was character.AddPlayerMessage("Hi") +await agent.AddAssistantMessage("Hello"); // was character.AddAIMessage("Hello") +``` + +### **Step 4: Update Grammar** + +``` c# +agent.grammar = "your grammar here"; // was character.grammarString = "your grammar here" +// or +agent.LoadGrammar("grammar.gbnf"); // was await character.SetGrammar("path/to/grammar.gbnf"); or await character.SetJSONGrammar("path/to/schema.json"); +``` + +### **Step 5: Update Save/Load** + +``` c# +agent.save = "history"; // Set once +await agent.SaveHistory(); // was await character.Save("history"); +await agent.LoadHistory(); // was await character.Load("history"); +``` + +### **Step 6: Remove Chat Template** + +``` c# +// OLD +// Remove: +// llm.SetTemplate("phi-3"); +``` diff --git a/Migration.md.meta b/Migration.md.meta new file mode 100644 index 00000000..585a65b1 --- /dev/null +++ b/Migration.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 950db95695065d546bdb3e9f742544ce +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/LLMBuilder.cs b/Runtime/LLMBuilder.cs index 9d595c2a..a4c642c1 100644 --- a/Runtime/LLMBuilder.cs +++ b/Runtime/LLMBuilder.cs @@ -15,7 +15,7 @@ namespace LLMUnity public class LLMBuilder : AssetPostprocessor { static List movedPairs = new List(); - public static string BuildTempDir = Path.Combine(Application.temporaryCachePath, "LLMUnityBuild"); + public static string BuildTempDir = Path.Combine(Directory.GetParent(Application.dataPath).FullName, "LLMUnityBuild"); static string movedCache = Path.Combine(BuildTempDir, "moved.json"); [InitializeOnLoadMethod] @@ -36,6 +36,28 @@ public static string PluginLibraryDir(string platform, bool relative = false) return Path.Combine(PluginDir(platform, relative), LLMUnitySetup.libraryName); } + public static void Retry(System.Action action, int retries = 10, int delayMs = 100) + { + for (int i = 0; i < retries; i++) + { + try + { + action(); + return; + } + catch (IOException) + { + if (i == retries - 1) throw; + System.Threading.Thread.Sleep(delayMs); + } + catch (System.UnauthorizedAccessException) + { + if (i == retries - 1) throw; + System.Threading.Thread.Sleep(delayMs); + } + } + } + /// /// Performs an action for a file or a directory recursively /// @@ -46,7 +68,7 @@ public static void HandleActionFileRecursive(string source, string target, Actio { if (File.Exists(source)) { - actionCallback(source, target); + Retry(() => actionCallback(source, target)); } else if (Directory.Exists(source)) { @@ -106,8 +128,8 @@ public static bool DeletePath(string path) LLMUnitySetup.LogError($"Safeguard: {path} will not be deleted because it may not be safe"); return false; } - if (File.Exists(path)) File.Delete(path); - else if (Directory.Exists(path)) Directory.Delete(path, true); + if (File.Exists(path)) Retry(() => File.Delete(path)); + else if (Directory.Exists(path)) Retry(() => Directory.Delete(path, true)); return true; } @@ -300,8 +322,21 @@ public static void Reset() bool refresh = false; foreach (var pair in movedPairs) { - if (pair.source == "") refresh |= DeletePath(pair.target); - else refresh |= MoveAction(pair.target, pair.source, false); + if (pair.source == "") + { + refresh |= DeletePath(pair.target); + } + else + { + if (File.Exists(pair.source) || Directory.Exists(pair.source)) + { + refresh |= DeletePath(pair.target); + } + else + { + refresh |= MoveAction(pair.target, pair.source, false); + } + } } if (refresh) AssetDatabase.Refresh(); DeletePath(movedCache);