mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 05:20:46 +08:00
feat: split group and direct context enhancement
This commit is contained in:
parent
38096d1589
commit
75ebaebf2e
21 changed files with 1423 additions and 873 deletions
|
|
@ -4,6 +4,12 @@ export const CONTEXT_ENHANCEMENT_FIELDS = Object.freeze([
|
|||
]);
|
||||
|
||||
export const CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH = 8_000;
|
||||
export const CONTEXT_GROUP_GUIDANCE_EXAMPLE = `仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。
|
||||
当前消息来自群聊,请使用严肃、克制、简洁的表达方式。`;
|
||||
export const CONTEXT_DIRECT_GUIDANCE_EXAMPLE = `仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。
|
||||
当前消息来自私聊,可以使用更轻松、幽默、详细的表达方式。`;
|
||||
|
||||
// Kept for integrations that imported the original combined example.
|
||||
export const CONTEXT_GUIDANCE_EXAMPLE = `仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。
|
||||
conversationType是群聊时回复严肃一点,conversationType是私聊时回复一定要幽默搞笑,像周星驰的电影一样搞笑`;
|
||||
|
||||
|
|
@ -11,13 +17,21 @@ conversationType是群聊时回复严肃一点,conversationType是私聊时回
|
|||
export const DEFAULT_CONTEXT_GUIDANCE = CONTEXT_GUIDANCE_EXAMPLE;
|
||||
|
||||
export const DEFAULT_CONTEXT_ENHANCEMENT_CONFIG = Object.freeze({
|
||||
groupEnabled: false,
|
||||
directEnabled: false,
|
||||
fields: Object.freeze(['senderId']),
|
||||
guidance: '',
|
||||
group: Object.freeze({
|
||||
enabled: false,
|
||||
fields: Object.freeze(['senderId']),
|
||||
guidance: '',
|
||||
}),
|
||||
direct: Object.freeze({
|
||||
enabled: false,
|
||||
fields: Object.freeze(['senderId']),
|
||||
guidance: '',
|
||||
}),
|
||||
});
|
||||
|
||||
const CONFIG_KEYS = ['groupEnabled', 'directEnabled', 'fields', 'guidance'];
|
||||
const CONFIG_KEYS = ['group', 'direct'];
|
||||
const SCOPE_KEYS = ['enabled', 'fields', 'guidance'];
|
||||
const LEGACY_CONFIG_KEYS = ['groupEnabled', 'directEnabled', 'fields', 'guidance'];
|
||||
const CHANNELS = new Set([
|
||||
'wecom', 'weixin', 'feishu', 'dingtalk', 'qq',
|
||||
'slack', 'telegram', 'discord', 'whatsapp',
|
||||
|
|
@ -31,16 +45,19 @@ function invalidConfig(message) {
|
|||
return error;
|
||||
}
|
||||
|
||||
/** Validate the complete atomic save, preserving explicit empty selections/text. */
|
||||
export function validateContextEnhancementConfig(input) {
|
||||
if (!input || typeof input !== 'object' || Array.isArray(input)
|
||||
|| ![Object.prototype, null].includes(Object.getPrototypeOf(input))
|
||||
|| Reflect.ownKeys(input).length !== CONFIG_KEYS.length
|
||||
|| !CONFIG_KEYS.every((key) => Object.hasOwn(input, key))) {
|
||||
function hasExactKeys(input, keys) {
|
||||
return input && typeof input === 'object' && !Array.isArray(input)
|
||||
&& [Object.prototype, null].includes(Object.getPrototypeOf(input))
|
||||
&& Reflect.ownKeys(input).length === keys.length
|
||||
&& keys.every((key) => Object.hasOwn(input, key));
|
||||
}
|
||||
|
||||
function validateContextEnhancementScope(input) {
|
||||
if (!hasExactKeys(input, SCOPE_KEYS)) {
|
||||
throw invalidConfig('请提交完整的上下文增强设置。');
|
||||
}
|
||||
const { groupEnabled, directEnabled, fields, guidance } = input;
|
||||
if (typeof groupEnabled !== 'boolean' || typeof directEnabled !== 'boolean') {
|
||||
const { enabled, fields, guidance } = input;
|
||||
if (typeof enabled !== 'boolean') {
|
||||
throw invalidConfig('群聊和私聊开关必须是布尔值。');
|
||||
}
|
||||
if (!Array.isArray(fields) || ![...fields].every((field) => CONTEXT_ENHANCEMENT_FIELDS.includes(field))) {
|
||||
|
|
@ -50,19 +67,51 @@ export function validateContextEnhancementConfig(input) {
|
|||
throw invalidConfig(`增强提示词不得超过 ${CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH} 个字符。`);
|
||||
}
|
||||
return Object.freeze({
|
||||
groupEnabled,
|
||||
directEnabled,
|
||||
enabled,
|
||||
fields: Object.freeze(CONTEXT_ENHANCEMENT_FIELDS.filter((field) => fields.includes(field))),
|
||||
guidance: guidance.trim() ? guidance : '',
|
||||
});
|
||||
}
|
||||
|
||||
/** Validate the complete atomic save, preserving explicit empty selections/text. */
|
||||
export function validateContextEnhancementConfig(input) {
|
||||
if (!hasExactKeys(input, CONFIG_KEYS)) {
|
||||
throw invalidConfig('请提交完整的上下文增强设置。');
|
||||
}
|
||||
return Object.freeze({
|
||||
group: validateContextEnhancementScope(input.group),
|
||||
direct: validateContextEnhancementScope(input.direct),
|
||||
});
|
||||
}
|
||||
|
||||
function migrateLegacyContextEnhancementConfig(input) {
|
||||
if (!hasExactKeys(input, LEGACY_CONFIG_KEYS)) {
|
||||
throw invalidConfig('请提交完整的上下文增强设置。');
|
||||
}
|
||||
return validateContextEnhancementConfig({
|
||||
group: {
|
||||
enabled: input.groupEnabled,
|
||||
fields: input.fields,
|
||||
guidance: input.guidance,
|
||||
},
|
||||
direct: {
|
||||
enabled: input.directEnabled,
|
||||
fields: input.fields,
|
||||
guidance: input.guidance,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Missing or damaged enhancement settings must never break an existing bot. */
|
||||
export function normalizeContextEnhancementConfig(input) {
|
||||
try {
|
||||
return validateContextEnhancementConfig(input);
|
||||
} catch {
|
||||
return DEFAULT_CONTEXT_ENHANCEMENT_CONFIG;
|
||||
try {
|
||||
return migrateLegacyContextEnhancementConfig(input);
|
||||
} catch {
|
||||
return DEFAULT_CONTEXT_ENHANCEMENT_CONFIG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,11 +120,15 @@ export function captureContextEnhancement(provider, conversationType) {
|
|||
if (conversationType !== 'group' && conversationType !== 'direct') return null;
|
||||
try {
|
||||
const settings = provider?.getSettings?.();
|
||||
const enabledKey = conversationType === 'group' ? 'groupEnabled' : 'directEnabled';
|
||||
if (settings?.[enabledKey] !== true) return null;
|
||||
const legacyEnabledKey = conversationType === 'group' ? 'groupEnabled' : 'directEnabled';
|
||||
const enabled = Object.hasOwn(settings ?? {}, conversationType)
|
||||
? settings?.[conversationType]?.enabled
|
||||
: settings?.[legacyEnabledKey];
|
||||
if (enabled !== true) return null;
|
||||
const config = normalizeContextEnhancementConfig(settings);
|
||||
if (config[enabledKey] !== true) return null;
|
||||
return Object.freeze({ config, botId: provider.botId, conversationType });
|
||||
const scope = config[conversationType];
|
||||
if (scope.enabled !== true) return null;
|
||||
return Object.freeze({ config: scope, botId: provider.botId, conversationType });
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue