| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- from fastapi import APIRouter, HTTPException
- from pydantic import BaseModel
- from typing import Optional
- from app.graphs.chat_graph import create_chat_graph
- from app.graphs.analysis_graph import create_analysis_graph
- import uuid
- import time
- router = APIRouter(prefix="/api/v1", tags=["adapter"])
- # ---- Dify-compatible request/response models ----
- class DifyMessage(BaseModel):
- role: str
- content: str
- class DifyChatRequest(BaseModel):
- query: str = ""
- user: str = "anonymous"
- conversation_id: str = ""
- messages: list[DifyMessage] = []
- inputs: dict = {}
- response_mode: str = "blocking" # blocking / streaming
- user_id: str = ""
- bot_name: str = "AI管家"
- class DifyAnalysisRequest(BaseModel):
- report_id: Optional[int] = None
- user_id: Optional[str] = None
- focus: Optional[str] = None
- messages: list[DifyMessage] = []
- class DifyChoice(BaseModel):
- index: int
- message: dict
- finish_reason: str = "stop"
- class DifyUsage(BaseModel):
- prompt_tokens: int = 0
- completion_tokens: int = 0
- total_tokens: int = 0
- class DifyResponse(BaseModel):
- id: str
- object: str = "chat.completion"
- created: int
- model: str = "langgraph-cfc"
- choices: list[DifyChoice]
- usage: DifyUsage = DifyUsage()
- metadata: dict = {}
- # ---- Helpers ----
- def _now_ts() -> int:
- return int(time.time())
- def _extract_query(req: DifyChatRequest) -> str:
- if req.query:
- return req.query
- # fallback: take last user message
- for msg in reversed(req.messages):
- if msg.role == "user":
- return msg.content
- return ""
- def _to_langgraph_context(req: DifyChatRequest) -> dict:
- ctx = {}
- if isinstance(req.inputs, dict):
- ctx["child_id"] = req.inputs.get("child_id")
- ctx["report_id"] = req.inputs.get("report_id")
- ctx["family_id"] = req.inputs.get("family_id")
- return ctx
- # ---- Dify-compatible endpoints ----
- @router.post("/chat/completion", response_model=DifyResponse)
- async def chat_completion(req: DifyChatRequest):
- query = _extract_query(req)
- if not query:
- raise HTTPException(status_code=400, detail="query 为空")
- graph = create_chat_graph()
- initial_state = {
- "query": query,
- "user_id": int(req.user_id) if str(req.user_id).isdigit() else 0,
- "conversation_id": req.conversation_id or None,
- "intent": None,
- "context": _to_langgraph_context(req),
- "messages": None,
- "answer": None,
- "tasks": [],
- "sources": [],
- }
- config = {
- "configurable": {"thread_id": req.conversation_id or str(req.user_id or "default")},
- }
- result = await graph.ainvoke(initial_state, config)
- answer = result.get("answer") or ""
- return DifyResponse(
- id=f"chatcmpl-{uuid.uuid4().hex[:24]}",
- created=_now_ts(),
- choices=[
- DifyChoice(
- index=0,
- message={"role": "assistant", "content": answer},
- finish_reason="stop",
- )
- ],
- usage=DifyUsage(
- prompt_tokens=len(query.split()),
- completion_tokens=len(answer.split()),
- total_tokens=len(query.split()) + len(answer.split()),
- ),
- )
- @router.post("/analysis/run", response_model=DifyResponse)
- async def analysis_run(req: DifyAnalysisRequest):
- graph = create_analysis_graph()
- report_id = req.report_id
- if report_id is None and isinstance(req.inputs, dict):
- report_id = req.inputs.get("report_id")
- initial_state = {
- "report_id": report_id,
- "user_id": int(req.user_id) if str(req.user_id).isdigit() else 0,
- "focus": req.focus,
- "report_data": None,
- "survey_data": None,
- "dimension_scores": None,
- "analysis": None,
- "recommendations": [],
- }
- result = await graph.ainvoke(initial_state, {})
- analysis = result.get("analysis") or ""
- recommendations = result.get("recommendations") or []
- content = analysis
- if recommendations:
- content += "\n\n建议:\n" + "\n".join(f"- {r}" for r in recommendations)
- return DifyResponse(
- id=f"analysis-{uuid.uuid4().hex[:24]}",
- created=_now_ts(),
- choices=[
- DifyChoice(
- index=0,
- message={"role": "assistant", "content": content},
- finish_reason="stop",
- )
- ],
- usage=DifyUsage(
- prompt_tokens=len((req.focus or "").split()),
- completion_tokens=len(content.split()),
- total_tokens=len((req.focus or "").split()) + len(content.split()),
- ),
- )
|