feat(chat): refactor chat message handling and introduce private messaging support
- Updated `uni_message_sender.py` to allow for private messaging by removing the mandatory group ID and adding user ID handling. - Enhanced chat history retrieval and clearing functions in `routes.py` and `service.py` to support both group and private chat scenarios. - Introduced a new `ChatScrollContext` for managing message scrolling and highlighting in the chat UI. - Created a `ListItemEditorHookFactory` for rendering a rich UI editor for list items in configuration settings, replacing the previous JSON text display. - Improved message serialization for consistent display in chat history. - Added detailed logging for chat history operations and error handling. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -1,15 +1,98 @@
|
||||
import { createJsonFieldHook } from './JsonFieldHookFactory'
|
||||
import { createListItemEditorHook } from './ListItemEditorHookFactory'
|
||||
|
||||
export const ChatTalkValueRulesHook = createJsonFieldHook({
|
||||
emptyValue: [],
|
||||
helperText: '复杂对象数组使用 JSON 编辑。每一项对应一个聊天频率规则对象。',
|
||||
placeholder: '[\n {\n "platform": "",\n "item_id": "",\n "rule_type": "group",\n "time": "00:00-23:59",\n "value": 1.0\n }\n]',
|
||||
const ruleTypeLabel = (rule: unknown) => {
|
||||
if (rule === 'private') return '私聊'
|
||||
if (rule === 'group') return '群聊'
|
||||
return rule ? String(rule) : '未指定'
|
||||
}
|
||||
|
||||
const platformLabel = (item: Record<string, unknown>) => {
|
||||
const platform = typeof item.platform === 'string' ? item.platform.trim() : ''
|
||||
const itemId = typeof item.item_id === 'string' ? item.item_id.trim() : ''
|
||||
if (!platform && !itemId) return '全局'
|
||||
if (!platform) return itemId
|
||||
if (!itemId) return platform
|
||||
return `${platform}:${itemId}`
|
||||
}
|
||||
|
||||
const truncate = (text: string, max = 32) => {
|
||||
if (text.length <= max) return text
|
||||
return `${text.slice(0, max)}…`
|
||||
}
|
||||
|
||||
const collectStringList = (value: unknown): string[] => {
|
||||
if (!Array.isArray(value)) return []
|
||||
return value
|
||||
.map((item) => (typeof item === 'string' ? item.trim() : ''))
|
||||
.filter((item) => item.length > 0)
|
||||
}
|
||||
|
||||
export const ChatTalkValueRulesHook = createListItemEditorHook({
|
||||
addLabel: '添加发言频率规则',
|
||||
helperText: '可按平台/聊天流/时段分别配置发言频率,留空表示全局。',
|
||||
emptyText: '尚未配置任何规则,将使用全局默认频率。',
|
||||
itemTitle: (item) => {
|
||||
const time =
|
||||
typeof item.time === 'string' && item.time.trim()
|
||||
? item.time.trim()
|
||||
: '全天'
|
||||
const value =
|
||||
typeof item.value === 'number' ? item.value.toFixed(2) : '—'
|
||||
return `${platformLabel(item)} · ${ruleTypeLabel(item.rule_type)} · ${time} · 频率 ${value}`
|
||||
},
|
||||
})
|
||||
|
||||
export const ExpressionLearningListHook = createJsonFieldHook({
|
||||
emptyValue: [],
|
||||
helperText: '表达学习配置较复杂,使用 JSON 编辑更稳妥。每一项对应一个学习规则。',
|
||||
placeholder: '[\n {\n "platform": "",\n "item_id": "",\n "rule_type": "group",\n "use_expression": true,\n "enable_learning": true,\n "enable_jargon_learning": true\n }\n]',
|
||||
export const ExpressionLearningListHook = createListItemEditorHook({
|
||||
addLabel: '添加表达学习规则',
|
||||
helperText: '为不同聊天流单独配置是否启用表达/jargon 学习。',
|
||||
emptyText: '尚未配置任何学习规则。',
|
||||
itemTitle: (item) => {
|
||||
const flags: string[] = []
|
||||
if (item.use_expression) flags.push('表达')
|
||||
if (item.enable_learning) flags.push('优化学习')
|
||||
if (item.enable_jargon_learning) flags.push('jargon')
|
||||
const flagText = flags.length ? flags.join(' / ') : '全部关闭'
|
||||
return `${platformLabel(item)} · ${ruleTypeLabel(item.rule_type)} · ${flagText}`
|
||||
},
|
||||
})
|
||||
|
||||
export const KeywordRulesHook = createListItemEditorHook({
|
||||
addLabel: '添加关键词规则',
|
||||
helperText: '匹配命中后会用 reaction 内容作为额外上下文。keywords 至少填一条,或使用正则模式。',
|
||||
emptyText: '尚未添加任何关键词规则。',
|
||||
itemTitle: (item) => {
|
||||
const keywords = collectStringList(item.keywords)
|
||||
const regex = collectStringList(item.regex)
|
||||
const reaction =
|
||||
typeof item.reaction === 'string' ? item.reaction.trim() : ''
|
||||
const left = keywords.length
|
||||
? `关键词 ${keywords.length} 条`
|
||||
: regex.length
|
||||
? `正则 ${regex.length} 条`
|
||||
: '未配置匹配项'
|
||||
const right = reaction ? `→ ${truncate(reaction)}` : '→ 未填写反应'
|
||||
return `${left} ${right}`
|
||||
},
|
||||
})
|
||||
|
||||
export const RegexRulesHook = createListItemEditorHook({
|
||||
addLabel: '添加正则规则',
|
||||
helperText: '正则模式按 Python 语法编写,命中时把 reaction 作为提示注入。',
|
||||
emptyText: '尚未添加任何正则规则。',
|
||||
itemTitle: (item) => {
|
||||
const regex = collectStringList(item.regex)
|
||||
const keywords = collectStringList(item.keywords)
|
||||
const reaction =
|
||||
typeof item.reaction === 'string' ? item.reaction.trim() : ''
|
||||
const left = regex.length
|
||||
? `正则 ${regex.length} 条`
|
||||
: keywords.length
|
||||
? `关键词 ${keywords.length} 条`
|
||||
: '未配置匹配项'
|
||||
const right = reaction ? `→ ${truncate(reaction)}` : '→ 未填写反应'
|
||||
return `${left} ${right}`
|
||||
},
|
||||
})
|
||||
|
||||
export const ExpressionGroupsHook = createJsonFieldHook({
|
||||
@@ -24,18 +107,6 @@ export const ExperimentalChatPromptsHook = createJsonFieldHook({
|
||||
placeholder: '[\n {\n "platform": "qq",\n "item_id": "123456",\n "rule_type": "group",\n "prompt": "这里填写额外提示词"\n }\n]',
|
||||
})
|
||||
|
||||
export const KeywordRulesHook = createJsonFieldHook({
|
||||
emptyValue: [],
|
||||
helperText: '关键词规则为对象数组,建议直接编辑 JSON。',
|
||||
placeholder: '[\n {\n "keywords": ["早安"],\n "regex": [],\n "reaction": "早安呀"\n }\n]',
|
||||
})
|
||||
|
||||
export const RegexRulesHook = createJsonFieldHook({
|
||||
emptyValue: [],
|
||||
helperText: '正则规则为对象数组,建议直接编辑 JSON。',
|
||||
placeholder: '[\n {\n "keywords": [],\n "regex": ["https?://[^\\\\s]+"],\n "reaction": "检测到链接:[0]"\n }\n]',
|
||||
})
|
||||
|
||||
export const MCPRootItemsHook = createJsonFieldHook({
|
||||
emptyValue: [],
|
||||
helperText: 'MCP Roots 条目为对象数组,使用 JSON 编辑。',
|
||||
|
||||
Reference in New Issue
Block a user