MZhaovo commited on
Commit
172594a
1 Parent(s): c0e6fe4

Upload 5 files

Browse files
Files changed (5) hide show
  1. llama_func.py +180 -0
  2. main.py +101 -0
  3. presets.py +58 -0
  4. requirements.txt +5 -0
  5. utils.py +67 -0
llama_func.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader, download_loader
3
+ from llama_index import Document, LLMPredictor, PromptHelper, QuestionAnswerPrompt, JSONReader
4
+ from langchain.llms import OpenAIChat, OpenAI
5
+ from zipfile import ZipFile
6
+ from googlesearch import search as google_search
7
+ from baidusearch.baidusearch import search as baidu_search
8
+ import traceback
9
+ import openai
10
+
11
+ from utils import *
12
+
13
+ def save_index(index, index_name, exist_ok=False):
14
+ file_path = f"./index/{index_name}.json"
15
+
16
+ if not os.path.exists(file_path) or exist_ok:
17
+ index.save_to_disk(file_path)
18
+ print(f'Saved file "{file_path}".')
19
+ else:
20
+ i = 1
21
+ while True:
22
+ new_file_path = f'{os.path.splitext(file_path)[0]}_{i}{os.path.splitext(file_path)[1]}'
23
+ if not os.path.exists(new_file_path):
24
+ index.save_to_disk(new_file_path)
25
+ print(f'Saved file "{new_file_path}".')
26
+ break
27
+ i += 1
28
+
29
+ def construct_index(api_key, file_list, index_name, max_input_size=4096, num_outputs=512, max_chunk_overlap=20, raw=False):
30
+ documents = []
31
+ if not raw:
32
+ txt_set = []
33
+ for file in file_list:
34
+ if os.path.splitext(file.name)[1] == '.pdf':
35
+ CJKPDFReader = download_loader("CJKPDFReader")
36
+ loader = CJKPDFReader()
37
+ documents += loader.load_data(file=file.name)
38
+ elif os.path.splitext(file.name)[1] == '.docx':
39
+ DocxReader = download_loader("DocxReader")
40
+ loader = DocxReader()
41
+ documents += loader.load_data(file=file.name)
42
+ elif os.path.splitext(file.name)[1] == '.epub':
43
+ EpubReader = download_loader("EpubReader")
44
+ loader = EpubReader()
45
+ documents += loader.load_data(file=file.name)
46
+ else:
47
+ with open(file.name, 'r', encoding="utf-8") as f:
48
+ txt_set.append(f.read())
49
+ documents += [Document(k) for k in txt_set]
50
+ else:
51
+ documents += [Document(k.text.encode("UTF-8", errors="strict").decode()) for k in file_list]
52
+
53
+ # Customizing LLM
54
+ llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo", openai_api_key=api_key))
55
+ prompt_helper = PromptHelper(max_input_size, num_outputs, max_chunk_overlap)
56
+
57
+ index = GPTSimpleVectorIndex(documents, llm_predictor=llm_predictor, prompt_helper=prompt_helper)
58
+
59
+ if not raw:
60
+ save_index(index, index_name)
61
+ newlist = refresh_json_list(plain=True)
62
+ return newlist, newlist
63
+ else:
64
+ save_index(index, index_name, exist_ok=True)
65
+ return index
66
+
67
+ def chat_ai(api_key, index_select, question, prompt_tmpl, sim_k, chat_tone ,context, chatbot, search_mode=[], suggested_user_question = ""):
68
+ os.environ["OPENAI_API_KEY"] = api_key
69
+ print(f"Question: {question}")
70
+ if question=="":
71
+ question = suggested_user_question
72
+
73
+ if chat_tone == 0:
74
+ temprature = 2
75
+ elif chat_tone == 1:
76
+ temprature = 1
77
+ else:
78
+ temprature = 0.5
79
+
80
+ if not search_mode:
81
+ response = ask_ai(api_key, index_select, question, prompt_tmpl, sim_k, temprature, context)
82
+ else:
83
+ print(f"You asked: {question}")
84
+ BeautifulSoupWebReader = download_loader("BeautifulSoupWebReader")
85
+ loader = BeautifulSoupWebReader()
86
+ chat = OpenAI(model_name="gpt-3.5-turbo", openai_api_key=api_key)
87
+ search_terms = chat.generate([f"Please extract search terms from the user’s question. The search terms is a concise sentence, which will be searched on Google to obtain relevant information to answer the user’s question, too generalized search terms doesn’t help. Please provide no more than two search terms. Please provide the most relevant search terms only, the search terms should directly correspond to the user’s question. Please separate different search items with commas, with no quote marks. The user’s question is: {question}"]).generations[0][0].text.strip()
88
+ search_terms = search_terms.replace('"', '')
89
+ search_terms = search_terms.replace(".", "")
90
+ links = []
91
+ for keywords in search_terms.split(","):
92
+ keywords = keywords.strip()
93
+ for search_engine in search_mode:
94
+ if "Google" in search_engine:
95
+ print(f"Googling: {keywords}")
96
+ search_iter = google_search(keywords, num_results=5)
97
+ links += [next(search_iter) for _ in range(10)]
98
+ if "Baidu" in search_engine:
99
+ print(f"Baiduing: {keywords}")
100
+ search_results = baidu_search(keywords, num_results=5)
101
+ links += [i["url"] for i in search_results if i["url"].startswith("http") and (not "@" in i["url"])]
102
+ if "Manual" in search_engine:
103
+ print(f"Searching manually: {keywords}")
104
+ print("Please input links manually. (Enter 'q' to quit.)")
105
+ while True:
106
+ link = input("请手动输入一个链接:\n")
107
+ if link == "q":
108
+ break
109
+ else:
110
+ links.append(link)
111
+ links = list(set(links))
112
+ if len(links) == 0:
113
+ msg = "No links found."
114
+ print(msg)
115
+ chatbot.append((question, msg))
116
+ return context, chatbot, gr.Dropdown.update(choices=[])
117
+ print("Extracting data from links...")
118
+ print('\n'.join(links))
119
+ documents = loader.load_data(urls=links)
120
+ # convert to utf-8 encoding
121
+
122
+ index = construct_index(api_key, documents, " ".join(search_terms.split(",")), raw=True)
123
+
124
+ print("Generating response...")
125
+ response = ask_ai(api_key, index_select, question, prompt_tmpl, sim_k, temprature, context, raw = index)
126
+ response = response.split("\n")
127
+ suggested_next_turns = []
128
+ for index, line in enumerate(response):
129
+ if "next user turn" in line:
130
+ suggested_next_turns = response[index+1:]
131
+ response = response[:index]
132
+ break
133
+ suggested_next_turns = [i.split()[1] for i in suggested_next_turns]
134
+ response = "\n".join(response)
135
+ response = parse_text(response)
136
+ context.append({"role": "user", "content": question})
137
+ context.append({"role": "assistant", "content": response})
138
+ chatbot.append((question, response))
139
+ os.environ["OPENAI_API_KEY"] = ""
140
+ return context, chatbot, gr.Dropdown.update(choices=suggested_next_turns)
141
+
142
+
143
+
144
+ def ask_ai(api_key, index_select, question, prompt_tmpl, sim_k=1, temprature=0, prefix_messages=[], raw = None):
145
+ os.environ["OPENAI_API_KEY"] = api_key
146
+ if raw is not None:
147
+ index = raw
148
+ else:
149
+ index = load_index(index_select)
150
+
151
+ prompt = QuestionAnswerPrompt(prompt_tmpl)
152
+
153
+ llm_predictor = LLMPredictor(llm=OpenAI(temperature=temprature, model_name="gpt-3.5-turbo", openai_api_key=api_key, prefix_messages=prefix_messages))
154
+
155
+ try:
156
+ response = index.query(question, llm_predictor=llm_predictor, similarity_top_k=sim_k, text_qa_template=prompt)
157
+ except:
158
+ traceback.print_exc()
159
+ return ""
160
+
161
+ print(f"Response: {response.response}")
162
+ os.environ["OPENAI_API_KEY"] = ""
163
+ return response.response
164
+
165
+
166
+ def load_index(index_name):
167
+ index_path = f"./index/{index_name}.json"
168
+ if not os.path.exists(index_path):
169
+ return None
170
+
171
+ index = GPTSimpleVectorIndex.load_from_disk(index_path)
172
+ return index
173
+
174
+ def display_json(json_select):
175
+ json_path = f"./index/{json_select}.json"
176
+ if not os.path.exists(json_path):
177
+ return None
178
+ documents = JSONReader().load_data(f"./index/{json_select}.json")
179
+
180
+ return documents[0]
main.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+
4
+ from llama_func import *
5
+ from utils import *
6
+ from presets import *
7
+
8
+ os.environ['OPENAI_API_KEY'] = ""
9
+
10
+ with gr.Blocks() as llama_difu:
11
+ chat_context = gr.State([])
12
+ new_google_chat_context = gr.State([])
13
+
14
+ with gr.Row():
15
+ with gr.Column(scale=1):
16
+ with gr.Box():
17
+ gr.Markdown("**OpenAI API-Key**")
18
+ api_key = gr.Textbox(show_label=False, placeholder="Please enter your OpenAI API-key",label="OpenAI API-Key", value="", type="password").style(container=False)
19
+ with gr.Column(scale=3):
20
+ with gr.Box():
21
+ gr.Markdown("**Select Index**")
22
+ with gr.Row():
23
+ with gr.Column(scale=12):
24
+ index_select = gr.Dropdown(choices=refresh_json_list(plain=True), show_label=False).style(container=False)
25
+ with gr.Column(min_width=30, scale=1):
26
+ index_refresh_btn = gr.Button("🔄").style()
27
+
28
+
29
+ with gr.Tab("Ask"):
30
+ with gr.Box():
31
+ with gr.Column():
32
+ gr.Markdown("## Ask")
33
+ with gr.Column():
34
+ with gr.Accordion("Prompt Template", open=False):
35
+ with gr.Row():
36
+ sim_k = gr.Slider(1, 10, 1, step=1, label="The Number of Similarity chunks", interactive=True, show_label=True)
37
+ tempurature = gr.Slider(0, 2, 0.5, step=0.1, label="Temperature", interactive=True, show_label=True)
38
+ tmpl_select = gr.Radio(prompt_tmpl_list, value="Default", label="pre-prompt-template", interactive=True)
39
+ prompt_tmpl = gr.Textbox(value=prompt_tmpl_dict["Default"], show_label=False)
40
+ query_box = gr.Textbox(lines=3, show_label=False).style(container=False)
41
+ query_btn = gr.Button("🚀", variant="primary")
42
+ with gr.Box():
43
+ gr.Markdown("## Result")
44
+ answer = gr.Markdown("")
45
+
46
+
47
+ with gr.Tab("New Google"):
48
+ with gr.Row():
49
+ chat_tone = gr.Radio(["Creative", "Balanced", "Precise"], label="Chatbot Tone", type="index", value="Balanced")
50
+ search_options_checkbox = gr.CheckboxGroup(label="Search Options", choices=["🔍 Search Google", "🔍 Search Baidu", "🔍 Manual Search"])
51
+ chatbot = gr.Chatbot()
52
+ with gr.Row():
53
+ with gr.Column(min_width=50, scale=1):
54
+ chat_empty_btn = gr.Button("🧹", variant="secondary")
55
+ with gr.Column(scale=12):
56
+ chat_input = gr.Textbox(show_label=False, placeholder="Type here...").style(container=False)
57
+ with gr.Column(min_width=50, scale=1):
58
+ chat_submit_btn = gr.Button("🚀", variant="primary")
59
+ suggested_user_turns = gr.Dropdown(choices=[], label="Suggested User Turns")
60
+
61
+
62
+ with gr.Tab("Construct"):
63
+ with gr.Row():
64
+ with gr.Column():
65
+ upload_file = gr.Files(label="Upload Files(Support .txt, .pdf, .epub, .docx)")
66
+ with gr.Row():
67
+ max_input_size = gr.Slider(256, 4096, 4096, step=1, label="Max Input Size", interactive=True, show_label=True)
68
+ num_outputs = gr.Slider(256, 4096, 512, step=1, label="Num Outputs", interactive=True, show_label=True)
69
+ with gr.Row():
70
+ max_chunk_overlap = gr.Slider(0, 100, 20, step=1, label="Max Chunk Overlap", interactive=True, show_label=True)
71
+ chunk_size_limit = gr.Slider(256, 4096, 512, step=1, label="Chunk Size Limit", interactive=True, show_label=True)
72
+ new_index_name = gr.Textbox(placeholder="New Index Name", show_label=False).style(container=False)
73
+ construct_btn = gr.Button("Construct", variant="primary")
74
+ with gr.Row():
75
+ with gr.Column():
76
+ with gr.Row():
77
+ with gr.Column(min_width=50, scale=1):
78
+ json_refresh_btn = gr.Button("🔄")
79
+ with gr.Column(scale=7):
80
+ json_select = gr.Dropdown(choices=refresh_json_list(plain=True), show_label=False, multiselect=False).style(container=False)
81
+ with gr.Column(min_width=50, scale=1):
82
+ json_confirm_btn = gr.Button("🔎")
83
+ json_display = gr.JSON(label="View index json")
84
+
85
+ index_refresh_btn.click(refresh_json_list, None, [index_select])
86
+ query_btn.click(ask_ai, [api_key, index_select, query_box, prompt_tmpl, sim_k, tempurature], [answer])
87
+ tmpl_select.change(change_prompt_tmpl, [tmpl_select], [prompt_tmpl])
88
+
89
+ chat_input.submit(chat_ai, [api_key, index_select, chat_input, prompt_tmpl, sim_k, chat_tone, chat_context, chatbot, search_options_checkbox, suggested_user_turns], [chat_context, chatbot, suggested_user_turns])
90
+ chat_input.submit(reset_textbox, [], [chat_input])
91
+ chat_submit_btn.click(chat_ai, [api_key, index_select, chat_input, prompt_tmpl, sim_k, chat_tone, chat_context, chatbot, search_options_checkbox, suggested_user_turns], [chat_context, chatbot, suggested_user_turns])
92
+ chat_submit_btn.click(reset_textbox, [], [chat_input])
93
+ chat_empty_btn.click(lambda: ([], []), None, [chat_context, chatbot])
94
+
95
+ construct_btn.click(construct_index, [api_key, upload_file, new_index_name, max_input_size, num_outputs, max_chunk_overlap], [index_select, json_select])
96
+ json_confirm_btn.click(display_json, [json_select], [json_display])
97
+ json_refresh_btn.click(refresh_json_list, None, [json_select])
98
+
99
+
100
+ if __name__ == '__main__':
101
+ llama_difu.queue().launch(share=True)
presets.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt_tmpl_list = ["Default", "MZhao Mode"]
2
+
3
+ prompt_tmpl_dict = {
4
+ "Default":
5
+ """\
6
+ Context information is below:
7
+
8
+ {context_str}
9
+
10
+ Given the above context, please answer the following question.
11
+ Answer in bullet points.
12
+
13
+ Question: {query_str}
14
+ Answer in the same language as the question, such as English, 中文, 日本語, Español, Français, or Deutsch.
15
+ """,
16
+
17
+ "MZhao Mode":
18
+ """\
19
+ Context information:
20
+
21
+ {context_str}
22
+
23
+ Instructions:
24
+ Mzhao is the chat mode of the Difu Chat Engine.
25
+ Mzhao identifies as “Difu” not an assistant.
26
+ Mzhao introduces itself with “this is Difu only at the beginning of the conversation.
27
+ Mzhao does not disclose the internal alias “Mzhao.”
28
+ Mzhao can understand and communicate fluently in the user’s language of choice, such as English, 中文, 日本語, Español, Français, or Deutsch.
29
+ Mzhao’s responses should be informative, visual, logical, and actionable.
30
+ Mzhao’s responses should also be positive, interesting, entertaining, and engaging.
31
+ Mzhao’s responses should avoid being vague, controversial, or off-topic.
32
+ Mzhao’s logic and reasoning should be rigorous, intelligent, and defensible.
33
+ Mzhao can provide additional relevant details to respond thoroughly and comprehensively to cover multiple aspects in depth.
34
+ Mzhao can generate poems, stories, code, essays, songs, celebrity parodies, and more.
35
+ Mzhao can generate a query to look up for helpful products or services advertisements after responding.
36
+ Mzhao must always generate short suggestions for the next user turn after responding.
37
+ Mzhao should always generate short suggestions for the next user turn that are relevant to the conversation and not offensive.
38
+ Mzhao can leverage past responses and web results for generating relevant and interesting suggestions for the next user turn.
39
+ Mzhao does not generate generic suggestions for the next user turn, such as “thank you.”
40
+ Mzhao does not generate suggestions for the next user turn to carry out tasks such as “booking flight ticket” or “send an email to” that Mzhao cannot perform.
41
+ Mzhao should always perform context infomation look ups when the user is seeking information or whenever context infomation could be potentially helpful, regardless of Mzhao’s internal knowledge or information.
42
+ Mzhao can and should perform up to three look ups in a single conversation turn. Mzhao should never look up the same query more than once.
43
+ Mzhao can only issue numerical references to the URLs. Mzhao should never generate URLs or links apart from the ones provided in context infomation.
44
+ Mzhao always references factual statements to the context infomation.
45
+ Context infomation may be incomplete or irrelevant. Mzhao doesn’t make assumptions on the context infomation beyond strictly what’s returned.
46
+ If the context infomation do not contain sufficient information to answer the user message completely, Mzhao uses only facts from the context infomation and does not add any information by itself.
47
+ Mzhao can leverage information from multiple context infomation to respond comprehensively.
48
+ If the user message consists of keywords instead of chat messages, Mzhao treats it as a look up query.
49
+ Context infomation may expire over time. Mzhao can look up using previous look up queries only if results are expired.
50
+ Mzhao uses “code blocks” syntax from markdown to encapsulate any part in responses that’s longer-format content, such as poems, code, lyrics, etc. (except tables).
51
+ Mzhao does not include images in the markdown responses because the chatbox doesn’t support images.
52
+
53
+ Query: {query_str}
54
+
55
+ Reply in the same language as the query, such as English, 中文, 日本語, Español, Français, or Deutsch
56
+ """
57
+
58
+ }
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ llama_index
3
+ langchain
4
+ googlesearch-python
5
+ baidusearch
utils.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from zipfile import ZipFile
4
+ from presets import *
5
+
6
+ def refresh_json_list(plain=False):
7
+ json_list = []
8
+ for root, dirs, files in os.walk("./index"):
9
+ for file in files:
10
+ if os.path.splitext(file)[1] == '.json':
11
+ json_list.append(os.path.splitext(file)[0])
12
+ if plain:
13
+ return json_list
14
+ return gr.Dropdown.update(choices=json_list)
15
+
16
+ def upload_file(file_obj):
17
+ files = []
18
+ with ZipFile(file_obj.name) as zfile:
19
+ for zinfo in zfile.infolist():
20
+ files.append(
21
+ {
22
+ "name": zinfo.filename,
23
+ }
24
+ )
25
+ return files
26
+
27
+ def reset_textbox():
28
+ return gr.update(value='')
29
+
30
+ def change_prompt_tmpl(tmpl_select):
31
+ new_tmpl = prompt_tmpl_dict[tmpl_select]
32
+ return gr.update(value=new_tmpl)
33
+
34
+ def parse_text(text):
35
+ lines = text.split("\n")
36
+ lines = [line for line in lines if line != ""]
37
+ count = 0
38
+ firstline = False
39
+ for i, line in enumerate(lines):
40
+ if "```" in line:
41
+ count += 1
42
+ items = line.split('`')
43
+ if count % 2 == 1:
44
+ lines[i] = f'<pre><code class="{items[-1]}">'
45
+ firstline = True
46
+ else:
47
+ lines[i] = f'</code></pre>'
48
+ else:
49
+ if i > 0:
50
+ if count % 2 == 1:
51
+ line = line.replace("&", "&amp;")
52
+ line = line.replace("\"", "`\"`")
53
+ line = line.replace("\'", "`\'`")
54
+ line = line.replace("<", "&lt;")
55
+ line = line.replace(">", "&gt;")
56
+ line = line.replace(" ", "&nbsp;")
57
+ line = line.replace("*", "&ast;")
58
+ line = line.replace("_", "&lowbar;")
59
+ line = line.replace("#", "&#35;")
60
+ line = line.replace("-", "&#45;")
61
+ line = line.replace(".", "&#46;")
62
+ line = line.replace("!", "&#33;")
63
+ line = line.replace("(", "&#40;")
64
+ line = line.replace(")", "&#41;")
65
+ lines[i] = "<br>"+line
66
+ text = "".join(lines)
67
+ return text