// 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; }