Files
writer-work-flow/apps/web/lib/workbench/clarify.ts

95 lines
3.7 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.

// AI 反问澄清(纯逻辑 + 前端 view-model 类型WFW-9 M1路线A 两阶段)。
// 「润色/再沟通」意见不清晰时,先走独立非流式 JSON 预检端点让 AI 反问给选项,
// 作者点选/自由输入后,把答案「折进」既有 refine 的 instruction 字符串(零迁移、不动 refine 端点)。
// 这里只放确定性纯函数 + 本地 VM 类型camelCase与后端 snake_case ClarifyDecision 的映射由主线接线时完成。
// 澄清选项(锚定本段/本章的一种具体走法)。
export interface ClarifyOptionVM {
label: string;
value: string;
}
// 一个澄清问题:反问 + 24 个具体选项(可为空退化为纯自由输入)+ 常驻自由输入兜底。
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,
};
}