Files
writing-agent/web/chat-keep-stats.js
chenmoranzhi 9dc927282f 预设兼容:按真实抓包对齐装配与包装层,补上声明式工具与预设正则。
- 装配只认 prompt_order.enabled;squash_system_messages 只合并相邻 system(新增 system 档,连接符按酒馆用单换行)
- 包装层:new_chat_prompt、示例对话(new_example_chat_prompt + <START> 切段 + name)、角色卡三块与 personality_format/scenario_format、{{user}}/{{char}} 取值、任务契约并进预设条目
- 工具协议:按 ToolBindings / 伪工具协议数据声明真 tools(presetToolPolicy=preset),工具描述用到的条目不再重复作为消息发出
- 正则:promptOnly 作用于对话历史(含 depth 过滤)、写入时脚本改写回复;{{//}} 注释按配平扫描
- tools/st-request-diff:真假上游抓包对比工具、三份预设的机制结论与实测报告
2026-10-23 00:13:08 +08:00

142 lines
4.3 KiB
JavaScript

/**
* 直聊「保留」计数器:
* 按会话记下每个模型被保留(点「保留此回复」)了多少次,存在浏览器缓存里。
* 只算数,不碰 DOM。
*/
const STORAGE_KEY = "wa-direct-chat-keep-stats-v1";
function storage() {
try {
return globalThis.localStorage || null;
} catch {
return null;
}
}
function readAll() {
const ls = storage();
if (!ls) return {};
try {
const parsed = JSON.parse(ls.getItem(STORAGE_KEY) || "{}");
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
? parsed
: {};
} catch {
return {};
}
}
function writeAll(all) {
const ls = storage();
if (!ls) return;
try {
ls.setItem(STORAGE_KEY, JSON.stringify(all));
} catch {
/* 存不进缓存也不影响当前会话 */
}
}
function isBucket(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
/**
* 模型身份:同一配置同一型号算同一个模型,
* 窗口被删掉再加回来(窗口 id 变了)也还认作它。
*/
export function keepStatId(w) {
const model = String(w?.model || "");
const key = String(w?.profileId || w?.profileName || model || "unknown");
return model && model !== key ? `${key}::${model}` : key;
}
/** 展示名与型号:配置名 · 型号,重复时只留一个 */
export function keepStatText(w) {
const model = String(w?.model || "");
const label = String(
w?.profileName || w?.profileId || model || "未知模型",
);
return { id: keepStatId(w), label, model: model === label ? "" : model };
}
/** 按次数降序,同次数再按名字排 */
export function sortKeepStats(entries) {
return [...entries].sort(
(a, b) =>
Number(b.count) - Number(a.count) ||
String(a.label).localeCompare(String(b.label), "zh-CN"),
);
}
/** 条状图宽度百分比:独占一行时满格,次数少的也至少留一点可见宽度 */
export function keepStatPercent(count, max) {
const top = Number(max) || 0;
const value = Number(count) || 0;
if (top <= 0 || value <= 0) return 0;
return Math.max(6, Math.min(100, Math.round((value / top) * 100)));
}
export function readKeepStats(conversationId) {
if (!conversationId) return [];
const bucket = readAll()[conversationId];
if (!isBucket(bucket)) return [];
const entries = Object.entries(bucket).map(([id, value]) => ({
id,
label: String(value?.label || id),
model: String(value?.model || ""),
count: Math.max(1, Number(value?.count) || 0),
}));
return sortKeepStats(entries);
}
export function keepStatTotal(entries) {
return entries.reduce((sum, entry) => sum + (Number(entry.count) || 0), 0);
}
/**
* 加减一次保留次数;减到 0 就把这个模型从记录里删掉。
* @param {string} conversationId
* @param {{profileId?: string, profileName?: string, model?: string}} window
* @param {number} [delta] 保留 +1、取消保留 -1
*/
export function bumpKeepStats(conversationId, window, delta = 1) {
if (!conversationId || !window) return readKeepStats(conversationId);
const step = Number(delta) || 0;
if (!step) return readKeepStats(conversationId);
const all = readAll();
const bucket = isBucket(all[conversationId])
? { ...all[conversationId] }
: {};
const { id, label, model } = keepStatText(window);
const before = isBucket(bucket[id]) ? Number(bucket[id].count) || 0 : 0;
const count = Math.max(0, before + step);
if (count > 0) bucket[id] = { label, model, count };
else delete bucket[id];
if (Object.keys(bucket).length) all[conversationId] = bucket;
else delete all[conversationId];
writeAll(all);
return readKeepStats(conversationId);
}
/** 清掉一个会话的计数(会话被删时用) */
export function clearKeepStats(conversationId) {
if (!conversationId) return;
const all = readAll();
if (!(conversationId in all)) return;
delete all[conversationId];
writeAll(all);
}
/** 临时会话存成持久会话时,把计数跟着搬到新会话上 */
export function copyKeepStats(fromId, toId) {
if (!fromId || !toId || fromId === toId) return readKeepStats(toId);
const all = readAll();
const bucket = all[fromId];
if (!isBucket(bucket)) return readKeepStats(toId);
all[toId] = { ...(isBucket(all[toId]) ? all[toId] : {}), ...bucket };
delete all[fromId];
writeAll(all);
return readKeepStats(toId);
}