Skip to content

Commit d3a08d0

Browse files
fix: session rename create new session instead of remaining existing one (#11576)
* fix(frontend): resolve session rename bugs and prevent message loss * fix(playground): Fix session rename not showing messages immediately * [autofix.ci] apply automated fixes * fix(playground): Eliminate duplicate logic in session-selector --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent b7e834b commit d3a08d0

12 files changed

Lines changed: 191 additions & 55 deletions

File tree

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-header.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export function ChatHeader({
119119
onMessageLogs={onMessageLogs}
120120
onClearChat={handleClearChat}
121121
onDelete={handleDeleteSessionInternal}
122+
showRename={!isDefaultSession}
122123
showClearChat={isDefaultSession}
123124
showDelete={!isDefaultSession}
124125
side="bottom"

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-sidebar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface ChatSidebarProps {
1313
currentSessionId?: string;
1414
onDeleteSession?: (sessionId: string) => void;
1515
onOpenLogs?: (sessionId: string) => void;
16+
renameLocalSession?: (oldSessionId: string, newSessionId: string) => void;
1617
}
1718

1819
export function ChatSidebar({
@@ -22,9 +23,13 @@ export function ChatSidebar({
2223
currentSessionId,
2324
onDeleteSession,
2425
onOpenLogs,
26+
renameLocalSession,
2527
}: ChatSidebarProps) {
2628
const currentFlowId = useGetFlowId();
27-
const { handleDelete } = useEditSessionInfo({ flowId: currentFlowId });
29+
const { handleDelete, handleRename } = useEditSessionInfo({
30+
flowId: currentFlowId,
31+
renameLocalSession,
32+
});
2833

2934
const sessionIds = useMemo(() => sessions, [sessions]);
3035

@@ -81,6 +86,7 @@ export function ChatSidebar({
8186
isVisible={visibleSession === session}
8287
updateVisibleSession={handleSessionClick}
8388
inspectSession={onOpenLogs}
89+
handleRename={handleRename}
8490
setActiveSession={() => {
8591
// TODO: Implement active session
8692
}}

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-more-menu.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface SessionMoreMenuProps {
1515
onDelete: () => void;
1616
onClearChat?: () => void;
1717
showMessageLogs?: boolean;
18+
showRename?: boolean;
1819
showDelete?: boolean;
1920
showClearChat?: boolean;
2021
// Positioning props
@@ -36,6 +37,7 @@ export function SessionMoreMenu({
3637
onDelete,
3738
onClearChat,
3839
showMessageLogs = true,
40+
showRename = true,
3941
showDelete = true,
4042
showClearChat = false,
4143
side = "bottom",
@@ -102,15 +104,17 @@ export function SessionMoreMenu({
102104
sideOffset={sideOffset}
103105
className={cn("p-0", contentClassName)}
104106
>
105-
<SelectItem value="rename" className="session-more-menu-item">
106-
<div className="flex items-center">
107-
<ForwardedIconComponent
108-
name="SquarePen"
109-
className="mr-2 h-4 w-4"
110-
/>
111-
Rename
112-
</div>
113-
</SelectItem>
107+
{showRename && (
108+
<SelectItem value="rename" className="session-more-menu-item">
109+
<div className="flex items-center">
110+
<ForwardedIconComponent
111+
name="SquarePen"
112+
className="mr-2 h-4 w-4"
113+
/>
114+
Rename
115+
</div>
116+
</SelectItem>
117+
)}
114118
{showMessageLogs && (
115119
<SelectItem value="messageLogs" className="session-more-menu-item">
116120
<div className="flex items-center">

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-selector.tsx

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface SessionSelectorProps {
1818
setSelectedView?: (view: { type: string; id: string } | undefined) => void;
1919
playgroundPage?: boolean;
2020
setActiveSession?: (session: string) => void;
21+
handleRename?: (oldSessionId: string, newSessionId: string) => Promise<void>;
2122
}
2223

2324
export function SessionSelector({
@@ -32,6 +33,7 @@ export function SessionSelector({
3233
setSelectedView,
3334
playgroundPage = false,
3435
setActiveSession,
36+
handleRename,
3537
}: SessionSelectorProps) {
3638
const [isEditing, setIsEditing] = useState(false);
3739
const { mutate: updateSessionName } = useUpdateSessionName();
@@ -43,29 +45,47 @@ export function SessionSelector({
4345
setIsEditing(true);
4446
};
4547

46-
const handleRenameSave = (newSessionId: string) => {
48+
const handleRenameSave = async (newSessionId: string) => {
4749
setIsEditing(false);
4850
const trimmed = newSessionId.trim();
4951
if (!trimmed || trimmed === session) return;
50-
updateSessionName(
51-
{ old_session_id: session, new_session_id: trimmed },
52-
{
53-
onSuccess: () => {
54-
if (isVisible) {
52+
53+
// Use handleRename if provided (from sidebar), otherwise use mutation directly (from header)
54+
if (handleRename) {
55+
await handleRename(session, trimmed);
56+
updateVisibleSession(trimmed);
57+
if (
58+
selectedView?.type === "Session" &&
59+
selectedView?.id === session &&
60+
setSelectedView
61+
) {
62+
setSelectedView({ type: "Session", id: trimmed });
63+
}
64+
} else {
65+
// Wait for the mutation to complete before updating visible session
66+
await updateSessionName(
67+
{ old_session_id: session, new_session_id: trimmed },
68+
{
69+
onSuccess: () => {
70+
// Update visible session after rename is complete
5571
updateVisibleSession(trimmed);
56-
}
57-
if (
58-
selectedView?.type === "Session" &&
59-
selectedView?.id === session &&
60-
setSelectedView
61-
) {
62-
setSelectedView({ type: "Session", id: trimmed });
63-
}
72+
if (
73+
selectedView?.type === "Session" &&
74+
selectedView?.id === session &&
75+
setSelectedView
76+
) {
77+
setSelectedView({ type: "Session", id: trimmed });
78+
}
79+
},
6480
},
65-
},
66-
);
81+
);
82+
}
6783
};
6884

85+
// Default session (flowId) cannot be renamed or deleted
86+
const isDefaultSession = session === currentFlowId;
87+
const canModifySession = !isDefaultSession;
88+
6989
return (
7090
<div
7191
data-testid="session-selector"
@@ -100,7 +120,7 @@ export function SessionSelector({
100120
<ShadTooltip styleClasses="z-50" content={session}>
101121
<div className="relative w-full overflow-hidden">
102122
<span className="w-full truncate bg-transparent text-mmd">
103-
{session === currentFlowId ? "Default Session" : session}
123+
{isDefaultSession ? "Default Session" : session}
104124
</span>
105125
</div>
106126
</ShadTooltip>
@@ -111,7 +131,8 @@ export function SessionSelector({
111131
onRename={handleEditClick}
112132
onMessageLogs={() => inspectSession?.(session)}
113133
onDelete={() => deleteSession(session)}
114-
showDelete={session !== currentFlowId}
134+
showRename={canModifySession}
135+
showDelete={canModifySession}
115136
side="bottom"
116137
align="end"
117138
sideOffset={4}

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/hooks/use-edit-session-info.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ import { useGetSessionsFromFlowQuery } from "@/controllers/API/queries/messages/
33
import { useUpdateSessionName } from "@/controllers/API/queries/messages/use-rename-session";
44
import { usePlaygroundStore } from "@/stores/playgroundStore";
55

6+
const LOCAL_SESSIONS_STORAGE_KEY = (flowId: string) =>
7+
`langflow_local_sessions_${flowId}`;
8+
69
export const useEditSessionInfo = ({
710
flowId,
811
dbSessions: providedDbSessions,
12+
renameLocalSession,
913
}: {
1014
flowId?: string;
1115
dbSessions?: string[];
16+
renameLocalSession?: (oldSessionId: string, newSessionId: string) => void;
1217
}) => {
1318
const setSelectedSession = usePlaygroundStore(
1419
(state) => state.setSelectedSession,
@@ -41,12 +46,18 @@ export const useEditSessionInfo = ({
4146
};
4247

4348
const handleRename = async (sessionId: string, newSessionId: string) => {
44-
if (dbSessions.includes(sessionId)) {
45-
await updateSessionName({
46-
old_session_id: sessionId,
47-
new_session_id: newSessionId,
48-
});
49+
// Update session name via API or sessionStorage
50+
await updateSessionName({
51+
old_session_id: sessionId,
52+
new_session_id: newSessionId,
53+
});
54+
55+
// Update local sessions list using the provided function
56+
if (renameLocalSession) {
57+
renameLocalSession(sessionId, newSessionId);
4958
}
59+
60+
// Update selected session if the renamed session is currently selected
5061
if (flowId && sessionId === selectedSession) {
5162
setSelectedSession(newSessionId);
5263
}

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/hooks/use-get-add-sessions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface UseGetAddSessionsProps {
1111
type UseGetAddSessionsReturnType = (props: UseGetAddSessionsProps) => {
1212
addNewSession: (allSessions?: string[]) => string;
1313
removeLocalSession: (sessionId: string) => void;
14+
renameLocalSession: (oldSessionId: string, newSessionId: string) => void;
1415
sessions: string[];
1516
fetchedSessions: string[];
1617
};
@@ -138,11 +139,22 @@ export const useGetAddSessions: UseGetAddSessionsReturnType = ({
138139
});
139140
};
140141

142+
const renameLocalSession = (oldSessionId: string, newSessionId: string) => {
143+
setLocalSessions((prev) => {
144+
const updated = new Set(prev);
145+
// Remove old session name and add new one
146+
const hadOld = updated.delete(oldSessionId);
147+
updated.add(newSessionId);
148+
return updated;
149+
});
150+
};
151+
141152
const stableSessions = useMemo(() => [...sessions], [sessions]);
142153

143154
return {
144155
addNewSession,
145156
removeLocalSession,
157+
renameLocalSession,
146158
sessions: stableSessions,
147159
fetchedSessions,
148160
};

src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/hooks/use-chat-history.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export const useChatHistory = (visibleSession: string | null) => {
2929
queryFn: () => {
3030
// Return cached data immediately - this makes the query active and reactive
3131
// We're using useQuery purely as a subscription mechanism, not for fetching
32-
return queryClient.getQueryData<Message[]>(sessionCacheKey) || [];
32+
const cachedData =
33+
queryClient.getQueryData<Message[]>(sessionCacheKey) || [];
34+
return cachedData;
3335
},
3436
staleTime: Infinity, // Never refetch - updates come from setQueryData, not server
3537
gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes (allows cleanup of old sessions)
@@ -69,12 +71,13 @@ export const useChatHistory = (visibleSession: string | null) => {
6971
// In the default session, we show messages that have the same session_id as the flow_id
7072
// OR messages that have NO session_id (legacy behavior)
7173
if (visibleSession === currentFlowId) {
72-
return (
74+
const matches =
7375
isCurrentFlow &&
74-
(message.session_id === visibleSession || !message.session_id)
75-
);
76+
(message.session_id === visibleSession || !message.session_id);
77+
return matches;
7678
}
77-
return isCurrentFlow && message.session_id === visibleSession;
79+
const matches = isCurrentFlow && message.session_id === visibleSession;
80+
return matches;
7881
})
7982
.map((message: Message) => {
8083
let files = message.files;
@@ -111,7 +114,8 @@ export const useChatHistory = (visibleSession: string | null) => {
111114
};
112115
});
113116

114-
return [...filteredMessages].sort(sortSenderMessages);
117+
const sorted = [...filteredMessages].sort(sortSenderMessages);
118+
return sorted;
115119
}, [messages, visibleSession, currentFlowId]);
116120

117121
return chatHistory;

src/frontend/src/components/core/playgroundComponent/chat-view/hooks/use-send-message.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@ export const useSendMessage = ({ sessionId }: UseSendMessageProps = {}) => {
3333
}): Promise<void> => {
3434
// Resolve session: sessionId prop > selectedSession > flowId (default)
3535
const actualSession = sessionId ?? selectedSession ?? flowId;
36-
37-
console.debug("[useSendMessage] invoked", {
38-
sessionId: actualSession,
39-
chatInputId,
40-
hasFiles: Boolean(files?.length),
41-
});
42-
4336
// Add placeholder user message immediately
4437
addUserMessage({
4538
id: null,

src/frontend/src/components/core/playgroundComponent/sliding-container/components/flow-page-sliding-container.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,20 @@ export function FlowPageSlidingContainerContent({
3737
);
3838
const [openLogsModal, setOpenLogsModal] = useState(false);
3939

40-
const { sessions, addNewSession, removeLocalSession, fetchedSessions } =
41-
useGetAddSessions({
42-
flowId: currentFlowId,
43-
currentSessionId,
44-
});
40+
const {
41+
sessions,
42+
addNewSession,
43+
removeLocalSession,
44+
renameLocalSession,
45+
fetchedSessions,
46+
} = useGetAddSessions({
47+
flowId: currentFlowId,
48+
currentSessionId,
49+
});
4550
const { handleDelete } = useEditSessionInfo({
4651
flowId: currentFlowId,
4752
dbSessions: fetchedSessions,
53+
renameLocalSession,
4854
});
4955

5056
// Ensure currentFlowId is always first in sessions list
@@ -157,6 +163,7 @@ export function FlowPageSlidingContainerContent({
157163
currentSessionId={currentSessionId}
158164
onDeleteSession={handleDeleteSession}
159165
onOpenLogs={handleOpenLogs}
166+
renameLocalSession={renameLocalSession}
160167
/>
161168
</div>
162169
</div>

0 commit comments

Comments
 (0)