Spaces:
Build error
Build error
Upload 9 files
Browse files- .gitignore +50 -0
- Ingest.py +61 -0
- README.md +92 -10
- apna-lawyer-firebase-adminsdk-fbsvc-4171a65364.json +13 -0
- app.py +896 -0
- firebase_config.py +14 -0
- footer.py +68 -0
- key.env +1 -0
- requirements.txt +22 -0
.gitignore
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
.env
|
| 3 |
+
*.pyc
|
| 4 |
+
__pycache__/
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
build/
|
| 8 |
+
develop-eggs/
|
| 9 |
+
dist/
|
| 10 |
+
downloads/
|
| 11 |
+
eggs/
|
| 12 |
+
.eggs/
|
| 13 |
+
lib/
|
| 14 |
+
lib64/
|
| 15 |
+
parts/
|
| 16 |
+
sdist/
|
| 17 |
+
var/
|
| 18 |
+
wheels/
|
| 19 |
+
*.egg-info/
|
| 20 |
+
.installed.cfg
|
| 21 |
+
*.egg
|
| 22 |
+
|
| 23 |
+
# Environments
|
| 24 |
+
.env
|
| 25 |
+
.venv
|
| 26 |
+
env/
|
| 27 |
+
venv/
|
| 28 |
+
ENV/
|
| 29 |
+
env.bak/
|
| 30 |
+
venv.bak/
|
| 31 |
+
|
| 32 |
+
# Streamlit
|
| 33 |
+
.streamlit/
|
| 34 |
+
|
| 35 |
+
# Firebase
|
| 36 |
+
*.json
|
| 37 |
+
serviceAccountKey.json
|
| 38 |
+
|
| 39 |
+
# IDE
|
| 40 |
+
.vscode/
|
| 41 |
+
.idea/
|
| 42 |
+
*.swp
|
| 43 |
+
*.swo
|
| 44 |
+
|
| 45 |
+
# Temp files
|
| 46 |
+
*.tmp
|
| 47 |
+
*.temp
|
| 48 |
+
|
| 49 |
+
# Logs
|
| 50 |
+
*.log
|
Ingest.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ray
|
| 2 |
+
import logging
|
| 3 |
+
from langchain_community.document_loaders import DirectoryLoader
|
| 4 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
| 5 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 6 |
+
from langchain_community.vectorstores import FAISS
|
| 7 |
+
from faiss import IndexFlatL2 # Assuming using L2 distance for simplicity
|
| 8 |
+
|
| 9 |
+
# Initialize Ray
|
| 10 |
+
ray.init()
|
| 11 |
+
|
| 12 |
+
# Set up basic configuration for logging
|
| 13 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 14 |
+
|
| 15 |
+
# Load documents with logging
|
| 16 |
+
logging.info("Loading documents...")
|
| 17 |
+
loader = DirectoryLoader('data', glob="./*.txt") #ipc_law.txt file get loaded
|
| 18 |
+
documents = loader.load()
|
| 19 |
+
|
| 20 |
+
# Extract text from documents and split into manageable texts with logging
|
| 21 |
+
logging.info("Extracting and splitting texts from documents...")
|
| 22 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=200)
|
| 23 |
+
texts = []
|
| 24 |
+
for document in documents:
|
| 25 |
+
if hasattr(document, 'get_text'):
|
| 26 |
+
text_content = document.get_text() # Adjust according to actual method
|
| 27 |
+
else:
|
| 28 |
+
text_content = "" # Default to empty string if no text method is available
|
| 29 |
+
|
| 30 |
+
texts.extend(text_splitter.split_text(text_content))
|
| 31 |
+
|
| 32 |
+
# Define embedding function
|
| 33 |
+
def embedding_function(text):
|
| 34 |
+
embeddings_model = HuggingFaceEmbeddings(model_name="law-ai/InLegalBERT")
|
| 35 |
+
return embeddings_model.embed_query(text)
|
| 36 |
+
|
| 37 |
+
# Create FAISS index for embeddings
|
| 38 |
+
index = IndexFlatL2(768) # Dimension of embeddings, adjust as needed
|
| 39 |
+
|
| 40 |
+
# Assuming docstore as a simple dictionary to store document texts
|
| 41 |
+
docstore = {i: text for i, text in enumerate(texts)}
|
| 42 |
+
index_to_docstore_id = {i: i for i in range(len(texts))}
|
| 43 |
+
|
| 44 |
+
# Initialize FAISS
|
| 45 |
+
faiss_db = FAISS(embedding_function, index, docstore, index_to_docstore_id)
|
| 46 |
+
|
| 47 |
+
# Process and store embeddings
|
| 48 |
+
logging.info("Storing embeddings in FAISS...")
|
| 49 |
+
for i, text in enumerate(texts):
|
| 50 |
+
embedding = embedding_function(text)
|
| 51 |
+
faiss_db.add_documents([embedding])
|
| 52 |
+
|
| 53 |
+
# Exporting the vector embeddings database with logging
|
| 54 |
+
logging.info("Exporting the vector embeddings database...")
|
| 55 |
+
faiss_db.save_local("ipc_embed_db")
|
| 56 |
+
|
| 57 |
+
# Log a message to indicate the completion of the process
|
| 58 |
+
logging.info("Process completed successfully.")
|
| 59 |
+
|
| 60 |
+
# Shutdown Ray after the process
|
| 61 |
+
ray.shutdown()
|
README.md
CHANGED
|
@@ -1,13 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
# ApnaLaw: AI IPC Legal advice Assistant 📘
|
| 2 |
+
|
| 3 |
+
ApnaLaw is a sophisticated legal advisory chatbot focused on providing detailed and contextually accurate responses about the Indian Penal Code. It utilizes a powerful combination of machine learning technologies to efficiently process and retrieve legal information.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Features 🌟
|
| 8 |
+
|
| 9 |
+
- **Document Ingestion**: Automated processing of text documents to store legal information in a FAISS vector database.
|
| 10 |
+
- **Real-Time Interaction**: Real-time legal advice through a conversational interface built with Streamlit.
|
| 11 |
+
- **Legal Prompt Templating**: Structured prompt format ensuring clarity, detail, and legal accuracy in responses.
|
| 12 |
+
<br>
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
<h4><strong>🚀Blast off to discovery! Our project is waiting for you <a href= "https://huggingface.co/spaces/adarsh-maurya/ApnaLawyer">ApnaLaw</a>. Explore it today and elevate your understanding!🌟</strong><h4>
|
| 17 |
+
<br>
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
## Components 🛠️
|
| 22 |
+
|
| 23 |
+
### Ingestion Script (`Ingest.py`)
|
| 24 |
+
|
| 25 |
+
| Functionality | Description |
|
| 26 |
+
|----------------------|-------------|
|
| 27 |
+
| **Document Loading** | Loads text documents from a specified directory. |
|
| 28 |
+
| **Text Splitting** | Splits documents into manageable chunks for processing. |
|
| 29 |
+
| **Embedding Generation** | Utilizes `HuggingFace's InLegalBERT` to generate text embeddings. |
|
| 30 |
+
| **FAISS Database** | Indexes embeddings for fast and efficient retrieval. |
|
| 31 |
+
|
| 32 |
+
### Web Application (`app.py`)
|
| 33 |
+
|
| 34 |
+
| Feature | Description |
|
| 35 |
+
|-----------------------|-------------|
|
| 36 |
+
| **Streamlit Interface** | Provides a web interface for user interaction. |
|
| 37 |
+
| **Chat Functionality** | Manages conversational flow and stores chat history. |
|
| 38 |
+
| **Legal Information Retrieval** | Leverages FAISS index to fetch pertinent legal information based on queries.
|
| 39 |
+
|
| 40 |
---
|
| 41 |
+
|
| 42 |
+
## Setup 📦
|
| 43 |
+
|
| 44 |
+
### Prerequisites
|
| 45 |
+
|
| 46 |
+
- Python 3.8 or later
|
| 47 |
+
- ray
|
| 48 |
+
- langchain
|
| 49 |
+
- streamlit
|
| 50 |
+
- faiss
|
| 51 |
+
|
| 52 |
+
### Installation
|
| 53 |
+
|
| 54 |
+
1. **Clone the repository:**
|
| 55 |
+
```bash
|
| 56 |
+
git clone https://github.com/your-repo/ApnaLaw.git
|
| 57 |
+
cd ApnaLaw
|
| 58 |
+
```
|
| 59 |
+
2. **Install dependencies:**
|
| 60 |
+
```bash
|
| 61 |
+
pip install -r requirements.txt
|
| 62 |
+
```
|
| 63 |
+
3. **Set up the Together AI API Key:**
|
| 64 |
+
Obtain an API key from <a href="https://api.together.xyz/">Together AI</a>.
|
| 65 |
+
Sign up with Together AI today and get $25 worth of free credit! 🎉 Whether you choose to use it for a short-term project or opt for a long-term commitment, Together AI offers cost-effective solutions compared to the OpenAI API. 🚀 <br><br>
|
| 66 |
+
**> To set this API key as an environment variable on any OS, you can use the following approach:**
|
| 67 |
+
- On macOS and Linux:
|
| 68 |
+
```bash
|
| 69 |
+
echo "export TOGETHER_API_KEY='Your-API-Key-Here'" >> ~/.bash_profile
|
| 70 |
+
source ~/.bash_profile
|
| 71 |
+
```
|
| 72 |
+
- On Windows (using Command Prompt):
|
| 73 |
+
```cmd
|
| 74 |
+
setx TOGETHER_API_KEY "Your-API-Key-Here"
|
| 75 |
+
```
|
| 76 |
+
- On Windows (using PowerShell):
|
| 77 |
+
```powershell
|
| 78 |
+
[Environment]::SetEnvironmentVariable("TOGETHER_API_KEY", "Your-API-Key-Here", "User")
|
| 79 |
+
```
|
| 80 |
+
This key is crucial for the chatbot to access language model functionalities provided by Together AI.
|
| 81 |
+
|
| 82 |
+
## Running the Application
|
| 83 |
+
1. **Run the ingestion script to prepare the data:**
|
| 84 |
+
```bash
|
| 85 |
+
python ingest.py
|
| 86 |
+
```
|
| 87 |
+
2. Launch the Streamlit application:
|
| 88 |
+
```bash
|
| 89 |
+
streamlit run app.py
|
| 90 |
+
```
|
| 91 |
---
|
| 92 |
|
| 93 |
+
## Usage 🔍
|
| 94 |
+
|
| 95 |
+
Navigate to the local URL provided by Streamlit to interact with the ApnaLaw chatbot. Enter your legal queries and receive precise information derived from the indexed legal documents. Utilize the chat interface to engage in a legal discussion and get accurate advice.<br>
|
apna-lawyer-firebase-adminsdk-fbsvc-4171a65364.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "service_account",
|
| 3 |
+
"project_id": "apna-lawyer",
|
| 4 |
+
"private_key_id": "4171a65364e11ca6ede460482c459105e6b13429",
|
| 5 |
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDimw6wj4ek0ENk\n/zyfFToTO6PRILg+RwUfq5sT9wyR4UenX/qIJn5MqJBhNzv04IAmHDxIbLJj00gC\n8OPlSQ2cYxDCYreAqUuKXur/MiLrfR7W6bqZKuhipNoVz4UNpFQLrxc5cYAS4FER\nvJnsqngRi7thd+6LdYhnHYrdCnWTJigPjw3wKgWj+ATTAJ0QfqMhzHMswmxZpisX\nrlNbDmwJOhtgKCwjSGHZ4/Hk78oBVSVzkBXMYpykbgAf8QDQQy9yBf0uIbVkOPXk\nCRqsM9w4nHuimjPDJglKR8pKc+DDf9NVfgvRczoZKiu6jDJ6nmffSKbpN2xaooP2\nAeTsHqslAgMBAAECggEARXYUTajhgnLYhcf7VCRUlfqtH3ixIV3xwyB+O+N5tenh\nZfv0tvFdu6/b+gDNXyXGAmibTfQLxq/IHiarJOh8xJhVVdCewVCjuDmaPTmoHJlQ\n773blgHiFALGWvMurNzXanF0ZAZXpPGfyBkQ8cvbXD2B5cG2nIcdlcYnf9Qp8D2p\nHgFM2OIZ8dmpmc+CpIVntC/qsNYKHKps2cm4xOi0FhC2S/g2M332aNiJd29lh0TF\nFkACRg5ls5K2SJt2wXAzSXIlcEHCoUTpQpg1+0Y3bbKT4qZRKIsox0HaOfDy1+Ww\neDt4km6ym7GjI++GODoitHJ9l3RZVYPuM5mR/nAeDQKBgQD+cj1t0V+2OZ2VlHqo\nv/azmysyXMNNsvbl8t4npkGekugthtwW0mGBODpHHRoQlnGI/xbdaRVX6dI7ktUh\noYqGQRjysau68Boe7isc2c7F6ClDaLEIW+wEfH0yEuBHPXlmqRo+DOiRi0KWnGCm\ng9fCkJIq2+F+x8vA09sOpVxLAwKBgQDj/UvHNuAxUP/F5IDTZfOtZLZLPVmmE+0R\nh99KN+jjjJxR3/5inuBAkMDKzgrnCvQsQ0U70+ffiOrHLBjvrG85jThXsVFHcry/\ncU4wV8N+Rm8Ljt4mJ57txXzKZx6bNSY9ZZN93lfmv9Rn7D8EOcALqrrKk7Oh1EW4\nwOxc/FMEtwKBgQDxMQMv1ReAGa9Z+ewniAfnir/wtmPfhDRsFsMlHID5OtJhTYPV\nEkmg/tanUFvDu4gVz5AyAwlbU2aYWAA2J/Wye4SVkXty8WQhS6yMZZ6OlRqALn4Z\nqWDZg9P9Ik182jX47XTVutC+Hh7Zu5QWY8WjRf14KQPgdK2ctHXitTb+VwKBgGJB\nQ2szhyM52UEb3Tk98uqDQNzkL8KXS9AGUoDV35RRgPz4H9W4ysSInc3JRoGUAu8g\nfrHt/Twk8amso4KHOdf/uIxyaqj0FcwbtKq46BN/n1PH2o1u/dtTBRjloBcbrMNB\nB3NzY0aa3Zt8ARx9FvrwNVggl4Xiybl5y2O3ir+5AoGBAIHuvfs/t6F0/S1SQgVf\nTpN/y88/klytRvaYJhvp2FFQ2p5z1WZQBDlqiueJLnQ5xcvMDsxhX+KufDR0u0nm\nEwujQBx/4qTzyy1Df8qcW6nEL+E/ftQKI5sZxdyR5PGmQL9tddpORweuuOA1H3UN\ncxNKyn5YfYHTxabURRGA+1et\n-----END PRIVATE KEY-----\n",
|
| 6 |
+
"client_email": "[email protected]",
|
| 7 |
+
"client_id": "109468658206929239872",
|
| 8 |
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
| 9 |
+
"token_uri": "https://oauth2.googleapis.com/token",
|
| 10 |
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
| 11 |
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40apna-lawyer.iam.gserviceaccount.com",
|
| 12 |
+
"universe_domain": "googleapis.com"
|
| 13 |
+
}
|
app.py
ADDED
|
@@ -0,0 +1,896 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
import os
|
| 3 |
+
import streamlit as st
|
| 4 |
+
from langchain_community.vectorstores import FAISS
|
| 5 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
| 6 |
+
from langchain.prompts import PromptTemplate
|
| 7 |
+
from langchain.memory import ConversationBufferWindowMemory
|
| 8 |
+
from langchain.chains import ConversationalRetrievalChain
|
| 9 |
+
from langchain_together import Together
|
| 10 |
+
from footer import footer
|
| 11 |
+
from firebase_config import firebaseConfig
|
| 12 |
+
import pyrebase
|
| 13 |
+
# Import firebase_admin and credentials
|
| 14 |
+
import firebase_admin
|
| 15 |
+
from firebase_admin import credentials, initialize_app, db as firebase_db
|
| 16 |
+
|
| 17 |
+
# Add at the top of your file
|
| 18 |
+
import streamlit.components.v1 as components
|
| 19 |
+
import hashlib
|
| 20 |
+
import urllib.parse
|
| 21 |
+
from deep_translator import GoogleTranslator
|
| 22 |
+
from langdetect import detect
|
| 23 |
+
|
| 24 |
+
from transformers import pipeline
|
| 25 |
+
import soundfile as sf
|
| 26 |
+
import numpy as np
|
| 27 |
+
from io import BytesIO
|
| 28 |
+
import tempfile
|
| 29 |
+
# # Speech Recognition Imports (add these at top of file)
|
| 30 |
+
# import tempfile
|
| 31 |
+
import openai
|
| 32 |
+
from dotenv import load_dotenv
|
| 33 |
+
load_dotenv()
|
| 34 |
+
openai.api_key = os.getenv('OPENAI_API_KEY')
|
| 35 |
+
|
| 36 |
+
# Function to translate text
|
| 37 |
+
def translate_text(text, target_language):
|
| 38 |
+
try:
|
| 39 |
+
# Use GoogleTranslator from deep-translator
|
| 40 |
+
translated_text = GoogleTranslator(source='auto', target=target_language).translate(text)
|
| 41 |
+
return translated_text
|
| 42 |
+
except Exception as e:
|
| 43 |
+
return f"⚠ Translation failed: {str(e)}"
|
| 44 |
+
|
| 45 |
+
# Initialize the translator
|
| 46 |
+
#translator = Translator()
|
| 47 |
+
|
| 48 |
+
# Initialize Firebase (you would call this at the start of your app)
|
| 49 |
+
def initialize_firebase():
|
| 50 |
+
try:
|
| 51 |
+
if not firebase_admin._apps:
|
| 52 |
+
cred = credentials.Certificate("apna-lawyer-firebase-adminsdk-fbsvc-4171a65364.json")
|
| 53 |
+
firebase_app = initialize_app(cred, {
|
| 54 |
+
'databaseURL': firebaseConfig['databaseURL']
|
| 55 |
+
})
|
| 56 |
+
except Exception as e:
|
| 57 |
+
st.error(f"Firebase initialization error: {str(e)}")
|
| 58 |
+
|
| 59 |
+
# ----------------- Firebase Init -------------------
|
| 60 |
+
firebase = pyrebase.initialize_app(firebaseConfig)
|
| 61 |
+
auth = firebase.auth()
|
| 62 |
+
initialize_firebase()
|
| 63 |
+
|
| 64 |
+
# ----------------- Streamlit Config -------------------
|
| 65 |
+
st.set_page_config(page_title="ApnaLawyer", layout="centered")
|
| 66 |
+
|
| 67 |
+
col1, col2, col3 = st.columns([1, 30, 1])
|
| 68 |
+
with col2:
|
| 69 |
+
st.image("images/banner.jpg", use_container_width=True)
|
| 70 |
+
|
| 71 |
+
st.markdown("""
|
| 72 |
+
<style>
|
| 73 |
+
#MainMenu {visibility: hidden;}
|
| 74 |
+
footer {visibility: hidden;}
|
| 75 |
+
</style>
|
| 76 |
+
""", unsafe_allow_html=True)
|
| 77 |
+
|
| 78 |
+
# ----------------- Login/Signup Interface -------------------
|
| 79 |
+
import json
|
| 80 |
+
import requests
|
| 81 |
+
from streamlit.components.v1 import html
|
| 82 |
+
|
| 83 |
+
# Custom CSS for the enhanced UI
|
| 84 |
+
def inject_custom_css():
|
| 85 |
+
st.markdown("""
|
| 86 |
+
<style>
|
| 87 |
+
/* Main container styles */
|
| 88 |
+
.stApp {
|
| 89 |
+
background: radial-gradient(circle at top left, #0f0f0f, #050505);
|
| 90 |
+
color: white;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
/* Sidebar styles */
|
| 94 |
+
[data-testid="stSidebar"] {
|
| 95 |
+
background: rgba(255, 255, 255, 0.05) !important;
|
| 96 |
+
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
| 97 |
+
backdrop-filter: blur(12px) !important;
|
| 98 |
+
box-shadow: 0 0 30px rgba(0, 255, 255, 0.2) !important;
|
| 99 |
+
border-radius: 20px !important;
|
| 100 |
+
padding: 30px !important;
|
| 101 |
+
margin: 20px !important;
|
| 102 |
+
animation: fadeIn 1s ease !important;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
@keyframes fadeIn {
|
| 106 |
+
from { opacity: 0; transform: translateY(20px); }
|
| 107 |
+
to { opacity: 1; transform: translateY(0); }
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
/* Input field styles */
|
| 111 |
+
.stTextInput>div>div>input, .stPassword>div>div>input {
|
| 112 |
+
background: rgba(255, 255, 255, 0.1) !important;
|
| 113 |
+
color: white !important;
|
| 114 |
+
border: 1px solid transparent !important;
|
| 115 |
+
border-radius: 10px !important;
|
| 116 |
+
padding: 12px 20px !important;
|
| 117 |
+
transition: all 0.3s ease !important;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
.stTextInput>div>div>input:focus, .stPassword>div>div>input:focus {
|
| 121 |
+
border-color: #00ffff !important;
|
| 122 |
+
background: rgba(255, 255, 255, 0.15) !important;
|
| 123 |
+
box-shadow: 0 0 10px rgba(0, 255, 255, 0.2) !important;
|
| 124 |
+
outline: none !important;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
/* Button styles */
|
| 128 |
+
.stButton>button {
|
| 129 |
+
width: 100% !important;
|
| 130 |
+
padding: 12px 20px !important;
|
| 131 |
+
border-radius: 10px !important;
|
| 132 |
+
background: linear-gradient(45deg, #00ffff, #007fff) !important;
|
| 133 |
+
color: black !important;
|
| 134 |
+
font-weight: 600 !important;
|
| 135 |
+
border: none !important;
|
| 136 |
+
transition: all 0.3s ease !important;
|
| 137 |
+
box-shadow: 0 4px 15px rgba(0, 255, 255, 0.3) !important;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
.stButton>button:hover {
|
| 141 |
+
transform: translateY(-2px) !important;
|
| 142 |
+
box-shadow: 0 6px 20px rgba(0, 255, 255, 0.4) !important;
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/* Radio button styles */
|
| 146 |
+
.stRadio>div {
|
| 147 |
+
flex-direction: column !important;
|
| 148 |
+
gap: 10px !important;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
.stRadio>div>label {
|
| 152 |
+
color: white !important;
|
| 153 |
+
font-weight: 500 !important;
|
| 154 |
+
padding: 8px 12px !important;
|
| 155 |
+
border-radius: 8px !important;
|
| 156 |
+
background: rgba(255, 255, 255, 0.05) !important;
|
| 157 |
+
transition: all 0.3s ease !important;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.stRadio>div>label:hover {
|
| 161 |
+
background: rgba(255, 255, 255, 0.1) !important;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.stRadio>div>label[data-baseweb="radio"]>div:first-child {
|
| 165 |
+
border-color: #00ffff !important;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/* Link styles */
|
| 169 |
+
a {
|
| 170 |
+
color: #00ffff !important;
|
| 171 |
+
text-decoration: none !important;
|
| 172 |
+
transition: all 0.3s ease !important;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
a:hover {
|
| 176 |
+
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5) !important;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/* Error message styles */
|
| 180 |
+
.stAlert {
|
| 181 |
+
border-radius: 10px !important;
|
| 182 |
+
background: rgba(255, 0, 0, 0.1) !important;
|
| 183 |
+
border: 1px solid rgba(255, 0, 0, 0.2) !important;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
/* Success message styles */
|
| 187 |
+
.stSuccess {
|
| 188 |
+
border-radius: 10px !important;
|
| 189 |
+
background: rgba(0, 255, 0, 0.1) !important;
|
| 190 |
+
border: 1px solid rgba(0, 255, 0, 0.2) !important;
|
| 191 |
+
}
|
| 192 |
+
/* Audio recording animation */
|
| 193 |
+
@keyframes pulse {
|
| 194 |
+
0% { transform: scale(1); }
|
| 195 |
+
50% { transform: scale(1.1); }
|
| 196 |
+
100% { transform: scale(1); }
|
| 197 |
+
}
|
| 198 |
+
.recording-active {
|
| 199 |
+
animation: pulse 1.5s infinite;
|
| 200 |
+
color: #ff4b4b !important;
|
| 201 |
+
}
|
| 202 |
+
</style>
|
| 203 |
+
""", unsafe_allow_html=True)
|
| 204 |
+
|
| 205 |
+
def store_user_data(user_id, name, email):
|
| 206 |
+
try:
|
| 207 |
+
ref = firebase_db.reference(f'users/{user_id}')
|
| 208 |
+
ref.set({
|
| 209 |
+
'name': name,
|
| 210 |
+
'email': email,
|
| 211 |
+
'created_at': time.time()
|
| 212 |
+
})
|
| 213 |
+
return True
|
| 214 |
+
except Exception as e:
|
| 215 |
+
st.error(f"Error storing user data: {str(e)}")
|
| 216 |
+
return False
|
| 217 |
+
|
| 218 |
+
# Add these Firebase functions right after your existing Firebase functions
|
| 219 |
+
def save_chat_to_history(user_id, chat_title, messages):
|
| 220 |
+
try:
|
| 221 |
+
ref = firebase_db.reference(f'users/{user_id}/chats')
|
| 222 |
+
new_chat_ref = ref.push()
|
| 223 |
+
new_chat_ref.set({
|
| 224 |
+
'title': chat_title,
|
| 225 |
+
'messages': messages,
|
| 226 |
+
'timestamp': time.time(),
|
| 227 |
+
'last_updated': time.time()
|
| 228 |
+
})
|
| 229 |
+
return new_chat_ref.key
|
| 230 |
+
except Exception as e:
|
| 231 |
+
st.error(f"Error saving chat history: {str(e)}")
|
| 232 |
+
return None
|
| 233 |
+
|
| 234 |
+
def update_chat_history(user_id, chat_id, messages):
|
| 235 |
+
try:
|
| 236 |
+
ref = firebase_db.reference(f'users/{user_id}/chats/{chat_id}')
|
| 237 |
+
ref.update({
|
| 238 |
+
'messages': messages,
|
| 239 |
+
'last_updated': time.time()
|
| 240 |
+
})
|
| 241 |
+
return True
|
| 242 |
+
except Exception as e:
|
| 243 |
+
st.error(f"Error updating chat history: {str(e)}")
|
| 244 |
+
return False
|
| 245 |
+
|
| 246 |
+
def get_chat_history(user_id):
|
| 247 |
+
try:
|
| 248 |
+
ref = firebase_db.reference(f'users/{user_id}/chats')
|
| 249 |
+
chats = ref.get()
|
| 250 |
+
if chats:
|
| 251 |
+
return sorted(
|
| 252 |
+
[(chat_id, chat_data) for chat_id, chat_data in chats.items()],
|
| 253 |
+
key=lambda x: x[1]['last_updated'],
|
| 254 |
+
reverse=True
|
| 255 |
+
)
|
| 256 |
+
return []
|
| 257 |
+
except Exception as e:
|
| 258 |
+
st.error(f"Error fetching chat history: {str(e)}")
|
| 259 |
+
return []
|
| 260 |
+
|
| 261 |
+
def delete_chat_history(user_id, chat_id):
|
| 262 |
+
try:
|
| 263 |
+
ref = firebase_db.reference(f'users/{user_id}/chats/{chat_id}')
|
| 264 |
+
ref.delete()
|
| 265 |
+
return True
|
| 266 |
+
except Exception as e:
|
| 267 |
+
st.error(f"Error deleting chat history: {str(e)}")
|
| 268 |
+
return False
|
| 269 |
+
|
| 270 |
+
def generate_chat_title(messages):
|
| 271 |
+
for message in messages:
|
| 272 |
+
if message['role'] == 'user':
|
| 273 |
+
user_message = message['content']
|
| 274 |
+
return user_message[:30] + "..." if len(user_message) > 30 else user_message
|
| 275 |
+
return "New Chat"
|
| 276 |
+
|
| 277 |
+
def get_user_name(user_id):
|
| 278 |
+
try:
|
| 279 |
+
ref = firebase_db.reference(f'users/{user_id}')
|
| 280 |
+
user_data = ref.get()
|
| 281 |
+
return user_data.get('name', 'User') if user_data else 'User'
|
| 282 |
+
except Exception as e:
|
| 283 |
+
st.error(f"Error fetching user data: {str(e)}")
|
| 284 |
+
return 'User'
|
| 285 |
+
|
| 286 |
+
# Use the pyrebase auth object for login/signup
|
| 287 |
+
def login_signup_ui():
|
| 288 |
+
inject_custom_css()
|
| 289 |
+
|
| 290 |
+
st.sidebar.markdown("""
|
| 291 |
+
<div style="text-align: center; margin-bottom: 30px;">
|
| 292 |
+
<h1 style="color: #00ffff; font-size: 28px; margin-bottom: 5px;">🔐 ApnaLawyer</h1>
|
| 293 |
+
<p style="color: rgba(255,255,255,0.7); font-size: 14px;">Secure Legal Assistance Portal</p>
|
| 294 |
+
</div>
|
| 295 |
+
""", unsafe_allow_html=True)
|
| 296 |
+
|
| 297 |
+
choice = st.sidebar.radio("Select Option", ["Login", "Signup", "Forgot Password"], label_visibility="collapsed")
|
| 298 |
+
|
| 299 |
+
if choice == "Signup":
|
| 300 |
+
st.sidebar.markdown("### Create New Account")
|
| 301 |
+
name = st.sidebar.text_input("Full Name", key="signup_name")
|
| 302 |
+
email = st.sidebar.text_input("Email Address", key="signup_email")
|
| 303 |
+
password = st.sidebar.text_input("Password", type="password", key="signup_password")
|
| 304 |
+
confirm_password = st.sidebar.text_input("Confirm Password", type="password", key="signup_confirm")
|
| 305 |
+
|
| 306 |
+
if st.sidebar.button("Create Account", key="signup_button"):
|
| 307 |
+
if not name:
|
| 308 |
+
st.sidebar.error("Please enter your full name!")
|
| 309 |
+
elif not email:
|
| 310 |
+
st.sidebar.error("Email address is required!")
|
| 311 |
+
elif not password or not confirm_password:
|
| 312 |
+
st.sidebar.error("Password fields cannot be empty!")
|
| 313 |
+
elif password != confirm_password:
|
| 314 |
+
st.sidebar.error("Passwords do not match!")
|
| 315 |
+
else:
|
| 316 |
+
try:
|
| 317 |
+
user = auth.create_user_with_email_and_password(email, password)
|
| 318 |
+
if store_user_data(user['localId'], name, email):
|
| 319 |
+
auth.send_email_verification(user['idToken'])
|
| 320 |
+
st.sidebar.success("✅ Account created! Please verify your email before logging in.")
|
| 321 |
+
except Exception as e:
|
| 322 |
+
error_str = str(e)
|
| 323 |
+
if "EMAIL_EXISTS" in error_str:
|
| 324 |
+
st.sidebar.warning("⚠ Email already exists. Please try a different email address.")
|
| 325 |
+
elif "WEAK_PASSWORD" in error_str:
|
| 326 |
+
st.sidebar.warning("⚠ Password should be at least 6 characters long.")
|
| 327 |
+
else:
|
| 328 |
+
st.sidebar.error(f"Error: {error_str}")
|
| 329 |
+
|
| 330 |
+
elif choice == "Login":
|
| 331 |
+
st.sidebar.markdown("### Welcome Back")
|
| 332 |
+
email = st.sidebar.text_input("Email Address", key="login_email")
|
| 333 |
+
password = st.sidebar.text_input("Password", type="password", key="login_password")
|
| 334 |
+
|
| 335 |
+
if st.sidebar.button("Login", key="login_button"):
|
| 336 |
+
if not email:
|
| 337 |
+
st.sidebar.error("✋ Please enter your email address")
|
| 338 |
+
elif not password:
|
| 339 |
+
st.sidebar.error("✋ Please enter your password")
|
| 340 |
+
else:
|
| 341 |
+
try:
|
| 342 |
+
user = auth.sign_in_with_email_and_password(email, password)
|
| 343 |
+
user_info = auth.get_account_info(user['idToken'])
|
| 344 |
+
email_verified = user_info['users'][0]['emailVerified']
|
| 345 |
+
|
| 346 |
+
if email_verified:
|
| 347 |
+
user_name = get_user_name(user['localId'])
|
| 348 |
+
st.session_state.logged_in = True
|
| 349 |
+
st.session_state.user_email = email
|
| 350 |
+
st.session_state.user_token = user['idToken']
|
| 351 |
+
st.session_state.user_name = user_name
|
| 352 |
+
st.sidebar.success(f"🎉 Welcome back, {user_name}!")
|
| 353 |
+
st.rerun()
|
| 354 |
+
else:
|
| 355 |
+
st.sidebar.warning("📧 Email not verified. Please check your inbox.")
|
| 356 |
+
if st.sidebar.button("🔁 Resend Verification Email", key="resend_verification"):
|
| 357 |
+
auth.send_email_verification(user['idToken'])
|
| 358 |
+
st.sidebar.info("📬 Verification email sent again!")
|
| 359 |
+
except Exception as e:
|
| 360 |
+
error_str = str(e)
|
| 361 |
+
if "EMAIL_NOT_FOUND" in error_str or "no user record" in error_str.lower():
|
| 362 |
+
st.sidebar.error("📭 Account not found")
|
| 363 |
+
st.sidebar.warning("Don't have an account? Please sign up.")
|
| 364 |
+
elif "INVALID_PASSWORD" in error_str or "INVALID_LOGIN_CREDENTIALS" in error_str:
|
| 365 |
+
st.sidebar.error("🔐 Incorrect password")
|
| 366 |
+
else:
|
| 367 |
+
st.sidebar.error("⚠ Login error. Please try again.")
|
| 368 |
+
|
| 369 |
+
elif choice == "Forgot Password":
|
| 370 |
+
st.sidebar.markdown("### Reset Your Password")
|
| 371 |
+
email = st.sidebar.text_input("Enter your email address", key="reset_email")
|
| 372 |
+
|
| 373 |
+
if st.sidebar.button("Send Reset Link", key="reset_button"):
|
| 374 |
+
if not email:
|
| 375 |
+
st.sidebar.error("Please enter your email address!")
|
| 376 |
+
else:
|
| 377 |
+
try:
|
| 378 |
+
auth.send_password_reset_email(email)
|
| 379 |
+
st.sidebar.success("📬 Password reset link sent to your email.")
|
| 380 |
+
except Exception as e:
|
| 381 |
+
error_str = str(e)
|
| 382 |
+
if "EMAIL_NOT_FOUND" in error_str:
|
| 383 |
+
st.sidebar.error("❌ No account found with this email address")
|
| 384 |
+
st.sidebar.warning("⚠ Don't have an account? Please sign up.")
|
| 385 |
+
else:
|
| 386 |
+
st.sidebar.error(f"Error: {error_str}")
|
| 387 |
+
|
| 388 |
+
# In your login_signup_ui() function, replace the Google login section with:
|
| 389 |
+
st.sidebar.markdown("---")
|
| 390 |
+
st.sidebar.markdown("### Or continue with")
|
| 391 |
+
|
| 392 |
+
if st.sidebar.button("Continue with Google", key="google_login"):
|
| 393 |
+
try:
|
| 394 |
+
# Generate a unique state token
|
| 395 |
+
state_token = hashlib.sha256(str(time.time()).encode()).hexdigest()
|
| 396 |
+
|
| 397 |
+
# Get Firebase config values
|
| 398 |
+
client_id = "546645596018-nvtkegm7mi8e83upfv771tv6t58c7snn.apps.googleusercontent.com" # Use actual OAuth client ID
|
| 399 |
+
firebase_auth_domain = firebaseConfig["authDomain"]
|
| 400 |
+
redirect_uri = f"https://{firebase_auth_domain}/auth/handler"
|
| 401 |
+
|
| 402 |
+
# Build the Google OAuth URL
|
| 403 |
+
auth_url = (
|
| 404 |
+
f"https://accounts.google.com/o/oauth2/v2/auth?"
|
| 405 |
+
f"response_type=code&"
|
| 406 |
+
f"client_id={client_id}&"
|
| 407 |
+
f"redirect_uri={urllib.parse.quote(redirect_uri)}&"
|
| 408 |
+
f"scope=email%20profile%20openid&"
|
| 409 |
+
f"state={state_token}"
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
# Store state token in session
|
| 413 |
+
st.session_state.oauth_state = state_token
|
| 414 |
+
|
| 415 |
+
# Open OAuth flow in new tab
|
| 416 |
+
components.html(
|
| 417 |
+
f"""
|
| 418 |
+
<script>
|
| 419 |
+
window.open('{auth_url}', '_blank').focus();
|
| 420 |
+
</script>
|
| 421 |
+
""",
|
| 422 |
+
height=0
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
st.sidebar.info("Google login window should open. Please allow popups if it doesn't appear.")
|
| 426 |
+
|
| 427 |
+
except Exception as e:
|
| 428 |
+
st.sidebar.error(f"Failed to start Google login: {str(e)}")
|
| 429 |
+
|
| 430 |
+
def check_google_callback():
|
| 431 |
+
try:
|
| 432 |
+
if 'code' in st.query_params and 'state' in st.query_params:
|
| 433 |
+
# Verify state token matches
|
| 434 |
+
if st.query_params['state'] != st.session_state.get('oauth_state'):
|
| 435 |
+
st.error("Security verification failed. Please try logging in again.")
|
| 436 |
+
return
|
| 437 |
+
|
| 438 |
+
auth_code = st.query_params['code']
|
| 439 |
+
|
| 440 |
+
# Initialize the Google Auth Provider
|
| 441 |
+
provider = firebase.auth.GoogleAuthProvider()
|
| 442 |
+
|
| 443 |
+
# Sign in with the auth code
|
| 444 |
+
credential = provider.credential(
|
| 445 |
+
None, # No ID token needed for code flow
|
| 446 |
+
auth_code
|
| 447 |
+
)
|
| 448 |
+
|
| 449 |
+
# Sign in with credential
|
| 450 |
+
user = auth.sign_in_with_credential(credential)
|
| 451 |
+
|
| 452 |
+
# Store user in session
|
| 453 |
+
st.session_state.logged_in = True
|
| 454 |
+
st.session_state.user_email = user['email']
|
| 455 |
+
st.session_state.user_name = user.get('displayName', 'Google User')
|
| 456 |
+
st.session_state.user_token = user['idToken']
|
| 457 |
+
|
| 458 |
+
# Store user data if new
|
| 459 |
+
if not user_exists(user['localId']):
|
| 460 |
+
store_user_data(
|
| 461 |
+
user['localId'],
|
| 462 |
+
st.session_state.user_name,
|
| 463 |
+
user['email']
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
# Clear the OAuth code from URL
|
| 467 |
+
st.query_params.clear()
|
| 468 |
+
st.rerun()
|
| 469 |
+
|
| 470 |
+
except Exception as e:
|
| 471 |
+
st.error(f"Google login failed: {str(e)}")
|
| 472 |
+
|
| 473 |
+
# Initialize speech-to-text model (cached to avoid reloading)
|
| 474 |
+
@st.cache_resource
|
| 475 |
+
def load_speech_to_text_model():
|
| 476 |
+
return pipeline("automatic-speech-recognition", model="openai/whisper-base")
|
| 477 |
+
|
| 478 |
+
# Function to translate text
|
| 479 |
+
def translate_text(text, target_language):
|
| 480 |
+
try:
|
| 481 |
+
translated_text = GoogleTranslator(source='auto', target=target_language).translate(text)
|
| 482 |
+
return translated_text
|
| 483 |
+
except Exception as e:
|
| 484 |
+
return f"⚠ Translation failed: {str(e)}"
|
| 485 |
+
|
| 486 |
+
def transcribe_audio(audio_bytes):
|
| 487 |
+
"""Transcribe audio using Whisper API"""
|
| 488 |
+
try:
|
| 489 |
+
# Create temp file
|
| 490 |
+
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
|
| 491 |
+
tmpfile.write(audio_bytes)
|
| 492 |
+
tmp_path = tmpfile.name
|
| 493 |
+
|
| 494 |
+
# Transcribe with Whisper
|
| 495 |
+
with open(tmp_path, "rb") as audio_file:
|
| 496 |
+
transcript = openai.audio.transcriptions.create(
|
| 497 |
+
model="whisper-1",
|
| 498 |
+
file=audio_file,
|
| 499 |
+
language=st.session_state.get('language', 'en')
|
| 500 |
+
)
|
| 501 |
+
|
| 502 |
+
# Clean up temp file
|
| 503 |
+
try:
|
| 504 |
+
os.remove(tmp_path)
|
| 505 |
+
except:
|
| 506 |
+
pass # Don't fail if temp file deletion fails
|
| 507 |
+
|
| 508 |
+
return transcript['text']
|
| 509 |
+
|
| 510 |
+
except Exception as e:
|
| 511 |
+
st.error(f"Error transcribing audio: {str(e)}")
|
| 512 |
+
return None
|
| 513 |
+
|
| 514 |
+
# def transcribe_audio(audio_bytes):
|
| 515 |
+
# """Transcribe audio using Whisper API without PyDub"""
|
| 516 |
+
# try:
|
| 517 |
+
# # Create temp file
|
| 518 |
+
# with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
|
| 519 |
+
# tmpfile.write(audio_bytes)
|
| 520 |
+
# tmp_path = tmpfile.name
|
| 521 |
+
|
| 522 |
+
# # Transcribe with Whisper
|
| 523 |
+
# with open(tmp_path, "rb") as audio_file:
|
| 524 |
+
# transcript = openai.Audio.transcribe(
|
| 525 |
+
# model="whisper-1",
|
| 526 |
+
# file=audio_file,
|
| 527 |
+
# language=st.session_state.get('language', 'en')
|
| 528 |
+
# )
|
| 529 |
+
|
| 530 |
+
# os.remove(tmp_path)
|
| 531 |
+
# return transcript['text']
|
| 532 |
+
|
| 533 |
+
# except Exception as e:
|
| 534 |
+
# st.error(f"Error transcribing audio: {str(e)}")
|
| 535 |
+
# return None
|
| 536 |
+
|
| 537 |
+
# Add this function with your other utility functions
|
| 538 |
+
def check_rate_limit():
|
| 539 |
+
"""Simple rate limiting for audio transcription"""
|
| 540 |
+
if 'last_transcription_time' not in st.session_state:
|
| 541 |
+
st.session_state.last_transcription_time = time.time()
|
| 542 |
+
return True
|
| 543 |
+
|
| 544 |
+
current_time = time.time()
|
| 545 |
+
time_since_last = current_time - st.session_state.last_transcription_time
|
| 546 |
+
|
| 547 |
+
if time_since_last < 10: # 10 second cooldown between transcriptions
|
| 548 |
+
return False
|
| 549 |
+
|
| 550 |
+
st.session_state.last_transcription_time = current_time
|
| 551 |
+
return True
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
def chatbot_ui():
|
| 555 |
+
# Initialize language state
|
| 556 |
+
if "language" not in st.session_state:
|
| 557 |
+
st.session_state.language = "en" # Default language is English
|
| 558 |
+
|
| 559 |
+
# Initialize with personalized welcome message if first time
|
| 560 |
+
if "messages" not in st.session_state:
|
| 561 |
+
st.session_state.messages = [{
|
| 562 |
+
"role": "assistant",
|
| 563 |
+
"content": f"🎉✨ Welcome {st.session_state.user_name}! ✨🎉\n\nI'm ApnaLawyer, your AI legal assistant. "
|
| 564 |
+
"I can help explain Indian laws in simple terms. What would you like to know?"
|
| 565 |
+
}]
|
| 566 |
+
|
| 567 |
+
if "memory" not in st.session_state:
|
| 568 |
+
st.session_state.memory = ConversationBufferWindowMemory(k=2, memory_key="chat_history", return_messages=True)
|
| 569 |
+
|
| 570 |
+
# Get user ID
|
| 571 |
+
if "user_id" not in st.session_state:
|
| 572 |
+
try:
|
| 573 |
+
user_info = auth.get_account_info(st.session_state.user_token)
|
| 574 |
+
st.session_state.user_id = user_info['users'][0]['localId']
|
| 575 |
+
st.session_state.chat_history = get_chat_history(st.session_state.user_id)
|
| 576 |
+
except Exception as e:
|
| 577 |
+
st.error(f"Error getting user info: {str(e)}")
|
| 578 |
+
|
| 579 |
+
@st.cache_resource
|
| 580 |
+
def load_embeddings():
|
| 581 |
+
return HuggingFaceEmbeddings(model_name="law-ai/InLegalBERT")
|
| 582 |
+
|
| 583 |
+
embeddings = load_embeddings()
|
| 584 |
+
db = FAISS.load_local("ipc_embed_db", embeddings, allow_dangerous_deserialization=True)
|
| 585 |
+
db_retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 3})
|
| 586 |
+
|
| 587 |
+
# Audio recording section - modified to remove duplicate input
|
| 588 |
+
audio_file = st.audio_input("", key="audio_recorder")
|
| 589 |
+
audio_bytes = audio_file.getvalue() if audio_file else None
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
# Process audio if recorded
|
| 593 |
+
if audio_bytes:
|
| 594 |
+
if not os.getenv('OPENAI_API_KEY'):
|
| 595 |
+
st.error("Voice input disabled - OpenAI API key not configured")
|
| 596 |
+
elif not check_rate_limit():
|
| 597 |
+
st.warning("Please wait a moment before sending another voice message")
|
| 598 |
+
else:
|
| 599 |
+
with st.spinner("Transcribing your voice..."):
|
| 600 |
+
try:
|
| 601 |
+
transcribed_text = transcribe_audio(audio_bytes)
|
| 602 |
+
if transcribed_text:
|
| 603 |
+
# Auto-inject the transcription
|
| 604 |
+
st.session_state.audio_transcription = transcribed_text
|
| 605 |
+
st.rerun()
|
| 606 |
+
except Exception as e:
|
| 607 |
+
st.error(f"Transcription failed: {str(e)}")
|
| 608 |
+
|
| 609 |
+
# Single input handling for both text and voice
|
| 610 |
+
# prompt = st.chat_input("Type or speak your message...")
|
| 611 |
+
# if 'audio_transcription' in st.session_state:
|
| 612 |
+
# prompt = st.session_state.audio_transcription
|
| 613 |
+
# del st.session_state.audio_transcription
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
prompt_template = """
|
| 617 |
+
<s>[INST]
|
| 618 |
+
You are ApnaLawyer.bot, a friendly legal assistant specializing in Indian law. You provide clear, accurate information about the Indian Penal Code (IPC) and related laws.
|
| 619 |
+
|
| 620 |
+
Key principles:
|
| 621 |
+
- Be concise but thorough
|
| 622 |
+
- Use simple language anyone can understand
|
| 623 |
+
- Always clarify when something is outside your expertise
|
| 624 |
+
- Structure responses logically but conversationally
|
| 625 |
+
|
| 626 |
+
CONTEXT: {context}
|
| 627 |
+
CHAT HISTORY: {chat_history}
|
| 628 |
+
QUESTION: {question}
|
| 629 |
+
|
| 630 |
+
Provide a helpful response that:
|
| 631 |
+
1. Directly answers the question
|
| 632 |
+
2. Cites relevant laws/sections when possible
|
| 633 |
+
3. Notes important exceptions
|
| 634 |
+
4. Suggests next steps if needed
|
| 635 |
+
</s>[INST]
|
| 636 |
+
"""
|
| 637 |
+
|
| 638 |
+
prompt = PromptTemplate(template=prompt_template, input_variables=['context', 'question', 'chat_history'])
|
| 639 |
+
|
| 640 |
+
api_key = os.getenv('TOGETHER_API_KEY')
|
| 641 |
+
if not api_key:
|
| 642 |
+
st.error("API key for Together is missing. Please set the TOGETHER_API_KEY environment variable.")
|
| 643 |
+
|
| 644 |
+
llm = Together(model="mistralai/Mixtral-8x22B-Instruct-v0.1", temperature=0.5, max_tokens=1024, together_api_key=api_key)
|
| 645 |
+
|
| 646 |
+
qa = ConversationalRetrievalChain.from_llm(llm=llm, memory=st.session_state.memory, retriever=db_retriever,
|
| 647 |
+
combine_docs_chain_kwargs={'prompt': prompt})
|
| 648 |
+
|
| 649 |
+
# Voice input functionality
|
| 650 |
+
def handle_voice_input():
|
| 651 |
+
st.session_state.recording = True
|
| 652 |
+
audio_file = audio_recorder()
|
| 653 |
+
if audio_file:
|
| 654 |
+
try:
|
| 655 |
+
# Extract bytes from UploadedFile
|
| 656 |
+
audio_bytes = audio_file.getvalue() # Correctly extract bytes from UploadedFile
|
| 657 |
+
|
| 658 |
+
# Convert bytes to numpy array
|
| 659 |
+
audio_array, sample_rate = sf.read(BytesIO(audio_bytes))
|
| 660 |
+
audio_dict = {"raw": audio_array, "sampling_rate": sample_rate}
|
| 661 |
+
|
| 662 |
+
# Load model if not already loaded
|
| 663 |
+
if "speech_to_text" not in st.session_state:
|
| 664 |
+
st.session_state.speech_to_text = load_speech_to_text_model()
|
| 665 |
+
|
| 666 |
+
# Convert speech to text
|
| 667 |
+
text = st.session_state.speech_to_text(audio_dict)["text"]
|
| 668 |
+
|
| 669 |
+
if text.strip():
|
| 670 |
+
# Process the transcribed text as user input
|
| 671 |
+
process_user_input(text)
|
| 672 |
+
|
| 673 |
+
except Exception as e:
|
| 674 |
+
st.error(f"Error processing audio: {str(e)}")
|
| 675 |
+
st.session_state.recording = False
|
| 676 |
+
|
| 677 |
+
def extract_answer(full_response):
|
| 678 |
+
try:
|
| 679 |
+
answer_start = full_response.find("Response:")
|
| 680 |
+
if answer_start != -1:
|
| 681 |
+
answer_start += len("Response:")
|
| 682 |
+
return full_response[answer_start:].strip()
|
| 683 |
+
return full_response.strip()
|
| 684 |
+
except Exception as e:
|
| 685 |
+
return f"Error extracting answer: {str(e)}"
|
| 686 |
+
|
| 687 |
+
def create_new_chat():
|
| 688 |
+
st.session_state.messages = [{
|
| 689 |
+
"role": "assistant",
|
| 690 |
+
"content": f"🆕 New chat started {st.session_state.user_name}! What legal question can I help you with?"
|
| 691 |
+
}]
|
| 692 |
+
st.session_state.memory.clear()
|
| 693 |
+
st.session_state.current_chat_id = None
|
| 694 |
+
st.rerun()
|
| 695 |
+
|
| 696 |
+
def load_chat(chat_id):
|
| 697 |
+
try:
|
| 698 |
+
ref = firebase_db.reference(f'users/{st.session_state.user_id}/chats/{chat_id}')
|
| 699 |
+
chat_data = ref.get()
|
| 700 |
+
if chat_data:
|
| 701 |
+
st.session_state.messages = chat_data['messages']
|
| 702 |
+
st.session_state.current_chat_id = chat_id
|
| 703 |
+
st.session_state.memory.clear()
|
| 704 |
+
st.rerun()
|
| 705 |
+
except Exception as e:
|
| 706 |
+
st.error(f"Error loading chat: {str(e)}")
|
| 707 |
+
|
| 708 |
+
def delete_chat(chat_id):
|
| 709 |
+
if delete_chat_history(st.session_state.user_id, chat_id):
|
| 710 |
+
if st.session_state.current_chat_id == chat_id:
|
| 711 |
+
create_new_chat()
|
| 712 |
+
st.session_state.chat_history = get_chat_history(st.session_state.user_id)
|
| 713 |
+
st.rerun()
|
| 714 |
+
|
| 715 |
+
# Static translations dictionary
|
| 716 |
+
static_translations = {
|
| 717 |
+
"language_switched": {
|
| 718 |
+
"en": "Language switched to",
|
| 719 |
+
"hi": "भाषा बदल दी गई है",
|
| 720 |
+
"fr": "Langue changée en",
|
| 721 |
+
"es": "Idioma cambiado a",
|
| 722 |
+
"de": "Sprache gewechselt zu",
|
| 723 |
+
"zh-cn": "语言切换为",
|
| 724 |
+
"ar": "تم تغيير اللغة إلى",
|
| 725 |
+
"ru": "Язык переключен на",
|
| 726 |
+
"ja": "言語が変更されました",
|
| 727 |
+
"ko": "언어가 변경되었습니다",
|
| 728 |
+
"it": "Lingua cambiata in",
|
| 729 |
+
"pt": "Idioma alterado para",
|
| 730 |
+
"bn": "ভাষা পরিবর্তন করা হয়েছে",
|
| 731 |
+
"ta": "மொழி மாற்றப்பட்டது",
|
| 732 |
+
"te": "భాష మార్చబడింది",
|
| 733 |
+
"ml": "ഭാഷ മാറ്റി",
|
| 734 |
+
"kn": "ಭಾಷೆಯನ್ನು ಬದಲಿಸಲಾಗಿದೆ",
|
| 735 |
+
"mr": "भाषा बदलली गेली आहे",
|
| 736 |
+
"gu": "ભાષા બદલાઈ ગઈ છે",
|
| 737 |
+
"pa": "ਭਾਸ਼ਾ ਬਦਲ ਦਿੱਤੀ ਗਈ ਹੈ",
|
| 738 |
+
"or": "ଭାଷା ପରିବର୍ତ୍ତନ କରାଯାଇଛି",
|
| 739 |
+
"as": "ভাষা পৰিবৰ্তন কৰা হৈছে",
|
| 740 |
+
"ne": "भाषा परिवर्तन गरिएको छ",
|
| 741 |
+
"ur": "زبان تبدیل کر دی گئی ہے",
|
| 742 |
+
"sd": "ٻولي تبديل ڪئي وئي آهي",
|
| 743 |
+
"sa": "भाषा परिवर्तिता अस्ति",
|
| 744 |
+
"kok": "भाषा बदलली",
|
| 745 |
+
"mni": "লানগুয়েজ চেঞ্জ করা হইছে",
|
| 746 |
+
"doi": "भाषा बदल दी गई है",
|
| 747 |
+
"sat": "ᱵᱟᱹᱡᱟ ᱯᱟᱹᱨᱤᱵᱟᱹᱨᱛᱤᱱ ᱠᱟᱹᱨᱟᱹᱜ ᱠᱟᱹᱨᱟᱹᱜ",
|
| 748 |
+
"brx": "भाषा बदल दी गई है",
|
| 749 |
+
"mai": "भाषा बदलल ग��ल अछि"
|
| 750 |
+
}
|
| 751 |
+
}
|
| 752 |
+
|
| 753 |
+
|
| 754 |
+
supported_languages = [
|
| 755 |
+
"en", # English
|
| 756 |
+
"as", # Assamese
|
| 757 |
+
"bn", # Bengali
|
| 758 |
+
"brx", # Bodo
|
| 759 |
+
"doi", # Dogri
|
| 760 |
+
"gu", # Gujarati
|
| 761 |
+
"hi", # Hindi
|
| 762 |
+
"kn", # Kannada
|
| 763 |
+
"ks", # Kashmiri
|
| 764 |
+
"kok", # Konkani
|
| 765 |
+
"mai", # Maithili
|
| 766 |
+
"ml", # Malayalam
|
| 767 |
+
"mni", # Manipuri
|
| 768 |
+
"mr", # Marathi
|
| 769 |
+
"ne", # Nepali
|
| 770 |
+
"or", # Odia
|
| 771 |
+
"pa", # Punjabi
|
| 772 |
+
"sa", # Sanskrit
|
| 773 |
+
"sat", # Santali
|
| 774 |
+
"sd", # Sindhi
|
| 775 |
+
"ta", # Tamil
|
| 776 |
+
"te", # Telugu
|
| 777 |
+
"ur" # Urdu
|
| 778 |
+
]
|
| 779 |
+
|
| 780 |
+
# Display chat messages
|
| 781 |
+
for message in st.session_state.messages:
|
| 782 |
+
with st.chat_message(message["role"]):
|
| 783 |
+
st.write(message["content"])
|
| 784 |
+
|
| 785 |
+
# Handle user input
|
| 786 |
+
if prompt := st.chat_input("Say something..."):
|
| 787 |
+
with st.chat_message("user"):
|
| 788 |
+
st.markdown(f"You: {prompt}")
|
| 789 |
+
|
| 790 |
+
# Detect user language
|
| 791 |
+
user_language = detect(prompt)
|
| 792 |
+
if user_language in static_translations["language_switched"]:
|
| 793 |
+
st.session_state.language = user_language
|
| 794 |
+
|
| 795 |
+
# Check for language change command
|
| 796 |
+
if prompt.startswith("/language"):
|
| 797 |
+
lang_code = prompt.split(" ")[1].strip()
|
| 798 |
+
if lang_code not in supported_languages:
|
| 799 |
+
st.session_state.messages.append({
|
| 800 |
+
"role": "assistant",
|
| 801 |
+
"content": f"⚠ Unsupported language code: {lang_code}. Supported languages are: {', '.join(supported_languages)}."
|
| 802 |
+
})
|
| 803 |
+
st.experimental_rerun()
|
| 804 |
+
st.session_state.language = lang_code
|
| 805 |
+
st.session_state.messages.append({
|
| 806 |
+
"role": "assistant",
|
| 807 |
+
"content": f"{static_translations['language_switched'].get(lang_code, 'Language switched to')} {lang_code.upper()}."
|
| 808 |
+
})
|
| 809 |
+
st.experimental_rerun()
|
| 810 |
+
|
| 811 |
+
# Process user input
|
| 812 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 813 |
+
|
| 814 |
+
with st.chat_message("assistant"):
|
| 815 |
+
with st.spinner("Thinking 💡..."):
|
| 816 |
+
result = qa.invoke(input=prompt)
|
| 817 |
+
answer = extract_answer(result["answer"])
|
| 818 |
+
|
| 819 |
+
# Translate the chatbot's response if necessary
|
| 820 |
+
if st.session_state.language != "en":
|
| 821 |
+
answer = translate_text(answer, st.session_state.language)
|
| 822 |
+
|
| 823 |
+
message_placeholder = st.empty()
|
| 824 |
+
full_response = "⚠ Gentle reminder: We generally ensure precise information, but do double-check. \n\n\n"
|
| 825 |
+
for chunk in answer:
|
| 826 |
+
full_response += chunk
|
| 827 |
+
time.sleep(0.02)
|
| 828 |
+
message_placeholder.markdown(full_response + " |", unsafe_allow_html=True)
|
| 829 |
+
message_placeholder.markdown(full_response)
|
| 830 |
+
|
| 831 |
+
st.session_state.messages.append({"role": "assistant", "content": answer})
|
| 832 |
+
|
| 833 |
+
# Save or update chat
|
| 834 |
+
if len(st.session_state.messages) > 2: # Only save if there's actual conversation
|
| 835 |
+
chat_title = generate_chat_title(st.session_state.messages)
|
| 836 |
+
if hasattr(st.session_state, 'current_chat_id') and st.session_state.current_chat_id:
|
| 837 |
+
update_chat_history(st.session_state.user_id, st.session_state.current_chat_id, st.session_state.messages)
|
| 838 |
+
else:
|
| 839 |
+
chat_id = save_chat_to_history(st.session_state.user_id, chat_title, st.session_state.messages)
|
| 840 |
+
st.session_state.current_chat_id = chat_id
|
| 841 |
+
# Refresh chat history
|
| 842 |
+
st.session_state.chat_history = get_chat_history(st.session_state.user_id)
|
| 843 |
+
|
| 844 |
+
# Sidebar UI
|
| 845 |
+
with st.sidebar:
|
| 846 |
+
st.markdown(f"""
|
| 847 |
+
<div style="margin-bottom: 20px;">
|
| 848 |
+
<h3 style="color: #00ffff; margin-bottom: 5px;">👤 {st.session_state.user_name}</h3>
|
| 849 |
+
<p style="color: rgba(255,255,255,0.7); font-size: 12px; margin-top: 0;">{st.session_state.user_email}</p>
|
| 850 |
+
</div>
|
| 851 |
+
""", unsafe_allow_html=True)
|
| 852 |
+
|
| 853 |
+
if st.button("➕ New Chat", use_container_width=True):
|
| 854 |
+
create_new_chat()
|
| 855 |
+
|
| 856 |
+
st.markdown("---")
|
| 857 |
+
st.markdown("### Chat History")
|
| 858 |
+
|
| 859 |
+
if hasattr(st.session_state, 'chat_history') and st.session_state.chat_history:
|
| 860 |
+
for chat_id, chat_data in st.session_state.chat_history:
|
| 861 |
+
timestamp = time.strftime('%d %b %Y, %I:%M %p', time.localtime(chat_data['last_updated']))
|
| 862 |
+
|
| 863 |
+
col1, col2 = st.columns([0.8, 0.2])
|
| 864 |
+
with col1:
|
| 865 |
+
if st.button(
|
| 866 |
+
f"{chat_data['title']}",
|
| 867 |
+
key=f"chat_{chat_id}",
|
| 868 |
+
help=f"Last updated: {timestamp}",
|
| 869 |
+
use_container_width=True
|
| 870 |
+
):
|
| 871 |
+
load_chat(chat_id)
|
| 872 |
+
with col2:
|
| 873 |
+
if st.button("🗑", key=f"delete_{chat_id}"):
|
| 874 |
+
delete_chat(chat_id)
|
| 875 |
+
else:
|
| 876 |
+
st.markdown("<p style='color: rgba(255,255,255,0.5);'>No chat history yet</p>", unsafe_allow_html=True)
|
| 877 |
+
|
| 878 |
+
st.markdown("---")
|
| 879 |
+
|
| 880 |
+
if st.button("🚪 Logout", use_container_width=True):
|
| 881 |
+
st.session_state.logged_in = False
|
| 882 |
+
st.session_state.user_email = None
|
| 883 |
+
st.session_state.user_name = None
|
| 884 |
+
st.session_state.user_id = None
|
| 885 |
+
st.rerun()
|
| 886 |
+
|
| 887 |
+
footer()
|
| 888 |
+
|
| 889 |
+
# ----------------- Main App -------------------
|
| 890 |
+
if "logged_in" not in st.session_state:
|
| 891 |
+
st.session_state.logged_in = False
|
| 892 |
+
|
| 893 |
+
if not st.session_state.logged_in:
|
| 894 |
+
login_signup_ui()
|
| 895 |
+
else:
|
| 896 |
+
chatbot_ui()
|
firebase_config.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pyrebase
|
| 2 |
+
|
| 3 |
+
firebaseConfig = {
|
| 4 |
+
"apiKey": "AIzaSyAiWuuPRnw4JfNhYE6OQDlQsNJ1AxMzXFo",
|
| 5 |
+
"authDomain": "apna-lawyer.firebaseapp.com",
|
| 6 |
+
"projectId": "apna-lawyer",
|
| 7 |
+
"storageBucket": "apna-lawyer.firebasestorage.app",
|
| 8 |
+
"messagingSenderId": "19684757095",
|
| 9 |
+
"appId": "1:19684757095:web:ab63c06848201e29ba8ec2",
|
| 10 |
+
"databaseURL": "https://apna-lawyer-default-rtdb.firebaseio.com/", # Not needed unless using Realtime DB
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
firebase = pyrebase.initialize_app(firebaseConfig)
|
| 14 |
+
auth = firebase.auth()
|
footer.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from htbuilder import HtmlElement, div, a, p, img, styles
|
| 3 |
+
from htbuilder.units import percent, px
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def image(src_as_string, **style):
|
| 7 |
+
return img(src=src_as_string, style=styles(**style))
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def link(link, text, **style):
|
| 11 |
+
return a(_href=link, _target="_blank", style=styles(**style))(text)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def layout(*args):
|
| 15 |
+
|
| 16 |
+
style = """
|
| 17 |
+
<style>
|
| 18 |
+
# MainMenu {visibility: hidden;}
|
| 19 |
+
footer {visibility: hidden;}
|
| 20 |
+
.stApp { bottom: 40px; }
|
| 21 |
+
.st-emotion-cache-139wi93 {
|
| 22 |
+
width: 100%;
|
| 23 |
+
padding: 1rem 1rem 15px;
|
| 24 |
+
max-width: 46rem;
|
| 25 |
+
}
|
| 26 |
+
</style>
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
style_div = styles(
|
| 30 |
+
position="fixed",
|
| 31 |
+
left=0,
|
| 32 |
+
bottom=0,
|
| 33 |
+
margin=px(0, 0, 0, 0),
|
| 34 |
+
width=percent(100),
|
| 35 |
+
color="white",
|
| 36 |
+
text_align="center",
|
| 37 |
+
height="auto",
|
| 38 |
+
opacity=1
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
body = p()
|
| 42 |
+
foot = div(
|
| 43 |
+
style=style_div
|
| 44 |
+
)(
|
| 45 |
+
body
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
st.markdown(style, unsafe_allow_html=True)
|
| 49 |
+
|
| 50 |
+
for arg in args:
|
| 51 |
+
if isinstance(arg, str):
|
| 52 |
+
body(arg)
|
| 53 |
+
|
| 54 |
+
elif isinstance(arg, HtmlElement):
|
| 55 |
+
body(arg)
|
| 56 |
+
|
| 57 |
+
st.markdown(str(foot), unsafe_allow_html=True)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def footer():
|
| 61 |
+
myargs = [
|
| 62 |
+
"Made with ❤️ by Adarsh, Anand, Anurag and Aditya",
|
| 63 |
+
]
|
| 64 |
+
layout(*myargs)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
if __name__ == "__main__":
|
| 68 |
+
footer()
|
key.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
OPENAI_API_KEY=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
|
requirements.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
langchain==0.1.15
|
| 2 |
+
pypdf
|
| 3 |
+
transformers==4.39.3
|
| 4 |
+
sentence-transformers
|
| 5 |
+
accelerate
|
| 6 |
+
faiss-cpu
|
| 7 |
+
streamlit==1.33.0
|
| 8 |
+
langchain-fireworks
|
| 9 |
+
einops
|
| 10 |
+
langchain-together
|
| 11 |
+
ray==2.10.0
|
| 12 |
+
unstructured
|
| 13 |
+
htbuilder
|
| 14 |
+
pyrebase
|
| 15 |
+
pycryptodome
|
| 16 |
+
firebase-admin
|
| 17 |
+
openai # OpenAI Python client for Whisper
|
| 18 |
+
requests # For making API calls
|
| 19 |
+
sounddevice
|
| 20 |
+
numpy
|
| 21 |
+
wave
|
| 22 |
+
python-dotenv
|