From 4f72f0e72fa788696a9f01cf3b38a279c3d5f7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=81=97?= Date: Fri, 5 Sep 2025 16:46:38 +0800 Subject: [PATCH 1/2] fix ui display bugs --- apps/Android/MnnLlmChat/.gitignore | 3 +- .../app/src/main/assets/test_page.html | 59 ++++++++++++------- .../mnnllm/android/chat/ChatActivity.kt | 2 +- .../api/openai/manager/ApiServiceManager.kt | 7 ++- .../api/openai/manager/CurrentModelManager.kt | 28 +++++++++ .../api/openai/manager/NotificationManager.kt | 1 - .../network/services/MNNModelsService.kt | 46 ++++++++++++--- .../api/openai/service/OpenAIService.kt | 19 +++++- 8 files changed, 127 insertions(+), 38 deletions(-) create mode 100644 apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt diff --git a/apps/Android/MnnLlmChat/.gitignore b/apps/Android/MnnLlmChat/.gitignore index 77a3a8f4..b54bd090 100644 --- a/apps/Android/MnnLlmChat/.gitignore +++ b/apps/Android/MnnLlmChat/.gitignore @@ -1,4 +1,5 @@ *.jks toc.pb *.apks -.cursorrules \ No newline at end of file +.cursorrules +release_outputs/ \ No newline at end of file diff --git a/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html b/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html index 6b7ebe1e..1c851aa1 100644 --- a/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html +++ b/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html @@ -74,6 +74,22 @@ background: white; } + #model-select:disabled { + background-color: #f8f9fa; + color: #6c757d; + cursor: not-allowed; + opacity: 0.7; + } + + .model-notice { + margin-top: 0.5rem; + padding: 0.5rem; + background-color: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 6px; + text-align: center; + } + .button { padding: 0.5rem 1rem; font-size: 0.9rem; @@ -254,10 +270,13 @@
- - + +
+
+ ⚠️ Model switching is temporarily not supported. Pleas change models in the app.
@@ -299,9 +318,14 @@ refreshModels(); }; + // 显示模型切换提醒 + function showModelSwitchAlert() { + alert("Model switching is temporarily not supported.\n\nTo change models, please:\n1. Close this web page\n2. Restart the MNN Chat app\n3. Select a different model from the model list"); + } + async function refreshModels() { try { - updateStatus("Loading models..."); + updateStatus("Loading current model..."); const response = await fetch("/v1/models", { method: "GET", headers: { @@ -324,21 +348,16 @@ modelSelect.innerHTML = ''; updateStatus("No models available"); } else { - availableModels.forEach(model => { - const option = document.createElement("option"); - option.value = model.id; - // 只显示模型名称的后缀部分 - const modelName = extractModelSuffix(model.id); - option.textContent = modelName; - modelSelect.appendChild(option); - }); + // 只显示第一个模型(当前使用的模型) + const currentModelData = availableModels[0]; + const option = document.createElement("option"); + option.value = currentModelData.id; + option.textContent = extractModelSuffix(currentModelData.id); + modelSelect.appendChild(option); - // Set first model as default - if (availableModels.length > 0) { - currentModel = availableModels[0].id; - modelSelect.value = currentModel; - updateStatus(`Model loaded: ${extractModelSuffix(currentModel)}`); - } + currentModel = currentModelData.id; + modelSelect.value = currentModel; + updateStatus(`Current model: ${extractModelSuffix(currentModel)}`); } } catch (error) { console.error("Failed to load models:", error); @@ -378,11 +397,7 @@ return suffix || modelId; } - // Handle model selection change - document.getElementById("model-select").addEventListener("change", function() { - currentModel = this.value; - updateStatus(`Model switched to: ${extractModelSuffix(currentModel)}`); - }); + // Model selection change is disabled - no event listener needed function updateStatus(message) { document.getElementById("status").textContent = message; diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt index efc4abed..768c7345 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt @@ -337,7 +337,7 @@ class ChatActivity : AppCompatActivity() { } // Check API service settings and start service if (isApiServiceEnabled(this)) { - ApiServiceManager.startApiService(this) + ApiServiceManager.startApiService(this, modelId) } } } diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt index 2cb20b5f..dc3ac4d4 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt @@ -16,12 +16,13 @@ object ApiServiceManager { /** * 启动API服务 * @param context 上下文,必须是ChatActivity实例 + * @param modelId 当前模型ID * @return 是否成功启动 */ - fun startApiService(context: Context): Boolean { + fun startApiService(context: Context, modelId: String? = null): Boolean { return try { - OpenAIService.startService(context) - Timber.tag(TAG).i("API service start requested") + OpenAIService.startService(context, modelId) + Timber.tag(TAG).i("API service start requested with modelId: $modelId") true } catch (e: Exception) { Timber.tag(TAG).e(e, "Failed to start API service") diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt new file mode 100644 index 00000000..b3177380 --- /dev/null +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt @@ -0,0 +1,28 @@ +package com.alibaba.mnnllm.api.openai.manager + +/** + * Current model manager + * Used to store and access the currently active model ID in the API service + */ +object CurrentModelManager { + private var currentModelId: String? = null + + /** + * Set current model ID + */ + fun setCurrentModelId(modelId: String?) { + currentModelId = modelId + } + + /** + * Get current model ID + */ + fun getCurrentModelId(): String? = currentModelId + + /** + * Clear current model ID + */ + fun clearCurrentModelId() { + currentModelId = null + } +} diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt index 0f6af3ce..948042cb 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt @@ -136,7 +136,6 @@ class ApiNotificationManager(private val context: Context) { .setOngoing(true) .setAutoCancel(false) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentIntent(mainActivityPendingIntent) // .addAction( // R.drawable.ic_dialog_alert, // context.getString(com.alibaba.mnnllm.android.R.string.api_service_stop), diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt index e31213ab..e8a6717d 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt @@ -6,6 +6,7 @@ import com.alibaba.mnnllm.api.openai.network.models.ModelPermission import com.alibaba.mnnllm.api.openai.network.models.ModelsResponse import com.alibaba.mnnllm.android.modelist.ModelListManager import com.alibaba.mnnllm.android.MnnLlmApplication +import com.alibaba.mnnllm.api.openai.manager.CurrentModelManager import io.ktor.http.HttpStatusCode import io.ktor.server.application.call import io.ktor.server.response.respond @@ -23,31 +24,58 @@ class MNNModelsService { try { logger.logRequestStart(traceId, call) + val currentModelId = CurrentModelManager.getCurrentModelId() + if (currentModelId == null) { + Timber.tag("MNNModelsService").w("No current model ID available") + logger.logError(traceId, Exception("No current model ID available"), "No current model ID available") + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to "No current model available")) + return + } + val context = MnnLlmApplication.getAppContext() val availableModels = runBlocking { ModelListManager.loadAvailableModels(context) } - val modelDataList = availableModels.map { modelWrapper -> - ModelData( - id = modelWrapper.modelItem.modelId ?: "unknown", + + // 只返回当前正在使用的模型 + val currentModelWrapper = availableModels.find { + it.modelItem.modelId == currentModelId + } + + val modelDataList = if (currentModelWrapper != null) { + listOf(ModelData( + id = currentModelWrapper.modelItem.modelId ?: "unknown", created = System.currentTimeMillis() / 1000, // Unix timestamp permission = listOf( ModelPermission( - id = "modelperm-${modelWrapper.modelItem.modelId}", + id = "modelperm-${currentModelWrapper.modelItem.modelId}", created = System.currentTimeMillis() / 1000 ) ) - ) + )) + } else { + // 如果找不到当前模型,返回一个默认的模型数据 + listOf(ModelData( + id = currentModelId, + created = System.currentTimeMillis() / 1000, + permission = listOf( + ModelPermission( + id = "modelperm-$currentModelId", + created = System.currentTimeMillis() / 1000 + ) + ) + )) } + val response = ModelsResponse(data = modelDataList) call.respond(response) - logger.logInfo(traceId, "Models list returned successfully") + logger.logInfo(traceId, "Current model returned successfully: $currentModelId") } catch (e: Exception) { - Timber.tag("MNNModelsService").e(e, "Error getting available models") - logger.logError(traceId, e, "Failed to get available models") - call.respond(HttpStatusCode.InternalServerError, mapOf("error" to "Failed to get available models")) + Timber.tag("MNNModelsService").e(e, "Error getting current model") + logger.logError(traceId, e, "Failed to get current model") + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to "Failed to get current model")) } } } diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt index f258d3b4..d3a73d29 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt @@ -16,17 +16,19 @@ import androidx.core.content.ContextCompat import com.alibaba.mnnllm.android.chat.ChatActivity import com.alibaba.mnnllm.api.openai.service.ApiServiceCoordinator import com.alibaba.mnnllm.api.openai.manager.ApiNotificationManager +import com.alibaba.mnnllm.api.openai.manager.CurrentModelManager import timber.log.Timber class OpenAIService : Service() { private val TAG = this::class.java.simpleName private lateinit var coordinator: ApiServiceCoordinator + private var currentModelId: String? = null companion object { private var isServiceRunning = false private var serviceConnection: ServiceConnection? = null - fun startService(context: Context) { + fun startService(context: Context, modelId: String? = null) { if (context !is ChatActivity) { Timber.tag("ServiceStartCondition").w("Invalid context. Not starting service.") return @@ -44,6 +46,8 @@ class OpenAIService : Service() { } val serviceIntent = Intent(context, OpenAIService::class.java) + // 传递 modelId 到服务 + modelId?.let { serviceIntent.putExtra("modelId", it) } isServiceRunning = true try { context.startForegroundService(serviceIntent) @@ -135,6 +139,14 @@ class OpenAIService : Service() { stopSelf() return START_NOT_STICKY } + + // 获取传递的 modelId + intent?.getStringExtra("modelId")?.let { modelId -> + currentModelId = modelId + CurrentModelManager.setCurrentModelId(modelId) + Timber.tag(TAG).i("Service started with modelId: $modelId") + } + val notification = coordinator.getNotification() if (notification != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -186,6 +198,9 @@ class OpenAIService : Service() { } catch (e: Exception) { Timber.tag(TAG).e(e, "Failed to cleanup coordinator") } + + // 清除全局模型ID + CurrentModelManager.clearCurrentModelId() try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -218,4 +233,6 @@ class OpenAIService : Service() { fun getServerPort(): Int? = coordinator.getServerPort() fun isServerRunning(): Boolean = coordinator.isServerRunning + + fun getCurrentModelId(): String? = currentModelId } From 58c2b9ef16e0b7d6c3ea7757901a69413e2ee553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=81=97?= Date: Fri, 5 Sep 2025 17:18:49 +0800 Subject: [PATCH 2/2] update api service --- apps/Android/MnnLlmChat/.gitignore | 3 +- apps/Android/MnnLlmChat/README.md | 4 +- apps/Android/MnnLlmChat/README_CN.md | 2 +- apps/Android/MnnLlmChat/app/build.gradle | 4 +- .../app/src/main/assets/test_page.html | 59 ++++++++++++------- .../mnnllm/android/chat/ChatActivity.kt | 2 +- .../api/openai/manager/ApiServiceManager.kt | 7 ++- .../api/openai/manager/CurrentModelManager.kt | 28 +++++++++ .../api/openai/manager/NotificationManager.kt | 1 - .../network/services/MNNModelsService.kt | 46 ++++++++++++--- .../api/openai/service/OpenAIService.kt | 19 +++++- apps/Android/MnnLlmChat/scripts/release.sh | 28 ++++++--- 12 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt diff --git a/apps/Android/MnnLlmChat/.gitignore b/apps/Android/MnnLlmChat/.gitignore index 77a3a8f4..b54bd090 100644 --- a/apps/Android/MnnLlmChat/.gitignore +++ b/apps/Android/MnnLlmChat/.gitignore @@ -1,4 +1,5 @@ *.jks toc.pb *.apks -.cursorrules \ No newline at end of file +.cursorrules +release_outputs/ \ No newline at end of file diff --git a/apps/Android/MnnLlmChat/README.md b/apps/Android/MnnLlmChat/README.md index 8d9e475a..34658a64 100644 --- a/apps/Android/MnnLlmChat/README.md +++ b/apps/Android/MnnLlmChat/README.md @@ -61,8 +61,8 @@ This is our full multimodal language model (LLM) Android app ``` # Releases -## Version 0.7.3 -+ Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_3.apk) +## Version 0.7.3.1 ++ Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_3_1.apk) + Optimize ApiService ## Version 0.7.2 + Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_2.apk) diff --git a/apps/Android/MnnLlmChat/README_CN.md b/apps/Android/MnnLlmChat/README_CN.md index e9aa41a5..706f2e00 100644 --- a/apps/Android/MnnLlmChat/README_CN.md +++ b/apps/Android/MnnLlmChat/README_CN.md @@ -54,7 +54,7 @@ # Releases ## Version 0.7.3 -+ 点击这里 [下载](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_3.apk) ++ 点击这里 [下载](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_3_1.apk) + 优化 API 服务 ## 版本 0.7.2 diff --git a/apps/Android/MnnLlmChat/app/build.gradle b/apps/Android/MnnLlmChat/app/build.gradle index 79ffc3ac..019bafea 100644 --- a/apps/Android/MnnLlmChat/app/build.gradle +++ b/apps/Android/MnnLlmChat/app/build.gradle @@ -59,8 +59,8 @@ android { applicationId "com.alibaba.mnnllm.android" minSdk 26 targetSdk 35 - versionCode 703 - versionName "0.7.3" + versionCode 731 + versionName "0.7.3.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { diff --git a/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html b/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html index 6b7ebe1e..1c851aa1 100644 --- a/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html +++ b/apps/Android/MnnLlmChat/app/src/main/assets/test_page.html @@ -74,6 +74,22 @@ background: white; } + #model-select:disabled { + background-color: #f8f9fa; + color: #6c757d; + cursor: not-allowed; + opacity: 0.7; + } + + .model-notice { + margin-top: 0.5rem; + padding: 0.5rem; + background-color: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 6px; + text-align: center; + } + .button { padding: 0.5rem 1rem; font-size: 0.9rem; @@ -254,10 +270,13 @@
- - + +
+
+ ⚠️ Model switching is temporarily not supported. Pleas change models in the app.
@@ -299,9 +318,14 @@ refreshModels(); }; + // 显示模型切换提醒 + function showModelSwitchAlert() { + alert("Model switching is temporarily not supported.\n\nTo change models, please:\n1. Close this web page\n2. Restart the MNN Chat app\n3. Select a different model from the model list"); + } + async function refreshModels() { try { - updateStatus("Loading models..."); + updateStatus("Loading current model..."); const response = await fetch("/v1/models", { method: "GET", headers: { @@ -324,21 +348,16 @@ modelSelect.innerHTML = ''; updateStatus("No models available"); } else { - availableModels.forEach(model => { - const option = document.createElement("option"); - option.value = model.id; - // 只显示模型名称的后缀部分 - const modelName = extractModelSuffix(model.id); - option.textContent = modelName; - modelSelect.appendChild(option); - }); + // 只显示第一个模型(当前使用的模型) + const currentModelData = availableModels[0]; + const option = document.createElement("option"); + option.value = currentModelData.id; + option.textContent = extractModelSuffix(currentModelData.id); + modelSelect.appendChild(option); - // Set first model as default - if (availableModels.length > 0) { - currentModel = availableModels[0].id; - modelSelect.value = currentModel; - updateStatus(`Model loaded: ${extractModelSuffix(currentModel)}`); - } + currentModel = currentModelData.id; + modelSelect.value = currentModel; + updateStatus(`Current model: ${extractModelSuffix(currentModel)}`); } } catch (error) { console.error("Failed to load models:", error); @@ -378,11 +397,7 @@ return suffix || modelId; } - // Handle model selection change - document.getElementById("model-select").addEventListener("change", function() { - currentModel = this.value; - updateStatus(`Model switched to: ${extractModelSuffix(currentModel)}`); - }); + // Model selection change is disabled - no event listener needed function updateStatus(message) { document.getElementById("status").textContent = message; diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt index efc4abed..768c7345 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/android/chat/ChatActivity.kt @@ -337,7 +337,7 @@ class ChatActivity : AppCompatActivity() { } // Check API service settings and start service if (isApiServiceEnabled(this)) { - ApiServiceManager.startApiService(this) + ApiServiceManager.startApiService(this, modelId) } } } diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt index 2cb20b5f..dc3ac4d4 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/ApiServiceManager.kt @@ -16,12 +16,13 @@ object ApiServiceManager { /** * 启动API服务 * @param context 上下文,必须是ChatActivity实例 + * @param modelId 当前模型ID * @return 是否成功启动 */ - fun startApiService(context: Context): Boolean { + fun startApiService(context: Context, modelId: String? = null): Boolean { return try { - OpenAIService.startService(context) - Timber.tag(TAG).i("API service start requested") + OpenAIService.startService(context, modelId) + Timber.tag(TAG).i("API service start requested with modelId: $modelId") true } catch (e: Exception) { Timber.tag(TAG).e(e, "Failed to start API service") diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt new file mode 100644 index 00000000..b3177380 --- /dev/null +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/CurrentModelManager.kt @@ -0,0 +1,28 @@ +package com.alibaba.mnnllm.api.openai.manager + +/** + * Current model manager + * Used to store and access the currently active model ID in the API service + */ +object CurrentModelManager { + private var currentModelId: String? = null + + /** + * Set current model ID + */ + fun setCurrentModelId(modelId: String?) { + currentModelId = modelId + } + + /** + * Get current model ID + */ + fun getCurrentModelId(): String? = currentModelId + + /** + * Clear current model ID + */ + fun clearCurrentModelId() { + currentModelId = null + } +} diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt index 0f6af3ce..948042cb 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/manager/NotificationManager.kt @@ -136,7 +136,6 @@ class ApiNotificationManager(private val context: Context) { .setOngoing(true) .setAutoCancel(false) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentIntent(mainActivityPendingIntent) // .addAction( // R.drawable.ic_dialog_alert, // context.getString(com.alibaba.mnnllm.android.R.string.api_service_stop), diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt index e31213ab..e8a6717d 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/network/services/MNNModelsService.kt @@ -6,6 +6,7 @@ import com.alibaba.mnnllm.api.openai.network.models.ModelPermission import com.alibaba.mnnllm.api.openai.network.models.ModelsResponse import com.alibaba.mnnllm.android.modelist.ModelListManager import com.alibaba.mnnllm.android.MnnLlmApplication +import com.alibaba.mnnllm.api.openai.manager.CurrentModelManager import io.ktor.http.HttpStatusCode import io.ktor.server.application.call import io.ktor.server.response.respond @@ -23,31 +24,58 @@ class MNNModelsService { try { logger.logRequestStart(traceId, call) + val currentModelId = CurrentModelManager.getCurrentModelId() + if (currentModelId == null) { + Timber.tag("MNNModelsService").w("No current model ID available") + logger.logError(traceId, Exception("No current model ID available"), "No current model ID available") + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to "No current model available")) + return + } + val context = MnnLlmApplication.getAppContext() val availableModels = runBlocking { ModelListManager.loadAvailableModels(context) } - val modelDataList = availableModels.map { modelWrapper -> - ModelData( - id = modelWrapper.modelItem.modelId ?: "unknown", + + // 只返回当前正在使用的模型 + val currentModelWrapper = availableModels.find { + it.modelItem.modelId == currentModelId + } + + val modelDataList = if (currentModelWrapper != null) { + listOf(ModelData( + id = currentModelWrapper.modelItem.modelId ?: "unknown", created = System.currentTimeMillis() / 1000, // Unix timestamp permission = listOf( ModelPermission( - id = "modelperm-${modelWrapper.modelItem.modelId}", + id = "modelperm-${currentModelWrapper.modelItem.modelId}", created = System.currentTimeMillis() / 1000 ) ) - ) + )) + } else { + // 如果找不到当前模型,返回一个默认的模型数据 + listOf(ModelData( + id = currentModelId, + created = System.currentTimeMillis() / 1000, + permission = listOf( + ModelPermission( + id = "modelperm-$currentModelId", + created = System.currentTimeMillis() / 1000 + ) + ) + )) } + val response = ModelsResponse(data = modelDataList) call.respond(response) - logger.logInfo(traceId, "Models list returned successfully") + logger.logInfo(traceId, "Current model returned successfully: $currentModelId") } catch (e: Exception) { - Timber.tag("MNNModelsService").e(e, "Error getting available models") - logger.logError(traceId, e, "Failed to get available models") - call.respond(HttpStatusCode.InternalServerError, mapOf("error" to "Failed to get available models")) + Timber.tag("MNNModelsService").e(e, "Error getting current model") + logger.logError(traceId, e, "Failed to get current model") + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to "Failed to get current model")) } } } diff --git a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt index f258d3b4..d3a73d29 100644 --- a/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt +++ b/apps/Android/MnnLlmChat/app/src/main/java/com/alibaba/mnnllm/api/openai/service/OpenAIService.kt @@ -16,17 +16,19 @@ import androidx.core.content.ContextCompat import com.alibaba.mnnllm.android.chat.ChatActivity import com.alibaba.mnnllm.api.openai.service.ApiServiceCoordinator import com.alibaba.mnnllm.api.openai.manager.ApiNotificationManager +import com.alibaba.mnnllm.api.openai.manager.CurrentModelManager import timber.log.Timber class OpenAIService : Service() { private val TAG = this::class.java.simpleName private lateinit var coordinator: ApiServiceCoordinator + private var currentModelId: String? = null companion object { private var isServiceRunning = false private var serviceConnection: ServiceConnection? = null - fun startService(context: Context) { + fun startService(context: Context, modelId: String? = null) { if (context !is ChatActivity) { Timber.tag("ServiceStartCondition").w("Invalid context. Not starting service.") return @@ -44,6 +46,8 @@ class OpenAIService : Service() { } val serviceIntent = Intent(context, OpenAIService::class.java) + // 传递 modelId 到服务 + modelId?.let { serviceIntent.putExtra("modelId", it) } isServiceRunning = true try { context.startForegroundService(serviceIntent) @@ -135,6 +139,14 @@ class OpenAIService : Service() { stopSelf() return START_NOT_STICKY } + + // 获取传递的 modelId + intent?.getStringExtra("modelId")?.let { modelId -> + currentModelId = modelId + CurrentModelManager.setCurrentModelId(modelId) + Timber.tag(TAG).i("Service started with modelId: $modelId") + } + val notification = coordinator.getNotification() if (notification != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -186,6 +198,9 @@ class OpenAIService : Service() { } catch (e: Exception) { Timber.tag(TAG).e(e, "Failed to cleanup coordinator") } + + // 清除全局模型ID + CurrentModelManager.clearCurrentModelId() try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -218,4 +233,6 @@ class OpenAIService : Service() { fun getServerPort(): Int? = coordinator.getServerPort() fun isServerRunning(): Boolean = coordinator.isServerRunning + + fun getCurrentModelId(): String? = currentModelId } diff --git a/apps/Android/MnnLlmChat/scripts/release.sh b/apps/Android/MnnLlmChat/scripts/release.sh index b56d1b8d..3b60ef74 100755 --- a/apps/Android/MnnLlmChat/scripts/release.sh +++ b/apps/Android/MnnLlmChat/scripts/release.sh @@ -107,11 +107,15 @@ build_standard_debug() { ./gradlew assembleStandardDebug - # Copy APK to output directory + # Generate version-based filename (replace dots with underscores) + VERSION_FILENAME=$(echo "$VERSION_NAME" | sed 's/\./_/g') + APK_FILENAME="mnn_chat_${VERSION_FILENAME}.apk" + + # Copy APK to output directory with version-based name APK_PATH="$BUILD_DIR/outputs/apk/standard/debug/app-standard-debug.apk" if [[ -f "$APK_PATH" ]]; then - cp "$APK_PATH" "$CDN_UPLOAD_DIR/" - log_success "Standard debug APK built: $CDN_UPLOAD_DIR/app-standard-debug.apk" + cp "$APK_PATH" "$CDN_UPLOAD_DIR/$APK_FILENAME" + log_success "Standard debug APK built: $CDN_UPLOAD_DIR/$APK_FILENAME" else log_error "Standard debug APK not found at $APK_PATH" exit 1 @@ -171,13 +175,17 @@ upload_to_cdn() { # Configure ossutil ossutil config -e "$CDN_ENDPOINT" -i "$CDN_ACCESS_KEY" -k "$CDN_SECRET_KEY" + # Generate version-based filename for upload + VERSION_FILENAME=$(echo "$VERSION_NAME" | sed 's/\./_/g') + APK_FILENAME="mnn_chat_${VERSION_FILENAME}.apk" + # Upload APK to CDN - APK_FILE="$CDN_UPLOAD_DIR/app-standard-debug.apk" + APK_FILE="$CDN_UPLOAD_DIR/$APK_FILENAME" if [[ -f "$APK_FILE" ]]; then - ossutil cp "$APK_FILE" "oss://$CDN_BUCKET/releases/$VERSION_NAME/app-standard-debug-$VERSION_NAME-$BUILD_DATE.apk" - log_success "APK uploaded to CDN: oss://$CDN_BUCKET/releases/$VERSION_NAME/app-standard-debug-$VERSION_NAME-$BUILD_DATE.apk" + ossutil cp "$APK_FILE" "oss://$CDN_BUCKET/releases/$VERSION_NAME/$APK_FILENAME" + log_success "APK uploaded to CDN: oss://$CDN_BUCKET/releases/$VERSION_NAME/$APK_FILENAME" else - log_error "APK file not found for CDN upload" + log_error "APK file not found for CDN upload: $APK_FILE" fi } @@ -237,6 +245,10 @@ EOF generate_release_notes() { log_info "Generating release notes..." + # Generate version-based filename for documentation + VERSION_FILENAME=$(echo "$VERSION_NAME" | sed 's/\./_/g') + APK_FILENAME="mnn_chat_${VERSION_FILENAME}.apk" + RELEASE_NOTES_FILE="$OUTPUT_DIR/release_notes.md" cat > "$RELEASE_NOTES_FILE" << EOF # Release Notes - $PROJECT_NAME v$VERSION_NAME @@ -250,7 +262,7 @@ generate_release_notes() { ## Build Outputs ### Standard Flavor (Debug) -- **APK**: \`app-standard-debug.apk\` +- **APK**: \`$APK_FILENAME\` - **Purpose**: CDN distribution - **Location**: \`$CDN_UPLOAD_DIR/\`