diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index 447d8795..91a25c56 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -10,14 +10,19 @@ Information about release notes of Coco Server is provided here. ## Latest (In development) ### Features + - Add timeout to fusion search #174 - Add api to disable or enable server #185 +- Networked search supports selection of data sources #209 ### Breaking changes + ### Bug fix + - Fix to access deeplink for linux #148 ### Improvements + - Improve app startup, init application search in background #172 - Refactoring login #173 - Init icons in background during start #176 @@ -29,8 +34,9 @@ Information about release notes of Coco Server is provided here. ## 0.1.0 (2015-02-16) ### Features + - Fusion Search -- Chat with AI Assistant +- Chat with AI Assistant - RAG-based AI Chat - General Settings - Global Shortcut @@ -44,4 +50,3 @@ Information about release notes of Coco Server is provided here. ### Bug fix ### Improvements - diff --git a/src-tauri/src/assistant/mod.rs b/src-tauri/src/assistant/mod.rs index d42b2062..f261d905 100644 --- a/src-tauri/src/assistant/mod.rs +++ b/src-tauri/src/assistant/mod.rs @@ -166,15 +166,10 @@ pub async fn send_message( }; let body = reqwest::Body::from(serde_json::to_string(&msg).unwrap()); - let response = HttpClient::advanced_post( - &server_id, - path.as_str(), - None, - query_params, - Some(body), - ) - .await - .map_err(|e| format!("Error cancel session: {}", e))?; + let response = + HttpClient::advanced_post(&server_id, path.as_str(), None, query_params, Some(body)) + .await + .map_err(|e| format!("Error cancel session: {}", e))?; handle_raw_response(response).await? } diff --git a/src/components/Assistant/Chat.tsx b/src/components/Assistant/Chat.tsx index 75c3fcf4..1eb701ae 100644 --- a/src/components/Assistant/Chat.tsx +++ b/src/components/Assistant/Chat.tsx @@ -20,6 +20,7 @@ import { useWindows } from "@/hooks/useWindows"; import { ChatHeader } from "./ChatHeader"; import { Sidebar } from "@/components/Assistant/Sidebar"; import { useConnectStore } from "@/stores/connectStore"; +import { useSearchStore } from "@/stores/searchStore"; interface ChatAIProps { isTransitioned: boolean; @@ -97,6 +98,7 @@ const ChatAI = memo( const [isSidebarOpenChat, setIsSidebarOpenChat] = useState(isSidebarOpen); const [chats, setChats] = useState([]); + const sourceDataIds = useSearchStore((state) => state.sourceDataIds); useEffect(() => { activeChatProp && setActiveChat(activeChatProp); @@ -285,9 +287,11 @@ const ChatAI = memo( const createNewChat = useCallback(async (value: string = "") => { chatClose(); try { + console.log("sourceDataIds", sourceDataIds); let response: any = await invoke("new_chat", { serverId: currentService?.id, message: value, + datasource: sourceDataIds.join(","), }); console.log("_new", response); const newChat: Chat = response; @@ -326,6 +330,7 @@ const ChatAI = memo( if (!newChat?._id || !content) return; setTimedoutShow(false); try { + console.log("sourceDataIds", sourceDataIds); let response: any = await invoke("send_message", { serverId: currentService?.id, sessionId: newChat?._id, @@ -334,6 +339,7 @@ const ChatAI = memo( deep_thinking: isDeepThinkActive, }, message: content, + datasource: sourceDataIds.join(","), }); response = JSON.parse(response || ""); console.log("_send", response, websocketIdRef.current); diff --git a/src/components/Search/InputBox.tsx b/src/components/Search/InputBox.tsx index 1ac0e804..25a438d4 100644 --- a/src/components/Search/InputBox.tsx +++ b/src/components/Search/InputBox.tsx @@ -1,4 +1,14 @@ -import { ArrowBigLeft, Search, Send, Globe, Brain } from "lucide-react"; +import { + ArrowBigLeft, + Search, + Send, + Globe, + Brain, + ChevronDownIcon, + RefreshCw, + CheckIcon, + Layers, +} from "lucide-react"; import { useCallback, useEffect, useRef, useState } from "react"; import { listen } from "@tauri-apps/api/event"; import { invoke, isTauri } from "@tauri-apps/api/core"; @@ -11,6 +21,16 @@ import StopIcon from "@/icons/Stop"; import { useAppStore } from "@/stores/appStore"; import { useSearchStore } from "@/stores/searchStore"; import { metaOrCtrlKey } from "@/utils/keyboardUtils"; +import { + Checkbox, + Popover, + PopoverButton, + PopoverPanel, +} from "@headlessui/react"; +import clsx from "clsx"; +import { useConnectStore } from "@/stores/connectStore"; +import TypeIcon from "../Common/Icons/TypeIcon"; +import { useReactive, useUpdateEffect } from "ahooks"; interface ChatInputProps { onSend: (message: string) => void; @@ -41,7 +61,7 @@ export default function ChatInput({ isDeepThinkActive, setIsDeepThinkActive, }: ChatInputProps) { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const showTooltip = useAppStore( (state: { showTooltip: boolean }) => state.showTooltip @@ -56,6 +76,37 @@ export default function ChatInput({ (state: { setSourceData: any }) => state.setSourceData ); + const currentService = useConnectStore((state) => state.currentService); + + const datasourceData = useConnectStore((state) => state.datasourceData); + + const state = useReactive<{ dataSourceList: any[] }>({ + dataSourceList: [], + }); + + const sourceDataIds = useSearchStore((state) => state.sourceDataIds); + const setSourceDataIds = useSearchStore((state) => state.setSourceDataIds); + + const getDataSourceList = () => { + if (!currentService?.id) return []; + + state.dataSourceList = [ + { + id: "all", + name: t("search.input.searchPopover.allScope"), + }, + ...datasourceData[currentService.id], + ]; + + onSelectDataSource("all", true, true); + }; + + useEffect(() => { + getDataSourceList(); + }, [currentService, datasourceData, i18n.language]); + + const [isRefreshDataSource, setIsRefreshDataSource] = useState(false); + useEffect(() => { setSourceData(undefined); }, []); @@ -175,6 +226,7 @@ export default function ChatInput({ useEffect(() => { if (!isTauri()) return; + const setupListener = async () => { const unlisten = await listen("tauri://focus", () => { // console.log("Window focused!"); @@ -228,6 +280,30 @@ export default function ChatInput({ setIsDeepThinkActive(); }; + const onSelectDataSource = (id: string, checked: boolean, isAll: boolean) => { + console.log("id", id); + console.log("checked", checked); + console.log("isAll", isAll); + + console.log("state.dataSourceList", state.dataSourceList); + + if (isAll) { + if (checked) { + setSourceDataIds(state.dataSourceList.slice(1).map((item) => item.id)); + } else { + setSourceDataIds([]); + } + + return; + } + + if (checked) { + setSourceDataIds([...new Set([...sourceDataIds, id])]); + } else { + setSourceDataIds(sourceDataIds.filter((item) => item !== id)); + } + }; + return (
@@ -391,8 +467,8 @@ export default function ChatInput({ {t("search.input.deepThink")} - + + + + + + + +
{ + e.stopPropagation(); + }} + > +
+ {t("search.input.searchPopover.title")} + + +
+
    + {state.dataSourceList?.map((item, index) => { + const { id, name } = item; + + const isAll = index === 0; + + return ( +
  • +
    + {isAll ? ( + + ) : ( + + )} + + {name} +
    + + + onSelectDataSource(id, value, isAll) + } + className="group size-[14px] rounded-sm border border-black/30 dark:border-white/30 data-[checked]:bg-[#2F54EB] data-[checked]:!border-[#2F54EB] transition" + > + {isAll && ( +
    +
    +
    + )} + + +
    +
  • + ); + })} +
+
+
+
+
+ {/* void; + sourceDataIds: string[]; + setSourceDataIds: (prevSourceDataId: string[]) => void; }; export const useSearchStore = create()( @@ -11,6 +13,8 @@ export const useSearchStore = create()( (set) => ({ sourceData: undefined, setSourceData: (sourceData: any) => set({ sourceData }), + sourceDataIds: [], + setSourceDataIds: (sourceDataIds: string[]) => set({ sourceDataIds }), }), { name: "search-store",