import datetime import json import os import shutil from pathlib import Path import add_qwen_libs # NOQA import gradio as gr import jsonlines from qwen_agent.actions import (ContinueWriting, ReAct, RetrievalQA, WriteFromScratch) from qwen_agent.actions.function_calling import FunctionCalling from qwen_agent.llm import get_chat_model from qwen_agent.log import logger from qwen_agent.memory import Memory from qwen_agent.tools import call_plugin, list_of_all_functions from qwen_agent.utils.utils import (format_answer, get_last_one_line_context, has_chinese_chars, save_text_to_file) from qwen_server.schema import GlobalConfig from qwen_server.utils import extract_and_cache_document # Read config with open(Path(__file__).resolve().parent / 'server_config.json', 'r') as f: server_config = json.load(f) server_config = GlobalConfig(**server_config) llm = get_chat_model(model=server_config.server.llm, api_key=server_config.server.api_key, model_server=server_config.server.model_server) mem = Memory(llm=llm, stream=False) app_global_para = { 'time': [str(datetime.date.today()), str(datetime.date.today())], 'cache_file': os.path.join(server_config.path.cache_root, 'browse.jsonl'), 'messages': [], 'last_turn_msg_id': [], 'is_first_upload': True, } DOC_OPTION = 'Document QA' CI_OPTION = 'Code Interpreter' CODE_FLAG = '/code' PLUGIN_FLAG = '/plug' TITLE_FLAG = '/title' with open(Path(__file__).resolve().parent / 'css/main.css', 'r') as f: css = f.read() with open(Path(__file__).resolve().parent / 'js/main.js', 'r') as f: js = f.read() def add_text(history, text): history = history + [(text, None)] app_global_para['last_turn_msg_id'] = [] return history, gr.update(value='', interactive=False) def pure_add_text(history, text): history = history + [(text, None)] return history, gr.update(value='', interactive=False) def rm_text(history): if not history: gr.Warning('No input content!') elif not history[-1][1]: return history, gr.update(value='', interactive=False) else: history = history[:-1] + [(history[-1][0], None)] return history, gr.update(value='', interactive=False) def chat_clear(): app_global_para['messages'] = [] return None, None def chat_clear_last(): for index in app_global_para['last_turn_msg_id'][::-1]: del app_global_para['messages'][index] app_global_para['last_turn_msg_id'] = [] def add_file(file, chosen_plug): output_filepath = server_config.path.code_interpreter_ws fn = os.path.basename(file.name) fn_type = fn.split('.')[-1].lower() logger.info('file type: ' + fn_type) if chosen_plug == DOC_OPTION and (fn_type not in ['pdf', 'docx', 'pptx']): new_path = ( 'Upload failed: only adding [\'.pdf\', \'.docx\', \'.pptx\'] documents as references is supported!' ) else: new_path = os.path.join(output_filepath, fn) if os.path.exists(new_path): os.remove(new_path) shutil.move(file.name, output_filepath) if chosen_plug == CI_OPTION: app_global_para['is_first_upload'] = True # upload references if chosen_plug == DOC_OPTION: data = { 'content': '', 'query': '', 'url': new_path, 'task': 'cache', 'type': fn_type, } extract_and_cache_document( data, app_global_para['cache_file'], server_config.path.cache_root) # waiting for analyse file return new_path def read_records(file, times=None): lines = [] if times: for line in jsonlines.open(file): if times[0] <= line['time'] <= times[1]: lines.append(line) return lines def update_app_global_para(date1, date2): app_global_para['time'][0] = date1 app_global_para['time'][1] = date2 def refresh_date(): option = [ str(datetime.date.today() - datetime.timedelta(days=i)) for i in range(server_config.server.max_days) ] return (gr.update(choices=option, value=str(datetime.date.today())), gr.update(choices=option, value=str(datetime.date.today()))) def update_browser_list(): if not os.path.exists(app_global_para['cache_file']): return 'No browsing records' lines = read_records(app_global_para['cache_file'], times=app_global_para['time']) br_list = [[line['url'], line['extract'], line['checked']] for line in lines] res = '
    {bl}
