fix(web): keep rectification replies as model text, hide thinking
Give the interview a hidden reasoning channel so planning leaves the spoken reply, publish terminal text-delta as-is, and stop regex or Case templates from replacing the model. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -52,10 +52,7 @@ import {
|
||||
stableChoiceActionKey,
|
||||
type ChoiceOptionId,
|
||||
} from "@/lib/rectification-agentic/v9/choice-action";
|
||||
import {
|
||||
finalizeRectificationSpokenAndThinking,
|
||||
settleRectificationSpokenAndThinking,
|
||||
} from "@/lib/rectification-agentic/v9/spoken-answer";
|
||||
import { finalizeRectificationSpokenAndThinking } from "@/lib/rectification-agentic/v9/spoken-answer";
|
||||
import {
|
||||
CHOICE_STOP_MESSAGE,
|
||||
choiceCardUserMessage,
|
||||
@@ -229,11 +226,9 @@ function messagesFromTurns(initialTurns: readonly PersistedTurn[]): RenderMessag
|
||||
if (failed && !raw) return [];
|
||||
if (isIncompleteRunBanner(raw)) return [];
|
||||
const split = raw ? finalizeRectificationSpokenAndThinking(raw) : { thinking: "", spoken: raw };
|
||||
const thinkingText = split.thinking.trim() || undefined;
|
||||
return [{
|
||||
role: "assistant",
|
||||
text: split.spoken,
|
||||
...(thinkingText ? { thinkingText } : {}),
|
||||
renderKey: key,
|
||||
state: turn.status === "completed" || failed ? "settled" : "thinking",
|
||||
completedReceipt: completedReceiptFromPersisted(turn.receipt),
|
||||
@@ -449,7 +444,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
setDraft("");
|
||||
|
||||
let raw = "";
|
||||
let thinkingRaw = "";
|
||||
let activityTrace: readonly AgentActivityTraceItem[] = emptyActivityTrace();
|
||||
let activityReceiptState = createRectificationActivityReceiptState();
|
||||
let completedReceipt = receiptFromRectificationActivityState(activityReceiptState);
|
||||
@@ -528,14 +522,12 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
if (event.type === "answer.delta" && typeof event.text === "string") {
|
||||
raw += event.text;
|
||||
activityTrace = freezeLiveThink(activityTrace);
|
||||
const settled = settleRectificationSpokenAndThinking(raw, thinkingRaw);
|
||||
setMessages((current) => current.map((message) => message.renderKey === assistantRenderKey
|
||||
? {
|
||||
...message,
|
||||
text: settled.spoken,
|
||||
thinkingText: settled.thinking.trim() || undefined,
|
||||
text: raw,
|
||||
activityTrace,
|
||||
state: settled.spoken ? "streaming" : "thinking",
|
||||
state: raw.trim() ? "streaming" : "thinking",
|
||||
activity: nextActivityView(message.activity, {
|
||||
phase: "answer-composition",
|
||||
label: "正在组织回答…",
|
||||
@@ -555,7 +547,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
: message));
|
||||
} else if (event.type === "attempt.reset") {
|
||||
raw = "";
|
||||
thinkingRaw = "";
|
||||
activityTrace = emptyActivityTrace();
|
||||
activityReceiptState = createRectificationActivityReceiptState();
|
||||
completedReceipt = receiptFromRectificationActivityState(activityReceiptState);
|
||||
@@ -639,8 +630,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const settled = settleRectificationSpokenAndThinking(raw, thinkingRaw);
|
||||
const parsed = completed && !streamFailed ? parseAgentReply(settled.spoken) : { text: "", title: undefined };
|
||||
const parsed = completed && !streamFailed ? parseAgentReply(raw) : { text: "", title: undefined };
|
||||
const succeeded = completed && !streamFailed && Boolean(parsed.text);
|
||||
setMessages((current) => current.flatMap((message): RenderMessage[] => {
|
||||
if (message.renderKey !== assistantRenderKey) return [message];
|
||||
@@ -648,7 +638,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
return [{
|
||||
...message,
|
||||
text: parsed.text,
|
||||
thinkingText: settled.thinking.trim() || undefined,
|
||||
activityTrace: completeActivityTrace(activityTrace),
|
||||
state: "settled",
|
||||
completedReceipt,
|
||||
@@ -657,11 +646,10 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
activity: undefined,
|
||||
}];
|
||||
}
|
||||
if (streamFailed || hasActivityReceipt(completedReceipt) || settled.spoken.trim()) {
|
||||
if (streamFailed || hasActivityReceipt(completedReceipt) || raw.trim()) {
|
||||
return [{
|
||||
...message,
|
||||
text: settled.spoken,
|
||||
thinkingText: settled.thinking.trim() || undefined,
|
||||
text: raw,
|
||||
activityTrace: completeActivityTrace(activityTrace),
|
||||
state: "settled",
|
||||
completedReceipt,
|
||||
@@ -671,7 +659,7 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
}
|
||||
return [];
|
||||
}));
|
||||
if (!succeeded && settled.spoken.trim()) {
|
||||
if (!succeeded && raw.trim()) {
|
||||
setError((current) => current || "回答未完成,已保留现有内容;本次不会扣点。");
|
||||
} else if (!succeeded && runFailedMessage) {
|
||||
setError((current) => current || runFailedMessage);
|
||||
@@ -691,14 +679,12 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
? caught.name === "AbortError"
|
||||
: caught instanceof Error && caught.name === "AbortError";
|
||||
if (aborted) {
|
||||
const settled = settleRectificationSpokenAndThinking(raw, thinkingRaw);
|
||||
setMessages((current) => current.flatMap((message): RenderMessage[] => {
|
||||
if (message.renderKey !== assistantRenderKey) return [message];
|
||||
if (settled.spoken.trim() || settled.thinking.trim() || hasActivityReceipt(completedReceipt)) {
|
||||
if (raw.trim() || hasActivityReceipt(completedReceipt)) {
|
||||
return [{
|
||||
...message,
|
||||
text: settled.spoken,
|
||||
thinkingText: settled.thinking.trim() || undefined,
|
||||
text: raw,
|
||||
activityTrace: completeActivityTrace(activityTrace),
|
||||
state: "settled",
|
||||
completedReceipt,
|
||||
|
||||
Reference in New Issue
Block a user