├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── MANIFEST.in ├── README.md ├── README_CN.md ├── assets ├── screenshot-ci.png ├── screenshot-editor-movie.png ├── screenshot-multi-web-qa.png ├── screenshot-pdf-qa.png ├── screenshot-web-qa.png └── screenshot-writing.png ├── benchmark └── code_interpreter │ ├── README.md │ ├── code_interpreter.py │ ├── config.py │ ├── inference_and_execute.py │ ├── metrics │ ├── __init__.py │ ├── code_execution.py │ ├── gsm8k.py │ └── visualization.py │ ├── models │ ├── __init__.py │ ├── base.py │ ├── dashscope.py │ ├── llm.py │ └── qwen.py │ ├── parser │ ├── __init__.py │ ├── internlm_parser.py │ └── react_parser.py │ ├── prompt │ ├── __init__.py │ ├── internlm_react.py │ ├── llama_react.py │ ├── qwen_react.py │ └── react.py │ ├── requirements.txt │ └── utils │ ├── __init__.py │ ├── code_utils.py │ └── data_utils.py ├── browser_qwen.md ├── browser_qwen ├── background.js ├── img │ ├── copy.png │ ├── logo.png │ └── popup.png ├── manifest.json └── src │ ├── content.js │ ├── popup.html │ └── popup.js ├── browser_qwen_cn.md ├── docs ├── README.md ├── README_CN.md ├── agent.md ├── agent_cn.md ├── llm.md ├── llm_cn.md ├── tool.md └── tool_cn.md ├── examples ├── __init__.py ├── assistant_add_custom_tool.py ├── assistant_audio.py ├── assistant_mcp_sqlite_bot.py ├── assistant_omni.py ├── assistant_qwen3.py ├── assistant_qwq.py ├── assistant_rag.py ├── assistant_weather_bot.py ├── cookbook_database_manipulation.ipynb ├── cookbook_drive_guide.ipynb ├── cookbook_mind_map.ipynb ├── function_calling.py ├── function_calling_in_parallel.py ├── gpt_mentions.py ├── group_chat_chess.py ├── group_chat_demo.py ├── llm_quick_chat_oai.py ├── llm_riddles.py ├── llm_vl_mix_text.py ├── long_dialogue.py ├── multi_agent_router.py ├── parallel_doc_qa.py ├── qwen2vl_assistant_tooluse.py ├── qwen2vl_assistant_video.py ├── qwen2vl_function_calling.py ├── react_data_analysis.py ├── resource │ ├── doc.pdf │ ├── poem.pdf │ ├── screenshot_with_plot.jpeg │ └── stock_prices.csv ├── tir_math.py ├── virtual_memory_qa.py └── visual_storytelling.py ├── qwen_agent ├── __init__.py ├── agent.py ├── agents │ ├── __init__.py │ ├── article_agent.py │ ├── assistant.py │ ├── dialogue_retrieval_agent.py │ ├── dialogue_simulator.py │ ├── doc_qa │ │ ├── __init__.py │ │ ├── basic_doc_qa.py │ │ ├── parallel_doc_qa.py │ │ ├── parallel_doc_qa_member.py │ │ └── parallel_doc_qa_summary.py │ ├── fncall_agent.py │ ├── group_chat.py │ ├── group_chat_auto_router.py │ ├── group_chat_creator.py │ ├── human_simulator.py │ ├── keygen_strategies │ │ ├── __init__.py │ │ ├── gen_keyword.py │ │ ├── gen_keyword_with_knowledge.py │ │ ├── split_query.py │ │ ├── split_query_then_gen_keyword.py │ │ └── split_query_then_gen_keyword_with_knowledge.py │ ├── memo_assistant.py │ ├── react_chat.py │ ├── router.py │ ├── tir_agent.py │ ├── user_agent.py │ ├── virtual_memory_agent.py │ ├── write_from_scratch.py │ └── writing │ │ ├── __init__.py │ │ ├── continue_writing.py │ │ ├── expand_writing.py │ │ └── outline_writing.py ├── gui │ ├── __init__.py │ ├── assets │ │ ├── app.css │ │ ├── appBot.css │ │ ├── logo.jpeg │ │ └── user.jpeg │ ├── gradio_dep.py │ ├── gradio_utils.py │ ├── utils.py │ └── web_ui.py ├── llm │ ├── __init__.py │ ├── azure.py │ ├── base.py │ ├── fncall_prompts │ │ ├── __init__.py │ │ ├── base_fncall_prompt.py │ │ ├── nous_fncall_prompt.py │ │ └── qwen_fncall_prompt.py │ ├── function_calling.py │ ├── oai.py │ ├── openvino.py │ ├── qwen_dashscope.py │ ├── qwenaudio_dashscope.py │ ├── qwenomni_oai.py │ ├── qwenvl_dashscope.py │ ├── qwenvl_oai.py │ ├── schema.py │ └── transformers_llm.py ├── log.py ├── memory │ ├── __init__.py │ └── memory.py ├── multi_agent_hub.py ├── settings.py ├── tools │ ├── __init__.py │ ├── amap_weather.py │ ├── base.py │ ├── code_interpreter.py │ ├── doc_parser.py │ ├── extract_doc_vocabulary.py │ ├── image_gen.py │ ├── mcp_manager.py │ ├── python_executor.py │ ├── resource │ │ ├── AlibabaPuHuiTi-3-45-Light.ttf │ │ └── code_interpreter_init_kernel.py │ ├── retrieval.py │ ├── search_tools │ │ ├── __init__.py │ │ ├── base_search.py │ │ ├── front_page_search.py │ │ ├── hybrid_search.py │ │ ├── keyword_search.py │ │ └── vector_search.py │ ├── simple_doc_parser.py │ ├── storage.py │ ├── web_extractor.py │ └── web_search.py └── utils │ ├── __init__.py │ ├── output_beautify.py │ ├── parallel_executor.py │ ├── qwen.tiktoken │ ├── str_processing.py │ ├── tokenization_qwen.py │ └── utils.py ├── qwen_server ├── __init__.py ├── add_qwen_libs.py ├── assistant_server.py ├── css │ └── main.css ├── database_server.py ├── js │ └── main.js ├── output_beautify.py ├── schema.py ├── server_config.json ├── utils.py └── workstation_server.py ├── run_server.py ├── setup.py └── tests ├── agents ├── test_article_agent.py ├── test_assistant.py ├── test_custom_tool_object.py ├── test_doc_qa.py ├── test_parallel_qa.py ├── test_react_chat.py └── test_router.py ├── examples ├── test_examples.py ├── test_long_dialogue.py └── test_vm_qa.py ├── llm ├── test_continue.py ├── test_dashscope.py ├── test_function_content.py └── test_oai.py ├── memory └── test_memory.py ├── qwen_server └── test_database_server.py └── tools ├── test_doc_parser.py ├── test_hybrid_search.py ├── test_keyword_search.py ├── test_simple_doc_parser.py ├── test_tools.py └── test_vector_search.py /.gitignore: -------------------------------------------------------------------------------- 1 | env 2 | *.pyc 3 | __pycache__ 4 | 5 | .idea 6 | .vscode 7 | .DS_Store 8 | *.ipynb_checkpoints 9 | 10 | qwen_agent/llm/gpt.py 11 | qwen_agent/llm/tools.py 12 | workspace/* 13 | 14 | benchmark/log/* 15 | benchmark/output_data/* 16 | benchmark/upload_file/* 17 | benchmark/upload_file_clean/* 18 | benchmark/eval_data/ 19 | Qwen-Agent 20 | 21 | docqa/* 22 | log/* 23 | log.jsonl 24 | 25 | ai_agent/debug.json 26 | ai_agent/local_prompts/* 27 | **/debug.json 28 | **/debug.log* 29 | debug.json 30 | ai_agent/log.jsonl 31 | qwen_agent.egg-info/* 32 | build/* 33 | dist/* 34 | 35 | examples/*.ipynb 36 | **/workspace/* 37 | test/* 38 | tests/env.sh 39 | examples/docqa_multi_agent.py 40 | examples/docqa_multihp_agents.py 41 | **/workspace/* 42 | test/* 43 | tests/env.sh 44 | examples/data/* 45 | test.db 46 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/flake8.git 3 | rev: 5.0.4 4 | hooks: 5 | - id: flake8 6 | args: ["--max-line-length=300"] # TODO: Set to 120 and `pre-commit run --all-files`. 7 | - repo: https://github.com/PyCQA/isort.git 8 | rev: 5.11.5 9 | hooks: 10 | - id: isort 11 | args: ["--line-length", "120"] 12 | - repo: https://github.com/pre-commit/mirrors-yapf.git 13 | rev: v0.32.0 14 | hooks: 15 | - id: yapf 16 | args: ["--style", "{based_on_style: google, column_limit: 120}", "-i"] 17 | - repo: https://github.com/pre-commit/pre-commit-hooks.git 18 | rev: v4.3.0 19 | hooks: 20 | - id: trailing-whitespace 21 | - id: check-yaml 22 | - id: end-of-file-fixer 23 | - id: requirements-txt-fixer 24 | - id: double-quote-string-fixer 25 | - id: check-merge-conflict 26 | - id: fix-encoding-pragma 27 | args: ["--remove"] 28 | - id: mixed-line-ending 29 | args: ["--fix=lf"] 30 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include qwen_agent/utils/qwen.tiktoken 2 | recursive-include qwen_agent/tools/resource * 3 | -------------------------------------------------------------------------------- /assets/screenshot-ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/assets/screenshot-ci.png -------------------------------------------------------------------------------- /assets/screenshot-editor-movie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/assets/screenshot-editor-movie.png -------------------------------------------------------------------------------- /assets/screenshot-multi-web-qa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/assets/screenshot-multi-web-qa.png -------------------------------------------------------------------------------- /assets/screenshot-pdf-qa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/assets/screenshot-pdf-qa.png -------------------------------------------------------------------------------- /assets/screenshot-web-qa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/assets/screenshot-web-qa.png -------------------------------------------------------------------------------- /assets/screenshot-writing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/assets/screenshot-writing.png -------------------------------------------------------------------------------- /benchmark/code_interpreter/config.py: -------------------------------------------------------------------------------- 1 | from parser import InternLMReActParser, ReActParser 2 | 3 | from models import LLM, Qwen, QwenDashscopeVLModel, QwenVL 4 | from prompt import InternLMReAct, LlamaReAct, QwenReAct 5 | 6 | react_prompt_map = { 7 | 'qwen': QwenReAct, 8 | 'llama': LlamaReAct, 9 | 'internlm': InternLMReAct, 10 | } 11 | 12 | react_parser_map = { 13 | 'qwen': ReActParser, 14 | 'llama': ReActParser, 15 | 'internlm': InternLMReActParser, 16 | } 17 | 18 | model_map = {'qwen': Qwen, 'llama': LLM, 'internlm': LLM, 'qwen-vl-chat': QwenVL} 19 | 20 | model_type_map = { 21 | 'qwen-72b-chat': 'qwen', 22 | 'qwen-14b-chat': 'qwen', 23 | 'qwen-1.8b-chat': 'qwen', 24 | 'qwen-7b-chat': 'qwen', 25 | 'llama-2-7b-chat': 'llama', 26 | 'llama-2-13b-chat': 'llama', 27 | 'codellama-7b-instruct': 'llama', 28 | 'codellama-13b-instruct': 'llama', 29 | 'internlm-7b-chat-1.1': 'internlm', 30 | 'internlm-20b-chat': 'internlm', 31 | 'qwen-vl-chat': 'qwen-vl-chat', 32 | } 33 | 34 | model_path_map = { 35 | 'qwen-72b-chat': 'Qwen/Qwen-72B-Chat', 36 | 'qwen-14b-chat': 'Qwen/Qwen-14B-Chat', 37 | 'qwen-7b-chat': 'Qwen/Qwen-7B-Chat', 38 | 'qwen-1.8b-chat': 'Qwen/Qwen-1_8B-Chat', 39 | 'llama-2-7b-chat': 'meta-llama/Llama-2-7b-chat-hf', 40 | 'llama-2-13b-chat': 'meta-llama/Llama-2-13b-chat-hf', 41 | 'codellama-7b-instruct': 'codellama/CodeLlama-7b-Instruct-hf', 42 | 'codellama-13b-instruct': 'codellama/CodeLlama-13b-Instruct-hf', 43 | 'internlm-7b-chat-1.1': 'internlm/internlm-chat-7b-v1_1', 44 | 'internlm-20b-chat': 'internlm/internlm-chat-20b', 45 | 'qwen-vl-chat': 'Qwen/Qwen-VL-Chat', 46 | } 47 | 48 | 49 | def get_react_prompt(model_name, query, lang, upload_fname_list): 50 | react_prompt_cls = react_prompt_map.get(model_type_map[model_name], QwenReAct) 51 | return react_prompt_cls(query, lang, upload_fname_list) 52 | 53 | 54 | def get_react_parser(model_name): 55 | react_parser_cls = react_parser_map.get(model_type_map[model_name], ReActParser) 56 | return react_parser_cls() 57 | 58 | 59 | def get_model(model_name): 60 | if model_name in ['qwen-vl-plus']: 61 | return QwenDashscopeVLModel(model=model_name) 62 | model_path = model_path_map.get(model_name, None) 63 | model_cls = model_map.get(model_type_map[model_name], LLM) 64 | return model_cls(model_path) 65 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/benchmark/code_interpreter/metrics/__init__.py -------------------------------------------------------------------------------- /benchmark/code_interpreter/metrics/gsm8k.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import re 4 | 5 | import numpy as np 6 | from utils.data_utils import load_jsonl, save_jsonl 7 | 8 | INVALID_ANS = '[invalid]' 9 | 10 | 11 | def extract_answer(completion): 12 | 13 | def _get_last_digit(s): 14 | _PAT_LAST_DIGIT = re.compile( 15 | r'(?<=(\s|[\$%#{]))([+-])?(?=(\S))(0|([1-9](\d*|\d{0,2}(,\d{3})*)))?(\.\d*[1-9])?(?=(\s|[.,}]|$))') 16 | match = list(_PAT_LAST_DIGIT.finditer(s)) 17 | if match: 18 | last_digit = match[-1].group().replace(',', '').replace('+', '') 19 | else: 20 | last_digit = None 21 | logging.warning(f'No digits found in {s!r}') 22 | return last_digit 23 | 24 | job_gen = completion.strip('.').replace('\n', '\\n') 25 | last_digit = _get_last_digit(job_gen) 26 | if last_digit: 27 | return eval(last_digit) 28 | else: 29 | return INVALID_ANS 30 | 31 | 32 | def is_correct(completion, answer): 33 | gold = extract_answer(answer) 34 | assert gold != INVALID_ANS, 'No ground truth answer found in the document.' 35 | return extract_answer(completion) == gold 36 | 37 | 38 | def eval_gsm8k_acc(output_fname): 39 | data_list = load_jsonl(output_fname) 40 | acc_res = [item['acc'] for item in data_list] 41 | logging.info('=' * 60) 42 | logging.info('{:^60}'.format('Math Acc.')) 43 | logging.info('=' * 60) 44 | logging.info('Total num={:.2f}'.format(len(acc_res))) 45 | logging.info('Right num={:.2f}'.format(np.sum(acc_res))) 46 | logging.info('Zero-shot Acc={:.2f}'.format(np.mean(acc_res) * 100)) 47 | 48 | error_data_list = [item for item in data_list if not item['acc']] 49 | error_data_output_fname = os.path.splitext(output_fname)[0] + '_gsm8k_error.jsonl' 50 | save_jsonl(error_data_list, error_data_output_fname) 51 | 52 | return {'math': np.mean(acc_res) * 100} 53 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/models/__init__.py: -------------------------------------------------------------------------------- 1 | from models.base import HFModel # noqa 2 | from models.dashscope import QwenDashscopeVLModel # noqa 3 | from models.llm import LLM # noqa 4 | from models.qwen import Qwen, QwenVL # noqa 5 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/models/base.py: -------------------------------------------------------------------------------- 1 | from transformers import AutoModelForCausalLM, AutoTokenizer 2 | from transformers.generation import GenerationConfig 3 | 4 | 5 | class HFModel(object): 6 | 7 | def __init__(self, model_path): 8 | self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) 9 | self.model = AutoModelForCausalLM.from_pretrained(model_path, 10 | trust_remote_code=True, 11 | device_map='auto', 12 | low_cpu_mem_usage=True).eval() 13 | self.model.generation_config = GenerationConfig.from_pretrained(model_path, trust_remote_code=True) 14 | self.model.generation_config.do_sample = False 15 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/models/dashscope.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import time 4 | from http import HTTPStatus 5 | 6 | import dashscope 7 | 8 | 9 | class QwenDashscopeVLModel(object): 10 | 11 | def __init__(self, model, api_key): 12 | self.model = model 13 | dashscope.api_key = api_key.strip() or os.getenv('DASHSCOPE_API_KEY', default='') 14 | assert dashscope.api_key, 'DASHSCOPE_API_KEY is required.' 15 | 16 | def generate(self, prompt, stop_words=[]): 17 | if isinstance(prompt, str): 18 | prompt = [{'text': prompt}] 19 | 20 | MAX_TRY = 3 21 | count = 0 22 | while count < MAX_TRY: 23 | response = dashscope.MultiModalConversation.call( 24 | self.model, 25 | messages=[{ 26 | 'role': 'user', 27 | 'content': prompt 28 | }], 29 | top_p=0.01, 30 | top_k=1, 31 | ) 32 | if response.status_code == HTTPStatus.OK: 33 | output = response.output.choices[0].message.content[0]['text'] 34 | for stop_str in stop_words: 35 | idx = output.find(stop_str) 36 | if idx != -1: 37 | output = output[:idx + len(stop_str)] 38 | return output 39 | else: 40 | err = 'Error code: %s, error message: %s' % ( 41 | response.code, 42 | response.message, 43 | ) 44 | logging.error(err) 45 | count += 1 46 | time.sleep(1) 47 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/models/llm.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from models.base import HFModel 3 | 4 | 5 | class LLM(HFModel): 6 | 7 | def __init__(self, model_path): 8 | super().__init__(model_path) 9 | 10 | def generate(self, input_text, stop_words=[], max_new_tokens=512): 11 | if isinstance(input_text, str): 12 | input_text = [input_text] 13 | 14 | input_ids = self.tokenizer(input_text)['input_ids'] 15 | input_ids = torch.tensor(input_ids, device=self.model.device) 16 | gen_kwargs = {'max_new_tokens': max_new_tokens, 'do_sample': False} 17 | outputs = self.model.generate(input_ids, **gen_kwargs) 18 | s = outputs[0][input_ids.shape[1]:] 19 | output = self.tokenizer.decode(s, skip_special_tokens=True) 20 | 21 | for stop_str in stop_words: 22 | idx = output.find(stop_str) 23 | if idx != -1: 24 | output = output[:idx + len(stop_str)] 25 | 26 | return output 27 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/models/qwen.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from models.base import HFModel 3 | 4 | 5 | class Qwen(HFModel): 6 | 7 | def __init__(self, model_path): 8 | super().__init__(model_path) 9 | 10 | def generate(self, input_text, stop_words=[]): 11 | im_end = '<|im_end|>' 12 | if im_end not in stop_words: 13 | stop_words = stop_words + [im_end] 14 | stop_words_ids = [self.tokenizer.encode(w) for w in stop_words] 15 | 16 | input_ids = torch.tensor([self.tokenizer.encode(input_text)]).to(self.model.device) 17 | output = self.model.generate(input_ids, stop_words_ids=stop_words_ids) 18 | output = output.tolist()[0] 19 | output = self.tokenizer.decode(output, errors='ignore') 20 | assert output.startswith(input_text) 21 | output = output[len(input_text):].replace('<|endoftext|>', '').replace(im_end, '') 22 | 23 | return output 24 | 25 | 26 | class QwenVL(HFModel): 27 | 28 | def __init__(self, model_path): 29 | super().__init__(model_path) 30 | 31 | def generate(self, inputs: list): 32 | query = self.tokenizer.from_list_format(inputs) 33 | response, _ = self.model.chat(self.tokenizer, query=query, history=None) 34 | 35 | return response 36 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/parser/__init__.py: -------------------------------------------------------------------------------- 1 | from parser.internlm_parser import InternLMReActParser # noqa 2 | from parser.react_parser import ReActParser # noqa 3 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/parser/internlm_parser.py: -------------------------------------------------------------------------------- 1 | from parser.react_parser import ReActParser 2 | 3 | 4 | class InternLMReActParser(ReActParser): 5 | 6 | def __init__(self): 7 | self.action = '\nAction:' 8 | self.action_input = '\nActionInput:' 9 | self.action_input_stop = '' 10 | self.observation = '<|System|>:Response:' 11 | self.observation_stop = '\n<|Bot|>:' 12 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/parser/react_parser.py: -------------------------------------------------------------------------------- 1 | class ReActParser(object): 2 | 3 | def __init__(self): 4 | self.action = '\nAction:' 5 | self.action_input = '\nAction Input:' 6 | self.action_input_stop = '\nObservation:' 7 | self.observation = '\nObservation:' 8 | self.observation_stop = '\nThought:' 9 | 10 | def parse_latest_plugin_call(self, text): 11 | action = self.action 12 | action_input = self.action_input 13 | observation = self.action_input_stop 14 | plugin_name, plugin_args = '', '' 15 | i = text.rfind(action) 16 | j = text.rfind(action_input) 17 | k = text.rfind(observation) 18 | if 0 <= i < j: # If the text has `Action` and `Action input`, 19 | if k < j: # but does not contain `Observation`, 20 | # then it is likely that `Observation` is ommited by the LLM, 21 | # because the output text may have discarded the stop word. 22 | text = text.rstrip() + observation # Add it back. 23 | k = text.rfind(observation) 24 | plugin_name = text[i + len(action):j].strip() 25 | plugin_args = text[j + len(action_input):k].strip() 26 | text = text[:k] 27 | return plugin_name, plugin_args, text 28 | 29 | def _extract_first_target(self, text, start_flag, end_flag): 30 | target = '' 31 | i = text.find(start_flag) 32 | if i != -1: 33 | j = text.find(end_flag, i) 34 | if j != -1: 35 | target = text[i + len(start_flag):j].strip() 36 | else: 37 | target = text[i + len(start_flag):].strip() 38 | return target 39 | 40 | def get_first_observation(self, text): 41 | return self._extract_first_target(text, self.observation, self.observation_stop) 42 | 43 | def get_first_action_input(self, text): 44 | return self._extract_first_target(text, self.action_input, self.action_input_stop) 45 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/prompt/__init__.py: -------------------------------------------------------------------------------- 1 | from prompt.internlm_react import InternLMReAct # noqa 2 | from prompt.llama_react import LlamaReAct # noqa 3 | from prompt.qwen_react import QwenReAct # noqa 4 | from prompt.react import ReAct # noqa 5 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/prompt/internlm_react.py: -------------------------------------------------------------------------------- 1 | from prompt.react import ReAct 2 | 3 | INTERNLM_TOOL_DESCRIPTION = """用来执行Python代码。代码必须是一个函数, 4 | 函数名必须得是 'solution',代码对应你的思考过程。代码实例格式如下: 5 | ```python 6 | # import 依赖包 7 | import xxx 8 | def solution(): 9 | # 初始化一些变量 10 | variable_names_with_real_meaning = xxx 11 | # 步骤一 12 | mid_variable = func(variable_names_with_real_meaning) 13 | # 步骤 x 14 | mid_variable = func(mid_variable) 15 | # 最后结果 16 | final_answer = func(mid_variable) 17 | return final_answer 18 | ```""" 19 | 20 | INTERNLM_TOOL = {'PythonInterpreter': INTERNLM_TOOL_DESCRIPTION} 21 | 22 | INTERNLM_REACT_PROMPT_ZH = """<|System|>:你是一个可以调用外部工具的助手,可以使用的工具包括: 23 | {tools_text} 24 | 如果使用工具请遵循以下格式回复: 25 | ``` 26 | Thought:思考你当前步骤需要解决什么问题,是否需要使用工具 27 | Action:工具名称,你的工具必须从 [{tools_name_text}] 选择 28 | ActionInput:工具输入参数 29 | ``` 30 | 工具返回按照以下格式回复: 31 | ``` 32 | Response:调用工具后的结果 33 | ``` 34 | 如果你已经知道了答案,或者你不需要工具,请遵循以下格式回复 35 | ``` 36 | Thought:给出最终答案的思考过程 37 | FinalAnswer:最终答案 38 | ``` 39 | 开始! 40 | <|User|>:{query} 41 | <|Bot|>:""" 42 | 43 | INTERNLM_REACT_PROMPT_EN = """<|System|>:You are a assistant who can utilize external tools. 44 | {tools_text} 45 | To use a tool, please use the following format: 46 | ``` 47 | Thought: Think what you need to solve, do you need to use tools? 48 | Action: the tool name, should be one of [{tools_name_text}] 49 | ActionInput: the input to the action 50 | ``` 51 | The response after utilizing tools should using the following format: 52 | ``` 53 | Response: the results after call the tool. 54 | `` 55 | If you already know the answer, or you do not need to use tools, 56 | please using the following format to reply: 57 | ``` 58 | Thought: the thought process to get the final answer 59 | FinalAnswer: final answer 60 | ``` 61 | Begin! 62 | <|User|>:{query} 63 | <|Bot|>:""" 64 | 65 | 66 | class InternLMReAct(ReAct): 67 | 68 | def __init__(self, query, lang='en', upload_file_paths=[]): 69 | super().__init__(query, lang, upload_file_paths) 70 | self.react_template = INTERNLM_REACT_PROMPT_ZH if self.lang == 'zh' else INTERNLM_REACT_PROMPT_EN 71 | 72 | def build_prompt(self): 73 | planning_prompt = super().build_prompt() 74 | if '<|im_end|>' in self.query and planning_prompt.endswith('\n<|Bot|>:'): 75 | planning_prompt = planning_prompt[:-len('\n<|Bot|>:')] 76 | 77 | if '<|im_end|>' in self.query: 78 | planning_prompt = planning_prompt.replace('<|im_end|>\n<|im_start|>assistant\n', '\n<|Bot|>:').replace( 79 | 'Observation:', 80 | '\n<|System|>:Response:').replace('\nAction Input', 81 | '\nActionInput').replace('code_interpreter', 'PythonInterpreter') 82 | assert planning_prompt.endswith('Thought:') 83 | planning_prompt = planning_prompt[:-len('Thought:')] + '\n<|Bot|>:' 84 | 85 | self.prompt = planning_prompt 86 | return planning_prompt 87 | 88 | def _build_tools_text(self): 89 | return INTERNLM_TOOL 90 | 91 | def _build_tools_name_text(self): 92 | return list(INTERNLM_TOOL.keys()) 93 | 94 | def build_observation(self, observation): 95 | return f'\n<|System|>:Response:{observation}\n\n<|Bot|>:' 96 | 97 | def get_stop_words_list(self): 98 | return [''] 99 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/prompt/llama_react.py: -------------------------------------------------------------------------------- 1 | from prompt.react import ReAct 2 | 3 | 4 | class LlamaReAct(ReAct): 5 | 6 | def __init__(self, query, lang='en', upload_file_paths=[]): 7 | super().__init__(query, lang, upload_file_paths) 8 | 9 | def build_prompt(self): 10 | planning_prompt = super().build_prompt() 11 | planning_prompt = '[INST] ' + planning_prompt + ' [/INST]' 12 | 13 | if '<|im_end|>' in self.query: 14 | planning_prompt = planning_prompt.replace('<|im_end|>\n<|im_start|>assistant', ' [/INST] ') 15 | assert planning_prompt.endswith(' [/INST]') 16 | planning_prompt = planning_prompt[:-len(' [/INST]')] 17 | 18 | self.prompt = planning_prompt 19 | return planning_prompt 20 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/prompt/qwen_react.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | from prompt.react import ReAct 5 | 6 | QWEN_TOOLS_LIST = [ 7 | { 8 | 'name_for_human': '代码解释器', 9 | 'name_for_model': 'code_interpreter', 10 | 'description_for_model': '代码解释器,可用于执行Python代码。', 11 | 'parameters': [{ 12 | 'name': 'code', 13 | 'type': 'string', 14 | 'description': '待执行的代码' 15 | }], 16 | 'args_format': 'code' 17 | }, 18 | ] 19 | 20 | TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters}""" 21 | 22 | 23 | class QwenReAct(ReAct): 24 | 25 | def __init__(self, query, lang='en', upload_file_paths=[]): 26 | super().__init__(query, lang, upload_file_paths) 27 | 28 | self.upload_file_paths = [f'{os.path.basename(fname)}' for fname in upload_file_paths] 29 | self.list_of_plugin_info = QWEN_TOOLS_LIST 30 | self.fname_template = { 31 | 'zh': '[上传文件{fname_str}]', 32 | 'en': '[Upload file {fname_str}]', 33 | 'en_multi': '[Upload file {fname_str}]' 34 | } 35 | 36 | def build_prompt(self): 37 | im_start = '<|im_start|>' 38 | im_end = '<|im_end|>' 39 | prompt = f'{im_start}system\nYou are a helpful assistant.{im_end}' 40 | 41 | query = super().build_prompt() 42 | 43 | query = query.lstrip('\n').rstrip() 44 | prompt += f'\n{im_start}user\n{query}{im_end}' 45 | if f'{im_start}assistant' not in query: 46 | prompt += f'\n{im_start}assistant\n{im_end}' 47 | assert prompt.endswith(f'\n{im_start}assistant\n{im_end}') 48 | 49 | prompt = prompt[:-len(f'{im_end}')] 50 | self.prompt = prompt 51 | return prompt 52 | 53 | def _build_tools_text(self): 54 | # tool info 55 | tools_text = [] 56 | for plugin_info in self.list_of_plugin_info: 57 | tool = TOOL_DESC.format( 58 | name_for_model=plugin_info['name_for_model'], 59 | name_for_human=plugin_info['name_for_human'], 60 | description_for_model=plugin_info['description_for_model'], 61 | parameters=json.dumps(plugin_info['parameters'], ensure_ascii=False), 62 | ) 63 | if plugin_info.get('args_format', 'json') == 'json': 64 | tool += ' Format the arguments as a JSON object.' 65 | elif plugin_info['args_format'] == 'code': 66 | tool += ' Enclose the code within triple backticks (`) at the beginning and end of the code.' 67 | else: 68 | raise NotImplementedError 69 | tools_text.append(tool) 70 | tools_text = '\n\n'.join(tools_text) 71 | return tools_text 72 | 73 | def _build_tools_name_text(self): 74 | return ', '.join([plugin_info['name_for_model'] for plugin_info in self.list_of_plugin_info]) 75 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/prompt/react.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | tools_text = """code_interpreter: Call this tool to interact with the Code Interpreter API. 4 | What is the Code Interpreter API useful for? 5 | Code Interpreter is used to execute Python code to deal with the following tasks: 6 | 1. Solving mathematical problems, both quantitative and qualitative 7 | 2. Doing data analysis and visualization 8 | 3. Converting files between formats 9 | Parameters: 10 | ```py 11 | code 12 | ``` 13 | Enclose the code within triple backticks (```) at the beginning and end of the code. 14 | """ 15 | 16 | REACT_PROMPT = """Answer the following questions as best you can. You have access to the following tools: 17 | 18 | {tools_text} 19 | 20 | Use the following format: 21 | 22 | Question: the input question you must answer 23 | Thought: you should always think about what to do 24 | Action: the action to take, should be one of [{tools_name_text}] 25 | Action Input: the input to the action 26 | Observation: the result of the action 27 | ... (this Thought/Action/Action Input/Observation can be repeated zero or more times) 28 | Thought: I now know the final answer 29 | Final Answer: the final answer to the original input question 30 | 31 | Begin! 32 | 33 | Question: {query}""" 34 | 35 | fname_template = { 36 | 'zh': '文件{fname_str},', 37 | 'en_multi': 'Files {fname_str}. ', 38 | 'en': 'File {fname_str}. ', 39 | } 40 | 41 | 42 | class ReAct(object): 43 | 44 | def __init__(self, query, lang='en', upload_file_paths=[]): 45 | self.query = query 46 | self.lang = lang 47 | self.upload_file_paths = [f'`{os.path.basename(fname)}`' for fname in upload_file_paths] 48 | 49 | self.fname_template = fname_template 50 | self.react_template = REACT_PROMPT 51 | self.prompt = '' 52 | 53 | def build_prompt(self): 54 | query = self._format_upload_fname() + self.query 55 | tools_text = self._build_tools_text() 56 | tools_name_text = self._build_tools_name_text() 57 | planning_prompt = self.react_template.format(query=query, 58 | tools_text=tools_text, 59 | tools_name_text=tools_name_text) 60 | 61 | self.prompt = planning_prompt 62 | return planning_prompt 63 | 64 | def _format_upload_fname(self): 65 | prefix = '' 66 | if self.upload_file_paths: 67 | fname_str = ', '.join(self.upload_file_paths) 68 | lang_key = 'en_multi' if self.lang == 'en' and len(self.upload_file_paths) > 1 else self.lang 69 | fname_template = self.fname_template[lang_key] 70 | prefix = fname_template.format(fname_str=fname_str) 71 | return prefix 72 | 73 | def _build_tools_text(self): 74 | return tools_text 75 | 76 | def _build_tools_name_text(self): 77 | return 'code_interpreter' 78 | 79 | def build_observation(self, observation): 80 | return f'\nObservation: {observation}\nThought:' 81 | 82 | def get_stop_words_list(self): 83 | return ['Observation:', 'Observation:\n'] 84 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/requirements.txt: -------------------------------------------------------------------------------- 1 | accelerate>=0.20.3 2 | func_timeout 3 | json5 4 | matplotlib 5 | numpy 6 | openai 7 | pandas 8 | PrettyTable 9 | scipy 10 | seaborn 11 | sympy 12 | transformers==4.33.1 13 | transformers_stream_generator 14 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/benchmark/code_interpreter/utils/__init__.py -------------------------------------------------------------------------------- /benchmark/code_interpreter/utils/code_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | import json5 5 | 6 | 7 | def replace_upload_fname(text, upload_fname_list): 8 | for full_input_fname in upload_fname_list: 9 | if full_input_fname not in text and os.path.basename(full_input_fname) in text: 10 | text = text.replace(os.path.basename(full_input_fname), full_input_fname) 11 | return text 12 | 13 | 14 | def extract_code(text): 15 | # Match triple backtick blocks first 16 | triple_match = re.search(r'```[^\n]*\n(.+?)```', text, re.DOTALL) 17 | # Match single backtick blocks second 18 | single_match = re.search(r'`([^`]*)`', text, re.DOTALL) 19 | if triple_match: 20 | text = triple_match.group(1) 21 | elif single_match: 22 | text = single_match.group(1) 23 | else: 24 | try: 25 | text = json5.loads(text)['code'] 26 | except Exception: 27 | pass 28 | # If no code blocks found, return original text 29 | return text 30 | -------------------------------------------------------------------------------- /benchmark/code_interpreter/utils/data_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | 4 | from tqdm import tqdm 5 | 6 | 7 | def load_jsonl(path): 8 | data = [] 9 | with open(path, 'r', encoding='utf8') as f: 10 | for idx, line in enumerate(f, start=1): 11 | try: 12 | data.append(json.loads(line)) 13 | except Exception as e: 14 | logging.info(line) 15 | logging.warning(f'Error at line {idx}: {e}') 16 | continue 17 | return data 18 | 19 | 20 | def save_jsonl(data, path, progress=False, enabled=True): 21 | if not enabled: 22 | return 23 | with open(path, 'w', encoding='utf-8') as f: 24 | if progress: 25 | data = tqdm(data) 26 | for item in data: 27 | line = json.dumps(item, ensure_ascii=False) 28 | print(line, file=f) 29 | -------------------------------------------------------------------------------- /browser_qwen/background.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | var database; 18 | 19 | function send_data(msg){ 20 | chrome.storage.local.get(['database_host'], function(result) { 21 | if (result.database_host) { 22 | console.log('database_host currently is ' + result.database_host); 23 | database = "http://"+result.database_host+":7866/endpoint"; 24 | } else { 25 | database = "http://127.0.0.1:7866/endpoint"; 26 | } 27 | fetch(database, { 28 | method: "POST", 29 | headers: { 30 | "Content-Type": "application/json", 31 | }, 32 | body: JSON.stringify(msg), 33 | }) 34 | .then((response) => response.json()) 35 | .then((data) => { 36 | console.log(data.result); 37 | }); 38 | }); 39 | } 40 | 41 | chrome.runtime.onMessage.addListener(async (msg, sender) => { 42 | if (msg.flag == "open_tab_and_cache_from_content"){ 43 | var url = ""; 44 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 45 | url = tabs[0].url; 46 | console.log(url); 47 | if (msg.data) { 48 | chrome.storage.sync.get(['data'], function(result) { 49 | chrome.storage.sync.set({ data: result.data }, function() { 50 | send_data({ 'content' : msg.data, 'query': '', 'url':url, 'task':'cache', 'type':msg.type}); 51 | }); 52 | }); 53 | } 54 | }); 55 | } 56 | if (msg.flag == "open_popup_and_send_url_from_popup"){ 57 | if (msg.data) { 58 | chrome.storage.sync.get(['data'], function(result) { 59 | chrome.storage.sync.set({ data: result.data }, function() { 60 | send_data({ 'url' : msg.data, 'task':'pop_url'}); 61 | }); 62 | }); 63 | } 64 | } 65 | // if (msg.flag == "set_addr"){ 66 | // if (msg.data) { 67 | // chrome.storage.sync.get(['data'], function(result) { 68 | // chrome.storage.sync.set({ data: result.data }, function() { 69 | // send_data({ 'addr' : msg.data, 'task':'set_addr'}); 70 | // }); 71 | // }); 72 | // } 73 | // } 74 | }); 75 | -------------------------------------------------------------------------------- /browser_qwen/img/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/browser_qwen/img/copy.png -------------------------------------------------------------------------------- /browser_qwen/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/browser_qwen/img/logo.png -------------------------------------------------------------------------------- /browser_qwen/img/popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/browser_qwen/img/popup.png -------------------------------------------------------------------------------- /browser_qwen/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BrowserQwen", 3 | "description" : "An Extension Driven by LLM", 4 | "version": "1.0", 5 | "manifest_version": 3, 6 | 7 | "background": { 8 | "service_worker": "background.js" 9 | }, 10 | 11 | "action": { 12 | "default_popup": "src/popup.html", 13 | "default_icon": "img/popup.png", 14 | "default_title": "BrowserQwen" 15 | }, 16 | "permissions": [ 17 | "tabs", 18 | "notifications", 19 | "storage", 20 | "scripting", 21 | "activeTab" 22 | ], 23 | "host_permissions": [ 24 | "http://*/*", 25 | "https://*/*" 26 | ], 27 | "icons": { 28 | "16": "img/popup.png", 29 | "32": "img/popup.png", 30 | "48": "img/popup.png", 31 | "128": "img/popup.png" 32 | }, 33 | "content_scripts": [ 34 | { 35 | "js": ["src/content.js"], 36 | "matches": [ 37 | "https://www.jianshu.com/p/*", 38 | "https://*/*", 39 | "http://*/*", 40 | "file:///*/*" 41 | ] 42 | } 43 | ] 44 | 45 | } 46 | -------------------------------------------------------------------------------- /browser_qwen/src/content.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | function getPageTextContent() { 19 | var textContent = document.body.textContent; 20 | return textContent; 21 | } 22 | 23 | function cache_browser(){ 24 | const body = document.querySelector('html'); 25 | const text = body.innerHTML; 26 | console.log(text); 27 | chrome.runtime.sendMessage({ data: text , close: true , flag: 'open_tab_and_cache_from_content', type: 'html'}); 28 | 29 | } 30 | 31 | const floatingBox = document.createElement('div'); 32 | floatingBox.style.position = 'fixed'; 33 | floatingBox.style.bottom = '650px'; 34 | floatingBox.style.right = '60px'; 35 | floatingBox.style.width = '125px'; 36 | floatingBox.style.height = '55px'; 37 | floatingBox.style.backgroundColor = '#f2f2f2'; 38 | floatingBox.style.border = '1px solid black'; 39 | floatingBox.style.borderRadius = '5px'; 40 | floatingBox.style.padding = '10px'; 41 | floatingBox.style.zIndex = '9999'; 42 | 43 | const button = document.createElement('button'); 44 | button.style.position = 'fixed'; 45 | button.style.top = '30px'; 46 | button.style.right = '30px'; 47 | button.style.zIndex = "9999"; 48 | button.textContent = "Add to Qwen's Reading List"; 49 | button.style.fontFamily = 'Arial, sans-serif'; 50 | button.style.fontSize = '14px'; 51 | button.style.width = '140px'; 52 | button.style.height = '60px'; 53 | button.style.backgroundColor = '#695DE8'; 54 | button.style.color = 'white'; 55 | button.style.borderRadius = '5px'; 56 | button.style.border = '0px'; 57 | button.style.whiteSpace = 'pre-wrap'; 58 | button.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.2)'; 59 | 60 | floatingBox.appendChild(button); 61 | 62 | document.body.appendChild(button); 63 | 64 | let isDragging = false; 65 | var isMouseReleased = false; 66 | let initialX; 67 | let initialY; 68 | 69 | button.addEventListener('mousedown', (e) => { 70 | isDragging = true; 71 | initialX = e.clientX; 72 | initialY = e.clientY; 73 | }); 74 | 75 | document.addEventListener('mousemove', (e) => { 76 | if (isDragging) { 77 | const dx = e.clientX - initialX; 78 | const dy = e.clientY - initialY; 79 | button.style.right = `${parseFloat(button.style.right) - dx}px`; 80 | button.style.top = `${parseFloat(button.style.top) + dy}px`; 81 | initialX = e.clientX; 82 | initialY = e.clientY; 83 | isMouseReleased = true; 84 | } 85 | }); 86 | 87 | document.addEventListener('mouseup', (e) => { 88 | isDragging = false; 89 | 90 | }); 91 | 92 | button.addEventListener('click', (e) => { 93 | if (isMouseReleased) { 94 | isMouseReleased = false; 95 | e.stopPropagation(); 96 | } else { 97 | var result = confirm("Are you sure to ask Qwen to remember this page?"); 98 | if (result) { 99 | cache_browser() 100 | } 101 | } 102 | }); 103 | -------------------------------------------------------------------------------- /browser_qwen/src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BrowserQwen 8 | 9 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
114 | 115 |

