fix(chat): 重新生成前撤销旧消息
Build Push and Deploy / docker-image (push) Successful in 1m47s
Build Push and Deploy / deploy-fallback-log (push) Has been skipped

This commit is contained in:
2026-06-08 14:38:52 +08:00
parent f7cd5ebfa7
commit 40cc355fff
5 changed files with 45 additions and 7 deletions
+8 -7
View File
@@ -251,7 +251,7 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
</IconButton> </IconButton>
) )
) : null} ) : null}
<FormControl size="small" sx={{ minWidth: 102 }}> <FormControl size="small" sx={{ minWidth: 96 }}>
<Select <Select
value={approvalMode} value={approvalMode}
onChange={(event) => onChange={(event) =>
@@ -260,13 +260,13 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
disabled={isHydrating || isStreaming} disabled={isHydrating || isStreaming}
aria-label="权限批准模式" aria-label="权限批准模式"
renderValue={(val) => ( renderValue={(val) => (
<Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}> <Box sx={{ display: "flex", alignItems: "center", gap: 0.45 }}>
{val === "always" ? ( {val === "always" ? (
<AdminPanelSettingsRounded sx={{ fontSize: 16, color: "inherit" }} /> <AdminPanelSettingsRounded sx={{ fontSize: 18, color: "inherit" }} />
) : ( ) : (
<VerifiedUserRounded sx={{ fontSize: 16, color: "inherit" }} /> <VerifiedUserRounded sx={{ fontSize: 18, color: "inherit" }} />
)} )}
<Typography sx={{ fontSize: "0.78rem", fontWeight: 700, color: "inherit" }}> <Typography sx={{ fontSize: "0.75rem", fontWeight: 600, color: "inherit" }}>
{val === "always" ? "始终允许" : "请求批准"} {val === "always" ? "始终允许" : "请求批准"}
</Typography> </Typography>
</Box> </Box>
@@ -312,6 +312,7 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
py: 0, py: 0,
pl: 1, pl: 1,
pr: "28px !important", pr: "28px !important",
minHeight: 36,
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
}, },
@@ -326,14 +327,14 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
}} }}
> >
<MenuItem value="request"> <MenuItem value="request">
<VerifiedUserRounded className="icon" sx={{ mr: 1.5, mt: 0.2, fontSize: 18, color: "text.secondary" }} /> <VerifiedUserRounded className="icon" sx={{ mr: 1.5, mt: 0.15, fontSize: 18, color: "text.secondary" }} />
<Box> <Box>
<Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2 }}></Typography> <Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2 }}></Typography>
<Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}></Typography> <Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}></Typography>
</Box> </Box>
</MenuItem> </MenuItem>
<MenuItem value="always"> <MenuItem value="always">
<AdminPanelSettingsRounded className="icon" sx={{ mr: 1.5, mt: 0.2, fontSize: 18, color: "text.secondary" }} /> <AdminPanelSettingsRounded className="icon" sx={{ mr: 1.5, mt: 0.15, fontSize: 18, color: "text.secondary" }} />
<Box> <Box>
<Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2 }}></Typography> <Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2 }}></Typography>
<Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}></Typography> <Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}></Typography>
@@ -521,4 +521,33 @@ describe("useAgentChatSession", () => {
expect.anything(), expect.anything(),
); );
}); });
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("重新分析压力异常");
});
await act(async () => {
await result.current.regenerate();
});
expect(streamAgentChat).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
message: "重新分析压力异常",
regenerateFromMessageIndex: 0,
}),
);
});
}); });
@@ -56,6 +56,7 @@ type UseAgentChatSessionOptions = {
type PromptRunOptions = { type PromptRunOptions = {
prompt: string; prompt: string;
sessionIdOverride?: string; sessionIdOverride?: string;
regenerateFromMessageIndex?: number;
preparedMessages?: Message[]; preparedMessages?: Message[];
userMessage?: Message; userMessage?: Message;
assistantMessage?: Message; assistantMessage?: Message;
@@ -609,6 +610,7 @@ export const useAgentChatSession = ({
async ({ async ({
prompt: rawPrompt, prompt: rawPrompt,
sessionIdOverride, sessionIdOverride,
regenerateFromMessageIndex,
preparedMessages, preparedMessages,
userMessage, userMessage,
assistantMessage, assistantMessage,
@@ -644,6 +646,7 @@ 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, {
@@ -991,6 +994,7 @@ export const useAgentChatSession = ({
setMessages(nextMessages); setMessages(nextMessages);
await runPrompt({ await runPrompt({
prompt: lastUserContent, prompt: lastUserContent,
regenerateFromMessageIndex: lastUserIndex,
preparedMessages: [ preparedMessages: [
...nextMessages, ...nextMessages,
nextUserMessage, nextUserMessage,
+1
View File
@@ -73,6 +73,7 @@ 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,
}), }),
}), }),
); );
+3
View File
@@ -71,6 +71,7 @@ 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;
}; };
@@ -286,6 +287,7 @@ export const streamAgentChat = async ({
sessionId, sessionId,
model, model,
approvalMode, approvalMode,
regenerateFromMessageIndex,
signal, signal,
onEvent, onEvent,
}: StreamOptions) => { }: StreamOptions) => {
@@ -305,6 +307,7 @@ 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",