diff --git a/docs-src/lpmm_parameters_guide.md b/docs-src/lpmm_parameters_guide.md index aad9d874..5d190589 100644 --- a/docs-src/lpmm_parameters_guide.md +++ b/docs-src/lpmm_parameters_guide.md @@ -75,7 +75,6 @@ max_embedding_workers = 12 # 嵌入/抽取并发线程数 embedding_chunk_size = 16 # 每批嵌入的条数 info_extraction_workers = 3 # 实体抽取同时执行线程数 enable_ppr = true # 是否启用PPR,低配机器可关闭 -ppr_node_cap = 8000 # 图节点数超过该值时自动跳过PPR ``` - `embedding_dimension` @@ -101,8 +100,6 @@ ppr_node_cap = 8000 # 图节点数超过该值时自动跳过PPR - `true`:检索会结合向量+知识图,效果更好,但略慢; - `false`:只用向量检索,牺牲一定效果,性能更稳定。 -- `ppr_node_cap` - 安全阈值:当图节点数超过阈值时自动跳过 PPR,以避免“大图”导致卡顿。 > 调参建议: > - 若导入/检索阶段机器明显“顶不住”(>=1MB的大文本,且分配配置<4C),优先调低: diff --git a/src/chat/knowledge/__init__.py b/src/chat/knowledge/__init__.py index a570277c..57e94472 100644 --- a/src/chat/knowledge/__init__.py +++ b/src/chat/knowledge/__init__.py @@ -42,7 +42,10 @@ def lpmm_start_up(): # sourcery skip: extract-duplicate-method logger.info("创建LLM客户端") # 初始化Embedding库 - embed_manager = EmbeddingManager() + embed_manager = EmbeddingManager( + max_workers=global_config.lpmm_knowledge.max_embedding_workers, + chunk_size=global_config.lpmm_knowledge.embedding_chunk_size, + ) logger.info("正在从文件加载Embedding库") try: embed_manager.load_from_file() diff --git a/src/chat/knowledge/kg_manager.py b/src/chat/knowledge/kg_manager.py index 245d6e9e..8108c296 100644 --- a/src/chat/knowledge/kg_manager.py +++ b/src/chat/knowledge/kg_manager.py @@ -358,12 +358,9 @@ class KGManager: paragraph_search_result: ParagraphEmbedding的搜索结果(paragraph_hash, similarity) embed_manager: EmbeddingManager对象 """ - # 性能保护:关闭或超限时直接返回向量检索结果(仅基于节点规模与开关) - if ( - not global_config.lpmm_knowledge.enable_ppr - or len(self.graph.get_node_list()) > global_config.lpmm_knowledge.ppr_node_cap - ): - logger.info("PPR 已禁用或超出阈值,使用纯向量检索结果") + # 性能保护:关闭时直接返回向量检索结果 + if not global_config.lpmm_knowledge.enable_ppr: + logger.info("PPR 已禁用,使用纯向量检索结果") return paragraph_search_result, None # 图中存在的节点总集 existed_nodes = self.graph.get_node_list() diff --git a/src/config/config.py b/src/config/config.py index 949898ab..4001d4ff 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -34,6 +34,7 @@ from src.config.official_configs import ( MemoryConfig, DebugConfig, DreamConfig, + WebUIConfig, ) from .api_ada_configs import ( @@ -347,6 +348,7 @@ class Config(ConfigBase): response_post_process: ResponsePostProcessConfig response_splitter: ResponseSplitterConfig telemetry: TelemetryConfig + webui: WebUIConfig experimental: ExperimentalConfig maim_message: MaimMessageConfig lpmm_knowledge: LPMMKnowledgeConfig diff --git a/src/config/official_configs.py b/src/config/official_configs.py index de4bc1da..3e176424 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -597,6 +597,38 @@ class TelemetryConfig(ConfigBase): """是否启用遥测""" +@dataclass +class WebUIConfig(ConfigBase): + """WebUI配置类""" + + enabled: bool = True + """是否启用WebUI""" + + mode: Literal["development", "production"] = "production" + """运行模式:development(开发) 或 production(生产)""" + + host: str = "0.0.0.0" + """WebUI服务器监听地址""" + + port: int = 8001 + """WebUI服务器端口""" + + anti_crawler_mode: Literal["false", "strict", "loose", "basic"] = "basic" + """防爬虫模式:false(禁用) / strict(严格) / loose(宽松) / basic(基础-只记录不阻止)""" + + allowed_ips: str = "127.0.0.1" + """IP白名单(逗号分隔,支持精确IP、CIDR格式和通配符)""" + + trusted_proxies: str = "" + """信任的代理IP列表(逗号分隔),只有来自这些IP的X-Forwarded-For才被信任""" + + trust_xff: bool = False + """是否启用X-Forwarded-For代理解析(默认false)""" + + secure_cookie: bool = False + """是否启用安全Cookie(仅通过HTTPS传输,默认false)""" + + @dataclass class DebugConfig(ConfigBase): """调试配置类""" @@ -722,6 +754,18 @@ class LPMMKnowledgeConfig(ConfigBase): embedding_dimension: int = 1024 """嵌入向量维度,应该与模型的输出维度一致""" + max_embedding_workers: int = 3 + """嵌入/抽取并发线程数""" + + embedding_chunk_size: int = 4 + """每批嵌入的条数""" + + max_synonym_entities: int = 2000 + """同义边参与的实体数上限,超限则跳过""" + + enable_ppr: bool = True + """是否启用PPR,低配机器可关闭""" + @dataclass class DreamConfig(ConfigBase): diff --git a/src/main.py b/src/main.py index b6141ad4..57950bf9 100644 --- a/src/main.py +++ b/src/main.py @@ -44,10 +44,9 @@ class MainSystem: def _setup_webui_server(self): """设置独立的 WebUI 服务器""" - import os + from src.config.config import global_config - webui_enabled = os.getenv("WEBUI_ENABLED", "false").lower() == "true" - if not webui_enabled: + if not global_config.webui.enabled: logger.info("WebUI 已禁用") return diff --git a/src/webui/anti_crawler.py b/src/webui/anti_crawler.py index 7daf27f3..28d87170 100644 --- a/src/webui/anti_crawler.py +++ b/src/webui/anti_crawler.py @@ -124,10 +124,8 @@ SCANNER_SPECIFIC_HEADERS = { # strict: 严格模式(更严格的检测,更低的频率限制) # loose: 宽松模式(较宽松的检测,较高的频率限制) # basic: 基础模式(只记录恶意访问,不阻止,不限制请求数,不跟踪IP) -ANTI_CRAWLER_MODE = os.getenv("WEBUI_ANTI_CRAWLER_MODE", "basic").lower() - -# IP白名单配置(从环境变量读取,逗号分隔) +# IP白名单配置(从配置文件读取,逗号分隔) # 支持格式: # - 精确IP:127.0.0.1, 192.168.1.100 # - CIDR格式:192.168.1.0/24, 172.17.0.0/16 (适用于Docker网络) @@ -236,13 +234,23 @@ def _convert_wildcard_to_regex(wildcard_pattern: str) -> Optional[str]: return regex -ALLOWED_IPS = _parse_allowed_ips(os.getenv("WEBUI_ALLOWED_IPS", "")) +# 从配置读取防爬虫设置(延迟导入避免循环依赖) +def _get_anti_crawler_config(): + """获取防爬虫配置""" + from src.config.config import global_config + return { + 'mode': global_config.webui.anti_crawler_mode, + 'allowed_ips': _parse_allowed_ips(global_config.webui.allowed_ips), + 'trusted_proxies': _parse_allowed_ips(global_config.webui.trusted_proxies), + 'trust_xff': global_config.webui.trust_xff + } -# 信任的代理IP配置(从环境变量读取,逗号分隔) -# 只有在信任的代理IP下才使用X-Forwarded-For头 -# 默认关闭(空),不信任任何代理 -TRUSTED_PROXIES = _parse_allowed_ips(os.getenv("WEBUI_TRUSTED_PROXIES", "")) -TRUST_XFF = os.getenv("WEBUI_TRUST_XFF", "false").lower() == "true" +# 初始化配置(将在模块加载时执行) +_config = _get_anti_crawler_config() +ANTI_CRAWLER_MODE = _config['mode'] +ALLOWED_IPS = _config['allowed_ips'] +TRUSTED_PROXIES = _config['trusted_proxies'] +TRUST_XFF = _config['trust_xff'] def _get_mode_config(mode: str) -> dict: diff --git a/src/webui/auth.py b/src/webui/auth.py index 6f527a04..db0fc675 100644 --- a/src/webui/auth.py +++ b/src/webui/auth.py @@ -3,10 +3,10 @@ WebUI 认证模块 提供统一的认证依赖,支持 Cookie 和 Header 两种方式 """ -import os from typing import Optional from fastapi import HTTPException, Cookie, Header, Response, Request from src.common.logger import get_logger +from src.config.config import global_config from .token_manager import get_token_manager logger = get_logger("webui.auth") @@ -23,23 +23,18 @@ def _is_secure_environment() -> bool: Returns: bool: 如果应该使用 secure cookie 则返回 True """ - # 检查环境变量 - secure_cookie_env = os.environ.get("WEBUI_SECURE_COOKIE", "") - if secure_cookie_env.lower() in ("true", "1", "yes"): - logger.info(f"WEBUI_SECURE_COOKIE 设置为 {secure_cookie_env},启用 secure cookie") + # 从配置读取 + if global_config.webui.secure_cookie: + logger.info("配置中启用了 secure_cookie") return True - if secure_cookie_env.lower() in ("false", "0", "no"): - logger.info(f"WEBUI_SECURE_COOKIE 设置为 {secure_cookie_env},禁用 secure cookie") - return False - + # 检查是否是生产环境 - env = os.environ.get("WEBUI_MODE", "").lower() - if env in ("production", "prod"): - logger.info(f"WEBUI_MODE 设置为 {env},启用 secure cookie") + if global_config.webui.mode == "production": + logger.info("WebUI运行在生产模式,启用 secure cookie") return True # 默认:开发环境不启用(因为通常是 HTTP) - logger.debug(f"未设置特殊环境变量 (WEBUI_SECURE_COOKIE={secure_cookie_env}, WEBUI_MODE={env}),禁用 secure cookie") + logger.debug("WebUI运行在开发模式,禁用 secure cookie") return False @@ -111,7 +106,7 @@ def set_auth_cookie(response: Response, token: str, request: Optional[Request] = logger.warning("=" * 80) logger.warning("检测到 HTTP 连接但环境配置要求 HTTPS (secure cookie)") logger.warning("已自动禁用 secure 标志以允许登录,但建议修改配置:") - logger.warning("1. 在 .env 文件中设置: WEBUI_SECURE_COOKIE=false") + logger.warning("1. 在配置文件中设置: webui.secure_cookie = false") logger.warning("2. 如果使用反向代理,请确保正确配置 X-Forwarded-Proto 头") logger.warning("=" * 80) is_secure = False diff --git a/src/webui/webui_server.py b/src/webui/webui_server.py index 267787c4..bf6b404d 100644 --- a/src/webui/webui_server.py +++ b/src/webui/webui_server.py @@ -1,6 +1,5 @@ """独立的 WebUI 服务器 - 运行在 0.0.0.0:8001""" -import os import asyncio import mimetypes from pathlib import Path @@ -130,9 +129,10 @@ class WebUIServer: """配置防爬虫中间件""" try: from src.webui.anti_crawler import AntiCrawlerMiddleware + from src.config.config import global_config - # 从环境变量读取防爬虫模式(false/strict/loose/basic) - anti_crawler_mode = os.getenv("WEBUI_ANTI_CRAWLER_MODE", "basic").lower() + # 从配置读取防爬虫模式 + anti_crawler_mode = global_config.webui.anti_crawler_mode # 注意:中间件按注册顺序反向执行,所以先注册的中间件后执行 # 我们需要在CORS之前注册,这样防爬虫检查会在CORS之前执行 @@ -186,7 +186,7 @@ class WebUIServer: error_msg = f"❌ WebUI 服务器启动失败: 端口 {self.port} 已被占用" logger.error(error_msg) logger.error(f"💡 请检查是否有其他程序正在使用端口 {self.port}") - logger.error("💡 可以通过环境变量 WEBUI_PORT 修改 WebUI 端口") + logger.error("💡 可以在配置文件中修改 webui.port 来更改 WebUI 端口") logger.error(f"💡 Windows 用户可以运行: netstat -ano | findstr :{self.port}") logger.error(f"💡 Linux/Mac 用户可以运行: lsof -i :{self.port}") raise OSError(f"端口 {self.port} 已被占用,无法启动 WebUI 服务器") @@ -212,7 +212,7 @@ class WebUIServer: if "address already in use" in str(e).lower() or e.errno in (98, 10048): # 98: Linux, 10048: Windows logger.error(f"❌ WebUI 服务器启动失败: 端口 {self.port} 已被占用") logger.error(f"💡 请检查是否有其他程序正在使用端口 {self.port}") - logger.error("💡 可以通过环境变量 WEBUI_PORT 修改 WebUI 端口") + logger.error("💡 可以在配置文件中修改 webui.port 来更改 WebUI 端口") else: logger.error(f"❌ WebUI 服务器启动失败 (网络错误): {e}") raise @@ -257,8 +257,9 @@ def get_webui_server() -> WebUIServer: """获取全局 WebUI 服务器实例""" global _webui_server if _webui_server is None: - # 从环境变量读取配置 - host = os.getenv("WEBUI_HOST", "0.0.0.0") - port = int(os.getenv("WEBUI_PORT", "8001")) + # 从配置读取 + from src.config.config import global_config + host = global_config.webui.host + port = global_config.webui.port _webui_server = WebUIServer(host=host, port=port) return _webui_server diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 27da018f..f8dd7c71 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "7.2.2" +version = "7.2.3" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- # 如果你想要修改配置文件,请递增version的值 @@ -191,8 +191,6 @@ max_embedding_workers = 3 # 嵌入/抽取并发线程数 embedding_chunk_size = 4 # 每批嵌入的条数 max_synonym_entities = 2000 # 同义边参与的实体数上限,超限则跳过 enable_ppr = true # 是否启用PPR,低配机器可关闭 -ppr_node_cap = 8000 # 图节点数超过该值时跳过PPR -webui_graph_default_limit = 200 # WebUI /graph 默认返回的最大节点数,避免大图负载 [keyword_reaction] keyword_rules = [ @@ -260,6 +258,22 @@ api_server_allowed_api_keys = [] # 新版API Server允许的API Key列表,为 [telemetry] #发送统计信息,主要是看全球有多少只麦麦 enable = true +[webui] # WebUI 独立服务器配置 +enabled = true # 是否启用WebUI +mode = "production" # 模式: development(开发) 或 production(生产) +host = "0.0.0.0" # WebUI 服务器监听地址 +port = 8001 # WebUI 服务器端口 + +# 防爬虫配置 +anti_crawler_mode = "basic" # 防爬虫模式: false(禁用) / strict(严格) / loose(宽松) / basic(基础-只记录不阻止) +allowed_ips = "127.0.0.1" # IP白名单(逗号分隔,支持精确IP、CIDR格式和通配符) + # 示例: 127.0.0.1,192.168.1.0/24,172.17.0.0/16 +trusted_proxies = "" # 信任的代理IP列表(逗号分隔),只有来自这些IP的X-Forwarded-For才被信任 + # 示例: 127.0.0.1,192.168.1.1,172.17.0.1 +trust_xff = false # 是否启用X-Forwarded-For代理解析(默认false) + # 启用后,仍要求直连IP在trusted_proxies中才会信任XFF头 +secure_cookie = false # 是否启用安全Cookie(仅通过HTTPS传输,默认false) + [experimental] #实验性功能 # 为指定聊天添加额外的prompt配置 # 格式: ["platform:id:type:prompt内容", ...]