This guide covers how to monitor, log, and extend the MobileGraph ADK.
What: A "hook" system that intercepts requests and responses to add behaviors like logging, retries, or security.
Why: Decouples "business logic" (AI) from "infrastructure logic" (logging). It allows you to add features like automatic retries to any model without modifying its source code.
How:
class SecurityMiddleware : ChatModelMiddleware {
override suspend fun intercept(input: ChatModelInput, context: ExecutionContext, next: ...) : ModelOutput {
val redacted = redactPII(input.prompt)
return next(input.copy(prompt = redacted), context)
}
}What: A real-time stream of events representing the internal lifecycle of AI operations (requests starting, completing, or failing).
Why: To build real-time UI indicators (loading states) and track metrics without manual logging at every call site.
How:
viewModelScope.launch {
session.events.collect { event ->
when (event) {
is MobileGraphEvent.RequestStarted -> println("AI is thinking...")
is MobileGraphEvent.RequestCompleted -> println("AI finished!")
is MobileGraphEvent.RequestFailed -> println("AI error: ${event.errorMessage}")
}
}
}What: Integration of platform-specific logging into the ADK via the MobileGraphLogger interface.
Why: Integrate with native logging systems (like Android's Logcat) for better filtering and debugging.
How:
class ApplicationLogger : MobileGraphLogger {
override fun log(message: String, severity: MobileGraphLogger.Severity) {
when(severity) {
MobileGraphLogger.Severity.ERROR -> Log.e("MobileGraph", message)
// ... handle other severities
}
}
}
// Register via LoggingMiddleware in Initialization
withModels {
chat("gpt-4o", model) {
middleware {
+LoggingMiddleware(logger = ApplicationLogger())
}
}
}AI history can consume significant RAM. Always close sessions when they are no longer needed.
class ChatViewModel(val session: MobileGraphSession) : ViewModel() {
override fun onCleared() {
session.close() // Triggers internal cleanup
}
}Always use the RetryMiddleware with exponential backoff to handle "dead zones" without draining the battery.
middleware {
+RetryMiddleware(maxRetries = 3)
}