diff --git a/.gitignore b/.gitignore
index 206b0dac1..ac50b1485 100644
--- a/.gitignore
+++ b/.gitignore
@@ -351,4 +351,5 @@ site/
**/appsettings.Local.json
/LLama/runtimes/deps
/LLama/runtimes/deps.zip
-/LLama/runtimes/release_id.txt
\ No newline at end of file
+/LLama/runtimes/release_id.txt
+/Llama.Mobile/Resources/Raw/Llama-3.2-1B-Instruct-Q4_0.gguf
diff --git a/LLamaSharp.sln b/LLamaSharp.sln
index 7c970aa99..e18363591 100644
--- a/LLamaSharp.sln
+++ b/LLamaSharp.sln
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLama.Experimental", "LLama
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLama.Benchmark", "LLama.Benchmark\LLama.Benchmark.csproj", "{90D38FEE-68EA-459E-A4EE-268B9DFA1CD5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Llama.Mobile", "Llama.Mobile\Llama.Mobile.csproj", "{0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -196,6 +198,25 @@ Global
{90D38FEE-68EA-459E-A4EE-268B9DFA1CD5}.Release|Arm64.Build.0 = Release|Any CPU
{90D38FEE-68EA-459E-A4EE-268B9DFA1CD5}.Release|x64.ActiveCfg = Release|Any CPU
{90D38FEE-68EA-459E-A4EE-268B9DFA1CD5}.Release|x64.Build.0 = Release|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|Arm64.ActiveCfg = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|Arm64.Build.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Debug|x64.Build.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.GPU|Any CPU.ActiveCfg = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.GPU|Any CPU.Build.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.GPU|Arm64.ActiveCfg = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.GPU|Arm64.Build.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.GPU|x64.ActiveCfg = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.GPU|x64.Build.0 = Debug|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Release|Arm64.ActiveCfg = Release|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Release|Arm64.Build.0 = Release|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Release|x64.ActiveCfg = Release|Any CPU
+ {0E058BB0-83C6-4FBE-BC80-E8C5F7E29651}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Llama.Mobile/Llama.Mobile.csproj b/Llama.Mobile/Llama.Mobile.csproj
index a51a3eb0f..450cbcf1c 100644
--- a/Llama.Mobile/Llama.Mobile.csproj
+++ b/Llama.Mobile/Llama.Mobile.csproj
@@ -13,7 +13,7 @@
net8.0-android
-
+
@@ -51,7 +51,64 @@
6.5
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::Combine($(DestinationFolder), $(LocalFileName)))
+
+
+
+
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf
+ Resources/Raw
+ Llama-3.2-1B-Instruct-Q4_0.gguf
+
+
@@ -76,7 +133,7 @@
-
+
diff --git a/Llama.Mobile/MainPage.xaml b/Llama.Mobile/MainPage.xaml
index 5bd8e7e94..c640229d0 100644
--- a/Llama.Mobile/MainPage.xaml
+++ b/Llama.Mobile/MainPage.xaml
@@ -1,14 +1,110 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Llama.Mobile/MainPage.xaml.cs b/Llama.Mobile/MainPage.xaml.cs
index 8c9cb310c..6046e0959 100644
--- a/Llama.Mobile/MainPage.xaml.cs
+++ b/Llama.Mobile/MainPage.xaml.cs
@@ -1,16 +1,119 @@
namespace Llama.Mobile;
+using Android.Icu.Text;
+using Java.Lang;
+using Javax.Annotation;
+using Llama.Mobile.Src;
+using LLama;
+using LLama.Common;
using LLama.Native;
+using LLama.Sampling;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Xml.Linq;
+using Xamarin.Google.Crypto.Tink.Subtle;
+using Xamarin.KotlinX.Coroutines;
+using static System.Net.Mime.MediaTypeNames;
+using StringBuilder = System.Text.StringBuilder;
public partial class MainPage : ContentPage
{
- public MainPage()
+
+ public ObservableCollection Messages { get; } = new();
+
+ //Put the gguf model in the directory resources/raw and write its name in the following string
+ private const string modelName = "Llama-3.2-1B-Instruct-Q4_0.gguf";
+
+ private ChatSession? _session;
+ ChatSession Session
+ {
+ get
+ {
+ if (_session is null) throw new NullReferenceException("_session can't be null");
+ return _session;
+ }
+
+ set
+ {
+ _session = value;
+ }
+ }
+
+ private static InferenceParams InferenceParams = new InferenceParams()
+ {
+ MaxTokens = 256, // No more than 256 tokens should appear in answer. Remove it if antiprompt is enough for control.
+ AntiPrompts = new List { "User:" }, // Stop generation once antiprompts appear.
+
+ SamplingPipeline = new DefaultSamplingPipeline(),
+
+ };
+
+public MainPage()
{
InitializeComponent();
+ chat.BindingContext = this;
+ }
+ protected override async void OnAppearing()
+ {
+ base.OnAppearing();
+ string modelPath = Path.Combine(FileSystem.Current.AppDataDirectory, modelName);
+
+ if (!File.Exists(modelPath))
+ {
+ //get the data stream of the model stored in the apk
+ using Stream inputStream = await FileSystem.Current.OpenAppPackageFileAsync(modelName);
+
+ //copy the data from the inputStream in to a new file, with te same name, in the the app data directory
+ using FileStream outputStream = File.Create(modelPath);
+ await inputStream.CopyToAsync(outputStream);
+ outputStream.Close();
+ inputStream.Close();
+ }
+
+
+ var parameters = new ModelParams(modelPath)
+ {
+ ContextSize = 1024, // The longest length of chat as memory.
+ GpuLayerCount = 5 // How many layers to offload to GPU. Please adjust it according to your GPU memory.
+ };
+ var model = LLamaWeights.LoadFromFile(parameters);
+ var context = model.CreateContext(parameters);
+ var executor = new InteractiveExecutor(context);
+
+ // Add chat histories as prompt to tell AI how to act.
+ var chatHistory = new ChatHistory();
+ chatHistory.AddMessage(AuthorRole.System, "Transcript of a dialog, where the User interacts with an Assistant named Bob. Bob is helpful, kind, honest, good at writing, and never fails to answer the User's requests immediately and with precision.");
+ chatHistory.AddMessage(AuthorRole.User, "Hello, Bob.");
+ chatHistory.AddMessage(AuthorRole.Assistant, "Hello. How may I help you today?");
- //Load the native library
- NativeApi.llama_empty_call();
+ Session = new(executor, chatHistory);
+ Session.WithOutputTransform(new LLamaTransforms.KeywordTextOutputStreamTransform(
+ new string[] { "\n\nUser:", "\nUser:", "User:", "Assistant: " },
+ redundancyLength: 8));
- label1.Text = "llama.cpp loaded successfully";
+ pnl_loading.IsVisible = false;
+ btn_ask.IsEnabled = true;
+
+ await Task.Delay(100); //on the emulator without this little delay the popup isn't shown
+
+ await DisplayAlert("Loaded", "model correctly Loaded", "OK");
+ }
+
+ private async void OnAskClicked(object sender, EventArgs e)
+ {
+ btn_ask.IsEnabled = false;
+ Messages.Add(new Message { Type = messageType.User, Text = tx_userPrompt.Text, IsPreparing = false });
+ string userPrompt = tx_userPrompt.Text;
+ tx_userPrompt.Text="";
+ Message response = new Message { Type = messageType.other, Text = "", IsPreparing = true };
+ Messages.Add(response);
+ chat.ScrollTo(Messages.Last(), position: ScrollToPosition.End, animate: false);
+ await foreach (string text in Session.ChatAsync(new ChatHistory.Message(AuthorRole.User, userPrompt), InferenceParams))
+ {
+ response.IsPreparing = false;
+ response.AppendText(text);
+ chat.ScrollTo(Messages.Last(), position: ScrollToPosition.End, animate: false);
+ }
+ btn_ask.IsEnabled = true;
}
}
diff --git a/Llama.Mobile/Src/ChatMessageTemplateSelector.cs b/Llama.Mobile/Src/ChatMessageTemplateSelector.cs
new file mode 100644
index 000000000..00ded87bf
--- /dev/null
+++ b/Llama.Mobile/Src/ChatMessageTemplateSelector.cs
@@ -0,0 +1,16 @@
+using Microsoft.Maui.Controls;
+namespace Llama.Mobile.Src
+{
+ public class ChatMessageTemplateSelector : DataTemplateSelector
+ {
+ public DataTemplate UserTemplate { get; set; }
+ public DataTemplate OtherTemplate { get; set; }
+
+
+ protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
+ {
+ var message = (Message)item;
+ return message.Type == messageType.User ? UserTemplate : OtherTemplate;
+ }
+ }
+}
diff --git a/Llama.Mobile/Src/InverseBooleanConverter.cs b/Llama.Mobile/Src/InverseBooleanConverter.cs
new file mode 100644
index 000000000..366426afa
--- /dev/null
+++ b/Llama.Mobile/Src/InverseBooleanConverter.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Llama.Mobile.Src
+{
+ public class InverseBooleanConverter : IValueConverter
+ {
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ return !boolValue;
+ else
+ throw new InvalidOperationException("The target must be a boolean");
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Llama.Mobile/Src/Message.cs b/Llama.Mobile/Src/Message.cs
new file mode 100644
index 000000000..b05f84c3e
--- /dev/null
+++ b/Llama.Mobile/Src/Message.cs
@@ -0,0 +1,63 @@
+using Android.Content;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Llama.Mobile.Src
+{
+
+ public enum messageType
+ {
+ User,
+ other,
+ }
+
+ public class Message : INotifyPropertyChanged
+ {
+ private StringBuilder _textBuilder = new StringBuilder();
+ public string Text {
+ get => _textBuilder.ToString();
+ set{
+ _textBuilder = new StringBuilder(value);
+ OnPropertyChanged(nameof(Text));
+ }
+ }
+ public void AppendText(string text)
+ {
+ _textBuilder.Append(text);
+ OnPropertyChanged(nameof(Text));
+ }
+
+ private bool _isPreparing;
+ public bool IsPreparing
+ {
+ get => _isPreparing;
+ set
+ {
+ if (_isPreparing != value)
+ {
+ _isPreparing = value;
+ OnPropertyChanged(nameof(IsPreparing));
+ }
+ }
+ }
+
+ public required messageType Type { get; set; }
+
+
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+
+
+
+}