Skip to content

Commit 858dbf7

Browse files
authored
Merge branch 'release-1.8.0' into fix-agent-component-watsonx
2 parents aa8f003 + 804f4b5 commit 858dbf7

9 files changed

Lines changed: 340 additions & 39 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export const useEditSessionInfo = ({
3737
const { mutate: deleteSession } = useDeleteSession();
3838

3939
const handleDelete = (sessionId: string) => {
40-
if (sessionId && dbSessions.includes(sessionId)) {
41-
deleteSession({ sessionId: sessionId });
40+
if (sessionId && flowId) {
41+
deleteSession({ sessionId: sessionId, flowId: flowId });
4242
}
4343
if (flowId && sessionId === selectedSession) {
4444
setSelectedSession(flowId);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export const useGetAddSessions: UseGetAddSessionsReturnType = ({
138138
};
139139

140140
const removeLocalSession = (sessionId: string) => {
141+
// Update state - the useEffect on line 67-77 will sync to sessionStorage
141142
setLocalSessions((prev) => {
142143
const updated = new Set(prev);
143144
updated.delete(sessionId);

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

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useGetFlowId } from "@/components/core/playgroundComponent/hooks/use-ge
44
import { useGetMessagesQuery } from "@/controllers/API/queries/messages";
55
import type { ChatMessageType } from "@/types/chat";
66
import type { Message } from "@/types/messages";
7+
import { isMessageForSession } from "../../utils/session-filter";
78
import sortSenderMessages from "../utils/sort-sender-messages";
89

910
export const useChatHistory = (visibleSession: string | null) => {
@@ -45,17 +46,20 @@ export const useChatHistory = (visibleSession: string | null) => {
4546
if (queryData && typeof queryData === "object" && "rows" in queryData) {
4647
const rowsData = queryData.rows as { data?: Message[] } | undefined;
4748
if (rowsData && typeof rowsData === "object" && "data" in rowsData) {
48-
const backendMessages = rowsData.data || [];
49+
const backendMessages = (rowsData.data || []).filter((msg: Message) =>
50+
isMessageForSession(msg, currentFlowId, visibleSession),
51+
);
52+
4953
const existingCache =
5054
queryClient.getQueryData<Message[]>(sessionCacheKey) || [];
5155

52-
// Only initialize if cache is empty and we have backend messages
56+
// Only initialize if cache is empty and we have backend messages for this session
5357
if (existingCache.length === 0 && backendMessages.length > 0) {
5458
queryClient.setQueryData(sessionCacheKey, backendMessages);
5559
}
5660
}
5761
}
58-
}, [queryData, queryClient, sessionCacheKey]);
62+
}, [queryData, queryClient, sessionCacheKey, currentFlowId, visibleSession]);
5963

6064
// Use session cache as the single source of truth
6165
// updateMessage and addUserMessage handle all updates (placeholders, streaming, etc.)
@@ -65,21 +69,10 @@ export const useChatHistory = (visibleSession: string | null) => {
6569
const chatHistory = useMemo(() => {
6670
// Filter messages for current session
6771
const filteredMessages: ChatMessageType[] = messages
68-
.filter((message: Message) => {
69-
const isCurrentFlow = message.flow_id === currentFlowId;
70-
// If visibleSession is the flow_id, it means we are in the default session
71-
// In the default session, we show messages that have the same session_id as the flow_id
72-
// OR messages that have NO session_id (legacy behavior)
73-
if (visibleSession === currentFlowId) {
74-
const matches =
75-
isCurrentFlow &&
76-
(message.session_id === visibleSession || !message.session_id);
77-
return matches;
78-
}
79-
const matches = isCurrentFlow && message.session_id === visibleSession;
80-
return matches;
81-
})
82-
.map((message: Message) => {
72+
.filter((message: Message) =>
73+
isMessageForSession(message, currentFlowId, visibleSession),
74+
)
75+
.map((message: Message): ChatMessageType => {
8376
let files = message.files;
8477
// Handle the "[]" case, empty string, or already parsed array
8578
if (Array.isArray(files)) {
@@ -96,6 +89,28 @@ export const useChatHistory = (visibleSession: string | null) => {
9689
}
9790
const messageText = message.text || "";
9891

92+
// Convert Message.properties to ChatMessageType.properties (PropertiesType)
93+
// Properties are now properly typed in Message, no cast needed
94+
let properties: ChatMessageType["properties"] = undefined;
95+
if (message.properties?.source?.id) {
96+
properties = {
97+
source: {
98+
id: message.properties.source.id,
99+
display_name: message.properties.source.display_name || "",
100+
source: message.properties.source.source || "",
101+
},
102+
state: message.properties.state,
103+
icon: message.properties.icon,
104+
background_color: message.properties.background_color,
105+
text_color: message.properties.text_color,
106+
targets: message.properties.targets,
107+
edited: message.properties.edited,
108+
allow_markdown: message.properties.allow_markdown,
109+
positive_feedback: message.properties.positive_feedback,
110+
build_duration: message.properties.build_duration,
111+
};
112+
}
113+
99114
return {
100115
isSend: message.sender === "User",
101116
message: messageText,
@@ -110,7 +125,7 @@ export const useChatHistory = (visibleSession: string | null) => {
110125
text_color: message.text_color,
111126
content_blocks: message.content_blocks,
112127
category: message.category,
113-
properties: message.properties,
128+
properties: properties,
114129
};
115130
});
116131

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Message } from "@/types/messages";
2+
3+
/**
4+
* Determines if a message belongs to a specific session.
5+
*
6+
* Why this logic exists:
7+
* - Default session (sessionId === flowId): Shows messages with matching session_id OR no session_id (legacy)
8+
* - Named sessions: Only shows messages with exact session_id match
9+
* - This prevents cross-session data leakage after deletions
10+
*
11+
* @param msg - The message to check
12+
* @param flowId - The current flow ID
13+
* @param sessionId - The session ID to filter for (null means no filtering)
14+
* @returns true if the message belongs to the session
15+
*/
16+
export function isMessageForSession(
17+
msg: Message,
18+
flowId: string,
19+
sessionId: string | null,
20+
): boolean {
21+
if (!sessionId) return false;
22+
23+
const isCurrentFlow = msg.flow_id === flowId;
24+
25+
if (sessionId === flowId) {
26+
// Default session: include messages with matching session_id or no session_id (legacy behavior)
27+
return isCurrentFlow && (msg.session_id === sessionId || !msg.session_id);
28+
}
29+
30+
return isCurrentFlow && msg.session_id === sessionId;
31+
}

src/frontend/src/controllers/API/queries/messages/use-delete-sessions.ts

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,98 @@
11
import type { UseMutationResult } from "@tanstack/react-query";
2-
import type { useMutationFunctionType } from "@/types/api";
2+
import type {
3+
DeleteSessionError,
4+
DeleteSessionParams,
5+
DeleteSessionResponse,
6+
} from "@/types/messages/session";
37
import { api } from "../../api";
48
import { getURL } from "../../helpers/constants";
59
import { UseRequestProcessor } from "../../services/request-processor";
610

7-
interface DeleteSessionParams {
8-
sessionId: string;
9-
}
10-
11-
export const useDeleteSession: useMutationFunctionType<
12-
undefined,
13-
DeleteSessionParams
14-
> = (options?) => {
11+
export const useDeleteSession = (options?: {
12+
onSuccess?: (
13+
data: DeleteSessionResponse,
14+
variables: DeleteSessionParams,
15+
context: unknown,
16+
) => void;
17+
onSettled?: (
18+
data: DeleteSessionResponse | undefined,
19+
error: DeleteSessionError | null,
20+
variables: DeleteSessionParams,
21+
context: unknown,
22+
) => void;
23+
onError?: (error: DeleteSessionError) => void;
24+
}) => {
1525
const { mutate, queryClient } = UseRequestProcessor();
1626

1727
const deleteSession = async ({
1828
sessionId,
19-
}: DeleteSessionParams): Promise<any> => {
29+
}: DeleteSessionParams): Promise<DeleteSessionResponse> => {
2030
const response = await api.delete(
2131
`${getURL("MESSAGES")}/session/${sessionId}`,
2232
);
2333
return response.data;
2434
};
2535

2636
const mutation: UseMutationResult<
27-
DeleteSessionParams,
28-
any,
37+
DeleteSessionResponse,
38+
DeleteSessionError,
2939
DeleteSessionParams
3040
> = mutate(["useDeleteSession"], deleteSession, {
3141
...options,
32-
onSettled: (data, error, variables, context) => {
42+
onSuccess: (data, variables, context, ...rest) => {
43+
// Cast needed because UseRequestProcessor's mutate doesn't properly infer callback types
44+
const vars = variables as unknown as DeleteSessionParams;
45+
46+
// Remove all message queries for this session immediately to prevent stale data
47+
if (vars.flowId) {
48+
// Remove session-specific queries
49+
queryClient.removeQueries({
50+
queryKey: [
51+
"useGetMessagesQuery",
52+
{ id: vars.flowId, session_id: vars.sessionId },
53+
],
54+
});
55+
56+
// Also remove any queries that might have the session_id in params (e.g., Message Logs)
57+
queryClient.removeQueries({
58+
predicate: (query) => {
59+
const queryKey = query.queryKey;
60+
if (
61+
Array.isArray(queryKey) &&
62+
queryKey[0] === "useGetMessagesQuery"
63+
) {
64+
const params = queryKey[1] as Record<string, unknown>;
65+
if (params?.params && typeof params.params === "object") {
66+
const nestedParams = params.params as Record<string, unknown>;
67+
if (nestedParams.session_id === vars.sessionId) {
68+
return true;
69+
}
70+
}
71+
}
72+
return false;
73+
},
74+
});
75+
}
76+
options?.onSuccess?.(data, vars, context);
77+
},
78+
onSettled: (data, error, variables, context, ...rest) => {
79+
// Cast needed because UseRequestProcessor's mutate doesn't properly infer callback types
80+
const vars = variables as unknown as DeleteSessionParams;
81+
82+
// Invalidate sessions list to refresh the sidebar
3383
queryClient.invalidateQueries({
3484
queryKey: ["useGetSessionsFromFlowQuery"],
3585
});
36-
options?.onSettled?.(data, error, variables, context);
86+
87+
// Invalidate all message queries to ensure fresh data everywhere
88+
if (vars.flowId) {
89+
queryClient.invalidateQueries({
90+
queryKey: ["useGetMessagesQuery"],
91+
refetchType: "none", // Prevent automatic refetching to avoid race conditions
92+
});
93+
}
94+
95+
options?.onSettled?.(data, error, vars, context);
3796
},
3897
});
3998

src/frontend/src/modals/IOModal/components/session-view.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ export default function SessionView({
5656
const rowsData = queryData.rows as { data?: any[] } | undefined;
5757
if (rowsData && typeof rowsData === "object" && "data" in rowsData) {
5858
const fetchedMessages = rowsData.data || [];
59-
if (fetchedMessages.length > 0) {
60-
setMessages(fetchedMessages);
61-
}
59+
setMessages(fetchedMessages);
6260
}
6361
}
6462
}, [queryData, setMessages]);

src/frontend/src/types/messages/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@ type Message = {
1515
category?: string;
1616
properties?: {
1717
state?: "partial" | "complete";
18-
source?: { id?: string };
18+
source?: {
19+
id?: string;
20+
display_name?: string;
21+
source?: string;
22+
};
23+
icon?: string;
24+
background_color?: string;
25+
text_color?: string;
26+
targets?: string[];
27+
edited?: boolean;
28+
allow_markdown?: boolean;
29+
positive_feedback?: boolean | null;
30+
build_duration?: number | null;
1931
[key: string]: unknown;
2032
};
2133
content_blocks?: ContentBlock[];
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export interface DeleteSessionParams {
2+
sessionId: string;
3+
flowId?: string;
4+
}
5+
6+
export interface DeleteSessionResponse {
7+
message: string;
8+
}
9+
10+
export interface DeleteSessionError {
11+
response?: {
12+
data?: {
13+
detail?: string;
14+
};
15+
};
16+
message?: string;
17+
}

0 commit comments

Comments
 (0)