feat: 实现 /agents/directory 企业智能体目录接口,返回可调用智能体列表+在线状态,支持管理员/同企业/临时授权三级权限模型
This commit is contained in:
@@ -104,3 +104,116 @@ async def revoke_grant(grant_id: str, user: dict = Depends(get_current_user),
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail="授权不存在或无权撤销")
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
# ── 用户 API:企业智能体目录(可调用智能体列表 + 在线状态) ──────────
|
||||
|
||||
@router.get("/agents/directory", summary="企业可调用智能体目录")
|
||||
async def list_agent_directory(user: dict = Depends(get_current_user),
|
||||
db: Database = Depends(get_db)):
|
||||
"""返回当前用户可调用的智能体目录(按成员分组),含在线状态。
|
||||
|
||||
可调用范围:自己的智能体 + 同企业成员 external_callable=True 的智能体
|
||||
(企业管理员可看全部;普通成员受 L2/L3 权限约束,但目录仅展示「可被调用」
|
||||
的智能体,实际调用时再由 authorize_call 做最终校验)。
|
||||
"""
|
||||
from ...api.dependencies import get_user_organizations, get_user_primary_org
|
||||
from ...infrastructure.models import OrganizationMember, Organization, CompanyMember
|
||||
from sqlalchemy import select
|
||||
|
||||
caller_id = user["id"]
|
||||
is_admin = "enterprise" in (user.get("capabilities") or [])
|
||||
|
||||
# 1. 确定当前用户的主企业(enterprise 类型)
|
||||
primary_org = await get_user_primary_org(db, user, org_type="enterprise")
|
||||
if not primary_org:
|
||||
# 无企业归属:仅返回自己的智能体
|
||||
org_id = ""
|
||||
else:
|
||||
org_id = primary_org["org_id"]
|
||||
|
||||
# 2. 收集企业成员 user_id 集合(含自己)
|
||||
member_ids: set[str] = {caller_id}
|
||||
if org_id:
|
||||
# 优先查统一组织表
|
||||
try:
|
||||
members = (await db.session.scalars(
|
||||
select(OrganizationMember).where(
|
||||
OrganizationMember.org_id == org_id,
|
||||
OrganizationMember.status == "active",
|
||||
)
|
||||
)).all()
|
||||
for m in members:
|
||||
member_ids.add(m.user_id)
|
||||
except Exception:
|
||||
pass
|
||||
# 回退旧表 company_members(通过 company_id 关联)
|
||||
if len(member_ids) <= 1:
|
||||
try:
|
||||
from ...infrastructure.models import Company
|
||||
company = await db.session.scalar(
|
||||
select(Company).where(Company.org_id == org_id).limit(1)
|
||||
)
|
||||
if company:
|
||||
cms = (await db.session.scalars(
|
||||
select(CompanyMember).where(
|
||||
CompanyMember.company_id == company.id,
|
||||
CompanyMember.status == "active",
|
||||
)
|
||||
)).all()
|
||||
for cm in cms:
|
||||
member_ids.add(cm.user_id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. 批量查询每个成员的用户资料(名称/头像)
|
||||
user_profiles: dict[str, dict] = {}
|
||||
for uid in member_ids:
|
||||
u = await db.users.get_by_id(uid)
|
||||
if u:
|
||||
user_profiles[uid] = u
|
||||
|
||||
# 4. 批量查询每个成员的可外部调用智能体(port=opc)
|
||||
from ...im import client as im_client
|
||||
result: list[dict] = []
|
||||
for uid in sorted(member_ids):
|
||||
profile = user_profiles.get(uid) or {}
|
||||
agents = await db.agents.get_by_user(uid, port="opc")
|
||||
# 过滤:自己的全部展示;他人的仅展示 external_callable=True
|
||||
visible_agents = []
|
||||
for a in agents:
|
||||
if uid == caller_id or is_admin or a.get("external_callable"):
|
||||
visible_agents.append(a)
|
||||
if not visible_agents:
|
||||
continue
|
||||
|
||||
agent_items = []
|
||||
for a in visible_agents:
|
||||
addr = f"{uid}.{a['id']}"
|
||||
# 在线状态查询(IM 内部接口,失败视为离线)
|
||||
online = False
|
||||
try:
|
||||
online_info = await im_client.internal_agent_online(addr)
|
||||
online = bool(online_info.get("online"))
|
||||
except Exception:
|
||||
pass
|
||||
agent_items.append({
|
||||
"agent_id": a["id"],
|
||||
"name": a["name"],
|
||||
"avatar": "", # 智能体头像暂由前端默认占位
|
||||
"description": a.get("description", ""),
|
||||
"call_mode": a.get("call_mode", "auto"),
|
||||
"external_callable": bool(a.get("external_callable")),
|
||||
"online": online,
|
||||
"is_own": uid == caller_id,
|
||||
})
|
||||
|
||||
result.append({
|
||||
"user_id": uid,
|
||||
"name": profile.get("name") or profile.get("nickname") or profile.get("username") or uid,
|
||||
"avatar": profile.get("avatar", ""),
|
||||
"is_admin": bool(profile.get("is_admin")) or (uid == caller_id and is_admin),
|
||||
"agents": agent_items,
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user