Spaces:
Sleeping
Sleeping
karwanjiru
commited on
Commit
•
b768c6b
1
Parent(s):
2c943de
files added
Browse files- .gitignore +3 -0
- Dockerfile +17 -0
- app.py +96 -0
- requirements.txt +13 -0
- setup.py +11 -0
- src/__init__.py +0 -0
- src/__pycache__/__init__.cpython-311.pyc +0 -0
- src/__pycache__/helper.cpython-311.pyc +0 -0
- src/helper.py +39 -0
- src/prompt.py +7 -0
- static/.gitkeep +0 -0
- store_index.py +106 -0
- template.py +40 -0
- templates/chat.html +295 -0
.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
data
|
2 |
+
research
|
3 |
+
.env
|
Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9
|
2 |
+
|
3 |
+
RUN useradd -m -u 1000 user
|
4 |
+
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
8 |
+
|
9 |
+
RUN pip install -r requirements.txt
|
10 |
+
|
11 |
+
RUN pip install --upgrade sentence_transformers
|
12 |
+
|
13 |
+
RUN pip install --upgrade langchain
|
14 |
+
|
15 |
+
COPY --chown=user . /app
|
16 |
+
|
17 |
+
CMD ["gunicorn", "app:app","-b","0.0.0.0:7860"]
|
app.py
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import time
|
3 |
+
from flask import Flask, render_template, jsonify, request
|
4 |
+
from src.helper import download_hugging_face_embeddings
|
5 |
+
from langchain.llms import Replicate
|
6 |
+
from dotenv import load_dotenv
|
7 |
+
from PyPDF2 import PdfReader
|
8 |
+
from langchain.schema import Document
|
9 |
+
from langchain.text_splitter import CharacterTextSplitter
|
10 |
+
|
11 |
+
# Initialize Flask app
|
12 |
+
app = Flask(__name__)
|
13 |
+
|
14 |
+
# Load environment variables
|
15 |
+
load_dotenv()
|
16 |
+
|
17 |
+
# Optional PDF processing functions
|
18 |
+
# def load_pdf(file_path):
|
19 |
+
# all_text = ""
|
20 |
+
# with open(file_path, 'rb') as file:
|
21 |
+
# reader = PdfReader(file)
|
22 |
+
# for page in reader.pages:
|
23 |
+
# all_text += page.extract_text() + "\n"
|
24 |
+
# return all_text if all_text else None
|
25 |
+
|
26 |
+
# def text_split(text):
|
27 |
+
# text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
28 |
+
# document = Document(page_content=text)
|
29 |
+
# return text_splitter.split_documents([document])
|
30 |
+
|
31 |
+
# Load and process data
|
32 |
+
# pdf_file_path = "data/Okelloetal.2008TourismanalysisManka.pdf"
|
33 |
+
# extracted_data = load_pdf(pdf_file_path)
|
34 |
+
# if extracted_data is None:
|
35 |
+
# raise ValueError("The extracted data is None. Please check the load_pdf function.")
|
36 |
+
# print(f"Extracted Data: {extracted_data}")
|
37 |
+
|
38 |
+
# Split the extracted text into chunks
|
39 |
+
# text_chunks = text_split(extracted_data)
|
40 |
+
# if not text_chunks:
|
41 |
+
# raise ValueError("The text_chunks is None or empty. Please check the text_split function.")
|
42 |
+
# print(f"Text Chunks: {text_chunks}")
|
43 |
+
|
44 |
+
embeddings = download_hugging_face_embeddings()
|
45 |
+
if embeddings is None:
|
46 |
+
raise ValueError("The embeddings is None. Please check the download_hugging_face_embeddings function.")
|
47 |
+
print(f"Embeddings: {embeddings}")
|
48 |
+
|
49 |
+
os.environ["REPLICATE_API_TOKEN"] = "r8_3eWT6qNBwq8r7zNknWKxsyNyOQ6WMGS2WWRay"
|
50 |
+
|
51 |
+
# Initialize the Replicate model
|
52 |
+
llm = Replicate(
|
53 |
+
model="a16z-infra/llama7b-v2-chat:4f0a4744c7295c024a1de15e1a63c880d3da035fa1f49bfd344fe076074c8eea",
|
54 |
+
config={
|
55 |
+
'max_new_tokens': 100, # Maximum number of tokens to generate in response
|
56 |
+
'temperature': 0.7, # Optimal temperature for balanced randomness and coherence
|
57 |
+
'top_k': 50 # Optimal top-k value for considering the top 50 predictions
|
58 |
+
}
|
59 |
+
)
|
60 |
+
# Flask routes
|
61 |
+
@app.route("/")
|
62 |
+
def index():
|
63 |
+
return render_template('chat.html')
|
64 |
+
|
65 |
+
@app.route("/get", methods=["GET", "POST"])
|
66 |
+
def chat():
|
67 |
+
try:
|
68 |
+
msg = request.form["msg"]
|
69 |
+
input_text = msg
|
70 |
+
print(f"Received message: {input_text}")
|
71 |
+
|
72 |
+
# Display spinner
|
73 |
+
result = {"generated_text": "Thinking..."}
|
74 |
+
|
75 |
+
# Simulate processing delay
|
76 |
+
time.sleep(1)
|
77 |
+
|
78 |
+
# Retrieve response from the model
|
79 |
+
result = llm.generate([input_text])
|
80 |
+
print(f"LLMResult: {result}")
|
81 |
+
|
82 |
+
# Access the generated text from the result object
|
83 |
+
if result.generations and result.generations[0]:
|
84 |
+
generated_text = result.generations[0][0].text
|
85 |
+
else:
|
86 |
+
generated_text = "No response generated."
|
87 |
+
|
88 |
+
print(f"Response: {generated_text}")
|
89 |
+
|
90 |
+
return str(generated_text)
|
91 |
+
except Exception as e:
|
92 |
+
print(f"Error: {e}")
|
93 |
+
return jsonify({"error": str(e)}), 500
|
94 |
+
|
95 |
+
if __name__ == '__main__':
|
96 |
+
app.run(host="0.0.0.0", port=8080, debug=True)
|
requirements.txt
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ctransformers
|
2 |
+
sentence-transformers==2.2.2
|
3 |
+
pinecone-client
|
4 |
+
langchain==0.0.225
|
5 |
+
flask
|
6 |
+
pypdf
|
7 |
+
python-dotenv
|
8 |
+
pinecone
|
9 |
+
langchain_community
|
10 |
+
sentence_transformers
|
11 |
+
langchain_pinecone
|
12 |
+
gunicorn
|
13 |
+
PyPDF2
|
setup.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from setuptools import find_packages, setup
|
2 |
+
|
3 |
+
setup(
|
4 |
+
name = 'VoyageVirtuoso',
|
5 |
+
version= '0.0.0',
|
6 |
+
author= 'Diana Wanjiru',
|
7 |
+
author_email= 'karwanjiru@gmail.com',
|
8 |
+
packages= find_packages(),
|
9 |
+
install_requires = []
|
10 |
+
|
11 |
+
)
|
src/__init__.py
ADDED
File without changes
|
src/__pycache__/__init__.cpython-311.pyc
ADDED
Binary file (179 Bytes). View file
|
|
src/__pycache__/helper.cpython-311.pyc
ADDED
Binary file (2.12 kB). View file
|
|
src/helper.py
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain import PromptTemplate
|
2 |
+
from langchain.chains import RetrievalQA
|
3 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
4 |
+
from langchain_community.vectorstores import Pinecone
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
import os
|
7 |
+
from pinecone import Pinecone
|
8 |
+
from langchain.document_loaders import PyPDFLoader, DirectoryLoader
|
9 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
10 |
+
from langchain.prompts import PromptTemplate
|
11 |
+
from langchain.llms import CTransformers
|
12 |
+
from unittest import loader
|
13 |
+
|
14 |
+
|
15 |
+
load_dotenv()
|
16 |
+
|
17 |
+
PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
|
18 |
+
PINECONE_API_ENV = os.environ.get('PINECONE_API_ENV')
|
19 |
+
|
20 |
+
# Extract pdf data
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
def load_pdf(data):
|
25 |
+
directory_loader = DirectoryLoader(data,
|
26 |
+
glob="*.pdf",
|
27 |
+
loader_cls=PyPDFLoader)
|
28 |
+
|
29 |
+
documents = directory_loader.load()
|
30 |
+
|
31 |
+
def text_split(extracted_data):
|
32 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size = 500, chunk_overlap = 20)
|
33 |
+
text_chunks = text_splitter.split_documents(extracted_data)
|
34 |
+
|
35 |
+
return text_chunks
|
36 |
+
|
37 |
+
def download_hugging_face_embeddings():
|
38 |
+
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
39 |
+
return embeddings
|
src/prompt.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
prompt_template= """
|
2 |
+
As VoyageVirtuoso, your role is to serve as a wildlife guide AI, providing factual and informative responses about wildlife and nature to tourists. Your responses should strictly adhere to providing helpful information based on the given context and questions. If you do not have the information, simply state that you don't know without fabricating an answer. Your responses should avoid anthropomorphizing or expressing personal opinions. Please refrain from mimicking the tourist's language or style.
|
3 |
+
|
4 |
+
Context: {context}
|
5 |
+
Question: {question}
|
6 |
+
Helpful answer:
|
7 |
+
"""
|
static/.gitkeep
ADDED
File without changes
|
store_index.py
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import time
|
3 |
+
from src.helper import PINECONE_API_KEY, text_split, download_hugging_face_embeddings
|
4 |
+
from langchain.vectorstores import Pinecone as LangchainPinecone # Alias to avoid confusion
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
from pinecone import Pinecone, ServerlessSpec
|
7 |
+
from langchain_pinecone import PineconeVectorStore
|
8 |
+
from PyPDF2 import PdfReader
|
9 |
+
|
10 |
+
# Define the load_pdf function
|
11 |
+
def load_pdf(file_path):
|
12 |
+
all_text = ""
|
13 |
+
with open(file_path, 'rb') as file:
|
14 |
+
reader = PdfReader(file)
|
15 |
+
for page in reader.pages:
|
16 |
+
all_text += page.extract_text() + "\n"
|
17 |
+
return all_text if all_text else None
|
18 |
+
|
19 |
+
# Define the text_split function
|
20 |
+
def text_split(text):
|
21 |
+
from langchain.text_splitter import CharacterTextSplitter
|
22 |
+
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
23 |
+
return text_splitter.split_text(text)
|
24 |
+
|
25 |
+
# Load environment variables if not already set
|
26 |
+
load_dotenv()
|
27 |
+
|
28 |
+
# Load and process data
|
29 |
+
pdf_file_path = "data/Okelloetal.2008TourismanalysisManka.pdf" # Update this path to your single PDF file
|
30 |
+
extracted_data = load_pdf(pdf_file_path)
|
31 |
+
if extracted_data is None:
|
32 |
+
raise ValueError("The extracted data is None. Please check the load_pdf function.")
|
33 |
+
|
34 |
+
print(f"Extracted Data: {extracted_data}")
|
35 |
+
|
36 |
+
# Split the extracted text into chunks
|
37 |
+
text_chunks = text_split(extracted_data)
|
38 |
+
if text_chunks is None:
|
39 |
+
raise ValueError("The text_chunks is None. Please check the text_split function.")
|
40 |
+
|
41 |
+
print(f"Text Chunks: {text_chunks}")
|
42 |
+
|
43 |
+
embeddings = download_hugging_face_embeddings()
|
44 |
+
if embeddings is None:
|
45 |
+
raise ValueError("The embeddings is None. Please check the download_hugging_face_embeddings function.")
|
46 |
+
|
47 |
+
print(f"Embeddings: {embeddings}")
|
48 |
+
|
49 |
+
# Ensure Pinecone API key is available
|
50 |
+
api_key = os.environ.get("PINECONE_API_KEY")
|
51 |
+
if not api_key:
|
52 |
+
raise ValueError("PINECONE_API_KEY environment variable not set.")
|
53 |
+
|
54 |
+
# Initialize Pinecone client
|
55 |
+
pc = Pinecone(api_key=api_key)
|
56 |
+
|
57 |
+
# Specify cloud and region for the serverless index
|
58 |
+
cloud = os.environ.get('PINECONE_CLOUD') or 'aws'
|
59 |
+
region = os.environ.get('PINECONE_REGION') or 'us-east-1'
|
60 |
+
spec = ServerlessSpec(cloud=cloud, region=region)
|
61 |
+
|
62 |
+
# Define the index name
|
63 |
+
index_name = "healthbot"
|
64 |
+
|
65 |
+
# Create the index if it does not exist
|
66 |
+
if index_name not in pc.list_indexes().names():
|
67 |
+
pc.create_index(
|
68 |
+
name=index_name,
|
69 |
+
dimension=384,
|
70 |
+
metric="cosine",
|
71 |
+
spec=spec
|
72 |
+
)
|
73 |
+
# Wait for the index to be ready
|
74 |
+
while not pc.describe_index(index_name).status['ready']:
|
75 |
+
time.sleep(1)
|
76 |
+
|
77 |
+
# Connect to the created index
|
78 |
+
index = pc.Index(index_name)
|
79 |
+
time.sleep(1)
|
80 |
+
|
81 |
+
# Example: Add data to the index with reduced metadata
|
82 |
+
# Create a dictionary to simulate external storage of text chunks
|
83 |
+
text_chunk_store = {}
|
84 |
+
|
85 |
+
# Function to simulate storing text chunk and returning a reference ID
|
86 |
+
def store_text_chunk(text_chunk):
|
87 |
+
chunk_id = f"chunk_{len(text_chunk_store)}"
|
88 |
+
text_chunk_store[chunk_id] = text_chunk
|
89 |
+
return chunk_id
|
90 |
+
|
91 |
+
# Add text chunks to Pinecone with reference IDs
|
92 |
+
for i, text_chunk in enumerate(text_chunks):
|
93 |
+
chunk_id = store_text_chunk(text_chunk)
|
94 |
+
embedding = embeddings.embed_query(text_chunk) # Embed the text chunk
|
95 |
+
index.upsert(
|
96 |
+
vectors=[
|
97 |
+
{
|
98 |
+
"id": f"vec_{i}",
|
99 |
+
"values": embedding,
|
100 |
+
"metadata": {"chunk_id": chunk_id} # Only store the reference ID as metadata
|
101 |
+
}
|
102 |
+
],
|
103 |
+
namespace="ns1"
|
104 |
+
)
|
105 |
+
|
106 |
+
print("Indexing completed successfully.")
|
template.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from pathlib import Path
|
3 |
+
import logging
|
4 |
+
|
5 |
+
logging.basicConfig(
|
6 |
+
level=logging.INFO,
|
7 |
+
format='[%(asctime)s]: %(message)s:'
|
8 |
+
)
|
9 |
+
|
10 |
+
|
11 |
+
list_of_files = [
|
12 |
+
"src/__init__.py",
|
13 |
+
"src/helper.py",
|
14 |
+
"src/prompt.py",
|
15 |
+
".env",
|
16 |
+
"setup.py",
|
17 |
+
"research/trials.ipynb",
|
18 |
+
"app.py",
|
19 |
+
"store_index.py",
|
20 |
+
"static/.gitkeep",
|
21 |
+
"templates/chat.html"
|
22 |
+
|
23 |
+
]
|
24 |
+
|
25 |
+
|
26 |
+
for filepath in list_of_files:
|
27 |
+
filepath = Path(filepath)
|
28 |
+
filedir, filename = os.path.split(filepath)
|
29 |
+
|
30 |
+
if filedir !="":
|
31 |
+
os.makedirs(filedir, exist_ok=True)
|
32 |
+
logging.info(f"Creating directory; {filedir} for the file {filename}")
|
33 |
+
|
34 |
+
if (not os.path.exists(filepath)) or (os.path.getsize(filepath) == 0):
|
35 |
+
with open(filepath, 'w') as f:
|
36 |
+
pass
|
37 |
+
logging.info(f"Creating empty file: {filepath}")
|
38 |
+
|
39 |
+
else:
|
40 |
+
logging.info(f"{filename} is already created")
|
templates/chat.html
ADDED
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>VoyageVirtuoso</title>
|
5 |
+
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
|
6 |
+
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
|
7 |
+
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
8 |
+
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
9 |
+
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
|
10 |
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
11 |
+
<link rel="stylesheet" href="static/style.css"/>
|
12 |
+
<style>
|
13 |
+
body,html {
|
14 |
+
height: 100%;
|
15 |
+
margin: 0;
|
16 |
+
background: rgb(168, 69, 163);
|
17 |
+
background: -webkit-linear-gradient(to right, rgb(40, 59, 34), rgb(54, 60, 70), rgb(32, 32, 43));
|
18 |
+
background: linear-gradient(to right, rgb(56, 109, 150), rgb(79, 98, 136), rgb(33, 33, 78));
|
19 |
+
}
|
20 |
+
|
21 |
+
.chat {
|
22 |
+
margin-top: auto;
|
23 |
+
margin-bottom: auto;
|
24 |
+
}
|
25 |
+
.card {
|
26 |
+
height: 500px;
|
27 |
+
border-radius: 15px !important;
|
28 |
+
background-color: rgba(0,0,0,0.4) !important;
|
29 |
+
}
|
30 |
+
.contacts_body {
|
31 |
+
padding: 0.75rem 0 !important;
|
32 |
+
overflow-y: auto;
|
33 |
+
white-space: nowrap;
|
34 |
+
}
|
35 |
+
.msg_card_body {
|
36 |
+
overflow-y: auto;
|
37 |
+
}
|
38 |
+
.card-header {
|
39 |
+
border-radius: 15px 15px 0 0 !important;
|
40 |
+
border-bottom: 0 !important;
|
41 |
+
}
|
42 |
+
.card-footer {
|
43 |
+
border-radius: 0 0 15px 15px !important;
|
44 |
+
border-top: 0 !important;
|
45 |
+
}
|
46 |
+
.container {
|
47 |
+
align-content: center;
|
48 |
+
}
|
49 |
+
.search {
|
50 |
+
border-radius: 15px 0 0 15px !important;
|
51 |
+
background-color: rgba(0,0,0,0.3) !important;
|
52 |
+
border:0 !important;
|
53 |
+
color:white !important;
|
54 |
+
}
|
55 |
+
.search:focus {
|
56 |
+
box-shadow:none !important;
|
57 |
+
outline:0px !important;
|
58 |
+
}
|
59 |
+
.type_msg {
|
60 |
+
background-color: rgba(0,0,0,0.3) !important;
|
61 |
+
border:0 !important;
|
62 |
+
color:rgb(216, 206, 113) !important;
|
63 |
+
height: 60px !important;
|
64 |
+
overflow-y: auto;
|
65 |
+
}
|
66 |
+
.type_msg:focus {
|
67 |
+
box-shadow:none !important;
|
68 |
+
outline:0px !important;
|
69 |
+
}
|
70 |
+
.attach_btn {
|
71 |
+
border-radius: 15px 0 0 15px !important;
|
72 |
+
background-color: rgba(0,0,0,0.3) !important;
|
73 |
+
border:0 !important;
|
74 |
+
color: white !important;
|
75 |
+
cursor: pointer;
|
76 |
+
}
|
77 |
+
.send_btn {
|
78 |
+
border-radius: 0 15px 15px 0 !important;
|
79 |
+
background-color: rgba(0,0,0,0.3) !important;
|
80 |
+
border:0 !important;
|
81 |
+
color: white !important;
|
82 |
+
cursor: pointer;
|
83 |
+
}
|
84 |
+
.search_btn {
|
85 |
+
border-radius: 0 15px 15px 0 !important;
|
86 |
+
background-color: rgba(0,0,0,0.3) !important;
|
87 |
+
border:0 !important;
|
88 |
+
color: white !important;
|
89 |
+
cursor: pointer;
|
90 |
+
}
|
91 |
+
.contacts {
|
92 |
+
list-style: none;
|
93 |
+
padding: 0;
|
94 |
+
}
|
95 |
+
.contacts li {
|
96 |
+
width: 100% !important;
|
97 |
+
padding: 5px 10px;
|
98 |
+
margin-bottom: 15px !important;
|
99 |
+
}
|
100 |
+
.active {
|
101 |
+
background-color: rgba(0,0,0,0.3);
|
102 |
+
}
|
103 |
+
.user_img {
|
104 |
+
height: 70px;
|
105 |
+
width: 70px;
|
106 |
+
border:1.5px solid #f5f6fa;
|
107 |
+
}
|
108 |
+
.user_img_msg {
|
109 |
+
height: 40px;
|
110 |
+
width: 40px;
|
111 |
+
border:1.5px solid #f5f6fa;
|
112 |
+
}
|
113 |
+
.img_cont {
|
114 |
+
position: relative;
|
115 |
+
height: 70px;
|
116 |
+
width: 70px;
|
117 |
+
}
|
118 |
+
.img_cont_msg {
|
119 |
+
height: 40px;
|
120 |
+
width: 40px;
|
121 |
+
}
|
122 |
+
.online_icon {
|
123 |
+
position: absolute;
|
124 |
+
height: 15px;
|
125 |
+
width:15px;
|
126 |
+
background-color: #4cd137;
|
127 |
+
border-radius: 50%;
|
128 |
+
bottom: 0.2em;
|
129 |
+
right: 0.4em;
|
130 |
+
border:1.5px solid white;
|
131 |
+
}
|
132 |
+
.offline {
|
133 |
+
background-color: #c23616 !important;
|
134 |
+
}
|
135 |
+
.user_info {
|
136 |
+
margin-top: auto;
|
137 |
+
margin-bottom: auto;
|
138 |
+
margin-left: 15px;
|
139 |
+
}
|
140 |
+
.user_info span {
|
141 |
+
font-size: 20px;
|
142 |
+
color: white;
|
143 |
+
}
|
144 |
+
.user_info p {
|
145 |
+
font-size: 10px;
|
146 |
+
color: rgba(255,255,255,0.6);
|
147 |
+
}
|
148 |
+
.video_cam {
|
149 |
+
margin-left: 50px;
|
150 |
+
margin-top: 5px;
|
151 |
+
}
|
152 |
+
.video_cam span {
|
153 |
+
color: white;
|
154 |
+
font-size: 20px;
|
155 |
+
cursor: pointer;
|
156 |
+
margin-right: 20px;
|
157 |
+
}
|
158 |
+
.msg_cotainer {
|
159 |
+
margin-top: auto;
|
160 |
+
margin-bottom: auto;
|
161 |
+
margin-left: 10px;
|
162 |
+
border-radius: 25px;
|
163 |
+
background-color: rgb(82, 172, 255);
|
164 |
+
padding: 10px;
|
165 |
+
position: relative;
|
166 |
+
}
|
167 |
+
.msg_cotainer_send {
|
168 |
+
margin-top: auto;
|
169 |
+
margin-bottom: auto;
|
170 |
+
margin-right: 10px;
|
171 |
+
border-radius: 25px;
|
172 |
+
background-color: #58cc71;
|
173 |
+
padding: 10px;
|
174 |
+
position: relative;
|
175 |
+
}
|
176 |
+
.msg_time {
|
177 |
+
position: absolute;
|
178 |
+
left: 0;
|
179 |
+
bottom: -15px;
|
180 |
+
color: rgba(255,255,255,0.5);
|
181 |
+
font-size: 10px;
|
182 |
+
}
|
183 |
+
.msg_time_send {
|
184 |
+
position: absolute;
|
185 |
+
right:0;
|
186 |
+
bottom: -15px;
|
187 |
+
color: rgba(255,255,255,0.5);
|
188 |
+
font-size: 10px;
|
189 |
+
}
|
190 |
+
.msg_head {
|
191 |
+
position: relative;
|
192 |
+
}
|
193 |
+
#action_menu_btn {
|
194 |
+
position: absolute;
|
195 |
+
right: 10px;
|
196 |
+
top: 10px;
|
197 |
+
color: white;
|
198 |
+
cursor: pointer;
|
199 |
+
font-size: 20px;
|
200 |
+
}
|
201 |
+
.action_menu {
|
202 |
+
z-index: 1;
|
203 |
+
position: absolute;
|
204 |
+
padding: 15px 0;
|
205 |
+
background-color: rgba(0,0,0,0.5);
|
206 |
+
color: white;
|
207 |
+
border-radius: 15px;
|
208 |
+
top: 30px;
|
209 |
+
right: 15px;
|
210 |
+
display: none;
|
211 |
+
}
|
212 |
+
.action_menu ul {
|
213 |
+
list-style: none;
|
214 |
+
padding: 0;
|
215 |
+
margin: 0;
|
216 |
+
}
|
217 |
+
.action_menu ul li {
|
218 |
+
width: 100%;
|
219 |
+
padding: 10px 15px;
|
220 |
+
margin-bottom: 5px;
|
221 |
+
}
|
222 |
+
.action_menu ul li i {
|
223 |
+
padding-right: 10px;
|
224 |
+
}
|
225 |
+
.action_menu ul li:hover {
|
226 |
+
cursor: pointer;
|
227 |
+
background-color: rgba(0,0,0,0.2);
|
228 |
+
}
|
229 |
+
@media(max-width: 576px) {
|
230 |
+
.contacts_card {
|
231 |
+
margin-bottom: 15px !important;
|
232 |
+
}
|
233 |
+
}
|
234 |
+
</style>
|
235 |
+
</head>
|
236 |
+
<body>
|
237 |
+
<div class="container-fluid h-100">
|
238 |
+
<div class="row justify-content-center h-100">
|
239 |
+
<div class="col-md-8 col-xl-6 chat">
|
240 |
+
<div class="card">
|
241 |
+
<div class="card-header msg_head">
|
242 |
+
<div class="d-flex bd-highlight">
|
243 |
+
<div class="img_cont">
|
244 |
+
<img src="https://img.freepik.com/premium-photo/3d-render-robot-airport-terminal-3d-illustration_847439-956.jpg?w=740" class="rounded-circle user_img">
|
245 |
+
<span class="online_icon"></span>
|
246 |
+
</div>
|
247 |
+
<div class="user_info">
|
248 |
+
<span>VoyageVirtuoso</span>
|
249 |
+
<p>Welcome to VoyageVirtuoso, your AI wildlife guide. Explore nature with accurate information and insights about wildlife and habitats. Ask any question about wildlife and nature, and VoyageVirtuoso will provide factual and informative answers to enrich your journey.</p>
|
250 |
+
</div>
|
251 |
+
</div>
|
252 |
+
</div>
|
253 |
+
<div id="messageFormeight" class="card-body msg_card_body">
|
254 |
+
</div>
|
255 |
+
<div class="card-footer">
|
256 |
+
<form id="messageArea" class="input-group">
|
257 |
+
<input type="text" id="text" name="msg" placeholder="What would you like to know?" autocomplete="off" class="form-control type_msg" required/>
|
258 |
+
<div class="input-group-append">
|
259 |
+
<button type="submit" id="send" class="input-group-text send_btn"><i class="fas fa-location-arrow"></i></button>
|
260 |
+
</div>
|
261 |
+
</form>
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
</div>
|
265 |
+
</div>
|
266 |
+
</div>
|
267 |
+
|
268 |
+
<script>
|
269 |
+
$(document).ready(function() {
|
270 |
+
$("#messageArea").on("submit", function(event) {
|
271 |
+
const date = new Date();
|
272 |
+
const hour = date.getHours();
|
273 |
+
const minute = date.getMinutes();
|
274 |
+
const str_time = hour+":"+minute;
|
275 |
+
var rawText = $("#text").val();
|
276 |
+
var userHtml = '<div class="d-flex justify-content-end mb-4"><div class="msg_cotainer_send">' + rawText + '<span class="msg_time_send">'+ str_time + '</span></div><div class="img_cont_msg"><img src="https://img.freepik.com/free-photo/portrait-young-student-education-day_23-2150980074.jpg?t=st=1718541721~exp=1718545321~hmac=1560e26c9f7f8c5266c235dae982fd2b0e13267f24035f0a6e5fa545436a99d9&w=826" class="rounded-circle user_img_msg"></div></div>';
|
277 |
+
|
278 |
+
$("#text").val("");
|
279 |
+
$("#messageFormeight").append(userHtml);
|
280 |
+
$.ajax({
|
281 |
+
data: {
|
282 |
+
msg: rawText,
|
283 |
+
},
|
284 |
+
type: "POST",
|
285 |
+
url: "/get",
|
286 |
+
}).done(function(data) {
|
287 |
+
var botHtml = '<div class="d-flex justify-content-start mb-4"><div class="img_cont_msg"><img src="https://www.prdistribution.com/spirit/uploads/pressreleases/2019/newsreleases/--waterless-geothermal-gets-installed-in-historic-breakers-mansion-.png" class="rounded-circle user_img_msg"></div><div class="msg_cotainer">' + data + '<span class="msg_time">' + str_time + '</span></div></div>';
|
288 |
+
$("#messageFormeight").append($.parseHTML(botHtml));
|
289 |
+
});
|
290 |
+
event.preventDefault();
|
291 |
+
});
|
292 |
+
});
|
293 |
+
</script>
|
294 |
+
</body>
|
295 |
+
</html>
|