Add backend api and engine
This commit is contained in:
11
api/app/routers/__init__.py
Normal file
11
api/app/routers/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import assistants
|
||||
from . import history
|
||||
from . import knowledge
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(assistants.router)
|
||||
router.include_router(history.router)
|
||||
router.include_router(knowledge.router)
|
||||
157
api/app/routers/assistants.py
Normal file
157
api/app/routers/assistants.py
Normal file
@@ -0,0 +1,157 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from ..db import get_db
|
||||
from ..models import Assistant, Voice, Workflow
|
||||
from ..schemas import (
|
||||
AssistantCreate, AssistantUpdate, AssistantOut,
|
||||
VoiceOut,
|
||||
WorkflowCreate, WorkflowUpdate, WorkflowOut
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# ============ Voices ============
|
||||
@router.get("/voices", response_model=List[VoiceOut])
|
||||
def list_voices(db: Session = Depends(get_db)):
|
||||
"""获取声音库列表"""
|
||||
voices = db.query(Voice).all()
|
||||
return voices
|
||||
|
||||
|
||||
# ============ Assistants ============
|
||||
@router.get("/assistants")
|
||||
def list_assistants(
|
||||
page: int = 1,
|
||||
limit: int = 50,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取助手列表"""
|
||||
query = db.query(Assistant)
|
||||
total = query.count()
|
||||
assistants = query.order_by(Assistant.created_at.desc()) \
|
||||
.offset((page-1)*limit).limit(limit).all()
|
||||
return {"total": total, "page": page, "limit": limit, "list": assistants}
|
||||
|
||||
|
||||
@router.get("/assistants/{id}", response_model=AssistantOut)
|
||||
def get_assistant(id: str, db: Session = Depends(get_db)):
|
||||
"""获取单个助手详情"""
|
||||
assistant = db.query(Assistant).filter(Assistant.id == id).first()
|
||||
if not assistant:
|
||||
raise HTTPException(status_code=404, detail="Assistant not found")
|
||||
return assistant
|
||||
|
||||
|
||||
@router.post("/assistants", response_model=AssistantOut)
|
||||
def create_assistant(data: AssistantCreate, db: Session = Depends(get_db)):
|
||||
"""创建新助手"""
|
||||
assistant = Assistant(
|
||||
id=str(uuid.uuid4())[:8],
|
||||
user_id=1, # 默认用户,后续添加认证
|
||||
name=data.name,
|
||||
opener=data.opener,
|
||||
prompt=data.prompt,
|
||||
knowledge_base_id=data.knowledgeBaseId,
|
||||
language=data.language,
|
||||
voice=data.voice,
|
||||
speed=data.speed,
|
||||
hotwords=data.hotwords,
|
||||
tools=data.tools,
|
||||
interruption_sensitivity=data.interruptionSensitivity,
|
||||
config_mode=data.configMode,
|
||||
api_url=data.apiUrl,
|
||||
api_key=data.apiKey,
|
||||
)
|
||||
db.add(assistant)
|
||||
db.commit()
|
||||
db.refresh(assistant)
|
||||
return assistant
|
||||
|
||||
|
||||
@router.put("/assistants/{id}")
|
||||
def update_assistant(id: str, data: AssistantUpdate, db: Session = Depends(get_db)):
|
||||
"""更新助手"""
|
||||
assistant = db.query(Assistant).filter(Assistant.id == id).first()
|
||||
if not assistant:
|
||||
raise HTTPException(status_code=404, detail="Assistant not found")
|
||||
|
||||
update_data = data.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(assistant, field, value)
|
||||
|
||||
assistant.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(assistant)
|
||||
return assistant
|
||||
|
||||
|
||||
@router.delete("/assistants/{id}")
|
||||
def delete_assistant(id: str, db: Session = Depends(get_db)):
|
||||
"""删除助手"""
|
||||
assistant = db.query(Assistant).filter(Assistant.id == id).first()
|
||||
if not assistant:
|
||||
raise HTTPException(status_code=404, detail="Assistant not found")
|
||||
db.delete(assistant)
|
||||
db.commit()
|
||||
return {"message": "Deleted successfully"}
|
||||
|
||||
|
||||
# ============ Workflows ============
|
||||
@router.get("/workflows", response_model=List[WorkflowOut])
|
||||
def list_workflows(db: Session = Depends(get_db)):
|
||||
"""获取工作流列表"""
|
||||
workflows = db.query(Workflow).all()
|
||||
return workflows
|
||||
|
||||
|
||||
@router.post("/workflows", response_model=WorkflowOut)
|
||||
def create_workflow(data: WorkflowCreate, db: Session = Depends(get_db)):
|
||||
"""创建工作流"""
|
||||
workflow = Workflow(
|
||||
id=str(uuid.uuid4())[:8],
|
||||
user_id=1,
|
||||
name=data.name,
|
||||
node_count=data.nodeCount,
|
||||
created_at=data.createdAt or datetime.utcnow().isoformat(),
|
||||
updated_at=data.updatedAt or "",
|
||||
global_prompt=data.globalPrompt,
|
||||
nodes=data.nodes,
|
||||
edges=data.edges,
|
||||
)
|
||||
db.add(workflow)
|
||||
db.commit()
|
||||
db.refresh(workflow)
|
||||
return workflow
|
||||
|
||||
|
||||
@router.put("/workflows/{id}", response_model=WorkflowOut)
|
||||
def update_workflow(id: str, data: WorkflowUpdate, db: Session = Depends(get_db)):
|
||||
"""更新工作流"""
|
||||
workflow = db.query(Workflow).filter(Workflow.id == id).first()
|
||||
if not workflow:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
|
||||
update_data = data.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(workflow, field, value)
|
||||
|
||||
workflow.updated_at = datetime.utcnow().isoformat()
|
||||
db.commit()
|
||||
db.refresh(workflow)
|
||||
return workflow
|
||||
|
||||
|
||||
@router.delete("/workflows/{id}")
|
||||
def delete_workflow(id: str, db: Session = Depends(get_db)):
|
||||
"""删除工作流"""
|
||||
workflow = db.query(Workflow).filter(Workflow.id == id).first()
|
||||
if not workflow:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
db.delete(workflow)
|
||||
db.commit()
|
||||
return {"message": "Deleted successfully"}
|
||||
188
api/app/routers/history.py
Normal file
188
api/app/routers/history.py
Normal file
@@ -0,0 +1,188 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional, List
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from ..db import get_db
|
||||
from ..models import CallRecord, CallTranscript, CallAudioSegment
|
||||
from ..storage import get_audio_url
|
||||
|
||||
router = APIRouter(prefix="/history", tags=["history"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_history(
|
||||
assistant_id: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
page: int = 1,
|
||||
limit: int = 20,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取通话记录列表"""
|
||||
query = db.query(CallRecord)
|
||||
|
||||
if assistant_id:
|
||||
query = query.filter(CallRecord.assistant_id == assistant_id)
|
||||
if status:
|
||||
query = query.filter(CallRecord.status == status)
|
||||
|
||||
total = query.count()
|
||||
records = query.order_by(CallRecord.started_at.desc()) \
|
||||
.offset((page-1)*limit).limit(limit).all()
|
||||
|
||||
return {"total": total, "page": page, "limit": limit, "list": records}
|
||||
|
||||
|
||||
@router.get("/{call_id}")
|
||||
def get_history_detail(call_id: str, db: Session = Depends(get_db)):
|
||||
"""获取通话详情"""
|
||||
record = db.query(CallRecord).filter(CallRecord.id == call_id).first()
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Call record not found")
|
||||
|
||||
# 获取转写
|
||||
transcripts = db.query(CallTranscript) \
|
||||
.filter(CallTranscript.call_id == call_id) \
|
||||
.order_by(CallTranscript.turn_index).all()
|
||||
|
||||
# 补充音频 URL
|
||||
transcript_list = []
|
||||
for t in transcripts:
|
||||
audio_url = t.audio_url or get_audio_url(call_id, t.turn_index)
|
||||
transcript_list.append({
|
||||
"turnIndex": t.turn_index,
|
||||
"speaker": t.speaker,
|
||||
"content": t.content,
|
||||
"confidence": t.confidence,
|
||||
"startMs": t.start_ms,
|
||||
"endMs": t.end_ms,
|
||||
"durationMs": t.duration_ms,
|
||||
"audioUrl": audio_url,
|
||||
})
|
||||
|
||||
return {
|
||||
"id": record.id,
|
||||
"user_id": record.user_id,
|
||||
"assistant_id": record.assistant_id,
|
||||
"source": record.source,
|
||||
"status": record.status,
|
||||
"started_at": record.started_at,
|
||||
"ended_at": record.ended_at,
|
||||
"duration_seconds": record.duration_seconds,
|
||||
"summary": record.summary,
|
||||
"transcripts": transcript_list,
|
||||
}
|
||||
|
||||
|
||||
@router.post("")
|
||||
def create_call_record(
|
||||
user_id: int,
|
||||
assistant_id: Optional[str] = None,
|
||||
source: str = "debug",
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建通话记录(引擎回调使用)"""
|
||||
record = CallRecord(
|
||||
id=str(uuid.uuid4())[:8],
|
||||
user_id=user_id,
|
||||
assistant_id=assistant_id,
|
||||
source=source,
|
||||
status="connected",
|
||||
started_at=datetime.utcnow().isoformat(),
|
||||
)
|
||||
db.add(record)
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
return record
|
||||
|
||||
|
||||
@router.put("/{call_id}")
|
||||
def update_call_record(
|
||||
call_id: str,
|
||||
status: Optional[str] = None,
|
||||
summary: Optional[str] = None,
|
||||
duration_seconds: Optional[int] = None,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""更新通话记录"""
|
||||
record = db.query(CallRecord).filter(CallRecord.id == call_id).first()
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Call record not found")
|
||||
|
||||
if status:
|
||||
record.status = status
|
||||
if summary:
|
||||
record.summary = summary
|
||||
if duration_seconds:
|
||||
record.duration_seconds = duration_seconds
|
||||
record.ended_at = datetime.utcnow().isoformat()
|
||||
|
||||
db.commit()
|
||||
return {"message": "Updated successfully"}
|
||||
|
||||
|
||||
@router.post("/{call_id}/transcripts")
|
||||
def add_transcript(
|
||||
call_id: str,
|
||||
turn_index: int,
|
||||
speaker: str,
|
||||
content: str,
|
||||
start_ms: int,
|
||||
end_ms: int,
|
||||
confidence: Optional[float] = None,
|
||||
duration_ms: Optional[int] = None,
|
||||
emotion: Optional[str] = None,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""添加转写片段"""
|
||||
transcript = CallTranscript(
|
||||
call_id=call_id,
|
||||
turn_index=turn_index,
|
||||
speaker=speaker,
|
||||
content=content,
|
||||
confidence=confidence,
|
||||
start_ms=start_ms,
|
||||
end_ms=end_ms,
|
||||
duration_ms=duration_ms,
|
||||
emotion=emotion,
|
||||
)
|
||||
db.add(transcript)
|
||||
db.commit()
|
||||
db.refresh(transcript)
|
||||
|
||||
# 补充音频 URL
|
||||
audio_url = get_audio_url(call_id, turn_index)
|
||||
|
||||
return {
|
||||
"id": transcript.id,
|
||||
"turn_index": turn_index,
|
||||
"speaker": speaker,
|
||||
"content": content,
|
||||
"confidence": confidence,
|
||||
"start_ms": start_ms,
|
||||
"end_ms": end_ms,
|
||||
"duration_ms": duration_ms,
|
||||
"audio_url": audio_url,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{call_id}/audio/{turn_index}")
|
||||
def get_audio(call_id: str, turn_index: int):
|
||||
"""获取音频文件"""
|
||||
audio_url = get_audio_url(call_id, turn_index)
|
||||
if not audio_url:
|
||||
raise HTTPException(status_code=404, detail="Audio not found")
|
||||
from fastapi.responses import RedirectResponse
|
||||
return RedirectResponse(audio_url)
|
||||
|
||||
|
||||
@router.delete("/{call_id}")
|
||||
def delete_call_record(call_id: str, db: Session = Depends(get_db)):
|
||||
"""删除通话记录"""
|
||||
record = db.query(CallRecord).filter(CallRecord.id == call_id).first()
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Call record not found")
|
||||
db.delete(record)
|
||||
db.commit()
|
||||
return {"message": "Deleted successfully"}
|
||||
234
api/app/routers/knowledge.py
Normal file
234
api/app/routers/knowledge.py
Normal file
@@ -0,0 +1,234 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
import uuid
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from ..db import get_db
|
||||
from ..models import KnowledgeBase, KnowledgeDocument
|
||||
from ..schemas import (
|
||||
KnowledgeBaseCreate, KnowledgeBaseUpdate, KnowledgeBaseOut,
|
||||
KnowledgeSearchQuery, KnowledgeSearchResult, KnowledgeStats,
|
||||
DocumentIndexRequest,
|
||||
)
|
||||
from ..vector_store import (
|
||||
vector_store, search_knowledge, index_document, delete_document_from_vector
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/knowledge", tags=["knowledge"])
|
||||
|
||||
|
||||
def kb_to_dict(kb: KnowledgeBase) -> dict:
|
||||
return {
|
||||
"id": kb.id,
|
||||
"user_id": kb.user_id,
|
||||
"name": kb.name,
|
||||
"description": kb.description,
|
||||
"embedding_model": kb.embedding_model,
|
||||
"chunk_size": kb.chunk_size,
|
||||
"chunk_overlap": kb.chunk_overlap,
|
||||
"doc_count": kb.doc_count,
|
||||
"chunk_count": kb.chunk_count,
|
||||
"status": kb.status,
|
||||
"created_at": kb.created_at.isoformat() if kb.created_at else None,
|
||||
"updated_at": kb.updated_at.isoformat() if kb.updated_at else None,
|
||||
}
|
||||
|
||||
|
||||
def doc_to_dict(d: KnowledgeDocument) -> dict:
|
||||
return {
|
||||
"id": d.id,
|
||||
"kb_id": d.kb_id,
|
||||
"name": d.name,
|
||||
"size": d.size,
|
||||
"file_type": d.file_type,
|
||||
"storage_url": d.storage_url,
|
||||
"status": d.status,
|
||||
"chunk_count": d.chunk_count,
|
||||
"error_message": d.error_message,
|
||||
"upload_date": d.upload_date,
|
||||
"created_at": d.created_at.isoformat() if d.created_at else None,
|
||||
"processed_at": d.processed_at.isoformat() if d.processed_at else None,
|
||||
}
|
||||
|
||||
|
||||
# ============ Knowledge Bases ============
|
||||
@router.get("/bases")
|
||||
def list_knowledge_bases(user_id: int = 1, db: Session = Depends(get_db)):
|
||||
kbs = db.query(KnowledgeBase).filter(KnowledgeBase.user_id == user_id).all()
|
||||
result = []
|
||||
for kb in kbs:
|
||||
docs = db.query(KnowledgeDocument).filter(KnowledgeDocument.kb_id == kb.id).all()
|
||||
kb_data = kb_to_dict(kb)
|
||||
kb_data["documents"] = [doc_to_dict(d) for d in docs]
|
||||
result.append(kb_data)
|
||||
return {"total": len(result), "list": result}
|
||||
|
||||
|
||||
@router.get("/bases/{kb_id}")
|
||||
def get_knowledge_base(kb_id: str, db: Session = Depends(get_db)):
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
if not kb:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base not found")
|
||||
docs = db.query(KnowledgeDocument).filter(KnowledgeDocument.kb_id == kb_id).all()
|
||||
kb_data = kb_to_dict(kb)
|
||||
kb_data["documents"] = [doc_to_dict(d) for d in docs]
|
||||
return kb_data
|
||||
|
||||
|
||||
@router.post("/bases")
|
||||
def create_knowledge_base(data: KnowledgeBaseCreate, user_id: int = 1, db: Session = Depends(get_db)):
|
||||
kb = KnowledgeBase(
|
||||
id=str(uuid.uuid4())[:8],
|
||||
user_id=user_id,
|
||||
name=data.name,
|
||||
description=data.description,
|
||||
embedding_model=data.embeddingModel,
|
||||
chunk_size=data.chunkSize,
|
||||
chunk_overlap=data.chunkOverlap,
|
||||
)
|
||||
db.add(kb)
|
||||
db.commit()
|
||||
db.refresh(kb)
|
||||
vector_store.create_collection(kb.id, data.embeddingModel)
|
||||
return kb_to_dict(kb)
|
||||
|
||||
|
||||
@router.put("/bases/{kb_id}")
|
||||
def update_knowledge_base(kb_id: str, data: KnowledgeBaseUpdate, db: Session = Depends(get_db)):
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
if not kb:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base not found")
|
||||
update_data = data.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(kb, field, value)
|
||||
kb.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(kb)
|
||||
return kb_to_dict(kb)
|
||||
|
||||
|
||||
@router.delete("/bases/{kb_id}")
|
||||
def delete_knowledge_base(kb_id: str, db: Session = Depends(get_db)):
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
if not kb:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base not found")
|
||||
vector_store.delete_collection(kb_id)
|
||||
docs = db.query(KnowledgeDocument).filter(KnowledgeDocument.kb_id == kb_id).all()
|
||||
for doc in docs:
|
||||
db.delete(doc)
|
||||
db.delete(kb)
|
||||
db.commit()
|
||||
return {"message": "Deleted successfully"}
|
||||
|
||||
|
||||
# ============ Documents ============
|
||||
@router.post("/bases/{kb_id}/documents")
|
||||
def upload_document(
|
||||
kb_id: str,
|
||||
name: str = Query(...),
|
||||
size: str = Query(...),
|
||||
file_type: str = Query("txt"),
|
||||
storage_url: Optional[str] = Query(None),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
if not kb:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base not found")
|
||||
doc = KnowledgeDocument(
|
||||
id=str(uuid.uuid4())[:8],
|
||||
kb_id=kb_id,
|
||||
name=name,
|
||||
size=size,
|
||||
file_type=file_type,
|
||||
storage_url=storage_url,
|
||||
status="pending",
|
||||
upload_date=datetime.utcnow().isoformat()
|
||||
)
|
||||
db.add(doc)
|
||||
db.commit()
|
||||
db.refresh(doc)
|
||||
return {"id": doc.id, "name": doc.name, "status": doc.status, "message": "Document created"}
|
||||
|
||||
|
||||
@router.post("/bases/{kb_id}/documents/{doc_id}/index")
|
||||
def index_document_content(kb_id: str, doc_id: str, request: DocumentIndexRequest, db: Session = Depends(get_db)):
|
||||
# 检查文档是否存在,不存在则创建
|
||||
doc = db.query(KnowledgeDocument).filter(
|
||||
KnowledgeDocument.id == doc_id,
|
||||
KnowledgeDocument.kb_id == kb_id
|
||||
).first()
|
||||
|
||||
if not doc:
|
||||
doc = KnowledgeDocument(
|
||||
id=doc_id,
|
||||
kb_id=kb_id,
|
||||
name=f"doc-{doc_id}.txt",
|
||||
size=str(len(request.content)),
|
||||
file_type="txt",
|
||||
status="pending",
|
||||
upload_date=datetime.utcnow().isoformat()
|
||||
)
|
||||
db.add(doc)
|
||||
db.commit()
|
||||
db.refresh(doc)
|
||||
else:
|
||||
# 更新已有文档
|
||||
doc.size = str(len(request.content))
|
||||
doc.status = "pending"
|
||||
db.commit()
|
||||
|
||||
try:
|
||||
chunk_count = index_document(kb_id, doc_id, request.content)
|
||||
doc.status = "completed"
|
||||
doc.chunk_count = chunk_count
|
||||
doc.processed_at = datetime.utcnow()
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
kb.doc_count = db.query(KnowledgeDocument).filter(
|
||||
KnowledgeDocument.kb_id == kb_id,
|
||||
KnowledgeDocument.status == "completed"
|
||||
).count()
|
||||
kb.chunk_count += chunk_count
|
||||
db.commit()
|
||||
return {"message": "Document indexed", "chunkCount": chunk_count}
|
||||
except Exception as e:
|
||||
doc.status = "failed"
|
||||
doc.error_message = str(e)
|
||||
db.commit()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/bases/{kb_id}/documents/{doc_id}")
|
||||
def delete_document(kb_id: str, doc_id: str, db: Session = Depends(get_db)):
|
||||
doc = db.query(KnowledgeDocument).filter(
|
||||
KnowledgeDocument.id == doc_id,
|
||||
KnowledgeDocument.kb_id == kb_id
|
||||
).first()
|
||||
if not doc:
|
||||
raise HTTPException(status_code=404, detail="Document not found")
|
||||
try:
|
||||
delete_document_from_vector(kb_id, doc_id)
|
||||
except Exception:
|
||||
pass
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
kb.chunk_count -= doc.chunk_count
|
||||
kb.doc_count -= 1
|
||||
db.delete(doc)
|
||||
db.commit()
|
||||
return {"message": "Deleted successfully"}
|
||||
|
||||
|
||||
# ============ Search ============
|
||||
@router.post("/search")
|
||||
def search_knowledge_base(query: KnowledgeSearchQuery):
|
||||
return search_knowledge(kb_id=query.kb_id, query=query.query, n_results=query.nResults)
|
||||
|
||||
|
||||
# ============ Stats ============
|
||||
@router.get("/bases/{kb_id}/stats")
|
||||
def get_knowledge_stats(kb_id: str, db: Session = Depends(get_db)):
|
||||
kb = db.query(KnowledgeBase).filter(KnowledgeBase.id == kb_id).first()
|
||||
if not kb:
|
||||
raise HTTPException(status_code=404, detail="Knowledge base not found")
|
||||
return {"kb_id": kb_id, "docCount": kb.doc_count, "chunkCount": kb.chunk_count}
|
||||
Reference in New Issue
Block a user