95 lines
3.7 KiB
TypeScript
95 lines
3.7 KiB
TypeScript
// AI 反问澄清(纯逻辑 + 前端 view-model 类型;WFW-9 M1,路线A 两阶段)。
|
||
// 「润色/再沟通」意见不清晰时,先走独立非流式 JSON 预检端点让 AI 反问给选项,
|
||
// 作者点选/自由输入后,把答案「折进」既有 refine 的 instruction 字符串(零迁移、不动 refine 端点)。
|
||
// 这里只放确定性纯函数 + 本地 VM 类型(camelCase);与后端 snake_case ClarifyDecision 的映射由主线接线时完成。
|
||
|
||
// 澄清选项(锚定本段/本章的一种具体走法)。
|
||
export interface ClarifyOptionVM {
|
||
label: string;
|
||
value: string;
|
||
}
|
||
|
||
// 一个澄清问题:反问 + 2–4 个具体选项(可为空退化为纯自由输入)+ 常驻自由输入兜底。
|
||
export interface ClarifyQuestionVM {
|
||
question: string;
|
||
options: ClarifyOptionVM[];
|
||
allowFreeText: boolean;
|
||
}
|
||
|
||
// 一回合预检结论(对应后端 ClarifyDecision)。
|
||
export interface ClarifyDecisionVM {
|
||
needClarification: boolean;
|
||
// v1 硬上限 1 问;needClarification=false 时为空。
|
||
questions: ClarifyQuestionVM[];
|
||
// needClarification=false 时给一句「我这样理解对吗」确认语,可空。
|
||
verification?: string;
|
||
}
|
||
|
||
// 作者对某个澄清问题的回答(点选回填 value 或自由输入文本)。
|
||
export interface ClarifyAnswer {
|
||
question: string;
|
||
answer: string;
|
||
}
|
||
|
||
const CLARIFY_PREFIX = "已澄清:";
|
||
const CLARIFY_ARROW = " → ";
|
||
const LINE_SEPARATOR = "\n";
|
||
|
||
// 把作者对各澄清问题的回答逐条折进 instruction(原意见在前,每条澄清另起一行),供既有 refine 端点直接使用。
|
||
// 纯函数、确定性、不可变:不改动入参;跳过空回答;保留传入顺序。
|
||
export function foldClarifications(
|
||
instruction: string,
|
||
answers: readonly ClarifyAnswer[],
|
||
): string {
|
||
const lines = answers
|
||
.map((entry) => ({
|
||
question: entry.question.trim(),
|
||
answer: entry.answer.trim(),
|
||
}))
|
||
.filter((entry) => entry.answer.length > 0)
|
||
.map((entry) =>
|
||
entry.question
|
||
? `${CLARIFY_PREFIX}${entry.question}${CLARIFY_ARROW}${entry.answer}`
|
||
: `${CLARIFY_PREFIX}${entry.answer}`,
|
||
);
|
||
|
||
const trimmedInstruction = instruction.trim();
|
||
const parts = trimmedInstruction ? [trimmedInstruction, ...lines] : lines;
|
||
return parts.join(LINE_SEPARATOR);
|
||
}
|
||
|
||
// 门控:instruction 去空白后长度不足 minChars(意见太空/太短)才需要预检反问;清晰意见直接走 refine 不发问。
|
||
export function needsClarifyGate(instruction: string, minChars: number): boolean {
|
||
return instruction.trim().length < minChars;
|
||
}
|
||
|
||
// 澄清预检结论为「放行」(直接去 refine/rewrite,不反问)——后端 error/前端异常时的确定性回退。
|
||
export const PROCEED: ClarifyDecisionVM = { needClarification: false, questions: [] };
|
||
|
||
// 后端 snake_case ClarifyDecision 的原始形状(外部数据,全部可选——不信任、显式取值 + 默认)。
|
||
export interface ClarifyDecisionRaw {
|
||
need_clarification?: boolean;
|
||
questions?: {
|
||
question?: string;
|
||
options?: { label?: string; value?: string }[];
|
||
allow_free_text?: boolean;
|
||
}[];
|
||
verification?: string | null;
|
||
}
|
||
|
||
// 后端 snake_case ClarifyDecision → 前端 camelCase VM(纯映射,refine/rewrite 两侧预检共用)。
|
||
export function toVM(data: ClarifyDecisionRaw): ClarifyDecisionVM {
|
||
return {
|
||
needClarification: Boolean(data.need_clarification),
|
||
questions: (data.questions ?? []).map((q) => ({
|
||
question: q.question ?? "",
|
||
options: (q.options ?? []).map((o) => ({
|
||
label: o.label ?? "",
|
||
value: o.value ?? "",
|
||
})),
|
||
allowFreeText: Boolean(q.allow_free_text),
|
||
})),
|
||
verification: data.verification ?? undefined,
|
||
};
|
||
}
|