Skip to content

Commit c4c28ef

Browse files
committed
update
1 parent e280380 commit c4c28ef

7 files changed

Lines changed: 202 additions & 40 deletions

File tree

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ModelConfiguration.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ data class ModelConfiguration(
1919
val modelType: ModelType = ModelType.LLAMA_3,
2020
val backendType: BackendType = BackendType.XNNPACK,
2121
val temperature: Double = ModuleSettings.DEFAULT_TEMPERATURE,
22-
val displayName: String = ""
22+
val displayName: String = "",
23+
val adapterFilePaths: List<String> = emptyList()
2324
) {
2425
companion object {
2526
fun create(

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ModuleSettings.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ data class ModuleSettings(
2828
// LoRA mode toggle - when enabled, allows multiple model selection
2929
val isLoraMode: Boolean = false,
3030

31+
// Foundation PTD path - shared base weights for all LoRA models
32+
val foundationDataPath: String = "",
33+
3134
// Multi-model support fields (used when isLoraMode is true)
3235
val models: List<ModelConfiguration> = emptyList(),
3336
val activeModelId: String = "",
@@ -72,10 +75,11 @@ data class ModuleSettings(
7275
}
7376

7477
/**
75-
* Gets the effective shared data path (falls back to legacy dataPath).
78+
* Gets the effective foundation data path for LoRA mode.
79+
* Priority: foundationDataPath > sharedDataPath > dataPath
7680
*/
7781
fun getEffectiveDataPath(): String {
78-
return sharedDataPath.ifEmpty { dataPath }
82+
return foundationDataPath.ifEmpty { sharedDataPath.ifEmpty { dataPath } }
7983
}
8084

8185
/**

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/WelcomeActivity.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ class WelcomeActivity : ComponentActivity() {
5656
},
5757
onAppSettingsClick = {
5858
startActivity(Intent(this@WelcomeActivity, AppSettingsActivity::class.java))
59-
},
60-
onStartChatClick = {
61-
startActivity(Intent(this@WelcomeActivity, MainActivity::class.java))
6259
}
6360
)
6461
}

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/screens/ModelSettingsScreen.kt

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,19 @@ fun ModelSettingsScreen(
163163

164164
// ========== Conditional UI based on LoRA mode ==========
165165
if (viewModel.moduleSettings.isLoraMode) {
166+
// Foundation PTD selector (required for LoRA)
167+
SettingsRow(
168+
label = "Foundation PTD",
169+
value = viewModel.getFilenameFromPath(viewModel.moduleSettings.foundationDataPath)
170+
.ifEmpty { "no foundation PTD selected" },
171+
onClick = {
172+
viewModel.refreshFileLists()
173+
viewModel.showFoundationDataPathDialog = true
174+
}
175+
)
176+
177+
Spacer(modifier = Modifier.height(16.dp))
178+
166179
// LoRA Mode: Multi-model selection
167180
Text(
168181
text = "Models",
@@ -440,6 +453,7 @@ fun ModelSettingsScreen(
440453
ModelDialog(viewModel)
441454
TokenizerDialog(viewModel)
442455
DataPathDialog(viewModel)
456+
FoundationDataPathDialog(viewModel)
443457
ModelTypeDialog(viewModel)
444458
LoadModelDialog(viewModel, onLoadModel, onBackPressed)
445459
ResetSystemPromptDialog(viewModel)
@@ -618,6 +632,36 @@ private fun DataPathDialog(viewModel: ModelSettingsViewModel) {
618632
}
619633
}
620634

635+
@Composable
636+
private fun FoundationDataPathDialog(viewModel: ModelSettingsViewModel) {
637+
if (viewModel.showFoundationDataPathDialog) {
638+
if (viewModel.dataPathFiles.isEmpty()) {
639+
AlertDialog(
640+
onDismissRequest = { viewModel.showFoundationDataPathDialog = false },
641+
title = { Text("Select Foundation PTD") },
642+
text = {
643+
Text("No PTD files (.ptd) found in /data/local/tmp/llama/\n\nPlease push foundation PTD file using:\nadb push <foundation>.ptd /data/local/tmp/llama/")
644+
},
645+
confirmButton = {
646+
TextButton(onClick = { viewModel.showFoundationDataPathDialog = false }) {
647+
Text("OK")
648+
}
649+
}
650+
)
651+
} else {
652+
SingleChoiceDialog(
653+
title = "Select Foundation PTD",
654+
options = viewModel.dataPathFiles.toList(),
655+
onSelect = { selected ->
656+
viewModel.selectFoundationDataPath(selected)
657+
viewModel.showFoundationDataPathDialog = false
658+
},
659+
onDismiss = { viewModel.showFoundationDataPathDialog = false }
660+
)
661+
}
662+
}
663+
}
664+
621665
@Composable
622666
private fun ModelTypeDialog(viewModel: ModelSettingsViewModel) {
623667
if (viewModel.showModelTypeDialog) {
@@ -914,6 +958,87 @@ private fun AddModelDialog(viewModel: ModelSettingsViewModel) {
914958
}
915959
}
916960
},
961+
confirmButton = {
962+
TextButton(onClick = { viewModel.goToAddModelStep(4) }) {
963+
Text("Next")
964+
}
965+
},
966+
dismissButton = {
967+
TextButton(onClick = { viewModel.previousAddModelStep() }) {
968+
Text("Back")
969+
}
970+
}
971+
)
972+
}
973+
4 -> {
974+
// Step 4: Select adapter PTDs (optional, multi-select)
975+
val appColors = LocalAppColors.current
976+
977+
AlertDialog(
978+
onDismissRequest = { viewModel.previousAddModelStep() },
979+
title = { Text("Step 4: Select Adapter PTDs (Optional)") },
980+
text = {
981+
Column(
982+
modifier = Modifier
983+
.fillMaxWidth()
984+
.height(400.dp)
985+
) {
986+
Text(
987+
text = "Select one or more adapter PTD files for this model:",
988+
fontSize = 14.sp,
989+
modifier = Modifier.padding(bottom = 8.dp)
990+
)
991+
992+
if (viewModel.dataPathFiles.isEmpty()) {
993+
Text(
994+
text = "No .ptd files found in /data/local/tmp/llama/",
995+
fontSize = 12.sp,
996+
color = appColors.settingsSecondaryText
997+
)
998+
} else {
999+
Column(
1000+
modifier = Modifier
1001+
.weight(1f)
1002+
.verticalScroll(rememberScrollState())
1003+
) {
1004+
viewModel.dataPathFiles.forEach { adapterPath ->
1005+
val isSelected = viewModel.tempAdapterPaths.contains(adapterPath)
1006+
Row(
1007+
modifier = Modifier
1008+
.fillMaxWidth()
1009+
.clickable {
1010+
if (isSelected) {
1011+
viewModel.removeTempAdapter(adapterPath)
1012+
} else {
1013+
viewModel.addTempAdapter(adapterPath)
1014+
}
1015+
}
1016+
.padding(vertical = 4.dp),
1017+
verticalAlignment = Alignment.CenterVertically
1018+
) {
1019+
androidx.compose.material3.Checkbox(
1020+
checked = isSelected,
1021+
onCheckedChange = null
1022+
)
1023+
Text(
1024+
text = adapterPath.substringAfterLast('/'),
1025+
modifier = Modifier.padding(start = 8.dp),
1026+
fontSize = 14.sp
1027+
)
1028+
}
1029+
}
1030+
}
1031+
}
1032+
1033+
Spacer(modifier = Modifier.height(8.dp))
1034+
1035+
Text(
1036+
text = "Selected: ${viewModel.tempAdapterPaths.size} adapter(s)",
1037+
fontSize = 12.sp,
1038+
color = appColors.settingsSecondaryText
1039+
)
1040+
}
1041+
},
9171042
confirmButton = {
9181043
TextButton(onClick = { viewModel.confirmAddModel() }) {
9191044
Text("Add Model")

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/viewmodel/ChatViewModel.kt

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -214,27 +214,26 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), L
214214
if (!modelConfig.isValid()) continue
215215

216216
try {
217+
// Build dataFiles list: foundation PTD + adapter PTDs
218+
val dataFiles = mutableListOf<String>()
219+
if (sharedDataPath.isNotEmpty()) {
220+
dataFiles.add(sharedDataPath)
221+
}
222+
dataFiles.addAll(modelConfig.adapterFilePaths)
223+
224+
val dataFilesLog = if (dataFiles.isEmpty()) "no data files" else dataFiles.joinToString(", ")
217225
ETLogging.getInstance().log(
218-
"LoRA: Loading model ${modelConfig.displayName} with tokenizer ${modelConfig.tokenizerFilePath} data path $sharedDataPath"
226+
"LoRA: Loading model ${modelConfig.displayName} with tokenizer ${modelConfig.tokenizerFilePath}, data files: $dataFilesLog"
219227
)
220228

221229
val runStartTime = System.currentTimeMillis()
222-
val llmModule = if (sharedDataPath.isEmpty()) {
223-
LlmModule(
224-
ModelUtils.getModelCategory(modelConfig.modelType, modelConfig.backendType),
225-
modelConfig.modelFilePath,
226-
modelConfig.tokenizerFilePath,
227-
modelConfig.temperature.toFloat()
228-
)
229-
} else {
230-
LlmModule(
231-
ModelUtils.getModelCategory(modelConfig.modelType, modelConfig.backendType),
232-
modelConfig.modelFilePath,
233-
modelConfig.tokenizerFilePath,
234-
modelConfig.temperature.toFloat(),
235-
sharedDataPath
236-
)
237-
}
230+
val llmModule = LlmModule(
231+
ModelUtils.getModelCategory(modelConfig.modelType, modelConfig.backendType),
232+
modelConfig.modelFilePath,
233+
modelConfig.tokenizerFilePath,
234+
modelConfig.temperature.toFloat(),
235+
dataFiles
236+
)
238237

239238
llmModule.load()
240239
val loadDuration = System.currentTimeMillis() - runStartTime
@@ -319,23 +318,21 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), L
319318
isModelReady = false
320319

321320
try {
322-
val runStartTime = System.currentTimeMillis()
323-
val llmModule = if (sharedDataPath.isEmpty()) {
324-
LlmModule(
325-
ModelUtils.getModelCategory(modelConfig.modelType, modelConfig.backendType),
326-
modelConfig.modelFilePath,
327-
modelConfig.tokenizerFilePath,
328-
modelConfig.temperature.toFloat()
329-
)
330-
} else {
331-
LlmModule(
332-
ModelUtils.getModelCategory(modelConfig.modelType, modelConfig.backendType),
333-
modelConfig.modelFilePath,
334-
modelConfig.tokenizerFilePath,
335-
modelConfig.temperature.toFloat(),
336-
sharedDataPath
337-
)
321+
// Build dataFiles list: foundation PTD + adapter PTDs
322+
val dataFiles = mutableListOf<String>()
323+
if (sharedDataPath.isNotEmpty()) {
324+
dataFiles.add(sharedDataPath)
338325
}
326+
dataFiles.addAll(modelConfig.adapterFilePaths)
327+
328+
val runStartTime = System.currentTimeMillis()
329+
val llmModule = LlmModule(
330+
ModelUtils.getModelCategory(modelConfig.modelType, modelConfig.backendType),
331+
modelConfig.modelFilePath,
332+
modelConfig.tokenizerFilePath,
333+
modelConfig.temperature.toFloat(),
334+
dataFiles
335+
)
339336

340337
llmModule.load()
341338
val loadDuration = System.currentTimeMillis() - runStartTime

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/viewmodel/ModelSettingsViewModel.kt

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class ModelSettingsViewModel : ViewModel() {
3636
var showModelDialog by mutableStateOf(false)
3737
var showTokenizerDialog by mutableStateOf(false)
3838
var showDataPathDialog by mutableStateOf(false)
39+
var showFoundationDataPathDialog by mutableStateOf(false)
40+
var showAdapterDialog by mutableStateOf(false)
3941
var showModelTypeDialog by mutableStateOf(false)
4042
var showLoadModelDialog by mutableStateOf(false)
4143
var showResetSystemPromptDialog by mutableStateOf(false)
@@ -55,6 +57,8 @@ class ModelSettingsViewModel : ViewModel() {
5557
private set
5658
var tempModelType by mutableStateOf(ModelType.LLAMA_3)
5759
private set
60+
var tempAdapterPaths by mutableStateOf<List<String>>(emptyList())
61+
private set
5862

5963
// Model to be removed (for confirmation dialog)
6064
var modelToRemove by mutableStateOf<String?>(null)
@@ -149,6 +153,11 @@ class ModelSettingsViewModel : ViewModel() {
149153
)
150154
}
151155

156+
// Foundation PTD selection (for LoRA mode)
157+
fun selectFoundationDataPath(dataPath: String) {
158+
moduleSettings = moduleSettings.copy(foundationDataPath = dataPath)
159+
}
160+
152161
// Model type selection
153162
fun selectModelType(modelType: ModelType) {
154163
moduleSettings = moduleSettings.copy(
@@ -250,6 +259,7 @@ class ModelSettingsViewModel : ViewModel() {
250259
tempModelPath = ""
251260
tempTokenizerPath = ""
252261
tempModelType = ModelType.LLAMA_3
262+
tempAdapterPaths = emptyList()
253263
addModelStep = 1
254264
showAddModelDialog = true
255265
refreshFileLists()
@@ -283,6 +293,13 @@ class ModelSettingsViewModel : ViewModel() {
283293
tempModelType = modelType
284294
}
285295

296+
/**
297+
* Sets the add model step (for navigation).
298+
*/
299+
fun goToAddModelStep(step: Int) {
300+
addModelStep = step
301+
}
302+
286303
/**
287304
* Confirms and adds the new model.
288305
*/
@@ -295,7 +312,7 @@ class ModelSettingsViewModel : ViewModel() {
295312
modelType = tempModelType,
296313
backendType = moduleSettings.backendType,
297314
temperature = ModuleSettings.DEFAULT_TEMPERATURE
298-
)
315+
).copy(adapterFilePaths = tempAdapterPaths)
299316

300317
moduleSettings = moduleSettings.addModel(newModel)
301318
cancelAddModel()
@@ -310,6 +327,7 @@ class ModelSettingsViewModel : ViewModel() {
310327
tempModelPath = ""
311328
tempTokenizerPath = ""
312329
tempModelType = ModelType.LLAMA_3
330+
tempAdapterPaths = emptyList()
313331
}
314332

315333
/**
@@ -324,6 +342,9 @@ class ModelSettingsViewModel : ViewModel() {
324342
3 -> {
325343
addModelStep = 2
326344
}
345+
4 -> {
346+
addModelStep = 3
347+
}
327348
else -> cancelAddModel()
328349
}
329350
}
@@ -386,4 +407,20 @@ class ModelSettingsViewModel : ViewModel() {
386407
showMemoryWarningDialog = false
387408
showLoadModelDialog = true
388409
}
410+
411+
/**
412+
* Adds an adapter PTD to the temp adapter list.
413+
*/
414+
fun addTempAdapter(adapterPath: String) {
415+
if (adapterPath.isNotEmpty() && !tempAdapterPaths.contains(adapterPath)) {
416+
tempAdapterPaths = tempAdapterPaths + adapterPath
417+
}
418+
}
419+
420+
/**
421+
* Removes an adapter PTD from the temp adapter list.
422+
*/
423+
fun removeTempAdapter(adapterPath: String) {
424+
tempAdapterPaths = tempAdapterPaths.filter { it != adapterPath }
425+
}
389426
}

llm/android/LlamaDemo/gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ kotlin.code.style=official
2121
# resources declared in the library itself and none from the library's dependencies,
2222
# thereby reducing the size of the R class for that library
2323
android.nonTransitiveRClass=true
24+
useLocalAar=true

0 commit comments

Comments
 (0)