Skip to content

Commit

Permalink
Merge pull request #1034 from ManishMadan2882/main
Browse files Browse the repository at this point in the history
Feat: sharing endpoints
  • Loading branch information
dartpain committed Jul 16, 2024
2 parents 1776f6e + 0f059f2 commit bacd2a6
Show file tree
Hide file tree
Showing 20 changed files with 672 additions and 111 deletions.
75 changes: 74 additions & 1 deletion application/api/user/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import requests
from pymongo import MongoClient
from bson.objectid import ObjectId
from bson.binary import Binary, UuidRepresentation
from werkzeug.utils import secure_filename

from bson.dbref import DBRef
from application.api.user.tasks import ingest, ingest_remote

from application.core.settings import settings
Expand All @@ -20,6 +21,8 @@
prompts_collection = db["prompts"]
feedback_collection = db["feedback"]
api_key_collection = db["api_keys"]
shared_conversations_collections = db["shared_conversations"]

user = Blueprint("user", __name__)

current_dir = os.path.dirname(
Expand Down Expand Up @@ -491,3 +494,73 @@ def delete_api_key():
}
)
return {"status": "ok"}


#route to share conversation
##isPromptable should be passed through queries
@user.route("/api/share",methods=["POST"])
def share_conversation():
try:
data = request.get_json()
user = "local"
if(hasattr(data,"user")):
user = data["user"]
conversation_id = data["conversation_id"]
isPromptable = request.args.get("isPromptable").lower() == "true"
conversation = conversations_collection.find_one({"_id": ObjectId(conversation_id)})
current_n_queries = len(conversation["queries"])
pre_existing = shared_conversations_collections.find_one({
"conversation_id":DBRef("conversations",ObjectId(conversation_id)),
"isPromptable":isPromptable,
"first_n_queries":current_n_queries
})
print("pre_existing",pre_existing)
if(pre_existing is not None):
explicit_binary = pre_existing["uuid"]
return jsonify({"success":True, "identifier":str(explicit_binary.as_uuid())}),200
else:
explicit_binary = Binary.from_uuid(uuid.uuid4(), UuidRepresentation.STANDARD)
shared_conversations_collections.insert_one({
"uuid":explicit_binary,
"conversation_id": {
"$ref":"conversations",
"$id":ObjectId(conversation_id)
} ,
"isPromptable":isPromptable,
"first_n_queries":current_n_queries,
"user":user
})
## Identifier as route parameter in frontend
return jsonify({"success":True, "identifier":str(explicit_binary.as_uuid())}),201
except Exception as err:
return jsonify({"success":False,"error":str(err)}),400

#route to get publicly shared conversations
@user.route("/api/shared_conversation/<string:identifier>",methods=["GET"])
def get_publicly_shared_conversations(identifier : str):
try:
query_uuid = Binary.from_uuid(uuid.UUID(identifier), UuidRepresentation.STANDARD)
shared = shared_conversations_collections.find_one({"uuid":query_uuid})
conversation_queries=[]
if shared and 'conversation_id' in shared and isinstance(shared['conversation_id'], DBRef):
# Resolve the DBRef
conversation_ref = shared['conversation_id']
conversation = db.dereference(conversation_ref)
if(conversation is None):
return jsonify({"sucess":False,"error":"might have broken url or the conversation does not exist"}),404
conversation_queries = conversation['queries'][:(shared["first_n_queries"])]
for query in conversation_queries:
query.pop("sources") ## avoid exposing sources
else:
return jsonify({"sucess":False,"error":"might have broken url or the conversation does not exist"}),404
date = conversation["_id"].generation_time.isoformat()
return jsonify({
"success":True,
"queries":conversation_queries,
"title":conversation["name"],
"timestamp":date
}), 200
except Exception as err:
print (err)
return jsonify({"success":False,"error":str(err)}),400

4 changes: 3 additions & 1 deletion application/vectorstore/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ class EmbeddingsSingleton:
@staticmethod
def get_instance(embeddings_name, *args, **kwargs):
if embeddings_name not in EmbeddingsSingleton._instances:
EmbeddingsSingleton._instances[embeddings_name] = EmbeddingsSingleton._create_instance(embeddings_name, *args, **kwargs)
EmbeddingsSingleton._instances[embeddings_name] = EmbeddingsSingleton._create_instance(
embeddings_name, *args, **kwargs
)
return EmbeddingsSingleton._instances[embeddings_name]

@staticmethod
Expand Down
10 changes: 10 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 35 additions & 10 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Routes, Route } from 'react-router-dom';
import { useEffect } from 'react';
import Navigation from './Navigation';
import Conversation from './conversation/Conversation';
import About from './About';
Expand All @@ -8,29 +9,53 @@ import { useMediaQuery } from './hooks';
import { useState } from 'react';
import Setting from './settings';
import './locale/i18n';

import { Outlet } from 'react-router-dom';
import SharedConversation from './conversation/SharedConversation';
import { useDarkTheme } from './hooks';
inject();

