Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fd85cd7
feat(benchmark): add --retrieve-k override for per-k latency sweep
nmrenyi May 14, 2026
8848bee
feat(benchmark): capture retrieved chunks + response text per run
nmrenyi May 14, 2026
33604df
fix(benchmark): update model-file check to match production stack
nmrenyi May 14, 2026
197a7bc
feat(benchmark): add --rag-only flag to skip No-RAG mode
nmrenyi May 14, 2026
795ac84
fix(benchmark): use suspending delay() instead of Thread.sleep()
nmrenyi May 14, 2026
12fd358
fix(benchmark): keep CPU alive through screen-off (PARTIAL_WAKE_LOCK …
nmrenyi May 14, 2026
7ac3b36
refactor(benchmark): move benchmark to a foreground service
nmrenyi May 14, 2026
ef96538
fix(benchmark): record actual backend (GPU/CPU) in config metadata
nmrenyi May 14, 2026
ede273f
analysis: k-sweep latency report (GPU + CPU on Snapdragon 8 Elite)
nmrenyi May 14, 2026
4daf626
analysis: add CPU k=20 — confirms 4096-token wall is backend-invariant
nmrenyi May 14, 2026
2a592d2
review: address Copilot feedback on PR #57
nmrenyi May 14, 2026
659d3f0
review: explicit None checks in aggregate report formatting
nmrenyi May 15, 2026
497d2fc
review: replace THRESHOLD_TS heuristic with explicit allowlist
nmrenyi May 15, 2026
f372f88
review: document :benchmark process model in service KDoc
nmrenyi May 15, 2026
e205fdf
review: document retrieve_k=-1 sentinel and other intent-extra defaults
nmrenyi May 15, 2026
574601c
review: mark BenchmarkForegroundService dataSync type as dev-only
nmrenyi May 15, 2026
d3d475c
review: guard aggregate_overall subscripts against empty dicts
nmrenyi May 15, 2026
e29443e
review: fix wrong survivor count in k=20 narrative (8 → 10)
nmrenyi May 15, 2026
57722db
review: reject --retrieve-k 0 (footgun)
nmrenyi May 15, 2026
42326ee
review: acquire wake lock in onStartCommand after startForeground
nmrenyi May 15, 2026
25a1a42
review: executor.shutdownNow() + brief await to avoid race in onDestroy
nmrenyi May 15, 2026
2b5cb9c
review: warn loudly when JSON has no recorded backend
nmrenyi May 15, 2026
291725c
review: warn in logcat when both skip_retrieval and rag_only are set
nmrenyi May 15, 2026
f77effc
review: bump PARTIAL_WAKE_LOCK failsafe from 6h to 24h
nmrenyi May 15, 2026
882e738
review: guard against double-start in onStartCommand
nmrenyi May 15, 2026
7c2360f
review: use BENCH_TAG for query-failure log
nmrenyi May 15, 2026
8cb9712
review: log skipped JSON files in aggregate_k_sweep
nmrenyi May 15, 2026
f971145
review: derive Methodology text from sample run's config, not hardcoded
nmrenyi May 15, 2026
5fa5c6e
review: use statistics.quantiles for p95 instead of int(n*0.95) index
nmrenyi May 15, 2026
9ecc54e
review: use STOP_FOREGROUND_REMOVE overload on API 24+
nmrenyi May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Required on Android 14+ for network-data foreground services -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<!-- BenchmarkForegroundService acquires a PARTIAL_WAKE_LOCK so the CPU
keeps running when the screen is off or locked. Without this,
multi-hour benchmarks stall silently when the device idles.
(BenchmarkActivity is now a thin launcher that just starts the
service; the wake lock lives in the service.) -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>

<application
android:label="MAM-AI"
Expand Down Expand Up @@ -45,6 +51,26 @@
android:foregroundServiceType="dataSync"
android:exported="false" />

<!-- Foreground service for the on-device latency benchmark. Holds a
PARTIAL_WAKE_LOCK + sticky notification so the work survives
screen-off and device-lock through hours-long k-sweeps. Runs
in its own :benchmark process to keep the main app isolated.
DEV-ONLY: foregroundServiceType="dataSync" is technically a
misuse here (no actual data sync) — Google Play would reject
this declaration. Acceptable because BenchmarkForegroundService
is launched only via `adb shell am start` for in-house
benchmarking; it never appears in any user-facing flow and
this manifest entry should be stripped from any Play Store
build. If we ever need to ship benchmark capabilities, switch
to foregroundServiceType="specialUse" and add the corresponding
android.permission.FOREGROUND_SERVICE_SPECIAL_USE permission
plus the PROPERTY_SPECIAL_USE_FGS_SUBTYPE property. -->
<service
android:name=".BenchmarkForegroundService"
android:foregroundServiceType="dataSync"
android:exported="false"
android:process=":benchmark" />
Comment thread
nmrenyi marked this conversation as resolved.
Comment thread
nmrenyi marked this conversation as resolved.
Comment thread
nmrenyi marked this conversation as resolved.
Comment thread
nmrenyi marked this conversation as resolved.

<!-- FileProvider for sharing PDF files from getExternalFilesDir with viewer apps -->
<provider
android:name="androidx.core.content.FileProvider"
Expand Down
396 changes: 38 additions & 358 deletions app/android/app/src/main/kotlin/com/example/app/BenchmarkActivity.kt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions app/android/app/src/main/kotlin/com/example/app/RagPipeline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,20 @@ class RagPipeline(application: Application) {
}
}

/** Generates the response from the LLM with conversation history support. */
/** Generates the response from the LLM with conversation history support.
*
* [retrieveKOverride] — when non-null, replaces `retrievalConfig.top_k`
* for this call only. Used by [BenchmarkActivity] for the per-k latency
* sweep; production callers leave it null and inherit the runtime config.
*/
suspend fun generateResponse(
prompt: String,
history: List<Map<String, String>>,
useRetrieval: Boolean = true,
language: String = "en",
retrievalListener: (docs: List<RetrievedDoc>) -> Unit,
generationListener: (partial: String, done: Boolean) -> Unit,
retrieveKOverride: Int? = null,
): String =
coroutineScope {
awaitLlmReady()
Expand All @@ -235,10 +241,11 @@ class RagPipeline(application: Application) {
val qStart = System.currentTimeMillis()

val docs = if (useRetrieval) {
val effectiveTopK = retrieveKOverride ?: retrievalConfig.getInt("top_k")
val retrievalRequest = RetrievalRequest.create(
prompt,
RetrievalConfig.create(
retrievalConfig.getInt("top_k"),
effectiveTopK,
retrievalConfig.getDouble("similarity_threshold").toFloat(),
TaskType.RETRIEVAL_QUERY,
),
Expand Down
Loading
Loading