Skip to content

Commit 45aff3c

Browse files
authored
♻️ Improvement: Now ModelEngine interface won't wait till timeout while related environment variables are not set
2 parents f0c528c + bd15541 commit 45aff3c

File tree

4 files changed

+198
-123
lines changed

4 files changed

+198
-123
lines changed

backend/apps/me_model_managment_app.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from fastapi.responses import JSONResponse
66

77
from consts.exceptions import TimeoutException, NotFoundException, MEConnectionException
8-
from consts.model import ModelConnectStatusEnum
9-
from services.me_model_management_service import get_me_models_impl
8+
from services.me_model_management_service import get_me_models_impl, check_me_variable_set
109
from services.model_health_service import check_me_connectivity_impl
1110

1211
router = APIRouter(prefix="/me")
@@ -23,6 +22,15 @@ async def get_me_models(
2322
Get list of models from model engine API
2423
"""
2524
try:
25+
# Pre-check ME environment variables; return empty list if not configured
26+
if not await check_me_variable_set():
27+
return JSONResponse(
28+
status_code=HTTPStatus.OK,
29+
content={
30+
"message": "Retrieve skipped",
31+
"data": []
32+
}
33+
)
2634
filtered_result = await get_me_models_impl(timeout=timeout, type=type)
2735
return JSONResponse(
2836
status_code=HTTPStatus.OK,
@@ -48,12 +56,21 @@ async def check_me_connectivity(timeout: int = Query(default=2, description="Tim
4856
Health check from model engine API
4957
"""
5058
try:
59+
# Pre-check ME environment variables; return not connected if not configured
60+
if not await check_me_variable_set():
61+
return JSONResponse(
62+
status_code=HTTPStatus.OK,
63+
content={
64+
"connectivity": False,
65+
"message": "ModelEngine platform necessary environment variables not configured. Healthcheck skipped.",
66+
}
67+
)
5168
await check_me_connectivity_impl(timeout)
5269
return JSONResponse(
5370
status_code=HTTPStatus.OK,
5471
content={
5572
"connectivity": True,
56-
"message": "ModelEngine model connect successfully.",
73+
"message": "ModelEngine platform connect successfully.",
5774
}
5875
)
5976
except MEConnectionException as e:

backend/services/me_model_management_service.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,12 @@ async def get_me_models_impl(timeout: int = 2, type: str = "") -> List:
5050
raise TimeoutException("Request timeout.")
5151
except Exception as e:
5252
raise Exception(f"Failed to get model list: {str(e)}.")
53+
54+
55+
async def check_me_variable_set() -> bool:
56+
"""
57+
Check if the ME environment variables are correctly set.
58+
Returns:
59+
bool: True if both MODEL_ENGINE_APIKEY and MODEL_ENGINE_HOST are set and non-empty, False otherwise.
60+
"""
61+
return bool(MODEL_ENGINE_APIKEY and MODEL_ENGINE_HOST)

test/backend/app/test_me_model_managment_app.py

Lines changed: 89 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,26 @@ async def mock_list_models_with_filter(type: str = Query(None)):
193193
assert response_data["data"][0]["name"] == "model2"
194194

195195

196+
@pytest.mark.asyncio
197+
async def test_get_me_models_env_not_configured_returns_skip_message_and_empty_list():
198+
"""When ME env not configured, endpoint returns 200 with skip message and empty data."""
199+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=False)):
200+
response = client.get("/me/model/list")
201+
202+
assert response.status_code == HTTPStatus.OK
203+
data = response.json()
204+
assert data["message"] == "Retrieve skipped"
205+
assert data["data"] == []
206+
207+
196208
@pytest.mark.asyncio
197209
async def test_get_me_models_not_found_filter():
198210
# Patch the service impl to raise NotFoundException so the route returns 404
199-
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
200-
mock_impl.side_effect = NotFoundException(
201-
"No models found with type 'nonexistent'.")
202-
response = client.get("/me/model/list?type=nonexistent")
211+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
212+
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
213+
mock_impl.side_effect = NotFoundException(
214+
"No models found with type 'nonexistent'.")
215+
response = client.get("/me/model/list?type=nonexistent")
203216

204217
# Assertions - route maps NotFoundException -> 404 and raises HTTPException with detail
205218
assert response.status_code == HTTPStatus.NOT_FOUND
@@ -211,10 +224,11 @@ async def test_get_me_models_not_found_filter():
211224
async def test_get_me_models_timeout():
212225
"""Test model list retrieval with timeout via real route"""
213226
# Patch service to raise TimeoutException so the real route returns 408
214-
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
215-
mock_impl.side_effect = TimeoutException("Request timeout.")
227+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
228+
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
229+
mock_impl.side_effect = TimeoutException("Request timeout.")
216230

217-
response = client.get("/me/model/list")
231+
response = client.get("/me/model/list")
218232

219233
assert response.status_code == HTTPStatus.REQUEST_TIMEOUT
220234
body = response.json()
@@ -224,9 +238,10 @@ async def test_get_me_models_timeout():
224238
@pytest.mark.asyncio
225239
async def test_get_me_models_exception():
226240
"""Test model list retrieval with generic exception"""
227-
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
228-
mock_impl.side_effect = Exception("boom")
229-
response = client.get("/me/model/list")
241+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
242+
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
243+
mock_impl.side_effect = Exception("boom")
244+
response = client.get("/me/model/list")
230245

