Files
Jyotisha/frontend/src/lib/agent-reply.ts
T

79 lines
3.9 KiB
TypeScript
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 type { ConsultationDomain } from "./consultation-domain-registry.ts";
export type ReplyTheme = ConsultationDomain;
import type { ConsultationReplyMetadata } from "./consultation-reply-metadata.ts";
const fallbackSuggestions: Record<ReplyTheme, readonly [string, string, string]> = {
career: ["我更适合怎样的职业路径?", "未来一年事业上要避开什么?", "我该如何发挥自己的优势?"],
marriage: ["我在关系里容易重复什么模式?", "怎样的伴侣更适合我?", "未来一年关系上要注意什么?"],
wealth: ["我的财富增长方式是什么?", "接下来财务上要避开什么?", "我该如何稳定提升收入?"],
health: ["近期压力主要来自哪里?", "怎样安排休息与恢复?", "哪些信号值得持续观察?"],
education: ["我更适合怎样的学习路径?", "现在该补哪项能力?", "什么阶段适合考试或进修?"],
migration: ["迁居方向该优先考虑什么?", "置业与流动之间如何取舍?", "海外发展要注意哪些条件?"],
family: ["家庭关系中该建立什么边界?", "我承担的责任是否失衡?", "接下来适合如何沟通?"],
annual: ["未来一年最重要的主题是什么?", "哪些阶段适合主动推进?", "哪些阶段更适合调整?"],
timing: ["接下来最值得把握的阶段是什么?", "哪些时期更适合主动行动?", "我现在应该优先准备什么?"],
general: ["未来一年,事业和收入该关注什么?", "我的关系模式是什么?", "未来哪些阶段值得把握?"],
};
function readSuggestions(value: unknown): string[] {
if (!Array.isArray(value)) return [];
return [...new Set(value
.filter((item): item is string => typeof item === "string")
.map((item) => item.replace(/\s+/g, " ").trim().slice(0, 80))
.filter(Boolean))].slice(0, 3);
}
function readTitle(value: string): string | undefined {
const title = value.replace(/\s+/g, " ").trim();
if (!title || /[\d\p{P}\p{S}]/u.test(title)) return undefined;
if (/\p{Script=Han}/u.test(title)) {
const length = Array.from(title.replace(/\s/g, "")).length;
return length >= 6 && length <= 14 ? title : undefined;
}
const words = title.split(" ").filter(Boolean);
return words.length >= 3 && words.length <= 7 && title.length <= 64 ? title : undefined;
}
function stripAgentReplyMetadata(value: string) {
let suggestions: string[] = [];
let title: string | undefined;
const withoutSuggestions = value.replace(/<!--AYANAM_SUGGESTIONS:(\[[\s\S]*?\])-->/g, (_, json: string) => {
try {
suggestions = readSuggestions(JSON.parse(json));
} catch {
suggestions = [];
}
return "";
});
const text = withoutSuggestions.replace(/<!--AYANAM_TITLE:([\s\S]*?)-->/g, (_, rawTitle: string) => {
title = readTitle(rawTitle);
return "";
}).replace(/<!--AYANAM_[\s\S]*$/, "").trim();
return { text, suggestions, title };
}
export function parseAgentReply(value: string, theme: ReplyTheme, metadata?: ConsultationReplyMetadata) {
const parsed = stripAgentReplyMetadata(value);
return {
text: parsed.text,
suggestions: metadata?.suggestions ?? (parsed.suggestions.length === 3 ? parsed.suggestions : [...fallbackSuggestions[theme]]),
title: metadata?.title ?? parsed.title,
};
}
export function parseAgentReplyBody(value: string) {
const parsed = stripAgentReplyMetadata(value);
return { text: parsed.text, title: parsed.title };
}
export function resolveSessionTitle(question: string, modelTitle?: string): string {
if (modelTitle && modelTitle !== "一般占星咨询") return modelTitle;
const normalized = question.replace(/\s+/g, " ").trim().replace(/[?!,;:]+$/u, "");
if (!normalized) return "新对话";
const characters = Array.from(normalized);
return characters.length > 14 ? `${characters.slice(0, 14).join("")}` : normalized;
}