Skip to content

Commit 2f62c2a

Browse files
authored
✨ Release/v1.7.5.1
2 parents ac7c76d + 3443edf commit 2f62c2a

File tree

30 files changed

+2247
-243
lines changed

30 files changed

+2247
-243
lines changed

backend/apps/model_managment_app.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ async def create_model(request: ModelRequest, authorization: Optional[str] = Hea
7373
except ValueError as e:
7474
logging.error(f"Failed to create model: {str(e)}")
7575
raise HTTPException(status_code=HTTPStatus.CONFLICT,
76-
detail="Failed to create model: name conflict")
76+
detail=str(e))
7777
except Exception as e:
7878
logging.error(f"Failed to create model: {str(e)}")
7979
raise HTTPException(
80-
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Failed to create model")
80+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
8181

8282

8383
@router.post("/provider/create")
@@ -103,7 +103,7 @@ async def create_provider_model(request: ProviderModelRequest, authorization: Op
103103
except Exception as e:
104104
logging.error(f"Failed to create provider model: {str(e)}")
105105
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
106-
detail="Failed to create provider model")
106+
detail=str(e))
107107

108108

109109
@router.post("/provider/batch_create")
@@ -129,7 +129,7 @@ async def batch_create_models(request: BatchCreateModelsRequest, authorization:
129129
except Exception as e:
130130
logging.error(f"Failed to batch create models: {str(e)}")
131131
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
132-
detail="Failed to batch create models")
132+
detail=str(e))
133133

134134

135135
@router.post("/provider/list")
@@ -153,7 +153,7 @@ async def get_provider_list(request: ProviderModelRequest, authorization: Option
153153
except Exception as e:
154154
logging.error(f"Failed to get provider list: {str(e)}")
155155
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
156-
detail="Failed to get provider list")
156+
detail=str(e))
157157

158158

159159
@router.post("/update")
@@ -179,11 +179,11 @@ async def update_single_model(request: dict, authorization: Optional[str] = Head
179179
except ValueError as e:
180180
logging.error(f"Failed to update model: {str(e)}")
181181
raise HTTPException(status_code=HTTPStatus.CONFLICT,
182-
detail="Failed to update model: name conflict")
182+
detail=str(e))
183183
except Exception as e:
184184
logging.error(f"Failed to update model: {str(e)}")
185185
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
186-
detail="Failed to update model")
186+
detail=str(e))
187187

188188

189189
@router.post("/batch_update")
@@ -203,7 +203,7 @@ async def batch_update_models(request: List[dict], authorization: Optional[str]
203203
except Exception as e:
204204
logging.error(f"Failed to batch update models: {str(e)}")
205205
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
206-
detail="Failed to batch update models")
206+
detail=str(e))
207207

208208

209209
@router.post("/delete")
@@ -230,11 +230,11 @@ async def delete_model(display_name: str = Query(..., embed=True), authorization
230230
except LookupError as e:
231231
logging.error(f"Failed to delete model: {str(e)}")
232232
raise HTTPException(status_code=HTTPStatus.NOT_FOUND,
233-
detail="Failed to delete model: model not found")
233+
detail=str(e))
234234
except Exception as e:
235235
logging.error(f"Failed to delete model: {str(e)}")
236236
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
237-
detail="Failed to delete model")
237+
detail=str(e))
238238

239239

240240
@router.get("/list")
@@ -256,7 +256,7 @@ async def get_model_list(authorization: Optional[str] = Header(None)):
256256
except Exception as e:
257257
logging.error(f"Failed to list models: {str(e)}")
258258
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
259-
detail="Failed to retrieve model list")
259+
detail=str(e))
260260

261261

262262
@router.get("/llm_list")
@@ -272,7 +272,7 @@ async def get_llm_model_list(authorization: Optional[str] = Header(None)):
272272
except Exception as e:
273273
logging.error(f"Failed to retrieve LLM list: {str(e)}")
274274
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
275-
detail="Failed to retrieve LLM list")
275+
detail=str(e))
276276

277277

278278
@router.post("/healthcheck")
@@ -296,15 +296,15 @@ async def check_model_health(
296296
except LookupError as e:
297297
logging.error(f"Failed to check model connectivity: {str(e)}")
298298
raise HTTPException(status_code=HTTPStatus.NOT_FOUND,
299-
detail="Model configuration not found")
299+
detail=str(e))
300300
except ValueError as e:
301301
logging.error(f"Invalid model configuration: {str(e)}")
302302
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
303-
detail="Invalid model configuration")
303+
detail=str(e))
304304
except Exception as e:
305305
logging.error(f"Failed to check model connectivity: {str(e)}")
306306
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
307-
detail="Failed to check model connectivity")
307+
detail=str(e))
308308

