Skip to content

Commit 21474d4

Browse files
committed
Merge remote-tracking branch 'origin/main' into multi-model
2 parents a1bad6b + 9a8b021 commit 21474d4

66 files changed

Lines changed: 4494 additions & 449 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/android-build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ jobs:
3131
path: llm/android/LlamaDemo
3232
- name: DeepLabV3Demo
3333
path: dl3/android/DeepLabV3Demo
34+
- name: MV3Demo
35+
path: mv3/android/MV3Demo
36+
3437

3538
name: Build ${{ matrix.name }}
3639
steps:
@@ -108,6 +111,7 @@ jobs:
108111
## Apps included:
109112
- LlamaDemo APKs (`app-debug-LlamaDemo.apk`, `app-release-unsigned-LlamaDemo.apk`)
110113
- DeepLabV3Demo APKs (`app-debug-DeepLabV3Demo.apk`, `app-release-unsigned-DeepLabV3Demo.apk`)
114+
- MV3Demo APKs (`app-debug-MV3Demo.apk`, `app-release-unsigned-MV3Demo.apk`)
111115
112116
**Build Date:** ${{ steps.tag.outputs.build_date }}
113117
**Commit:** ${{ github.sha }}

.github/workflows/llm-android.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ on:
3232
description: 'Custom URL for tokenizer file (only used when model_preset is custom)'
3333
required: false
3434
type: string
35+
local_aar:
36+
description: 'URL to download a local AAR file. When set, the workflow will download the AAR and use it instead of the Maven dependency.'
37+
required: false
38+
type: string
3539

3640
permissions:
3741
contents: read
@@ -74,6 +78,12 @@ jobs:
7478
- name: Setup Gradle
7579
uses: gradle/actions/setup-gradle@v4
7680

81+
- name: Download local AAR
82+
if: ${{ inputs.local_aar }}
83+
run: |
84+
mkdir -p llm/android/LlamaDemo/app/libs
85+
curl -fL -o llm/android/LlamaDemo/app/libs/executorch.aar "${{ inputs.local_aar }}"
86+
7787
- name: AVD cache
7888
uses: actions/cache@v4
7989
id: avd-cache
@@ -145,6 +155,7 @@ jobs:
145155
uses: reactivecircus/android-emulator-runner@v2
146156
env:
147157
MODEL_PRESET: ${{ inputs.model_preset || 'stories' }}
158+
USE_LOCAL_AAR: ${{ inputs.local_aar != '' }}
148159
with:
149160
api-level: ${{ env.API_LEVEL }}
150161
arch: ${{ env.ARCH }}
@@ -154,7 +165,7 @@ jobs:
154165
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-snapshot-save -memory 16384
155166
disable-animations: true
156167
working-directory: llm/android/LlamaDemo
157-
script: bash ./scripts/run-ci-tests.sh "$MODEL_PRESET" "$MODEL_FILE" "$TOKENIZER_FILE"
168+
script: bash ./scripts/run-ci-tests.sh "$MODEL_PRESET" "$MODEL_FILE" "$TOKENIZER_FILE" "$USE_LOCAL_AAR"
158169

159170
- name: Add model response to summary
160171
if: always()

Yolo/android/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ android {
4242
}
4343
kotlinOptions { jvmTarget = "1.8" }
4444
buildFeatures { compose = true }
45-
composeOptions { kotlinCompilerExtensionVersion = "1.4.3" }
45+
composeOptions { kotlinCompilerExtensionVersion = "1.5.14" }
4646
packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } }
4747
}
4848

Yolo/android/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88

99
// Top-level build file where you can add configuration options common to all sub-projects/modules.
1010
plugins {
11-
id("com.android.application") version "8.1.0" apply false
12-
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
11+
id("com.android.application") version "8.7.3" apply false
12+
id("org.jetbrains.kotlin.android") version "1.9.24" apply false
1313
}

