Files
ST-Amily2-Chat-Optimisation/utils/config/RuleProfileManager.js

305 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { extension_settings } from "/scripts/extensions.js";
import { saveSettingsDebounced } from "/script.js";
import { extensionName } from "../settings.js";
const RULE_PROFILE_KEY = 'ruleProfiles';
const RULE_ASSIGNMENTS_KEY = 'ruleProfileAssignments';
// ── 功能槽定义 ──────────────────────────────────────────────────────────────────
export const RULE_SLOTS = {
table: { label: '表格提取规则' },
historiography: { label: '史官/总结提取规则' },
condensation: { label: '翰林院·浓缩规则' },
queryPreprocessing:{ label: '翰林院·查询预处理规则' },
};
function sanitizeRuleProfile(profile = {}) {
const exclusionRules = Array.isArray(profile.exclusionRules)
? profile.exclusionRules
.map(rule => ({
start: String(rule?.start ?? '').trim(),
end: String(rule?.end ?? '').trim(),
}))
.filter(rule => rule.start)
: [];
return {
id: String(profile.id ?? '').trim(),
name: String(profile.name ?? '').trim(),
tagExtractionEnabled: Boolean(profile.tagExtractionEnabled),
tags: String(profile.tags ?? ''),
exclusionRules,
};
}
function cloneRuleProfile(profile = {}) {
return {
id: profile.id || '',
name: profile.name || '',
tagExtractionEnabled: Boolean(profile.tagExtractionEnabled),
tags: profile.tags || '',
exclusionRules: Array.isArray(profile.exclusionRules)
? profile.exclusionRules.map(rule => ({
start: rule.start || '',
end: rule.end || '',
}))
: [],
};
}
function createRuleProfileId(name = 'rule-profile') {
const base = String(name || 'rule-profile')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '') || 'rule-profile';
return `${base}-${Date.now().toString(36)}`;
}
function ensureSettingsRoot() {
if (!extension_settings[extensionName]) {
extension_settings[extensionName] = {};
}
return extension_settings[extensionName];
}
function ensureProfileMap() {
const settings = ensureSettingsRoot();
if (!settings[RULE_PROFILE_KEY] || typeof settings[RULE_PROFILE_KEY] !== 'object' || Array.isArray(settings[RULE_PROFILE_KEY])) {
settings[RULE_PROFILE_KEY] = {};
}
return settings[RULE_PROFILE_KEY];
}
function ensureAssignments() {
const settings = ensureSettingsRoot();
if (!settings[RULE_ASSIGNMENTS_KEY] || typeof settings[RULE_ASSIGNMENTS_KEY] !== 'object' || Array.isArray(settings[RULE_ASSIGNMENTS_KEY])) {
settings[RULE_ASSIGNMENTS_KEY] = {};
}
return settings[RULE_ASSIGNMENTS_KEY];
}
function mergeRuleConfig(profile, fallback = {}) {
const safeFallback = sanitizeRuleProfile({
id: fallback.id,
name: fallback.name,
tagExtractionEnabled: fallback.tagExtractionEnabled,
tags: fallback.tags,
exclusionRules: fallback.exclusionRules,
});
if (!profile) {
return safeFallback;
}
return {
id: profile.id,
name: profile.name,
tagExtractionEnabled: profile.tagExtractionEnabled,
tags: profile.tags,
exclusionRules: cloneRuleProfile(profile).exclusionRules.length > 0
? cloneRuleProfile(profile).exclusionRules
: safeFallback.exclusionRules,
};
}
function _dispatchChange() {
const profiles = Object.values(ensureProfileMap())
.map(p => cloneRuleProfile(p))
.sort((a, b) => (a.name || a.id).localeCompare(b.name || b.id, 'zh-Hans-CN'));
const assignments = { ...ensureAssignments() };
document.dispatchEvent(new CustomEvent('amily2:ruleProfilesChanged', {
detail: { profiles, assignments },
}));
}
export class RuleProfileManager {
listProfiles() {
const profiles = Object.values(ensureProfileMap())
.map(profile => cloneRuleProfile(profile))
.sort((left, right) => {
const leftName = left.name || left.id;
const rightName = right.name || right.id;
return leftName.localeCompare(rightName, 'zh-Hans-CN');
});
return profiles;
}
getProfile(id) {
if (!id) return null;
const profile = ensureProfileMap()[id];
return profile ? cloneRuleProfile(profile) : null;
}
saveProfile(profile) {
const normalized = sanitizeRuleProfile(profile);
const profileId = normalized.id || createRuleProfileId(normalized.name);
const nextProfile = {
...normalized,
id: profileId,
name: normalized.name || profileId,
};
ensureProfileMap()[profileId] = nextProfile;
saveSettingsDebounced();
_dispatchChange();
return cloneRuleProfile(nextProfile);
}
deleteProfile(id) {
if (!id) return false;
const profiles = ensureProfileMap();
if (!profiles[id]) return false;
delete profiles[id];
saveSettingsDebounced();
_dispatchChange();
return true;
}
resolveProfile(id, fallback = {}) {
return mergeRuleConfig(this.getProfile(id), fallback);
}
// ── 功能槽分配 ──────────────────────────────────────────────────────────────
getAssignment(slot) {
if (!RULE_SLOTS[slot]) return null;
return ensureAssignments()[slot] || null;
}
setAssignment(slot, profileId) {
if (!RULE_SLOTS[slot]) return false;
const assignments = ensureAssignments();
if (profileId) {
assignments[slot] = profileId;
} else {
delete assignments[slot];
}
saveSettingsDebounced();
_dispatchChange();
return true;
}
getAssignedProfile(slot) {
const id = this.getAssignment(slot);
if (!id) return null;
const profile = ensureProfileMap()[id];
return profile ? cloneRuleProfile(profile) : null;
}
}
export const ruleProfileManager = new RuleProfileManager();
export function resolveRuleConfig(ruleProfileId, fallback = {}) {
return ruleProfileManager.resolveProfile(ruleProfileId, fallback);
}
/**
* 通过功能槽名解析规则配置(推荐方式)
* 先查 assignments再回退到旧字段
*/
export function resolveSlotRuleConfig(slot, legacyFallback = {}) {
const assignedId = ruleProfileManager.getAssignment(slot);
if (assignedId) {
const profile = ruleProfileManager.getProfile(assignedId);
if (profile) return profile;
}
// 回退到旧的 resolve 路径
return sanitizeRuleProfile(legacyFallback);
}
export function resolveCondensationRuleConfig(settings = {}) {
const condensation = settings.condensation || {};
return resolveSlotRuleConfig('condensation', {
...condensation,
ruleProfileId: condensation.ruleProfileId,
});
}
export function resolveQueryPreprocessingRuleConfig(settings = {}) {
const queryPreprocessing = settings.queryPreprocessing || {};
return resolveSlotRuleConfig('queryPreprocessing', {
...queryPreprocessing,
ruleProfileId: queryPreprocessing.ruleProfileId,
});
}
export function resolveTableRuleConfig(settings = {}) {
return resolveSlotRuleConfig('table', {
id: settings.table_rule_profile_id,
tagExtractionEnabled: Boolean(settings.table_tags_to_extract),
tags: settings.table_tags_to_extract || '',
exclusionRules: settings.table_exclusion_rules || [],
});
}
export function resolveHistoriographyRuleConfig(settings = {}) {
return resolveSlotRuleConfig('historiography', {
id: settings.historiographyRuleProfileId,
tagExtractionEnabled: settings.historiographyTagExtractionEnabled ?? false,
tags: settings.historiographyTags || '',
exclusionRules: settings.historiographyExclusionRules || [],
});
}
// ── 一次性迁移:旧分散 profileId 字段 → 统一 assignments ─────────────────────
;(() => {
const settings = ensureSettingsRoot();
const assignments = ensureAssignments();
let changed = false;
// table: table_rule_profile_id → assignments.table
if (settings.table_rule_profile_id && !assignments.table) {
assignments.table = settings.table_rule_profile_id;
changed = true;
}
// historiography: historiographyRuleProfileId → assignments.historiography
if (settings.historiographyRuleProfileId && !assignments.historiography) {
assignments.historiography = settings.historiographyRuleProfileId;
changed = true;
}
// condensation: condensation.ruleProfileId → assignments.condensation
const condensation = settings.condensation || {};
if (condensation.ruleProfileId && !assignments.condensation) {
assignments.condensation = condensation.ruleProfileId;
changed = true;
}
// queryPreprocessing: queryPreprocessing.ruleProfileId → assignments.queryPreprocessing
const queryPreprocessing = settings.queryPreprocessing || {};
if (queryPreprocessing.ruleProfileId && !assignments.queryPreprocessing) {
assignments.queryPreprocessing = queryPreprocessing.ruleProfileId;
changed = true;
}
if (changed) {
saveSettingsDebounced();
console.log('[RuleProfiles] 已迁移旧规则配置分配到统一 assignments。', assignments);
}
})();
setTimeout(() => {
try {
const ctx = window.Amily2Bus?.register('RuleProfiles');
if (!ctx) {
console.warn('[RuleProfiles] Amily2Bus 尚未就绪,注册跳过。');
return;
}
ctx.expose({
listProfiles: () => ruleProfileManager.listProfiles(),
getProfile: (id) => ruleProfileManager.getProfile(id),
saveProfile: (profile) => ruleProfileManager.saveProfile(profile),
deleteProfile: (id) => ruleProfileManager.deleteProfile(id),
resolveProfile: (id, fallback) => ruleProfileManager.resolveProfile(id, fallback),
getAssignment: (slot) => ruleProfileManager.getAssignment(slot),
setAssignment: (slot, id) => ruleProfileManager.setAssignment(slot, id),
getAssignedProfile: (slot) => ruleProfileManager.getAssignedProfile(slot),
RULE_SLOTS,
});
ctx.log('RuleProfiles', 'info', 'RuleProfiles 服务已注册到 Bus。');
} catch (error) {
console.error('[RuleProfiles] Bus 注册失败:', error);
}
}, 0);