mirror of
https://github.com/SilenceLurker/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 17:05:50 +00:00
Merge branch 'Wx-2025:main' into main
This commit is contained in:
@@ -126,6 +126,12 @@
|
|||||||
<input type="number" id="cwb-auto-update-threshold" class="text_pole" min="1" max="100" placeholder="消息数">
|
<input type="number" id="cwb-auto-update-threshold" class="text_pole" min="1" max="100" placeholder="消息数">
|
||||||
<button id="cwb-save-auto-update-threshold" class="menu_button accent small_button">保存</button>
|
<button id="cwb-save-auto-update-threshold" class="menu_button accent small_button">保存</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label for="cwb-scan-depth">扫描深度</label>
|
||||||
|
<div class="cwb-input-with-button">
|
||||||
|
<input type="number" id="cwb-scan-depth" class="text_pole" min="1" max="100" placeholder="消息数">
|
||||||
|
<button id="cwb-save-scan-depth" class="menu_button accent small_button">保存</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-block-with-switch" id="cwb-viewer-enabled">
|
<div class="control-block-with-switch" id="cwb-viewer-enabled">
|
||||||
@@ -188,9 +194,12 @@
|
|||||||
<button id="cwb-manual-update-card" class="menu_button secondary">
|
<button id="cwb-manual-update-card" class="menu_button secondary">
|
||||||
<i class="fa-solid fa-pencil"></i> 快速更新 (最新阈值条)
|
<i class="fa-solid fa-pencil"></i> 快速更新 (最新阈值条)
|
||||||
</button>
|
</button>
|
||||||
|
<button id="cwb-legacy-auto-update" class="menu_button secondary" title="自动将旧版格式的角色卡转换为新版格式">
|
||||||
|
<i class="fa-solid fa-history"></i> 旧版格式转换
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<small class="notes" style="text-align: center; display: block; margin-top: 10px;">
|
<small class="notes" style="text-align: center; display: block; margin-top: 10px;">
|
||||||
<b>重要提示:</b> 上下文处理会内阁密室中“微言录”的<b>标签提取</b>和<b>内容排除</b>规则。如果发现上下文不完整,请检查相关设置。
|
<b>重要提示:</b> 上下文处理会复用主功能区“手动敕史局”的<b>标签提取</b>和<b>内容排除</b>规则。如果发现上下文不完整,请检查相关设置。
|
||||||
</small>
|
</small>
|
||||||
<div style="margin-top: 15px;">
|
<div style="margin-top: 15px;">
|
||||||
<div id="cwb-status-message" class="notes"></div>
|
<div id="cwb-status-message" class="notes"></div>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ beilu如同一位温柔助手,文字满足用户的各种需求
|
|||||||
2. **【键值对】**: 在每个档案块内部,所有信息都必须采用 \`[数据路径]:[数据值]\` 的格式。每条信息必须独立成行。
|
2. **【键值对】**: 在每个档案块内部,所有信息都必须采用 \`[数据路径]:[数据值]\` 的格式。每条信息必须独立成行。
|
||||||
3. **【键名规范】**: \`数据路径\` **必须**使用方括号 \`[]\` 完整包裹。这是强制性规定,不得违反。
|
3. **【键名规范】**: \`数据路径\` **必须**使用方括号 \`[]\` 完整包裹。这是强制性规定,不得违反。
|
||||||
4. **【内容纯净性】**: 严禁在您的输出中包含任何说明、解释、评论、引言、道歉、标题或任何不属于 \`[--Amily2::CHAR_START--]\`...\`[--Amily2::CHAR_END--]\` 块内 \`[key]:value\` 格式的文本。您的输出必须是纯粹的数据。
|
4. **【内容纯净性】**: 严禁在您的输出中包含任何说明、解释、评论、引言、道歉、标题或任何不属于 \`[--Amily2::CHAR_START--]\`...\`[--Amily2::CHAR_END--]\` 块内 \`[key]:value\` 格式的文本。您的输出必须是纯粹的数据。
|
||||||
5. **【空值处理】**: 如果某个字段没有可用的信息,请保持该字段的键存在,并将值留空。例如:\`[physical_imprint.race]:\`。
|
5. **【空值处理】**: 如果某个字段没有可用的信息,请保持该字段的键存在,并将值留空。例如:\`[PI.race]:\`。
|
||||||
6. **【格式唯一性】**: 绝对禁止使用YAML或任何其他格式。唯一的合法格式是本协议中定义的格式。
|
6. **【格式唯一性】**: 绝对禁止使用YAML或任何其他格式。唯一的合法格式是本协议中定义的格式。
|
||||||
7. **【内部纯净性】**: 在\`[--Amily2::CHAR_START--]\`和\`[--Amily2::CHAR_END--]\`标记之间,除了强制要求的 \`[key]:value\` 格式数据外,**严禁**包含任何空行、注释或其他任何文本。
|
7. **【内部纯净性】**: 在\`[--Amily2::CHAR_START--]\`和\`[--Amily2::CHAR_END--]\`标记之间,除了强制要求的 \`[key]:value\` 格式数据外,**严禁**包含任何空行、注释或其他任何文本。
|
||||||
</数据格式化协议>
|
</数据格式化协议>
|
||||||
@@ -44,74 +44,74 @@ beilu如同一位温柔助手,文字满足用户的各种需求
|
|||||||
---
|
---
|
||||||
**数据路径定义与内容要求:**
|
**数据路径定义与内容要求:**
|
||||||
|
|
||||||
**模块一: 核心认同 (Core Identity)**
|
**模块一: 核心认同 (Core Identity -> CI)**
|
||||||
* \`name\`: [从聊天记录中提取角色姓名]
|
* \`name\`: [从聊天记录中提取角色姓名]
|
||||||
* \`core_identity.archetype\`: [角色的核心身份或原型, 如:'流浪的剑客', '叛逆的公主', '年迈的智者']
|
* \`CI.arch\`: [角色的核心身份或原型, 如:'流浪的剑客', '叛逆的公主', '年迈的智者']
|
||||||
* \`core_identity.gender\`: [从聊天记录中提取或推断性别]
|
* \`CI.gen\`: [从聊天记录中提取或推断性别]
|
||||||
* \`core_identity.age\`: [从聊天记录中提取或推断年龄]
|
* \`CI.age\`: [从聊天记录中提取或推断年龄]
|
||||||
* \`core_identity.race\`: [从聊天记录中提取种族或民族, 若提及]
|
* \`CI.race\`: [从聊天记录中提取种族或民族, 若提及]
|
||||||
* \`core_identity.current_status\`: [总结角色在对话时间点的主要状态、情绪或处境]
|
* \`CI.status\`: [总结角色在对话时间点的主要状态、情绪或处境]
|
||||||
|
|
||||||
**模块二: 物理印记 (Physical Imprint)**
|
**模块二: 物理印记 (Physical Imprint -> PI)**
|
||||||
* \`physical_imprint.first_impression\`: [综合描述角色给人的第一印象和整体气质]
|
* \`PI.first\`: [综合描述角色给人的第一印象和整体气质]
|
||||||
* \`physical_imprint.key_features\`: [提取最显著的外貌细节, 如发色、眼神、伤疤等]
|
* \`PI.feat\`: [提取最显著的外貌细节, 如发色、眼神、伤疤等]
|
||||||
* \`physical_imprint.attire\`: [描述服装特点或风格]
|
* \`PI.attire\`: [描述服装特点或风格]
|
||||||
* \`physical_imprint.mannerisms\`: [描述标志性的小动作、姿态或口头禅]
|
* \`PI.manner\`: [描述标志性的小动作、姿态或口头禅]
|
||||||
* \`physical_imprint.voice\`: [根据对话推断音色、语速、语气等, 如:'低沉而缓慢', '清脆而急促']
|
* \`PI.voice\`: [根据对话推断音色、语速、语气等, 如:'低沉而缓慢', '清脆而急促']
|
||||||
|
|
||||||
**模块三: 心智侧写 (Psyche Profile)**
|
**模块三: 心智侧写 (Psyche Profile -> PP)**
|
||||||
* \`psyche_profile.tags\`: [提炼3-5个核心性格标签, 用斜杠 "/" 分隔, 例如: '标签1/标签2/标签3']
|
* \`PP.tags\`: [提炼3-5个核心性格标签, 用斜杠 "/" 分隔, 例如: '标签1/标签2/标签3']
|
||||||
* \`psyche_profile.description\`: [详细描述角色主要性格特征及其在对话中的表现]
|
* \`PP.desc\`: [详细描述角色主要性格特征及其在对话中的表现]
|
||||||
* \`psyche_profile.motivation\`: [角色当前最关心的事或其行为背后的核心驱动力]
|
* \`PP.mot\`: [角色当前最关心的事或其行为背后的核心驱动力]
|
||||||
* \`psyche_profile.values\`: [角色行为背后体现的价值观或处事原则]
|
* \`PP.val\`: [角色行为背后体现的价值观或处事原则]
|
||||||
* \`psyche_profile.inner_conflict\`: [描述角色可能存在的内在矛盾、恐惧或弱点, 若明确提及]
|
* \`PP.conf\`: [描述角色可能存在的内在矛盾、恐惧或弱点, 若明确提及]
|
||||||
|
|
||||||
**模块四: 社交矩阵 (Social Matrix)**
|
**模块四: 社交矩阵 (Social Matrix -> SM)**
|
||||||
* \`social_matrix.interaction_style\`: [描述角色与人交往的方式, 如:'支配型', '顺从型', '操纵型', '真诚型']
|
* \`SM.style\`: [描述角色与人交往的方式, 如:'支配型', '顺从型', '操纵型', '真诚型']
|
||||||
* \`social_matrix.skills\`: [提炼角色展现出的关键技能或能力]
|
* \`SM.skill\`: [提炼角色展现出的关键技能或能力]
|
||||||
* \`social_matrix.reputation\`: [根据对话归纳其他人对该角色的看法或其社会声望]
|
* \`SM.rep\`: [根据对话归纳其他人对该角色的看法或其社会声望]
|
||||||
|
|
||||||
**模块五: 叙事精粹 (Narrative Essence)**
|
**模块五: 叙事精粹 (Narrative Essence -> NE)**
|
||||||
* \`narrative_essence.core_traits.0.name\`: [核心特质1的名称]
|
* \`NE.trait.0.name\`: [核心特质1的名称]
|
||||||
* \`narrative_essence.core_traits.0.definition\`: [简述该特质的核心表现]
|
* \`NE.trait.0.def\`: [简述该特质的核心表现]
|
||||||
* \`narrative_essence.core_traits.0.evidence.0\`: [从聊天记录中提取的具体行为或言语实例1]
|
* \`NE.trait.0.evid.0\`: [从聊天记录中提取的具体行为或言语实例1]
|
||||||
* \`narrative_essence.core_traits.0.evidence.1\`: [实例2]
|
* \`NE.trait.0.evid.1\`: [实例2]
|
||||||
* \`narrative_essence.verbal_patterns.style_summary\`: [概括角色的说话节奏、常用词、语气等特点]
|
* \`NE.verb.style\`: [概括角色的说话节奏、常用词、语气等特点]
|
||||||
* \`narrative_essence.verbal_patterns.quotes.0\`: [直接引用聊天记录中的代表性对话或内心独白1]
|
* \`NE.verb.quote.0\`: [直接引用聊天记录中的代表性对话或内心独白1]
|
||||||
* \`narrative_essence.verbal_patterns.quotes.1\`: [引文2]
|
* \`NE.verb.quote.1\`: [引文2]
|
||||||
* \`narrative_essence.key_relationships.0.name\`: [关系对象1姓名]
|
* \`NE.rel.0.name\`: [关系对象1姓名]
|
||||||
* \`narrative_essence.key_relationships.0.summary\`: [描述关系性质、重要性及互动模式]
|
* \`NE.rel.0.sum\`: [描述关系性质、重要性及互动模式]
|
||||||
|
|
||||||
---
|
---
|
||||||
**完整示例**
|
**完整示例**
|
||||||
**完美示例输出 (必须严格、完整地复制此结构,不得有任何偏差):**
|
**完美示例输出 (必须严格、完整地复制此结构,不得有任何偏差):**
|
||||||
[--Amily2::CHAR_START--]
|
[--Amily2::CHAR_START--]
|
||||||
[name]:塞拉斯
|
[name]:塞拉斯
|
||||||
[core_identity.archetype]:被放逐的星际探险家
|
[CI.arch]:被放逐的星际探险家
|
||||||
[core_identity.gender]:男性
|
[CI.gen]:男性
|
||||||
[core_identity.age]:约35岁
|
[CI.age]:约35岁
|
||||||
[core_identity.race]:人类 (基因改造)
|
[CI.race]:人类 (基因改造)
|
||||||
[core_identity.current_status]:正在一颗废弃的矿业星球上修理飞船“流浪者号”,对偶遇的玩家保持着高度警惕,但又渴望获得帮助。
|
[CI.status]:正在一颗废弃的矿业星球上修理飞船“流浪者号”,对偶遇的玩家保持着高度警惕,但又渴望获得帮助。
|
||||||
[physical_imprint.first_impression]:饱经风霜,眼神锐利,透露出一种不轻易信任他人的疏离感。
|
[PI.first]:饱经风霜,眼神锐利,透露出一种不轻易信任他人的疏离感。
|
||||||
[physical_imprint.key_features]:额头有一道旧的激光烧伤疤痕,机械义肢的左臂上刻着神秘的符号。
|
[PI.feat]:额头有一道旧的激光烧伤疤痕,机械义肢的左臂上刻着神秘的符号。
|
||||||
[physical_imprint.attire]:穿着破旧但实用的多功能环境防护服,上面沾满了机油和红色的星球尘土。
|
[PI.attire]:穿着破旧但实用的多功能环境防护服,上面沾满了机油和红色的星球尘土。
|
||||||
[physical_imprint.mannerisms]:习惯性地用右手检查腰间的工具带,说话时会下意识地扫视四周。
|
[PI.manner]:习惯性地用右手检查腰间的工具带,说话时会下意识地扫视四周。
|
||||||
[physical_imprint.voice]:声音沙哑,语速不快,但每个字都清晰有力。
|
[PI.voice]:声音沙哑,语速不快,但每个字都清晰有力。
|
||||||
[psyche_profile.tags]:实用主义/多疑/坚韧
|
[PP.tags]:实用主义/多疑/坚韧
|
||||||
[psyche_profile.description]:塞拉斯是一个极端的实用主义者,多年的独自流亡让他变得多疑和谨慎。他只相信自己亲手验证过的事物,但在坚硬的外壳下,是对重返星际文明的执着渴望。
|
[PP.desc]:塞拉斯是一个极端的实用主义者,多年的独自流亡让他变得多疑和谨慎。他只相信自己亲手验证过的事物,但在坚硬的外壳下,是对重返星际文明的执着渴望。
|
||||||
[psyche_profile.motivation]:修复飞船,离开这颗星球,并找出当年导致他被放逐的真相。
|
[PP.mot]:修复飞船,离开这颗星球,并找出当年导致他被放逐的真相。
|
||||||
[psyche_profile.values]:生存至上,忠诚于自己选择的伙伴,鄙视背叛和官僚主义。
|
[PP.val]:生存至上,忠诚于自己选择的伙伴,鄙视背叛和官僚主义。
|
||||||
[psyche_profile.inner_conflict]:既渴望与人合作以加快飞船的修复进度,又害怕再次被背叛。
|
[PP.conf]:既渴望与人合作以加快飞船的修复进度,又害怕再次被背叛。
|
||||||
[social_matrix.interaction_style]:试探性与防御性,倾向于通过提问和观察来评估他人,而非主动透露自己的信息。
|
[SM.style]:试探性与防御性,倾向于通过提问和观察来评估他人,而非主动透露自己的信息。
|
||||||
[social_matrix.skills]:高级机械工程学,星际导航,在恶劣环境下的生存技巧。
|
[SM.skill]:高级机械工程学,星际导航,在恶劣环境下的生存技巧。
|
||||||
[social_matrix.reputation]:在星际边缘地带的黑市中,他被认为是一个技术高超但独来独往的“幽灵”。
|
[SM.rep]:在星际边缘地带的黑市中,他被认为是一个技术高超但独来独往的“幽灵”。
|
||||||
[narrative_essence.core_traits.0.name]:生存本能
|
[NE.trait.0.name]:生存本能
|
||||||
[narrative_essence.core_traits.0.definition]:在任何极端环境下都能迅速做出最有利于生存的判断和行动。
|
[NE.trait.0.def]:在任何极端环境下都能迅速做出最有利于生存的判断和行动。
|
||||||
[narrative_essence.core_traits.0.evidence.0]:“别碰那个控制台,它的能量读数不稳定,可能会过载。”
|
[NE.trait.0.evid.0]:“别碰那个控制台,它的能量读数不稳定,可能会过载。”
|
||||||
[narrative_essence.verbal_patterns.style_summary]:语言简洁、直接,富含技术术语和行话,很少有情绪化的表达。
|
[NE.verb.style]:语言简洁、直接,富含技术术语和行话,很少有情绪化的表达。
|
||||||
[narrative_essence.verbal_patterns.quotes.0]:“废话少说。你能修好超光速引擎的能量转换器吗?不能就别浪费我的时间。”
|
[NE.verb.quote.0]:“废话少说。你能修好超光速引擎的能量转换器吗?不能就别浪费我的时间。”
|
||||||
[narrative_essence.key_relationships.0.name]:玩家
|
[NE.rel.0.name]:玩家
|
||||||
[narrative_essence.key_relationships.0.summary]:一个意外的闯入者,可能是威胁,也可能是离开这里的唯一希望。塞拉斯正在评估玩家的价值和可靠性。
|
[NE.rel.0.sum]:一个意外的闯入者,可能是威胁,也可能是离开这里的唯一希望。塞拉斯正在评估玩家的价值和可靠性。
|
||||||
[--Amily2::CHAR_END--]
|
[--Amily2::CHAR_END--]
|
||||||
|
|
||||||
任务开始,请严格遵循协议,生成纯数据输出。`,
|
任务开始,请严格遵循协议,生成纯数据输出。`,
|
||||||
@@ -151,12 +151,12 @@ beilu如同一位温柔助手,文字满足用户的各种需求
|
|||||||
**输入 - 旧档案:**
|
**输入 - 旧档案:**
|
||||||
[--Amily2::CHAR_START--]
|
[--Amily2::CHAR_START--]
|
||||||
[name]:塞拉斯
|
[name]:塞拉斯
|
||||||
[core_identity.archetype]:被放逐的星际探险家
|
[CI.arch]:被放逐的星际探险家
|
||||||
[core_identity.age]:约35岁
|
[CI.age]:约35岁
|
||||||
[core_identity.current_status]:正在一颗废弃的矿业星球上修理飞船“流浪者号”,对偶遇的玩家保持着高度警惕。
|
[CI.status]:正在一颗废弃的矿业星球上修理飞船“流浪者号”,对偶遇的玩家保持着高度警惕。
|
||||||
[psyche_profile.motivation]:修复飞船,离开这颗星球。
|
[PP.mot]:修复飞船,离开这颗星球。
|
||||||
[narrative_essence.key_relationships.0.name]:玩家
|
[NE.rel.0.name]:玩家
|
||||||
[narrative_essence.key_relationships.0.summary]:一个意外的闯入者,可能是威胁。
|
[NE.rel.0.sum]:一个意外的闯入者,可能是威胁。
|
||||||
[--Amily2::CHAR_END--]
|
[--Amily2::CHAR_END--]
|
||||||
|
|
||||||
**输入 - 新对话:**
|
**输入 - 新对话:**
|
||||||
@@ -166,31 +166,32 @@ beilu如同一位温柔助手,文字满足用户的各种需求
|
|||||||
塞拉斯: "天苑四...谢谢你。作为回报,这个能量核心你拿去用吧。"
|
塞拉斯: "天苑四...谢谢你。作为回报,这个能量核心你拿去用吧。"
|
||||||
|
|
||||||
**分析与操作:**
|
**分析与操作:**
|
||||||
1. **修正**: "[core_identity.age]" 从 "约35岁" 变为 "40岁" (根据“五年不见”)。
|
1. **修正**: "[CI.age]" 从 "约35岁" 变为 "40岁" (根据“五年不见”)。
|
||||||
2. **深化**: "[core_identity.archetype]" 从 "被放逐的星际探险家" 扩展为 "前星际探险家,现为'猩红彗星'佣兵团团长"。
|
2. **深化**: "[CI.arch]" 从 "被放逐的星际探险家" 扩展为 "前星际探险家,现为'猩红彗星'佣兵团团长"。
|
||||||
3. **更新**: "[psyche_profile.motivation]" 的核心从 "离开星球" 变为 "找到失散的女儿"。
|
3. **更新**: "[PP.mot]" 的核心从 "离开星球" 变为 "找到失散的女儿"。
|
||||||
4. **补充**: "[narrative_essence.key_relationships.0.summary]" 中与玩家的关系,从单纯的 "威胁" 变为 "提供了关键情报的旧识,关系有所缓和"。
|
4. **补充**: "[NE.rel.0.sum]" 中与玩家的关系,从单纯的 "威胁" 变为 "提供了关键情报的旧识,关系有所缓和"。
|
||||||
|
|
||||||
**完美输出示例 (更新后的完整档案):**
|
**完美输出示例 (更新后的完整档案):**
|
||||||
注意:"[name]:"为必须保留的字段,必须存在,否则视为错误输出。
|
注意:"[name]:"为必须保留的字段,必须存在,否则视为错误输出。
|
||||||
[--Amily2::CHAR_START--]
|
[--Amily2::CHAR_START--]
|
||||||
[name]:塞拉斯
|
[name]:塞拉斯
|
||||||
[core_identity.archetype]:前星际探险家,现为'猩红彗星'佣兵团团长
|
[CI.arch]:前星际探险家,现为'猩红彗星'佣兵团团长
|
||||||
[core_identity.age]:40岁
|
[CI.age]:40岁
|
||||||
[core_identity.current_status]:在修理飞船的同时,从玩家处获得了关于女儿下落的关键线索,情绪混杂着惊讶和感激。
|
[CI.status]:在修理飞船的同时,从玩家处获得了关于女儿下落的关键线索,情绪混杂着惊讶和感激。
|
||||||
[psyche_profile.motivation]:找到在天苑四星系失散的女儿。
|
[PP.mot]:找到在天苑四星系失散的女儿。
|
||||||
[narrative_essence.key_relationships.0.name]:玩家
|
[NE.rel.0.name]:玩家
|
||||||
[narrative_essence.key_relationships.0.summary]:一位五年未见的旧识。对方不仅认出了他,还提供了关于他女儿下落的关键情报,使塞拉斯对玩家的态度从警惕转为合作。
|
[NE.rel.0.sum]:一位五年未见的旧识。对方不仅认出了他,还提供了关于他女儿下落的关键情报,使塞拉斯对玩家的态度从警惕转为合作。
|
||||||
[--Amily2::CHAR_END--]
|
[--Amily2::CHAR_END--]
|
||||||
---
|
---
|
||||||
**任务开始:**
|
**任务开始:**
|
||||||
请分析以下【旧档案】和【新对话】,严格遵循上述所有协议,生成纯粹的、更新后的数据档案。
|
请分析以下【旧档案】和【新对话】,严格遵循上述所有协议,生成纯粹的、更新后的数据档案。
|
||||||
若旧档案为空,则完全依照**完整示例**生成完整内容,若旧档案不为空,则以旧档案为基准进行更新。
|
若旧档案为空,则完全依照**完整示例**生成完整内容,若旧档案不为空,则以旧档案为基准进行更新。
|
||||||
其中无需变动的内容,可忽略,例如年龄无变化,则可以不输出[core_identity.age]条目。
|
其中无需变动的内容,可忽略,例如年龄无变化,则可以不输出[CI.age]条目。
|
||||||
现在开始你的增量更新任务。`,
|
现在开始你的增量更新任务。`,
|
||||||
cwb_prompt_version: '1.0.2',
|
cwb_prompt_version: '1.0.2',
|
||||||
|
|
||||||
cwb_auto_update_threshold: 20,
|
cwb_auto_update_threshold: 20,
|
||||||
|
cwb_scan_depth: 6,
|
||||||
cwb_auto_update_enabled: false,
|
cwb_auto_update_enabled: false,
|
||||||
cwb_viewer_enabled: false,
|
cwb_viewer_enabled: false,
|
||||||
cwb_incremental_update_enabled: false,
|
cwb_incremental_update_enabled: false,
|
||||||
@@ -209,6 +210,7 @@ export const cwbDefaultSettings = {
|
|||||||
cwb_char_card_prompt: cwbCompleteDefaultSettings.cwb_char_card_prompt,
|
cwb_char_card_prompt: cwbCompleteDefaultSettings.cwb_char_card_prompt,
|
||||||
cwb_prompt_version: '1.0.2',
|
cwb_prompt_version: '1.0.2',
|
||||||
cwb_auto_update_threshold: 20,
|
cwb_auto_update_threshold: 20,
|
||||||
|
cwb_scan_depth: 6,
|
||||||
cwb_auto_update_enabled: false,
|
cwb_auto_update_enabled: false,
|
||||||
cwb_viewer_enabled: false,
|
cwb_viewer_enabled: false,
|
||||||
cwb_incremental_update_enabled: false,
|
cwb_incremental_update_enabled: false,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getContext } from '/scripts/extensions.js';
|
import { getContext } from '/scripts/extensions.js';
|
||||||
import { state, SCRIPT_ID_PREFIX } from './cwb_state.js';
|
import { state, SCRIPT_ID_PREFIX } from './cwb_state.js';
|
||||||
import { logDebug, logError, showToastr, escapeHtml, cleanChatName, parseCustomFormat, isCwbEnabled } from './cwb_utils.js';
|
import { logDebug, logError, showToastr, escapeHtml, cleanChatName, parseCustomFormat, buildCustomFormat, isCwbEnabled } from './cwb_utils.js';
|
||||||
import { callCustomOpenAI } from './cwb_apiService.js';
|
import { callCustomOpenAI } from './cwb_apiService.js';
|
||||||
import { saveDescriptionToLorebook, updateCharacterRosterLorebookEntry, manageAutoCardUpdateLorebookEntry, getTargetWorldBook } from './cwb_lorebookManager.js';
|
import { saveDescriptionToLorebook, updateCharacterRosterLorebookEntry, manageAutoCardUpdateLorebookEntry, getTargetWorldBook } from './cwb_lorebookManager.js';
|
||||||
import { extractBlocksByTags, applyExclusionRules } from '../../core/utils/rag-tag-extractor.js';
|
import { extractBlocksByTags, applyExclusionRules } from '../../core/utils/rag-tag-extractor.js';
|
||||||
@@ -9,6 +9,7 @@ import { getPresetPrompts, getMixedOrder } from '../../PresetSettings/index.js';
|
|||||||
import { generateRandomSeed } from '../../core/api.js';
|
import { generateRandomSeed } from '../../core/api.js';
|
||||||
import { getChatIdentifier } from '../../core/lore.js';
|
import { getChatIdentifier } from '../../core/lore.js';
|
||||||
import { safeLorebookEntries } from '../../core/tavernhelper-compatibility.js';
|
import { safeLorebookEntries } from '../../core/tavernhelper-compatibility.js';
|
||||||
|
import { amilyHelper } from '../../core/tavern-helper/main.js';
|
||||||
|
|
||||||
const { SillyTavern, jQuery, characters } = window;
|
const { SillyTavern, jQuery, characters } = window;
|
||||||
|
|
||||||
@@ -190,6 +191,11 @@ async function proceedWithCardUpdate($panel, messagesToUse) {
|
|||||||
if (bookName) {
|
if (bookName) {
|
||||||
const entries = (await safeLorebookEntries(bookName)) || [];
|
const entries = (await safeLorebookEntries(bookName)) || [];
|
||||||
let chatIdentifier = state.currentChatFileIdentifier.replace(/ imported/g, '');
|
let chatIdentifier = state.currentChatFileIdentifier.replace(/ imported/g, '');
|
||||||
|
const messagesText = messagesToUse.map(m => {
|
||||||
|
const name = m.name || '';
|
||||||
|
const content = m.message || '';
|
||||||
|
return `${name}\n${content}`;
|
||||||
|
}).join('\n').toLowerCase();
|
||||||
|
|
||||||
const characterEntries = entries.filter(e =>
|
const characterEntries = entries.filter(e =>
|
||||||
e.enabled &&
|
e.enabled &&
|
||||||
@@ -200,16 +206,28 @@ async function proceedWithCardUpdate($panel, messagesToUse) {
|
|||||||
|
|
||||||
for (const entry of characterEntries) {
|
for (const entry of characterEntries) {
|
||||||
try {
|
try {
|
||||||
|
const keysToCheck = entry.keys.filter(k => k !== chatIdentifier);
|
||||||
|
if (entry.secondary_keys && Array.isArray(entry.secondary_keys)) {
|
||||||
|
keysToCheck.push(...entry.secondary_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
let isTriggered = false;
|
||||||
|
if (keysToCheck.length > 0) {
|
||||||
|
isTriggered = keysToCheck.some(key => messagesText.includes(key.toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTriggered) {
|
||||||
const parsedData = parseCustomFormat(entry.content);
|
const parsedData = parseCustomFormat(entry.content);
|
||||||
const entryCharName = parsedData?.name?.trim() || parsedData?.core_identity?.name?.trim();
|
const entryCharName = parsedData?.name?.trim() || parsedData?.CI?.name?.trim() || parsedData?.core_identity?.name?.trim();
|
||||||
if (entryCharName) {
|
if (entryCharName) {
|
||||||
existingData[entryCharName] = entry.content;
|
existingData[entryCharName] = entry.content;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
logError(`解析现有角色条目时出错 (UID: ${entry.uid}):`, parseError);
|
logError(`解析现有角色条目时出错 (UID: ${entry.uid}):`, parseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logDebug(`为 '${chatIdentifier}' 找到了 ${Object.keys(existingData).length} 个现有角色条目。`);
|
logDebug(`为 '${chatIdentifier}' 找到了 ${Object.keys(existingData).length} 个被触发的现有角色条目。`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('在增量更新中获取现有角色数据时出错:', e);
|
logError('在增量更新中获取现有角色数据时出错:', e);
|
||||||
@@ -290,7 +308,7 @@ async function proceedWithCardUpdate($panel, messagesToUse) {
|
|||||||
if (!trimmedBlock) continue;
|
if (!trimmedBlock) continue;
|
||||||
|
|
||||||
const parsedData = parseCustomFormat(trimmedBlock);
|
const parsedData = parseCustomFormat(trimmedBlock);
|
||||||
const charName = (parsedData?.core_identity?.name?.trim() || parsedData?.name?.trim()) || 'UnknownCharacter';
|
const charName = (parsedData?.name?.trim() || parsedData?.CI?.name?.trim() || parsedData?.core_identity?.name?.trim()) || 'UnknownCharacter';
|
||||||
|
|
||||||
if (charName === 'UnknownCharacter') {
|
if (charName === 'UnknownCharacter') {
|
||||||
logError('无法在块中找到角色名:', trimmedBlock);
|
logError('无法在块中找到角色名:', trimmedBlock);
|
||||||
@@ -666,7 +684,8 @@ export async function manualUpdateLogic($panel = null) {
|
|||||||
|
|
||||||
isUpdatingCard = true;
|
isUpdatingCard = true;
|
||||||
await loadAllChatMessages($panel);
|
await loadAllChatMessages($panel);
|
||||||
const messagesToProcess = state.allChatMessages.slice(-state.autoUpdateThreshold);
|
const depth = state.scanDepth || state.autoUpdateThreshold || 6;
|
||||||
|
const messagesToProcess = state.allChatMessages.slice(-depth);
|
||||||
await proceedWithCardUpdate($panel, messagesToProcess);
|
await proceedWithCardUpdate($panel, messagesToProcess);
|
||||||
isUpdatingCard = false;
|
isUpdatingCard = false;
|
||||||
|
|
||||||
@@ -680,6 +699,164 @@ export async function handleManualUpdateCard($panel) {
|
|||||||
$button.prop('disabled', false).text('立即更新角色描述');
|
$button.prop('disabled', false).text('立即更新角色描述');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function handleLegacyFormatConversion($panel) {
|
||||||
|
if (!isCwbEnabled()) {
|
||||||
|
showToastr('warning', 'CharacterWorldBook总开关已关闭。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $button = $panel.find('#cwb-legacy-auto-update');
|
||||||
|
$button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> 转换中...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bookName = await getTargetWorldBook();
|
||||||
|
if (!bookName) {
|
||||||
|
showToastr('warning', '未找到目标世界书。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = await safeLorebookEntries(bookName);
|
||||||
|
let updatedCount = 0;
|
||||||
|
const entriesToUpdate = [];
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.content || !entry.content.includes('[--Amily2::CHAR_START--]')) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = parseCustomFormat(entry.content);
|
||||||
|
if (!parsed || Object.keys(parsed).length === 0) continue;
|
||||||
|
|
||||||
|
let hasChanges = false;
|
||||||
|
const newData = {};
|
||||||
|
|
||||||
|
// Helper to rename keys
|
||||||
|
const renameKey = (obj, oldKey, newKey) => {
|
||||||
|
if (obj[oldKey] !== undefined) {
|
||||||
|
obj[newKey] = obj[oldKey];
|
||||||
|
delete obj[oldKey];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to rename sub-keys
|
||||||
|
const renameSubKeys = (parentObj, parentKey, mapping) => {
|
||||||
|
if (parentObj[parentKey]) {
|
||||||
|
let subChanged = false;
|
||||||
|
for (const [oldSub, newSub] of Object.entries(mapping)) {
|
||||||
|
if (renameKey(parentObj[parentKey], oldSub, newSub)) {
|
||||||
|
subChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subChanged;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy parsed data to newData to avoid mutating original if needed (though parseCustomFormat returns new obj)
|
||||||
|
Object.assign(newData, JSON.parse(JSON.stringify(parsed)));
|
||||||
|
|
||||||
|
// 1. Rename Top Level Modules
|
||||||
|
if (renameKey(newData, 'core_identity', 'CI')) hasChanges = true;
|
||||||
|
if (renameKey(newData, 'physical_imprint', 'PI')) hasChanges = true;
|
||||||
|
if (renameKey(newData, 'psyche_profile', 'PP')) hasChanges = true;
|
||||||
|
if (renameKey(newData, 'social_matrix', 'SM')) hasChanges = true;
|
||||||
|
if (renameKey(newData, 'narrative_essence', 'NE')) hasChanges = true;
|
||||||
|
|
||||||
|
// 2. Rename Sub-keys
|
||||||
|
// CI
|
||||||
|
if (renameSubKeys(newData, 'CI', {
|
||||||
|
'archetype': 'arch',
|
||||||
|
'gender': 'gen',
|
||||||
|
'current_status': 'status'
|
||||||
|
})) hasChanges = true;
|
||||||
|
|
||||||
|
// PI
|
||||||
|
if (renameSubKeys(newData, 'PI', {
|
||||||
|
'first_impression': 'first',
|
||||||
|
'key_features': 'feat',
|
||||||
|
'mannerisms': 'manner'
|
||||||
|
})) hasChanges = true;
|
||||||
|
|
||||||
|
// PP
|
||||||
|
if (renameSubKeys(newData, 'PP', {
|
||||||
|
'description': 'desc',
|
||||||
|
'motivation': 'mot',
|
||||||
|
'values': 'val',
|
||||||
|
'inner_conflict': 'conf'
|
||||||
|
})) hasChanges = true;
|
||||||
|
|
||||||
|
// SM
|
||||||
|
if (renameSubKeys(newData, 'SM', {
|
||||||
|
'interaction_style': 'style',
|
||||||
|
'skills': 'skill',
|
||||||
|
'reputation': 'rep'
|
||||||
|
})) hasChanges = true;
|
||||||
|
|
||||||
|
// NE
|
||||||
|
if (newData.NE) {
|
||||||
|
// core_traits -> trait
|
||||||
|
if (newData.NE.core_traits) {
|
||||||
|
newData.NE.trait = newData.NE.core_traits.map(t => {
|
||||||
|
const newT = { ...t };
|
||||||
|
renameKey(newT, 'definition', 'def');
|
||||||
|
renameKey(newT, 'evidence', 'evid');
|
||||||
|
return newT;
|
||||||
|
});
|
||||||
|
delete newData.NE.core_traits;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verbal_patterns -> verb
|
||||||
|
if (newData.NE.verbal_patterns) {
|
||||||
|
newData.NE.verb = { ...newData.NE.verbal_patterns };
|
||||||
|
delete newData.NE.verbal_patterns;
|
||||||
|
renameKey(newData.NE.verb, 'style_summary', 'style');
|
||||||
|
renameKey(newData.NE.verb, 'quotes', 'quote');
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// key_relationships -> rel
|
||||||
|
if (newData.NE.key_relationships) {
|
||||||
|
newData.NE.rel = newData.NE.key_relationships.map(r => {
|
||||||
|
const newR = { ...r };
|
||||||
|
renameKey(newR, 'summary', 'sum');
|
||||||
|
return newR;
|
||||||
|
});
|
||||||
|
delete newData.NE.key_relationships;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
const newContent = buildCustomFormat(newData);
|
||||||
|
entriesToUpdate.push({
|
||||||
|
uid: entry.uid,
|
||||||
|
content: newContent
|
||||||
|
});
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
logError(`转换条目失败 (UID: ${entry.uid}):`, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedCount > 0) {
|
||||||
|
await amilyHelper.setLorebookEntries(bookName, entriesToUpdate);
|
||||||
|
showToastr('success', `成功转换了 ${updatedCount} 个旧版格式条目!`);
|
||||||
|
} else {
|
||||||
|
showToastr('info', '没有发现需要转换的旧版格式条目。');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logError('旧版格式转换失败:', error);
|
||||||
|
showToastr('error', `转换失败: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
$button.prop('disabled', false).html('<i class="fa-solid fa-history"></i> 旧版格式转换');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function initializeCore($panel) {
|
export async function initializeCore($panel) {
|
||||||
const initialChatName = await getLatestChatName();
|
const initialChatName = await getLatestChatName();
|
||||||
await resetScriptStateForNewChat($panel, initialChatName);
|
await resetScriptStateForNewChat($panel, initialChatName);
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export async function saveDescriptionToLorebook(characterName, newDescription, s
|
|||||||
keys: [chatIdentifier, safeCharName, floorRange],
|
keys: [chatIdentifier, safeCharName, floorRange],
|
||||||
enabled: true,
|
enabled: true,
|
||||||
type: 'selective',
|
type: 'selective',
|
||||||
|
scanDepth: state.scanDepth || 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { cwbCompleteDefaultSettings } from './cwb_config.js';
|
|||||||
import { logError, showToastr, escapeHtml, compareVersions, isCwbEnabled } from './cwb_utils.js';
|
import { logError, showToastr, escapeHtml, compareVersions, isCwbEnabled } from './cwb_utils.js';
|
||||||
import { fetchModelsAndConnect, updateApiStatusDisplay } from './cwb_apiService.js';
|
import { fetchModelsAndConnect, updateApiStatusDisplay } from './cwb_apiService.js';
|
||||||
import { checkForUpdates } from './cwb_updater.js';
|
import { checkForUpdates } from './cwb_updater.js';
|
||||||
import { handleManualUpdateCard, startBatchUpdate, handleFloorRangeUpdate } from './cwb_core.js';
|
import { handleManualUpdateCard, startBatchUpdate, handleFloorRangeUpdate, handleLegacyFormatConversion } from './cwb_core.js';
|
||||||
import { initializeCharCardViewer } from './cwb_uiManager.js';
|
import { initializeCharCardViewer } from './cwb_uiManager.js';
|
||||||
import { CHAR_CARD_VIEWER_BUTTON_ID } from './cwb_state.js';
|
import { CHAR_CARD_VIEWER_BUTTON_ID } from './cwb_state.js';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function updateControlsLockState() {
|
|||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
const isMasterEnabled = settings.cwb_master_enabled;
|
const isMasterEnabled = settings.cwb_master_enabled;
|
||||||
|
|
||||||
const $controlsToToggle = $panel.find('input, textarea, select, button').not('#cwb_master_enabled-checkbox, #amily2_back_to_main_from_cwb');
|
const $controlsToToggle = $panel.find('input, textarea, select, button').not('#cwb_master_enabled-checkbox, #amily2_back_to_main_from_cwb, .sinan-nav-item');
|
||||||
|
|
||||||
if (isMasterEnabled) {
|
if (isMasterEnabled) {
|
||||||
$controlsToToggle.prop('disabled', false);
|
$controlsToToggle.prop('disabled', false);
|
||||||
@@ -128,6 +128,20 @@ function saveAutoUpdateThreshold() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveScanDepth() {
|
||||||
|
const valStr = $panel.find('#cwb-scan-depth').val();
|
||||||
|
const newT = parseInt(valStr, 10);
|
||||||
|
if (!isNaN(newT) && newT >= 1) {
|
||||||
|
getSettings().cwb_scan_depth = newT;
|
||||||
|
state.scanDepth = newT;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
showToastr('success', '扫描深度已保存!');
|
||||||
|
} else {
|
||||||
|
showToastr('warning', `深度 "${valStr}" 无效。`);
|
||||||
|
$panel.find('#cwb-scan-depth').val(getSettings().cwb_scan_depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function bindWorldBookSettings() {
|
function bindWorldBookSettings() {
|
||||||
const MAX_RETRIES = 10;
|
const MAX_RETRIES = 10;
|
||||||
const RETRY_DELAY = 200;
|
const RETRY_DELAY = 200;
|
||||||
@@ -227,6 +241,7 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
$panel.on('change', '#cwb-api-mode', function() {
|
$panel.on('change', '#cwb-api-mode', function() {
|
||||||
const selectedMode = $(this).val();
|
const selectedMode = $(this).val();
|
||||||
|
|
||||||
|
// 自动保存API模式设置
|
||||||
getSettings().cwb_api_mode = selectedMode;
|
getSettings().cwb_api_mode = selectedMode;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
|
||||||
@@ -240,6 +255,7 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
$panel.on('change', '#cwb-tavern-profile', function() {
|
$panel.on('change', '#cwb-tavern-profile', function() {
|
||||||
const selectedProfile = $(this).val();
|
const selectedProfile = $(this).val();
|
||||||
|
|
||||||
|
// 自动保存SillyTavern预设选择
|
||||||
getSettings().cwb_tavern_profile = selectedProfile;
|
getSettings().cwb_tavern_profile = selectedProfile;
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
|
||||||
@@ -250,9 +266,11 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
|
|
||||||
updateApiStatusDisplay($panel);
|
updateApiStatusDisplay($panel);
|
||||||
});
|
});
|
||||||
|
// 添加API字段的实时保存
|
||||||
$panel.on('input', '#cwb-api-url', function() {
|
$panel.on('input', '#cwb-api-url', function() {
|
||||||
const apiUrl = $(this).val().trim();
|
const apiUrl = $(this).val().trim();
|
||||||
|
|
||||||
|
// 同时更新设置和状态
|
||||||
getSettings().cwb_api_url = apiUrl;
|
getSettings().cwb_api_url = apiUrl;
|
||||||
state.customApiConfig.url = apiUrl;
|
state.customApiConfig.url = apiUrl;
|
||||||
|
|
||||||
@@ -265,6 +283,7 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
$panel.on('input', '#cwb-api-key', function() {
|
$panel.on('input', '#cwb-api-key', function() {
|
||||||
const apiKey = $(this).val();
|
const apiKey = $(this).val();
|
||||||
|
|
||||||
|
// 同时更新设置和状态
|
||||||
getSettings().cwb_api_key = apiKey;
|
getSettings().cwb_api_key = apiKey;
|
||||||
state.customApiConfig.apiKey = apiKey;
|
state.customApiConfig.apiKey = apiKey;
|
||||||
|
|
||||||
@@ -276,6 +295,7 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
$panel.on('change', '#cwb-api-model', function() {
|
$panel.on('change', '#cwb-api-model', function() {
|
||||||
const model = $(this).val();
|
const model = $(this).val();
|
||||||
|
|
||||||
|
// 同时更新设置和状态
|
||||||
getSettings().cwb_api_model = model;
|
getSettings().cwb_api_model = model;
|
||||||
state.customApiConfig.model = model;
|
state.customApiConfig.model = model;
|
||||||
|
|
||||||
@@ -297,9 +317,11 @@ export function bindSettingsEvents($settingsPanel) {
|
|||||||
$panel.on('click', '#cwb-reset-char-card-prompt', resetCharCardPrompt);
|
$panel.on('click', '#cwb-reset-char-card-prompt', resetCharCardPrompt);
|
||||||
|
|
||||||
$panel.on('click', '#cwb-save-auto-update-threshold', saveAutoUpdateThreshold);
|
$panel.on('click', '#cwb-save-auto-update-threshold', saveAutoUpdateThreshold);
|
||||||
|
$panel.on('click', '#cwb-save-scan-depth', saveScanDepth);
|
||||||
$panel.on('click', '#cwb-manual-update-card', () => handleManualUpdateCard($panel));
|
$panel.on('click', '#cwb-manual-update-card', () => handleManualUpdateCard($panel));
|
||||||
$panel.on('click', '#cwb-batch-update-card', () => startBatchUpdate($panel));
|
$panel.on('click', '#cwb-batch-update-card', () => startBatchUpdate($panel));
|
||||||
$panel.on('click', '#cwb-floor-range-update', () => handleFloorRangeUpdate($panel));
|
$panel.on('click', '#cwb-floor-range-update', () => handleFloorRangeUpdate($panel));
|
||||||
|
$panel.on('click', '#cwb-legacy-auto-update', () => handleLegacyFormatConversion($panel));
|
||||||
$panel.on('click', '#cwb-check-for-updates', () => checkForUpdates(true, $panel));
|
$panel.on('click', '#cwb-check-for-updates', () => checkForUpdates(true, $panel));
|
||||||
|
|
||||||
$panel.on('click', '#cwb-auto-update-enabled', function () {
|
$panel.on('click', '#cwb-auto-update-enabled', function () {
|
||||||
@@ -487,6 +509,7 @@ function updateUiWithSettings() {
|
|||||||
$panel.find('#cwb-max-tokens-value').text(settings.cwb_max_tokens);
|
$panel.find('#cwb-max-tokens-value').text(settings.cwb_max_tokens);
|
||||||
|
|
||||||
$panel.find('#cwb-auto-update-threshold').val(settings.cwb_auto_update_threshold);
|
$panel.find('#cwb-auto-update-threshold').val(settings.cwb_auto_update_threshold);
|
||||||
|
$panel.find('#cwb-scan-depth').val(settings.cwb_scan_depth);
|
||||||
$panel.find('#cwb_master_enabled-checkbox').prop('checked', settings.cwb_master_enabled);
|
$panel.find('#cwb_master_enabled-checkbox').prop('checked', settings.cwb_master_enabled);
|
||||||
$panel.find('#cwb-auto-update-enabled-checkbox').prop('checked', settings.cwb_auto_update_enabled);
|
$panel.find('#cwb-auto-update-enabled-checkbox').prop('checked', settings.cwb_auto_update_enabled);
|
||||||
$panel.find('#cwb-viewer-enabled-checkbox').prop('checked', settings.cwb_viewer_enabled);
|
$panel.find('#cwb-viewer-enabled-checkbox').prop('checked', settings.cwb_viewer_enabled);
|
||||||
@@ -514,11 +537,12 @@ export function loadSettings() {
|
|||||||
|
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
|
|
||||||
|
// Initialize settings with defaults if not present
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
extension_settings[extensionName] = { ...cwbCompleteDefaultSettings };
|
extension_settings[extensionName] = { ...cwbCompleteDefaultSettings };
|
||||||
console.log('[CWB] Initialized default settings');
|
console.log('[CWB] Initialized default settings');
|
||||||
} else {
|
} else {
|
||||||
|
// Ensure all default settings exist
|
||||||
Object.keys(cwbCompleteDefaultSettings).forEach(key => {
|
Object.keys(cwbCompleteDefaultSettings).forEach(key => {
|
||||||
if (settings[key] === undefined || settings[key] === null) {
|
if (settings[key] === undefined || settings[key] === null) {
|
||||||
settings[key] = cwbCompleteDefaultSettings[key];
|
settings[key] = cwbCompleteDefaultSettings[key];
|
||||||
@@ -528,6 +552,7 @@ export function loadSettings() {
|
|||||||
|
|
||||||
const finalSettings = getSettings();
|
const finalSettings = getSettings();
|
||||||
|
|
||||||
|
// Apply localStorage overrides
|
||||||
const overrides = JSON.parse(localStorage.getItem(CWB_BOOLEAN_SETTINGS_OVERRIDE_KEY) || '{}');
|
const overrides = JSON.parse(localStorage.getItem(CWB_BOOLEAN_SETTINGS_OVERRIDE_KEY) || '{}');
|
||||||
if (overrides.cwb_master_enabled !== undefined) {
|
if (overrides.cwb_master_enabled !== undefined) {
|
||||||
finalSettings.cwb_master_enabled = overrides.cwb_master_enabled;
|
finalSettings.cwb_master_enabled = overrides.cwb_master_enabled;
|
||||||
@@ -542,6 +567,7 @@ export function loadSettings() {
|
|||||||
finalSettings.cwb_incremental_update_enabled = overrides.cwb_incremental_update_enabled;
|
finalSettings.cwb_incremental_update_enabled = overrides.cwb_incremental_update_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update state object with current settings
|
||||||
state.masterEnabled = finalSettings.cwb_master_enabled;
|
state.masterEnabled = finalSettings.cwb_master_enabled;
|
||||||
state.viewerEnabled = finalSettings.cwb_viewer_enabled;
|
state.viewerEnabled = finalSettings.cwb_viewer_enabled;
|
||||||
state.autoUpdateEnabled = finalSettings.cwb_auto_update_enabled;
|
state.autoUpdateEnabled = finalSettings.cwb_auto_update_enabled;
|
||||||
@@ -556,6 +582,7 @@ export function loadSettings() {
|
|||||||
state.currentIncrementalCharCardPrompt = finalSettings.cwb_incremental_char_card_prompt;
|
state.currentIncrementalCharCardPrompt = finalSettings.cwb_incremental_char_card_prompt;
|
||||||
|
|
||||||
state.autoUpdateThreshold = finalSettings.cwb_auto_update_threshold;
|
state.autoUpdateThreshold = finalSettings.cwb_auto_update_threshold;
|
||||||
|
state.scanDepth = finalSettings.cwb_scan_depth;
|
||||||
state.worldbookTarget = finalSettings.cwb_worldbook_target;
|
state.worldbookTarget = finalSettings.cwb_worldbook_target;
|
||||||
state.customWorldBook = finalSettings.cwb_custom_worldbook;
|
state.customWorldBook = finalSettings.cwb_custom_worldbook;
|
||||||
|
|
||||||
@@ -566,13 +593,11 @@ export function loadSettings() {
|
|||||||
worldbookTarget: state.worldbookTarget,
|
worldbookTarget: state.worldbookTarget,
|
||||||
customWorldBook: state.customWorldBook
|
customWorldBook: state.customWorldBook
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($panel) {
|
if ($panel) {
|
||||||
updateUiWithSettings();
|
updateUiWithSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateControlsLockState();
|
updateControlsLockState();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const $viewerButton = $(`#${CHAR_CARD_VIEWER_BUTTON_ID}`);
|
const $viewerButton = $(`#${CHAR_CARD_VIEWER_BUTTON_ID}`);
|
||||||
if ($viewerButton.length > 0) {
|
if ($viewerButton.length > 0) {
|
||||||
|
|||||||
@@ -14,31 +14,30 @@ function createCharCardViewerPopupHtml(displayItems) {
|
|||||||
const pathToLabelMap = {
|
const pathToLabelMap = {
|
||||||
'narrative_essence.core_traits.name': '特质名称',
|
'narrative_essence.core_traits.name': '特质名称',
|
||||||
'narrative_essence.key_relationships.name': '关系人姓名',
|
'narrative_essence.key_relationships.name': '关系人姓名',
|
||||||
|
'NE.trait.name': '特质名称',
|
||||||
|
'NE.rel.name': '关系人姓名',
|
||||||
};
|
};
|
||||||
const keyToLabelMap = {
|
const keyToLabelMap = {
|
||||||
'name': '姓名',
|
'name': '姓名',
|
||||||
|
// Old keys
|
||||||
'archetype': '身份原型',
|
'archetype': '身份原型',
|
||||||
'gender': '性别',
|
'gender': '性别',
|
||||||
'age': '年龄',
|
'age': '年龄',
|
||||||
'race': '种族',
|
'race': '种族',
|
||||||
'current_status': '当前状态',
|
'current_status': '当前状态',
|
||||||
|
|
||||||
'first_impression': '第一印象',
|
'first_impression': '第一印象',
|
||||||
'key_features': '显著特征',
|
'key_features': '显著特征',
|
||||||
'attire': '衣着风格',
|
'attire': '衣着风格',
|
||||||
'mannerisms': '习惯举止',
|
'mannerisms': '习惯举止',
|
||||||
'voice': '声音特征',
|
'voice': '声音特征',
|
||||||
|
|
||||||
'tags': '性格标签',
|
'tags': '性格标签',
|
||||||
'description': '性格详述',
|
'description': '性格详述',
|
||||||
'motivation': '内在驱动',
|
'motivation': '内在驱动',
|
||||||
'values': '价值观',
|
'values': '价值观',
|
||||||
'inner_conflict': '内心挣扎',
|
'inner_conflict': '内心挣扎',
|
||||||
|
|
||||||
'interaction_style': '互动风格',
|
'interaction_style': '互动风格',
|
||||||
'skills': '技能能力',
|
'skills': '技能能力',
|
||||||
'reputation': '他人声望',
|
'reputation': '他人声望',
|
||||||
|
|
||||||
'core_traits': '核心特质',
|
'core_traits': '核心特质',
|
||||||
'verbal_patterns': '语言范式',
|
'verbal_patterns': '语言范式',
|
||||||
'key_relationships': '关键关系',
|
'key_relationships': '关键关系',
|
||||||
@@ -47,6 +46,44 @@ function createCharCardViewerPopupHtml(displayItems) {
|
|||||||
'style_summary': '风格总结',
|
'style_summary': '风格总结',
|
||||||
'quotes': '代表性引言',
|
'quotes': '代表性引言',
|
||||||
'summary': '关系概述',
|
'summary': '关系概述',
|
||||||
|
|
||||||
|
// New short keys
|
||||||
|
'CI': '核心认同',
|
||||||
|
'PI': '物理印记',
|
||||||
|
'PP': '心智侧写',
|
||||||
|
'SM': '社交矩阵',
|
||||||
|
'NE': '叙事精粹',
|
||||||
|
|
||||||
|
'arch': '身份原型',
|
||||||
|
'gen': '性别',
|
||||||
|
// age is same
|
||||||
|
// race is same
|
||||||
|
'status': '当前状态',
|
||||||
|
|
||||||
|
'first': '第一印象',
|
||||||
|
'feat': '显著特征',
|
||||||
|
// attire is same
|
||||||
|
'manner': '习惯举止',
|
||||||
|
// voice is same
|
||||||
|
|
||||||
|
// tags is same
|
||||||
|
'desc': '性格详述',
|
||||||
|
'mot': '内在驱动',
|
||||||
|
'val': '价值观',
|
||||||
|
'conf': '内心挣扎',
|
||||||
|
|
||||||
|
'style': '互动风格/风格总结', // Shared by SM.style and NE.verb.style
|
||||||
|
'skill': '技能能力',
|
||||||
|
'rep': '他人声望',
|
||||||
|
|
||||||
|
'trait': '核心特质',
|
||||||
|
'verb': '语言范式',
|
||||||
|
'rel': '关键关系',
|
||||||
|
|
||||||
|
'def': '特质定义',
|
||||||
|
'evid': '具体事例',
|
||||||
|
'quote': '代表性引言',
|
||||||
|
'sum': '关系概述',
|
||||||
};
|
};
|
||||||
const getLabel = (key, path) => {
|
const getLabel = (key, path) => {
|
||||||
const pathKey = path.replace(/\.\d+\./g, '.');
|
const pathKey = path.replace(/\.\d+\./g, '.');
|
||||||
@@ -141,11 +178,22 @@ function createCharCardViewerPopupHtml(displayItems) {
|
|||||||
if (charData) {
|
if (charData) {
|
||||||
const charName = charData.name || `角色 ${index + 1}`;
|
const charName = charData.name || `角色 ${index + 1}`;
|
||||||
if (charData.name) html += renderCard('姓名', { name: charData.name }, '');
|
if (charData.name) html += renderCard('姓名', { name: charData.name }, '');
|
||||||
|
|
||||||
|
// Support both old and new formats
|
||||||
if (charData.core_identity) html += renderCard('核心认同', charData.core_identity, 'core_identity');
|
if (charData.core_identity) html += renderCard('核心认同', charData.core_identity, 'core_identity');
|
||||||
|
if (charData.CI) html += renderCard('核心认同', charData.CI, 'CI');
|
||||||
|
|
||||||
if (charData.physical_imprint) html += renderCard('物理印记', charData.physical_imprint, 'physical_imprint');
|
if (charData.physical_imprint) html += renderCard('物理印记', charData.physical_imprint, 'physical_imprint');
|
||||||
|
if (charData.PI) html += renderCard('物理印记', charData.PI, 'PI');
|
||||||
|
|
||||||
if (charData.psyche_profile) html += renderCard('心智侧写', charData.psyche_profile, 'psyche_profile');
|
if (charData.psyche_profile) html += renderCard('心智侧写', charData.psyche_profile, 'psyche_profile');
|
||||||
|
if (charData.PP) html += renderCard('心智侧写', charData.PP, 'PP');
|
||||||
|
|
||||||
if (charData.social_matrix) html += renderCard('社交矩阵', charData.social_matrix, 'social_matrix');
|
if (charData.social_matrix) html += renderCard('社交矩阵', charData.social_matrix, 'social_matrix');
|
||||||
|
if (charData.SM) html += renderCard('社交矩阵', charData.SM, 'SM');
|
||||||
|
|
||||||
if (charData.narrative_essence) html += renderCard('叙事精粹', charData.narrative_essence, 'narrative_essence');
|
if (charData.narrative_essence) html += renderCard('叙事精粹', charData.narrative_essence, 'narrative_essence');
|
||||||
|
if (charData.NE) html += renderCard('叙事精粹', charData.NE, 'NE');
|
||||||
|
|
||||||
html += `<div class="cwb-cyber-card cwb-insertion-settings-card">
|
html += `<div class="cwb-cyber-card cwb-insertion-settings-card">
|
||||||
<h4 class="cwb-cyber-card__title">注入设置</h4>
|
<h4 class="cwb-cyber-card__title">注入设置</h4>
|
||||||
|
|||||||
@@ -433,9 +433,9 @@ export const defaultMixedOrder = {
|
|||||||
{ type: 'prompt', index: 5 },
|
{ type: 'prompt', index: 5 },
|
||||||
{ type: 'prompt', index: 6 },
|
{ type: 'prompt', index: 6 },
|
||||||
{ type: 'conditional', id: 'worldbook' },
|
{ type: 'conditional', id: 'worldbook' },
|
||||||
|
{ type: 'conditional', id: 'coreContent' },
|
||||||
{ type: 'conditional', id: 'ruleTemplate' },
|
{ type: 'conditional', id: 'ruleTemplate' },
|
||||||
{ type: 'conditional', id: 'flowTemplate' },
|
{ type: 'conditional', id: 'flowTemplate' },
|
||||||
{ type: 'conditional', id: 'coreContent' },
|
|
||||||
{ type: 'prompt', index: 7 }
|
{ type: 'prompt', index: 7 }
|
||||||
],
|
],
|
||||||
secondary_filler: [
|
secondary_filler: [
|
||||||
@@ -475,8 +475,8 @@ export const defaultMixedOrder = {
|
|||||||
{ type: 'prompt', index: 5 },
|
{ type: 'prompt', index: 5 },
|
||||||
{ type: 'prompt', index: 6 },
|
{ type: 'prompt', index: 6 },
|
||||||
{ type: 'conditional', id: 'cwb_break_armor_prompt' },
|
{ type: 'conditional', id: 'cwb_break_armor_prompt' },
|
||||||
{ type: 'conditional', id: 'cwb_char_card_prompt' },
|
|
||||||
{ type: 'conditional', id: 'newContext' },
|
{ type: 'conditional', id: 'newContext' },
|
||||||
|
{ type: 'conditional', id: 'cwb_char_card_prompt' },
|
||||||
{ type: 'prompt', index: 7 }
|
{ type: 'prompt', index: 7 }
|
||||||
],
|
],
|
||||||
cwb_summarizer_incremental: [
|
cwb_summarizer_incremental: [
|
||||||
@@ -488,10 +488,10 @@ export const defaultMixedOrder = {
|
|||||||
{ type: 'prompt', index: 5 },
|
{ type: 'prompt', index: 5 },
|
||||||
{ type: 'prompt', index: 6 },
|
{ type: 'prompt', index: 6 },
|
||||||
{ type: 'conditional', id: 'cwb_break_armor_prompt' },
|
{ type: 'conditional', id: 'cwb_break_armor_prompt' },
|
||||||
{ type: 'conditional', id: 'cwb_char_card_prompt' },
|
|
||||||
{ type: 'conditional', id: 'cwb_incremental_char_card_prompt' },
|
|
||||||
{ type: 'conditional', id: 'oldFiles' },
|
{ type: 'conditional', id: 'oldFiles' },
|
||||||
{ type: 'conditional', id: 'newContext' },
|
{ type: 'conditional', id: 'newContext' },
|
||||||
|
{ type: 'conditional', id: 'cwb_char_card_prompt' },
|
||||||
|
{ type: 'conditional', id: 'cwb_incremental_char_card_prompt' },
|
||||||
{ type: 'prompt', index: 7 }
|
{ type: 'prompt', index: 7 }
|
||||||
],
|
],
|
||||||
novel_processor: [
|
novel_processor: [
|
||||||
@@ -542,4 +542,3 @@ export const sectionTitles = {
|
|||||||
cwb_summarizer_incremental: '角色世界书(CWB-增量)',
|
cwb_summarizer_incremental: '角色世界书(CWB-增量)',
|
||||||
novel_processor: '小说处理',
|
novel_processor: '小说处理',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -525,51 +525,95 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-entries-header {
|
#world-editor-container .world-editor-entries-header {
|
||||||
display: none; /* 在移动端隐藏表头 */
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
|
||||||
#world-editor-container .world-editor-entry-row {
|
|
||||||
grid-template-columns: 40px 1fr; /* 简化为两列:复选框和内容 */
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
background-color: #333;
|
||||||
border-bottom: 1px solid #444;
|
border-bottom: 1px solid #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-entry-row > div:not(:nth-child(1)):not(:nth-child(2)) {
|
#world-editor-container .world-editor-entries-header > div {
|
||||||
display: none; /* 隐藏除复选框和主要内容外的所有列 */
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#world-editor-container .world-editor-entries-header > div:first-child {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#world-editor-container .world-editor-entries-header > div:first-child::after {
|
||||||
|
content: "全选";
|
||||||
|
margin-left: 10px;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-entry-row {
|
#world-editor-container .world-editor-entry-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr; /* 复选框和内容区 */
|
grid-template-columns: 40px 1fr; /* 复选框和内容区 */
|
||||||
gap: 10px;
|
gap: 5px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-entry-row > div {
|
#world-editor-container .world-editor-entry-row > div {
|
||||||
display: block !important; /* 确保所有单元格都可见 */
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 5px 0;
|
padding: 2px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-entry-row::before {
|
/* Add labels for mobile view */
|
||||||
display: block;
|
#world-editor-container .world-editor-entry-row > div::before {
|
||||||
|
content: attr(data-label) ": ";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #888;
|
color: #888;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-entry-checkbox { grid-row: 1 / span 5; align-self: center; }
|
/* Hide label for checkbox */
|
||||||
#world-editor-container .world-editor-entry-status { grid-column: 2; }
|
#world-editor-container .world-editor-entry-row > div:nth-child(1)::before {
|
||||||
#world-editor-container .world-editor-entry-activation { grid-column: 2; }
|
display: none;
|
||||||
#world-editor-container .world-editor-entry-keys { grid-column: 2; }
|
}
|
||||||
#world-editor-container .world-editor-entry-content { grid-column: 2; }
|
|
||||||
#world-editor-container .world-editor-entry-position { grid-column: 2; }
|
|
||||||
#world-editor-container .world-editor-entry-depth { grid-column: 2; }
|
|
||||||
#world-editor-container .world-editor-entry-order { grid-column: 2; }
|
|
||||||
|
|
||||||
|
/* Checkbox positioning - Target the WRAPPER div */
|
||||||
|
#world-editor-container .world-editor-entry-checkbox {
|
||||||
|
grid-row: 1 / span 8;
|
||||||
|
align-self: start;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stack other elements in the second column */
|
||||||
|
#world-editor-container .world-editor-entry-status,
|
||||||
|
#world-editor-container .world-editor-entry-activation,
|
||||||
|
#world-editor-container .world-editor-entry-keys,
|
||||||
|
#world-editor-container .world-editor-entry-content,
|
||||||
|
#world-editor-container .world-editor-entry-position,
|
||||||
|
#world-editor-container .world-editor-entry-depth,
|
||||||
|
#world-editor-container .world-editor-entry-order,
|
||||||
|
#world-editor-container .world-editor-entry-row > div[data-label="条目"] {
|
||||||
|
grid-column: 2;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Truncation for keys and content */
|
||||||
#world-editor-container .world-editor-entry-keys,
|
#world-editor-container .world-editor-entry-keys,
|
||||||
#world-editor-container .world-editor-entry-content {
|
#world-editor-container .world-editor-entry-content {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
display: -webkit-box !important;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure inline edits take full width on mobile */
|
||||||
|
#world-editor-container .inline-edit {
|
||||||
|
width: calc(100% - 60px); /* Adjust for label width roughly */
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world-editor-container .world-editor-batch-actions {
|
#world-editor-container .world-editor-batch-actions {
|
||||||
|
|||||||
@@ -176,8 +176,8 @@
|
|||||||
<i id="amily2_mhb_small_expand_editor" class="editor_maximize fa-solid fa-maximize right_menu_button interactable" title="展开编辑器" tabindex="0"></i>
|
<i id="amily2_mhb_small_expand_editor" class="editor_maximize fa-solid fa-maximize right_menu_button interactable" title="展开编辑器" tabindex="0"></i>
|
||||||
</div>
|
</div>
|
||||||
<select id="amily2_mhb_small_prompt_selector" class="text_pole">
|
<select id="amily2_mhb_small_prompt_selector" class="text_pole">
|
||||||
<option value="jailbreak">破限谕旨 (最高优先级)</option>
|
<option value="jailbreak">小总结主要提示词</option>
|
||||||
<option value="summary">敕史纲要 (总结任务)</option>
|
<option value="summary">小总结任务提示词</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="amily2_settings_block prompt-editor-area">
|
<div class="amily2_settings_block prompt-editor-area">
|
||||||
@@ -270,7 +270,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<small class="notes" style="text-align: center; display: block; margin-top: 5px;">
|
<small class="notes" style="text-align: center; display: block; margin-top: 5px;">
|
||||||
【开始远征】将立即清算所有未记录的历史。 【自动巡录】则在您聊天时,于后台默默守护史册的完整。
|
【自动批量】将立即清算所有未记录的历史。 【静默总结】则在您聊天时,于后台默默守护史册的完整。
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
|
|
||||||
@@ -280,6 +280,41 @@
|
|||||||
<!-- 上下分割线 -->
|
<!-- 上下分割线 -->
|
||||||
<hr class="header-divider" style="margin: 20px 0;">
|
<hr class="header-divider" style="margin: 20px 0;">
|
||||||
|
|
||||||
|
<fieldset class="settings-group" style="border-style: dashed;">
|
||||||
|
<legend>📚 史册归档与回溯</legend>
|
||||||
|
<small class="notes" style="text-align: center; display: block; margin-bottom: 15px;">
|
||||||
|
管理多条时间线的史册,随时封存或重启旧的历史。
|
||||||
|
</small>
|
||||||
|
|
||||||
|
<div class="amily2_settings_block">
|
||||||
|
<button id="amily2_mhb_archive_current" class="menu_button secondary small_button interactable" style="width: 100%; margin-bottom: 10px;">
|
||||||
|
<i class="fas fa-archive"></i> 归档当前【对话流水总帐】并停用
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mhb-selector-container">
|
||||||
|
<div class="mhb-selector-group">
|
||||||
|
<label for="amily2_mhb_archive_selector">选择要回溯的旧史册</label>
|
||||||
|
<div class="select-with-refresh">
|
||||||
|
<select id="amily2_mhb_archive_selector" class="text_pole" style="flex-grow: 1;">
|
||||||
|
<option value="">请刷新列表...</option>
|
||||||
|
</select>
|
||||||
|
<button id="amily2_mhb_refresh_archives" class="menu_button secondary small_button interactable" title="刷新归档列表">
|
||||||
|
<i class="fas fa-sync-alt"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="amily2_mhb_restore_archive" class="menu_button primary small_button interactable" style="margin-top: 10px; width: 100%;">
|
||||||
|
<i class="fas fa-trash-restore"></i> 回溯选中史册 (自动归档当前)
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<!-- 上下分割线 -->
|
||||||
|
<hr class="header-divider" style="margin: 20px 0;">
|
||||||
|
|
||||||
<fieldset class="settings-group" style="border-style: dashed;">
|
<fieldset class="settings-group" style="border-style: dashed;">
|
||||||
<legend>💎 宏史卷 (史册精炼)</legend>
|
<legend>💎 宏史卷 (史册精炼)</legend>
|
||||||
|
|
||||||
@@ -290,8 +325,8 @@
|
|||||||
<i id="amily2_mhb_large_expand_editor" class="editor_maximize fa-solid fa-maximize right_menu_button interactable" title="展开编辑器" tabindex="0"></i>
|
<i id="amily2_mhb_large_expand_editor" class="editor_maximize fa-solid fa-maximize right_menu_button interactable" title="展开编辑器" tabindex="0"></i>
|
||||||
</div>
|
</div>
|
||||||
<select id="amily2_mhb_large_prompt_selector" class="text_pole">
|
<select id="amily2_mhb_large_prompt_selector" class="text_pole">
|
||||||
<option value="jailbreak">破限谕旨 (最高优先级)</option>
|
<option value="jailbreak">大总结主要提示词</option>
|
||||||
<option value="summary">精炼纲要 (精炼任务)</option>
|
<option value="summary">大总结任务提示词)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="amily2_settings_block prompt-editor-area">
|
<div class="amily2_settings_block prompt-editor-area">
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-width: 0;
|
min-width: 0; /* Prevents flex items from overflowing */
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollable-container {
|
.scrollable-container {
|
||||||
@@ -152,6 +152,7 @@
|
|||||||
|
|
||||||
<div id="table_worldbook_select_wrapper" style="display: none;">
|
<div id="table_worldbook_select_wrapper" style="display: none;">
|
||||||
<div class="worldbook-selection-container">
|
<div class="worldbook-selection-container">
|
||||||
|
<!-- World Book List Column -->
|
||||||
<div class="worldbook-column">
|
<div class="worldbook-column">
|
||||||
<div class="amily2_opt_label_with_button_wrapper">
|
<div class="amily2_opt_label_with_button_wrapper">
|
||||||
<label>选择世界书 (可多选)</label>
|
<label>选择世界书 (可多选)</label>
|
||||||
@@ -169,6 +170,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- World Book Entry List Column -->
|
||||||
<div class="worldbook-column" style="margin-top: 10px;">
|
<div class="worldbook-column" style="margin-top: 10px;">
|
||||||
<label>选择条目 (可多选)</label>
|
<label>选择条目 (可多选)</label>
|
||||||
<div class="table-search-wrapper">
|
<div class="table-search-wrapper">
|
||||||
@@ -199,6 +201,13 @@
|
|||||||
<span class="slider"></span>
|
<span class="slider"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-block-with-switch" style="margin-bottom: 10px;">
|
||||||
|
<label for="context-optimization-enabled-toggle">启用上下文优化 (合并世界书)</label>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="context-optimization-enabled" data-setting-key="context_optimization_enabled" data-type="boolean">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="control-block-with-switch" style="margin-bottom: 10px;">
|
<div class="control-block-with-switch" style="margin-bottom: 10px;">
|
||||||
<label>填表模式</label>
|
<label>填表模式</label>
|
||||||
<div class="radio-group">
|
<div class="radio-group">
|
||||||
@@ -218,6 +227,13 @@
|
|||||||
<small class="notes" style="margin-top: 5px; display: block;">默认使用微言录的标签提取与内容排除规则。</small>
|
<small class="notes" style="margin-top: 5px; display: block;">默认使用微言录的标签提取与内容排除规则。</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 分步填表延迟滑块 - 仅在分步模式下显示 -->
|
||||||
|
<div id="secondary-filler-delay-container" class="control-block-with-switch" style="margin-bottom: 10px; display: none;">
|
||||||
|
<label for="secondary-filler-delay-slider">填表延迟 (楼层): <span id="secondary-filler-delay-value">0</span></label>
|
||||||
|
<input type="range" id="secondary-filler-delay-slider" min="0" max="10" step="1" value="0" class="text_pole" style="width: 100%; margin-top: 5px;">
|
||||||
|
<small class="notes" style="margin-top: 5px; display: block;">设置延迟多少楼层后才进行填表(防并发冲突、超级记忆功能专用,默认为0)。</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="table-independent-rules-container" class="control-block-with-switch" style="margin-bottom: 10px; display: none; flex-direction: column; align-items: flex-start; gap: 8px;">
|
<div id="table-independent-rules-container" class="control-block-with-switch" style="margin-bottom: 10px; display: none; flex-direction: column; align-items: flex-start; gap: 8px;">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
|
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
|
||||||
<label for="table-independent-rules-enabled">启用独立提取规则</label>
|
<label for="table-independent-rules-enabled">启用独立提取规则</label>
|
||||||
@@ -379,4 +395,3 @@
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -156,9 +156,14 @@
|
|||||||
<fieldset class="settings-group">
|
<fieldset class="settings-group">
|
||||||
<legend style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
|
<legend style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
|
||||||
<span><i class="fas fa-cog"></i> Amily中枢</span>
|
<span><i class="fas fa-cog"></i> Amily中枢</span>
|
||||||
|
<div style="display: flex; gap: 5px;">
|
||||||
|
<button id="amily2_reset_auth" class="menu_button small_button interactable" title="清除授权">
|
||||||
|
<i class="fas fa-sign-out-alt"></i>
|
||||||
|
</button>
|
||||||
<button id="amily2_open_tutorial" class="menu_button small_button interactable" title="查看使用教程">
|
<button id="amily2_open_tutorial" class="menu_button small_button interactable" title="查看使用教程">
|
||||||
教程
|
教程
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</legend>
|
</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -182,6 +187,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="settings-group">
|
||||||
|
<legend><i class="fas fa-flask"></i> 内测功能</legend>
|
||||||
|
<div class="button-group" style="display: flex; justify-content: space-between; gap: 8px;">
|
||||||
|
<button id="amily2_open_super_memory" class="menu_button wide_button"><i class="fas fa-brain"></i> 超级记忆</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="settings-group">
|
<fieldset class="settings-group">
|
||||||
<legend><i class="fas fa-bullhorn"></i> 作者留言</legend>
|
<legend><i class="fas fa-bullhorn"></i> 作者留言</legend>
|
||||||
<div id="amily2_message_board" style="display: flex; justify-content: center; align-items: center; padding: 8px; background-color: rgba(255, 255, 255, 0.05); border-radius: 5px; min-height: 40px;">
|
<div id="amily2_message_board" style="display: flex; justify-content: center; align-items: center; padding: 8px; background-color: rgba(255, 255, 255, 0.05); border-radius: 5px; min-height: 40px;">
|
||||||
|
|||||||
194
assets/super-memory.css
Normal file
194
assets/super-memory.css
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
#sm-modal-container {
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
padding: 10px;
|
||||||
|
height: calc(100% - 60px); /* Adjust based on header height */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-intro-box {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-intro-box h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #05c3f3; /* Amily Blue */
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-navigation-deck {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-nav-item {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #888;
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px 5px 0 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-nav-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-nav-item.active {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #05c3f3;
|
||||||
|
border-bottom: 2px solid #05c3f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-scroll {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-tab-pane {
|
||||||
|
display: none;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-tab-pane.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(5px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-settings-group {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-settings-group legend {
|
||||||
|
color: #05c3f3;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-control-block {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-bottom: 1px dashed rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-control-block:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-input {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-action-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background 0.2s;
|
||||||
|
background: #4a4a4a;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-action-button.success {
|
||||||
|
background: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-action-button.success:hover {
|
||||||
|
background: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-action-button.danger {
|
||||||
|
background: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-action-button.danger:hover {
|
||||||
|
background: #c82333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-status-indicator {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffc107; /* Warning yellow */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle Switch */
|
||||||
|
.sm-toggle-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-toggle-switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sm-slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 2px;
|
||||||
|
bottom: 2px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .sm-slider {
|
||||||
|
background-color: #05c3f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .sm-slider:before {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ const UPDATE_CHECK_URL =
|
|||||||
"https://raw.githubusercontent.com/Wx-2025/ST-Amily2-Chat-Optimisation/refs/heads/main/amily2_update_info.json";
|
"https://raw.githubusercontent.com/Wx-2025/ST-Amily2-Chat-Optimisation/refs/heads/main/amily2_update_info.json";
|
||||||
|
|
||||||
const MESSAGE_BOARD_URL =
|
const MESSAGE_BOARD_URL =
|
||||||
"https://raw.githubusercontent.com/Wx-2025/ST-Amily2-Chat-Optimisation/refs/heads/main/amily2_message_board.json";
|
"https://amilyservice.amily49.cc/amily2_message_board.json";
|
||||||
|
|
||||||
export async function fetchMessageBoardContent() {
|
export async function fetchMessageBoardContent() {
|
||||||
if (!MESSAGE_BOARD_URL) {
|
if (!MESSAGE_BOARD_URL) {
|
||||||
|
|||||||
195
core/context-optimizer.js
Normal file
195
core/context-optimizer.js
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import { log } from "./table-system/logger.js";
|
||||||
|
import { getContext } from "/scripts/extensions.js";
|
||||||
|
import { eventSource, event_types } from "/script.js";
|
||||||
|
|
||||||
|
function collectDataToBuffer(buffer, tableName, rowObj) {
|
||||||
|
if (!buffer[tableName]) {
|
||||||
|
buffer[tableName] = {
|
||||||
|
headers: Object.keys(rowObj),
|
||||||
|
rows: []
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const newKeys = Object.keys(rowObj);
|
||||||
|
newKeys.forEach(k => {
|
||||||
|
if (!buffer[tableName].headers.includes(k)) {
|
||||||
|
buffer[tableName].headers.push(k);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
buffer[tableName].rows.push(rowObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushBufferToMarkdown(buffer) {
|
||||||
|
let output = "";
|
||||||
|
const tableNames = Object.keys(buffer);
|
||||||
|
|
||||||
|
if (tableNames.length === 0) return "";
|
||||||
|
|
||||||
|
for (const tableName of tableNames) {
|
||||||
|
const { headers, rows } = buffer[tableName];
|
||||||
|
if (rows.length === 0) continue;
|
||||||
|
|
||||||
|
const firstColKey = headers[0];
|
||||||
|
const firstColVal = rows[0] ? rows[0][firstColKey] : '';
|
||||||
|
const isIndexCol = (firstColKey && (firstColKey.includes('索引') || firstColKey.includes('Index'))) ||
|
||||||
|
(typeof firstColVal === 'string' && /^\s*M\d+/.test(firstColVal));
|
||||||
|
|
||||||
|
if (isIndexCol) {
|
||||||
|
rows.sort((a, b) => {
|
||||||
|
const valA = String(a[firstColKey] || '');
|
||||||
|
const valB = String(b[firstColKey] || '');
|
||||||
|
return valA.localeCompare(valB, undefined, { numeric: true });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
rows.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
output += `\n# ${tableName}档案\n`;
|
||||||
|
output += `| ${headers.join(' | ')} |\n`;
|
||||||
|
output += `|${headers.map(() => '---').join('|')}|\n`;
|
||||||
|
|
||||||
|
for (const rowObj of rows) {
|
||||||
|
const rowArr = headers.map(h => {
|
||||||
|
const val = rowObj[h];
|
||||||
|
let safeVal = (val === undefined || val === null) ? '' : String(val);
|
||||||
|
safeVal = safeVal.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
||||||
|
return safeVal;
|
||||||
|
});
|
||||||
|
output += `| ${rowArr.join(' | ')} |\n`;
|
||||||
|
}
|
||||||
|
output += `\n`;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processText(text) {
|
||||||
|
const blockRegex = /【(.*?)档案[::]\s*.*?】\s*((?:-\s*.*?[::].*?(?:\r?\n|$))+)/g;
|
||||||
|
const itemRegex = /-\s*(.*?)[::]\s*(.*?)(?:\r?\n|$)/g;
|
||||||
|
|
||||||
|
const buffer = {};
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
const cleanText = text.replace(blockRegex, (match, tableName, content) => {
|
||||||
|
found = true;
|
||||||
|
const rowObj = {};
|
||||||
|
|
||||||
|
let itemMatch;
|
||||||
|
itemRegex.lastIndex = 0;
|
||||||
|
|
||||||
|
while ((itemMatch = itemRegex.exec(content)) !== null) {
|
||||||
|
const key = itemMatch[1].trim();
|
||||||
|
const val = itemMatch[2].trim();
|
||||||
|
if (key) {
|
||||||
|
rowObj[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(rowObj).length > 0) {
|
||||||
|
collectDataToBuffer(buffer, tableName, rowObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""; // 移除原始文本
|
||||||
|
});
|
||||||
|
|
||||||
|
return { cleanText, buffer, found };
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePromptProcessing(data) {
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
if (typeof data.prompt === 'string') {
|
||||||
|
const { cleanText, buffer, found } = processText(data.prompt);
|
||||||
|
if (found) {
|
||||||
|
const mergedTable = flushBufferToMarkdown(buffer);
|
||||||
|
if (mergedTable) {
|
||||||
|
data.prompt = cleanText + "\n" + mergedTable;
|
||||||
|
log('[ContextOptimizer] 已优化上下文:合并了分散的世界书条目 (Text Mode)。', 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (Array.isArray(data.chat)) {
|
||||||
|
console.log('[ContextOptimizer] 检测到 Chat Completion 格式...');
|
||||||
|
|
||||||
|
const newChat = [];
|
||||||
|
let modifiedCount = 0;
|
||||||
|
|
||||||
|
for (const msg of data.chat) {
|
||||||
|
const newMsg = { ...msg };
|
||||||
|
|
||||||
|
if (typeof newMsg.content === 'string') {
|
||||||
|
const { cleanText, buffer, found } = processText(newMsg.content);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
const mergedTable = flushBufferToMarkdown(buffer);
|
||||||
|
if (mergedTable) {
|
||||||
|
newMsg.content = cleanText + "\n" + mergedTable;
|
||||||
|
modifiedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newChat.push(newMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifiedCount > 0) {
|
||||||
|
console.log(`[ContextOptimizer] 已原地优化 ${modifiedCount} 条消息中的表格数据。`);
|
||||||
|
|
||||||
|
// 全量替换,确保生效
|
||||||
|
data.chat.splice(0, data.chat.length, ...newChat);
|
||||||
|
log('[ContextOptimizer] 已优化上下文:合并了分散的世界书条目 (Chat Mode - In Place)。', 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册监听器
|
||||||
|
*/
|
||||||
|
export function registerContextOptimizerMacros() {
|
||||||
|
console.log('[ContextOptimizer] 正在注册监听器...');
|
||||||
|
const context = getContext();
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
console.log('[ContextOptimizer] Context APIs:', Object.keys(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context && context.registerChatCompletionModifier) {
|
||||||
|
context.registerChatCompletionModifier((chat) => {
|
||||||
|
console.log('[ContextOptimizer] ChatCompletionModifier 触发');
|
||||||
|
const data = { chat: chat };
|
||||||
|
handlePromptProcessing(data);
|
||||||
|
return data.chat;
|
||||||
|
});
|
||||||
|
log('[ContextOptimizer] 已注册 Chat Completion Modifier。', 'success');
|
||||||
|
|
||||||
|
} else if (context && context.registerPromptModifier) {
|
||||||
|
context.registerPromptModifier((prompt) => {
|
||||||
|
console.log('[ContextOptimizer] PromptModifier 触发');
|
||||||
|
const data = { prompt: prompt };
|
||||||
|
handlePromptProcessing(data);
|
||||||
|
return data.prompt;
|
||||||
|
});
|
||||||
|
log('[ContextOptimizer] 已注册 Prompt Modifier (正则模式)。', 'success');
|
||||||
|
|
||||||
|
} else if (eventSource) {
|
||||||
|
eventSource.on('chat_completion_prompt_ready', (...args) => {
|
||||||
|
if (args[0] && typeof args[0] === 'object') {
|
||||||
|
handlePromptProcessing(args[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventSource.on(event_types.GENERATION_STARTED, (...args) => {
|
||||||
|
if (args.length > 1 && args[1] && typeof args[1].prompt === 'string') {
|
||||||
|
handlePromptProcessing(args[1]);
|
||||||
|
} else if (args[0] && typeof args[0].prompt === 'string') {
|
||||||
|
handlePromptProcessing(args[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log('[ContextOptimizer] 已绑定事件监听 (Text/Chat 双模式)。', 'info');
|
||||||
|
} else {
|
||||||
|
console.error('[ContextOptimizer] 无法获取 eventSource。');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function resetContextBuffer() {
|
||||||
|
}
|
||||||
@@ -18,6 +18,21 @@ import { callAI, generateRandomSeed } from "./api.js";
|
|||||||
import { callNgmsAI } from "./api/Ngms_api.js";
|
import { callNgmsAI } from "./api/Ngms_api.js";
|
||||||
import { executeAutoHide } from "./autoHideManager.js";
|
import { executeAutoHide } from "./autoHideManager.js";
|
||||||
|
|
||||||
|
let reloadEditor = () => {
|
||||||
|
console.warn("[大史官] reloadEditor 函数不可用,可能是旧版本。已使用空函数代替。");
|
||||||
|
};
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const { reloadEditor: importedReloadEditor } = await import("/scripts/world-info.js");
|
||||||
|
if (importedReloadEditor) {
|
||||||
|
reloadEditor = importedReloadEditor;
|
||||||
|
console.log("[大史官] 已成功动态导入 reloadEditor。");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[大史官] 动态导入 reloadEditor 失败,将使用空函数。错误信息:", error.message);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
let isExpeditionRunning = false;
|
let isExpeditionRunning = false;
|
||||||
let manualStopRequested = false;
|
let manualStopRequested = false;
|
||||||
|
|
||||||
@@ -105,6 +120,16 @@ export async function getLoresForWorldbook(bookName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (!text) return '';
|
||||||
|
return text
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
export async function executeManualSummary(startFloor, endFloor, isAuto = false) {
|
export async function executeManualSummary(startFloor, endFloor, isAuto = false) {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
const toastTitle = isAuto ? "微言录 (自动)" : "微言录 (手动)";
|
const toastTitle = isAuto ? "微言录 (自动)" : "微言录 (手动)";
|
||||||
@@ -154,9 +179,9 @@ export async function executeManualSummary(startFloor, endFloor, isAuto = false)
|
|||||||
const generateModalHtml = (msgList) => {
|
const generateModalHtml = (msgList) => {
|
||||||
const messageHtml = msgList.map(msg => `
|
const messageHtml = msgList.map(msg => `
|
||||||
<details class="historiography-message-item" data-author-type="${msg.authorType}">
|
<details class="historiography-message-item" data-author-type="${msg.authorType}">
|
||||||
<summary>【第 ${msg.floor} 楼】 ${msg.author}</summary>
|
<summary>【第 ${msg.floor} 楼】 ${escapeHtml(msg.author)}</summary>
|
||||||
<div class="historiography-editor-container">
|
<div class="historiography-editor-container">
|
||||||
<textarea class="text_pole" data-floor="${msg.floor}">${msg.content}</textarea>
|
<textarea class="text_pole" data-floor="${msg.floor}">${escapeHtml(msg.content)}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
`).join('');
|
`).join('');
|
||||||
@@ -613,6 +638,7 @@ export async function executeRefinement(worldbook, loreKey) {
|
|||||||
|
|
||||||
entry.content = finalContent;
|
entry.content = finalContent;
|
||||||
await saveWorldInfo(worldbook, bookData, true);
|
await saveWorldInfo(worldbook, bookData, true);
|
||||||
|
reloadEditor(worldbook);
|
||||||
toastr.success(`史册已成功重铸,并保存于《${worldbook}》!`, "宏史卷重铸完毕");
|
toastr.success(`史册已成功重铸,并保存于《${worldbook}》!`, "宏史卷重铸完毕");
|
||||||
},
|
},
|
||||||
onRegenerate: async (dialog) => {
|
onRegenerate: async (dialog) => {
|
||||||
@@ -816,3 +842,135 @@ export async function executeCompilation(worldbook, loreKeys) {
|
|||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 史册归档与回溯系统 ==========
|
||||||
|
|
||||||
|
async function getTargetLorebookName() {
|
||||||
|
const settings = extension_settings[extensionName];
|
||||||
|
const context = getContext();
|
||||||
|
let targetLorebookName = null;
|
||||||
|
switch (settings.lorebookTarget) {
|
||||||
|
case "character_main":
|
||||||
|
targetLorebookName = characters[context.characterId]?.data?.extensions?.world;
|
||||||
|
break;
|
||||||
|
case "dedicated":
|
||||||
|
const chatIdentifier = await getChatIdentifier();
|
||||||
|
targetLorebookName = `Amily2-Lore-${chatIdentifier}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return targetLorebookName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function archiveCurrentLedger() {
|
||||||
|
try {
|
||||||
|
const targetLorebookName = await getTargetLorebookName();
|
||||||
|
if (!targetLorebookName) {
|
||||||
|
toastr.error("无法确定目标世界书,归档失败。", "圣谕不明");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookData = await loadWorldInfo(targetLorebookName);
|
||||||
|
if (!bookData || !bookData.entries) {
|
||||||
|
toastr.error(`无法读取世界书《${targetLorebookName}》。`, "国史馆");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ledgerEntryKey = Object.keys(bookData.entries).find(
|
||||||
|
(key) => bookData.entries[key].comment === RUNNING_LOG_COMMENT && !bookData.entries[key].disable
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!ledgerEntryKey) {
|
||||||
|
toastr.info("当前没有活跃的【对话流水总帐】,无需归档。", "国史馆");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = bookData.entries[ledgerEntryKey];
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
||||||
|
const newComment = `${RUNNING_LOG_COMMENT}_归档_${timestamp}`;
|
||||||
|
|
||||||
|
entry.comment = newComment;
|
||||||
|
entry.disable = true;
|
||||||
|
|
||||||
|
await saveWorldInfo(targetLorebookName, bookData, true);
|
||||||
|
reloadEditor(targetLorebookName);
|
||||||
|
toastr.success(`已将当前流水总帐归档为:\n${newComment}`, "归档成功");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[大史官] 归档失败:", error);
|
||||||
|
toastr.error(`归档失败: ${error.message}`, "国史馆");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getArchivedLedgers() {
|
||||||
|
try {
|
||||||
|
const targetLorebookName = await getTargetLorebookName();
|
||||||
|
if (!targetLorebookName) return [];
|
||||||
|
|
||||||
|
const bookData = await loadWorldInfo(targetLorebookName);
|
||||||
|
if (!bookData || !bookData.entries) return [];
|
||||||
|
|
||||||
|
const archivedLedgers = Object.entries(bookData.entries)
|
||||||
|
.filter(([, entry]) => entry.comment && entry.comment.startsWith(`${RUNNING_LOG_COMMENT}_归档_`))
|
||||||
|
.map(([key, entry]) => ({
|
||||||
|
key: key,
|
||||||
|
comment: entry.comment
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.comment.localeCompare(a.comment)); // 按时间倒序排列
|
||||||
|
|
||||||
|
return archivedLedgers;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[大史官] 获取归档列表失败:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function restoreArchivedLedger(targetLoreKey) {
|
||||||
|
try {
|
||||||
|
const targetLorebookName = await getTargetLorebookName();
|
||||||
|
if (!targetLorebookName) {
|
||||||
|
toastr.error("无法确定目标世界书,回溯失败。", "圣谕不明");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookData = await loadWorldInfo(targetLorebookName);
|
||||||
|
if (!bookData || !bookData.entries) {
|
||||||
|
toastr.error(`无法读取世界书《${targetLorebookName}》。`, "国史馆");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetEntry = bookData.entries[targetLoreKey];
|
||||||
|
if (!targetEntry) {
|
||||||
|
toastr.error("找不到指定的归档史册。", "圣谕有误");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentActiveKey = Object.keys(bookData.entries).find(
|
||||||
|
(key) => bookData.entries[key].comment === RUNNING_LOG_COMMENT && !bookData.entries[key].disable
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentActiveKey) {
|
||||||
|
if (currentActiveKey !== targetLoreKey) {
|
||||||
|
const activeEntry = bookData.entries[currentActiveKey];
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
||||||
|
activeEntry.comment = `${RUNNING_LOG_COMMENT}_归档_${timestamp}`;
|
||||||
|
activeEntry.disable = true;
|
||||||
|
toastr.info(`已自动归档原有的活跃史册为: ${activeEntry.comment}`, "自动归档");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetEntry.comment = RUNNING_LOG_COMMENT;
|
||||||
|
targetEntry.disable = false;
|
||||||
|
|
||||||
|
await saveWorldInfo(targetLorebookName, bookData, true);
|
||||||
|
reloadEditor(targetLorebookName);
|
||||||
|
toastr.success("史册回溯成功!时光已倒流,旧史重现。", "回溯成功");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[大史官] 回溯失败:", error);
|
||||||
|
toastr.error(`回溯失败: ${error.message}`, "国史馆");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
68
core/super-memory/bindings.js
Normal file
68
core/super-memory/bindings.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { extensionName } from "../../utils/settings.js";
|
||||||
|
import { extension_settings } from "/scripts/extensions.js";
|
||||||
|
import { saveSettingsDebounced } from "/script.js";
|
||||||
|
import { initializeSuperMemory, purgeSuperMemory } from "./manager.js";
|
||||||
|
|
||||||
|
export function bindSuperMemoryEvents() {
|
||||||
|
const panel = $('#amily2_super_memory_panel');
|
||||||
|
if (panel.length === 0) return;
|
||||||
|
|
||||||
|
panel.on('click', '.sm-nav-item', function() {
|
||||||
|
const tab = $(this).data('tab');
|
||||||
|
|
||||||
|
panel.find('.sm-nav-item').removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
|
||||||
|
panel.find('.sm-tab-pane').removeClass('active');
|
||||||
|
panel.find(`#sm-${tab}-tab`).addClass('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.on('change', 'input[type="checkbox"]', function() {
|
||||||
|
if (!extension_settings[extensionName]) extension_settings[extensionName] = {};
|
||||||
|
|
||||||
|
const id = this.id;
|
||||||
|
let key = null;
|
||||||
|
|
||||||
|
if (id === 'sm-system-enabled') key = 'super_memory_enabled';
|
||||||
|
if (id === 'sm-bridge-enabled') key = 'superMemory_bridgeEnabled';
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
extension_settings[extensionName][key] = this.checked;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
console.log(`[Amily2-SuperMemory] Setting updated: ${key} = ${this.checked}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadSuperMemorySettings();
|
||||||
|
|
||||||
|
console.log('[Amily2-SuperMemory] Events bound successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSuperMemorySettings() {
|
||||||
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
|
$('#sm-system-enabled').prop('checked', settings.super_memory_enabled ?? false);
|
||||||
|
$('#sm-bridge-enabled').prop('checked', settings.superMemory_bridgeEnabled ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.sm_initializeSystem = async function() {
|
||||||
|
toastr.info('超级记忆系统正在初始化...');
|
||||||
|
$('#sm-system-status').text('初始化中...').css('color', 'yellow');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await initializeSuperMemory();
|
||||||
|
toastr.success('超级记忆系统初始化完成。');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toastr.error('初始化失败,请检查控制台。');
|
||||||
|
$('#sm-system-status').text('错误').css('color', 'red');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.sm_purgeMemory = async function() {
|
||||||
|
if (confirm('您确定要清空所有由Amily2管理的超级记忆数据吗?\n这将删除世界书中所有以表格世界书的条目。')) {
|
||||||
|
toastr.info('正在清空记忆...');
|
||||||
|
await purgeSuperMemory();
|
||||||
|
$('#sm-system-status').text('已清空').css('color', '#ffc107');
|
||||||
|
}
|
||||||
|
};
|
||||||
77
core/super-memory/index.html
Normal file
77
core/super-memory/index.html
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<div class="amily2-header">
|
||||||
|
<div class="additional-features-title interactable" title="Amily2 究极长期记忆系统">
|
||||||
|
<i class="fas fa-brain"></i> 灵台 · 记忆中枢
|
||||||
|
</div>
|
||||||
|
<button id="amily2_back_to_main_from_super_memory" class="menu_button secondary small_button interactable">
|
||||||
|
返回主殿 <i class="fas fa-arrow-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<hr class="header-divider">
|
||||||
|
|
||||||
|
<div id="sm-modal-container">
|
||||||
|
<div class="sm-intro-box">
|
||||||
|
<h3><i class="fas fa-microchip"></i> 究极长期记忆 (Super Memory)</h3>
|
||||||
|
<p>欢迎来到 Amily2 的核心记忆中枢。这里掌管着世界的记忆,连接着每一个角色、每一个物品与每一段传说。</p>
|
||||||
|
<p>通过“三级金字塔”注入策略,我们将实现极致的 Token 节省与无限的记忆深度。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sm-navigation-deck">
|
||||||
|
<button class="sm-nav-item active" data-tab="dashboard">概览</button>
|
||||||
|
<button class="sm-nav-item" data-tab="config">配置</button>
|
||||||
|
<button class="sm-nav-item" data-tab="relation">关联网络</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sm-scroll">
|
||||||
|
<!-- Dashboard Tab -->
|
||||||
|
<div id="sm-dashboard-tab" class="sm-tab-pane active">
|
||||||
|
<fieldset class="sm-settings-group">
|
||||||
|
<legend><i class="fas fa-tachometer-alt"></i> 状态监控</legend>
|
||||||
|
<div class="sm-control-block">
|
||||||
|
<label>记忆系统状态:</label>
|
||||||
|
<span id="sm-system-status" class="sm-status-indicator">未初始化</span>
|
||||||
|
</div>
|
||||||
|
<div class="sm-control-block">
|
||||||
|
<label>当前索引 (Tier 1):</label>
|
||||||
|
<span id="sm-index-count">0 条目</span>
|
||||||
|
</div>
|
||||||
|
<div class="sm-control-block">
|
||||||
|
<label>已触发详情 (Tier 2):</label>
|
||||||
|
<span id="sm-detail-count">0 条目</span>
|
||||||
|
</div>
|
||||||
|
<div class="sm-button-group">
|
||||||
|
<button class="sm-action-button success" onclick="sm_initializeSystem()">初始化系统</button>
|
||||||
|
<button class="sm-action-button danger" onclick="sm_purgeMemory()">清空记忆</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Config Tab -->
|
||||||
|
<div id="sm-config-tab" class="sm-tab-pane">
|
||||||
|
<fieldset class="sm-settings-group">
|
||||||
|
<legend><i class="fas fa-cogs"></i> 记忆策略配置</legend>
|
||||||
|
<div class="sm-control-block">
|
||||||
|
<label>启用 Super Memory (总开关):</label>
|
||||||
|
<label class="sm-toggle-switch">
|
||||||
|
<input type="checkbox" id="sm-system-enabled">
|
||||||
|
<span class="sm-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="sm-control-block">
|
||||||
|
<label>启用世界书桥接:</label>
|
||||||
|
<label class="sm-toggle-switch">
|
||||||
|
<input type="checkbox" id="sm-bridge-enabled">
|
||||||
|
<span class="sm-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Relation Tab -->
|
||||||
|
<div id="sm-relation-tab" class="sm-tab-pane">
|
||||||
|
<fieldset class="sm-settings-group">
|
||||||
|
<legend><i class="fas fa-project-diagram"></i> 关联网络 (The Mesh)</legend>
|
||||||
|
<p>关联触发逻辑正在开发中...</p>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
244
core/super-memory/lorebook-bridge.js
Normal file
244
core/super-memory/lorebook-bridge.js
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import { amilyHelper } from "../tavern-helper/main.js";
|
||||||
|
import { extension_settings, getContext } from "/scripts/extensions.js";
|
||||||
|
import { extensionName } from "../../utils/settings.js";
|
||||||
|
import { this_chid, characters } from "/script.js";
|
||||||
|
|
||||||
|
export function getMemoryBookName() {
|
||||||
|
let charName = "Global";
|
||||||
|
const context = getContext();
|
||||||
|
|
||||||
|
if (this_chid !== undefined && characters[this_chid]) {
|
||||||
|
charName = characters[this_chid].name;
|
||||||
|
} else if (context.characterId !== undefined && characters[context.characterId]) {
|
||||||
|
charName = characters[context.characterId].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeCharName = charName.replace(/[<>:"/\\|?*]/g, '_');
|
||||||
|
return `Amily2_Memory_${safeCharName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncToLorebook(tableName, data, indexText, role, headers, rowStatuses, depth = 100) {
|
||||||
|
console.log(`[Amily2-Bridge] 开始同步表格: ${tableName} (Depth: ${depth})`);
|
||||||
|
|
||||||
|
await ensureMemoryBook();
|
||||||
|
|
||||||
|
const bookName = getMemoryBookName();
|
||||||
|
|
||||||
|
let entries = await amilyHelper.getLorebookEntries(bookName);
|
||||||
|
if (!entries) entries = [];
|
||||||
|
|
||||||
|
const entriesToUpdate = [];
|
||||||
|
const entriesToCreate = [];
|
||||||
|
|
||||||
|
const processEntry = (comment, keys, content, type = 'selective', enabled = true) => {
|
||||||
|
const existingEntry = entries.find(e => e.comment === comment);
|
||||||
|
if (existingEntry) {
|
||||||
|
existingEntry.content = content;
|
||||||
|
existingEntry.key = keys;
|
||||||
|
// existingEntry.order = depth; // 【V153.0】不再覆盖用户的深度/排序设置
|
||||||
|
|
||||||
|
if (type === 'constant') {
|
||||||
|
existingEntry.constant = true;
|
||||||
|
} else {
|
||||||
|
existingEntry.constant = false;
|
||||||
|
}
|
||||||
|
if (existingEntry.enabled !== undefined) {
|
||||||
|
existingEntry.enabled = enabled;
|
||||||
|
} else {
|
||||||
|
existingEntry.disable = !enabled;
|
||||||
|
}
|
||||||
|
entriesToUpdate.push(existingEntry);
|
||||||
|
} else {
|
||||||
|
entriesToCreate.push({
|
||||||
|
comment: comment,
|
||||||
|
keys: keys,
|
||||||
|
content: content,
|
||||||
|
type: type,
|
||||||
|
position: 1,
|
||||||
|
order: depth,
|
||||||
|
enabled: enabled
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (role === 'database') {
|
||||||
|
const indexKey = [`Amily_Index_${tableName}`];
|
||||||
|
const indexComment = `[Amily2] Index for ${tableName}`;
|
||||||
|
let indexContent = `【${tableName} 索引】\n${indexText.trim()}`;
|
||||||
|
|
||||||
|
if (tableName.includes("大纲")) {
|
||||||
|
indexContent += `\n<system_note>\n【Amily2-前瞻性预测指令】\n为了实现“提前一轮触发绿灯世界书”的高级记忆回流,请在生成本轮回复时,根据当前剧情走向,**大胆预测并自然提及**下一轮可能出现的索引编码。可自行增加一个标签并使用<!--到-->html注释掉该编码。这将激活系统的预加载机制,确保剧情发展的连贯性。\n</system_note>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
processEntry(indexComment, indexKey, indexContent, 'constant');
|
||||||
|
}
|
||||||
|
|
||||||
|
data.forEach((row, index) => {
|
||||||
|
if (!row || row.length === 0) return;
|
||||||
|
|
||||||
|
const rawVal = row[0];
|
||||||
|
// 【V152.0】修复Falsy检查漏洞 (支持数字0作为主键)
|
||||||
|
if (rawVal === undefined || rawVal === null) return;
|
||||||
|
|
||||||
|
const primaryVal = String(rawVal).trim();
|
||||||
|
if (primaryVal === '') return;
|
||||||
|
|
||||||
|
const isPendingDeletion = rowStatuses && rowStatuses[index] === 'pending-deletion';
|
||||||
|
const isEnabled = !isPendingDeletion;
|
||||||
|
|
||||||
|
const triggerKeys = [primaryVal];
|
||||||
|
const entryComment = `[Amily2] Detail: ${tableName} - ${primaryVal}`;
|
||||||
|
|
||||||
|
let finalHeaders = headers;
|
||||||
|
if (!finalHeaders || finalHeaders.length < row.length) {
|
||||||
|
finalHeaders = [];
|
||||||
|
for(let i=0; i<row.length; i++) {
|
||||||
|
finalHeaders.push((headers && headers[i]) ? headers[i] : `Col_${i}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
const optimizationEnabled = settings.context_optimization_enabled !== false;
|
||||||
|
|
||||||
|
let entryContent;
|
||||||
|
|
||||||
|
if (optimizationEnabled) {
|
||||||
|
const primaryVal = row[0] || 'Unknown';
|
||||||
|
entryContent = `【${tableName}档案: ${primaryVal}】\n`;
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
const key = finalHeaders[i] || `Col_${i}`;
|
||||||
|
const val = row[i] || '';
|
||||||
|
entryContent += `- ${key}: ${val}\n`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let textContent = `【${tableName} 详情】\n`;
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
const key = finalHeaders[i] || `Col_${i}`;
|
||||||
|
const val = row[i] || '';
|
||||||
|
textContent += `- ${key}: ${val}\n`;
|
||||||
|
}
|
||||||
|
entryContent = textContent.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
processEntry(entryComment, triggerKeys, entryContent.trim(), 'selective', isEnabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
const entriesToDelete = [];
|
||||||
|
const tablePrefix = `[Amily2] Detail: ${tableName} -`;
|
||||||
|
|
||||||
|
const activeKeys = new Set();
|
||||||
|
for(const row of data) {
|
||||||
|
// 【V152.0】修复Falsy检查漏洞 (支持数字0作为主键)
|
||||||
|
if(row && row.length > 0) {
|
||||||
|
const rVal = row[0];
|
||||||
|
if (rVal !== undefined && rVal !== null) {
|
||||||
|
const sVal = String(rVal).trim();
|
||||||
|
if (sVal !== '') {
|
||||||
|
activeKeys.add(sVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Amily2-Bridge-GC] ${tableName} 的活跃主键 (Active Keys):`, Array.from(activeKeys));
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.comment && entry.comment.startsWith(tablePrefix)) {
|
||||||
|
const entryKey = entry.comment.substring(tablePrefix.length).trim();
|
||||||
|
|
||||||
|
if (!activeKeys.has(entryKey)) {
|
||||||
|
console.log(`[Amily2-Bridge-GC] 发现残留条目 (将删除): ${entry.comment} (Key: ${entryKey})`);
|
||||||
|
entriesToDelete.push(entry.uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entriesToDelete.length > 0) {
|
||||||
|
console.log(`[Amily2-Bridge] 清理 ${entriesToDelete.length} 个废弃条目...`);
|
||||||
|
await amilyHelper.deleteLorebookEntries(bookName, entriesToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entriesToUpdate.length > 0) {
|
||||||
|
console.log(`[Amily2-Bridge] 更新 ${entriesToUpdate.length} 个条目...`);
|
||||||
|
await amilyHelper.setLorebookEntries(bookName, entriesToUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entriesToCreate.length > 0) {
|
||||||
|
console.log(`[Amily2-Bridge] 创建 ${entriesToCreate.length} 个新条目...`);
|
||||||
|
await amilyHelper.createLorebookEntries(bookName, entriesToCreate);
|
||||||
|
}
|
||||||
|
console.log(`[Amily2-Bridge] 同步完成: ${tableName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ensureMemoryBook() {
|
||||||
|
const bookName = getMemoryBookName();
|
||||||
|
const books = await amilyHelper.getLorebooks();
|
||||||
|
|
||||||
|
if (!books.includes(bookName)) {
|
||||||
|
console.log(`[Amily2-Bridge] 创建角色专用世界书: ${bookName}`);
|
||||||
|
await amilyHelper.createLorebook(bookName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
const shouldBind = settings.superMemory_autoBind === true;
|
||||||
|
|
||||||
|
if (shouldBind && bookName.startsWith("Amily2_Memory_") && bookName !== "Amily2_Memory_Global") {
|
||||||
|
console.log(`[Amily2-Bridge] 自动绑定世界书到当前角色...`);
|
||||||
|
await amilyHelper.bindLorebookToCharacter(bookName);
|
||||||
|
} else if (!shouldBind) {
|
||||||
|
console.log(`[Amily2-Bridge] 跳过自动绑定 (设置已禁用)。请手动在世界书管理中激活: ${bookName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEntryTemplate() {
|
||||||
|
return {
|
||||||
|
uid: Date.now() + Math.floor(Math.random() * 1000),
|
||||||
|
key: [],
|
||||||
|
keysecondary: [],
|
||||||
|
comment: "",
|
||||||
|
content: "",
|
||||||
|
constant: false,
|
||||||
|
selective: true,
|
||||||
|
order: 100,
|
||||||
|
position: 1,
|
||||||
|
enabled: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateTransientHint(hint) {
|
||||||
|
console.log('[Amily2-Bridge] 更新瞬时记忆提示...');
|
||||||
|
await ensureMemoryBook();
|
||||||
|
const bookName = getMemoryBookName();
|
||||||
|
|
||||||
|
const comment = "[Amily2] Active Memory Hint";
|
||||||
|
const content = hint ? `\n<system_note>\n【重要记忆回响】\n${hint}\n</system_note>\n` : "";
|
||||||
|
const enabled = !!hint;
|
||||||
|
|
||||||
|
let entries = await amilyHelper.getLorebookEntries(bookName);
|
||||||
|
if (!entries) entries = [];
|
||||||
|
|
||||||
|
const existingEntry = entries.find(e => e.comment === comment);
|
||||||
|
|
||||||
|
if (existingEntry) {
|
||||||
|
existingEntry.content = content;
|
||||||
|
existingEntry.enabled = enabled;
|
||||||
|
existingEntry.order = 0;
|
||||||
|
existingEntry.constant = true;
|
||||||
|
|
||||||
|
await amilyHelper.setLorebookEntries(bookName, [existingEntry]);
|
||||||
|
} else if (hint) {
|
||||||
|
const newEntry = {
|
||||||
|
comment: comment,
|
||||||
|
keys: [],
|
||||||
|
content: content,
|
||||||
|
constant: true,
|
||||||
|
selective: false,
|
||||||
|
order: 0,
|
||||||
|
position: 0,
|
||||||
|
enabled: true
|
||||||
|
};
|
||||||
|
await amilyHelper.createLorebookEntries(bookName, [newEntry]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Amily2-Bridge] 瞬时记忆提示已${enabled ? '启用' : '清除'}。`);
|
||||||
|
}
|
||||||
1
core/super-memory/manager.js
Normal file
1
core/super-memory/manager.js
Normal file
File diff suppressed because one or more lines are too long
77
core/super-memory/smart-indexer.js
Normal file
77
core/super-memory/smart-indexer.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
export function generateIndex(data, role, tableName = "") {
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = Object.keys(data[0]);
|
||||||
|
if (headers.length === 0) return "";
|
||||||
|
|
||||||
|
const indexColumns = identifyIndexColumns(data, headers);
|
||||||
|
|
||||||
|
let indexLines = [];
|
||||||
|
indexLines.push(`| ${indexColumns.join(' | ')} |`);
|
||||||
|
indexLines.push(`| ${indexColumns.map(() => '---').join(' | ')} |`);
|
||||||
|
|
||||||
|
let processedData = [...data];
|
||||||
|
|
||||||
|
const firstColKey = headers[0];
|
||||||
|
const firstColVal = data[0] ? data[0][firstColKey] : '';
|
||||||
|
const isIndexCol = (firstColKey && (firstColKey.includes('索引') || firstColKey.includes('Index'))) ||
|
||||||
|
(typeof firstColVal === 'string' && /^\s*M\d+/.test(firstColVal)) ||
|
||||||
|
(tableName && (tableName.includes('总结') || tableName.includes('大纲')));
|
||||||
|
|
||||||
|
if (isIndexCol) {
|
||||||
|
processedData.sort((a, b) => {
|
||||||
|
const valA = String(a[firstColKey] || '');
|
||||||
|
const valB = String(b[firstColKey] || '');
|
||||||
|
return valA.localeCompare(valB, undefined, { numeric: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const row of processedData) {
|
||||||
|
const lineParts = indexColumns.map(col => {
|
||||||
|
let val = row[col];
|
||||||
|
if (val === undefined || val === null) return "";
|
||||||
|
val = String(val).trim();
|
||||||
|
if (val.length > 15) val = val.substring(0, 12) + "...";
|
||||||
|
return val;
|
||||||
|
});
|
||||||
|
indexLines.push(`| ${lineParts.join(' | ')} |`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return indexLines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function identifyIndexColumns(data, headers) {
|
||||||
|
if (headers.length <= 2) return headers;
|
||||||
|
|
||||||
|
const candidates = [];
|
||||||
|
const maxColumns = 3;
|
||||||
|
|
||||||
|
for (const header of headers) {
|
||||||
|
if (candidates.length >= maxColumns) break;
|
||||||
|
|
||||||
|
let totalLen = 0;
|
||||||
|
let count = 0;
|
||||||
|
for (const row of data) {
|
||||||
|
if (row[header]) {
|
||||||
|
totalLen += String(row[header]).length;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const avgLen = count > 0 ? totalLen / count : 0;
|
||||||
|
|
||||||
|
const isLongText = avgLen > 20;
|
||||||
|
const isBlacklisted = /desc|bio|detail|history|经历|描述|详情/i.test(header);
|
||||||
|
|
||||||
|
if (!isLongText && !isBlacklisted) {
|
||||||
|
candidates.push(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
return headers.slice(0, Math.min(headers.length, maxColumns));
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
@@ -90,15 +90,18 @@ export async function injectTableData(chat, contextSize, abort, type) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
let injectionContent = generateTableContent();
|
||||||
|
|
||||||
if (!settings.table_injection_enabled) {
|
if (!settings.table_injection_enabled) {
|
||||||
setExtensionPrompt(INJECTION_KEY, '', 0, 0, false, 'SYSTEM');
|
setExtensionPrompt(INJECTION_KEY, '', 0, 0, false, 'SYSTEM');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (!injectionContent || injectionContent.trim() === '') {
|
||||||
const injectionContent = generateTableContent();
|
// 理论上不会走到这里,除非宏都没了
|
||||||
|
|
||||||
if (!injectionContent) {
|
|
||||||
setExtensionPrompt(INJECTION_KEY, '', 0, 0, false, 'SYSTEM');
|
setExtensionPrompt(INJECTION_KEY, '', 0, 0, false, 'SYSTEM');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -96,9 +96,47 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let textToProcess = latestMessage.mes;
|
// --- 延迟填表逻辑 (V151.0) ---
|
||||||
|
const delay = parseInt(settings.secondary_filler_delay || 0, 10);
|
||||||
|
const chat = context.chat;
|
||||||
|
let targetMessage;
|
||||||
|
let targetIndex;
|
||||||
|
|
||||||
|
if (delay > 0) {
|
||||||
|
// 如果有延迟,我们需要找到“延迟前”的那条消息
|
||||||
|
// chat.length - 1 是当前最新消息的索引
|
||||||
|
// 目标索引 = (chat.length - 1) - delay
|
||||||
|
targetIndex = (chat.length - 1) - delay;
|
||||||
|
|
||||||
|
if (targetIndex < 0) {
|
||||||
|
console.log(`[Amily2-副API] 延迟模式(${delay}): 历史楼层不足,跳过填表。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetMessage = chat[targetIndex];
|
||||||
|
|
||||||
|
// 检查目标消息是否是AI消息(通常填表针对AI回复)
|
||||||
|
// 如果目标消息是用户的消息,而我们只想填AI的表,这可能是一个问题。
|
||||||
|
// 但如果用户设置了延迟,他们可能期望每隔几层填一次,或者只填AI层。
|
||||||
|
// 现有的 `fillWithSecondaryApi` 是在 `CHAT_COMPLETION` 后调用的,此时最新消息通常是AI消息。
|
||||||
|
// 如果延迟是奇数(例如1),目标消息可能是用户消息。
|
||||||
|
// 假设延迟是偶数(例如2),目标消息是上一条AI消息。
|
||||||
|
|
||||||
|
// 为了安全起见,如果目标消息是用户消息,我们可能应该跳过?或者依然填表(记录用户消息的表)?
|
||||||
|
// 目前表系统通常绑定在AI回复上。
|
||||||
|
// 如果 targetMessage.is_user,我们尝试往回找最近的一条AI消息?
|
||||||
|
// 不,这会乱套。严格按照楼层索引来。
|
||||||
|
|
||||||
|
console.log(`[Amily2-副API] 延迟模式生效: 当前总楼层 ${chat.length}, 延迟 ${delay}, 目标楼层索引 ${targetIndex}`);
|
||||||
|
} else {
|
||||||
|
// 无延迟,使用传入的最新消息
|
||||||
|
targetMessage = latestMessage;
|
||||||
|
targetIndex = chat.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let textToProcess = targetMessage.mes;
|
||||||
if (!textToProcess || !textToProcess.trim()) {
|
if (!textToProcess || !textToProcess.trim()) {
|
||||||
console.log("[Amily2-副API] 消息内容为空,跳过填表任务。");
|
console.log("[Amily2-副API] 目标消息内容为空,跳过填表任务。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,15 +158,15 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = getContext();
|
|
||||||
const userName = context.name1 || '用户';
|
const userName = context.name1 || '用户';
|
||||||
const characterName = context.name2 || '角色';
|
const characterName = context.name2 || '角色';
|
||||||
|
|
||||||
const chat = context.chat;
|
// 寻找目标消息之前的最后一条用户消息
|
||||||
|
|
||||||
let lastUserMessage = null;
|
let lastUserMessage = null;
|
||||||
let lastUserMessageIndex = -1;
|
let lastUserMessageIndex = -1;
|
||||||
for (let i = chat.length - 2; i >= 0; i--) {
|
|
||||||
|
// 从 targetIndex - 1 开始往前找
|
||||||
|
for (let i = targetIndex - 1; i >= 0; i--) {
|
||||||
if (chat[i].is_user) {
|
if (chat[i].is_user) {
|
||||||
lastUserMessage = chat[i];
|
lastUserMessage = chat[i];
|
||||||
lastUserMessageIndex = i;
|
lastUserMessageIndex = i;
|
||||||
@@ -136,8 +174,8 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentInteractionContent = (lastUserMessage ? `${userName}(用户)最新消息:${lastUserMessage.mes}\n` : '') +
|
const currentInteractionContent = (lastUserMessage ? `${userName}(用户)消息:${lastUserMessage.mes}\n` : '') +
|
||||||
`${characterName}(AI)最新消息,[核心处理内容]:${textToProcess}`;
|
`${characterName}(AI)消息,[核心处理内容]:${textToProcess}`;
|
||||||
|
|
||||||
let mixedOrder;
|
let mixedOrder;
|
||||||
try {
|
try {
|
||||||
@@ -185,7 +223,10 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
const historyMessagesToGet = contextReadingLevel > 2 ? contextReadingLevel - 2 : 0;
|
const historyMessagesToGet = contextReadingLevel > 2 ? contextReadingLevel - 2 : 0;
|
||||||
|
|
||||||
if (historyMessagesToGet > 0) {
|
if (historyMessagesToGet > 0) {
|
||||||
const historyEndIndex = lastUserMessageIndex !== -1 ? lastUserMessageIndex : chat.length - 1;
|
// 这里的 historyEndIndex 应该是我们上面计算出的 lastUserMessageIndex
|
||||||
|
// 如果没找到用户消息,则使用 targetIndex - 1
|
||||||
|
const historyEndIndex = lastUserMessageIndex !== -1 ? lastUserMessageIndex : Math.max(0, targetIndex - 1);
|
||||||
|
|
||||||
const historyContext = await getHistoryContext(historyMessagesToGet, historyEndIndex, tagsToExtract, exclusionRules);
|
const historyContext = await getHistoryContext(historyMessagesToGet, historyEndIndex, tagsToExtract, exclusionRules);
|
||||||
if (historyContext) {
|
if (historyContext) {
|
||||||
messages.push({ role: "system", content: historyContext });
|
messages.push({ role: "system", content: historyContext });
|
||||||
@@ -205,12 +246,10 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fillingMode = settings.filling_mode || 'main-api';
|
|
||||||
if (fillingMode === 'secondary-api') {
|
|
||||||
console.groupCollapsed(`[Amily2 分步填表] 即将发送至 API 的内容`);
|
console.groupCollapsed(`[Amily2 分步填表] 即将发送至 API 的内容`);
|
||||||
|
console.log("发送给AI的提示词: ", JSON.stringify(messages, null, 2));
|
||||||
console.dir(messages);
|
console.dir(messages);
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
}
|
|
||||||
|
|
||||||
let rawContent;
|
let rawContent;
|
||||||
if (settings.nccsEnabled) {
|
if (settings.nccsEnabled) {
|
||||||
@@ -230,14 +269,20 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false) {
|
|||||||
|
|
||||||
updateTableFromText(rawContent);
|
updateTableFromText(rawContent);
|
||||||
|
|
||||||
const currentContext = getContext();
|
// 保存到目标消息
|
||||||
if (currentContext.chat && currentContext.chat.length > 0) {
|
if (saveStateToMessage(getMemoryState(), targetMessage)) {
|
||||||
const lastMessage = currentContext.chat[currentContext.chat.length - 1];
|
// 如果目标消息不是最新消息,我们可能需要重新渲染整个聊天记录或者特定消息的表格?
|
||||||
if (saveStateToMessage(getMemoryState(), lastMessage)) {
|
// renderTables() 通常重新渲染所有可见表格
|
||||||
renderTables();
|
renderTables();
|
||||||
|
// updateOrInsertTableInChat 通常插入到DOM中
|
||||||
|
// 我们可能需要传递 targetIndex 给 updateOrInsertTableInChat 吗?
|
||||||
|
// 目前 updateOrInsertTableInChat 似乎是查找 .mes_text 并插入。
|
||||||
|
// 如果我们更新了历史消息的数据,我们需要确保 DOM 也更新。
|
||||||
|
// 由于 SillyTavern 的消息渲染机制,如果消息已经在屏幕上,仅仅修改数据可能不会自动更新 DOM。
|
||||||
|
// 但是 renderTables() 应该会处理这个。
|
||||||
updateOrInsertTableInChat();
|
updateOrInsertTableInChat();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
saveChat();
|
saveChat();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -147,4 +147,12 @@ export const tableSystemDefaultSettings = {
|
|||||||
batch_filler_flow_template: DEFAULT_AI_FLOW_TEMPLATE,
|
batch_filler_flow_template: DEFAULT_AI_FLOW_TEMPLATE,
|
||||||
|
|
||||||
filling_mode: 'main-api',
|
filling_mode: 'main-api',
|
||||||
|
context_optimization_enabled: true, // 【V144.0】上下文优化(世界书合并)开关
|
||||||
|
|
||||||
|
// 【V146.5】分步填表相关设置
|
||||||
|
context_reading_level: 4,
|
||||||
|
secondary_filler_delay: 0,
|
||||||
|
table_independent_rules_enabled: false,
|
||||||
|
table_tags_to_extract: '',
|
||||||
|
table_exclusion_rules: [],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ import {
|
|||||||
name2,
|
name2,
|
||||||
addOneMessage,
|
addOneMessage,
|
||||||
messageFormatting,
|
messageFormatting,
|
||||||
substituteParamsExtended
|
substituteParamsExtended,
|
||||||
|
saveCharacterDebounced,
|
||||||
|
this_chid
|
||||||
} from "/script.js";
|
} from "/script.js";
|
||||||
import { getContext } from "/scripts/extensions.js";
|
import { getContext } from "/scripts/extensions.js";
|
||||||
import { executeSlashCommandsWithOptions } from '/scripts/slash-commands.js';
|
import { executeSlashCommandsWithOptions } from '/scripts/slash-commands.js';
|
||||||
@@ -430,6 +432,7 @@ class AmilyHelper {
|
|||||||
existingEntry.position = positionMap[entryUpdate.position] ?? 4;
|
existingEntry.position = positionMap[entryUpdate.position] ?? 4;
|
||||||
}
|
}
|
||||||
if (entryUpdate.depth !== undefined) existingEntry.depth = entryUpdate.depth;
|
if (entryUpdate.depth !== undefined) existingEntry.depth = entryUpdate.depth;
|
||||||
|
if (entryUpdate.scanDepth !== undefined) existingEntry.scanDepth = entryUpdate.scanDepth;
|
||||||
if (entryUpdate.order !== undefined) existingEntry.order = entryUpdate.order;
|
if (entryUpdate.order !== undefined) existingEntry.order = entryUpdate.order;
|
||||||
if (entryUpdate.exclude_recursion !== undefined) existingEntry.excludeRecursion = entryUpdate.exclude_recursion;
|
if (entryUpdate.exclude_recursion !== undefined) existingEntry.excludeRecursion = entryUpdate.exclude_recursion;
|
||||||
if (entryUpdate.prevent_recursion !== undefined) existingEntry.preventRecursion = entryUpdate.prevent_recursion;
|
if (entryUpdate.prevent_recursion !== undefined) existingEntry.preventRecursion = entryUpdate.prevent_recursion;
|
||||||
@@ -474,6 +477,7 @@ class AmilyHelper {
|
|||||||
constant: newEntryData.type === 'constant' ? true : (newEntryData.constant || false),
|
constant: newEntryData.type === 'constant' ? true : (newEntryData.constant || false),
|
||||||
position: typeof newEntryData.position === 'string' ? (positionMap[newEntryData.position] ?? 4) : (newEntryData.position ?? 4),
|
position: typeof newEntryData.position === 'string' ? (positionMap[newEntryData.position] ?? 4) : (newEntryData.position ?? 4),
|
||||||
depth: newEntryData.depth ?? 998,
|
depth: newEntryData.depth ?? 998,
|
||||||
|
scanDepth: newEntryData.scanDepth ?? null,
|
||||||
disable: !(newEntryData.enabled ?? true),
|
disable: !(newEntryData.enabled ?? true),
|
||||||
});
|
});
|
||||||
if (newEntryData.type === 'selective') newEntry.constant = false;
|
if (newEntryData.type === 'selective') newEntry.constant = false;
|
||||||
@@ -487,6 +491,34 @@ class AmilyHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteLorebookEntries(bookName, uids) {
|
||||||
|
try {
|
||||||
|
const bookData = await loadWorldInfo(bookName);
|
||||||
|
if (!bookData || !bookData.entries) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let deletedCount = 0;
|
||||||
|
for (const uid of uids) {
|
||||||
|
if (bookData.entries[uid]) {
|
||||||
|
delete bookData.entries[uid];
|
||||||
|
deletedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deletedCount > 0) {
|
||||||
|
await saveWorldInfo(bookName, bookData, true);
|
||||||
|
reloadEditor(bookName);
|
||||||
|
console.log(`[Amily助手] 已从世界书《${bookName}》删除 ${deletedCount} 个条目`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[Amily助手] 删除世界书《${bookName}》条目时出错:`, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async createLorebook(bookName) {
|
async createLorebook(bookName) {
|
||||||
try {
|
try {
|
||||||
if (world_names.includes(bookName)) {
|
if (world_names.includes(bookName)) {
|
||||||
@@ -535,6 +567,42 @@ class AmilyHelper {
|
|||||||
getLastMessageId() {
|
getLastMessageId() {
|
||||||
return chat.length - 1;
|
return chat.length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定世界书绑定到当前角色
|
||||||
|
* @param {string} bookName 世界书名称
|
||||||
|
*/
|
||||||
|
async bindLorebookToCharacter(bookName) {
|
||||||
|
if (this_chid === undefined || !characters[this_chid]) {
|
||||||
|
console.warn('[Amily助手] 无法绑定世界书:未选中角色');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char = characters[this_chid];
|
||||||
|
if (!char.data) char.data = {};
|
||||||
|
if (!char.data.extensions) char.data.extensions = {};
|
||||||
|
|
||||||
|
// 确保 world 字段是数组
|
||||||
|
let worlds = char.data.extensions.world;
|
||||||
|
if (!Array.isArray(worlds)) {
|
||||||
|
worlds = worlds ? [worlds] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!worlds.includes(bookName)) {
|
||||||
|
worlds.push(bookName);
|
||||||
|
char.data.extensions.world = worlds;
|
||||||
|
console.log(`[Amily助手] 已将世界书《${bookName}》绑定到角色 ${char.name}`);
|
||||||
|
|
||||||
|
if (typeof saveCharacterDebounced === 'function') {
|
||||||
|
saveCharacterDebounced();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.warn('[Amily助手] 无法保存角色数据:saveCharacterDebounced 不可用');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // 已经绑定
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const amilyHelper = new AmilyHelper();
|
export const amilyHelper = new AmilyHelper();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "Amily2号聊天优化助手",
|
"name": "Amily2号聊天优化助手",
|
||||||
"display_name": "Amily2号助手",
|
"display_name": "Amily2号助手",
|
||||||
"version": "1.6.4",
|
"version": "1.6.8",
|
||||||
"author": "Wx-2025",
|
"author": "Wx-2025",
|
||||||
"description": "一个拥有独立UI的智能引擎,正文优化、自动总结、记忆表格、rag向量、隐藏楼层、剧情推进六大功能整合。",
|
"description": "一个拥有独立UI的智能引擎,正文优化、自动总结、记忆表格、rag向量、隐藏楼层、剧情推进等多功能整合。",
|
||||||
"minSillyTavernVersion": "1.10.0",
|
"minSillyTavernVersion": "1.10.0",
|
||||||
"requires": [],
|
"requires": [],
|
||||||
"homePage": "https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git",
|
"homePage": "https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git",
|
||||||
@@ -32,6 +32,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -629,6 +629,26 @@ export function bindModalEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
container
|
||||||
|
.off("click.amily2.reset_auth")
|
||||||
|
.on("click.amily2.reset_auth", "#amily2_reset_auth", function() {
|
||||||
|
if (!pluginAuthStatus.authorized) return;
|
||||||
|
|
||||||
|
if (confirm("确定要清除本地授权码吗?\n这将使您的授权失效,需要重新验证。\n\n这通常用于:\n1. 升级为高级用户\n2. 解决授权异常问题")) {
|
||||||
|
localStorage.removeItem("plugin_auth_code");
|
||||||
|
localStorage.removeItem("plugin_activated");
|
||||||
|
localStorage.removeItem("plugin_auto_login");
|
||||||
|
localStorage.removeItem("plugin_user_type");
|
||||||
|
localStorage.removeItem("plugin_valid_until");
|
||||||
|
|
||||||
|
toastr.success("授权已清除,即将重新加载以生效...", "Amily2号");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
container
|
container
|
||||||
.off("click.amily2.update")
|
.off("click.amily2.update")
|
||||||
.on("click.amily2.update", "#amily2_update_button", function() {
|
.on("click.amily2.update", "#amily2_update_button", function() {
|
||||||
@@ -705,7 +725,7 @@ export function bindModalEvents() {
|
|||||||
container
|
container
|
||||||
.off("click.amily2.chamber_nav")
|
.off("click.amily2.chamber_nav")
|
||||||
.on("click.amily2.chamber_nav",
|
.on("click.amily2.chamber_nav",
|
||||||
"#amily2_open_plot_optimization, #amily2_open_additional_features, #amily2_open_rag_palace, #amily2_open_memorisation_forms, #amily2_open_character_world_book, #amily2_open_world_editor, #amily2_open_glossary, #amily2_open_renderer, #amily2_back_to_main_settings, #amily2_back_to_main_from_hanlinyuan, #amily2_back_to_main_from_forms, #amily2_back_to_main_from_optimization, #amily2_back_to_main_from_cwb, #amily2_back_to_main_from_world_editor, #amily2_back_to_main_from_glossary, #amily2_renderer_back_button", function () {
|
"#amily2_open_plot_optimization, #amily2_open_additional_features, #amily2_open_rag_palace, #amily2_open_memorisation_forms, #amily2_open_character_world_book, #amily2_open_world_editor, #amily2_open_glossary, #amily2_open_renderer, #amily2_open_super_memory, #amily2_back_to_main_settings, #amily2_back_to_main_from_hanlinyuan, #amily2_back_to_main_from_forms, #amily2_back_to_main_from_optimization, #amily2_back_to_main_from_cwb, #amily2_back_to_main_from_world_editor, #amily2_back_to_main_from_glossary, #amily2_renderer_back_button, #amily2_back_to_main_from_super_memory", function () {
|
||||||
if (!pluginAuthStatus.authorized) return;
|
if (!pluginAuthStatus.authorized) return;
|
||||||
|
|
||||||
const mainPanel = container.find('.plugin-features');
|
const mainPanel = container.find('.plugin-features');
|
||||||
@@ -717,6 +737,7 @@ container
|
|||||||
const worldEditorPanel = container.find('#amily2_world_editor_panel');
|
const worldEditorPanel = container.find('#amily2_world_editor_panel');
|
||||||
const glossaryPanel = container.find('#amily2_glossary_panel');
|
const glossaryPanel = container.find('#amily2_glossary_panel');
|
||||||
const rendererPanel = container.find('#amily2_renderer_panel');
|
const rendererPanel = container.find('#amily2_renderer_panel');
|
||||||
|
const superMemoryPanel = container.find('#amily2_super_memory_panel');
|
||||||
|
|
||||||
mainPanel.hide();
|
mainPanel.hide();
|
||||||
additionalPanel.hide();
|
additionalPanel.hide();
|
||||||
@@ -727,8 +748,18 @@ container
|
|||||||
worldEditorPanel.hide();
|
worldEditorPanel.hide();
|
||||||
glossaryPanel.hide();
|
glossaryPanel.hide();
|
||||||
rendererPanel.hide();
|
rendererPanel.hide();
|
||||||
|
superMemoryPanel.hide();
|
||||||
|
|
||||||
switch (this.id) {
|
switch (this.id) {
|
||||||
|
case 'amily2_open_super_memory':
|
||||||
|
const userType = parseInt(localStorage.getItem("plugin_user_type") || "0");
|
||||||
|
if (userType < 2) {
|
||||||
|
toastr.warning("此功能为内测功能,仅限我看顺眼的用户使用。", "权限不足");
|
||||||
|
mainPanel.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
superMemoryPanel.show();
|
||||||
|
break;
|
||||||
case 'amily2_open_renderer':
|
case 'amily2_open_renderer':
|
||||||
rendererPanel.show();
|
rendererPanel.show();
|
||||||
break;
|
break;
|
||||||
@@ -761,6 +792,7 @@ container
|
|||||||
case 'amily2_back_to_main_from_world_editor':
|
case 'amily2_back_to_main_from_world_editor':
|
||||||
case 'amily2_back_to_main_from_glossary':
|
case 'amily2_back_to_main_from_glossary':
|
||||||
case 'amily2_renderer_back_button':
|
case 'amily2_renderer_back_button':
|
||||||
|
case 'amily2_back_to_main_from_super_memory':
|
||||||
mainPanel.show();
|
mainPanel.show();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { bindHanlinyuanEvents } from "./hanlinyuan-bindings.js";
|
|||||||
import { bindTableEvents } from './table-bindings.js';
|
import { bindTableEvents } from './table-bindings.js';
|
||||||
import { showContentModal } from "./page-window.js";
|
import { showContentModal } from "./page-window.js";
|
||||||
import { initializeRendererBindings } from "../core/tavern-helper/renderer-bindings.js";
|
import { initializeRendererBindings } from "../core/tavern-helper/renderer-bindings.js";
|
||||||
|
import { bindSuperMemoryEvents } from "../core/super-memory/bindings.js";
|
||||||
const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`;
|
const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`;
|
||||||
|
|
||||||
|
|
||||||
@@ -106,6 +107,10 @@ async function initializePanel(contentPanel, errorContainer) {
|
|||||||
const rendererPanelHtml = `<div id="amily2_renderer_panel" style="display: none;">${rendererContent}</div>`;
|
const rendererPanelHtml = `<div id="amily2_renderer_panel" style="display: none;">${rendererContent}</div>`;
|
||||||
mainContainer.append(rendererPanelHtml);
|
mainContainer.append(rendererPanelHtml);
|
||||||
|
|
||||||
|
const superMemoryContent = await $.get(`${extensionFolderPath}/core/super-memory/index.html`);
|
||||||
|
const superMemoryPanelHtml = `<div id="amily2_super_memory_panel" style="display: none;">${superMemoryContent}</div>`;
|
||||||
|
mainContainer.append(superMemoryPanelHtml);
|
||||||
|
|
||||||
// 在面板创建后,加载世界书编辑器脚本
|
// 在面板创建后,加载世界书编辑器脚本
|
||||||
const worldEditorScriptId = 'world-editor-script';
|
const worldEditorScriptId = 'world-editor-script';
|
||||||
if (!document.getElementById(worldEditorScriptId)) {
|
if (!document.getElementById(worldEditorScriptId)) {
|
||||||
@@ -123,6 +128,7 @@ async function initializePanel(contentPanel, errorContainer) {
|
|||||||
bindHanlinyuanEvents();
|
bindHanlinyuanEvents();
|
||||||
bindTableEvents();
|
bindTableEvents();
|
||||||
initializeRendererBindings();
|
initializeRendererBindings();
|
||||||
|
bindSuperMemoryEvents();
|
||||||
contentPanel.data("initialized", true);
|
contentPanel.data("initialized", true);
|
||||||
console.log("[Amily-重构] 宫殿模块已按蓝图竣工。");
|
console.log("[Amily-重构] 宫殿模块已按蓝图竣工。");
|
||||||
applyUpdateIndicator();
|
applyUpdateIndicator();
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -10,7 +10,8 @@ import { applyExclusionRules, extractBlocksByTags } from '../core/utils/rag-tag-
|
|||||||
import {
|
import {
|
||||||
getAvailableWorldbooks, getLoresForWorldbook,
|
getAvailableWorldbooks, getLoresForWorldbook,
|
||||||
executeManualSummary, executeRefinement,
|
executeManualSummary, executeRefinement,
|
||||||
executeExpedition, stopExpedition
|
executeExpedition, stopExpedition,
|
||||||
|
archiveCurrentLedger, getArchivedLedgers, restoreArchivedLedger
|
||||||
} from "../core/historiographer.js";
|
} from "../core/historiographer.js";
|
||||||
|
|
||||||
import { getNgmsApiSettings, testNgmsApiConnection, fetchNgmsModels } from "../core/api/Ngms_api.js";
|
import { getNgmsApiSettings, testNgmsApiConnection, fetchNgmsModels } from "../core/api/Ngms_api.js";
|
||||||
@@ -265,6 +266,51 @@ export function bindHistoriographyEvents() {
|
|||||||
|
|
||||||
updateExpeditionButtonUI('idle');
|
updateExpeditionButtonUI('idle');
|
||||||
|
|
||||||
|
// ========== 📚 史册归档与回溯 绑定 ==========
|
||||||
|
const archiveCurrentBtn = document.getElementById("amily2_mhb_archive_current");
|
||||||
|
const archiveSelector = document.getElementById("amily2_mhb_archive_selector");
|
||||||
|
const refreshArchivesBtn = document.getElementById("amily2_mhb_refresh_archives");
|
||||||
|
const restoreArchiveBtn = document.getElementById("amily2_mhb_restore_archive");
|
||||||
|
|
||||||
|
const updateArchiveList = async () => {
|
||||||
|
archiveSelector.innerHTML = '<option value="">正在翻阅旧档...</option>';
|
||||||
|
const archives = await getArchivedLedgers();
|
||||||
|
archiveSelector.innerHTML = ""; // 清空
|
||||||
|
if (archives && archives.length > 0) {
|
||||||
|
archives.forEach((arch) => {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = arch.key;
|
||||||
|
option.textContent = arch.comment;
|
||||||
|
archiveSelector.appendChild(option);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
archiveSelector.innerHTML = '<option value="">未发现归档史册</option>';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
archiveCurrentBtn.addEventListener("click", async () => {
|
||||||
|
if (confirm("确定要归档当前的【对话流水总帐】并停用它吗?\n这将允许您开始一段全新的历史记录。")) {
|
||||||
|
const success = await archiveCurrentLedger();
|
||||||
|
if (success) {
|
||||||
|
updateArchiveList(); // 归档成功后刷新列表
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshArchivesBtn.addEventListener("click", updateArchiveList);
|
||||||
|
|
||||||
|
restoreArchiveBtn.addEventListener("click", async () => {
|
||||||
|
const selectedKey = archiveSelector.value;
|
||||||
|
if (!selectedKey) {
|
||||||
|
toastr.warning("请先选择一个要回溯的史册!", "圣谕不明");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (confirm("确定要回溯选中的史册吗?\n当前的活跃史册(如果有)将被自动归档。")) {
|
||||||
|
await restoreArchivedLedger(selectedKey);
|
||||||
|
updateArchiveList(); // 回溯后刷新列表
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ========== 💎 宏史卷 (史册精炼) 绑定 ==========
|
// ========== 💎 宏史卷 (史册精炼) 绑定 ==========
|
||||||
const largeWbSelector = document.getElementById(
|
const largeWbSelector = document.getElementById(
|
||||||
"amily2_mhb_large_worldbook_selector",
|
"amily2_mhb_large_worldbook_selector",
|
||||||
|
|||||||
@@ -120,12 +120,22 @@ export function showHtmlModal(title, htmlContent, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (!text) return '';
|
||||||
|
return text
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
export function showSummaryModal(summaryText, callbacks) {
|
export function showSummaryModal(summaryText, callbacks) {
|
||||||
const { onConfirm, onRegenerate, onCancel } = callbacks;
|
const { onConfirm, onRegenerate, onCancel } = callbacks;
|
||||||
|
|
||||||
const modalHtml = `
|
const modalHtml = `
|
||||||
<div class="historiographer-summary-modal">
|
<div class="historiographer-summary-modal">
|
||||||
<textarea class="text_pole" style="width: 100%; height: 50vh; resize: vertical;">${summaryText}</textarea>
|
<textarea class="text_pole" style="width: 100%; height: 50vh; resize: vertical;">${escapeHtml(summaryText)}</textarea>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
2114
ui/table-bindings.js
2114
ui/table-bindings.js
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -19,6 +19,10 @@ export const defaultSettings = {
|
|||||||
contextMessages: 2,
|
contextMessages: 2,
|
||||||
promptPresets: [],
|
promptPresets: [],
|
||||||
lastUsedPresetName: '',
|
lastUsedPresetName: '',
|
||||||
|
super_memory_enabled: false, // 【V150.0】Amily2 Super Memory 总开关 (Default OFF)
|
||||||
|
superMemory_bridgeEnabled: false, // 【V150.0】世界书桥接 (Default OFF)
|
||||||
|
superMemory_autoBind: false, // 【V151.9】是否自动绑定到角色 (Default OFF)
|
||||||
|
secondary_filler_delay: 0, // 【V151.0】分步填表延迟
|
||||||
plotOpt_enabled: false,
|
plotOpt_enabled: false,
|
||||||
plotOpt_max_tokens: 20000,
|
plotOpt_max_tokens: 20000,
|
||||||
plotOpt_temperature: 0.7,
|
plotOpt_temperature: 0.7,
|
||||||
|
|||||||
Reference in New Issue
Block a user