From aee7df608fbecf2c24f0e4542e4b7a9c75804064 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Tue, 22 Apr 2025 18:32:20 +0800 Subject: [PATCH] refactor: use timeout value specified in settings in query_coco_fusion() (#413) * refactor: allow setting connection_timeout in query_coco_fusion() * refactor: adding dynamic parameters to a request * refactor: rename `connection_timeout` to `connectionTimeout`. * refactor: simplifying object property assignment syntax * feat: add query timeout function * refactor: set min query_timeout to 1s * refactor: rename connection_timeout to query_timeout * fix: persist the setting entry --------- Co-authored-by: ayang <473033518@qq.com> --- src-tauri/src/search/mod.rs | 3 +- src/commands/servers.ts | 39 +++-- src/components/Settings/Advanced/index.tsx | 33 ++++- src/hooks/useSyncStore.ts | 13 ++ src/locales/en/translation.json | 4 + src/locales/zh/translation.json | 4 + src/pages/main/index.tsx | 8 +- src/stores/connectStore.ts | 165 +++++++++++---------- src/types/platform.ts | 2 + 9 files changed, 176 insertions(+), 95 deletions(-) diff --git a/src-tauri/src/search/mod.rs b/src-tauri/src/search/mod.rs index 856ba9d4..efbc8471 100644 --- a/src-tauri/src/search/mod.rs +++ b/src-tauri/src/search/mod.rs @@ -16,6 +16,7 @@ pub async fn query_coco_fusion( from: u64, size: u64, query_strings: HashMap, + query_timeout: u64, ) -> Result { let data_source_to_search = query_strings.get("datasource"); @@ -28,7 +29,7 @@ pub async fn query_coco_fusion( let sources_list = sources_future.await; // Time limit for each query - let timeout_duration = Duration::from_millis(500); //TODO, settings + let timeout_duration = Duration::from_secs(query_timeout); // Push all queries into futures for query_source in sources_list { diff --git a/src/commands/servers.ts b/src/commands/servers.ts index 7cd03de2..c4b58a03 100644 --- a/src/commands/servers.ts +++ b/src/commands/servers.ts @@ -15,7 +15,7 @@ import { TranscriptionResponse, MultiSourceQueryResponse, } from "@/types/commands"; -import { useAppStore } from '@/stores/appStore'; +import { useAppStore } from "@/stores/appStore"; async function invokeWithErrorHandler( command: string, @@ -26,7 +26,7 @@ async function invokeWithErrorHandler( const result = await invoke(command, args); // console.log(command, result); - if (result && typeof result === 'object' && 'failed' in result) { + if (result && typeof result === "object" && "failed" in result) { const failedResult = result as any; if (failedResult.failed?.length > 0) { failedResult.failed.forEach((error: any) => { @@ -36,17 +36,17 @@ async function invokeWithErrorHandler( } } - if (typeof result === 'string') { + if (typeof result === "string") { const res = JSON.parse(result); - if (typeof res === 'string') { + if (typeof res === "string") { throw new Error(result); } } return result; } catch (error: any) { - const errorMessage = error || 'Command execution failed'; - addError(errorMessage, 'error'); + const errorMessage = error || "Command execution failed"; + addError(errorMessage, "error"); throw error; } } @@ -234,7 +234,10 @@ export function send_message({ } export const delete_session_chat = (serverId: string, sessionId: string) => { - return invokeWithErrorHandler(`delete_session_chat`, { serverId, sessionId }); + return invokeWithErrorHandler(`delete_session_chat`, { + serverId, + sessionId, + }); }; export const update_session_chat = (payload: { @@ -255,9 +258,12 @@ export const assistant_search = (payload: { }; export const upload_attachment = async (payload: UploadAttachmentPayload) => { - const response = await invokeWithErrorHandler("upload_attachment", { - ...payload, - }); + const response = await invokeWithErrorHandler( + "upload_attachment", + { + ...payload, + } + ); if (response?.acknowledged) { return response.attachments; @@ -265,7 +271,9 @@ export const upload_attachment = async (payload: UploadAttachmentPayload) => { }; export const get_attachment = (payload: GetAttachmentPayload) => { - return invokeWithErrorHandler("get_attachment", { ...payload }); + return invokeWithErrorHandler("get_attachment", { + ...payload, + }); }; export const delete_attachment = (payload: DeleteAttachmentPayload) => { @@ -273,13 +281,18 @@ export const delete_attachment = (payload: DeleteAttachmentPayload) => { }; export const transcription = (payload: TranscriptionPayload) => { - return invokeWithErrorHandler("transcription", { ...payload }); + return invokeWithErrorHandler("transcription", { + ...payload, + }); }; export const query_coco_fusion = (payload: { from: number; size: number; query_strings: Record; + connection_timeout: number; }) => { - return invokeWithErrorHandler("query_coco_fusion", { ...payload }); + return invokeWithErrorHandler("query_coco_fusion", { + ...payload, + }); }; diff --git a/src/components/Settings/Advanced/index.tsx b/src/components/Settings/Advanced/index.tsx index 1bf58917..b1a53b55 100644 --- a/src/components/Settings/Advanced/index.tsx +++ b/src/components/Settings/Advanced/index.tsx @@ -33,13 +33,26 @@ const Advanced = () => { const setConnectionTimeout = useConnectStore((state) => { return state.setConnectionTimeout; }); + const queryTimeout = useConnectStore((state) => { + return state.queryTimeout; + }); + const setQueryTimeout = useConnectStore((state) => { + return state.setQueryTimeout; + }); useEffect(() => { - const unlisten = useStartupStore.subscribe((state) => { + const unsubscribeStartup = useStartupStore.subscribe((state) => { emit("change-startup-store", state); }); - return unlisten; + const unsubscribeConnect = useConnectStore.subscribe((state) => { + emit("change-connect-store", state); + }); + + return () => { + unsubscribeStartup(); + unsubscribeConnect(); + }; }, []); const startupList = [ @@ -162,6 +175,22 @@ const Advanced = () => { }} /> + + + { + setQueryTimeout(Number(event.target.value) || 5); + }} + /> + ); diff --git a/src/hooks/useSyncStore.ts b/src/hooks/useSyncStore.ts index 6659ee02..0ff356c1 100644 --- a/src/hooks/useSyncStore.ts +++ b/src/hooks/useSyncStore.ts @@ -1,3 +1,4 @@ +import { useConnectStore } from "@/stores/connectStore"; import { useShortcutsStore } from "@/stores/shortcutsStore"; import { useStartupStore } from "@/stores/startupStore"; import platformAdapter from "@/utils/platformAdapter"; @@ -61,6 +62,12 @@ export const useSyncStore = () => { const setResetFixedWindow = useShortcutsStore((state) => { return state.setResetFixedWindow; }); + const setConnectionTimeout = useConnectStore((state) => { + return state.setConnectionTimeout; + }); + const setQueryTimeout = useConnectStore((state) => { + return state.setQueryTimeout; + }); useEffect(() => { if (!resetFixedWindow) { @@ -113,6 +120,12 @@ export const useSyncStore = () => { setDefaultContentForSearchWindow(defaultContentForSearchWindow); setDefaultContentForChatWindow(defaultContentForChatWindow); }), + + platformAdapter.listenEvent("change-connect-store", ({ payload }) => { + const { connectionTimeout, queryTimeout } = payload; + setConnectionTimeout(connectionTimeout); + setQueryTimeout(queryTimeout); + }), ]); return () => { diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 6f9dd3d4..dd74efe2 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -141,6 +141,10 @@ "connectionTimeout": { "title": "Connection Timeout", "description": "Retries the connection if no response is received within this time. Default: 120s." + }, + "queryTimeout": { + "title": "Query Timeout", + "description": "Terminates the query if no search results are returned within this time. Default: 5s." } } }, diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index a04dd786..a3536d46 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -141,6 +141,10 @@ "connectionTimeout": { "title": "连接超时", "description": "如果在此时间内未收到响应,则重试连接。默认值:120 秒。" + }, + "queryTimeout": { + "title": "查询超时", + "description": "在此时间内未返回搜索结果,则终止查询。默认值:5 秒。" } } }, diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index d8388ac7..496f726d 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -5,10 +5,14 @@ import platformAdapter from "@/utils/platformAdapter"; import { useAppStore } from "@/stores/appStore"; import { useSyncStore } from "@/hooks/useSyncStore"; import { useFeatureControl } from "@/hooks/useFeatureControl"; +import { useConnectStore } from "@/stores/connectStore"; function MainApp() { const setIsTauri = useAppStore((state) => state.setIsTauri); setIsTauri(true); + const queryTimeout = useConnectStore((state) => { + return state.queryTimeout; + }); const querySearch = useCallback(async (input: string) => { try { @@ -18,6 +22,7 @@ function MainApp() { from: 0, size: 10, queryStrings: { query: input }, + queryTimeout: queryTimeout, } ); if (!response || typeof response !== "object") { @@ -39,6 +44,7 @@ function MainApp() { from, size, queryStrings, + queryTimeout: queryTimeout, } ); return response; @@ -59,7 +65,7 @@ function MainApp() { const hasFeature = useFeatureControl({ initialFeatures: ["think", "search"], featureToToggle: "think", - condition: (item) => item?._source?.type === "simple" + condition: (item) => item?._source?.type === "simple", }); return ( diff --git a/src/stores/connectStore.ts b/src/stores/connectStore.ts index 89a67bd3..71c6ddae 100644 --- a/src/stores/connectStore.ts +++ b/src/stores/connectStore.ts @@ -1,5 +1,5 @@ import { create } from "zustand"; -import { persist } from "zustand/middleware"; +import { persist, subscribeWithSelector } from "zustand/middleware"; import { produce } from "immer"; import platformAdapter from "@/utils/platformAdapter"; @@ -26,86 +26,95 @@ export type IConnectStore = { setCurrentSessionId: (currentSessionId?: string) => void; currentAssistant: any; setCurrentAssistant: (assistant: any) => void; + queryTimeout: number; + setQueryTimeout: (queryTimeout: number) => void; }; export const useConnectStore = create()( - persist( - (set) => ({ - serverList: [], - setServerList: (serverList: []) => { - console.log("set serverList:", serverList); - set( - produce((draft) => { - draft.serverList = serverList; - }) - ); - }, - currentService: "default_coco_server", - setCurrentService: (server: any) => { - console.log("set default server:", server); - set( - produce((draft) => { - draft.currentService = server; - }) - ); - }, - connector_data: {}, - setConnectorData: async (connector_data: any[], key: string) => { - set( - produce((draft) => { - draft.connector_data[key] = connector_data; - }) - ); - await platformAdapter.emitEvent(CONNECTOR_CHANGE_EVENT, { - connector_data, - }); - }, - datasourceData: {}, - setDatasourceData: async (datasourceData: any[], key: string) => { - set( - produce((draft) => { - draft.datasourceData[key] = datasourceData; - }) - ); - await platformAdapter.emitEvent(DATASOURCE_CHANGE_EVENT, { - datasourceData, - }); - }, - initializeListeners: () => { - platformAdapter.listenEvent(CONNECTOR_CHANGE_EVENT, (event: any) => { - const { connector_data } = event.payload; - set({ connector_data }); - }); - platformAdapter.listenEvent(DATASOURCE_CHANGE_EVENT, (event: any) => { - const { datasourceData } = event.payload; - set({ datasourceData }); - }); - }, - connectionTimeout: 120, - setConnectionTimeout: (connectionTimeout: number) => { - return set(() => ({ connectionTimeout })); - }, - setCurrentSessionId(currentSessionId) { - return set(() => ({ currentSessionId })); - }, - currentAssistant: null, - setCurrentAssistant: (assistant: any) => { - set( - produce((draft) => { - draft.currentAssistant = assistant; - }) - ); - }, - }), - { - name: "connect-store", - partialize: (state) => ({ - currentService: state.currentService, - connector_data: state.connector_data, - datasourceData: state.datasourceData, - connectionTimeout: state.connectionTimeout, - currentAssistant: state.currentAssistant, + subscribeWithSelector( + persist( + (set) => ({ + serverList: [], + setServerList: (serverList: []) => { + console.log("set serverList:", serverList); + set( + produce((draft) => { + draft.serverList = serverList; + }) + ); + }, + currentService: "default_coco_server", + setCurrentService: (server: any) => { + console.log("set default server:", server); + set( + produce((draft) => { + draft.currentService = server; + }) + ); + }, + connector_data: {}, + setConnectorData: async (connector_data: any[], key: string) => { + set( + produce((draft) => { + draft.connector_data[key] = connector_data; + }) + ); + await platformAdapter.emitEvent(CONNECTOR_CHANGE_EVENT, { + connector_data, + }); + }, + datasourceData: {}, + setDatasourceData: async (datasourceData: any[], key: string) => { + set( + produce((draft) => { + draft.datasourceData[key] = datasourceData; + }) + ); + await platformAdapter.emitEvent(DATASOURCE_CHANGE_EVENT, { + datasourceData, + }); + }, + initializeListeners: () => { + platformAdapter.listenEvent(CONNECTOR_CHANGE_EVENT, (event: any) => { + const { connector_data } = event.payload; + set({ connector_data }); + }); + platformAdapter.listenEvent(DATASOURCE_CHANGE_EVENT, (event: any) => { + const { datasourceData } = event.payload; + set({ datasourceData }); + }); + }, + connectionTimeout: 120, + setConnectionTimeout: (connectionTimeout: number) => { + return set(() => ({ connectionTimeout })); + }, + setCurrentSessionId(currentSessionId) { + return set(() => ({ currentSessionId })); + }, + currentAssistant: null, + setCurrentAssistant: (assistant: any) => { + set( + produce((draft) => { + draft.currentAssistant = assistant; + }) + ); + }, + queryTimeout: 5, + setQueryTimeout: (queryTimeout: number) => { + return set(() => ({ queryTimeout })); + }, }), - } + { + name: "connect-store", + partialize: (state) => ({ + currentService: state.currentService, + connector_data: state.connector_data, + datasourceData: state.datasourceData, + connectionTimeout: state.connectionTimeout, + currentAssistant: state.currentAssistant, + queryTimeout: state.queryTimeout, + }), + } + ) ) ); diff --git a/src/types/platform.ts b/src/types/platform.ts index 72d487c0..9736115e 100644 --- a/src/types/platform.ts +++ b/src/types/platform.ts @@ -1,3 +1,4 @@ +import { IConnectStore } from "@/stores/connectStore"; import { IShortcutsStore } from "@/stores/shortcutsStore"; import { IStartupStore } from "@/stores/startupStore"; import { AppTheme } from "@/types/index"; @@ -36,6 +37,7 @@ export interface EventPayloads { [key: `ws-message-${string}`]: string; "change-startup-store": IStartupStore; "change-shortcuts-store": IShortcutsStore; + "change-connect-store": IConnectStore; } // Window operation interface