309309

310310
@router.post("/temporary_healthcheck")
@@ -324,4 +324,4 @@ async def check_temporary_model_health(request: ModelRequest):
324324
except Exception as e:
325325
logging.error(f"Failed to verify model connectivity: {str(e)}")
326326
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
327-
detail="Failed to verify model connectivity")
327+
detail=str(e))

backend/consts/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,4 @@
265265
os.getenv("LLM_SLOW_TOKEN_RATE_THRESHOLD", "10.0")) # tokens per second
266266

267267
# APP Version
268-
APP_VERSION = "v1.7.4.1"
268+
APP_VERSION = "v1.7.5.1"

backend/consts/model.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ class ExportAndImportAgentInfo(BaseModel):
274274
enabled: bool
275275
tools: List[ToolConfig]
276276
managed_agents: List[int]
277+
model_id: Optional[int] = None
278+
model_name: Optional[str] = None
279+
business_logic_model_id: Optional[int] = None
280+
business_logic_model_name: Optional[str] = None
277281

278282
class Config:
279283
arbitrary_types_allowed = True

backend/data_process/tasks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,11 @@ def process(
218218
logger.info(
219219
f"[{self.request.id}] PROCESS TASK: Ray processing completed, got {len(chunks) if chunks else 0} chunks")
220220

221-
# Persist chunks into Redis via Ray (fire-and-forget, don't block)
221+
# Persist chunks into Redis via Ray (synchronous to ensure data is ready before forward task)
222222
redis_key = f"dp:{task_id}:chunks"
223223
actor.store_chunks_in_redis.remote(redis_key, chunks)
224224
logger.info(
225-
f"[{self.request.id}] PROCESS TASK: Scheduled store_chunks_in_redis for key '{redis_key}'")
225+
f"[{self.request.id}] PROCESS TASK: Stored chunks in Redis at key '{redis_key}'")
226226

227227
end_time = time.time()
228228
elapsed_time = end_time - start_time
@@ -252,11 +252,11 @@ def process(
252252
logger.info(
253253
f"[{self.request.id}] PROCESS TASK: Ray processing completed, got {len(chunks) if chunks else 0} chunks")
254254

255-
# Persist chunks into Redis via Ray (fire-and-forget, don't block)
255+
# Persist chunks into Redis via Ray (synchronous to ensure data is ready before forward task)
256256
redis_key = f"dp:{task_id}:chunks"
257257
actor.store_chunks_in_redis.remote(redis_key, chunks)
258258
logger.info(
259-
f"[{self.request.id}] PROCESS TASK: Scheduled store_chunks_in_redis for key '{redis_key}'")
259+
f"[{self.request.id}] PROCESS TASK: Stored chunks in Redis at key '{redis_key}'")
260260

261261
end_time = time.time()
262262
elapsed_time = end_time - start_time

backend/database/db_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class TenantConfig(TableBase):
257257
value_type = Column(String(
258258
100), doc=" the data type of config_value, optional values: single/multi", default="single")
259259
config_key = Column(String(100), doc="the key of the config")
260-
config_value = Column(String(10000), doc="the value of the config")
260+
config_value = Column(Text, doc="the value of the config")
261261

262262

263263
class MemoryUserConfig(TableBase):

