pygeai 0.2.7b51__py3-none-any.whl → 0.2.7b52__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygeai/chat/clients.py +47 -1
- pygeai/chat/endpoints.py +1 -0
- pygeai/cli/commands/chat.py +95 -1
- pygeai/cli/commands/configuration.py +26 -1
- pygeai/cli/commands/lab/ai_lab.py +191 -0
- pygeai/core/common/config.py +11 -0
- pygeai/lab/agents/clients.py +80 -1
- pygeai/lab/agents/endpoints.py +2 -0
- pygeai/lab/models.py +4 -0
- pygeai/lab/tools/clients.py +42 -2
- pygeai/lab/tools/endpoints.py +1 -0
- pygeai/proxy/servers.py +1 -0
- pygeai/proxy/tool.py +6 -3
- pygeai/tests/proxy/__init__.py +1 -0
- pygeai/tests/proxy/test_clients.py +395 -0
- pygeai/tests/proxy/test_config.py +171 -0
- pygeai/tests/proxy/test_integration.py +304 -0
- pygeai/tests/proxy/test_managers.py +312 -0
- pygeai/tests/proxy/test_servers.py +387 -0
- pygeai/tests/proxy/test_tool.py +176 -0
- pygeai/tests/snippets/lab/tools/create_tool.py +1 -2
- {pygeai-0.2.7b51.dist-info → pygeai-0.2.7b52.dist-info}/METADATA +1 -1
- {pygeai-0.2.7b51.dist-info → pygeai-0.2.7b52.dist-info}/RECORD +27 -20
- {pygeai-0.2.7b51.dist-info → pygeai-0.2.7b52.dist-info}/WHEEL +0 -0
- {pygeai-0.2.7b51.dist-info → pygeai-0.2.7b52.dist-info}/entry_points.txt +0 -0
- {pygeai-0.2.7b51.dist-info → pygeai-0.2.7b52.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.2.7b51.dist-info → pygeai-0.2.7b52.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import Mock, patch, MagicMock, AsyncMock
|
|
3
|
+
import asyncio
|
|
4
|
+
import uuid
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
|
|
9
|
+
from pygeai.proxy.config import ProxySettingsManager
|
|
10
|
+
from pygeai.proxy.tool import ProxiedTool
|
|
11
|
+
from pygeai.proxy.clients import ProxyClient, ToolProxyData, ToolProxyJob, ToolProxyJobResult
|
|
12
|
+
from pygeai.proxy.managers import ServerManager
|
|
13
|
+
from pygeai.proxy.servers import A2AServer, MCPServer
|
|
14
|
+
from types import SimpleNamespace
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestProxyIntegration(unittest.IsolatedAsyncioTestCase):
|
|
18
|
+
"""
|
|
19
|
+
python -m unittest pygeai.tests.proxy.test_integration.TestProxyIntegration
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def setUp(self):
|
|
23
|
+
"""Set up test fixtures."""
|
|
24
|
+
self.settings = ProxySettingsManager()
|
|
25
|
+
self.test_uuid = uuid.uuid4()
|
|
26
|
+
self.test_affinity = uuid.uuid4()
|
|
27
|
+
|
|
28
|
+
def test_tool_to_proxy_data_integration(self):
|
|
29
|
+
"""Test integration between ProxiedTool and ToolProxyData."""
|
|
30
|
+
# Create a tool
|
|
31
|
+
tool = ProxiedTool(
|
|
32
|
+
server_name="test_server",
|
|
33
|
+
name="test_tool",
|
|
34
|
+
description="Test tool description",
|
|
35
|
+
public_prefix="public.prefix",
|
|
36
|
+
input_schema={
|
|
37
|
+
"type": "object",
|
|
38
|
+
"properties": {
|
|
39
|
+
"param1": {"type": "string"},
|
|
40
|
+
"param2": {"type": "number"}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Create proxy data with the tool
|
|
46
|
+
proxy_data = ToolProxyData(
|
|
47
|
+
id=self.test_uuid,
|
|
48
|
+
name="Test Proxy",
|
|
49
|
+
description="Test Description",
|
|
50
|
+
affinity=self.test_affinity,
|
|
51
|
+
tools=[tool]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Convert to dictionary
|
|
55
|
+
data_dict = proxy_data.to_dict()
|
|
56
|
+
|
|
57
|
+
# Verify the tool data is correctly included
|
|
58
|
+
self.assertEqual(len(data_dict["tools"]), 1)
|
|
59
|
+
tool_dict = data_dict["tools"][0]
|
|
60
|
+
|
|
61
|
+
self.assertEqual(tool_dict["name"], "test_server__test_tool")
|
|
62
|
+
self.assertEqual(tool_dict["publicName"], "public.prefix.test_server__test_tool")
|
|
63
|
+
self.assertEqual(tool_dict["server"], "test_server")
|
|
64
|
+
self.assertEqual(tool_dict["description"], "Test tool description")
|
|
65
|
+
|
|
66
|
+
# Verify the input schema is correctly formatted
|
|
67
|
+
input_schema = json.loads(tool_dict["inputSchema"])
|
|
68
|
+
self.assertEqual(input_schema["function"]["parameters"]["type"], "object")
|
|
69
|
+
|
|
70
|
+
def test_job_processing_integration(self):
|
|
71
|
+
"""Test integration between job creation and result processing."""
|
|
72
|
+
# Create a job
|
|
73
|
+
job = ToolProxyJob(
|
|
74
|
+
id=uuid.uuid4(),
|
|
75
|
+
proxy_id=self.test_uuid,
|
|
76
|
+
proxy_status="active",
|
|
77
|
+
job_status="pending",
|
|
78
|
+
input=json.dumps({
|
|
79
|
+
"function": {
|
|
80
|
+
"name": "test_server__test_tool",
|
|
81
|
+
"arguments": '{"param1": "value1", "param2": 42}'
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
84
|
+
server="test_server"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Simulate job execution
|
|
88
|
+
job.job_status = "completed"
|
|
89
|
+
job.output = "Execution completed successfully"
|
|
90
|
+
|
|
91
|
+
# Create result
|
|
92
|
+
result = ToolProxyJobResult(success=True, job=job)
|
|
93
|
+
result_dict = result.to_dict()
|
|
94
|
+
|
|
95
|
+
# Verify result structure
|
|
96
|
+
self.assertTrue(result_dict["success"])
|
|
97
|
+
self.assertEqual(result_dict["job"]["jobStatus"], "completed")
|
|
98
|
+
self.assertEqual(result_dict["job"]["output"], "Execution completed successfully")
|
|
99
|
+
|
|
100
|
+
@patch('pygeai.proxy.managers.ProxyClient')
|
|
101
|
+
async def test_server_manager_integration(self, mock_client_class):
|
|
102
|
+
"""Test integration between ServerManager and its components."""
|
|
103
|
+
# Configure settings
|
|
104
|
+
self.settings.set_proxy_id(self.test_uuid)
|
|
105
|
+
self.settings.set_proxy_name("Test Proxy")
|
|
106
|
+
self.settings.set_proxy_description("Test Description")
|
|
107
|
+
self.settings.set_proxy_affinity(self.test_affinity)
|
|
108
|
+
|
|
109
|
+
# Mock settings methods
|
|
110
|
+
self.settings.get_current_alias = Mock(return_value="default")
|
|
111
|
+
self.settings.get_api_key = Mock(return_value="test_api_key")
|
|
112
|
+
self.settings.get_base_url = Mock(return_value="https://api.example.com")
|
|
113
|
+
|
|
114
|
+
# Create server configuration
|
|
115
|
+
servers_cfg = [
|
|
116
|
+
{
|
|
117
|
+
"name": "test_mcp_server",
|
|
118
|
+
"type": "mcp",
|
|
119
|
+
"command": "test_command",
|
|
120
|
+
"args": ["arg1", "arg2"]
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
# Create manager
|
|
125
|
+
manager = ServerManager(servers_cfg, self.settings)
|
|
126
|
+
|
|
127
|
+
# Add a mock server and tool
|
|
128
|
+
tool = ProxiedTool(
|
|
129
|
+
server_name="test_mcp_server",
|
|
130
|
+
name="test_tool",
|
|
131
|
+
description="Test tool",
|
|
132
|
+
public_prefix="public.prefix",
|
|
133
|
+
input_schema={"type": "object"}
|
|
134
|
+
)
|
|
135
|
+
manager.tools["test_mcp_server__test_tool"] = tool
|
|
136
|
+
|
|
137
|
+
# Mock client
|
|
138
|
+
mock_client = Mock()
|
|
139
|
+
mock_client.register.return_value = {"status": "registered"}
|
|
140
|
+
mock_client_class.return_value = mock_client
|
|
141
|
+
|
|
142
|
+
# Add a mock server and tool
|
|
143
|
+
mock_server = AsyncMock()
|
|
144
|
+
mock_server.name = "test_mcp_server"
|
|
145
|
+
mock_server.exit_stack = AsyncMock()
|
|
146
|
+
mock_server.list_tools.return_value = []
|
|
147
|
+
|
|
148
|
+
manager.servers["test_mcp_server"] = mock_server
|
|
149
|
+
|
|
150
|
+
# Test client initialization
|
|
151
|
+
client = await manager._initialize_client()
|
|
152
|
+
|
|
153
|
+
# Verifica que el mock fue llamado
|
|
154
|
+
mock_client.register.assert_called_once()
|
|
155
|
+
self.assertEqual(client, mock_client)
|
|
156
|
+
|
|
157
|
+
# Verify registration data - get the actual arguments passed to register
|
|
158
|
+
call_args = mock_client.register.call_args
|
|
159
|
+
self.assertIsNotNone(call_args)
|
|
160
|
+
|
|
161
|
+
# The register method is called with keyword arguments, so we need to access them properly
|
|
162
|
+
proxy_data = call_args.kwargs.get('proxy_data')
|
|
163
|
+
self.assertIsNotNone(proxy_data)
|
|
164
|
+
self.assertEqual(proxy_data.id, self.test_uuid)
|
|
165
|
+
self.assertEqual(proxy_data.name, "Test Proxy")
|
|
166
|
+
self.assertEqual(proxy_data.description, "Test Description")
|
|
167
|
+
self.assertEqual(proxy_data.affinity, self.test_affinity)
|
|
168
|
+
self.assertEqual(len(proxy_data.tools), 1)
|
|
169
|
+
|
|
170
|
+
@patch('pygeai.proxy.managers.Console')
|
|
171
|
+
async def test_tool_execution_integration(self, mock_console):
|
|
172
|
+
"""Test integration of tool execution flow."""
|
|
173
|
+
# Create manager
|
|
174
|
+
servers_cfg = []
|
|
175
|
+
manager = ServerManager(servers_cfg, self.settings)
|
|
176
|
+
|
|
177
|
+
# Add mock server
|
|
178
|
+
mock_server = AsyncMock()
|
|
179
|
+
mock_server.execute_tool.return_value = "execution_result"
|
|
180
|
+
manager.servers["test_server"] = mock_server
|
|
181
|
+
|
|
182
|
+
# Add tool
|
|
183
|
+
tool = ProxiedTool(
|
|
184
|
+
server_name="test_server",
|
|
185
|
+
name="test_tool",
|
|
186
|
+
description="Test tool",
|
|
187
|
+
public_prefix="public.prefix",
|
|
188
|
+
input_schema={"type": "object"}
|
|
189
|
+
)
|
|
190
|
+
manager.tools["test_server__test_tool"] = tool
|
|
191
|
+
|
|
192
|
+
# Test tool execution
|
|
193
|
+
result = await manager.execute_tool("test_server", "test_server__test_tool", {"param": "value"})
|
|
194
|
+
|
|
195
|
+
# Verify execution
|
|
196
|
+
self.assertEqual(result, "execution_result")
|
|
197
|
+
mock_server.execute_tool.assert_called_once_with("test_tool", {"param": "value"}, 2, 1.0)
|
|
198
|
+
|
|
199
|
+
def test_function_call_parsing_integration(self):
|
|
200
|
+
"""Test integration of function call parsing."""
|
|
201
|
+
# Create manager
|
|
202
|
+
servers_cfg = []
|
|
203
|
+
manager = ServerManager(servers_cfg, self.settings)
|
|
204
|
+
|
|
205
|
+
# Test function call parsing
|
|
206
|
+
function_call_json = json.dumps({
|
|
207
|
+
"function": {
|
|
208
|
+
"name": "test_server__test_tool",
|
|
209
|
+
"arguments": '{"param1": "value1", "param2": 42}'
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
name, arguments = manager.extract_function_call_info(function_call_json)
|
|
214
|
+
|
|
215
|
+
# Verify parsing
|
|
216
|
+
self.assertEqual(name, "test_server__test_tool")
|
|
217
|
+
self.assertEqual(arguments, '{"param1": "value1", "param2": 42}')
|
|
218
|
+
|
|
219
|
+
# Test with parsed arguments
|
|
220
|
+
parsed_args = json.loads(arguments)
|
|
221
|
+
self.assertEqual(parsed_args["param1"], "value1")
|
|
222
|
+
self.assertEqual(parsed_args["param2"], 42)
|
|
223
|
+
|
|
224
|
+
@patch('pygeai.proxy.clients.requests.Session')
|
|
225
|
+
def test_client_request_integration(self, mock_session):
|
|
226
|
+
"""Test integration of client request handling."""
|
|
227
|
+
# Create client
|
|
228
|
+
client = ProxyClient("test_api_key", "https://api.example.com", self.test_uuid)
|
|
229
|
+
|
|
230
|
+
# Mock response
|
|
231
|
+
mock_response = Mock()
|
|
232
|
+
mock_response.json.return_value = {"status": "success", "data": "test_data"}
|
|
233
|
+
mock_response.raise_for_status.return_value = None
|
|
234
|
+
mock_session.return_value.request.return_value = mock_response
|
|
235
|
+
|
|
236
|
+
# Test request
|
|
237
|
+
result = client._make_request("GET", "/test")
|
|
238
|
+
|
|
239
|
+
# Verify request
|
|
240
|
+
self.assertEqual(result, {"status": "success", "data": "test_data"})
|
|
241
|
+
mock_session.return_value.request.assert_called_once()
|
|
242
|
+
|
|
243
|
+
def test_settings_integration(self):
|
|
244
|
+
"""Test integration of settings management."""
|
|
245
|
+
# Set various settings
|
|
246
|
+
self.settings.set_proxy_id(self.test_uuid)
|
|
247
|
+
self.settings.set_proxy_name("Test Proxy")
|
|
248
|
+
self.settings.set_proxy_description("Test Description")
|
|
249
|
+
self.settings.set_proxy_affinity(self.test_affinity)
|
|
250
|
+
|
|
251
|
+
# Get complete configuration
|
|
252
|
+
config = self.settings.get_proxy_config()
|
|
253
|
+
|
|
254
|
+
# Verify configuration
|
|
255
|
+
expected = {
|
|
256
|
+
"id": str(self.test_uuid),
|
|
257
|
+
"name": "Test Proxy",
|
|
258
|
+
"description": "Test Description",
|
|
259
|
+
"affinity": str(self.test_affinity)
|
|
260
|
+
}
|
|
261
|
+
self.assertEqual(config, expected)
|
|
262
|
+
|
|
263
|
+
def test_tool_formatting_integration(self):
|
|
264
|
+
"""Test integration of tool formatting for different scenarios."""
|
|
265
|
+
# Test public tool
|
|
266
|
+
public_tool = ProxiedTool(
|
|
267
|
+
server_name="test_server",
|
|
268
|
+
name="public_tool",
|
|
269
|
+
description="Public tool description",
|
|
270
|
+
public_prefix="public.prefix",
|
|
271
|
+
input_schema={"type": "object"}
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Test private tool
|
|
275
|
+
private_tool = ProxiedTool(
|
|
276
|
+
server_name="test_server",
|
|
277
|
+
name="private_tool",
|
|
278
|
+
description="Private tool description",
|
|
279
|
+
public_prefix=None,
|
|
280
|
+
input_schema={"type": "object"}
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Create proxy data with both tools
|
|
284
|
+
proxy_data = ToolProxyData(
|
|
285
|
+
id=self.test_uuid,
|
|
286
|
+
name="Test Proxy",
|
|
287
|
+
tools=[public_tool, private_tool]
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Convert to dictionary
|
|
291
|
+
data_dict = proxy_data.to_dict()
|
|
292
|
+
|
|
293
|
+
# Verify public tool has publicName
|
|
294
|
+
public_tool_dict = data_dict["tools"][0]
|
|
295
|
+
self.assertIn("publicName", public_tool_dict)
|
|
296
|
+
self.assertEqual(public_tool_dict["publicName"], "public.prefix.test_server__public_tool")
|
|
297
|
+
|
|
298
|
+
# Verify private tool doesn't have publicName
|
|
299
|
+
private_tool_dict = data_dict["tools"][1]
|
|
300
|
+
self.assertNotIn("publicName", private_tool_dict)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
if __name__ == '__main__':
|
|
304
|
+
unittest.main()
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import Mock, patch, MagicMock, AsyncMock
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import uuid
|
|
6
|
+
from pygeai.proxy.managers import ServerManager
|
|
7
|
+
from pygeai.proxy.config import ProxySettingsManager
|
|
8
|
+
from pygeai.proxy.clients import ProxyClient, ToolProxyData, ToolProxyJobResult
|
|
9
|
+
from pygeai.proxy.tool import ProxiedTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestServerManager(unittest.IsolatedAsyncioTestCase):
|
|
13
|
+
"""
|
|
14
|
+
python -m unittest pygeai.tests.proxy.test_managers.TestServerManager
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def setUp(self):
|
|
18
|
+
"""Set up test fixtures."""
|
|
19
|
+
self.servers_cfg = [
|
|
20
|
+
{
|
|
21
|
+
"name": "test_mcp_server",
|
|
22
|
+
"type": "mcp",
|
|
23
|
+
"command": "test_command",
|
|
24
|
+
"args": ["arg1", "arg2"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "test_a2a_server",
|
|
28
|
+
"type": "a2a",
|
|
29
|
+
"url": "https://test.a2a.com",
|
|
30
|
+
"headers": {"Authorization": "Bearer token"}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
self.settings = ProxySettingsManager()
|
|
34
|
+
self.manager = ServerManager(self.servers_cfg, self.settings)
|
|
35
|
+
|
|
36
|
+
def test_initialization(self):
|
|
37
|
+
"""Test manager initialization."""
|
|
38
|
+
self.assertEqual(self.manager.servers_cfg, self.servers_cfg)
|
|
39
|
+
self.assertEqual(self.manager.settings, self.settings)
|
|
40
|
+
self.assertEqual(self.manager.servers, {})
|
|
41
|
+
self.assertEqual(self.manager.tools, {})
|
|
42
|
+
|
|
43
|
+
@patch('pygeai.proxy.managers.MCPServer')
|
|
44
|
+
@patch('pygeai.proxy.managers.A2AServer')
|
|
45
|
+
@patch('pygeai.proxy.managers.Console')
|
|
46
|
+
async def test_initialize_servers_success(self, mock_console, mock_a2a_server_class, mock_mcp_server_class):
|
|
47
|
+
"""Test successful server initialization."""
|
|
48
|
+
# Mock server instances
|
|
49
|
+
mock_mcp_server = AsyncMock()
|
|
50
|
+
mock_mcp_server.name = "test_mcp_server"
|
|
51
|
+
mock_mcp_server.exit_stack = AsyncMock()
|
|
52
|
+
mock_mcp_server.list_tools.return_value = []
|
|
53
|
+
|
|
54
|
+
mock_a2a_server = AsyncMock()
|
|
55
|
+
mock_a2a_server.name = "test_a2a_server"
|
|
56
|
+
mock_a2a_server.exit_stack = AsyncMock()
|
|
57
|
+
mock_a2a_server.list_tools.return_value = []
|
|
58
|
+
|
|
59
|
+
mock_mcp_server_class.return_value = mock_mcp_server
|
|
60
|
+
mock_a2a_server_class.return_value = mock_a2a_server
|
|
61
|
+
|
|
62
|
+
await self.manager._initialize_servers()
|
|
63
|
+
|
|
64
|
+
# Verify servers were created and initialized
|
|
65
|
+
mock_mcp_server_class.assert_called_once_with(
|
|
66
|
+
"test_mcp_server", self.servers_cfg[0], self.settings
|
|
67
|
+
)
|
|
68
|
+
mock_a2a_server_class.assert_called_once_with(
|
|
69
|
+
"test_a2a_server", self.servers_cfg[1], self.settings
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
mock_mcp_server.initialize.assert_called_once()
|
|
73
|
+
mock_a2a_server.initialize.assert_called_once()
|
|
74
|
+
|
|
75
|
+
@patch('pygeai.proxy.managers.MCPServer')
|
|
76
|
+
@patch('pygeai.proxy.managers.Console')
|
|
77
|
+
async def test_initialize_servers_invalid_type(self, mock_console, mock_mcp_server_class):
|
|
78
|
+
"""Test server initialization with invalid server type."""
|
|
79
|
+
invalid_config = [{"name": "invalid_server", "type": "invalid_type"}]
|
|
80
|
+
manager = ServerManager(invalid_config, self.settings)
|
|
81
|
+
|
|
82
|
+
with self.assertRaises(ValueError, msg="Invalid server type: invalid_type"):
|
|
83
|
+
await manager._initialize_servers()
|
|
84
|
+
|
|
85
|
+
@patch('pygeai.proxy.managers.MCPServer')
|
|
86
|
+
@patch('pygeai.proxy.managers.Console')
|
|
87
|
+
async def test_initialize_servers_initialization_error(self, mock_console, mock_mcp_server_class):
|
|
88
|
+
"""Test server initialization error."""
|
|
89
|
+
mock_server = AsyncMock()
|
|
90
|
+
mock_server.name = "test_server"
|
|
91
|
+
mock_server.initialize.side_effect = Exception("Initialization failed")
|
|
92
|
+
mock_mcp_server_class.return_value = mock_server
|
|
93
|
+
|
|
94
|
+
with self.assertRaises(Exception):
|
|
95
|
+
await self.manager._initialize_servers()
|
|
96
|
+
|
|
97
|
+
@patch('pygeai.proxy.managers.ProxyClient')
|
|
98
|
+
@patch('pygeai.proxy.managers.Console')
|
|
99
|
+
async def test_initialize_client_success(self, mock_console, mock_client_class):
|
|
100
|
+
"""Test successful client initialization."""
|
|
101
|
+
# Mock settings methods
|
|
102
|
+
self.settings.get_current_alias = Mock(return_value="default")
|
|
103
|
+
self.settings.get_api_key = Mock(return_value="test_api_key")
|
|
104
|
+
self.settings.get_base_url = Mock(return_value="https://api.example.com")
|
|
105
|
+
self.settings.get_proxy_id = Mock(return_value=uuid.uuid4())
|
|
106
|
+
self.settings.get_proxy_name = Mock(return_value="Test Proxy")
|
|
107
|
+
self.settings.get_proxy_description = Mock(return_value="Test Description")
|
|
108
|
+
self.settings.get_proxy_affinity = Mock(return_value=uuid.uuid4())
|
|
109
|
+
|
|
110
|
+
# Mock client
|
|
111
|
+
mock_client = Mock()
|
|
112
|
+
mock_client.register.return_value = {"status": "success"}
|
|
113
|
+
mock_client_class.return_value = mock_client
|
|
114
|
+
|
|
115
|
+
# Add some tools to the manager
|
|
116
|
+
tool = ProxiedTool(
|
|
117
|
+
server_name="test_server",
|
|
118
|
+
name="test_tool",
|
|
119
|
+
description="Test tool",
|
|
120
|
+
public_prefix="public.prefix",
|
|
121
|
+
input_schema={"type": "object"}
|
|
122
|
+
)
|
|
123
|
+
self.manager.tools["test_server__test_tool"] = tool
|
|
124
|
+
|
|
125
|
+
result = await self.manager._initialize_client()
|
|
126
|
+
|
|
127
|
+
self.assertEqual(result, mock_client)
|
|
128
|
+
mock_client.register.assert_called_once()
|
|
129
|
+
|
|
130
|
+
@patch('pygeai.proxy.managers.ProxyClient')
|
|
131
|
+
@patch('pygeai.proxy.managers.Console')
|
|
132
|
+
async def test_initialize_client_connection_error(self, mock_console, mock_client_class):
|
|
133
|
+
"""Test client initialization with connection error."""
|
|
134
|
+
# Mock settings methods
|
|
135
|
+
self.settings.get_current_alias = Mock(return_value="default")
|
|
136
|
+
self.settings.get_api_key = Mock(return_value="test_api_key")
|
|
137
|
+
self.settings.get_base_url = Mock(return_value="https://api.example.com")
|
|
138
|
+
self.settings.get_proxy_id = Mock(return_value=uuid.uuid4())
|
|
139
|
+
|
|
140
|
+
# Mock client to raise connection error
|
|
141
|
+
mock_client = Mock()
|
|
142
|
+
mock_client.register.side_effect = ConnectionError("Connection failed")
|
|
143
|
+
mock_client_class.return_value = mock_client
|
|
144
|
+
|
|
145
|
+
with self.assertRaises(ConnectionError):
|
|
146
|
+
await self.manager._initialize_client()
|
|
147
|
+
|
|
148
|
+
async def test_execute_tool_server_not_found(self):
|
|
149
|
+
"""Test executing tool with non-existent server."""
|
|
150
|
+
with self.assertRaises(RuntimeError, msg="Server non_existent not found"):
|
|
151
|
+
await self.manager.execute_tool("non_existent", "test_tool", {})
|
|
152
|
+
|
|
153
|
+
async def test_execute_tool_tool_not_found(self):
|
|
154
|
+
"""Test executing non-existent tool."""
|
|
155
|
+
# Add a server but no tools
|
|
156
|
+
self.manager.servers["test_server"] = Mock()
|
|
157
|
+
|
|
158
|
+
with self.assertRaises(RuntimeError, msg="Tool non_existent not found"):
|
|
159
|
+
await self.manager.execute_tool("test_server", "non_existent", {})
|
|
160
|
+
|
|
161
|
+
@patch('pygeai.proxy.managers.Console')
|
|
162
|
+
async def test_execute_tool_success(self, mock_console):
|
|
163
|
+
"""Test successful tool execution."""
|
|
164
|
+
# Mock server
|
|
165
|
+
mock_server = AsyncMock()
|
|
166
|
+
mock_server.execute_tool.return_value = "success"
|
|
167
|
+
self.manager.servers["test_server"] = mock_server
|
|
168
|
+
|
|
169
|
+
# Add tool
|
|
170
|
+
tool = ProxiedTool(
|
|
171
|
+
server_name="test_server",
|
|
172
|
+
name="test_tool",
|
|
173
|
+
description="Test tool",
|
|
174
|
+
public_prefix="public.prefix",
|
|
175
|
+
input_schema={"type": "object"}
|
|
176
|
+
)
|
|
177
|
+
self.manager.tools["test_server__test_tool"] = tool
|
|
178
|
+
|
|
179
|
+
result = await self.manager.execute_tool("test_server", "test_server__test_tool", {"param": "value"})
|
|
180
|
+
|
|
181
|
+
self.assertEqual(result, "success")
|
|
182
|
+
mock_server.execute_tool.assert_called_once_with("test_tool", {"param": "value"}, 2, 1.0)
|
|
183
|
+
|
|
184
|
+
@patch('pygeai.proxy.managers.Console')
|
|
185
|
+
async def test_execute_tool_with_custom_retries(self, mock_console):
|
|
186
|
+
"""Test tool execution with custom retry parameters."""
|
|
187
|
+
mock_server = AsyncMock()
|
|
188
|
+
mock_server.execute_tool.return_value = "success"
|
|
189
|
+
self.manager.servers["test_server"] = mock_server
|
|
190
|
+
|
|
191
|
+
tool = ProxiedTool(
|
|
192
|
+
server_name="test_server",
|
|
193
|
+
name="test_tool",
|
|
194
|
+
description="Test tool",
|
|
195
|
+
public_prefix="public.prefix",
|
|
196
|
+
input_schema={"type": "object"}
|
|
197
|
+
)
|
|
198
|
+
self.manager.tools["test_server__test_tool"] = tool
|
|
199
|
+
|
|
200
|
+
await self.manager.execute_tool("test_server", "test_server__test_tool", {}, retries=5, delay=2.0)
|
|
201
|
+
|
|
202
|
+
mock_server.execute_tool.assert_called_once_with("test_tool", {}, 5, 2.0)
|
|
203
|
+
|
|
204
|
+
@patch('pygeai.proxy.managers.Console')
|
|
205
|
+
async def test_execute_tool_execution_error(self, mock_console):
|
|
206
|
+
"""Test tool execution with error."""
|
|
207
|
+
mock_server = AsyncMock()
|
|
208
|
+
mock_server.execute_tool.side_effect = RuntimeError("Tool execution failed")
|
|
209
|
+
self.manager.servers["test_server"] = mock_server
|
|
210
|
+
|
|
211
|
+
tool = ProxiedTool(
|
|
212
|
+
server_name="test_server",
|
|
213
|
+
name="test_tool",
|
|
214
|
+
description="Test tool",
|
|
215
|
+
public_prefix="public.prefix",
|
|
216
|
+
input_schema={"type": "object"}
|
|
217
|
+
)
|
|
218
|
+
self.manager.tools["test_server__test_tool"] = tool
|
|
219
|
+
|
|
220
|
+
with self.assertRaises(Exception, msg="Failed to execute tool test_server__test_tool on server test_server: Tool execution failed"):
|
|
221
|
+
await self.manager.execute_tool("test_server", "test_server__test_tool", {})
|
|
222
|
+
|
|
223
|
+
def test_extract_function_call_info_success(self):
|
|
224
|
+
"""Test successful function call info extraction."""
|
|
225
|
+
function_call_json = json.dumps({
|
|
226
|
+
"function": {
|
|
227
|
+
"name": "test_function",
|
|
228
|
+
"arguments": '{"param": "value"}'
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
name, arguments = self.manager.extract_function_call_info(function_call_json)
|
|
233
|
+
|
|
234
|
+
self.assertEqual(name, "test_function")
|
|
235
|
+
self.assertEqual(arguments, '{"param": "value"}')
|
|
236
|
+
|
|
237
|
+
def test_extract_function_call_info_invalid_json(self):
|
|
238
|
+
"""Test function call info extraction with invalid JSON."""
|
|
239
|
+
name, arguments = self.manager.extract_function_call_info("invalid json")
|
|
240
|
+
|
|
241
|
+
self.assertIsNone(name)
|
|
242
|
+
self.assertIsNone(arguments)
|
|
243
|
+
|
|
244
|
+
def test_extract_function_call_info_missing_keys(self):
|
|
245
|
+
"""Test function call info extraction with missing keys."""
|
|
246
|
+
function_call_json = json.dumps({"other_key": "value"})
|
|
247
|
+
|
|
248
|
+
name, arguments = self.manager.extract_function_call_info(function_call_json)
|
|
249
|
+
|
|
250
|
+
self.assertIsNone(name)
|
|
251
|
+
self.assertIsNone(arguments)
|
|
252
|
+
|
|
253
|
+
@patch('pygeai.proxy.managers.ServerManager._initialize_servers')
|
|
254
|
+
@patch('pygeai.proxy.managers.ServerManager._initialize_client')
|
|
255
|
+
@patch('pygeai.proxy.managers.Console')
|
|
256
|
+
async def test_start_success(self, mock_console, mock_init_client, mock_init_servers):
|
|
257
|
+
"""Test successful start method."""
|
|
258
|
+
# Mock client
|
|
259
|
+
mock_client = Mock()
|
|
260
|
+
mock_client.dequeue.return_value = []
|
|
261
|
+
mock_init_client.return_value = mock_client
|
|
262
|
+
|
|
263
|
+
# Mock settings
|
|
264
|
+
self.settings.get_current_alias = Mock(return_value="default")
|
|
265
|
+
self.settings.get_proxy_id = Mock(return_value=uuid.uuid4())
|
|
266
|
+
|
|
267
|
+
# Create a task to run start method
|
|
268
|
+
task = asyncio.create_task(self.manager.start())
|
|
269
|
+
|
|
270
|
+
# Let it run for a short time
|
|
271
|
+
await asyncio.sleep(0.1)
|
|
272
|
+
|
|
273
|
+
# Cancel the task
|
|
274
|
+
task.cancel()
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
await task
|
|
278
|
+
except asyncio.CancelledError:
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
# Verify initialization was called
|
|
282
|
+
mock_init_servers.assert_called_once()
|
|
283
|
+
mock_init_client.assert_called_once()
|
|
284
|
+
|
|
285
|
+
@patch('pygeai.proxy.managers.ServerManager._initialize_servers')
|
|
286
|
+
@patch('pygeai.proxy.managers.ServerManager._initialize_client')
|
|
287
|
+
@patch('pygeai.proxy.managers.Console')
|
|
288
|
+
async def test_start_client_initialization_error(self, mock_console, mock_init_client, mock_init_servers):
|
|
289
|
+
"""Test start method with client initialization error."""
|
|
290
|
+
mock_init_client.side_effect = ConnectionError("Connection failed")
|
|
291
|
+
|
|
292
|
+
# Create a task to run start method
|
|
293
|
+
task = asyncio.create_task(self.manager.start())
|
|
294
|
+
|
|
295
|
+
# Let it run for a short time
|
|
296
|
+
await asyncio.sleep(0.1)
|
|
297
|
+
|
|
298
|
+
# Cancel the task
|
|
299
|
+
task.cancel()
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
await task
|
|
303
|
+
except asyncio.CancelledError:
|
|
304
|
+
pass
|
|
305
|
+
|
|
306
|
+
# Verify initialization was called
|
|
307
|
+
mock_init_servers.assert_called_once()
|
|
308
|
+
mock_init_client.assert_called_once()
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
if __name__ == '__main__':
|
|
312
|
+
unittest.main()
|