-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
123 lines (105 loc) · 3.98 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import os
import time
import warnings # suppress warning for awhile
import click
from piazza_api import Piazza
from piazza_api.network import FolderFilter
from html2text import HTML2Text
from chain import get_chain
def cache_check_set(id: str) -> bool:
if os.path.exists("post_cache.txt"):
with open("post_cache.txt") as w:
ids = w.readlines()
else:
ids = []
ids = list(map(lambda x: x.strip(), ids))
if id in ids:
return True
ids.append(id)
with open("post_cache.txt", "w") as w:
w.write("\n".join(ids))
return False
def gpt_reply(course, chain, HTML_2_TEXT, verbose: bool):
"""
Fetch a question from Piazza, answer it, and post the answer back to Piazza.
Parameters
----------
course
a Network object representing a Piazza course
(https://github.com/hfaran/piazza-api/blob/develop/piazza_api/network.py#L46)
chain
a chain used to answer questions
HTML_2_TEXT
an object used to convert HTML into clean text
verbose : bool
if true, print a question posted on Piazza, and the answer of that question
to a console
(if false, any questions/answers will only show up on Piazza but not on the console)
"""
# This is still a hacky way to fetch a question from Piazza and post the answer to it.
posts = course.get_filtered_feed(FolderFilter(folder_name="gpt"))
for post in posts["feed"]:
# skip anything we have seen before
if cache_check_set(post["id"]):
continue
subject = HTML_2_TEXT.handle(post["subject"]).strip()
content = HTML_2_TEXT.handle(
post["content_snipet"]
).strip() # typo is intentional from library
text_question = f"{subject}\nQuestion content: {content}"
# The subject of the post will be a question.
if verbose:
print("Question:", text_question)
answer = chain({"question": text_question}, return_only_outputs=True)
if verbose:
print("Answer:", answer)
# Post the answer to that question
course.create_followup(post, answer["answer"] + "\n-gpt on behalf of Kaiser")
@click.command()
@click.option("--email", default="", help="Piazza username")
@click.option("--password", default="", help="Piazza password")
@click.option(
"--network_id",
default="lrnlq1hx7ux6vw",
help="Piazza course id (can be found in the URL: https://piazza.com/class/{network_id})",
)
@click.option("--openai_api_key", default="", help="A key for OpenAI APIs")
@click.option("--cache_dir", default="./cache", help="Folder that saved embeddings")
def main(
email: str, password: str, network_id: str, openai_api_key: str, cache_dir: str
):
"""
Automatically answer Piazza questions.
Parameters
----------
email : str
an email address used to login Piazza account
password : str
a password used to login Piazza account
network_id : str
Piazza course id (can be found in the URL: https://piazza.com/class/{network_id})
(default is "lcguqjpvo0q39j" which is a course id of CS 538, Spring 2023)
openai_api_key : str
a key for OpenAI APIs
"""
print("Initializing the program...")
os.environ["TOKENIZERS_PARALLELISM"] = "false"
if openai_api_key:
os.environ["OPENAI_API_KEY"] = openai_api_key
email = email or os.environ["PIAZZA_USERNAME"]
password = password or os.environ["PIAZZA_PASSWORD"]
piazza = Piazza()
piazza.user_login(email=email, password=password)
course = piazza.network(network_id)
HTML_2_TEXT = HTML2Text()
HTML_2_TEXT.ignore_links = True
with warnings.catch_warnings(): # suppress warning for awhile
warnings.simplefilter("ignore")
chain = get_chain(cache_dir)
print("Done initialization!")
while True:
print("Checking for new posts to gpt folder...")
gpt_reply(course, chain, HTML_2_TEXT, True)
time.sleep(600)
if __name__ == "__main__":
main()