From 899b49ddc6cac3456b7ad4b4471c1952397b159e Mon Sep 17 00:00:00 2001 From: zstar <65890619+zstar1003@users.noreply.github.com> Date: Sat, 17 May 2025 15:13:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5#6041=20Fix:=20chat=5Fcomplet?= =?UTF-8?q?ion=20answer=20data=20incorrect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/apps/sdk/session.py | 141 +++++++++++++++------------------------- 1 file changed, 54 insertions(+), 87 deletions(-) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index d29666a..5fb41a7 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -37,7 +37,8 @@ from api.db.services.file_service import FileService from flask import jsonify, request, Response -@manager.route('/chats//sessions', methods=['POST']) # noqa: F821 + +@manager.route("/chats//sessions", methods=["POST"]) # noqa: F821 @token_required def create(tenant_id, chat_id): req = request.json @@ -50,7 +51,7 @@ def create(tenant_id, chat_id): "dialog_id": req["dialog_id"], "name": req.get("name", "New session"), "message": [{"role": "assistant", "content": dia[0].prompt_config.get("prologue")}], - "user_id": req.get("user_id", "") + "user_id": req.get("user_id", ""), } if not conv.get("name"): return get_error_data_result(message="`name` can not be empty.") @@ -59,20 +60,20 @@ def create(tenant_id, chat_id): if not e: return get_error_data_result(message="Fail to create a session!") conv = conv.to_dict() - conv['messages'] = conv.pop("message") + conv["messages"] = conv.pop("message") conv["chat_id"] = conv.pop("dialog_id") del conv["reference"] return get_result(data=conv) -@manager.route('/agents//sessions', methods=['POST']) # noqa: F821 +@manager.route("/agents//sessions", methods=["POST"]) # noqa: F821 @token_required def create_agent_session(tenant_id, agent_id): req = request.json if not request.is_json: req = request.form files = request.files - user_id = request.args.get('user_id', '') + user_id = request.args.get("user_id", "") e, cvs = UserCanvasService.get_by_id(agent_id) if not e: @@ -113,7 +114,7 @@ def create_agent_session(tenant_id, agent_id): ele.pop("value") else: if req is not None and req.get(ele["key"]): - ele["value"] = req[ele['key']] + ele["value"] = req[ele["key"]] else: if "value" in ele: ele.pop("value") @@ -121,20 +122,13 @@ def create_agent_session(tenant_id, agent_id): for ans in canvas.run(stream=False): pass cvs.dsl = json.loads(str(canvas)) - conv = { - "id": get_uuid(), - "dialog_id": cvs.id, - "user_id": user_id, - "message": [{"role": "assistant", "content": canvas.get_prologue()}], - "source": "agent", - "dsl": cvs.dsl - } + conv = {"id": get_uuid(), "dialog_id": cvs.id, "user_id": user_id, "message": [{"role": "assistant", "content": canvas.get_prologue()}], "source": "agent", "dsl": cvs.dsl} API4ConversationService.save(**conv) conv["agent_id"] = conv.pop("dialog_id") return get_result(data=conv) -@manager.route('/chats//sessions/', methods=['PUT']) # noqa: F821 +@manager.route("/chats//sessions/", methods=["PUT"]) # noqa: F821 @token_required def update(tenant_id, chat_id, session_id): req = request.json @@ -156,14 +150,14 @@ def update(tenant_id, chat_id, session_id): return get_result() -@manager.route('/chats//completions', methods=['POST']) # noqa: F821 +@manager.route("/chats//completions", methods=["POST"]) # noqa: F821 @token_required def chat_completion(tenant_id, chat_id): req = request.json if not req: req = {"question": ""} if not req.get("session_id"): - req["question"]="" + req["question"] = "" if not DialogService.query(tenant_id=tenant_id, id=chat_id, status=StatusEnum.VALID.value): return get_error_data_result(f"You don't own the chat {chat_id}") if req.get("session_id"): @@ -185,7 +179,7 @@ def chat_completion(tenant_id, chat_id): return get_result(data=answer) -@manager.route('chats_openai//chat/completions', methods=['POST']) # noqa: F821 +@manager.route("chats_openai//chat/completions", methods=["POST"]) # noqa: F821 @validate_request("model", "messages") # noqa: F821 @token_required def chat_completion_openai_like(tenant_id, chat_id): @@ -259,39 +253,23 @@ def chat_completion_openai_like(tenant_id, chat_id): # The choices field on the last chunk will always be an empty array []. def streamed_response_generator(chat_id, dia, msg): token_used = 0 - should_split_index = 0 + answer_cache = "" response = { "id": f"chatcmpl-{chat_id}", - "choices": [ - { - "delta": { - "content": "", - "role": "assistant", - "function_call": None, - "tool_calls": None - }, - "finish_reason": None, - "index": 0, - "logprobs": None - } - ], + "choices": [{"delta": {"content": "", "role": "assistant", "function_call": None, "tool_calls": None}, "finish_reason": None, "index": 0, "logprobs": None}], "created": int(time.time()), "model": "model", "object": "chat.completion.chunk", "system_fingerprint": "", - "usage": None + "usage": None, } try: for ans in chat(dia, msg, True): answer = ans["answer"] - incremental = answer[should_split_index:] + incremental = answer.replace(answer_cache, "", 1) + answer_cache = answer.rstrip("") token_used += len(incremental) - if incremental.endswith(""): - response_data_len = len(incremental.rstrip("")) - else: - response_data_len = len(incremental) - should_split_index += response_data_len response["choices"][0]["delta"]["content"] = incremental yield f"data:{json.dumps(response, ensure_ascii=False)}\n\n" except Exception as e: @@ -301,15 +279,10 @@ def chat_completion_openai_like(tenant_id, chat_id): # The last chunk response["choices"][0]["delta"]["content"] = None response["choices"][0]["finish_reason"] = "stop" - response["usage"] = { - "prompt_tokens": len(prompt), - "completion_tokens": token_used, - "total_tokens": len(prompt) + token_used - } + response["usage"] = {"prompt_tokens": len(prompt), "completion_tokens": token_used, "total_tokens": len(prompt) + token_used} yield f"data:{json.dumps(response, ensure_ascii=False)}\n\n" yield "data:[DONE]\n\n" - resp = Response(streamed_response_generator(chat_id, dia, msg), mimetype="text/event-stream") resp.headers.add_header("Cache-control", "no-cache") resp.headers.add_header("Connection", "keep-alive") @@ -324,7 +297,7 @@ def chat_completion_openai_like(tenant_id, chat_id): break content = answer["answer"] - response = { + response = { "id": f"chatcmpl-{chat_id}", "object": "chat.completion", "created": int(time.time()), @@ -336,25 +309,15 @@ def chat_completion_openai_like(tenant_id, chat_id): "completion_tokens_details": { "reasoning_tokens": context_token_used, "accepted_prediction_tokens": len(content), - "rejected_prediction_tokens": 0 # 0 for simplicity - } + "rejected_prediction_tokens": 0, # 0 for simplicity + }, }, - "choices": [ - { - "message": { - "role": "assistant", - "content": content - }, - "logprobs": None, - "finish_reason": "stop", - "index": 0 - } - ] + "choices": [{"message": {"role": "assistant", "content": content}, "logprobs": None, "finish_reason": "stop", "index": 0}], } return jsonify(response) -@manager.route('/agents//completions', methods=['POST']) # noqa: F821 +@manager.route("/agents//completions", methods=["POST"]) # noqa: F821 @token_required def agent_completions(tenant_id, agent_id): req = request.json @@ -365,8 +328,8 @@ def agent_completions(tenant_id, agent_id): dsl = cvs[0].dsl if not isinstance(dsl, str): dsl = json.dumps(dsl) - #canvas = Canvas(dsl, tenant_id) - #if canvas.get_preset_param(): + # canvas = Canvas(dsl, tenant_id) + # if canvas.get_preset_param(): # req["question"] = "" conv = API4ConversationService.query(id=req["session_id"], dialog_id=agent_id) if not conv: @@ -380,9 +343,7 @@ def agent_completions(tenant_id, agent_id): states = {field: current_dsl.get(field, []) for field in state_fields} current_dsl.update(new_dsl) current_dsl.update(states) - API4ConversationService.update_by_id(req["session_id"], { - "dsl": current_dsl - }) + API4ConversationService.update_by_id(req["session_id"], {"dsl": current_dsl}) else: req["question"] = "" if req.get("stream", True): @@ -399,7 +360,7 @@ def agent_completions(tenant_id, agent_id): return get_error_data_result(str(e)) -@manager.route('/chats//sessions', methods=['GET']) # noqa: F821 +@manager.route("/chats//sessions", methods=["GET"]) # noqa: F821 @token_required def list_session(tenant_id, chat_id): if not DialogService.query(tenant_id=tenant_id, id=chat_id, status=StatusEnum.VALID.value): @@ -418,7 +379,7 @@ def list_session(tenant_id, chat_id): if not convs: return get_result(data=[]) for conv in convs: - conv['messages'] = conv.pop("message") + conv["messages"] = conv.pop("message") infos = conv["messages"] for info in infos: if "prompt" in info: @@ -452,7 +413,7 @@ def list_session(tenant_id, chat_id): return get_result(data=convs) -@manager.route('/agents//sessions', methods=['GET']) # noqa: F821 +@manager.route("/agents//sessions", methods=["GET"]) # noqa: F821 @token_required def list_agent_session(tenant_id, agent_id): if not UserCanvasService.query(user_id=tenant_id, id=agent_id): @@ -468,12 +429,11 @@ def list_agent_session(tenant_id, agent_id): desc = True # dsl defaults to True in all cases except for False and false include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false" - convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, - user_id, include_dsl) + convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, user_id, include_dsl) if not convs: return get_result(data=[]) for conv in convs: - conv['messages'] = conv.pop("message") + conv["messages"] = conv.pop("message") infos = conv["messages"] for info in infos: if "prompt" in info: @@ -506,7 +466,7 @@ def list_agent_session(tenant_id, agent_id): return get_result(data=convs) -@manager.route('/chats//sessions', methods=["DELETE"]) # noqa: F821 +@manager.route("/chats//sessions", methods=["DELETE"]) # noqa: F821 @token_required def delete(tenant_id, chat_id): if not DialogService.query(id=chat_id, tenant_id=tenant_id, status=StatusEnum.VALID.value): @@ -532,14 +492,14 @@ def delete(tenant_id, chat_id): return get_result() -@manager.route('/agents//sessions', methods=["DELETE"]) # noqa: F821 +@manager.route("/agents//sessions", methods=["DELETE"]) # noqa: F821 @token_required def delete_agent_session(tenant_id, agent_id): req = request.json cvs = UserCanvasService.query(user_id=tenant_id, id=agent_id) if not cvs: return get_error_data_result(f"You don't own the agent {agent_id}") - + convs = API4ConversationService.query(dialog_id=agent_id) if not convs: return get_error_data_result(f"Agent {agent_id} has no sessions") @@ -555,16 +515,16 @@ def delete_agent_session(tenant_id, agent_id): conv_list.append(conv.id) else: conv_list = ids - + for session_id in conv_list: conv = API4ConversationService.query(id=session_id, dialog_id=agent_id) if not conv: return get_error_data_result(f"The agent doesn't own the session ${session_id}") API4ConversationService.delete_by_id(session_id) return get_result() - -@manager.route('/sessions/ask', methods=['POST']) # noqa: F821 + +@manager.route("/sessions/ask", methods=["POST"]) # noqa: F821 @token_required def ask_about(tenant_id): req = request.json @@ -590,9 +550,7 @@ def ask_about(tenant_id): for ans in ask(req["question"], req["kb_ids"], uid): yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n" except Exception as e: - yield "data:" + json.dumps({"code": 500, "message": str(e), - "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, - ensure_ascii=False) + "\n\n" + yield "data:" + json.dumps({"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, ensure_ascii=False) + "\n\n" yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" resp = Response(stream(), mimetype="text/event-stream") @@ -603,7 +561,7 @@ def ask_about(tenant_id): return resp -@manager.route('/sessions/related_questions', methods=['POST']) # noqa: F821 +@manager.route("/sessions/related_questions", methods=["POST"]) # noqa: F821 @token_required def related_questions(tenant_id): req = request.json @@ -635,18 +593,27 @@ Reason: - At the same time, related terms can also help search engines better understand user needs and return more accurate search results. """ - ans = chat_mdl.chat(prompt, [{"role": "user", "content": f""" + ans = chat_mdl.chat( + prompt, + [ + { + "role": "user", + "content": f""" Keywords: {question} Related search terms: - """}], {"temperature": 0.9}) + """, + } + ], + {"temperature": 0.9}, + ) return get_result(data=[re.sub(r"^[0-9]\. ", "", a) for a in ans.split("\n") if re.match(r"^[0-9]\. ", a)]) -@manager.route('/chatbots//completions', methods=['POST']) # noqa: F821 +@manager.route("/chatbots//completions", methods=["POST"]) # noqa: F821 def chatbot_completions(dialog_id): req = request.json - token = request.headers.get('Authorization').split() + token = request.headers.get("Authorization").split() if len(token) != 2: return get_error_data_result(message='Authorization is not valid!"') token = token[1] @@ -669,11 +636,11 @@ def chatbot_completions(dialog_id): return get_result(data=answer) -@manager.route('/agentbots//completions', methods=['POST']) # noqa: F821 +@manager.route("/agentbots//completions", methods=["POST"]) # noqa: F821 def agent_bot_completions(agent_id): req = request.json - token = request.headers.get('Authorization').split() + token = request.headers.get("Authorization").split() if len(token) != 2: return get_error_data_result(message='Authorization is not valid!"') token = token[1]