咨询台接 Ragflow 检索与示例知识库,支持追问、判定、引用和留资。 Co-authored-by: haijd <haijd@163.com>
153 lines
4.4 KiB
Python
153 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI, HTTPException
|
|
from fastapi.responses import FileResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from pydantic import BaseModel, Field
|
|
|
|
from .config_loader import business_config
|
|
from .leads import add_lead, list_leads
|
|
from .ragflow_client import RagflowClient, RagflowError
|
|
from .scheme_engine import create_session, handle_message
|
|
from .session_store import store
|
|
from .settings import ROOT, settings
|
|
|
|
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
|
CORPUS_DIR = ROOT / "knowledge" / "corpus"
|
|
|
|
app = FastAPI(title=settings.app_name, version="0.2.0")
|
|
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
|
|
|
|
|
class ChatRequest(BaseModel):
|
|
message: str = Field(..., min_length=1)
|
|
session_id: str | None = None
|
|
|
|
|
|
class SessionResponse(BaseModel):
|
|
session_id: str
|
|
|
|
|
|
class HandoffRequest(BaseModel):
|
|
session_id: str
|
|
name: str = Field(..., min_length=1, max_length=40)
|
|
phone: str = Field(..., min_length=5, max_length=30)
|
|
note: str = ""
|
|
|
|
|
|
@app.get("/")
|
|
def index() -> FileResponse:
|
|
return FileResponse(STATIC_DIR / "index.html")
|
|
|
|
|
|
@app.get("/api/health")
|
|
def health() -> dict:
|
|
rag = RagflowClient().health()
|
|
return {
|
|
"ok": True,
|
|
"app": settings.app_name,
|
|
"retrieval_backend": settings.retrieval_backend,
|
|
"slots": len(business_config.slots),
|
|
"rules": len(business_config.rules),
|
|
"ragflow": rag,
|
|
"ragflow_web": settings.ragflow_web_url,
|
|
"dataset_ids_configured": bool(settings.ragflow_dataset_ids.strip()),
|
|
}
|
|
|
|
|
|
@app.get("/api/config/slots")
|
|
def get_slots() -> dict:
|
|
return business_config.slots_doc
|
|
|
|
|
|
@app.get("/api/config/decision-tree")
|
|
def get_tree() -> dict:
|
|
return business_config.tree_doc
|
|
|
|
|
|
@app.post("/api/admin/reload-config")
|
|
def reload_config() -> dict:
|
|
business_config.reload()
|
|
return {"reloaded": True, "slots": len(business_config.slots), "rules": len(business_config.rules)}
|
|
|
|
|
|
@app.get("/api/knowledge/files")
|
|
def knowledge_files() -> dict:
|
|
files = []
|
|
if CORPUS_DIR.exists():
|
|
for path in sorted(CORPUS_DIR.glob("*.md")):
|
|
files.append({"name": path.name, "bytes": path.stat().st_size})
|
|
return {"files": files}
|
|
|
|
|
|
@app.get("/api/ragflow/status")
|
|
def ragflow_status() -> dict:
|
|
client = RagflowClient()
|
|
health = client.health()
|
|
datasets: list[dict] = []
|
|
error = ""
|
|
if health.get("ok") and settings.ragflow_api_key:
|
|
try:
|
|
for item in client.list_datasets():
|
|
datasets.append(
|
|
{
|
|
"id": item.get("id"),
|
|
"name": item.get("name"),
|
|
"document_count": item.get("document_count"),
|
|
"chunk_count": item.get("chunk_count"),
|
|
}
|
|
)
|
|
except Exception as exc:
|
|
error = str(exc)
|
|
return {
|
|
"health": health,
|
|
"web": settings.ragflow_web_url,
|
|
"api_key_set": bool(settings.ragflow_api_key),
|
|
"dataset_ids": [x for x in settings.ragflow_dataset_ids.split(",") if x.strip()],
|
|
"datasets": datasets,
|
|
"error": error,
|
|
}
|
|
|
|
|
|
@app.get("/api/leads")
|
|
def api_leads() -> dict:
|
|
return {"leads": list_leads()}
|
|
|
|
|
|
@app.post("/api/sessions", response_model=SessionResponse)
|
|
def api_create_session() -> SessionResponse:
|
|
session = create_session()
|
|
return SessionResponse(session_id=session.id)
|
|
|
|
|
|
@app.post("/api/chat")
|
|
def api_chat(body: ChatRequest) -> dict:
|
|
session_id = body.session_id
|
|
if not session_id:
|
|
session_id = create_session().id
|
|
try:
|
|
return handle_message(session_id, body.message)
|
|
except KeyError:
|
|
raise HTTPException(status_code=404, detail="session_not_found") from None
|
|
|
|
|
|
@app.post("/api/handoff")
|
|
def api_handoff(body: HandoffRequest) -> dict:
|
|
session = store.get(body.session_id)
|
|
slots = session.slots if session else {}
|
|
scheme = session.last_scheme if session else None
|
|
lead_id = add_lead(
|
|
session_id=body.session_id,
|
|
name=body.name.strip(),
|
|
phone=body.phone.strip(),
|
|
note=body.note.strip(),
|
|
slots_json=json.dumps(slots, ensure_ascii=False),
|
|
scheme_json=json.dumps(scheme or {}, ensure_ascii=False),
|
|
)
|
|
if session:
|
|
session.phase = "handoff"
|
|
return {"ok": True, "lead_id": lead_id}
|