diff --git a/agent/agent_config.py b/agent/agent_config.py index 6a84cb0..e1b752e 100644 --- a/agent/agent_config.py +++ b/agent/agent_config.py @@ -312,10 +312,13 @@ class AgentConfig: # 使 workspace / system_prompt 路径 / skills 目录都落到 projects/robot/{bot_id}-{user} effective_bot_id = bot_config.get("project_dir_key") or request.bot_id + # 前端可通过请求体覆盖 model_id(仅本次请求生效) + effective_model_name = request.model_id or bot_config.get("model", "qwen/qwen3-next-80b-a3b-instruct") + config = cls( bot_id=effective_bot_id, api_key=bot_config.get("api_key", ""), - model_name=bot_config.get("model", "qwen/qwen3-next-80b-a3b-instruct"), + model_name=effective_model_name, model_server=bot_config.get("model_server", ""), language=language, system_prompt=system_prompt, diff --git a/routes/bot_manager.py b/routes/bot_manager.py index 61f1e03..0dc1a58 100644 --- a/routes/bot_manager.py +++ b/routes/bot_manager.py @@ -1603,6 +1603,56 @@ async def delete_bot(bot_uuid: str, authorization: Optional[str] = Header(None)) # ============== Bot 设置 API ============== +async def _fetch_user_models_and_datasets(user_id: str): + """获取当前用户可切换的 New API 模型列表与已建知识库 dataset_id 列表。 + + 子账号自动取主账号的资源。返回 (models_list, dataset_ids), + 任何异常都不影响主流程(返回空列表)。 + """ + models_list: List[NewAPIModelResponse] = [] + dataset_ids: List[str] = [] + + if user_id == "__masterkey__": + return models_list, dataset_ids + + pool = get_db_pool_manager().pool + try: + async with pool.connection() as conn: + async with conn.cursor() as cursor: + effective_user_id = await get_parent_user_id(user_id) + await cursor.execute( + "SELECT new_api_session, new_api_user_id FROM agent_user WHERE id = %s", + (effective_user_id,) + ) + row = await cursor.fetchone() + if row and row[0] and row[1]: + proxy = get_new_api_proxy() + cookies = {"session": row[0]} + result = await proxy.get_user_models(cookies, row[1]) + if result.get("success"): + data = result.get("data", []) + if isinstance(data, list): + for model in data: + models_list.append(NewAPIModelResponse( + id=model if isinstance(model, str) else model.get("id", ""), + object="model", + created=None, + owned_by="system" + )) + + # 用户已创建的知识库(子账号使用主账号的) + await cursor.execute( + "SELECT dataset_id FROM user_datasets WHERE user_id = %s ORDER BY created_at DESC", + (effective_user_id,) + ) + rows = await cursor.fetchall() + dataset_ids = [r[0] for r in rows] if rows else [] + except Exception as e: + print(f"Failed to fetch user models/datasets: {e}") + + return models_list, dataset_ids + + @router.get("/api/v1/general-agent/settings", response_model=BotSettingsResponse) async def get_general_agent_settings_api(authorization: Optional[str] = Header(None)): """获取通用智能体设置(配置文件驱动,对所有登录用户只读)""" @@ -1613,6 +1663,16 @@ async def get_general_agent_settings_api(authorization: Optional[str] = Header(N settings = get_general_agent_settings() model_id = settings.get("model_id", "") + + # 拉取当前用户可切换的模型与已建知识库,供前端在首页选择 + models_list, user_dataset_ids = await _fetch_user_models_and_datasets(user_id) + + # 如果配置中的 model_id 不在用户可用列表里,回退到第一个可用模型(仅展示层) + if model_id and models_list and model_id not in [m.id for m in models_list]: + model_id = models_list[0].id + if not model_id and models_list: + model_id = models_list[0].id + model_info = None if model_id: model_info = ModelInfo( @@ -1629,12 +1689,12 @@ async def get_general_agent_settings_api(authorization: Optional[str] = Header(N name=settings.get("name", "通用智能体"), model_id=model_id or None, model=model_info, - models=[], + models=models_list, language=settings.get("language", "zh"), avatar_url=settings.get("avatar_url") or None, description=settings.get("description") or None, suggestions=settings.get("suggestions") or [], - dataset_ids=settings.get("dataset_ids") or [], + dataset_ids=user_dataset_ids, system_prompt=settings.get("system_prompt") or None, enable_memori=settings.get("enable_memori", False), enable_thinking=settings.get("enable_thinking", False), diff --git a/routes/chat.py b/routes/chat.py index 965d88d..c00bce6 100644 --- a/routes/chat.py +++ b/routes/chat.py @@ -938,6 +938,11 @@ async def chat_completions_v3(request: ChatRequestV3, authorization: Optional[st # 从数据库获取机器人配置 bot_config = await fetch_bot_config_from_db(bot_id, request.user_identifier) + # 允许前端通过请求体覆盖 model_id / dataset_ids(仅本次请求生效,不写库; + # 用于只读配置的 bot,如 general-agent 静态 json 配置)。 + if request.dataset_ids is not None: + bot_config["dataset_ids"] = request.dataset_ids + # 构造类 v2 的请求格式 # 从数据库配置中提取参数 language = bot_config.get("language", "zh") diff --git a/utils/api_models.py b/utils/api_models.py index 467c34e..b5bbb1a 100644 --- a/utils/api_models.py +++ b/utils/api_models.py @@ -85,12 +85,18 @@ class ChatRequestV3(BaseModel): - messages: 对话消息列表 - session_id: 可选的会话ID - user_identifier: 当前登录用户的用户名,用于标识用户身份 + + 可选覆盖项(仅本次请求生效,不写库;用于只读配置的 bot,如 general-agent): + - model_id: 模型 ID,覆盖数据库配置 + - dataset_ids: 数据集 ID 列表,覆盖数据库配置 """ messages: List[Message] bot_id: str stream: Optional[bool] = False session_id: Optional[str] = None user_identifier: Optional[str] = None + model_id: Optional[str] = None + dataset_ids: Optional[List[str]] = None class VisionMessage(BaseModel):