Skip to content

Commit e79a7b2

Browse files
committed
ProjectCommit1
0 parents  commit e79a7b2

49 files changed

Lines changed: 19588 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Build & Deploy React App (CRA)
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout repo
14+
uses: actions/checkout@v3
15+
16+
- name: Set up Node.js
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: '18' # or match your project's Node version
20+
21+
- name: Install dependencies
22+
run: npm ci
23+
working-directory: frontend
24+
25+
- name: Build the app
26+
run: npm run build
27+
working-directory: frontend
28+
29+
- name: Add .nojekyll (optional for GitHub Pages)
30+
run: echo > frontend/build/.nojekyll
31+
32+
- name: Deploy to GitHub Pages
33+
uses: JamesIves/github-pages-deploy-action@v4
34+
with:
35+
branch: gh-pages # Target branch
36+
folder: frontend/build # CRA build output

App.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import logo from './logo.svg';
2+
import './App.css';
3+
4+
function App() {
5+
return (
6+
<div className="App">
7+
<header className="App-header">
8+
<img src={logo} className="App-logo" alt="logo" />
9+
<p>
10+
Edit <code>src/App.js</code> and save to reload.
11+
</p>
12+
<a
13+
className="App-link"
14+
href="https://reactjs.org"
15+
target="_blank"
16+
rel="noopener noreferrer"
17+
>
18+
Learn React
19+
</a>
20+
</header>
21+
</div>
22+
);
23+
}
24+
25+
export default App;

Backend/Requirements.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
flask
2+
flask-socketio
3+
pymongo
4+
bson
5+
langchain
6+
langgraph
7+
typing-extensions
8+
gunicorn
9+
eventlet

