| import unittest |
| import pprint |
| import pytest |
| from unittest.mock import Mock |
|
|
| from mcpuniverse.mcp.manager import MCPManager |
| from mcpuniverse.llm.manager import ModelManager |
| from mcpuniverse.agent.function_call import FunctionCall, FunctionCallConfig |
| from mcpuniverse.tracer import Tracer |
| from mcpuniverse.tracer.collectors import SQLiteCollector |
| from mcpuniverse.callbacks.base import Printer |
|
|
|
|
| class TestFunctionCallAgent(unittest.IsolatedAsyncioTestCase): |
|
|
| @pytest.mark.skip |
| async def test_prompt(self): |
| """Test the prompt building functionality of FunctionCall agent.""" |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={"servers": [{"name": "weather"}]} |
| ) |
| await agent.initialize() |
| prompt = agent._build_prompt(question="What's the weather in San Francisco now?") |
| self.assertTrue("What's the weather in San Francisco now?" in prompt) |
| await agent.cleanup() |
|
|
| @pytest.mark.skip |
| async def test_tool_conversion(self): |
| """Test MCP tools to function call conversion.""" |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={"servers": [{"name": "weather"}]} |
| ) |
| await agent.initialize() |
| |
| |
| empty_tools = agent._convert_mcp_tools_to_function_calls({}) |
| self.assertEqual(empty_tools, []) |
|
|
| |
| if agent._tools: |
| function_calls = agent._convert_mcp_tools_to_function_calls(agent._tools) |
| self.assertIsInstance(function_calls, list) |
| for fc in function_calls: |
| self.assertIn("type", fc) |
| self.assertEqual(fc["type"], "function") |
| self.assertIn("function", fc) |
| self.assertIn("name", fc["function"]) |
| self.assertIn("description", fc["function"]) |
| self.assertIn("parameters", fc["function"]) |
|
|
| await agent.cleanup() |
|
|
| @pytest.mark.skip |
| async def test_function_name_parsing(self): |
| """Test function name parsing functionality.""" |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={"servers": [{"name": "weather"}]} |
| ) |
| await agent.initialize() |
|
|
| |
| server, tool = agent._parse_function_call_name("weather__get_current_weather") |
| self.assertEqual(server, "weather") |
| self.assertEqual(tool, "get_current_weather") |
|
|
| server, tool = agent._parse_function_call_name("search__web_search") |
| self.assertEqual(server, "search") |
| self.assertEqual(tool, "web_search") |
|
|
| |
| server, tool = agent._parse_function_call_name("server__tool__with__underscores") |
| self.assertEqual(server, "server") |
| self.assertEqual(tool, "tool__with__underscores") |
|
|
| |
| with self.assertRaises(ValueError): |
| agent._parse_function_call_name("invalid_name_without_double_underscore") |
|
|
| await agent.cleanup() |
|
|
| @pytest.mark.skip |
| async def test_configuration(self): |
| """Test FunctionCall configuration.""" |
| config = { |
| "name": "test_fc", |
| "instruction": "You are a helpful assistant that can use tools to answer questions.", |
| "max_iterations": 3, |
| "summarize_tool_response": True, |
| "servers": [{"name": "weather"}] |
| } |
| |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config=config |
| ) |
| await agent.initialize() |
| |
| self.assertEqual(agent._config.name, "test_fc") |
| self.assertEqual(agent._config.max_iterations, 3) |
| self.assertTrue(agent._config.summarize_tool_response) |
| self.assertIn("function_call", agent.alias) |
| self.assertIn("fc", agent.alias) |
| self.assertIn("function-call", agent.alias) |
| |
| await agent.cleanup() |
|
|
| @pytest.mark.skip |
| async def test_history_management(self): |
| """Test conversation history management.""" |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={"servers": [{"name": "weather"}]} |
| ) |
| await agent.initialize() |
| |
| |
| agent._add_history("thought", "I need to check the weather") |
| agent._add_history("action", "Using weather API") |
| agent._add_history("result", "Temperature is 72°F") |
| |
| history = agent.get_history() |
| self.assertIn("Thought: I need to check the weather", history) |
| self.assertIn("Action: Using weather API", history) |
| self.assertIn("Result: Temperature is 72°F", history) |
| |
| |
| agent.clear_history() |
| self.assertEqual(len(agent._history), 0) |
| self.assertEqual(agent.get_history(), "") |
| |
| |
| agent._add_history("test", "test message") |
| agent.reset() |
| self.assertEqual(len(agent._history), 0) |
| |
| await agent.cleanup() |
|
|
| @pytest.mark.skip |
| async def test_execute_weather(self): |
| """Test executing a weather query using function calling.""" |
| question = "I live in San Francisco. Do I need to bring an umbrella if I need to go outside?" |
| tracer = Tracer() |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={ |
| "instruction": "You are a helpful weather assistant that can check current weather conditions.", |
| "servers": [{"name": "weather"}], |
| "max_iterations": 3 |
| } |
| ) |
| await agent.initialize() |
| print(f"Agent description: {agent.get_description()}") |
| |
| response = await agent.execute( |
| message=question, |
| tracer=tracer, |
| callbacks=Printer() |
| ) |
| print(f"Response: {response}") |
| print(f"History: {agent.get_history()}") |
| await agent.cleanup() |
| pprint.pprint(tracer.get_trace()) |
|
|
| @pytest.mark.skip |
| async def test_execute_with_output_format(self): |
| """Test executing with specific output format.""" |
| question = "What's the current weather in New York City?" |
| tracer = Tracer() |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={ |
| "instruction": "You are a weather assistant.", |
| "servers": [{"name": "weather"}], |
| "max_iterations": 5, |
| "summarize_tool_response": False |
| } |
| ) |
| await agent.initialize() |
|
|
| output_format = { |
| "weather_summary": "<Brief weather summary>", |
| "temperature": "<Temperature in Fahrenheit>", |
| "recommendation": "<Clothing or activity recommendation>" |
| } |
|
|
| response = await agent.execute( |
| message=question, |
| output_format=output_format, |
| tracer=tracer, |
| callbacks=Printer() |
| ) |
| print(f"Formatted response: {response}") |
| print(f"Agent history: {agent.get_history()}") |
| await agent.cleanup() |
| pprint.pprint(tracer.get_trace()) |
|
|
| @pytest.mark.skip |
| async def test_execute_multi_server(self): |
| """Test executing with multiple servers.""" |
| question = "Search for information about the tallest building in LA and then get the current weather there." |
| tracer = Tracer() |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={ |
| "instruction": "You are an assistant that can search for information and check weather.", |
| "servers": [ |
| {"name": "weather"}, |
| {"name": "google-search"} |
| ], |
| "max_iterations": 5, |
| } |
| ) |
| await agent.initialize() |
| print(f"Available tools: {agent.get_description(with_tools_description=True)}") |
|
|
| response = await agent.execute( |
| message=question, |
| tracer=tracer, |
| callbacks=Printer() |
| ) |
| print(f"Multi-server response: {response}") |
| print(f"Execution history: {agent.get_history()}") |
| await agent.cleanup() |
| pprint.pprint(tracer.get_trace()) |
|
|
| @pytest.mark.skip |
| async def test_tool_calls_with_content(self): |
| """Test that agent can handle responses with both tool_calls and content (e.g., GPT-5).""" |
| print("Testing FunctionCall agent with both tool_calls and content...") |
|
|
| |
| agent = FunctionCall( |
| mcp_manager=MCPManager(), |
| llm=ModelManager().build_model(name="openai"), |
| config={"max_iterations": 2, "servers": []} |
| ) |
| await agent.initialize() |
|
|
| |
| class MockToolCall: |
| def __init__(self): |
| self.id = "call_test_123" |
| self.function = Mock() |
| self.function.name = "test_server__get_weather" |
| self.function.arguments = '{"location": "Beijing"}' |
|
|
| class MockMessage: |
| def __init__(self): |
| self.content = "I'll help you get the weather. Let me check the current conditions." |
| self.tool_calls = [MockToolCall()] |
|
|
| class MockChoice: |
| def __init__(self): |
| self.message = MockMessage() |
|
|
| class MockResponse: |
| def __init__(self): |
| self.choices = [MockChoice()] |
|
|
| mock_response = MockResponse() |
| message_obj = mock_response.choices[0].message |
|
|
| |
| has_tool_calls = hasattr(message_obj, 'tool_calls') and message_obj.tool_calls |
| has_content = hasattr(message_obj, 'content') and message_obj.content |
|
|
| print(f"has_tool_calls: {has_tool_calls}") |
| print(f"has_content: {has_content}") |
|
|
| |
| self.assertTrue(has_tool_calls, "Mock should have tool_calls") |
| self.assertTrue(has_content, "Mock should have content") |
|
|
| |
| self.assertIn("I'll help you get the weather", message_obj.content) |
|
|
| |
| self.assertEqual(len(message_obj.tool_calls), 1) |
| self.assertEqual(message_obj.tool_calls[0].function.name, "test_server__get_weather") |
|
|
| |
| |
| agent.clear_history() |
| initial_history = agent.get_history() |
|
|
| |
| if has_tool_calls and has_content: |
| content = message_obj.content.strip() |
| if content: |
| agent._add_history( |
| history_type="thought", |
| message=f"LLM reasoning with tool calls: {content}" |
| ) |
|
|
| |
| final_history = agent.get_history() |
| |
| |
| if isinstance(final_history, str): |
| self.assertIn("LLM reasoning with tool calls", final_history) |
| self.assertIn("I'll help you get the weather", final_history) |
| else: |
| |
| reasoning_found = any("LLM reasoning with tool calls" in str(entry) and "I'll help you get the weather" in str(entry) for entry in final_history) |
| self.assertTrue(reasoning_found, "History should contain the LLM reasoning content") |
|
|
| print("Tool calls with content logic test passed!") |
| print("History updated correctly") |
|
|
| |
| messages = [{"role": "user", "content": "What's the weather in Beijing?"}] |
|
|
| |
| if has_content: |
| content = message_obj.content.strip() |
| messages.append({ |
| "role": "assistant", |
| "content": content |
| }) |
|
|
| |
| self.assertEqual(len(messages), 2) |
| assistant_msg = messages[1] |
| self.assertEqual(assistant_msg["role"], "assistant") |
| self.assertIn("I'll help you get the weather", assistant_msg["content"]) |
|
|
| print("Message structure test passed!") |
| print("All core logic for handling tool_calls + content works correctly!") |
|
|
| await agent.cleanup() |
|
|
|
|
| if __name__ == "__main__": |
| unittest.main() |
|
|