-
Notifications
You must be signed in to change notification settings - Fork 274
New Learning Path: Add an LLM to your Android app with Arm's AI Chat library #3169
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
Open
BmanClark
wants to merge
3
commits into
ArmDeveloperEcosystem:main
Choose a base branch
from
BmanClark:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
65 changes: 65 additions & 0 deletions
65
content/learning-paths/mobile-graphics-and-gaming/android-ai-chat-lib/_index.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| --- | ||
| title: Add an LLM to your Android app with Arm's AI Chat library | ||
|
|
||
| minutes_to_complete: 15 | ||
|
|
||
| who_is_this_for: Android App Developers who wish to add LLM capabilities to an app. | ||
|
|
||
| learning_objectives: | ||
| - Easily add an on-device LLM to any Android app | ||
|
|
||
| prerequisites: | ||
| - Android Studio | ||
| - Android phone for testing (in Developer Mode, with USB cable to connect) | ||
|
|
||
| author: Ben Clark | ||
|
|
||
| ### Tags | ||
| skilllevels: Introductory | ||
| subjects: ML | ||
| armips: | ||
| - Arm AI Chat library | ||
| tools_software_languages: | ||
| - Kotlin | ||
| - Neon | ||
| - SVE2 | ||
| - SME2 | ||
| - LLM | ||
| operatingsystems: | ||
| - Android | ||
|
|
||
|
|
||
|
|
||
| further_reading: | ||
| - resource: | ||
| title: AI Chat - Explore and evaluate LLMs on Android and ChromeOS | ||
| link: https://developer.arm.com/community/arm-community-blogs/b/announcements/posts/ai-chat-explore-and-evaluate-llms-on-android-and-chromeos | ||
| type: blog | ||
| - resource: | ||
| title: Arm AI Chat LLM test app | ||
| link: https://play.google.com/store/apps/details?id=com.arm.aichat | ||
| type: example app | ||
| - resource: | ||
| title: AI Chat library @ Maven Central | ||
| link: https://central.sonatype.com/artifact/com.arm/ai-chat | ||
| type: documentation | ||
| - resource: | ||
| title: AI Chat library on GitHub | ||
| link: https://github.com/arm/ai-chat | ||
| type: website | ||
| - resource: | ||
| title: Arm KleidiAI - Helping AI frameworks elevate their performance on Arm CPUs | ||
| link: https://developer.arm.com/community/arm-community-blogs/b/ai-blog/posts/kleidiai | ||
| type: blog | ||
| - resource: | ||
| title: SME2 | ||
| link: https://www.arm.com/technologies/sme2 | ||
| type: website | ||
|
|
||
|
|
||
| ### FIXED, DO NOT MODIFY | ||
| # ================================================================================ | ||
| weight: 1 # _index.md always has weight of 1 to order correctly | ||
| layout: "learningpathall" # All files under learning paths have this same wrapper | ||
| learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. | ||
| --- |
8 changes: 8 additions & 0 deletions
8
...nt/learning-paths/mobile-graphics-and-gaming/android-ai-chat-lib/_next-steps.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| # ================================================================================ | ||
| # FIXED, DO NOT MODIFY THIS FILE | ||
| # ================================================================================ | ||
| weight: 21 # The weight controls the order of the pages. _index.md always has weight 1. | ||
| title: "Next Steps" # Always the same, html page title. | ||
| layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. | ||
| --- |
37 changes: 37 additions & 0 deletions
37
...rning-paths/mobile-graphics-and-gaming/android-ai-chat-lib/add-ai-chat-lib-2.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| --- | ||
| title: Add AI Chat Library | ||
| weight: 3 | ||
|
|
||
| ### FIXED, DO NOT MODIFY | ||
| layout: learningpathall | ||
| --- | ||
|
|
||
| Your freshly created Android Studio project should already have top-level repositories including `google()` and `mavenCentral()` in `settings.gradle.kts`, but check that it does, to ensure the app can find the library. | ||
|
|
||
| ## Add the Maven Dependency | ||
| We need to add the AI Chat library in the app module build file, `app/build.gradle.kts` (not the project `build.gradle.kts` in the root of the project). | ||
|
|
||
| In here we add into the `dependencies` section at the bottom: | ||
| ```kotlin | ||
| implementation("com.arm:ai-chat:0.1.0") | ||
| ``` | ||
|
|
||
| This adds the library to your project, and all the LLM functionality. Also in this file, check that: | ||
| - `targetSdk` and `compileSdk` are 36 | ||
| - the Java version in `compileOptions` is `JavaVersion.VERSION_17` | ||
| - the `jvmTarget` in `kotlinOptions` is 17 | ||
|
|
||
| In `libs.version.toml` we need to change the `kotlin` version to `2.2.20`, as the library requires a more recent version than the default. | ||
|
|
||
| Finally, there will be a banner at the top of these settings files saying the "Gradle files have changed since last project sync." Choose the option to "Sync Now", and let the project update. | ||
|
|
||
| ## Adjust the Manifest | ||
| We need to adjust the `AndroidManifest.xml` file, which is in `app\src\main`. | ||
|
|
||
| Most of the file is an xml tag `<application ...>`. Add in among the other similar looking options in the tag: | ||
| ```xml | ||
| <application | ||
| android:extractNativeLibs="true" | ||
| ... > | ||
| ``` | ||
| This enables the app to load the needed llama.cpp native libraries. | ||
174 changes: 174 additions & 0 deletions
174
...g-paths/mobile-graphics-and-gaming/android-ai-chat-lib/add-layouts-adapter-3.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| --- | ||
| title: Add UI Code | ||
| weight: 4 | ||
|
|
||
| ### FIXED, DO NOT MODIFY | ||
| layout: learningpathall | ||
| --- | ||
|
|
||
| ## Add Layouts | ||
| Currently the `activity_main.xml` layout file in your `app\src\res\layout` directory is nearly empty, containing just a "Hello World!" piece of text that we wish to remove. Instead, replace it with the following, which will create a status area at the top, a place for messages in the middle, and a place for you to type at the bottom with a button to send: | ||
| ```xml | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| android:id="@+id/root" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent"> | ||
|
|
||
| <TextView | ||
| android:id="@+id/status_text" | ||
| android:layout_width="0dp" | ||
| android:layout_height="wrap_content" | ||
| android:layout_marginStart="16dp" | ||
| android:layout_marginTop="16dp" | ||
| android:layout_marginEnd="16dp" | ||
| android:text="Loading model..." | ||
| android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" | ||
| app:layout_constraintEnd_toEndOf="parent" | ||
| app:layout_constraintStart_toStartOf="parent" | ||
| app:layout_constraintTop_toTopOf="parent" /> | ||
|
|
||
| <androidx.recyclerview.widget.RecyclerView | ||
| android:id="@+id/messages" | ||
| android:layout_width="0dp" | ||
| android:layout_height="0dp" | ||
| android:layout_marginStart="16dp" | ||
| android:layout_marginTop="12dp" | ||
| android:layout_marginEnd="16dp" | ||
| android:clipToPadding="false" | ||
| android:paddingBottom="8dp" | ||
| app:layout_constraintBottom_toTopOf="@+id/input_row" | ||
| app:layout_constraintEnd_toEndOf="parent" | ||
| app:layout_constraintStart_toStartOf="parent" | ||
| app:layout_constraintTop_toBottomOf="@+id/status_text" /> | ||
|
|
||
| <LinearLayout | ||
| android:id="@+id/input_row" | ||
| android:layout_width="0dp" | ||
| android:layout_height="wrap_content" | ||
| android:layout_margin="16dp" | ||
| android:orientation="horizontal" | ||
| app:layout_constraintBottom_toBottomOf="parent" | ||
| app:layout_constraintEnd_toEndOf="parent" | ||
| app:layout_constraintStart_toStartOf="parent"> | ||
| <EditText | ||
| android:id="@+id/user_input" | ||
| android:layout_width="0dp" | ||
| android:layout_height="wrap_content" | ||
| android:layout_weight="1" | ||
| android:enabled="false" | ||
| android:hint="Model is loading..." | ||
| android:inputType="textMultiLine" | ||
| android:maxLines="4" | ||
| android:minHeight="48dp" | ||
| android:padding="12dp" /> | ||
| <com.google.android.material.button.MaterialButton | ||
| android:id="@+id/send_button" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="wrap_content" | ||
| android:layout_marginStart="12dp" | ||
| android:enabled="false" | ||
| android:text="Import model" /> | ||
| </LinearLayout> | ||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||
| ``` | ||
|
|
||
| We also need two small "helper" layouts to format the messages from the user and the AI assistant. In the `layout` folder alongside the main layout, first create `item_message_user.xml` and insert the following: | ||
| ```xml | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:paddingTop="6dp" | ||
| android:paddingBottom="6dp"> | ||
|
|
||
| <TextView | ||
| android:id="@+id/msg_content" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="wrap_content" | ||
| android:layout_gravity="end" | ||
| android:background="#D7F0FF" | ||
| android:padding="12dp" | ||
| android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" /> | ||
| </FrameLayout> | ||
| ``` | ||
|
|
||
| Then create `item_message_assistant.xml` and put the following as its contents: | ||
| ```xml | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:paddingTop="6dp" | ||
| android:paddingBottom="6dp"> | ||
|
|
||
| <TextView | ||
| android:id="@+id/msg_content" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="wrap_content" | ||
| android:layout_gravity="start" | ||
| android:background="#EFEFEF" | ||
| android:padding="12dp" | ||
| android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" /> | ||
| </FrameLayout> | ||
| ``` | ||
|
|
||
| ## Add the MessageAdaptor class | ||
| As a final bit of UI code, we add a `MessageAdapter.kt` code file to put our messages into the correct bit of layout. The file should sit alongside the `MainActivity` class that is auto-created with the project. | ||
|
|
||
| The file contents of `MessageAdapter.kt` are: | ||
| ```kotlin | ||
| package com.example.simpleaichat | ||
|
|
||
| import android.view.LayoutInflater | ||
| import android.view.View | ||
| import android.view.ViewGroup | ||
| import android.widget.TextView | ||
| import androidx.recyclerview.widget.RecyclerView | ||
|
|
||
| data class Message( | ||
| val id: String, | ||
| val content: String, | ||
| val isUser: Boolean | ||
| ) | ||
|
|
||
| class MessageAdapter( | ||
| private val messages: List<Message> | ||
| ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||
|
|
||
| override fun getItemViewType(position: Int): Int { | ||
| return if (messages[position].isUser) VIEW_TYPE_USER else VIEW_TYPE_ASSISTANT | ||
| } | ||
|
|
||
| override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { | ||
| val inflater = LayoutInflater.from(parent.context) | ||
| return if (viewType == VIEW_TYPE_USER) { | ||
| UserMessageViewHolder(inflater.inflate(R.layout.item_message_user, parent, false)) | ||
| } else { | ||
| AssistantMessageViewHolder(inflater.inflate(R.layout.item_message_assistant, parent, false)) | ||
| } | ||
| } | ||
|
|
||
| override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { | ||
| holder.itemView.findViewById<TextView>(R.id.msg_content).text = messages[position].content | ||
| } | ||
|
|
||
| override fun getItemCount(): Int = messages.size | ||
|
|
||
| class UserMessageViewHolder(view: View) : RecyclerView.ViewHolder(view) | ||
| class AssistantMessageViewHolder(view: View) : RecyclerView.ViewHolder(view) | ||
|
|
||
| companion object { | ||
| private const val VIEW_TYPE_USER = 1 | ||
| private const val VIEW_TYPE_ASSISTANT = 2 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Quickly going through the important parts: | ||
| - `Message` is the smallest useful chat piece: one id, the message text, and a flag saying whether it came from the user or the assistant. | ||
| - `getItemViewType(...)` decides which row layout to use for each message. | ||
| - `onCreateViewHolder(...)` inflates either the user bubble or the assistant bubble layout. | ||
| - `onBindViewHolder(...)` writes the current message text into the row. | ||
| - `getItemCount()` tells the `RecyclerView` how many chat rows it should render. |
21 changes: 21 additions & 0 deletions
21
...earning-paths/mobile-graphics-and-gaming/android-ai-chat-lib/add-model-run-5.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| --- | ||
| title: Add Model And Run | ||
| weight: 6 | ||
|
|
||
| ### FIXED, DO NOT MODIFY | ||
| layout: learningpathall | ||
| --- | ||
|
|
||
| ## Download a Mobile-compatible GGUF | ||
| Before you run you'll need to download a GGUF model file to run. To be mobile compatible, it'll need to run within your test Android phone's memory. A common Android memory size is 8GB, although some modern premium phones have more. Other things will also need to fit in memory, so the model size will need to be noticeably less than 8GB. | ||
|
|
||
| A good example model is [google_gemma-3-4b-it-Q4_0.gguf](https://huggingface.co/bartowski/google_gemma-3-4b-it-GGUF/blob/main/google_gemma-3-4b-it-Q4_0.gguf). Gemma 3 is a powerful model, and this 4 billion parameter version has been int4 quantized with the Q4_0 schema that works particularly well with Arm's [KleidiAI library](https://developer.arm.com/ai/kleidi-libraries), enabling speed-ups on phones with [SME2](https://www.arm.com/technologies/sme2), [SVE2](https://developer.arm.com/documentation/102340/0100/Introducing-SVE2) and [Neon](https://www.arm.com/technologies/neon). | ||
|
|
||
| Download Gemma 3 or another suitable model onto your phone, ready to run the app. | ||
|
|
||
| ## Run the App! | ||
| In Android Studio, if you connect your test Android phone with a USB cable to your computer, you should now be able to run your LLM chatbot app. Make sure when you connect the phone it is in Developer Mode and you allow USB debugging. | ||
|
|
||
| In the bottom right there is a button "Import model". Clicking this will take you to downloads to be able to select the model you've downloaded, so the app can download it. Once it has finished copying and loading the model it will say "Model ready" at the top of the screen. Now if you click the text entry area at the bottom, you can type your questions and chat with the LLM. | ||
|
|
||
|  |
Binary file added
BIN
+77 KB
...earning-paths/mobile-graphics-and-gaming/android-ai-chat-lib/app_screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions
20
content/learning-paths/mobile-graphics-and-gaming/android-ai-chat-lib/setup-1.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| --- | ||
| title: Project Setup | ||
| weight: 2 | ||
|
|
||
| ### FIXED, DO NOT MODIFY | ||
| layout: learningpathall | ||
| --- | ||
|
|
||
| ## Objective | ||
| In this learning path you will create a small chatbot Android app from scratch. The app will load a GGUF model of your choosing, and then run it in a chatbot format. | ||
|
|
||
| The app will use Arm's AI Chat library available from Maven Central, which provides an Android wrapper around llama.cpp, providing high-performance running of LLM models in the GGUF format. | ||
|
|
||
| For other examples of chatbots using this library you can use: | ||
| - the fully featured [Arm AI Chat app on Google Play](https://play.google.com/store/apps/details?id=com.arm.aichat), which can be used to test the performance and capabilities of mobile models, or | ||
| - the [AI Chat library GitHub example](https://github.com/arm/ai-chat/tree/use-maven-library/examples/llama.android), which is only slightly more complicated than this Learning Path. | ||
|
|
||
| ## Project Setup | ||
| Open Android Studio and create a new project of the type "Empty Views Activity". Name it however you like, and leave other options on default - for instance Minimum SDK will be 33. | ||
|
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Add in among the other similar looking options in the tag" reads a bit confusing? Maybe rephrase?