-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:yale-swe/s24-bluebook-ai into kaili…
…-sentiment-classif
- Loading branch information
Showing
32 changed files
with
2,219 additions
and
269 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
name: Python application test | ||
|
||
on: | ||
push: | ||
branches: | ||
- "*" | ||
pull_request: | ||
branches: | ||
- "*" | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
env: | ||
OPENAI_API_KEY: "" | ||
MONGO_URI: "" | ||
FLASK_SECRET_KEY: "" | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Set up Python 3.8 | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.8 | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -r requirements.txt | ||
pip install pytest flask_testing requests_mock | ||
- name: Run Tests | ||
run: | | ||
cd backend | ||
pytest | ||
- name: Cache Python dependencies | ||
uses: actions/cache@v2 | ||
with: | ||
path: ~/.cache/pip | ||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} | ||
restore-keys: | | ||
${{ runner.os }}-pip- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
/bluebook/ | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env | ||
*/.env | ||
*.local | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts | ||
|
||
/api/__pycache__ | ||
/api/testmongo.py | ||
|
||
# python venv | ||
*/bluebook_env | ||
bluebook_env | ||
|
||
# data | ||
data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,110 @@ | ||
# s24-bluebook-ai | ||
To run frontend or backend code, please add an `.env` file inside that directory and put your API key in it, such as | ||
# BluebookAI | ||
|
||
Course selections play a pivotal role in shaping student learning experiences at Yale. Each semester, Yale has a vast array of course offerings, and it is difficult for students to select specific courses that fit their needs and interests. Beyond the official Yale Course Search, students often turn to CourseTable for additional insights from other students’ reviews and experiences. | ||
|
||
With CourseTable, students retrieve information using keyword search, filtering, and sorting across classes offered in current and previous semesters. However, CourseTable currently uses exact match only, which can be less helpful when the student doesn’t know what specific course(s) they are searching for. For example, a student who searches for “DevOps” may not see CPSC 439/539 Software Engineering in returned search results. | ||
|
||
In this project, we aim to enhance students’ course selection experience by augmenting CourseTable with a natural language interface that can provide customized course recommendations in response to student queries. By supplying more relevant and dynamic results and expanding students’ means of interaction with course data, this will enable students to more easily and effectively determine the best course schedule for themselves. | ||
|
||
## Get Started | ||
|
||
### Frontend | ||
|
||
0. If you don't already have Node.js installed, you can download it [here](https://nodejs.org/en/download/). | ||
1. Enter the `frontend` directory and install the dependencies: | ||
|
||
```bash | ||
cd frontend | ||
npm install | ||
``` | ||
|
||
2. Start the Next.js app: | ||
|
||
```bash | ||
npm run dev | ||
``` | ||
|
||
### Backend | ||
|
||
1. Enter the `backend` directory, create a virtual environment, activate it, and install the dependencies. Make sure you have Python 3.10+ installed. | ||
|
||
```bash | ||
cd backend | ||
python -m venv bluebook_env | ||
source bluebook_env/bin/activate | ||
pip install -r requirements.txt | ||
``` | ||
|
||
2. You will also need to create `.env` in the the `backend` directory that contains your API key to OpenAI and the MongoDB URI. The `.env` file should look like this: | ||
|
||
``` | ||
OPENAI_API_KEY=sk-XXX | ||
MONGO_URI="mongodb+srv://xxx" | ||
OPENAI_API_KEY="sk-xxx" | ||
``` | ||
|
||
Don't push your API key to this repo! | ||
|
||
To run sentiment classification, first create a conda environment for Python 3 using the requirements.txt file: | ||
|
||
``` | ||
conda create --name <env_name> --file sentiment_classif_requirements.txt | ||
``` | ||
|
||
Activate the conda environment by running: | ||
|
||
``` | ||
conda activate <env_name> | ||
``` | ||
|
||
where `<env_name>` is your name of choice for the conda environment. | ||
|
||
You can get an OpenAI API key [here](https://platform.openai.com/api-keys). The MongoDB URI is shared by the team. You will need to have your IP address allowlisted by MongoDB to query the database. Contact the team for access. | ||
|
||
3. Start the Flask server: | ||
|
||
```bash | ||
python app.py | ||
``` | ||
|
||
## Usage | ||
|
||
1. Enter the `frontend` directory and run | ||
|
||
```bash | ||
npm run dev | ||
``` | ||
|
||
2. Enter the `backend` directory and run | ||
|
||
```bash | ||
python app.py | ||
``` | ||
|
||
3. Ask away! | ||
|
||
![demo](./demo.png) | ||
|
||
4. You can also use your favorite API client (e.g., Postman) to send a POST request to `http://localhost:8000/api/chat` with the following JSON payload: | ||
|
||
```json | ||
{ | ||
"role": "user", | ||
"content": "Tell me some courses about personal finance" | ||
} | ||
``` | ||
|
||
You should receive a response with the recommended courses like this: | ||
|
||
```json | ||
{ | ||
"courses": [ | ||
{ | ||
"course_code": "ECON 436", | ||
"description": "How much should I be saving at age 35? How much of my portfolio should be invested in stocks at age 50? Which mortgage should I choose, and when should I refinance it? How much can I afford to spend per year in retirement? This course covers prescriptive models of personal saving, asset allocation, borrowing, and spending. The course is designed to answer questions facing anybody who manages their own money or is a manager in an organization that is trying to help clients manage their money.", | ||
"title": "Personal Finance" | ||
}, | ||
... | ||
], | ||
"response": "To learn more about personal finance, you can start by taking courses or workshops that focus on financial management, budgeting, investing, and retirement planning. Some universities and educational platforms offer online courses on personal finance, such as ECON 436: Personal Finance and ECON 361: Corporate Finance. Additionally, you can explore resources like books, podcasts, and websites dedicated to personal finance advice and tips. It may also be helpful to consult with a financial advisor or planner for personalized guidance on managing your finances effectively." | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
""" Flask app that uses the OpenAI API to generate responses to user messages.""" | ||
|
||
import os | ||
import certifi | ||
import openai | ||
from flask import Flask, request, jsonify | ||
from flask_cors import CORS | ||
from dotenv import load_dotenv | ||
from pymongo import MongoClient | ||
from pymongo.errors import ConnectionFailure | ||
from lib import chat_completion_request, create_embedding | ||
|
||
load_dotenv() | ||
|
||
ca = certifi.where() | ||
URI = os.getenv("MONGODB_URI") | ||
client = MongoClient(URI, tlsCAFile=ca) | ||
db = client["course_db"] | ||
collection = db["parsed_courses"] | ||
|
||
try: | ||
client.admin.command("ping") | ||
print("Pinged your deployment. You successfully connected to MongoDB!") | ||
except ConnectionFailure as db_error: | ||
print(f"Database connection error: {db_error}") | ||
|
||
app = Flask(__name__) | ||
CORS(app) | ||
|
||
|
||
@app.route("/") | ||
def home(): | ||
"""Route handler for the home page ("/"). Returns a welcome message.""" | ||
return "Welcome to the Flask App!" | ||
|
||
|
||
conversation_history = [] | ||
|
||
|
||
@app.route("/chat", methods=["POST"]) | ||
def chat(): | ||
""" | ||
Route handler for "/chat" endpoint. Receives a JSON payload with user messages, | ||
generates a response using the OpenAI API, and returns it as a JSON object. | ||
""" | ||
data = request.get_json() | ||
|
||
if "message" not in data: | ||
return jsonify({"error": "Missing 'message' in request body"}), 400 | ||
|
||
user_message = data["message"] | ||
conversation_history.append({"role": "user", "content": user_message}) | ||
|
||
query_vector = create_embedding(user_message) | ||
|
||
database_response = list( | ||
collection.aggregate( | ||
[ | ||
{ | ||
"$vectorSearch": { | ||
"index": "parsed_courses_title_description_index", | ||
"path": "embedding", | ||
"queryVector": query_vector, | ||
"numCandidates": 30, | ||
"limit": 5, | ||
} | ||
} | ||
] | ||
) | ||
) | ||
|
||
recommended_courses = [ | ||
{ | ||
"course_code": course["course_code"], | ||
"title": course["title"], | ||
"description": course["description"], | ||
} | ||
for course in database_response | ||
] | ||
|
||
recommendation_prompt = ( | ||
"Here are some courses that might be relevant to your request:\n\n" | ||
+ "\n\n".join( | ||
f'{course["course_code"]}: {course["title"]}\n{course["description"]}' | ||
for course in recommended_courses | ||
) | ||
+ "\nProvide a response to the user. Incorporate this information only if relevant." | ||
) | ||
|
||
conversation_history.append({"role": "system", "content": recommendation_prompt}) | ||
|
||
try: | ||
response = chat_completion_request(messages=conversation_history) | ||
ai_response_content = response.choices[0].message.content | ||
conversation_history.append( | ||
{"role": "assistant", "content": ai_response_content} | ||
) | ||
except openai.BadRequestError as chat_error: | ||
print(f"Error during chat completion request: {chat_error}") | ||
ai_response_content = ( | ||
"Sorry, I encountered an error while processing your request." | ||
) | ||
|
||
return jsonify({"response": ai_response_content, "courses": recommended_courses}) | ||
|
||
if __name__ == "__main__": | ||
app.run(debug=True) |
Oops, something went wrong.