mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
Merge pull request #1049 from siiddhantt/fix/conversation-textarea
fix: changed query box from div to textarea
This commit is contained in:
@@ -44,7 +44,7 @@ def delete_conversation():
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@user.route("/api/delete_all_conversations", methods=["POST"])
|
||||
@user.route("/api/delete_all_conversations", methods=["GET"])
|
||||
def delete_all_conversations():
|
||||
user_id = "local"
|
||||
conversations_collection.delete_many({"user": user_id})
|
||||
@@ -256,7 +256,7 @@ def combined_json():
|
||||
"docLink": "default",
|
||||
"model": settings.EMBEDDINGS_NAME,
|
||||
"location": "remote",
|
||||
"tokens":""
|
||||
"tokens": "",
|
||||
}
|
||||
]
|
||||
# structure: name, language, version, description, fullName, date, docLink
|
||||
@@ -273,7 +273,7 @@ def combined_json():
|
||||
"docLink": index["location"],
|
||||
"model": settings.EMBEDDINGS_NAME,
|
||||
"location": "local",
|
||||
"tokens" : index["tokens"] if ("tokens" in index.keys()) else ""
|
||||
"tokens": index["tokens"] if ("tokens" in index.keys()) else "",
|
||||
}
|
||||
)
|
||||
if settings.VECTOR_STORE == "faiss":
|
||||
@@ -295,7 +295,7 @@ def combined_json():
|
||||
"docLink": "duckduck_search",
|
||||
"model": settings.EMBEDDINGS_NAME,
|
||||
"location": "custom",
|
||||
"tokens":""
|
||||
"tokens": "",
|
||||
}
|
||||
)
|
||||
if "brave_search" in settings.RETRIEVERS_ENABLED:
|
||||
@@ -310,7 +310,7 @@ def combined_json():
|
||||
"docLink": "brave_search",
|
||||
"model": settings.EMBEDDINGS_NAME,
|
||||
"location": "custom",
|
||||
"tokens":""
|
||||
"tokens": "",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -506,49 +506,69 @@ def share_conversation():
|
||||
conversation_id = data["conversation_id"]
|
||||
isPromptable = request.args.get("isPromptable").lower() == "true"
|
||||
|
||||
conversation = conversations_collection.find_one({"_id": ObjectId(conversation_id)})
|
||||
conversation = conversations_collection.find_one(
|
||||
{"_id": ObjectId(conversation_id)}
|
||||
)
|
||||
current_n_queries = len(conversation["queries"])
|
||||
|
||||
##generate binary representation of uuid
|
||||
explicit_binary = Binary.from_uuid(uuid.uuid4(), UuidRepresentation.STANDARD)
|
||||
|
||||
if(isPromptable):
|
||||
if isPromptable:
|
||||
source = "default" if "source" not in data else data["source"]
|
||||
prompt_id = "default" if "prompt_id" not in data else data["prompt_id"]
|
||||
chunks = "2" if "chunks" not in data else data["chunks"]
|
||||
|
||||
name = conversation["name"] + "(shared)"
|
||||
pre_existing_api_document = api_key_collection.find_one({
|
||||
pre_existing_api_document = api_key_collection.find_one(
|
||||
{
|
||||
"prompt_id": prompt_id,
|
||||
"chunks": chunks,
|
||||
"source": source,
|
||||
"user":user
|
||||
})
|
||||
"user": user,
|
||||
}
|
||||
)
|
||||
api_uuid = str(uuid.uuid4())
|
||||
if(pre_existing_api_document):
|
||||
if pre_existing_api_document:
|
||||
api_uuid = pre_existing_api_document["key"]
|
||||
pre_existing = shared_conversations_collections.find_one({
|
||||
"conversation_id":DBRef("conversations",ObjectId(conversation_id)),
|
||||
pre_existing = shared_conversations_collections.find_one(
|
||||
{
|
||||
"conversation_id": DBRef(
|
||||
"conversations", ObjectId(conversation_id)
|
||||
),
|
||||
"isPromptable": isPromptable,
|
||||
"first_n_queries": current_n_queries,
|
||||
"user": user,
|
||||
"api_key":api_uuid
|
||||
})
|
||||
if(pre_existing is not None):
|
||||
return jsonify({"success":True, "identifier":str(pre_existing["uuid"].as_uuid())}),200
|
||||
"api_key": api_uuid,
|
||||
}
|
||||
)
|
||||
if pre_existing is not None:
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"identifier": str(pre_existing["uuid"].as_uuid()),
|
||||
}
|
||||
),
|
||||
200,
|
||||
)
|
||||
else:
|
||||
shared_conversations_collections.insert_one({
|
||||
shared_conversations_collections.insert_one(
|
||||
{
|
||||
"uuid": explicit_binary,
|
||||
"conversation_id": {
|
||||
"$ref": "conversations",
|
||||
"$id":ObjectId(conversation_id)
|
||||
"$id": ObjectId(conversation_id),
|
||||
},
|
||||
"isPromptable": isPromptable,
|
||||
"first_n_queries": current_n_queries,
|
||||
"user": user,
|
||||
"api_key":api_uuid
|
||||
})
|
||||
return jsonify({"success":True,"identifier":str(explicit_binary.as_uuid())})
|
||||
"api_key": api_uuid,
|
||||
}
|
||||
)
|
||||
return jsonify(
|
||||
{"success": True, "identifier": str(explicit_binary.as_uuid())}
|
||||
)
|
||||
else:
|
||||
api_key_collection.insert_one(
|
||||
{
|
||||
@@ -560,72 +580,118 @@ def share_conversation():
|
||||
"chunks": chunks,
|
||||
}
|
||||
)
|
||||
shared_conversations_collections.insert_one({
|
||||
shared_conversations_collections.insert_one(
|
||||
{
|
||||
"uuid": explicit_binary,
|
||||
"conversation_id": {
|
||||
"$ref": "conversations",
|
||||
"$id":ObjectId(conversation_id)
|
||||
"$id": ObjectId(conversation_id),
|
||||
},
|
||||
"isPromptable": isPromptable,
|
||||
"first_n_queries": current_n_queries,
|
||||
"user": user,
|
||||
"api_key":api_uuid
|
||||
})
|
||||
"api_key": api_uuid,
|
||||
}
|
||||
)
|
||||
## Identifier as route parameter in frontend
|
||||
return jsonify({"success":True, "identifier":str(explicit_binary.as_uuid())}),201
|
||||
return (
|
||||
jsonify(
|
||||
{"success": True, "identifier": str(explicit_binary.as_uuid())}
|
||||
),
|
||||
201,
|
||||
)
|
||||
|
||||
##isPromptable = False
|
||||
pre_existing = shared_conversations_collections.find_one({
|
||||
pre_existing = shared_conversations_collections.find_one(
|
||||
{
|
||||
"conversation_id": DBRef("conversations", ObjectId(conversation_id)),
|
||||
"isPromptable": isPromptable,
|
||||
"first_n_queries": current_n_queries,
|
||||
"user":user
|
||||
})
|
||||
if(pre_existing is not None):
|
||||
return jsonify({"success":True, "identifier":str(pre_existing["uuid"].as_uuid())}),200
|
||||
"user": user,
|
||||
}
|
||||
)
|
||||
if pre_existing is not None:
|
||||
return (
|
||||
jsonify(
|
||||
{"success": True, "identifier": str(pre_existing["uuid"].as_uuid())}
|
||||
),
|
||||
200,
|
||||
)
|
||||
else:
|
||||
shared_conversations_collections.insert_one({
|
||||
shared_conversations_collections.insert_one(
|
||||
{
|
||||
"uuid": explicit_binary,
|
||||
"conversation_id": {
|
||||
"$ref": "conversations",
|
||||
"$id":ObjectId(conversation_id)
|
||||
"$id": ObjectId(conversation_id),
|
||||
},
|
||||
"isPromptable": isPromptable,
|
||||
"first_n_queries": current_n_queries,
|
||||
"user":user
|
||||
})
|
||||
"user": user,
|
||||
}
|
||||
)
|
||||
## Identifier as route parameter in frontend
|
||||
return jsonify({"success":True, "identifier":str(explicit_binary.as_uuid())}),201
|
||||
return (
|
||||
jsonify(
|
||||
{"success": True, "identifier": str(explicit_binary.as_uuid())}
|
||||
),
|
||||
201,
|
||||
)
|
||||
except Exception as err:
|
||||
print(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)
|
||||
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):
|
||||
if (
|
||||
shared
|
||||
and "conversation_id" in shared
|
||||
and isinstance(shared["conversation_id"], DBRef)
|
||||
):
|
||||
# Resolve the DBRef
|
||||
conversation_ref = shared['conversation_id']
|
||||
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"])]
|
||||
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
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"sucess": False,
|
||||
"error": "might have broken url or the conversation does not exist",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
date = conversation["_id"].generation_time.isoformat()
|
||||
res = {
|
||||
"success": True,
|
||||
"queries": conversation_queries,
|
||||
"title": conversation["name"],
|
||||
"timestamp":date
|
||||
"timestamp": date,
|
||||
}
|
||||
if(shared["isPromptable"] and "api_key" in shared):
|
||||
if shared["isPromptable"] and "api_key" in shared:
|
||||
res["api_key"] = shared["api_key"]
|
||||
return jsonify(res), 200
|
||||
except Exception as err:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function Conversation() {
|
||||
const conversationId = useSelector(selectConversationId);
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const endMessageRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
const [hasScrolledToLast, setHasScrolledToLast] = useState(true);
|
||||
const fetchStream = useRef<any>(null);
|
||||
@@ -48,7 +48,7 @@ export default function Conversation() {
|
||||
}, [queries.length, queries[queries.length - 1]]);
|
||||
|
||||
useEffect(() => {
|
||||
const element = document.getElementById('inputbox') as HTMLInputElement;
|
||||
const element = document.getElementById('inputbox') as HTMLTextAreaElement;
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
@@ -119,14 +119,14 @@ export default function Conversation() {
|
||||
};
|
||||
|
||||
const handleQuestionSubmission = () => {
|
||||
if (inputRef.current?.textContent && status !== 'loading') {
|
||||
if (inputRef.current?.value && status !== 'loading') {
|
||||
if (lastQueryReturnedErr) {
|
||||
// update last failed query with new prompt
|
||||
dispatch(
|
||||
updateQuery({
|
||||
index: queries.length - 1,
|
||||
query: {
|
||||
prompt: inputRef.current.textContent,
|
||||
prompt: inputRef.current.value,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -135,9 +135,10 @@ export default function Conversation() {
|
||||
isRetry: true,
|
||||
});
|
||||
} else {
|
||||
handleQuestion({ question: inputRef.current.textContent });
|
||||
handleQuestion({ question: inputRef.current.value });
|
||||
}
|
||||
inputRef.current.textContent = '';
|
||||
inputRef.current.value = '';
|
||||
handleInput();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -191,12 +192,24 @@ export default function Conversation() {
|
||||
return responseView;
|
||||
};
|
||||
|
||||
const handlePaste = (e: React.ClipboardEvent) => {
|
||||
e.preventDefault();
|
||||
const text = e.clipboardData.getData('text/plain');
|
||||
inputRef.current && (inputRef.current.innerText = text);
|
||||
const handleInput = () => {
|
||||
if (inputRef.current) {
|
||||
if (window.innerWidth < 350) inputRef.current.style.height = 'auto';
|
||||
else inputRef.current.style.height = '64px';
|
||||
inputRef.current.style.height = `${Math.min(
|
||||
inputRef.current.scrollHeight,
|
||||
96,
|
||||
)}px`;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleInput();
|
||||
window.addEventListener('resize', handleInput);
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleInput);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<div className="flex h-screen flex-col gap-7 pb-2">
|
||||
{conversationId && (
|
||||
@@ -267,22 +280,21 @@ export default function Conversation() {
|
||||
</div>
|
||||
|
||||
<div className="flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 pb-1 sm:w-8/12">
|
||||
<div className="flex h-full w-full items-center rounded-[40px] border border-silver bg-white py-1 dark:bg-raisin-black">
|
||||
<div
|
||||
<div className="flex w-full items-center rounded-[40px] border border-silver bg-white py-1 dark:bg-raisin-black">
|
||||
<textarea
|
||||
id="inputbox"
|
||||
ref={inputRef}
|
||||
tabIndex={1}
|
||||
placeholder={t('inputPlaceholder')}
|
||||
contentEditable
|
||||
onPaste={handlePaste}
|
||||
className={`inputbox-style max-h-24 w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-white pt-5 pb-[22px] text-base leading-tight opacity-100 focus:outline-none dark:bg-raisin-black dark:text-bright-gray`}
|
||||
className={`inputbox-style h-16 w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-white pt-5 pb-[22px] text-base leading-tight opacity-100 focus:outline-none dark:bg-raisin-black dark:text-bright-gray`}
|
||||
onInput={handleInput}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleQuestionSubmission();
|
||||
}
|
||||
}}
|
||||
></div>
|
||||
></textarea>
|
||||
{status === 'loading' ? (
|
||||
<img
|
||||
src={isDarkTheme ? SpinnerDark : Spinner}
|
||||
|
||||
@@ -424,7 +424,8 @@ template {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.inputbox-style[contenteditable] {
|
||||
.inputbox-style {
|
||||
resize: none;
|
||||
padding-left: 36px;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user