Home

Awesome

<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset=".github/logo_white.png"> <source media="(prefers-color-scheme: light)" srcset=".github/logo.png"> <img src=".github/logo.png" height="150"/> </picture> </p> <h3 align="center">Create characters in Unity with LLMs!</h3>

License: MIT <a href="https://discord.gg/RwXKQb6zdv"><img src="https://discordapp.com/api/guilds/1194779009284841552/widget.png?style=shield"/></a> Reddit LinkedIn Asset Store GitHub Repo stars Documentation

LLM for Unity enables seamless integration of Large Language Models (LLMs) within the Unity engine.<br> It allows to create intelligent characters that your players can interact with for an immersive experience.<br> The package also features a Retrieval-Augmented Generation (RAG) system that allows to performs semantic search across your data, which can be used to enhance the character's knowledge. LLM for Unity is built on top of the awesome llama.cpp library.

<sub> <a href="#at-a-glance" style="color: black">At a glance</a>&nbsp;&nbsp;•&nbsp; <a href="#how-to-help" style=color: black>How to help</a>&nbsp;&nbsp;•&nbsp; <a href="#games-using-llm-for-unity" style=color: black>Games using LLM for Unity</a>&nbsp;&nbsp;•&nbsp; <a href="#setup" style=color: black>Setup</a>&nbsp;&nbsp;•&nbsp; <a href="#how-to-use" style=color: black>How to use</a>&nbsp;&nbsp;•&nbsp; <a href="#semantic-search-with-a-retrieval-augmented-generation-rag-system" style=color: black>RAG</a>&nbsp;&nbsp;•&nbsp; <a href="#llm-model-management" style=color: black>LLM model management</a>&nbsp;&nbsp;•&nbsp; <a href="#examples" style=color: black>Examples</a>&nbsp;&nbsp;•&nbsp; <a href="#options" style=color: black>Options</a>&nbsp;&nbsp;•&nbsp; <a href="#license" style=color: black>License</a> </sub>

At a glance

🧪 Tested on Unity: 2021 LTS, 2022 LTS, 2023, Unity 6<br> 🚦 Upcoming Releases

How to help

Games using LLM for Unity

Contact us to add your project!

Setup

Method 1: Install using the asset store

Method 2: Install using the GitHub repo:

How to use

<img height="300" src=".github/character.png"/>

First you will setup the LLM for your game 🏎:

Then you can setup each of your characters as follows 🙋‍♀️:

You can also adjust the LLM and character settings according to your preference (see Options).

In your script you can then use it as follows 🦄:

using LLMUnity;

public class MyScript {
  public LLMCharacter llmCharacter;
  
  void HandleReply(string reply){
    // do something with the reply from the model
    Debug.Log(reply);
  }
  
  void Game(){
    // your game function
    ...
    string message = "Hello bot!";
    _ = llmCharacter.Chat(message, HandleReply);
    ...
  }
}

You can also specify a function to call when the model reply has been completed.<br> This is useful if the Stream option is enabled for continuous output from the model (default behaviour):

  void ReplyCompleted(){
    // do something when the reply from the model is complete
    Debug.Log("The AI replied");
  }
  
  void Game(){
    // your game function
    ...
    string message = "Hello bot!";
    _ = llmCharacter.Chat(message, HandleReply, ReplyCompleted);
    ...
  }

To stop the chat without waiting for its completion you can use:

    llmCharacter.CancelRequests();

That's all ✨! <br><br> You can also:

<details> <summary>Build a mobile app</summary>

iOS iOS can be built with the default player settings.

Android On Android you need to specify the IL2CPP scripting backend and the ARM64 as the target architecture in the player settings.<br> These settings can be accessed from the Edit > Project Settings menu within the Player > Other Settings section.<br> <img width="400" src=".github/android.png">

Since mobile app sizes are typically small, you can download the LLM models the first time the app launches. This functionality can be enabled with the Download on Build option. In your project you can wait until the model download is complete with:

await LLM.WaitUntilModelSetup();

You can also receive calls during the download with the download progress:

await LLM.WaitUntilModelSetup(SetProgress);

void SetProgress(float progress){
  string progressPercent = ((int)(progress * 100)).ToString() + "%";
  Debug.Log($"Download progress: {progressPercent}");
}

This is useful to present a progress bar or something similar. The MobileDemo is an example application for Android / iOS.

</details> <details> <summary>Restrict the output of the LLM / Function calling</summary>

To restrict the output of the LLM you can use a GBNF grammar, read more here.<br> The grammar can be saved in a .gbnf file and loaded at the LLMCharacter with the Load Grammar button (Advanced options).<br> For instance to receive replies in json format you can use the json.gbnf grammar.<br>

Alternatively you can set the grammar directly with code:

llmCharacter.grammarString = "your grammar here";

