Files
2026-07-13 12:49:17 +08:00

316 lines
12 KiB
Python

# Copyright (c) 2025 ByteDance Ltd. and/or its affiliates
# SPDX-License-Identifier: MIT
"""
Unit tests for the GoogleClient.
WARNING: These tests should not be run in a GitHub Actions workflow
because they require an API key.
"""
import os
import unittest
from unittest.mock import MagicMock, patch
from trae_agent.tools.base import Tool, ToolCall, ToolResult
from trae_agent.utils.config import ModelConfig, ModelProvider
from trae_agent.utils.llm_clients.google_client import GoogleClient
from trae_agent.utils.llm_clients.llm_basics import LLMMessage
TEST_MODEL = "gemini-2.5-flash"
@unittest.skipIf(
os.getenv("SKIP_GOOGLE_TEST", "").lower() == "true",
"Google tests skipped due to SKIP_GOOGLE_TEST environment variable",
)
class TestGoogleClient(unittest.TestCase):
@patch("trae_agent.utils.google_client.genai.Client")
def test_google_client_init(self, mock_genai_client):
"""Test the initialization of the GoogleClient."""
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-api-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=7.0,
top_k=8,
parallel_tool_calls=False,
max_retries=1,
)
google_client = GoogleClient(model_config)
mock_genai_client.assert_called_once_with(api_key="test-api-key")
self.assertIsNotNone(google_client.client)
@patch("trae_agent.utils.google_client.genai.Client")
@patch.dict(os.environ, {"GOOGLE_API_KEY": "test-env-api-key"})
def test_google_client_init_with_env_key(self, mock_genai_client):
"""
Test that the google client initializes using the GOOGLE_API_KEY environment variable.
"""
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=7.0,
top_k=8,
parallel_tool_calls=False,
max_retries=1,
)
google_client = GoogleClient(model_config)
mock_genai_client.assert_called_once_with(api_key="test-env-api-key")
self.assertEqual(google_client.api_key, "test-env-api-key")
@patch.dict(os.environ, {"GOOGLE_API_KEY": ""})
def test_google_client_init_no_key_raises_error(self):
"""
Test that a ValueError is raised if no API key is provided.
"""
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=7.0,
top_k=8,
parallel_tool_calls=False,
max_retries=1,
)
with self.assertRaises(ValueError):
GoogleClient(model_config)
@patch("trae_agent.utils.google_client.genai.Client")
def test_google_set_chat_history(self, mock_genai_client):
"""
Test that the chat history is correctly parsed and stored.
"""
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-api-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=7.0,
top_k=8,
parallel_tool_calls=False,
max_retries=1,
)
google_client = GoogleClient(model_config)
messages = [
LLMMessage("system", "You are a helpful assistant."),
LLMMessage("user", "Hello, world!"),
]
google_client.set_chat_history(messages)
self.assertEqual(google_client.system_instruction, "You are a helpful assistant.")
self.assertEqual(len(google_client.message_history), 1)
self.assertEqual(google_client.message_history[0].role, "user")
self.assertEqual(google_client.message_history[0].parts[0].text, "Hello, world!")
@patch("trae_agent.utils.google_client.genai.Client")
def test_google_chat(self, mock_genai_client):
"""
Test the chat method with a simple user message.
"""
mock_model = MagicMock()
mock_response = MagicMock()
mock_response.candidates = [MagicMock()]
mock_response.candidates[0].content.parts = [MagicMock(text="Hello!")]
mock_response.candidates[0].finish_reason.name = "STOP"
mock_response.usage_metadata = MagicMock(prompt_token_count=10, candidates_token_count=20)
mock_model.generate_content.return_value = mock_response
mock_genai_client.return_value.models = mock_model
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-api-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=7.0,
top_k=8,
parallel_tool_calls=False,
max_retries=1,
)
google_client = GoogleClient(model_config)
message = LLMMessage("user", "this is a test message")
response = google_client.chat(messages=[message], model_config=model_config)
mock_model.generate_content.assert_called_once()
self.assertEqual(response.content, "Hello!")
self.assertEqual(response.usage.input_tokens, 10)
self.assertEqual(response.usage.output_tokens, 20)
self.assertEqual(response.finish_reason, "STOP")
@patch("trae_agent.utils.google_client.genai.Client")
def test_google_chat_with_tool_call(self, mock_genai_client):
"""
Test the chat method's ability to handle tool calls.
"""
mock_model = MagicMock()
mock_response = MagicMock()
mock_function_call = MagicMock()
mock_function_call.name = "get_weather"
mock_function_call.args = {"location": "Boston"}
mock_response.candidates = [MagicMock()]
mock_response.candidates[0].content.parts = [
MagicMock(function_call=mock_function_call, text=None)
]
mock_response.candidates[0].finish_reason.name = "TOOL_CALL"
mock_response.usage_metadata = MagicMock(prompt_token_count=30, candidates_token_count=15)
mock_model.generate_content.return_value = mock_response
mock_genai_client.return_value.models = mock_model
mock_tool = MagicMock(spec=Tool)
mock_tool.name = "get_weather"
mock_tool.description = "Gets the weather for a location."
mock_tool.get_input_schema.return_value = {
"type": "object",
"properties": {"location": {"type": "string"}},
}
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-api-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=1.0,
top_k=1,
parallel_tool_calls=True,
max_retries=1,
)
google_client = GoogleClient(model_config)
message = LLMMessage("user", "What is the weather in Boston?")
response = google_client.chat(
messages=[message], model_config=model_config, tools=[mock_tool]
)
self.assertEqual(response.content, "")
self.assertIsNotNone(response.tool_calls)
self.assertEqual(len(response.tool_calls), 1)
tool_call = response.tool_calls[0]
self.assertEqual(tool_call.name, "get_weather")
self.assertEqual(tool_call.arguments, {"location": "Boston"})
self.assertEqual(response.finish_reason, "TOOL_CALL")
def test_parse_messages(self):
"""Test the parse_messages method with various message types."""
google_client = GoogleClient(
ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=1.0,
top_k=1,
parallel_tool_calls=True,
max_retries=1,
)
)
messages = [
LLMMessage("system", "Be concise."),
LLMMessage("user", "Hello"),
LLMMessage(
"model",
"Hi there!",
tool_call=ToolCall(name="search", arguments={"query": "news"}, call_id="tool-123"),
),
LLMMessage(
"tool",
"Search results",
tool_result=ToolResult(
call_id="12345", name="search", result="news data", success=True
),
),
]
parsed_messages, system_instruction = google_client.parse_messages(messages)
self.assertEqual(system_instruction, "Be concise.")
self.assertEqual(len(parsed_messages), 3)
self.assertEqual(parsed_messages[0].role, "user")
self.assertEqual(parsed_messages[0].parts[0].text, "Hello")
self.assertEqual(parsed_messages[1].role, "model")
self.assertEqual(parsed_messages[1].parts[0].function_call.name, "search")
self.assertEqual(parsed_messages[2].role, "tool")
self.assertEqual(parsed_messages[2].parts[0].function_response.name, "search")
def test_parse_tool_call_result(self):
"""
Test the _parse_tool_call_result method.
"""
google_client = GoogleClient(
ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=1.0,
top_k=1,
parallel_tool_calls=True,
max_retries=1,
)
)
# Test with a simple result
tool_result_simple = ToolResult(
call_id="1", name="test_tool", result={"status": "done"}, success=True
)
parsed_part_simple = google_client.parse_tool_call_result(tool_result_simple)
self.assertEqual(parsed_part_simple.function_response.name, "test_tool")
self.assertEqual(
parsed_part_simple.function_response.response,
{"result": {"status": "done"}},
)
# Test with an error
tool_result_error = ToolResult(
call_id="2",
name="test_tool",
result="some data",
error="Something went wrong",
success=False,
)
parsed_part_error = google_client.parse_tool_call_result(tool_result_error)
self.assertIn("error", parsed_part_error.function_response.response)
self.assertEqual(
parsed_part_error.function_response.response["error"],
"Something went wrong",
)
# Test with non-serializable result
non_serializable_obj = object()
tool_result_non_serializable = ToolResult(
call_id="3", name="test_tool", result=non_serializable_obj, success=True
)
parsed_part_non_serializable = google_client.parse_tool_call_result(
tool_result_non_serializable
)
self.assertIn("result", parsed_part_non_serializable.function_response.response)
self.assertEqual(
parsed_part_non_serializable.function_response.response["result"],
str(non_serializable_obj),
)
def test_supports_tool_calling(self):
"""Test the supports_tool_calling method."""
model_config = ModelConfig(
model=TEST_MODEL,
model_provider=ModelProvider(api_key="test-api-key", provider="google"),
max_tokens=1000,
temperature=0.8,
top_p=7.0,
top_k=8,
parallel_tool_calls=False,
max_retries=1,
base_url=None,
)
google_client = GoogleClient(model_config)
self.assertEqual(google_client.supports_tool_calling(model_config), True)
model_config.model = "no such model"
self.assertEqual(google_client.supports_tool_calling(model_config), False)
if __name__ == "__main__":
unittest.main()