diff --git a/README.md b/README.md index f78f513..70ddf97 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,18 @@ WebUI(bot).run() # bot is the agent defined in the above code, we do not repeat ``` Now you can chat with the Agent in the web UI. Please refer to the [examples](https://github.com/QwenLM/Qwen-Agent/blob/main/examples) directory for more usage examples. +If you need to provide API interfaces, you can quickly launch an API server using the following code: + +```py +from qwen_agent.api import ChatApi +ChatApi(bot).run_apiserver() # bot is the agent defined in the above code, we do not repeat the definition here for saving space. +``` +Now you can chat with the Agent with api. + +``` +curl -vvv http://127.0.0.1:8080/chat -H "Content-Type: application/json" -d '{"messages": [{"role":"user","content":"介绍一下自己"}],"stream":true}' +``` + # FAQ ## Do you have function calling (aka tool calling)? diff --git a/README_CN.md b/README_CN.md index 4caf68b..653b58a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -17,10 +17,11 @@ Qwen-Agent是一个开发框架。开发者可基于本框架开发Agent应用 - 从 PyPI 安装稳定版本: ```bash -pip install -U "qwen-agent[rag,code_interpreter,python_executor,gui]" +pip install -U "qwen-agent[rag,code_interpreter,python_executor,gui,apiserver]" # 或者,使用 `pip install -U qwen-agent` 来安装最小依赖。 # 可使用双括号指定如下的可选依赖: # [gui] 用于提供基于 Gradio 的 GUI 支持; +# [apiserver] 用于提供apiserver的支持; # [rag] 用于支持 RAG; # [code_interpreter] 用于提供代码解释器相关支持; # [python_executor] 用于支持 Qwen2.5-Math 基于工具的推理。 @@ -147,6 +148,18 @@ WebUI(bot).run() # bot is the agent defined in the above code, we do not repeat 现在您可以在Web UI中和Agent对话了。更多使用示例,请参阅[examples](./examples)目录。 +如果需要提供api接口,可以使用以下代码快速启动apiserver: + +```py +from qwen_agent.api import ChatApi +ChatApi(bot).run_apiserver() # bot is the agent defined in the above code, we do not repeat the definition here for saving space. +``` +现在您可以通过api接口和Agent对话了。 + +``` +curl -vvv http://127.0.0.1:8080/chat -H "Content-Type: application/json" -d '{"messages": [{"role":"user","content":"介绍一下自己"}],"stream":true}' +``` + # FAQ ## 支持函数调用(也称为工具调用)吗? diff --git a/qwen_agent/api/__init__.py b/qwen_agent/api/__init__.py new file mode 100644 index 0000000..1f38cc0 --- /dev/null +++ b/qwen_agent/api/__init__.py @@ -0,0 +1,3 @@ +from .chat_api import ChatApi + +__all__ = ['ChatApi'] diff --git a/qwen_agent/api/api_server.py b/qwen_agent/api/api_server.py new file mode 100644 index 0000000..4472433 --- /dev/null +++ b/qwen_agent/api/api_server.py @@ -0,0 +1,33 @@ + +import json +from flask import Flask, Response, request, jsonify + + +app = Flask("apiserver") + + +def event_stream(items): + for item in items: + msg = json.dumps(item, ensure_ascii=False) + yield f'data: {msg}\n\n' + yield f'data: [DONE]\n\n' + + +@app.route("/chat", methods=['POST']) +def chat(): + if not request.is_json: + return jsonify({"status": 400}), 400 + data = request.json + if 'messages' not in data: + return jsonify({"status": 400}), 400 + messages = data['messages'] + stream = data.get("stream", False) + if stream: + return Response(event_stream(app.chat.chat(messages, stream)), mimetype='text/event-stream') + else: + return jsonify({"messages": list(app.chat.chat(messages, stream))}), 200 + + +def start_apiserver(chat, server_host='0.0.0.0', server_port=8080): + app.chat = chat + app.run(server_host, server_port) diff --git a/qwen_agent/api/chat_api.py b/qwen_agent/api/chat_api.py new file mode 100644 index 0000000..d4e5894 --- /dev/null +++ b/qwen_agent/api/chat_api.py @@ -0,0 +1,71 @@ + + +class ChatApi: + + def __init__(self, agent): + self.agent = agent + + def run_webui(self, chatbot_config=None, server_name='0.0.0.0', server_port=7860): + from qwen_agent.gui import WebUI + WebUI( + self.agent, + chatbot_config=chatbot_config, + ).run(server_name=server_name, server_port=server_port) + + def run_apiserver(self, server_name='0.0.0.0', server_port=8080): + from .api_server import start_apiserver + start_apiserver(self, server_name, server_port) + + def add_type(self, item): + if 'function_call' in item: + item['type'] = 'function_call' + elif item['role'] == 'function': + item['type'] = 'function_call_output' + elif 'chunk' in item: + item['type'] = 'chunk' + else: + item['type'] = 'message' + return item + + def gen_stream(self, response, add_full_msg=False): + last = [] + last_msg = "" + for rsp in response: + now = rsp[-1] + now_is_msg = 'function_call' not in now and now['role'] != 'function' + is_new_line = len(last) != len(rsp) + if is_new_line and last: + res = self.add_type(last[-1]) + last_msg = '' + if add_full_msg or res['type'] != 'message': + yield res + if now_is_msg: + msg = now['content'] + assert msg.startswith(last_msg) + stream_msg = msg[len(last_msg):] + yield self.add_type({'role': now['role'], 'content': '', 'chunk': stream_msg}) + last_msg = msg + last = rsp + + if last: + res = self.add_type(last[-1]) + last_msg = '' + if add_full_msg or res['type'] != 'message': + yield res + + def gen(self, response): + last = None + for rsp in response: + if last is not None: + if len(last) != len(rsp): + yield last[-1] + last = rsp + if last: + yield last[-1] + + def chat(self, messages, stream=True): + response = self.agent.run(messages) + if stream: + return self.gen_stream(response) + else: + return self.gen(response) diff --git a/setup.py b/setup.py index 5dae7fe..1e245d8 100644 --- a/setup.py +++ b/setup.py @@ -103,6 +103,9 @@ def read_description() -> str: 'gradio-client==1.4.0', 'modelscope_studio==1.0.0-beta.8', ], + 'apiserver': [ + 'flask>=3' + ], }, url='https://github.com/QwenLM/Qwen-Agent', )