Skip to content

Commit 6dc7498

Browse files
authored
⭐️ ModelEngine Models Interrogation
2 parents a6a8886 + 9d654be commit 6dc7498

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1748
-1310
lines changed

backend/agents/create_agent_info.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ async def create_model_config_list(tenant_id):
4343
model_repo=record["model_repo"],
4444
model_name=record["model_name"],
4545
),
46-
url=record["base_url"]))
46+
url=record["base_url"],
47+
ssl_verify=record.get("ssl_verify", True)))
4748
# fit for old version, main_model and sub_model use default model
4849
main_model_config = tenant_config_manager.get_model_config(
4950
key=MODEL_CONFIG_MAPPING["llm"], tenant_id=tenant_id)
@@ -52,13 +53,15 @@ async def create_model_config_list(tenant_id):
5253
api_key=main_model_config.get("api_key", ""),
5354
model_name=get_model_name_from_config(main_model_config) if main_model_config.get(
5455
"model_name") else "",
55-
url=main_model_config.get("base_url", "")))
56+
url=main_model_config.get("base_url", ""),
57+
ssl_verify=main_model_config.get("ssl_verify", True)))
5658
model_list.append(
5759
ModelConfig(cite_name="sub_model",
5860
api_key=main_model_config.get("api_key", ""),
5961
model_name=get_model_name_from_config(main_model_config) if main_model_config.get(
6062
"model_name") else "",
61-
url=main_model_config.get("base_url", "")))
63+
url=main_model_config.get("base_url", ""),
64+
ssl_verify=main_model_config.get("ssl_verify", True)))
6265

6366
return model_list
6467

backend/apps/me_model_managment_app.py

Lines changed: 17 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,44 @@
44
from fastapi import APIRouter, Query, HTTPException
55
from fastapi.responses import JSONResponse
66

7-
from consts.exceptions import TimeoutException, NotFoundException, MEConnectionException
8-
from services.me_model_management_service import get_me_models_impl, check_me_variable_set
9-
from services.model_health_service import check_me_connectivity_impl
7+
from consts.exceptions import MEConnectionException, TimeoutException
8+
from services.me_model_management_service import check_me_variable_set, check_me_connectivity
109

1110
router = APIRouter(prefix="/me")
1211

1312

14-
@router.get("/model/list")
15-
async def get_me_models(
16-
type: str = Query(
17-
default="", description="Model type: embed/chat/rerank"),
18-
timeout: int = Query(
19-
default=2, description="Request timeout in seconds")
20-
):
21-
"""
22-
Get list of models from model engine API
23-
"""
24-
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-
)
34-
filtered_result = await get_me_models_impl(timeout=timeout, type=type)
35-
return JSONResponse(
36-
status_code=HTTPStatus.OK,
37-
content={
38-
"message": "Successfully retrieved",
39-
"data": filtered_result
40-
}
41-
)
42-
except TimeoutException as e:
43-
logging.error(f"Request me model timeout: {str(e)}")
44-
raise HTTPException(status_code=HTTPStatus.REQUEST_TIMEOUT, detail="Failed to get ModelEngine model list: timeout")
45-
except NotFoundException as e:
46-
logging.error(f"Request me model not found: {str(e)}")
47-
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="ModelEngine model not found")
48-
except Exception as e:
49-
logging.error(f"Failed to get me model list: {str(e)}")
50-
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Failed to get ModelEngine model list")
51-
52-
5313
@router.get("/healthcheck")
54-
async def check_me_connectivity(timeout: int = Query(default=2, description="Timeout in seconds")):
14+
async def check_me_health(timeout: int = Query(default=30, description="Timeout in seconds")):
5515
"""
56-
Health check from model engine API
16+
Health check for ModelEngine platform by actually calling the API.
17+
Returns connectivity status based on actual API response.
5718
"""
5819
try:
59-
# Pre-check ME environment variables; return not connected if not configured
20+
# First check if environment variables are configured
6021
if not await check_me_variable_set():
6122
return JSONResponse(
6223
status_code=HTTPStatus.OK,
6324
content={
6425
"connectivity": False,
65-
"message": "ModelEngine platform necessary environment variables not configured. Healthcheck skipped.",
26+
"message": "ModelEngine platform environment variables not configured. Healthcheck skipped.",
6627
}
6728
)
68-
await check_me_connectivity_impl(timeout)
29+
30+
# Then check actual connectivity
31+
await check_me_connectivity(timeout)
6932
return JSONResponse(
7033
status_code=HTTPStatus.OK,
7134
content={
7235
"connectivity": True,
73-
"message": "ModelEngine platform connect successfully.",
36+
"message": "ModelEngine platform connected successfully.",
7437
}
7538
)
7639
except MEConnectionException as e:
77-
logging.error(f"ModelEngine model healthcheck failed: {str(e)}")
78-
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail="ModelEngine model connect failed.")
40+
logging.error(f"ModelEngine healthcheck failed: {str(e)}")
41+
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=f"ModelEngine connection failed: {str(e)}")
7942
except TimeoutException as e:
80-
logging.error(f"ModelEngine model healthcheck timeout: {str(e)}")
81-
raise HTTPException(status_code=HTTPStatus.REQUEST_TIMEOUT, detail="ModelEngine model connect timeout.")
43+
logging.error(f"ModelEngine healthcheck timeout: {str(e)}")
44+
raise HTTPException(status_code=HTTPStatus.REQUEST_TIMEOUT, detail="ModelEngine connection timeout.")
8245
except Exception as e:
83-
logging.error(f"ModelEngine model healthcheck failed with unknown error: {str(e)}.")
84-
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="ModelEngine model connect failed.")
46+
logging.error(f"ModelEngine healthcheck failed with unknown error: {str(e)}")
47+
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=f"ModelEngine healthcheck failed: {str(e)}")

