From 6443aa7cfbdaac1f0ae1114e50d21713295f2756 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Sun, 17 Mar 2024 18:31:56 +0200 Subject: [PATCH 01/28] add react-router --- frontend/package.json | 1 + frontend/yarn.lock | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index cc40c377..1120cef3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", + "react-router-dom": "^6.22.3", "tailwind-merge": "^2.0.0", "uuid": "^9.0.1" }, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 3d29aae8..4c7aee65 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -456,6 +456,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@remix-run/router@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.3.tgz#d2509048d69dbb72d5389a14945339f1430b2d3c" + integrity sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w== + "@tailwindcss/forms@^0.5.6": version "0.5.6" resolved "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.6.tgz" @@ -1746,6 +1751,21 @@ react-refresh@^0.14.0: resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== +react-router-dom@^6.22.3: + version "6.22.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.3.tgz#9781415667fd1361a475146c5826d9f16752a691" + integrity sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw== + dependencies: + "@remix-run/router" "1.15.3" + react-router "6.22.3" + +react-router@6.22.3: + version "6.22.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.3.tgz#9d9142f35e08be08c736a2082db5f0c9540a885e" + integrity sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ== + dependencies: + "@remix-run/router" "1.15.3" + react@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" From c89251a25574ec218feb03b9550cd4628bd662b7 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Sun, 17 Mar 2024 19:47:12 +0200 Subject: [PATCH 02/28] extract state to App.tsx --- frontend/src/App.tsx | 118 +++++++++++++++++-------- frontend/src/components/ChatList.tsx | 12 +-- frontend/src/components/Config.tsx | 11 ++- frontend/src/components/ConfigList.tsx | 8 +- frontend/src/components/NewChat.tsx | 8 +- frontend/src/hooks/useChatList.ts | 12 +-- frontend/src/hooks/useConfigList.ts | 23 ++--- frontend/src/main.tsx | 15 +++- 8 files changed, 124 insertions(+), 83 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index df449b19..390a2f65 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,16 +11,27 @@ import { useConfigList } from "./hooks/useConfigList"; import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; import { TYPE_NAME } from "./constants.ts"; +import { Route, Routes, useNavigate, useParams } from "react-router-dom"; + +function NotFound() { + return
Page not found.
; +} function App() { + const navigate = useNavigate(); const [sidebarOpen, setSidebarOpen] = useState(false); const { configSchema, configDefaults } = useSchemas(); - const { chats, currentChat, createChat, enterChat } = useChatList(); - const { configs, currentConfig, saveConfig, enterConfig } = useConfigList(); + const { chats, createChat } = useChatList(); + const { configs, saveConfig } = useConfigList(); const { startStream, stopStream, stream } = useStreamState(); const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = useState(false); + const { assistantId, chatId } = useParams(); + const currentConfig = + configs?.find((config) => config.assistant_id === assistantId) ?? null; + const currentChat = chats?.find((chat) => chat.thread_id === chatId) ?? null; + useEffect(() => { let configurable = null; if (currentConfig) { @@ -93,6 +104,7 @@ function App() { message.message, currentConfig.assistant_id, ); + navigate(`/thread/${chat.thread_id}`); return startTurn(message, chat); }, [createChat, startTurn, currentConfig], @@ -103,53 +115,81 @@ function App() { if (currentChat) { stopStream?.(true); } - enterChat(id); if (!id) { - enterConfig(configs?.[0]?.assistant_id ?? null); + const firstAssistant = configs?.[0]?.assistant_id ?? null; + navigate(firstAssistant ? `/assistant/${firstAssistant}` : ""); window.scrollTo({ top: 0 }); + } else { + navigate(`/thread/${id}`); } if (sidebarOpen) { setSidebarOpen(false); } }, - [enterChat, stopStream, sidebarOpen, currentChat, enterConfig, configs], + [stopStream, sidebarOpen, currentChat, configs], ); - const selectConfig = useCallback( - (id: string | null) => { - enterConfig(id); - enterChat(null); - }, - [enterConfig, enterChat], - ); + const selectConfig = useCallback((id: string | null) => { + navigate(id ? `/assistant/${id}` : ""); + }, []); - const content = currentChat ? ( - - ) : currentConfig ? ( - - ) : ( - + const content = ( + + + } + /> + + } + /> + + } + /> + + } + /> + } /> + ); const currentChatConfig = configs?.find( diff --git a/frontend/src/components/ChatList.tsx b/frontend/src/components/ChatList.tsx index a8764296..314a3d2e 100644 --- a/frontend/src/components/ChatList.tsx +++ b/frontend/src/components/ChatList.tsx @@ -1,15 +1,15 @@ import { PlusIcon } from "@heroicons/react/24/outline"; -import { ChatListProps } from "../hooks/useChatList"; -import { ConfigListProps } from "../hooks/useConfigList"; +import { Chat, ChatListProps } from "../hooks/useChatList"; +import { Config } from "../hooks/useConfigList"; import { cn } from "../utils/cn"; export function ChatList(props: { chats: ChatListProps["chats"]; - currentChat: ChatListProps["currentChat"]; - enterChat: ChatListProps["enterChat"]; - currentConfig: ConfigListProps["currentConfig"]; - enterConfig: ConfigListProps["enterConfig"]; + currentChat: Chat | null; + enterChat: (id: string | null) => void; + currentConfig: Config | null; + enterConfig: (id: string | null) => void; }) { return ( <> diff --git a/frontend/src/components/Config.tsx b/frontend/src/components/Config.tsx index 3220bea7..f60eb552 100644 --- a/frontend/src/components/Config.tsx +++ b/frontend/src/components/Config.tsx @@ -4,7 +4,10 @@ import { useDropzone } from "react-dropzone"; import { orderBy, last } from "lodash"; import { v4 as uuidv4 } from "uuid"; -import { ConfigListProps } from "../hooks/useConfigList"; +import { + ConfigListProps, + Config as ConfigInterface, +} from "../hooks/useConfigList"; import { SchemaField, Schemas } from "../hooks/useSchemas"; import { cn } from "../utils/cn"; import { FileUploadDropzone } from "./FileUpload"; @@ -478,8 +481,9 @@ export function Config(props: { className?: string; configSchema: Schemas["configSchema"]; configDefaults: Schemas["configDefaults"]; - config: ConfigListProps["currentConfig"]; + config: ConfigInterface | null; saveConfig: ConfigListProps["saveConfig"]; + enterConfig: (id: string | null) => void; }) { const [values, setValues] = useState( props.config?.config ?? props.configDefaults, @@ -579,7 +583,8 @@ export function Config(props: { vals.configurable["type==agent/tools"] = [...selectedTools]; setSelectedTools([]); } - await props.saveConfig(key, vals!, files, isPublic); + const assistantId = await props.saveConfig(key, vals!, files, isPublic); + props.enterConfig(assistantId); setInflight(false); }} > diff --git a/frontend/src/components/ConfigList.tsx b/frontend/src/components/ConfigList.tsx index a2646590..a2878ea9 100644 --- a/frontend/src/components/ConfigList.tsx +++ b/frontend/src/components/ConfigList.tsx @@ -4,8 +4,8 @@ import { cn } from "../utils/cn"; function ConfigItem(props: { config: Config; - currentConfig: ConfigListProps["currentConfig"]; - enterConfig: ConfigListProps["enterConfig"]; + currentConfig: Config | null; + enterConfig: (id: string | null) => void; }) { return (
  • @@ -48,8 +48,8 @@ function ConfigItem(props: { export function ConfigList(props: { configs: ConfigListProps["configs"]; - currentConfig: ConfigListProps["currentConfig"]; - enterConfig: ConfigListProps["enterConfig"]; + currentConfig: Config | null; + enterConfig: (id: string | null) => void; }) { return ( <> diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index ba967ad3..2388f85a 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -2,13 +2,18 @@ import { ConfigList } from "./ConfigList"; import { Schemas } from "../hooks/useSchemas"; import TypingBox from "./TypingBox"; import { Config } from "./Config"; -import { ConfigListProps } from "../hooks/useConfigList"; +import { + ConfigListProps, + Config as ConfigInterface, +} from "../hooks/useConfigList"; import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; configDefaults: Schemas["configDefaults"]; + currentConfig: ConfigInterface | null; + enterConfig: (id: string | null) => void; startChat: (message: MessageWithFiles) => Promise; isDocumentRetrievalActive: boolean; } @@ -38,6 +43,7 @@ export function NewChat(props: NewChatProps) { configSchema={props.configSchema} configDefaults={props.configDefaults} saveConfig={props.saveConfig} + enterConfig={props.enterConfig} /> diff --git a/frontend/src/hooks/useChatList.ts b/frontend/src/hooks/useChatList.ts index b4360f74..9d95a556 100644 --- a/frontend/src/hooks/useChatList.ts +++ b/frontend/src/hooks/useChatList.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useReducer, useState } from "react"; +import { useCallback, useEffect, useReducer } from "react"; import orderBy from "lodash/orderBy"; import { v4 as uuidv4 } from "uuid"; @@ -34,13 +34,11 @@ export interface Chat { export interface ChatListProps { chats: Chat[] | null; - currentChat: Chat | null; createChat: ( name: string, assistant_id: string, thread_id?: string, ) => Promise; - enterChat: (id: string | null) => void; } function chatsReducer( @@ -60,7 +58,6 @@ function chatsReducer( export function useChatList(): ChatListProps { const [chats, setChats] = useReducer(chatsReducer, null); - const [current, setCurrent] = useState(null); useEffect(() => { async function fetchChats() { @@ -90,20 +87,13 @@ export function useChatList(): ChatListProps { }, }).then((r) => r.json()); setChats(saved); - setCurrent(saved.thread_id); return saved; }, [], ); - const enterChat = useCallback((id: string | null) => { - setCurrent(id); - }, []); - return { chats, - currentChat: chats?.find((c) => c.thread_id === current) || null, createChat, - enterChat, }; } diff --git a/frontend/src/hooks/useConfigList.ts b/frontend/src/hooks/useConfigList.ts index 561da5ab..5229f9a4 100644 --- a/frontend/src/hooks/useConfigList.ts +++ b/frontend/src/hooks/useConfigList.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useReducer, useState } from "react"; +import { useCallback, useEffect, useReducer } from "react"; import orderBy from "lodash/orderBy"; import { v4 as uuidv4 } from "uuid"; @@ -18,14 +18,12 @@ export interface Config { export interface ConfigListProps { configs: Config[] | null; - currentConfig: Config | null; saveConfig: ( key: string, config: Config["config"], files: File[], isPublic: boolean, - ) => Promise; - enterConfig: (id: string | null) => void; + ) => Promise; } function configsReducer( @@ -45,7 +43,6 @@ function configsReducer( export function useConfigList(): ConfigListProps { const [configs, setConfigs] = useReducer(configsReducer, null); - const [current, setCurrent] = useState(null); useEffect(() => { async function fetchConfigs() { @@ -69,19 +66,11 @@ export function useConfigList(): ConfigListProps { ).then((r) => r.json()), ]); setConfigs(myConfigs.concat(publicConfigs)); - if (publicConfigs.find((a: Config) => a.assistant_id === shared_id)) { - setCurrent(shared_id); - } } fetchConfigs(); }, []); - const enterConfig = useCallback((key: string | null) => { - setCurrent(key); - window.scrollTo({ top: 0 }); - }, []); - const saveConfig = useCallback( async ( name: string, @@ -89,7 +78,7 @@ export function useConfigList(): ConfigListProps { files: File[], isPublic: boolean, assistant_id: string = uuidv4(), - ) => { + ): Promise => { const formData = files.reduce((formData, file) => { formData.append("files", file); return formData; @@ -115,15 +104,13 @@ export function useConfigList(): ConfigListProps { : Promise.resolve(), ]); setConfigs({ ...saved, mine: true }); - enterConfig(saved.assistant_id); + return saved.assistant_id; }, - [enterConfig], + [], ); return { configs, - currentConfig: configs?.find((c) => c.assistant_id === current) || null, saveConfig, - enterConfig, }; } diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 6d36793d..a773b178 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,9 +2,22 @@ import ReactDOM from "react-dom/client"; import { v4 as uuidv4 } from "uuid"; import App from "./App.tsx"; import "./index.css"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { StrictMode } from "react"; if (document.cookie.indexOf("user_id") === -1) { document.cookie = `opengpts_user_id=${uuidv4()}`; } -ReactDOM.createRoot(document.getElementById("root")!).render(); +const router = createBrowserRouter([ + { + path: "*", + element: , + }, +]); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); From e7024996440a1f93b1f06afd7bc4d5c182609eb5 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Sun, 17 Mar 2024 21:59:36 +0200 Subject: [PATCH 03/28] Chat.tsx: working --- frontend/src/App.tsx | 21 ++++++++++++--------- frontend/src/components/Chat.tsx | 10 +++++++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 390a2f65..9c72626a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,7 +11,7 @@ import { useConfigList } from "./hooks/useConfigList"; import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; import { TYPE_NAME } from "./constants.ts"; -import { Route, Routes, useNavigate, useParams } from "react-router-dom"; +import { Route, Routes, useNavigate } from "react-router-dom"; function NotFound() { return
    Page not found.
    ; @@ -27,10 +27,12 @@ function App() { const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = useState(false); - const { assistantId, chatId } = useParams(); + const [currentChatId, setCurrentChatId] = useState(null); + const [currentConfigId, setCurrentConfigId] = useState(null); const currentConfig = - configs?.find((config) => config.assistant_id === assistantId) ?? null; - const currentChat = chats?.find((chat) => chat.thread_id === chatId) ?? null; + configs?.find((config) => config.assistant_id === currentConfigId) ?? null; + const currentChat = + chats?.find((chat) => chat.thread_id === currentChatId) ?? null; useEffect(() => { let configurable = null; @@ -130,6 +132,7 @@ function App() { ); const selectConfig = useCallback((id: string | null) => { + setCurrentChatId(null); navigate(id ? `/assistant/${id}` : ""); }, []); @@ -139,11 +142,11 @@ function App() { path="/thread/:chatId" element={ } /> @@ -163,11 +166,11 @@ function App() { } /> { - chat: ChatType; startStream: (message?: MessageWithFiles) => Promise; isDocumentRetrievalActive: boolean; + setCurrentChatId: (id: string | null) => void; } function usePrevious(value: T): T | undefined { @@ -22,11 +22,15 @@ function usePrevious(value: T): T | undefined { } export function Chat(props: ChatProps) { + const { chatId } = useParams(); const { messages, resumeable } = useChatMessages( - props.chat.thread_id, + chatId ?? null, props.stream, props.stopStream, ); + useEffect(() => { + props.setCurrentChatId(chatId ?? null); + }, [chatId, props]); const prevMessages = usePrevious(messages); useEffect(() => { scrollTo({ From 9fe758c54e1c8d53257856b157efaa922c8b0e70 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Sun, 17 Mar 2024 22:37:56 +0200 Subject: [PATCH 04/28] assistant confs --- frontend/src/App.tsx | 33 +++++++++++------------ frontend/src/components/NewChat.tsx | 38 +++++++++++++++++++++------ frontend/src/components/TypingBox.tsx | 2 +- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9c72626a..36f329f8 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -100,12 +100,8 @@ function App() { ); const startChat = useCallback( - async (message: MessageWithFiles) => { - if (!currentConfig) return; - const chat = await createChat( - message.message, - currentConfig.assistant_id, - ); + async (assistantId: string, message: MessageWithFiles) => { + const chat = await createChat(message.message, assistantId); navigate(`/thread/${chat.thread_id}`); return startTurn(message, chat); }, @@ -118,8 +114,7 @@ function App() { stopStream?.(true); } if (!id) { - const firstAssistant = configs?.[0]?.assistant_id ?? null; - navigate(firstAssistant ? `/assistant/${firstAssistant}` : ""); + navigate("/thread/new"); window.scrollTo({ top: 0 }); } else { navigate(`/thread/${id}`); @@ -128,13 +123,17 @@ function App() { setSidebarOpen(false); } }, - [stopStream, sidebarOpen, currentChat, configs], + [currentChat, sidebarOpen, stopStream, navigate], ); - const selectConfig = useCallback((id: string | null) => { - setCurrentChatId(null); - navigate(id ? `/assistant/${id}` : ""); - }, []); + const selectConfig = useCallback( + (id: string | null) => { + setCurrentChatId(null); + setCurrentConfigId(id); + navigate(id ? `/assistant/${id}` : ""); + }, + [navigate], + ); const content = ( @@ -158,7 +157,6 @@ function App() { configSchema={configSchema} configDefaults={configDefaults} configs={configs} - currentConfig={currentConfig} saveConfig={saveConfig} enterConfig={selectConfig} isDocumentRetrievalActive={isDocumentRetrievalActive} @@ -168,13 +166,14 @@ function App() { } /> diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 2388f85a..2c5ce076 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -8,38 +8,56 @@ import { } from "../hooks/useConfigList"; import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; +import {useEffect, useState} from "react"; +import {useNavigate, useParams} from "react-router-dom"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; configDefaults: Schemas["configDefaults"]; - currentConfig: ConfigInterface | null; enterConfig: (id: string | null) => void; - startChat: (message: MessageWithFiles) => Promise; + startChat: (assistantId: string, message: MessageWithFiles) => Promise; isDocumentRetrievalActive: boolean; } export function NewChat(props: NewChatProps) { + const navigator = useNavigate(); + const {assistantId} = useParams(); + const [selectedConfig, setSelectedConfig] = useState( + null, + ); + + useEffect(() => { + if (assistantId) { + setSelectedConfig( + props.configs?.find((c) => c.assistant_id === assistantId) ?? null, + ) + } + }, [assistantId, props.configs]); + + return (
    + navigator(`/assistant/${id}`) + } />
    { + if (selectedConfig) { + await props.startChat(selectedConfig.assistant_id, msg); + } + }} isDocumentRetrievalActive={props.isDocumentRetrievalActive} />
    diff --git a/frontend/src/components/TypingBox.tsx b/frontend/src/components/TypingBox.tsx index 6097c87e..401ef427 100644 --- a/frontend/src/components/TypingBox.tsx +++ b/frontend/src/components/TypingBox.tsx @@ -43,7 +43,7 @@ function convertBytesToReadableSize(bytes: number) { } export default function TypingBox(props: { - onSubmit: (data: MessageWithFiles) => Promise; + onSubmit: (data: MessageWithFiles) => void; onInterrupt?: () => void; inflight?: boolean; isDocumentRetrievalActive: boolean; From 170ca4e444eaf322c3baf2d0c3d236a6f7bb9f09 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Sun, 17 Mar 2024 23:32:47 +0200 Subject: [PATCH 05/28] remove thread/new --- frontend/src/App.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 36f329f8..46f56c95 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -114,7 +114,8 @@ function App() { stopStream?.(true); } if (!id) { - navigate("/thread/new"); + const firstAssistant = configs?.[0]?.assistant_id ?? null; + navigate(firstAssistant ? `/assistant/${firstAssistant}` : "/"); window.scrollTo({ top: 0 }); } else { navigate(`/thread/${id}`); @@ -130,7 +131,7 @@ function App() { (id: string | null) => { setCurrentChatId(null); setCurrentConfigId(id); - navigate(id ? `/assistant/${id}` : ""); + navigate(id ? `/assistant/${id}` : "/"); }, [navigate], ); @@ -149,20 +150,6 @@ function App() { /> } /> - - } - /> Date: Mon, 18 Mar 2024 11:09:27 +0200 Subject: [PATCH 06/28] update useEffect deps --- frontend/src/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 46f56c95..b29e8d0e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -105,7 +105,7 @@ function App() { navigate(`/thread/${chat.thread_id}`); return startTurn(message, chat); }, - [createChat, startTurn, currentConfig], + [createChat, navigate, startTurn], ); const selectChat = useCallback( @@ -124,7 +124,7 @@ function App() { setSidebarOpen(false); } }, - [currentChat, sidebarOpen, stopStream, navigate], + [currentChat, sidebarOpen, stopStream, configs, navigate], ); const selectConfig = useCallback( From 7c832352111fb295bf32c3c2c1abc2acd9c73cc8 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 11:16:06 +0200 Subject: [PATCH 07/28] config without shared_id --- frontend/src/components/Config.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Config.tsx b/frontend/src/components/Config.tsx index f60eb552..a94af64b 100644 --- a/frontend/src/components/Config.tsx +++ b/frontend/src/components/Config.tsx @@ -403,7 +403,7 @@ function ToolSelectionField(props: { function PublicLink(props: { assistantId: string }) { const currentLink = window.location.href; - const link = currentLink.includes("shared_id=") + const link = currentLink.includes(props.assistantId) ? currentLink : currentLink + "?shared_id=" + props.assistantId; return ( From 9dcb2f39d0e5a4ff25810bdbb2d29bad3ff25a8b Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 11:42:47 +0200 Subject: [PATCH 08/28] sharing --- frontend/src/components/NewChat.tsx | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 2c5ce076..54d78b2d 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -8,8 +8,8 @@ import { } from "../hooks/useConfigList"; import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; -import {useEffect, useState} from "react"; -import {useNavigate, useParams} from "react-router-dom"; +import { useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; @@ -21,20 +21,32 @@ interface NewChatProps extends ConfigListProps { export function NewChat(props: NewChatProps) { const navigator = useNavigate(); - const {assistantId} = useParams(); + const { assistantId } = useParams(); const [selectedConfig, setSelectedConfig] = useState( null, ); useEffect(() => { if (assistantId) { - setSelectedConfig( - props.configs?.find((c) => c.assistant_id === assistantId) ?? null, - ) + (async () => { + let matchingConfig = + props.configs?.find((c) => c.assistant_id === assistantId) ?? null; + if (!matchingConfig) { + const response = await fetch( + `/assistants/public/?shared_id=${assistantId}`, + { + headers: { + Accept: "application/json", + }, + }, + ); + matchingConfig = await response.json(); + } + setSelectedConfig(matchingConfig); + })(); } }, [assistantId, props.configs]); - return (
    - navigator(`/assistant/${id}`) - } + enterConfig={(id) => navigator(`/assistant/${id}`)} />
    From bbbd4ea4d282eda4ae74f4b2ee01c4a2671b4024 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 12:05:52 +0200 Subject: [PATCH 09/28] fe: refactor --- frontend/src/App.tsx | 88 +++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b29e8d0e..b370b243 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -136,51 +136,6 @@ function App() { [navigate], ); - const content = ( - - - } - /> - - } - /> - - } - /> - } /> - - ); - const currentChatConfig = configs?.find( (c) => c.assistant_id === currentChat?.assistant_id, ); @@ -217,7 +172,48 @@ function App() { /> } > - {configSchema ? content : null} + + + } + /> + + } + /> + + } + /> + } /> + ); } From a8cbb5589cc0acc979ae85a2b6222b550c70b5a4 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 12:14:24 +0200 Subject: [PATCH 10/28] matchingConfig --- frontend/src/components/NewChat.tsx | 11 +++++++---- frontend/src/hooks/useChatList.ts | 5 +++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 54d78b2d..78d8853d 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -29,8 +29,9 @@ export function NewChat(props: NewChatProps) { useEffect(() => { if (assistantId) { (async () => { - let matchingConfig = - props.configs?.find((c) => c.assistant_id === assistantId) ?? null; + let matchingConfig = props.configs?.find( + (c) => c.assistant_id === assistantId, + ); if (!matchingConfig) { const response = await fetch( `/assistants/public/?shared_id=${assistantId}`, @@ -40,9 +41,11 @@ export function NewChat(props: NewChatProps) { }, }, ); - matchingConfig = await response.json(); + matchingConfig = ((await response.json()) as ConfigInterface[]).find( + (c) => c.assistant_id === assistantId, + ); } - setSelectedConfig(matchingConfig); + setSelectedConfig(matchingConfig ?? null); })(); } }, [assistantId, props.configs]); diff --git a/frontend/src/hooks/useChatList.ts b/frontend/src/hooks/useChatList.ts index 9d95a556..ff36daad 100644 --- a/frontend/src/hooks/useChatList.ts +++ b/frontend/src/hooks/useChatList.ts @@ -78,14 +78,15 @@ export function useChatList(): ChatListProps { assistant_id: string, thread_id: string = uuidv4(), ) => { - const saved = await fetch(`/threads/${thread_id}`, { + const response = await fetch(`/threads/${thread_id}`, { method: "PUT", body: JSON.stringify({ assistant_id, name }), headers: { "Content-Type": "application/json", Accept: "application/json", }, - }).then((r) => r.json()); + }); + const saved = await response.json(); setChats(saved); return saved; }, From 34d90c02306955699d6afcf703817e72a811fa7e Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 14:20:31 +0200 Subject: [PATCH 11/28] cookie --- frontend/src/App.tsx | 33 ++++++++++++++++------------- frontend/src/components/Chat.tsx | 13 +++++++++--- frontend/src/components/NewChat.tsx | 7 ++++-- frontend/src/main.tsx | 2 +- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b370b243..fc75e739 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,10 +4,13 @@ import { Chat } from "./components/Chat"; import { ChatList } from "./components/ChatList"; import { Layout } from "./components/Layout"; import { NewChat } from "./components/NewChat"; -import { Chat as ChatType, useChatList } from "./hooks/useChatList"; +import { useChatList } from "./hooks/useChatList"; import { useSchemas } from "./hooks/useSchemas"; import { useStreamState } from "./hooks/useStreamState"; -import { useConfigList } from "./hooks/useConfigList"; +import { + useConfigList, + Config as ConfigInterface, +} from "./hooks/useConfigList"; import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; import { TYPE_NAME } from "./constants.ts"; @@ -60,12 +63,11 @@ function App() { }, [currentConfig, currentChat, configs]); const startTurn = useCallback( - async (message?: MessageWithFiles, chat: ChatType | null = currentChat) => { - if (!chat) return; - const config = configs?.find( - (c) => c.assistant_id === chat.assistant_id, - )?.config; - if (!config) return; + async ( + message: MessageWithFiles | null, + thread_id: string, + assistant_id: string, + ) => { const files = message?.files || []; if (files.length > 0) { const formData = files.reduce((formData, file) => { @@ -74,7 +76,7 @@ function App() { }, new FormData()); formData.append( "config", - JSON.stringify({ configurable: { thread_id: chat.thread_id } }), + JSON.stringify({ configurable: { thread_id } }), ); await fetch(`/ingest`, { method: "POST", @@ -92,18 +94,18 @@ function App() { }, ] : null, - chat.assistant_id, - chat.thread_id, + assistant_id, + thread_id, ); }, - [currentChat, startStream, configs], + [startStream], ); const startChat = useCallback( - async (assistantId: string, message: MessageWithFiles) => { - const chat = await createChat(message.message, assistantId); + async (config: ConfigInterface, message: MessageWithFiles) => { + const chat = await createChat(message.message, config.assistant_id); navigate(`/thread/${chat.thread_id}`); - return startTurn(message, chat); + return startTurn(message, chat.thread_id, chat.assistant_id); }, [createChat, navigate, startTurn], ); @@ -182,6 +184,7 @@ function App() { stream={stream} isDocumentRetrievalActive={isDocumentRetrievalActive} setCurrentChatId={setCurrentChatId} + assistantId={currentChat?.assistant_id ?? null} /> } /> diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index f4257144..47eb2aaa 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -8,9 +8,14 @@ import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; interface ChatProps extends Pick { - startStream: (message?: MessageWithFiles) => Promise; + startStream: ( + message: MessageWithFiles | null, + thread_id: string, + assistant_id: string, + ) => Promise; isDocumentRetrievalActive: boolean; setCurrentChatId: (id: string | null) => void; + assistantId: string | null; } function usePrevious(value: T): T | undefined { @@ -42,6 +47,8 @@ export function Chat(props: ChatProps) { }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [messages]); + if (!chatId || !props.assistantId) return
    ...
    ; + return (
    {messages?.map((msg, i) => ( @@ -68,7 +75,7 @@ export function Chat(props: ChatProps) { {resumeable && props.stream?.status !== "inflight" && (
    props.startStream()} + onClick={() => props.startStream(null, chatId, props.assistantId!)} > Click to continue. @@ -76,7 +83,7 @@ export function Chat(props: ChatProps) { )}
    props.startStream(msg, chatId, props.assistantId!)} onInterrupt={ props.stream?.status === "inflight" ? props.stopStream : undefined } diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 78d8853d..38231069 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -15,7 +15,10 @@ interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; configDefaults: Schemas["configDefaults"]; enterConfig: (id: string | null) => void; - startChat: (assistantId: string, message: MessageWithFiles) => Promise; + startChat: ( + config: ConfigInterface, + message: MessageWithFiles, + ) => Promise; isDocumentRetrievalActive: boolean; } @@ -83,7 +86,7 @@ export function NewChat(props: NewChatProps) { { if (selectedConfig) { - await props.startChat(selectedConfig.assistant_id, msg); + await props.startChat(selectedConfig, msg); } }} isDocumentRetrievalActive={props.isDocumentRetrievalActive} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index a773b178..f90d616e 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -6,7 +6,7 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { StrictMode } from "react"; if (document.cookie.indexOf("user_id") === -1) { - document.cookie = `opengpts_user_id=${uuidv4()}`; + document.cookie = `opengpts_user_id=${uuidv4()}; path=/; SameSite=Lax`; } const router = createBrowserRouter([ From ba0f9c31ae9242e7c328264916a6313bf86bea72 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 15:17:10 +0200 Subject: [PATCH 12/28] remove old mechanism --- frontend/src/hooks/useConfigList.ts | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/frontend/src/hooks/useConfigList.ts b/frontend/src/hooks/useConfigList.ts index 5229f9a4..276056e8 100644 --- a/frontend/src/hooks/useConfigList.ts +++ b/frontend/src/hooks/useConfigList.ts @@ -46,26 +46,14 @@ export function useConfigList(): ConfigListProps { useEffect(() => { async function fetchConfigs() { - const searchParams = new URLSearchParams(window.location.search); - const shared_id = searchParams.get("shared_id"); - const [myConfigs, publicConfigs] = await Promise.all([ - fetch("/assistants/", { - headers: { - Accept: "application/json", - }, - }) - .then((r) => r.json()) - .then((li) => li.map((c: Config) => ({ ...c, mine: true }))), - fetch( - "/assistants/public/" + (shared_id ? `?shared_id=${shared_id}` : ""), - { - headers: { - Accept: "application/json", - }, - }, - ).then((r) => r.json()), - ]); - setConfigs(myConfigs.concat(publicConfigs)); + const myConfigs = await fetch("/assistants/", { + headers: { + Accept: "application/json", + }, + }) + .then((r) => r.json()) + .then((li) => li.map((c: Config) => ({ ...c, mine: true }))); + setConfigs(myConfigs); } fetchConfigs(); From da9ba0d6d61865fcc7ff2077709069bfe511702a Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Mon, 18 Mar 2024 15:51:29 +0200 Subject: [PATCH 13/28] show all chats --- frontend/src/App.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fc75e739..4c70b18e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -163,9 +163,7 @@ function App() { { if (configs === null || chats === null) return null; - return chats.filter((c) => - configs.some((config) => config.assistant_id === c.assistant_id), - ); + return chats; }, [chats, configs])} currentChat={currentChat} enterChat={selectChat} From 4f195cd43aa34e9ab1c33b1955562b3892c9650b Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Wed, 20 Mar 2024 12:42:32 +0200 Subject: [PATCH 14/28] add react query --- frontend/package.json | 1 + frontend/src/main.tsx | 7 +++- frontend/yarn.lock | 85 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 1120cef3..f8f1c7c2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", + "react-query": "^3.39.3", "react-router-dom": "^6.22.3", "tailwind-merge": "^2.0.0", "uuid": "^9.0.1" diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index f90d616e..47738bcf 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -4,11 +4,14 @@ import App from "./App.tsx"; import "./index.css"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { StrictMode } from "react"; +import {QueryClient, QueryClientProvider} from "react-query"; if (document.cookie.indexOf("user_id") === -1) { document.cookie = `opengpts_user_id=${uuidv4()}; path=/; SameSite=Lax`; } +const queryClient = new QueryClient(); + const router = createBrowserRouter([ { path: "*", @@ -18,6 +21,8 @@ const router = createBrowserRouter([ ReactDOM.createRoot(document.getElementById("root")!).render( - + + + , ); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4c7aee65..67836c5b 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -191,6 +191,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.8", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.1.tgz#431f9a794d173b53720e69a6464abc6f0e2a5c57" + integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz" @@ -759,6 +766,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +big-integer@^1.6.16: + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" @@ -779,6 +791,20 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + browserslist@^4.21.10, browserslist@^4.21.9: version "4.22.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz" @@ -916,6 +942,11 @@ deep-is@^0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +detect-node@^2.0.4, detect-node@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" @@ -1360,6 +1391,11 @@ jiti@^1.19.1: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" @@ -1475,6 +1511,14 @@ marked@^9.1.5: resolved "https://registry.npmjs.org/marked/-/marked-9.1.5.tgz" integrity sha512-14QG3shv8Kg/xc0Yh6TNkMj90wXH9mmldi5941I2OevfJ/FQAFLEwtwU2/FfgSAOMlWHrEukWSGQf8MiVYNG2A== +match-sorter@^6.0.2: + version "6.3.4" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.4.tgz#afa779d8e922c81971fbcb4781c7003ace781be7" + integrity sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg== + dependencies: + "@babel/runtime" "^7.23.8" + remove-accents "0.5.0" + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" @@ -1488,6 +1532,11 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + mini-svg-data-uri@^1.2.3: version "1.4.4" resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz" @@ -1514,6 +1563,13 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA== + dependencies: + big-integer "^1.6.16" + nanoid@^3.3.6: version "3.3.6" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz" @@ -1549,6 +1605,11 @@ object-hash@^3.0.0: resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + once@^1.3.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" @@ -1746,6 +1807,15 @@ react-is@^16.13.1: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-query@^3.39.3: + version "3.39.3" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35" + integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-refresh@^0.14.0: version "0.14.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz" @@ -1792,6 +1862,11 @@ regenerator-runtime@^0.14.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz" integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== +remove-accents@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687" + integrity sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -1811,7 +1886,7 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -2015,6 +2090,14 @@ typescript@^5.0.2: resolved "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + update-browserslist-db@^1.0.13: version "1.0.13" resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" From a324ed8cbbb989be3e86ead7b98b88c804177fc9 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Wed, 20 Mar 2024 13:42:49 +0200 Subject: [PATCH 15/28] isDocumentRetrieval: move useEffect to TypingBox --- frontend/src/App.tsx | 35 +++-------------------- frontend/src/components/Chat.tsx | 10 +++++-- frontend/src/components/NewChat.tsx | 5 ++-- frontend/src/components/TypingBox.tsx | 40 ++++++++++++++++++++++++--- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4c70b18e..4e64b078 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { InformationCircleIcon } from "@heroicons/react/24/outline"; import { Chat } from "./components/Chat"; import { ChatList } from "./components/ChatList"; @@ -13,7 +13,6 @@ import { } from "./hooks/useConfigList"; import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; -import { TYPE_NAME } from "./constants.ts"; import { Route, Routes, useNavigate } from "react-router-dom"; function NotFound() { @@ -27,8 +26,6 @@ function App() { const { chats, createChat } = useChatList(); const { configs, saveConfig } = useConfigList(); const { startStream, stopStream, stream } = useStreamState(); - const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = - useState(false); const [currentChatId, setCurrentChatId] = useState(null); const [currentConfigId, setCurrentConfigId] = useState(null); @@ -37,31 +34,6 @@ function App() { const currentChat = chats?.find((chat) => chat.thread_id === currentChatId) ?? null; - useEffect(() => { - let configurable = null; - if (currentConfig) { - configurable = currentConfig?.config?.configurable; - } - if (currentChat && configs) { - const conf = configs.find( - (c) => c.assistant_id === currentChat.assistant_id, - ); - configurable = conf?.config?.configurable; - } - const agent_type = configurable?.["type"] as TYPE_NAME | null; - if (agent_type === null || agent_type === "chatbot") { - setIsDocumentRetrievalActive(false); - return; - } - if (agent_type === "chat_retrieval") { - setIsDocumentRetrievalActive(true); - return; - } - const tools = - (configurable?.["type==agent/tools"] as { name: string }[]) ?? []; - setIsDocumentRetrievalActive(tools.some((t) => t.name === "Retrieval")); - }, [currentConfig, currentChat, configs]); - const startTurn = useCallback( async ( message: MessageWithFiles | null, @@ -180,9 +152,11 @@ function App() { startStream={startTurn} stopStream={stopStream} stream={stream} - isDocumentRetrievalActive={isDocumentRetrievalActive} setCurrentChatId={setCurrentChatId} assistantId={currentChat?.assistant_id ?? null} + currentChat={currentChat} + config={currentConfig} + configs={configs} /> } /> @@ -196,7 +170,6 @@ function App() { configs={configs} saveConfig={saveConfig} enterConfig={selectConfig} - isDocumentRetrievalActive={isDocumentRetrievalActive} /> } /> diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 47eb2aaa..b50e0d05 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -1,11 +1,13 @@ import { useEffect, useRef } from "react"; import { StreamStateProps } from "../hooks/useStreamState"; import { useChatMessages } from "../hooks/useChatMessages"; +import { Chat as ChatInterface } from "../hooks/useChatList"; import TypingBox from "./TypingBox"; import { Message } from "./Message"; import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; +import {Config} from "../hooks/useConfigList.ts"; interface ChatProps extends Pick { startStream: ( @@ -13,9 +15,11 @@ interface ChatProps extends Pick { thread_id: string, assistant_id: string, ) => Promise; - isDocumentRetrievalActive: boolean; setCurrentChatId: (id: string | null) => void; assistantId: string | null; + configs: Config[] | null; + config: Config | null; + currentChat: ChatInterface | null } function usePrevious(value: T): T | undefined { @@ -88,7 +92,9 @@ export function Chat(props: ChatProps) { props.stream?.status === "inflight" ? props.stopStream : undefined } inflight={props.stream?.status === "inflight"} - isDocumentRetrievalActive={props.isDocumentRetrievalActive} + configs={props.configs || []} + currentConfig={props.config} + currentChat={props.currentChat} />
    diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 38231069..e77e4df0 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -19,7 +19,6 @@ interface NewChatProps extends ConfigListProps { config: ConfigInterface, message: MessageWithFiles, ) => Promise; - isDocumentRetrievalActive: boolean; } export function NewChat(props: NewChatProps) { @@ -89,7 +88,9 @@ export function NewChat(props: NewChatProps) { await props.startChat(selectedConfig, msg); } }} - isDocumentRetrievalActive={props.isDocumentRetrievalActive} + currentConfig={selectedConfig} + configs={props.configs || []} + currentChat={null} />
    diff --git a/frontend/src/components/TypingBox.tsx b/frontend/src/components/TypingBox.tsx index 401ef427..f59d195b 100644 --- a/frontend/src/components/TypingBox.tsx +++ b/frontend/src/components/TypingBox.tsx @@ -7,10 +7,12 @@ import { DocumentIcon, } from "@heroicons/react/20/solid"; import { cn } from "../utils/cn"; -import { Fragment, useCallback, useState } from "react"; +import {Fragment, useCallback, useEffect, useState} from "react"; import { useDropzone } from "react-dropzone"; import { MessageWithFiles } from "../utils/formTypes.ts"; -import { DROPZONE_CONFIG } from "../constants.ts"; +import {DROPZONE_CONFIG, TYPE_NAME} from "../constants.ts"; +import {Config} from "../hooks/useConfigList.ts"; +import {Chat} from "../hooks/useChatList.ts"; function getFileTypeIcon(fileType: string) { switch (fileType) { @@ -46,11 +48,41 @@ export default function TypingBox(props: { onSubmit: (data: MessageWithFiles) => void; onInterrupt?: () => void; inflight?: boolean; - isDocumentRetrievalActive: boolean; + configs: Config[]; + currentConfig: Config | null; + currentChat: Chat | null; }) { const [inflight, setInflight] = useState(false); const isInflight = props.inflight || inflight; const [files, setFiles] = useState([]); + const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = useState(false); + + const {currentConfig, currentChat, configs} = props; + + useEffect(() => { + let configurable = null; + if (currentConfig) { + configurable = currentConfig.config?.configurable; + } + if (currentChat && configs) { + const conf = configs.find( + (c) => c.assistant_id === currentChat.assistant_id, + ); + configurable = conf?.config?.configurable; + } + const agent_type = configurable?.["type"] as TYPE_NAME | null; + if (agent_type === null || agent_type === "chatbot") { + setIsDocumentRetrievalActive(false); + return; + } + if (agent_type === "chat_retrieval") { + setIsDocumentRetrievalActive(true); + return; + } + const tools = + (configurable?.["type==agent/tools"] as { name: string }[]) ?? []; + setIsDocumentRetrievalActive(tools.some((t) => t.name === "Retrieval")); + }, [currentConfig, currentChat, configs]); const onDrop = useCallback((acceptedFiles: File[]) => { setFiles((prevFiles) => { @@ -146,7 +178,7 @@ export default function TypingBox(props: { placeholder="Send a message" readOnly={isInflight} /> - {props.isDocumentRetrievalActive && ( + {isDocumentRetrievalActive && (
    Date: Wed, 20 Mar 2024 17:35:33 +0200 Subject: [PATCH 16/28] query --- frontend/src/api/assistants.ts | 22 ++++++++++ frontend/src/components/Chat.tsx | 4 +- frontend/src/components/NewChat.tsx | 59 ++++++++++++--------------- frontend/src/components/TypingBox.tsx | 13 +++--- frontend/src/main.tsx | 2 +- 5 files changed, 59 insertions(+), 41 deletions(-) create mode 100644 frontend/src/api/assistants.ts diff --git a/frontend/src/api/assistants.ts b/frontend/src/api/assistants.ts new file mode 100644 index 00000000..422ac1dc --- /dev/null +++ b/frontend/src/api/assistants.ts @@ -0,0 +1,22 @@ +import { Config } from "../hooks/useConfigList"; + +export async function getAssistant( + assistantId: string, +): Promise { + console.log("HERE", assistantId) + try { + let response = await fetch(`/assistants/${assistantId}`); + if (!response.ok) { + // If the first request fails, try the public assistant endpoint + response = await fetch(`/assistants/public/?shared_id=${assistantId}`); + if (!response.ok) { + return null; + } + } + + return (await response.json()) as Config; + } catch (error) { + console.error("Failed to fetch assistant:", error); + return null; + } +} diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index b50e0d05..d3deaad1 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -7,7 +7,7 @@ import { Message } from "./Message"; import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; -import {Config} from "../hooks/useConfigList.ts"; +import { Config } from "../hooks/useConfigList.ts"; interface ChatProps extends Pick { startStream: ( @@ -19,7 +19,7 @@ interface ChatProps extends Pick { assistantId: string | null; configs: Config[] | null; config: Config | null; - currentChat: ChatInterface | null + currentChat: ChatInterface | null; } function usePrevious(value: T): T | undefined { diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index e77e4df0..9e5d97e6 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -8,8 +8,9 @@ import { } from "../hooks/useConfigList"; import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; -import { useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import { useQuery } from "react-query"; +import { getAssistant } from "../api/assistants.ts"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; @@ -24,46 +25,40 @@ interface NewChatProps extends ConfigListProps { export function NewChat(props: NewChatProps) { const navigator = useNavigate(); const { assistantId } = useParams(); - const [selectedConfig, setSelectedConfig] = useState( - null, + + const { + data: assistantConfig, + isLoading, + isError, + error, + } = useQuery( + ["assistant", assistantId], + () => getAssistant(assistantId as string), + { + enabled: !!assistantId, + }, ); - useEffect(() => { - if (assistantId) { - (async () => { - let matchingConfig = props.configs?.find( - (c) => c.assistant_id === assistantId, - ); - if (!matchingConfig) { - const response = await fetch( - `/assistants/public/?shared_id=${assistantId}`, - { - headers: { - Accept: "application/json", - }, - }, - ); - matchingConfig = ((await response.json()) as ConfigInterface[]).find( - (c) => c.assistant_id === assistantId, - ); - } - setSelectedConfig(matchingConfig ?? null); - })(); - } - }, [assistantId, props.configs]); + if (isLoading) return
    Loading...
    ; + if (isError) { + const message = (error as Error).message; + return
    Error: {message}
    ; + } + if (!assistantConfig) + return
    Could not find assistant with given id.
    ; return (
    navigator(`/assistant/${id}`)} />
    @@ -72,7 +67,7 @@ export function NewChat(props: NewChatProps) {
    { - if (selectedConfig) { - await props.startChat(selectedConfig, msg); + if (assistantConfig) { + await props.startChat(assistantConfig, msg); } }} - currentConfig={selectedConfig} + currentConfig={assistantConfig} configs={props.configs || []} currentChat={null} /> diff --git a/frontend/src/components/TypingBox.tsx b/frontend/src/components/TypingBox.tsx index f59d195b..915eb987 100644 --- a/frontend/src/components/TypingBox.tsx +++ b/frontend/src/components/TypingBox.tsx @@ -7,12 +7,12 @@ import { DocumentIcon, } from "@heroicons/react/20/solid"; import { cn } from "../utils/cn"; -import {Fragment, useCallback, useEffect, useState} from "react"; +import { Fragment, useCallback, useEffect, useState } from "react"; import { useDropzone } from "react-dropzone"; import { MessageWithFiles } from "../utils/formTypes.ts"; -import {DROPZONE_CONFIG, TYPE_NAME} from "../constants.ts"; -import {Config} from "../hooks/useConfigList.ts"; -import {Chat} from "../hooks/useChatList.ts"; +import { DROPZONE_CONFIG, TYPE_NAME } from "../constants.ts"; +import { Config } from "../hooks/useConfigList.ts"; +import { Chat } from "../hooks/useChatList.ts"; function getFileTypeIcon(fileType: string) { switch (fileType) { @@ -55,9 +55,10 @@ export default function TypingBox(props: { const [inflight, setInflight] = useState(false); const isInflight = props.inflight || inflight; const [files, setFiles] = useState([]); - const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = useState(false); + const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = + useState(false); - const {currentConfig, currentChat, configs} = props; + const { currentConfig, currentChat, configs } = props; useEffect(() => { let configurable = null; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 47738bcf..11465432 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -4,7 +4,7 @@ import App from "./App.tsx"; import "./index.css"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { StrictMode } from "react"; -import {QueryClient, QueryClientProvider} from "react-query"; +import { QueryClient, QueryClientProvider } from "react-query"; if (document.cookie.indexOf("user_id") === -1) { document.cookie = `opengpts_user_id=${uuidv4()}; path=/; SameSite=Lax`; From fb7409131f3e747b32f0a8e6781daba080175baa Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Wed, 20 Mar 2024 17:49:31 +0200 Subject: [PATCH 17/28] get_assistant interface returning also public assistants --- frontend/src/api/assistants.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/frontend/src/api/assistants.ts b/frontend/src/api/assistants.ts index 422ac1dc..74402629 100644 --- a/frontend/src/api/assistants.ts +++ b/frontend/src/api/assistants.ts @@ -3,17 +3,11 @@ import { Config } from "../hooks/useConfigList"; export async function getAssistant( assistantId: string, ): Promise { - console.log("HERE", assistantId) try { - let response = await fetch(`/assistants/${assistantId}`); + const response = await fetch(`/assistants/${assistantId}`); if (!response.ok) { - // If the first request fails, try the public assistant endpoint - response = await fetch(`/assistants/public/?shared_id=${assistantId}`); - if (!response.ok) { - return null; - } + return null; } - return (await response.json()) as Config; } catch (error) { console.error("Failed to fetch assistant:", error); From 076010f1d426c1720efa5be099e6c516eb2f641a Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 14:00:22 +0200 Subject: [PATCH 18/28] Chat.tsx: use react query --- frontend/src/App.tsx | 3 --- frontend/src/api/threads.ts | 16 +++++++++++++ frontend/src/components/Chat.tsx | 39 ++++++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 frontend/src/api/threads.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4e64b078..2dde8c01 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -153,9 +153,6 @@ function App() { stopStream={stopStream} stream={stream} setCurrentChatId={setCurrentChatId} - assistantId={currentChat?.assistant_id ?? null} - currentChat={currentChat} - config={currentConfig} configs={configs} /> } diff --git a/frontend/src/api/threads.ts b/frontend/src/api/threads.ts new file mode 100644 index 00000000..ee30ad49 --- /dev/null +++ b/frontend/src/api/threads.ts @@ -0,0 +1,16 @@ +import {Chat} from "../hooks/useChatList.ts"; + +export async function getThread( + threadId: string, +): Promise { + try { + const response = await fetch(`/threads/${threadId}`); + if (!response.ok) { + return null; + } + return (await response.json()) as Chat; + } catch (error) { + console.error("Failed to fetch assistant:", error); + return null; + } +} diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index d3deaad1..3a3d1499 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -1,13 +1,15 @@ import { useEffect, useRef } from "react"; import { StreamStateProps } from "../hooks/useStreamState"; import { useChatMessages } from "../hooks/useChatMessages"; -import { Chat as ChatInterface } from "../hooks/useChatList"; import TypingBox from "./TypingBox"; import { Message } from "./Message"; import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; import { Config } from "../hooks/useConfigList.ts"; +import {useQuery} from "react-query"; +import {getAssistant} from "../api/assistants.ts"; +import {getThread} from "../api/threads.ts"; interface ChatProps extends Pick { startStream: ( @@ -16,10 +18,7 @@ interface ChatProps extends Pick { assistant_id: string, ) => Promise; setCurrentChatId: (id: string | null) => void; - assistantId: string | null; configs: Config[] | null; - config: Config | null; - currentChat: ChatInterface | null; } function usePrevious(value: T): T | undefined { @@ -37,6 +36,28 @@ export function Chat(props: ChatProps) { props.stream, props.stopStream, ); + + const { + data: currentChat, + } = useQuery( + ["thread", chatId], + () => getThread(chatId as string), + { + enabled: !!chatId, + }, + ); + + const { + data: assistantConfig, + } = useQuery( + ["assistant", currentChat?.assistant_id], + () => getAssistant(currentChat?.assistant_id as string), + { + enabled: !!currentChat, + }, + ); + + useEffect(() => { props.setCurrentChatId(chatId ?? null); }, [chatId, props]); @@ -51,7 +72,7 @@ export function Chat(props: ChatProps) { }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [messages]); - if (!chatId || !props.assistantId) return
    ...
    ; + if (!currentChat || !assistantConfig) return
    ...
    ; return (
    @@ -79,7 +100,7 @@ export function Chat(props: ChatProps) { {resumeable && props.stream?.status !== "inflight" && (
    props.startStream(null, chatId, props.assistantId!)} + onClick={() => props.startStream(null, currentChat.thread_id, currentChat.assistant_id)} > Click to continue. @@ -87,14 +108,14 @@ export function Chat(props: ChatProps) { )}
    props.startStream(msg, chatId, props.assistantId!)} + onSubmit={(msg) => props.startStream(msg, currentChat.thread_id, currentChat.assistant_id)} onInterrupt={ props.stream?.status === "inflight" ? props.stopStream : undefined } inflight={props.stream?.status === "inflight"} configs={props.configs || []} - currentConfig={props.config} - currentChat={props.currentChat} + currentConfig={assistantConfig} + currentChat={currentChat} />
    From 5ce945842a3b28a272ba26bb46bb382d7da9de90 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 14:06:31 +0200 Subject: [PATCH 19/28] TypingBox: remove dependency to configs --- frontend/src/components/Chat.tsx | 3 --- frontend/src/components/NewChat.tsx | 1 - frontend/src/components/TypingBox.tsx | 13 +++---------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 3a3d1499..0a159a5a 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -6,7 +6,6 @@ import { Message } from "./Message"; import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; -import { Config } from "../hooks/useConfigList.ts"; import {useQuery} from "react-query"; import {getAssistant} from "../api/assistants.ts"; import {getThread} from "../api/threads.ts"; @@ -18,7 +17,6 @@ interface ChatProps extends Pick { assistant_id: string, ) => Promise; setCurrentChatId: (id: string | null) => void; - configs: Config[] | null; } function usePrevious(value: T): T | undefined { @@ -113,7 +111,6 @@ export function Chat(props: ChatProps) { props.stream?.status === "inflight" ? props.stopStream : undefined } inflight={props.stream?.status === "inflight"} - configs={props.configs || []} currentConfig={assistantConfig} currentChat={currentChat} /> diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 9e5d97e6..5b78676a 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -84,7 +84,6 @@ export function NewChat(props: NewChatProps) { } }} currentConfig={assistantConfig} - configs={props.configs || []} currentChat={null} />
    diff --git a/frontend/src/components/TypingBox.tsx b/frontend/src/components/TypingBox.tsx index 915eb987..c369c9d6 100644 --- a/frontend/src/components/TypingBox.tsx +++ b/frontend/src/components/TypingBox.tsx @@ -48,8 +48,7 @@ export default function TypingBox(props: { onSubmit: (data: MessageWithFiles) => void; onInterrupt?: () => void; inflight?: boolean; - configs: Config[]; - currentConfig: Config | null; + currentConfig: Config; currentChat: Chat | null; }) { const [inflight, setInflight] = useState(false); @@ -58,19 +57,13 @@ export default function TypingBox(props: { const [isDocumentRetrievalActive, setIsDocumentRetrievalActive] = useState(false); - const { currentConfig, currentChat, configs } = props; + const { currentConfig, currentChat } = props; useEffect(() => { let configurable = null; if (currentConfig) { configurable = currentConfig.config?.configurable; } - if (currentChat && configs) { - const conf = configs.find( - (c) => c.assistant_id === currentChat.assistant_id, - ); - configurable = conf?.config?.configurable; - } const agent_type = configurable?.["type"] as TYPE_NAME | null; if (agent_type === null || agent_type === "chatbot") { setIsDocumentRetrievalActive(false); @@ -83,7 +76,7 @@ export default function TypingBox(props: { const tools = (configurable?.["type==agent/tools"] as { name: string }[]) ?? []; setIsDocumentRetrievalActive(tools.some((t) => t.name === "Retrieval")); - }, [currentConfig, currentChat, configs]); + }, [currentConfig, currentChat]); const onDrop = useCallback((acceptedFiles: File[]) => { setFiles((prevFiles) => { From 54cb2658f3a2b78bb73d7b99a7263a1db6ed8927 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 15:59:03 +0200 Subject: [PATCH 20/28] useThreadAndAssistant --- frontend/src/App.tsx | 25 ++++-------- frontend/src/api/threads.ts | 6 +-- frontend/src/components/Chat.tsx | 45 +++++++++------------ frontend/src/components/NewChat.tsx | 20 +-------- frontend/src/components/NotFound.tsx | 3 ++ frontend/src/hooks/useThreadAndAssistant.ts | 37 +++++++++++++++++ 6 files changed, 71 insertions(+), 65 deletions(-) create mode 100644 frontend/src/components/NotFound.tsx create mode 100644 frontend/src/hooks/useThreadAndAssistant.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2dde8c01..e495846d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -14,23 +14,18 @@ import { import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; import { Route, Routes, useNavigate } from "react-router-dom"; - -function NotFound() { - return
    Page not found.
    ; -} +import { useThreadAndAssistant } from "./hooks/useThreadAndAssistant.ts"; +import { NotFound } from "./components/NotFound.tsx"; function App() { const navigate = useNavigate(); const [sidebarOpen, setSidebarOpen] = useState(false); - const { configSchema, configDefaults } = useSchemas(); const { chats, createChat } = useChatList(); const { configs, saveConfig } = useConfigList(); const { startStream, stopStream, stream } = useStreamState(); + const { configSchema, configDefaults } = useSchemas(); const [currentChatId, setCurrentChatId] = useState(null); - const [currentConfigId, setCurrentConfigId] = useState(null); - const currentConfig = - configs?.find((config) => config.assistant_id === currentConfigId) ?? null; const currentChat = chats?.find((chat) => chat.thread_id === currentChatId) ?? null; @@ -104,26 +99,23 @@ function App() { const selectConfig = useCallback( (id: string | null) => { setCurrentChatId(null); - setCurrentConfigId(id); navigate(id ? `/assistant/${id}` : "/"); }, [navigate], ); - const currentChatConfig = configs?.find( - (c) => c.assistant_id === currentChat?.assistant_id, - ); + const { assistantConfig } = useThreadAndAssistant(); return ( - {currentChatConfig.name} + {assistantConfig.name} { - selectConfig(currentChatConfig.assistant_id); + selectConfig(assistantConfig.assistant_id); }} /> @@ -139,7 +131,7 @@ function App() { }, [chats, configs])} currentChat={currentChat} enterChat={selectChat} - currentConfig={currentConfig} + currentConfig={assistantConfig || null} enterConfig={selectConfig} /> } @@ -153,7 +145,6 @@ function App() { stopStream={stopStream} stream={stream} setCurrentChatId={setCurrentChatId} - configs={configs} /> } /> diff --git a/frontend/src/api/threads.ts b/frontend/src/api/threads.ts index ee30ad49..4e4e29c3 100644 --- a/frontend/src/api/threads.ts +++ b/frontend/src/api/threads.ts @@ -1,8 +1,6 @@ -import {Chat} from "../hooks/useChatList.ts"; +import { Chat } from "../hooks/useChatList.ts"; -export async function getThread( - threadId: string, -): Promise { +export async function getThread(threadId: string): Promise { try { const response = await fetch(`/threads/${threadId}`); if (!response.ok) { diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 0a159a5a..bb607941 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -6,9 +6,7 @@ import { Message } from "./Message"; import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; -import {useQuery} from "react-query"; -import {getAssistant} from "../api/assistants.ts"; -import {getThread} from "../api/threads.ts"; +import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; interface ChatProps extends Pick { startStream: ( @@ -35,26 +33,7 @@ export function Chat(props: ChatProps) { props.stopStream, ); - const { - data: currentChat, - } = useQuery( - ["thread", chatId], - () => getThread(chatId as string), - { - enabled: !!chatId, - }, - ); - - const { - data: assistantConfig, - } = useQuery( - ["assistant", currentChat?.assistant_id], - () => getAssistant(currentChat?.assistant_id as string), - { - enabled: !!currentChat, - }, - ); - + const { currentChat, assistantConfig, isLoading } = useThreadAndAssistant(); useEffect(() => { props.setCurrentChatId(chatId ?? null); @@ -70,7 +49,9 @@ export function Chat(props: ChatProps) { }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [messages]); - if (!currentChat || !assistantConfig) return
    ...
    ; + + if (isLoading) return
    Loading...
    ; + if (!currentChat || !assistantConfig) return
    No data.
    ; return (
    @@ -98,7 +79,13 @@ export function Chat(props: ChatProps) { {resumeable && props.stream?.status !== "inflight" && (
    props.startStream(null, currentChat.thread_id, currentChat.assistant_id)} + onClick={() => + props.startStream( + null, + currentChat.thread_id, + currentChat.assistant_id, + ) + } > Click to continue. @@ -106,7 +93,13 @@ export function Chat(props: ChatProps) { )}
    props.startStream(msg, currentChat.thread_id, currentChat.assistant_id)} + onSubmit={(msg) => + props.startStream( + msg, + currentChat.thread_id, + currentChat.assistant_id, + ) + } onInterrupt={ props.stream?.status === "inflight" ? props.stopStream : undefined } diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 5b78676a..4d22e19a 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -9,8 +9,7 @@ import { import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useNavigate, useParams } from "react-router-dom"; -import { useQuery } from "react-query"; -import { getAssistant } from "../api/assistants.ts"; +import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; @@ -26,24 +25,9 @@ export function NewChat(props: NewChatProps) { const navigator = useNavigate(); const { assistantId } = useParams(); - const { - data: assistantConfig, - isLoading, - isError, - error, - } = useQuery( - ["assistant", assistantId], - () => getAssistant(assistantId as string), - { - enabled: !!assistantId, - }, - ); + const { assistantConfig, isLoading } = useThreadAndAssistant(); if (isLoading) return
    Loading...
    ; - if (isError) { - const message = (error as Error).message; - return
    Error: {message}
    ; - } if (!assistantConfig) return
    Could not find assistant with given id.
    ; diff --git a/frontend/src/components/NotFound.tsx b/frontend/src/components/NotFound.tsx new file mode 100644 index 00000000..6a4fe3b4 --- /dev/null +++ b/frontend/src/components/NotFound.tsx @@ -0,0 +1,3 @@ +export function NotFound() { + return
    Page not found.
    ; +} diff --git a/frontend/src/hooks/useThreadAndAssistant.ts b/frontend/src/hooks/useThreadAndAssistant.ts new file mode 100644 index 00000000..4b48fd9e --- /dev/null +++ b/frontend/src/hooks/useThreadAndAssistant.ts @@ -0,0 +1,37 @@ +import { useQuery } from "react-query"; +import { useParams } from "react-router-dom"; +import { getAssistant } from "../api/assistants"; +import { getThread } from "../api/threads"; + +export function useThreadAndAssistant() { + // Extract route parameters + const { chatId, assistantId } = useParams(); + + // React Query to fetch chat details if chatId is present + const { data: currentChat, isLoading: isLoadingChat } = useQuery( + ["thread", chatId], + () => getThread(chatId as string), + { + enabled: !!chatId, + }, + ); + + // Determine the assistantId to use: either from the chat or the route directly + const effectiveAssistantId = assistantId || currentChat?.assistant_id; + + // React Query to fetch assistant configuration based on the effectiveAssistantId + const { data: assistantConfig, isLoading: isLoadingAssistant } = useQuery( + ["assistant", effectiveAssistantId], + () => getAssistant(effectiveAssistantId as string), + { + enabled: !!effectiveAssistantId, + }, + ); + + // Return both loading states, the chat data, and the assistant configuration + return { + currentChat, + assistantConfig, + isLoading: isLoadingChat || isLoadingAssistant, + }; +} From c6596d63cd67909d4d18a54c15689e8d0d51bff9 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 16:16:13 +0200 Subject: [PATCH 21/28] working --- frontend/src/App.tsx | 6 ++++-- frontend/src/components/Chat.tsx | 10 ++++++++++ frontend/src/components/Config.tsx | 6 ++++++ frontend/src/components/NewChat.tsx | 8 ++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e495846d..199bf387 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -28,6 +28,7 @@ function App() { const [currentChatId, setCurrentChatId] = useState(null); const currentChat = chats?.find((chat) => chat.thread_id === currentChatId) ?? null; + const [assistantConfig, setAssistantConfig] = useState(null); const startTurn = useCallback( async ( @@ -104,8 +105,6 @@ function App() { [navigate], ); - const { assistantConfig } = useThreadAndAssistant(); - return ( } /> @@ -158,6 +158,7 @@ function App() { configs={configs} saveConfig={saveConfig} enterConfig={selectConfig} + setCurrentConfig={setAssistantConfig} /> } /> @@ -171,6 +172,7 @@ function App() { configDefaults={configDefaults} saveConfig={saveConfig} enterConfig={selectConfig} + setCurrentConfig={setAssistantConfig} /> } /> diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index bb607941..0e0257cb 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -7,6 +7,7 @@ import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; +import {Config} from "../hooks/useConfigList.ts"; interface ChatProps extends Pick { startStream: ( @@ -15,6 +16,7 @@ interface ChatProps extends Pick { assistant_id: string, ) => Promise; setCurrentChatId: (id: string | null) => void; + setCurrentConfig: (config: Config | null) => void; } function usePrevious(value: T): T | undefined { @@ -38,6 +40,14 @@ export function Chat(props: ChatProps) { useEffect(() => { props.setCurrentChatId(chatId ?? null); }, [chatId, props]); + + useEffect(() => { + if (!isLoading) { + props.setCurrentConfig(assistantConfig || null); + } + }, [assistantConfig, isLoading, props]); + + const prevMessages = usePrevious(messages); useEffect(() => { scrollTo({ diff --git a/frontend/src/components/Config.tsx b/frontend/src/components/Config.tsx index a94af64b..df58802a 100644 --- a/frontend/src/components/Config.tsx +++ b/frontend/src/components/Config.tsx @@ -484,6 +484,7 @@ export function Config(props: { config: ConfigInterface | null; saveConfig: ConfigListProps["saveConfig"]; enterConfig: (id: string | null) => void; + setCurrentConfig: (config: ConfigInterface | null) => void; }) { const [values, setValues] = useState( props.config?.config ?? props.configDefaults, @@ -498,6 +499,11 @@ export function Config(props: { const dropzone = useDropzone(DROPZONE_CONFIG); const [isPublic, setPublic] = useState(props.config?.public ?? false); + useEffect(() => { + if (props.config === null) + props.setCurrentConfig(null); + }, [props]); + useEffect(() => { if (!values) return; if (!values.configurable) return; diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 4d22e19a..47fd5a91 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -10,6 +10,7 @@ import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useNavigate, useParams } from "react-router-dom"; import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; +import {useEffect} from "react"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; @@ -19,6 +20,7 @@ interface NewChatProps extends ConfigListProps { config: ConfigInterface, message: MessageWithFiles, ) => Promise; + setCurrentConfig: (config: ConfigInterface | null) => void; } export function NewChat(props: NewChatProps) { @@ -27,6 +29,12 @@ export function NewChat(props: NewChatProps) { const { assistantConfig, isLoading } = useThreadAndAssistant(); + useEffect(() => { + if (!isLoading) { + props.setCurrentConfig(assistantConfig || null); + } + }, [assistantConfig, isLoading, props]); + if (isLoading) return
    Loading...
    ; if (!assistantConfig) return
    Could not find assistant with given id.
    ; From c484e434f6ecbb287e57f6028ebab5724788766d Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 16:20:40 +0200 Subject: [PATCH 22/28] fix lint --- frontend/src/App.tsx | 3 ++- frontend/src/components/Chat.tsx | 3 +-- frontend/src/components/Config.tsx | 3 +-- frontend/src/components/NewChat.tsx | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 199bf387..4a8784cd 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -28,7 +28,8 @@ function App() { const [currentChatId, setCurrentChatId] = useState(null); const currentChat = chats?.find((chat) => chat.thread_id === currentChatId) ?? null; - const [assistantConfig, setAssistantConfig] = useState(null); + const [assistantConfig, setAssistantConfig] = + useState(null); const startTurn = useCallback( async ( diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 0e0257cb..89c08d88 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -7,7 +7,7 @@ import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; -import {Config} from "../hooks/useConfigList.ts"; +import { Config } from "../hooks/useConfigList.ts"; interface ChatProps extends Pick { startStream: ( @@ -47,7 +47,6 @@ export function Chat(props: ChatProps) { } }, [assistantConfig, isLoading, props]); - const prevMessages = usePrevious(messages); useEffect(() => { scrollTo({ diff --git a/frontend/src/components/Config.tsx b/frontend/src/components/Config.tsx index df58802a..899b7897 100644 --- a/frontend/src/components/Config.tsx +++ b/frontend/src/components/Config.tsx @@ -500,8 +500,7 @@ export function Config(props: { const [isPublic, setPublic] = useState(props.config?.public ?? false); useEffect(() => { - if (props.config === null) - props.setCurrentConfig(null); + if (props.config === null) props.setCurrentConfig(null); }, [props]); useEffect(() => { diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 47fd5a91..e3076a0b 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -10,7 +10,7 @@ import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useNavigate, useParams } from "react-router-dom"; import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; -import {useEffect} from "react"; +import { useEffect } from "react"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; From 8d2cc647723068f881297dc6aeb53d20c8c0f161 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 16:43:38 +0200 Subject: [PATCH 23/28] fix type --- frontend/src/components/NewChat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index e3076a0b..0ca28aa2 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -64,6 +64,7 @@ export function NewChat(props: NewChatProps) { configDefaults={props.configDefaults} saveConfig={props.saveConfig} enterConfig={props.enterConfig} + setCurrentConfig={props.setCurrentConfig} />
    From 17746270d9bf2ec69c0cfdeeccff31964fd167d0 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Thu, 21 Mar 2024 16:45:42 +0200 Subject: [PATCH 24/28] rmeove unused import --- frontend/src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4a8784cd..53070a9f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -14,7 +14,6 @@ import { import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; import { Route, Routes, useNavigate } from "react-router-dom"; -import { useThreadAndAssistant } from "./hooks/useThreadAndAssistant.ts"; import { NotFound } from "./components/NotFound.tsx"; function App() { From 241b93c63b819d5c74b39da76c46f090012478cd Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Tue, 2 Apr 2024 13:04:26 +0300 Subject: [PATCH 25/28] Routes to the top --- frontend/src/App.tsx | 84 ++++++++++------------------- frontend/src/components/Chat.tsx | 13 ----- frontend/src/components/Config.tsx | 5 -- frontend/src/components/NewChat.tsx | 9 ---- frontend/src/main.tsx | 19 +++---- 5 files changed, 39 insertions(+), 91 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 53070a9f..017d8dd8 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useState } from "react"; import { InformationCircleIcon } from "@heroicons/react/24/outline"; import { Chat } from "./components/Chat"; import { ChatList } from "./components/ChatList"; @@ -13,8 +13,8 @@ import { } from "./hooks/useConfigList"; import { Config } from "./components/Config"; import { MessageWithFiles } from "./utils/formTypes.ts"; -import { Route, Routes, useNavigate } from "react-router-dom"; -import { NotFound } from "./components/NotFound.tsx"; +import { useNavigate } from "react-router-dom"; +import { useThreadAndAssistant } from "./hooks/useThreadAndAssistant.ts"; function App() { const navigate = useNavigate(); @@ -24,11 +24,7 @@ function App() { const { startStream, stopStream, stream } = useStreamState(); const { configSchema, configDefaults } = useSchemas(); - const [currentChatId, setCurrentChatId] = useState(null); - const currentChat = - chats?.find((chat) => chat.thread_id === currentChatId) ?? null; - const [assistantConfig, setAssistantConfig] = - useState(null); + const { currentChat, assistantConfig, isLoading } = useThreadAndAssistant(); const startTurn = useCallback( async ( @@ -99,12 +95,13 @@ function App() { const selectConfig = useCallback( (id: string | null) => { - setCurrentChatId(null); navigate(id ? `/assistant/${id}` : "/"); }, [navigate], ); + if (isLoading) return
    Loading...
    ; + return ( { - if (configs === null || chats === null) return null; - return chats; - }, [chats, configs])} - currentChat={currentChat} + chats={chats} + currentChat={currentChat || null} enterChat={selectChat} currentConfig={assistantConfig || null} enterConfig={selectConfig} /> } > - - - } - /> - - } + {currentChat && assistantConfig && ( + + )} + {!currentChat && assistantConfig && ( + - - } + )} + {!currentChat && !assistantConfig && ( + - } /> - + )} ); } diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index 89c08d88..d2dbecc1 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -7,7 +7,6 @@ import { ArrowDownCircleIcon } from "@heroicons/react/24/outline"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useParams } from "react-router-dom"; import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; -import { Config } from "../hooks/useConfigList.ts"; interface ChatProps extends Pick { startStream: ( @@ -15,8 +14,6 @@ interface ChatProps extends Pick { thread_id: string, assistant_id: string, ) => Promise; - setCurrentChatId: (id: string | null) => void; - setCurrentConfig: (config: Config | null) => void; } function usePrevious(value: T): T | undefined { @@ -37,16 +34,6 @@ export function Chat(props: ChatProps) { const { currentChat, assistantConfig, isLoading } = useThreadAndAssistant(); - useEffect(() => { - props.setCurrentChatId(chatId ?? null); - }, [chatId, props]); - - useEffect(() => { - if (!isLoading) { - props.setCurrentConfig(assistantConfig || null); - } - }, [assistantConfig, isLoading, props]); - const prevMessages = usePrevious(messages); useEffect(() => { scrollTo({ diff --git a/frontend/src/components/Config.tsx b/frontend/src/components/Config.tsx index 899b7897..a94af64b 100644 --- a/frontend/src/components/Config.tsx +++ b/frontend/src/components/Config.tsx @@ -484,7 +484,6 @@ export function Config(props: { config: ConfigInterface | null; saveConfig: ConfigListProps["saveConfig"]; enterConfig: (id: string | null) => void; - setCurrentConfig: (config: ConfigInterface | null) => void; }) { const [values, setValues] = useState( props.config?.config ?? props.configDefaults, @@ -499,10 +498,6 @@ export function Config(props: { const dropzone = useDropzone(DROPZONE_CONFIG); const [isPublic, setPublic] = useState(props.config?.public ?? false); - useEffect(() => { - if (props.config === null) props.setCurrentConfig(null); - }, [props]); - useEffect(() => { if (!values) return; if (!values.configurable) return; diff --git a/frontend/src/components/NewChat.tsx b/frontend/src/components/NewChat.tsx index 0ca28aa2..4d22e19a 100644 --- a/frontend/src/components/NewChat.tsx +++ b/frontend/src/components/NewChat.tsx @@ -10,7 +10,6 @@ import { cn } from "../utils/cn"; import { MessageWithFiles } from "../utils/formTypes.ts"; import { useNavigate, useParams } from "react-router-dom"; import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; -import { useEffect } from "react"; interface NewChatProps extends ConfigListProps { configSchema: Schemas["configSchema"]; @@ -20,7 +19,6 @@ interface NewChatProps extends ConfigListProps { config: ConfigInterface, message: MessageWithFiles, ) => Promise; - setCurrentConfig: (config: ConfigInterface | null) => void; } export function NewChat(props: NewChatProps) { @@ -29,12 +27,6 @@ export function NewChat(props: NewChatProps) { const { assistantConfig, isLoading } = useThreadAndAssistant(); - useEffect(() => { - if (!isLoading) { - props.setCurrentConfig(assistantConfig || null); - } - }, [assistantConfig, isLoading, props]); - if (isLoading) return
    Loading...
    ; if (!assistantConfig) return
    Could not find assistant with given id.
    ; @@ -64,7 +56,6 @@ export function NewChat(props: NewChatProps) { configDefaults={props.configDefaults} saveConfig={props.saveConfig} enterConfig={props.enterConfig} - setCurrentConfig={props.setCurrentConfig} />
    diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 11465432..af504278 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,9 +2,10 @@ import ReactDOM from "react-dom/client"; import { v4 as uuidv4 } from "uuid"; import App from "./App.tsx"; import "./index.css"; -import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; import { StrictMode } from "react"; import { QueryClient, QueryClientProvider } from "react-query"; +import { NotFound } from "./components/NotFound.tsx"; if (document.cookie.indexOf("user_id") === -1) { document.cookie = `opengpts_user_id=${uuidv4()}; path=/; SameSite=Lax`; @@ -12,17 +13,17 @@ if (document.cookie.indexOf("user_id") === -1) { const queryClient = new QueryClient(); -const router = createBrowserRouter([ - { - path: "*", - element: , - }, -]); - ReactDOM.createRoot(document.getElementById("root")!).render( - + + + } /> + } /> + } /> + } /> + + , ); From 5f881b6059bf149ef42aefda2fa6318a6691bc36 Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Tue, 2 Apr 2024 14:11:59 +0300 Subject: [PATCH 26/28] focus items on lists --- frontend/src/App.tsx | 2 -- frontend/src/components/ChatList.tsx | 21 +++++++++++---------- frontend/src/components/ConfigList.tsx | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 017d8dd8..40ab42ad 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -122,9 +122,7 @@ function App() { sidebar={ } diff --git a/frontend/src/components/ChatList.tsx b/frontend/src/components/ChatList.tsx index 314a3d2e..f67aae18 100644 --- a/frontend/src/components/ChatList.tsx +++ b/frontend/src/components/ChatList.tsx @@ -1,22 +1,23 @@ import { PlusIcon } from "@heroicons/react/24/outline"; -import { Chat, ChatListProps } from "../hooks/useChatList"; -import { Config } from "../hooks/useConfigList"; +import { ChatListProps } from "../hooks/useChatList"; import { cn } from "../utils/cn"; +import {useThreadAndAssistant} from "../hooks/useThreadAndAssistant.ts"; export function ChatList(props: { chats: ChatListProps["chats"]; - currentChat: Chat | null; enterChat: (id: string | null) => void; - currentConfig: Config | null; enterConfig: (id: string | null) => void; }) { + + const { currentChat, assistantConfig } = useThreadAndAssistant(); + return ( <>
    props.enterChat(null)} className={cn( - props.currentChat === null && props.currentConfig !== null + !currentChat && assistantConfig ? "bg-gray-50 text-indigo-600" : "text-gray-700 hover:text-indigo-600 hover:bg-gray-50", "group flex gap-x-3 rounded-md -mx-2 p-2 leading-6 font-semibold cursor-pointer", @@ -24,7 +25,7 @@ export function ChatList(props: { > props.enterConfig(null)} className={cn( - props.currentConfig === null + !assistantConfig ? "bg-gray-50 text-indigo-600" : "text-gray-700 hover:text-indigo-600 hover:bg-gray-50", "mt-1 group flex gap-x-3 rounded-md -mx-2 p-2 leading-6 font-semibold cursor-pointer", @@ -46,7 +47,7 @@ export function ChatList(props: { > props.enterChat(chat.thread_id)} className={cn( - chat === props.currentChat + chat.thread_id === currentChat?.thread_id ? "bg-gray-50 text-indigo-600" : "text-gray-700 hover:text-indigo-600 hover:bg-gray-50", "group flex gap-x-3 rounded-md p-2 leading-6 cursor-pointer", @@ -74,7 +75,7 @@ export function ChatList(props: { > props.enterConfig(props.config.assistant_id)} className={cn( - props.config === props.currentConfig + props.config.assistant_id === props.currentConfig?.assistant_id ? "bg-gray-50 text-indigo-600" : "text-gray-700 hover:text-indigo-600 hover:bg-gray-50", "group flex gap-x-3 rounded-md p-2 leading-6 cursor-pointer", @@ -20,7 +20,7 @@ function ConfigItem(props: { > Date: Tue, 2 Apr 2024 14:19:09 +0300 Subject: [PATCH 27/28] tune loading a bit --- frontend/src/App.tsx | 5 ++--- frontend/src/components/ChatList.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 40ab42ad..33ce1eb4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -100,8 +100,6 @@ function App() { [navigate], ); - if (isLoading) return
    Loading...
    ; - return ( )} - {!currentChat && !assistantConfig && ( + {!currentChat && !assistantConfig && !isLoading && ( )} + {isLoading &&
    Loading...
    }
    ); } diff --git a/frontend/src/components/ChatList.tsx b/frontend/src/components/ChatList.tsx index f67aae18..16118c23 100644 --- a/frontend/src/components/ChatList.tsx +++ b/frontend/src/components/ChatList.tsx @@ -2,15 +2,14 @@ import { PlusIcon } from "@heroicons/react/24/outline"; import { ChatListProps } from "../hooks/useChatList"; import { cn } from "../utils/cn"; -import {useThreadAndAssistant} from "../hooks/useThreadAndAssistant.ts"; +import { useThreadAndAssistant } from "../hooks/useThreadAndAssistant.ts"; export function ChatList(props: { chats: ChatListProps["chats"]; enterChat: (id: string | null) => void; enterConfig: (id: string | null) => void; }) { - - const { currentChat, assistantConfig } = useThreadAndAssistant(); + const { currentChat, assistantConfig } = useThreadAndAssistant(); return ( <> From 78ec2a231ddcc1cbf6ce97571823c038676f724c Mon Sep 17 00:00:00 2001 From: Mikko Korpela Date: Tue, 2 Apr 2024 14:32:51 +0300 Subject: [PATCH 28/28] more unique key for message --- frontend/src/components/Chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index d2dbecc1..898d23c9 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -54,7 +54,7 @@ export function Chat(props: ChatProps) { {messages?.map((msg, i) => (