package top.jie65535.mirai import kotlinx.serialization.Serializable import net.mamoe.mirai.console.data.AutoSavePluginConfig import net.mamoe.mirai.console.data.ValueDescription import net.mamoe.mirai.console.data.value /** * 聊天模型备用接入点。用于主接入点(openAiApi/openAiToken/chatModel)连续失败时容灾切换。 * 任一字段留空则继承主接入点对应配置,因此可只换 API KEY、只换模型、或整体换一个服务商。 */ @Serializable data class ChatFallbackEndpoint( val api: String = "", val token: String = "", val model: String = "", val extraBody: String = "", ) object PluginConfig : AutoSavePluginConfig("Config") { @ValueDescription("主人QQ,AI可以通过工具向主人发起请求,会等待一段时间") val ownerId: Long by value() @ValueDescription("OpenAI API base url") val openAiApi: String by value("https://dashscope.aliyuncs.com/compatible-mode/v1/") @ValueDescription("OpenAI API Token") var openAiToken: String by value("") @ValueDescription("Chat模型") var chatModel: String by value("qwen-max") @ValueDescription("Chat模型温度,默认为null") var chatTemperature: Double? by value(null) @ValueDescription("推理模型API") var reasoningModelApi: String by value("https://dashscope.aliyuncs.com/compatible-mode/v1/") @ValueDescription("推理模型Token") var reasoningModelToken: String by value("") @ValueDescription("推理模型") var reasoningModel: String by value("qwq-plus") @ValueDescription("视觉模型API") var visualModelApi: String by value("https://dashscope.aliyuncs.com/compatible-mode/v1/") @ValueDescription("视觉模型Token") var visualModelToken: String by value("") @ValueDescription("视觉模型") var visualModel: String by value("qwen-vl-plus") @ValueDescription("聊天模型额外请求体JSON,会合并到请求体中。例如DeepSeek关闭思维: {\"thinking\": {\"type\": \"disabled\"}}") val chatModelExtraBody: String by value("") @ValueDescription("聊天模型备用接入点列表(容灾)。主接入点连续失败时按顺序切换;每项留空的字段会继承主接入点,例如只换API KEY就只填token,只换模型就只填model") val chatFallbacks: List by value() @ValueDescription("备用接入点冷却时间(分钟)。某接入点失败后在此时间内会被排到重试队尾,避免每条消息都先卡在故障接入点上。设为0禁用,默认5分钟") val fallbackCooldownMinutes: Long by value(5L) @ValueDescription("推理模型额外请求体JSON,会合并到请求体中。例如DeepSeek启用思维: {\"thinking\": {\"type\": \"enabled\"}}") val reasoningModelExtraBody: String by value("") @ValueDescription("视觉模型额外请求体JSON,会合并到请求体中。") val visualModelExtraBody: String by value("") @ValueDescription("视觉模型是否先由机器人下载图片并以Base64上传。建议开启,可避免百炼下载QQ临时图片链接失败") val visualImageBase64Enabled: Boolean by value(true) @ValueDescription("视觉模型单次工具调用的最大尝试次数,取值1~3,默认2次。图片只下载和编码一次,重试仅重新请求模型") val visualRetryMax: Int by value(2) @ValueDescription("百炼平台API KEY") val dashScopeApiKey: String by value("") @ValueDescription("百炼平台图像模型,支持文生图与图像编辑。可选:qwen-image-2.0 / qwen-image-2.0-pro / qwen-image-edit-max / qwen-image-edit-plus 等") val imageModel: String by value("qwen-image-2.0") @ValueDescription("是否在生成的图片右下角添加 Qwen-Image 水印") val imageWatermark: Boolean by value(false) @ValueDescription("百炼平台TTS模型。qwen3-tts-instruct-flash 支持 instructions 指令控制;纯发音可用 qwen3-tts-flash 或 qwen-tts") val ttsModel: String by value("qwen3-tts-instruct-flash") @ValueDescription("Jina API Key") val jinaApiKey by value("") @ValueDescription("SearXNG 搜索引擎地址,如 http://127.0.0.1:8080/search 必须启用允许json格式返回") val searXngUrl: String by value("") @ValueDescription("在线运行代码 glot.io 的 api token,在官网注册账号即可获取。") val glotToken: String by value("") @ValueDescription("和风天气专属 API Host,例如 abc1234xyz.def.qweatherapi.com") val qWeatherApiHost: String by value("") @ValueDescription("和风天气项目 ID,用于 JWT 的 sub") val qWeatherProjectId: String by value("") @ValueDescription("和风天气凭据 ID,用于 JWT 的 kid") val qWeatherCredentialId: String by value("") @ValueDescription("和风天气 Ed25519 私钥文件路径,相对于插件配置目录,也可以填写绝对路径") val qWeatherPrivateKeyPath: String by value("qweather-ed25519-private.pem") @ValueDescription("群管理是否自动拥有对话权限,默认是") val groupOpHasChatPermission: Boolean by value(true) @ValueDescription("好友是否自动拥有对话权限,默认是") val friendHasChatPermission: Boolean by value(true) @ValueDescription("机器人是否可以禁言别人,默认禁止") val canMute: Boolean by value(false) @ValueDescription("群荣誉等级权限门槛,达到这个等级相当于自动拥有对话权限。") val temperaturePermission: Int by value(50) @ValueDescription("等待响应超时时间(整个请求的总超时与socket读超时),单位毫秒,默认60秒") val timeout: Long by value(60000L) @ValueDescription("首块响应超时时间,单位毫秒,默认10秒。若连接建立后在此时间内没收到首块data:则中断走重试") val firstChunkTimeout: Long by value(10000L) @ValueDescription("视觉模型首块响应超时时间,单位毫秒,默认120秒。视觉模型需先下载图片再出首块,比对话天然慢,故单独放宽") val visualFirstChunkTimeout: Long by value(120000L) @ValueDescription("推理模型首块响应超时时间,单位毫秒,默认90秒。推理模型出首块前常有思考预热,比对话慢,故单独放宽") val reasoningFirstChunkTimeout: Long by value(90000L) @Deprecated("使用外部文件而不是在配置文件内保存提示词") @ValueDescription("系统提示词,该字段已弃用,使用提示词文件而不是在这里修改") var prompt: String by value("你是一个乐于助人的助手") @ValueDescription("系统提示词文件路径,相对于插件配置目录") val promptFile: String by value("SystemPrompt.md") @ValueDescription("创建Prompt时取最近多少分钟内的消息") val historyWindowMin: Int by value(10) @ValueDescription("创建Prompt时取最多几条消息") val historyMessageLimit: Int by value(20) @ValueDescription("启用对话上下文内存缓存,允许在短时间内保持上下文连续") val enableContextCache by value(true) @ValueDescription("上下文缓存有效期(分钟),超过此时间未活动则重新创建上下文") val contextCacheTimeoutMinutes by value(10) @ValueDescription("是否打印Prompt便于调试") val logPrompt by value(false) @ValueDescription("达到需要合并转发消息的阈值") val messageMergeThreshold by value(150) @ValueDescription("最大循环次数,至少2次") val retryMax: Int by value(5) @ValueDescription("关键字呼叫,支持正则表达式") val callKeyword by value("[小筱][林淋月玥]") @ValueDescription("是否显示工具调用消息,默认是") val showToolCallingMessage by value(true) @ValueDescription("是否启用记忆编辑功能,记忆存在data目录,提示词中需要加上{memory}来填充记忆,每个群都有独立记忆") val memoryEnabled by value(true) @ValueDescription("是否启用技能系统,技能存在data/skills目录(全局跨群),提示词中需要加上{skills}来注入技能索引") val skillsEnabled by value(true) @ValueDescription("是否启用好感度系统") val enableFavorabilitySystem by value(true) @ValueDescription("好感度每日基础偏移速度(点/天)") val favorabilityBaseShiftSpeed by value(2.0) @ValueDescription("表情包路径,配置后会加载目录下的文件名,提示词中需要用{meme}来插入上下文") val memeDir: String by value("") @ValueDescription("请求主人回复等待时间,单位毫秒,默认300秒") val requestOwnerWaitTimeout: Long by value(300000L) @ValueDescription("单个工具调用返回内容的最大字符数,超过将被截断并标注") val maxToolOutputLength: Int by value(15000) @ValueDescription("聊天记录搜索最大天数") val searchHistoryMaxDays: Int by value(30) @ValueDescription("聊天记录搜索最大查询条数,防止内存溢出") val searchHistoryMaxRecords: Int by value(5000) }