Files
adk-python/tests/unittests/models/test_gemini_llm_connection.py
T
GenkiNoguchiandCopybara-Service d22920bd7f feat: support realtime input config
Merge https://github.com/google/adk-python/pull/981

issue: https://github.com/google/adk-python/issues/982

This pull request introduces a new configuration option, `realtime_input_config`, to the `RunConfig` class.

**Reason for this change:**

Currently, there is no direct way to configure real-time audio input behaviors, such as Voice Activity Detection (VAD), for live agents through the `RunConfig`. The Gemini API documentation (specifically [Configure automatic VAD](https://ai.google.dev/gemini-api/docs/live#configure-automatic-vad)) outlines parameters for VAD that users may want to customize.

This change enables users to pass these real-time input configurations, providing more granular control over the audio input for live agents.

**Changes made:**

- Added a new optional field `realtime_input_config: Optional[types.RealtimeInputConfig]` to the `RunConfig` class.
- The docstring for `realtime_input_config` has been added to explain its purpose.

**Example Usage (Conceptual):**

While the specific structure of `types.RealtimeInputConfig` would define the exact parameters, a user might configure it like this:

```python
# (Assuming types.RealtimeInputConfig and types.VadConfig are defined elsewhere)
# import your_project.types as types

run_config = RunConfig(
    # ... other configurations ...
    realtime_input_config=types.RealtimeInputConfig(
        automatic_activity_detection =types.AutomaticActivityDetection(
            # VAD specific parameters like sensitivity, endpoint_duration_millis etc.
            # based on https://ai.google.dev/gemini-api/docs/live#configure-automatic-vad
        )
        # Potentially other real-time input settings could be added here in the future
    )
)
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/981 from ammmr:patch-add-realtime-input-config b2e17fbf5742d264029ad49bf632422b5c5b1e0a
PiperOrigin-RevId: 770797640
2025-06-12 14:33:05 -07:00

112 lines
3.4 KiB
Python

# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from unittest import mock
from google.adk.models.gemini_llm_connection import GeminiLlmConnection
from google.genai import types
import pytest
@pytest.fixture
def mock_gemini_session():
"""Mock Gemini session for testing."""
return mock.AsyncMock()
@pytest.fixture
def gemini_connection(mock_gemini_session):
"""GeminiLlmConnection instance with mocked session."""
return GeminiLlmConnection(mock_gemini_session)
@pytest.fixture
def test_blob():
"""Test blob for audio data."""
return types.Blob(data=b'\x00\xFF\x00\xFF', mime_type='audio/pcm')
@pytest.mark.asyncio
async def test_send_realtime_default_behavior(
gemini_connection, mock_gemini_session, test_blob
):
"""Test send_realtime with default automatic_activity_detection value (True)."""
await gemini_connection.send_realtime(test_blob)
# Should call send once
mock_gemini_session.send.assert_called_once_with(input=test_blob.model_dump())
@pytest.mark.asyncio
async def test_send_history(gemini_connection, mock_gemini_session):
"""Test send_history method."""
history = [
types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
types.Content(
role='model', parts=[types.Part.from_text(text='Hi there!')]
),
]
await gemini_connection.send_history(history)
mock_gemini_session.send.assert_called_once()
call_args = mock_gemini_session.send.call_args[1]
assert 'input' in call_args
assert call_args['input'].turns == history
assert call_args['input'].turn_complete is False # Last message is from model
@pytest.mark.asyncio
async def test_send_content_text(gemini_connection, mock_gemini_session):
"""Test send_content with text content."""
content = types.Content(
role='user', parts=[types.Part.from_text(text='Hello')]
)
await gemini_connection.send_content(content)
mock_gemini_session.send.assert_called_once()
call_args = mock_gemini_session.send.call_args[1]
assert 'input' in call_args
assert call_args['input'].turns == [content]
assert call_args['input'].turn_complete is True
@pytest.mark.asyncio
async def test_send_content_function_response(
gemini_connection, mock_gemini_session
):
"""Test send_content with function response."""
function_response = types.FunctionResponse(
name='test_function', response={'result': 'success'}
)
content = types.Content(
role='user', parts=[types.Part(function_response=function_response)]
)
await gemini_connection.send_content(content)
mock_gemini_session.send.assert_called_once()
call_args = mock_gemini_session.send.call_args[1]
assert 'input' in call_args
assert call_args['input'].function_responses == [function_response]
@pytest.mark.asyncio
async def test_close(gemini_connection, mock_gemini_session):
"""Test close method."""
await gemini_connection.close()
mock_gemini_session.close.assert_called_once()