diff --git a/apps/jingrow/frontend/src/app/router/index.ts b/apps/jingrow/frontend/src/app/router/index.ts index 7e7eb04..88d434d 100644 --- a/apps/jingrow/frontend/src/app/router/index.ts +++ b/apps/jingrow/frontend/src/app/router/index.ts @@ -145,6 +145,16 @@ const router = createRouter({ name: 'NodeDetail', component: () => import('../../views/dev/NodeDetail.vue') }, + { + path: 'agent-marketplace', + name: 'AgentMarketplace', + component: () => import('../../views/dev/AgentMarketplace.vue') + }, + { + path: 'agent-marketplace/:name', + name: 'AgentDetail', + component: () => import('../../views/dev/AgentDetail.vue') + }, { path: 'app-marketplace/:name', name: 'AppDetail', diff --git a/apps/jingrow/frontend/src/shared/stores/menu.ts b/apps/jingrow/frontend/src/shared/stores/menu.ts index 9dd0869..1a9b0b5 100644 --- a/apps/jingrow/frontend/src/shared/stores/menu.ts +++ b/apps/jingrow/frontend/src/shared/stores/menu.ts @@ -64,6 +64,7 @@ function getDefaultMenus(): AppMenuItem[] { { id: 'app-marketplace', key: 'AppMarketplace', label: 'App Marketplace', icon: 'tabler:shopping-cart', type: 'route', routeName: 'AppMarketplace', parentId: 'dev-group', order: 7 }, { id: 'my-published-apps', key: 'MyPublishedApps', label: 'My Published Apps', icon: 'tabler:cloud-upload', type: 'route', routeName: 'MyPublishedApps', parentId: 'dev-group', order: 7.5 }, { id: 'node-marketplace', key: 'NodeMarketplace', label: 'Node Marketplace', icon: 'carbon:add-child-node', type: 'route', routeName: 'NodeMarketplace', parentId: 'dev-group', order: 8 }, + { id: 'agent-marketplace', key: 'AgentMarketplace', label: 'Agent Marketplace', icon: 'hugeicons:robotic', type: 'route', routeName: 'AgentMarketplace', parentId: 'dev-group', order: 8.5 }, { id: 'menuManager', key: 'MenuManager', label: 'Menu Management', icon: 'tabler:menu-2', type: 'route', routeName: 'MenuManager', order: 10 }, { id: 'settings', key: 'Settings', label: 'Settings', icon: 'tabler:settings', routeName: 'Settings', order: 11, type: 'route' } ] diff --git a/apps/jingrow/frontend/src/views/dev/AgentDetail.vue b/apps/jingrow/frontend/src/views/dev/AgentDetail.vue new file mode 100644 index 0000000..1193bda --- /dev/null +++ b/apps/jingrow/frontend/src/views/dev/AgentDetail.vue @@ -0,0 +1,602 @@ + + + + + + diff --git a/apps/jingrow/frontend/src/views/dev/AgentMarketplace.vue b/apps/jingrow/frontend/src/views/dev/AgentMarketplace.vue new file mode 100644 index 0000000..dea7ba4 --- /dev/null +++ b/apps/jingrow/frontend/src/views/dev/AgentMarketplace.vue @@ -0,0 +1,629 @@ + + + + + + diff --git a/apps/jingrow/jingrow/api/node_management.py b/apps/jingrow/jingrow/api/node_management.py index 66ba0ac..f12641a 100644 --- a/apps/jingrow/jingrow/api/node_management.py +++ b/apps/jingrow/jingrow/api/node_management.py @@ -732,3 +732,179 @@ async def publish_node_to_marketplace( logger.error(f"发布节点失败: {str(e)}") logger.error(f"Traceback: {traceback.format_exc()}") raise HTTPException(status_code=500, detail=f"发布节点失败: {str(e)}") + + +# ==================== 智能体市场 API ==================== + +@router.get("/jingrow/agent-marketplace") +async def get_agent_marketplace( + search: Optional[str] = None, + page: int = 1, + page_size: int = 20, + sort_by: Optional[str] = None +): + """获取智能体市场数据,支持搜索、分页和排序""" + try: + url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_agent_list" + + # 构建过滤条件 + filters = {"public": 1} + if search: + filters["agent_name"] = ["like", f"%{search}%"] + filters["title"] = ["like", f"%{search}%"] + + # 1. 先获取总数(不分页) + total_params = { + 'filters': json.dumps(filters, ensure_ascii=False), + 'limit_start': 0, + 'limit_page_length': 0 + } + + headers = get_jingrow_cloud_api_headers() + total_response = requests.get(url, params=total_params, headers=headers, timeout=20) + + total_count = 0 + if total_response.status_code == 200: + total_data = total_response.json() + total_count = len(total_data.get('message', [])) + + # 2. 获取分页数据 + params = { + 'filters': json.dumps(filters, ensure_ascii=False) + } + + # 排序参数 + if sort_by: + params['order_by'] = sort_by + + # 分页参数 + limit_start = (page - 1) * page_size + params['limit_start'] = limit_start + params['limit_page_length'] = page_size + + response = requests.get(url, params=params, headers=headers, timeout=20) + + if response.status_code == 200: + data = response.json() + agents = data.get('message', []) + + return { + "items": agents, + "total": total_count, + "page": page, + "page_size": page_size + } + else: + raise HTTPException(status_code=response.status_code, detail="获取智能体市场数据失败") + + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取智能体市场数据失败: {str(e)}") + + +@router.get("/jingrow/agent-marketplace/{name}") +async def get_agent_detail(name: str): + """获取智能体详情""" + try: + url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.get_local_agent" + params = {"name": name} + + headers = get_jingrow_cloud_api_headers() + response = requests.get(url, params=params, headers=headers, timeout=20) + + if response.status_code == 200: + data = response.json() + return data.get('message') + else: + raise HTTPException(status_code=404, detail="智能体不存在") + except Exception as e: + raise HTTPException(status_code=500, detail=f"获取智能体详情失败: {str(e)}") + + +@router.get("/jingrow/check-agent/{agent_name}") +async def check_agent_exists(agent_name: str): + """检查智能体是否已安装""" + try: + exists_res = get_record_id( + pagetype="Local Ai Agent", + field="agent_name", + value=agent_name, + ) + + return {"exists": exists_res.get("success", False)} + except Exception as e: + logger.error(f"检查智能体是否存在失败: {str(e)}") + return {"exists": False} + + +@router.post("/jingrow/install-agent") +async def install_agent(payload: Dict[str, Any]): + """安装智能体 - 只需要流程数据和智能体名称""" + try: + agent_name = payload.get("agent_name") + agent_flow = payload.get("agent_flow") + + if not agent_name: + raise HTTPException(status_code=400, detail="智能体名称不能为空") + + if not agent_flow: + raise HTTPException(status_code=400, detail="流程数据不能为空") + + # 检查是否已存在 + exists_res = get_record_id( + pagetype="Local Ai Agent", + field="agent_name", + value=agent_name, + ) + + # 准备数据 + agent_data = { + "agent_name": agent_name, + "agent_flow": json.dumps(agent_flow, ensure_ascii=False) if isinstance(agent_flow, dict) else agent_flow, + "status": "Active", + "enabled": 1, + "trigger_mode": "Manual Trigger" + } + + if exists_res.get("success"): + # 更新现有记录 + record_name = exists_res.get('name') + if not record_name: + return {'success': False, 'error': '获取记录名称失败'} + res = update_record("Local Ai Agent", record_name, agent_data) + else: + # 创建新记录 + res = create_record("Local Ai Agent", agent_data) + + if res.get("success"): + return {'success': True, 'agent_name': agent_name, 'message': f'智能体 {agent_name} 安装成功'} + else: + return {'success': False, 'error': res.get('error', '导入数据库失败')} + + except HTTPException: + raise + except Exception as e: + logger.error(f"安装智能体失败: {str(e)}") + raise HTTPException(status_code=500, detail=f"安装智能体失败: {str(e)}") + + +@router.get("/jingrow/installed-agent-names") +async def get_installed_agent_names(): + """获取已安装的智能体名称列表""" + try: + agents = get_record_list( + pagetype="Local Ai Agent", + fields=["agent_name"], + limit_start=0, + limit_page_length=1000 + ) + + agent_names = [] + if agents: + for agent in agents: + if agent.get("agent_name"): + agent_names.append(agent["agent_name"]) + + return {"success": True, "agents": agent_names} + except Exception as e: + logger.error(f"获取已安装智能体列表失败: {str(e)}") + return {"success": False, "agents": []}