Backend/app.py

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
from flask import Flask, render_template, request, jsonify, send_from_directory, session,url_for
2+
from pymongo import MongoClient
3+
from langchain_core.messages import HumanMessage, BaseMessage, AIMessage
4+
from langchain.chat_models import init_chat_model
5+
from datetime import datetime
6+
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
7+
from langgraph.checkpoint.memory import MemorySaver
8+
from langgraph.graph import START, StateGraph
9+
from langgraph.graph.message import add_messages
10+
from typing import Sequence
11+
from typing_extensions import Annotated, TypedDict
12+
import os
13+
from dotenv import load_dotenv
14+
from flask_cors import CORS
15+
from werkzeug.security import generate_password_hash, check_password_hash
16+
import re
17+
18+
# ----- Flask setup -----
19+
app = Flask(__name__, static_folder='../frontend/build', static_url_path='/')
20+
CORS(app, supports_credentials=True)
21+
app.secret_key = os.urandom(24) # Add secret key for session management
22+
23+
# ----- MongoDB Setup -----
24+
client = MongoClient('mongodb://localhost:27017/')
25+
db = client['HealthFirst']
26+
chat_collection = db['Chats']
27+
users_collection = db['Users'] # New collection for users
28+
29+
# ----- LangGraph initialization -----
30+
load_dotenv()
31+
32+
model = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")
33+
prompt_template = ChatPromptTemplate.from_messages([
34+
("system",
35+
"You are a healthcare bot. Your job is to advice homely remedies to patients who contact you. If the query seems too serious you should advice to seek professional help."),
36+
MessagesPlaceholder(variable_name="messages")
37+
])
38+
39+
class State(TypedDict):
40+
messages: Annotated[Sequence[BaseMessage], add_messages]
41+
42+
workflow = StateGraph(state_schema=State)
43+
44+
def call_model(state: State):
45+
prompt = prompt_template.invoke({"messages": state["messages"]})
46+
response = model.invoke(prompt)
47+
return {"messages": [response]}
48+
49+
workflow.add_node("model", call_model)
50+
workflow.add_edge(START, "model")
51+
memory = MemorySaver()
52+
langgraph_app = workflow.compile(checkpointer=memory)
53+
54+
# ----- MongoDB helper functions -----
55+
def save_message(session_id, message, sender):
56+
user_email = session.get('user_email')
57+
update_data = {
58+
"$push": {"messages": {"sender": sender, "content": message, "timestamp": datetime.utcnow()}},
59+
"$set": {"last_updated": datetime.utcnow()}
60+
}
61+
62+
# Add user_email to the document if available in session
63+
if user_email:
64+
update_data["$set"]["user_email"] = user_email
65+
66+
chat_collection.update_one(
67+
{"session_id": session_id},
68+
update_data,
69+
upsert=True
70+
)
71+
72+
def get_sessions():
73+
# Get user_email from session
74+
user_email = session.get('user_email')
75+
76+
# Filter by user_email if available
77+
query = {"user_email": user_email} if user_email else {}
78+
sessions = chat_collection.find(query, {"session_id": 1, "last_updated": 1, "_id": 0}).sort("last_updated", -1)
79+
return [{"session_id": doc['session_id'], "last_updated": doc['last_updated'].strftime("%Y-%m-%d %H:%M:%S")} for doc in sessions]
80+
81+
def get_messages_for_session(session_id):
82+
user_email = session.get('user_email')
83+
84+
query = {"session_id": session_id}
85+
if user_email:
86+
query["user_email"] = user_email
87+
88+
chat = chat_collection.find_one(query, {"_id": 0, "messages": 1})
89+
return chat.get("messages", []) if chat else []
90+
91+
def generate_bot_response(session_id, user_input):
92+
# Get previous messages
93+
previous_messages = get_messages_for_session(session_id)
94+
95+
# Convert to LangChain message format
96+
history = []
97+
for msg in previous_messages:
98+
content = msg.get('content') or msg.get('messages')
99+
if content:
100+
if msg['sender'] == 'user':
101+
history.append(HumanMessage(content=content))
102+
else:
103+
history.append(AIMessage(content=content))
104+
105+
# Add current message
106+
history.append(HumanMessage(content=user_input))
107+
108+
# Generate response
109+
state = {'messages': history}
110+
response = langgraph_app.invoke(
111+
state,
112+
config={"configurable": {"thread_id": session_id}}
113+
)
114+
answer = response['messages'][-1].content if response['messages'] else "No response"
115+
formatted_answer = answer.replace("\u2022", "\n•")
116+
117+
return formatted_answer
118+
119+
# ----- Flask routes -----
120+
121+
# Serve the React app
122+
@app.route('/')
123+
def serve():
124+
if 'user_email' not in session:
125+
return send_from_directory(app.static_folder,'index.html')
126+
return send_from_directory(app.static_folder, 'index.html')
127+
128+
# Serve static files (JS, CSS, etc.)
129+
@app.route('/<path:path>')
130+
def static_proxy(path):
131+
return send_from_directory(app.static_folder, path)
132+
133+
# API route to get all sessions for a user
134+
@app.route("/api/sessions", methods=['GET'])
135+
def get_all_sessions():
136+
user_email = session.get('user_email')
137+
138+
if not user_email:
139+
return jsonify({"error": "Not authenticated. Please log in."}), 401
140+
141+
sessions = get_sessions()
142+
return jsonify(sessions)
143+
144+
# API route to create a new session
145+
@app.route("/api/sessions", methods=['POST'])
146+
def create_session():
147+
user_email = session.get('user_email')
148+
149+
if not user_email:
150+
return jsonify({"error": "Not authenticated. Please log in."}), 401
151+
152+
data = request.json
153+
session_id = data.get('session_id', datetime.utcnow().strftime("%Y%m%d%H%M%S%f"))
154+
155+
# Create a new session with user_email from session
156+
chat_collection.insert_one({
157+
"session_id": session_id,
158+
"user_email": user_email,
159+
"messages": [],
160+
"last_updated": datetime.utcnow()
161+
})
162+
163+
return jsonify({"session_id": session_id, "status": "created"})
164+
165+
# API route to get messages for a session
166+
@app.route("/api/sessions/<session_id>/messages", methods=['GET'])
167+
def get_session_messages(session_id):
168+
user_email = session.get('user_email')
169+
170+
if not user_email:
171+
return jsonify({"error": "Not authenticated. Please log in."}), 401
172+
173+
messages = get_messages_for_session(session_id)
174+
return jsonify(messages)
175+
176+
@app.route("/api/logout", methods=["POST"])
177+
def logout():
178+
session.pop('user_email', None) # Clear session data
179+
return jsonify({"message": "Logged out successfully", "redirect": "/login"}), 200
180+
181+
182+
# API route to send a message and get a response
183+
@app.route("/api/sessions/<session_id>/messages", methods=['POST'])
184+
def send_message(session_id):
185+
user_email = session.get('user_email')
186+
187+
if not user_email:
188+
return jsonify({"error": "Not authenticated. Please log in."}), 401
189+
190+
data = request.json
191+
user_input = data.get('message')
192+
193+
if not user_input:
194+
return jsonify({'error': 'No message provided'}), 400
195+
196+
# Save user message
197+
save_message(session_id, user_input, 'user')
198+
199+
# Generate bot response
200+
bot_response = generate_bot_response(session_id, user_input)
201+
202+
# Save bot response
203+
save_message(session_id, bot_response, 'bot')
204+
205+
return jsonify({
206+
'user_message': user_input,
207+
'bot_response': bot_response
208+
})
209+
210+
# ----- User Authentication Routes -----
211+
212+
# API route for user signup
213+
@app.route("/api/signup", methods=['POST'])
214+
def signup():
215+
data = request.get_json()
216+
217+
# Validate input data
218+
if not data or not data.get('email') or not data.get('password'):
219+
return jsonify({"error": "Email and password are required"}), 400
220+
221+
email = data['email']
222+
password = data['password']
223+
224+
# Basic email validation
225+
email_pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
226+
if not re.match(email_pattern, email):
227+
return jsonify({"error": "Invalid email format"}), 400
228+
229+
# Password validation (at least 8 characters)
230+
if len(password) < 8:
231+
return jsonify({"error": "Password must be at least 8 characters long"}), 400
232+
233+
try:
234+
# Check if email already exists
235+
existing_user = users_collection.find_one({"email": email})
236+
if existing_user:
237+
return jsonify({"error": "Email already registered"}), 409
238+
239+
# Hash the password for security
240+
hashed_password = generate_password_hash(password)
241+
242+
# Insert new user
243+
users_collection.insert_one({
244+
"email": email,
245+
"password": hashed_password,
246+
"created_at": datetime.utcnow()
247+
})
248+
249+
# Set user email in session after successful signup
250+
session['user_email'] = email
251+
252+
return jsonify({"message": "User registered successfully"}), 201
253+
254+
except Exception as e:
255+
return jsonify({"error": str(e)}), 500
256+
257+
# API route for user login
258+
@app.route("/api/login", methods=['POST'])
259+
def login():
260+
data = request.get_json()
261+
262+
if not data or not data.get('email') or not data.get('password'):
263+
return jsonify({"error": "Email and password are required"}), 400
264+
265+
email = data['email']
266+
password = data['password']
267+
268+
try:
269+
# Find user
270+
user = users_collection.find_one({"email": email})
271+
272+
# Check if user exists
273+
if not user:
274+
return jsonify({"verified": False, "error": "User not found"}), 401
275+
276+
# Verify password
277+
if not check_password_hash(user['password'], password):
278+
return jsonify({"verified": False, "error": "Invalid password"}), 401
279+
280+
# Store email in session
281+
session['user_email'] = email
282+
283+
# User verification successful
284+
return jsonify({
285+
"verified": True,
286+
"message": "Login successful",
287+
"user": {
288+
"email": user['email'],
289+
"created_at": user['created_at']
290+
}
291+
}), 200
292+
293+
except Exception as e:
294+
return jsonify({"verified": False, "error": str(e)}), 500
295+
296+
@app.route("/api/check-auth", methods=["GET"])
297+
def check_auth():
298+
user_email = session.get('user_email')
299+
300+
if not user_email:
301+
return jsonify({"authenticated": False, "error": "User not authenticated"}), 401
302+
303+
user = users_collection.find_one({"email": user_email})
304+
305+
if user:
306+
return jsonify({"authenticated": True, "email": user_email}), 200
307+
else:
308+
# Clear invalid session
309+
session.pop('user_email', None)
310+
return jsonify({"authenticated": False, "error": "User not found"}), 401
311+
312+
if __name__ == '__main__':
313+
app.run(host='127.0.0.1', port=5000, debug=True)

0 commit comments

Comments
 (0)