38个文件已修改
1个文件已删除
5个文件已添加
1918 ■■■■ 已修改文件
app/api/__init__.py 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/agent.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/auth.py 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/files.py 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/knowledge.py 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/system.py 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/user.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/v2/chat.py 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/v2/mindmap.py 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/v2/public_api.py 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config/const.py 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config/env_conf/admin.yaml 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config/env_conf/config.yaml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config/env_conf/default_agent_conf.json 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config/env_conf/menu_conf.json 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config/env_conf/system.yaml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/init_config/init_run_data.py 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/__init__.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/dialog_model.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/knowledge_model.py 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/system.py 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/user_model.py 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/v2/chat.py 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/v2/mindmap.py 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/v2/session_model.py 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/auth.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/dialog.py 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/files.py 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/knowledge.py 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/pom/public_key.pem 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/system.py 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/user.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/v2/app_driver/chat_agent.py 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/v2/app_driver/chat_dialog.py 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/v2/chat.py 339 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/v2/initialize_data.py 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/service/v2/mindmap.py 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/task/fetch_agent.py 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/task/sync_account_token.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/task/sync_resources.py 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/task/sync_system.py 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/utils/common.py 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
requirements.txt 补丁 | 查看 | 原始文档 | blame | 历史
app/api/__init__.py
@@ -1,9 +1,11 @@
import urllib
from datetime import datetime
from typing import Callable, Any
from urllib.parse import urlencode
import jwt
# from cryptography.fernet import Fernet
from fastapi import FastAPI, Depends, HTTPException, Header
from fastapi import FastAPI, Depends, HTTPException, Header, Request
from fastapi.security import OAuth2PasswordBearer
from passlib.context import CryptContext
from pydantic import BaseModel
@@ -11,8 +13,9 @@
from starlette.websockets import WebSocket, WebSocketDisconnect
from Log import logger
from app.models.base_model import SessionLocal
# from app.models.app_model import AppRegisterModel
from app.models.user_model import UserModel
from app.models.user_model import UserModel, UserApiTokenModel
from app.service.auth import SECRET_KEY, ALGORITHM
from app.config.config import settings
@@ -35,9 +38,44 @@
    data: list[dict] = []
def verify_token(token: str) -> Any:
    """
    验证 Token 是否有效
    """
    db = SessionLocal()
    try:
        db_token = db.query(UserApiTokenModel).filter(UserApiTokenModel.token == token, UserApiTokenModel.is_active == 1).first()
        return db_token is not None and (db_token.expires_at is None or db_token.expires_at > datetime.now())
    finally:
        db.close()
def token_required()-> Callable:
    def decorated_function(request: Request)-> Any:
        authorization_str = request.headers.get("Authorization")
        if not authorization_str:
            raise HTTPException(status_code=401, detail="Authorization` can't be empty")
        authorization_list = authorization_str.split()
        if len(authorization_list) < 2:
            raise HTTPException(status_code=401, detail="Invalid token")
        token = authorization_list[1]
        objs = verify_token(token)
        if not objs:
            raise HTTPException(status_code=401, detail="Invalid token")
        user = UserModel(username="", id=objs.user_id)
        return user
    return decorated_function
