fix(chat): remove regenerate action
This commit is contained in:
@@ -15,7 +15,6 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import ContentCopyRounded from "@mui/icons-material/ContentCopyRounded";
|
import ContentCopyRounded from "@mui/icons-material/ContentCopyRounded";
|
||||||
import RefreshRounded from "@mui/icons-material/RefreshRounded";
|
|
||||||
import { TbArrowsSplit2 } from "react-icons/tb";
|
import { TbArrowsSplit2 } from "react-icons/tb";
|
||||||
import type { PermissionReply } from "@/lib/chatStream";
|
import type { PermissionReply } from "@/lib/chatStream";
|
||||||
import {
|
import {
|
||||||
@@ -46,7 +45,6 @@ type AgentTurnProps = {
|
|||||||
onResume: () => void;
|
onResume: () => void;
|
||||||
onStopSpeech: () => void;
|
onStopSpeech: () => void;
|
||||||
isTtsSupported: boolean;
|
isTtsSupported: boolean;
|
||||||
onRegenerate: (messageId: string) => void;
|
|
||||||
onCreateBranch: (messageId: string) => void;
|
onCreateBranch: (messageId: string) => void;
|
||||||
onReplyPermission: (requestId: string, reply: PermissionReply) => void;
|
onReplyPermission: (requestId: string, reply: PermissionReply) => void;
|
||||||
onReplyQuestion: (requestId: string, answers: string[][]) => void;
|
onReplyQuestion: (requestId: string, answers: string[][]) => void;
|
||||||
@@ -62,7 +60,6 @@ export const AgentTurn = React.memo(
|
|||||||
onResume,
|
onResume,
|
||||||
onStopSpeech,
|
onStopSpeech,
|
||||||
isTtsSupported,
|
isTtsSupported,
|
||||||
onRegenerate,
|
|
||||||
onCreateBranch,
|
onCreateBranch,
|
||||||
onReplyPermission,
|
onReplyPermission,
|
||||||
onReplyQuestion,
|
onReplyQuestion,
|
||||||
@@ -316,18 +313,6 @@ export const AgentTurn = React.memo(
|
|||||||
<ContentCopyRounded sx={{ fontSize: 16 }} />
|
<ContentCopyRounded sx={{ fontSize: 16 }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="重新生成">
|
|
||||||
<IconButton
|
|
||||||
size="small"
|
|
||||||
aria-label="重新生成"
|
|
||||||
onClick={() => {
|
|
||||||
onRegenerate(message.id);
|
|
||||||
}}
|
|
||||||
sx={{ width: 28, height: 28, color: "text.secondary", "&:hover": { color: "#00acc1", bgcolor: alpha("#00acc1", 0.1) } }}
|
|
||||||
>
|
|
||||||
<RefreshRounded sx={{ fontSize: 16 }} />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="拆分为新会话">
|
<Tooltip title="拆分为新会话">
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ describe("AgentWorkspace", () => {
|
|||||||
onResumeSpeech: jest.fn(),
|
onResumeSpeech: jest.fn(),
|
||||||
onStopSpeech: jest.fn(),
|
onStopSpeech: jest.fn(),
|
||||||
isTtsSupported: false,
|
isTtsSupported: false,
|
||||||
onRegenerate: jest.fn(),
|
|
||||||
onCreateBranch: jest.fn(),
|
onCreateBranch: jest.fn(),
|
||||||
onReplyPermission: jest.fn(),
|
onReplyPermission: jest.fn(),
|
||||||
onReplyQuestion: jest.fn(),
|
onReplyQuestion: jest.fn(),
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ type AgentWorkspaceProps = {
|
|||||||
onResumeSpeech: () => void;
|
onResumeSpeech: () => void;
|
||||||
onStopSpeech: () => void;
|
onStopSpeech: () => void;
|
||||||
isTtsSupported: boolean;
|
isTtsSupported: boolean;
|
||||||
onRegenerate: (messageId: string) => void;
|
|
||||||
onCreateBranch: (messageId: string) => void;
|
onCreateBranch: (messageId: string) => void;
|
||||||
onReplyPermission: (requestId: string, reply: PermissionReply) => void;
|
onReplyPermission: (requestId: string, reply: PermissionReply) => void;
|
||||||
onReplyQuestion: (requestId: string, answers: string[][]) => void;
|
onReplyQuestion: (requestId: string, answers: string[][]) => void;
|
||||||
@@ -44,7 +43,6 @@ type TurnListProps = {
|
|||||||
onResumeSpeech: () => void;
|
onResumeSpeech: () => void;
|
||||||
onStopSpeech: () => void;
|
onStopSpeech: () => void;
|
||||||
isTtsSupported: boolean;
|
isTtsSupported: boolean;
|
||||||
onRegenerate: (messageId: string) => void;
|
|
||||||
onCreateBranch: (messageId: string) => void;
|
onCreateBranch: (messageId: string) => void;
|
||||||
onReplyPermission: (requestId: string, reply: PermissionReply) => void;
|
onReplyPermission: (requestId: string, reply: PermissionReply) => void;
|
||||||
onReplyQuestion: (requestId: string, answers: string[][]) => void;
|
onReplyQuestion: (requestId: string, answers: string[][]) => void;
|
||||||
@@ -64,7 +62,6 @@ const TurnListInner = ({
|
|||||||
onResumeSpeech,
|
onResumeSpeech,
|
||||||
onStopSpeech,
|
onStopSpeech,
|
||||||
isTtsSupported,
|
isTtsSupported,
|
||||||
onRegenerate,
|
|
||||||
onCreateBranch,
|
onCreateBranch,
|
||||||
onReplyPermission,
|
onReplyPermission,
|
||||||
onReplyQuestion,
|
onReplyQuestion,
|
||||||
@@ -82,7 +79,6 @@ const TurnListInner = ({
|
|||||||
onResume={onResumeSpeech}
|
onResume={onResumeSpeech}
|
||||||
onStopSpeech={onStopSpeech}
|
onStopSpeech={onStopSpeech}
|
||||||
isTtsSupported={isTtsSupported}
|
isTtsSupported={isTtsSupported}
|
||||||
onRegenerate={onRegenerate}
|
|
||||||
onCreateBranch={onCreateBranch}
|
onCreateBranch={onCreateBranch}
|
||||||
onReplyPermission={onReplyPermission}
|
onReplyPermission={onReplyPermission}
|
||||||
onReplyQuestion={onReplyQuestion}
|
onReplyQuestion={onReplyQuestion}
|
||||||
@@ -104,7 +100,6 @@ const TurnList = React.memo(
|
|||||||
prevProps.onResumeSpeech === nextProps.onResumeSpeech &&
|
prevProps.onResumeSpeech === nextProps.onResumeSpeech &&
|
||||||
prevProps.onStopSpeech === nextProps.onStopSpeech &&
|
prevProps.onStopSpeech === nextProps.onStopSpeech &&
|
||||||
prevProps.isTtsSupported === nextProps.isTtsSupported &&
|
prevProps.isTtsSupported === nextProps.isTtsSupported &&
|
||||||
prevProps.onRegenerate === nextProps.onRegenerate &&
|
|
||||||
prevProps.onCreateBranch === nextProps.onCreateBranch &&
|
prevProps.onCreateBranch === nextProps.onCreateBranch &&
|
||||||
prevProps.onReplyPermission === nextProps.onReplyPermission &&
|
prevProps.onReplyPermission === nextProps.onReplyPermission &&
|
||||||
prevProps.onReplyQuestion === nextProps.onReplyQuestion &&
|
prevProps.onReplyQuestion === nextProps.onReplyQuestion &&
|
||||||
@@ -238,7 +233,6 @@ export const AgentWorkspace = ({
|
|||||||
onResumeSpeech,
|
onResumeSpeech,
|
||||||
onStopSpeech,
|
onStopSpeech,
|
||||||
isTtsSupported,
|
isTtsSupported,
|
||||||
onRegenerate,
|
|
||||||
onCreateBranch,
|
onCreateBranch,
|
||||||
onReplyPermission,
|
onReplyPermission,
|
||||||
onReplyQuestion,
|
onReplyQuestion,
|
||||||
@@ -287,7 +281,6 @@ export const AgentWorkspace = ({
|
|||||||
onResumeSpeech={onResumeSpeech}
|
onResumeSpeech={onResumeSpeech}
|
||||||
onStopSpeech={onStopSpeech}
|
onStopSpeech={onStopSpeech}
|
||||||
isTtsSupported={isTtsSupported}
|
isTtsSupported={isTtsSupported}
|
||||||
onRegenerate={onRegenerate}
|
|
||||||
onCreateBranch={onCreateBranch}
|
onCreateBranch={onCreateBranch}
|
||||||
onReplyPermission={onReplyPermission}
|
onReplyPermission={onReplyPermission}
|
||||||
onReplyQuestion={onReplyQuestion}
|
onReplyQuestion={onReplyQuestion}
|
||||||
@@ -304,7 +297,6 @@ export const AgentWorkspace = ({
|
|||||||
onResumeSpeech={onResumeSpeech}
|
onResumeSpeech={onResumeSpeech}
|
||||||
onStopSpeech={onStopSpeech}
|
onStopSpeech={onStopSpeech}
|
||||||
isTtsSupported={isTtsSupported}
|
isTtsSupported={isTtsSupported}
|
||||||
onRegenerate={onRegenerate}
|
|
||||||
onCreateBranch={onCreateBranch}
|
onCreateBranch={onCreateBranch}
|
||||||
onReplyPermission={onReplyPermission}
|
onReplyPermission={onReplyPermission}
|
||||||
onReplyQuestion={onReplyQuestion}
|
onReplyQuestion={onReplyQuestion}
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
|
|||||||
isStreaming,
|
isStreaming,
|
||||||
sessionTitle,
|
sessionTitle,
|
||||||
sendPrompt,
|
sendPrompt,
|
||||||
regenerate,
|
|
||||||
createBranch,
|
createBranch,
|
||||||
abort,
|
abort,
|
||||||
replyPermission,
|
replyPermission,
|
||||||
@@ -352,7 +351,6 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
|
|||||||
onResumeSpeech={handleResumeSpeech}
|
onResumeSpeech={handleResumeSpeech}
|
||||||
onStopSpeech={handleStopSpeech}
|
onStopSpeech={handleStopSpeech}
|
||||||
isTtsSupported={isTtsSupported}
|
isTtsSupported={isTtsSupported}
|
||||||
onRegenerate={regenerate}
|
|
||||||
onCreateBranch={createBranch}
|
onCreateBranch={createBranch}
|
||||||
onReplyPermission={replyPermission}
|
onReplyPermission={replyPermission}
|
||||||
onReplyQuestion={replyQuestion}
|
onReplyQuestion={replyQuestion}
|
||||||
|
|||||||
@@ -359,84 +359,6 @@ describe("useAgentChatSession actions", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("asks the backend to undo the previous user turn before regenerating", async () => {
|
|
||||||
listChatSessions.mockResolvedValue([]);
|
|
||||||
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useAgentChatSession({
|
|
||||||
projectId: "project-1",
|
|
||||||
onToolCall: jest.fn(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(result.current.isHydrating).toBe(false));
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await result.current.sendPrompt("重新分析压力异常");
|
|
||||||
});
|
|
||||||
const assistantMessageId = result.current.messages[1]?.id ?? "";
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await result.current.regenerate(assistantMessageId);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(streamAgentChat).toHaveBeenNthCalledWith(
|
|
||||||
2,
|
|
||||||
expect.objectContaining({
|
|
||||||
message: "重新分析压力异常",
|
|
||||||
regenerateFromMessageIndex: 0,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("replaces the current chain when regenerating a middle assistant message", async () => {
|
|
||||||
listChatSessions.mockResolvedValue([]);
|
|
||||||
|
|
||||||
const { result } = renderHook(() =>
|
|
||||||
useAgentChatSession({
|
|
||||||
projectId: "project-1",
|
|
||||||
onToolCall: jest.fn(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(result.current.isHydrating).toBe(false));
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await result.current.sendPrompt("第一轮");
|
|
||||||
});
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await result.current.sendPrompt("第二轮");
|
|
||||||
});
|
|
||||||
|
|
||||||
const firstAssistantMessageId = result.current.messages[1]?.id ?? "";
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await result.current.regenerate(firstAssistantMessageId);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.messages).toHaveLength(2);
|
|
||||||
expect(result.current.messages[0]).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
role: "user",
|
|
||||||
content: "第一轮",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
expect(result.current.messages[1]).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
role: "assistant",
|
|
||||||
content: "",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
expect(streamAgentChat).toHaveBeenNthCalledWith(
|
|
||||||
3,
|
|
||||||
expect.objectContaining({
|
|
||||||
message: "第一轮",
|
|
||||||
regenerateFromMessageIndex: 0,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("forks a copied conversation from an assistant message", async () => {
|
it("forks a copied conversation from an assistant message", async () => {
|
||||||
listChatSessions.mockResolvedValue([]);
|
listChatSessions.mockResolvedValue([]);
|
||||||
|
|
||||||
|
|||||||
@@ -407,7 +407,6 @@ export const useAgentChatSession = ({
|
|||||||
async ({
|
async ({
|
||||||
prompt: rawPrompt,
|
prompt: rawPrompt,
|
||||||
sessionIdOverride,
|
sessionIdOverride,
|
||||||
regenerateFromMessageIndex,
|
|
||||||
preparedMessages,
|
preparedMessages,
|
||||||
userMessage,
|
userMessage,
|
||||||
assistantMessage,
|
assistantMessage,
|
||||||
@@ -442,7 +441,6 @@ export const useAgentChatSession = ({
|
|||||||
sessionId: sessionIdOverride ?? sessionIdRef.current,
|
sessionId: sessionIdOverride ?? sessionIdRef.current,
|
||||||
model: getModel?.(),
|
model: getModel?.(),
|
||||||
approvalMode: getApprovalMode?.(),
|
approvalMode: getApprovalMode?.(),
|
||||||
regenerateFromMessageIndex,
|
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
onEvent: (event) =>
|
onEvent: (event) =>
|
||||||
applyStreamEvent(event, {
|
applyStreamEvent(event, {
|
||||||
@@ -893,43 +891,6 @@ export const useAgentChatSession = ({
|
|||||||
[isHydrating, messages],
|
[isHydrating, messages],
|
||||||
);
|
);
|
||||||
|
|
||||||
const regenerate = useCallback(async (messageId: string) => {
|
|
||||||
if (isHydrating || isStreaming || messages.length === 0) return;
|
|
||||||
|
|
||||||
const targetAssistantIndex = messages.findIndex(
|
|
||||||
(message) => message.id === messageId && message.role === "assistant",
|
|
||||||
);
|
|
||||||
if (targetAssistantIndex < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let targetUserIndex = targetAssistantIndex - 1;
|
|
||||||
while (targetUserIndex >= 0 && messages[targetUserIndex].role !== "user") {
|
|
||||||
targetUserIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetUserIndex < 0) return;
|
|
||||||
|
|
||||||
const targetUser = messages[targetUserIndex];
|
|
||||||
const targetUserContent = targetUser.content;
|
|
||||||
const nextMessages = cloneMessages(messages.slice(0, targetUserIndex));
|
|
||||||
const nextUserMessage = createUserMessage(targetUserContent);
|
|
||||||
const nextAssistantMessage = createAssistantMessage();
|
|
||||||
|
|
||||||
setMessages(nextMessages);
|
|
||||||
await runPrompt({
|
|
||||||
prompt: targetUserContent,
|
|
||||||
regenerateFromMessageIndex: targetUserIndex,
|
|
||||||
preparedMessages: [
|
|
||||||
...nextMessages,
|
|
||||||
nextUserMessage,
|
|
||||||
nextAssistantMessage,
|
|
||||||
],
|
|
||||||
userMessage: nextUserMessage,
|
|
||||||
assistantMessage: nextAssistantMessage,
|
|
||||||
});
|
|
||||||
}, [isHydrating, isStreaming, messages, runPrompt]);
|
|
||||||
|
|
||||||
const createBranch = useCallback(
|
const createBranch = useCallback(
|
||||||
async (messageId: string) => {
|
async (messageId: string) => {
|
||||||
if (isHydrating || isStreaming) return;
|
if (isHydrating || isStreaming) return;
|
||||||
@@ -975,7 +936,6 @@ export const useAgentChatSession = ({
|
|||||||
sessionTitle,
|
sessionTitle,
|
||||||
sessionId,
|
sessionId,
|
||||||
sendPrompt,
|
sendPrompt,
|
||||||
regenerate,
|
|
||||||
createBranch,
|
createBranch,
|
||||||
abort,
|
abort,
|
||||||
replyPermission,
|
replyPermission,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export type UseAgentChatSessionOptions = {
|
|||||||
export type PromptRunOptions = {
|
export type PromptRunOptions = {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
sessionIdOverride?: string;
|
sessionIdOverride?: string;
|
||||||
regenerateFromMessageIndex?: number;
|
|
||||||
preparedMessages?: Message[];
|
preparedMessages?: Message[];
|
||||||
userMessage?: Message;
|
userMessage?: Message;
|
||||||
assistantMessage?: Message;
|
assistantMessage?: Message;
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ describe("streamAgentChat", () => {
|
|||||||
session_id: undefined,
|
session_id: undefined,
|
||||||
model: "deepseek/deepseek-v4-pro",
|
model: "deepseek/deepseek-v4-pro",
|
||||||
approval_mode: undefined,
|
approval_mode: undefined,
|
||||||
regenerate_from_message_index: undefined,
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ type StreamOptions = {
|
|||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
model?: AgentModel;
|
model?: AgentModel;
|
||||||
approvalMode?: AgentApprovalMode;
|
approvalMode?: AgentApprovalMode;
|
||||||
regenerateFromMessageIndex?: number;
|
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
onEvent: (event: StreamEvent) => void;
|
onEvent: (event: StreamEvent) => void;
|
||||||
};
|
};
|
||||||
@@ -460,7 +459,6 @@ export const streamAgentChat = async ({
|
|||||||
sessionId,
|
sessionId,
|
||||||
model,
|
model,
|
||||||
approvalMode,
|
approvalMode,
|
||||||
regenerateFromMessageIndex,
|
|
||||||
signal,
|
signal,
|
||||||
onEvent,
|
onEvent,
|
||||||
}: StreamOptions) => {
|
}: StreamOptions) => {
|
||||||
@@ -480,7 +478,6 @@ export const streamAgentChat = async ({
|
|||||||
session_id: sessionId,
|
session_id: sessionId,
|
||||||
model,
|
model,
|
||||||
approval_mode: approvalMode,
|
approval_mode: approvalMode,
|
||||||
regenerate_from_message_index: regenerateFromMessageIndex,
|
|
||||||
}),
|
}),
|
||||||
projectHeaderMode: "include",
|
projectHeaderMode: "include",
|
||||||
userHeaderMode: "include",
|
userHeaderMode: "include",
|
||||||
|
|||||||
Reference in New Issue
Block a user