Add CRUD functionality for knowledge bases, including routes for listing, creating, updating, and deleting knowledge bases. Update the assistant model to include foreign key references to knowledge bases and modify the assistant configuration to handle external API keys securely. Refactor related services and routes to accommodate these changes, ensuring proper handling of credential resolution and configuration normalization.
91 lines
2.8 KiB
Python
91 lines
2.8 KiB
Python
"""知识库 CRUD。前端助手编辑页的"知识库"下拉对接这里。
|
|
|
|
KB 自身引用一个 Embedding 凭证(embeddingCredentialId)。被助手引用时禁止删除
|
|
(DB 层 ON DELETE RESTRICT),这里把外键冲突翻译成 409。
|
|
"""
|
|
|
|
import uuid
|
|
|
|
from db.models import KnowledgeBase
|
|
from db.session import get_session
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from schemas import KnowledgeBaseOut, KnowledgeBaseUpsert
|
|
from sqlalchemy import select
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
router = APIRouter(prefix="/api/knowledge-bases", tags=["knowledge-bases"])
|
|
|
|
|
|
def _to_out(kb: KnowledgeBase) -> KnowledgeBaseOut:
|
|
return KnowledgeBaseOut(
|
|
id=kb.id,
|
|
name=kb.name,
|
|
description=kb.description,
|
|
embedding_credential_id=kb.embedding_credential_id,
|
|
status=kb.status,
|
|
updated_at=kb.updated_at.isoformat() if kb.updated_at else None,
|
|
)
|
|
|
|
|
|
@router.get("", response_model=list[KnowledgeBaseOut])
|
|
async def list_knowledge_bases(session: AsyncSession = Depends(get_session)):
|
|
rows = (
|
|
await session.execute(select(KnowledgeBase).order_by(KnowledgeBase.name))
|
|
).scalars().all()
|
|
return [_to_out(kb) for kb in rows]
|
|
|
|
|
|
@router.post("", response_model=KnowledgeBaseOut)
|
|
async def create_knowledge_base(
|
|
body: KnowledgeBaseUpsert, session: AsyncSession = Depends(get_session)
|
|
):
|
|
kb = KnowledgeBase(id=f"kb_{uuid.uuid4().hex[:12]}", **body.model_dump())
|
|
session.add(kb)
|
|
await session.commit()
|
|
await session.refresh(kb)
|
|
return _to_out(kb)
|
|
|
|
|
|
@router.get("/{kb_id}", response_model=KnowledgeBaseOut)
|
|
async def get_knowledge_base(
|
|
kb_id: str, session: AsyncSession = Depends(get_session)
|
|
):
|
|
kb = await session.get(KnowledgeBase, kb_id)
|
|
if not kb:
|
|
raise HTTPException(404, "知识库不存在")
|
|
return _to_out(kb)
|
|
|
|
|
|
@router.put("/{kb_id}", response_model=KnowledgeBaseOut)
|
|
async def update_knowledge_base(
|
|
kb_id: str,
|
|
body: KnowledgeBaseUpsert,
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
kb = await session.get(KnowledgeBase, kb_id)
|
|
if not kb:
|
|
raise HTTPException(404, "知识库不存在")
|
|
for k, v in body.model_dump().items():
|
|
setattr(kb, k, v)
|
|
await session.commit()
|
|
await session.refresh(kb)
|
|
return _to_out(kb)
|
|
|
|
|
|
@router.delete("/{kb_id}")
|
|
async def delete_knowledge_base(
|
|
kb_id: str, session: AsyncSession = Depends(get_session)
|
|
):
|
|
kb = await session.get(KnowledgeBase, kb_id)
|
|
if not kb:
|
|
raise HTTPException(404, "知识库不存在")
|
|
try:
|
|
await session.delete(kb)
|
|
await session.commit()
|
|
except IntegrityError:
|
|
# 被助手引用(ON DELETE RESTRICT):先解绑再删
|
|
await session.rollback()
|
|
raise HTTPException(409, "知识库正被助手引用,无法删除")
|
|
return {"ok": True}
|