For function calling you can define similarly a grammar that allows only the function names as output, and then call the respective function.<br> You can look into the FunctionCalling sample for an example implementation.

</details> <details> <summary>Access / Save / Load your chat history</summary> The chat history of a `LLMCharacter` is retained in the `chat` variable that is a list of `ChatMessage` objects.<br> The ChatMessage is a struct that defines the `role` of the message and the `content`.<br> The first element of the list is always the system prompt and then alternating messages with the player prompt and the AI reply.<br> You can modify the chat history directly in this list.<br>

To automatically save / load your chat history, you can specify the Save parameter of the LLMCharacter to the filename (or relative path) of your choice. The file is saved in the persistentDataPath folder of Unity. This also saves the state of the LLM which means that the previously cached prompt does not need to be recomputed.

To manually save your chat history, you can use:

    llmCharacter.Save("filename");

and to load the history:

    llmCharacter.Load("filename");

where filename the filename or relative path of your choice.

</details> <details> <summary>Process the prompt at the beginning of your app for faster initial processing time</summary>
  void WarmupCompleted(){
    // do something when the warmup is complete
    Debug.Log("The AI is nice and ready");
  }

  void Game(){
    // your game function
    ...
    _ = llmCharacter.Warmup(WarmupCompleted);
    ...
  }
</details> <details> <summary>Decide whether or not to add the message to the chat/prompt history</summary>

The last argument of the Chat function is a boolean that specifies whether to add the message to the history (default: true):

  void Game(){
    // your game function
    ...
    string message = "Hello bot!";
    _ = llmCharacter.Chat(message, HandleReply, ReplyCompleted, false);
    ...
  }
</details> <details> <summary>Use pure text completion</summary>
  void Game(){
    // your game function
    ...
    string message = "The cat is away";
    _ = llmCharacter.Complete(message, HandleReply, ReplyCompleted);
    ...
  }
</details> <details> <summary>Wait for the reply before proceeding to the next lines of code</summary>

For this you can use the async/await functionality:

  async void Game(){
    // your game function
    ...
    string message = "Hello bot!";
    string reply = await llmCharacter.Chat(message, HandleReply, ReplyCompleted);
    Debug.Log(reply);
    ...
  }
</details> <details> <summary>Add a LLM / LLMCharacter component programmatically</summary>
using UnityEngine;
using LLMUnity;

public class MyScript : MonoBehaviour
{
    LLM llm;
    LLMCharacter llmCharacter;

    async void Start()
    {
        // disable gameObject so that theAwake is not called immediately
        gameObject.SetActive(false);

        // Add an LLM object
        llm = gameObject.AddComponent<LLM>();
        // set the model using the filename of the model.
        // The model needs to be added to the LLM model manager (see LLM model management) by loading or downloading it.
        // Otherwise the model file can be copied directly inside the StreamingAssets folder.
        llm.SetModel("Phi-3-mini-4k-instruct-q4.gguf");
        // optional: you can also set loras in a similar fashion and set their weights (if needed)
        llm.AddLora("my-lora.gguf");
        llm.SetLoraWeight(0.5f);
        // optional: you can set the chat template of the model if it is not correctly identified
        // You can find a list of chat templates in the ChatTemplate.templates.Keys
        llm.SetTemplate("phi-3");
        // optional: set number of threads
        llm.numThreads = -1;
        // optional: enable GPU by setting the number of model layers to offload to it
        llm.numGPULayers = 10;

        // Add an LLMCharacter object
        llmCharacter = gameObject.AddComponent<LLMCharacter>();
        // set the LLM object that handles the model
        llmCharacter.llm = llm;
        // set the character prompt
        llmCharacter.SetPrompt("A chat between a curious human and an artificial intelligence assistant.");
        // set the AI and player name
        llmCharacter.AIName = "AI";
        llmCharacter.playerName = "Human";
        // optional: set streaming to false to get the complete result in one go
        // llmCharacter.stream = true;
        // optional: set a save path
        // llmCharacter.save = "AICharacter1";
        // optional: enable the save cache to avoid recomputation when loading a save file (requires ~100 MB)
        // llmCharacter.saveCache = true;
        // optional: set a grammar
        // await llmCharacter.SetGrammar("json.gbnf");

        // re-enable gameObject
        gameObject.SetActive(true);
    }
}
</details> <details> <summary>Use a remote server</summary>

You can use a remote server to carry out the processing and implement characters that interact with it.

Create the server<br> To create the server:

Alternatively you can use a server binary for easier deployment:

Create the characters<br> Create a second project with the game characters using the LLMCharacter script as described above. Enable the Remote option and configure the host with the IP address (starting with "http://") and port of the server.

</details> <details> <summary>Compute embeddings using a LLM</summary>

