Skip to content

Add structured output / function calling sample #281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 2, 2024
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
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ LLM for Unity is built on top of the awesome [llama.cpp](https://github.com/gger
<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="#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;
Expand Down Expand Up @@ -168,6 +168,22 @@ void SetProgress(float progress){
This is useful to present a progress bar or something similar.
The [MobileDemo](Samples~/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](https://github.com/ggerganov/llama.cpp/tree/master/grammars).<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](https://github.com/ggerganov/llama.cpp/blob/b4218/grammars/json.gbnf) grammar.<br>

Alternatively you can set the grammar directly with code:
``` c#
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](Samples~/FunctionCalling) sample for an example implementation.

</details>
<details>
<summary>Save / Load your chat history</summary>
Expand Down Expand Up @@ -448,12 +464,13 @@ If you have loaded a model locally you need to set its URL through the expanded

## Examples
The [Samples~](Samples~) folder contains several examples of interaction 🤖:
- [SimpleInteraction](Samples~/SimpleInteraction): Demonstrates a simple interaction with an AI character
- [MultipleCharacters](Samples~/MultipleCharacters): Demonstrates a simple interaction using multiple AI characters
- [RAG](Samples~/RAG): RAG sample. Includes an example using the RAG to feed information to a LLM
- [ChatBot](Samples~/ChatBot): Demonstrates interaction between a player and a AI with a UI similar to a messaging app (see image below)
- [KnowledgeBaseGame](Samples~/KnowledgeBaseGame): Simple detective game using a knowledge base to provide information to the LLM based on [google/mysteryofthreebots](https://github.com/google/mysteryofthreebots)
- [SimpleInteraction](Samples~/SimpleInteraction): Simple interaction with an AI character
- [MultipleCharacters](Samples~/MultipleCharacters): Simple interaction using multiple AI characters
- [FunctionCalling](Samples~/FunctionCalling): Function calling sample with structured output from the LLM
- [RAG](Samples~/RAG): Semantic search using a Retrieval Augmented Generation (RAG) system. Includes example using a RAG to feed information to a LLM
- [MobileDemo](Samples~/MobileDemo): Example mobile app for Android / iOS with an initial screen displaying the model download progress
- [ChatBot](Samples~/ChatBot): Interaction between a player and a AI with a UI similar to a messaging app (see image below)
- [KnowledgeBaseGame](Samples~/KnowledgeBaseGame): Simple detective game using a knowledge base to provide information to the LLM based on [google/mysteryofthreebots](https://github.com/google/mysteryofthreebots)

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

Expand Down
8 changes: 8 additions & 0 deletions Samples~/FunctionCalling.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 102 additions & 0 deletions Samples~/FunctionCalling/FunctionCalling.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using UnityEngine;
using LLMUnity;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Reflection;

namespace LLMUnitySamples
{
public static class Functions
{
static System.Random random = new System.Random();


public static string Weather()
{
string[] weather = new string[]{"sunny", "rainy", "cloudy", "snowy"};
return "The weather is " + weather[random.Next(weather.Length)];
}

public static string Time()
{
return "The time is " + random.Next(24).ToString("D2") + ":" + random.Next(60).ToString("D2");
}

public static string Emotion()
{
string[] emotion = new string[]{"happy", "sad", "exhilarated", "ok"};
return "I am feeling " + emotion[random.Next(emotion.Length)];
}
}

public class FunctionCalling : MonoBehaviour
{
public LLMCharacter llmCharacter;
public InputField playerText;
public Text AIText;

void Start()
{
playerText.onSubmit.AddListener(onInputFieldSubmit);
playerText.Select();
llmCharacter.grammarString = MultipleChoiceGrammar();
}

string[] GetFunctionNames()
{
List<string> functionNames = new List<string>();
foreach (var function in typeof(Functions).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)) functionNames.Add(function.Name);
return functionNames.ToArray();
}

string MultipleChoiceGrammar()
{
return "root ::= (\"" + string.Join("\" | \"", GetFunctionNames()) + "\")";
}

string ConstructPrompt(string message)
{
string prompt = "Which of the following choices matches best the input?\n\n";
prompt += "Input:" + message + "\n\n";
prompt += "Choices:\n";
foreach(string functionName in GetFunctionNames()) prompt += $"- {functionName}\n";
prompt += "\nAnswer directly with the choice";
return prompt;
}

string CallFunction(string functionName)
{
return (string) typeof(Functions).GetMethod(functionName).Invoke(null, null);
}

async void onInputFieldSubmit(string message)
{
playerText.interactable = false;
string functionName = await llmCharacter.Chat(ConstructPrompt(message));
string result = CallFunction(functionName);
AIText.text = $"Calling {functionName}\n{result}";
playerText.interactable = true;
}

public void CancelRequests()
{
llmCharacter.CancelRequests();
}

public void ExitGame()
{
Debug.Log("Exit button clicked");
Application.Quit();
}

bool onValidateWarning = true;
void OnValidate()
{
if (onValidateWarning && !llmCharacter.remote && llmCharacter.llm != null && llmCharacter.llm.model == "")
{
Debug.LogWarning($"Please select a model in the {llmCharacter.llm.gameObject.name} GameObject!");
onValidateWarning = false;
}
}
}
}
11 changes: 11 additions & 0 deletions Samples~/FunctionCalling/FunctionCalling.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading