{"repo": "anbeime/antinet", "files": {"backend/main.py": {"c61af6c5507b61a08b5f588514b27f86ef60d85c": {"t": 1785768246.1310368, "n": 17536, "h": "2e278ff3d6c2812d", "c": "#!/usr/bin/env python3\r\n# backend/main.py - 主API服务 (重构版)\r\n\"\"\"\r\n知易智能知识管家 - 后端API服务\r\n参考 SiYuan 架构重构,配置和中间件集中管理\r\n\"\"\"\r\n\r\nimport os\r\nimport sys\r\nfrom pathlib import Path\r\nfrom contextlib import asynccontextmanager\r\nfrom dotenv import load_dotenv\r\n\r\n# ============================================================\r\n# 1. 路径设置(必须在导入之前)\r\n# ============================================================\r\nif getattr(sys, 'frozen', False):\r\n # PyInstaller 打包后,exe 已在 backend/ 目录下\r\n backend_dir = os.path.dirname(sys.executable)\r\n project_root = os.path.dirname(backend_dir)\r\nelse:\r\n backend_dir = os.path.dirname(os.path.abspath(__file__))\r\n project_root = os.path.dirname(backend_dir)\r\nload_dotenv(os.path.join(project_root, '.env'))\r\n\r\nif backend_dir not in sys.path:\r\n sys.path.insert(0, backend_dir)\r\nif project_root not in sys.path:\r\n sys.path.insert(0, project_root)\r\n\r\n# ============================================================\r\n# 2. NPU 库路径配置(使用新的配置模块)\r\n# ============================================================\r\ntry:\r\n from conf.npu import NPUConfig\r\n\r\n npu_config = NPUConfig()\r\n lib_path = npu_config.get_libs_path()\r\n\r\n print(f\"[SETUP] QAI 库路径: {lib_path}\")\r\n\r\n # 设置环境变量\r\n paths_to_add = [lib_path]\r\n current_path = os.environ.get('PATH', '')\r\n for p in paths_to_add:\r\n if p not in current_path:\r\n current_path = p + ';' + current_path\r\n os.environ['PATH'] = current_path\r\n os.environ['QAI_LIBS_PATH'] = lib_path\r\n os.environ['QNN_LOG_LEVEL'] = npu_config.QNN_LOG_LEVEL\r\n\r\n # 添加 DLL 目录\r\n for p in paths_to_add:\r\n if os.path.exists(p):\r\n os.add_dll_directory(p)\r\n print(f\"[SETUP] 添加 DLL 目录: {p}\")\r\n\r\n print(f\"[SETUP] NPU 配置: backend={npu_config.QNN_BACKEND}, device={npu_config.QNN_DEVICE}\")\r\nexcept Exception as e:\r\n print(f\"[WARN] NPU 初始化失败(不影响 Genie HTTP 调用): {e}\")\r\n\r\n# ============================================================\r\n# 3. FastAPI 应用创建\r\n# ============================================================\r\nfrom fastapi import FastAPI\r\nfrom fastapi.responses import JSONResponse\r\n\r\n# 使用新的配置模块\r\nfrom conf.app import AppConfig\r\n\r\napp_config = AppConfig()\r\n\r\n@asynccontextmanager\r\nasync def lifespan(app: FastAPI):\r\n \"\"\"FastAPI 生命周期管理(替代已弃用的 on_event)\"\"\"\r\n # 启动时\r\n try:\r\n from services.reminder_service import start_reminder_service\r\n start_reminder_service()\r\n print(\"[OK] 提醒服务已启动\")\r\n except Exception as e:\r\n print(f\"[WARN] 提醒服务启动失败: {e}\")\r\n yield\r\n # 关闭时\r\n try:\r\n from services.reminder_service import stop_reminder_service\r\n stop_reminder_service()\r\n print(\"[OK] 提醒服务已停止\")\r\n except Exception as e:\r\n print(f\"[WARN] 提醒服务停止失败: {e}\")\r\n\r\napp = FastAPI(\r\n title=app_config.APP_NAME,\r\n version=app_config.APP_VERSION,\r\n description=app_config.APP_DESCRIPTION,\r\n lifespan=lifespan,\r\n max_form_memory_size=app_config.MAX_UPLOAD_SIZE,\r\n)\r\n\r\n# ============================================================\r\n# 4. 中间件设置(使用新的中间件模块)\r\n# ============================================================\r\nfrom middleware import create_middleware_stack\r\n\r\ncreate_middleware_stack(app)\r\nprint(\"[OK] 中间件栈已配置\")\r\n\r\n# ============================================================\r\n# 5. 数据库初始化\r\n# ============================================================\r\nfrom conf.database import DatabaseConfig\r\n\r\ndb_config = DatabaseConfig()\r\nprint(f\"[Database] 数据库路径: {db_config.DB_PATH}\")\r\n\r\nfrom database import DatabaseManager\r\n\r\ndb_manager = DatabaseManager(db_config.DB_PATH)\r\nprint(\"[OK] 数据库初始化完成\")\r\n\r\n# ============================================================\r\n# 6. AI 服务初始化(使用新的 AI 服务模块)\r\n# ============================================================\r\nfrom services.ai.factory import AIServiceFactory, create_ai_service\r\n\r\nAIServiceFactory.create_default_services()\r\n\r\n# 仅本地模型 (NPU/Genie),不再注册云端 Sensenova\r\n# 如需使用 Sensenova 云端模型,可取消下方注释\r\n# _sensenova_cfg = {\r\n# 'api_key': 'sk-aMuGLXz1jMSznP9zSUxOfS4uTG7wlsFI',\r\n# 'base_url': 'https://token.sensenova.cn/v1',\r\n# 'model': 'sensenova-6.7-flash-lite',\r\n# 'timeout': 60,\r\n# 'max_tokens': 2048,\r\n# 'temperature': 1.0,\r\n# }\r\n# _sensenova = create_ai_service('openai', _sensenova_cfg)\r\n# if _sensenova:\r\n# AIServiceFactory.register('sensenova', _sensenova, set_default=False)\r\n# print(\"[OK] Sensenova 服务已注册(chat/skill/workflow 专用)\")\r\n\r\n# 注册 NVIDIA NIM 服务(API Key 通过环境变量 NVIDIA_API_KEY 设置)\r\n_nim_cfg = {\r\n 'api_key': os.getenv('NVIDIA_API_KEY', ''),\r\n 'base_url': 'https://integrate.api.nvidia.com/v1',\r\n 'model': 'minimaxai/minimax-m2.7',\r\n 'timeout': 60,\r\n 'max_tokens': 8192,\r\n 'temperature': 1.0,\r\n}\r\n_nim = create_ai_service('nim', _nim_cfg)\r\nif _nim and _nim_cfg['api_key']:\r\n AIServiceFactory.register('nim', _nim, set_default=False)\r\n print(\"[OK] NVIDIA NIM 服务已注册\")\r\nelif _nim:\r\n print(\"[SKIP] NVIDIA NIM 服务跳过:未设置 NVIDIA_API_KEY 环境变量\")\r\n\r\nprint(\"[OK] AI 服务工厂已初始化(仅本地模型)\")\n\r\n# ============================================================\r\n# 6.5 AI 模型预热(使用 QAI/QAIRT SDK 的本地模型,无须 Ollama)\r\n# ============================================================\r\n\r\n# ============================================================\r\n# 7. 提醒服务已通过 lifespan 事件管理(见上方 app = FastAPI(lifespan=...) 定义)\r\n# ============================================================\r\n\r\n# ============================================================\r\n# 8. 路由注册(简化版)\r\n# ============================================================\r\ndef register_router(module_name: str):\r\n \"\"\"直接注册路由模块\"\"\"\r\n try:\r\n # 动态导入\r\n parts = module_name.split('.')\r\n if len(parts) == 2:\r\n # routes.xxx 格式\r\n router_module = __import__(module_name, fromlist=['router'])\r\n router = getattr(router_module, 'router', None)\r\n if router:\r\n # 直接使用路由自己的 prefix\r\n actual_prefix = getattr(router, 'prefix', '') or '(无前缀)'\r\n app.include_router(router)\r\n print(f\"[OK] 路由已注册: {module_name} -> {actual_prefix}\")\r\n return True\r\n else:\r\n print(f\"[WARN] {module_name} 无 router 属性\")\r\n return False\r\n except Exception as e:\r\n import traceback\r\n print(f\"[WARN] 无法导入 {module_name}: {e}\")\r\n traceback.print_exc()\r\n return False\r\n\r\n# 核心路由 - 使用各自的 prefix\r\nprint(\"[INFO] 开始注册路由...\")\r\nregister_router(\"routes.knowledge_routes\")\r\nregister_router(\"routes.chat_routes\")\r\nregister_router(\"routes.data_routes\")\r\nregister_router(\"routes.agent_routes\")\r\nregister_router(\"routes.skill_routes\")\r\nregister_router(\"routes.npu_routes\")\r\nregister_router(\"routes.pdf_routes\")\r\nregister_router(\"routes.pdf_opendataloader_routes\") # OpenDataLoader 长 PDF 专用路由\r\nregister_router(\"routes.ocr_routes\") # OCR 路由已存在但未注册\r\nregister_router(\"routes.excel_routes\")\r\nregister_router(\"routes.ppt_routes\")\r\nregister_router(\"routes.multi_model_routes\")\r\nregister_router(\"routes.genie_playground_routes\")\r\nregister_router(\"routes.gtd_routes\")\r\nregister_router(\"routes.backlink_routes\")\r\nregister_router(\"routes.integration_routes\")\r\nregister_router(\"routes.moc_routes\")\r\nregister_router(\"routes.vision_routes\")\r\nregister_router(\"routes.invoice_routes\")\r\n\r\nprint(\"[INFO] 注册增强版聊天路由...\")\r\nregister_router(\"routes.enhanced_chat_routes\")\r\nregister_router(\"routes.hermes_chat_routes\") # Hermes + 8 Agent 协同\r\nregister_router(\"routes.evolving_chat_routes\") # 自进化聊天\r\nregister_router(\"routes.chat_context_routes\")\r\nregister_router(\"routes.md2pdf_routes\")\r\nregister_router(\"routes.card_pdf_routes\")\r\nregister_router(\"routes.libreoffice_routes\")\r\n# register_router(\"routes.vector_search\") # 无router属性,是工具模块\r\nregister_router(\"routes.wiki\")\r\nregister_router(\"routes.markdown_converter_routes\")\r\nregister_router(\"routes.meeting_routes\")\r\nregister_router(\"routes.speech_routes\")\r\nregister_router(\"routes.research_routes\")\r\nregister_router(\"routes.ppt_structure_routes\")\r\nregister_router(\"routes.image_routes\") # 图片上传路由\r\nregister_router(\"routes.analysis_routes\")\r\nregister_router(\"routes.report_routes\")\r\nregister_router(\"routes.collaboration_routes\") # 实时协作 (WebSocket + REST)\r\nregister_router(\"routes.mindmap_routes\") # 思维导图\r\nregister_router(\"routes.remotion_routes\") # Remotion 动态演示\r\nregister_router(\"routes.pdf_edit_routes\") # PDF 文本编辑\r\nregister_router(\"routes.design_system_routes\") # 统一设计系统\r\nregister_router(\"routes.ppt_preview_routes\") # PPT 预览(增强版)\r\nregister_router(\"routes.ppt_native_routes\") # PPT 原生形状生成(SVG→DrawingML)\r\nregister_router(\"routes.collab_docs_routes\") # 协作文档 CRUD + 版本 + 权限\r\nregister_router(\"routes.file_browser_routes\") # 文件浏览器 + 卡片索引联动层\r\nregister_router(\"routes.auth_routes\") # JWT 认证\r\n\r\n# ============================================================\r\n# 9. 初始化各模块的数据库连接\r\n# ============================================================\r\nprint(\"[INFO] 开始初始化各模块数据库连接...\")\r\n\r\n# 自动设置所有路由模块的db_manager\r\nimport routes\r\nfor attr_name in dir(routes):\r\n mod = getattr(routes, attr_name, None)\r\n if hasattr(mod, 'db_manager') and hasattr(mod, 'set_db_manager'):\r\n try:\r\n mod.set_db_manager(db_manager)\r\n print(f\"[OK] {attr_name} 数据库已连接\")\r\n except Exception as e:\r\n print(f\"[WARN] {attr_name} 设置失败: {e}\")\r\n elif hasattr(mod, 'db_manager'):\r\n try:\r\n mod.db_manager = db_manager\r\n print(f\"[OK] {attr_name} db_manager已设置\")\r\n except Exception as e:\r\n pass\r\n\r\n# 单独设置其他需要db_manager的模块\r\ntry:\r\n from routes import auto_card\r\n if hasattr(auto_card, 'set_db_manager'):\r\n auto_card.set_db_manager(db_manager)\r\n print(\"[OK] auto_card 数据库已连接\")\r\n auto_card.db_manager = db_manager\r\nexcept Exception as e:\r\n print(f\"[WARN] auto_card: {e}\")\r\n\r\ntry:\r\n from routes import data_routes\r\n if hasattr(data_routes, 'set_db_manager'):\r\n data_routes.set_db_manager(db_manager)\r\n print(\"[OK] data_routes 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] data_routes: {e}\")\r\n\r\ntry:\r\n from routes import enhanced_chat_routes\r\n enhanced_chat_routes.set_db_manager(db_manager)\r\n print(\"[OK] enhanced_chat 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] enhanced_chat: {e}\")\r\n\r\ntry:\r\n from routes import chat_routes\r\n chat_routes.db_manager = db_manager\r\n print(\"[OK] chat_routes 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] chat_routes: {e}\")\r\n\r\ntry:\r\n from routes import ppt_routes\r\n ppt_routes.set_db_manager(db_manager)\r\n print(\"[OK] ppt_routes 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] ppt_routes: {e}\")\r\n\r\ntry:\r\n from routes import invoice_routes\r\n invoice_routes.set_db_manager(db_manager)\r\n print(\"[OK] invoice_routes 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] invoice_routes: {e}\")\r\n\r\ntry:\r\n from routes import vector_search\r\n vector_search.set_db_manager(db_manager)\r\n vector_search.init_on_startup()\r\n print(\"[OK] vector_search 数据库已连接,embedding 已初始化\")\r\n\r\n from routes import rag_pipeline\r\n rag_pipeline.set_db_manager(db_manager)\r\n print(\"[OK] rag_pipeline 数据库已连接\")\r\n\r\n from routes import auth_routes\r\n auth_routes.set_db_manager(db_manager)\r\n print(\"[OK] auth_routes 数据库已连接\")\r\n\r\n from middleware import audit\r\n audit.set_db_manager(db_manager)\r\n print(\"[OK] audit 中间件数据库已连接\")\nexcept Exception as e:\r\n print(f\"[WARN] vector_search: {e}\")\r\n\r\n# 初始化协作文档路由数据库\r\ntry:\r\n from routes import collab_docs_routes\r\n collab_docs_routes.set_db_manager(db_manager)\r\n print(\"[OK] collab_docs_routes 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] collab_docs_routes: {e}\")\r\n\r\ntry:\r\n from routes import collaboration_routes\r\n collaboration_routes.set_db_manager(db_manager)\r\n print(\"[OK] collaboration_routes 数据库已连接\")\r\nexcept Exception as e:\r\n print(f\"[WARN] collaboration_routes: {e}\")\r\n\r\nprint(\"[INFO] 数据库连接初始化完成\")\r\n\r\n# ============================================================\r\n# 10. 健康检查端点\r\n# ============================================================\r\n@app.get(\"/api/health\")\r\nasync def health_check():\r\n \"\"\"健康检查端点\"\"\"\r\n return {\r\n \"status\": \"ok\",\r\n \"app\": app_config.APP_NAME,\r\n \"version\": app_config.APP_VERSION,\r\n \"database_initialized\": True,\r\n }\r\n\r\n@app.get(\"/api/debug/routes\")\r\nasync def debug_routes():\r\n \"\"\"调试端点 - 列出所有已注册的路由\"\"\"\r\n all_routes = []\r\n for route in app.routes:\r\n if hasattr(route, 'path'):\r\n all_routes.append(route.path)\r\n return {\r\n \"total_routes\": len(all_routes),\r\n \"routes\": sorted(all_routes)\r\n }\r\n\r\n# ============================================================\r\n# 11. 启动服务\r\n# ============================================================\r\nif __name__ == \"__main__\":\r\n import socket\r\n import subprocess\r\n import uvicorn\r\n import sys\r\n import traceback\r\n\r\n def _excepthook(exc_type, exc_value, exc_tb):\r\n msg = \"\".join(traceback.format_exception(exc_type, exc_value, exc_tb))\r\n sys.stderr.write(f\"[UNCAUGHT] {msg}\\n\")\r\n try:\r\n with open(\"uncaught.log\", \"a\", encoding=\"utf-8\") as f:\r\n f.write(f\"\\n[{__import__('datetime').datetime.now()}] {msg}\\n\")\r\n except Exception:\r\n pass\r\n sys.excepthook = _excepthook\r\n\r\n try:\r\n from services.global_error_handler import install_global_handlers\r\n install_global_handlers()\r\n except Exception:\r\n pass\r\n \r\n # 检查端口是否被占用,如果是则释放\r\n def check_and_free_port(port: int):\r\n try:\r\n with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\r\n s.settimeout(1)\r\n result = s.connect_ex(('127.0.0.1', port))\r\n if result == 0:\r\n print(f\"[PORT] 端口 {port} 已被占用,尝试释放...\")\r\n output = subprocess.run(\r\n ['powershell', '-Command',\r\n f\"Get-NetTCPConnection -LocalPort {port} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object {{ Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }}\"],\r\n capture_output=True, text=True, timeout=10\r\n )\r\n import time\r\n time.sleep(2)\r\n # 再次检查\r\n with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s2:\r\n s2.settimeout(1)\r\n result2 = s2.connect_ex(('127.0.0.1', port))\r\n if result2 == 0:\r\n print(f\"[WARN] 无法释放端口 {port},请手动关闭占用进程\")\r\n return False\r\n else:\r\n print(f\"[OK] 端口 {port} 已释放\")\r\n return True\r\n else:\r\n print(f\"[OK] 端口 {port} 可用\")\r\n return True\r\n except Exception as e:\r\n print(f\"[WARN] 检查端口 {port} 时出错: {e}\")\r\n return True # 继续尝试启动\r\n \r\n check_and_free_port(app_config.PORT)\r\n \r\n # 打印所有已注册路由(便于诊断 404)\r\n print(f\"\\n{'─'*50}\")\r\n print(f\"已注册路由 ({len([r for r in app.routes if hasattr(r, 'path')])} 条):\")\r\n for route in sorted(app.routes, key=lambda r: getattr(r, 'path', '')):\r\n if hasattr(route, 'path') and hasattr(route, 'methods'):\r\n print(f\" {route.path:50s} {route.methods}\")\r\n print(f\"{'─'*50}\")\r\n \r\n print(f\"\\n{'='*50}\")\r\n print(f\"启动 {app_config.APP_NAME} v{app_config.APP_VERSION}\")\r\n print(f\"服务地址: http://{app_config.HOST}:{app_config.PORT}\")\r\n print(f\"{'='*50}\\n\")\r\n \r\n _auto_restart = os.environ.get(\"ZHIYI_AUTO_RESTART\", \"1\") == \"1\"\r\n _attempt = 0\r\n while True:\r\n try:\r\n uvicorn.run(\r\n app, # 直接传递 app 对象(PyInstaller frozen 模式需要,避免 \"main:app\" 字符串导入失败)\r\n host=app_config.HOST,\r\n port=app_config.PORT,\r\n reload=False,\r\n log_level=\"info\"\r\n )\r\n break\r\n except KeyboardInterrupt:\r\n raise\r\n except SystemExit as e:\r\n if not _auto_restart or e.code == 0:\r\n break\r\n _attempt += 1\r\n print(f\"[WATCHDOG] uvicorn SystemExit({e.code}); restart #{_attempt} in 2s\")\r\n check_and_free_port(app_config.PORT)\r\n import time as _t; _t.sleep(2)\r\n continue\r\n except BaseException as e:\r\n _attempt += 1\r\n print(f\"[WATCHDOG] uvicorn crashed: {e!r}; restart #{_attempt} in 2s\")\r\n traceback.print_exc()\r\n check_and_free_port(app_config.PORT)\r\n import time as _t; _t.sleep(2)\r\n continue\r\n\n# 初始化技能热插拔系统\ntry:\n from services.skill_hotplug import init_hotplug_manager\n hotplug_mgr = init_hotplug_manager()\n hotplug_mgr.start_watching()\n print(\"[OK] 技能热插拔系统已初始化\")\nexcept Exception as e:\n print(f\"[WARN] 技能热插拔系统初始化失败: {e}\")\r\nregister_router(\"routes.skill_hotplug_routes\") # 技能热插拔管理\r\nregister_router(\"routes.chain_word_routes\") # 链词机制\r\n\n# 初始化链词提取器\ntry:\n from services.chain_word_extractor import get_chain_word_extractor\n extractor = get_chain_word_extractor()\n print(\"[OK] 链词提取器已初始化\")\nexcept Exception as e:\n print(f\"[WARN] 链词提取器初始化失败: {e}\")\r\nregister_router(\"routes.ppf_routes\") # PPF 自动化处理流程\r\n\n# 初始化 PPF 处理器\ntry:\n from services.ppf_processor import init_ppf_processor\n init_ppf_processor(db_manager=db_manager)\n print(\"[OK] PPF 处理器已初始化\")\nexcept Exception as e:\n print(f\"[WARN] PPF 处理器初始化失败: {e}\")\r\n"}}}}