The Embeddings function can be used to obtain the emdeddings of a phrase:

    List<float> embeddings = await llmCharacter.Embeddings("hi, how are you?");
</details>

A <b>detailed documentation</b> on function level can be found here: <a href="https://undream.ai/LLMUnity"><img src="https://img.shields.io/badge/Documentation-white.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwEAYAAAAHkiXEAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAATqSURBVHic7ZtbiE1RGMc349K4M5EwklwjzUhJCMmTJPJAYjQXJJcH8+Blkry4lPJA8aAoJbekDLmUS6E8SHJL5AW5JPf77eHv93C22Wfttc/ee+0zc/4vv+bMXvusvfZa3/q+b33H80oqqaSSSmqrKnPdgXjUvbvYq5f4+7f486eb/rRajRsn7t4tPngg/vol/vkj/vghXr0q7tghzpyZ//79+on79omXLombNondukXrd9GoSxdx8mSxqUm8eVNkgAvl0aPioEFip07i6dP52z15Ig4fbvVY2VVFhbhokXjrlogJiWvAg/jwoXjqVO73+leUny9eiFVV5mfMlLDRBw+KX76ISQ+0LZ8/F00v4uJFsWPHFh83O+rdWzx3TnQ9wCZ+/Sqyl5iux1RmTu3aiYcPi64H1pasALypoOv4/8SJXraEbXc9kLbECxo2TKyuFj9/zt9u+XIvG8LWv3wpuh5QW86f3/JznT+fv93s2S23C1Z72wbhtH692LdvMvdPSgzkhAkiJhT16ZO/PRPOmcr+Rda4aa5nclTeuZP7PDgRpr1g40bPrQYOFF0PYKHEC+raVVy8OFy7R49EArvURU4mrUAqaTY0iB8/2rXD+XCm5mbR9QAWylevorV7/VpkL0ld06eLpkiyWPj9u93179+LpFZwZ1PXtGnitWui64GMStPmG7SH1NSIJBNHjvTSFZvRvHlise0N9JcBtW1/44Y4dqx45IjnU0JxAGLpklPx+9VZFwPp/9v/eZDGjxcZh7dv4+mXtch+up7Rca+MsJvxiRNi6nvBhg25HWprZMaPGeOlqxEjxGKz+XGRTAAmyJnq6sR370TXA2NLW+8HNjZ62dLOnaLrAQ1r2zmqPH482n0mTfJCKmEvCJHUooNZE/369Elct06kqiKsONRfulTEFDsX8QDlIa5nup9374pE8IiZHPY+ly+LZE/37/cM6mC6IB6Vl4urV6fzfUG6d0/csyf37wsXRFInaM4ckTjGdPg+apTYs6dI3RIWwH//1DV1qkiuxNY2FzrTd+2y6y8z2HQU6efZs+KBAyJZ4v+V0h6ArlwROaQP0uPH4ooV4sqV8Xz/4MF211M2wwoOq1mzRAq5Pnywa5+4KDHE9mI7ly0TO3fOvZ6/eZCoKwB32HS0SMFV1DNtImBKHYstBROoQ4fEQk2RaS+qrxejmj5M7NatIhWARS82xUJfAKahzFcdPnq0GLYgy7Rnbd8e6rGKRyzpuNzPBQty709RcNSZf/KkuHCh2GpMDyKbGNcLYE+YMkVks336NFx7XhTZ3szXiBaqtWvFuAOxM2dEZiyH8UErgc8JLNun7E0aFffSI7RP6owZmz9kSO73HjsmXr8ukppYsybSYyQvBp5QfOjQ3M9tRR496pGgLf1JtLlzRZJzlFzGp4SWDnUxFCrdvy+uWiWa3DJe3N69oj8uSEq8CER88uaNOGBAOv2ILGY69TBBJoM8O0t72zaRoztXBzlLlrT8XARW/IQq82JTMv3mKmv0/9CC4mJMYPwrMSETxAyurRUxQVmXP1fEid7mzeK3b+n2Jzb16CFu2SIWmtNJiriVxANsyq0uoCJfTk4G9y4t24/bSQ0rTkP6gVTG3mz//uKMGSK/ucId5Xe9lZUi5eMMLGUgz56J5Hxu3xZ50Xg3RMIltVn9BRja26PYsBHgAAAAAElFTkSuQmCC"/></a>

Semantic search with a Retrieval-Augmented Generation (RAG) system

LLM for Unity implements a super-fast similarity search functionality with a Retrieval-Augmented Generation (RAG) system.<br> It is based on the LLM functionality, and the Approximate Nearest Neighbors (ANN) search from the usearch library.<br> Semantic search works as follows.

Building the data You provide text inputs (a phrase, paragraph, document) to add to the data.<br> Each input is split into chunks (optional) and encoded into embeddings with a LLM.

