diff --git a/src/api/create-chat-session.ts b/src/api/create-chat-session.ts index 89b5233..3ce5ae1 100644 --- a/src/api/create-chat-session.ts +++ b/src/api/create-chat-session.ts @@ -17,11 +17,9 @@ export const createChatSession = async () => { interface CreateChatSessionResponse { data: { - chat_session: { session_id: string; created_at: string; session_title: string; - }; }; status: "success" | "error"; } diff --git a/src/api/create-chat-stream.ts b/src/api/create-chat-stream.ts index e1aaf54..577310e 100644 --- a/src/api/create-chat-stream.ts +++ b/src/api/create-chat-stream.ts @@ -29,6 +29,6 @@ export const createChatStream = async (props: ChatProps) => { interface ChatStreamResponse { success: "success" | "error", data: { - stream_id: string + chat_message_id: string } } diff --git a/src/api/create-session.ts b/src/api/create-session.ts deleted file mode 100644 index 92b1a10..0000000 --- a/src/api/create-session.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BASE_URL } from "@/config"; -import ky from "ky"; - -export const createSession = async () => { - try { - const response = await ky.post(`${BASE_URL}/v1/storymap/create-chat-session`); - const data = await response.json(); - return data; - } catch (error) { - console.error(error); - throw error; - } -}; - -interface CreateSessionResponse { - data: { - session_id: string; - created_at: string; - session_title: string; - }; - status: string; -} - diff --git a/src/api/create-storymap.ts b/src/api/create-storymap.ts deleted file mode 100644 index 367554c..0000000 --- a/src/api/create-storymap.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { BASE_URL } from "@/config" -import ky from "ky" - - - -export const createStorymap = async (sessionId: string) => { - try { - const response = await ky.post(`${BASE_URL}/v1/storymap/create-story-map`, { - json: { - session_id: sessionId, - // usecase: "storymap", - // user_query: "Create a storymap" - }, - timeout: 30000 - }) - const data = await response.json() - return data - } catch (error) { - console.error(error) - throw error - } -} - - -interface CreateStorymapResponse { - status: string; - data: { - session: { - session_id: string; - session_title: string; - created_at: string; - }; - story_map: { - id: number; - item_id: string | null; - status: string; - user_query: string; - usecase: string; - events: { - event: { - text: string; - success: boolean; - }; - status: string; - }[]; - item_data: Record; - created_at: string; - session: string; - }; - }; -} diff --git a/src/api/storymap-status-poll.ts b/src/api/storymap-status-poll.ts deleted file mode 100644 index bed57c7..0000000 --- a/src/api/storymap-status-poll.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BASE_URL } from "@/config"; -import ky from "ky"; - - - -export const storymapStatusPoll = async (storyId: string) => { - - try { - const response = await ky.get(`${BASE_URL}/v1/storymap/poll-story-map/${storyId}`, { - timeout: 30000 - }); - const data = await response.json(); - return data; - } catch (error) { - console.error(error); - throw error; - } -}; - - -interface StorymapStatusPollResponse { - status: string; - data: { - story_map: { - id: number; - item_id: string | null; - status: string; - user_query: string; - usecase: string; - events: { - event: { - text: string; - success: boolean; - }; - status: string; - }[]; - item_data: { - url: string; - }; - created_at: string; - session: string; - }; - }; -} diff --git a/src/config.ts b/src/config.ts index 0af0ab6..a54a5bf 100644 --- a/src/config.ts +++ b/src/config.ts @@ -16,4 +16,4 @@ export const LOCAL_API_URL = "http://localhost:8000" // } // } -export const BASE_URL = API_URL \ No newline at end of file +export const BASE_URL = LOCAL_API_URL \ No newline at end of file diff --git a/src/features/chat.tsx b/src/features/chat.tsx index 78f4ce7..569fa98 100644 --- a/src/features/chat.tsx +++ b/src/features/chat.tsx @@ -4,19 +4,23 @@ import { LocationPin } from "@/assets/location-pin-svg"; import { Button } from "@/components/ui/button"; import { JobStatusIndicator } from "@/components/ui/job-status-indicator"; import { Textarea } from "@/components/ui/textarea"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { useUserStore } from "@/store/user-store"; import type { JobStatusEventType } from "@/types/events.types"; import type { StorymapTemplate } from "@/types/storymap.types"; import { useMutation } from "@tanstack/react-query"; -import { ArrowRight, Check, Copy } from 'lucide-react'; -import { useCallback, useEffect, useRef, useState } from 'react'; -import ReactMarkdown from 'react-markdown'; +import { ArrowRight, Check, Copy } from "lucide-react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import solarizedDarkAtom from "react-syntax-highlighter/dist/esm/styles/prism/solarized-dark-atom"; import rehypeRaw from "rehype-raw"; -import remarkGfm from 'remark-gfm'; -import { v4 as uuidv4 } from 'uuid'; - +import remarkGfm from "remark-gfm"; +import { v4 as uuidv4 } from "uuid"; const queries = [ "Build me an interactive presentation for 1 S Main St, Bel Air, MD 21014 for use as a Restaurant", @@ -25,59 +29,68 @@ const queries = [ ]; export const Chat = () => { - const [messages, setMessages] = useState>([]); - const [inputValue, setInputValue] = useState(''); + const [messages, setMessages] = useState< + Array<{ type: "user" | "assistant"; content: string }> + >([]); + const [inputValue, setInputValue] = useState(""); const [autoScroll, setAutoScroll] = useState(true); const [isUserScrolling, setIsUserScrolling] = useState(false); - const { sessionId, setStreamId, streamId, setStorymapContent } = useUserStore(); + const { sessionId, setStreamId, streamId, setStorymapContent, setIsProcessingMessage, isProcessingMessage } = + useUserStore(); const { mutate: chatMutation } = useMutation({ - mutationFn: async (variables: { prompt: string } ) => { + mutationFn: async (variables: { prompt: string }) => { const { prompt } = variables; return await createChatStream({ sessionId: sessionId!, prompt }); }, onSuccess: (result) => { console.log("Result:", result); - const streamId = result.data.stream_id - console.log("Stream ID:", streamId); - setStreamId(streamId) + const chatMessageId = result.data.chat_message_id; + console.log("Chat message ID:", chatMessageId); + setStreamId(chatMessageId); }, onError: (error) => { console.error("Chat mutation error:", error); - setMessages(prev => [...prev.slice(0, -1), { type: 'assistant', content: 'Sorry, I had trouble connecting. Please try again.' }]); + setMessages((prev) => [ + ...prev.slice(0, -1), + { + type: "assistant", + content: "Sorry, I had trouble connecting. Please try again.", + }, + ]); }, }); + const handleSendMessage = (content: string) => { + console.log("Is processing initial state: ", isProcessingMessage); + console.log("Is processing message: ", isProcessingMessage); - const handleSendMessage = (content: string) => { - // ALWAYS reset auto-scroll when user sends a new message setAutoScroll(true); setIsUserScrolling(false); - - setMessages(prev => [ + setIsProcessingMessage(true); + setMessages((prev) => [ ...prev, - { type: 'user', content }, - { type: 'assistant', content: '' } + { type: "user", content }, + { type: "assistant", content: "" }, ]); - + chatMutation({ prompt: content, }); - + if (content === inputValue) { - setInputValue(''); + setInputValue(""); } }; - useEffect(() => { if (streamId) { - createStreamManager({ - sessionId: sessionId!, - streamId: streamId!, + createStreamManager({ + sessionId: sessionId!, + streamId: streamId!, onStreamStart() { - setStreamId(streamId!) + setStreamId(streamId!); const setJobStatus = useUserStore.getState().setJobStatus; setJobStatus([]); }, @@ -86,21 +99,22 @@ export const Chat = () => { setStreamId(null); const setJobStatus = useUserStore.getState().setJobStatus; setJobStatus([]); + setIsProcessingMessage(false); }, onStreaming: (chunk) => { console.log("Chunk:", chunk); - setMessages(prev => { + setMessages((prev) => { const newMessages = [...prev]; const lastMessage = newMessages[newMessages.length - 1]; - + // If the last message is from assistant, append the chunk - if (lastMessage && lastMessage.type === 'assistant') { + if (lastMessage && lastMessage.type === "assistant") { lastMessage.content += chunk; } else { // If no assistant message exists, create one - newMessages.push({ type: 'assistant', content: chunk }); + newMessages.push({ type: "assistant", content: chunk }); } - + return newMessages; }); }, @@ -113,35 +127,35 @@ export const Chat = () => { // firstly we need to stop the already in progress job status and then keep the current one in progress. const jobStatus = useUserStore.getState().jobStatus; const setJobStatus = useUserStore.getState().setJobStatus; - const newJobStatus = jobStatus.map(job => ({ + const newJobStatus = jobStatus.map((job) => ({ ...job, - isInProgress: false + isInProgress: false, })); const currentJobStatus = { id: uuidv4(), message: status.message, isInProgress: true, - streamId: streamId! - } + streamId: streamId!, + }; setJobStatus([...newJobStatus, currentJobStatus]); console.log("New job status: ", newJobStatus); - } + }, }); } - }, [sessionId, streamId, setStreamId, setStorymapContent]); + }, [sessionId, streamId, setStreamId, setStorymapContent, setIsProcessingMessage]); return (
- - + { const ChatHeader = () => { // const { selectedTemplate } = useUserStore(); - + return (
- Interactive Presentation Agent -
-
+ + PlaceStory Agent +
+
); }; -const QueryPrompts = ({ onSendMessage }: { onSendMessage: (content: string) => void }) => { +const QueryPrompts = ({ + onSendMessage, +}: { + onSendMessage: (content: string) => void; +}) => { return (
@@ -188,20 +207,20 @@ const QueryPrompts = ({ onSendMessage }: { onSendMessage: (content: string) => v ); }; -const ChatMessages = ({ - messages, - onSendMessage, - autoScroll, - setAutoScroll, - isUserScrolling, +const ChatMessages = ({ + messages, + onSendMessage, + autoScroll, + setAutoScroll, + isUserScrolling, setIsUserScrolling, -}: { - messages: Array<{type: 'user' | 'assistant', content: string}>, - onSendMessage: (content: string) => void, - autoScroll: boolean, - setAutoScroll: (value: boolean) => void, - isUserScrolling: boolean, - setIsUserScrolling: (value: boolean) => void, +}: { + messages: Array<{ type: "user" | "assistant"; content: string }>; + onSendMessage: (content: string) => void; + autoScroll: boolean; + setAutoScroll: (value: boolean) => void; + isUserScrolling: boolean; + setIsUserScrolling: (value: boolean) => void; }) => { const messagesEndRef = useRef(null); const scrollContainerRef = useRef(null); @@ -220,16 +239,17 @@ const ChatMessages = ({ // Check if user is near the bottom (within 100px) const isNearBottom = () => { if (!scrollContainerRef.current) return true; - const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current; + const { scrollTop, scrollHeight, clientHeight } = + scrollContainerRef.current; return scrollHeight - scrollTop - clientHeight < 100; }; // Handle manual scroll - detect user intervention const handleScroll = () => { if (!scrollContainerRef.current) return; - + const isAtBottom = isNearBottom(); - + // If user scrolled away from bottom, disable auto-scroll if (!isAtBottom && !isUserScrolling) { setAutoScroll(false); @@ -256,34 +276,43 @@ const ChatMessages = ({ }, [messages, scrollToBottom]); return ( -
- {messages.length === 0 && ( - - )} - + {messages.length === 0 && } + {messages.length > 0 && (
{messages.map((message, index) => (
-
-
- +
+
{message.type === "assistant" && (
- IP Agent + + IP Agent +
)} - +

{children}

); }, h2({ children, ...props }) { return ( -

+

{children}

); }, h3({ children, ...props }) { return ( -

+

{children}

); }, ul({ children, ...props }) { return ( -
    +
      {children}
    ); }, ol({ children, ...props }) { return ( -
      +
        {children}
      ); @@ -438,11 +486,11 @@ const ChatMessages = ({ {message.type === "assistant" && !streamId && (
      - -