bing-chat-api / apis /chat_api.py
Hansimov's picture
:gem: [Feature] ChatAPIApp: New /readme route
a178fd6
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