Files
server-core/app/api/routers/agents.py
T

145 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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