first vommit
This commit is contained in:
commit
a04e68bc95
4926 files changed
+764440
No files matched your search
@@ -0,0 +1 @@
|
||||
"""Tests for the Google GenAI SDK's chats module."""
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,602 @@
|
||||
# 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
|
||||
|
||||
import pytest
|
||||
|
||||
from ... import chats
|
||||
from ... import client
|
||||
from ... import models
|
||||
from ... import types
|
||||
|
||||
AFC_HISTORY = [
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='afc input')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[
|
||||
types.Part(
|
||||
function_call=types.FunctionCall(
|
||||
name='foo', args={'bar': 'baz'}
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
pytest_plugins = 'pytest_asyncio'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_api_client(vertexai=False):
|
||||
api_client = mock.MagicMock(spec=client.ApiClient)
|
||||
api_client.api_key = 'TEST_API_KEY'
|
||||
api_client._host = lambda: 'test_host'
|
||||
api_client._http_options = {'headers': {}} # Ensure headers exist
|
||||
api_client.vertexai = vertexai
|
||||
return api_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_generate_content_with_empty_text_part():
|
||||
with mock.patch.object(
|
||||
models.Models, 'generate_content'
|
||||
) as mock_generate_content:
|
||||
mock_generate_content.return_value = types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[types.Part(text='')],
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
yield mock_generate_content
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_generate_content_empty_content():
|
||||
with mock.patch.object(
|
||||
models.Models, 'generate_content'
|
||||
) as mock_generate_content:
|
||||
mock_generate_content.return_value = types.GenerateContentResponse(
|
||||
candidates=[]
|
||||
)
|
||||
yield mock_generate_content
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_generate_content_stream_with_empty_text_part():
|
||||
with mock.patch.object(
|
||||
models.Models, 'generate_content_stream'
|
||||
) as mock_generate_content:
|
||||
mock_generate_content.return_value = [
|
||||
types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[types.Part(text='')],
|
||||
),
|
||||
finish_reason=types.FinishReason.STOP,
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
yield mock_generate_content
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_generate_content_stream_empty_content():
|
||||
with mock.patch.object(
|
||||
models.Models, 'generate_content_stream'
|
||||
) as mock_generate_content:
|
||||
mock_generate_content.return_value = [
|
||||
types.GenerateContentResponse(candidates=[])
|
||||
]
|
||||
yield mock_generate_content
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_generate_content_afc_history():
|
||||
with mock.patch.object(
|
||||
models.Models, 'generate_content'
|
||||
) as mock_generate_content:
|
||||
mock_generate_content.return_value = types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='afc output')],
|
||||
)
|
||||
)
|
||||
],
|
||||
automatic_function_calling_history=AFC_HISTORY,
|
||||
)
|
||||
yield mock_generate_content
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_generate_content_stream_afc_history():
|
||||
with mock.patch.object(
|
||||
models.Models, 'generate_content_stream'
|
||||
) as mock_generate_content:
|
||||
mock_generate_content.return_value = [
|
||||
types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='afc output')],
|
||||
),
|
||||
finish_reason=types.FinishReason.STOP,
|
||||
)
|
||||
],
|
||||
automatic_function_calling_history=AFC_HISTORY,
|
||||
)
|
||||
]
|
||||
yield mock_generate_content
|
||||
|
||||
|
||||
def test_history_start_with_valid_model_content():
|
||||
history = [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='Hello there! how can I help you?')],
|
||||
),
|
||||
types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_history_start_with_invalid_model_content():
|
||||
history = [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[],
|
||||
),
|
||||
types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == [types.Content(role='user', parts=[types.Part.from_text(text='Hello')])]
|
||||
|
||||
|
||||
def test_history_with_consecutive_valid_user_inputs():
|
||||
history = [
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='user input 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='user input 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_history_with_valid_and_invalid_user_inputs():
|
||||
history = [
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='user input 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[], # invalid content
|
||||
),
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='user input 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_history_with_consecutive_valid_model_outputs():
|
||||
history = [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_history_with_valid_and_invalid_model_output():
|
||||
history = [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[], # invalid content
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == []
|
||||
|
||||
|
||||
def test_history_end_with_user_input():
|
||||
history = [
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='user input 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output')],
|
||||
),
|
||||
types.Content(
|
||||
role='user',
|
||||
parts=[types.Part.from_text(text='user input 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_unrecognized_role_in_history():
|
||||
history = [
|
||||
types.Content(role='user', parts=[types.Part.from_text(text='Hello')]),
|
||||
types.Content(
|
||||
role='invalid_role',
|
||||
parts=[types.Part.from_text(text='Hello there! how can I help you?')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
with pytest.raises(ValueError) as e:
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert 'Role must be user or model' in str(e)
|
||||
|
||||
|
||||
def test_sync_chat_create():
|
||||
history = [
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='user input turn 1')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='user input turn 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_async_chat_create():
|
||||
history = [
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='user input turn 1')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='user input turn 2')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 2')],
|
||||
),
|
||||
]
|
||||
|
||||
models_module = models.AsyncModels(mock_api_client)
|
||||
chats_module = chats.AsyncChats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
assert chat.get_history() == history
|
||||
assert chat.get_history(curated=True) == history
|
||||
|
||||
|
||||
def test_sync_chat_create_with_history_dict():
|
||||
history = [
|
||||
{'role': 'user', 'parts': [{'text': 'user input turn 1'}]},
|
||||
{'role': 'model', 'parts': [{'text': 'model output turn 1'}]},
|
||||
{'role': 'user', 'parts': [{'text': 'user input turn 2'}]},
|
||||
{'role': 'model', 'parts': [{'text': 'model output turn 2'}]},
|
||||
]
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
expected_history = [
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='user input turn 1')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='user input turn 2')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 2')],
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_history
|
||||
assert chat.get_history(curated=True) == expected_history
|
||||
|
||||
|
||||
def test_async_chat_create_with_history_dict():
|
||||
history = [
|
||||
{'role': 'user', 'parts': [{'text': 'user input turn 1'}]},
|
||||
{'role': 'model', 'parts': [{'text': 'model output turn 1'}]},
|
||||
{'role': 'user', 'parts': [{'text': 'user input turn 2'}]},
|
||||
{'role': 'model', 'parts': [{'text': 'model output turn 2'}]},
|
||||
]
|
||||
models_module = models.AsyncModels(mock_api_client)
|
||||
chats_module = chats.AsyncChats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash', history=history)
|
||||
|
||||
expected_history = [
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='user input turn 1')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 1')],
|
||||
),
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='user input turn 2')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='model output turn 2')],
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_history
|
||||
assert chat.get_history(curated=True) == expected_history
|
||||
|
||||
|
||||
def test_history_with_invalid_turns():
|
||||
valid_input = types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='Hello')]
|
||||
)
|
||||
valid_output = [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='Hello there! how can I help you?')],
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='Hello there! how can I help you?')],
|
||||
),
|
||||
]
|
||||
invalid_input = types.Content(
|
||||
role='user',
|
||||
parts=[
|
||||
types.Part.from_text(text='a input will be rejected by the model')
|
||||
],
|
||||
)
|
||||
invalid_output = types.Content(
|
||||
role='model',
|
||||
parts=[],
|
||||
)
|
||||
comprehensive_history = []
|
||||
comprehensive_history.append(valid_input)
|
||||
comprehensive_history.extend(valid_output)
|
||||
comprehensive_history.append(invalid_input)
|
||||
comprehensive_history.append(invalid_output)
|
||||
curated_history = []
|
||||
curated_history.append(valid_input)
|
||||
curated_history.extend(valid_output)
|
||||
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(
|
||||
model='gemini-2.5-flash', history=comprehensive_history
|
||||
)
|
||||
|
||||
assert chat.get_history() == comprehensive_history
|
||||
assert chat.get_history(curated=True) == curated_history
|
||||
|
||||
|
||||
def test_chat_with_empty_text_part(mock_generate_content_with_empty_text_part):
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash')
|
||||
|
||||
chat.send_message('Hello')
|
||||
|
||||
expected_comprehensive_history = [
|
||||
types.UserContent(parts=[types.Part.from_text(text='Hello')]),
|
||||
types.Content(
|
||||
parts=[types.Part(text='')],
|
||||
role='model',
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_comprehensive_history
|
||||
assert chat.get_history(curated=True) == expected_comprehensive_history
|
||||
|
||||
|
||||
def test_chat_with_empty_content(mock_generate_content_empty_content):
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash')
|
||||
|
||||
chat.send_message('Hello')
|
||||
|
||||
expected_comprehensive_history = [
|
||||
types.UserContent(parts=[types.Part.from_text(text='Hello')]),
|
||||
types.Content(
|
||||
parts=[],
|
||||
role='model',
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_comprehensive_history
|
||||
assert not chat.get_history(curated=True)
|
||||
|
||||
|
||||
def test_chat_stream_with_empty_text_part(
|
||||
mock_generate_content_stream_with_empty_text_part,
|
||||
):
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash')
|
||||
|
||||
chunks = chat.send_message_stream('Hello')
|
||||
for chunk in chunks:
|
||||
pass
|
||||
|
||||
expected_comprehensive_history = [
|
||||
types.UserContent(parts=[types.Part.from_text(text='Hello')]),
|
||||
types.Content(
|
||||
parts=[types.Part(text='')],
|
||||
role='model',
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_comprehensive_history
|
||||
assert chat.get_history(curated=True) == expected_comprehensive_history
|
||||
|
||||
|
||||
def test_chat_stream_with_empty_content(
|
||||
mock_generate_content_stream_empty_content,
|
||||
):
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash')
|
||||
|
||||
chunks = chat.send_message_stream('Hello')
|
||||
for chunk in chunks:
|
||||
pass
|
||||
|
||||
expected_comprehensive_history = [
|
||||
types.UserContent(parts=[types.Part.from_text(text='Hello')]),
|
||||
types.Content(
|
||||
parts=[],
|
||||
role='model',
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_comprehensive_history
|
||||
assert not chat.get_history(curated=True)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
'config.getoption("--private")',
|
||||
reason='AFC logic in private is re-written',
|
||||
)
|
||||
def test_chat_with_afc_history(mock_generate_content_afc_history):
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash')
|
||||
|
||||
chat.send_message('Hello')
|
||||
|
||||
expected_history = AFC_HISTORY + [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='afc output')],
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_history
|
||||
assert chat.get_history(curated=True) == expected_history
|
||||
|
||||
|
||||
def test_chat_stream_with_afc_history(mock_generate_content_stream_afc_history):
|
||||
models_module = models.Models(mock_api_client)
|
||||
chats_module = chats.Chats(modules=models_module)
|
||||
chat = chats_module.create(model='gemini-2.5-flash')
|
||||
|
||||
chunks = chat.send_message_stream('Hello')
|
||||
for chunk in chunks:
|
||||
pass
|
||||
|
||||
expected_history = AFC_HISTORY + [
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='afc output')],
|
||||
),
|
||||
]
|
||||
assert chat.get_history() == expected_history
|
||||
assert chat.get_history(curated=True) == expected_history
|
||||
@@ -0,0 +1,947 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import ValidationError
|
||||
import pytest
|
||||
|
||||
from .. import pytest_helper
|
||||
from ... import errors
|
||||
from ... import types
|
||||
|
||||
try:
|
||||
from mcp import types as mcp_types
|
||||
from mcp import ClientSession as McpClientSession
|
||||
except ImportError as e:
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
raise ImportError(
|
||||
'MCP Tool requires Python 3.10 or above. Please upgrade your Python'
|
||||
' version.'
|
||||
) from e
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
pytestmark = [
|
||||
pytest_helper.setup(
|
||||
file=__file__,
|
||||
globals_for_file=globals(),
|
||||
),
|
||||
pytest.mark.skipif(
|
||||
"config.getoption('--private')",
|
||||
reason="AFC re-written for private SDK",
|
||||
),
|
||||
]
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
|
||||
MODEL_NAME = 'gemini-2.5-flash'
|
||||
|
||||
def divide_intergers_with_customized_math_rule(
|
||||
numerator: int, denominator: int
|
||||
) -> int:
|
||||
"""Divides two integers with customized math rule."""
|
||||
return numerator // denominator + 1
|
||||
|
||||
|
||||
def square_integer(given_integer: int) -> int:
|
||||
return given_integer*given_integer
|
||||
|
||||
|
||||
def power_disco_ball(power: bool) -> bool:
|
||||
"""Powers the spinning disco ball."""
|
||||
print(f"Disco ball is {'spinning!' if power else 'stopped.'}")
|
||||
return True
|
||||
|
||||
def start_music(energetic: bool, loud: bool, bpm: int) -> str:
|
||||
"""Play some music matching the specified parameters.
|
||||
|
||||
Args:
|
||||
energetic: Whether the music is energetic or not.
|
||||
loud: Whether the music is loud or not.
|
||||
bpm: The beats per minute of the music.
|
||||
|
||||
Returns: The name of the song being played.
|
||||
"""
|
||||
print(f"Starting music! {energetic=} {loud=}, {bpm=}")
|
||||
return "Never gonna give you up."
|
||||
|
||||
def dim_lights(brightness: float) -> bool:
|
||||
"""Dim the lights.
|
||||
|
||||
Args:
|
||||
brightness: The brightness of the lights, 0.0 is off, 1.0 is full.
|
||||
"""
|
||||
print(f"Lights are now set to {brightness:.0%}")
|
||||
return True
|
||||
|
||||
def test_text(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chat.send_message(
|
||||
'tell me a story in 100 words',
|
||||
)
|
||||
|
||||
|
||||
def test_part(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chat.send_message(
|
||||
types.Part.from_text(text='tell me a story in 100 words'),
|
||||
)
|
||||
|
||||
|
||||
def test_parts(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chat.send_message(
|
||||
[
|
||||
types.Part.from_text(text='tell me a US city'),
|
||||
types.Part.from_text(text='the city is in west coast'),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_image(client, image_jpeg):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chat.send_message(
|
||||
[
|
||||
'what is the image about?',
|
||||
image_jpeg,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_thinking_budget(client):
|
||||
"""Tests that the thinking budget is respected and generates thoughts."""
|
||||
chat = client.chats.create(
|
||||
model=MODEL_NAME,
|
||||
config={
|
||||
'thinking_config': {
|
||||
'include_thoughts': True,
|
||||
'thinking_budget': 10000,
|
||||
},
|
||||
},
|
||||
)
|
||||
response1 = chat.send_message(
|
||||
'what is the sum of natural numbers from 1 to 100?',
|
||||
)
|
||||
has_thought1 = False
|
||||
if response1.candidates:
|
||||
for candidate in response1.candidates:
|
||||
for part in candidate.content.parts:
|
||||
if part.thought:
|
||||
has_thought1 = True
|
||||
break
|
||||
assert has_thought1
|
||||
|
||||
response2 = chat.send_message(
|
||||
'can you help me to understand the logic better?'
|
||||
)
|
||||
has_thought2 = False
|
||||
if response2.candidates:
|
||||
for candidate in response2.candidates:
|
||||
for part in candidate.content.parts:
|
||||
if part.thought:
|
||||
has_thought2 = True
|
||||
break
|
||||
assert has_thought2
|
||||
|
||||
|
||||
def test_thinking_budget_stream(client):
|
||||
"""Tests that the thinking budget is respected and generates thoughts."""
|
||||
chat = client.chats.create(
|
||||
model=MODEL_NAME,
|
||||
config={
|
||||
'thinking_config': {
|
||||
'include_thoughts': True,
|
||||
'thinking_budget': 10000,
|
||||
},
|
||||
},
|
||||
)
|
||||
has_thought1 = False
|
||||
for chunk in chat.send_message_stream(
|
||||
'what is the sum of natural numbers from 1 to 100?',
|
||||
):
|
||||
if chunk.candidates:
|
||||
for candidate in chunk.candidates:
|
||||
for part in candidate.content.parts:
|
||||
if part.thought:
|
||||
has_thought1 = True
|
||||
break
|
||||
assert has_thought1
|
||||
|
||||
has_thought2 = False
|
||||
for chunk in chat.send_message_stream(
|
||||
'can you help me to understand the logic better?'
|
||||
):
|
||||
if chunk.candidates:
|
||||
for candidate in chunk.candidates:
|
||||
for part in candidate.content.parts:
|
||||
if part.thought:
|
||||
has_thought2 = True
|
||||
break
|
||||
assert has_thought2
|
||||
|
||||
|
||||
def test_google_cloud_storage_uri(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
with pytest_helper.exception_if_mldev(client, errors.ClientError):
|
||||
chat.send_message(
|
||||
[
|
||||
'what is the image about?',
|
||||
types.Part.from_uri(
|
||||
file_uri='gs://unified-genai-dev/imagen-inputs/google_small.png',
|
||||
mime_type='image/png',
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_uploaded_file_uri(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
with pytest_helper.exception_if_vertex(client, errors.ClientError):
|
||||
chat.send_message(
|
||||
[
|
||||
'what is the image about?',
|
||||
types.Part.from_uri(
|
||||
file_uri='https://generativelanguage.googleapis.com/v1beta/files/az606f58k7zj',
|
||||
mime_type='image/png',
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_config_override(client):
|
||||
chat_config = {'candidate_count': 1}
|
||||
chat = client.chats.create(model=MODEL_NAME, config=chat_config)
|
||||
request_config = {'candidate_count': 2}
|
||||
request_config_response = chat.send_message(
|
||||
'tell me a story in 100 words',
|
||||
config=request_config)
|
||||
default_config_response = chat.send_message(
|
||||
'tell me a story in 100 words')
|
||||
|
||||
assert len(request_config_response.candidates) == 2
|
||||
assert len(default_config_response.candidates) == 1
|
||||
|
||||
|
||||
def test_history(client):
|
||||
history = [
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='define a=5, b=10')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='Hello there! how can I help you?')],
|
||||
),
|
||||
]
|
||||
chat = client.chats.create(model=MODEL_NAME, history=history)
|
||||
chat.send_message('what is a + b?')
|
||||
|
||||
assert len(chat.get_history()) > 2
|
||||
|
||||
|
||||
def test_send_2_messages(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chat.send_message('write a python function to check if a year is a leap year')
|
||||
chat.send_message('write a unit test for the function')
|
||||
|
||||
|
||||
def test_with_afc_history(client):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [divide_intergers_with_customized_math_rule]},
|
||||
)
|
||||
_ = chat.send_message('what is the result of 100/2?')
|
||||
chat_history = chat.get_history()
|
||||
|
||||
assert len(chat_history) == 4
|
||||
assert chat_history[0].role == 'user'
|
||||
assert chat_history[0].parts[0].text == 'what is the result of 100/2?'
|
||||
|
||||
assert chat_history[1].role == 'model'
|
||||
assert (
|
||||
chat_history[1].parts[0].function_call.name
|
||||
== 'divide_intergers_with_customized_math_rule'
|
||||
)
|
||||
assert chat_history[1].parts[0].function_call.args == {
|
||||
'numerator': 100,
|
||||
'denominator': 2,
|
||||
}
|
||||
|
||||
assert chat_history[2].role == 'user'
|
||||
assert (
|
||||
chat_history[2].parts[0].function_response.name
|
||||
== 'divide_intergers_with_customized_math_rule'
|
||||
)
|
||||
assert chat_history[2].parts[0].function_response.response == {'result': 51}
|
||||
|
||||
assert chat_history[3].role == 'model'
|
||||
assert '51' in chat_history[3].parts[0].text
|
||||
|
||||
|
||||
def test_existing_chat_history_extends_afc_history(client):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [divide_intergers_with_customized_math_rule]},
|
||||
)
|
||||
_ = chat.send_message('hello')
|
||||
_ = chat.send_message('could you help me with a math problem?')
|
||||
_ = chat.send_message('what is the result of 100/2?')
|
||||
chat_history = chat.get_history()
|
||||
content_strings = []
|
||||
for content in chat_history:
|
||||
content_strings.append(content.model_dump_json())
|
||||
|
||||
# checks that the history is not duplicated
|
||||
assert len(content_strings) == len(set(content_strings))
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.version_info >= (3, 13),
|
||||
reason=(
|
||||
'object type is dumped as <Type.OBJECT: "OBJECT"> as opposed to'
|
||||
' "OBJECT" in Python 3.13'
|
||||
),
|
||||
)
|
||||
def test_with_afc_multiple_remote_calls(client):
|
||||
|
||||
house_fns = [power_disco_ball, start_music, dim_lights]
|
||||
config = {
|
||||
'tools': house_fns,
|
||||
# Force the model to act (call 'any' function), instead of chatting.
|
||||
'tool_config': {
|
||||
'function_calling_config': {
|
||||
'mode': 'ANY',
|
||||
}
|
||||
},
|
||||
'automatic_function_calling': {
|
||||
'maximum_remote_calls': 3,
|
||||
}
|
||||
}
|
||||
chat = client.chats.create(model=MODEL_NAME, config=config)
|
||||
chat.send_message('Turn this place into a party!')
|
||||
curated_history = chat.get_history()
|
||||
|
||||
assert len(curated_history) == 8
|
||||
assert curated_history[0].role == 'user'
|
||||
assert curated_history[0].parts[0].text == 'Turn this place into a party!'
|
||||
assert curated_history[1].role == 'model'
|
||||
assert len(curated_history[1].parts) == 3
|
||||
for part in curated_history[1].parts:
|
||||
assert part.function_call
|
||||
assert curated_history[2].role == 'user'
|
||||
assert len(curated_history[2].parts) == 3
|
||||
for part in curated_history[2].parts:
|
||||
assert part.function_response
|
||||
assert curated_history[3].role == 'model'
|
||||
assert len(curated_history[3].parts) == 3
|
||||
for part in curated_history[3].parts:
|
||||
assert part.function_call
|
||||
assert curated_history[4].role == 'user'
|
||||
assert len(curated_history[4].parts) == 3
|
||||
for part in curated_history[4].parts:
|
||||
assert part.function_response
|
||||
assert curated_history[5].role == 'model'
|
||||
assert len(curated_history[5].parts) == 3
|
||||
for part in curated_history[5].parts:
|
||||
assert part.function_call
|
||||
assert curated_history[6].role == 'user'
|
||||
assert len(curated_history[6].parts) == 3
|
||||
for part in curated_history[6].parts:
|
||||
assert part.function_response
|
||||
assert curated_history[7].role == 'model'
|
||||
assert len(curated_history[7].parts) == 3
|
||||
for part in curated_history[7].parts:
|
||||
assert part.function_call
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.version_info >= (3, 13),
|
||||
reason=(
|
||||
'object type is dumped as <Type.OBJECT: "OBJECT"> as opposed to'
|
||||
' "OBJECT" in Python 3.13'
|
||||
),
|
||||
)
|
||||
def test_with_afc_multiple_remote_calls_async(client):
|
||||
|
||||
house_fns = [power_disco_ball, start_music, dim_lights]
|
||||
config = {
|
||||
'tools': house_fns,
|
||||
# Force the model to act (call 'any' function), instead of chatting.
|
||||
'tool_config': {
|
||||
'function_calling_config': {
|
||||
'mode': 'ANY',
|
||||
}
|
||||
},
|
||||
'automatic_function_calling': {
|
||||
'maximum_remote_calls': 3,
|
||||
}
|
||||
}
|
||||
chat = client.chats.create(model=MODEL_NAME, config=config)
|
||||
chat.send_message('Turn this place into a party!')
|
||||
curated_history = chat.get_history()
|
||||
|
||||
assert len(curated_history) == 8
|
||||
assert curated_history[0].role == 'user'
|
||||
assert curated_history[0].parts[0].text == 'Turn this place into a party!'
|
||||
assert curated_history[1].role == 'model'
|
||||
assert len(curated_history[1].parts) == 3
|
||||
for part in curated_history[1].parts:
|
||||
assert part.function_call
|
||||
assert curated_history[2].role == 'user'
|
||||
assert len(curated_history[2].parts) == 3
|
||||
for part in curated_history[2].parts:
|
||||
assert part.function_response
|
||||
assert curated_history[3].role == 'model'
|
||||
assert len(curated_history[3].parts) == 3
|
||||
for part in curated_history[3].parts:
|
||||
assert part.function_call
|
||||
assert curated_history[4].role == 'user'
|
||||
assert len(curated_history[4].parts) == 3
|
||||
for part in curated_history[4].parts:
|
||||
assert part.function_response
|
||||
assert curated_history[5].role == 'model'
|
||||
assert len(curated_history[5].parts) == 3
|
||||
for part in curated_history[5].parts:
|
||||
assert part.function_call
|
||||
assert curated_history[6].role == 'user'
|
||||
assert len(curated_history[6].parts) == 3
|
||||
for part in curated_history[6].parts:
|
||||
assert part.function_response
|
||||
assert curated_history[7].role == 'model'
|
||||
assert len(curated_history[7].parts) == 3
|
||||
for part in curated_history[7].parts:
|
||||
assert part.function_call
|
||||
|
||||
def test_with_afc_disabled(client):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={
|
||||
'tools': [square_integer],
|
||||
'automatic_function_calling': {'disable': True},
|
||||
},
|
||||
)
|
||||
chat.send_message(
|
||||
'Do the square of 3.',
|
||||
)
|
||||
chat_history = chat.get_history()
|
||||
|
||||
assert len(chat_history) == 2
|
||||
assert chat_history[0].role == 'user'
|
||||
assert chat_history[0].parts[0].text == 'Do the square of 3.'
|
||||
|
||||
assert chat_history[1].role == 'model'
|
||||
assert chat_history[1].parts[0].function_call.name == 'square_integer'
|
||||
assert chat_history[1].parts[0].function_call.args == {
|
||||
'given_integer': 3,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_with_afc_history_async(client):
|
||||
chat = client.aio.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [divide_intergers_with_customized_math_rule]},
|
||||
)
|
||||
_ = await chat.send_message('what is the result of 100/2?')
|
||||
chat_history = chat.get_history()
|
||||
|
||||
assert len(chat_history) == 4
|
||||
assert chat_history[0].role == 'user'
|
||||
assert chat_history[0].parts[0].text == 'what is the result of 100/2?'
|
||||
|
||||
assert chat_history[1].role == 'model'
|
||||
assert (
|
||||
chat_history[1].parts[0].function_call.name
|
||||
== 'divide_intergers_with_customized_math_rule'
|
||||
)
|
||||
assert chat_history[1].parts[0].function_call.args == {
|
||||
'numerator': 100,
|
||||
'denominator': 2,
|
||||
}
|
||||
|
||||
assert chat_history[2].role == 'user'
|
||||
assert (
|
||||
chat_history[2].parts[0].function_response.name
|
||||
== 'divide_intergers_with_customized_math_rule'
|
||||
)
|
||||
assert chat_history[2].parts[0].function_response.response == {'result': 51}
|
||||
|
||||
assert chat_history[3].role == 'model'
|
||||
assert '51' in chat_history[3].parts[0].text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_with_afc_disabled_async(client):
|
||||
chat = client.aio.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={
|
||||
'tools': [square_integer],
|
||||
'automatic_function_calling': {'disable': True},
|
||||
},
|
||||
)
|
||||
await chat.send_message(
|
||||
'Do the square of 3.',
|
||||
)
|
||||
chat_history = chat.get_history()
|
||||
|
||||
assert len(chat_history) == 2
|
||||
assert chat_history[0].role == 'user'
|
||||
assert chat_history[0].parts[0].text == 'Do the square of 3.'
|
||||
|
||||
assert chat_history[1].role == 'model'
|
||||
assert chat_history[1].parts[0].function_call.name == 'square_integer'
|
||||
assert chat_history[1].parts[0].function_call.args == {
|
||||
'given_integer': 3,
|
||||
}
|
||||
|
||||
|
||||
def test_stream_text(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chunks = 0
|
||||
for chunk in chat.send_message_stream(
|
||||
'tell me a story in 100 words',
|
||||
):
|
||||
chunks += 1
|
||||
|
||||
assert chunks > 1
|
||||
|
||||
|
||||
def test_stream_part(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chunks = 0
|
||||
for chunk in chat.send_message_stream(
|
||||
types.Part.from_text(text='tell me a story in 100 words'),
|
||||
):
|
||||
chunks += 1
|
||||
|
||||
assert chunks > 1
|
||||
|
||||
|
||||
def test_stream_parts(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
chunks = 0
|
||||
for chunk in chat.send_message_stream(
|
||||
[
|
||||
types.Part.from_text(text='tell me a story in 100 words'),
|
||||
types.Part.from_text(text='the story is about a car'),
|
||||
],
|
||||
):
|
||||
chunks += 1
|
||||
|
||||
assert chunks > 2
|
||||
|
||||
|
||||
def test_stream_config_override(client):
|
||||
chat_config = {'response_mime_type': 'text/plain'}
|
||||
chat = client.chats.create(model=MODEL_NAME, config=chat_config)
|
||||
request_config = {'response_mime_type': 'application/json'}
|
||||
request_config_text = ''
|
||||
for chunk in chat.send_message_stream(
|
||||
'tell me a story in 100 words', config=request_config
|
||||
):
|
||||
request_config_text += chunk.text
|
||||
default_config_text = ''
|
||||
for chunk in chat.send_message_stream('tell me a story in 100 words'):
|
||||
default_config_text += chunk.text
|
||||
|
||||
assert json.loads(request_config_text)
|
||||
with pytest.raises(json.JSONDecodeError):
|
||||
json.loads(default_config_text)
|
||||
|
||||
|
||||
def test_stream_function_calling(client):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [divide_intergers_with_customized_math_rule]},
|
||||
)
|
||||
# Now we support AFC.
|
||||
for chunk in chat.send_message_stream(
|
||||
'what is the result of 100/2?',
|
||||
):
|
||||
pass
|
||||
for chunk in chat.send_message_stream(
|
||||
'what is the result of 50/2?',
|
||||
):
|
||||
pass
|
||||
chat_history = chat.get_history()
|
||||
|
||||
assert chat_history[0].role == 'user'
|
||||
assert chat_history[0].parts[0].text == 'what is the result of 100/2?'
|
||||
|
||||
assert chat_history[1].role == 'model'
|
||||
assert (
|
||||
chat_history[1].parts[0].function_call.name
|
||||
== 'divide_intergers_with_customized_math_rule'
|
||||
)
|
||||
assert chat_history[1].parts[0].function_call.args == {
|
||||
'numerator': 100,
|
||||
'denominator': 2,
|
||||
}
|
||||
|
||||
|
||||
def test_stream_send_2_messages(client):
|
||||
chat = client.chats.create(model=MODEL_NAME)
|
||||
for chunk in chat.send_message_stream(
|
||||
'write a python function to check if a year is a leap year'
|
||||
):
|
||||
pass
|
||||
|
||||
for chunk in chat.send_message_stream('write a unit test for the function'):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_text(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
await chat.send_message('tell me a story in 100 words')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_part(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
await chat.send_message(types.Part.from_text(text='tell me a story in 100 words'))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_parts(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
await chat.send_message(
|
||||
[
|
||||
types.Part.from_text(text='tell me a US city'),
|
||||
types.Part.from_text(text='the city is in west coast'),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_config_override(client):
|
||||
chat_config = {'candidate_count': 1}
|
||||
chat = client.aio.chats.create(model=MODEL_NAME, config=chat_config)
|
||||
request_config = {'candidate_count': 2}
|
||||
request_config_response = await chat.send_message(
|
||||
'tell me a story in 100 words',
|
||||
config=request_config)
|
||||
default_config_response = await chat.send_message(
|
||||
'tell me a story in 100 words')
|
||||
|
||||
assert len(request_config_response.candidates) == 2
|
||||
assert len(default_config_response.candidates) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_history(client):
|
||||
history = [
|
||||
types.Content(
|
||||
role='user', parts=[types.Part.from_text(text='define a=5, b=10')]
|
||||
),
|
||||
types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='Hello there! how can I help you?')],
|
||||
),
|
||||
]
|
||||
chat = client.aio.chats.create(model=MODEL_NAME, history=history)
|
||||
await chat.send_message('what is a + b?')
|
||||
|
||||
assert len(chat.get_history()) > 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_stream_text(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
chunks = 0
|
||||
async for chunk in await chat.send_message_stream('tell me a story in 100 words'):
|
||||
chunks += 1
|
||||
|
||||
assert chunks > 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_stream_part(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
chunks = 0
|
||||
async for chunk in await chat.send_message_stream(
|
||||
types.Part.from_text(text='tell me a story in 100 words')
|
||||
):
|
||||
chunks += 1
|
||||
|
||||
assert chunks > 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_stream_parts(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
chunks = 0
|
||||
async for chunk in await chat.send_message_stream(
|
||||
[
|
||||
types.Part.from_text(text='tell me a story in 100 words'),
|
||||
types.Part.from_text(text='the story is about a car'),
|
||||
],
|
||||
):
|
||||
chunks += 1
|
||||
|
||||
assert chunks > 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_stream_config_override(client):
|
||||
chat_config = {'response_mime_type': 'text/plain'}
|
||||
chat = client.aio.chats.create(model=MODEL_NAME, config=chat_config)
|
||||
request_config = {'response_mime_type': 'application/json'}
|
||||
request_config_text = ''
|
||||
async for chunk in await chat.send_message_stream(
|
||||
'tell me a story in 100 words', config=request_config
|
||||
):
|
||||
request_config_text += chunk.text
|
||||
default_config_text = ''
|
||||
|
||||
async for chunk in await chat.send_message_stream('tell me family friendly story in 100 words'):
|
||||
default_config_text += chunk.text
|
||||
|
||||
assert json.loads(request_config_text)
|
||||
with pytest_helper.exception_if_mldev(client, json.JSONDecodeError):
|
||||
json.loads(default_config_text)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_stream_function_calling(client):
|
||||
chat = client.aio.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [divide_intergers_with_customized_math_rule]},
|
||||
)
|
||||
# Now we support AFC.
|
||||
async for chunk in await chat.send_message_stream('what is the result of 100/2?'):
|
||||
pass
|
||||
async for chunk in await chat.send_message_stream('what is the result of 50/2?'):
|
||||
pass
|
||||
chat_history = chat.get_history()
|
||||
|
||||
assert chat_history[0].role == 'user'
|
||||
assert chat_history[0].parts[0].text == 'what is the result of 100/2?'
|
||||
|
||||
assert chat_history[1].role == 'model'
|
||||
assert (
|
||||
chat_history[1].parts[0].function_call.name
|
||||
== 'divide_intergers_with_customized_math_rule'
|
||||
)
|
||||
assert chat_history[1].parts[0].function_call.args == {
|
||||
'numerator': 100,
|
||||
'denominator': 2,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_stream_send_2_messages(client):
|
||||
chat = client.aio.chats.create(model=MODEL_NAME)
|
||||
async for chunk in await chat.send_message_stream(
|
||||
'write a python function to check if a year is a leap year'
|
||||
):
|
||||
pass
|
||||
async for chunk in await chat.send_message_stream(
|
||||
'write a unit test for the function'
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def test_mcp_tools(client):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [
|
||||
mcp_types.Tool(
|
||||
name='get_weather',
|
||||
description='Get the weather in a city.',
|
||||
inputSchema={
|
||||
'type': 'object',
|
||||
'properties': {'location': {'type': 'string'}},
|
||||
},
|
||||
)
|
||||
],},
|
||||
)
|
||||
response = chat.send_message('What is the weather in Boston?')
|
||||
response = chat.send_message('What is the weather in San Francisco?')
|
||||
|
||||
|
||||
def test_mcp_tools_stream(client):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [
|
||||
mcp_types.Tool(
|
||||
name='get_weather',
|
||||
description='Get the weather in a city.',
|
||||
inputSchema={
|
||||
'type': 'object',
|
||||
'properties': {'location': {'type': 'string'}},
|
||||
},
|
||||
)
|
||||
],
|
||||
},
|
||||
)
|
||||
for chunk in chat.send_message_stream(
|
||||
'What is the weather in Boston?'
|
||||
):
|
||||
pass
|
||||
for chunk in chat.send_message_stream(
|
||||
'What is the weather in San Francisco?'
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_mcp_tools(client):
|
||||
chat = client.aio.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [
|
||||
mcp_types.Tool(
|
||||
name='get_weather',
|
||||
description='Get the weather in a city.',
|
||||
inputSchema={
|
||||
'type': 'object',
|
||||
'properties': {'location': {'type': 'string'}},
|
||||
},
|
||||
)
|
||||
],},
|
||||
)
|
||||
await chat.send_message('What is the weather in Boston?');
|
||||
await chat.send_message('What is the weather in San Francisco?');
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_mcp_tools_stream(client):
|
||||
chat = client.aio.chats.create(
|
||||
model='gemini-2.0-flash-exp',
|
||||
config={'tools': [
|
||||
mcp_types.Tool(
|
||||
name='get_weather',
|
||||
description='Get the weather in a city.',
|
||||
inputSchema={
|
||||
'type': 'object',
|
||||
'properties': {'location': {'type': 'string'}},
|
||||
},
|
||||
)
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
async for chunk in await chat.send_message_stream(
|
||||
'What is the weather in Boston?'
|
||||
):
|
||||
pass
|
||||
async for chunk in await chat.send_message_stream(
|
||||
'What is the weather in San Francisco?'
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def test_server_side_mcp_tools(client):
|
||||
with pytest_helper.exception_if_vertex(client, ValueError):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.5-flash',
|
||||
config={
|
||||
'tools': [
|
||||
{
|
||||
'mcp_servers': [
|
||||
{
|
||||
'name': 'weather_server',
|
||||
'streamable_http_transport': {
|
||||
'url': (
|
||||
'https://gemini-api-demos.uc.r.appspot.com/mcp'
|
||||
),
|
||||
'headers': {
|
||||
'AUTHORIZATION': 'Bearer github_pat_XXXX',
|
||||
},
|
||||
'timeout': '10s',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
response = chat.send_message('What is the weather in Boston on 02/02/2026?')
|
||||
response = chat.send_message(
|
||||
'What is the weather in San Francisco on 02/02/2026?'
|
||||
)
|
||||
|
||||
|
||||
def test_server_side_mcp_tools_stream(client):
|
||||
with pytest_helper.exception_if_vertex(client, ValueError):
|
||||
chat = client.chats.create(
|
||||
model='gemini-2.5-flash',
|
||||
config={
|
||||
'tools': [
|
||||
{
|
||||
'mcp_servers': [
|
||||
{
|
||||
'name': 'weather_server',
|
||||
'streamable_http_transport': {
|
||||
'url': (
|
||||
'https://gemini-api-demos.uc.r.appspot.com/mcp'
|
||||
),
|
||||
'headers': {
|
||||
'AUTHORIZATION': 'Bearer github_pat_XXXX',
|
||||
},
|
||||
'timeout': '10s',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
for chunk in chat.send_message_stream(
|
||||
'What is the weather in Boston on 02/02/2026?'
|
||||
):
|
||||
pass
|
||||
for chunk in chat.send_message_stream(
|
||||
'What is the weather in San Francisco on 02/02/2026?'
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_server_side_mcp_tools(client):
|
||||
with pytest_helper.exception_if_vertex(client, ValueError):
|
||||
chat = client.aio.chats.create(
|
||||
model='gemini-2.5-flash',
|
||||
config={
|
||||
'tools': [
|
||||
{
|
||||
'mcp_servers': [
|
||||
{
|
||||
'name': 'weather_server',
|
||||
'streamable_http_transport': {
|
||||
'url': (
|
||||
'https://gemini-api-demos.uc.r.appspot.com/mcp'
|
||||
),
|
||||
'headers': {
|
||||
'AUTHORIZATION': 'Bearer github_pat_XXXX',
|
||||
},
|
||||
'timeout': '10s',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
await chat.send_message('What is the weather in Boston on 02/02/2026?')
|
||||
await chat.send_message(
|
||||
'What is the weather in San Francisco on 02/02/2026?'
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
# 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 ... import chats
|
||||
from ... import types
|
||||
|
||||
_validate_response = chats._validate_response
|
||||
|
||||
|
||||
def test_validate_response_default_response():
|
||||
response = types.GenerateContentResponse()
|
||||
|
||||
assert not _validate_response(response)
|
||||
|
||||
|
||||
def test_validate_response_empty_content():
|
||||
response = types.GenerateContentResponse(candidates=[])
|
||||
|
||||
assert not _validate_response(response)
|
||||
|
||||
|
||||
def test_validate_response_empty_parts():
|
||||
response = types.GenerateContentResponse(
|
||||
candidates=[types.Candidate(content=types.Content(parts=[]))]
|
||||
)
|
||||
|
||||
assert not _validate_response(response)
|
||||
|
||||
|
||||
def test_validate_response_empty_part():
|
||||
response = types.GenerateContentResponse(
|
||||
candidates=[types.Candidate(content=types.Content(parts=[types.Part()]))]
|
||||
)
|
||||
|
||||
assert not _validate_response(response)
|
||||
|
||||
|
||||
def test_validate_response_part_with_empty_text():
|
||||
response = types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(content=types.Content(parts=[types.Part(text='')]))
|
||||
]
|
||||
)
|
||||
|
||||
assert _validate_response(response)
|
||||
|
||||
|
||||
def test_validate_response_part_with_text():
|
||||
response = types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(
|
||||
content=types.Content(
|
||||
parts=[types.Part(text='response from model')]
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
assert _validate_response(response)
|
||||
|
||||
|
||||
def test_validate_response_part_with_function_call():
|
||||
response = types.GenerateContentResponse(
|
||||
candidates=[
|
||||
types.Candidate(
|
||||
content=types.Content(
|
||||
parts=[
|
||||
types.Part(
|
||||
function_call=types.FunctionCall(
|
||||
name='foo', args={'bar': 'baz'}
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
assert _validate_response(response)
|
||||
Reference in new issue
Block a user