cifar/android/CifarETTrainingDemo/app/src/main/java/com/example/democifar10/MainActivity.kt

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class MainActivity : ComponentActivity() {
250250

251251
override fun onCreate(savedInstanceState: Bundle?) {
252252
val batchSize = 4
253-
val numEpochs = 5
253+
val numEpochs = 10
254254
val width = 32
255255
val height = 32
256256
val channels = 3
@@ -405,15 +405,48 @@ class MainActivity : ComponentActivity() {
405405
}
406406
}
407407

408-
// Start fine-tuning
409-
trainModel(
410-
tModule!!, trnImgData!!, trnLblData!!, tstImgData!!, tstLblData!!, numEpochs, batchSize
411-
)
408+
// Start fine-tuning in a background thread to prevent ANR
409+
Thread {
410+
try {
411+
trainModel(
412+
tModule!!, trnImgData!!, trnLblData!!, tstImgData!!, tstLblData!!, numEpochs, batchSize
413+
)
414+
} catch (e: Exception) {
415+
Log.e(debugTag, "Error during training: ${e.message}", e)
416+
runOnUiThread {
417+
val statusText = findViewById<TextView>(R.id.statusText)
418+
statusText.text = "Training failed: ${e.message}"
419+
}
420+
}
421+
}.start()
412422
}
413423

414424
evaluateButton.setOnClickListener {
415-
// Evaluate the model
416-
evaluateModel(tModule!!, tstImgData!!, tstLblData!!, batchSize)
425+
Log.d("Button", "Evaluate button clicked")
426+
427+
// Show progress immediately on the UI thread
428+
runOnUiThread {
429+
try {
430+
val statusText = findViewById<TextView>(R.id.statusText)
431+
statusText.text = "Starting evaluation..."
432+
} catch (e: Exception) {
433+
Log.e("StatusText", "Error in evaluate button click: ${e.message}")
434+
e.printStackTrace()
435+
}
436+
}
437+
438+
// Evaluate the model in a background thread to prevent ANR
439+
Thread {
440+
try {
441+
evaluateModel(tModule!!, tstImgData!!, tstLblData!!, batchSize)
442+
} catch (e: Exception) {
443+
Log.e(debugTag, "Error during evaluation: ${e.message}", e)
444+
runOnUiThread {
445+
val statusText = findViewById<TextView>(R.id.statusText)
446+
statusText.text = "Evaluation failed: ${e.message}"
447+
}
448+
}
449+
}.start()
417450
}
418451
}
419452

dl3/android/DeepLabV3Demo/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ android {
3838
}
3939
kotlinOptions { jvmTarget = "1.8" }
4040
buildFeatures { compose = true }
41-
composeOptions { kotlinCompilerExtensionVersion = "1.4.3" }
41+
composeOptions { kotlinCompilerExtensionVersion = "1.5.14" }
4242
}
4343

