Skip to content

Commit

Permalink
feat: networked search supports selection of data sources
Browse files Browse the repository at this point in the history
  • Loading branch information
ayangweb committed Feb 26, 2025
1 parent 0f74d6c commit 7b4df60
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 16 deletions.
15 changes: 6 additions & 9 deletions src-tauri/src/assistant/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,19 @@ pub async fn send_message<R: Runtime>(
session_id: String,
message: String,
query_params: Option<HashMap<String, String>>, //search,deep_thinking
datasource: String,
) -> Result<String, String> {
println!("datasource: {}", datasource);
let path = format!("/chat/{}/_send", session_id);
let msg = ChatRequestMessage {
message: Some(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?
}
6 changes: 6 additions & 0 deletions src/components/Assistant/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -97,6 +98,7 @@ const ChatAI = memo(

const [isSidebarOpenChat, setIsSidebarOpenChat] = useState(isSidebarOpen);
const [chats, setChats] = useState<Chat[]>([]);
const sourceDataIds = useSearchStore((state) => state.sourceDataIds);

useEffect(() => {
activeChatProp && setActiveChat(activeChatProp);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand Down
181 changes: 176 additions & 5 deletions src/components/Search/InputBox.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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);
}, []);
Expand Down Expand Up @@ -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!");
Expand Down Expand Up @@ -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 (
<div className="w-full relative">
<div className="p-2 flex items-center dark:text-[#D8D8D8] bg-[#ededed] dark:bg-[#202126] rounded transition-all relative">
Expand Down Expand Up @@ -391,8 +467,8 @@ export default function ChatInput({
{t("search.input.deepThink")}
</span>
</button>
<button
className={`h-5 px-2 inline-flex items-center border rounded-[10px] transition-colors relative ${
<div
className={`h-5 px-2 inline-flex items-center border rounded-[10px] transition-colors relative cursor-pointer ${
isSearchActive
? "bg-[rgba(0,114,255,0.3)] border-[rgba(0,114,255,0.3)]"
: "border-[#262727]"
Expand All @@ -406,14 +482,109 @@ export default function ChatInput({
: "text-[#333] dark:text-white"
}`}
/>

<span
className={
isSearchActive ? "text-[#0072FF]" : "dark:text-white"
}
>
{t("search.input.search")}
</span>
</button>

<Popover>
<PopoverButton className={clsx("flex items-center")}>
<ChevronDownIcon
className={clsx("size-4", [
isSearchActive
? "text-[#0072FF] dark:text-[#0072FF]"
: "text-[#333] dark:text-white",
])}
/>
</PopoverButton>

<PopoverPanel
anchor="top start"
className="min-w-[220px] bg-white dark:bg-[#202126] rounded-lg shadow-lg border border-gray-200 dark:border-gray-700"
>
<div
className="text-sm px-[12px] py-[18px]"
onClick={(e) => {
e.stopPropagation();
}}
>
<div className="flex justify-between mb-[18px]">
<span>{t("search.input.searchPopover.title")}</span>

<button
onClick={async () => {
setIsRefreshDataSource(true);

getDataSourceList();

setTimeout(() => {
setIsRefreshDataSource(false);
}, 1000);
}}
className="size-[24px] flex justify-center items-center rounded-lg border border-black/10 dark:border-white/10"
disabled={isRefreshDataSource}
>
<RefreshCw
className={`size-3 text-[#0287FF] transition-transform duration-1000 ${
isRefreshDataSource ? "animate-spin" : ""
}`}
/>
</button>
</div>
<ul className="flex flex-col gap-[16px]">
{state.dataSourceList?.map((item, index) => {
const { id, name } = item;

const isAll = index === 0;

return (
<li
key={id}
className="flex justify-between items-center"
>
<div className="flex items-center gap-[8px]">
{isAll ? (
<Layers className="size-[16px] text-[#0287FF]" />
) : (
<TypeIcon item={item} className="size-[16px]" />
)}

<span>{name}</span>
</div>

<Checkbox
checked={
isAll
? sourceDataIds.length ===
state.dataSourceList.length - 1
: sourceDataIds?.includes(id)
}
onChange={(value) =>
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 && (
<div className="size-full flex items-center justify-center group-data-[checked]:hidden">
<div className="size-[6px] bg-[#2F54EB]"></div>
</div>
)}

<CheckIcon className="hidden size-[12px] text-white group-data-[checked]:block" />
</Checkbox>
</li>
);
})}
</ul>
</div>
</PopoverPanel>
</Popover>
</div>

{/*<button*/}
{/* className="inline-flex items-center rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors relative"*/}
{/* onClick={openChatAI}*/}
Expand Down
6 changes: 5 additions & 1 deletion src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@
"connectionError": "Unable to connect to the server",
"reconnect": "Reconnect",
"deepThink": "Deep Think",
"search": "Search"
"search": "Search",
"searchPopover": {
"title": "Search Scope",
"allScope": "All Scope"
}
},
"main": {
"noDataAlt": "No data image",
Expand Down
6 changes: 5 additions & 1 deletion src/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@
"connectionError": "无法连接到服务器",
"reconnect": "重新连接",
"deepThink": "深度思考",
"search": "联网搜索"
"search": "联网搜索",
"searchPopover": {
"title": "搜索范围",
"allScope": "所有范围"
}
},
"main": {
"noDataAlt": "无数据图片",
Expand Down
6 changes: 6 additions & 0 deletions src/stores/searchStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import { persist } from "zustand/middleware";
export type ISearchStore = {
sourceData: any;
setSourceData: (sourceData: any) => void;
sourceDataIds: string[];
setSourceDataIds: (prevSourceDataId: string[]) => void;
};

export const useSearchStore = create<ISearchStore>()(
persist(
(set) => ({
sourceData: undefined,
setSourceData: (sourceData: any) => set({ sourceData }),
sourceDataIds: [],
setSourceDataIds: (sourceDataIds: string[]) => {
set({ sourceDataIds });
},
}),
{
name: "search-store",
Expand Down

0 comments on commit 7b4df60

Please # to comment.