diff --git a/.gitignore b/.gitignore index d6b88489..a5f512bc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ third_party/runtime/ !third_party/runtime/CMakeLists.txt __pycache__/ -.pytest_cache/ \ No newline at end of file +.pytest_cache/ +.venv diff --git a/agents/examples/android/README.md b/agents/examples/android/README.md index 8d047378..025718ae 100644 --- a/agents/examples/android/README.md +++ b/agents/examples/android/README.md @@ -10,16 +10,36 @@ Example Android app demonstrating the Notifications Summarizer Agent integration ## Setup -1. **Add Credentials in Code** - Open `app/src/main/java/dev/deliteai/examples/App.kt` and replace the placeholder values in the `NimbleNetConfig` block (`YOUR_CLIENT_ID`, `YOUR_HOST`, `YOUR_CLIENT_SECRET`) with your own credentials. +1. **Run the following commands** -2. **Open in Android Studio** + **Change to repo root** ```bash - cd deliteAI/agents/examples/android + cd "$(git rev-parse --show-toplevel)" + ``` + +2. **Download Llama ONNX assets** + ```bash + python3 coreruntime/tests/utils/download_from_s3.py \ + --default_bucket deliteai \ + --prefix build-dependencies/llama-3.2-1B/onnx \ + --output agents/examples/android/app/src/main/assets/llama-3 \ + --archive_output True + ``` + +3. **Generate Python AST and move it into your Android assets** + ```bash + coreruntime/scripts/gen_python_ast.py \ + agents/notifications_summarizer/delitepyAssets/main.py \ + && mv "${_%.py}.ast" agents/examples/android/app/src/main/assets/ + ``` + +4. **Open in Android Studio** + ```bash + cd agents/examples/android ``` Open this directory in Android Studio. -3. **Sync and Build** +5. **Sync and Build** Let Gradle sync, then build the project. ## Running the App @@ -54,4 +74,4 @@ App-Wise Summary: com.whatsapp Meeting reminder message. Group chat has 2 new messages. -``` \ No newline at end of file +``` diff --git a/agents/examples/android/app/build.gradle.kts b/agents/examples/android/app/build.gradle.kts index abdb505b..e3b408b2 100644 --- a/agents/examples/android/app/build.gradle.kts +++ b/agents/examples/android/app/build.gradle.kts @@ -20,14 +20,15 @@ if (propertiesFile.exists()) { } android { - signingConfigs { - create("release") { - storeFile = file(localProperties["storeFile"] as String) - storePassword = localProperties["storePassword"] as String - keyPassword = localProperties["keyPassword"] as String - keyAlias = localProperties["keyAlias"] as String - } - } +// TODO: uncomment when building for release +// signingConfigs { +// create("release") { +// storeFile = file(localProperties["storeFile"] as String) +// storePassword = localProperties["storePassword"] as String +// keyPassword = localProperties["keyPassword"] as String +// keyAlias = localProperties["keyAlias"] as String +// } +// } lint { disable += "NullSafeMutableLiveData" } @@ -42,6 +43,9 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + // Resolve NimbleNet external flavor for missing dimension + missingDimensionStrategy("default", "external") } buildTypes { @@ -51,7 +55,8 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - signingConfig = signingConfigs.getByName("release") +// TODO: uncomment when building for release +// signingConfig = signingConfigs.getByName("release") } } compileOptions { diff --git a/agents/examples/android/app/src/main/AndroidManifest.xml b/agents/examples/android/app/src/main/AndroidManifest.xml index c3984a41..1d3093c7 100644 --- a/agents/examples/android/app/src/main/AndroidManifest.xml +++ b/agents/examples/android/app/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ android:supportsRtl="true" android:theme="@style/Theme.Examples" tools:targetApi="31"> - + + + + + + + - Log.i(HOST_TAG, "initializeAgent: $notificationSummary") + Log.i("AGENTS-EXAMPLE", "initializeAgent: $notificationSummary") } ) - copyEspeakDataIfNeeded(application, "espeak-ng-data") + val assets: List> = listOf( + mapOf( + "name" to "llama-3", + "version" to "1.0.0", + "type" to "llm", + "location" to mapOf( + "path" to "llama-3" + ) + ), + mapOf( + "name" to "script", + "version" to "1.0.0", + "type" to "script", + "location" to mapOf( + "path" to "main.ast" + ) + ) + ) - val nimbleConfig = NimbleNetConfig( - clientId = "YOUR_CLIENT_ID", - host = "YOUR_HOST", - deviceId = "test-device", - clientSecret = "YOUR_CLIENT_SECRET", + val nnConfig = NimbleNetConfig( debug = true, - compatibilityTag = "agent_notification_summarizer", - libraryVariant = NIMBLENET_VARIANTS.STATIC + online = false ) - val res = NimbleNet.initialize(application, nimbleConfig) + val res = NimbleNet.initialize(application, nnConfig, JSONArray(assets)) check(res.status) while (!NimbleNet.isReady().status) delay(1000) diff --git a/agents/examples/android/app/src/main/java/dev/deliteai/examples/gmail_assistant/GmailActivity.kt b/agents/examples/android/app/src/main/java/dev/deliteai/examples/GmailActivity.kt similarity index 98% rename from agents/examples/android/app/src/main/java/dev/deliteai/examples/gmail_assistant/GmailActivity.kt rename to agents/examples/android/app/src/main/java/dev/deliteai/examples/GmailActivity.kt index 58064370..469b9c6d 100644 --- a/agents/examples/android/app/src/main/java/dev/deliteai/examples/gmail_assistant/GmailActivity.kt +++ b/agents/examples/android/app/src/main/java/dev/deliteai/examples/GmailActivity.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package dev.deliteai.examples.gmail_assistant +package dev.deliteai.examples import android.accounts.AccountManager import android.app.Application @@ -52,9 +52,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -const val HOST_TAG = "GMAIL_AGENT" - class GmailActivity : ComponentActivity() { + private val HOST_TAG = "GMAIL_AGENT" private val RC_SIGN_IN = 1001 private val REQUEST_AUTHORIZATION = 1002 diff --git a/agents/examples/android/app/src/main/java/dev/deliteai/examples/MainActivity.kt b/agents/examples/android/app/src/main/java/dev/deliteai/examples/MainActivity.kt index 14612119..8d43c64f 100644 --- a/agents/examples/android/app/src/main/java/dev/deliteai/examples/MainActivity.kt +++ b/agents/examples/android/app/src/main/java/dev/deliteai/examples/MainActivity.kt @@ -6,63 +6,26 @@ package dev.deliteai.examples -import dev.deliteai.examples.ui.theme.ExamplesTheme -import dev.deliteai.notifications_summarizer.NotificationsSummarizerAgent -import android.Manifest -import android.app.AlarmManager -import android.app.Application -import android.content.Context import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri -import android.os.Build import android.os.Bundle -import android.provider.Settings import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.core.content.ContextCompat -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.io.File -import java.io.FileOutputStream -import java.time.LocalDate - -val HOST_TAG = "NE-HOST" +import dev.deliteai.examples.ui.theme.ExamplesTheme +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.platform.LocalContext class MainActivity : ComponentActivity() { - private val notificationPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission() - ) { isGranted -> - // Handle the permission result if needed - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -72,285 +35,31 @@ class MainActivity : ComponentActivity() { } } } - - private fun requestNotificationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ContextCompat.checkSelfPermission( - this, - Manifest.permission.POST_NOTIFICATIONS - ) != PackageManager.PERMISSION_GRANTED - ) { - notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) - } - } - } - - private fun requestExactAlarmPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager - if (!alarmManager.canScheduleExactAlarms()) { - val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply { - data = Uri.parse("package:$packageName") - } - startActivity(intent) - } - } - } - - private fun openNotificationListenerSettings() { - val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) - startActivity(intent) - } - - @Composable - fun MainScreen() { - val context = LocalContext.current - val activity = context as ComponentActivity - val application = context.applicationContext as Application - - var resultText by remember { mutableStateOf("") } - val coroutineScope = rememberCoroutineScope() - - Column( - modifier = Modifier - .fillMaxSize() - .padding(vertical = 40.dp, horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Card( - modifier = Modifier - .fillMaxWidth() - .height(360.dp), - elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) - ) { - Text( - text = if (resultText.isEmpty()) "Output will appear here..." else resultText, - modifier = Modifier - .fillMaxSize() - .padding(12.dp) - .verticalScroll(rememberScrollState()), - fontSize = 14.sp, - textAlign = TextAlign.Start - ) - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Button( - onClick = { - requestNotificationPermission() - resultText = "Requested notification permission" - }, - modifier = Modifier - .weight(1f) - .height(36.dp) - ) { - Text(text = "Notification", fontSize = 10.sp) - } - - Button( - onClick = { - requestExactAlarmPermission() - resultText = "Requested exact alarm permission" - }, - modifier = Modifier - .weight(1f) - .height(36.dp) - ) { - Text(text = "Alarm", fontSize = 10.sp) - } - - Button( - onClick = { - openNotificationListenerSettings() - resultText = "Opened notification listener settings" - }, - modifier = Modifier - .weight(1f) - .height(36.dp) - ) { - Text(text = "Listener", fontSize = 10.sp) - } - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Button( - onClick = { - coroutineScope.launch { - resultText = scheduleNotificationJob() - } - }, - modifier = Modifier - .weight(1f) - .height(40.dp) - ) { - Text(text = "Schedule", fontSize = 11.sp) - } - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Button( - onClick = { - coroutineScope.launch { - resultText = getCurrentNotificationSummary() - } - }, - modifier = Modifier - .weight(1f) - .height(40.dp) - ) { - Text(text = "Summarize Current", fontSize = 11.sp) - } - - Button( - onClick = { - coroutineScope.launch { - resultText = getSummaryById("test-id") - } - }, - modifier = Modifier - .weight(1f) - .height(40.dp) - ) { - Text(text = "By ID", fontSize = 11.sp) - } - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Button( - onClick = { - coroutineScope.launch { - resultText = getSummariesForToday() - } - }, - modifier = Modifier - .weight(1f) - .height(40.dp) - ) { - Text(text = "Today", fontSize = 11.sp) - } - - Button( - onClick = { - coroutineScope.launch { - resultText = getSummariesForLast7Days() - } - }, - modifier = Modifier - .weight(1f) - .height(40.dp) - ) { - Text(text = "Last 7 Days", fontSize = 11.sp) - } - } - } - } -} - -//will schedule a job that will go off in the next 10 seconds for demo -private suspend fun scheduleNotificationJob(): String { - return runCatching { - val timeInMillis = System.currentTimeMillis() + 10_000L - NotificationsSummarizerAgent.scheduleNotificationSummaryJob(timeInMillis) - "scheduleNotificationSummaryJob returned" - }.getOrElse { - "scheduleNotificationSummaryJob failed: ${it.message}" - } } -private suspend fun getCurrentNotificationSummary(): String { - return runCatching { - val summary = NotificationsSummarizerAgent.getSummaryOfCurrentNotification() - summary.toString() - }.getOrElse { - "getSummaryOfCurrentNotification failed: ${it.message}" - } -} - -private suspend fun getSummaryById(id: String): String { - return runCatching { - val summary = NotificationsSummarizerAgent.getSummary(id) - summary.toString() - }.getOrElse { - "getSummary(id) failed: ${it.message}" - } -} - -private suspend fun getSummariesForToday(): String { - return runCatching { - val today = LocalDate.now() - val list = NotificationsSummarizerAgent.getSummary(today).payload!! - list.joinToString("\n") - }.getOrElse { - "getSummary(date) failed: ${it.message}" - } -} - -private suspend fun getSummariesForLast7Days(): String { - return runCatching { - val endDate = LocalDate.now() - val startDate = endDate.minusDays(7) - val list = NotificationsSummarizerAgent.getSummary(startDate, endDate).payload!! - list.joinToString("\n") - }.getOrElse { - "getSummaries(range) failed: ${it.message}" - } -} - -suspend fun copyEspeakDataIfNeeded(context: Context, assetPath: String) { - val prefs = context.getSharedPreferences(assetPath, Context.MODE_PRIVATE) - val alreadyCopied = prefs.getBoolean(assetPath, false) - - if (alreadyCopied) return - - withContext(Dispatchers.IO) { - try { - val assetFolder = "espeak-ng-data" - val outputFolder = File(context.filesDir, "nimbleSDK") - - copyAssetFolder(context, assetFolder, outputFolder) - - prefs.edit().putBoolean(assetPath, true).apply() - } catch (e: Exception) { - e.printStackTrace() +@Composable +private fun MainScreen() { + val context = LocalContext.current + + Column( + modifier = Modifier + .fillMaxSize() + .padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically) + ) { + Button(onClick = { + val intent = Intent(context, NotificationSummarizerActivity::class.java) + context.startActivity(intent) + }) { + Text(text = "Notification Summarizer", fontSize = 16.sp) } - } -} -private fun copyAssetFolder(context: Context, assetPath: String, outDir: File) { - val assetManager = context.assets - val assets = assetManager.list(assetPath) ?: return - - if (!outDir.exists()) { - outDir.mkdirs() - } - - for (asset in assets) { - val subPath = "$assetPath/$asset" - val outFile = File(outDir, asset) - - val subAssets = assetManager.list(subPath) - if (subAssets.isNullOrEmpty()) { - if (!outFile.exists()) { - assetManager.open(subPath).use { inputStream -> - FileOutputStream(outFile).use { outputStream -> - inputStream.copyTo(outputStream) - } - } - } - } else { - copyAssetFolder(context, subPath, outFile) + Button(onClick = { + val intent = Intent(context, GmailActivity::class.java) + context.startActivity(intent) + }) { + Text(text = "Gmail Assistant", fontSize = 16.sp) } } } diff --git a/agents/examples/android/app/src/main/java/dev/deliteai/examples/NotificationSummarizerActivity.kt b/agents/examples/android/app/src/main/java/dev/deliteai/examples/NotificationSummarizerActivity.kt new file mode 100644 index 00000000..40b713f1 --- /dev/null +++ b/agents/examples/android/app/src/main/java/dev/deliteai/examples/NotificationSummarizerActivity.kt @@ -0,0 +1,306 @@ +/* + * SPDX-FileCopyrightText: (C) 2025 DeliteAI Authors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package dev.deliteai.examples + +import dev.deliteai.examples.ui.theme.ExamplesTheme +import dev.deliteai.notifications_summarizer.NotificationsSummarizerAgent +import android.Manifest +import android.app.AlarmManager +import android.app.Application +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat +import kotlinx.coroutines.launch +import java.time.LocalDate + + +class NotificationSummarizerActivity : ComponentActivity() { + private val HOST_TAG = "NOTIFICATION-AGENT" + + private val notificationPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted -> + // Handle the permission result if needed + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + ExamplesTheme { + MainScreen() + } + } + } + + private fun requestNotificationPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + } + } + } + + private fun requestExactAlarmPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager + if (!alarmManager.canScheduleExactAlarms()) { + val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply { + data = Uri.parse("package:$packageName") + } + startActivity(intent) + } + } + } + + private fun openNotificationListenerSettings() { + val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) + startActivity(intent) + } + + @Composable + fun MainScreen() { + val context = LocalContext.current + val activity = context as ComponentActivity + val application = context.applicationContext as Application + + var resultText by remember { mutableStateOf("") } + val coroutineScope = rememberCoroutineScope() + + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 40.dp, horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(360.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) + ) { + Text( + text = if (resultText.isEmpty()) "Output will appear here..." else resultText, + modifier = Modifier + .fillMaxSize() + .padding(12.dp) + .verticalScroll(rememberScrollState()), + fontSize = 14.sp, + textAlign = TextAlign.Start + ) + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Button( + onClick = { + requestNotificationPermission() + resultText = "Requested notification permission" + }, + modifier = Modifier + .weight(1f) + .height(36.dp) + ) { + Text(text = "Notification", fontSize = 10.sp) + } + + Button( + onClick = { + requestExactAlarmPermission() + resultText = "Requested exact alarm permission" + }, + modifier = Modifier + .weight(1f) + .height(36.dp) + ) { + Text(text = "Alarm", fontSize = 10.sp) + } + + Button( + onClick = { + openNotificationListenerSettings() + resultText = "Opened notification listener settings" + }, + modifier = Modifier + .weight(1f) + .height(36.dp) + ) { + Text(text = "Listener", fontSize = 10.sp) + } + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Button( + onClick = { + coroutineScope.launch { + resultText = scheduleNotificationJob() + } + }, + modifier = Modifier + .weight(1f) + .height(40.dp) + ) { + Text(text = "Schedule", fontSize = 11.sp) + } + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Button( + onClick = { + coroutineScope.launch { + resultText = getCurrentNotificationSummary() + } + }, + modifier = Modifier + .weight(1f) + .height(40.dp) + ) { + Text(text = "Summarize Current", fontSize = 11.sp) + } + + Button( + onClick = { + coroutineScope.launch { + resultText = getSummaryById("test-id") + } + }, + modifier = Modifier + .weight(1f) + .height(40.dp) + ) { + Text(text = "By ID", fontSize = 11.sp) + } + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Button( + onClick = { + coroutineScope.launch { + resultText = getSummariesForToday() + } + }, + modifier = Modifier + .weight(1f) + .height(40.dp) + ) { + Text(text = "Today", fontSize = 11.sp) + } + + Button( + onClick = { + coroutineScope.launch { + resultText = getSummariesForLast7Days() + } + }, + modifier = Modifier + .weight(1f) + .height(40.dp) + ) { + Text(text = "Last 7 Days", fontSize = 11.sp) + } + } + } + } +} + +//will schedule a job that will go off in the next 10 seconds for demo +private suspend fun scheduleNotificationJob(): String { + return runCatching { + val timeInMillis = System.currentTimeMillis() + 10_000L + NotificationsSummarizerAgent.scheduleNotificationSummaryJob(timeInMillis) + "scheduleNotificationSummaryJob returned" + }.getOrElse { + "scheduleNotificationSummaryJob failed: ${it.message}" + } +} + +private suspend fun getCurrentNotificationSummary(): String { + return runCatching { + val summary = NotificationsSummarizerAgent.getSummaryOfCurrentNotification() + summary.toString() + }.getOrElse { + "getSummaryOfCurrentNotification failed: ${it.message}" + } +} + +private suspend fun getSummaryById(id: String): String { + return runCatching { + val summary = NotificationsSummarizerAgent.getSummary(id) + summary.toString() + }.getOrElse { + "getSummary(id) failed: ${it.message}" + } +} + +private suspend fun getSummariesForToday(): String { + return runCatching { + val today = LocalDate.now() + val list = NotificationsSummarizerAgent.getSummary(today).payload!! + list.joinToString("\n") + }.getOrElse { + "getSummary(date) failed: ${it.message}" + } +} + +private suspend fun getSummariesForLast7Days(): String { + return runCatching { + val endDate = LocalDate.now() + val startDate = endDate.minusDays(7) + val list = NotificationsSummarizerAgent.getSummary(startDate, endDate).payload!! + list.joinToString("\n") + }.getOrElse { + "getSummaries(range) failed: ${it.message}" + } +} diff --git a/agents/examples/android/gradle.properties b/agents/examples/android/gradle.properties index 132244e5..29bd29da 100644 --- a/agents/examples/android/gradle.properties +++ b/agents/examples/android/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. For more details, visit # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects diff --git a/agents/examples/android/settings.gradle.kts b/agents/examples/android/settings.gradle.kts index 6871297c..d5b96c5b 100644 --- a/agents/examples/android/settings.gradle.kts +++ b/agents/examples/android/settings.gradle.kts @@ -22,6 +22,15 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + mavenLocal() + } +} + +// Include the SDK as a composite build +includeBuild("../../../sdks/android") { + dependencySubstitution { + substitute(module("dev.deliteai:nimblenet_ktx")).using(project(":nimblenet_ktx")) + substitute(module("dev.deliteai:nimblenet_core")).using(project(":nimblenet_core")) } } diff --git a/agents/gmail_assistant/android/build.gradle.kts b/agents/gmail_assistant/android/build.gradle.kts index 077de29f..a4e816ec 100644 --- a/agents/gmail_assistant/android/build.gradle.kts +++ b/agents/gmail_assistant/android/build.gradle.kts @@ -19,6 +19,9 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") + + // Choose the external flavor of NimbleNet SDK when resolving variant ambiguity + missingDimensionStrategy("default", "external") } buildTypes { @@ -28,6 +31,10 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) + matchingFallbacks += "externalRelease" + } + debug { + matchingFallbacks += "externalRelease" } } compileOptions { @@ -50,12 +57,12 @@ dependencies { implementation("androidx.room:room-ktx:$roomVersion") kapt("androidx.room:room-compiler:$roomVersion") - api("dev.deliteai:nimblenet_ktx:5.0.0-dev") - api("dev.deliteai:nimblenet_core:5.0.0-dev") + api("dev.deliteai:nimblenet_ktx:+") + api("dev.deliteai:nimblenet_core:+") implementation("com.google.android.gms:play-services-auth:20.7.0") - implementation("com.google.api-client:google-api-client-android:1.34.0") - implementation("com.google.apis:google-api-services-gmail:v1-rev110-1.25.0") + api("com.google.api-client:google-api-client-android:1.34.0") + api("com.google.apis:google-api-services-gmail:v1-rev110-1.25.0") implementation("com.google.code.gson:gson:2.10.1") testImplementation(libs.junit) diff --git a/agents/notifications_summarizer/android/build.gradle.kts b/agents/notifications_summarizer/android/build.gradle.kts index 320ea347..0309cce2 100644 --- a/agents/notifications_summarizer/android/build.gradle.kts +++ b/agents/notifications_summarizer/android/build.gradle.kts @@ -19,6 +19,9 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") + + // Choose external flavor of NimbleNet when variant ambiguity arises + missingDimensionStrategy("default", "external") } buildTypes { @@ -28,6 +31,10 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) + matchingFallbacks += "externalRelease" + } + debug { + matchingFallbacks += "externalRelease" } } compileOptions { @@ -41,7 +48,7 @@ android { dependencies { val roomVersion = "2.6.1" - + implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) @@ -50,8 +57,8 @@ dependencies { implementation("androidx.room:room-ktx:$roomVersion") kapt("androidx.room:room-compiler:$roomVersion") - api("dev.deliteai:nimblenet_ktx:5.0.0-dev") - api("dev.deliteai:nimblenet_core:5.0.0-dev") + api("dev.deliteai:nimblenet_ktx:+") + api("dev.deliteai:nimblenet_core:+") testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/agents/notifications_summarizer/delitepyAssets/main.py b/agents/notifications_summarizer/delitepyAssets/main.py index 407a509c..3120e82a 100644 --- a/agents/notifications_summarizer/delitepyAssets/main.py +++ b/agents/notifications_summarizer/delitepyAssets/main.py @@ -5,7 +5,7 @@ num_cores = int(hardware_info["numCores"]) nm.set_xnnpack_num_threads(int(num_cores / 2 + 1)) -llm = nm.llm("llama-3") +llm = nm.llm({"name": "llama-3"}) # System / user prompts SYSTEM_PROMPT_BEGIN = "<|start_header_id|>system<|end_header_id|>" @@ -159,3 +159,4 @@ def summarize_notification(inp): except Exception as e: print("Error processing notifications:", e) return {"output": "Error: " + str(e)} + diff --git a/sdks/android/gradle.properties b/sdks/android/gradle.properties index b06a581b..6a7686b4 100644 --- a/sdks/android/gradle.properties +++ b/sdks/android/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/sdks/android/nimblenet_core/build.gradle.kts b/sdks/android/nimblenet_core/build.gradle.kts index 54644741..d9bf28b9 100644 --- a/sdks/android/nimblenet_core/build.gradle.kts +++ b/sdks/android/nimblenet_core/build.gradle.kts @@ -16,6 +16,9 @@ plugins { apply(plugin = Deps.Plugins.KOTLIN_ANDROID) +// Set the Maven group ID so that composite build substitution matches coordinates +group = "dev.deliteai" + android { namespace = "dev.deliteai.nimblenet_core" compileSdk = neGradleConfig.compileSdk @@ -78,7 +81,6 @@ android { // Apply publishing configuration using the Publishing class Publishing(project, neGradleConfig, "nimblenet_core").apply() - } tasks.withType { diff --git a/sdks/android/nimblenet_ktx/build.gradle.kts b/sdks/android/nimblenet_ktx/build.gradle.kts index a5c8e345..94063c6b 100644 --- a/sdks/android/nimblenet_ktx/build.gradle.kts +++ b/sdks/android/nimblenet_ktx/build.gradle.kts @@ -1,11 +1,11 @@ -import org.jetbrains.dokka.gradle.DokkaTask - /* * SPDX-FileCopyrightText: (C) 2025 DeliteAI Authors * * SPDX-License-Identifier: Apache-2.0 */ +import org.jetbrains.dokka.gradle.DokkaTask + val neGradleConfig = NEGradleConfig(project.extra) val localProperties = fetchLocalProperties(rootDir) @@ -23,6 +23,9 @@ jacoco { toolVersion = Versions.JACOCO } apply(plugin = Deps.Plugins.KOTLIN_ANDROID) +// Set the Maven group ID so that composite build substitution matches coordinates +group = "dev.deliteai" + android { namespace = "dev.deliteai.nimblenet_ktx" compileSdk = neGradleConfig.compileSdk