Skip to content

Commit 09bc207

Browse files
Add deep Mistral request logging and tighten coordinator wait
1 parent e5d16ab commit 09bc207

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

app/src/main/kotlin/com/google/ai/sample/feature/multimodal/PhotoReasoningViewModel.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,10 @@ class PhotoReasoningViewModel(
10341034
screenInfoForPrompt: String? = null,
10351035
imageUrisForChat: List<String>? = null
10361036
) {
1037+
Log.d(
1038+
TAG,
1039+
"reasonWithMistral: start, images=${selectedImages.size}, screenInfo=${!screenInfoForPrompt.isNullOrBlank()}, chatSize=${_chatState.getAllMessages().size}"
1040+
)
10371041
_uiState.value = PhotoReasoningUiState.Loading
10381042
_showStopNotificationFlow.value = true
10391043
val context = appContext
@@ -1062,6 +1066,7 @@ class PhotoReasoningViewModel(
10621066
currentReasoningJob?.cancel()
10631067
currentReasoningJob = viewModelScope.launch(Dispatchers.IO) {
10641068
try {
1069+
Log.d(TAG, "reasonWithMistral: launched IO job")
10651070
val currentModel = com.google.ai.sample.GenerativeAiViewModelFactory.getCurrentModel()
10661071
val genSettings = com.google.ai.sample.util.GenerationSettingsPreferences.loadSettings(context, currentModel.modelName)
10671072

@@ -1132,6 +1137,7 @@ class PhotoReasoningViewModel(
11321137
val availableKeys = apiKeyManager.getApiKeys(ApiProvider.MISTRAL)
11331138
.filter { it.isNotBlank() }
11341139
.distinct()
1140+
Log.d(TAG, "reasonWithMistral: availableKeys=${availableKeys.size}")
11351141
if (availableKeys.isEmpty()) {
11361142
throw IOException("Mistral API key not found.")
11371143
}
@@ -1149,6 +1155,7 @@ class PhotoReasoningViewModel(
11491155
client.newCall(buildRequest(selectedKey)).execute()
11501156
}
11511157
val finalResponse = coordinated.response
1158+
Log.d(TAG, "reasonWithMistral: coordinated response code=${finalResponse.code}")
11521159

11531160
if (!finalResponse.isSuccessful) {
11541161
val errBody = finalResponse.body?.string()
@@ -1163,6 +1170,7 @@ class PhotoReasoningViewModel(
11631170
processCommandsIncrementally(accText)
11641171
}
11651172
}
1173+
Log.d(TAG, "reasonWithMistral: stream parse finished, responseLength=${aiResponseText.length}")
11661174
finalResponse.close()
11671175

11681176
withContext(Dispatchers.Main) {
@@ -1181,6 +1189,7 @@ class PhotoReasoningViewModel(
11811189
}
11821190
} finally {
11831191
withContext(Dispatchers.Main) {
1192+
Log.d(TAG, "reasonWithMistral: finally, draining queued auto-screenshot requests")
11841193
releaseAndDrainMistralAutoScreenshotQueue()
11851194
refreshStopButtonState()
11861195
}
@@ -2332,9 +2341,10 @@ private fun processCommands(text: String) {
23322341
synchronized(mistralAutoScreenshotQueueLock) {
23332342
if (mistralAutoScreenshotInFlight) {
23342343
queuedMistralScreenshotRequest = request
2335-
Log.d(TAG, "Mistral auto screenshot request queued (latest wins).")
2344+
Log.d(TAG, "Mistral auto screenshot request queued (latest wins). uri=$screenshotUri")
23362345
} else {
23372346
mistralAutoScreenshotInFlight = true
2347+
Log.d(TAG, "Mistral auto screenshot request becomes in-flight. uri=$screenshotUri")
23382348
shouldStartNow = true
23392349
}
23402350
}
@@ -2344,6 +2354,7 @@ private fun processCommands(text: String) {
23442354
}
23452355

23462356
private fun dispatchMistralAutoScreenshotRequest(request: QueuedMistralScreenshotRequest) {
2357+
Log.d(TAG, "Dispatching Mistral auto screenshot request. uri=${request.screenshotUri}")
23472358
reason(
23482359
userInput = createGenericScreenshotPrompt(),
23492360
selectedImages = listOf(request.bitmap),
@@ -2357,9 +2368,11 @@ private fun processCommands(text: String) {
23572368
val queued = queuedMistralScreenshotRequest
23582369
if (queued == null) {
23592370
mistralAutoScreenshotInFlight = false
2371+
Log.d(TAG, "Mistral auto screenshot queue drained completely. inFlight=false")
23602372
null
23612373
} else {
23622374
queuedMistralScreenshotRequest = null
2375+
Log.d(TAG, "Mistral auto screenshot queue has pending request to drain.")
23632376
queued
23642377
}
23652378
}

app/src/main/kotlin/com/google/ai/sample/network/MistralRequestCoordinator.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.google.ai.sample.network
22

3+
import android.util.Log
34
import kotlinx.coroutines.delay
45
import kotlinx.coroutines.sync.Mutex
56
import kotlinx.coroutines.sync.withLock
67
import okhttp3.Response
8+
import java.util.concurrent.atomic.AtomicLong
79
import kotlin.math.max
810
import kotlin.math.roundToLong
911

@@ -13,10 +15,17 @@ internal data class MistralCoordinatedResponse(
1315
)
1416

1517
internal object MistralRequestCoordinator {
18+
private const val TAG = "MistralCoordinator"
1619
private const val MIN_INTERVAL_MS = 1500L
17-
private const val MAX_SERVER_DELAY_MS = 60_000L
20+
private const val MAX_SERVER_DELAY_MS = 5_000L
1821
private val cooldownMutex = Mutex()
1922
private val nextAllowedRequestAtMsByKey = mutableMapOf<String, Long>()
23+
private val requestId = AtomicLong(0L)
24+
25+
private fun keyFingerprint(key: String): String {
26+
if (key.length <= 8) return key
27+
return "${key.take(4)}${key.takeLast(4)}"
28+
}
2029

2130
private suspend fun markKeyCooldown(
2231
key: String,
@@ -71,6 +80,8 @@ internal object MistralRequestCoordinator {
7180
request: suspend (apiKey: String) -> Response
7281
): MistralCoordinatedResponse {
7382
require(apiKeys.isNotEmpty()) { "No Mistral API keys provided." }
83+
val rid = requestId.incrementAndGet()
84+
Log.d(TAG, "[$rid] execute start: keys=${apiKeys.size}, maxAttempts=$maxAttempts")
7485

7586
var consecutiveFailures = 0
7687
var blockedKeysThisRound = mutableSetOf<String>()
@@ -91,6 +102,10 @@ internal object MistralRequestCoordinator {
91102
selectedKey = candidate
92103
}
93104
}
105+
Log.d(
106+
TAG,
107+
"[$rid] attempt=${consecutiveFailures + 1}, selectedKey=${keyFingerprint(selectedKey)}, waitMs=$waitMs, blocked=${blockedKeysThisRound.size}"
108+
)
94109
if (waitMs > 0L) {
95110
delay(waitMs)
96111
}
@@ -101,26 +116,41 @@ internal object MistralRequestCoordinator {
101116
val retryAfterMs = parseRetryAfterMs(response.header("Retry-After"))
102117
val resetDelayMs = parseRateLimitResetDelayMs(response, requestEndMs)
103118
val serverRequestedDelayMs = max(retryAfterMs ?: 0L, resetDelayMs ?: 0L)
119+
Log.d(
120+
TAG,
121+
"[$rid] response code=${response.code}, retryAfterMs=${retryAfterMs ?: -1}, resetDelayMs=${resetDelayMs ?: -1}, appliedDelayMs=$serverRequestedDelayMs"
122+
)
104123
markKeyCooldown(selectedKey, requestEndMs, serverRequestedDelayMs)
105124

106125
if (response.isSuccessful || !isRetryableFailure(response.code)) {
126+
Log.d(TAG, "[$rid] returning response code=${response.code} with key=${keyFingerprint(selectedKey)}")
107127
return MistralCoordinatedResponse(response = response, apiKey = selectedKey)
108128
}
109129

110130
response.close()
111131
blockedKeysThisRound.add(selectedKey)
112132
consecutiveFailures++
113133
val adaptiveDelay = adaptiveRetryDelayMs(consecutiveFailures)
134+
Log.w(
135+
TAG,
136+
"[$rid] retryable failure code=${response.code}, consecutiveFailures=$consecutiveFailures, adaptiveDelay=$adaptiveDelay"
137+
)
114138
markKeyCooldown(selectedKey, requestEndMs, max(serverRequestedDelayMs, adaptiveDelay))
115139
} catch (e: Exception) {
116140
val requestEndMs = System.currentTimeMillis()
117141
blockedKeysThisRound.add(selectedKey)
118142
consecutiveFailures++
143+
Log.e(
144+
TAG,
145+
"[$rid] exception on key=${keyFingerprint(selectedKey)}, consecutiveFailures=$consecutiveFailures: ${e.message}",
146+
e
147+
)
119148
markKeyCooldown(selectedKey, requestEndMs, adaptiveRetryDelayMs(consecutiveFailures))
120149
if (consecutiveFailures >= maxAttempts) throw e
121150
}
122151
}
123152

153+
Log.e(TAG, "[$rid] exhausted attempts ($maxAttempts) without success")
124154
throw IllegalStateException("Mistral request failed after $maxAttempts attempts.")
125155
}
126156
}

0 commit comments

Comments
 (0)