231246
# Assertions
232247
assert response.status_code == 500
@@ -238,14 +253,15 @@ async def test_get_me_models_exception():
238253
async def test_get_me_models_success_response():
239254
"""Test successful model list retrieval with proper JSONResponse format"""
240255
# Mock the service implementation to return test data
241-
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
242-
mock_impl.return_value = [
243-
{"name": "model1", "type": "embed", "version": "1.0"},
244-
{"name": "model2", "type": "chat", "version": "1.0"}
245-
]
256+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
257+
with patch('backend.apps.me_model_managment_app.get_me_models_impl') as mock_impl:
258+
mock_impl.return_value = [
259+
{"name": "model1", "type": "embed", "version": "1.0"},
260+
{"name": "model2", "type": "chat", "version": "1.0"}
261+
]
246262

247-
# Test the endpoint
248-
response = client.get("/me/model/list")
263+
# Test the endpoint
264+
response = client.get("/me/model/list")
249265

250266
# Assertions
251267
assert response.status_code == HTTPStatus.OK
@@ -258,39 +274,69 @@ async def test_get_me_models_success_response():
258274
assert len(response_data["data"]) == 2
259275

260276

277+
@pytest.mark.asyncio
278+
async def test_check_me_connectivity_env_not_configured_returns_skip_message():
279+
"""When ME env not configured, healthcheck returns connectivity False and skip message."""
280+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=False)):
281+
response = client.get("/me/healthcheck")
282+
283+
assert response.status_code == HTTPStatus.OK
284+
body = response.json()
285+
assert body["connectivity"] is False
286+
assert body["message"] == "ModelEngine platform necessary environment variables not configured. Healthcheck skipped."
287+
288+
261289
@pytest.mark.asyncio
262290
async def test_check_me_connectivity_success():
263291
"""Test successful ME connectivity check"""
264292
# Mock the check_me_connectivity_impl function in the app module
265-
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
266-
mock_connectivity.return_value = (
267-
HTTPStatus.OK,
268-
"Connection successful",
269-
{
270-
"status": "Connected",
271-
"desc": "Connection successful",
272-
"connect_status": "available"
273-
}
274-
)
293+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
294+
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
295+
mock_connectivity.return_value = (
296+
HTTPStatus.OK,
297+
"Connection successful",
298+
{
299+
"status": "Connected",
300+
"desc": "Connection successful",
301+
"connect_status": "available"
302+
}
303+
)
275304

276-
# Test with TestClient
277-
response = client.get("/me/healthcheck")
305+
# Test with TestClient
306+
response = client.get("/me/healthcheck")
278307

279308
# Assertions
280309
assert response.status_code == 200
281310
response_data = response.json()
282311
assert response_data["connectivity"]
312+
# Updated success message string
313+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
314+
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity2:
315+
mock_connectivity2.return_value = (
316+
HTTPStatus.OK,
317+
"Connection successful",
318+
{
319+
"status": "Connected",
320+
"desc": "Connection successful",
321+
"connect_status": "available"
322+
}
323+
)
324+
response2 = client.get("/me/healthcheck")
325+
assert response2.status_code == 200
326+
assert response2.json()[
327+
"message"] == "ModelEngine platform connect successfully."
283328

284329

285330
@pytest.mark.asyncio
286331
async def test_check_me_connectivity_failure():
287332
"""Trigger MEConnectionException to simulate connectivity failure"""
288333
# Patch the impl to raise MEConnectionException so the route returns 503
289-
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
290-
mock_connectivity.side_effect = MEConnectionException(
291-
"Downstream 404 or similar")
334+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
335+
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
336+
mock_connectivity.side_effect = MEConnectionException(
337+
"Downstream 404 or similar")
292338

293-
response = client.get("/me/healthcheck")
339+
response = client.get("/me/healthcheck")
294340

295341
assert response.status_code == HTTPStatus.SERVICE_UNAVAILABLE
296342

@@ -299,10 +345,12 @@ async def test_check_me_connectivity_failure():
299345
async def test_check_me_connectivity_timeout():
300346
"""Test ME connectivity check with timeout"""
301347
# Mock the impl to raise TimeoutException so the route returns 408
302-
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
303-
mock_connectivity.side_effect = TimeoutException("timeout simulated")
348+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
349+
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
350+
mock_connectivity.side_effect = TimeoutException(
351+
"timeout simulated")
304352

305-
response = client.get("/me/healthcheck")
353+
response = client.get("/me/healthcheck")
306354

307355
# Assertions - route maps TimeoutException -> 408 and returns status/desc/connect_status
308356
assert response.status_code == HTTPStatus.REQUEST_TIMEOUT
@@ -312,10 +360,12 @@ async def test_check_me_connectivity_timeout():
312360
async def test_check_me_connectivity_generic_exception():
313361
"""Test ME connectivity check with generic exception"""
314362
# Mock the impl to raise a generic Exception so the route returns 500
315-
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
316-
mock_connectivity.side_effect = Exception("Unexpected error occurred")
363+
with patch('backend.apps.me_model_managment_app.check_me_variable_set', AsyncMock(return_value=True)):
364+
with patch('backend.apps.me_model_managment_app.check_me_connectivity_impl') as mock_connectivity:
365+
mock_connectivity.side_effect = Exception(
366+
"Unexpected error occurred")
317367

318-
response = client.get("/me/healthcheck")
368+
response = client.get("/me/healthcheck")
319369

320370
# Assertions - route maps generic Exception -> 500 and returns status/desc/connect_status
321371
assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR

0 commit comments

Comments
 (0)