Searching You can then search for a query text input. <br> The input is again encoded and the most similar text inputs or chunks in the data are retrieved.

To use semantic serch:

Alternatively, you can create the RAG from code (where llm is your LLM):

  RAG rag = gameObject.AddComponent<RAG>();
  rag.Init(SearchMethods.DBSearch, ChunkingMethods.SentenceSplitter, llm);

In your script you can then use it as follows :unicorn::

using LLMUnity;

public class MyScript : MonoBehaviour
{
  RAG rag;

  async void Game(){
    ...
    string[] inputs = new string[]{
      "Hi! I'm a search system.",
      "the weather is nice. I like it.",
      "I'm a RAG system"
    };
    // add the inputs to the RAG
    foreach (string input in inputs) await rag.Add(input);
    // get the 2 most similar inputs and their distance (dissimilarity) to the search query
    (string[] results, float[] distances) = await rag.Search("hello!", 2);
    // to get the most similar text parts (chnuks) you can enable the returnChunks option
    rag.ReturnChunks(true);
    (results, distances) = await rag.Search("hello!", 2);
    ...
  }
}

You can also add / search text inputs for groups of data e.g. for a specific character or scene:

    // add the inputs to the RAG for a group of data e.g. an orc character
    foreach (string input in inputs) await rag.Add(input, "orc");
    // get the 2 most similar inputs for the group of data e.g. the orc character
    (string[] results, float[] distances) = await rag.Search("how do you feel?", 2, "orc");
...

You can save the RAG state (stored in the `Assets/StreamingAssets` folder):
``` c#
rag.Save("rag.zip");

and load it from disk:

await rag.Load("rag.zip");

You can use the RAG to feed relevant data to the LLM based on a user message:

  string message = "How is the weather?";
  (string[] similarPhrases, float[] distances) = await rag.Search(message, 3);

  string prompt = "Answer the user query based on the provided data.\n\n";
  prompt += $"User query: {message}\n\n";
  prompt += $"Data:\n";
  foreach (string similarPhrase in similarPhrases) prompt += $"\n- {similarPhrase}";

  _ = llmCharacter.Chat(prompt, HandleReply, ReplyCompleted);

The RAG sample includes an example RAG implementation as well as an example RAG-LLM integration.

That's all :sparkles:!

LLM model management

LLM for Unity uses a model manager that allows to load or download LLMs and ship them directly in your game.<br> The model manager can be found as part of the LLM GameObject:<br> <img width="360" src=".github/LLM_manager.png">

You can download models with the Download model button.<br> LLM for Unity includes different state of the art models built-in for different model sizes, quantised with the Q4_K_M method.<br> Alternative models can be downloaded from HuggingFace in the .gguf format.<br> You can download a model locally and load it with the Load model button, or copy the URL in the Download model > Custom URL field to directly download it.<br> If a HuggingFace model does not provide a gguf file, it can be converted to gguf with this online converter.<br>

The chat template used for constructing the prompts is determined automatically from the model (if a relevant entry exists) or the model name. <br> If incorrecly identified, you can select another template from the chat template dropdown.<br> <br> Models added in the model manager are copied to the game during the building process.<br> You can omit a model from being built in by deselecting the "Build" checkbox.<br> To remove the model (but not delete it from disk) you can click the bin button.<br> The the path and URL (if downloaded) of each added model is diplayed in the expanded view of the model manager access with the >> button:<br> <img width="600" src=".github/LLM_manager_expanded.png">

You can create lighter builds by selecting the Download on Build option.<br> Using this option the models will be downloaded the first time the game starts instead of copied in the build.<br> If you have loaded a model locally you need to set its URL through the expanded view, otherwise it will be copied in the build.<br>

❕ Before using any model make sure you check their license

Examples

The Samples~ folder contains several examples of interaction 🤖:

<img width="400" src=".github/demo.gif">

To install a sample:

The samples can be run with the Scene.unity scene they contain inside their folder.<br> In the scene, select the LLM GameObject and click the Download Model button to download a default model or Load model to load your own model (see LLM model management).<br> Save the scene, run and enjoy!

Options

LLM Settings

💻 Setup Settings

<div> <img width="300" src=".github/LLM_GameObject.png" align="right"/> </div> </details>

Server Security Settings

</details>

🤗 Model Settings

</details>

LLMCharacter Settings

💻 Setup Settings

<div> <img width="300" src=".github/LLMCharacter_GameObject.png" align="right"/> </div>

🗨️ Chat Settings

🤗 Model Settings

</details>

License

The license of LLM for Unity is MIT (LICENSE.md) and uses third-party software with MIT and Apache licenses. Some models included in the asset define their own license terms, please review them before using each model. Third-party licenses can be found in the (Third Party Notices.md).