@sidleo3/dsh-chat 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/bot-list.js +243 -0
- package/client/bot-settings.js +175 -0
- package/client/bot-shared-settings.js +561 -0
- package/client/chat-ui.js +134 -0
- package/client/context-enhancement.js +435 -0
- package/client/delivery-targets.js +334 -0
- package/client/diagnostics.js +160 -0
- package/client/i18n.js +371 -0
- package/client/index.js +77 -0
- package/client/list-order.js +144 -0
- package/client/rpc.js +52 -0
- package/client/scoped-mode-editor.js +111 -0
- package/client/section.js +250 -0
- package/client/session-badges.js +263 -0
- package/client/styles.js +960 -0
- package/client/version-panel.js +97 -0
- package/cordis.patch.yml +5 -0
- package/host/bot-model.mjs +53 -0
- package/host/bot-settings.mjs +247 -0
- package/host/channel-registry.mjs +237 -0
- package/host/commands.mjs +857 -0
- package/host/deferred.mjs +291 -0
- package/host/delivery.mjs +377 -0
- package/host/file-log.mjs +169 -0
- package/host/guidance.mjs +73 -0
- package/host/index.mjs +7 -0
- package/host/interactions.mjs +330 -0
- package/host/json-store.mjs +144 -0
- package/host/log-tail.mjs +63 -0
- package/host/panel.mjs +1012 -0
- package/host/paths.mjs +50 -0
- package/host/plugin.mjs +873 -0
- package/host/prompt-context.mjs +70 -0
- package/host/rpc.mjs +147 -0
- package/host/session-keys.mjs +25 -0
- package/host/session-store.mjs +187 -0
- package/host/sessions.mjs +1348 -0
- package/host/tools.mjs +283 -0
- package/lib/client.js +4431 -0
- package/lib/index.js +5676 -0
- package/package.json +63 -0
- package/shared/access-policy.mjs +263 -0
- package/shared/channel-rail.mjs +156 -0
- package/shared/context-enhancement.mjs +415 -0
- package/shared/contract.mjs +120 -0
- package/shared/panel-sections.mjs +76 -0
- package/shared/reply-reference.mjs +115 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 引用回复(用户在 IM 里**引用一条消息**再提问):把被引用的内容做成一个"引用块"拼进提示词。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有它:用户说"这条你怎么看"时,模型只能看到那一句话——被引用的正文根本没进 Prompt,
|
|
5
|
+
* 于是它要么猜、要么答非所问(上游 dsh-im 的 Issue #106 就是这个问题,九个渠道一起踩)。
|
|
6
|
+
*
|
|
7
|
+
* 分工(与来源块同一套):
|
|
8
|
+
* - **平台概念留渠道**:渠道只把平台字段映射成 `reply`(正文/类型/文件名/发送者/消息 id,
|
|
9
|
+
* 平台没给快照就自己查一次),不做任何拼装;
|
|
10
|
+
* - **拼装留 hub**:形状、限长、标签、安全转义都在这里实现一次,所有渠道复用。
|
|
11
|
+
*
|
|
12
|
+
* 边界(照上游 Issue #106 的最小切片):
|
|
13
|
+
* - 只做一层,**不递归**展开"引用的消息又引用了另一条";
|
|
14
|
+
* - 引用正文限长(`MAX_QUOTE_TEXT`)——它是上下文,不是全文转录;
|
|
15
|
+
* - 读不到被引用消息时**不丢当前问题**,只放一个"内容不可用"的结构化标记;
|
|
16
|
+
* - 不因为引用关系新建/切换会话,也不放宽群聊 @ 规则(那是渠道自己的事)。
|
|
17
|
+
*
|
|
18
|
+
* @module dsh-chat/shared/reply-reference
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** 引用块的标签:与 `<dsh_im_source>` 同族,便于在会话历史里一眼认出。 */
|
|
22
|
+
export const REPLY_TAG_OPEN = '<dsh_im_reply>';
|
|
23
|
+
export const REPLY_TAG_CLOSE = '</dsh_im_reply>';
|
|
24
|
+
|
|
25
|
+
/** 引用正文的限长:这是上下文,不是转录(上游同一口径)。 */
|
|
26
|
+
export const MAX_QUOTE_TEXT = 1000;
|
|
27
|
+
|
|
28
|
+
/** 非文字消息的可读描述。 */
|
|
29
|
+
const KIND_LABELS = Object.freeze({
|
|
30
|
+
image: '图片',
|
|
31
|
+
file: '文件',
|
|
32
|
+
audio: '语音',
|
|
33
|
+
media: '视频',
|
|
34
|
+
sticker: '表情',
|
|
35
|
+
post: '富文本',
|
|
36
|
+
interactive: '卡片',
|
|
37
|
+
system: '系统消息',
|
|
38
|
+
share_chat: '群名片',
|
|
39
|
+
share_user: '个人名片',
|
|
40
|
+
location: '位置',
|
|
41
|
+
todo: '任务',
|
|
42
|
+
calendar: '日程',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** 标签内容里不能再出现标签本身,否则模型会以为块提前结束了。 */
|
|
46
|
+
function safeText(value) {
|
|
47
|
+
return String(value ?? '')
|
|
48
|
+
.split(REPLY_TAG_OPEN).join('<dsh_im_reply>')
|
|
49
|
+
.split(REPLY_TAG_CLOSE).join('</dsh_im_reply>')
|
|
50
|
+
.trim();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function clip(value, max = MAX_QUOTE_TEXT) {
|
|
54
|
+
const text = safeText(value);
|
|
55
|
+
return text.length > max ? `${text.slice(0, max)}…(已截断)` : text;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 拼一个引用块。
|
|
60
|
+
*
|
|
61
|
+
* @param reply - `{ text?, kind?, fileName?, senderId?, messageId?, reason? }`;
|
|
62
|
+
* `reason` 有值表示**读不到**被引用消息(删除/无权限/超时),此时只出结构化标记。
|
|
63
|
+
* @returns 引用块字符串;`reply` 为空时返回 null(没有引用就什么都不加)。
|
|
64
|
+
*/
|
|
65
|
+
export function replyReferenceBlock(reply) {
|
|
66
|
+
if (!reply || typeof reply !== 'object') return null;
|
|
67
|
+
const lines = [];
|
|
68
|
+
const id = typeof reply.messageId === 'string' && reply.messageId ? reply.messageId : null;
|
|
69
|
+
const sender = typeof reply.senderId === 'string' && reply.senderId ? reply.senderId : null;
|
|
70
|
+
const from = [id, sender].filter(Boolean).join(',来自 ');
|
|
71
|
+
const head = from ? `用户引用了一条消息(${from}):` : '用户引用了一条消息:';
|
|
72
|
+
|
|
73
|
+
if (reply.reason) {
|
|
74
|
+
// 读不到也要说清:用户的问题照样要进 Prompt,模型得知道"引用内容不可用"而不是以为没引用。
|
|
75
|
+
lines.push(head);
|
|
76
|
+
lines.push(`(引用内容不可用:${clip(reply.reason, 200)})`);
|
|
77
|
+
} else {
|
|
78
|
+
const kind = typeof reply.kind === 'string' && reply.kind ? reply.kind : 'text';
|
|
79
|
+
const label = KIND_LABELS[kind] ?? null;
|
|
80
|
+
lines.push(head);
|
|
81
|
+
const body = [];
|
|
82
|
+
if (typeof reply.text === 'string' && reply.text.trim()) body.push(clip(reply.text));
|
|
83
|
+
if (label || reply.fileName) {
|
|
84
|
+
const name = reply.fileName ? `:${clip(reply.fileName, 200)}` : '';
|
|
85
|
+
body.push(label ? `(被引用的是${label}${name})` : `(被引用的消息类型:${clip(kind, 40)}${name})`);
|
|
86
|
+
} else if (kind !== 'text') {
|
|
87
|
+
// 认不出的类型也要写出来:只说"没有正文"等于把"引用了什么"又丢了一次。
|
|
88
|
+
body.push(`(被引用的消息类型:${clip(kind, 40)})`);
|
|
89
|
+
}
|
|
90
|
+
// 既没有正文、也不是可识别的类型:至少说一句,别让块是空的。
|
|
91
|
+
if (body.length === 0) body.push('(这条消息没有可读的正文)');
|
|
92
|
+
lines.push(...body);
|
|
93
|
+
}
|
|
94
|
+
return `${REPLY_TAG_OPEN}\n${lines.join('\n')}\n${REPLY_TAG_CLOSE}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 把引用块拼到当前内容**前面**(与 `enhanceContent` 同形态:字符串或内容数组)。
|
|
99
|
+
*
|
|
100
|
+
* @param content - 当前消息的内容(字符串或 `[{type,...}]`)。
|
|
101
|
+
* @param reply - 见 `replyReferenceBlock`。
|
|
102
|
+
* @returns 拼好的内容;没有引用时原样返回。
|
|
103
|
+
*/
|
|
104
|
+
export function enhanceReplyReference(content, reply) {
|
|
105
|
+
const block = replyReferenceBlock(reply);
|
|
106
|
+
if (!block) return content;
|
|
107
|
+
try {
|
|
108
|
+
if (typeof content === 'string') return content ? `${block}\n\n${content}` : block;
|
|
109
|
+
if (Array.isArray(content)) return [{ type: 'text', text: block }, ...content];
|
|
110
|
+
return content;
|
|
111
|
+
} catch {
|
|
112
|
+
// 只有引用拼装被隔离:当前消息照常发出去。
|
|
113
|
+
return content;
|
|
114
|
+
}
|
|
115
|
+
}
|