修复会话记录可能存储两次的bug;更改会话行为,默认进入新对话
This commit is contained in:
@@ -32,6 +32,7 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
|
|||||||
|
|
||||||
const bottomRef = useRef<HTMLDivElement>(null);
|
const bottomRef = useRef<HTMLDivElement>(null);
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const hasResetForOpenRef = useRef(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const currentProjectId = useProjectStore((state) => state.currentProjectId);
|
const currentProjectId = useProjectStore((state) => state.currentProjectId);
|
||||||
|
|
||||||
@@ -87,13 +88,22 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
|
|||||||
}, [messages, isStreaming]);
|
}, [messages, isStreaming]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) {
|
||||||
|
hasResetForOpenRef.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hasResetForOpenRef.current || isHydrating) return;
|
||||||
|
hasResetForOpenRef.current = true;
|
||||||
|
|
||||||
const timer = window.setTimeout(() => {
|
const timer = window.setTimeout(() => {
|
||||||
|
createSession();
|
||||||
|
setInput("");
|
||||||
|
setIsHistoryOpen(false);
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
bottomRef.current?.scrollIntoView({ behavior: "auto" });
|
bottomRef.current?.scrollIntoView({ behavior: "auto" });
|
||||||
}, 0);
|
}, 0);
|
||||||
return () => window.clearTimeout(timer);
|
return () => window.clearTimeout(timer);
|
||||||
}, [open]);
|
}, [createSession, isHydrating, open]);
|
||||||
|
|
||||||
const handleSend = useCallback(() => {
|
const handleSend = useCallback(() => {
|
||||||
const prompt = input.trim();
|
const prompt = input.trim();
|
||||||
@@ -112,7 +122,7 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
|
|||||||
const handleNewConversation = useCallback(() => {
|
const handleNewConversation = useCallback(() => {
|
||||||
handleStopSpeech();
|
handleStopSpeech();
|
||||||
stopListening();
|
stopListening();
|
||||||
void createSession();
|
createSession();
|
||||||
setInput("");
|
setInput("");
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
createEmptyChatSession,
|
|
||||||
loadActiveChatState,
|
loadActiveChatState,
|
||||||
saveActiveChatState,
|
saveActiveChatState,
|
||||||
} from "./chatStorage";
|
} from "./chatStorage";
|
||||||
@@ -16,33 +15,22 @@ describe("chatStorage backend-only persistence", () => {
|
|||||||
apiFetch.mockReset();
|
apiFetch.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("loads the active remote session when localStorage has an active id", async () => {
|
it("starts from an empty conversation instead of restoring a stored active id", async () => {
|
||||||
window.localStorage.setItem("tjwater_agent_active_session_id_v2", "chat-active-1");
|
window.localStorage.setItem("tjwater_agent_active_session_id_v2", "chat-active-1");
|
||||||
|
|
||||||
apiFetch.mockImplementation(async (url: string) => {
|
|
||||||
if (url.endsWith("/api/v1/agent/chat/session/chat-active-1")) {
|
|
||||||
return {
|
|
||||||
ok: true,
|
|
||||||
json: async () => ({
|
|
||||||
id: "chat-active-1",
|
|
||||||
title: "已存在会话",
|
|
||||||
is_title_manually_edited: false,
|
|
||||||
session_id: "chat-active-1",
|
|
||||||
messages: [],
|
|
||||||
branch_groups: [],
|
|
||||||
}),
|
|
||||||
} as Response;
|
|
||||||
}
|
|
||||||
throw new Error(`Unexpected request ${url}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const loaded = await loadActiveChatState();
|
const loaded = await loadActiveChatState();
|
||||||
|
|
||||||
expect(loaded.storageSessionId).toBe("chat-active-1");
|
expect(loaded).toMatchObject({
|
||||||
expect(loaded.title).toBe("已存在会话");
|
storageSessionId: undefined,
|
||||||
|
title: undefined,
|
||||||
|
messages: [],
|
||||||
|
sessionId: undefined,
|
||||||
|
branchGroups: [],
|
||||||
|
});
|
||||||
|
expect(apiFetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("loads the active remote session from the current project's storage key", async () => {
|
it("starts from an empty conversation when a project has a stored active id", async () => {
|
||||||
window.localStorage.setItem(
|
window.localStorage.setItem(
|
||||||
"tjwater_agent_active_session_id_v2:project-a",
|
"tjwater_agent_active_session_id_v2:project-a",
|
||||||
"chat-project-a",
|
"chat-project-a",
|
||||||
@@ -52,27 +40,12 @@ describe("chatStorage backend-only persistence", () => {
|
|||||||
"chat-project-b",
|
"chat-project-b",
|
||||||
);
|
);
|
||||||
|
|
||||||
apiFetch.mockImplementation(async (url: string) => {
|
|
||||||
if (url.endsWith("/api/v1/agent/chat/session/chat-project-b")) {
|
|
||||||
return {
|
|
||||||
ok: true,
|
|
||||||
json: async () => ({
|
|
||||||
id: "chat-project-b",
|
|
||||||
title: "项目 B 会话",
|
|
||||||
is_title_manually_edited: false,
|
|
||||||
session_id: "chat-project-b",
|
|
||||||
messages: [],
|
|
||||||
branch_groups: [],
|
|
||||||
}),
|
|
||||||
} as Response;
|
|
||||||
}
|
|
||||||
throw new Error(`Unexpected request ${url}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const loaded = await loadActiveChatState("project-b");
|
const loaded = await loadActiveChatState("project-b");
|
||||||
|
|
||||||
expect(loaded.storageSessionId).toBe("chat-project-b");
|
expect(loaded.storageSessionId).toBeUndefined();
|
||||||
expect(loaded.title).toBe("项目 B 会话");
|
expect(loaded.title).toBeUndefined();
|
||||||
|
expect(loaded.messages).toEqual([]);
|
||||||
|
expect(apiFetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("creates a backend conversation when saving the first non-empty state", async () => {
|
it("creates a backend conversation when saving the first non-empty state", async () => {
|
||||||
@@ -122,14 +95,7 @@ describe("chatStorage backend-only persistence", () => {
|
|||||||
expect(savedSessionId).toBe("chat-new-1");
|
expect(savedSessionId).toBe("chat-new-1");
|
||||||
expect(
|
expect(
|
||||||
window.localStorage.getItem("tjwater_agent_active_session_id_v2:project-a"),
|
window.localStorage.getItem("tjwater_agent_active_session_id_v2:project-a"),
|
||||||
).toBe("chat-new-1");
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not persist a blank new session before there is chat content", async () => {
|
|
||||||
const session = await createEmptyChatSession();
|
|
||||||
|
|
||||||
expect(session.storageSessionId).toBeUndefined();
|
|
||||||
expect(session.title).toBe("新对话");
|
|
||||||
expect(apiFetch).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import type {
|
|||||||
} from "./GlobalChatbox.types";
|
} from "./GlobalChatbox.types";
|
||||||
import { cloneBranchGroups, cloneMessages } from "./GlobalChatbox.utils";
|
import { cloneBranchGroups, cloneMessages } from "./GlobalChatbox.utils";
|
||||||
|
|
||||||
const ACTIVE_SESSION_STORAGE_KEY = "tjwater_agent_active_session_id_v2";
|
|
||||||
|
|
||||||
type RemoteSessionPayload = {
|
type RemoteSessionPayload = {
|
||||||
id?: string;
|
id?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -60,34 +58,6 @@ const toMillis = (value: string | number | undefined) =>
|
|||||||
|
|
||||||
const normalizeTitle = (value?: string) => value?.trim() || "新对话";
|
const normalizeTitle = (value?: string) => value?.trim() || "新对话";
|
||||||
|
|
||||||
const getActiveSessionStorageKey = (projectId?: string | null) => {
|
|
||||||
const normalizedProjectId = projectId?.trim();
|
|
||||||
return normalizedProjectId
|
|
||||||
? `${ACTIVE_SESSION_STORAGE_KEY}:${encodeURIComponent(normalizedProjectId)}`
|
|
||||||
: ACTIVE_SESSION_STORAGE_KEY;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStoredActiveSessionId = (projectId?: string | null) => {
|
|
||||||
if (typeof window === "undefined") return undefined;
|
|
||||||
const stored = window.localStorage
|
|
||||||
.getItem(getActiveSessionStorageKey(projectId))
|
|
||||||
?.trim();
|
|
||||||
return stored || undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setStoredActiveSessionId = (
|
|
||||||
sessionId?: string,
|
|
||||||
projectId?: string | null,
|
|
||||||
) => {
|
|
||||||
if (typeof window === "undefined") return;
|
|
||||||
const storageKey = getActiveSessionStorageKey(projectId);
|
|
||||||
if (sessionId) {
|
|
||||||
window.localStorage.setItem(storageKey, sessionId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.localStorage.removeItem(storageKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchRemoteChatSessions = async (): Promise<ChatSessionSummary[]> => {
|
const fetchRemoteChatSessions = async (): Promise<ChatSessionSummary[]> => {
|
||||||
const response = await apiFetch(`${config.AGENT_URL}/api/v1/agent/chat/sessions`, {
|
const response = await apiFetch(`${config.AGENT_URL}/api/v1/agent/chat/sessions`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -247,36 +217,18 @@ const deleteRemoteChatSession = async (sessionId: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const loadActiveChatState = async (
|
export const loadActiveChatState = async (
|
||||||
projectId?: string | null,
|
_projectId?: string | null,
|
||||||
): Promise<LoadedChatState> => {
|
): Promise<LoadedChatState> => {
|
||||||
if (typeof window === "undefined") return emptyLoadedChatState();
|
return emptyLoadedChatState();
|
||||||
|
|
||||||
const activeSessionId = getStoredActiveSessionId(projectId);
|
|
||||||
if (activeSessionId) {
|
|
||||||
const activeSession = await fetchRemoteChatSession(activeSessionId);
|
|
||||||
if (activeSession.storageSessionId) {
|
|
||||||
return activeSession;
|
|
||||||
}
|
|
||||||
setStoredActiveSessionId(undefined, projectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sessions = await fetchRemoteChatSessions();
|
|
||||||
const latestSession = sessions[0];
|
|
||||||
if (!latestSession) {
|
|
||||||
return emptyLoadedChatState();
|
|
||||||
}
|
|
||||||
setStoredActiveSessionId(latestSession.id, projectId);
|
|
||||||
return await fetchRemoteChatSession(latestSession.id);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveActiveChatState = async (
|
export const saveActiveChatState = async (
|
||||||
state: LoadedChatState,
|
state: LoadedChatState,
|
||||||
projectId?: string | null,
|
_projectId?: string | null,
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
if (typeof window === "undefined") return state.storageSessionId;
|
if (typeof window === "undefined") return state.storageSessionId;
|
||||||
|
|
||||||
if (!hasChatContent(state)) {
|
if (!hasChatContent(state)) {
|
||||||
setStoredActiveSessionId(undefined, projectId);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +242,6 @@ export const saveActiveChatState = async (
|
|||||||
storageSessionId: remoteSessionId,
|
storageSessionId: remoteSessionId,
|
||||||
sessionId: remoteSessionId,
|
sessionId: remoteSessionId,
|
||||||
});
|
});
|
||||||
setStoredActiveSessionId(savedSessionId, projectId);
|
|
||||||
return savedSessionId;
|
return savedSessionId;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -317,39 +268,22 @@ export const updateChatSessionTitle = async (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createEmptyChatSession = async (
|
|
||||||
projectId?: string | null,
|
|
||||||
): Promise<LoadedChatState> => {
|
|
||||||
if (typeof window === "undefined") return emptyLoadedChatState();
|
|
||||||
|
|
||||||
setStoredActiveSessionId(undefined, projectId);
|
|
||||||
return {
|
|
||||||
...emptyLoadedChatState(),
|
|
||||||
title: "新对话",
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const loadChatSessionById = async (
|
export const loadChatSessionById = async (
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
projectId?: string | null,
|
_projectId?: string | null,
|
||||||
): Promise<LoadedChatState> => {
|
): Promise<LoadedChatState> => {
|
||||||
if (typeof window === "undefined") return emptyLoadedChatState();
|
if (typeof window === "undefined") return emptyLoadedChatState();
|
||||||
|
|
||||||
const loaded = await fetchRemoteChatSession(sessionId);
|
return await fetchRemoteChatSession(sessionId);
|
||||||
if (loaded.storageSessionId) {
|
|
||||||
setStoredActiveSessionId(sessionId, projectId);
|
|
||||||
}
|
|
||||||
return loaded;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteChatSession = async (
|
export const deleteChatSession = async (
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
projectId?: string | null,
|
_projectId?: string | null,
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
if (typeof window === "undefined") return undefined;
|
if (typeof window === "undefined") return undefined;
|
||||||
|
|
||||||
await deleteRemoteChatSession(sessionId);
|
await deleteRemoteChatSession(sessionId);
|
||||||
const nextActiveSession = (await listChatSessions())[0];
|
const nextActiveSession = (await listChatSessions())[0];
|
||||||
setStoredActiveSessionId(nextActiveSession?.id, projectId);
|
|
||||||
return nextActiveSession?.id;
|
return nextActiveSession?.id;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { act, renderHook, waitFor } from "@testing-library/react";
|
|||||||
|
|
||||||
import { useAgentChatSession } from "./useAgentChatSession";
|
import { useAgentChatSession } from "./useAgentChatSession";
|
||||||
import { streamAgentChat } from "@/lib/chatStream";
|
import { streamAgentChat } from "@/lib/chatStream";
|
||||||
|
import type { StreamEvent } from "@/lib/chatStream";
|
||||||
|
|
||||||
jest.mock("@/lib/chatStream", () => ({
|
jest.mock("@/lib/chatStream", () => ({
|
||||||
abortAgentChat: jest.fn(async () => undefined),
|
abortAgentChat: jest.fn(async () => undefined),
|
||||||
@@ -13,6 +14,7 @@ jest.mock("@/lib/chatStream", () => ({
|
|||||||
|
|
||||||
const loadActiveChatState = jest.fn();
|
const loadActiveChatState = jest.fn();
|
||||||
const listChatSessions = jest.fn();
|
const listChatSessions = jest.fn();
|
||||||
|
const saveActiveChatState = jest.fn();
|
||||||
const updateChatSessionTitle = jest.fn();
|
const updateChatSessionTitle = jest.fn();
|
||||||
|
|
||||||
jest.mock("../chatStorage", () => ({
|
jest.mock("../chatStorage", () => ({
|
||||||
@@ -27,7 +29,7 @@ jest.mock("../chatStorage", () => ({
|
|||||||
sessionId: undefined,
|
sessionId: undefined,
|
||||||
branchGroups: [],
|
branchGroups: [],
|
||||||
})),
|
})),
|
||||||
saveActiveChatState: jest.fn(async (state) => state.storageSessionId),
|
saveActiveChatState: (...args: unknown[]) => saveActiveChatState(...args),
|
||||||
updateChatSessionTitle: (...args: unknown[]) => updateChatSessionTitle(...args),
|
updateChatSessionTitle: (...args: unknown[]) => updateChatSessionTitle(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -35,8 +37,10 @@ describe("useAgentChatSession", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
loadActiveChatState.mockReset();
|
loadActiveChatState.mockReset();
|
||||||
listChatSessions.mockReset();
|
listChatSessions.mockReset();
|
||||||
|
saveActiveChatState.mockReset();
|
||||||
updateChatSessionTitle.mockReset();
|
updateChatSessionTitle.mockReset();
|
||||||
jest.mocked(streamAgentChat).mockReset();
|
jest.mocked(streamAgentChat).mockReset();
|
||||||
|
saveActiveChatState.mockImplementation(async (state) => state.storageSessionId);
|
||||||
|
|
||||||
loadActiveChatState.mockResolvedValue({
|
loadActiveChatState.mockResolvedValue({
|
||||||
storageSessionId: undefined,
|
storageSessionId: undefined,
|
||||||
@@ -105,6 +109,59 @@ describe("useAgentChatSession", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("waits for the stream session id before persisting a new streaming conversation", async () => {
|
||||||
|
listChatSessions.mockResolvedValue([]);
|
||||||
|
let emitStreamEvent: ((event: StreamEvent) => void) | undefined;
|
||||||
|
jest.mocked(streamAgentChat).mockImplementationOnce(async ({ onEvent }) => {
|
||||||
|
emitStreamEvent = onEvent;
|
||||||
|
await new Promise<void>(() => undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAgentChatSession({
|
||||||
|
projectId: "project-1",
|
||||||
|
onToolCall: jest.fn(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current.isHydrating).toBe(false));
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
try {
|
||||||
|
await act(async () => {
|
||||||
|
void result.current.sendPrompt("第一条消息");
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isStreaming).toBe(true);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
jest.advanceTimersByTime(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(saveActiveChatState).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
emitStreamEvent?.({
|
||||||
|
type: "token",
|
||||||
|
sessionId: "chat-stream-1",
|
||||||
|
content: "收到",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
jest.advanceTimersByTime(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(saveActiveChatState).toHaveBeenCalledTimes(1);
|
||||||
|
expect(saveActiveChatState.mock.calls[0][0]).toMatchObject({
|
||||||
|
sessionId: "chat-stream-1",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
jest.useRealTimers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("ignores generated session titles after the title was edited manually", async () => {
|
it("ignores generated session titles after the title was edited manually", async () => {
|
||||||
listChatSessions.mockResolvedValue([]);
|
listChatSessions.mockResolvedValue([]);
|
||||||
loadActiveChatState.mockResolvedValue({
|
loadActiveChatState.mockResolvedValue({
|
||||||
|
|||||||
@@ -269,6 +269,15 @@ export const useAgentChatSession = ({
|
|||||||
sessionId,
|
sessionId,
|
||||||
branchGroups,
|
branchGroups,
|
||||||
};
|
};
|
||||||
|
if (
|
||||||
|
isStreaming &&
|
||||||
|
!state.storageSessionId &&
|
||||||
|
!state.sessionId &&
|
||||||
|
state.messages.length > 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentStateKey = createPersistedStateKey(state);
|
const currentStateKey = createPersistedStateKey(state);
|
||||||
if (currentStateKey === lastPersistedStateKeyRef.current) {
|
if (currentStateKey === lastPersistedStateKeyRef.current) {
|
||||||
return;
|
return;
|
||||||
@@ -296,7 +305,7 @@ export const useAgentChatSession = ({
|
|||||||
return () => {
|
return () => {
|
||||||
window.clearTimeout(persistTimer);
|
window.clearTimeout(persistTimer);
|
||||||
};
|
};
|
||||||
}, [branchGroups, isHydrating, isSessionTitleManuallyEdited, messages, projectId, sessionId, sessionTitle]);
|
}, [branchGroups, isHydrating, isSessionTitleManuallyEdited, isStreaming, messages, projectId, sessionId, sessionTitle]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBranchGroups((prev) => {
|
setBranchGroups((prev) => {
|
||||||
@@ -538,42 +547,7 @@ export const useAgentChatSession = ({
|
|||||||
cancelPromiseRef.current = trackedCancelPromise;
|
cancelPromiseRef.current = trackedCancelPromise;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
const createSession = useCallback(() => {
|
||||||
const controller = abortRef.current;
|
|
||||||
controller?.abort();
|
|
||||||
const activeSessionId = sessionIdRef.current;
|
|
||||||
if (activeSessionId) {
|
|
||||||
const cancelPromise = abortAgentChat(activeSessionId).catch((error) => {
|
|
||||||
console.error("[GlobalChatbox] Failed to abort agent session during reset:", error);
|
|
||||||
});
|
|
||||||
const trackedCancelPromise = cancelPromise.finally(() => {
|
|
||||||
if (cancelPromiseRef.current === trackedCancelPromise) {
|
|
||||||
cancelPromiseRef.current = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cancelPromiseRef.current = trackedCancelPromise;
|
|
||||||
}
|
|
||||||
setMessages([]);
|
|
||||||
setSessionTitle(undefined);
|
|
||||||
setIsSessionTitleManuallyEdited(false);
|
|
||||||
setBranchGroups([]);
|
|
||||||
setBranchTransition(null);
|
|
||||||
setSessionId(undefined);
|
|
||||||
sessionIdRef.current = undefined;
|
|
||||||
storageSessionIdRef.current = undefined;
|
|
||||||
lastPersistedStateKeyRef.current = createPersistedStateKey({
|
|
||||||
storageSessionId: undefined,
|
|
||||||
title: undefined,
|
|
||||||
isTitleManuallyEdited: false,
|
|
||||||
messages: [],
|
|
||||||
sessionId: undefined,
|
|
||||||
branchGroups: [],
|
|
||||||
});
|
|
||||||
titleUpdateNonceRef.current += 1;
|
|
||||||
setIsStreaming(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const createSession = useCallback(async () => {
|
|
||||||
if (isHydrating || isStreaming) return;
|
if (isHydrating || isStreaming) return;
|
||||||
|
|
||||||
const controller = abortRef.current;
|
const controller = abortRef.current;
|
||||||
@@ -903,7 +877,6 @@ export const useAgentChatSession = ({
|
|||||||
cycleBranch,
|
cycleBranch,
|
||||||
abort,
|
abort,
|
||||||
createSession,
|
createSession,
|
||||||
reset,
|
|
||||||
renameSession,
|
renameSession,
|
||||||
removeSession,
|
removeSession,
|
||||||
switchSession,
|
switchSession,
|
||||||
|
|||||||
Reference in New Issue
Block a user