backend/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ data-process = [
2525
"celery>=5.3.6",
2626
"flower>=2.0.1",
2727
"nest_asyncio>=1.5.6",
28-
"unstructured[csv,docx,pdf,pptx,xlsx,md]"
28+
"unstructured[csv,docx,pdf,pptx,xlsx,md]==0.18.14"
2929
]
3030
test = [
3131
"pytest",

backend/services/agent_service.py

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from agents.agent_run_manager import agent_run_manager
1414
from agents.create_agent_info import create_agent_run_info, create_tool_config_list
1515
from agents.preprocess_manager import preprocess_manager
16-
from consts.const import MEMORY_SEARCH_START_MSG, MEMORY_SEARCH_DONE_MSG, MEMORY_SEARCH_FAIL_MSG, TOOL_TYPE_MAPPING, LANGUAGE, MESSAGE_ROLE
16+
from consts.const import MEMORY_SEARCH_START_MSG, MEMORY_SEARCH_DONE_MSG, MEMORY_SEARCH_FAIL_MSG, TOOL_TYPE_MAPPING, LANGUAGE, MESSAGE_ROLE, MODEL_CONFIG_MAPPING
1717
from consts.exceptions import MemoryPreparationException
1818
from consts.model import (
1919
AgentInfoRequest,
@@ -37,7 +37,7 @@
3737
search_blank_sub_agent_by_main_agent_id,
3838
update_agent
3939
)
40-
from database.model_management_db import get_model_by_model_id
40+
from database.model_management_db import get_model_by_model_id, get_model_id_by_display_name
4141
from database.remote_mcp_db import check_mcp_name_exists, get_mcp_server_by_name_and_tenant
4242
from database.tool_db import (
4343
check_tool_is_available,
@@ -52,6 +52,7 @@
5252
from services.remote_mcp_service import add_remote_mcp_server_list
5353
from services.tool_configuration_service import update_tool_list
5454
from utils.auth_utils import get_current_user_info, get_user_language
55+
from utils.config_utils import tenant_config_manager
5556
from utils.memory_utils import build_memory_config
5657
from utils.thread_utils import submit
5758

@@ -82,6 +83,57 @@ def _resolve_user_tenant_language(
8283
return user_id, tenant_id, get_user_language(http_request)
8384

8485

86+
def _resolve_model_with_fallback(
87+
model_display_name: str | None,
88+
exported_model_id: str | None,
89+
model_label: str,
90+
tenant_id: str
91+
) -> str | None:
92+
"""
93+
Resolve model_id from model_display_name with fallback to quick config LLM model.
94+
95+
Args:
96+
model_display_name: Display name of the model to lookup
97+
exported_model_id: Original model_id from export (for logging only)
98+
model_label: Label for logging (e.g., "Model", "Business logic model")
99+
tenant_id: Tenant ID for model lookup
100+
101+
Returns:
102+
Resolved model_id or None if not found and no fallback available
103+
"""
104+
if not model_display_name:
105+
return None
106+
107+
# Try to find model by display name in current tenant
108+
resolved_id = get_model_id_by_display_name(model_display_name, tenant_id)
109+
110+
if resolved_id:
111+
logger.info(
112+
f"{model_label} '{model_display_name}' found in tenant {tenant_id}, "
113+
f"mapped to model_id: {resolved_id} (exported model_id was: {exported_model_id})")
114+
return resolved_id
115+
116+
# Model not found, try fallback to quick config LLM model
117+
logger.warning(
118+
f"{model_label} '{model_display_name}' (exported model_id: {exported_model_id}) "
119+
f"not found in tenant {tenant_id}, falling back to quick config LLM model.")
120+
121+
quick_config_model = tenant_config_manager.get_model_config(
122+
key=MODEL_CONFIG_MAPPING["llm"],
123+
tenant_id=tenant_id
124+
)
125+
126+
if quick_config_model:
127+
fallback_id = quick_config_model.get("model_id")
128+
logger.info(
129+
f"Using quick config LLM model for {model_label.lower()}: "
130+
f"{quick_config_model.get('display_name')} (model_id: {fallback_id})")
131+
return fallback_id
132+
133+
logger.warning(f"No quick config LLM model found for tenant {tenant_id}")
134+
return None
135+
136+
85137
async def _stream_agent_chunks(
86138
agent_request: "AgentRequest",
87139
user_id: str,
@@ -429,6 +481,20 @@ async def export_agent_by_agent_id(agent_id: int, tenant_id: str, user_id: str)
429481
if tool.class_name == "KnowledgeBaseSearchTool":
430482
tool.metadata = {}
431483

484+
# Get model_id and model display name from agent_info
485+
model_id = agent_info.get("model_id")
486+
model_display_name = None
487+
if model_id is not None:
488+
model_info = get_model_by_model_id(model_id)
489+
model_display_name = model_info.get("display_name") if model_info is not None else None
490+
491+
# Get business_logic_model_id and business logic model display name
492+
business_logic_model_id = agent_info.get("business_logic_model_id")
493+
business_logic_model_display_name = None
494+
if business_logic_model_id is not None:
495+
business_logic_model_info = get_model_by_model_id(business_logic_model_id)
496+
business_logic_model_display_name = business_logic_model_info.get("display_name") if business_logic_model_info is not None else None
497+
432498
agent_info = ExportAndImportAgentInfo(agent_id=agent_id,
433499
name=agent_info["name"],
434500
display_name=agent_info["display_name"],
@@ -444,7 +510,11 @@ async def export_agent_by_agent_id(agent_id: int, tenant_id: str, user_id: str)
444510
"few_shots_prompt"),
445511
enabled=agent_info["enabled"],
446512
tools=tool_list,
447-
managed_agents=agent_relation_in_db)
513+
managed_agents=agent_relation_in_db,
514+
model_id=model_id,
515+
model_name=model_display_name,
516+
business_logic_model_id=business_logic_model_id,
517+
business_logic_model_name=business_logic_model_display_name)
448518
return agent_info
449519

450520

@@ -565,12 +635,33 @@ async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo,
565635
if not import_agent_info.name.isidentifier():
566636
raise ValueError(
567637
f"Invalid agent name: {import_agent_info.name}. agent name must be a valid python variable name.")
638+
639+
# Resolve model IDs with fallback
640+
# Note: We use model_display_name for cross-tenant compatibility
641+
# The exported model_id is kept for reference/debugging only
642+
model_id = _resolve_model_with_fallback(
643+
model_display_name=import_agent_info.model_name,
644+
exported_model_id=import_agent_info.model_id,
645+
model_label="Model",
646+
tenant_id=tenant_id
647+
)
648+
649+
business_logic_model_id = _resolve_model_with_fallback(
650+
model_display_name=import_agent_info.business_logic_model_name,
651+
exported_model_id=import_agent_info.business_logic_model_id,
652+
model_label="Business logic model",
653+
tenant_id=tenant_id
654+
)
655+
568656
# create a new agent
569657
new_agent = create_agent(agent_info={"name": import_agent_info.name,
570658
"display_name": import_agent_info.display_name,
571659
"description": import_agent_info.description,
572660
"business_description": import_agent_info.business_description,
573-
"model_id": None,
661+
"model_id": model_id,
662+
"model_name": import_agent_info.model_name,
663+
"business_logic_model_id": business_logic_model_id,
664+
"business_logic_model_name": import_agent_info.business_logic_model_name,
574665
"max_steps": import_agent_info.max_steps,
575666
"provide_run_summary": import_agent_info.provide_run_summary,
576667
"duty_prompt": import_agent_info.duty_prompt,

backend/services/model_health_service.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ async def verify_model_config_connectivity(model_config: dict):
201201
Args:
202202
model_config: Model configuration dictionary, containing necessary connection parameters
203203
Returns:
204-
dict: Contains the result of the connectivity test
204+
dict: Contains the result of the connectivity test and error message if failed
205205
"""
206206
try:
207207
model_name = model_config.get("model_name", "")
@@ -214,23 +214,34 @@ async def verify_model_config_connectivity(model_config: dict):
214214
connectivity = await _perform_connectivity_check(
215215
model_name, model_type, model_base_url, model_api_key
216216
)
217+
218+
if not connectivity:
219+
return {
220+
"connectivity": False,
221+
"model_name": model_name,
222+
"error": f"Failed to connect to model '{model_name}' at {model_base_url}. Please verify the URL, API key, and network connection."
223+
}
224+
225+
return {
226+
"connectivity": True,
227+
"model_name": model_name,
228+
}
217229
except ValueError as e:
218-
logger.warning(f"UNCONNECTED: {model_name}; Base URL: {model_base_url}; API Key: {model_api_key}; Error: {str(e)}")
230+
error_msg = str(e)
231+
logger.warning(f"UNCONNECTED: {model_name}; Base URL: {model_base_url}; API Key: {model_api_key}; Error: {error_msg}")
219232
return {
220233
"connectivity": False,
221-
"model_name": model_name
234+
"model_name": model_name,
235+
"error": error_msg
222236
}
223237

224-
return {
225-
"connectivity": connectivity,
226-
"model_name": model_name,
227-
}
228-
229238
except Exception as e:
230-
logger.error(f"Failed to check connectivity of models: {str(e)}")
239+
error_msg = str(e)
240+
logger.error(f"Failed to check connectivity of models: {error_msg}")
231241
return {
232242
"connectivity": False,
233-
"model_name": model_config.get("model_name", "UNKNOWN_MODEL")
243+
"model_name": model_config.get("model_name", "UNKNOWN_MODEL"),
244+
"error": f"Connection verification failed: {error_msg}"
234245
}
235246

236247

0 commit comments

Comments
 (0)