backend/consts/provider.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ class ProviderEnum(str, Enum):
55
"""Supported model providers"""
66
SILICON = "silicon"
77
OPENAI = "openai"
8+
MODELENGINE = "modelengine"
89

910

1011
# Silicon Flow
1112
SILICON_BASE_URL = "https://api.siliconflow.cn/v1/"
1213
SILICON_GET_URL = "https://api.siliconflow.cn/v1/models"
14+
15+
# ModelEngine
16+
# Base URL and API key are loaded from environment variables at runtime

backend/database/db_models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ class ModelRecord(TableBase):
160160
Integer, doc="Expected chunk size for embedding models, used during document chunking")
161161
maximum_chunk_size = Column(
162162
Integer, doc="Maximum chunk size for embedding models, used during document chunking")
163+
ssl_verify = Column(
164+
Boolean, default=True, doc="Whether to verify SSL certificates when connecting to this model API. Default is true. Set to false for local services without SSL support.")
163165

164166

165167
class ToolInfo(TableBase):

backend/database/model_management_db.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,25 @@ def get_models_by_tenant_factory_type(tenant_id: str, model_factory: str, model_
252252
"model_type": model_type
253253
}
254254
return get_model_records(filters, tenant_id)
255+
256+
257+
def get_model_by_name_factory(model_name: str, model_factory: str, tenant_id: str) -> Optional[Dict[str, Any]]:
258+
"""
259+
Get a model record by model_name and model_factory for deduplication.
260+
261+
Args:
262+
model_name: Model name (e.g., "deepseek-r1-distill-qwen-14b")
263+
model_factory: Model factory (e.g., "ModelEngine")
264+
tenant_id: Tenant ID
265+
266+
Returns:
267+
Optional[Dict[str, Any]]: Model record if found, None otherwise
268+
"""
269+
filters = {
270+
'model_name': model_name,
271+
'model_factory': model_factory
272+
}
273+
records = get_model_records(filters, tenant_id)
274+
return records[0] if records else None
275+
276+

