Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
@@ -2,6 +2,7 @@ import random
|
|||||||
from typing import Any, ClassVar
|
from typing import Any, ClassVar
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
from src.common.security import VerifiedDep
|
||||||
|
|
||||||
# 修正导入路径,让Pylance不再抱怨
|
# 修正导入路径,让Pylance不再抱怨
|
||||||
from src.plugin_system import (
|
from src.plugin_system import (
|
||||||
@@ -20,10 +21,12 @@ from src.plugin_system import (
|
|||||||
register_plugin,
|
register_plugin,
|
||||||
)
|
)
|
||||||
from src.plugin_system.base.base_event import HandlerResult
|
from src.plugin_system.base.base_event import HandlerResult
|
||||||
|
from src.plugin_system.base.base_http_component import BaseRouterComponent
|
||||||
from src.plugin_system.base.component_types import InjectionRule, InjectionType
|
from src.plugin_system.base.component_types import InjectionRule, InjectionType
|
||||||
|
|
||||||
logger = get_logger("hello_world_plugin")
|
logger = get_logger("hello_world_plugin")
|
||||||
|
|
||||||
|
|
||||||
class StartupMessageHandler(BaseEventHandler):
|
class StartupMessageHandler(BaseEventHandler):
|
||||||
"""启动时打印消息的事件处理器。"""
|
"""启动时打印消息的事件处理器。"""
|
||||||
|
|
||||||
@@ -198,12 +201,25 @@ class WeatherPrompt(BasePrompt):
|
|||||||
return "当前天气:晴朗,温度25°C。"
|
return "当前天气:晴朗,温度25°C。"
|
||||||
|
|
||||||
|
|
||||||
|
class HelloWorldRouter(BaseRouterComponent):
|
||||||
|
"""一个简单的HTTP端点示例。"""
|
||||||
|
|
||||||
|
component_name = "hello_world_router"
|
||||||
|
component_description = "提供一个简单的 /greet HTTP GET 端点。"
|
||||||
|
|
||||||
|
def register_endpoints(self) -> None:
|
||||||
|
@self.router.get("/greet", summary="返回一个问候消息")
|
||||||
|
def greet(_=VerifiedDep):
|
||||||
|
"""这个端点返回一个固定的问候语。"""
|
||||||
|
return {"message": "Hello from your new API endpoint!"}
|
||||||
|
|
||||||
|
|
||||||
@register_plugin
|
@register_plugin
|
||||||
class HelloWorldPlugin(BasePlugin):
|
class HelloWorldPlugin(BasePlugin):
|
||||||
"""一个包含四大核心组件和高级配置功能的入门示例插件。"""
|
"""一个包含四大核心组件和高级配置功能的入门示例插件。"""
|
||||||
|
|
||||||
plugin_name = "hello_world_plugin"
|
plugin_name = "hello_world_plugin"
|
||||||
enable_plugin = False
|
enable_plugin: bool = True
|
||||||
dependencies: ClassVar = []
|
dependencies: ClassVar = []
|
||||||
python_dependencies: ClassVar = []
|
python_dependencies: ClassVar = []
|
||||||
config_file_name = "config.toml"
|
config_file_name = "config.toml"
|
||||||
@@ -225,7 +241,7 @@ class HelloWorldPlugin(BasePlugin):
|
|||||||
|
|
||||||
def get_plugin_components(self) -> list[tuple[ComponentInfo, type]]:
|
def get_plugin_components(self) -> list[tuple[ComponentInfo, type]]:
|
||||||
"""根据配置文件动态注册插件的功能组件。"""
|
"""根据配置文件动态注册插件的功能组件。"""
|
||||||
components: ClassVar[list[tuple[ComponentInfo, type]] ] = []
|
components: list[tuple[ComponentInfo, type]] = []
|
||||||
|
|
||||||
components.append((StartupMessageHandler.get_handler_info(), StartupMessageHandler))
|
components.append((StartupMessageHandler.get_handler_info(), StartupMessageHandler))
|
||||||
components.append((GetSystemInfoTool.get_tool_info(), GetSystemInfoTool))
|
components.append((GetSystemInfoTool.get_tool_info(), GetSystemInfoTool))
|
||||||
@@ -239,4 +255,7 @@ class HelloWorldPlugin(BasePlugin):
|
|||||||
# 注册新的Prompt组件
|
# 注册新的Prompt组件
|
||||||
components.append((WeatherPrompt.get_prompt_info(), WeatherPrompt))
|
components.append((WeatherPrompt.get_prompt_info(), WeatherPrompt))
|
||||||
|
|
||||||
|
# 注册新的Router组件
|
||||||
|
components.append((HelloWorldRouter.get_router_info(), HelloWorldRouter))
|
||||||
|
|
||||||
return components
|
return components
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ version = "0.12.0"
|
|||||||
description = "MoFox-Bot 是一个基于大语言模型的可交互智能体"
|
description = "MoFox-Bot 是一个基于大语言模型的可交互智能体"
|
||||||
requires-python = ">=3.11,<=3.13"
|
requires-python = ">=3.11,<=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"slowapi>=0.1.8",
|
||||||
"aiohttp>=3.12.14",
|
"aiohttp>=3.12.14",
|
||||||
"aiohttp-cors>=0.8.1",
|
"aiohttp-cors>=0.8.1",
|
||||||
"aiofiles>=23.1.0",
|
"aiofiles>=23.1.0",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ faiss-cpu
|
|||||||
fastapi
|
fastapi
|
||||||
fastmcp
|
fastmcp
|
||||||
filetype
|
filetype
|
||||||
|
slowapi
|
||||||
rjieba
|
rjieba
|
||||||
jsonlines
|
jsonlines
|
||||||
maim_message
|
maim_message
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from fastapi import APIRouter, HTTPException, Query, Request
|
|||||||
from fastapi.responses import HTMLResponse, JSONResponse
|
from fastapi.responses import HTMLResponse, JSONResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
|
|
||||||
# 调整项目根目录的计算方式
|
# 调整项目根目录的计算方式
|
||||||
project_root = Path(__file__).parent.parent.parent
|
project_root = Path(__file__).parent.parent.parent
|
||||||
data_dir = project_root / "data" / "memory_graph"
|
data_dir = project_root / "data" / "memory_graph"
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import time
|
import time
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
|
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
from src.common.security import get_api_key
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.plugin_system.apis import message_api, person_api
|
from src.plugin_system.apis import message_api, person_api
|
||||||
|
|
||||||
logger = get_logger("HTTP消息API")
|
logger = get_logger("HTTP消息API")
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter(dependencies=[Depends(get_api_key)])
|
||||||
|
|
||||||
|
|
||||||
@router.get("/messages/recent")
|
@router.get("/messages/recent")
|
||||||
@@ -161,5 +162,3 @@ async def get_message_stats_by_chat(
|
|||||||
# 统一异常处理
|
# 统一异常处理
|
||||||
logger.error(f"获取消息统计时发生错误: {e}")
|
logger.error(f"获取消息统计时发生错误: {e}")
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
|
|
||||||
from src.chat.utils.statistic import (
|
from src.chat.utils.statistic import (
|
||||||
StatisticOutputTask,
|
StatisticOutputTask,
|
||||||
)
|
)
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
from src.common.security import get_api_key
|
||||||
|
|
||||||
logger = get_logger("LLM统计API")
|
logger = get_logger("LLM统计API")
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter(dependencies=[Depends(get_api_key)])
|
||||||
|
|
||||||
# 定义统计数据的键,以减少魔法字符串
|
# 定义统计数据的键,以减少魔法字符串
|
||||||
TOTAL_REQ_CNT = "total_requests"
|
TOTAL_REQ_CNT = "total_requests"
|
||||||
|
|||||||
37
src/common/security.py
Normal file
37
src/common/security.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from fastapi import Depends, HTTPException, Security
|
||||||
|
from fastapi.security.api_key import APIKeyHeader
|
||||||
|
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
from src.config.config import global_config as bot_config
|
||||||
|
|
||||||
|
logger = get_logger("security")
|
||||||
|
|
||||||
|
API_KEY_HEADER = "X-API-Key"
|
||||||
|
api_key_header_auth = APIKeyHeader(name=API_KEY_HEADER, auto_error=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_api_key(api_key: str = Security(api_key_header_auth)) -> str:
|
||||||
|
"""
|
||||||
|
FastAPI 依赖项,用于验证API密钥。
|
||||||
|
从请求头中提取 X-API-Key 并验证它是否存在于配置的有效密钥列表中。
|
||||||
|
"""
|
||||||
|
valid_keys = bot_config.plugin_http_system.plugin_api_valid_keys
|
||||||
|
if not valid_keys:
|
||||||
|
logger.warning("API密钥认证已启用,但未配置任何有效的API密钥。所有请求都将被拒绝。")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="服务未正确配置API密钥",
|
||||||
|
)
|
||||||
|
if api_key not in valid_keys:
|
||||||
|
logger.warning(f"无效的API密钥: {api_key}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTP_403_FORBIDDEN,
|
||||||
|
detail="无效的API密钥",
|
||||||
|
)
|
||||||
|
return api_key
|
||||||
|
|
||||||
|
# 创建一个可重用的依赖项,供插件开发者在其需要验证的端点上使用
|
||||||
|
# 用法: @router.get("/protected_route", dependencies=[VerifiedDep])
|
||||||
|
# 或者: async def my_endpoint(_=VerifiedDep): ...
|
||||||
|
VerifiedDep = Depends(get_api_key)
|
||||||
@@ -1,32 +1,60 @@
|
|||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from fastapi import APIRouter, FastAPI
|
from fastapi import APIRouter, FastAPI, Request, Response
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
from uvicorn import Config
|
from uvicorn import Config
|
||||||
from uvicorn import Server as UvicornServer
|
from uvicorn import Server as UvicornServer
|
||||||
|
|
||||||
|
from slowapi import Limiter, _rate_limit_exceeded_handler
|
||||||
|
from slowapi.errors import RateLimitExceeded
|
||||||
|
from slowapi.middleware import SlowAPIMiddleware
|
||||||
|
from slowapi.util import get_remote_address
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
from src.config.config import global_config as bot_config
|
||||||
|
|
||||||
install(extra_lines=3)
|
install(extra_lines=3)
|
||||||
|
|
||||||
logger = get_logger("Server")
|
logger = get_logger("Server")
|
||||||
|
|
||||||
|
|
||||||
|
def rate_limit_exceeded_handler(request: Request, exc: Exception) -> Response:
|
||||||
|
"""自定义速率限制超出处理器以解决类型提示问题"""
|
||||||
|
# 由于此处理器专门用于 RateLimitExceeded,我们可以安全地断言异常类型。
|
||||||
|
# 这满足了类型检查器的要求,并确保了运行时安全。
|
||||||
|
assert isinstance(exc, RateLimitExceeded)
|
||||||
|
return _rate_limit_exceeded_handler(request, exc)
|
||||||
|
|
||||||
|
|
||||||
class Server:
|
class Server:
|
||||||
def __init__(self, host: str | None = None, port: int | None = None, app_name: str = "MaiMCore"):
|
def __init__(self, host: str | None = None, port: int | None = None, app_name: str = "MoFox-Bot"):
|
||||||
|
# 根据配置初始化速率限制器
|
||||||
|
limiter = Limiter(
|
||||||
|
key_func=get_remote_address,
|
||||||
|
default_limits=[bot_config.plugin_http_system.plugin_api_rate_limit_default],
|
||||||
|
)
|
||||||
|
|
||||||
self.app = FastAPI(title=app_name)
|
self.app = FastAPI(title=app_name)
|
||||||
self.host: str = "127.0.0.1"
|
self.host: str = "127.0.0.1"
|
||||||
self.port: int = 8080
|
self.port: int = 8080
|
||||||
self._server: UvicornServer | None = None
|
self._server: UvicornServer | None = None
|
||||||
self.set_address(host, port)
|
self.set_address(host, port)
|
||||||
|
|
||||||
|
# 设置速率限制
|
||||||
|
self.app.state.limiter = limiter
|
||||||
|
self.app.add_exception_handler(RateLimitExceeded, rate_limit_exceeded_handler)
|
||||||
|
|
||||||
|
# 根据配置决定是否添加中间件
|
||||||
|
if bot_config.plugin_http_system.plugin_api_rate_limit_enable:
|
||||||
|
logger.info(f"已为插件API启用全局速率限制: {bot_config.plugin_http_system.plugin_api_rate_limit_default}")
|
||||||
|
self.app.add_middleware(SlowAPIMiddleware)
|
||||||
|
|
||||||
# 配置 CORS
|
# 配置 CORS
|
||||||
origins = [
|
origins = [
|
||||||
"http://localhost:3000", # 允许的前端源
|
"http://localhost:3000", # 允许的前端源
|
||||||
"http://127.0.0.1:3000",
|
"http://127.0.0.1:3000",
|
||||||
"http://127.0.0.1:3000",
|
|
||||||
# 在生产环境中,您应该添加实际的前端域名
|
# 在生产环境中,您应该添加实际的前端域名
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,6 @@ class ModelInfo(ValidatedConfigBase):
|
|||||||
default="light", description="扰动强度(light/medium/heavy)"
|
default="light", description="扰动强度(light/medium/heavy)"
|
||||||
)
|
)
|
||||||
enable_semantic_variants: bool = Field(default=False, description="是否启用语义变体作为扰动策略")
|
enable_semantic_variants: bool = Field(default=False, description="是否启用语义变体作为扰动策略")
|
||||||
|
|
||||||
prepend_noise_instruction: bool = Field(default=False, description="是否在提示词前部添加抗审查指令")
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_prices(cls, v):
|
def validate_prices(cls, v):
|
||||||
"""验证价格必须为非负数"""
|
"""验证价格必须为非负数"""
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from src.config.official_configs import (
|
|||||||
PermissionConfig,
|
PermissionConfig,
|
||||||
PersonalityConfig,
|
PersonalityConfig,
|
||||||
PlanningSystemConfig,
|
PlanningSystemConfig,
|
||||||
|
PluginHttpSystemConfig,
|
||||||
ProactiveThinkingConfig,
|
ProactiveThinkingConfig,
|
||||||
ReactionConfig,
|
ReactionConfig,
|
||||||
ResponsePostProcessConfig,
|
ResponsePostProcessConfig,
|
||||||
@@ -414,6 +415,9 @@ class Config(ValidatedConfigBase):
|
|||||||
proactive_thinking: ProactiveThinkingConfig = Field(
|
proactive_thinking: ProactiveThinkingConfig = Field(
|
||||||
default_factory=lambda: ProactiveThinkingConfig(), description="主动思考配置"
|
default_factory=lambda: ProactiveThinkingConfig(), description="主动思考配置"
|
||||||
)
|
)
|
||||||
|
plugin_http_system: PluginHttpSystemConfig = Field(
|
||||||
|
default_factory=lambda: PluginHttpSystemConfig(), description="插件HTTP端点系统配置"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class APIAdapterConfig(ValidatedConfigBase):
|
class APIAdapterConfig(ValidatedConfigBase):
|
||||||
|
|||||||
@@ -736,6 +736,23 @@ class CommandConfig(ValidatedConfigBase):
|
|||||||
command_prefixes: list[str] = Field(default_factory=lambda: ["/", "!", ".", "#"], description="支持的命令前缀列表")
|
command_prefixes: list[str] = Field(default_factory=lambda: ["/", "!", ".", "#"], description="支持的命令前缀列表")
|
||||||
|
|
||||||
|
|
||||||
|
class PluginHttpSystemConfig(ValidatedConfigBase):
|
||||||
|
"""插件http系统相关配置"""
|
||||||
|
|
||||||
|
enable_plugin_http_endpoints: bool = Field(
|
||||||
|
default=True, description="总开关,是否允许插件创建HTTP端点"
|
||||||
|
)
|
||||||
|
plugin_api_rate_limit_enable: bool = Field(
|
||||||
|
default=True, description="是否为插件API启用全局速率限制"
|
||||||
|
)
|
||||||
|
plugin_api_rate_limit_default: str = Field(
|
||||||
|
default="100/minute", description="插件API的默认速率限制策略"
|
||||||
|
)
|
||||||
|
plugin_api_valid_keys: list[str] = Field(
|
||||||
|
default_factory=list, description="有效的API密钥列表,用于插件认证"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MasterPromptConfig(ValidatedConfigBase):
|
class MasterPromptConfig(ValidatedConfigBase):
|
||||||
"""主人身份提示词配置"""
|
"""主人身份提示词配置"""
|
||||||
|
|
||||||
|
|||||||
@@ -500,8 +500,8 @@ class _PromptProcessor:
|
|||||||
final_prompt_parts = []
|
final_prompt_parts = []
|
||||||
user_prompt = prompt
|
user_prompt = prompt
|
||||||
|
|
||||||
# 步骤 A: (可选) 添加抗审查指令
|
# 步骤 A: 添加抗审查指令
|
||||||
if getattr(model_info, "prepend_noise_instruction", False):
|
if model_info.enable_prompt_perturbation:
|
||||||
final_prompt_parts.append(self.noise_instruction)
|
final_prompt_parts.append(self.noise_instruction)
|
||||||
|
|
||||||
# 步骤 B: (可选) 应用统一的提示词扰动
|
# 步骤 B: (可选) 应用统一的提示词扰动
|
||||||
@@ -516,7 +516,7 @@ class _PromptProcessor:
|
|||||||
final_prompt_parts.append(user_prompt)
|
final_prompt_parts.append(user_prompt)
|
||||||
|
|
||||||
# 步骤 C: (可选) 添加反截断指令
|
# 步骤 C: (可选) 添加反截断指令
|
||||||
if getattr(model_info, "use_anti_truncation", False):
|
if model_info.anti_truncation:
|
||||||
final_prompt_parts.append(self.anti_truncation_instruction)
|
final_prompt_parts.append(self.anti_truncation_instruction)
|
||||||
logger.info(f"模型 '{model_info.name}' (任务: '{task_name}') 已启用反截断功能。")
|
logger.info(f"模型 '{model_info.name}' (任务: '{task_name}') 已启用反截断功能。")
|
||||||
|
|
||||||
@@ -882,7 +882,7 @@ class _RequestStrategy:
|
|||||||
|
|
||||||
# --- 响应内容处理和空回复/截断检查 ---
|
# --- 响应内容处理和空回复/截断检查 ---
|
||||||
content = response.content or ""
|
content = response.content or ""
|
||||||
use_anti_truncation = getattr(model_info, "use_anti_truncation", False)
|
use_anti_truncation = model_info.anti_truncation
|
||||||
processed_content, reasoning, is_truncated = await self.prompt_processor.process_response(
|
processed_content, reasoning, is_truncated = await self.prompt_processor.process_response(
|
||||||
content, use_anti_truncation
|
content, use_anti_truncation
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ from .base import (
|
|||||||
PluginInfo,
|
PluginInfo,
|
||||||
# 新增的增强命令系统
|
# 新增的增强命令系统
|
||||||
PlusCommand,
|
PlusCommand,
|
||||||
|
BaseRouterComponent,
|
||||||
PythonDependency,
|
PythonDependency,
|
||||||
ToolInfo,
|
ToolInfo,
|
||||||
ToolParamType,
|
ToolParamType,
|
||||||
@@ -56,7 +57,7 @@ from .utils.dependency_manager import configure_dependency_manager, get_dependen
|
|||||||
|
|
||||||
__version__ = "2.0.0"
|
__version__ = "2.0.0"
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [ # noqa: RUF022
|
||||||
"ActionActivationType",
|
"ActionActivationType",
|
||||||
"ActionInfo",
|
"ActionInfo",
|
||||||
"BaseAction",
|
"BaseAction",
|
||||||
@@ -82,6 +83,7 @@ __all__ = [
|
|||||||
"PluginInfo",
|
"PluginInfo",
|
||||||
# 增强命令系统
|
# 增强命令系统
|
||||||
"PlusCommand",
|
"PlusCommand",
|
||||||
|
"BaseRouterComponent"
|
||||||
"PythonDependency",
|
"PythonDependency",
|
||||||
"ToolInfo",
|
"ToolInfo",
|
||||||
"ToolParamType",
|
"ToolParamType",
|
||||||
@@ -114,4 +116,4 @@ __all__ = [
|
|||||||
# "ManifestGenerator",
|
# "ManifestGenerator",
|
||||||
# "validate_plugin_manifest",
|
# "validate_plugin_manifest",
|
||||||
# "generate_plugin_manifest",
|
# "generate_plugin_manifest",
|
||||||
]
|
] # type: ignore
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
from .base_action import BaseAction
|
from .base_action import BaseAction
|
||||||
from .base_command import BaseCommand
|
from .base_command import BaseCommand
|
||||||
from .base_events_handler import BaseEventHandler
|
from .base_events_handler import BaseEventHandler
|
||||||
|
from .base_http_component import BaseRouterComponent
|
||||||
from .base_plugin import BasePlugin
|
from .base_plugin import BasePlugin
|
||||||
from .base_prompt import BasePrompt
|
from .base_prompt import BasePrompt
|
||||||
from .base_tool import BaseTool
|
from .base_tool import BaseTool
|
||||||
@@ -55,7 +56,7 @@ __all__ = [
|
|||||||
"PluginMetadata",
|
"PluginMetadata",
|
||||||
# 增强命令系统
|
# 增强命令系统
|
||||||
"PlusCommand",
|
"PlusCommand",
|
||||||
"PlusCommandAdapter",
|
"BaseRouterComponent"
|
||||||
"PlusCommandInfo",
|
"PlusCommandInfo",
|
||||||
"PythonDependency",
|
"PythonDependency",
|
||||||
"ToolInfo",
|
"ToolInfo",
|
||||||
|
|||||||
40
src/plugin_system/base/base_http_component.py
Normal file
40
src/plugin_system/base/base_http_component.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from .component_types import ComponentType, RouterInfo
|
||||||
|
|
||||||
|
|
||||||
|
class BaseRouterComponent(ABC):
|
||||||
|
"""
|
||||||
|
用于暴露HTTP端点的组件基类。
|
||||||
|
插件开发者应继承此类,并实现 register_endpoints 方法来定义API路由。
|
||||||
|
"""
|
||||||
|
# 组件元数据,由插件管理器读取
|
||||||
|
component_name: str
|
||||||
|
component_description: str
|
||||||
|
component_version: str = "1.0.0"
|
||||||
|
|
||||||
|
# 每个组件实例都会管理自己的APIRouter
|
||||||
|
router: APIRouter
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.router = APIRouter()
|
||||||
|
self.register_endpoints()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def register_endpoints(self) -> None:
|
||||||
|
"""
|
||||||
|
【开发者必须实现】
|
||||||
|
在此方法中定义所有HTTP端点。
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_router_info(cls) -> "RouterInfo":
|
||||||
|
"""从类属性生成RouterInfo"""
|
||||||
|
return RouterInfo(
|
||||||
|
name=cls.component_name,
|
||||||
|
description=getattr(cls, "component_description", "路由组件"),
|
||||||
|
component_type=ComponentType.ROUTER,
|
||||||
|
)
|
||||||
@@ -53,6 +53,7 @@ class ComponentType(Enum):
|
|||||||
CHATTER = "chatter" # 聊天处理器组件
|
CHATTER = "chatter" # 聊天处理器组件
|
||||||
INTEREST_CALCULATOR = "interest_calculator" # 兴趣度计算组件
|
INTEREST_CALCULATOR = "interest_calculator" # 兴趣度计算组件
|
||||||
PROMPT = "prompt" # Prompt组件
|
PROMPT = "prompt" # Prompt组件
|
||||||
|
ROUTER = "router" # 路由组件
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.value
|
return self.value
|
||||||
@@ -146,6 +147,7 @@ class PermissionNodeField:
|
|||||||
node_name: str # 节点名称 (例如 "manage" 或 "view")
|
node_name: str # 节点名称 (例如 "manage" 或 "view")
|
||||||
description: str # 权限描述
|
description: str # 权限描述
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ComponentInfo:
|
class ComponentInfo:
|
||||||
"""组件信息"""
|
"""组件信息"""
|
||||||
@@ -442,3 +444,11 @@ class MaiMessages:
|
|||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.message_segments is None:
|
if self.message_segments is None:
|
||||||
self.message_segments = []
|
self.message_segments = []
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RouterInfo(ComponentInfo):
|
||||||
|
"""路由组件信息"""
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
super().__post_init__()
|
||||||
|
self.component_type = ComponentType.ROUTER
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ from pathlib import Path
|
|||||||
from re import Pattern
|
from re import Pattern
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
|
from fastapi import Depends
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
from src.config.config import global_config as bot_config
|
||||||
from src.plugin_system.base.base_action import BaseAction
|
from src.plugin_system.base.base_action import BaseAction
|
||||||
from src.plugin_system.base.base_chatter import BaseChatter
|
from src.plugin_system.base.base_chatter import BaseChatter
|
||||||
from src.plugin_system.base.base_command import BaseCommand
|
from src.plugin_system.base.base_command import BaseCommand
|
||||||
from src.plugin_system.base.base_events_handler import BaseEventHandler
|
from src.plugin_system.base.base_events_handler import BaseEventHandler
|
||||||
|
from src.plugin_system.base.base_http_component import BaseRouterComponent
|
||||||
from src.plugin_system.base.base_interest_calculator import BaseInterestCalculator
|
from src.plugin_system.base.base_interest_calculator import BaseInterestCalculator
|
||||||
from src.plugin_system.base.base_prompt import BasePrompt
|
from src.plugin_system.base.base_prompt import BasePrompt
|
||||||
from src.plugin_system.base.base_tool import BaseTool
|
from src.plugin_system.base.base_tool import BaseTool
|
||||||
@@ -24,6 +28,7 @@ from src.plugin_system.base.component_types import (
|
|||||||
PluginInfo,
|
PluginInfo,
|
||||||
PlusCommandInfo,
|
PlusCommandInfo,
|
||||||
PromptInfo,
|
PromptInfo,
|
||||||
|
RouterInfo,
|
||||||
ToolInfo,
|
ToolInfo,
|
||||||
)
|
)
|
||||||
from src.plugin_system.base.plus_command import PlusCommand, create_legacy_command_adapter
|
from src.plugin_system.base.plus_command import PlusCommand, create_legacy_command_adapter
|
||||||
@@ -40,6 +45,7 @@ ComponentClassType = (
|
|||||||
| type[BaseChatter]
|
| type[BaseChatter]
|
||||||
| type[BaseInterestCalculator]
|
| type[BaseInterestCalculator]
|
||||||
| type[BasePrompt]
|
| type[BasePrompt]
|
||||||
|
| type[BaseRouterComponent]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -194,6 +200,10 @@ class ComponentRegistry:
|
|||||||
assert isinstance(component_info, PromptInfo)
|
assert isinstance(component_info, PromptInfo)
|
||||||
assert issubclass(component_class, BasePrompt)
|
assert issubclass(component_class, BasePrompt)
|
||||||
ret = self._register_prompt_component(component_info, component_class)
|
ret = self._register_prompt_component(component_info, component_class)
|
||||||
|
case ComponentType.ROUTER:
|
||||||
|
assert isinstance(component_info, RouterInfo)
|
||||||
|
assert issubclass(component_class, BaseRouterComponent)
|
||||||
|
ret = self._register_router_component(component_info, component_class)
|
||||||
case _:
|
case _:
|
||||||
logger.warning(f"未知组件类型: {component_type}")
|
logger.warning(f"未知组件类型: {component_type}")
|
||||||
ret = False
|
ret = False
|
||||||
@@ -373,6 +383,43 @@ class ComponentRegistry:
|
|||||||
logger.debug(f"已注册Prompt组件: {prompt_name}")
|
logger.debug(f"已注册Prompt组件: {prompt_name}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _register_router_component(self, router_info: RouterInfo, router_class: type[BaseRouterComponent]) -> bool:
|
||||||
|
"""注册Router组件并将其端点挂载到主服务器"""
|
||||||
|
# 1. 检查总开关是否开启
|
||||||
|
if not bot_config.plugin_http_system.enable_plugin_http_endpoints:
|
||||||
|
logger.info("插件HTTP端点功能已禁用,跳过路由注册")
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
from src.common.server import get_global_server
|
||||||
|
|
||||||
|
router_name = router_info.name
|
||||||
|
plugin_name = router_info.plugin_name
|
||||||
|
|
||||||
|
# 2. 实例化组件以触发其 __init__ 和 register_endpoints
|
||||||
|
component_instance = router_class()
|
||||||
|
|
||||||
|
# 3. 获取配置好的 APIRouter
|
||||||
|
plugin_router = component_instance.router
|
||||||
|
|
||||||
|
# 4. 获取全局服务器实例
|
||||||
|
server = get_global_server()
|
||||||
|
|
||||||
|
# 5. 生成唯一的URL前缀
|
||||||
|
prefix = f"/plugins/{plugin_name}"
|
||||||
|
|
||||||
|
# 6. 注册路由,并使用插件名作为API文档的分组标签
|
||||||
|
# 移除了dependencies参数,因为现在由每个端点自行决定是否需要验证
|
||||||
|
server.app.include_router(
|
||||||
|
plugin_router, prefix=prefix, tags=[plugin_name]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(f"成功将插件 '{plugin_name}' 的路由组件 '{router_name}' 挂载到: {prefix}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"注册路由组件 '{router_info.name}' 时出错: {e}", exc_info=True)
|
||||||
|
return False
|
||||||
|
|
||||||
# === 组件移除相关 ===
|
# === 组件移除相关 ===
|
||||||
|
|
||||||
async def remove_component(self, component_name: str, component_type: ComponentType, plugin_name: str) -> bool:
|
async def remove_component(self, component_name: str, component_type: ComponentType, plugin_name: str) -> bool:
|
||||||
@@ -616,6 +663,7 @@ class ComponentRegistry:
|
|||||||
| BaseChatter
|
| BaseChatter
|
||||||
| BaseInterestCalculator
|
| BaseInterestCalculator
|
||||||
| BasePrompt
|
| BasePrompt
|
||||||
|
| BaseRouterComponent
|
||||||
]
|
]
|
||||||
| None
|
| None
|
||||||
):
|
):
|
||||||
@@ -643,6 +691,8 @@ class ComponentRegistry:
|
|||||||
| type[PlusCommand]
|
| type[PlusCommand]
|
||||||
| type[BaseChatter]
|
| type[BaseChatter]
|
||||||
| type[BaseInterestCalculator]
|
| type[BaseInterestCalculator]
|
||||||
|
| type[BasePrompt]
|
||||||
|
| type[BaseRouterComponent]
|
||||||
| None,
|
| None,
|
||||||
self._components_classes.get(namespaced_name),
|
self._components_classes.get(namespaced_name),
|
||||||
)
|
)
|
||||||
@@ -825,6 +875,7 @@ class ComponentRegistry:
|
|||||||
def get_plugin_components(self, plugin_name: str) -> list["ComponentInfo"]:
|
def get_plugin_components(self, plugin_name: str) -> list["ComponentInfo"]:
|
||||||
"""获取插件的所有组件"""
|
"""获取插件的所有组件"""
|
||||||
plugin_info = self.get_plugin_info(plugin_name)
|
plugin_info = self.get_plugin_info(plugin_name)
|
||||||
|
logger.info(plugin_info.components)
|
||||||
return plugin_info.components if plugin_info else []
|
return plugin_info.components if plugin_info else []
|
||||||
|
|
||||||
def get_plugin_config(self, plugin_name: str) -> dict:
|
def get_plugin_config(self, plugin_name: str) -> dict:
|
||||||
@@ -867,6 +918,7 @@ class ComponentRegistry:
|
|||||||
plus_command_components: int = 0
|
plus_command_components: int = 0
|
||||||
chatter_components: int = 0
|
chatter_components: int = 0
|
||||||
prompt_components: int = 0
|
prompt_components: int = 0
|
||||||
|
router_components: int = 0
|
||||||
for component in self._components.values():
|
for component in self._components.values():
|
||||||
if component.component_type == ComponentType.ACTION:
|
if component.component_type == ComponentType.ACTION:
|
||||||
action_components += 1
|
action_components += 1
|
||||||
@@ -882,6 +934,8 @@ class ComponentRegistry:
|
|||||||
chatter_components += 1
|
chatter_components += 1
|
||||||
elif component.component_type == ComponentType.PROMPT:
|
elif component.component_type == ComponentType.PROMPT:
|
||||||
prompt_components += 1
|
prompt_components += 1
|
||||||
|
elif component.component_type == ComponentType.ROUTER:
|
||||||
|
router_components += 1
|
||||||
return {
|
return {
|
||||||
"action_components": action_components,
|
"action_components": action_components,
|
||||||
"command_components": command_components,
|
"command_components": command_components,
|
||||||
@@ -891,6 +945,7 @@ class ComponentRegistry:
|
|||||||
"plus_command_components": plus_command_components,
|
"plus_command_components": plus_command_components,
|
||||||
"chatter_components": chatter_components,
|
"chatter_components": chatter_components,
|
||||||
"prompt_components": prompt_components,
|
"prompt_components": prompt_components,
|
||||||
|
"router_components": router_components,
|
||||||
"total_components": len(self._components),
|
"total_components": len(self._components),
|
||||||
"total_plugins": len(self._plugins),
|
"total_plugins": len(self._plugins),
|
||||||
"components_by_type": {
|
"components_by_type": {
|
||||||
|
|||||||
@@ -405,13 +405,14 @@ class PluginManager:
|
|||||||
plus_command_count = stats.get("plus_command_components", 0)
|
plus_command_count = stats.get("plus_command_components", 0)
|
||||||
chatter_count = stats.get("chatter_components", 0)
|
chatter_count = stats.get("chatter_components", 0)
|
||||||
prompt_count = stats.get("prompt_components", 0)
|
prompt_count = stats.get("prompt_components", 0)
|
||||||
|
router_count = stats.get("router_components", 0)
|
||||||
total_components = stats.get("total_components", 0)
|
total_components = stats.get("total_components", 0)
|
||||||
|
|
||||||
# 📋 显示插件加载总览
|
# 📋 显示插件加载总览
|
||||||
if total_registered > 0:
|
if total_registered > 0:
|
||||||
logger.info("🎉 插件系统加载完成!")
|
logger.info("🎉 插件系统加载完成!")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count})"
|
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count}, Router: {router_count})"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 显示详细的插件列表
|
# 显示详细的插件列表
|
||||||
@@ -452,6 +453,9 @@ class PluginManager:
|
|||||||
prompt_components = [
|
prompt_components = [
|
||||||
c for c in plugin_info.components if c.component_type == ComponentType.PROMPT
|
c for c in plugin_info.components if c.component_type == ComponentType.PROMPT
|
||||||
]
|
]
|
||||||
|
router_components = [
|
||||||
|
c for c in plugin_info.components if c.component_type == ComponentType.ROUTER
|
||||||
|
]
|
||||||
|
|
||||||
if action_components:
|
if action_components:
|
||||||
action_details = [format_component(c) for c in action_components]
|
action_details = [format_component(c) for c in action_components]
|
||||||
@@ -478,6 +482,9 @@ class PluginManager:
|
|||||||
if prompt_components:
|
if prompt_components:
|
||||||
prompt_details = [format_component(c) for c in prompt_components]
|
prompt_details = [format_component(c) for c in prompt_components]
|
||||||
logger.info(f" 📝 Prompt组件: {', '.join(prompt_details)}")
|
logger.info(f" 📝 Prompt组件: {', '.join(prompt_details)}")
|
||||||
|
if router_components:
|
||||||
|
router_details = [format_component(c) for c in router_components]
|
||||||
|
logger.info(f" 🌐 Router组件: {', '.join(router_details)}")
|
||||||
|
|
||||||
# 权限节点信息
|
# 权限节点信息
|
||||||
if plugin_instance := self.loaded_plugins.get(plugin_name):
|
if plugin_instance := self.loaded_plugins.get(plugin_name):
|
||||||
|
|||||||
@@ -80,37 +80,39 @@ class ReplyTrackerService:
|
|||||||
if old_data_file.exists():
|
if old_data_file.exists():
|
||||||
logger.info(f"检测到旧的数据文件 '{old_data_file}',开始执行一次性迁移...")
|
logger.info(f"检测到旧的数据文件 '{old_data_file}',开始执行一次性迁移...")
|
||||||
try:
|
try:
|
||||||
# 读取旧文件内容
|
# 步骤1: 读取旧文件内容并立即关闭文件
|
||||||
with open(old_data_file, "rb") as f:
|
with open(old_data_file, "rb") as f:
|
||||||
file_content = f.read()
|
file_content = f.read()
|
||||||
# 如果文件为空,直接删除,无需迁移
|
|
||||||
if not file_content.strip():
|
|
||||||
logger.warning("旧数据文件为空,无需迁移。")
|
|
||||||
os.remove(old_data_file)
|
|
||||||
logger.info(f"空的旧数据文件 '{old_data_file}' 已被删除。")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 解析JSON数据
|
# 步骤2: 处理文件内容
|
||||||
old_data = orjson.loads(file_content)
|
# 如果文件为空,直接删除,无需迁移
|
||||||
|
if not file_content.strip():
|
||||||
|
logger.warning("旧数据文件为空,无需迁移。")
|
||||||
|
os.remove(old_data_file)
|
||||||
|
logger.info(f"空的旧数据文件 '{old_data_file}' 已被删除。")
|
||||||
|
return
|
||||||
|
|
||||||
# 验证数据格式是否正确
|
# 解析JSON数据
|
||||||
if self._validate_data(old_data):
|
old_data = orjson.loads(file_content)
|
||||||
# 验证通过,将数据写入新的存储API
|
|
||||||
self.storage.set("data", old_data)
|
|
||||||
# 立即强制保存,确保迁移数据落盘
|
|
||||||
self.storage._save_data()
|
|
||||||
logger.info("旧数据已成功迁移到新的存储API。")
|
|
||||||
|
|
||||||
# 将旧文件重命名为备份文件,而不是直接删除,以防万一
|
# 步骤3: 验证数据并执行迁移/备份
|
||||||
backup_file = old_data_file.with_suffix(f".json.bak.migrated.{int(time.time())}")
|
if self._validate_data(old_data):
|
||||||
old_data_file.rename(backup_file)
|
# 验证通过,将数据写入新的存储API
|
||||||
logger.info(f"旧数据文件已成功迁移并备份为: {backup_file}")
|
self.storage.set("data", old_data)
|
||||||
else:
|
# 立即强制保存,确保迁移数据落盘
|
||||||
# 如果数据格式无效,迁移中止,并备份损坏的文件
|
self.storage._save_data()
|
||||||
logger.error("旧数据文件格式无效,迁移中止。")
|
logger.info("旧数据已成功迁移到新的存储API。")
|
||||||
backup_file = old_data_file.with_suffix(f".json.bak.invalid.{int(time.time())}")
|
|
||||||
old_data_file.rename(backup_file)
|
# 将旧文件重命名为备份文件
|
||||||
logger.warning(f"已将无效的旧数据文件备份为: {backup_file}")
|
backup_file = old_data_file.with_suffix(f".json.bak.migrated.{int(time.time())}")
|
||||||
|
old_data_file.rename(backup_file)
|
||||||
|
logger.info(f"旧数据文件已成功迁移并备份为: {backup_file}")
|
||||||
|
else:
|
||||||
|
# 如果数据格式无效,迁移中止,并备份损坏的文件
|
||||||
|
logger.error("旧数据文件格式无效,迁移中止。")
|
||||||
|
backup_file = old_data_file.with_suffix(f".json.bak.invalid.{int(time.time())}")
|
||||||
|
old_data_file.rename(backup_file)
|
||||||
|
logger.warning(f"已将无效的旧数据文件备份为: {backup_file}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 捕获迁移过程中可能出现的任何异常
|
# 捕获迁移过程中可能出现的任何异常
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[inner]
|
[inner]
|
||||||
version = "7.7.1"
|
version = "7.7.4"
|
||||||
|
|
||||||
#----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读----
|
#----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读----
|
||||||
#如果你想要修改配置文件,请递增version的值
|
#如果你想要修改配置文件,请递增version的值
|
||||||
@@ -59,6 +59,25 @@ cache_max_item_size_mb = 5 # 单个缓存条目最大大小(MB),超过此
|
|||||||
# 示例:[["qq", "123456"], ["telegram", "user789"]]
|
# 示例:[["qq", "123456"], ["telegram", "user789"]]
|
||||||
master_users = []# ["qq", "123456789"], # 示例:QQ平台的Master用户
|
master_users = []# ["qq", "123456789"], # 示例:QQ平台的Master用户
|
||||||
|
|
||||||
|
# ==================== 插件HTTP端点系统配置 ====================
|
||||||
|
[plugin_http_system]
|
||||||
|
# 总开关,用于启用或禁用所有插件的HTTP端点功能
|
||||||
|
enable_plugin_http_endpoints = true
|
||||||
|
|
||||||
|
# ==================== 安全相关配置 ====================
|
||||||
|
# --- 插件API速率限制 ---
|
||||||
|
# 是否为插件暴露的API启用全局速率限制
|
||||||
|
plugin_api_rate_limit_enable = true
|
||||||
|
# 默认的速率限制策略 (格式: "次数/时间单位")
|
||||||
|
# 可用单位: second, minute, hour, day
|
||||||
|
plugin_api_rate_limit_default = "100/minute"
|
||||||
|
|
||||||
|
# --- 插件API密钥认证 ---
|
||||||
|
# 用于访问需要认证的插件API的有效密钥列表
|
||||||
|
# 如果列表为空,则所有需要认证的API都将无法访问
|
||||||
|
# 例如: ["your-secret-key-1", "your-secret-key-2"]
|
||||||
|
plugin_api_valid_keys = []
|
||||||
|
|
||||||
[permission.master_prompt] # 主人身份提示词配置
|
[permission.master_prompt] # 主人身份提示词配置
|
||||||
enable = false # 是否启用主人/非主人提示注入
|
enable = false # 是否启用主人/非主人提示注入
|
||||||
master_hint = "你正在与自己的主人交流,注意展现亲切与尊重。" # 主人提示词
|
master_hint = "你正在与自己的主人交流,注意展现亲切与尊重。" # 主人提示词
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[inner]
|
[inner]
|
||||||
version = "1.3.8"
|
version = "1.3.9"
|
||||||
|
|
||||||
# 配置文件版本号迭代规则同bot_config.toml
|
# 配置文件版本号迭代规则同bot_config.toml
|
||||||
|
|
||||||
@@ -37,10 +37,11 @@ name = "deepseek-v3" # 模型名称(可随意命名,在后面
|
|||||||
api_provider = "DeepSeek" # API服务商名称(对应在api_providers中配置的服务商名称)
|
api_provider = "DeepSeek" # API服务商名称(对应在api_providers中配置的服务商名称)
|
||||||
price_in = 2.0 # 输入价格(用于API调用统计,单位:元/ M token)(可选,若无该字段,默认值为0)
|
price_in = 2.0 # 输入价格(用于API调用统计,单位:元/ M token)(可选,若无该字段,默认值为0)
|
||||||
price_out = 8.0 # 输出价格(用于API调用统计,单位:元/ M token)(可选,若无该字段,默认值为0)
|
price_out = 8.0 # 输出价格(用于API调用统计,单位:元/ M token)(可选,若无该字段,默认值为0)
|
||||||
#force_stream_mode = true # 强制流式输出模式(若模型不支持非流式输出,请取消该注释,启用强制流式输出,若无该字段,默认值为false)
|
#force_stream_mode = false # [可选] 强制流式输出模式。如果模型不支持非流式输出,请取消注释以启用。默认为 false。
|
||||||
#use_anti_truncation = true # [可选] 启用反截断功能。当模型输出不完整时,系统会自动重试。建议只为有需要的模型(如Gemini)开启。
|
#anti_truncation = false # [可选] 启用反截断功能。当模型输出不完整时,系统会自动重试。建议只为需要的模型(如Gemini)开启。默认为 false。
|
||||||
#enable_content_obfuscation = true # [可选] 启用内容混淆功能,用于特定场景下的内容处理(例如某些内容审查比较严的模型和稀疏注意模型)
|
#enable_prompt_perturbation = false # [可选] 启用提示词扰动。此功能整合了内容混淆和注意力优化,默认为 false。
|
||||||
#obfuscation_intensity = 2 # 混淆强度(1-3级,1=低强度,2=中强度,3=高强度)
|
#perturbation_strength = "light" # [可选] 扰动强度。仅在 enable_prompt_perturbation 为 true 时生效。可选值为 "light", "medium", "heavy"。默认为 "light"。
|
||||||
|
#enable_semantic_variants = false # [可选] 启用语义变体。作为一种扰动策略,生成语义上相似但表达不同的提示。默认为 false。
|
||||||
|
|
||||||
[[models]]
|
[[models]]
|
||||||
model_identifier = "deepseek-ai/DeepSeek-V3.2-Exp"
|
model_identifier = "deepseek-ai/DeepSeek-V3.2-Exp"
|
||||||
|
|||||||
Reference in New Issue
Block a user