hoson/qGen
0
1# -*- coding: utf-8 -*-2"""3Created on Thu Jul 25 11:57:10 20244 5*** json.xxxs where 's' means String datatype6API Ref: https://docs.llamaindex.ai/en/stable/api_reference/7Question generation: https://docs.llamaindex.ai/en/stable/examples/finetuning/llm_judge/correctness/finetune_llm_judge_single_grading_correctness/8 9pip install llama-index10pip install llama-index-llms-azure-openai11pip install ipywidgets12pip install opencc #for CN-CN (e.g traditional<->simplified) translation13 14@author: hoson15"""16import os17import random18import json19from llama_index.core import SimpleDirectoryReader20from llama_index.core.evaluation import DatasetGenerator21from llama_index.llms.azure_openai import AzureOpenAI as LmaIdxAzOpenAI #pip install llama-index-llms-azure-openai22import gradio as gr23# import myLanChLib as lc24 25FOR_HUG_FACE= True; SHARE_H=FOR_HUG_FACE26 27DEBUG_L1=True28if DEBUG_L1: index=0 # for print() index of the randomly picked Question 29GRADIO_ENABLE = True #True #False to Disable Gradio UI, Debugging30ADD_REF = True # False31 32NUM_PER_CK =5; NO_OF_QUESTIONS=500 #can't work for 1000, 800, 500 when per chunck is 25 (6Aug2024)33entry_1st_global=True34 35LMA_IDX_QUESTION_GEN_PROMPT = ( #string as a prompt for llamaindex36 "You are a Teacher/ Professor. Your task is to setup " #default prompt by llamIdx37 "a quiz/examination. Using the provided context, formulate " #default prompt by llamIdx38 "a single question that captures an important fact from the " #default prompt by llamIdx39 # "do not ask like What is the title of the document or the size for a document" #add by me on 25Jul202440 "context. Restrict the question to the context information provided." #default prompt by llamIdx41 )42# LMA_IDX_QUESTION_GEN_PROMPT = (43# "你是一名教師/教授。你的任務是設定"44# "測驗/考試。使用提供的上下文,製定"45# "一個從中捕捉到重要事實的問題"46# "上下文。將問題限制在提供的上下文資訊範圍內."47# )48 49LLM = LmaIdxAzOpenAI( #Azure OpenAI module from llamaindex50 engine='gpt4o-mini', #'g4o', #"GPT-4Omni", # =deployment_name in Langchain Azure51 api_version="2024-05-01-preview",52 model='gpt-4o-mini', #'gpt-4o', # = Model name in Azure53 azure_endpoint="https://4davatar.openai.azure.com/",54 api_key=os.getenv("4davatar1_KEY"),55 temperature=0.0,56 )57 58remove_list=[', as outlined in the provided context',59 'as outlined in the provided context',60 61 ', as mentioned in the provided document',62 'as mentioned in the provided document',63 64 ', as mentioned in the context',65 'as mentioned in the context',66 67 ', as mentioned in the provided context',68 'as mentioned in the provided context',69 70 ', as mentioned in the report',71 'as mentioned in the report',72 73 ' as mentioned in the reference material',74 'mentioned in the reference material'75 76 ', as presented in the document',77 'presented in the document',78 79 ', as described in the document'80 'as described in the document'81 82 'that is referenced in the document',83 ', as referenced in the document'84 85 'in the provided context',86 'according to the provided context',87 'as outlined',88 ', as suggested',89 ', as mentioned',90 ]91 92def remove_sentences(content:str, sentences_list:list):93 """94 Remove a sentence from a big paragraph.95 96 Parameters97 ----------98 content : str99 DESCRIPTION.100 sentences_list : list101 List of all sentences to be removed.102 103 Returns104 -------105 content : str106 The modified comtent107 108 """109 for sent in sentences_list:110 content=content.replace(sent, '')111 return content112 113def dict2str_json(dictionary:dict): #'s' means String114 str_json = json.dumps(dictionary, indent=2) # convert a Python object into formatted Json string.115 return str_json116 117def str_json2file(str_json:str, filename):118 with open(filename, "w") as f: #save119 json.dump(str_json, f) #, indent=4) # writing/dumping formatted JSON to a file/socket.120 print(f"Saved in {filename}.")121 return122 123def translate(text:str, mode='no'):124 assert mode in ('no', 'e2zh', 'zh2e'), 'Error: Invalid mode'125 match mode:126 # case 'e2zh': text = lc.a4o_en2cn_traditional(text)127 # case 'zh2e': text = lc.lc.a4o_cn_traditional2en(text)128 case 'no': pass129 130 return text131 132def gen_question_ans(prompt:str, mode='no'):133 """134 Generate questions and answers from the documents in the folder 'data'.135 136 Parameters137 ----------138 prompt : str139 The prompt instructe an LLM to generate question from the corpus in the folder 'data'140 mode : string, optional141 The default is 'no'.142 143 Returns144 -------145 dicts_list: list of Dict146 147 """148 assert mode in ('no', 'e2zh', 'zh2e'), 'Error: Invalid mode'149# generate questions against chunks 150 print("Loading documents from folder....")151 documents = SimpleDirectoryReader("data").load_data() #folder 'data' in current directory152 dataset_generator = DatasetGenerator.from_documents( # instantiate a DatasetGenerator153 documents,154 question_gen_query=prompt,155 llm=LLM,156 num_questions_per_chunk=NUM_PER_CK #25, #5, #25,157 )158 print("Starting to generate question......, wait for 5-15min")159 # ==============generate questions against chunks==============================160 import nest_asyncio; nest_asyncio.apply() #avoid nested asyn Runtime error, https://pypi.org/project/nest-asyncio/161 qrd = dataset_generator.generate_dataset_from_nodes(num=NO_OF_QUESTIONS)#350) #num=actual no of questions to gen162 # don't need nest_asyncio if use .agenerate_dataset_from_nodes163 # # qrd = dataset_generator.agenerate_dataset_from_nodes(num=50)#350) #num=actual no of questions to gen164 # =============================================================================165 queries=qrd.queries #Questions166 responses=qrd.responses #Model Answers167 168 print("Generating Q&A Dict for mind miner.")169 dicts_list = []170 for question, answer in zip(queries.values(), responses.values() ):171 match mode: #'no', 'e2zh', 'zh2e'172 case 'e2zh':173 question=translate(question, 'e2zh')174 answer=translate(answer, 'e2zh') 175 case 'zh2e':176 question=translate(question, 'zh2e')177 answer=translate(answer, 'zh2e')178 case 'no':179 pass180 181 dicts_list.append({ # start with '{' is a Dict by default, https://medium.com/@ahmedbilalumer3/fine-tuning-llama-factory-phi-1-3-1-5b-mimicking-researchers-writing-style-1802260ae2b3182 "instruction": question,183 "input": "", #Llamafactory require this.184 "output": answer185 })186 187 return dicts_list #in Llamafactory required format188 189def load_Qans_dict(filename:str, mode):190 tmp=os.listdir() #get all file names in the current directory.191 file_exist=filename in tmp #check file exist or else192 193 choice='no'194 if not FOR_HUG_FACE: choice=input("Create new queston database, 'yes'? or press Enter to skip: ")195 196 if not file_exist or choice=='yes':197 print(f"Generating new question database '{filename}', wait for minutes.......")198 obj_dict=gen_question_ans(LMA_IDX_QUESTION_GEN_PROMPT, mode)199 str_json=dict2str_json(obj_dict)200 str_json2file(str_json, filename) #save to file201 print(f"Save generated {filename} into current folder.")202 else:203 print(f"Loading an existing {filename} in current directory.")204 with open(filename, "r") as f:205 str_json = json.load(f) # load Json formatted string from file206 obj_dict = json.loads(str_json) # load from Json formatted string to Python Object207 208 return obj_dict209 210# def groq_chat(question:str, history):211# response=lc.groq_chat_complete(question)212# return response213 214def chatbot_simulator(question:str, history):215 global entry_1st_global, length_global216 if DEBUG_L1: global index217 """218 ....219 ----------220 question : string 221 DESCRIPTION. User prompt question222 history : string223 DESCRIPTION. A list of list representing the conversations up until that point. 224 Each inner list consists of two str representing a pair: [user input, bot response].225 Returns226 -------227 response : string228 DESCRIPTION. The texts show to the User229 """230 idx=random.randint(0, length_global-1) #included both end points. https://www.w3schools.com/python/ref_random_randint.asp231 if DEBUG_L1: index=idx; print(f"index={idx}") #print to Console not GRADIO GUI232 if entry_1st_global==True: #first entry to this function233 response="Hello, have a nice day !\n" + QA_DICT[idx]['instruction']234 entry_1st_global=not(entry_1st_global)235 else: # other than first entry as above236 response=QA_DICT[idx]['instruction']237 238 # if DEBUG_L1: pass #print(f"History is:{str(history)}")239 240 response =remove_sentences(response, remove_list)241 output = f"{response} \n Ref Ans is: {QA_DICT[idx]['output']}" if ADD_REF else f"{response}"242 return output243 244if __name__ == "__main__":245 QA_DICT=load_Qans_dict("Qans.json", 'no') #'no', 'e2zh', 'zh2e'246 length_global=len(QA_DICT)247 248 if GRADIO_ENABLE:249 gr.ChatInterface( #https://www.gradio.app/guides/creating-a-chatbot-fast250 chatbot_simulator, ####1) Replace this function to return chatbot searched texts251 chatbot=gr.Chatbot(height=300),252 253 ####2) Put the Apps' on-screen texts here(green text as show below).254 title="Master Mind",255 description=" Learning, Exam and Career !",256 # examples=['What is a contract?', 'What is CIC?'], 257 textbox=gr.Textbox(placeholder=f"Press SUBMIT to get a question from ({NO_OF_QUESTIONS} Qs).", container=False, scale=7),258 259 ####3) Other settings260 theme="soft",261 undo_btn="Delete Previous", #change the text on the button262 clear_btn="Clear", #change the text on the button263 cache_examples=False, #True,264 # multimodal=True,265 retry_btn=None,266 fill_height=True,267 ).launch(share=SHARE_H) #False) #True)268 # Complicated Bot: https://www.gradio.app/guides/creating-a-custom-chatbot-with-blocks269 270 271""" Reference:272JSON stands for JavaScript Object Notation. It means that a script (executable) file which is made of text273in a programming language, is used to store and transfer the data. Great uTube: https://www.youtube.com/watch?v=iiADhChRriM274Python supports JSON through a built-in package called JSON.275 276Text in JSON is done through quoted-string which contains a value in key-value mapping within { }. 277It is similar to the dictionary in Python.278"""