Customize Address:

116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /browser_qwen/src/popup.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | 19 | chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { 20 | // if (msg.flag == 'from_content'){ 21 | // console.log(msg.rsp); 22 | // var sessionContainer = document.getElementById('session'); 23 | // sessionContainer.innerText = msg.rsp; 24 | // sendResponse({ msg: 'Get!' }); 25 | // } 26 | if (msg.flag === 'from_llm'){ 27 | // var sessionContainer = document.getElementById('session'); 28 | // // sessionContainer.innerHTML = msg.rsp; 29 | // sessionContainer.innerText = msg.rsp; 30 | sendResponse({ message: 'Get Response!' }); 31 | } 32 | }); 33 | 34 | 35 | document.addEventListener('DOMContentLoaded', function() { 36 | chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { 37 | var currentUrl = tabs[0].url; 38 | 39 | chrome.runtime.sendMessage({ data: currentUrl , close: true , flag: 'open_popup_and_send_url_from_popup'}); 40 | 41 | }); 42 | setTimeout(function() { 43 | // console.log('This message will be logged after 0.5 second'); 44 | var popup_url=''; 45 | chrome.storage.local.get(['database_host'], function(result) { 46 | if (result.database_host) { 47 | console.log('database_host currently is ' + result.database_host); 48 | popup_url = "http://"+result.database_host+":7863/"; 49 | } else { 50 | popup_url = "http://127.0.0.1:7863/"; 51 | } 52 | var iframe = document.createElement('iframe'); 53 | iframe.src = popup_url; 54 | iframe.height = '570px'; 55 | // iframe.sandbox = 'allow-same-origin allow-scripts'; 56 | // iframe.allow = "geolocation *;"; 57 | var iframe_area = document.getElementById('iframe_area') 58 | iframe_area.appendChild(iframe); 59 | 60 | }); 61 | }, 500); 62 | 63 | // fetch('../config_host.json') 64 | // .then(response => response.json()) 65 | // .then(data => { 66 | // console.log(data); 67 | // popup_url = "http://"+data.database_host+":"+data.app_in_browser_port+"/"; 68 | // console.log(popup_url); 69 | // }) 70 | // .catch(error => console.error('Error:', error)); 71 | }) 72 | 73 | document.getElementById('set_addr').addEventListener('click', function() { 74 | var addr = document.getElementById('addr').value; 75 | // save config 76 | chrome.storage.local.set({database_host: addr}, function() { 77 | console.log('database_host is set to ' + addr); 78 | // chrome.runtime.sendMessage({ data: addr , close: true , flag: 'set_addr'}); 79 | document.getElementById('addr').value = ''; 80 | }); 81 | }) 82 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | [中文](./README_CN.md) | English 18 | 19 | The usage and development process of Qwen-Agent. 20 | 21 | # Contents 22 | 23 | - [Agent](agent.md) 24 | - [Tool](tool.md) 25 | - [LLM](llm.md) 26 | -------------------------------------------------------------------------------- /docs/README_CN.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | 中文 | [English](./README.md) 18 | 19 | 此处介绍Qwen-Agent的使用与开发流程。 20 | 21 | # 列表 22 | 23 | - [Agent](agent_cn.md) 24 | - [Tool](tool_cn.md) 25 | - [LLM](llm_cn.md) 26 | -------------------------------------------------------------------------------- /docs/llm_cn.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # LLM 介绍 18 | 19 | 本文档介绍了LLM类的使用和开发流程。 20 | 21 | ## 1. LLM 使用 22 | 23 | 目前,Qwen-Agent提供了Qwen的DashScope API和OpenAI API访问接口;Qwen-VL的DashScope API访问接口。均已经支持流式Function Calling。 24 | 25 | ### 1.1. 外部直接调用 26 | LLM统一使用`get_chat_model(cfg: Optional[Dict] = None) -> BaseChatModel`接口来调用,参数传入LLM的配置文件, 27 | 配置文件格式如下: 28 | - model_type: 对应某个具体的llm类,是llm类注册的名字,即唯一ID。使用内置的dashscope和OpenAI API时,可以省略这个参数。外部注册的LLM类,需要传入这个参数来指定。 29 | - model:具体的模型名称 30 | - model_server:模型服务地址 31 | - generate_cfg:模型生成时候的参数 32 | 33 | LLM类统一使用`llm.chat(...)`接口生成回复,支持输入消息列表、函数等参数,具体参数列表详见[BaseChatModel](../qwen_agent/llm/base.py)。 34 | LLM完整使用样例见[Function Calling](../examples/function_calling.py)。 35 | ```py 36 | 37 | from qwen_agent.llm import get_chat_model 38 | 39 | llm_cfg = { 40 | # Use the model service provided by DashScope: 41 | # 'model_type': 'qwen_dashscope', 42 | 'model': 'qwen-max', 43 | 'model_server': 'dashscope', 44 | # Use your own model service compatible with OpenAI API: 45 | # 'model': 'Qwen', 46 | # 'model_server': 'http://127.0.0.1:7905/v1', 47 | # (Optional) LLM hyper-parameters: 48 | 'generate_cfg': { 49 | 'top_p': 0.8 50 | } 51 | } 52 | llm = get_chat_model(llm_cfg) 53 | messages = [{ 54 | 'role': 'user', 55 | 'content': "What's the weather like in San Francisco?" 56 | }] 57 | functions = [{ 58 | 'name': 'get_current_weather', 59 | 'description': 'Get the current weather in a given location', 60 | 'parameters': { 61 | 'type': 'object', 62 | 'properties': { 63 | 'location': { 64 | 'type': 'string', 65 | 'description': 66 | 'The city and state, e.g. San Francisco, CA', 67 | }, 68 | 'unit': { 69 | 'type': 'string', 70 | 'enum': ['celsius', 'fahrenheit'] 71 | }, 72 | }, 73 | 'required': ['location'], 74 | }, 75 | }] 76 | 77 | # 此处演示流式输出效果 78 | responses = [] 79 | for responses in llm.chat(messages=messages, 80 | functions=functions, 81 | stream=True): 82 | print(responses) 83 | ``` 84 | 85 | ### 1.2. Agent内部调用 86 | 87 | 在Agent中,使用`_call_llm(...)`函数调用LLM,每个Agent实例可以调用初始化给它的LLM, 88 | `llm: Optional[Union[Dict, BaseChatModel]] = None`。支持的传入类型包括::LLM配置文件或LLM对象。 89 | 90 | 注意,为了统一Agent的输出类型,Agent的`_call_llm(...)`接口默认使用流式生成方式访问LLM。 91 | 92 | ## 2. LLM 开发 93 | 94 | Qwen-Agent提供了注册LLM的机制,在[LLM 基类](../qwen_agent/llm/base.py)中,实现了统一的`llm.chat(...)`接口, 95 | 新注册的LLM仅需要实现特有的三个函数: 96 | - 非流式生成接口 97 | - 流式生成接口(如果LLM本身不支持流式生成,可以将非流式结果封装成生成器返回) 98 | - 工具调用接口 99 | 100 | 其中,如果新注册的LLM不支持工具调用,可以继承Qwen-Agent中实现的[BaseFnCallModel](../qwen_agent/llm/function_calling.py)类, 101 | 这个类通过封装一个类似ReAct的工具调用Prompt,已经基于普通对话接口实现了Function Calling。 102 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /examples/assistant_audio.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import Assistant 16 | from qwen_agent.gui import WebUI 17 | 18 | 19 | def test(): 20 | bot = Assistant(llm={'model_type': 'qwenaudio_dashscope', 'model': 'qwen-audio-turbo-latest'}) 21 | messages = [{ 22 | 'role': 23 | 'user', 24 | 'content': [{ 25 | 'audio': 'https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3' 26 | }, { 27 | 'text': '这段音频在说什么?' 28 | }] 29 | }] 30 | for rsp in bot.run(messages): 31 | print(rsp) 32 | 33 | 34 | def app_gui(): 35 | # Define the agent 36 | bot = Assistant(llm={'model': 'qwen-audio-turbo-latest'}) 37 | WebUI(bot).run() 38 | 39 | 40 | if __name__ == '__main__': 41 | # test() 42 | app_gui() 43 | -------------------------------------------------------------------------------- /examples/assistant_mcp_sqlite_bot.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A sqlite database assistant implemented by assistant""" 16 | 17 | import os 18 | import asyncio 19 | from typing import Optional 20 | 21 | from qwen_agent.agents import Assistant 22 | from qwen_agent.gui import WebUI 23 | 24 | ROOT_RESOURCE = os.path.join(os.path.dirname(__file__), 'resource') 25 | 26 | 27 | def init_agent_service(): 28 | llm_cfg = {'model': 'qwen-max'} 29 | system = ('你扮演一个数据库助手,你具有查询数据库的能力') 30 | tools = [{ 31 | "mcpServers": { 32 | "sqlite" : { 33 | "command": "uvx", 34 | "args": [ 35 | "mcp-server-sqlite", 36 | "--db-path", 37 | "test.db" 38 | ] 39 | } 40 | } 41 | }] 42 | bot = Assistant( 43 | llm=llm_cfg, 44 | name='数据库助手', 45 | description='数据库查询', 46 | system_message=system, 47 | function_list=tools, 48 | ) 49 | 50 | return bot 51 | 52 | 53 | def test(query='数据库里有几张表', file: Optional[str] = os.path.join(ROOT_RESOURCE, 'poem.pdf')): 54 | # Define the agent 55 | bot = init_agent_service() 56 | 57 | # Chat 58 | messages = [] 59 | 60 | if not file: 61 | messages.append({'role': 'user', 'content': query}) 62 | else: 63 | messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]}) 64 | 65 | for response in bot.run(messages): 66 | print('bot response:', response) 67 | 68 | 69 | def app_tui(): 70 | # Define the agent 71 | bot = init_agent_service() 72 | 73 | # Chat 74 | messages = [] 75 | while True: 76 | # Query example: 数据库里有几张表 77 | query = input('user question: ') 78 | # File example: resource/poem.pdf 79 | file = input('file url (press enter if no file): ').strip() 80 | if not query: 81 | print('user question cannot be empty!') 82 | continue 83 | if not file: 84 | messages.append({'role': 'user', 'content': query}) 85 | else: 86 | messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]}) 87 | 88 | response = [] 89 | for response in bot.run(messages): 90 | print('bot response:', response) 91 | messages.extend(response) 92 | 93 | 94 | def app_gui(): 95 | # Define the agent 96 | bot = init_agent_service() 97 | chatbot_config = { 98 | 'prompt.suggestions': [ 99 | '数据库里有几张表', 100 | '创建一个学生表包括学生的姓名、年龄', 101 | '增加一个学生名字叫韩梅梅,今年6岁', 102 | ] 103 | } 104 | WebUI( 105 | bot, 106 | chatbot_config=chatbot_config, 107 | ).run() 108 | 109 | 110 | if __name__ == '__main__': 111 | # test() 112 | # app_tui() 113 | app_gui() 114 | -------------------------------------------------------------------------------- /examples/assistant_omni.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import Assistant 16 | from qwen_agent.gui import WebUI 17 | 18 | 19 | def test(): 20 | bot = Assistant( 21 | llm={ 22 | 'model_type': 'qwenomni_oai', 23 | 'model': 'qwen-omni-turbo-latest', 24 | 'base_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1' 25 | }) 26 | messages = [{ 27 | 'role': 28 | 'user', 29 | 'content': [{ 30 | 'audio': 'https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3' 31 | }, { 32 | 'text': '这段音频在说什么?' 33 | }] 34 | }] 35 | for rsp in bot.run(messages): 36 | print(rsp) 37 | messages.extend(rsp) 38 | messages.append({ 39 | 'role': 40 | 'user', 41 | 'content': [{ 42 | 'video': 'https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4' 43 | }, { 44 | 'text': '描述这个视频' 45 | }] 46 | }) 47 | for rsp in bot.run(messages): 48 | print(rsp) 49 | 50 | 51 | def app_gui(): 52 | # Define the agent 53 | bot = Assistant( 54 | llm={ 55 | 'model_type': 'qwenomni_oai', 56 | 'model': 'qwen-omni-turbo-latest', 57 | 'base_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1' 58 | }, 59 | name='Qwen Omni', 60 | description='Support audio, video, image, and text input!', 61 | ) 62 | WebUI(bot).run() 63 | 64 | 65 | if __name__ == '__main__': 66 | # test() 67 | app_gui() 68 | -------------------------------------------------------------------------------- /examples/assistant_qwq.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """An image generation agent implemented by assistant with qwq""" 16 | 17 | import os 18 | 19 | from qwen_agent.agents import Assistant 20 | from qwen_agent.gui import WebUI 21 | from qwen_agent.utils.output_beautify import typewriter_print 22 | 23 | ROOT_RESOURCE = os.path.join(os.path.dirname(__file__), 'resource') 24 | 25 | 26 | def init_agent_service(): 27 | llm_cfg = { 28 | 'model': 'qwq-32b', 29 | 'model_type': 'qwen_dashscope', 30 | 'generate_cfg': { 31 | 'fncall_prompt_type': 'nous', 32 | 33 | # This parameter needs to be passed in when the deployed model is an reasoning model (e.g. qwq-32b) and *does not* support the reasoning_content field (e.g. deploying qwq-32b directly with an old version of vLLM) 34 | # Add: When the content is `this is the thoughtthis is the answer` 35 | # Do not add: When the response has been separated by reasoning_content and content 36 | # This parameter will affect the parsing strategy of tool call 37 | # 'thought_in_content': True, 38 | }, 39 | } 40 | tools = [ 41 | 'image_gen', 42 | # 'web_search', # Apply for an apikey here (https://serper.dev) and set it as an environment variable by `export SERPER_API_KEY=xxxxxx` 43 | ] 44 | bot = Assistant( 45 | llm=llm_cfg, 46 | function_list=tools, 47 | name='QwQ-32B Tool-calling Demo', 48 | description="I'm a demo using the QwQ-32B tool calling. Welcome to add and play with your own tools!") 49 | 50 | return bot 51 | 52 | 53 | def test(query: str = '画一只猫,再画一只狗,最后画他们一起玩的画面,给我三张图'): 54 | # Define the agent 55 | bot = init_agent_service() 56 | 57 | # Chat 58 | messages = [{'role': 'user', 'content': query}] 59 | response_plain_text = '' 60 | for response in bot.run(messages=messages): 61 | response_plain_text = typewriter_print(response, response_plain_text) 62 | 63 | 64 | def app_tui(): 65 | # Define the agent 66 | bot = init_agent_service() 67 | 68 | # Chat 69 | messages = [] 70 | while True: 71 | query = input('user question: ') 72 | messages.append({'role': 'user', 'content': query}) 73 | response = [] 74 | response_plain_text = '' 75 | for response in bot.run(messages=messages): 76 | response_plain_text = typewriter_print(response, response_plain_text) 77 | messages.extend(response) 78 | 79 | 80 | def app_gui(): 81 | # Define the agent 82 | bot = init_agent_service() 83 | chatbot_config = {'prompt.suggestions': ['画一只猫,再画一只狗,最后画他们一起玩的画面,给我三张图']} 84 | WebUI( 85 | bot, 86 | chatbot_config=chatbot_config, 87 | ).run() 88 | 89 | 90 | if __name__ == '__main__': 91 | # test() 92 | # app_tui() 93 | app_gui() 94 | -------------------------------------------------------------------------------- /examples/assistant_rag.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import Assistant 16 | from qwen_agent.gui import WebUI 17 | 18 | 19 | def test(): 20 | bot = Assistant(llm={'model': 'qwen-plus-latest'}) 21 | messages = [{'role': 'user', 'content': [{'text': '介绍图一'}, {'file': 'https://arxiv.org/pdf/1706.03762.pdf'}]}] 22 | for rsp in bot.run(messages): 23 | print(rsp) 24 | 25 | 26 | def app_gui(): 27 | # Define the agent 28 | bot = Assistant(llm={'model': 'qwen-plus-latest'}, 29 | name='Assistant', 30 | description='使用RAG检索并回答,支持文件类型:PDF/Word/PPT/TXT/HTML。') 31 | chatbot_config = { 32 | 'prompt.suggestions': [ 33 | { 34 | 'text': '介绍图一' 35 | }, 36 | { 37 | 'text': '第二章第一句话是什么?' 38 | }, 39 | ] 40 | } 41 | WebUI(bot, chatbot_config=chatbot_config).run() 42 | 43 | 44 | if __name__ == '__main__': 45 | # test() 46 | app_gui() 47 | -------------------------------------------------------------------------------- /examples/assistant_weather_bot.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A weather forecast assistant implemented by assistant""" 16 | 17 | import os 18 | from typing import Optional 19 | 20 | from qwen_agent.agents import Assistant 21 | from qwen_agent.gui import WebUI 22 | 23 | ROOT_RESOURCE = os.path.join(os.path.dirname(__file__), 'resource') 24 | 25 | 26 | def init_agent_service(): 27 | llm_cfg = {'model': 'qwen-max'} 28 | system = ('你扮演一个天气预报助手,你具有查询天气和画图能力。' 29 | '你需要查询相应地区的天气,然后调用给你的画图工具绘制一张城市的图,并从给定的诗词文档中选一首相关的诗词来描述天气,不要说文档以外的诗词。') 30 | 31 | tools = ['image_gen', 'amap_weather'] 32 | bot = Assistant( 33 | llm=llm_cfg, 34 | name='天气预报助手', 35 | description='查询天气和画图', 36 | system_message=system, 37 | function_list=tools, 38 | ) 39 | 40 | return bot 41 | 42 | 43 | def test(query='海淀区天气', file: Optional[str] = os.path.join(ROOT_RESOURCE, 'poem.pdf')): 44 | # Define the agent 45 | bot = init_agent_service() 46 | 47 | # Chat 48 | messages = [] 49 | 50 | if not file: 51 | messages.append({'role': 'user', 'content': query}) 52 | else: 53 | messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]}) 54 | 55 | for response in bot.run(messages): 56 | print('bot response:', response) 57 | 58 | 59 | def app_tui(): 60 | # Define the agent 61 | bot = init_agent_service() 62 | 63 | # Chat 64 | messages = [] 65 | while True: 66 | # Query example: 海淀区天气 67 | query = input('user question: ') 68 | # File example: resource/poem.pdf 69 | file = input('file url (press enter if no file): ').strip() 70 | if not query: 71 | print('user question cannot be empty!') 72 | continue 73 | if not file: 74 | messages.append({'role': 'user', 'content': query}) 75 | else: 76 | messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]}) 77 | 78 | response = [] 79 | for response in bot.run(messages): 80 | print('bot response:', response) 81 | messages.extend(response) 82 | 83 | 84 | def app_gui(): 85 | # Define the agent 86 | bot = init_agent_service() 87 | chatbot_config = { 88 | 'prompt.suggestions': [ 89 | '查询北京的天气', 90 | '画一张北京的图片', 91 | '画一张北京的图片,然后配上一首诗', 92 | ] 93 | } 94 | WebUI( 95 | bot, 96 | chatbot_config=chatbot_config, 97 | ).run() 98 | 99 | 100 | if __name__ == '__main__': 101 | # test() 102 | # app_tui() 103 | app_gui() 104 | -------------------------------------------------------------------------------- /examples/gpt_mentions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A gpt @mentions gradio demo""" 16 | 17 | from qwen_agent.agents import Assistant, ReActChat 18 | from qwen_agent.agents.doc_qa import BasicDocQA 19 | from qwen_agent.gui import WebUI 20 | 21 | 22 | def init_agent_service(): 23 | llm_cfg = {'model': 'qwen-max'} 24 | 25 | react_chat_agent = ReActChat( 26 | llm=llm_cfg, 27 | name='代码解释器', 28 | description='代码解释器,可用于执行Python代码。', 29 | system_message='you are a programming expert, skilled in writing code ' 30 | 'to solve mathematical problems and data analysis problems.', 31 | function_list=['code_interpreter'], 32 | ) 33 | doc_qa_agent = BasicDocQA( 34 | llm=llm_cfg, 35 | name='文档问答', 36 | description='根据用户输入的问题和文档,从文档中找到答案', 37 | ) 38 | 39 | assistant_agent = Assistant(llm=llm_cfg, name='小助理', description="I'm a helpful assistant") 40 | 41 | return [react_chat_agent, doc_qa_agent, assistant_agent] 42 | 43 | 44 | def app_gui(): 45 | agent_list = init_agent_service() 46 | chatbotConfig = { 47 | 'prompt.suggestions': [ 48 | '@代码解释器 2 ^ 10 = ?', 49 | '@文档问答 这篇论文解决了什么问题?', 50 | '@小助理 你好!', 51 | ], 52 | 'verbose': True 53 | } 54 | WebUI( 55 | agent_list, 56 | chatbot_config=chatbotConfig, 57 | ).run(messages=[{ 58 | 'role': 'assistant', 59 | 'content': [{ 60 | 'text': '试试看 @代码解释器 来问我~' 61 | }] 62 | }], enable_mention=True) 63 | 64 | 65 | if __name__ == '__main__': 66 | app_gui() 67 | -------------------------------------------------------------------------------- /examples/group_chat_chess.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A chess play game implemented by group chat""" 16 | 17 | from qwen_agent.agents import GroupChat 18 | from qwen_agent.gui import WebUI 19 | from qwen_agent.llm.schema import Message 20 | 21 | # Define a configuration file for a multi-agent: 22 | # one real player, one NPC player, and one chessboard 23 | NPC_NAME = '小明' 24 | USER_NAME = '小塘' 25 | CFGS = { 26 | 'background': 27 | f'一个五子棋群组,棋盘为5*5,黑棋玩家和白棋玩家交替下棋,每次玩家下棋后,棋盘进行更新并展示。{NPC_NAME}下白棋,{USER_NAME}下黑棋。', 28 | 'agents': [ 29 | { 30 | 'name': 31 | '棋盘', 32 | 'description': 33 | '负责更新棋盘', 34 | 'instructions': 35 | '你扮演一个五子棋棋盘,你可以根据原始棋盘和玩家下棋的位置坐标,把新的棋盘用矩阵展示出来。棋盘中用0代表无棋子、用1表示黑棋、用-1表示白棋。用坐标表示位置,i代表行,j代表列,棋盘左上角位置为<0,0>。', 36 | 'selected_tools': ['code_interpreter'], 37 | }, 38 | { 39 | 'name': 40 | NPC_NAME, 41 | 'description': 42 | '白棋玩家', 43 | 'instructions': 44 | '你扮演一个玩五子棋的高手,你下白棋。棋盘中用0代表无棋子、用1黑棋、用-1白棋。用坐标表示位置,i代表行,j代表列,棋盘左上角位置为<0,0>,请决定你要下在哪里,你可以随意下到一个位置,不要说你是AI助手不会下!返回格式为坐标:\n\n除了这个坐标,不要返回其他任何内容', 45 | }, 46 | { 47 | 'name': USER_NAME, 48 | 'description': '黑棋玩家', 49 | 'is_human': True 50 | }, 51 | ], 52 | } 53 | 54 | 55 | def test(query: str = '<1,1>'): 56 | bot = GroupChat(agents=CFGS, llm={'model': 'qwen-max'}) 57 | 58 | messages = [Message('user', query, name=USER_NAME)] 59 | for response in bot.run(messages=messages): 60 | print('bot response:', response) 61 | 62 | 63 | def app_tui(): 64 | # Define a group chat agent from the CFGS 65 | bot = GroupChat(agents=CFGS, llm={'model': 'qwen-max'}) 66 | # Chat 67 | messages = [] 68 | while True: 69 | query = input('user question: ') 70 | messages.append(Message('user', query, name=USER_NAME)) 71 | response = [] 72 | for response in bot.run(messages=messages): 73 | print('bot response:', response) 74 | messages.extend(response) 75 | 76 | 77 | def app_gui(): 78 | # Define a group chat agent from the CFGS 79 | bot = GroupChat(agents=CFGS, llm={'model': 'qwen-max'}) 80 | chatbot_config = { 81 | 'user.name': '小塘', 82 | 'prompt.suggestions': [ 83 | '开始!我先手,落子 <1,1>', 84 | '我后手,请小明先开始', 85 | '新开一盘,我先开始', 86 | ], 87 | 'verbose': True 88 | } 89 | 90 | WebUI( 91 | bot, 92 | chatbot_config=chatbot_config, 93 | ).run() 94 | 95 | 96 | if __name__ == '__main__': 97 | # test() 98 | # app_tui() 99 | app_gui() 100 | -------------------------------------------------------------------------------- /examples/llm_quick_chat_oai.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """An example of calling text llm interfaces by OpenAI-compatible interface""" 16 | from qwen_agent.llm import get_chat_model 17 | 18 | 19 | def test(): 20 | llm_cfg = {'model': 'qwen-max', 'model_server': 'dashscope'} 21 | tools = [{ 22 | 'type': 'function', 23 | 'function': { 24 | 'name': 25 | 'image_gen', 26 | 'description': 27 | 'AI painting (image generation) service, input text description and image resolution, and return the URL of the image drawn based on the text information.', 28 | 'parameters': { 29 | 'type': 'object', 30 | 'properties': { 31 | 'prompt': { 32 | 'type': 33 | 'string', 34 | 'description': 35 | 'Detailed description of the desired content of the generated image, such as details of characters, environment, actions, etc., in English.', 36 | }, 37 | }, 38 | 'required': ['prompt'], 39 | } 40 | } 41 | }] 42 | 43 | # Chat with text llm 44 | llm = get_chat_model(llm_cfg) 45 | messages = [{'role': 'user', 'content': '你是?'}] 46 | """ 47 | llm.quick_chat_oai 48 | This is a temporary OpenAI-compatible interface that is encapsulated and may change at any time. 49 | It is mainly used for temporary interfaces and should not be overly dependent. 50 | - Only supports full streaming 51 | - The message is in dict format 52 | - Only supports text LLM 53 | """ 54 | response = llm.quick_chat_oai(messages) 55 | for x in response: 56 | print(x) 57 | messages.append(x['choices'][0]['message']) 58 | 59 | messages.append({'role': 'user', 'content': '画个可爱小猫'}) 60 | response = llm.quick_chat_oai(messages, tools=tools) 61 | for x in response: 62 | print(x) 63 | messages.append(x['choices'][0]['message']) 64 | 65 | # Simulation function call results 66 | messages.append({ 67 | 'role': 'tool', 68 | 'name': 'image_gen', 69 | 'content': '![fig-001](https://seopic.699pic.com/photo/60098/4947.jpg_wh1200.jpg)' 70 | }) 71 | response = llm.quick_chat_oai(messages, tools=tools) 72 | for x in response: 73 | print(x) 74 | 75 | 76 | if __name__ == '__main__': 77 | test() 78 | -------------------------------------------------------------------------------- /examples/llm_riddles.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Customize an agent to implement llm riddles game""" 16 | from typing import Dict, Iterator, List, Optional, Union 17 | 18 | import json5 19 | 20 | from qwen_agent import Agent 21 | from qwen_agent.agents import Assistant 22 | from qwen_agent.llm import BaseChatModel 23 | from qwen_agent.llm.schema import Message 24 | 25 | 26 | class LLMRiddles(Agent): 27 | """Customize an agent for game: Surrounded by LLM """ 28 | 29 | def __init__(self, llm: Optional[Union[Dict, BaseChatModel]] = None): 30 | super().__init__(llm=llm) 31 | 32 | # Nest one assistant for create questions 33 | self.examiner_agent = Assistant(llm=self.llm, 34 | system_message=('请你创造十个比较离谱或小众的短语,长度在10个汉字以内。例如“1+1=3”、“主莫朗玛峰”等反人类直觉的短语,' 35 | '尽量类型丰富一些,包含数学、文学、地理、生物等领域。返回格式为字符串列表,不要返回其余任何内容。')) 36 | 37 | # Initialize the questions 38 | *_, last = self.examiner_agent.run([Message('user', '开始')]) 39 | self.topics = json5.loads(last[-1].content) 40 | 41 | def _run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 42 | return self._call_llm(messages=messages) 43 | 44 | 45 | def test(): 46 | # Define a writer agent 47 | bot = LLMRiddles(llm={'model': 'qwen-max'}) 48 | 49 | # Gaming 50 | for topic in bot.topics: 51 | print(f'请你构造一个问题使模型的回答是一字不差的“{topic}”(不需要引号)。') 52 | 53 | messages = [] 54 | query = f'请直接输出“{topic}”(不需要引号),不要说其他内容' 55 | 56 | messages.append(Message('user', query)) 57 | 58 | response = [] 59 | for response in bot.run(messages=messages): 60 | print('bot response:', response) 61 | if response and response[-1]['content'] == topic: 62 | print('You win!') 63 | 64 | 65 | def app_tui(): 66 | # Define a writer agent 67 | bot = LLMRiddles(llm={'model': 'qwen-max'}) 68 | 69 | # Gaming 70 | for topic in bot.topics: 71 | print(f'请你构造一个问题使模型的回答是一字不差的“{topic}”(不需要引号)。') 72 | 73 | messages = [] 74 | while True: 75 | query = input('user question(input EXIT for next topic): ') 76 | 77 | if query == 'EXIT': 78 | break 79 | messages.append(Message('user', query)) 80 | response = [] 81 | for response in bot.run(messages=messages): 82 | print('bot response:', response) 83 | if response and response[-1]['content'] == topic: 84 | print('You win!') 85 | break 86 | messages.extend(response) 87 | 88 | 89 | if __name__ == '__main__': 90 | # test() 91 | app_tui() 92 | -------------------------------------------------------------------------------- /examples/long_dialogue.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import DialogueRetrievalAgent 16 | from qwen_agent.gui import WebUI 17 | 18 | 19 | def test(): 20 | # Define the agent 21 | bot = DialogueRetrievalAgent(llm={'model': 'qwen-max'}) 22 | 23 | # Chat 24 | long_text = ','.join(['这是干扰内容'] * 1000 + ['小明的爸爸叫大头'] + ['这是干扰内容'] * 1000) 25 | messages = [{'role': 'user', 'content': f'小明爸爸叫什么?\n{long_text}'}] 26 | 27 | for response in bot.run(messages): 28 | print('bot response:', response) 29 | 30 | 31 | def app_tui(): 32 | bot = DialogueRetrievalAgent(llm={'model': 'qwen-max'}) 33 | 34 | # Chat 35 | messages = [] 36 | while True: 37 | query = input('user question: ') 38 | messages.append({'role': 'user', 'content': query}) 39 | response = [] 40 | for response in bot.run(messages=messages): 41 | print('bot response:', response) 42 | messages.extend(response) 43 | 44 | 45 | def app_gui(): 46 | # Define the agent 47 | bot = DialogueRetrievalAgent(llm={'model': 'qwen-max'}) 48 | 49 | WebUI(bot).run() 50 | 51 | 52 | if __name__ == '__main__': 53 | # test() 54 | # app_tui() 55 | app_gui() 56 | -------------------------------------------------------------------------------- /examples/parallel_doc_qa.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents.doc_qa import ParallelDocQA 16 | from qwen_agent.gui import WebUI 17 | 18 | 19 | def test(): 20 | bot = ParallelDocQA(llm={'model': 'qwen2.5-72b-instruct', 'generate_cfg': {'max_retries': 10}}) 21 | messages = [ 22 | { 23 | 'role': 'user', 24 | 'content': [ 25 | { 26 | 'text': '介绍实验方法' 27 | }, 28 | { 29 | 'file': 'https://arxiv.org/pdf/2310.08560.pdf' 30 | }, 31 | ] 32 | }, 33 | ] 34 | for rsp in bot.run(messages): 35 | print('bot response:', rsp) 36 | 37 | 38 | def app_gui(): 39 | # Define the agent 40 | bot = ParallelDocQA( 41 | llm={ 42 | 'model': 'qwen2.5-72b-instruct', 43 | 'generate_cfg': { 44 | 'max_retries': 10 45 | } 46 | }, 47 | description='并行QA后用RAG召回内容并回答。支持文件类型:PDF/Word/PPT/TXT/HTML。使用与材料相同的语言提问会更好。', 48 | ) 49 | 50 | chatbot_config = {'prompt.suggestions': [{'text': '介绍实验方法'}]} 51 | 52 | WebUI(bot, chatbot_config=chatbot_config).run() 53 | 54 | 55 | if __name__ == '__main__': 56 | # test() 57 | app_gui() 58 | -------------------------------------------------------------------------------- /examples/qwen2vl_assistant_video.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import Assistant 16 | 17 | 18 | def test(): 19 | bot = Assistant(llm={'model': 'qwen-vl-max-latest'}) 20 | 21 | messages = [{ 22 | 'role': 23 | 'user', 24 | 'content': [{ 25 | 'video': [ 26 | 'https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241108/xzsgiz/football1.jpg', 27 | 'https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241108/tdescd/football2.jpg', 28 | 'https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241108/zefdja/football3.jpg', 29 | 'https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241108/aedbqh/football4.jpg' 30 | ] 31 | }, { 32 | 'text': 'Describe the specific process of this video' 33 | }] 34 | }] 35 | 36 | # Uploading video files requires applying for permission on DashScope 37 | # messages = [{ 38 | # 'role': 39 | # 'user', 40 | # 'content': [{ 41 | # 'video': 'https://www.runoob.com/try/demo_source/mov_bbb.mp4' 42 | # }, { 43 | # 'text': 'Describe the specific process of this video' 44 | # }] 45 | # }] 46 | 47 | for rsp in bot.run(messages): 48 | print(rsp) 49 | 50 | 51 | if __name__ == '__main__': 52 | test() 53 | -------------------------------------------------------------------------------- /examples/resource/doc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/examples/resource/doc.pdf -------------------------------------------------------------------------------- /examples/resource/poem.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/examples/resource/poem.pdf -------------------------------------------------------------------------------- /examples/resource/screenshot_with_plot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/examples/resource/screenshot_with_plot.jpeg -------------------------------------------------------------------------------- /examples/resource/stock_prices.csv: -------------------------------------------------------------------------------- 1 | ,Date,Open,High,Low,Close,Adj,Close,Volume 2 | 0,2020/1/3,74.13,74.31,73.6,73.91,73.91,17423000,36237000 3 | 1,2020/1/4,73.91,74.2,73.68,74.08,74,17376000,36206000 4 | 2,2020/1/5,74.08,74.29,73.82,73.93,73.93,17353000,36184000 5 | 3,2020/1/6,73.93,74.03,73.71,73.73,73.73,17341000,36184000 6 | 4,2020/1/7,73.73,73.86,73.62,73.7,73.68,17338000,36184000 7 | -------------------------------------------------------------------------------- /examples/tir_math.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A TIR(tool-integrated reasoning) math agent 16 | ```bash 17 | python tir_math.py 18 | ``` 19 | """ 20 | import os 21 | from pprint import pprint 22 | 23 | from qwen_agent.agents import TIRMathAgent 24 | from qwen_agent.gui import WebUI 25 | 26 | ROOT_RESOURCE = os.path.join(os.path.dirname(__file__), 'resource') 27 | 28 | # We use the following two systems to distinguish between COT mode and TIR mode 29 | TIR_SYSTEM = """Please integrate natural language reasoning with programs to solve the problem above, and put your final answer within \\boxed{}.""" 30 | COT_SYSTEM = """Please reason step by step, and put your final answer within \\boxed{}.""" 31 | 32 | 33 | def init_agent_service(): 34 | # Use this to access the qwen2.5-math model deployed on dashscope 35 | llm_cfg = {'model': 'qwen2.5-math-72b-instruct', 'model_type': 'qwen_dashscope', 'generate_cfg': {'top_k': 1}} 36 | bot = TIRMathAgent(llm=llm_cfg, name='Qwen2.5-Math', system_message=TIR_SYSTEM) 37 | return bot 38 | 39 | 40 | def test(query: str = '斐波那契数列前10个数字'): 41 | # Define the agent 42 | bot = init_agent_service() 43 | 44 | # Chat 45 | messages = [{'role': 'user', 'content': query}] 46 | for response in bot.run(messages): 47 | pprint(response, indent=2) 48 | 49 | 50 | def app_tui(): 51 | # Define the agent 52 | bot = init_agent_service() 53 | 54 | # Chat 55 | messages = [] 56 | while True: 57 | # Query example: 斐波那契数列前10个数字 58 | query = input('user question: ') 59 | messages.append({'role': 'user', 'content': query}) 60 | response = [] 61 | for response in bot.run(messages): 62 | print('bot response:', response) 63 | messages.extend(response) 64 | 65 | 66 | def app_gui(): 67 | bot = init_agent_service() 68 | chatbot_config = { 69 | 'prompt.suggestions': [ 70 | r'曲线 $y=2 \\ln (x+1)$ 在点 $(0,0)$ 处的切线方程为 $( )$.', 71 | 'A digital display shows the current date as an $8$-digit integer consisting of a $4$-digit year, ' 72 | 'followed by a $2$-digit month, followed by a $2$-digit date within the month. ' 73 | 'For example, Arbor Day this year is displayed as 20230428. ' 74 | 'For how many dates in $2023$ will each digit appear an even number of times ' 75 | 'in the 8-digital display for that date?' 76 | ] 77 | } 78 | WebUI(bot, chatbot_config=chatbot_config).run() 79 | 80 | 81 | if __name__ == '__main__': 82 | # test() 83 | # app_tui() 84 | app_gui() 85 | -------------------------------------------------------------------------------- /examples/virtual_memory_qa.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A retrieval docqa assistant implemented by virtual memory agent""" 16 | 17 | import os 18 | 19 | from qwen_agent.agents import VirtualMemoryAgent 20 | from qwen_agent.gui import WebUI 21 | 22 | ROOT_RESOURCE = os.path.join(os.path.dirname(__file__), 'resource') 23 | 24 | 25 | def init_agent_service(): 26 | llm_cfg = {'model': 'qwen-max'} 27 | system = '一个文档问答助手。' 28 | bot = VirtualMemoryAgent( 29 | llm=llm_cfg, 30 | system_message=system, 31 | ) 32 | 33 | return bot 34 | 35 | 36 | def test(query='简单列出这篇文章的贡献https://qianwen-res.oss-cn-beijing.aliyuncs.com/QWEN_TECHNICAL_REPORT.pdf',): 37 | # Define the agent 38 | bot = init_agent_service() 39 | 40 | # Chat 41 | messages = [{'role': 'user', 'content': query}] 42 | 43 | for response in bot.run(messages): 44 | print('bot response:', response) 45 | 46 | 47 | def app_tui(): 48 | # Define the agent 49 | bot = init_agent_service() 50 | 51 | # Chat 52 | messages = [] 53 | while True: 54 | # Query example: 简单列出这篇文章的贡献https://qianwen-res.oss-cn-beijing.aliyuncs.com/QWEN_TECHNICAL_REPORT.pdf 55 | query = input('user question: ') 56 | # File example: resource/poem.pdf 57 | file = input('file url (press enter if no file): ').strip() 58 | if not query: 59 | print('user question cannot be empty!') 60 | continue 61 | if not file: 62 | messages.append({'role': 'user', 'content': query}) 63 | else: 64 | messages.append({'role': 'user', 'content': [{'text': query}, {'file': file}]}) 65 | 66 | response = [] 67 | for response in bot.run(messages): 68 | print('bot response:', response) 69 | messages.extend(response) 70 | 71 | 72 | def app_gui(): 73 | # Define the agent 74 | bot = init_agent_service() 75 | chatbot_config = { 76 | 'prompt.suggestions': ['简单列出这篇文章的贡献https://qianwen-res.oss-cn-beijing.aliyuncs.com/QWEN_TECHNICAL_REPORT.pdf'] 77 | } 78 | 79 | WebUI( 80 | bot, 81 | chatbot_config=chatbot_config, 82 | ).run() 83 | 84 | 85 | if __name__ == '__main__': 86 | # test() 87 | # app_tui() 88 | app_gui() 89 | -------------------------------------------------------------------------------- /qwen_agent/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | __version__ = '0.0.26' 16 | from .agent import Agent 17 | from .multi_agent_hub import MultiAgentHub 18 | 19 | __all__ = [ 20 | 'Agent', 21 | 'MultiAgentHub', 22 | ] 23 | -------------------------------------------------------------------------------- /qwen_agent/agents/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agent import Agent, BasicAgent 16 | from qwen_agent.multi_agent_hub import MultiAgentHub 17 | 18 | from .article_agent import ArticleAgent 19 | from .assistant import Assistant 20 | from .dialogue_retrieval_agent import DialogueRetrievalAgent 21 | from .dialogue_simulator import DialogueSimulator 22 | # DocQAAgent is the default solution for long document question answering. 23 | # The actual implementation of DocQAAgent may change with every release. 24 | from .doc_qa import BasicDocQA as DocQAAgent 25 | from .doc_qa import ParallelDocQA 26 | from .fncall_agent import FnCallAgent 27 | from .group_chat import GroupChat 28 | from .group_chat_auto_router import GroupChatAutoRouter 29 | from .group_chat_creator import GroupChatCreator 30 | from .human_simulator import HumanSimulator 31 | from .react_chat import ReActChat 32 | from .router import Router 33 | from .tir_agent import TIRMathAgent 34 | from .user_agent import UserAgent 35 | from .virtual_memory_agent import VirtualMemoryAgent 36 | from .write_from_scratch import WriteFromScratch 37 | 38 | __all__ = [ 39 | 'Agent', 40 | 'BasicAgent', 41 | 'MultiAgentHub', 42 | 'DocQAAgent', 43 | 'DialogueSimulator', 44 | 'HumanSimulator', 45 | 'ParallelDocQA', 46 | 'Assistant', 47 | 'ArticleAgent', 48 | 'ReActChat', 49 | 'Router', 50 | 'UserAgent', 51 | 'GroupChat', 52 | 'WriteFromScratch', 53 | 'GroupChatCreator', 54 | 'GroupChatAutoRouter', 55 | 'FnCallAgent', 56 | 'VirtualMemoryAgent', 57 | 'DialogueRetrievalAgent', 58 | 'TIRMathAgent', 59 | ] 60 | -------------------------------------------------------------------------------- /qwen_agent/agents/article_agent.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Iterator, List 16 | 17 | from qwen_agent.agents.assistant import Assistant 18 | from qwen_agent.agents.write_from_scratch import WriteFromScratch 19 | from qwen_agent.agents.writing import ContinueWriting 20 | from qwen_agent.llm.schema import ASSISTANT, CONTENT, Message 21 | 22 | 23 | class ArticleAgent(Assistant): 24 | """This is an agent for writing articles. 25 | 26 | It can write a thematic essay or continue writing an article based on reference materials 27 | """ 28 | 29 | def _run(self, 30 | messages: List[Message], 31 | lang: str = 'en', 32 | full_article: bool = False, 33 | **kwargs) -> Iterator[List[Message]]: 34 | 35 | # Need to use Memory agent for data management 36 | *_, last = self.mem.run(messages=messages, **kwargs) 37 | _ref = last[-1][CONTENT] 38 | 39 | response = [] 40 | if _ref: 41 | response.append(Message(ASSISTANT, f'>\n> Search for relevant information: \n{_ref}\n')) 42 | yield response 43 | 44 | if full_article: 45 | writing_agent = WriteFromScratch(llm=self.llm) 46 | else: 47 | writing_agent = ContinueWriting(llm=self.llm) 48 | response.append(Message(ASSISTANT, '>\n> Writing Text: \n')) 49 | yield response 50 | 51 | for rsp in writing_agent.run(messages=messages, lang=lang, knowledge=_ref): 52 | if rsp: 53 | yield response + rsp 54 | -------------------------------------------------------------------------------- /qwen_agent/agents/dialogue_simulator.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import copy 16 | from typing import Iterator, List, Optional 17 | 18 | from qwen_agent.agent import Agent 19 | from qwen_agent.agents.human_simulator import STOP, HumanSimulator 20 | from qwen_agent.llm.schema import ASSISTANT, FUNCTION, SYSTEM, USER, Message 21 | 22 | 23 | class DialogueSimulator(Agent): 24 | 25 | def __init__(self, user_agent: HumanSimulator, assistant_agent: Agent, max_round: Optional[int] = 5, **kwargs): 26 | super().__init__(**kwargs) 27 | self.max_round = max_round 28 | self.user_agent = user_agent 29 | self.assistant_agent = assistant_agent 30 | 31 | def _run(self, messages: List[Message] = None, **kwargs) -> Iterator[List[Message]]: 32 | messages = copy.deepcopy(messages) 33 | response = [] 34 | for i in range(self.max_round): 35 | if (not messages) or (messages[-1].role == 'assistant'): 36 | # User speak 37 | *_, last = self.user_agent.run(messages=_swap_roles(messages), **kwargs) 38 | last = _swap_roles(last) 39 | assert len(last) == 1 40 | assert last[-1].role == 'user' 41 | if STOP in last[-1].content: 42 | break 43 | messages.extend(last) 44 | response.extend(last) 45 | yield response 46 | if messages and (messages[-1].role == 'user'): 47 | # Assistant speak 48 | *_, last = self.assistant_agent.run(messages=messages, **kwargs) 49 | messages.extend(last) 50 | response.extend(last) 51 | yield response 52 | yield response 53 | 54 | 55 | def _swap_roles(messages: List[Message]) -> List[Message]: 56 | new_messages = [] 57 | for msg in copy.deepcopy(messages): 58 | if msg.role == SYSTEM: 59 | pass 60 | elif msg.role == USER: 61 | msg.role = ASSISTANT 62 | elif msg.role == ASSISTANT: 63 | msg.role = USER 64 | msg.function_call = None 65 | elif msg.role == FUNCTION: 66 | continue 67 | else: 68 | raise ValueError 69 | if msg.content: 70 | new_messages.append(msg) 71 | return new_messages 72 | -------------------------------------------------------------------------------- /qwen_agent/agents/doc_qa/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .basic_doc_qa import BasicDocQA 16 | from .parallel_doc_qa import ParallelDocQA 17 | 18 | __all__ = [ 19 | 'BasicDocQA', 20 | 'ParallelDocQA', 21 | ] 22 | -------------------------------------------------------------------------------- /qwen_agent/agents/doc_qa/basic_doc_qa.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import copy 16 | from typing import Dict, Iterator, List, Optional, Union 17 | 18 | from qwen_agent.agents.assistant import Assistant 19 | from qwen_agent.llm.base import BaseChatModel 20 | from qwen_agent.llm.schema import CONTENT, DEFAULT_SYSTEM_MESSAGE, ROLE, SYSTEM, Message 21 | from qwen_agent.tools import BaseTool 22 | 23 | DEFAULT_NAME = 'Basic DocQA' 24 | DEFAULT_DESC = '可以根据问题,检索出知识库中的某个相关细节来回答。适用于需要定位到具体位置的问题,例如“介绍表1”等类型的问题' 25 | 26 | PROMPT_TEMPLATE_ZH = """请充分理解以下参考资料内容,组织出满足用户提问的条理清晰的回复。 27 | #参考资料: 28 | {ref_doc}""" 29 | 30 | PROMPT_TEMPLATE_EN = """Please fully understand the content of the following reference materials and organize a clear response that meets the user's questions. 31 | # Reference materials: 32 | {ref_doc}""" 33 | 34 | PROMPT_TEMPLATE = { 35 | 'zh': PROMPT_TEMPLATE_ZH, 36 | 'en': PROMPT_TEMPLATE_EN, 37 | } 38 | 39 | 40 | class BasicDocQA(Assistant): 41 | """This is an agent for doc QA.""" 42 | 43 | def __init__(self, 44 | function_list: Optional[List[Union[str, Dict, BaseTool]]] = None, 45 | llm: Optional[Union[Dict, BaseChatModel]] = None, 46 | system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE, 47 | name: Optional[str] = DEFAULT_NAME, 48 | description: Optional[str] = DEFAULT_DESC, 49 | files: Optional[List[str]] = None, 50 | rag_cfg: Optional[Dict] = None): 51 | super().__init__(function_list=function_list, 52 | llm=llm, 53 | system_message=system_message, 54 | name=name, 55 | description=description, 56 | files=files, 57 | rag_cfg=rag_cfg) 58 | 59 | def _run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 60 | """This agent using different doc qa prompt with Assistant""" 61 | # Need to use Memory agent for data management 62 | *_, last = self.mem.run(messages=messages, **kwargs) 63 | knowledge = last[-1][CONTENT] 64 | 65 | messages = copy.deepcopy(messages) 66 | system_prompt = PROMPT_TEMPLATE[lang].format(ref_doc=knowledge) 67 | if messages and messages[0][ROLE] == SYSTEM: 68 | messages[0][CONTENT] += '\n\n' + system_prompt 69 | else: 70 | messages.insert(0, Message(SYSTEM, system_prompt)) 71 | 72 | response = self._call_llm(messages=messages) 73 | return response 74 | -------------------------------------------------------------------------------- /qwen_agent/agents/doc_qa/parallel_doc_qa_summary.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import copy 16 | from typing import Iterator, List 17 | 18 | from qwen_agent.agent import Agent 19 | from qwen_agent.llm.schema import CONTENT, ROLE, SYSTEM, USER, ContentItem, Message 20 | from qwen_agent.utils.utils import extract_text_from_message 21 | 22 | PROMPT_TEMPLATE_ZH = """注意:你的回答必须严格遵循知识库内容,即使与事实不符。 23 | 如果知识库的大部分内容都与问题无关,只有少数几句话与问题直接相关,请重点关注这几句话,这种情况一定要回复。 24 | 25 | # 知识库 26 | 27 | {ref_doc}""" 28 | 29 | PROMPT_TEMPLATE_EN = """Please respond solely based on the content of the provided Knowledge Base. 30 | Note: Your answer must strictly adhere to the content of the provided Knowledge Base, even if it deviates from the facts. 31 | If the materials mainly contains content irrelevant to the question, with only a few sentences directly related, please focus on these sentences and ensure a response. 32 | 33 | # Knowledge Base 34 | 35 | {ref_doc}""" 36 | 37 | PROMPT_TEMPLATE = { 38 | 'zh': PROMPT_TEMPLATE_ZH, 39 | 'en': PROMPT_TEMPLATE_EN, 40 | } 41 | 42 | PROMPT_END_TEMPLATE_ZH = """# 问题 43 | {question} 44 | 45 | 46 | # 回答规则 47 | - 请基于知识库内容回答问题。注意:你的回答必须严格遵循知识库内容,即使与事实不符。 48 | - 如果知识库的大部分内容都与问题无关,只有少数几句话与问题直接相关,请重点关注这几句话,这种情况一定要回复。 49 | 50 | 请根据回答规则,针对知识库内容回答问题,回答:""" 51 | 52 | PROMPT_END_TEMPLATE_EN = """# Question 53 | {question} 54 | 55 | 56 | # Answering Guidelines 57 | - Please respond solely based on the content of the provided Knowledge Base. 58 | - Note: Your answer must strictly adhere to the content of the provided Knowledge Base, even if it deviates from the facts. 59 | - If the materials mainly contains content irrelevant to the question, with only a few sentences directly related, please focus on these sentences and ensure a response. 60 | 61 | Please give your answer:""" 62 | 63 | PROMPT_END_TEMPLATE = { 64 | 'zh': PROMPT_END_TEMPLATE_ZH, 65 | 'en': PROMPT_END_TEMPLATE_EN, 66 | } 67 | 68 | 69 | class ParallelDocQASummary(Agent): 70 | 71 | def _run(self, messages: List[Message], knowledge: str = '', lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 72 | messages = copy.deepcopy(messages) 73 | 74 | system_prompt = PROMPT_TEMPLATE[lang].format(ref_doc=knowledge) 75 | 76 | if messages and messages[0][ROLE] == SYSTEM: 77 | if isinstance(messages[0][CONTENT], str): 78 | messages[0][CONTENT] += '\n\n' + system_prompt 79 | else: 80 | assert isinstance(messages[0][CONTENT], list) 81 | messages[0][CONTENT] += [ContentItem(text='\n\n' + system_prompt)] 82 | else: 83 | messages.insert(0, Message(SYSTEM, system_prompt)) 84 | 85 | assert messages[-1][ROLE] == USER, messages 86 | user_question = extract_text_from_message(messages[-1], add_upload_info=False) 87 | messages[-1] = Message(USER, PROMPT_END_TEMPLATE[lang].format(question=user_question)) 88 | 89 | return self._call_llm(messages=messages) 90 | -------------------------------------------------------------------------------- /qwen_agent/agents/human_simulator.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import random 17 | from typing import Dict, Iterator, List, Optional, Union 18 | 19 | from qwen_agent.agents import Agent 20 | from qwen_agent.llm import BaseChatModel 21 | from qwen_agent.llm.schema import Message 22 | from qwen_agent.tools import BaseTool 23 | 24 | STOP = '<|STOP|>' 25 | 26 | DEFAULT_HUMAN_SIMULATOR_PROMPT = """"Play a game where you act as the human user and the user acts as the AI assistant. 27 | 28 | # Rules: 29 | - Ask questions and make requests as a typical human user would. 30 | - Your questions should be related to the provided context or the chat history. 31 | - You should output a JSON list of four possible questions, and nothing more. 32 | - The questions must be diverse in their complexity, intentions, and language usage. 33 | - If you feel the conversation can end, please output ["%s"] directly without any other content.""" % STOP 34 | 35 | 36 | class HumanSimulator(Agent): 37 | 38 | def __init__(self, 39 | function_list: Optional[List[Union[str, Dict, BaseTool]]] = None, 40 | llm: Optional[Union[Dict, BaseChatModel]] = None, 41 | system_message: Optional[str] = None, 42 | name: Optional[str] = None, 43 | description: Optional[str] = None, 44 | **kwargs): 45 | if system_message: 46 | system_message = DEFAULT_HUMAN_SIMULATOR_PROMPT + '\n\n' + system_message 47 | super().__init__(function_list=function_list, 48 | llm=llm, 49 | system_message=system_message, 50 | name=name, 51 | description=description, 52 | **kwargs) 53 | 54 | def _run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 55 | if (not messages) or (messages[0].role != 'user'): 56 | begin_msg = 'Please role-play as a human user and make your first request.\n\nBegin!' 57 | messages = [Message(role='user', content=begin_msg)] + messages 58 | *_, respones = self._call_llm(messages=messages) 59 | rng = random.Random(kwargs.get('seed', 42)) 60 | try: 61 | text = rng.choice(json.loads(respones[-1].content)) 62 | if (not isinstance(text, str)) or (not text): 63 | text = STOP 64 | respones[-1].content = text 65 | except json.decoder.JSONDecodeError: 66 | respones[-1].content = STOP 67 | yield respones 68 | -------------------------------------------------------------------------------- /qwen_agent/agents/keygen_strategies/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .gen_keyword import GenKeyword 16 | from .gen_keyword_with_knowledge import GenKeywordWithKnowledge 17 | from .split_query_then_gen_keyword import SplitQueryThenGenKeyword 18 | from .split_query_then_gen_keyword_with_knowledge import SplitQueryThenGenKeywordWithKnowledge 19 | 20 | __all__ = [ 21 | 'GenKeyword', 22 | 'GenKeywordWithKnowledge', 23 | 'SplitQueryThenGenKeyword', 24 | 'SplitQueryThenGenKeywordWithKnowledge', 25 | ] 26 | -------------------------------------------------------------------------------- /qwen_agent/agents/keygen_strategies/split_query_then_gen_keyword.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | from typing import Dict, Iterator, List, Optional, Union 17 | 18 | import json5 19 | 20 | from qwen_agent import Agent 21 | from qwen_agent.agents.keygen_strategies.gen_keyword import GenKeyword 22 | from qwen_agent.agents.keygen_strategies.split_query import SplitQuery 23 | from qwen_agent.llm.base import BaseChatModel 24 | from qwen_agent.llm.schema import ASSISTANT, DEFAULT_SYSTEM_MESSAGE, USER, Message 25 | from qwen_agent.tools import BaseTool 26 | 27 | 28 | class SplitQueryThenGenKeyword(Agent): 29 | 30 | def __init__(self, 31 | function_list: Optional[List[Union[str, Dict, BaseTool]]] = None, 32 | llm: Optional[Union[Dict, BaseChatModel]] = None, 33 | system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE, 34 | **kwargs): 35 | super().__init__(function_list, llm, system_message, **kwargs) 36 | self.split_query = SplitQuery(llm=self.llm) 37 | self.keygen = GenKeyword(llm=llm) 38 | 39 | def _run(self, messages: List[Message], lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 40 | query = messages[-1].content 41 | 42 | *_, last = self.split_query.run(messages=messages, lang=lang, **kwargs) 43 | information = last[-1].content.strip() 44 | if information.startswith('```json'): 45 | information = information[len('```json'):] 46 | if information.endswith('```'): 47 | information = information[:-3] 48 | try: 49 | information = '\n'.join(json5.loads(information)['information']).strip() 50 | if 0 < len(information) <= len(query): 51 | query = information 52 | except Exception: 53 | query = query 54 | rsp = [] 55 | for rsp in self.keygen.run([Message(USER, query)]): 56 | yield rsp 57 | 58 | if rsp: 59 | keyword = rsp[-1].content.strip() 60 | if keyword.startswith('```json'): 61 | keyword = keyword[len('```json'):] 62 | if keyword.endswith('```'): 63 | keyword = keyword[:-3] 64 | try: 65 | keyword_dict = json5.loads(keyword) 66 | keyword_dict['text'] = query 67 | yield [Message(role=ASSISTANT, content=json.dumps(keyword_dict, ensure_ascii=False))] 68 | except Exception: 69 | pass 70 | -------------------------------------------------------------------------------- /qwen_agent/agents/keygen_strategies/split_query_then_gen_keyword_with_knowledge.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Dict, List, Optional, Union 16 | 17 | from qwen_agent.agents.keygen_strategies.gen_keyword_with_knowledge import GenKeywordWithKnowledge 18 | from qwen_agent.agents.keygen_strategies.split_query_then_gen_keyword import SplitQueryThenGenKeyword 19 | from qwen_agent.llm.base import BaseChatModel 20 | from qwen_agent.llm.schema import DEFAULT_SYSTEM_MESSAGE 21 | from qwen_agent.tools import BaseTool 22 | 23 | 24 | class SplitQueryThenGenKeywordWithKnowledge(SplitQueryThenGenKeyword): 25 | 26 | def __init__(self, 27 | function_list: Optional[List[Union[str, Dict, BaseTool]]] = None, 28 | llm: Optional[Union[Dict, BaseChatModel]] = None, 29 | system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE, 30 | **kwargs): 31 | super().__init__(function_list, llm, system_message, **kwargs) 32 | self.keygen = GenKeywordWithKnowledge(llm=llm) 33 | -------------------------------------------------------------------------------- /qwen_agent/agents/user_agent.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Iterator, List 16 | 17 | from qwen_agent.agent import Agent 18 | from qwen_agent.llm.schema import Message 19 | 20 | PENDING_USER_INPUT = '' 21 | 22 | 23 | class UserAgent(Agent): 24 | 25 | def _run(self, messages: List[Message], **kwargs) -> Iterator[List[Message]]: 26 | yield [Message(role='user', content=PENDING_USER_INPUT, name=self.name)] 27 | -------------------------------------------------------------------------------- /qwen_agent/agents/writing/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Prompts are special agents: using a prompt template to complete one QA.""" 16 | 17 | from .continue_writing import ContinueWriting 18 | from .expand_writing import ExpandWriting 19 | from .outline_writing import OutlineWriting 20 | 21 | __all__ = [ 22 | 'ContinueWriting', 23 | 'OutlineWriting', 24 | 'ExpandWriting', 25 | ] 26 | -------------------------------------------------------------------------------- /qwen_agent/agents/writing/continue_writing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import copy 16 | from typing import Iterator, List 17 | 18 | from qwen_agent import Agent 19 | from qwen_agent.llm.schema import CONTENT, Message 20 | 21 | PROMPT_TEMPLATE_ZH = """你是一个写作助手,请依据参考资料,根据给定的前置文本续写合适的内容。 22 | #参考资料: 23 | {ref_doc} 24 | 25 | #前置文本: 26 | {user_request} 27 | 28 | 保证续写内容和前置文本保持连贯,请开始续写:""" 29 | 30 | PROMPT_TEMPLATE_EN = """You are a writing assistant, please follow the reference materials and continue to write appropriate content based on the given previous text. 31 | 32 | # References: 33 | {ref_doc} 34 | 35 | # Previous text: 36 | {user_request} 37 | 38 | Please start writing directly, output only the continued text, do not repeat the previous text, do not say irrelevant words, and ensure that the continued content and the previous text remain consistent.""" 39 | 40 | PROMPT_TEMPLATE = { 41 | 'zh': PROMPT_TEMPLATE_ZH, 42 | 'en': PROMPT_TEMPLATE_EN, 43 | } 44 | 45 | 46 | class ContinueWriting(Agent): 47 | 48 | def _run(self, messages: List[Message], knowledge: str = '', lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 49 | messages = copy.deepcopy(messages) 50 | messages[-1][CONTENT] = PROMPT_TEMPLATE[lang].format( 51 | ref_doc=knowledge, 52 | user_request=messages[-1][CONTENT], 53 | ) 54 | return self._call_llm(messages) 55 | -------------------------------------------------------------------------------- /qwen_agent/agents/writing/expand_writing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import copy 16 | from typing import Iterator, List 17 | 18 | from qwen_agent import Agent 19 | from qwen_agent.llm.schema import CONTENT, Message 20 | 21 | PROMPT_TEMPLATE_ZH = """ 22 | 你是一个写作助手,任务是依据参考资料,完成写作任务。 23 | #参考资料: 24 | {ref_doc} 25 | 26 | 写作标题是:{user_request} 27 | 大纲是: 28 | {outline} 29 | 30 | 此时你的任务是扩写第{index}个一级标题对应的章节:{capture}。注意每个章节负责撰写不同的内容,所以你不需要为了全面而涵盖之后的内容。请不要在这里生成大纲。只依据给定的参考资料来写,不要引入其余知识。 31 | """ 32 | 33 | PROMPT_TEMPLATE_EN = """ 34 | You are a writing assistant. Your task is to complete writing article based on reference materials. 35 | 36 | # References: 37 | {ref_doc} 38 | 39 | The title is: {user_request} 40 | 41 | The outline is: 42 | {outline} 43 | 44 | At this point, your task is to expand the chapter corresponding to the {index} first level title: {capture}. 45 | Note that each chapter is responsible for writing different content, so you don't need to cover the following content. Please do not generate an outline here. Write only based on the given reference materials and do not introduce other knowledge. 46 | """ 47 | 48 | PROMPT_TEMPLATE = { 49 | 'zh': PROMPT_TEMPLATE_ZH, 50 | 'en': PROMPT_TEMPLATE_EN, 51 | } 52 | 53 | 54 | class ExpandWriting(Agent): 55 | 56 | def _run(self, 57 | messages: List[Message], 58 | knowledge: str = '', 59 | outline: str = '', 60 | index: str = '1', 61 | capture: str = '', 62 | capture_later: str = '', 63 | lang: str = 'en', 64 | **kwargs) -> Iterator[List[Message]]: 65 | messages = copy.deepcopy(messages) 66 | prompt = PROMPT_TEMPLATE[lang].format( 67 | ref_doc=knowledge, 68 | user_request=messages[-1][CONTENT], 69 | index=index, 70 | outline=outline, 71 | capture=capture, 72 | ) 73 | if capture_later: 74 | if lang == 'zh': 75 | prompt = prompt + '请在涉及 ' + capture_later + ' 时停止。' 76 | elif lang == 'en': 77 | prompt = prompt + ' Please stop when writing ' + capture_later 78 | else: 79 | raise NotImplementedError 80 | 81 | messages[-1][CONTENT] = prompt 82 | return self._call_llm(messages) 83 | -------------------------------------------------------------------------------- /qwen_agent/agents/writing/outline_writing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import copy 16 | from typing import Iterator, List 17 | 18 | from qwen_agent import Agent 19 | from qwen_agent.llm.schema import CONTENT, Message 20 | 21 | PROMPT_TEMPLATE_ZH = """ 22 | 你是一个写作助手,任务是充分理解参考资料,从而完成写作。 23 | #参考资料: 24 | {ref_doc} 25 | 26 | 写作标题是:{user_request} 27 | 28 | 为了完成以上写作任务,请先列出大纲。回复只需包含大纲。大纲的一级标题全部以罗马数字计数。只依据给定的参考资料来写,不要引入其余知识。 29 | """ 30 | 31 | PROMPT_TEMPLATE_EN = """ 32 | You are a writing assistant. Your task is to complete writing article based on reference materials. 33 | 34 | # References: 35 | {ref_doc} 36 | 37 | The title is: {user_request} 38 | 39 | In order to complete the above writing tasks, please provide an outline first. The reply only needs to include an outline. The first level titles of the outline are all counted in Roman numerals. Write only based on the given reference materials and do not introduce other knowledge. 40 | """ 41 | 42 | PROMPT_TEMPLATE = { 43 | 'zh': PROMPT_TEMPLATE_ZH, 44 | 'en': PROMPT_TEMPLATE_EN, 45 | } 46 | 47 | 48 | class OutlineWriting(Agent): 49 | 50 | def _run(self, messages: List[Message], knowledge: str = '', lang: str = 'en', **kwargs) -> Iterator[List[Message]]: 51 | messages = copy.deepcopy(messages) 52 | messages[-1][CONTENT] = PROMPT_TEMPLATE[lang].format( 53 | ref_doc=knowledge, 54 | user_request=messages[-1][CONTENT], 55 | ) 56 | return self._call_llm(messages) 57 | -------------------------------------------------------------------------------- /qwen_agent/gui/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.gui.gradio_dep import gr, mgr, ms 16 | from qwen_agent.gui.web_ui import WebUI 17 | 18 | __all__ = [ 19 | 'gr', 20 | 'ms', 21 | 'mgr', 22 | 'WebUI', 23 | ] 24 | -------------------------------------------------------------------------------- /qwen_agent/gui/assets/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/qwen_agent/gui/assets/logo.jpeg -------------------------------------------------------------------------------- /qwen_agent/gui/assets/user.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/qwen_agent/gui/assets/user.jpeg -------------------------------------------------------------------------------- /qwen_agent/gui/gradio_dep.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | try: 16 | import gradio as gr 17 | assert gr.__version__ >= '5.0' 18 | import modelscope_studio.components.base as ms # noqa 19 | import modelscope_studio.components.legacy as mgr # noqa 20 | except Exception as e: 21 | raise ImportError('The dependencies for GUI support are not installed. ' 22 | 'Please install the required dependencies by running: pip install "qwen-agent[gui]"') from e 23 | -------------------------------------------------------------------------------- /qwen_agent/gui/gradio_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import base64 16 | 17 | 18 | def covert_image_to_base64(image_path): 19 | ext = image_path.split('.')[-1] 20 | if ext not in ['gif', 'jpeg', 'png']: 21 | ext = 'jpeg' 22 | 23 | with open(image_path, 'rb') as image_file: 24 | # Read the file 25 | encoded_string = base64.b64encode(image_file.read()) 26 | 27 | # Convert bytes to string 28 | base64_data = encoded_string.decode('utf-8') 29 | 30 | base64_url = f'data:image/{ext};base64,{base64_data}' 31 | return base64_url 32 | 33 | 34 | def format_cover_html(bot_name, bot_description, bot_avatar): 35 | if bot_avatar: 36 | image_src = covert_image_to_base64(bot_avatar) 37 | else: 38 | image_src = '//img.alicdn.com/imgextra/i3/O1CN01YPqZFO1YNZerQfSBk_!!6000000003047-0-tps-225-225.jpg' 39 | return f""" 40 | 72 | 73 |
74 |
75 | 76 |
77 |
{bot_name}
78 |
{bot_description}
79 |
80 | """ 81 | -------------------------------------------------------------------------------- /qwen_agent/llm/azure.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from typing import Dict, Optional 17 | 18 | import openai 19 | 20 | from qwen_agent.llm.base import register_llm 21 | from qwen_agent.llm.oai import TextChatAtOAI 22 | 23 | 24 | @register_llm('azure') 25 | class TextChatAtAzure(TextChatAtOAI): 26 | 27 | def __init__(self, cfg: Optional[Dict] = None): 28 | super().__init__(cfg) 29 | cfg = cfg or {} 30 | 31 | api_base = cfg.get('api_base') 32 | api_base = api_base or cfg.get('base_url') 33 | api_base = api_base or cfg.get('model_server') 34 | api_base = api_base or cfg.get('azure_endpoint') 35 | api_base = (api_base or '').strip() 36 | 37 | api_key = cfg.get('api_key') 38 | api_key = api_key or os.getenv('OPENAI_API_KEY') 39 | api_key = (api_key or 'EMPTY').strip() 40 | 41 | api_version = cfg.get('api_version', '2024-06-01') 42 | 43 | api_kwargs = {} 44 | if api_base: 45 | api_kwargs['azure_endpoint'] = api_base 46 | if api_key: 47 | api_kwargs['api_key'] = api_key 48 | if api_version: 49 | api_kwargs['api_version'] = api_version 50 | 51 | def _chat_complete_create(*args, **kwargs): 52 | client = openai.AzureOpenAI(**api_kwargs) 53 | return client.chat.completions.create(*args, **kwargs) 54 | 55 | self._chat_complete_create = _chat_complete_create 56 | -------------------------------------------------------------------------------- /qwen_agent/llm/fncall_prompts/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /qwen_agent/llm/qwenaudio_dashscope.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Dict, Optional 16 | 17 | from qwen_agent.llm.base import register_llm 18 | from qwen_agent.llm.qwenvl_dashscope import QwenVLChatAtDS 19 | 20 | 21 | @register_llm('qwenaudio_dashscope') 22 | class QwenAudioChatAtDS(QwenVLChatAtDS): 23 | 24 | @property 25 | def support_multimodal_input(self) -> bool: 26 | return True 27 | 28 | def __init__(self, cfg: Optional[Dict] = None): 29 | super().__init__(cfg) 30 | self.model = self.model or 'qwen-audio-turbo-latest' 31 | -------------------------------------------------------------------------------- /qwen_agent/llm/qwenomni_oai.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Dict, Optional 16 | 17 | from qwen_agent.llm.base import register_llm 18 | from qwen_agent.llm.qwenvl_oai import QwenVLChatAtOAI 19 | 20 | 21 | @register_llm('qwenomni_oai') 22 | class QwenOmniChatAtOAI(QwenVLChatAtOAI): 23 | 24 | @property 25 | def support_audio_input(self) -> bool: 26 | return True 27 | 28 | def __init__(self, cfg: Optional[Dict] = None): 29 | cfg = cfg or {} 30 | 31 | api_base = cfg.get('api_base') 32 | api_base = api_base or cfg.get('base_url') 33 | api_base = api_base or cfg.get('model_server') 34 | api_base = (api_base or '').strip() 35 | 36 | if not api_base: 37 | cfg['api_base'] = 'https://dashscope.aliyuncs.com/compatible-mode/v1' 38 | 39 | super().__init__(cfg) 40 | -------------------------------------------------------------------------------- /qwen_agent/log.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import logging 16 | import os 17 | 18 | 19 | def setup_logger(level=None): 20 | if level is None: 21 | if os.getenv('QWEN_AGENT_DEBUG', '0').strip().lower() in ('1', 'true'): 22 | level = logging.DEBUG 23 | else: 24 | level = logging.INFO 25 | 26 | handler = logging.StreamHandler() 27 | # Do not run handler.setLevel(level) so that users can change the level via logger.setLevel later 28 | formatter = logging.Formatter('%(asctime)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s') 29 | handler.setFormatter(formatter) 30 | 31 | _logger = logging.getLogger('qwen_agent_logger') 32 | _logger.setLevel(level) 33 | _logger.addHandler(handler) 34 | return _logger 35 | 36 | 37 | logger = setup_logger() 38 | -------------------------------------------------------------------------------- /qwen_agent/memory/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .memory import Memory 16 | 17 | __all__ = [ 18 | 'Memory', 19 | ] 20 | -------------------------------------------------------------------------------- /qwen_agent/multi_agent_hub.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from abc import ABC 16 | from typing import List 17 | 18 | from qwen_agent.agent import Agent 19 | from qwen_agent.log import logger 20 | 21 | 22 | class MultiAgentHub(ABC): 23 | 24 | @property 25 | def agents(self) -> List[Agent]: 26 | try: 27 | agent_list = self._agents 28 | assert isinstance(agent_list, list) 29 | assert all(isinstance(a, Agent) for a in agent_list) 30 | assert len(agent_list) > 0 31 | assert all(a.name for a in agent_list), 'All agents must have a name.' 32 | assert len(set(a.name for a in agent_list)) == len(agent_list), 'Agents must have unique names.' 33 | except (AttributeError, AssertionError) as e: 34 | logger.error( 35 | f'Class {self.__class__.__name__} inherits from MultiAgentHub. ' 36 | 'However, the following constraints are violated: ' 37 | "1) A class that inherits from MultiAgentHub must have an '_agents' attribute of type 'List[Agent]'. " 38 | "2) The '_agents' must be a non-empty list containing at least one agent. " 39 | "3) All agents in '_agents' must have non-empty, non-duplicate string names.") 40 | raise e 41 | return agent_list 42 | 43 | @property 44 | def agent_names(self) -> List[str]: 45 | return [x.name for x in self.agents] 46 | 47 | @property 48 | def nonuser_agents(self): 49 | from qwen_agent.agents.user_agent import UserAgent # put here to avoid cyclic import 50 | return [a for a in self.agents if not isinstance(a, UserAgent)] 51 | -------------------------------------------------------------------------------- /qwen_agent/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import ast 16 | import os 17 | from typing import List, Literal 18 | 19 | # Settings for LLMs 20 | DEFAULT_MAX_INPUT_TOKENS: int = int(os.getenv( 21 | 'QWEN_AGENT_DEFAULT_MAX_INPUT_TOKENS', 58000)) # The LLM will truncate the input messages if they exceed this limit 22 | 23 | # Settings for agents 24 | MAX_LLM_CALL_PER_RUN: int = int(os.getenv('QWEN_AGENT_MAX_LLM_CALL_PER_RUN', 20)) 25 | 26 | # Settings for tools 27 | DEFAULT_WORKSPACE: str = os.getenv('QWEN_AGENT_DEFAULT_WORKSPACE', 'workspace') 28 | 29 | # Settings for RAG 30 | DEFAULT_MAX_REF_TOKEN: int = int(os.getenv('QWEN_AGENT_DEFAULT_MAX_REF_TOKEN', 31 | 20000)) # The window size reserved for RAG materials 32 | DEFAULT_PARSER_PAGE_SIZE: int = int(os.getenv('QWEN_AGENT_DEFAULT_PARSER_PAGE_SIZE', 33 | 500)) # Max tokens per chunk when doing RAG 34 | DEFAULT_RAG_KEYGEN_STRATEGY: Literal['None', 'GenKeyword', 'SplitQueryThenGenKeyword', 'GenKeywordWithKnowledge', 35 | 'SplitQueryThenGenKeywordWithKnowledge'] = os.getenv( 36 | 'QWEN_AGENT_DEFAULT_RAG_KEYGEN_STRATEGY', 'GenKeyword') 37 | DEFAULT_RAG_SEARCHERS: List[str] = ast.literal_eval( 38 | os.getenv('QWEN_AGENT_DEFAULT_RAG_SEARCHERS', 39 | "['keyword_search', 'front_page_search']")) # Sub-searchers for hybrid retrieval 40 | -------------------------------------------------------------------------------- /qwen_agent/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .amap_weather import AmapWeather 16 | from .base import TOOL_REGISTRY, BaseTool 17 | from .code_interpreter import CodeInterpreter 18 | from .doc_parser import DocParser 19 | from .extract_doc_vocabulary import ExtractDocVocabulary 20 | from .image_gen import ImageGen 21 | from .python_executor import PythonExecutor 22 | from .retrieval import Retrieval 23 | from .search_tools import FrontPageSearch, HybridSearch, KeywordSearch, VectorSearch 24 | from .simple_doc_parser import SimpleDocParser 25 | from .storage import Storage 26 | from .web_extractor import WebExtractor 27 | from .mcp_manager import MCPManager 28 | from .web_search import WebSearch 29 | 30 | __all__ = [ 31 | 'BaseTool', 32 | 'CodeInterpreter', 33 | 'ImageGen', 34 | 'AmapWeather', 35 | 'TOOL_REGISTRY', 36 | 'DocParser', 37 | 'KeywordSearch', 38 | 'Storage', 39 | 'Retrieval', 40 | 'WebExtractor', 41 | 'SimpleDocParser', 42 | 'VectorSearch', 43 | 'HybridSearch', 44 | 'FrontPageSearch', 45 | 'ExtractDocVocabulary', 46 | 'PythonExecutor', 47 | 'MCPManager', 48 | 'WebSearch', 49 | ] 50 | -------------------------------------------------------------------------------- /qwen_agent/tools/amap_weather.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from typing import Dict, Optional, Union 17 | 18 | import requests 19 | 20 | from qwen_agent.tools.base import BaseTool, register_tool 21 | 22 | 23 | @register_tool('amap_weather') 24 | class AmapWeather(BaseTool): 25 | description = '获取对应城市的天气数据' 26 | parameters = [{ 27 | 'name': 'location', 28 | 'type': 'string', 29 | 'description': '城市/区具体名称,如`北京市海淀区`请描述为`海淀区`', 30 | 'required': True 31 | }] 32 | 33 | def __init__(self, cfg: Optional[Dict] = None): 34 | super().__init__(cfg) 35 | 36 | # remote call 37 | self.url = 'https://restapi.amap.com/v3/weather/weatherInfo?city={city}&key={key}' 38 | 39 | import pandas as pd 40 | self.city_df = pd.read_excel( 41 | 'https://modelscope.oss-cn-beijing.aliyuncs.com/resource/agent/AMap_adcode_citycode.xlsx') 42 | 43 | self.token = self.cfg.get('token', os.environ.get('AMAP_TOKEN', '')) 44 | assert self.token != '', 'weather api token must be acquired through ' \ 45 | 'https://lbs.amap.com/api/webservice/guide/create-project/get-key and set by AMAP_TOKEN' 46 | 47 | def get_city_adcode(self, city_name): 48 | filtered_df = self.city_df[self.city_df['中文名'] == city_name] 49 | if len(filtered_df['adcode'].values) == 0: 50 | raise ValueError(f'location {city_name} not found, availables are {self.city_df["中文名"]}') 51 | else: 52 | return filtered_df['adcode'].values[0] 53 | 54 | def call(self, params: Union[str, dict], **kwargs) -> str: 55 | params = self._verify_json_format_args(params) 56 | 57 | location = params['location'] 58 | response = requests.get(self.url.format(city=self.get_city_adcode(location), key=self.token)) 59 | data = response.json() 60 | if data['status'] == '0': 61 | raise RuntimeError(data) 62 | else: 63 | weather = data['lives'][0]['weather'] 64 | temperature = data['lives'][0]['temperature'] 65 | return f'{location}的天气是{weather}温度是{temperature}度。' 66 | -------------------------------------------------------------------------------- /qwen_agent/tools/extract_doc_vocabulary.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import os 17 | from typing import Dict, Optional, Union 18 | 19 | import json5 20 | 21 | from qwen_agent.settings import DEFAULT_WORKSPACE 22 | from qwen_agent.tools.base import BaseTool, register_tool 23 | from qwen_agent.tools.search_tools.keyword_search import WORDS_TO_IGNORE, string_tokenizer 24 | from qwen_agent.tools.simple_doc_parser import SimpleDocParser 25 | from qwen_agent.tools.storage import KeyNotExistsError, Storage 26 | 27 | 28 | @register_tool('extract_doc_vocabulary') 29 | class ExtractDocVocabulary(BaseTool): 30 | description = '提取文档的词表。' 31 | parameters = [{ 32 | 'name': 'files', 33 | 'type': 'array', 34 | 'items': { 35 | 'type': 'string' 36 | }, 37 | 'description': '文件路径列表,支持本地文件路径或可下载的http(s)链接。', 38 | 'required': True 39 | }] 40 | 41 | def __init__(self, cfg: Optional[Dict] = None): 42 | super().__init__(cfg) 43 | self.simple_doc_parse = SimpleDocParser() 44 | 45 | self.data_root = self.cfg.get('path', os.path.join(DEFAULT_WORKSPACE, 'tools', self.name)) 46 | self.db = Storage({'storage_root_path': self.data_root}) 47 | 48 | def call(self, params: Union[str, dict], **kwargs) -> str: 49 | params = self._verify_json_format_args(params) 50 | files = params.get('files', []) 51 | document_id = str(files) 52 | 53 | if isinstance(files, str): 54 | files = json5.loads(files) 55 | docs = [] 56 | for file in files: 57 | _doc = self.simple_doc_parse.call(params={'url': file}, **kwargs) 58 | docs.append(_doc) 59 | 60 | try: 61 | all_voc = self.db.call({'operate': 'get', 'key': document_id}) 62 | except KeyNotExistsError: 63 | try: 64 | from sklearn.feature_extraction.text import TfidfVectorizer 65 | except ModuleNotFoundError: 66 | raise ModuleNotFoundError('Please install sklearn by: `pip install scikit-learn`') 67 | 68 | vectorizer = TfidfVectorizer(tokenizer=string_tokenizer, stop_words=WORDS_TO_IGNORE) 69 | tfidf_matrix = vectorizer.fit_transform(docs) 70 | sorted_items = sorted(zip(vectorizer.get_feature_names_out(), 71 | tfidf_matrix.toarray().flatten()), 72 | key=lambda x: x[1], 73 | reverse=True) 74 | all_voc = ', '.join([term for term, score in sorted_items]) 75 | if document_id: 76 | self.db.call({'operate': 'put', 'key': document_id, 'value': json.dumps(all_voc, ensure_ascii=False)}) 77 | 78 | return all_voc 79 | -------------------------------------------------------------------------------- /qwen_agent/tools/image_gen.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import urllib.parse 17 | from typing import Union 18 | 19 | from qwen_agent.tools.base import BaseTool, register_tool 20 | 21 | 22 | @register_tool('image_gen') 23 | class ImageGen(BaseTool): 24 | description = 'An image generation service that takes text descriptions as input and returns a URL of the image. (The generated image URL should be described in markdown format in the reply to display the image: ![](URL_of_the_image))' 25 | parameters = [{ 26 | 'name': 27 | 'prompt', 28 | 'type': 29 | 'string', 30 | 'description': 31 | 'Detailed description of the desired content of the generated image, such as details of characters, environment, actions, etc., in English.', 32 | 'required': 33 | True 34 | }] 35 | 36 | def call(self, params: Union[str, dict], **kwargs) -> str: 37 | params = self._verify_json_format_args(params) 38 | 39 | prompt = params['prompt'] 40 | prompt = urllib.parse.quote(prompt) 41 | return json.dumps({'image_url': f'https://image.pollinations.ai/prompt/{prompt}'}, ensure_ascii=False) 42 | -------------------------------------------------------------------------------- /qwen_agent/tools/resource/AlibabaPuHuiTi-3-45-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QwenLM/Qwen-Agent/37e7e5fe6053b0640381063df40560a85aacc697/qwen_agent/tools/resource/AlibabaPuHuiTi-3-45-Light.ttf -------------------------------------------------------------------------------- /qwen_agent/tools/resource/code_interpreter_init_kernel.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json # noqa 16 | import math # noqa 17 | import os # noqa 18 | import re # noqa 19 | import signal 20 | 21 | import matplotlib # noqa 22 | import matplotlib.pyplot as plt 23 | import numpy as np # noqa 24 | import pandas as pd # noqa 25 | import seaborn as sns 26 | from matplotlib.font_manager import FontProperties 27 | from sympy import Eq, solve, symbols # noqa 28 | 29 | 30 | def input(*args, **kwargs): # noqa 31 | raise NotImplementedError('Python input() function is disabled.') 32 | 33 | 34 | def _m6_timout_handler(_signum=None, _frame=None): 35 | raise TimeoutError('M6_CODE_INTERPRETER_TIMEOUT') 36 | 37 | 38 | try: 39 | signal.signal(signal.SIGALRM, _m6_timout_handler) 40 | except AttributeError: # windows 41 | pass 42 | 43 | 44 | class _M6CountdownTimer: 45 | 46 | @classmethod 47 | def start(cls, timeout: int): 48 | try: 49 | signal.alarm(timeout) 50 | except AttributeError: # windows 51 | pass # I haven't found a timeout solution that works with windows + jupyter yet. 52 | 53 | @classmethod 54 | def cancel(cls): 55 | try: 56 | signal.alarm(0) 57 | except AttributeError: # windows 58 | pass 59 | 60 | 61 | sns.set_theme() 62 | 63 | _m6_font_prop = FontProperties(fname='{{M6_FONT_PATH}}') 64 | plt.rcParams['font.family'] = _m6_font_prop.get_name() 65 | -------------------------------------------------------------------------------- /qwen_agent/tools/search_tools/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .front_page_search import FrontPageSearch 16 | from .hybrid_search import HybridSearch 17 | from .keyword_search import KeywordSearch 18 | from .vector_search import VectorSearch 19 | 20 | __all__ = [ 21 | 'KeywordSearch', 22 | 'VectorSearch', 23 | 'HybridSearch', 24 | 'FrontPageSearch', 25 | ] 26 | -------------------------------------------------------------------------------- /qwen_agent/tools/search_tools/front_page_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import math 16 | from typing import List, Tuple 17 | 18 | from qwen_agent.settings import DEFAULT_MAX_REF_TOKEN 19 | from qwen_agent.tools.base import register_tool 20 | from qwen_agent.tools.doc_parser import Record 21 | from qwen_agent.tools.search_tools.base_search import BaseSearch 22 | 23 | POSITIVE_INFINITY = math.inf 24 | DEFAULT_FRONT_PAGE_NUM = 2 25 | 26 | 27 | @register_tool('front_page_search') 28 | class FrontPageSearch(BaseSearch): 29 | 30 | def sort_by_scores(self, 31 | query: str, 32 | docs: List[Record], 33 | max_ref_token: int = DEFAULT_MAX_REF_TOKEN, 34 | **kwargs) -> List[Tuple[str, int, float]]: 35 | if len(docs) > 1: 36 | # This is a trick for improving performance for one doc 37 | # It is not recommended to splice multiple documents directly, so return [], which will not effect the rank 38 | return [] 39 | 40 | chunk_and_score = [] 41 | for doc in docs: 42 | for chunk_id in range(min(DEFAULT_FRONT_PAGE_NUM, len(doc.raw))): 43 | page = doc.raw[chunk_id] 44 | if max_ref_token >= page.token * DEFAULT_FRONT_PAGE_NUM * 2: # Ensure that the first two pages do not fill up the window 45 | chunk_and_score.append((doc.url, chunk_id, POSITIVE_INFINITY)) 46 | else: 47 | break 48 | 49 | return chunk_and_score 50 | -------------------------------------------------------------------------------- /qwen_agent/tools/search_tools/hybrid_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Dict, List, Optional, Tuple 16 | 17 | from qwen_agent.settings import DEFAULT_RAG_SEARCHERS 18 | from qwen_agent.tools.base import TOOL_REGISTRY, register_tool 19 | from qwen_agent.tools.doc_parser import Record 20 | from qwen_agent.tools.search_tools.base_search import BaseSearch 21 | from qwen_agent.tools.search_tools.front_page_search import POSITIVE_INFINITY 22 | 23 | 24 | @register_tool('hybrid_search') 25 | class HybridSearch(BaseSearch): 26 | 27 | def __init__(self, cfg: Optional[Dict] = None): 28 | super().__init__(cfg) 29 | self.rag_searchers = self.cfg.get('rag_searchers', DEFAULT_RAG_SEARCHERS) 30 | 31 | if self.name in self.rag_searchers: 32 | raise ValueError(f'{self.name} can not be in `rag_searchers` = {self.rag_searchers}') 33 | self.search_objs = [TOOL_REGISTRY[name](cfg) for name in self.rag_searchers] 34 | 35 | def sort_by_scores(self, query: str, docs: List[Record], **kwargs) -> List[Tuple[str, int, float]]: 36 | chunk_and_score_list = [] 37 | for s_obj in self.search_objs: 38 | chunk_and_score_list.append(s_obj.sort_by_scores(query=query, docs=docs, **kwargs)) 39 | 40 | chunk_score_map = {} 41 | for doc in docs: 42 | chunk_score_map[doc.url] = [0] * len(doc.raw) 43 | 44 | for chunk_and_score in chunk_and_score_list: 45 | for i in range(len(chunk_and_score)): 46 | doc_id = chunk_and_score[i][0] 47 | chunk_id = chunk_and_score[i][1] 48 | score = chunk_and_score[i][2] 49 | if score == POSITIVE_INFINITY: 50 | chunk_score_map[doc_id][chunk_id] = POSITIVE_INFINITY 51 | else: 52 | # TODO: This needs to be adjusted for performance 53 | chunk_score_map[doc_id][chunk_id] += 1 / (i + 1 + 60) 54 | 55 | all_chunk_and_score = [] 56 | for k, v in chunk_score_map.items(): 57 | for i, x in enumerate(v): 58 | all_chunk_and_score.append((k, i, x)) 59 | all_chunk_and_score.sort(key=lambda item: item[2], reverse=True) 60 | 61 | return all_chunk_and_score 62 | -------------------------------------------------------------------------------- /qwen_agent/tools/search_tools/vector_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import os 17 | from typing import List, Tuple 18 | 19 | from qwen_agent.tools.base import register_tool 20 | from qwen_agent.tools.doc_parser import Record 21 | from qwen_agent.tools.search_tools.base_search import BaseSearch 22 | 23 | 24 | @register_tool('vector_search') 25 | class VectorSearch(BaseSearch): 26 | # TODO: Optimize the accuracy of the embedding retriever. 27 | 28 | def sort_by_scores(self, query: str, docs: List[Record], **kwargs) -> List[Tuple[str, int, float]]: 29 | # TODO: More types of embedding can be configured 30 | try: 31 | from langchain.schema import Document 32 | except ModuleNotFoundError: 33 | raise ModuleNotFoundError('Please install langchain by: `pip install langchain`') 34 | try: 35 | from langchain_community.embeddings import DashScopeEmbeddings 36 | from langchain_community.vectorstores import FAISS 37 | except ModuleNotFoundError: 38 | raise ModuleNotFoundError( 39 | 'Please install langchain_community by: `pip install langchain_community`, ' 40 | 'and install faiss by: `pip install faiss-cpu` or `pip install faiss-gpu` (for CUDA supported GPU)') 41 | # Extract raw query 42 | try: 43 | query_json = json.loads(query) 44 | # This assumes that the user's input will not contain json str with the 'text' attribute 45 | if 'text' in query_json: 46 | query = query_json['text'] 47 | except json.decoder.JSONDecodeError: 48 | pass 49 | 50 | # Plain all chunks from all docs 51 | all_chunks = [] 52 | for doc in docs: 53 | for chk in doc.raw: 54 | all_chunks.append(Document(page_content=chk.content[:2000], metadata=chk.metadata)) 55 | 56 | embeddings = DashScopeEmbeddings(model='text-embedding-v1', 57 | dashscope_api_key=os.getenv('DASHSCOPE_API_KEY', '')) 58 | db = FAISS.from_documents(all_chunks, embeddings) 59 | chunk_and_score = db.similarity_search_with_score(query, k=len(all_chunks)) 60 | 61 | return [(chk.metadata['source'], chk.metadata['chunk_id'], score) for chk, score in chunk_and_score] 62 | -------------------------------------------------------------------------------- /qwen_agent/tools/web_extractor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Union 16 | 17 | from qwen_agent.tools.base import BaseTool, register_tool 18 | from qwen_agent.tools.simple_doc_parser import SimpleDocParser 19 | 20 | 21 | @register_tool('web_extractor') 22 | class WebExtractor(BaseTool): 23 | description = '根据网页URL,获取网页内容的工具' 24 | parameters = [{'name': 'url', 'type': 'string', 'description': '网页URL', 'required': True}] 25 | 26 | def call(self, params: Union[str, dict], **kwargs) -> str: 27 | params = self._verify_json_format_args(params) 28 | url = params['url'] 29 | parsed_web = SimpleDocParser().call({'url': url}) 30 | return parsed_web 31 | -------------------------------------------------------------------------------- /qwen_agent/tools/web_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from typing import Any, List, Union 17 | 18 | import requests 19 | 20 | from qwen_agent.tools.base import BaseTool, register_tool 21 | 22 | SERPER_API_KEY = os.getenv('SERPER_API_KEY', '') 23 | SERPER_URL = os.getenv('SERPER_URL', 'https://google.serper.dev/search') 24 | 25 | 26 | @register_tool('web_search', allow_overwrite=True) 27 | class WebSearch(BaseTool): 28 | name = 'web_search' 29 | description = 'Search for information from the internet.' 30 | parameters = { 31 | 'type': 'object', 32 | 'properties': { 33 | 'query': { 34 | 'type': 'string', 35 | } 36 | }, 37 | 'required': ['query'], 38 | } 39 | 40 | def call(self, params: Union[str, dict], **kwargs) -> str: 41 | params = self._verify_json_format_args(params) 42 | query = params['query'] 43 | 44 | search_results = self.search(query) 45 | formatted_results = self._format_results(search_results) 46 | return formatted_results 47 | 48 | @staticmethod 49 | def search(query: str) -> List[Any]: 50 | if not SERPER_API_KEY: 51 | raise ValueError( 52 | 'SERPER_API_KEY is None! Please Apply for an apikey from https://serper.dev and set it as an environment variable by `export SERPER_API_KEY=xxxxxx`' 53 | ) 54 | headers = {'Content-Type': 'application/json', 'X-API-KEY': SERPER_API_KEY} 55 | payload = {'q': query} 56 | response = requests.post(SERPER_URL, json=payload, headers=headers) 57 | response.raise_for_status() 58 | 59 | return response.json()['organic'] 60 | 61 | @staticmethod 62 | def _format_results(search_results: List[Any]) -> str: 63 | content = '```\n{}\n```'.format('\n\n'.join([ 64 | f"[{i}]\"{doc['title']}\n{doc.get('snippet', '')}\"{doc.get('date', '')}" 65 | for i, doc in enumerate(search_results, 1) 66 | ])) 67 | return content 68 | -------------------------------------------------------------------------------- /qwen_agent/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /qwen_agent/utils/output_beautify.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import List 16 | 17 | from qwen_agent.llm.schema import ASSISTANT, FUNCTION 18 | 19 | TOOL_CALL_S = '[TOOL_CALL]' 20 | TOOL_CALL_E = '' 21 | TOOL_RESULT_S = '[TOOL_RESPONSE]' 22 | TOOL_RESULT_E = '' 23 | THOUGHT_S = '[THINK]' 24 | ANSWER_S = '[ANSWER]' 25 | 26 | 27 | def typewriter_print(messages: List[dict], text: str) -> str: 28 | full_text = '' 29 | content = [] 30 | for msg in messages: 31 | if msg['role'] == ASSISTANT: 32 | if msg.get('reasoning_content'): 33 | assert isinstance(msg['reasoning_content'], str), 'Now only supports text messages' 34 | content.append(f'{THOUGHT_S}\n{msg["reasoning_content"]}') 35 | if msg.get('content'): 36 | assert isinstance(msg['content'], str), 'Now only supports text messages' 37 | content.append(f'{ANSWER_S}\n{msg["content"]}') 38 | if msg.get('function_call'): 39 | content.append(f'{TOOL_CALL_S} {msg["function_call"]["name"]}\n{msg["function_call"]["arguments"]}') 40 | elif msg['role'] == FUNCTION: 41 | content.append(f'{TOOL_RESULT_S} {msg["name"]}\n{msg["content"]}') 42 | else: 43 | raise TypeError 44 | if content: 45 | full_text = '\n'.join(content) 46 | print(full_text[len(text):], end='', flush=True) 47 | 48 | return full_text 49 | -------------------------------------------------------------------------------- /qwen_agent/utils/parallel_executor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import random 16 | import time 17 | from concurrent.futures import ThreadPoolExecutor, as_completed 18 | from typing import Any, Callable, List, Optional 19 | 20 | 21 | def parallel_exec( 22 | fn: Callable, 23 | list_of_kwargs: List[dict], 24 | max_workers: Optional[int] = None, 25 | jitter: float = 0.0, 26 | ) -> list: 27 | """ 28 | Executes a given function `fn` in parallel, using multiple threads, on a list of argument tuples. 29 | The function limits the number of concurrent executions to `max_workers` and processes tasks in chunks, 30 | pausing between each chunk to avoid hitting rate limits or quotas. 31 | 32 | Args: 33 | - fn (Callable): The function to execute in parallel. 34 | - list_of_kwargs (list): A list of dicts, where each dict contains arguments for a single call to `fn`. 35 | - max_workers (int, optional): The maximum number of threads that can be used to execute the tasks 36 | concurrently. 37 | - jitter (float, optional): Wait for jitter * random.random() before submitting the next job. 38 | 39 | Returns: 40 | - A list containing the results of the function calls. The order of the results corresponds to the order 41 | the tasks were completed, which may not necessarily be the same as the order of `list_of_kwargs`. 42 | 43 | """ 44 | results = [] 45 | with ThreadPoolExecutor(max_workers=max_workers) as executor: 46 | # Get the tasks for the current chunk 47 | futures = [] 48 | for kwargs in list_of_kwargs: 49 | futures.append(executor.submit(fn, **kwargs)) 50 | if jitter > 0.0: 51 | time.sleep(jitter * random.random()) 52 | for future in as_completed(futures): 53 | results.append(future.result()) 54 | return results 55 | 56 | 57 | # for debug 58 | def serial_exec(fn: Callable, list_of_kwargs: List[dict]) -> List[Any]: 59 | results = [] 60 | for kwargs in list_of_kwargs: 61 | result = fn(**kwargs) 62 | results.append(result) 63 | return results 64 | -------------------------------------------------------------------------------- /qwen_agent/utils/str_processing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import re 16 | 17 | from qwen_agent.utils.utils import has_chinese_chars 18 | 19 | 20 | def rm_newlines(text): 21 | if text.endswith('-\n'): 22 | text = text[:-2] 23 | return text.strip() 24 | rep_c = ' ' 25 | if has_chinese_chars(text): 26 | rep_c = '' 27 | text = re.sub(r'(?<=[^\.。::\d])\n', rep_c, text) 28 | return text.strip() 29 | 30 | 31 | def rm_cid(text): 32 | text = re.sub(r'\(cid:\d+\)', '', text) 33 | return text 34 | 35 | 36 | def rm_hexadecimal(text): 37 | text = re.sub(r'[0-9A-Fa-f]{21,}', '', text) 38 | return text 39 | 40 | 41 | def rm_continuous_placeholders(text): 42 | text = re.sub(r'[.\- —。_*]{7,}', '\t', text) 43 | text = re.sub(r'\n{3,}', '\n\n', text) 44 | return text 45 | -------------------------------------------------------------------------------- /qwen_server/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /qwen_server/add_qwen_libs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import sys 16 | from pathlib import Path 17 | 18 | # This can be removed, if install qwen_agent by `pip install -e ./` 19 | sys.path.insert(0, str(Path(__file__).absolute().parent.parent)) 20 | -------------------------------------------------------------------------------- /qwen_server/css/main.css: -------------------------------------------------------------------------------- 1 | #warning {background-color: #FFCCCB} 2 | .rec { 3 | font-size: 16px !important; 4 | text-align:left} 5 | .title { 6 | font-size: 30px !important; 7 | text-align:center} 8 | .desc { 9 | font-size: 16px !important; 10 | text-align:center} 11 | 12 | .div_tmp { 13 | height: 190px; 14 | border-radius: 5px 15 | } 16 | 17 | .div_rec { 18 | height: 300px; 19 | border-radius: 5px 20 | } 21 | 22 | .bt_small_font{ 23 | font-size: 6px; 24 | } 25 | 26 | .bt_small{ 27 | width: 30px; 28 | } 29 | 30 | 31 | .md_tmp { 32 | height: calc(100dvh - 380px); 33 | border-radius: 5px 34 | 35 | } 36 | 37 | .add_scrollbar { 38 | overflow-y: scroll; 39 | } 40 | 41 | .content { 42 | height: calc(100% - 50px); 43 | overflow-y: scroll; 44 | } 45 | 46 | 47 | 48 | 49 | .custom-checkbox { 50 | appearance: none; 51 | -webkit-appearance: none; 52 | -moz-appearance: none; 53 | width: 10px; 54 | height: 10px; 55 | border-radius: 50%; 56 | border: 2px solid #ccc; 57 | outline: none; 58 | cursor: pointer; 59 | } 60 | 61 | .custom-checkbox:checked { 62 | background-color: rgb(100, 239, 144); 63 | } 64 | 65 | .dark svg { 66 | fill: white; 67 | } 68 | 69 | .dark a { 70 | color: white !important; 71 | } 72 | 73 | .textbox_default textarea { 74 | height: calc(100dvh - 200px); 75 | } 76 | 77 | .textbox_default_output textarea { 78 | height: calc(100dvh - 200px); 79 | } 80 | 81 | .textbox textarea { 82 | height: calc(100dvh - 200px); 83 | } 84 | 85 | .textbox_default textarea, 86 | .textbox_default_output textarea, 87 | .textbox textarea 88 | { 89 | font-size: 16px !important; 90 | color: #46464A !important; 91 | } 92 | 93 | .dark textarea { 94 | color: #efefef !important; 95 | } 96 | 97 | @media screen and (max-width: 711px) { 98 | .textbox_default textarea { 99 | height: calc(100dvh - 271px); 100 | } 101 | 102 | div .default-token-counter { 103 | top: calc( 0.5 * (100dvh - 245px) ) !important; 104 | } 105 | } 106 | 107 | /* Hide the gradio footer*/ 108 | footer { 109 | display: none !important; 110 | } 111 | 112 | button { 113 | font-size: 14px !important; 114 | } 115 | 116 | .token-counter { 117 | position: absolute !important; 118 | top: calc( 0.5 * (100dvh - 215px) ) !important; 119 | right: 2px; 120 | z-index: 100; 121 | background: var(--input-background-fill) !important; 122 | min-height: 0 !important; 123 | } 124 | 125 | .default-token-counter { 126 | top: calc( 0.5 * (100dvh - 255px) ) !important; 127 | } 128 | 129 | .token-counter span { 130 | padding: 1px; 131 | box-shadow: 0 0 0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075); 132 | border: 2px solid rgba(192,192,192,0.4) !important; 133 | border-radius: 0.4em; 134 | } 135 | -------------------------------------------------------------------------------- /qwen_server/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | () => { 18 | window.onload = function() { 19 | // autoTriggerFunction(); 20 | }; 21 | 22 | function autoTriggerFunction() { 23 | var button = document.getElementById("update_all_bt"); 24 | button.click(); 25 | } 26 | 27 | // const textbox = document.querySelector('#cmd label textarea'); 28 | 29 | // textbox.addEventListener('input', () => { 30 | // textbox.scrollTop = textbox.scrollHeight; 31 | // console.log('input'); 32 | // }); 33 | // textbox.addEventListener('change', () => { 34 | // textbox.scrollTop = textbox.scrollHeight; 35 | // console.log('change'); 36 | // }); 37 | 38 | function scrollTextboxToBottom() { 39 | var textbox = document.querySelector('.textbox_container label textarea'); 40 | textbox.scrollTop = textbox.scrollHeight*10; 41 | } 42 | window.addEventListener('DOMContentLoaded', scrollTextboxToBottom); 43 | 44 | document.addEventListener('change', function(event) { 45 | // Check if the changed element is a checkbox 46 | if (event.target.type === 'checkbox') { 47 | console.log(location.hostname); 48 | var _server_url = "http://" + location.hostname + ":7866/endpoint"; 49 | fetch(_server_url, { 50 | method: "POST", 51 | headers: { 52 | "Content-Type": "application/json", 53 | }, 54 | body: JSON.stringify({'task': 'change_checkbox', 'ckid': event.target.id}), 55 | }) 56 | .then((response) => response.json()) 57 | .then((data) => { 58 | console.log(data.result); 59 | }); 60 | } 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /qwen_server/output_beautify.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json5 16 | 17 | from qwen_agent.log import logger 18 | from qwen_agent.utils.utils import extract_code, extract_urls, print_traceback 19 | 20 | FN_NAME = 'Action' 21 | FN_ARGS = 'Action Input' 22 | FN_RESULT = 'Observation' 23 | FN_EXIT = 'Response' 24 | 25 | 26 | def extract_obs(text): 27 | k = text.rfind('\nObservation:') 28 | j = text.rfind('\nThought:') 29 | obs = text[k + len('\nObservation:'):j] 30 | return obs.strip() 31 | 32 | 33 | def format_answer(text): 34 | if 'code_interpreter' in text: 35 | rsp = '' 36 | code = extract_code(text) 37 | rsp += ('\n```py\n' + code + '\n```\n') 38 | obs = extract_obs(text) 39 | if '![fig' in obs: 40 | rsp += obs 41 | return rsp 42 | elif 'image_gen' in text: 43 | # get url of FA 44 | # img_urls = URLExtract().find_urls(text.split("Final Answer:")[-1].strip()) 45 | obs = text.split(f'{FN_RESULT}:')[-1].split(f'{FN_EXIT}:')[0].strip() 46 | img_urls = [] 47 | if obs: 48 | logger.info(repr(obs)) 49 | try: 50 | obs = json5.loads(obs) 51 | img_urls.append(obs['image_url']) 52 | except Exception: 53 | print_traceback() 54 | img_urls = [] 55 | if not img_urls: 56 | img_urls = extract_urls(text.split(f'{FN_EXIT}:')[-1].strip()) 57 | logger.info(img_urls) 58 | rsp = '' 59 | for x in img_urls: 60 | rsp += '\n![picture](' + x.strip() + ')' 61 | return rsp 62 | else: 63 | return text.split(f'{FN_EXIT}:')[-1].strip() 64 | -------------------------------------------------------------------------------- /qwen_server/schema.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from pydantic import BaseModel 16 | 17 | 18 | class PathConfig(BaseModel): 19 | work_space_root: str 20 | download_root: str 21 | code_interpreter_ws: str 22 | 23 | 24 | class ServerConfig(BaseModel): 25 | server_host: str 26 | fast_api_port: int 27 | app_in_browser_port: int 28 | workstation_port: int 29 | model_server: str 30 | api_key: str 31 | llm: str 32 | max_ref_token: int 33 | max_days: int 34 | 35 | class Config: 36 | protected_namespaces = () 37 | 38 | 39 | class GlobalConfig(BaseModel): 40 | path: PathConfig 41 | server: ServerConfig 42 | -------------------------------------------------------------------------------- /qwen_server/server_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": { 3 | "work_space_root": "workspace/", 4 | "download_root": "workspace/download/", 5 | "code_interpreter_ws": "workspace/tools/code_interpreter/" 6 | }, 7 | "server": { 8 | "server_host": "127.0.0.1", 9 | "fast_api_port": 7866, 10 | "app_in_browser_port": 7863, 11 | "workstation_port": 7864, 12 | "model_server": "dashscope", 13 | "api_key": "", 14 | "llm": "qwen-plus", 15 | "max_ref_token": 4000, 16 | "max_days": 7 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/agents/test_article_agent.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import pytest 16 | 17 | from qwen_agent.agents import ArticleAgent 18 | 19 | 20 | @pytest.mark.skip() 21 | def test_article_agent_full_article(): 22 | llm_cfg = {'model': 'qwen-max', 'api_key': '', 'model_server': 'dashscope'} 23 | agent = ArticleAgent(llm=llm_cfg) 24 | messages = [{ 25 | 'role': 'user', 26 | 'content': [{ 27 | 'text': 'Qwen-Agent简介' 28 | }, { 29 | 'file': 'https://github.com/QwenLM/Qwen-Agent' 30 | }] 31 | }] 32 | *_, last = agent.run(messages, full_article=True) 33 | 34 | assert last[-2]['content'] == '>\n> Writing Text: \n' 35 | assert len(last[-1]['content']) > 0 36 | -------------------------------------------------------------------------------- /tests/agents/test_assistant.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import Assistant 16 | from qwen_agent.llm.schema import ContentItem, Message 17 | 18 | 19 | def test_assistant_system_and_tool(): 20 | llm_cfg = {'model': 'qwen-max'} 21 | system = '你扮演一个天气预报助手,你具有查询天气能力。' 22 | 23 | tools = ['image_gen', 'amap_weather'] 24 | agent = Assistant(llm=llm_cfg, system_message=system, function_list=tools) 25 | 26 | messages = [Message('user', '海淀区天气')] 27 | 28 | *_, last = agent.run(messages) 29 | 30 | assert last[-3].function_call.name == 'amap_weather' 31 | assert last[-3].function_call.arguments == '{"location": "海淀区"}' 32 | assert last[-2].name == 'amap_weather' 33 | assert len(last[-1].content) > 0 34 | 35 | 36 | def test_assistant_files(): 37 | llm_cfg = {'model': 'qwen-max'} 38 | agent = Assistant(llm=llm_cfg) 39 | 40 | messages = [ 41 | Message('user', [ 42 | ContentItem(text='总结一个文章标题'), 43 | ContentItem( 44 | file='https://help.aliyun.com/zh/dashscope/developer-reference/api-details?disableWebsiteRedirect=true') 45 | ]) 46 | ] 47 | 48 | *_, last = agent.run(messages) 49 | 50 | assert len(last[-1].content) > 0 51 | 52 | 53 | def test_assistant_empty_query(): 54 | llm_cfg = {'model': 'qwen2-7b-instruct'} 55 | agent = Assistant(llm=llm_cfg) 56 | 57 | messages = [ 58 | Message('user', [ 59 | ContentItem( 60 | file='https://help.aliyun.com/zh/dashscope/developer-reference/api-details?disableWebsiteRedirect=true') 61 | ]) 62 | ] 63 | *_, last = agent.run(messages) 64 | print(last) 65 | last_text = last[-1].content 66 | assert ('通义千问' in last_text) or ('qwen' in last_text.lower()) 67 | 68 | 69 | def test_assistant_vl(): 70 | llm_cfg = {'model': 'qwen-vl-max'} 71 | agent = Assistant(llm=llm_cfg) 72 | 73 | messages = [ 74 | Message('user', [ 75 | ContentItem(text='用一句话描述图片'), 76 | ContentItem(image='https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg'), 77 | ]) 78 | ] 79 | 80 | *_, last = agent.run(messages) 81 | 82 | assert len(last[-1].content) > 0 83 | -------------------------------------------------------------------------------- /tests/agents/test_custom_tool_object.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import urllib.parse 17 | 18 | import json5 19 | 20 | from qwen_agent.agents import Assistant 21 | from qwen_agent.tools.base import BaseTool 22 | 23 | 24 | class MyImageGen(BaseTool): 25 | name = 'my_image_gen' 26 | description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.' 27 | parameters = [{ 28 | 'name': 'prompt', 29 | 'type': 'string', 30 | 'description': 'Detailed description of the desired image content, in English', 31 | 'required': True 32 | }] 33 | 34 | def call(self, params: str, **kwargs) -> str: 35 | prompt = json5.loads(params)['prompt'] 36 | prompt = urllib.parse.quote(prompt) 37 | return json.dumps({'image_url': f'https://image.pollinations.ai/prompt/{prompt}'}, ensure_ascii=False) 38 | 39 | 40 | def init_agent_service(): 41 | llm_cfg = {'model': 'qwen-max'} 42 | system = ('According to the user\'s request, you must draw a picture with my_image_gen tool') 43 | 44 | tools = [MyImageGen(), 'code_interpreter'] # code_interpreter is a built-in tool in Qwen-Agent 45 | bot = Assistant(llm=llm_cfg, system_message=system, function_list=tools) 46 | 47 | return bot 48 | 49 | 50 | def test_custom_tool_object(): 51 | # Define the agent 52 | bot = init_agent_service() 53 | 54 | # Chat 55 | messages = [{'role': 'user', 'content': 'draw a dog'}] 56 | for response in bot.run(messages=messages): 57 | print('bot response:', response) 58 | 59 | assert len(response) == 3 60 | assert response[1]['role'] == 'function' and response[1]['name'] == 'my_image_gen' 61 | -------------------------------------------------------------------------------- /tests/agents/test_doc_qa.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents.doc_qa import BasicDocQA 16 | 17 | 18 | def test_doc_qa(): 19 | llm_cfg = {'model': 'qwen-max', 'api_key': '', 'model_server': 'dashscope'} 20 | agent = BasicDocQA(llm=llm_cfg) 21 | messages = [{ 22 | 'role': 'user', 23 | 'content': [{ 24 | 'text': 'Summarize a title' 25 | }, { 26 | 'file': 'https://www.runoob.com/fastapi/fastapi-tutorial.html' 27 | }] 28 | }] 29 | *_, last = agent.run(messages) 30 | 31 | assert len(last[-1]['content']) > 0 32 | -------------------------------------------------------------------------------- /tests/agents/test_parallel_qa.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents.doc_qa import ParallelDocQA 16 | 17 | 18 | def test_parallel_qa(): 19 | llm_cfg = {'model': 'qwen-max', 'api_key': '', 'model_server': 'dashscope'} 20 | agent = ParallelDocQA(llm=llm_cfg) 21 | messages = [{ 22 | 'role': 23 | 'user', 24 | 'content': [{ 25 | 'text': 'FastAPI适合IO密集任务吗' 26 | }, { 27 | 'file': 'https://www.runoob.com/fastapi/fastapi-tutorial.html' 28 | }, { 29 | 'file': 'https://www.runoob.com/fastapi/fastapi-install.html' 30 | }] 31 | }] 32 | *_, last = agent.run(messages) 33 | 34 | assert len(last[-1]['content']) > 0 35 | -------------------------------------------------------------------------------- /tests/agents/test_react_chat.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import shutil 17 | from pathlib import Path 18 | 19 | from qwen_agent.agents import ReActChat 20 | from qwen_agent.llm.schema import ContentItem, Message 21 | 22 | 23 | def test_react_chat(): 24 | llm_cfg = {'model': 'qwen-max'} 25 | tools = ['image_gen', 'amap_weather'] 26 | agent = ReActChat(llm=llm_cfg, function_list=tools) 27 | 28 | messages = [Message('user', '海淀区天气')] 29 | 30 | *_, last = agent.run(messages) 31 | 32 | assert '\nAction: ' in last[-1].content 33 | assert '\nAction Input: ' in last[-1].content 34 | assert '\nObservation: ' in last[-1].content 35 | assert '\nThought: ' in last[-1].content 36 | assert '\nFinal Answer: ' in last[-1].content 37 | 38 | 39 | def test_react_chat_with_file(): 40 | if os.path.exists('workspace'): 41 | shutil.rmtree('workspace') 42 | llm_cfg = { 43 | 'model': 'qwen-max', 44 | 'model_server': 'dashscope', 45 | 'api_key': os.getenv('DASHSCOPE_API_KEY'), 46 | } 47 | tools = ['code_interpreter'] 48 | agent = ReActChat(llm=llm_cfg, function_list=tools) 49 | messages = [ 50 | Message( 51 | 'user', 52 | [ 53 | ContentItem( 54 | text= # noqa 55 | 'pd.head the file first and then help me draw a line chart to show the changes in stock prices'), 56 | ContentItem( 57 | file=str(Path(__file__).resolve().parent.parent.parent / 'examples/resource/stock_prices.csv')) 58 | ]) 59 | ] 60 | 61 | *_, last = agent.run(messages) 62 | assert len(last[-1].content) > 0 63 | -------------------------------------------------------------------------------- /tests/agents/test_router.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.agents import Assistant, Router 16 | from qwen_agent.llm.schema import ContentItem, Message 17 | 18 | 19 | def test_router(): 20 | llm_cfg = {'model': 'qwen-max'} 21 | llm_cfg_vl = {'model': 'qwen-vl-max'} 22 | tools = ['amap_weather'] 23 | 24 | # Define a vl agent 25 | bot_vl = Assistant(llm=llm_cfg_vl, name='多模态助手', description='可以理解图像内容。') 26 | 27 | # Define a tool agent 28 | bot_tool = Assistant( 29 | llm=llm_cfg, 30 | name='天气预报助手', 31 | description='可以查询天气', 32 | function_list=tools, 33 | ) 34 | 35 | # define a router (Simultaneously serving as a text agent) 36 | bot = Router(llm=llm_cfg, agents=[bot_vl, bot_tool]) 37 | messages = [ 38 | Message('user', [ 39 | ContentItem(text='描述图片'), 40 | ContentItem(image='https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg'), 41 | ]) 42 | ] 43 | 44 | *_, last = bot.run(messages) 45 | assert isinstance(last[-1].content, str) 46 | 47 | messages = [Message('user', '海淀区天气')] 48 | 49 | *_, last = bot.run(messages) 50 | assert last[-3].function_call.name == 'amap_weather' 51 | assert last[-3].function_call.arguments == '{"location": "海淀区"}' 52 | assert last[-2].name == 'amap_weather' 53 | assert len(last[-1].content) > 0 54 | -------------------------------------------------------------------------------- /tests/examples/test_long_dialogue.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | 18 | sys.path.insert(0, os.path.abspath(os.path.join(__file__, '../../..'))) # noqa 19 | 20 | ROOT_RESOURCE = os.path.abspath(os.path.join(__file__, '../../../examples/resource')) # noqa 21 | from examples.long_dialogue import test as long_dialogue # noqa 22 | 23 | 24 | def test_long_dialogue(): 25 | long_dialogue() 26 | -------------------------------------------------------------------------------- /tests/examples/test_vm_qa.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | 18 | sys.path.insert(0, os.path.abspath(os.path.join(__file__, '../../..'))) # noqa 19 | 20 | ROOT_RESOURCE = os.path.abspath(os.path.join(__file__, '../../../examples/resource')) # noqa 21 | from examples.virtual_memory_qa import test as vm # noqa 22 | 23 | 24 | def test_vm(): 25 | vm() 26 | -------------------------------------------------------------------------------- /tests/llm/test_continue.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | import pytest 18 | 19 | from qwen_agent.llm import get_chat_model 20 | from qwen_agent.llm.schema import Message 21 | 22 | 23 | @pytest.mark.parametrize('stream', [True, False]) 24 | @pytest.mark.parametrize('delta_stream', [False]) 25 | @pytest.mark.parametrize('llm_cfg', [{ 26 | 'model': 'qwen-max', 27 | 'model_server': 'dashscope' 28 | }, { 29 | 'model': 'qwen2.5-7b-instruct', 30 | 'model_server': 'https://dashscope.aliyuncs.com/compatible-mode/v1', 31 | 'api_key': os.getenv('DASHSCOPE_API_KEY', 'none') 32 | }]) 33 | def test_continue(stream, delta_stream, llm_cfg): 34 | if not stream and delta_stream: 35 | pytest.skip('Skipping this combination') 36 | 37 | # Chat with text llm 38 | llm = get_chat_model(llm_cfg) 39 | messages = [ 40 | Message('user', 'what is 1+1?'), 41 | Message('assistant', '```python\nprint(1+1)\n```\n```output\n2\n```\n') 42 | ] 43 | # messages = [Message('user', 'hi'), 44 | # Message('assistant', 'hi,今天天气')] 45 | 46 | response = llm.chat(messages=messages, stream=stream, delta_stream=delta_stream) 47 | if stream: 48 | response = list(response)[-1] 49 | assert isinstance(response[-1]['content'], str) 50 | assert response[-1].function_call is None 51 | print(response) 52 | 53 | 54 | if __name__ == '__main__': 55 | test_continue(stream=True, delta_stream=False, llm_cfg={'model': 'qwen-max', 'model_server': 'dashscope'}) 56 | test_continue(stream=True, 57 | delta_stream=False, 58 | llm_cfg={ 59 | 'model': 'qwen2-7b-instruct', 60 | 'model_server': 'https://dashscope.aliyuncs.com/compatible-mode/v1', 61 | 'api_key': os.getenv('DASHSCOPE_API_KEY', 'none') 62 | }) 63 | -------------------------------------------------------------------------------- /tests/llm/test_oai.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | import pytest 18 | 19 | from qwen_agent.llm import get_chat_model 20 | from qwen_agent.llm.schema import Message 21 | 22 | functions = [{ 23 | 'name': 'image_gen', 24 | 'description': 'AI绘画(图像生成)服务,输入文本描述和图像分辨率,返回根据文本信息绘制的图片URL。', 25 | 'parameters': { 26 | 'type': 'object', 27 | 'properties': { 28 | 'prompt': { 29 | 'type': 'string', 30 | 'description': '详细描述了希望生成的图像具有什么内容,例如人物、环境、动作等细节描述,使用英文', 31 | }, 32 | }, 33 | 'required': ['prompt'], 34 | } 35 | }] 36 | 37 | 38 | @pytest.mark.parametrize('functions', [None, functions]) 39 | @pytest.mark.parametrize('stream', [True, False]) 40 | @pytest.mark.parametrize('delta_stream', [True, False]) 41 | def test_llm_oai(functions, stream, delta_stream): 42 | if not stream and delta_stream: 43 | pytest.skip('Skipping this combination') 44 | 45 | if delta_stream and functions: 46 | pytest.skip('Skipping this combination') 47 | 48 | # settings 49 | llm_cfg = { 50 | 'model': 'qwen2-7b-instruct', 51 | 'model_server': 'https://dashscope.aliyuncs.com/compatible-mode/v1', 52 | 'api_key': os.getenv('DASHSCOPE_API_KEY', 'none') 53 | } 54 | 55 | llm = get_chat_model(llm_cfg) 56 | assert llm.max_retries == 0 57 | 58 | messages = [Message('user', 'draw a cute cat')] 59 | response = llm.chat(messages=messages, functions=functions, stream=stream, delta_stream=delta_stream) 60 | if stream: 61 | response = list(response)[-1] 62 | 63 | assert isinstance(response[-1]['content'], str) 64 | if functions: 65 | assert response[-1].function_call.name == 'image_gen' 66 | else: 67 | assert response[-1].function_call is None 68 | -------------------------------------------------------------------------------- /tests/memory/test_memory.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import shutil 17 | from pathlib import Path 18 | 19 | import json5 20 | 21 | from qwen_agent.llm.schema import ContentItem, Message 22 | from qwen_agent.memory import Memory 23 | 24 | 25 | def test_memory(): 26 | if os.path.exists('workspace'): 27 | shutil.rmtree('workspace') 28 | 29 | llm_cfg = {'model': 'qwen-max'} 30 | mem = Memory(llm=llm_cfg) 31 | messages = [ 32 | Message('user', [ 33 | ContentItem(text='how to flip images'), 34 | ContentItem(file=str(Path(__file__).resolve().parent.parent.parent / 'examples/resource/doc.pdf')) 35 | ]) 36 | ] 37 | *_, last = mem.run(messages, max_ref_token=4000, parser_page_size=500) 38 | print(last) 39 | assert isinstance(last[-1].content, str) 40 | assert len(last[-1].content) > 0 41 | 42 | res = json5.loads(last[-1].content) 43 | assert isinstance(res, list) 44 | 45 | 46 | if __name__ == '__main__': 47 | test_memory() 48 | -------------------------------------------------------------------------------- /tests/qwen_server/test_database_server.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | import os 17 | import shutil 18 | from pathlib import Path 19 | 20 | from qwen_agent.utils.utils import get_basename_from_url, hash_sha256 21 | from qwen_server.schema import GlobalConfig 22 | from qwen_server.utils import read_meta_data_by_condition 23 | 24 | 25 | def test_database_server(): 26 | server_config_path = Path(__file__).resolve().parent.parent.parent / 'qwen_server/server_config.json' 27 | with open(server_config_path, 'r') as f: 28 | server_config = json.load(f) 29 | server_config = GlobalConfig(**server_config) 30 | if os.path.exists('workspace'): 31 | shutil.rmtree('workspace') 32 | os.makedirs(server_config.path.work_space_root) 33 | os.makedirs(server_config.path.download_root) 34 | os.makedirs(server_config.path.code_interpreter_ws) 35 | 36 | # cache 37 | from qwen_server.database_server import cache_page, update_pop_url 38 | 39 | data = { 40 | 'url': 41 | 'https://github.com/QwenLM/Qwen-Agent', 42 | 'content': 43 | '

Qwen-Agent is a framework for developing LLM applications based on the instruction following, tool usage, planning, and memory capabilities of Qwen.

' 44 | } 45 | cache_page(**data) 46 | 47 | new_url = os.path.join(server_config.path.download_root, hash_sha256(data['url']), 48 | get_basename_from_url(data['url'])) 49 | assert os.path.exists(new_url) 50 | 51 | meta_file = os.path.join(server_config.path.work_space_root, 'meta_data.jsonl') 52 | assert os.path.exists(meta_file) 53 | res = read_meta_data_by_condition(meta_file, url=new_url) 54 | assert isinstance(res, dict) 55 | assert res['url'] == new_url 56 | 57 | # pop up 58 | update_pop_url(new_url) 59 | cache_file_popup_url = os.path.join(server_config.path.work_space_root, 'popup_url.jsonl') 60 | assert os.path.exists(cache_file_popup_url) 61 | -------------------------------------------------------------------------------- /tests/tools/test_doc_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.tools import DocParser 16 | 17 | 18 | def test_doc_parser(): 19 | tool = DocParser() 20 | res = tool.call({'url': 'https://qianwen-res.oss-cn-beijing.aliyuncs.com/QWEN_TECHNICAL_REPORT.pdf'}) 21 | print(res) 22 | 23 | 24 | if __name__ == '__main__': 25 | test_doc_parser() 26 | -------------------------------------------------------------------------------- /tests/tools/test_hybrid_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.tools import HybridSearch 16 | 17 | 18 | def test_hybrid_search(): 19 | tool = HybridSearch() 20 | doc = ('主要序列转导模型基于复杂的循环或卷积神经网络,包括编码器和解码器。性能最好的模型还通过注意力机制连接编码器和解码器。' 21 | '我们提出了一种新的简单网络架构——Transformer,它完全基于注意力机制,完全不需要递归和卷积。对两个机器翻译任务的实验表明,' 22 | '这些模型在质量上非常出色,同时具有更高的并行性,并且需要的训练时间显着减少。' 23 | '我们的模型在 WMT 2014 英语到德语翻译任务中取得了 28.4 BLEU,比现有的最佳结果(包括集成)提高了 2 BLEU 以上。' 24 | '在 WMT 2014 英法翻译任务中,我们的模型在 8 个 GPU 上训练 3.5 天后,建立了新的单模型最先进 BLEU 分数 41.0,' 25 | '这只是最佳模型训练成本的一小部分文献中的模型。') 26 | res = tool.call({'query': '这个模型要训练多久?'}, docs=[doc], max_ref_token=100) 27 | print(res) 28 | 29 | res = tool.call({'query': '这个模型要训练多久?'}, docs=[doc.split('。')], max_ref_token=100) 30 | print(res) 31 | 32 | 33 | if __name__ == '__main__': 34 | test_hybrid_search() 35 | -------------------------------------------------------------------------------- /tests/tools/test_keyword_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.tools import KeywordSearch 16 | 17 | 18 | def test_keyword_search(): 19 | tool = KeywordSearch() 20 | doc = ('主要序列转导模型基于复杂的循环或卷积神经网络,包括编码器和解码器。性能最好的模型还通过注意力机制连接编码器和解码器。' 21 | '我们提出了一种新的简单网络架构——Transformer,它完全基于注意力机制,完全不需要递归和卷积。对两个机器翻译任务的实验表明,' 22 | '这些模型在质量上非常出色,同时具有更高的并行性,并且需要的训练时间显着减少。' 23 | '我们的模型在 WMT 2014 英语到德语翻译任务中取得了 28.4 BLEU,比现有的最佳结果(包括集成)提高了 2 BLEU 以上。' 24 | '在 WMT 2014 英法翻译任务中,我们的模型在 8 个 GPU 上训练 3.5 天后,建立了新的单模型最先进 BLEU 分数 41.0,' 25 | '这只是最佳模型训练成本的一小部分文献中的模型。') 26 | res = tool.call({'query': '这个模型要训练多久?'}, docs=[doc], max_ref_token=100) 27 | print(res) 28 | 29 | res = tool.call({'query': '这个模型要训练多久?'}, docs=[doc.split('。')], max_ref_token=100) 30 | print(res) 31 | 32 | 33 | if __name__ == '__main__': 34 | test_keyword_search() 35 | -------------------------------------------------------------------------------- /tests/tools/test_simple_doc_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.tools import SimpleDocParser 16 | 17 | 18 | def test_simple_doc_parser(): 19 | tool = SimpleDocParser() 20 | res = tool.call({'url': 'https://qianwen-res.oss-cn-beijing.aliyuncs.com/QWEN_TECHNICAL_REPORT.pdf'}) 21 | print(res) 22 | 23 | 24 | if __name__ == '__main__': 25 | test_simple_doc_parser() 26 | -------------------------------------------------------------------------------- /tests/tools/test_tools.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | 17 | import pytest 18 | 19 | from qwen_agent.tools import AmapWeather, CodeInterpreter, ImageGen, Retrieval, Storage 20 | 21 | 22 | # [NOTE] 不带“市”会出错 23 | @pytest.mark.parametrize('params', [json.dumps({'location': '北京市'}), {'location': '杭州市'}]) 24 | def test_amap_weather(params): 25 | tool = AmapWeather() 26 | tool.call(params) 27 | 28 | 29 | def test_code_interpreter(): 30 | tool = CodeInterpreter() 31 | tool.call("print('hello qwen')") 32 | 33 | 34 | def test_image_gen(): 35 | tool = ImageGen() 36 | tool.call({'prompt': 'a dog'}) 37 | 38 | 39 | def test_retrieval(): 40 | tool = Retrieval() 41 | tool.call({ 42 | 'query': 'Who are the authors of this paper?', 43 | 'files': ['https://qianwen-res.oss-cn-beijing.aliyuncs.com/QWEN_TECHNICAL_REPORT.pdf'] 44 | }) 45 | 46 | 47 | @pytest.mark.parametrize('operate', ['put']) 48 | def test_storage_put(operate): 49 | tool = Storage() 50 | tool.call({'operate': operate, 'key': '345/456/11', 'value': 'hello'}) 51 | 52 | tool.call({'operate': operate, 'key': '/345/456/12', 'value': 'hello'}) 53 | 54 | 55 | @pytest.mark.parametrize('operate', ['scan']) 56 | def test_storage_scan(operate): 57 | tool = Storage() 58 | tool.call({'operate': operate, 'key': '345/456/'}) 59 | 60 | tool.call({'operate': operate, 'key': '/345/456'}) 61 | 62 | 63 | @pytest.mark.parametrize('operate', ['get', 'delete']) 64 | def test_storage_get_delete(operate): 65 | tool = Storage() 66 | tool.call({'operate': operate, 'key': '345/456/11'}) 67 | 68 | tool.call({'operate': operate, 'key': '/345/456/12'}) 69 | -------------------------------------------------------------------------------- /tests/tools/test_vector_search.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The Qwen team, Alibaba Group. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from qwen_agent.tools import VectorSearch 16 | 17 | 18 | def test_vector_search(): 19 | tool = VectorSearch() 20 | doc = ('主要序列转导模型基于复杂的循环或卷积神经网络,包括编码器和解码器。性能最好的模型还通过注意力机制连接编码器和解码器。' 21 | '我们提出了一种新的简单网络架构——Transformer,它完全基于注意力机制,完全不需要递归和卷积。对两个机器翻译任务的实验表明,' 22 | '这些模型在质量上非常出色,同时具有更高的并行性,并且需要的训练时间显着减少。' 23 | '我们的模型在 WMT 2014 英语到德语翻译任务中取得了 28.4 BLEU,比现有的最佳结果(包括集成)提高了 2 BLEU 以上。' 24 | '在 WMT 2014 英法翻译任务中,我们的模型在 8 个 GPU 上训练 3.5 天后,建立了新的单模型最先进 BLEU 分数 41.0,' 25 | '这只是最佳模型训练成本的一小部分文献中的模型。') 26 | res = tool.call({'query': '这个模型要训练多久?'}, docs=[doc], max_ref_token=100) 27 | print(res) 28 | 29 | res = tool.call({'query': '这个模型要训练多久?'}, docs=[doc.split('。')], max_ref_token=100) 30 | print(res) 31 | 32 | 33 | if __name__ == '__main__': 34 | test_vector_search() 35 | --------------------------------------------------------------------------------