Skip to content

Commit

Permalink
Merge branch 'main' of github.com:yale-swe/s24-bluebook-ai into kaili…
Browse files Browse the repository at this point in the history
…-sentiment-classif
  • Loading branch information
BuweiChen committed Apr 15, 2024
2 parents e4cbff8 + 7d1fe59 commit 882252b
Show file tree
Hide file tree
Showing 32 changed files with 2,219 additions and 269 deletions.
18 changes: 0 additions & 18 deletions .github/workflows/github-actions-demo.yml

This file was deleted.

39 changes: 39 additions & 0 deletions .github/workflows/python-app.yml
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-
48 changes: 48 additions & 0 deletions .gitignore
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
100 changes: 97 additions & 3 deletions README.md
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."
}
```
107 changes: 107 additions & 0 deletions api/app.py
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)
Loading

0 comments on commit 882252b

Please sign in to comment.