Skip to content

Commit

Permalink
Merge branch 'main' of github.com:yale-swe/s24-bluebook-ai into buwei…
Browse files Browse the repository at this point in the history
…-michal-cas
  • Loading branch information
BuweiChen committed Apr 1, 2024
2 parents 93469ed + 86daa2d commit 76369ad
Show file tree
Hide file tree
Showing 14 changed files with 5,499 additions and 381 deletions.
95 changes: 91 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,93 @@
# 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!
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."
}
```
4 changes: 3 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.env
.env
__pycache__/
bluebook_env/
55 changes: 0 additions & 55 deletions backend/README.md

This file was deleted.

Binary file removed backend/__pycache__/lib.cpython-311.pyc
Binary file not shown.
Binary file removed backend/__pycache__/lib.cpython-312.pyc
Binary file not shown.
82 changes: 43 additions & 39 deletions backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import xml.etree.ElementTree as ET

COURSE_QUERY_LIMIT = 5
SAFETY_CHECK_ENABLED = False
DATABASE_RELEVANCY_CHECK_ENABLED = True

load_dotenv()

Expand Down Expand Up @@ -97,26 +99,27 @@ def chat():

print(user_messages)

# for safety check, not to be included in final response
user_messages_safety_check = user_messages.copy()
user_messages_safety_check.append({
'role': 'user',
'content': 'Am I asking for help with courses or academics? Answer "yes" or "no".'
})
if SAFETY_CHECK_ENABLED:
# for safety check, not to be included in final response
user_messages_safety_check = user_messages.copy()
user_messages_safety_check.append({
'role': 'user',
'content': 'Am I asking for help with courses or academics? Answer "yes" or "no".'
})

response_safety_check = chat_completion_request(messages=user_messages_safety_check)
response_safety_check = response_safety_check.choices[0].message.content

if 'no' in response_safety_check.lower():
response = 'I am sorry, but I can only assist with questions related to courses or academics at this time.'
json_response = {
'response': response,
'courses': []
}
print('failed safety check')
return jsonify(json_response)
else:
print('passed safety check')
response_safety_check = chat_completion_request(messages=user_messages_safety_check)
response_safety_check = response_safety_check.choices[0].message.content
if 'no' in response_safety_check.lower():
response = 'I am sorry, but I can only assist with questions related to courses or academics at this time.'
json_response = {
'response': response,
'courses': []
}
print('failed safety check')
return jsonify(json_response)
else:
print('passed safety check')

# adding system message if user message does not include a system message header
if user_messages[0]['role'] != 'system':
Expand All @@ -125,27 +128,28 @@ def chat():
'content': 'Your name is Eli. You are a helpful assistant for Yale University students to ask questions about courses and academics.'
})

# checking if database query is necessary
user_messages_database_relevancy_check = user_messages.copy()
user_messages_database_relevancy_check.append({
'role': 'user',
'content': 'Will you be able to better answer my questions with information about specific courses related to the user query at Yale University? You should answer "yes" if you need information about courses at Yale that you don\'t have, otherwise you should answer "no".'
})
if DATABASE_RELEVANCY_CHECK_ENABLED:
# checking if database query is necessary
user_messages_database_relevancy_check = user_messages.copy()
user_messages_database_relevancy_check.append({
'role': 'user',
'content': 'Will you be able to better answer my question with access to specific courses at Yale University? If you answer "yes", you will be provided with courses that are semantically similar to my question. Answer "yes" or "no".'
})

user_messages_database_relevancy_check = chat_completion_request(messages=user_messages_database_relevancy_check)
response_user_messages_database_relevancy_check = user_messages_database_relevancy_check.choices[0].message.content
user_messages_database_relevancy_check = chat_completion_request(messages=user_messages_database_relevancy_check)
response_user_messages_database_relevancy_check = user_messages_database_relevancy_check.choices[0].message.content

if 'no' in response_user_messages_database_relevancy_check.lower():
response = chat_completion_request(messages=user_messages)
response = response.choices[0].message.content
json_response = {
'response': response,
'courses': []
}
print('no need to query database for course information')
return jsonify(json_response)
else:
print('need to query database for course information')
if 'no' in response_user_messages_database_relevancy_check.lower():
response = chat_completion_request(messages=user_messages)
response = response.choices[0].message.content
json_response = {
'response': response,
'courses': []
}
print('no need to query database for course information')
return jsonify(json_response)
else:
print('need to query database for course information')

# create embedding for user message to query against vector index
query_vector = create_embedding(user_messages[-1]['content'])
Expand Down Expand Up @@ -181,7 +185,7 @@ def chat():
recommendation_prompt = f'Here are some courses that might be relevant to the user request:\n\n'
for course in recommended_courses:
recommendation_prompt += f'{course["course_code"]}: {course["title"]}\n{course["description"]}\n\n'
recommendation_prompt += 'Provide a response to the user. Incorporate specific course information if it is relevant to the user request.'
recommendation_prompt += 'Provide a response to the user. Incorporate specific course information if it is relevant to the user request. If you include any course titles, make sure to wrap it in **double asterisks**. Do not order them in a list.'

user_messages.append({
'role': 'system',
Expand Down
Loading

0 comments on commit 76369ad

Please sign in to comment.