|
1 | 1 | import json |
2 | 2 | import os |
3 | 3 | import sys |
4 | | -from unittest.mock import AsyncMock, MagicMock, mock_open, patch |
| 4 | +from unittest.mock import MagicMock, mock_open, patch |
5 | 5 |
|
6 | 6 | import pytest |
7 | 7 |
|
8 | 8 | sys.path.append(os.path.join(os.path.dirname(__file__), "..")) |
9 | 9 |
|
10 | 10 | from src.opengradient.client import Client |
11 | 11 | from src.opengradient.types import ( |
12 | | - TEE_LLM, |
13 | 12 | StreamChunk, |
14 | 13 | x402SettlementMode, |
15 | 14 | ) |
16 | 15 |
|
17 | 16 |
|
18 | | -def _mock_http_response(body: dict, status_code: int = 200) -> AsyncMock: |
19 | | - """Create a mock httpx response with the given JSON body.""" |
20 | | - response = AsyncMock() |
21 | | - response.status_code = status_code |
22 | | - response.aread = AsyncMock(return_value=json.dumps(body).encode()) |
23 | | - response.raise_for_status = MagicMock() |
24 | | - return response |
25 | | - |
26 | | - |
27 | 17 | # --- Fixtures --- |
28 | 18 |
|
29 | 19 |
|
@@ -71,17 +61,6 @@ def mock_file_open(path, *args, **kwargs): |
71 | 61 | yield |
72 | 62 |
|
73 | 63 |
|
74 | | -@pytest.fixture |
75 | | -def client(mock_web3, mock_abi_files): |
76 | | - """Create a Client instance with mocked dependencies.""" |
77 | | - return Client( |
78 | | - private_key="0x" + "a" * 64, |
79 | | - rpc_url="https://test.rpc.url", |
80 | | - api_url="https://test.api.url", |
81 | | - contract_address="0x" + "b" * 40, |
82 | | - ) |
83 | | - |
84 | | - |
85 | 64 | # --- Client Initialization Tests --- |
86 | 65 |
|
87 | 66 |
|
@@ -137,16 +116,6 @@ def test_client_initialization_custom_llm_urls(self, mock_web3, mock_abi_files): |
137 | 116 | assert client.llm._tee_endpoint == custom_llm_url |
138 | 117 |
|
139 | 118 |
|
140 | | -class TestAlphaProperty: |
141 | | - def test_alpha_initialized_on_client_creation(self, client): |
142 | | - """Test that alpha is initialized during client creation.""" |
143 | | - assert client.alpha is not None |
144 | | - |
145 | | - def test_alpha_has_infer_method(self, client): |
146 | | - """Test that alpha namespace has the infer method.""" |
147 | | - assert hasattr(client.alpha, "infer") |
148 | | - |
149 | | - |
150 | 119 | # --- Authentication Tests --- |
151 | 120 |
|
152 | 121 |
|
@@ -197,166 +166,6 @@ def test_login_to_hub_failure(self, mock_web3, mock_abi_files): |
197 | 166 | ) |
198 | 167 |
|
199 | 168 |
|
200 | | -# --- LLM Tests --- |
201 | | - |
202 | | - |
203 | | -@pytest.mark.asyncio |
204 | | -class TestLLMCompletion: |
205 | | - async def test_llm_completion_success(self, client): |
206 | | - """Test successful LLM completion.""" |
207 | | - response = _mock_http_response( |
208 | | - { |
209 | | - "completion": "Hello! How can I help?", |
210 | | - "tee_signature": "sig123", |
211 | | - "tee_timestamp": "2025-01-01T00:00:00Z", |
212 | | - } |
213 | | - ) |
214 | | - client.llm._http_client.post = AsyncMock(return_value=response) |
215 | | - |
216 | | - result = await client.llm.completion( |
217 | | - model=TEE_LLM.GPT_5, |
218 | | - prompt="Hello", |
219 | | - max_tokens=100, |
220 | | - ) |
221 | | - |
222 | | - assert result.completion_output == "Hello! How can I help?" |
223 | | - assert result.tee_signature == "sig123" |
224 | | - assert result.transaction_hash == "external" |
225 | | - client.llm._http_client.post.assert_called_once() |
226 | | - |
227 | | - async def test_llm_completion_includes_stop_sequence(self, client): |
228 | | - """Test that stop sequences are included in the request payload.""" |
229 | | - response = _mock_http_response({"completion": "Hello"}) |
230 | | - client.llm._http_client.post = AsyncMock(return_value=response) |
231 | | - |
232 | | - await client.llm.completion( |
233 | | - model=TEE_LLM.GPT_5, |
234 | | - prompt="Hello", |
235 | | - stop_sequence=["END"], |
236 | | - ) |
237 | | - |
238 | | - call_kwargs = client.llm._http_client.post.call_args |
239 | | - payload = call_kwargs.kwargs.get("json") or call_kwargs[1].get("json") |
240 | | - assert payload["stop"] == ["END"] |
241 | | - |
242 | | - |
243 | | -@pytest.mark.asyncio |
244 | | -class TestLLMChat: |
245 | | - async def test_llm_chat_success_non_streaming(self, client): |
246 | | - """Test successful non-streaming LLM chat.""" |
247 | | - response = _mock_http_response( |
248 | | - { |
249 | | - "choices": [ |
250 | | - { |
251 | | - "message": {"role": "assistant", "content": "Hi there!"}, |
252 | | - "finish_reason": "stop", |
253 | | - } |
254 | | - ], |
255 | | - "tee_signature": "sig456", |
256 | | - } |
257 | | - ) |
258 | | - client.llm._http_client.post = AsyncMock(return_value=response) |
259 | | - |
260 | | - result = await client.llm.chat( |
261 | | - model=TEE_LLM.GPT_5, |
262 | | - messages=[{"role": "user", "content": "Hello"}], |
263 | | - stream=False, |
264 | | - ) |
265 | | - |
266 | | - assert result.chat_output["content"] == "Hi there!" |
267 | | - assert result.finish_reason == "stop" |
268 | | - client.llm._http_client.post.assert_called_once() |
269 | | - |
270 | | - async def test_llm_chat_flattens_content_blocks(self, client): |
271 | | - """Test that list-type content blocks are flattened to a string.""" |
272 | | - response = _mock_http_response( |
273 | | - { |
274 | | - "choices": [ |
275 | | - { |
276 | | - "message": { |
277 | | - "role": "assistant", |
278 | | - "content": [ |
279 | | - {"type": "text", "text": "Hello"}, |
280 | | - {"type": "text", "text": "world"}, |
281 | | - ], |
282 | | - }, |
283 | | - "finish_reason": "stop", |
284 | | - } |
285 | | - ], |
286 | | - } |
287 | | - ) |
288 | | - client.llm._http_client.post = AsyncMock(return_value=response) |
289 | | - |
290 | | - result = await client.llm.chat( |
291 | | - model=TEE_LLM.GPT_5, |
292 | | - messages=[{"role": "user", "content": "Hi"}], |
293 | | - ) |
294 | | - |
295 | | - assert result.chat_output["content"] == "Hello world" |
296 | | - |
297 | | - async def test_llm_chat_with_tools(self, client): |
298 | | - """Test that tools are included in the request payload.""" |
299 | | - tools = [{"type": "function", "function": {"name": "get_weather"}}] |
300 | | - response = _mock_http_response( |
301 | | - { |
302 | | - "choices": [ |
303 | | - { |
304 | | - "message": {"role": "assistant", "content": None, "tool_calls": [{"id": "1"}]}, |
305 | | - "finish_reason": "tool_calls", |
306 | | - } |
307 | | - ], |
308 | | - } |
309 | | - ) |
310 | | - client.llm._http_client.post = AsyncMock(return_value=response) |
311 | | - |
312 | | - result = await client.llm.chat( |
313 | | - model=TEE_LLM.GPT_5, |
314 | | - messages=[{"role": "user", "content": "Weather?"}], |
315 | | - tools=tools, |
316 | | - ) |
317 | | - |
318 | | - call_kwargs = client.llm._http_client.post.call_args |
319 | | - payload = call_kwargs.kwargs.get("json") or call_kwargs[1].get("json") |
320 | | - assert payload["tools"] == tools |
321 | | - assert payload["tool_choice"] == "auto" |
322 | | - assert result.chat_output["tool_calls"] == [{"id": "1"}] |
323 | | - |
324 | | - async def test_llm_chat_streaming(self, client): |
325 | | - """Test streaming LLM chat via SSE.""" |
326 | | - sse_lines = ( |
327 | | - b'data: {"model":"gpt-5","choices":[{"index":0,"delta":{"role":"assistant","content":"Hi"},"finish_reason":null}]}\n\n' |
328 | | - b'data: {"model":"gpt-5","choices":[{"index":0,"delta":{"content":" there"},"finish_reason":"stop"}],"tee_signature":"sig"}\n\n' |
329 | | - b"data: [DONE]\n\n" |
330 | | - ) |
331 | | - |
332 | | - mock_response = AsyncMock() |
333 | | - mock_response.status_code = 200 |
334 | | - |
335 | | - async def aiter_raw(): |
336 | | - yield sse_lines |
337 | | - |
338 | | - mock_response.aiter_raw = aiter_raw |
339 | | - |
340 | | - stream_ctx = AsyncMock() |
341 | | - stream_ctx.__aenter__ = AsyncMock(return_value=mock_response) |
342 | | - stream_ctx.__aexit__ = AsyncMock(return_value=False) |
343 | | - client.llm._http_client.stream = MagicMock(return_value=stream_ctx) |
344 | | - |
345 | | - result = await client.llm.chat( |
346 | | - model=TEE_LLM.GPT_5, |
347 | | - messages=[{"role": "user", "content": "Hello"}], |
348 | | - stream=True, |
349 | | - ) |
350 | | - |
351 | | - chunks = [] |
352 | | - async for chunk in result: |
353 | | - chunks.append(chunk) |
354 | | - assert len(chunks) == 2 |
355 | | - assert chunks[0].choices[0].delta.content == "Hi" |
356 | | - assert chunks[1].choices[0].delta.content == " there" |
357 | | - assert chunks[1].choices[0].finish_reason == "stop" |
358 | | - |
359 | | - |
360 | 169 | # --- StreamChunk Tests --- |
361 | 170 |
|
362 | 171 |
|
|
0 commit comments