fix(consult): treat pinched answers as failed and restore reply actions
Incomplete Flash generations were billed as completed consultations. Fail those runs, keep the partial text, and reuse the rectification like/copy/rerun bar on ordinary chat replies. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"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;
|
||||
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>
|
||||
<button
|
||||
aria-label="重新生成回答"
|
||||
disabled={!canRegenerate}
|
||||
title={canRegenerate ? "重新生成" : "只能重新生成最近一条回答"}
|
||||
type="button"
|
||||
onClick={onRegenerate}
|
||||
>
|
||||
<RotateCcw aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowUp, Check, Copy, RotateCcw, ThumbsDown, ThumbsUp } from "lucide-react";
|
||||
import { ArrowUp } from "lucide-react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { parseAgentReply } from "@/lib/agent-reply";
|
||||
import type { ChatMessage, ChatMessageView } from "@/lib/chat-message-view";
|
||||
@@ -23,6 +23,10 @@ import {
|
||||
} from "@/lib/rectification-agentic/v9/public-receipt";
|
||||
import type { PublicLanguageModel } from "@/lib/public-models";
|
||||
import { ChatMessageRow } from "./chat-message-row";
|
||||
import {
|
||||
ChatMessageActions,
|
||||
toggleChatMessageFeedback,
|
||||
} from "./chat-message-actions";
|
||||
import { CompletedActivityReceipt } from "./completed-activity-receipt";
|
||||
import { ModelSelector } from "./model-selector";
|
||||
import { Button } from "./ui/button";
|
||||
@@ -125,7 +129,7 @@ export function toggleRectificationFeedback(
|
||||
current: "up" | "down" | undefined,
|
||||
requested: "up" | "down",
|
||||
): "up" | "down" | undefined {
|
||||
return current === requested ? undefined : requested;
|
||||
return toggleChatMessageFeedback(current, requested);
|
||||
}
|
||||
|
||||
function activityPhase(tool: PublicRectificationTool): NonNullable<ChatMessageView["activity"]>["phase"] {
|
||||
@@ -584,48 +588,17 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
||||
/>
|
||||
)}
|
||||
{showActions && !regenerating && (
|
||||
<div className="rectification-message-actions" aria-label="Agent 回答操作">
|
||||
<button
|
||||
aria-label="赞"
|
||||
aria-pressed={feedback[message.renderKey] === "up"}
|
||||
className={feedback[message.renderKey] === "up" ? "is-active" : ""}
|
||||
title="赞"
|
||||
type="button"
|
||||
onClick={() => setFeedback((current) => ({
|
||||
...current,
|
||||
[message.renderKey]: toggleRectificationFeedback(current[message.renderKey], "up"),
|
||||
}))}
|
||||
>
|
||||
<ThumbsUp aria-hidden="true" />
|
||||
</button>
|
||||
<button
|
||||
aria-label="踩"
|
||||
aria-pressed={feedback[message.renderKey] === "down"}
|
||||
className={feedback[message.renderKey] === "down" ? "is-active" : ""}
|
||||
title="踩"
|
||||
type="button"
|
||||
onClick={() => setFeedback((current) => ({
|
||||
...current,
|
||||
[message.renderKey]: toggleRectificationFeedback(current[message.renderKey], "down"),
|
||||
}))}
|
||||
>
|
||||
<ThumbsDown aria-hidden="true" />
|
||||
</button>
|
||||
<button aria-label="复制回答" title="复制" type="button" onClick={() => void copyMessage(message)}>
|
||||
{copiedMessageKey === message.renderKey
|
||||
? <Check aria-hidden="true" />
|
||||
: <Copy aria-hidden="true" />}
|
||||
</button>
|
||||
<button
|
||||
aria-label="重新生成回答"
|
||||
disabled={!canRegenerate}
|
||||
title={canRegenerate ? "重新生成" : "只能重新生成最近一条回答"}
|
||||
type="button"
|
||||
onClick={() => void regenerateMessage(message)}
|
||||
>
|
||||
<RotateCcw aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<ChatMessageActions
|
||||
feedback={feedback[message.renderKey]}
|
||||
copied={copiedMessageKey === message.renderKey}
|
||||
canRegenerate={canRegenerate}
|
||||
onFeedback={(requested) => setFeedback((current) => ({
|
||||
...current,
|
||||
[message.renderKey]: toggleRectificationFeedback(current[message.renderKey], requested),
|
||||
}))}
|
||||
onCopy={() => void copyMessage(message)}
|
||||
onRegenerate={() => void regenerateMessage(message)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user