From 5b7efceee8c1d83bfaf0cb51849d13280ab4db66 Mon Sep 17 00:00:00 2001 From: jinzhenxiao Date: Fri, 12 Apr 2024 14:20:58 +0800 Subject: [PATCH 1/3] adjust some error in chat_response. --- .gitignore | 1 + app.py | 2 +- chatgpt/ChatService.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4c49bd7..f08d9d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +/.idea/ diff --git a/app.py b/app.py index 71d093d..c0d4912 100644 --- a/app.py +++ b/app.py @@ -39,4 +39,4 @@ async def send_conversation(request: Request, token=Depends(verify_token)): if __name__ == "__main__": import uvicorn - uvicorn.run("app:app", host="0.0.0.0", port=5005) + uvicorn.run(app, host="0.0.0.0", port=5005) diff --git a/chatgpt/ChatService.py b/chatgpt/ChatService.py index 06c53a3..ac960a6 100644 --- a/chatgpt/ChatService.py +++ b/chatgpt/ChatService.py @@ -81,7 +81,7 @@ async def stream_response(response, model, max_tokens): async def chat_response(resp, model, prompt_tokens, max_tokens): last_resp = None for i in reversed(resp): - if i != "data: [DONE]" and i.startswith("data: "): + if i != "data: [DONE]" and i.startswith("data: ") and '"message":' in i: try: last_resp = json.loads(i[6:]) break From 209dbc74ddb616c248058433e58ed378ba133de8 Mon Sep 17 00:00:00 2001 From: jinzhenxiao Date: Fri, 12 Apr 2024 16:16:39 +0800 Subject: [PATCH 2/3] support no authenticated mode. --- app.py | 40 +++++++++++++++++++++++++++------------- utils/authorization.py | 8 +++++--- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app.py b/app.py index c0d4912..7444c87 100644 --- a/app.py +++ b/app.py @@ -1,8 +1,5 @@ -from typing import AsyncGenerator - from fastapi import FastAPI, Request, Depends from fastapi.responses import StreamingResponse, JSONResponse - from chatgpt.ChatService import ChatService from utils.authorization import verify_token from utils.retry import async_retry @@ -10,33 +7,50 @@ from utils.retry import async_retry app = FastAPI() +async def to_send_conversation(request_data, access_token): + """ + 对话前先决条件准备 + :param request_data: 请求数据 + :param access_token: 访问令牌 + :return: chat_service + """ + chat_service = ChatService(request_data, access_token) + await chat_service.get_chat_requirements() + return chat_service + + @app.post("/v1/chat/completions") async def send_conversation(request: Request, token=Depends(verify_token)): + """ + 发送对话 + :param request: 请求入参 + :param token: 访问令牌 + :return: 对话结果 + """ access_token = None - if not token: - return JSONResponse({"error": "Not authenticated"}, status_code=401) - elif token.startswith("ey"): + if token and token.startswith("ey"): access_token = token + + # 入参格式校验 try: request_data = await request.json() except Exception: return JSONResponse({"error": "Invalid JSON body"}, status_code=400) - async def to_send_conversation(request_data): - chat_service = ChatService(request_data, access_token) - await chat_service.get_chat_requirements() - return chat_service - - chat_service = await async_retry(to_send_conversation, request_data) + # 对话前先决条件准备 + chat_service = await async_retry(to_send_conversation, request_data, access_token) chat_service.prepare_send_conversation() + + # 发送对话 stream = request_data.get("stream", False) if stream: + # 流式响应 return StreamingResponse(await chat_service.send_conversation_for_stream(), media_type="text/event-stream") else: + # 非流式响应 return JSONResponse(await chat_service.send_conversation(), media_type="application/json") if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=5005) diff --git a/utils/authorization.py b/utils/authorization.py index eb0eb0a..da447d4 100644 --- a/utils/authorization.py +++ b/utils/authorization.py @@ -1,15 +1,17 @@ -from fastapi import Depends +from fastapi import Depends, HTTPException from fastapi.security import OAuth2PasswordBearer from utils.config import authorization_list -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False) def verify_token(token: str = Depends(oauth2_scheme)): + if not token: + return None if not authorization_list or token in authorization_list: return token elif token.startswith("eyJhbGciOi"): return token else: - return False + raise HTTPException(status_code=401, detail="Not authenticated") From 510d0c2e30eb5308382f604b20bf5a061b39fa76 Mon Sep 17 00:00:00 2001 From: jinzhenxiao Date: Fri, 12 Apr 2024 17:03:47 +0800 Subject: [PATCH 3/3] support new conversation join history requested. --- chatgpt/ChatService.py | 67 ++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/chatgpt/ChatService.py b/chatgpt/ChatService.py index ac960a6..43b5678 100644 --- a/chatgpt/ChatService.py +++ b/chatgpt/ChatService.py @@ -73,22 +73,58 @@ async def stream_response(response, model, max_tokens): } completion_tokens += 1 yield f"data: {json.dumps(chunk_new_data)}\n\n" - except Exception as e: + except Exception: Logger.error(f"Error: {chunk}") continue async def chat_response(resp, model, prompt_tokens, max_tokens): + """ + 组装对话响应 + :param resp: 响应数据 + :param model: 模型 + :param prompt_tokens: prompt token数 + :param max_tokens: 最大token数量 + :return: + """ last_resp = None for i in reversed(resp): if i != "data: [DONE]" and i.startswith("data: ") and '"message":' in i: try: last_resp = json.loads(i[6:]) break - except Exception as e: + except Exception: Logger.error(f"Error: {i}") continue + usage, system_fingerprint, finish_reason, message = await init_param(last_resp, max_tokens, model, prompt_tokens) + return { + "id": f"chatcmpl-{''.join(random.choice(string.ascii_letters + string.digits) for _ in range(29))}", + "object": "chat.completion", + "created": int(time.time()), + "model": model, + "choices": [ + { + "index": 0, + "message": message, + "logprobs": None, + "finish_reason": finish_reason + } + ], + "usage": usage, + "system_fingerprint": system_fingerprint, + "conversation_id": last_resp["conversation_id"] + } + +async def init_param(last_resp, max_tokens, model, prompt_tokens): + """ + 组装参数 + :param last_resp: 最终响应 + :param max_tokens: 最大token数量 + :param model: 模型 + :param prompt_tokens: prompt token数 + :return: 对应参数 + """ if last_resp.get("type") == "moderation": message_content = moderation_message completion_tokens = 53 @@ -96,16 +132,10 @@ async def chat_response(resp, model, prompt_tokens, max_tokens): else: message_content = last_resp["message"]["content"]["parts"][0] message_content, completion_tokens, finish_reason = split_tokens_from_content(message_content, max_tokens, model) - chat_id = f"chatcmpl-{''.join(random.choice(string.ascii_letters + string.digits) for _ in range(29))}" - chat_object = "chat.completion" - created_time = int(time.time()) - index = 0 message = { "role": "assistant", "content": message_content, - } - logprobs = None usage = { "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, @@ -113,23 +143,7 @@ async def chat_response(resp, model, prompt_tokens, max_tokens): } system_fingerprint_list = model_system_fingerprint.get(model, None) system_fingerprint = random.choice(system_fingerprint_list) if system_fingerprint_list else None - chat_response_json = { - "id": chat_id, - "object": chat_object, - "created": created_time, - "model": model, - "choices": [ - { - "index": index, - "message": message, - "logprobs": logprobs, - "finish_reason": finish_reason - } - ], - "usage": usage, - "system_fingerprint": system_fingerprint - } - return chat_response_json + return usage, system_fingerprint, finish_reason, message def api_messages_to_chat(api_messages): @@ -157,6 +171,7 @@ class ChatService: self.chat_token = None self.data = data + self.conversation_id = self.data.get("conversation_id", None) self.model = self.data.get("model", "gpt-3.5-turbo-0125") self.api_messages = self.data.get("messages", []) self.prompt_tokens = num_tokens_from_messages(self.api_messages, self.model) @@ -247,6 +262,8 @@ class ChatService: "force_nulligen": False, "force_rate_limit": False, } + if self.conversation_id: + self.chat_request["conversation_id"] = self.conversation_id return self.chat_request async def send_conversation_for_stream(self):