' bl = '' for i, x in enumerate(br_list): ck = ' Writing Text: ' in context: text = context.split('> Writing Text: ')[-1].strip() res += '\n' res += text yield res elif 'Final Answer' in context: response = format_answer(context) res += '\n' res += response yield res else: res += context yield res with gr.Blocks(css=css, theme='soft') as demo: title = gr.Markdown('Qwen Agent: BrowserQwen', elem_classes='title') desc = gr.Markdown( 'This is the editing workstation of BrowserQwen, where Qwen has collected the browsing history. Qwen can assist you in completing your creative work!', elem_classes='desc', ) with gr.Row(): with gr.Column(): rec = gr.Markdown('Browsing History', elem_classes='rec') with gr.Row(): with gr.Column(scale=3, min_width=0): date1 = gr.Dropdown( [ str(datetime.date.today() - datetime.timedelta(days=i)) for i in range(server_config.server.max_days) ], value=str(datetime.date.today()), label='Start Date', ) date2 = gr.Dropdown( [ str(datetime.date.today() - datetime.timedelta(days=i)) for i in range(server_config.server.max_days) ], value=str(datetime.date.today()), label='End Date', ) with gr.Column(scale=7, min_width=0): browser_list = gr.HTML( value='', label='browser_list', elem_classes=['div_tmp', 'add_scrollbar'], ) with gr.Tab('Editor', elem_id='default-tab'): with gr.Row(): with gr.Column(): with gr.Row(): edit_area = gr.Textbox( value='', elem_classes=['textbox_default', 'add_scrollbar'], lines=30, label='Input', show_copy_button=True, ) # token_count = gr.HTML(value='0', # elem_classes=[ # 'token-counter', # 'default-token-counter' # ]) with gr.Row(): ctn_bt = gr.Button('Continue', variant='primary') stop_bt = gr.Button('Stop') clr_bt = gr.Button('Clear') dld_bt = gr.Button('Download') # with gr.Row(): # layout_bt = gr.Button('👉', variant='primary') with gr.Column(): cmd_area = gr.Textbox(lines=10, max_lines=10, label="Qwen's Inner Thought", elem_id='cmd') with gr.Tab('Markdown'): # md_out_bt = gr.Button('Render') md_out_area = gr.Markdown( elem_classes=['md_tmp', 'add_scrollbar']) with gr.Tab('HTML'): html_out_area = gr.HTML() with gr.Tab('Raw'): text_out_area = gr.Textbox( lines=20, label='', elem_classes=[ 'textbox_default_output', 'add_scrollbar' ], show_copy_button=True, ) clk_ctn_bt = ctn_bt.click(generate, edit_area, cmd_area) clk_ctn_bt.then(format_generate, [edit_area, cmd_area], edit_area) edit_area_change = edit_area.change(layout_to_right, edit_area, [text_out_area, md_out_area]) stop_bt.click(lambda: None, cancels=[clk_ctn_bt], queue=False) clr_bt.click( lambda: [None, None, None], None, [edit_area, cmd_area, md_out_area], queue=False, ) dld_bt.click(download_text, edit_area, None) # layout_bt.click(layout_to_right, # edit_area, [text_out_area, md_out_area], # queue=False) gr.Markdown(""" ### Usage Tips: - Browsing History: - Start Date/End Date: Selecting the browsed materials for the desired time period, including the start and end dates - The browsed materials list: supporting the selection or removal of specific browsing content - Editor: In the editing area, you can directly input content or special instructions, and then click the ```Continue``` button to have Qwen assist in completing the editing work: - After inputting the content, directly click the ```Continue``` button: Qwen will begin to continue writing based on the browsing information - Using special instructions: - /title + content: Qwen enables the built-in planning process and writes a complete manuscript - /code + content: Qwen enables the code interpreter plugin, writes and runs Python code, and generates replies - /plug + content: Qwen enables plugin and select appropriate plugin to generate reply - Chat: Interactive area. Qwen generates replies based on given reference materials. Selecting Code Interpreter will enable the code interpreter plugin """) with gr.Tab('Chat', elem_id='chat-tab'): with gr.Column(): chatbot = gr.Chatbot( [], elem_id='chatbot', height=680, show_copy_button=True, avatar_images=( None, (os.path.join( Path(__file__).resolve().parent, 'img/logo.png')), ), ) with gr.Row(): with gr.Column(scale=1, min_width=0): file_btn = gr.UploadButton('Upload', file_types=['file']) with gr.Column(scale=13): chat_txt = gr.Textbox( show_label=False, placeholder='Chat with Qwen...', container=False, ) with gr.Column(scale=1, min_width=0): chat_clr_bt = gr.Button('Clear') with gr.Column(scale=1, min_width=0): chat_stop_bt = gr.Button('Stop') with gr.Column(scale=1, min_width=0): chat_re_bt = gr.Button('Again') with gr.Row(): with gr.Column(scale=2, min_width=0): plug_bt = gr.Dropdown( [CI_OPTION, DOC_OPTION], label='Plugin', info='', value=DOC_OPTION, ) with gr.Column(scale=8, min_width=0): hidden_file_path = gr.Textbox( interactive=False, label='The uploaded file is displayed here') txt_msg = chat_txt.submit( add_text, [chatbot, chat_txt], [chatbot, chat_txt], queue=False).then(bot, [chatbot, hidden_file_path, plug_bt], chatbot) txt_msg.then(lambda: gr.update(interactive=True), None, [chat_txt], queue=False) # txt_msg_bt = chat_smt_bt.click(add_text, [chatbot, chat_txt], [chatbot, chat_txt], queue=False).then(bot, chatbot, chatbot) # txt_msg_bt.then(lambda: gr.update(interactive=True), None, [chat_txt], queue=False) # (None, None, None, cancels=[txt_msg], queue=False).then re_txt_msg = (chat_re_bt.click( rm_text, [chatbot], [chatbot, chat_txt], queue=False).then(chat_clear_last, None, None).then( bot, [chatbot, hidden_file_path, plug_bt], chatbot)) re_txt_msg.then(lambda: gr.update(interactive=True), None, [chat_txt], queue=False) file_msg = file_btn.upload(add_file, [file_btn, plug_bt], [hidden_file_path], queue=False) file_msg.then(update_browser_list, None, browser_list).then(lambda: None, None, None, _js=f'() => {{{js}}}') chat_clr_bt.click(chat_clear, None, [chatbot, hidden_file_path], queue=False) # re_bt.click(re_bot, chatbot, chatbot) chat_stop_bt.click(chat_clear_last, None, None, cancels=[txt_msg, re_txt_msg], queue=False) plug_bt.change(choose_plugin, plug_bt, [file_btn, hidden_file_path]) with gr.Tab('Pure Chat', elem_id='pure-chat-tab'): gr.Markdown( 'Note: The chat box on this tab will not use any browsing history!' ) with gr.Column(): pure_chatbot = gr.Chatbot( [], elem_id='pure_chatbot', height=680, show_copy_button=True, avatar_images=( None, (os.path.join( Path(__file__).resolve().parent, 'img/logo.png')), ), ) with gr.Row(): with gr.Column(scale=13): chat_txt = gr.Textbox( show_label=False, placeholder='Chat with Qwen...', container=False, ) with gr.Column(scale=1, min_width=0): chat_clr_bt = gr.Button('Clear') with gr.Column(scale=1, min_width=0): chat_stop_bt = gr.Button('Stop') with gr.Column(scale=1, min_width=0): chat_re_bt = gr.Button('Again') txt_msg = chat_txt.submit(pure_add_text, [pure_chatbot, chat_txt], [pure_chatbot, chat_txt], queue=False).then( pure_bot, pure_chatbot, pure_chatbot) txt_msg.then(lambda: gr.update(interactive=True), None, [chat_txt], queue=False) re_txt_msg = chat_re_bt.click(rm_text, [pure_chatbot], [pure_chatbot, chat_txt], queue=False).then( pure_bot, pure_chatbot, pure_chatbot) re_txt_msg.then(lambda: gr.update(interactive=True), None, [chat_txt], queue=False) chat_clr_bt.click(lambda: None, None, pure_chatbot, queue=False) chat_stop_bt.click(chat_clear_last, None, None, cancels=[txt_msg, re_txt_msg], queue=False) date1.change(update_app_global_para, [date1, date2], None).then(update_browser_list, None, browser_list).then(lambda: None, None, None, _js=f'() => {{{js}}}').then( chat_clear, None, [chatbot, hidden_file_path]) date2.change(update_app_global_para, [date1, date2], None).then(update_browser_list, None, browser_list).then(lambda: None, None, None, _js=f'() => {{{js}}}').then( chat_clear, None, [chatbot, hidden_file_path]) demo.load(update_app_global_para, [date1, date2], None).then(refresh_date, None, [date1, date2]).then( update_browser_list, None, browser_list).then(lambda: None, None, None, _js=f'() => {{{js}}}').then( chat_clear, None, [chatbot, hidden_file_path]) demo.queue().launch(server_name=server_config.server.server_host, server_port=server_config.server.workstation_port)