Spaces:
Paused
Paused
| import argparse | |
| import markdown2 | |
| import sys | |
| import uvicorn | |
| from pathlib import Path | |
| from fastapi import FastAPI | |
| from fastapi.responses import HTMLResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel, Field | |
| from sse_starlette.sse import EventSourceResponse | |
| from conversations import ( | |
| ConversationConnector, | |
| ConversationCreator, | |
| MessageComposer, | |
| ) | |
| from utils.logger import logger | |
| class ChatAPIApp: | |
| def __init__(self): | |
| self.app = FastAPI( | |
| docs_url="/", | |
| title="Bing Chat API", | |
| swagger_ui_parameters={"defaultModelsExpandDepth": -1}, | |
| version="1.0", | |
| ) | |
| self.setup_routes() | |
| self.app.mount("/docs", StaticFiles(directory="docs", html=True), name="docs") | |
| def get_available_models(self): | |
| self.available_models = { | |
| "object": "list", | |
| "data": [ | |
| { | |
| "id": "precise", | |
| "description": "Bing (Precise): Concise and straightforward.", | |
| "object": "model", | |
| "created": 1700000000, | |
| "owned_by": "bing", | |
| }, | |
| { | |
| "id": "balanced", | |
| "description": "Bing (Balanced): Informative and friendly.", | |
| "object": "model", | |
| "created": 1700000000, | |
| "owned_by": "bing", | |
| }, | |
| { | |
| "id": "creative", | |
| "description": "Bing (Creative): Original and imaginative.", | |
| "object": "model", | |
| "created": 1700000000, | |
| "owned_by": "bing", | |
| }, | |
| { | |
| "id": "precise-offline", | |
| "description": "Bing (Precise): (No Internet) Concise and straightforward.", | |
| "object": "model", | |
| "created": 1700000000, | |
| "owned_by": "bing", | |
| }, | |
| { | |
| "id": "balanced-offline", | |
| "description": "Bing (Balanced): (No Internet) Informative and friendly.", | |
| "object": "model", | |
| "created": 1700000000, | |
| "owned_by": "bing", | |
| }, | |
| { | |
| "id": "creative-offline", | |
| "description": "Bing (Creative): (No Internet) Original and imaginative.", | |
| "object": "model", | |
| "created": 1700000000, | |
| "owned_by": "bing", | |
| }, | |
| ], | |
| } | |
| return self.available_models | |
| class CreateConversationSessionPostItem(BaseModel): | |
| model: str = Field( | |
| default="precise", | |
| description="(str) `precise`, `balanced`, `creative`, `precise-offline`, `balanced-offline`, `creative-offline`", | |
| ) | |
| def create_conversation_session(self, item: CreateConversationSessionPostItem): | |
| creator = ConversationCreator() | |
| creator.create() | |
| return { | |
| "model": item.model, | |
| "sec_access_token": creator.sec_access_token, | |
| "client_id": creator.client_id, | |
| "conversation_id": creator.conversation_id, | |
| } | |
| class ChatCompletionsPostItem(BaseModel): | |
| model: str = Field( | |
| default="precise", | |
| description="(str) `precise`, `balanced`, `creative`, `precise-offline`, `balanced-offline`, `creative-offline`", | |
| ) | |
| messages: list = Field( | |
| default=[{"role": "user", "content": "Hello, who are you?"}], | |
| description="(list) Messages", | |
| ) | |
| def chat_completions(self, item: ChatCompletionsPostItem): | |
| creator = ConversationCreator() | |
| creator.create() | |
| connector = ConversationConnector( | |
| conversation_style=item.model, | |
| sec_access_token=creator.sec_access_token, | |
| client_id=creator.client_id, | |
| conversation_id=creator.conversation_id, | |
| cookies=creator.request_cookies, | |
| invocation_id=0, | |
| ) | |
| message_composer = MessageComposer() | |
| prompt = message_composer.merge(item.messages) | |
| logger.mesg(item.messages[-1]["content"]) | |
| system_prompt = message_composer.system_prompt | |
| return EventSourceResponse( | |
| connector.stream_chat( | |
| prompt=prompt, system_prompt=system_prompt, yield_output=True | |
| ), | |
| ping=2000, | |
| media_type="text/event-stream", | |
| ) | |
| def get_readme(self): | |
| readme_path = Path(__file__).parents[1] / "README.md" | |
| with open(readme_path, "r", encoding="utf-8") as rf: | |
| readme_str = rf.read() | |
| readme_html = markdown2.markdown( | |
| readme_str, extras=["table", "fenced-code-blocks", "highlightjs-lang"] | |
| ) | |
| return readme_html | |
| def setup_routes(self): | |
| for prefix in ["", "/v1", "/api", "/api/v1"]: | |
| include_in_schema = True if prefix == "" else False | |
| self.app.get( | |
| prefix + "/models", | |
| summary="Get available models", | |
| include_in_schema=include_in_schema, | |
| )(self.get_available_models) | |
| self.app.post( | |
| prefix + "/create", | |
| summary="Create a conversation session", | |
| include_in_schema=include_in_schema, | |
| )(self.create_conversation_session) | |
| self.app.post( | |
| prefix + "/chat/completions", | |
| summary="Chat completions in conversation session", | |
| include_in_schema=include_in_schema, | |
| )(self.chat_completions) | |
| self.app.get( | |
| "/readme", | |
| summary="README of Bing Chat API", | |
| response_class=HTMLResponse, | |
| include_in_schema=False, | |
| )(self.get_readme) | |
| class ArgParser(argparse.ArgumentParser): | |
| def __init__(self, *args, **kwargs): | |
| super(ArgParser, self).__init__(*args, **kwargs) | |
| self.add_argument( | |
| "-s", | |
| "--server", | |
| type=str, | |
| default="0.0.0.0", | |
| help="Server IP for Bing Chat API", | |
| ) | |
| self.add_argument( | |
| "-p", | |
| "--port", | |
| type=int, | |
| default=22222, | |
| help="Server Port for Bing Chat API", | |
| ) | |
| self.add_argument( | |
| "-d", | |
| "--dev", | |
| default=False, | |
| action="store_true", | |
| help="Run in dev mode", | |
| ) | |
| self.args = self.parse_args(sys.argv[1:]) | |
| app = ChatAPIApp().app | |
| if __name__ == "__main__": | |
| args = ArgParser().args | |
| if args.dev: | |
| uvicorn.run("__main__:app", host=args.server, port=args.port, reload=True) | |
| else: | |
| uvicorn.run("__main__:app", host=args.server, port=args.port, reload=False) | |
| # python -m apis.chat_api # [Docker] on product mode | |
| # python -m apis.chat_api -d # [Dev] on develop mode | |