320 lines
4.3 KiB
Python
320 lines
4.3 KiB
Python
# Auto-extracted from chat.py during Phase 1 refactor (Strangler Fig).
|
|
# Source of truth: scripts/phase1_extract.py. Do not hand-merge back.
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import and_, desc, asc, func
|
|
from pydantic import BaseModel, Field
|
|
from typing import Optional
|
|
import uuid
|
|
from datetime import datetime, timezone, timedelta
|
|
import re
|
|
import json
|
|
import asyncio
|
|
import math
|
|
import httpx
|
|
import os
|
|
import logging
|
|
from ..database import get_db, SessionLocal
|
|
from ..models import (
|
|
|
|
|
|
|
|
User, Conversation, Message, AIModel, ModelProvider, TokenUsageLog,
|
|
|
|
|
|
|
|
KnowledgeBase, KnowledgeDocument, KnowledgeDocumentChunk, EvolutionSkill,
|
|
|
|
|
|
|
|
EvolutionTrajectory, MarketApp, AppPurchase, AppInstallation,
|
|
|
|
|
|
|
|
MCPServer,
|
|
|
|
|
|
|
|
gen_uuid
|
|
|
|
|
|
|
|
)
|
|
from ..auth import get_current_user
|
|
from ..platform_knowledge import get_platform_knowledge
|
|
from ..mcp_client import list_mcp_tools, call_mcp_tool
|
|
from ..middleware.log_service import LogService
|
|
from ..capability_registry import resolve_auto_model
|
|
from app.api.chat_utils import (cst_now)
|
|
|
|
def _build_knowledge_graph(db: Session, kb: KnowledgeBase) -> dict:
|
|
|
|
|
|
|
|
"""从知识库文档构建知识图谱节点和边,返回 graph_data。"""
|
|
|
|
|
|
|
|
documents = db.query(KnowledgeDocument).filter(
|
|
|
|
|
|
|
|
and_(
|
|
|
|
|
|
|
|
KnowledgeDocument.knowledge_base_id == kb.id,
|
|
|
|
|
|
|
|
KnowledgeDocument.status == "completed",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
).all()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
graph_nodes = []
|
|
|
|
|
|
|
|
graph_edges = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
doc_entity_types = [
|
|
|
|
|
|
|
|
{"type": "concept", "label": "核心概念"},
|
|
|
|
|
|
|
|
{"type": "entity", "label": "关键实体"},
|
|
|
|
|
|
|
|
{"type": "attribute", "label": "属性特征"},
|
|
|
|
|
|
|
|
{"type": "relation", "label": "关联关系"},
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for idx, doc in enumerate(documents):
|
|
|
|
|
|
|
|
doc_node_id = f"doc-{doc.id[:8]}"
|
|
|
|
|
|
|
|
graph_nodes.append({
|
|
|
|
|
|
|
|
"id": doc_node_id, "label": doc.filename, "type": "document", "size": 18, "group": "document",
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
filename_base = doc.filename.rsplit(".", 1)[0]
|
|
|
|
|
|
|
|
words = filename_base.replace("_", " ").replace("-", " ").split()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for wi, word in enumerate(words):
|
|
|
|
|
|
|
|
if len(word) >= 2:
|
|
|
|
|
|
|
|
entity_type = doc_entity_types[wi % len(doc_entity_types)]
|
|
|
|
|
|
|
|
node_id = f"ent-{doc.id[:8]}-{wi}"
|
|
|
|
|
|
|
|
graph_nodes.append({
|
|
|
|
|
|
|
|
"id": node_id, "label": word, "type": entity_type["type"],
|
|
|
|
|
|
|
|
"size": 10 + (wi % 8), "group": entity_type["type"],
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
graph_edges.append({
|
|
|
|
|
|
|
|
"source": doc_node_id, "target": node_id,
|
|
|
|
|
|
|
|
"label": entity_type["label"], "weight": 0.5 + (0.5 / (wi + 1)),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i in range(len(documents)):
|
|
|
|
|
|
|
|
for j in range(i + 1, len(documents)):
|
|
|
|
|
|
|
|
fi = documents[i].filename.lower().rsplit(".", 1)[0]
|
|
|
|
|
|
|
|
fj = documents[j].filename.lower().rsplit(".", 1)[0]
|
|
|
|
|
|
|
|
words_i = set(fi.replace("_", " ").replace("-", " ").split())
|
|
|
|
|
|
|
|
words_j = set(fj.replace("_", " ").replace("-", " ").split())
|
|
|
|
|
|
|
|
common = words_i & words_j
|
|
|
|
|
|
|
|
if common and len(common) > 0:
|
|
|
|
|
|
|
|
graph_edges.append({
|
|
|
|
|
|
|
|
"source": f"doc-{documents[i].id[:8]}",
|
|
|
|
|
|
|
|
"target": f"doc-{documents[j].id[:8]}",
|
|
|
|
|
|
|
|
"label": f"共有关键词: {', '.join(list(common)[:3])}",
|
|
|
|
|
|
|
|
"weight": min(0.9, 0.3 * len(common)),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
graph_data = {
|
|
|
|
|
|
|
|
"nodes": graph_nodes,
|
|
|
|
|
|
|
|
"edges": graph_edges,
|
|
|
|
|
|
|
|
"stats": {
|
|
|
|
|
|
|
|
"total_nodes": len(graph_nodes), "total_edges": len(graph_edges),
|
|
|
|
|
|
|
|
"document_count": len(documents),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kb.knowledge_graph_count = len(graph_nodes)
|
|
|
|
|
|
|
|
kb.updated_at = cst_now()
|
|
|
|
|
|
|
|
return graph_data
|