Skip to content

Commit

Permalink
refactor python folder
Browse files Browse the repository at this point in the history
  • Loading branch information
dreamhunter2333 committed Apr 28, 2024
1 parent 181be93 commit 7acaa09
Show file tree
Hide file tree
Showing 20 changed files with 190 additions and 158 deletions.
29 changes: 0 additions & 29 deletions config.py

This file was deleted.

4 changes: 2 additions & 2 deletions frontend/src/views/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ const onSubmit = async () => {
async onopen(response) {
if (response.ok && response.headers.get('content-type') === EventStreamContentType) {
return;
} else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
throw new Error(`占卜失败: ${response.status}`);
} else if (response.status >= 400 && response.status < 500) {
throw new Error(`${response.status} ${await response.text()}`);
}
},
onmessage(msg) {
Expand Down
61 changes: 7 additions & 54 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,17 @@
import os
import logging
import uvicorn

from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import PlainTextResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from src.app import app
from src.config import settings

from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware

from src.limiter import limiter, get_real_ipaddr
from src.chatgpt_router import router as chatgpt_router
from src.divination_router import router as divination_router
from src.user_router import router as user_router


_logger = logging.getLogger(__name__)

app = FastAPI(title="Chatgpt Divination API")
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
app.add_middleware(SlowAPIMiddleware)

app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173",
"http://localhost",
"http://127.0.0.1"
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
logging.basicConfig(
format="%(asctime)s: %(levelname)s: %(name)s: %(message)s",
level=logging.INFO
)
_logger = logging.getLogger(__name__)

app.include_router(chatgpt_router)
app.include_router(divination_router)
app.include_router(user_router)

if os.path.exists("dist"):
@app.get("/")
@app.get("/login/{path}")
async def read_index(request: Request):
_logger.info(f"Request from {get_real_ipaddr(request)}")
return FileResponse(
"dist/index.html",
headers={"Cache-Control": "no-cache"}
)

app.mount("/", StaticFiles(directory="dist"), name="static")

_logger.info(f"settings: {settings.model_dump_json(indent=2)}")

@app.exception_handler(Exception)
async def exception_handler(request: Request, exc: Exception):
return PlainTextResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=f"Internal Server Error: {exc}",
)

if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
14 changes: 7 additions & 7 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
fastapi==0.95.0
python-dotenv==1.0.0
uvicorn==0.21.1
openai==0.27.4
python-multipart==0.0.6
slowapi==0.1.8
pyjwt==2.7.0
fastapi==0.110.2
pydantic-settings==2.2.1
pydantic==2.7.1
uvicorn==0.29.0
openai==1.23.6
pyjwt==2.8.0
requests==2.31.0
52 changes: 52 additions & 0 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import os
import logging

from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import PlainTextResponse, FileResponse
from fastapi.staticfiles import StaticFiles

from src.limiter import get_real_ipaddr
from src.chatgpt_router import router as chatgpt_router
from src.divination_router import router as divination_router
from src.user_router import router as user_router


_logger = logging.getLogger(__name__)

app = FastAPI(title="Chatgpt Divination API")
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173",
"http://localhost",
"http://127.0.0.1"
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

app.include_router(chatgpt_router)
app.include_router(divination_router)
app.include_router(user_router)

if os.path.exists("dist"):
@app.get("/")
@app.get("/login/{path}")
async def read_index(request: Request):
_logger.info(f"Request from {get_real_ipaddr(request)}")
return FileResponse(
"dist/index.html",
headers={"Cache-Control": "no-cache"}
)

app.mount("/", StaticFiles(directory="dist"), name="static")


@app.exception_handler(Exception)
async def exception_handler(request: Request, exc: Exception):
return PlainTextResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=f"Internal Server Error: {exc}",
)
56 changes: 24 additions & 32 deletions src/chatgpt_router.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import json
from typing import Optional
from fastapi.responses import StreamingResponse
import openai
from openai import OpenAI

import logging

from fastapi import Depends, HTTPException, Request, status


from config import settings
from src.config import settings
from fastapi import APIRouter

from models import DivinationBody, User
from src.models import DivinationBody, User
from src.user import get_user
from .limiter import get_real_ipaddr, limiter
from .divination import DivinationFactory
from src.limiter import get_real_ipaddr, check_rate_limit
from src.divination import DivinationFactory

openai.api_key = settings.api_key
openai.api_base = settings.api_base
client = OpenAI(api_key=settings.api_key, base_url=settings.api_base)
router = APIRouter()
_logger = logging.getLogger(__name__)
STOP_WORDS = [
Expand All @@ -26,40 +26,28 @@
]


@limiter.limit(settings.rate_limit)
def limit_when_not_login(request: Request):
"""
Limit when not login
"""


def limit_when_login(request: Request, user: User):
"""
Limit when login
"""
@limiter.limit(settings.user_rate_limit, key_func=lambda: (user.user_name, user.login_type))
def limit(request: Request):
"""
Limit when login
"""
limit(request)


