145 lines
5.1 KiB
Python
145 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""智能体路由:每用户隔离的身份 CRUD。
|
||
|
||
智能体"基础信息 + 身份 CRUD"托管在服务端(后续入数据库);运行时配置、workspace、
|
||
技能、对话仍由 PineAgents 主后端在本地管理。本地后端把 /agents 的身份部分转发到这里。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException
|
||
|
||
from ..dependencies import get_db, require_port
|
||
from ..schemas.agents import AgentCreate, AgentInfo, AgentUpdate
|
||
from ...infrastructure.repositories import Database
|
||
|
||
router = APIRouter(prefix="/agents", tags=["agents"])
|
||
|
||
|
||
def _agent_info(record: dict) -> AgentInfo:
|
||
"""构建 AgentInfo,`deletable` 以记录中的 flag 为准(服务端决定)。"""
|
||
rec = dict(record)
|
||
rec.setdefault("deletable", True)
|
||
rec.setdefault("port", "opc")
|
||
return AgentInfo(**rec)
|
||
|
||
|
||
@router.get("", response_model=list[AgentInfo], summary="当前用户智能体列表")
|
||
async def list_agents(
|
||
user: dict = Depends(require_port),
|
||
db: Database = Depends(get_db),
|
||
):
|
||
return [_agent_info(r) for r in await db.agents.get_by_user(user["id"], port=user["port"])]
|
||
|
||
|
||
@router.get("/directory", summary="企业智能体目录(智能体互通选择器用)")
|
||
async def agent_directory(
|
||
user: dict = Depends(require_port),
|
||
db: Database = Depends(get_db),
|
||
):
|
||
"""返回当前用户所在企业的成员及其可被调用的智能体(external_callable=1)。
|
||
|
||
用于桌面端 IM「调用智能体」选择器与授权管理页:
|
||
[{user_id, name, avatar, agents: [{agent_id, name, avatar, call_mode}]}]
|
||
非企业成员返回空列表(无权调用他人智能体)。
|
||
"""
|
||
memberships = await db.company_members.list_by_user(user["id"])
|
||
if not memberships:
|
||
return []
|
||
company_ids = list({m["company_id"] for m in memberships})
|
||
by_user: dict[str, dict] = {}
|
||
for cid in company_ids:
|
||
for m in await db.company_members.list_by_company(cid):
|
||
uid = m["user_id"]
|
||
if uid in by_user:
|
||
continue
|
||
rec = await db.users.get_by_id(uid)
|
||
by_user[uid] = {
|
||
"user_id": uid,
|
||
"name": (rec or {}).get("nickname") or (rec or {}).get("username") or uid,
|
||
"avatar": (rec or {}).get("avatar", ""),
|
||
"agents": [],
|
||
}
|
||
for uid in list(by_user):
|
||
for a in await db.agents.get_by_user(uid, port=user["port"]):
|
||
if not a.get("external_callable", True):
|
||
continue
|
||
from ...infrastructure.oss import resolve_url
|
||
by_user[uid]["agents"].append({
|
||
"agent_id": a["id"],
|
||
"name": a.get("name", ""),
|
||
"avatar": resolve_url(a.get("avatar", "") or ""),
|
||
"call_mode": a.get("call_mode", "auto"),
|
||
})
|
||
return list(by_user.values())
|
||
|
||
|
||
@router.get("/{agent_id}", response_model=AgentInfo, summary="智能体详情")
|
||
async def get_agent(
|
||
agent_id: str,
|
||
user: dict = Depends(require_port),
|
||
db: Database = Depends(get_db),
|
||
):
|
||
record = await db.agents.get(agent_id, user["id"], port=user["port"])
|
||
if record is None:
|
||
raise HTTPException(status_code=404, detail="Agent not found")
|
||
return _agent_info(record)
|
||
|
||
|
||
@router.post(
|
||
"",
|
||
response_model=AgentInfo,
|
||
status_code=201,
|
||
summary="创建智能体身份",
|
||
)
|
||
async def create_agent(
|
||
req: AgentCreate,
|
||
user: dict = Depends(require_port),
|
||
db: Database = Depends(get_db),
|
||
):
|
||
if not req.name.strip():
|
||
raise HTTPException(status_code=400, detail="Agent name is required")
|
||
record = await db.agents.create(
|
||
user["id"],
|
||
req.name,
|
||
description=req.description,
|
||
language=req.language or "zh",
|
||
model_name=req.model_name,
|
||
port=user["port"],
|
||
)
|
||
return _agent_info(record)
|
||
|
||
|
||
@router.put("/{agent_id}", response_model=AgentInfo, summary="更新智能体身份")
|
||
async def update_agent(
|
||
agent_id: str,
|
||
req: AgentUpdate,
|
||
user: dict = Depends(require_port),
|
||
db: Database = Depends(get_db),
|
||
):
|
||
payload = req.model_dump(exclude_none=True)
|
||
if not payload:
|
||
raise HTTPException(status_code=400, detail="Nothing to update")
|
||
record = await db.agents.update(agent_id, user["id"], payload, port=user["port"])
|
||
if record is None:
|
||
raise HTTPException(status_code=404, detail="Agent not found")
|
||
return _agent_info(record)
|
||
|
||
|
||
@router.delete("/{agent_id}", status_code=204, summary="删除智能体")
|
||
async def delete_agent(
|
||
agent_id: str,
|
||
user: dict = Depends(require_port),
|
||
db: Database = Depends(get_db),
|
||
):
|
||
record = await db.agents.get(agent_id, user["id"], port=user["port"])
|
||
if record is None:
|
||
raise HTTPException(status_code=404, detail="Agent not found")
|
||
if not record.get("deletable", True):
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="Cannot delete this agent",
|
||
)
|
||
if not await db.agents.delete(agent_id, user["id"], port=user["port"]):
|
||
raise HTTPException(status_code=404, detail="Agent not found")
|
||
return None
|