- 新增 GET /api/v1/admin/config-facts:记忆整理 7 键 + 安静时段 2 键的**进程生效值** + 声明默认值(反射读 default 标签,不读 .env) - 面板:记忆整理卡 + 安静时段卡(Miuix 只读),文案按新语义「只服务主动搭话,不静默回复」 - 后台思考行加 urgency(**只能是下限**,存档不可分 high/critical)+ quietHoursBypassed(与既有 bypassed 是两件事) - 红线实测:往响应里塞 JWT ⇒ TestAdminConfigFacts_NeverLeaksSecretValues 打红(连断言消息都把整个响应打出来了) - 前端:configFacts 24 checks / ConfigFactsCards 29 / BackthinkUrgencyTags 19 全过 - 契约 v0.68(纯增量:B12 新端点 + B8 两字段 + §4.10 形状与语义 + §7.4-17 下限限定)
111 lines
4.6 KiB
TypeScript
111 lines
4.6 KiB
TypeScript
import type { AdminConfigFact, AdminConfigFacts, AdminConfigQuietHours } from "../../lib/types";
|
|
|
|
/**
|
|
* 配置只读事实卡的**纯逻辑**(契约 v0.68 §4.10(a))。这里只做形状 → 文案的映射,
|
|
* 不发请求、不碰 DOM,因此可以用 plain-node 检查跑(见 configFacts.test.ts)。
|
|
*
|
|
* 三条不变量(与后端 admin_config_facts.go 同一口径):
|
|
* 1. 值一律取后端给的**进程生效值**,本层不推算、不格式化数值(只补单位/说明);
|
|
* 2. 「默认 / 已改」只由 `isDefault` 决定,**不**反推 `.env` 写过什么(面板读不到);
|
|
* 3. 后端没给的键如实显示「未报告」,**绝不**编一个值出来。
|
|
*/
|
|
|
|
/** 后端没给这条事实时的显示值(不是 0,也不是默认值)。 */
|
|
export const FACT_MISSING_VALUE = "未报告";
|
|
|
|
/** 记忆整理的 7 个键(与后端 configFactSpecs 的前 7 条同序)。 */
|
|
export const MEMORY_TIDY_KEYS: readonly string[] = [
|
|
"MEMORY_TIDY_ENABLED",
|
|
"MEMORY_TIDY_INTERVAL_SEC",
|
|
"MEMORY_TIDY_QUIET_START_HOUR",
|
|
"MEMORY_TIDY_QUIET_END_HOUR",
|
|
"MEMORY_TIDY_RECALL_THRESHOLD",
|
|
"MEMORY_TIDY_SCAN_LIMIT",
|
|
"MEMORY_TIDY_CANDIDATE_LIMIT",
|
|
];
|
|
|
|
/** 安静时段的两个键(后端事实表后两条)。 */
|
|
export const QUIET_HOURS_KEYS: readonly string[] = ["QUIET_START_HOUR", "QUIET_END_HOUR"];
|
|
|
|
export interface FactRow {
|
|
key: string;
|
|
/** 中文说明(这个键管什么)。 */
|
|
label: string;
|
|
/** 显示值(生效值 + 单位)。 */
|
|
value: string;
|
|
/** `"默认"` / `"已改"` / `"未报告"`。 */
|
|
state: string;
|
|
}
|
|
|
|
interface FactMeta {
|
|
label: string;
|
|
/** 值后缀(单位);空串表示不加。 */
|
|
unit?: string;
|
|
}
|
|
|
|
const FACT_META: Record<string, FactMeta> = {
|
|
MEMORY_TIDY_ENABLED: { label: "记忆整理总开关" },
|
|
MEMORY_TIDY_INTERVAL_SEC: { label: "整理周期", unit: " 秒" },
|
|
MEMORY_TIDY_QUIET_START_HOUR: { label: "整理低峰窗口起点", unit: " 时" },
|
|
MEMORY_TIDY_QUIET_END_HOUR: { label: "整理低峰窗口终点", unit: " 时" },
|
|
MEMORY_TIDY_RECALL_THRESHOLD: { label: "候选召回门(余弦)" },
|
|
MEMORY_TIDY_SCAN_LIMIT: { label: "每轮扫描上限" },
|
|
MEMORY_TIDY_CANDIDATE_LIMIT: { label: "每轮候选上限" },
|
|
QUIET_START_HOUR: { label: "安静时段起点(本地钟点)", unit: " 时" },
|
|
QUIET_END_HOUR: { label: "安静时段终点(本地钟点)", unit: " 时" },
|
|
};
|
|
|
|
/** factRows 把后端事实列表按给定键序映射成显示行;缺的键如实标「未报告」。 */
|
|
export function factRows(facts: AdminConfigFacts | null, keys: readonly string[]): FactRow[] {
|
|
const byKey = new Map<string, AdminConfigFact>();
|
|
for (const f of facts?.facts ?? []) byKey.set(f.key, f);
|
|
return keys.map((key) => {
|
|
const meta = FACT_META[key];
|
|
const got = byKey.get(key);
|
|
if (!got) {
|
|
return {
|
|
key,
|
|
label: meta ? meta.label : key,
|
|
value: FACT_MISSING_VALUE,
|
|
state: FACT_MISSING_VALUE,
|
|
};
|
|
}
|
|
return {
|
|
key,
|
|
label: meta ? meta.label : key,
|
|
value: `${got.value}${meta?.unit ?? ""}`,
|
|
state: got.isDefault ? "默认" : "已改",
|
|
};
|
|
});
|
|
}
|
|
|
|
/** 记忆整理事实行(7 条)。 */
|
|
export function memoryTidyRows(facts: AdminConfigFacts | null): FactRow[] {
|
|
return factRows(facts, MEMORY_TIDY_KEYS);
|
|
}
|
|
|
|
/** 安静时段事实行(2 条)。 */
|
|
export function quietHoursRows(facts: AdminConfigFacts | null): FactRow[] {
|
|
return factRows(facts, QUIET_HOURS_KEYS);
|
|
}
|
|
|
|
/** 安静时段窗口的一句话摘要(后端 label + 跨午夜 / 已关闭 / 「0/0 = 未填」的如实补充)。 */
|
|
export function quietHoursSummary(qh: AdminConfigQuietHours | null | undefined): string {
|
|
if (!qh) return FACT_MISSING_VALUE;
|
|
if (qh.disabled) return "已关闭(安静时段从不生效)";
|
|
const label = qh.label || `${qh.startHour}:00-${qh.endHour}:00`;
|
|
if (qh.fallbackToDefault) {
|
|
return `${label}(两端写 0 = 未填,运行时回落到默认窗口)`;
|
|
}
|
|
return qh.startHour > qh.endHour ? `${label}(跨午夜)` : label;
|
|
}
|
|
|
|
/**
|
|
* 安静时段的**用途文案**(主人裁决 10 的新语义)。
|
|
* ⚠️ 必须说清「只服务定时主动思考 → 主动搭话,不静默回复」——
|
|
* 写成「夜间全静默」是已废弃的旧实现语义,被这次改动明确否掉了。
|
|
*/
|
|
export function quietHoursPurpose(): string {
|
|
return "安静时段只服务「定时主动思考 → 主动搭话」:她在这个窗口里默认不主动找你。回复侧不受它约束——你任何时候说话,她都照常回,不会因为到了安静时段就被静默。群聊同样没有夜间静默。";
|
|
}
|