diff --git a/api/__pycache__/index.cpython-311.pyc b/api/__pycache__/index.cpython-311.pyc index 6319d2b..3f0a288 100644 Binary files a/api/__pycache__/index.cpython-311.pyc and b/api/__pycache__/index.cpython-311.pyc differ diff --git a/api/routes/__pycache__/auth_routes.cpython-311.pyc b/api/routes/__pycache__/auth_routes.cpython-311.pyc index a725a09..d92823a 100644 Binary files a/api/routes/__pycache__/auth_routes.cpython-311.pyc and b/api/routes/__pycache__/auth_routes.cpython-311.pyc differ diff --git a/api/routes/__pycache__/conversation_routes.cpython-311.pyc b/api/routes/__pycache__/conversation_routes.cpython-311.pyc index 1819319..94c2905 100644 Binary files a/api/routes/__pycache__/conversation_routes.cpython-311.pyc and b/api/routes/__pycache__/conversation_routes.cpython-311.pyc differ diff --git a/api/routes/conversation_routes.py b/api/routes/conversation_routes.py index 1486be2..95783c0 100644 --- a/api/routes/conversation_routes.py +++ b/api/routes/conversation_routes.py @@ -1,9 +1,9 @@ +from typing import List from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import StreamingResponse from pydantic import BaseModel from api.models.conversation import Message -from api.utils.conversation_utils import create_conversation, add_message, get_conversation_by_id, rename_conversation, get_conversations_by_user, update_conversation_model -from api.utils.llm_providers.openai import openai_generate_response +from api.utils.conversation_utils import update_conversation_messages, create_conversation, add_message, get_conversation_by_id, get_conversations_by_user, update_conversation_model from api.utils.llm_utils import generate_response_stream from api.utils.auth_utils import get_current_user from api.models.user import User @@ -40,6 +40,18 @@ async def add_message_route(conversation_id: str, message_create: MessageCreate, else: raise HTTPException(status_code=404, detail="Conversation not found") +@router.put("/conversations/{conversation_id}/messages") +async def update_messages_route( + conversation_id: str, + updated_messages: List[Message], + current_user: User = Depends(get_current_user), +): + success = await update_conversation_messages(conversation_id, updated_messages, current_user.email) + if success: + return {"message": "Messages updated successfully"} + else: + raise HTTPException(status_code=404, detail="Conversation not found") + @router.patch("/conversations/{conversation_id}") async def update_conversation_route( conversation_id: str, diff --git a/api/utils/__pycache__/auth_utils.cpython-311.pyc b/api/utils/__pycache__/auth_utils.cpython-311.pyc index 7a85155..1896285 100644 Binary files a/api/utils/__pycache__/auth_utils.cpython-311.pyc and b/api/utils/__pycache__/auth_utils.cpython-311.pyc differ diff --git a/api/utils/__pycache__/conversation_utils.cpython-311.pyc b/api/utils/__pycache__/conversation_utils.cpython-311.pyc index ac87f03..8b4eaa8 100644 Binary files a/api/utils/__pycache__/conversation_utils.cpython-311.pyc and b/api/utils/__pycache__/conversation_utils.cpython-311.pyc differ diff --git a/api/utils/__pycache__/db_utils.cpython-311.pyc b/api/utils/__pycache__/db_utils.cpython-311.pyc index 50f6e3f..d5aa821 100644 Binary files a/api/utils/__pycache__/db_utils.cpython-311.pyc and b/api/utils/__pycache__/db_utils.cpython-311.pyc differ diff --git a/api/utils/__pycache__/llm_utils.cpython-311.pyc b/api/utils/__pycache__/llm_utils.cpython-311.pyc index 7fe6921..b936cb2 100644 Binary files a/api/utils/__pycache__/llm_utils.cpython-311.pyc and b/api/utils/__pycache__/llm_utils.cpython-311.pyc differ diff --git a/api/utils/conversation_utils.py b/api/utils/conversation_utils.py index 0a8bca2..6e8f06d 100644 --- a/api/utils/conversation_utils.py +++ b/api/utils/conversation_utils.py @@ -1,4 +1,6 @@ from datetime import datetime, timezone +from typing import List + from api.utils.db_utils import get_db from api.models.conversation import Message, Conversation, LanguageModel from bson import ObjectId @@ -74,4 +76,17 @@ async def update_conversation_model(conversation_id: str, model_provider: str, m {"$set": update_fields} ) return result.modified_count > 0 - return False \ No newline at end of file + return False + +async def update_conversation_messages(conversation_id: str, updated_messages: List[Message], user_email: str): + db = await get_db() + conversations_collection = db["conversations"] + conversation = await get_conversation_by_id(conversation_id, user_email) + if conversation: + await conversations_collection.update_one( + {"_id": ObjectId(conversation_id)}, + {"$set": {"messages": [message.dict() for message in updated_messages]}}, + ) + return True + else: + return False \ No newline at end of file diff --git a/api/utils/llm_utils.py b/api/utils/llm_utils.py index 4cf9a05..4c7bc2a 100644 --- a/api/utils/llm_utils.py +++ b/api/utils/llm_utils.py @@ -5,6 +5,8 @@ from api.utils.llm_providers.anthropic import anthropic_generate_response, generate_conversation_name async def generate_response_stream(conversation): + visible_messages = [message for message in conversation.messages if not message.hidden] + conversation.messages = visible_messages collected_chunks = [] if conversation.model.provider == "openai": async for chunk in openai_generate_response(conversation): diff --git a/app/components/ConversationMessages.tsx b/app/components/ConversationMessages.tsx index c1d82ae..7b2d4c5 100644 --- a/app/components/ConversationMessages.tsx +++ b/app/components/ConversationMessages.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from "react"; -import { Box, Text, VStack } from "@chakra-ui/react"; +import { Box, Text, VStack, Button } from "@chakra-ui/react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; @@ -12,10 +12,12 @@ import { Components } from "react-markdown"; export interface Message { role: "user" | "assistant"; content: string; + hidden?: boolean; } interface ConversationMessagesProps { messages: Message[]; + handleHideMessage: (index: number) => Promise; userName?: string; } @@ -27,6 +29,7 @@ type MathComponents = { const ConversationMessages: React.FC = ({ messages, + handleHideMessage, userName = "User", }) => { const messagesEndRef = useRef(null); @@ -43,11 +46,15 @@ const ConversationMessages: React.FC = ({ borderRadius="none" borderWidth="1px" p={4} - borderColor="black" + borderColor={message.hidden ? "gray.300" : "black"} alignSelf={message.role === "user" ? "flex-end" : "flex-start"} maxWidth="100%" > - + [{index}] {message.role === "user" ? userName : "Assistant"} = ({ > {message.content} + ))}
diff --git a/app/components/Sidebar.tsx b/app/components/Sidebar.tsx index ff5ca44..3ab0f77 100644 --- a/app/components/Sidebar.tsx +++ b/app/components/Sidebar.tsx @@ -9,8 +9,9 @@ import { DrawerOverlay, useDisclosure, IconButton, + Text, + Divider, } from "@chakra-ui/react"; - import { ChevronRightIcon } from "@chakra-ui/icons"; import { ConversationInfo } from "../utils"; @@ -19,14 +20,34 @@ interface SidebarProps { setConversationId: (id: string) => void; } +const handleConversationClick = ( + id: string, + setConversationId: (id: string) => void, + onClose: () => void +) => { + setConversationId(id); + onClose(); +}; + const Sidebar = ({ conversations, setConversationId }: SidebarProps) => { const { isOpen, onOpen, onClose } = useDisclosure(); - const handleConversationClick = (id: string) => { - setConversationId(id); - onClose(); + const groupConversationsByDate = (conversations: ConversationInfo[]) => { + const groupedConversations: Record = {}; + + conversations.forEach((conversation) => { + const date = new Date(conversation.created_at).toDateString(); + if (!groupedConversations[date]) { + groupedConversations[date] = []; + } + groupedConversations[date].push(conversation); + }); + + return groupedConversations; }; + const groupedConversations = groupConversationsByDate(conversations); + return ( <> { variant="ghost" height="100px" /> - - {/* */} - - - - - {conversations.map((conversation) => ( - - ))} - - - - - {/* */} + + + + + + + {Object.entries(groupedConversations).map( + ([date, conversationsOnDate]) => ( + + + {date} + + {conversationsOnDate.map((conversation) => ( + + ))} + + + ) + )} + + + + + ); diff --git a/app/page.tsx b/app/page.tsx index 6c23d54..6be55f3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -48,6 +48,30 @@ export default function Home() { setTextValue(event.target.value); }; + const handleHideMessage = async (index: number) => { + try { + const updatedMessages = messages.map((msg, i) => + i === index ? { ...msg, hidden: !msg.hidden } : msg + ); + await fetch( + `${process.env.BACKEND_URL}/conversations/${conversationId}/messages`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${cookies.token}`, + }, + body: JSON.stringify({ messages: updatedMessages }), + mode: "cors", + credentials: "include", + } + ); + setMessages(updatedMessages); + } catch (error) { + console.error("Error toggling message visibility:", error); + } + }; + useEffect(() => { const token = searchParams.get("token"); @@ -139,7 +163,10 @@ export default function Home() { justify="space-between" > - +