Repository navigation
Feature/gemini live - #61
juliantoledo wants to merge 6 commits into
Conversation
|
Thanks for putting this together! Before diving into smaller implementation details, I'd like to align on the architecture around Since a live voice session is a stateful bidirectional stream rather than a DB-driven request/response turn loop, having a dedicated
|
|
Another question is around how the UI should work Right now, all the UI state and lifecycle for the Live session are wired directly into Instead of embedding the Live session state inside
|
… in VoiceInteractionService - Extract GetEnabledToolsUseCase and ExecuteToolCallUseCase in domain/appfunction/ so AgentOrchestrator and LiveAgentOrchestrator share a single tool discovery and execution pipeline - Replace GeminiLiveSessionManager with provider-agnostic LiveAgentOrchestrator in domain/ and move Gemini BidiGenerateContent WebSocket/JSON protocol details into GeminiLiveProviderImpl in data/ - Move Gemini Live UI state and session lifecycle out of AgentDemoScreen and AgentDemoViewModel into AgentVoiceInteractionService, AgentVoiceInteractionSession, and LiveVoiceSessionOverlay (with Digital Assistant selection via ACTION_VOICE_INPUT_SETTINGS) - Stabilize GeminiLiveAudioHelper full-duplex audio using VoIP playback/capture with AEC and NoiseSuppressor in MODE_IN_COMMUNICATION, software mic gating during tool calls, and serialized off-main-thread teardown
9a84fec to
69eef68
Compare
|
Thank you @mingweiliao, great feedback. I worked on those comments in the last commit. |
|
Taking a step back, this PR has grown to ~5,200 lines across 34 files, and because design changes in one layer ripple across the others, it will become quite messy if we iterate it as it is. Could we break this down into a series of smaller PRs so we can review and merge each piece in a logical order?
The tool refactoring in step 1 looks nearly ready as-is, so we should be able to merge that quickly and then focus on one layer at a time. |
mingweiliao
left a comment
There was a problem hiding this comment.
This is the review for step 1
| * Use case to discover all currently enabled AppFunction and internal agent tools, filtered by | ||
| * connected apps and optional [targetPackageName] scoping. | ||
| */ | ||
| class GetEnabledToolsUseCase |
There was a problem hiding this comment.
Instead of introducing GetEnabledToolsUseCase as a separate class, could we evolve GetAppFunctionsUseCase to accept filter parameters?
// Flatten them here, but it should be a param class
operator fun invoke(
// Some of the filters are nullable, null meaning don't apply the filter at all
targetPackageNames: Set<String>? = null,
isEnabled: Boolean? = null,
isConnected: Boolean? = null,
includeInternalTools: Boolean = true,
): Flow<Map<AppFunctionPackageMetadata, List<AppFunctionMetadata>>>So that there is a single use case to observe AppFunctions and the filter parameter allow them to read what they need.
| * discovered Android AppFunctions, handling remote file reference downloads, recursive content URI | ||
| * permission grants, argument conversion, and [AppFunctionExceptionFormatter] formatting. | ||
| */ | ||
| class ExecuteToolCallUseCase |
There was a problem hiding this comment.
We should evolve ExecuteAppFunctionUseCase instead of introducing ExecuteToolCallUseCase. That ensures any caller invoking an AppFunction (including DebuggingViewModel) goes through the same execution path, especially for common steps like granting URI permissions and dispatching internal tools.
ExecuteAppFunctionUseCase should still take (function: AppFunctionMetadata, parameters: AppFunctionData) rather than LlmResponsePart.ToolCall. Its only responsibility should be taking AppFunctionData, running common pre-execution processing (like URI permission grants), and returning ExecuteAppFunctionResult. Converting ToolCall arguments into AppFunctionData (such as resolving remote file references via ConvertInputToAppFunctionDataUseCase) and handling caller-specific post-processing (like mapping to ToolOutput or extracting MessageAttachment) should stay in the orchestrators or a dedicated conversion use case if needs to be shared.
…witching Update GeminiLiveProviderImpl.getSystemInstruction() so Gemini Live defaults to English for understanding and replying without locking out language switching when the user clearly speaks in or asks to switch to another language.
…or in Settings Add GeminiLiveVoice with all 30 prebuilt Gemini Live studio voices (defaulting to Zephyr), persist the selection in DataStoreSettingsRepository, expose a Gemini Live voice dropdown in SettingsScreen, and read the selected voice when initializing GeminiLiveProviderImpl sessions.
|
thank you, I will break it into smaller PRs shortly. |
…on overlay tap - Launch PendingIntent AppFunction results immediately via LaunchPendingIntentUseCase in LiveAgentOrchestrator instead of requiring a chat confirmation button - Update GeminiLiveProviderImpl system instruction to execute actions directly without claiming an on-screen confirmation card or button is displayed - Make LiveVoiceSessionOverlay tappable to open the active chat thread in MainActivity (singleTop) while keeping the Live voice session running
|
Closing this umbrella PR (keeping the
|
Title
feat(agent): add Gemini Live (gemini-3.8-live) voice session support for AppFunctions
Description
Summary
Adds real-time bidirectional voice interaction to the Agent Demo screen using the Gemini Live API (
models/gemini-3.8-live), enabling users to invoke Android AppFunctions and internal agent tools by voice while viewing live transcriptions, tool execution status, generated image attachments, andPendingIntentaction confirmation cards directly in the active chat thread.Key Changes
Gemini Live WebSocket Session (
GeminiLiveSessionManager&LiveSessionState):v1betaBidiGenerateContentWebSocket endpoint using KtorWebSockets(DataModule.provideHttpClient) and the configured Gemini API key fromSettingsRepository."models/gemini-3.8-live"withAUDIOresponse modality, input/output audio transcriptions, system instructions, and all enabled AppFunction and internal tool declarations.USERandASSISTANTmessages.Low-Latency PCM Audio I/O (
GeminiLiveAudioHelper):VOICE_COMMUNICATIONwithAcousticEchoCancelerenabled when available) and plays back 24 kHz mono 16-bit PCM model audio viaAudioTrack.AppFunction & Internal Tool Bridge (
AgentOrchestrator&GeminiToolConverter):AgentOrchestrator.getEnabledToolsandAgentOrchestrator.executeLiveToolCallsto execute AppFunctions and internal tools (getCurrentLocation,geocodeAddress,generateImage) during a Live session, updatingAgentStatus.InvokingTool, posting generated image attachments andPendingIntentconfirmation cards to the thread, and returningfunctionResponsesover the WebSocket.GeminiToolConverter.convertForLiveApito normalize tool schemas (enumfield naming and OpenAPI format compatibility) forBidiGenerateContent.Agent Demo UI & Permissions (
AgentDemoScreen,AgentDemoViewModel,AgentUiState,AndroidManifest.xml):RECORD_AUDIOpermission handling and@Appscoping support.LiveSessionIndicatorstatus banner when connecting or listening, and updatedModelDropdownin the top bar to displaygemini-3.8-livewhile a Live session is active.INTERNETandRECORD_AUDIOpermissions inAndroidManifest.xml.Unit Tests:
GeminiLiveSessionManagerTest,GeminiToolConverterTest.convertForLiveApi_normalizesEnumsAndStripsFileReferenceFormat, andAgentDemoViewModelTestcoverage forOnToggleLiveSession.