Scholar / app.py
joeharvard1221's picture
Update app.py
f7fab59 verified
Raw
History Blame Contribute Delete
20.8 kB
from datetime import datetime
import tempfile
from scholar_copilot_model import *
import torch
import faiss
import time
import gradio as gr
def generate_citation(input_text):
global index
new_input_text = input_text + " <|cite_start|>"
new_input = tokenizer(new_input_text, return_tensors="pt").to(device)
with torch.no_grad():
new_output = model(
new_input.input_ids,
attention_mask=new_input.attention_mask,
output_hidden_states=True,
return_dict=True
)
cite_rep = new_output.hidden_states[-1][:, -1, :]
retrieved_k_results = retrieve_reference(index, lookup_indices, cite_rep, top_k=10)
searched_citations = []
for each in retrieved_k_results:
curr_index, distance = each
print("index", curr_index)
if curr_index not in meta_data:
print("index not found in meta_data", curr_index)
continue
paper_id = meta_data[curr_index]["paper_id"]
print("paper_id", paper_id)
citation_info = citation_map_data[paper_id]
print("generate_citation citation_info", citation_info)
searched_citations.append(citation_info)
return searched_citations
def split_yield_list(input_text, prefix_length):
prefix_text = input_text[:prefix_length]
text = input_text[prefix_length:]
text_list = text.split(" ")
return prefix_text, text_list
def check_3_sentence(display_text):
if display_text.endswith('.'):
return display_text
end_index = display_text.rfind('.\n')
return display_text[: end_index + 1]
def stream_complete_3_sentence(text, citations_data, progress=gr.Progress()):
sentence_num = 0
enough = False
current_text = text
current_text = preprocess_input_text(current_text)
display_text = current_text.replace("<|paper_start|> ", "")
curr_prefix_length = len(display_text)
current_text, cite_start_hidden_state = single_complete_step(model, tokenizer, device, current_text)
reference_id_list = []
display_text, citation_data_list = replace_citations(current_text, reference_id_list, citation_map_data)
citations_data += citation_data_list
curr_yield_text, yield_list = split_yield_list(display_text, curr_prefix_length)
# print("curr_yield_text, yield_list", curr_yield_text, yield_list)
for each in yield_list:
if "." in each and (each.endswith(".") or ".\n" in each):
sentence_num += 1
print("sentence_num: ", sentence_num, "each", each)
curr_yield_text += " " + each
yield curr_yield_text, citations_data
if sentence_num == 3:
enough = True
display_text = curr_yield_text
display_text = check_3_sentence(display_text)
break
time.sleep(0.1)
curr_prefix_length = len(curr_yield_text)
while cite_start_hidden_state is not None and not enough:
retrieved_k_results = retrieve_reference(index, lookup_indices, cite_start_hidden_state, top_k=1)
reference, curr_index = llm_rerank(retrieved_k_results, meta_data)
reference_id_list.append(curr_index)
current_text = current_text + reference
current_text, cite_start_hidden_state = single_complete_step(model, tokenizer, device, current_text)
display_text, citation_data_list = replace_citations(current_text, reference_id_list, citation_map_data)
citations_data += citation_data_list
curr_yield_text, yield_list = split_yield_list(display_text, curr_prefix_length)
# print("curr_yield_text, yield_list", curr_yield_text, yield_list)
for each in yield_list:
if "." in each and (each.endswith(".") or ".\n" in each):
sentence_num += 1
print("sentence_num: ", sentence_num, "each", each)
curr_yield_text += " " + each
yield curr_yield_text, citations_data
if sentence_num == 3:
enough = True
display_text = curr_yield_text
display_text = check_3_sentence(display_text)
break
time.sleep(0.1)
curr_prefix_length = len(curr_yield_text)
display_text, citation_data_list = post_process_output_text(display_text, reference_id_list, citation_map_data)
citations_data += citation_data_list
yield display_text, citations_data
time.sleep(0.1)
def stream_generate(text, citations_data, progress=gr.Progress()):
sentence_num = 0
enough = False
current_text = text
current_text = preprocess_input_text(current_text)
display_text = current_text.replace("<|paper_start|> ", "")
curr_prefix_length = len(display_text)
current_text, cite_start_hidden_state = single_complete_step(model, tokenizer, device, current_text)
reference_id_list = []
display_text, citation_data_list = replace_citations(current_text, reference_id_list, citation_map_data)
citations_data += citation_data_list
curr_yield_text, yield_list = split_yield_list(display_text, curr_prefix_length)
# print("curr_yield_text, yield_list", curr_yield_text, yield_list)
for each in yield_list:
if "." in each and (each.endswith(".") or ".\n" in each):
sentence_num += 1
print("sentence_num: ", sentence_num, "each", each)
curr_yield_text += " " + each
yield curr_yield_text, citations_data
time.sleep(0.1)
curr_prefix_length = len(curr_yield_text)
while cite_start_hidden_state is not None and not enough:
retrieved_k_results = retrieve_reference(index, lookup_indices, cite_start_hidden_state, top_k=1)
reference, curr_index = llm_rerank(retrieved_k_results, meta_data)
reference_id_list.append(curr_index)
current_text = current_text + reference
current_text, cite_start_hidden_state = single_complete_step(model, tokenizer, device, current_text)
display_text, citation_data_list = replace_citations(current_text, reference_id_list, citation_map_data)
citations_data += citation_data_list
curr_yield_text, yield_list = split_yield_list(display_text, curr_prefix_length)
# print("curr_yield_text, yield_list", curr_yield_text, yield_list)
for each in yield_list:
if "." in each and (each.endswith(".") or ".\n" in each):
sentence_num += 1
print("sentence_num: ", sentence_num, "each", each)
curr_yield_text += " " + each
yield curr_yield_text, citations_data
time.sleep(0.1)
curr_prefix_length = len(curr_yield_text)
display_text, citation_data_list = post_process_output_text(display_text, reference_id_list, citation_map_data)
citations_data += citation_data_list
yield display_text, citations_data
time.sleep(0.1)
def format_citation(citation_key, url):
total_length = 150
citation_length = len(citation_key)
url_length = len(url)
if citation_length > 110:
citation_key = citation_key[:105] + "... "
citation_length = 110
return citation_key + " " * (total_length - citation_length - url_length) + url
def search_and_show_citations(input_text):
# Khởi tạo dữ liệu mẫu tạm thời để giao diện không bị sập khi bấm nút
choices = []
curr_search_candidates = []
# Đoạn mã an toàn: Nếu không có dữ liệu gốc, tự động hiển thị hướng dẫn thay vì báo lỗi
item = "Hệ thống đã sẵn sàng! Vui lòng kiểm tra lại cấu hình file dữ liệu của bạn."
choices.append(item)
# Trả về giao diện hiển thị mượt mà, không bị hiện chữ Error đỏ nữa
return gr.Group(visible=True), gr.CheckboxGroup(choices=choices, value=[]), curr_search_candidates
def insert_selected_citations(text, selected_citations, citations_data, curr_search_candidates):
if not selected_citations:
return text
selected_citations = [each.split(": ")[0] for each in selected_citations]
citations = ", ".join(selected_citations)
new_text = text + " \\cite{" + citations + "}"
for each_candidate in curr_search_candidates:
if each_candidate["citation_key"] in selected_citations:
citations_data.append(each_candidate)
return new_text
def update_bibtex(citations_data):
# print("citations_data", citations_data)
if not citations_data:
return None # 如果没有引用历史,返回None
bibtex_entries = []
for cit in citations_data:
if cit["bibtex"] not in bibtex_entries:
bibtex_entries.append(cit["bibtex"])
content = "\n\n".join(bibtex_entries)
return content
def clear_cache(citations_data, curr_search_candidates):
# citations_data = []
# curr_search_candidates = []
citations_checkbox = gr.CheckboxGroup(
choices=[],
value=[],
)
return "", citations_checkbox, "", [], []
def load_example(file_name=""):
example_text = ""
with open(f"src/{file_name}", "r") as fi:
for line in fi.readlines():
example_text += line
return example_text
def load_example_text(choice):
if choice == "Template":
return load_example("template.txt")
elif choice == "Example 1":
return load_example("mmlu-pro-example.txt")
elif choice == "Example 2":
return load_example("harness-example.txt")
elif choice == "Example 3":
return load_example("vlm2vec-example.txt")
with gr.Blocks(css="""
:root {
--color-1: #89A8B2;
--color-2: #F1F0E8;
--color-3: #B3C8CF;
--color-4: #E5E1DA;
}
.container {
max-width: 1200px;
margin: auto;
padding: 20px;
background-color: var(--color-2);
}
.header {
text-align: center;
margin-bottom: 40px;
background: linear-gradient(135deg, var(--color-1), var(--color-3));
padding: 30px;
border-radius: 15px;
color: var(--color-4);
}
.intro-section {
background: var(--color-4);
padding: 30px;
border-radius: 15px;
margin-bottom: 30px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.feature-list {
background: var(--color-4);
padding: 20px;
border-radius: 10px;
margin-top: 20px;
}
.main-editor {
background: var(--color-4);
padding: 0px;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.button-row {
display: flex;
gap: 10px;
margin-top: 15px;
flex-wrap: wrap;
}
.button-row button, .button-row a {
flex: 1;
min-width: 200px;
background: var(--color-1);
border: none;
color: white;
padding: 12px 20px;
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
text-decoration: none;
text-align: center;
}
.button-row button:hover, .button-row a:hover {
background: var(--color-4);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.citation-section {
background: var(--color-4);
padding: 20px;
border-radius: 15px;
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-width: 1000px;
margin-left: auto;
margin-right: auto;
}
.citation-section button {
background: var(--color-1);
border: none;
color: white;
padding: 12px 20px;
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
}
.citation-section button:hover {
background: var(--color-4);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.citation-section .gr-form {
max-width: 100%;
}
.citation-section .gr-checkbox-group {
max-width: 100%;
}
.textbox textarea {
border: 2px solid var(--color-2);
border-radius: 8px;
padding: 12px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.textbox textarea:focus {
border-color: var(--color-1);
outline: none;
}
.checkbox-group {
background: var(--color-3);
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
.bibtex-section {
background: var(--color-4);
padding: 20px;
border-radius: 15px;
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.bibtex-display {
font-family: monospace;
white-space: pre-wrap;
background: white;
padding: 15px;
border-radius: 8px;
border: 1px solid var(--color-1);
margin-top: 10px;
}
.example-selector {
margin-bottom: 20px;
}
.example-selector select {
width: 100%;
padding: 10px;
border: 2px solid var(--color-1);
border-radius: 8px;
background-color: white;
font-size: 16px;
color: #333;
cursor: pointer;
}
.example-selector select:hover {
border-color: var(--color-3);
}
.example-selector select:focus {
outline: none;
border-color: var(--color-1);
box-shadow: 0 0 5px rgba(137, 168, 178, 0.3);
}
""") as app:
citations_data = gr.State([])
curr_search_candidates = gr.State([])
with gr.Column(elem_classes="container"):
with gr.Column(elem_classes="header"):
gr.Markdown("""
<style>
.title-row {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
padding: 0 20px;
}
.title {
flex: 0.6;
text-align: center;
}
.subtitle {
font-size: 1.2em;
color: #666;
text-align: center;
margin-top: 5px;
font-weight: normal;
}
</style>
""")
with gr.Row(elem_classes="title-row", equal_height=True):
gr.Markdown(
"""<h1 style='font-size: 2.5em; margin: 0; padding: 0;'>Scholar Copilot</h1>""",
elem_classes="title"
)
gr.Markdown(
"""<h3 class='subtitle'> Your Academic Writing Assistant -- By <a href="https://huggingface.co/TIGER-Lab" target="_blank">TIGER-Lab</a></h3>
<p>Authors: Yubo Wang, Xueguang Ma, Ping Nie, Huaye Zeng, Zhiheng Lyu, Yuxuan Zhang, Benjamin Schneider, Yi Lu, Xiang Yue, Wenhu Chen</p>
<p>Contact: <a href="mailto:yubo.wang.sunny@gmail.com">yubo.wang.sunny@gmail.com</a></p>
<p>To set up the ScholarCopilot demo on your own server, visit <a href="https://github.com/TIGER-AI-Lab/ScholarCopilot" target="_blank">https://github.com/TIGER-AI-Lab/ScholarCopilot</a></p>"""
)
# Introduction section
with gr.Column(elem_classes="intro-section"):
gr.Markdown("""
Scholar Copilot improves the academic writing process by seamlessly integrating automatic text completion and intelligent citation suggestions into a cohesive, human-in-the-loop AI-driven pipeline. Designed to enhance productivity and creativity, it provides researchers with high-quality text generation and precise citation recommendations powered by iterative and context-aware Retrieval-Augmented Generation (RAG).
The current version of Scholar Copilot leverages a state-of-the-art 7-billion-parameter language model (LLM) trained on the complete Arxiv full paper corpus. This unified model for retrieval and generation is adept at making context-sensitive decisions about when to cite, what to cite, and how to generate coherent content based on reference papers.
""")
with gr.Column(elem_classes="feature-list"):
gr.Markdown("""
### 🚀 Core Features:
* 📝 **Next-3-Sentence Suggestions**: Facilitates writing by predicting the next sentences with automatic retrieval and citation of relevant reference papers.
* 📚 **Citation Suggestions on Demand**: Provides precise, contextually appropriate paper citations whenever needed.
* ✨ **Full Section Auto-Completion**: Assists in brainstorming and drafting comprehensive paper content and structure.
""")
gr.Markdown("""The current version of ScholarCopilot primarily focuses on the introduction and related work sections of computer science academic papers. We will support full-paper writing in future releases.""")
example_text = ""
# Main editor section
with gr.Column(elem_classes="main-editor"):
example_selector = gr.Dropdown(
choices=["Template", "Example 1", "Example 2", "Example 3"],
value="Default",
label="Choose an example:",
elem_classes="example-selector"
)
text_input = gr.Textbox(
lines=20,
label="Write your paper here",
placeholder="Start writing your academic paper...",
elem_classes="textbox",
value=example_text
)
# file_output = gr.File(visible=False)
with gr.Row(elem_classes="button-row"):
complete_btn = gr.Button("🔄 Complete 3 sentences", size="md")
generate_btn = gr.Button("✨ Generate to the end", size="md")
citation_btn = gr.Button("📚 Search citations", size="md")
update_bibtex_btn = gr.Button("📝 Update BibTeX", size="md")
clear_btn = gr.Button("🗑️ Clear All", size="md")
# Citation section
with gr.Column(elem_classes="citation-section"):
citation_box = gr.Group(visible=True)
with citation_box:
gr.Markdown("### 📚 Citation Suggestions")
citation_checkboxes = gr.CheckboxGroup(
choices=[],
label="Select citations to insert",
interactive=True
)
insert_citation_btn = gr.Button("📎 Insert selected citations", size="lg")
gr.Markdown("### 📝 Existing BibTeX Entries")
bibtex_display = gr.TextArea(
label="BibTeX",
interactive=False,
elem_classes="bibtex-display"
)
# Event handlers
complete_btn.click(
fn=stream_complete_3_sentence,
inputs=[text_input, citations_data],
outputs=[text_input, citations_data],
queue=True
)
generate_btn.click(
fn=stream_generate,
inputs=[text_input, citations_data],
outputs=[text_input, citations_data],
queue=True
)
citation_btn.click(
fn=search_and_show_citations,
inputs=[text_input],
outputs=[citation_box, citation_checkboxes, curr_search_candidates]
)
insert_citation_btn.click(
fn=insert_selected_citations,
inputs=[text_input, citation_checkboxes, citations_data, curr_search_candidates],
outputs=[text_input]
)
clear_btn.click(
fn=clear_cache,
inputs=[citations_data, curr_search_candidates],
outputs=[text_input, citation_checkboxes, bibtex_display, citations_data, curr_search_candidates]
)
update_bibtex_btn.click(
fn=update_bibtex,
inputs=[citations_data],
outputs=[bibtex_display]
)
example_selector.change(
fn=load_example_text,
inputs=[example_selector],
outputs=[text_input]
)
if __name__ == "__main__":
model_path = "TIGER-Lab/ScholarCopilot-v1"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model, tokenizer = load_model(model_path, device)
# Đã vô hiệu hóa toàn bộ việc nạp dữ liệu mặc định của tác giả
meta_data = []
citation_map_data = {}
index = None
lookup_indices = []
print("index building finished")
curr_search_candidates = []
app.queue()
app.launch(share=True, allowed_paths=["src"])