Spaces:
Running
Running
Jeff Myers II
commited on
Commit
·
64fde65
1
Parent(s):
13a2860
Completed Prototype
Browse files- Gemma_Model.py → Gemma.py +20 -57
- News.py +29 -24
- app.py +259 -52
Gemma_Model.py → Gemma.py
RENAMED
@@ -1,9 +1,4 @@
|
|
1 |
-
from transformers import
|
2 |
-
# AutoTokenizer,
|
3 |
-
# BitsAndBytesConfig,
|
4 |
-
Gemma3ForCausalLM,
|
5 |
-
GemmaTokenizerFast
|
6 |
-
)
|
7 |
import torch
|
8 |
import json
|
9 |
import os
|
@@ -14,21 +9,16 @@ class GemmaLLM:
|
|
14 |
|
15 |
def __init__(self):
|
16 |
model_id = "google/gemma-3-1b-it"
|
17 |
-
# quantization_config = BitsAndBytesConfig(load_in_8bit=True)
|
18 |
|
|
|
19 |
self.model = Gemma3ForCausalLM.from_pretrained(
|
20 |
model_id,
|
21 |
-
device_map="cpu",
|
22 |
-
# quantization_config=quantization_config,
|
23 |
-
# low_cpu_mem_usage=True,
|
24 |
torch_dtype=torch.float16,
|
25 |
token=os.environ.get("GEMMA_TOKEN"),
|
26 |
).eval()
|
27 |
|
28 |
-
self.tokenizer = GemmaTokenizerFast.from_pretrained(model_id, token=os.environ.get("GEMMA_TOKEN"))
|
29 |
-
|
30 |
def generate(self, message) -> str:
|
31 |
-
print("Generating...")
|
32 |
inputs = self.tokenizer.apply_chat_template(
|
33 |
message,
|
34 |
add_generation_prompt=True,
|
@@ -40,72 +30,45 @@ class GemmaLLM:
|
|
40 |
input_length = inputs["input_ids"].shape[1]
|
41 |
|
42 |
with torch.inference_mode():
|
43 |
-
outputs = self.model.generate(
|
44 |
-
|
45 |
-
)[0][input_length:]
|
46 |
-
|
47 |
-
outputs = self.tokenizer.decode(outputs, skip_special_tokens=True)
|
48 |
-
|
49 |
-
print("Completed generating!")
|
50 |
|
51 |
return outputs
|
52 |
|
53 |
-
def
|
54 |
|
55 |
summarize = "You are a helpful assistant. Your main task is to summarize articles. You will be given an article that you will generate a summary for. The summary should include all the key points of the article. ONLY RESPOND WITH THE SUMMARY!!!"
|
56 |
|
57 |
summary = f"Summarize the data in the following JSON into {num_paragraphs} paragraph(s) so that it is easy to read and understand:\n"
|
58 |
|
59 |
-
message = [
|
60 |
-
|
61 |
-
"role": "system",
|
62 |
-
"content": [
|
63 |
-
{"type": "text", "text": summarize},
|
64 |
-
],
|
65 |
-
},
|
66 |
-
{
|
67 |
-
"role": "user",
|
68 |
-
"content": [
|
69 |
-
{"type": "text", "text": summary + json.dumps(article, indent=4)},
|
70 |
-
],
|
71 |
-
},
|
72 |
-
]
|
73 |
|
74 |
return message
|
75 |
|
76 |
-
def get_summary(self,
|
|
|
77 |
summary = self.generate(message)
|
78 |
|
79 |
return summary
|
80 |
|
81 |
-
def
|
82 |
-
|
|
|
83 |
dict(question=str.__name__, correct_answer=str.__name__, false_answers=[str.__name__, str.__name__, str.__name__]),
|
84 |
dict(question=str.__name__, correct_answer=str.__name__, false_answers=[str.__name__, str.__name__, str.__name__]),
|
85 |
-
dict(question=str.__name__, correct_answer=str.__name__, false_answers=[str.__name__, str.__name__, str.__name__])
|
86 |
-
|
87 |
-
question = "You are a helpful assistant. Your main task is to generate " + str(num_questions) + " multiple choice questions from an article. Respond in the following JSON structure and schema:\n\njson\n```\n" + schema + "\n```\n\nThere should only be " + str(num_questions) + " questions generated. Each question should only have 3 false answers and 1 correct answer. The correct answer should be the most relevant answer based on the context derived from the article. False answers should not contain the correct answer. False answers should contain false information but also be reasonably plausible for answering the question. ONLY RESPOND WITH RAW JSON!!!"
|
88 |
|
89 |
-
questions = f"Generate {difficulty} questions and answers from the following article:\n"
|
90 |
|
91 |
-
message = [
|
92 |
-
|
93 |
-
"role": "system",
|
94 |
-
"content": [
|
95 |
-
{"type": "text", "text": question},
|
96 |
-
],
|
97 |
-
},
|
98 |
-
{
|
99 |
-
"role": "user",
|
100 |
-
"content": [
|
101 |
-
{"type": "text", "text": questions + summary},
|
102 |
-
],
|
103 |
-
},
|
104 |
-
]
|
105 |
|
106 |
return message
|
107 |
|
108 |
-
def get_questions(self,
|
|
|
109 |
questions = self.generate(message)
|
110 |
|
111 |
return json.loads(questions.strip("```").replace("json\n", ""))
|
|
|
1 |
+
from transformers import AutoTokenizer, Gemma3ForCausalLM
|
|
|
|
|
|
|
|
|
|
|
2 |
import torch
|
3 |
import json
|
4 |
import os
|
|
|
9 |
|
10 |
def __init__(self):
|
11 |
model_id = "google/gemma-3-1b-it"
|
|
|
12 |
|
13 |
+
self.tokenizer = AutoTokenizer.from_pretrained(model_id)
|
14 |
self.model = Gemma3ForCausalLM.from_pretrained(
|
15 |
model_id,
|
16 |
+
device_map="cuda" if torch.cuda.is_available() else "cpu",
|
|
|
|
|
17 |
torch_dtype=torch.float16,
|
18 |
token=os.environ.get("GEMMA_TOKEN"),
|
19 |
).eval()
|
20 |
|
|
|
|
|
21 |
def generate(self, message) -> str:
|
|
|
22 |
inputs = self.tokenizer.apply_chat_template(
|
23 |
message,
|
24 |
add_generation_prompt=True,
|
|
|
30 |
input_length = inputs["input_ids"].shape[1]
|
31 |
|
32 |
with torch.inference_mode():
|
33 |
+
outputs = self.model.generate(**inputs, max_new_tokens=1024)[0][input_length:]
|
34 |
+
outputs = self.tokenizer.decode(outputs, skip_special_tokens=True)
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
return outputs
|
37 |
|
38 |
+
def _get_summary_message(self, article, num_paragraphs) -> dict:
|
39 |
|
40 |
summarize = "You are a helpful assistant. Your main task is to summarize articles. You will be given an article that you will generate a summary for. The summary should include all the key points of the article. ONLY RESPOND WITH THE SUMMARY!!!"
|
41 |
|
42 |
summary = f"Summarize the data in the following JSON into {num_paragraphs} paragraph(s) so that it is easy to read and understand:\n"
|
43 |
|
44 |
+
message = [{"role": "system", "content": [{"type": "text", "text": summarize}]},
|
45 |
+
{"role": "user", "content": [{"type": "text", "text": summary + json.dumps(article, indent=4)}]}]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
return message
|
48 |
|
49 |
+
def get_summary(self, article, num_paragraphs) -> str:
|
50 |
+
message = self._get_summary_message(article, num_paragraphs)
|
51 |
summary = self.generate(message)
|
52 |
|
53 |
return summary
|
54 |
|
55 |
+
def _get_questions_message(self, summary, num_questions, difficulty) -> dict:
|
56 |
+
question = f"""
|
57 |
+
You are a helpful assistant. Your main task is to generate {num_questions} multiple choice questions from an article. Respond in the following JSON structure and schema:\n\njson\n```{json.dumps(list((
|
58 |
dict(question=str.__name__, correct_answer=str.__name__, false_answers=[str.__name__, str.__name__, str.__name__]),
|
59 |
dict(question=str.__name__, correct_answer=str.__name__, false_answers=[str.__name__, str.__name__, str.__name__]),
|
60 |
+
dict(question=str.__name__, correct_answer=str.__name__, false_answers=[str.__name__, str.__name__, str.__name__]))), indent=4)}```\n\nThere should only be {num_questions} questions generated. Each question should only have 3 false answers and 1 correct answer. The correct answer should be the most relevant answer based on the context derived from the article. False answers should not contain the correct answer. False answers should contain false information but also be reasonably plausible for answering the question. ONLY RESPOND WITH RAW JSON!!!
|
61 |
+
"""
|
|
|
62 |
|
63 |
+
questions = f"Generate {difficulty.lower()} questions and answers from the following article:\n"
|
64 |
|
65 |
+
message = [{"role": "system", "content": [{"type": "text", "text": question}]},
|
66 |
+
{"role": "user", "content": [{"type": "text", "text": questions + summary}]}]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
return message
|
69 |
|
70 |
+
def get_questions(self, summary, num_questions, difficulty) -> dict:
|
71 |
+
message = self._get_questions_message(summary, num_questions, difficulty)
|
72 |
questions = self.generate(message)
|
73 |
|
74 |
return json.loads(questions.strip("```").replace("json\n", ""))
|
News.py
CHANGED
@@ -5,58 +5,63 @@ import os
|
|
5 |
__export__ = ["News"]
|
6 |
|
7 |
class News:
|
8 |
-
__EX_SOURCES__ =
|
9 |
-
__CATEGORIES__ =
|
10 |
-
"
|
11 |
-
"
|
12 |
-
"
|
13 |
-
"
|
14 |
-
"
|
15 |
-
"
|
16 |
-
|
17 |
-
}
|
18 |
|
19 |
def __init__(self):
|
20 |
newsapi_key = os.environ.get("NEWS_API_KEY")
|
21 |
self.newsapi = NewsApiClient(api_key=newsapi_key)
|
22 |
|
23 |
def get_sources(self, category=None):
|
24 |
-
sources = self.newsapi.get_sources(language="en", country="us", category=category)["sources"]
|
25 |
sources = {source["name"] for source in sources if source["name"] not in self.__EX_SOURCES__}
|
26 |
-
print(sources)
|
27 |
return sources
|
28 |
-
|
29 |
|
30 |
-
def get_top_headlines(self, num_headlines=
|
31 |
-
sources = self.get_sources(category
|
32 |
|
33 |
headlines = self.newsapi.get_top_headlines(
|
34 |
sources=", ".join(sources),
|
35 |
-
page_size=num_headlines
|
|
|
36 |
)["articles"]
|
37 |
|
|
|
|
|
38 |
return headlines
|
39 |
|
40 |
-
def get_headlines(self, num_headlines=
|
41 |
sources = self.get_sources()
|
42 |
|
43 |
headlines = self.newsapi.get_everything(
|
44 |
q=query,
|
45 |
sources=", ".join(sources),
|
46 |
-
page_size=num_headlines
|
|
|
47 |
)["articles"]
|
48 |
|
|
|
|
|
49 |
return headlines
|
50 |
|
51 |
-
def
|
52 |
for headline in headlines:
|
|
|
|
|
|
|
|
|
53 |
article = Article(headline["url"])
|
54 |
article.download()
|
55 |
article.parse()
|
|
|
56 |
headline["content"] = article.text
|
57 |
-
|
58 |
-
headline["source"] = headline["source"]["name"]
|
59 |
-
del headline["author"]
|
60 |
-
# headline.pop("author", None)
|
61 |
-
|
62 |
return headlines
|
|
|
|
5 |
__export__ = ["News"]
|
6 |
|
7 |
class News:
|
8 |
+
__EX_SOURCES__ = ["ABC News", "Bloomberg", "The Hill", "Fox Sports", "Google News", "Newsweek"]
|
9 |
+
__CATEGORIES__ = [
|
10 |
+
"General",
|
11 |
+
"Business",
|
12 |
+
"Entertainment",
|
13 |
+
"Health",
|
14 |
+
"Science",
|
15 |
+
"Technology"
|
16 |
+
]
|
|
|
17 |
|
18 |
def __init__(self):
|
19 |
newsapi_key = os.environ.get("NEWS_API_KEY")
|
20 |
self.newsapi = NewsApiClient(api_key=newsapi_key)
|
21 |
|
22 |
def get_sources(self, category=None):
|
23 |
+
sources = self.newsapi.get_sources(language="en", country="us", category=category.lower() if category else category)["sources"]
|
24 |
sources = {source["name"] for source in sources if source["name"] not in self.__EX_SOURCES__}
|
|
|
25 |
return sources
|
|
|
26 |
|
27 |
+
def get_top_headlines(self, num_headlines=5, category=None):
|
28 |
+
sources = self.get_sources(category.lower() if category else category)
|
29 |
|
30 |
headlines = self.newsapi.get_top_headlines(
|
31 |
sources=", ".join(sources),
|
32 |
+
page_size=num_headlines,
|
33 |
+
language="en",
|
34 |
)["articles"]
|
35 |
|
36 |
+
headlines = self._get_articles_from_headlines(headlines)
|
37 |
+
|
38 |
return headlines
|
39 |
|
40 |
+
def get_headlines(self, num_headlines=5, query=None):
|
41 |
sources = self.get_sources()
|
42 |
|
43 |
headlines = self.newsapi.get_everything(
|
44 |
q=query,
|
45 |
sources=", ".join(sources),
|
46 |
+
page_size=num_headlines,
|
47 |
+
lanuguage="en",
|
48 |
)["articles"]
|
49 |
|
50 |
+
headlines = self._get_articles_from_headlines(headlines)
|
51 |
+
|
52 |
return headlines
|
53 |
|
54 |
+
def _get_articles_from_headlines(self, headlines):
|
55 |
for headline in headlines:
|
56 |
+
del headline["author"]
|
57 |
+
|
58 |
+
headline["source"] = headline["source"]["name"]
|
59 |
+
|
60 |
article = Article(headline["url"])
|
61 |
article.download()
|
62 |
article.parse()
|
63 |
+
|
64 |
headline["content"] = article.text
|
65 |
+
|
|
|
|
|
|
|
|
|
66 |
return headlines
|
67 |
+
|
app.py
CHANGED
@@ -1,64 +1,271 @@
|
|
1 |
-
|
2 |
-
|
|
|
3 |
from News import News
|
4 |
-
from
|
5 |
|
6 |
-
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
|
|
|
10 |
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
return jsonify(articles)
|
26 |
|
27 |
-
|
28 |
-
@app.get("/get_articles/<string:query>/")
|
29 |
-
@app.get("/get_articles/<int:num_articles>/")
|
30 |
-
@app.get("/get_articles/<int:num_articles>/<string:query>/")
|
31 |
-
def get_articles(num_articles=5, query=None):
|
32 |
-
if query is not None: query = query.lower()
|
33 |
-
articles = news.get_headlines(num_headlines=num_articles, query=query)
|
34 |
-
articles = news.get_articles_from_headlines(articles)
|
35 |
-
|
36 |
-
return jsonify(articles)
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
-
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
def get_questions(num_questions=3, difficulty="average"):
|
53 |
-
if "summary" in request.json:
|
54 |
-
summary = request.json["summary"]
|
55 |
-
questions = gemma.get_questions(gemma.get_questions_message(summary, num_questions, difficulty))
|
56 |
-
elif "summaries" in request.json:
|
57 |
-
summaries = request.json["summaries"]
|
58 |
-
messages = [gemma.get_questions_message(summary) for summary in summaries]
|
59 |
-
questions = gemma.get_questions(messages)
|
60 |
-
else: return jsonify({})
|
61 |
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# %%
|
2 |
+
import random
|
3 |
+
import gradio as gr
|
4 |
from News import News
|
5 |
+
from Gemma import GemmaLLM
|
6 |
|
7 |
+
# %%
|
8 |
+
class Cooldown:
|
9 |
+
...
|
10 |
|
11 |
+
cooldown = Cooldown() ################################ News fetching cooldown in seconds
|
12 |
+
news = News() ######################################## Initialize the News object
|
13 |
+
model = GemmaLLM() ################################### Initialize the Gemma model
|
14 |
|
15 |
+
# %%
|
16 |
+
with gr.Blocks() as demo:
|
17 |
+
gr.Markdown("# News Quiz Application")
|
18 |
+
|
19 |
+
######
|
20 |
+
###### State Variables and Components Initialization
|
21 |
+
######
|
22 |
+
|
23 |
+
init = ( ######################################### Initialize the Gradio interface with components and state variables
|
24 |
|
25 |
+
gr.Markdown("## News Articles"),
|
26 |
+
|
27 |
+
## State variables for news articles and quiz,
|
28 |
+
|
29 |
+
gr.State({}), gr.State([]), gr.State({}),
|
30 |
+
|
31 |
+
gr.Slider(label="Number of Articles", minimum=1, maximum=10, value=3, step=1),
|
32 |
+
gr.Radio(label="Category (optional)", choices=news.__CATEGORIES__),
|
33 |
+
gr.Button("Get Articles"),
|
34 |
+
|
35 |
+
gr.Radio(visible=False),
|
36 |
+
gr.Textbox(visible=False),
|
37 |
+
gr.Button(visible=False),
|
38 |
+
|
39 |
+
gr.Checkbox(visible=False),
|
40 |
+
gr.Textbox(visible=False),
|
41 |
+
gr.Button(visible=False),
|
42 |
|
43 |
+
## State variables for quiz
|
44 |
+
|
45 |
+
gr.State([]), gr.State([]), gr.State(),
|
46 |
+
|
47 |
+
[gr.Radio(visible=False) for _ in range(10)],
|
48 |
+
gr.Button(visible=False),
|
49 |
+
|
50 |
+
gr.Textbox(visible=False),
|
51 |
+
gr.Button(visible=False),
|
|
|
52 |
|
53 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
+
( ################################################ State variables and components for news articles and quiz
|
56 |
+
|
57 |
+
heading,
|
58 |
+
|
59 |
+
## Components for news articles
|
60 |
+
|
61 |
+
article, articles, descriptions,
|
62 |
+
num_headlines, category, get,
|
63 |
+
headline, description, show,
|
64 |
+
summarize, content, ready,
|
65 |
+
|
66 |
+
## Components for quiz
|
67 |
+
|
68 |
+
answers, response, results,
|
69 |
+
quiz, submit,
|
70 |
+
evaluation, read,
|
71 |
+
|
72 |
+
) = init
|
73 |
+
|
74 |
+
def hide_news(): ################################# Hide news-related components
|
75 |
+
num_headlines = gr.Slider(visible=False)
|
76 |
+
category = gr.Radio(visible=False)
|
77 |
+
get = gr.Button(visible=False)
|
78 |
+
|
79 |
+
headline = gr.Radio(visible=False)
|
80 |
+
description = gr.Textbox(visible=False)
|
81 |
+
show = gr.Button(visible=False)
|
82 |
+
|
83 |
+
summarize = gr.Checkbox(visible=False)
|
84 |
+
content = gr.Textbox(visible=False)
|
85 |
+
ready = gr.Button(visible=False)
|
86 |
|
87 |
+
return num_headlines, category, get, headline, description, show, summarize, content, ready
|
88 |
|
89 |
+
def show_news(): ################################# Show news-related components
|
90 |
+
num_headlines = gr.Slider(label="Number of Articles", minimum=1, maximum=10, value=3, step=1, visible=True)
|
91 |
+
category = gr.Radio(label="Category (optional)", choices=news.__CATEGORIES__, visible=True)
|
92 |
+
get = gr.Button("Get Articles", visible=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
|
94 |
+
return num_headlines, category, get
|
95 |
+
|
96 |
+
def show_headline(headlines, descriptions): ###### Show news headlines and descriptions
|
97 |
+
headline = gr.Radio(label="News Headlines", choices=headlines, value=headlines[0], interactive=True, visible=True)
|
98 |
+
description = gr.Textbox(label="Headline Description", value=descriptions[0], visible=True)
|
99 |
+
show = gr.Button("Show Content", visible=True)
|
100 |
+
|
101 |
+
return headline, description, show
|
102 |
+
|
103 |
+
def show_content(summary): ####################### Show article content and summary
|
104 |
+
summarize = gr.Checkbox(label="Show Summary?", value=True, interactive=True, visible=True)
|
105 |
+
content = gr.Textbox(label="Summary", value=summary, visible=True)
|
106 |
+
ready = gr.Button("Begin Quiz", visible=True)
|
107 |
+
|
108 |
+
return summarize, content, ready
|
109 |
+
|
110 |
+
def format_mcq(mcq): ############################# Format multiple choice question for quiz
|
111 |
+
if not mcq or not isinstance(mcq, dict):
|
112 |
+
print(f"Multiple choice question object is a {type(mcq)} but should be {type(dict())}.")
|
113 |
+
return "Invalid multiple choice question.", None
|
114 |
+
|
115 |
+
question = mcq.get('question', 'No question provided.')
|
116 |
+
answer = mcq.get('correct_answer', 'No correct answer provided.')
|
117 |
+
false_answers = mcq.get('false_answers', [])
|
118 |
+
|
119 |
+
if not isinstance(false_answers, list):
|
120 |
+
print(f"False answers is a {type(false_answers)} but should be {type(list())}.")
|
121 |
+
return "Invalid false answers format.", None
|
122 |
+
|
123 |
+
options = random.shuffle([answer] + false_answers)
|
124 |
+
|
125 |
+
print("Question:", question)
|
126 |
+
print(f"Formatted options: {options}")
|
127 |
+
|
128 |
+
return question, options, answer
|
129 |
+
|
130 |
+
def hide_quiz(): ################################# Hide quiz-related components
|
131 |
+
quiz = [gr.Radio(visible=False) for _ in range(10)]
|
132 |
+
submit = gr.Button(visible=False)
|
133 |
+
|
134 |
+
evaluation = gr.Textbox(visible=False)
|
135 |
+
read = gr.Button(visible=False)
|
136 |
|
137 |
+
return read, evaluation, submit, *quiz
|
138 |
+
|
139 |
+
def show_quiz(mcqs): ############################# Show quiz-related components
|
140 |
+
quiz = [(mcq["question"], mcq["false_answers"], mcq["correct_answer"]) for mcq in mcqs]
|
141 |
+
quiz = [(question, random.sample(distractors + [answer], 4), answer) for question, distractors, answer in quiz]
|
142 |
+
questions, options, answers = zip(*quiz) if quiz else ([], [], [])
|
143 |
+
|
144 |
+
print("options", len(options))
|
145 |
+
|
146 |
+
quiz = [gr.Radio(label=f"{i + 1}: {questions[i]}", choices=options[i], visible=True) for i in range(len(mcqs))]\
|
147 |
+
+ [gr.Radio(visible=False) for _ in range(10 - len(mcqs))]
|
148 |
+
|
149 |
+
print("quiz", len(quiz))
|
150 |
+
|
151 |
+
submit = gr.Button("Submit Answers", interactive=bool(answers), visible=True)
|
152 |
+
|
153 |
+
return submit, list(answers), *quiz
|
154 |
+
|
155 |
+
def show_eval(eva): ############################## Show evaluation of user's response to the quiz
|
156 |
+
evaluation = gr.Textbox(label="Evaluation", value=eva, visible=True)
|
157 |
+
read = gr.Button("Read Articles", visible=True)
|
158 |
+
|
159 |
+
return evaluation, read
|
160 |
+
|
161 |
+
######
|
162 |
+
###### Get and Display News Articles
|
163 |
+
######
|
164 |
+
|
165 |
+
def get_headline(category, num_headlines): ####### Get news headlines based on selected category and number
|
166 |
+
articles = news.get_top_headlines(category=category, num_headlines=num_headlines)
|
167 |
+
headlines, descriptions = zip(*[(article['title'], article.get('description', 'No description available.')) for article in articles])
|
168 |
+
show = show_headline(headlines, descriptions)
|
169 |
+
descriptions = {h: d for h, d in zip(headlines, descriptions)}
|
170 |
+
return articles, descriptions, *show
|
171 |
+
|
172 |
+
get.click(get_headline, inputs=[category, num_headlines], outputs=[articles, descriptions, headline, description, show])
|
173 |
+
|
174 |
+
def get_description(descriptions, headline): ##### Get description for the selected headline
|
175 |
+
description = "No description available."
|
176 |
+
|
177 |
+
if not descriptions: print("Descriptions are empty.")
|
178 |
+
elif not headline: print("Headline is empty.")
|
179 |
+
elif not isinstance(descriptions, dict): print(f"Descriptions is a {type(descriptions)} but should be {type(dict())}.")
|
180 |
+
else: description = descriptions.get(headline, description)
|
181 |
+
|
182 |
+
return description
|
183 |
+
|
184 |
+
headline.change(get_description, inputs=[descriptions, headline], outputs=[description])
|
185 |
+
|
186 |
+
def get_article(articles, headline): ############# Get article for the selected headline
|
187 |
+
headlines = [a['title'] for a in articles]
|
188 |
+
|
189 |
+
if headline not in headlines: return {}
|
190 |
+
return articles[headlines.index(headline)]
|
191 |
+
|
192 |
+
show.click(get_article, inputs=[articles, headline], outputs=[article])
|
193 |
+
|
194 |
+
def get_content(articles, article): ############## Get content for the selected article
|
195 |
+
if "summary" not in article:
|
196 |
+
idx = articles.index(article)
|
197 |
+
articles[idx]["summary"] = model.get_summary(article, 1)
|
198 |
+
article = articles[idx]
|
199 |
+
|
200 |
+
return articles, article, *show_content(article.get("summary", "No summary available."))
|
201 |
+
|
202 |
+
article.change(get_content, inputs=[articles, article], outputs=[articles, article, summarize, content, ready])
|
203 |
+
|
204 |
+
def toggle_summary(article, summarize): ########## Toggle between showing summary and full content
|
205 |
+
content = "No article available."
|
206 |
+
|
207 |
+
if not article: print("Selected article is empty.")
|
208 |
+
elif not isinstance(article, dict): print(f"Selected article is a {type(article)} but should be {type(dict())}.")
|
209 |
+
elif summarize: content = article.get("summary", "Summary not available.")
|
210 |
+
else: content = article.get("content", "Content not available.")
|
211 |
+
|
212 |
+
return content
|
213 |
+
|
214 |
+
summarize.change(toggle_summary, inputs=[article, summarize], outputs=[content])
|
215 |
+
|
216 |
+
######
|
217 |
+
###### Quiz Generation and Evaluation
|
218 |
+
######
|
219 |
+
|
220 |
+
def get_quiz(content): ########################### Generate quiz questions from the article content
|
221 |
+
multichoicequests = []
|
222 |
+
|
223 |
+
if not content: mcqs = multichoicequests
|
224 |
+
else: mcqs = model.get_questions(content, 3, "Moderate")
|
225 |
+
|
226 |
+
if not isinstance(mcqs, list): print(f"Multiple choice questions object is a {type(mcqs)} but should be {type(list())}.")
|
227 |
+
elif len(mcqs) == 0: print("Content is empty or no multiple choice questions generated.")
|
228 |
+
|
229 |
+
for mcq in mcqs:
|
230 |
+
missing = set()
|
231 |
+
|
232 |
+
if not isinstance(mcq, dict): print(f"Multiple choice question object is {type(mcq)} but should be {type(dict())}.")
|
233 |
+
else: missing = set(['question', 'correct_answer', 'false_answers']) - set(mcq.keys())
|
234 |
+
|
235 |
+
if missing: print(f"Multiple choice question object is missing keys: {missing}.")
|
236 |
+
else: multichoicequests.append(mcq)
|
237 |
+
|
238 |
+
return gr.Markdown("## News Quiz"), *hide_news(), *show_quiz(multichoicequests)
|
239 |
+
|
240 |
+
ready.click(get_quiz, inputs=[content], outputs=[
|
241 |
+
heading, num_headlines, category, get, headline, description, show, summarize, content, ready, submit, answers, *quiz])
|
242 |
+
|
243 |
+
def get_evaluation(answers, *quiz): ############## Evaluate the user's responses to the quiz
|
244 |
+
results = -1
|
245 |
+
|
246 |
+
if not answers: print("Answers are empty.")
|
247 |
+
elif not quiz: print("Quiz is empty.")
|
248 |
+
elif not isinstance(answers, list): print(f"Answers is a {type(answers)} but should be {type(list())}.")
|
249 |
+
else:
|
250 |
+
results = sum(1 for ans, resp in zip(answers, list(quiz)) if ans == resp) / len(answers)
|
251 |
+
results = round(results, 4)
|
252 |
+
|
253 |
+
if 0.9 <= results <= 1.0: evaluation = f"Excellent! You scored {results * 100}%."
|
254 |
+
elif 0.8 <= results < 0.9: evaluation = f"Great job! You scored {results * 100}%."
|
255 |
+
elif 0.7 <= results < 0.8: evaluation = f"Good effort! You scored {results * 100}%."
|
256 |
+
elif 0.6 <= results < 0.7: evaluation = f"You scored {results * 100}%. Keep practicing!"
|
257 |
+
elif 0.5 <= results < 0.6: evaluation = f"You scored {results * 100}%. You can do better!"
|
258 |
+
elif results < 0: evaluation = f"Unable to evaluate. Please try again."
|
259 |
+
else: evaluation = f"You scored {results * 100}%. Keep trying!"
|
260 |
+
|
261 |
+
return show_eval(evaluation)
|
262 |
+
|
263 |
+
submit.click(get_evaluation, inputs=[answers, *quiz], outputs=[evaluation, read])
|
264 |
+
|
265 |
+
def read_articles(): ############################# Reset the interface to read articles again
|
266 |
+
return gr.Markdown("## News Articles"), *show_news(), *hide_quiz()
|
267 |
+
|
268 |
+
read.click(read_articles, outputs=[heading, num_headlines, category, get, read, evaluation, submit, *quiz])
|
269 |
+
|
270 |
+
demo.launch()
|
271 |
+
|