4444
dependencies {

dl3/android/DeepLabV3Demo/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88

99
// Top-level build file where you can add configuration options common to all sub-projects/modules.
1010
plugins {
11-
id("com.android.application") version "8.1.0" apply false
12-
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
11+
id("com.android.application") version "8.7.3" apply false
12+
id("org.jetbrains.kotlin.android") version "1.9.24" apply false
1313
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
package com.example.executorchllamademo
10+
11+
import android.content.Context
12+
import android.util.Log
13+
import androidx.compose.ui.test.junit4.createAndroidComposeRule
14+
import androidx.compose.ui.test.onAllNodesWithText
15+
import androidx.compose.ui.test.onNodeWithTag
16+
import androidx.compose.ui.test.onNodeWithText
17+
import androidx.compose.ui.test.performClick
18+
import androidx.compose.ui.test.performTextInput
19+
import androidx.test.core.app.ApplicationProvider
20+
import androidx.test.ext.junit.runners.AndroidJUnit4
21+
import androidx.test.filters.LargeTest
22+
import org.junit.Before
23+
import org.junit.Rule
24+
import org.junit.Test
25+
import org.junit.runner.RunWith
26+
27+
/**
28+
* Preset model sanity test that validates the preset model UI workflow.
29+
*
30+
* This test validates:
31+
* 1. Navigate from Welcome screen to Preset model screen
32+
* 2. Load preset config from URL
33+
* 3. Verify Stories 110M model is displayed
34+
*
35+
* Note: Download and chat steps are skipped in CI due to network constraints.
36+
*/
37+
@RunWith(AndroidJUnit4::class)
38+
@LargeTest
39+
class PresetSanityTest {
40+
41+
companion object {
42+
private const val TAG = "PresetSanityTest"
43+
private const val DEFAULT_CONFIG_URL = "https://raw.githubusercontent.com/meta-pytorch/executorch-examples/889ccc6e88813cbf03775889beed29b793d0c8db/llm/android/LlamaDemo/app/src/main/assets/preset_models.json"
44+
}
45+
46+
@get:Rule
47+
val composeTestRule = createAndroidComposeRule<WelcomeActivity>()
48+
49+
@Before
50+
fun setUp() {
51+
// Clear SharedPreferences before test to ensure a clean state
52+
val context = ApplicationProvider.getApplicationContext<Context>()
53+
val prefs = context.getSharedPreferences(
54+
context.getString(R.string.demo_pref_file_key),
55+
Context.MODE_PRIVATE
56+
)
57+
prefs.edit().clear().commit()
58+
59+
// Also clear the preset config preferences
60+
val configPrefs = context.getSharedPreferences("preset_config_prefs", Context.MODE_PRIVATE)
61+
configPrefs.edit().clear().commit()
62+
}
63+
64+
/**
65+
* Loads the preset config from URL.
66+
* This is needed because the bundled preset_models.json is empty by default.
67+
*/
68+
private fun loadPresetConfigFromUrl() {
69+
Log.i(TAG, "Loading preset config from URL")
70+
71+
// Type the URL into the config URL field (it's empty by default)
72+
composeTestRule.onNodeWithTag("config_url_field").performClick()
73+
composeTestRule.waitForIdle()
74+
composeTestRule.onNodeWithTag("config_url_field").performTextInput(DEFAULT_CONFIG_URL)
75+
76+
// Small delay to ensure text is entered
77+
Thread.sleep(500)
78+
79+
// Click the Load button
80+
composeTestRule.onNodeWithText("Load").performClick()
81+
82+
// Wait for config to load (models should appear)
83+
// Don't use waitForIdle here as the loading spinner animation keeps Compose busy
84+
composeTestRule.waitUntil(timeoutMillis = 60000) {
85+
composeTestRule.onAllNodesWithText("Stories 110M").fetchSemanticsNodes().isNotEmpty()
86+
}
87+
Log.i(TAG, "Preset config loaded successfully")
88+
}
89+
90+
/**
91+
* Tests the preset model UI workflow:
92+
* 1. From Welcome screen, tap "Preset model" card
93+
* 2. Load preset config from URL (since bundled JSON is empty)
94+
* 3. Verify Stories 110M model is displayed
95+
*
96+
* Note: Download and chat steps are skipped in CI due to network constraints.
97+
*/
98+
@Test
99+
fun testPresetModelDownloadAndChat() {
100+
composeTestRule.waitForIdle()
101+
102+
// Step 1: From Welcome screen, tap "Preset model" card
103+
Log.i(TAG, "Step 1: Navigating to Preset model screen")
104+
composeTestRule.onNodeWithText("Preset model").performClick()
105+
composeTestRule.waitUntil(timeoutMillis = 5000) {
106+
composeTestRule.onAllNodesWithText("Download Preset Model").fetchSemanticsNodes().isNotEmpty()
107+
}
108+
109+
// Step 2: Load preset config from URL
110+
Log.i(TAG, "Step 2: Loading preset config from URL")
111+
loadPresetConfigFromUrl()
112+
113+
// Step 3: Find Stories 110M and verify it exists
114+
Log.i(TAG, "Step 3: Verifying Stories 110M is displayed")
115+
composeTestRule.onNodeWithText("Stories 110M").assertExists()
116+
Log.i(TAG, "Stories 110M found - preset model screen is working correctly")
117+
118+
// Note: Download and chat steps are skipped in CI due to network constraints
119+
}
120+
}

llm/android/LlamaDemo/app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
66
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
77
<uses-permission android:name="android.permission.CAMERA" />
8+
<uses-permission android:name="android.permission.INTERNET" />
89

910
<uses-feature android:name="android.hardware.camera" />
1011

@@ -27,6 +28,9 @@
2728
<activity
2829
android:name=".AppSettingsActivity"
2930
android:exported="false" />
31+
<activity
32+
android:name=".SelectPresetModelActivity"
33+
android:exported="false" />
3034
<activity
3135
android:name=".WelcomeActivity"
3236
android:exported="true"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"models": {
3+
}
4+
}

0 commit comments

Comments
 (0)