backend/services/conversation_management_service.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,13 @@ def call_llm_for_title(content: str, tenant_id: str, language: str = LANGUAGE["Z
263263
key=MODEL_CONFIG_MAPPING["llm"], tenant_id=tenant_id)
264264

265265
# Create OpenAIServerModel instance
266-
llm = OpenAIServerModel(model_id=get_model_name_from_config(model_config) if model_config.get("model_name") else "", api_base=model_config.get("base_url", ""),
267-
api_key=model_config.get("api_key", ""), temperature=0.7, top_p=0.95)
266+
llm = OpenAIServerModel(
267+
model_id=get_model_name_from_config(model_config) if model_config.get("model_name") else "",
268+
api_base=model_config.get("base_url", ""),
269+
api_key=model_config.get("api_key", ""),
270+
temperature=0.7,
271+
top_p=0.95
272+
)
268273

269274
# Build messages
270275
user_prompt = Template(prompt_template["USER_PROMPT"], undefined=StrictUndefined).render({
Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,55 @@
1-
import asyncio
2-
from typing import List
3-
41
import aiohttp
2+
import asyncio
53

64
from consts.const import MODEL_ENGINE_APIKEY, MODEL_ENGINE_HOST
7-
from consts.exceptions import TimeoutException, NotFoundException
5+
from consts.exceptions import MEConnectionException, TimeoutException
6+
7+
8+
async def check_me_variable_set() -> bool:
9+
"""
10+
Check if the ME environment variables are correctly set.
11+
Returns:
12+
bool: True if both MODEL_ENGINE_APIKEY and MODEL_ENGINE_HOST are set and non-empty, False otherwise.
13+
"""
14+
return bool(MODEL_ENGINE_APIKEY and MODEL_ENGINE_HOST)
815

916

10-
async def get_me_models_impl(timeout: int = 2, type: str = "") -> List:
17+
async def check_me_connectivity(timeout: int = 30) -> bool:
1118
"""
12-
Fetches a list of models from the model engine API with response formatting.
13-
Parameters:
14-
timeout (int): The total timeout for the request in seconds.
15-
type (str): The type of model to filter for. If empty, returns all models.
19+
Check ModelEngine connectivity by actually calling the API.
20+
21+
Args:
22+
timeout: Request timeout in seconds
23+
1624
Returns:
17-
- filtered_result: List of model data dictionaries
25+
bool: True if connection successful, False otherwise
26+
27+
Raises:
28+
MEConnectionException: If connection failed with specific error
29+
TimeoutException: If request timed out
1830
"""
31+
if not await check_me_variable_set():
32+
return False
33+
1934
try:
20-
headers = {
21-
'Authorization': f'Bearer {MODEL_ENGINE_APIKEY}',
22-
}
35+
headers = {"Authorization": f"Bearer {MODEL_ENGINE_APIKEY}"}
36+
2337
async with aiohttp.ClientSession(
24-
timeout=aiohttp.ClientTimeout(total=timeout),
25-
connector=aiohttp.TCPConnector(ssl=False)
38+
timeout=aiohttp.ClientTimeout(total=timeout),
39+
connector=aiohttp.TCPConnector(ssl=False)
2640
) as session:
2741
async with session.get(
28-
f"{MODEL_ENGINE_HOST}/open/router/v1/models",
29-
headers=headers
42+
f"{MODEL_ENGINE_HOST}/open/router/v1/models",
43+
headers=headers
3044
) as response:
31-
response.raise_for_status()
32-
result_data = await response.json()
33-
result: list = result_data['data']
34-
35-
# Type filtering
36-
filtered_result = []
37-
if type:
38-
for data in result:
39-
if data['type'] == type:
40-
filtered_result.append(data)
41-
if not filtered_result:
42-
result_types = set(data['type'] for data in result)
43-
raise NotFoundException(
44-
f"No models found with type '{type}'. Available types: {result_types}.")
45-
else:
46-
filtered_result = result
47-
48-
return filtered_result
45+
if response.status == 200:
46+
return True
47+
else:
48+
raise MEConnectionException(
49+
f"Connection failed, error code: {response.status}")
4950
except asyncio.TimeoutError:
50-
raise TimeoutException("Request timeout.")
51+
raise TimeoutException("Connection timed out")
52+
except MEConnectionException:
53+
raise
5154
except Exception as e:
52-
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)
55+
raise Exception(f"Unknown error occurred: {str(e)}")

backend/services/model_health_service.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
import asyncio
21
import logging
3-
import aiohttp
4-
from http import HTTPStatus
52

63
from nexent.core import MessageObserver
74
from nexent.core.models import OpenAIModel, OpenAIVLModel
85
from nexent.core.models.embedding_model import JinaEmbedding, OpenAICompatibleEmbedding
96

107
from services.voice_service import get_voice_service
11-
from consts.const import MODEL_ENGINE_APIKEY, MODEL_ENGINE_HOST, LOCALHOST_IP, LOCALHOST_NAME, DOCKER_INTERNAL_HOST
12-
from consts.exceptions import MEConnectionException, TimeoutException
8+
from consts.const import LOCALHOST_IP, LOCALHOST_NAME, DOCKER_INTERNAL_HOST
139
from consts.model import ModelConnectStatusEnum
1410
from database.model_management_db import get_model_by_display_name, update_model_record
1511
from utils.config_utils import get_model_name_from_config
@@ -57,6 +53,7 @@ async def _perform_connectivity_check(
5753
model_type: str,
5854
model_base_url: str,
5955
model_api_key: str,
56+
ssl_verify: bool = True,
6057
) -> bool:
6158
"""
6259
Perform specific model connectivity check
@@ -65,6 +62,7 @@ async def _perform_connectivity_check(
6562
model_type: Model type
6663
model_base_url: Model base URL
6764
model_api_key: API key
65+
ssl_verify: Whether to verify SSL certificates (default: True)
6866
Returns:
6967
bool: Connectivity check result
7068
"""
@@ -95,7 +93,8 @@ async def _perform_connectivity_check(
9593
observer,
9694
model_id=model_name,
9795
api_base=model_base_url,
98-
api_key=model_api_key
96+
api_key=model_api_key,
97+
ssl_verify=ssl_verify
9998
).check_connectivity()
10099
elif model_type == "rerank":
101100
connectivity = False
@@ -135,11 +134,12 @@ async def check_model_connectivity(display_name: str, tenant_id: str) -> dict:
135134
model_type = model["model_type"]
136135
model_base_url = model["base_url"]
137136
model_api_key = model["api_key"]
137+
ssl_verify = model.get("ssl_verify", True) # Default to True if not present
138138

139139
try:
140140
# Use the common connectivity check function
141141
connectivity = await _perform_connectivity_check(
142-
model_name, model_type, model_base_url, model_api_key
142+
model_name, model_type, model_base_url, model_api_key, ssl_verify
143143
)
144144
except Exception as e:
145145
update_data = {"connect_status": ModelConnectStatusEnum.UNAVAILABLE.value}
@@ -167,32 +167,6 @@ async def check_model_connectivity(display_name: str, tenant_id: str) -> dict:
167167
raise e
168168

169169

170-
async def check_me_connectivity_impl(timeout: int):
171-
"""
172-
Check ME connectivity and return structured response data
173-
Args:
174-
timeout: Request timeout in seconds
175-
"""
176-
try:
177-
headers = {'Authorization': f'Bearer {MODEL_ENGINE_APIKEY}'}
178-
179-
async with aiohttp.ClientSession(
180-
timeout=aiohttp.ClientTimeout(total=timeout),
181-
connector=aiohttp.TCPConnector(ssl=False)
182-
) as session:
183-
async with session.get(
184-
f"{MODEL_ENGINE_HOST}/open/router/v1/models",
185-
headers=headers
186-
) as response:
187-
if response.status == HTTPStatus.OK:
188-
return
189-
else:
190-
raise MEConnectionException(
191-
f"Connection failed, error code: {response.status}")
192-
except asyncio.TimeoutError:
193-
raise TimeoutException("Connection timed out")
194-
except Exception as e:
195-
raise Exception(f"Unknown error occurred: {str(e)}")
196170

197171

198172
async def verify_model_config_connectivity(model_config: dict):
@@ -208,11 +182,12 @@ async def verify_model_config_connectivity(model_config: dict):
208182
model_type = model_config["model_type"]
209183
model_base_url = model_config["base_url"]
210184
model_api_key = model_config["api_key"]
185+
ssl_verify = model_config.get("ssl_verify", True) # Default to True if not present
211186

212187
try:
213188
# Use the common connectivity check function
214189
connectivity = await _perform_connectivity_check(
215-
model_name, model_type, model_base_url, model_api_key
190+
model_name, model_type, model_base_url, model_api_key, ssl_verify
216191
)
217192

218193
if not connectivity:

0 commit comments

Comments
 (0)