fix: Retry sends message with retry flag to avoid duplicate storage
Added retry:true flag to sendChatMessage input. Server skips storing the user message when retry is set. Frontend uses a dedicated retryLastMessage function that skips the optimistic message add.
This commit is contained in:
@@ -26,6 +26,7 @@ export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
targetId: z.string().min(1),
|
targetId: z.string().min(1),
|
||||||
initiativeId: z.string().min(1),
|
initiativeId: z.string().min(1),
|
||||||
message: z.string().min(1),
|
message: z.string().min(1),
|
||||||
|
retry: z.boolean().optional(),
|
||||||
provider: z.string().optional(),
|
provider: z.string().optional(),
|
||||||
}))
|
}))
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
@@ -49,18 +50,20 @@ export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store user message
|
// Store user message (skip on retry — message already exists)
|
||||||
await chatRepo.createMessage({
|
if (!input.retry) {
|
||||||
chatSessionId: session.id,
|
await chatRepo.createMessage({
|
||||||
role: 'user',
|
chatSessionId: session.id,
|
||||||
content: input.message,
|
role: 'user',
|
||||||
});
|
content: input.message,
|
||||||
|
});
|
||||||
|
|
||||||
ctx.eventBus.emit({
|
ctx.eventBus.emit({
|
||||||
type: 'chat:message_created' as const,
|
type: 'chat:message_created' as const,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
payload: { chatSessionId: session.id, role: 'user' as const },
|
payload: { chatSessionId: session.id, role: 'user' as const },
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check if agent exists and is waiting for input
|
// Check if agent exists and is waiting for input
|
||||||
if (session.agentId) {
|
if (session.agentId) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ function ChatSlideOverInner({
|
|||||||
initiativeId: string;
|
initiativeId: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { messages, agentStatus, agentError, sendMessage, closeSession, isSending } =
|
const { messages, agentStatus, agentError, sendMessage, retryLastMessage, closeSession, isSending } =
|
||||||
useChatSession(target.type, target.id, initiativeId);
|
useChatSession(target.type, target.id, initiativeId);
|
||||||
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -145,11 +145,7 @@ function ChatSlideOverInner({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-6 gap-1.5 text-xs"
|
className="h-6 gap-1.5 text-xs"
|
||||||
onClick={() => {
|
onClick={retryLastMessage}
|
||||||
// Re-send the last user message to retry
|
|
||||||
const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
|
|
||||||
if (lastUserMsg) sendMessage(lastUserMsg.content);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<RotateCcw className="h-3 w-3" />
|
<RotateCcw className="h-3 w-3" />
|
||||||
Retry
|
Retry
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export interface UseChatSessionResult {
|
|||||||
agentStatus: ChatAgentStatus;
|
agentStatus: ChatAgentStatus;
|
||||||
agentError: string | null;
|
agentError: string | null;
|
||||||
sendMessage: (message: string) => void;
|
sendMessage: (message: string) => void;
|
||||||
|
retryLastMessage: () => void;
|
||||||
closeSession: () => void;
|
closeSession: () => void;
|
||||||
isSending: boolean;
|
isSending: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
@@ -163,6 +164,21 @@ export function useChatSession(
|
|||||||
[targetType, targetId, initiativeId],
|
[targetType, targetId, initiativeId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const messagesRef = useRef(messages);
|
||||||
|
messagesRef.current = messages;
|
||||||
|
|
||||||
|
const retryLastMessage = useCallback(() => {
|
||||||
|
const lastUserMsg = [...messagesRef.current].reverse().find(m => m.role === 'user');
|
||||||
|
if (!lastUserMsg) return;
|
||||||
|
sendMutateRef.current({
|
||||||
|
targetType,
|
||||||
|
targetId,
|
||||||
|
initiativeId,
|
||||||
|
message: lastUserMsg.content,
|
||||||
|
retry: true,
|
||||||
|
});
|
||||||
|
}, [targetType, targetId, initiativeId]);
|
||||||
|
|
||||||
const closeSession = useCallback(() => {
|
const closeSession = useCallback(() => {
|
||||||
const s = sessionRef.current;
|
const s = sessionRef.current;
|
||||||
if (s) {
|
if (s) {
|
||||||
@@ -179,6 +195,7 @@ export function useChatSession(
|
|||||||
agentStatus,
|
agentStatus,
|
||||||
agentError,
|
agentError,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
|
retryLastMessage,
|
||||||
closeSession,
|
closeSession,
|
||||||
isSending,
|
isSending,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|||||||
Reference in New Issue
Block a user