@router.post("/api/divination")
async def divination(
request: Request,
divination_body: DivinationBody,
user: Optional[User] = Depends(get_user)
):

real_ip = get_real_ipaddr(request)
# rate limit when not login
if not user:
limit_when_not_login(request)
max_reqs, time_window_seconds = settings.rate_limit
check_rate_limit(real_ip, time_window_seconds, max_reqs)
else:
limit_when_login(request, user)
max_reqs, time_window_seconds = settings.user_rate_limit
check_rate_limit(
f"{user.login_type}:{user.user_name}", time_window_seconds, max_reqs
)

_logger.info(
f"Request from {get_real_ipaddr(request)}, user={user.json(ensure_ascii=False) if user else None} body={divination_body.json(ensure_ascii=False)}"
f"Request from {real_ip}, "
f"user={user.model_dump_json(context=dict(ensure_ascii=False)) if user else None}, "
f"body={divination_body.model_dump_json(context=dict(ensure_ascii=False))}"
)
if any(w in divination_body.prompt.lower() for w in STOP_WORDS):
raise HTTPException(
Expand All @@ -75,7 +63,11 @@ async def divination(
prompt, system_prompt = divination_obj.build_prompt(divination_body)

def get_openai_generator():
openai_stream = openai.ChatCompletion.create(
for i in range(100):
contet = f'{i}'
yield f"data: {json.dumps(contet)}\n\n"
return
openai_stream = client.chat.completions.create(
model=settings.model,
max_tokens=1000,
temperature=0.9,
Expand All @@ -90,8 +82,8 @@ def get_openai_generator():
]
)
for event in openai_stream:
if "content" in event["choices"][0].delta:
current_response = event["choices"][0].delta.content
if event.choices and event.choices[0].delta and event.choices[0].delta.content:
current_response = event.choices[0].delta.content
yield f"data: {json.dumps(current_response)}\n\n"

return StreamingResponse(get_openai_generator(), media_type='text/event-stream')
37 changes: 37 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import logging
from typing import Tuple

from pydantic import Field
from pydantic_settings import BaseSettings

_logger = logging.getLogger(__name__)


class Settings(BaseSettings):
api_key: str = Field(default="sk-xxx", exclude=True)
api_base: str = "https://api.openai.com/v1"
model: str = "gpt-3.5-turbo"
# rate limit xxx request per xx seconds
rate_limit: Tuple[int, int] = (60, 60 * 60)
user_rate_limit: Tuple[int, int] = (600, 60 * 60)
github_client_id: str = ""
github_client_secret: str = Field(default="", exclude=True)
jwt_secret: str = Field(default="secret", exclude=True)
ad_client: str = ""
ad_slot: str = ""

def get_human_rate_limit(self) -> str:
max_reqs, time_window_seconds = self.rate_limit
# convert to human readable format
return f"{max_reqs}req/{time_window_seconds}seconds"

def get_human_user_rate_limit(self) -> str:
max_reqs, time_window_seconds = self.user_rate_limit
# convert to human readable format
return f"{max_reqs}req/{time_window_seconds}seconds"

class Config:
env_file = ".env"


settings = Settings()
2 changes: 1 addition & 1 deletion src/divination/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

from models import DivinationBody
from src.models import DivinationBody
from typing import Optional


Expand Down
2 changes: 1 addition & 1 deletion src/divination/birthday.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import datetime
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

BIRTHDAY_PROMPT = "我请求你担任中国传统的生辰八字算命的角色。" \
Expand Down
2 changes: 1 addition & 1 deletion src/divination/dream.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import HTTPException
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

DREAM_PROMPT = "我请求你担任中国传统的周公解梦师的角色。" \
Expand Down
2 changes: 1 addition & 1 deletion src/divination/fate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import HTTPException
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

SYS_PROMPT = "你是一个姻缘助手,我给你发两个人的名字,用逗号隔开,"\
Expand Down
2 changes: 1 addition & 1 deletion src/divination/name.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import HTTPException
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

NAME_PROMPT = "我请求你担任中国传统的姓名五格算命师的角色。" \
Expand Down
2 changes: 1 addition & 1 deletion src/divination/new_name.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
from fastapi import HTTPException
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

NEW_NAME_PROMPT = (
Expand Down
2 changes: 1 addition & 1 deletion src/divination/plum_flower.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import HTTPException
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

SYS_PROMPT = "我请求你担任中国传统的梅花易数占卜师的角色。" \
Expand Down
2 changes: 1 addition & 1 deletion src/divination/tarot.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import HTTPException
from models import DivinationBody
from src.models import DivinationBody
from .base import DivinationFactory

TAROT_PROMPT = "我请求你担任塔罗占卜师的角色。 " \
Expand Down
Loading

0 comments on commit 7acaa09

Please sign in to comment.