export default function App() {
function MainLayout() {
const { isMobile } = useMediaQuery();
const [navOpen, setNavOpen] = useState(!isMobile);
return (
<div className="min-h-full min-w-full dark:bg-raisin-black">
<div className="dark:bg-raisin-black">
<Navigation navOpen={navOpen} setNavOpen={setNavOpen} />
<div
className={`transition-all duration-200 ${
className={`min-h-screen ${
!isMobile
? `ml-0 ${!navOpen ? 'md:mx-auto lg:mx-auto' : 'md:ml-72'}`
: 'ml-0 md:ml-16'
}`}
>
<Routes>
<Route path="/" element={<Conversation />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<PageNotFound />} />
<Route path="/settings" element={<Setting />} />
</Routes>
<Outlet />
</div>
</div>
);
}

export default function App() {
const [isDarkTheme] = useDarkTheme();
useEffect(() => {
localStorage.setItem('selectedTheme', isDarkTheme ? 'Dark' : 'Light');
if (isDarkTheme) {
document
.getElementById('root')
?.classList.add('dark', 'dark:bg-raisin-black');
} else {
document.getElementById('root')?.classList.remove('dark');
}
}, [isDarkTheme]);
return (
<>
<Routes>
<Route element={<MainLayout />}>
<Route index element={<Conversation />} />
<Route path="/about" element={<About />} />
<Route path="/settings" element={<Setting />} />
</Route>
<Route path="/share/:identifier" element={<SharedConversation />} />
<Route path="/*" element={<PageNotFound />} />
</Routes>
</>
);
}
6 changes: 3 additions & 3 deletions frontend/src/PageNotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Link } from 'react-router-dom';

export default function PageNotFound() {
return (
<div className="mx-5 grid min-h-screen md:mx-36">
<p className="mx-auto my-auto mt-20 flex w-full max-w-6xl flex-col place-items-center gap-6 rounded-3xl bg-gray-100 p-6 text-jet lg:p-10 xl:p-16">
<div className="grid min-h-screen dark:bg-raisin-black">
<p className="mx-auto my-auto mt-20 flex w-full max-w-6xl flex-col place-items-center gap-6 rounded-3xl bg-gray-100 p-6 text-jet dark:bg-outer-space dark:text-gray-100 lg:p-10 xl:p-16">
<h1>404</h1>
<p>The page you are looking for does not exist.</p>
<button className="pointer-cursor mr-4 flex cursor-pointer items-center justify-center rounded-full bg-blue-1000 py-2 px-4 text-white hover:bg-blue-3000">
<button className="pointer-cursor mr-4 flex cursor-pointer items-center justify-center rounded-full bg-blue-1000 py-2 px-4 text-white transition-colors duration-100 hover:bg-blue-3000">
<Link to="/">Go Back Home</Link>
</button>
</p>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/red-trash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/share.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/three-dots.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions frontend/src/conversation/Conversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
selectStatus,
updateQuery,
} from './conversationSlice';
import { selectConversationId } from '../preferences/preferenceSlice';
import Send from './../assets/send.svg';
import SendDark from './../assets/send_dark.svg';
import Spinner from './../assets/spinner.svg';
Expand All @@ -20,9 +21,13 @@ import { sendFeedback } from './conversationApi';
import { useTranslation } from 'react-i18next';
import ArrowDown from './../assets/arrow-down.svg';
import RetryIcon from '../components/RetryIcon';
import ShareIcon from '../assets/share.svg';
import { ShareConversationModal } from '../modals/ShareConversationModal';

export default function Conversation() {
const queries = useSelector(selectQueries);
const status = useSelector(selectStatus);
const conversationId = useSelector(selectConversationId);
const dispatch = useDispatch<AppDispatch>();
const endMessageRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLDivElement>(null);
Expand All @@ -31,6 +36,7 @@ export default function Conversation() {
const fetchStream = useRef<any>(null);
const [eventInterrupt, setEventInterrupt] = useState(false);
const [lastQueryReturnedErr, setLastQueryReturnedErr] = useState(false);
const [isShareModalOpen, setShareModalState] = useState<boolean>(false);
const { t } = useTranslation();

const handleUserInterruption = () => {
Expand Down Expand Up @@ -192,6 +198,31 @@ export default function Conversation() {

return (
<div className="flex h-screen flex-col gap-7 pb-2">
{conversationId && (
<>
<button
title="Share"
onClick={() => {
setShareModalState(true);
}}
className="fixed top-4 right-20 z-30 rounded-full hover:bg-bright-gray dark:hover:bg-[#28292E]"
>
<img
className="m-2 h-5 w-5 filter dark:invert"
alt="share"
src={ShareIcon}
/>
</button>
{isShareModalOpen && (
<ShareConversationModal
close={() => {
setShareModalState(false);
}}
conversationId={conversationId}
/>
)}
</>
)}
<div
onWheel={handleUserInterruption}
onTouchMove={handleUserInterruption}
Expand Down
Loading

0 comments on commit bacd2a6

Please sign in to comment.