def get_current_user(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        expired_time = payload.get("lex")
        if not expired_time:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,  detail="令牌无效或已过期",
            headers={"WWW-Authenticate": "Bearer"})
        if datetime.strptime(expired_time, "%Y-%m-%d %H:%M:%S") < datetime.now():
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,  detail="系统授权已过期!",
            headers={"WWW-Authenticate": "Bearer"})
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(
app/api/agent.py
@@ -18,7 +18,7 @@
from app.service.dialog import get_session_history
from app.service.ragflow import RagflowService
from app.service.service_token import get_ragflow_token, get_bisheng_token
from app.task.fetch_agent import initialize_agents
# from app.task.fetch_agent import initialize_agents
router = APIRouter()
app/api/auth.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import json
from fastapi import APIRouter, Depends
@@ -5,7 +6,8 @@
from sqlalchemy.ext.asyncio import AsyncSession
from app.api import Response, pwd_context, get_current_user
from app.config.config import settings
from app.config.const import chat_server, RAGFLOW, workflow_server, DIFY, TMP_DICT
from app.config.const import chat_server, RAGFLOW, workflow_server, DIFY, TMP_DICT, SYSTEM_ID, SYSTEM_STATUS_ON
from app.models import SystemDataModel
from app.models.app_token_model import AppToken
from app.models.base_model import get_db
from app.models.postgresql_base_model import get_pdb
@@ -129,8 +131,11 @@
        except Exception as e:
            return Response(code=500, msg=f"Failed to login with {app['id']}: {str(e)}")
    """
    system = db.query(SystemDataModel).filter_by(id=SYSTEM_ID).first()
    if not system or system.status != SYSTEM_STATUS_ON:
        return Response(code=400, msg="系统状态异常,请授权激活后操作!")
    # 创建本地token
    access_token = create_access_token(data={"sub": user.username, "user_id": user.id})
    access_token = create_access_token(data={"sub": user.username, "user_id": user.id, "lex": system.expired_at.strftime('%Y-%m-%d %H:%M:%S')})
    # await update_token(db, user.id, access_token, token_dict)
    # await update_user_token(db, user.id, token_dict)
app/api/files.py
@@ -1,4 +1,5 @@
import io
import time
from typing import Optional, List
import requests
@@ -19,7 +20,7 @@
from app.models.user_model import UserModel
from app.service.basic import BasicService
from app.service.bisheng import BishengService
from app.service.files import read_file
from app.service.files import read_file, service_chat_message, generate_word_document
from app.service.v2.api_token import DfTokenDao
from app.service.difyService import DifyService
from app.service.ragflow import RagflowService
@@ -137,6 +138,8 @@
                    return Response(code=400, msg=str(e))
                try:
                    file_upload = await dify_service.upload(token, f.filename, file_content, current_user.id)
                    if not file_upload:
                        raise HTTPException(status_code=500, detail="上传文件出错")
                    try:
                        tokens = await read_file(file_content, f.filename, f.content_type)
                        file_upload["tokens"] = tokens
@@ -242,4 +245,24 @@
                    break
                yield data
    return StreamingResponse(generate(), media_type="application/octet-stream")
    return StreamingResponse(generate(), media_type="application/octet-stream")
@router.get("/message/{message_id}/download", response_model=Response)
async def chat_message_generate_docx(message_id: str, db=Depends(get_db)):
    title, content = await service_chat_message(db, message_id)
    if title:
        print(title)
        # 创建 Word 文档
        doc = await generate_word_document(title, content)
        # 保存到内存缓冲区
        buffer = io.BytesIO()
        doc.save(buffer)
        buffer.seek(0)
        # print(2323232)
        # 返回文件流
        return StreamingResponse(buffer,
                                 media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                                 headers={"Content-Disposition": f"attachment; filename={int(time.time()*1000)}.docx"})
    raise HTTPException(status_code=400, detail="Failed to generate Word document")
app/api/knowledge.py
@@ -2,41 +2,48 @@
from fastapi import APIRouter, Depends, Query, HTTPException
from app.api import Response, get_current_user, ResponseList
from app.models import klgParameter, klgIcon
from app.models import klgParameter, klgIcon, KlgOwner
from app.models.base_model import get_db
from app.models.user_model import UserModel
from app.service.knowledge import get_knowledge_list, create_knowledge_service, sync_knowledge_service, \
    delete_knowledge_service, update_knowledge_icon_service
    delete_knowledge_service, update_knowledge_icon_service, get_knowledge_users_service, set_knowledge_owner_service, \
    add_knowledge_user_service
from typing import Optional
knowledge_router = APIRouter()
@knowledge_router.get("/list", response_model=Response)
async def knowledge_list(
        current: int,
        pageSize: int,
        keyword: str="",
        status: str="",
        knowledge: str="1",
        location: str="",
        keyword: str = "",
        status: str = "",
        knowledge: str = "1",
        location: str = "",
        current_user: UserModel = Depends(get_current_user),
        db=Depends(get_db)):
    if current and not pageSize:
        return ResponseList(code=400, msg="缺少参数")
    getknowledgelist = await get_knowledge_list(db, current_user.id, keyword, pageSize, current, status, knowledge, location)
    getknowledgelist = await get_knowledge_list(db, current_user.id, keyword, pageSize, current, status, knowledge,
                                                location)
    return Response(code=200, msg="", data=getknowledgelist)
@knowledge_router.post("/create", response_model=Response)
async def create_knowledge_api(klg: klgParameter, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)):
    is_create = await create_knowledge_service(db, klg.id, klg.name, klg.description, klg.icon, klg.klgType, current_user.id)
async def create_knowledge_api(klg: klgParameter, current_user: UserModel = Depends(get_current_user),
                               db=Depends(get_db)):
    is_create = await create_knowledge_service(db, klg.id, klg.name, klg.description, klg.icon, klg.klgType,
                                               current_user.id)
    if not is_create:
        return Response(code=500, msg="role knowledge failure", data={})
    return Response(code=200, msg="role knowledge success", data={})
@knowledge_router.get("/update", response_model=Response)
async def change_knowledge_api(knowledgeId: str, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)):
async def change_knowledge_api(knowledgeId: str, current_user: UserModel = Depends(get_current_user),
                               db=Depends(get_db)):
    # is_create = await update_dialog_status_service(db, dialog.id, dialog.status)
    # if not is_create:
    #     return Response(code=500, msg="dialog update failure", data={})
@@ -59,10 +66,38 @@
    return Response(code=200, msg="knowledge delete success", data={})
@knowledge_router.put("/update_icon", response_model=Response)
async def change_dialog_icon(klg: klgIcon, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)):
    is_create = await update_knowledge_icon_service(db, klg.id, klg.icon)
    if not is_create:
        return Response(code=500, msg="knowledge update failure", data={})
    return Response(code=200, msg="knowledge update success", data={})
    return Response(code=200, msg="knowledge update success", data={})
@knowledge_router.get("/{knowledgeId}/users", response_model=Response)
async def get_kb_users(knowledgeId: str,
                       current: int,
                       pageSize: int,
                       member:int = 0,
                       current_user: UserModel = Depends(get_current_user), db=Depends(get_db)):
    kb_user = await get_knowledge_users_service(db, knowledgeId, current_user.id, current, pageSize, member)
    return Response(code=200, msg="knowledge user success", data={"rows": kb_user})
@knowledge_router.post("/transfer/owner", response_model=Response)
async def transfer_kb_owner(klgOwner: KlgOwner,
                       current_user: UserModel = Depends(get_current_user), db=Depends(get_db)):
    is_update = await set_knowledge_owner_service(db, klgOwner.knowledgeId, klgOwner.UserId, current_user.id)
    if not is_update:
        return Response(code=500, msg="knowledge update failure", data={})
    return Response(code=200, msg="knowledge update success", data={})
@knowledge_router.get("/{knowledgeId}/join", response_model=Response)
async def join_kb_user(knowledgeId: str,
                       current_user: UserModel = Depends(get_current_user), db=Depends(get_db)):
    is_add = await add_knowledge_user_service(db, knowledgeId, current_user.id)
    if not is_add:
        return Response(code=500, msg="knowledge join failure", data={})
    return Response(code=200, msg="knowledge join success", data={})
app/api/system.py
@@ -4,9 +4,10 @@
from app.api import Response, get_current_user
from app.models.base_model import get_db
from app.models.role_model import RoleData, RoleModel
from app.models.system import SystemData
from app.models.system import SystemData, SystemLicense
from app.models.user_model import UserModel
from app.service.system import services_get_system_data, services_update_system_data, service_upload_logo_image
from app.service.system import services_get_system_data, services_update_system_data, service_upload_logo_image, \
    services_update_system_license
system_router = APIRouter()
@@ -33,3 +34,13 @@
    if not file_name:
        return Response(code=500, msg="failed", data={"logo": ""})
    return Response(code=200, msg="successfully", data={"logo": file_name})
@system_router.put("/license", response_model=Response)
async def api_update_system_license(system: SystemLicense, db=Depends(get_db)):
    msg = await services_update_system_license(db, system.licenseCode)
    if msg:
        return Response(code=400, msg=msg, data={})
    return Response(code=200, msg="successfully", data={})
app/api/user.py
@@ -154,4 +154,9 @@
    is_edit = await edit_user_dept(db, user.userId, user.depts)
    if not is_edit:
        return Response(code=500, msg="user edit failure", data={})
    return Response(code=200, msg="user edit successfully", data={})
    return Response(code=200, msg="user edit successfully", data={})
@user_router.get("/ping", response_model=Response)
async def user_ping(current_user: UserModel = Depends(get_current_user)):
    return Response(code=200, msg="", data={})
app/api/v2/chat.py
@@ -1,22 +1,21 @@
import json
import uuid
from typing import List
from typing import List
from fastapi import Depends, APIRouter, File, UploadFile
from sqlalchemy.orm import Session
from starlette.responses import StreamingResponse, Response
from werkzeug.http import HTTP_STATUS_CODES
from app.api import get_current_user, get_api_key
from app.config.const import dialog_chat, advanced_chat, base_chat, agent_chat, workflow_chat, basic_chat, \
    smart_message_error, http_400, http_500, http_200
from app.config.const import smart_message_error, http_400, http_500, http_200, complex_dialog_chat, \
    complex_knowledge_chat_deep, complex_knowledge_chat
from app.models import UserModel
from app.models.base_model import get_db
from app.models.v2.chat import RetrievalRequest
from app.models.v2.chat import RetrievalRequest, ChatDataRequest, ComplexChatDao
from app.models.v2.session_model import ChatData
from app.service.v2.chat import service_chat_dialog, get_chat_info, service_chat_basic, \
    service_chat_workflow, service_chat_parameters, service_chat_sessions, service_chat_upload, \
    service_chat_sessions_list, service_chat_session_log, service_chunk_retrieval, service_base_chunk_retrieval
    service_chat_sessions_list, service_chat_session_log, service_chunk_retrieval, service_complex_chat, \
    service_complex_upload
chat_router_v2 = APIRouter()
@@ -37,14 +36,14 @@
                                 media_type="text/event-stream")
    if not session_id:
        session = await service_chat_sessions(db, chatId, dialog.query)
        print(session)
        # print(session)
        if not session or session.get("code") != 0:
            error_msg = json.dumps(
                {"message": smart_message_error, "error": "\n**ERROR**: chat agent error", "status": http_500})
            return StreamingResponse(f"data: {error_msg}\n\n",
                                     media_type="text/event-stream")
        session_id = session.get("data", {}).get("id")
    return StreamingResponse(service_chat_dialog(db, chatId, dialog.query, session_id, current_user.id, chat_info.mode),
    return StreamingResponse(service_chat_dialog(db, chatId, dialog.query, session_id, current_user.id, chat_info.mode, chat_info.get_kb_ids()),
                             media_type="text/event-stream")
@chat_router_v2.post("/agent/{chatId}/completions")
@@ -77,7 +76,7 @@
                             media_type="text/event-stream")
@chat_router_v2.post("/complex/{chatId}/completions")
@chat_router_v2.post("/develop/{chatId}/completions")
async def api_chat_dialog(chatId:str, dialog: ChatData, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
    chat_info = await get_chat_info(db, chatId)
    if not chat_info:
@@ -128,16 +127,42 @@
    return Response(data, media_type="application/json", status_code=http_200)
# @chat_router_v2.post("/conversation/mindmap")
# async def api_conversation_mindmap(chatId:str, current:int=1, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
#     data = await service_chat_sessions_list(db, chatId, current, pageSize, current_user.id, keyword)
#     return Response(data, media_type="application/json", status_code=http_200)
@chat_router_v2.post("/retrieval")
async def retrieve_chunks(request_data: RetrievalRequest, api_key: str = Depends(get_api_key)):
    records = await service_chunk_retrieval(request_data.query, request_data.knowledge_id, request_data.retrieval_setting.top_k, request_data.retrieval_setting.score_threshold, api_key)
    return {"records": records}
@chat_router_v2.post("/complex/chat/completions")
async def api_complex_chat_completions(chat: ChatDataRequest, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
    # chat_mode = chat.chatMode
    if chat.isDeep == 2 and chat.chatMode == complex_knowledge_chat:
        chat.chatMode = complex_knowledge_chat_deep
    complex_chat = await ComplexChatDao(db).get_complex_chat_by_mode(chat.chatMode)
    if complex_chat:
        if not chat.sessionId:
            chat.sessionId = str(uuid.uuid4()).replace("-", "")
        return StreamingResponse(service_complex_chat(db, complex_chat.id, complex_chat.mode, current_user.id, chat),
                                 media_type="text/event-stream")
    else:
        error_msg = json.dumps(
            {"message": smart_message_error, "error": "\n**ERROR**: 网络异常,无法生成对话结果!", "status": http_500})
        return StreamingResponse(f"data: {error_msg}\n\n",
                                 media_type="text/event-stream")
@chat_router_v2.post("/complex/upload/{chatMode}")
async def api_complex_upload(chatMode:int, file: List[UploadFile] = File(...), current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
    status_code = http_200
    complex_chat = await ComplexChatDao(db).get_complex_chat_by_mode(chatMode)
    if complex_chat:
        data = await service_complex_upload(db, complex_chat.id, file, current_user.id)
        if not data:
            status_code = http_400
            data = "{}"
    else:
        status_code = http_500
        data = "{}"
    return Response(data, media_type="application/json", status_code=status_code)
app/api/v2/mindmap.py
New file
@@ -0,0 +1,40 @@
import json
import uuid
from typing import List
from fastapi import Depends, APIRouter, File, UploadFile
from sqlalchemy.orm import Session
from werkzeug.http import HTTP_STATUS_CODES
from app.api import get_current_user, get_api_key, Response
from app.config.const import dialog_chat, advanced_chat, base_chat, agent_chat, workflow_chat, basic_chat, \
    smart_message_error, http_400, http_500, http_200, complex_mindmap_chat
from app.models import UserModel
from app.models.base_model import get_db
from app.models.v2.chat import RetrievalRequest, ComplexChatDao
from app.models.v2.mindmap import MindmapRequest
from app.models.v2.session_model import ChatData
from app.service.v2.mindmap import service_chat_mindmap, service_message_mindmap_parse
mind_map_router = APIRouter()
@mind_map_router.post("/create", response_model=Response)
async def api_chat_mindmap(mindmap: MindmapRequest, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
    complex_chat = await ComplexChatDao(db).get_complex_chat_by_mode(complex_mindmap_chat)
    if complex_chat:
        data = await service_chat_mindmap(db, mindmap.messageId, mindmap.query, complex_chat.id,current_user.id)
        if not data:
            return Response(code=500, msg="create failure", data={})
    else:
        return Response(code=500, msg="网络异常!failure", data={})
    return Response(code=200, msg="create success", data=data)
@mind_map_router.get("/{messageId}/parse", response_model=Response)
async def api_chat_mindmap(messageId: str, current_user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
    data = await service_message_mindmap_parse(db, messageId, current_user.id)
    if not data:
        return Response(code=500, msg="create failure", data={})
    return Response(code=200, msg="create success", data=data)
app/api/v2/public_api.py
@@ -1,19 +1,26 @@
import json
import uuid
from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse
from starlette.responses import StreamingResponse
from Log import logger
from app.api import Response
from app.api import Response, token_required
from app.config.const import IMAGE_TO_TEXT, DOCUMENT_TO_CLEANING, DOCUMENT_TO_REPORT, DIFY, BISHENG, RAGFLOW, \
    DOCUMENT_IA_QUESTIONS, DOCUMENT_TO_REPORT_TITLE, DOCUMENT_TO_TITLE, DOCUMENT_TO_PAPER, \
    DOCUMENT_IA_QUESTIONS_EQUIPMENT
from app.models.base_model import get_db
    DOCUMENT_IA_QUESTIONS_EQUIPMENT, dialog_chat, workflow_chat, advanced_chat, agent_chat, base_chat
from app.models.public_api_model import DfToken
from app.service.v2.api_token import DfTokenDao
from app.service.v2.initialize_data import dialog_menu_sync, create_menu_sync, user_update_app
from app.task.sync_resources import sync_knowledge, sync_dialog, sync_agent, sync_llm, sync_resource
# from app.task.sync_resources import sync_knowledge, sync_dialog, sync_agent, sync_llm, sync_resource
from fastapi import Depends, APIRouter, File, UploadFile
from sqlalchemy.orm import Session
from app.config.const import smart_message_error, http_400, http_500, http_200, complex_dialog_chat
from app.models import UserModel
from app.models.base_model import get_db
from app.models.v2.session_model import ChatData
from app.service.v2.chat import service_chat_dialog, get_chat_info, service_chat_sessions, service_chat_workflow
public_api = APIRouter()
@@ -94,3 +101,62 @@
        return Response(code=500, msg=str(e), data={})
    return Response(code=200, msg="success", data={})
@public_api.post("/chat/{chatId}/completions")
async def api_chat_dialog(chatId:str, dialog: ChatData, current_user: UserModel = Depends(token_required),db: Session = Depends(get_db)): #  current_user: UserModel = Depends(get_current_user)
    chat_info = await get_chat_info(db, chatId)
    if not chat_info:
        error_msg = json.dumps(
            {"message": smart_message_error, "error": "\n**ERROR**: parameter exception", "status": http_400})
        return StreamingResponse(f"data: {error_msg}\n\n",
                                 media_type="text/event-stream")
    if chat_info.mode == dialog_chat:
        session_id = dialog.sessionId
        if not dialog.query:
            error_msg = json.dumps(
                {"message": smart_message_error, "error": "\n**ERROR**: question cannot be empty.", "status": http_400})
            return StreamingResponse(f"data: {error_msg}\n\n",
                                     media_type="text/event-stream")
        if not session_id:
            session = await service_chat_sessions(db, chatId, dialog.query)
            # print(session)
            if not session or session.get("code") != 0:
                error_msg = json.dumps(
                    {"message": smart_message_error, "error": "\n**ERROR**: chat agent error", "status": http_500})
                return StreamingResponse(f"data: {error_msg}\n\n",
                                         media_type="text/event-stream")
            session_id = session.get("data", {}).get("id")
        return StreamingResponse(service_chat_dialog(db, chatId, dialog.query, session_id, current_user.id, chat_info.mode),
                                 media_type="text/event-stream")
    elif chat_info.mode == workflow_chat:
        chat_info = await get_chat_info(db, chatId)
        if not chat_info:
            error_msg = json.dumps(
                {"message": smart_message_error, "error": "\n**ERROR**: parameter exception", "status": http_400})
            return StreamingResponse(f"data: {error_msg}\n\n",
                                     media_type="text/event-stream")
        session_id = dialog.sessionId
        if not session_id:
            session_id = str(uuid.uuid4()).replace("-", "")
        return StreamingResponse(service_chat_workflow(db, chatId, dialog, session_id, current_user.id, chat_info.mode),
                                 media_type="text/event-stream")
    elif chat_info.mode == advanced_chat or chat_info.mode == agent_chat or chat_info.mode == base_chat:
        chat_info = await get_chat_info(db, chatId)
        if not chat_info:
            error_msg = json.dumps(
                {"message": smart_message_error, "error": "\n**ERROR**: parameter exception", "status": http_400})
            return StreamingResponse(f"data: {error_msg}\n\n",
                                     media_type="text/event-stream")
        session_id = dialog.sessionId
        if not session_id:
            session_id = str(uuid.uuid4()).replace("-", "")
        return StreamingResponse(service_chat_workflow(db, chatId, dialog, session_id, current_user.id, chat_info.mode),
                                 media_type="text/event-stream")
    else:
        error_msg = json.dumps(
            {"message": smart_message_error, "error": "\n**ERROR**: unknown chat", "status": http_400})
        return StreamingResponse(f"data: {error_msg}\n\n",
                                 media_type="text/event-stream")
app/config/const.py
@@ -1,5 +1,5 @@
### ----------dify------api token
### ----------df------api token
DOCUMENT_TO_CLEANING = "document_to_cleaning"
DOCUMENT_TO_REPORT = "document_to_report"
IMAGE_TO_TEXT = "image_and_text_conversion"
@@ -62,6 +62,7 @@
agent_chat = "agent-chat"
base_chat = "chat"
basic_chat = "agent-basic"
complex_chat = "complex-chat"
### ------------------------------
@@ -113,3 +114,22 @@
###-------------------------------system-------------------------------------------------
SYSTEM_ID = 1
SYSTEM_STATUS_EXPIRED = 2
SYSTEM_STATUS_ON = 1
SYSTEM_STATUS_OFF = 0
### --------------------------------complex mode----------------------------------------------
complex_dialog_chat = 1 # 文档和基础对话
complex_network_chat = 2 # 联网对话
complex_knowledge_chat = 3 # 知识库对话
complex_deep_chat = 6
complex_mindmap_chat = 5  # 思维导图
complex_content_optimization_chat = 4 # 内容优化
complex_clean_chat = 7 # 清洗
complex_title_chat = 8 # 小标题
complex_report_chat = 9 # 报告
complex_knowledge_chat_deep = 10 # 知识库对话-深度对话
### --------------------------------basic develop agent---------------------------------------------------
basic_report_talk ="basic_report_talk"
app/config/env_conf/admin.yaml
@@ -3,10 +3,10 @@
  password: gAAAAABnvAq8bErFiR9x_ZcODjUeOdrDo8Z5UVOzyqo6SxIhAvLpw81kciQN0frwIFVfY9wrxH1WqrpTICpEwfH7r2SkLjS7SQ==
chat_server:
  id: fe24dd2c9be611ef92880242ac160006
  account: user@example.com
  password: gAAAAABnvs3e3fZOYfUUAJ6uT80dkhNeN7rhylzZErTWRZThNSLzMbZGetPCe9A2BJ86V0nZBLMNNu8w6rWp4dC7JxYxByJcow==
  id: a1738c92a0df11ef89fd0242ac130006
  account: zhao1@example.com
  password: gAAAAABn7R9VgyoD8z7Yiw3PfChh74n-thNyBpLTYXa-_EwIBIFXft0XFz3sjX1-u0ovYkBzlkXyHQiCOzil_bXUQF6SFAWPKw==
workflow_server:
  account: basic@mail.com
  password: gAAAAABnvs5i7xUn9pb2szCozJciGSiWPGv80PH_2HFFzNM2r1ZLTOQqftnUso_bvchtmwAmccfNrf53sf9_WMFVTc0hjTKRRQ==
  account: zqg@mail.com
  password: gAAAAABn7R6WnM9b5vUMsWyBKF7NS481rI4m-UBPIRpUNu9xPf_g2LE8OjQfL96r1ZL-pu1Are89_fWbAI5kE23_FMWVb2nzQw==
app/config/env_conf/config.yaml
@@ -1,10 +1,10 @@
secret_key: your-secret-key
sgb_base_url: http://192.168.20.119:13001
sgb_websocket_url: ws://192.168.20.119:13001
fwr_base_url: http://192.168.20.116:11080
fwr_base_url: http://192.168.20.119:11080
database_url: mysql+pymysql://root:infini_rag_flow@192.168.20.119:5455/rag_basic
sgb_db_url: mysql+pymysql://root:1234@192.168.20.119:13306/bisheng
fwr_db_url: mysql+pymysql://root:infini_rag_flow@192.168.20.116:15455/rag_flow
fwr_db_url: mysql+pymysql://root:infini_rag_flow@192.168.20.119:15455/rag_flow
PUBLIC_KEY: |
  -----BEGIN PUBLIC KEY-----
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
app/config/env_conf/default_agent_conf.json
@@ -139,5 +139,105 @@
      "mode": "agent-basic"
    }
  ],
  "bs": []
  "complex": [
    {
      "id": "9992ba72fff111ef8d890242ac120004",
      "name": "知识库专家",
      "description": "知识库专家",
      "icon": "intellFrame4",
      "chat_mode": 3,
      "parameters": {
      },
      "dialogType": "1",
      "mode": "complex-chat"
    },
    {
      "id": "ad127cb5-a1ef-49a3-82ed-6467a670fd10",
      "name": "文档分析专家",
      "description": "文档分析专家",
      "icon": "intellFrame4",
      "chat_mode": 1,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "c6a9fda3-bfb8-40ef-9510-545766c7d6a0",
      "name": "联网搜索专家",
      "description": "联网搜索专家",
      "icon": "intellFrame4",
      "chat_mode": 2,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "1974a79d-35bf-446d-95ed-0724380ea65a",
      "name": "语段优化专家",
      "description": "语段优化专家",
      "icon": "intellFrame4",
      "chat_mode": 4,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "8c6b05e7-da35-4aa4-8874-32b5bba93e7a",
      "name": "思维导图专家",
      "description": "思维导图专家",
      "icon": "intellFrame4",
      "chat_mode": 5,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "8c6b05e7-da35-4aa4-8874-32b5bba93111",
      "name": "清洗工作流",
      "description": "清洗工作流",
      "icon": "intellFrame4",
      "chat_mode": 7,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "8c6b05e7-da35-4aa4-8874-32b5bba93222",
      "name": "小标题工作流",
      "description": "小标题工作流",
      "icon": "intellFrame4",
      "chat_mode": 8,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "8c6b05e7-da35-4aa4-8874-32b5bba9333",
      "name": "报告生成工作流",
      "description": "报告生成工作流",
      "icon": "intellFrame4",
      "chat_mode": 9,
      "parameters": {
      },
      "dialogType": "4",
      "mode": "complex-chat"
    },
    {
      "id": "8f3e8d9c0bab11f0896e0242ac120006",
      "name": "知识库专家-深度",
      "description": "知识库专家",
      "icon": "intellFrame4",
      "chat_mode": 10,
      "parameters": {
      },
      "dialogType": "1",
      "mode": "complex-chat"
    }
  ]
}
app/config/env_conf/menu_conf.json
@@ -146,19 +146,7 @@
      "rank": 91,
      "dialog": [
        {
          "id": "87b8e17c-594d-424f-b9f8-0b56036904c9",
          "chat_id": "basic_report_clean",
          "chat_type": "reportWorkflow",
          "agentType": 4
        },
        {
          "id": "61d26deb-9371-4f9c-b61b-b9f9a24a9188",
          "chat_id": "basic_report_clean",
          "chat_type": "reportWorkflow",
          "agentType": 4
        },
        {
          "id": "d6eea92d-444a-4658-b9e7-9ab7bd5cb19d",
          "id": "basic_report_talk",
          "chat_id": "basic_report_clean",
          "chat_type": "reportWorkflow",
          "agentType": 4
app/config/env_conf/system.yaml
@@ -1,3 +1,4 @@
smart_system:
  title: SmartAI大模型平台
  desc: SmartAI大模型平台
  version: 1.0.3
app/init_config/init_run_data.py
@@ -1,6 +1,6 @@
from app.models.base_model import SessionLocal
from app.service.v2.initialize_data import dialog_menu_sync, default_group_sync, default_role_sync, \
    basic_agent_sync, admin_account_sync, sync_rg_api_token
    basic_agent_sync, admin_account_sync, sync_rg_api_token, sync_complex_api_token, system_license_sync
from app.task.sync_account_token import sync_token
@@ -10,17 +10,16 @@
        await dialog_menu_sync(db)  # 小数
        await default_group_sync(db)  # 默认组
        await default_role_sync(db)  # 默认角色
        # await app_register_sync(db)  # 注册的应用
        await basic_agent_sync(db)  # 开发的agent
        await admin_account_sync(db)  #
        await sync_rg_api_token(db)  #
        await sync_token()  # 启动同步token任务
        await admin_account_sync(db)  # 管理员账号
        await sync_rg_api_token(db)  # rg token
        await sync_token()  # 账号token登录
        await sync_complex_api_token(db)  # 账号token登录
        await system_license_sync(db) # 同步系统license
    except Exception as e:
        print(e)
    finally:
        db.close()
    # await default_role_sync(db)  # 页面资源配置信息
    # await default_role_sync(db)  # 默认的角色资源
app/models/__init__.py
@@ -17,6 +17,8 @@
from .menu_model import *
from .label_model import *
from .v2.session_model import *
from .v2.chat import *
from .v2.mindmap import *
from .system import *
app/models/dialog_model.py
@@ -1,3 +1,4 @@
import json
from datetime import datetime
from typing import Optional
@@ -24,6 +25,7 @@
    # agent_id = Column(String(36))
    mode = Column(String(36))
    parameters = Column(Text)
    kb_ids = Column(String(128))
    def get_id(self):
        return str(self.id)
@@ -43,6 +45,9 @@
            'mode': self.mode,
        }
    def get_kb_ids(self):
        return json.loads(self.kb_ids) if self.kb_ids else []
class ConversationModel(Base):
    __tablename__ = 'conversation'
app/models/knowledge_model.py
@@ -30,7 +30,7 @@
    def get_id(self):
        return str(self.id)
    def to_json(self):
    def to_json(self, user_id=""):
        return {
            'id': self.id,
            'name': self.name,
@@ -42,7 +42,8 @@
            'status': self.status,
            'documents': self.documents if self.documents else 0,
            'icon': self.icon,
            'embd_id': self.embd_id
            'embd_id': self.embd_id,
            'link': True if (self.tenant_id == user_id or user_id == "admin" ) and self.permission == "team" else False,
        }
    def __repr__(self):
        return '<Knowledge name:%r url:%r>\n' % (self.name, self.id)
@@ -59,4 +60,40 @@
class klgIcon(BaseModel):
    id: str
    icon: str
    icon: str
class KnowledgeUserModel(Base):
    __tablename__ = 'knowledgebase_user'
    id = Column(Integer, primary_key=True)  # id
    kb_id = Column(String(36))
    user_id = Column(Integer)
    create_date = Column(DateTime, default=datetime.now())             # 创建时间
    update_date = Column(DateTime, default=datetime.now(), onupdate=datetime.now())             # 更新时间
    status = Column(Integer)  # 状态
    def get_id(self):
        return str(self.id)
    def to_json(self, user_id=""):
        return {
            'id': self.id,
            'kb_id': self.kb_id,
            'create_time': self.create_date.strftime('%Y-%m-%d %H:%M:%S'),
            'update_time': self.update_date.strftime('%Y-%m-%d %H:%M:%S'),
            'user_id': self.user_id,
            'status': self.status,
            'owner': True if str(self.user_id) == user_id else False,
        }
    def __repr__(self):
        return '<Knowledge name:%r url:%r>\n' % (self.name, self.id)
class KlgOwner(BaseModel):
    knowledgeId: str
    UserId: int
app/models/system.py
@@ -12,6 +12,11 @@
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(255))
    desc = Column(String(1000))
    version = Column(String(32))
    machine_id = Column(String(255))
    license_code = Column(String(1000))
    status = Column(Integer, default=0)
    expired_at = Column(DateTime)
    created_at = Column(DateTime, default=datetime.now())
    updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now())
@@ -21,6 +26,11 @@
            # 'id': self.id,
            'title': self.title,
            'desc': self.desc,
            'version': self.version,
            'machine_id': self.machine_id,
            'license_code': self.license_code,
            'status': self.status,
            'expired_at': self.expired_at.strftime('%Y-%m-%d %H:%M') if self.expired_at else '',
        }
    def __repr__(self):
@@ -32,3 +42,7 @@
    title: str
    desc: str
    logo: str
class SystemLicense(BaseModel):
    licenseCode: str
app/models/user_model.py
@@ -254,4 +254,27 @@
            'password': self.password,
            'access_token': self.access_token,
            'refresh_token': self.refresh_token,
        }
class UserApiTokenModel(Base):
    __tablename__ = "user_api_token"
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    token = Column(String(40), index=True)
    created_at = Column(DateTime, default=datetime.now())
    updated_at = Column(DateTime, default=datetime.now())
    expires_at = Column(DateTime)
    is_active = Column(Integer, default=1)
    def to_json(self):
        return {
            'id': self.id,
            'account': self.username,
            'createTime': self.created_at,
            'updateTime': self.updated_at,
            'password': self.password,
            'access_token': self.access_token,
            'refresh_token': self.refresh_token,
        }
app/models/v2/chat.py
@@ -1,5 +1,14 @@
from pydantic import BaseModel
import json
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
from sqlalchemy import Column, Integer, String, BigInteger, ForeignKey, DateTime, Text, TEXT
from sqlalchemy.orm import Session
from app.config.const import Dialog_STATSU_DELETE, Dialog_STATSU_ON, complex_knowledge_chat, complex_knowledge_chat_deep
from app.models.base_model import Base
from app.utils.common import current_time
class RetrievalSetting(BaseModel):
@@ -12,5 +21,249 @@
    query: str
    retrieval_setting: RetrievalSetting
class ChatDataRequest(BaseModel):
    sessionId: str
    parentId: Optional[str] = ""
    query: str
    chatMode: Optional[int] = 1  # 1= 普通对话,2=联网,3=知识库,4=深度
    isDeep: Optional[int] = 1  # 1= 普通, 2=深度
    optimizeType: Optional[str] = ""  # 优化类型:润色,扩写,缩写,调整语气,自定义
    knowledgeId: Optional[list] = []
    files: Optional[list] = []
    def to_dict(self):
        return {
            "sessionId": self.sessionId,
            "query": self.query,
            "chatMode": self.chatMode,
            "knowledgeId": self.knowledgeId,
            "files": self.files,
            "isDeep": self.isDeep,
            "optimizeType": self.optimizeType,
            "parentId": self.parentId,
        }
class ComplexChatModel(Base):
    __tablename__ = 'complex_chat'
    __mapper_args__ = {
        # "order_by": 'SEQ'
    }
    id = Column(String(36), primary_key=True)  #  id
    create_date = Column(DateTime, default=datetime.now())             # 创建时间
    update_date = Column(DateTime, default=datetime.now(), onupdate=datetime.now())             # 更新时间
    tenant_id = Column(String(36))              # 创建人
    name = Column(String(255))                 # 名称
    description = Column(Text)                 # 说明
    icon = Column(Text, default="intelligentFrame1")                         # 图标
    status = Column(String(1), default="1")                 # 状态
    dialog_type = Column(String(1))            #  平台
    mode = Column(String(36))
    parameters = Column(Text)
    chat_mode = Column(Integer) #1= 普通对话,2=联网,3=知识库,4=深度
    def to_json(self):
        return {
            'id': self.id,
            'create_date': self.create_date.strftime('%Y-%m-%d %H:%M:%S'),
            'update_date': self.update_date.strftime('%Y-%m-%d %H:%M:%S'),
            'user_id': self.tenant_id,
            'name': self.name,
            'description': self.description,
            'icon': self.icon,
            'status': self.status,
            'agentType': self.dialog_type,
            'mode': self.mode,
        }
class ComplexChatDao:
    def __init__(self, db: Session):
        self.db = db
    async def create_complex_chat(self, chat_id: str, **kwargs) -> ComplexChatModel:
        new_session = ComplexChatModel(
            id=chat_id,
            create_date=current_time(),
            update_date=current_time(),
            **kwargs
        )
        self.db.add(new_session)
        self.db.commit()
        self.db.refresh(new_session)
        return new_session
    async def get_complex_chat_by_id(self, chat_id: str) -> ComplexChatModel | None:
        session = self.db.query(ComplexChatModel).filter_by(id=chat_id).first()
        return session
    async def update_complex_chat_by_id(self, chat_id: str, session, message: dict, conversation_id=None) -> ComplexChatModel | None:
        if not session:
            session = await self.get_complex_chat_by_id(chat_id)
        if session:
            try:
                # TODO
                session.update_date = current_time()
                self.db.commit()
                self.db.refresh(session)
            except Exception as e:
                # logger.error(e)
                self.db.rollback()
        return session
    async def update_or_insert_by_id(self, chat_id: str, **kwargs) -> ComplexChatModel:
        existing_session = await self.get_complex_chat_by_id(chat_id)
        if existing_session:
            return await self.update_complex_chat_by_id(chat_id, existing_session, kwargs.get("message"))
        existing_session = await self.create_complex_chat(chat_id, **kwargs)
        return existing_session
    async def delete_complex_chat(self, chat_id: str) -> None:
        session = await self.get_complex_chat_by_id(chat_id)
        if session:
            self.db.delete(session)
            self.db.commit()
    async def aget_complex_chat_ids(self) -> List:
        session_list = self.db.query(ComplexChatModel).filter(ComplexChatModel.status!=Dialog_STATSU_DELETE).all()
        return [i.id for i in session_list]
    def get_complex_chat_ids(self) -> List:
        session_list = self.db.query(ComplexChatModel).filter(ComplexChatModel.status!=Dialog_STATSU_DELETE).all()
        return [i.id for i in session_list]
    async def get_complex_chat_by_mode(self, chat_mode: int) -> ComplexChatModel | None:
        session = self.db.query(ComplexChatModel).filter(ComplexChatModel.chat_mode==chat_mode, ComplexChatModel.status==Dialog_STATSU_ON).first()
        return session
class ComplexChatSessionModel(Base):
    __tablename__ = "complex_chat_sessions"
    id = Column(String(36), primary_key=True)
    chat_id = Column(String(36))
    session_id = Column(String(36), index=True)
    create_date = Column(DateTime, default=current_time, index=True)  # 创建时间,默认值为当前时区时间
    update_date = Column(DateTime, default=current_time, onupdate=current_time)  # 更新时间,默认值为当前时区时间,更新时自动更新
    tenant_id = Column(Integer, index=True)  # 创建人
    agent_type = Column(Integer) # 1=rg, 3=basic,4=df
    message_type = Column(Integer)  # 1=用户,2=机器人,3=系统
    content = Column(TEXT)
    mindmap = Column(TEXT)
    query = Column(TEXT)
    node_data = Column(TEXT)
    event_type = Column(String(16))
    conversation_id = Column(String(36))
    chat_mode = Column(Integer) # 1= 普通对话,2=联网,3=知识库,4=深度
    # to_dict 方法
    def to_dict(self):
        return {
            'session_id': self.id,
            'name': self.name,
            'agent_type': self.agent_type,
            'chat_id': self.agent_id,
            'event_type': self.event_type,
            'session_type': self.session_type if self.session_type else 0,
            'create_date': self.create_date.strftime("%Y-%m-%d %H:%M:%S"),
            'update_date': self.update_date.strftime("%Y-%m-%d %H:%M:%S"),
        }
    def log_to_json(self):
        query = {}
        if self.query:
            query = json.loads(self.query)
        if self.message_type == 1:
            return {
                'id': self.id,
                'role': "user",
                'content': self.content,
                'files': query.get("files", []),
            }
        else:
            res = {
                'id': self.id,
                'role': "assistant",
                'answer': self.content,
                'chat_mode': self.chat_mode,
                "parentId": query.get("parentId"),
                "isDeep": query.get("isDeep", 1),
                "mindmap": True if self.mindmap else False,
            }
            if self.chat_mode == complex_knowledge_chat or self.chat_mode == complex_knowledge_chat_deep:
                res['reference'] = json.loads(self.node_data) if self.node_data else {}
            else:
                res['node_list'] = json.loads(self.node_data) if self.node_data else []
            return res
class ComplexChatSessionDao:
    def __init__(self, db: Session):
        self.db = db
    async def get_session_by_session_id(self, session_id: str, chat_id:str) -> ComplexChatSessionModel | None:
        session = self.db.query(ComplexChatSessionModel).filter_by(chat_id=chat_id, session_id=session_id, message_type=2).first()
        return session
    async def create_session(self, message_id: str, **kwargs) -> ComplexChatSessionModel:
        new_session = ComplexChatSessionModel(
            id=message_id,
            create_date=current_time(),
            update_date=current_time(),
            **kwargs
        )
        self.db.add(new_session)
        self.db.commit()
        self.db.refresh(new_session)
        return new_session
    async def get_session_by_id(self, message_id: str) -> ComplexChatSessionModel | None:
        session = self.db.query(ComplexChatSessionModel).filter_by(id=message_id).first()
        return session
    async def update_mindmap_by_id(self, message_id: str, mindmap:str) -> ComplexChatSessionModel | None:
        # print(message)
        session = await self.get_session_by_id(message_id)
        if session:
            try:
                session.mindmap = mindmap
                session.update_date = current_time()
                self.db.commit()
                self.db.refresh(session)
            except Exception as e:
                # logger.error(e)
                self.db.rollback()
        return session
    async def update_or_insert_by_id(self, session_id: str, **kwargs) -> ComplexChatSessionModel:
        existing_session = await self.get_session_by_id(session_id)
        if existing_session:
            return await self.update_session_by_id(session_id, existing_session, kwargs.get("message"))
        existing_session = await self.create_session(session_id, **kwargs)
        return existing_session
    async def delete_session(self, session_id: str) -> None:
        session = await self.get_session_by_id(session_id)
        if session:
            self.db.delete(session)
            self.db.commit()
    async def get_session_list(self, session_id: int, keyword:str="", page: int=1, page_size: int=100) -> any:
        query = self.db.query(ComplexChatSessionModel).filter(ComplexChatSessionModel.session_id==session_id)
        if keyword:
            query = query.filter(ComplexChatSessionModel.content.like('%{}%'.format(keyword)))
        total = query.count()
        session_list = query.order_by(ComplexChatSessionModel.create_date.desc()).offset((page-1)*page_size).limit(page_size).all()
        return total, session_list
app/models/v2/mindmap.py
@@ -1,12 +1,16 @@
import json
from typing import Optional, Type, List
from datetime import datetime
from typing import List
from pydantic import BaseModel
from sqlalchemy import Column, Integer, String, BigInteger, ForeignKey, DateTime, Text
from sqlalchemy.orm import Session
from app.config.const import Dialog_STATSU_DELETE
from app.models.base_model import Base
from app.utils.common import current_time
class MindmapRequest(BaseModel):
    messageId: str
    query:str
class ChatData(BaseModel):
    sessionId: Optional[str] = ""
    class Config:
        extra = 'allow'  # 允许其他动态字段
app/models/v2/session_model.py
@@ -82,6 +82,17 @@
    def to_dict(self):
        res = {"files": [], "inputs": {}}
        if hasattr(self, 'files'):
            res['files'] = self.files
        if hasattr(self, 'inputs'):
            res['inputs'] = self.inputs
        return res
class ChatSessionDao:
    def __init__(self, db: Session):
        self.db = db
app/service/auth.py
@@ -1,4 +1,4 @@
import os.path
import os.path
import re
import uuid
import base64
app/service/dialog.py
@@ -245,6 +245,7 @@
            if app_dialog:
                dialog.name = app_dialog["name"]
                dialog.description = app_dialog["description"]
                dialog.kb_ids = app_dialog["kb_ids"]
                dialog.update_date = datetime.now()
                db.add(dialog)
                db.commit()
app/service/files.py
@@ -1,8 +1,11 @@
import json
import fitz
import io
from docx import Document
from dashscope import get_tokenizer  # dashscope版本 >= 1.14.0
from app.models import ComplexChatSessionDao
from app.service.auth import decode_access_token
@@ -52,4 +55,27 @@
            '.docx'):
        text = await read_word(file)
    return await get_str_token(text)
    return await get_str_token(text)
async def service_chat_message(db, message_id: str):
    message = await ComplexChatSessionDao(db).get_session_by_id(message_id)
    content = ""
    title = ""
    if message:
        content = message.content
        title= json.loads(message.query).get("query")
    return title, content
async def generate_word_document(title, content):
    doc = Document()
    # 添加标题
    doc.add_heading(title, level=1)
    # 将内容按段落分割并写入文档
    for paragraph in content.split('\n'):
        # print("--------------:", paragraph)
        doc.add_paragraph(paragraph)
    return doc
app/service/knowledge.py
@@ -2,7 +2,7 @@
from sqlalchemy import or_
from app.models import KnowledgeModel, group_knowledge_table
from app.models import KnowledgeModel, group_knowledge_table, KnowledgeUserModel
from app.models.user_model import UserModel
from Log import logger
from app.task.fetch_agent import get_one_from_ragflow_knowledge
@@ -15,23 +15,29 @@
    query = db.query(KnowledgeModel).filter(KnowledgeModel.knowledge_type==knowledge)
    if user.permission != "admin":
        klg_list = [j.id for i in user.groups for j in i.knowledges]
        for i in db.query(KnowledgeUserModel).filter(KnowledgeUserModel.user_id == user_id, KnowledgeUserModel.status == 1).all():
            if i.kb_id not in klg_list:
                klg_list.append(i.kb_id)
        query = query.filter(or_(KnowledgeModel.id.in_(klg_list), KnowledgeModel.tenant_id == str(user_id)))
    if location:
        query = query.filter(or_(KnowledgeModel.permission == "team", KnowledgeModel.tenant_id == str(user_id)))
        if location:
            query = query.filter(or_(KnowledgeModel.permission == "team", KnowledgeModel.tenant_id == str(user_id)))
    if keyword:
        query = query.filter(KnowledgeModel.name.like('%{}%'.format(keyword)))
    total = query.count()
    if page_size:
        query = query.order_by(KnowledgeModel.update_date.desc()).limit(page_size).offset((page_index - 1) * page_size)
    return {"total":  total, "rows": [kld.to_json() for kld in query.all()]}
    return {"total":  total, "rows": [kld.to_json(str(user_id) if user.permission != "admin" else "admin") for kld in query.all()]}
async def create_knowledge_service(db, klg_id, name, description, icon, klg_type, user_id):
    try:
        dialog_model = KnowledgeModel(id=klg_id,name=name, description=description,icon=icon, knowledge_type=klg_type, tenant_id=user_id,update_date=datetime.now(),create_date=datetime.now())
        db.add(dialog_model)
        kb_user = KnowledgeUserModel(kb_id=klg_id, user_id=user_id, status=1, update_date=datetime.now(),
                                     create_date=datetime.now())
        db.add(kb_user)
        db.commit()
        db.refresh(dialog_model)
    except Exception as e:
@@ -87,3 +93,47 @@
        return False
    return True
async def get_knowledge_users_service(db, kb_id, user_id, page, page_size, member):
    kb_user_list = []
    user_ids = []
    kb_info = db.query(KnowledgeModel).filter(KnowledgeModel.id == kb_id).first()
    query = db.query(KnowledgeUserModel).filter(KnowledgeUserModel.kb_id == kb_id)
    if member:
        query = query.filter(KnowledgeUserModel.user_id != user_id)
    total = query.count()
    for kld in query.limit(page_size).offset((page - 1) * page_size).all():
        user_ids.append(kld.user_id)
        print(kb_info.tenant_id)
        kb_user_list.append(kld.to_json(kb_info.tenant_id))
    user = {i.id: i.username for i in db.query(UserModel).filter(UserModel.id.in_(user_ids)).all()}
    for kld in kb_user_list:
        kld["username"] = user.get(kld["user_id"], "")
    return {"total": total, "rows": kb_user_list}
async def set_knowledge_owner_service(db, kb_id, user_id, u_id):
    try:
        db.query(KnowledgeModel).filter_by(id=kb_id).update({"tenant_id": str(user_id), "update_date": datetime.now()})
        db.commit()
    except Exception as e:
        logger.error(e)
        db.rollback()
        return False
    return True
async def add_knowledge_user_service(db, kb_id, user_id):
    try:
        kb_user = KnowledgeUserModel(kb_id=kb_id, user_id=user_id, status=1, update_date=datetime.now(),
                                     create_date=datetime.now())
        db.add(kb_user)
        db.commit()
    except Exception as e:
        logger.error(e)
        db.rollback()
        return False
    return True
app/service/pom/public_key.pem
New file
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8Jak4kGIkOE2kyI0oTcb
w3yk7OfZ78g1RGvxKlYbKWz93Prxi1pvywXHOnrL/IYDCaNFOybFy5aMbqqvqXOx
0LBCqwmB9F07AiEysmhH5m5OxlS9XsxGZb1WeRmobRbge3Hxl59DmUKvD/7Gdsre
JnDeSWxeaS/zIqLVUsvV3301B08biIywMAKamBQyuJNTPK1ir8iy6peSLPi022zk
Nl+Rm4ToOrF00oqwB8z5BOTdDcJW/eFlieOyTnWSAFBTIXAB9uqZSjn37kyLKYDh
yVqB71T/wQvMRip4PPFpCE4UCGGhLHHsKPhtCgxHj6YqE7vUCuGBXP/aagzpWC/H
ywIDAQAB
-----END PUBLIC KEY-----
app/service/system.py
@@ -1,15 +1,20 @@
import os
import shutil
import uuid
from datetime import datetime
import yaml
from fastapi import UploadFile
from datetime import datetime
from fastapi import UploadFile
from Log import logger
from app.api import pwd_context
from app.config.const import SYSTEM_ID, ENV_CONF_PATH, APP_STATIC_PATH
from app.config.const import SYSTEM_ID, ENV_CONF_PATH, APP_STATIC_PATH, APP_SERVICE_PATH, SYSTEM_STATUS_ON
from app.models.system import SystemDataModel
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
import base64
import json
from app.utils.common import get_machine_id
async def services_get_system_data(db):
@@ -18,7 +23,7 @@
        with open(os.path.join(ENV_CONF_PATH, "system.yaml"), 'r', encoding='utf-8') as file:
            # 加载JSON数据
            config = yaml.safe_load(file)
            system = SystemDataModel(id=SYSTEM_ID, title=config["smart_system"]["title"], desc=config["smart_system"]["desc"])
            system = SystemDataModel(id=SYSTEM_ID, title=config["smart_system"]["title"], desc=config["smart_system"]["desc"], version=config["smart_system"]["version"])
            db.add(system)
            db.commit()
            db.refresh(system)
@@ -53,3 +58,37 @@
    except Exception as e:
        logger.error(f"保存失败: {str(e)}")
        return ""
async def services_update_system_license(db, license_code):
    try:
        with open(os.path.join(APP_SERVICE_PATH, "pom/public_key.pem"), "rb") as f:
            public_key = serialization.load_pem_public_key(f.read())
        license_data, signature = base64.b64decode(license_code).split(b"-----", 1)
        # print(license_data)
        public_key.verify(
            signature,
            license_data,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            hashes.SHA256()
        )
        license_dict = json.loads(license_data.decode('utf-8'))
        # print(license_dict)
        expiration_date = datetime.fromisoformat(license_dict['expiration_date'])
        if expiration_date < datetime.now():
            return "授权码已过期"
        system = db.query(SystemDataModel).filter_by(id=SYSTEM_ID).first()
        if license_dict['machine_id'] != get_machine_id() or system.machine_id != license_dict['machine_id']:
            return "授权码无效"
        system.license_code = license_code
        system.expired_at = expiration_date
        system.status = SYSTEM_STATUS_ON
        system.updated_at = datetime.now()
        # db.query(SystemDataModel).filter_by(id=SYSTEM_ID).update({"license_code": license_code, "expired_at": expiration_date, "status": 1, "updated_at": datetime.now()})
        db.commit()
        return ""
    except Exception as e:
        return f"验证失败: {str(e)}"
app/service/user.py
@@ -3,7 +3,7 @@
from datetime import datetime
from app.api import pwd_context
from app.config.const import RAGFLOW, BISHENG, DIFY, USER_STATSU_DELETE, ROLE_STATUS_ON, DEPT_STATUS_ON, \
    Dialog_STATSU_ON
    Dialog_STATSU_ON, USER_STATSU_ON
from app.models import RoleModel, GroupModel, AgentType, role_resource_table, DialogModel, OrganizationModel, \
    ResourceModel
from app.models.menu_model import WebMenuModel, MenuCapacityModel
@@ -269,7 +269,7 @@
    menu_list = db.query(WebMenuModel.id, WebMenuModel.title, WebMenuModel.describe, WebMenuModel.icon, WebMenuModel.desc,WebMenuModel.rank,
                         WebMenuModel.img, MenuCapacityModel.capacity_id, MenuCapacityModel.capacity_type, MenuCapacityModel.chat_id.label("agentId"), MenuCapacityModel.chat_type).outerjoin(
        MenuCapacityModel, WebMenuModel.id == MenuCapacityModel.menu_id).outerjoin(
        DialogModel, MenuCapacityModel.capacity_id == DialogModel.id).filter(DialogModel.status=="1").all()
        DialogModel, MenuCapacityModel.capacity_id == DialogModel.id).filter(DialogModel.status==USER_STATSU_ON).all()
    for menu in menu_list:
        menu_dict[menu.id] = menu_dict.get(menu.id, []) + [menu]
app/service/v2/app_driver/chat_agent.py
@@ -1,5 +1,6 @@
import json
from Log import logger
# from Log import logger
from app.models.v2.session_model import ChatData
from app.service.v2.app_driver.chat_base import ChatBase
@@ -9,6 +10,7 @@
    async def chat_completions(self, url, data, headers):
        complete_response = ""
        # print(data)
        async for line in self.http_stream(url, data, headers):
            # logger.error(line)
            if line.startswith("data:"):
@@ -25,8 +27,8 @@
                yield json_data
            except json.JSONDecodeError as e:
                # logger.info("Invalid JSON data------------------")
                print(e)
                logger.info("Invalid JSON data------------------")
                # print(e)
    @staticmethod
    async def request_data(query: str, conversation_id: str, user:str, chat_data: ChatData) -> dict:
@@ -46,6 +48,21 @@
            "files": files
        }
    @staticmethod
    async def complex_request_data(query: str, conversation_id: str, user: str, files: list=None, inputs: dict=None) -> dict:
        if not files:
            files = []
        if not inputs:
            inputs = {}
        return {
            "inputs": inputs,
            "query": query,
            "response_mode": "streaming",
            "conversation_id": conversation_id,
            "user": user,
            "files": files
        }
if __name__ == "__main__":
    async def aa():
app/service/v2/app_driver/chat_dialog.py
@@ -22,6 +22,8 @@
                yield json_data
            except json.JSONDecodeError as e:
                # print(e)
                # print(complete_response)
                logger.info("Invalid JSON data------------------")
                # print(e)
@@ -43,9 +45,14 @@
            "session_id": session_id
        }
    @staticmethod
    async def complex_request_data(question, dataset_ids, session_id=""):
        return {
            "question": question,
            "stream": True,
            "session_id": session_id,
            "kb_ids": dataset_ids
        }
if __name__ == "__main__":
app/service/v2/chat.py
@@ -1,16 +1,20 @@
import asyncio
import io
import json
import time
import uuid
import fitz
from fastapi import HTTPException
from sqlalchemy import or_
from Log import logger
from app.config.agent_base_url import RG_CHAT_DIALOG, DF_CHAT_AGENT, DF_CHAT_PARAMETERS, RG_CHAT_SESSIONS, \
    DF_CHAT_WORKFLOW, DF_UPLOAD_FILE, RG_ORIGINAL_URL
from app.config.config import settings
from app.config.const import *
from app.models import DialogModel, ApiTokenModel, UserTokenModel
from app.models import DialogModel, ApiTokenModel, UserTokenModel, ComplexChatSessionDao, ChatDataRequest, \
    ComplexChatDao, KnowledgeModel, UserModel, KnowledgeUserModel
from app.models.v2.session_model import ChatSessionDao, ChatData
from app.service.v2.app_driver.chat_agent import ChatAgent
from app.service.v2.app_driver.chat_data import ChatBaseApply
@@ -30,7 +34,7 @@
async def add_session_log(db, session_id: str, question: str, chat_id: str, user_id, event_type: str,
                          conversation_id: str, agent_type):
                          conversation_id: str, agent_type, query: dict=None):
    try:
        session = await ChatSessionDao(db).update_or_insert_by_id(
            session_id=session_id,
@@ -38,7 +42,7 @@
            agent_id=chat_id,
            agent_type=agent_type,
            tenant_id=user_id,
            message={"role": "user", "content": question},
            message={"role": "user", "content": question, "query": query},
            conversation_id=conversation_id,
            event_type=event_type
        )
@@ -47,12 +51,12 @@
        logger.error(e)
    return None
async def get_app_token(db, app_id):
    app_token = db.query(UserTokenModel).filter_by(id=app_id).first()
    if app_token:
        return app_token.access_token
    return ""
async def get_chat_token(db, app_id):
@@ -71,7 +75,6 @@
        logger.error(e)
async def get_chat_info(db, chat_id: str):
    return db.query(DialogModel).filter_by(id=chat_id, status=Dialog_STATSU_ON).first()
@@ -85,17 +88,48 @@
        return ChatAgent(), url
async def service_chat_dialog(db, chat_id: str, question: str, session_id: str, user_id, mode: str):
async def get_user_kb(db, user_id: int, kb_ids: list) -> list:
    res = []
    user = db.query(UserModel).filter(UserModel.id == user_id).first()
    if user is None:
        return res
    query = db.query(KnowledgeModel)
    if user.permission != "admin":
        klg_list = [j.id for i in user.groups for j in i.knowledges]
        for i in db.query(KnowledgeUserModel).filter(KnowledgeUserModel.user_id == user_id, KnowledgeUserModel.status == 1).all():
            if i.kb_id not in klg_list:
                klg_list.append(i.kb_id)
        query = query.filter(or_(KnowledgeModel.id.in_(klg_list), KnowledgeModel.tenant_id == str(user_id)))
        kb_list= query.all()
        for kb in kb_list:
            if kb.id in kb_ids:
                if kb.permission == "team":
                    res.append(kb.id)
                elif kb.tenant_id == str(user_id):
                    res.append(kb.id)
        return res
    else:
        return kb_ids
async def service_chat_dialog(db, chat_id: str, question: str, session_id: str, user_id: int, mode: str, kb_ids: list):
    conversation_id = ""
    token = await get_chat_token(db, rg_api_token)
    url = settings.fwr_base_url + RG_CHAT_DIALOG.format(chat_id)
    kb_id = await get_user_kb(db, user_id, kb_ids)
    if not kb_id:
        yield "data: " + json.dumps({"message": smart_message_error,
                                     "error": "\n**ERROR**: The agent has no knowledge base to work with!", "status": http_400},
                                    ensure_ascii=False) + "\n\n"
        return
    chat = ChatDialog()
    session = await add_session_log(db, session_id, question, chat_id, user_id, mode, session_id, 1)
    session = await add_session_log(db, session_id, question, chat_id, user_id, mode, session_id, RG_TYPE)
    if session:
        conversation_id = session.conversation_id
    message = {"role": "assistant", "answer": "", "reference": {}}
    try:
        async for ans in chat.chat_completions(url, await chat.request_data(question, conversation_id),
        async for ans in chat.chat_completions(url, await chat.complex_request_data(question, kb_id, conversation_id),
                                               await chat.get_headers(token)):
            data = {}
            error = ""
@@ -134,6 +168,7 @@
        message["role"] = "assistant"
        await update_session_log(db, session_id, message, conversation_id)
async def data_process(data):
    if isinstance(data, str):
        return data.replace("dify", "smart")
@@ -170,8 +205,9 @@
    if hasattr(chat_data, "query"):
        query = chat_data.query
    else:
        query = "start new workflow"
    session = await add_session_log(db, session_id,query if query else "start new conversation", chat_id, user_id, mode, conversation_id, 3)
        query = "start new conversation"
    session = await add_session_log(db, session_id, query if query else "start new conversation", chat_id, user_id,
                                    mode, conversation_id, DF_TYPE, chat_data.to_dict())
    if session:
        conversation_id = session.conversation_id
    try:
@@ -215,7 +251,7 @@
                    [workflow_started, node_started, node_finished].index(ans.get("event"))]
            elif ans.get("event") == workflow_finished:
                data = ans.get("data", {})
                answer_workflow = data.get("outputs", {}).get("output")
                answer_workflow = data.get("outputs", {}).get("output", data.get("outputs", {}).get("answer"))
                download_url = data.get("outputs", {}).get("download_url")
                event = smart_workflow_finished
                if data.get("status") == "failed":
@@ -242,14 +278,22 @@
        except:
            ...
    finally:
        await update_session_log(db, session_id, {"role": "assistant", "answer": answer_event or answer_agent or answer_workflow or error,
                                                  "download_url":download_url,
        await update_session_log(db, session_id, {"role": "assistant",
                                                  "answer": answer_event or answer_agent or answer_workflow or error,
                                                  "download_url": download_url,
                                                  "node_list": node_list, "task_id": task_id, "id": message_id,
                                                  "error": error}, conversation_id)
async def service_chat_basic(db, chat_id: str, chat_data: ChatData, session_id: str, user_id, mode: str):
    ...
    if chat_id == basic_report_talk:
        complex_chat = await ComplexChatDao(db).get_complex_chat_by_mode(chat_data.report_mode)
        if complex_chat:
            ...
async def service_chat_parameters(db, chat_id, user_id):
@@ -257,6 +301,7 @@
    if not chat_info:
        return {}
    return chat_info.parameters
async def service_chat_sessions(db, chat_id, name):
    token = await get_chat_token(db, rg_api_token)
@@ -276,14 +321,20 @@
        page=current,
        page_size=page_size
    )
    return json.dumps({"total":total, "rows": [session.to_dict() for session in session_list]})
    return json.dumps({"total": total, "rows": [session.to_dict() for session in session_list]})
async def service_chat_session_log(db, session_id):
    session_log = await ChatSessionDao(db).get_session_by_id(session_id)
    return json.dumps(session_log.log_to_json())
    if not session_log:
        return {}
    log_info =session_log.log_to_json()
    if session_log.event_type ==  complex_chat:
        total, message_list = await ComplexChatSessionDao(db).get_session_list(session_id)
        log_info["message"] = [message.log_to_json() for message in message_list[::-1]]
    return json.dumps(log_info)
async def service_chat_upload(db, chat_id, file, user_id):
@@ -316,6 +367,7 @@
    tokens = tokenizer.encode(input_str)
    return len(tokens)
async def read_pdf(pdf_stream):
    text = ""
    with fitz.open(stream=pdf_stream, filetype="pdf") as pdf_document:
@@ -335,6 +387,7 @@
    return text
async def read_file(file, filename, content_type):
    text = ""
    if content_type == "application/pdf" or filename.endswith('.pdf'):
@@ -349,7 +402,7 @@
async def service_chunk_retrieval(query, knowledge_id, top_k, similarity_threshold, api_key):
    print(query)
    # print(query)
    try:
        request_data = json.loads(query)
@@ -357,7 +410,7 @@
            "question": request_data.get("query", ""),
            "dataset_ids": request_data.get("dataset_ids", []),
            "page_size": top_k,
            "similarity_threshold": similarity_threshold
            "similarity_threshold": similarity_threshold if similarity_threshold else 0.2
        }
    except json.JSONDecodeError as e:
        fixed_json = query.replace("'", '"')
@@ -367,15 +420,16 @@
                "question": request_data.get("query", ""),
                "dataset_ids": request_data.get("dataset_ids", []),
                "page_size": top_k,
                "similarity_threshold": similarity_threshold
                "similarity_threshold": similarity_threshold if similarity_threshold else 0.2
            }
        except Exception:
            payload = {
                "question":query,
                "dataset_ids":[knowledge_id],
                "question": query,
                "dataset_ids": [knowledge_id],
                "page_size": top_k,
                "similarity_threshold": similarity_threshold
                "similarity_threshold": similarity_threshold if similarity_threshold else 0.2
            }
    # print(payload)
    url = settings.fwr_base_url + RG_ORIGINAL_URL
    chat = ChatBaseApply()
    response = await  chat.chat_post(url, payload, await chat.get_headers(api_key))
@@ -388,11 +442,15 @@
            "title": chunk.get("document_keyword", "Unknown Document"),
            "metadata": {"document_id": chunk["document_id"],
                         "path": f"{settings.fwr_base_url}/document/{chunk['document_id']}?ext={chunk.get('document_keyword').split('.')[-1]}&prefix=document",
                         'highlight': chunk.get("highlight") , "image_id":  chunk.get("image_id"), "positions": chunk.get("positions"),}
                         'highlight': chunk.get("highlight"), "image_id": chunk.get("image_id"),
                         "positions": chunk.get("positions"), }
        }
        for chunk in response.get("data", {}).get("chunks", [])
    ]
    # print(len(records))
    # print(records)
    return records
async def service_base_chunk_retrieval(query, knowledge_id, top_k, similarity_threshold, api_key):
    # request_data = json.loads(query)
@@ -420,15 +478,246 @@
    return records
async def add_complex_log(db, message_id, chat_id, session_id, chat_mode, query, user_id, mode, agent_type, message_type, conversation_id="", node_data=None, query_data=None):
    if not node_data:
        node_data = []
    if not query_data:
        query_data = {}
    # print(node_data)
    # print("--------------------------------------------------------")
    # print(query_data)
    try:
        complex_log = ComplexChatSessionDao(db)
        if not conversation_id:
            session = await complex_log.get_session_by_session_id(session_id, chat_id)
            if session:
                conversation_id = session.conversation_id
        await complex_log.create_session(message_id,
                                     chat_id=chat_id,
                                     session_id=session_id,
                                     chat_mode=chat_mode,
                                     message_type=message_type,
                                     content=query,
                                     event_type=mode,
                                     tenant_id=user_id,
                                     conversation_id=conversation_id,
                                     node_data=json.dumps(node_data),
                                     query=json.dumps(query_data),
                                     agent_type=agent_type)
        return conversation_id, True
    except Exception as e:
        logger.error(e)
        return conversation_id, False
async def add_query_files(db, message_id):
    query = {}
    complex_log = await ComplexChatSessionDao(db).get_session_by_id(message_id)
    if complex_log:
        query = json.loads(complex_log.query)
    return query.get("files", [])
async def service_complex_chat(db, chat_id, mode, user_id, chat_request: ChatDataRequest):
    answer_event = ""
    answer_agent = ""
    answer_dialog = ""
    answer_workflow = ""
    download_url = ""
    message_id = ""
    task_id = ""
    error = ""
    node_list = []
    reference= {}
    conversation_id = ""
    query_data = chat_request.to_dict()
    new_message_id = str(uuid.uuid4())
    inputs = {"is_deep": chat_request.isDeep}
    files = chat_request.files
    if chat_request.chatMode == complex_content_optimization_chat:
        inputs["type"] = chat_request.optimizeType
    elif chat_request.chatMode == complex_dialog_chat:
        if not files and chat_request.parentId:
            files = await add_query_files(db, chat_request.parentId)
    if chat_request.chatMode != complex_content_optimization_chat:
        await add_session_log(db, chat_request.sessionId, chat_request.query if chat_request.query else "未命名会话", chat_id, user_id,
                                mode, "", DF_TYPE)
        conversation_id, message = await add_complex_log(db, new_message_id, chat_id, chat_request.sessionId, chat_request.chatMode, chat_request.query, user_id, mode, DF_TYPE, 1, query_data=query_data)
        if not message:
            yield "data: " + json.dumps({"message": smart_message_error,
                                         "error": "\n**ERROR**: 创建会话失败!", "status": http_500},
                                        ensure_ascii=False) + "\n\n"
            return
    query_data["parentId"] = new_message_id
    try:
        if chat_request.chatMode == complex_knowledge_chat or chat_request.chatMode == complex_knowledge_chat_deep:
            if not conversation_id:
                session = await service_chat_sessions(db, chat_id, chat_request.query)
                # print(session)
                if not session or session.get("code") != 0:
                    yield "data: " +  json.dumps(
                        {"message": smart_message_error, "error": "\n**ERROR**: chat agent error", "status": http_500})
                    return
                conversation_id = session.get("data", {}).get("id")
            token = await get_chat_token(db, rg_api_token)
            url = settings.fwr_base_url + RG_CHAT_DIALOG.format(chat_id)
            chat = ChatDialog()
            try:
                async for ans in chat.chat_completions(url, await chat.complex_request_data(chat_request.query, chat_request.knowledgeId, conversation_id),
                                                       await chat.get_headers(token)):
                    data = {}
                    error = ""
                    status = http_200
                    if ans.get("code", None) == 102:
                        error = ans.get("message", "error!")
                        status = http_400
                        event = smart_message_error
                    else:
                        if isinstance(ans.get("data"), bool) and ans.get("data") is True:
                            event = smart_message_end
                        else:
                            data = ans.get("data", {})
                            # conversation_id = data.get("session_id", "")
                            if "session_id" in data:
                                del data["session_id"]
                            data["prompt"] = ""
                            if not message_id:
                                message_id = data.get("id", "")
                            answer_dialog = data.get("answer", "")
                            reference = data.get("reference", {})
                            event = smart_message_cover
                    message_str = "data: " + json.dumps(
                        {"event": event, "data": data, "error": error, "status": status, "message_id":message_id,
                     "parent_id": new_message_id,
                     "session_id": chat_request.sessionId},
                        ensure_ascii=False) + "\n\n"
                    for i in range(0, len(message_str), max_chunk_size):
                        chunk = message_str[i:i + max_chunk_size]
                        # print(chunk)
                        yield chunk  # 发送分块消息
            except Exception as e:
                logger.error(e)
                try:
                    yield "data: " + json.dumps({"message": smart_message_error,
                                                 "error": "\n**ERROR**: " + str(e), "status": http_500},
                                                ensure_ascii=False) + "\n\n"
                except:
                    ...
        else:
            token = await get_chat_token(db, chat_id)
            chat, url = await get_chat_object(mode)
            async for ans in chat.chat_completions(url,
                                                   await chat.complex_request_data(chat_request.query, conversation_id, str(user_id), files=files, inputs=inputs),
                                                   await chat.get_headers(token)):
                # print(ans)
                data = {}
                status = http_200
                conversation_id = ans.get("conversation_id")
                task_id = ans.get("task_id")
                if ans.get("event") == message_error:
                    error = ans.get("message", "参数异常!")
                    status = http_400
                    event = smart_message_error
                elif ans.get("event") == message_agent:
                    data = {"answer": ans.get("answer", ""), "id": ans.get("message_id", "")}
                    answer_agent += ans.get("answer", "")
                    message_id = ans.get("message_id", "")
                    event = smart_message_stream
                elif ans.get("event") == message_event:
                    data = {"answer": ans.get("answer", ""), "id": ans.get("message_id", "")}
                    answer_event += ans.get("answer", "")
                    message_id = ans.get("message_id", "")
                    event = smart_message_stream
                elif ans.get("event") == message_file:
                    data = {"url": ans.get("url", ""), "id": ans.get("id", ""),
                            "type": ans.get("type", "")}
                    files.append(data)
                    event = smart_message_file
                elif ans.get("event") in [workflow_started, node_started, node_finished]:
                    data = ans.get("data", {})
                    data["inputs"] = await data_process(data.get("inputs", {}))
                    data["outputs"] = await data_process(data.get("outputs", {}))
                    data["files"] = await data_process(data.get("files", []))
                    data["process_data"] = ""
                    if data.get("status") == "failed":
                        status = http_500
                        error = data.get("error", "")
                    node_list.append(ans)
                    event = [smart_workflow_started, smart_node_started, smart_node_finished][
                        [workflow_started, node_started, node_finished].index(ans.get("event"))]
                elif ans.get("event") == workflow_finished:
                    data = ans.get("data", {})
                    answer_workflow = data.get("outputs", {}).get("output", data.get("outputs", {}).get("answer"))
                    download_url = data.get("outputs", {}).get("download_url")
                    event = smart_workflow_finished
                    if data.get("status") == "failed":
                        status = http_500
                        error = data.get("error", "")
                    node_list.append(ans)
                elif ans.get("event") == message_end:
                    event = smart_message_end
                else:
                    continue
                yield "data: " + json.dumps(
                    {"event": event, "data": data, "error": error, "status": status, "task_id": task_id, "message_id":message_id,
                     "parent_id": new_message_id,
                     "session_id": chat_request.sessionId},
                    ensure_ascii=False) + "\n\n"
    except Exception as e:
        logger.error(e)
        try:
            yield "data: " + json.dumps({"message": smart_message_error,
                                         "error": "\n**ERROR**: " + str(e), "status": http_500},
                                        ensure_ascii=False) + "\n\n"
        except:
            ...
    finally:
        # await update_session_log(db, session_id, {"role": "assistant",
        #                                           "answer": answer_event or answer_agent or answer_workflow or error,
        #                                           "download_url": download_url,
        #                                           "node_list": node_list, "task_id": task_id, "id": message_id,
        #                                           "error": error}, conversation_id)
        if message_id:
            await add_complex_log(db, message_id, chat_id, chat_request.sessionId, chat_request.chatMode, answer_event or answer_agent or answer_workflow or answer_dialog or error, user_id, mode, DF_TYPE, 2, conversation_id, node_data=node_list or reference, query_data=query_data)
async def service_complex_upload(db, chat_id, file, user_id):
    files = []
    token = await get_chat_token(db, chat_id)
    if not token:
        return files
    url = settings.dify_base_url + DF_UPLOAD_FILE
    chat = ChatBaseApply()
    for f in file:
        try:
            file_content = await f.read()
            file_upload = await chat.chat_upload(url, {"file": (f.filename, file_content)}, {"user": str(user_id)},
                                                 {'Authorization': f'Bearer {token}'})
            # try:
            #     tokens = await read_file(file_content, f.filename, f.content_type)
            #     file_upload["tokens"] = tokens
            # except:
            #     ...
            files.append(file_upload)
        except Exception as e:
            logger.error(e)
    return json.dumps(files) if files else ""
if __name__ == "__main__":
    q = json.dumps({"query": "设备", "dataset_ids": ["fc68db52f43111efb94a0242ac120004"]})
    top_k = 2
    similarity_threshold = 0.5
    api_key = "ragflow-Y4MGYwY2JlZjM2YjExZWY4ZWU5MDI0Mm"
    # a = service_chunk_retrieval(q, top_k, similarity_threshold, api_key)
    # print(a)
    async def a():
        b = await service_chunk_retrieval(q, top_k, similarity_threshold, api_key)
        print(b)
    asyncio.run(a())
    asyncio.run(a())
app/service/v2/initialize_data.py
@@ -7,12 +7,12 @@
from Log import logger
from app.config.agent_base_url import RG_APP_TOKEN_LIST, RG_APP_NEW_TOKEN
from app.config.agent_base_url import RG_APP_TOKEN_LIST, RG_APP_NEW_TOKEN, DF_CHAT_API_KEY
# from app.api import pwd_context
from app.config.const import DIFY, ENV_CONF_PATH, RAGFLOW, smart_server, chat_server, workflow_server, TMP_DICT, \
    rg_api_token
    rg_api_token, Dialog_STATSU_ON, SYSTEM_ID
from app.models import MenuCapacityModel, WebMenuModel, GroupModel, RoleModel, DialogModel, UserModel, UserAppModel, \
    cipher_suite, UserTokenModel, ApiTokenModel
    cipher_suite, UserTokenModel, ApiTokenModel, ComplexChatModel, SystemDataModel
from app.service.auth import UserAppDao
from app.service.bisheng import BishengService
from app.service.difyService import DifyService
@@ -21,6 +21,8 @@
from app.service.v2.app_driver.chat_data import ChatBaseApply
from app.service.v2.app_register import AppRegisterDao
from app.config.config import settings
from app.service.v2.chat import get_app_token
from app.utils.common import get_machine_id
from app.utils.password_handle import generate_password, password_encrypted, password_decrypted
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -131,10 +133,12 @@
async def basic_agent_sync(db):
    agent_list = []
    complex_list = []
    with open(os.path.join(ENV_CONF_PATH, "default_agent_conf.json"), 'r', encoding='utf-8') as file:
        # 加载JSON数据
        agent_dict = json.load(file)
        agent_list = agent_dict.get("basic", [])
        complex_list = agent_dict.get("complex", [])
    user = db.query(UserModel).filter_by(permission="admin").first()
    for agent in agent_list:
        dialog = db.query(DialogModel).filter(DialogModel.id == agent["id"]).first()
@@ -158,6 +162,41 @@
            except Exception as e:
                print(e)
                db.rollback()
    now_complex_list = []
    for agent in complex_list:
        now_complex_list.append(agent["id"])
        dialog = db.query(ComplexChatModel).filter(ComplexChatModel.id == agent["id"]).first()
        if dialog:
            try:
                dialog.name = agent["name"]
                dialog.description = agent["description"]
                dialog.icon = agent["icon"]
                dialog.mode = agent["mode"]
                dialog.chat_mode = agent["chat_mode"]
                dialog.status = Dialog_STATSU_ON
                # dialog.parameters = json.dumps(agent["parameters"])
                db.commit()
            except Exception as e:
                logger.error(e)
        else:
            try:
                dialog = ComplexChatModel(id=agent["id"], name=agent["name"], description=agent["description"],
                                     icon=agent["icon"], tenant_id=user.id if user else "", dialog_type=agent["dialogType"], mode=agent["mode"],chat_mode = agent["chat_mode"])
                db.add(dialog)
                db.commit()
                db.refresh(dialog)
            except Exception as e:
                print(e)
                db.rollback()
    for i in  db.query(ComplexChatModel).filter(ComplexChatModel.status == "1").all():
        if i.id not in now_complex_list:
            try:
                db.query(ComplexChatModel).filter(ComplexChatModel.id==i.id).update(({"status": "0"}))
                db.commit()
            except:
                ...
async def user_update_app(userid, db):
@@ -353,4 +392,57 @@
    except Exception as e:
        print(e)
        db.rollback()
        db.rollback()
async def sync_complex_api_token(db):
    token = ""
    try:
        complex_list = db.query(ComplexChatModel).all()
        for i in complex_list:
            user_token = db.query(ApiTokenModel).filter(ApiTokenModel.app_id == i.id).first()
            if not user_token:
                try:
                    chat = ChatBaseApply()
                    url = settings.dify_base_url + DF_CHAT_API_KEY.format(i.id)
                    access_token = await get_app_token(db, workflow_server)
                    param = await chat.chat_get(url, {}, await chat.get_headers(access_token))
                    if param and param.get("data"):
                        token = param.get("data", [{}])[0].get("token")
                        token_id = param.get("data", [{}])[0].get("id")
                        # dialog.parameters = json.dumps(param)
                    else:
                        param = await chat.chat_post(url, {}, await chat.get_headers(access_token))
                        if param:
                            token = param.get("token")
                            token_id = param.get("id")
                    if token:
                        db.add(ApiTokenModel(id=token_id, app_id=i.id, type="app", token=token))
                        db.commit()
                        print("df_api_token: 更新成功!")
                except Exception as e:
                    print(e)
    except Exception as e:
        print(e)
        db.rollback()
async def system_license_sync(db):
    with open(os.path.join(ENV_CONF_PATH, "system.yaml") , 'r', encoding='utf-8') as file:
        # 加载JSON数据
        config = json.load(file)
        try:
            system = db.query(SystemDataModel).filter_by(id=SYSTEM_ID).first()
            if system:
                system.version = config["smart_system"].get("version")
            else:
                system = SystemDataModel(id=SYSTEM_ID, version=config["smart_system"].get("version"), title=config["smart_system"].get("title"), desc=config["smart_system"].get("desc"), machine_id=get_machine_id())
                db.add(system)
            db.commit()
        except Exception as e:
            print(e)
            db.rollback()
app/service/v2/mindmap.py
New file
@@ -0,0 +1,235 @@
import json
from Log import logger
from app.config.agent_base_url import DF_CHAT_AGENT, RG_CHAT_DIALOG
from app.config.config import settings
from app.config.const import message_error, message_event, complex_knowledge_chat, rg_api_token, workflow_finished
from app.models import ComplexChatSessionDao, ChatData
from app.service.v2.app_driver.chat_agent import ChatAgent
from app.service.v2.app_driver.chat_dialog import ChatDialog
from app.service.v2.chat import get_chat_token
async def service_chat_mindmap_v1(db, message_id, message, mindmap_chat_id, user_id):
    res = {}
    mindmap_query = ""
    complex_log = ComplexChatSessionDao(db)
    session = await complex_log.get_session_by_id(message_id)
    if session:
        token = await get_chat_token(db, session.chat_id)
        chat = ChatAgent()
        url = settings.dify_base_url + DF_CHAT_AGENT
        if session.mindmap:
            chat_request = json.loads(session.query)
            try:
                async for ans in chat.chat_completions(url,
                                                       await chat.request_data(message, session.conversation_id,
                                                                               str(user_id), ChatData(), chat_request.get("files", [])),
                                                       await chat.get_headers(token)):
                    if ans.get("event") == message_error:
                        return res
                    elif ans.get("event") == message_event:
                        mindmap_query += ans.get("answer", "")
                    else:
                        continue
            except Exception as e:
                logger.error(e)
                return res
        else:
            mindmap_query = session.content
        try:
            mindmap_str = ""
            token = await get_chat_token(db, mindmap_chat_id)
            async for ans in chat.chat_completions(url,
                                                   await chat.request_data(mindmap_query, "",
                                                                           str(user_id), ChatData()),
                                                   await chat.get_headers(token)):
                if ans.get("event") == message_error:
                    return res
                elif ans.get("event") == message_event:
                    mindmap_str += ans.get("answer", "")
                else:
                    continue
        except Exception as e:
            logger.error(e)
            return res
        mindmap_list = mindmap_str.split("```")
        mindmap_str = mindmap_list[1].lstrip("markdown\n")
        if session.mindmap:
            node_list = await mindmap_to_merge(session.mindmap, mindmap_str, f"- {message}")
            mindmap_str = "\n".join(node_list)
        res["mindmap"] = mindmap_str
        await complex_log.update_mindmap_by_id(message_id, mindmap_str)
    return res
async def service_chat_mindmap(db, message_id, message, mindmap_chat_id, user_id):
    res = {}
    mindmap_query = ""
    complex_log = ComplexChatSessionDao(db)
    session = await complex_log.get_session_by_id(message_id)
    if session:
        token = await get_chat_token(db, session.chat_id)
        chat = ChatAgent()
        url = settings.dify_base_url + DF_CHAT_AGENT
        chat_request = json.loads(session.query)
        if session.mindmap:
            inputs = {"is_deep": chat_request.get("isDeep", 1)}
            if session.chat_mode == complex_knowledge_chat:
                token = await get_chat_token(db, rg_api_token)
                # print(token)
                dialog_url = settings.fwr_base_url + RG_CHAT_DIALOG.format(session.chat_id)
                dialog_chat = ChatDialog()
                try:
                    async for ans in dialog_chat.chat_completions(dialog_url, await dialog_chat.complex_request_data(f"简要总结:{message}",
                                                                                                chat_request["knowledgeId"],
                                                                                                session.conversation_id),
                                                           await dialog_chat.get_headers(token)):
                        if ans.get("code", None) == 102:
                            return res
                        else:
                            if isinstance(ans.get("data"), bool) and ans.get("data") is True:
                                break
                            else:
                                data = ans.get("data", {})
                                mindmap_query = data.get("answer", "")
                except Exception as e:
                    logger.error(e)
            else:
                try:
                    async for ans in chat.chat_completions(url,
                                                           await chat.complex_request_data(message, session.conversation_id,
                                                                                   str(user_id), files=chat_request.get("files", []), inputs=inputs),
                                                           await chat.get_headers(token)):
                        if ans.get("event") == message_error:
                            return res
                        elif ans.get("event") == workflow_finished:
                            mindmap_query = ans.get("data", {}).get("outputs", {}).get("answer", "")
                        else:
                            continue
                except Exception as e:
                    logger.error(e)
                    return res
        else:
            mindmap_query = session.content
        # print("-----------------", mindmap_query)
        try:
            if chat_request.get("isDeep", 1) == 2:
                mindmap_query = mindmap_query.split("</think>")[-1]
            mindmap_str = ""
            # print("mindmap_query", mindmap_query)
            token = await get_chat_token(db, mindmap_chat_id)
            async for ans in chat.chat_completions(url,
                                                   await chat.complex_request_data(mindmap_query, "",
                                                                           str(user_id)),
                                                   await chat.get_headers(token)):
                # print(ans)
                if ans.get("event") == message_error:
                    return res
                elif ans.get("event") == message_event:
                    mindmap_str += ans.get("answer", "")
                else:
                    continue
        except Exception as e:
            logger.error(e)
            return res
        # print(mindmap_str)
        if "```json" in mindmap_str:
            mindmap_list = mindmap_str.split("```")
            mindmap_str = mindmap_list[1].lstrip("json")
        mindmap_str = mindmap_str.replace("\n", "")
        if session.mindmap:
            mindmap_str = await mindmap_merge_dict(session.mindmap, mindmap_str, message)
        try:
            res_str = await mindmap_join_str(mindmap_str)
            res["mindmap"] = res_str
        except Exception as e:
            logger.error(e)
            return res
        await complex_log.update_mindmap_by_id(message_id, mindmap_str)
    return res
async def mindmap_merge_dict(parent, child, target_node):
    parent_dict = json.loads(parent)
    if child:
        child_dict = json.loads(child)
        def iter_dict(node):
            if "items" not in node:
                if node["title"] == target_node:
                    node["items"] = child_dict["items"]
                return
            else:
                for i in node["items"]:
                    iter_dict(i)
        iter_dict(parent_dict)
    return json.dumps(parent_dict)
async def mindmap_join_str(mindmap_json):
    try:
        parent_dict = json.loads(mindmap_json)
    except Exception as e:
        logger.error(e)
        return ""
    def join_node(node, level):
        mindmap_str = ""
        if level <= 2:
            mindmap_str += f"{'#'*level} {node['title']}\n"
        else:
            mindmap_str += f"{' '*(level-3)*2}- {node['title']}\n"
        for i in node.get("items", []):
            mindmap_str += join_node(i, level+1)
        return mindmap_str
    return join_node(parent_dict, 1)
async def mindmap_to_merge(parent, child, target_node):
    level = 0
    index = 0
    new_node_list = []
    parent_list= parent.split("\n")
    child_list= child.split("\n")
    child_list[0] = target_node
    for i, node in enumerate(parent_list):
        if node.endswith(target_node):
            level = len(node) - len(target_node)
            index = i
            break
    tmp_level = 0
    for child in child_list:
        if "#" in child:
            childs = child.split("#")
            tmp_level = len(childs) - 2
            new_node_list.append(" "*(level+tmp_level)+ "-"+childs[-1])
        elif len(child) == 0:
            continue
        else:
            new_node_list.append(" "*(level+tmp_level)+child)
    return parent_list[:index]+new_node_list+parent_list[index+1:]
async def service_message_mindmap_parse(db, message_id, user_id):
    res = {}
    complex_log = ComplexChatSessionDao(db)
    session = await complex_log.get_session_by_id(message_id)
    if session.mindmap:
        try:
            res_str = await mindmap_join_str(session.mindmap)
            res["mindmap"] = res_str
        except Exception as e:
            logger.error(e)
    return res
if __name__ == '__main__':
    a = '{  "title": "全生命周期管理",  "items": [    {      "title": "设备规划与采购",      "items": [        {          "title": "需求分析与选型"    ,"items": [{"title": "rererer"}, {"title": "trtrtrtrt"}]    },        {          "title": "供应商选择与合同管理"        }      ]    },    {      "title": "设备安装与调试",      "items": [        {          "title": "安装规范"        },        {          "title": "调试测试"        }      ]    },    {      "title": "设备使用",      "items": [        {          "title": "操作培训"        },        {          "title": "操作规程与记录"        }      ]    },    {      "title": "设备维护与维修",      "items": [        {          "title": "定期维护"        },        {          "title": "故障诊断"        },        {          "title": "备件管理"        }      ]    },    {      "title": "设备更新与改造",      "items": [        {          "title": "技术评估"        },        {          "title": "更新计划"        },        {          "title": "改造方案"        }      ]    },    {      "title": "设备报废",      "items": [        {          "title": "报废评估"        },        {          "title": "报废处理"        }      ]    },    {      "title": "信息化管理",      "items": [        {          "title": "设备管理系统"        },        {          "title": "数据分析"        },        {          "title": "远程监控"        }      ]    },    {      "title": "安全管理",      "items": [        {          "title": "安全培训"        },        {          "title": "安全检查"        },        {          "title": "应急预案"        }      ]    },    {      "title": "环境保护",      "items": [        {          "title": "环保设备"        },        {          "title": "废物处理"        },        {          "title": "节能减排"        }      ]    },    {      "title": "具体实践案例",      "items": [        {          "title": "高压开关设备润滑脂选用研究"        },        {          "title": "环保型 C4 混气 GIS 设备运维技术研究"        }      ]    },    {      "title": "总结",      "items": [        {          "title": "提高运营效率和竞争力"        }      ]    }  ]}'
    b = mindmap_merge_dict(a, {}, "设备规划与采购")
    print(b)
app/task/fetch_agent.py
@@ -9,7 +9,7 @@
from app.config.config import settings
from app.config.const import RAGFLOW, BISHENG, DIFY, ENV_CONF_PATH, Dialog_STATSU_DELETE, Dialog_STATSU_ON
from app.models import KnowledgeModel
from app.models import KnowledgeModel, ComplexChatDao
from app.models.dialog_model import DialogModel
from app.models.user_model import UserAppModel
from app.models.agent_model import AgentModel
@@ -43,6 +43,7 @@
    status = Column(String(1), nullable=False)
    description = Column(String(255), nullable=False)
    tenant_id = Column(String(36), nullable=False)
    kb_ids = Column(String(128), nullable=False)
class DfApps(Base):
@@ -239,7 +240,7 @@
        db.close()
def get_data_from_ragflow_v2(names: List[str], tenant_id) -> List[Dict]:
def get_data_from_ragflow_v2(base_db, names: List[str], tenant_id) -> List[Dict]:
    db = SessionRagflow()
    para = {
        "user_input_form": [],
@@ -251,25 +252,29 @@
        }
    }
    try:
        chat_ids = ComplexChatDao(base_db).get_complex_chat_ids()
        # print(chat_ids)
        if names:
            query = db.query(Dialog.id, Dialog.name, Dialog.description, Dialog.status, Dialog.tenant_id) \
                .filter(Dialog.name.in_(names), Dialog.status == "1")
        else:
            query = db.query(Dialog.id, Dialog.name, Dialog.description, Dialog.status, Dialog.tenant_id).filter(
            query = db.query(Dialog.id, Dialog.name, Dialog.description, Dialog.status, Dialog.tenant_id, Dialog.kb_ids).filter(
                Dialog.status == "1", Dialog.tenant_id == tenant_id)
        results = query.all()
        formatted_results = [
            {"id": row[0], "name": row[1], "description": row[2], "status": "1" if row[3] == "1" else "2",
             "user_id": str(row[4]), "mode": "agent-dialog", "parameters": para} for row in results]
             "user_id": str(row[4]), "mode": "agent-dialog", "parameters": para, "kb_ids": row[5]} for row in results if row[0] not in chat_ids]
        return formatted_results
    finally:
        db.close()
def get_data_from_dy_v2(names: List[str]) -> List[Dict]:
def get_data_from_dy_v2(base_db, names: List[str]) -> List[Dict]:
    db = SessionDify()
    try:
        chat_ids = ComplexChatDao(base_db).get_complex_chat_ids()
        # print(chat_ids)
        if names:
            query = db.query(DfApps.id, DfApps.name, DfApps.description, DfApps.status, DfApps.tenant_id, DfApps.mode) \
                .filter(DfApps.name.in_(names))
@@ -279,7 +284,7 @@
        results = query.all()
        formatted_results = [
            {"id": str(row[0]), "name": row[1], "description": row[2], "status": "1",
             "user_id": str(row[4]), "mode": row[5], "parameters": {}} for row in results]
             "user_id": str(row[4]), "mode": row[5], "parameters": {}} for row in results if str(row[0]) not in chat_ids]
        return formatted_results
    finally:
        db.close()
@@ -297,13 +302,14 @@
                existing_agent.name = row["name"]
                existing_agent.description = row["description"]
                existing_agent.mode = row["mode"]
                existing_agent.kb_ids = row.get("kb_ids", "")
                if existing_agent.status == Dialog_STATSU_DELETE:
                    existing_agent.status = Dialog_STATSU_ON
                if row["parameters"]:
                    existing_agent.parameters = json.dumps(row["parameters"])
            else:
                existing = DialogModel(id=row["id"], status=row["status"], name=row["name"],
                                       description=row["description"],
                                       description=row["description"], kb_ids=row.get("kb_ids", ""),
                                       tenant_id=get_rag_user_id(db, row["user_id"], type_dict[dialog_type]),
                                       dialog_type=dialog_type, mode=row["mode"], parameters=json.dumps(row["parameters"]))
                db.add(existing)
@@ -342,11 +348,11 @@
        for app in app_register:
            try:
                if app["id"] == RAGFLOW:
                    ragflow_data = get_data_from_ragflow_v2([], app["name"])
                    ragflow_data = get_data_from_ragflow_v2(db, [], app["name"])
                    if ragflow_data:
                        update_ids_in_local_v2(ragflow_data, "1")
                elif app["id"] == DIFY:
                    dify_data = get_data_from_dy_v2([])
                    dify_data = get_data_from_dy_v2(db, [])
                    if dify_data:
                        update_ids_in_local_v2(dify_data, "4")
            except Exception as e:
@@ -407,10 +413,10 @@
def get_one_from_ragflow_dialog(dialog_id):
    db = SessionRagflow()
    try:
        row = db.query(Dialog.id, Dialog.name, Dialog.description, Dialog.status, Dialog.tenant_id) \
        row = db.query(Dialog.id, Dialog.name, Dialog.description, Dialog.status, Dialog.tenant_id, Dialog.kb_ids) \
            .filter(Dialog.id==dialog_id).first()
        return {"id": row[0], "name": row[1], "description": row[2], "status": str(row[3]),
                "user_id": str(row[4])} if row else {}
                "user_id": str(row[4]), "kb_ids": row[5]} if row else {}
    finally:
        db.close()
app/task/sync_account_token.py
@@ -38,7 +38,7 @@
    async def sync_token_chat(token_id, url, data, is_crypt, ping_url, token):
        db = SessionLocal()
        # pdb = PostgresqlSessionLocal()
        current_time = datetime.now() - timedelta(hours=12)
        current_time = datetime.now() - timedelta(hours=24)
        try:
            user_token = db.query(UserTokenModel).filter(UserTokenModel.id == token_id).first()
            chat = ChatBaseApply()
app/task/sync_resources.py
File was deleted
app/task/sync_system.py
New file
@@ -0,0 +1,68 @@
import asyncio
import base64
import json
import os
from datetime import datetime
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from app.config.const import SYSTEM_ID, SYSTEM_STATUS_EXPIRED, SYSTEM_STATUS_ON, APP_SERVICE_PATH
from app.models import SystemDataModel
from app.models.base_model import SessionLocal
from app.utils.common import get_machine_id
async def sync_system_license():
    print("-------------------------------------------")
    db = SessionLocal()
    try:
        system = db.query(SystemDataModel).filter_by(id=SYSTEM_ID).first()
        if not system:
            return
        machine_id = get_machine_id()
        if system.machine_id:
            if system.machine_id != machine_id:
                system.machine_id = machine_id
                if system.status == SYSTEM_STATUS_ON:
                    system.status = SYSTEM_STATUS_EXPIRED
            else:
                if system.status == SYSTEM_STATUS_ON:
                    if not system.license_code or system.expired_at < datetime.now():
                        system.status = SYSTEM_STATUS_EXPIRED
                    else:
                        try:
                            with open(os.path.join(APP_SERVICE_PATH, "pom/public_key.pem"), "rb") as f:
                                public_key = serialization.load_pem_public_key(f.read())
                            license_data, signature = base64.b64decode(system.license_code).split(b"-----", 1)
                            # print(license_data)
                            public_key.verify(
                                signature,
                                license_data,
                                padding.PSS(
                                    mgf=padding.MGF1(hashes.SHA256()),
                                    salt_length=padding.PSS.MAX_LENGTH
                                ),
                                hashes.SHA256()
                            )
                            license_dict = json.loads(license_data.decode('utf-8'))
                            # print(license_dict)
                            expiration_date = datetime.fromisoformat(license_dict['expiration_date'])
                            system.expired_at = expiration_date
                            if expiration_date < datetime.now():
                                system.status = SYSTEM_STATUS_EXPIRED
                        except Exception as e:
                            print(e)
                            system.status = SYSTEM_STATUS_EXPIRED
        else:
            if system.status == SYSTEM_STATUS_ON:
                system.status = SYSTEM_STATUS_EXPIRED
        db.commit()
    except Exception as e:
        print(e)
    finally:
        db.close()
def sync_resource():
    asyncio.run(sync_system_license())
app/utils/common.py
New file
@@ -0,0 +1,31 @@
import pytz
import platform
import subprocess
from datetime import datetime
def current_time():
    tz = pytz.timezone('Asia/Shanghai')
    return datetime.now(tz)
def get_machine_id():
    """获取机器的唯一标识"""
    if platform.system() == "Windows":
        # Windows 系统
        command = "wmic csproduct get UUID"
        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
        output, _ = process.communicate()
        machine_id = output.decode().strip().split("\n")[1].strip()
    elif platform.system() == "Darwin":
        # macOS 系统
        command = "system_profiler SPHardwareDataType | grep 'Hardware UUID' | awk '{print $4}'"
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
        output, _ = process.communicate()
        machine_id = output.decode().strip()
    else:
        # Linux 系统
        machine_id = open("/etc/machine-id").read().strip()
    # print(machine_id)
    return machine_id
main.py
@@ -15,6 +15,7 @@
from app.api.organization import dept_router
from app.api.system import system_router
from app.api.v2.chat import chat_router_v2
from app.api.v2.mindmap import mind_map_router
from app.api.v2.public_api import public_api
from app.api.report import router as report_router
from app.api.resource import menu_router
@@ -26,6 +27,7 @@
    sync_resources_from_json
from app.init_config.init_run_data import sync_default_data
from app.task.sync_account_token import start_sync_token_task
from app.task.sync_system import sync_resource
init_db()
@@ -55,6 +57,7 @@
scheduler = BackgroundScheduler()
scheduler.add_job(sync_agents_v2, 'interval', minutes=60, id="sync_resource_data")
scheduler.add_job(start_sync_token_task, 'interval', minutes=5, id="sync_token_1")
scheduler.add_job(sync_resource, 'interval', minutes=5, id="sync_resource_1")
scheduler.start()
@@ -74,9 +77,10 @@
app.include_router(dialog_router, prefix='/api/dialog', tags=["dialog"])
app.include_router(canvas_router, prefix='/api/canvas', tags=["canvas"])
app.include_router(label_router, prefix='/api/label', tags=["label"])
app.include_router(public_api, prefix='/v1/api', tags=["public_api"])
app.include_router(public_api, prefix='/v1', tags=["public_api"])
app.include_router(chat_router_v2, prefix='/api/v1', tags=["chat1"])
app.include_router(system_router, prefix='/api/system', tags=["system"])
app.include_router(mind_map_router, prefix='/api/mindmap', tags=["mindmap"])
app.mount("/static", StaticFiles(directory="app/images"), name="static")
if __name__ == "__main__":
requirements.txt
Binary files differ