T2 of TASK-rectification-grounding-20260927 (product decision P1). The rectification 「重试(重新生成)」 was a blind agent.generate rewrite that persisted unchecked text (invented range / fit rate reproduced). Removed: the regenerate route, regenerate-turn.ts, the regeneration agent and its read-only tool set, the client regenerate action/state and the regenerating/canRegenerate props. ChatMessageActions renders the regenerate button only when onRegenerate is passed; ordinary consultation is unchanged. The DB function regenerate_agentic_rectification_turn is kept (AGENTS §7.6, retire in a later round). DESIGN.md / VOICE.md updated; source-contract tests follow with 原值/新值/原因 notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import { Check, Copy, RotateCcw, ThumbsDown, ThumbsUp } from "lucide-react";
|
|
|
|
export type ChatMessageFeedback = "up" | "down";
|
|
|
|
export function toggleChatMessageFeedback(
|
|
current: ChatMessageFeedback | undefined,
|
|
requested: ChatMessageFeedback,
|
|
): ChatMessageFeedback | undefined {
|
|
return current === requested ? undefined : requested;
|
|
}
|
|
|
|
export function ChatMessageActions({
|
|
feedback,
|
|
copied = false,
|
|
canRegenerate,
|
|
onFeedback,
|
|
onCopy,
|
|
onRegenerate,
|
|
}: Readonly<{
|
|
feedback?: ChatMessageFeedback;
|
|
copied?: boolean;
|
|
/** Without `onRegenerate` the regenerate button is not rendered (rectification, BUG-1056). */
|
|
canRegenerate?: boolean;
|
|
onFeedback: (value: ChatMessageFeedback) => void;
|
|
onCopy: () => void;
|
|
onRegenerate?: () => void;
|
|
}>) {
|
|
return (
|
|
<div className="message-actions" aria-label="Agent 回答操作">
|
|
<button
|
|
aria-label="赞"
|
|
aria-pressed={feedback === "up"}
|
|
className={feedback === "up" ? "is-active" : ""}
|
|
title="赞"
|
|
type="button"
|
|
onClick={() => onFeedback("up")}
|
|
>
|
|
<ThumbsUp aria-hidden="true" />
|
|
</button>
|
|
<button
|
|
aria-label="踩"
|
|
aria-pressed={feedback === "down"}
|
|
className={feedback === "down" ? "is-active" : ""}
|
|
title="踩"
|
|
type="button"
|
|
onClick={() => onFeedback("down")}
|
|
>
|
|
<ThumbsDown aria-hidden="true" />
|
|
</button>
|
|
<button aria-label="复制回答" title="复制" type="button" onClick={onCopy}>
|
|
{copied ? <Check aria-hidden="true" /> : <Copy aria-hidden="true" />}
|
|
</button>
|
|
{onRegenerate && (
|
|
<button
|
|
aria-label="重新生成回答"
|
|
disabled={!canRegenerate}
|
|
title={canRegenerate ? "重新生成" : "只能重新生成最近一条回答"}
|
|
type="button"
|
|
onClick={onRegenerate}
|
|
>
|
|
<RotateCcw aria-hidden="true" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|