49 lines
2.1 KiB
TypeScript
49 lines
2.1 KiB
TypeScript
import { z } from "zod";
|
|
|
|
const detachedStarterQuestionPattern = /印度占星|一般如何|通常(?:会)?(?:看|观察|分析|理解|包含)|哪些(?:因素|证据层)|如何划分/;
|
|
|
|
const userCenteredStarterQuestionSchema = z.string()
|
|
.trim()
|
|
.min(4)
|
|
.max(80)
|
|
.refine((text) => text.includes("我"), "starter_question_must_be_first_person")
|
|
.refine((text) => !detachedStarterQuestionPattern.test(text), "starter_question_must_not_be_encyclopedic");
|
|
|
|
const onboardingSchema = z.object({
|
|
greeting: z.string().trim().min(8).max(180),
|
|
suggestions: z.tuple([
|
|
z.object({ theme: z.literal("career"), text: userCenteredStarterQuestionSchema }),
|
|
z.object({ theme: z.literal("marriage"), text: userCenteredStarterQuestionSchema }),
|
|
z.object({ theme: z.literal("timing"), text: userCenteredStarterQuestionSchema }),
|
|
]),
|
|
});
|
|
|
|
export type OnboardingPayload = z.infer<typeof onboardingSchema>;
|
|
|
|
export const fallbackOnboardingPayload: OnboardingPayload = {
|
|
greeting: "我们从你此刻最关心的事情开始。可以选择下面的方向,也可以直接说出你的问题。",
|
|
suggestions: [
|
|
{ theme: "career", text: "请帮我看看事业优势更适合怎样发挥。" },
|
|
{ theme: "marriage", text: "请帮我看看关系里容易重复什么模式。" },
|
|
{ theme: "timing", text: "请帮我看看未来一年哪些阶段值得提前准备。" },
|
|
],
|
|
};
|
|
|
|
class OnboardingJsonError extends Error {
|
|
readonly name = "OnboardingJsonError";
|
|
}
|
|
|
|
export function parseOnboardingPayload(value: unknown): OnboardingPayload | null {
|
|
const parsed = onboardingSchema.safeParse(value);
|
|
return parsed.success ? parsed.data : null;
|
|
}
|
|
|
|
export function parseOnboardingText(text: string): OnboardingPayload | null {
|
|
const normalized = text.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
|
|
const start = normalized.indexOf("{");
|
|
const end = normalized.lastIndexOf("}");
|
|
if (start < 0 || end <= start) throw new OnboardingJsonError("onboarding_json_missing");
|
|
const parsed: unknown = JSON.parse(normalized.slice(start, end + 1));
|
|
return parseOnboardingPayload(parsed);
|
|
}
|