Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/anbeime_antinet.json
T

1 line
20 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"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"}}}}