feat(api): add joke generate endpoint with AI
This commit is contained in:
@@ -2,5 +2,9 @@ from .jokes import router as jokes_router
|
|||||||
from .categories import router as categories_router
|
from .categories import router as categories_router
|
||||||
from .auth import router as auth_router
|
from .auth import router as auth_router
|
||||||
from .admin import router as admin_router
|
from .admin import router as admin_router
|
||||||
|
from .settings import router as settings_router
|
||||||
|
from .links import router as links_router
|
||||||
|
from .feedback import router as feedback_router
|
||||||
|
from .generate import router as generate_router
|
||||||
|
|
||||||
__all__ = ["jokes_router", "categories_router", "auth_router", "admin_router"]
|
__all__ = ["jokes_router", "categories_router", "auth_router", "admin_router", "settings_router", "links_router", "feedback_router", "generate_router"]
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
"""智能笑话生成器 API"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from openai import OpenAI
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.database import get_db
|
||||||
|
from app.models.setting import AiSetting
|
||||||
|
from app.schemas.joke import GenerateRequest, GenerateResponse
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/generate", tags=["生成器"])
|
||||||
|
|
||||||
|
|
||||||
|
# AI Prompt
|
||||||
|
GENERATION_PROMPT = """你是一位幽默大师,专门创作轻松搞笑的短笑话。
|
||||||
|
|
||||||
|
{context}
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 根据场景和关键词创作一条原创笑话
|
||||||
|
2. 笑话要有反转或意外结局
|
||||||
|
3. 语言简洁,30-150字
|
||||||
|
4. 直接输出笑话内容,不需要解释
|
||||||
|
|
||||||
|
格式:
|
||||||
|
标题:xxx
|
||||||
|
内容:xxx
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _build_prompt(scenarios: list[str], keywords: list[str]) -> str:
|
||||||
|
"""构建 AI prompt"""
|
||||||
|
parts = []
|
||||||
|
if scenarios:
|
||||||
|
parts.append(f"场景:{', '.join(scenarios)}")
|
||||||
|
if keywords:
|
||||||
|
parts.append(f"关键词:{', '.join(keywords)}")
|
||||||
|
if not parts:
|
||||||
|
parts.append("场景:日常生活的各种趣事(不指定具体场景)")
|
||||||
|
return GENERATION_PROMPT.format(context="\n".join(parts))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", response_model=GenerateResponse)
|
||||||
|
def generate_joke(
|
||||||
|
req: GenerateRequest,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
"""调用 AI 生成笑话"""
|
||||||
|
# 获取激活的 AI 配置
|
||||||
|
setting = db.query(AiSetting).filter(AiSetting.is_active == True).first()
|
||||||
|
if not setting:
|
||||||
|
# 如果没有配置,尝试返回默认配置
|
||||||
|
setting = db.query(AiSetting).first()
|
||||||
|
|
||||||
|
if not setting or not setting.api_key:
|
||||||
|
raise HTTPException(status_code=503, detail="AI 服务未配置,请联系管理员")
|
||||||
|
|
||||||
|
# 调用 AI
|
||||||
|
try:
|
||||||
|
client = OpenAI(base_url=setting.api_base, api_key=setting.api_key)
|
||||||
|
prompt = _build_prompt(req.scenarios, req.keywords)
|
||||||
|
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
model=setting.model_name,
|
||||||
|
messages=[{"role": "user", "content": prompt}],
|
||||||
|
temperature=setting.temperature,
|
||||||
|
max_tokens=setting.max_tokens,
|
||||||
|
)
|
||||||
|
|
||||||
|
raw = response.choices[0].message.content
|
||||||
|
return _parse_response(raw)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"生成失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_response(raw: str) -> GenerateResponse:
|
||||||
|
"""解析 AI 返回内容,提取标题和内容"""
|
||||||
|
title = ""
|
||||||
|
content = raw
|
||||||
|
|
||||||
|
# 尝试提取 "标题:xxx" 或 "标题:xxx"
|
||||||
|
for line in raw.split("\n"):
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("标题:") or line.startswith("标题:"):
|
||||||
|
title = line.split(":", 1)[-1].split(":", 1)[-1].strip()
|
||||||
|
content = content.replace(line, "").strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
# 如果没有提取到标题,取第一行或前20字
|
||||||
|
if not title:
|
||||||
|
first_line = raw.split("\n")[0].strip()
|
||||||
|
if first_line.startswith("标题"):
|
||||||
|
first_line = first_line.split(":", 1)[-1].split(":", 1)[-1].strip()
|
||||||
|
title = first_line[:30] if len(first_line) > 30 else first_line
|
||||||
|
|
||||||
|
return GenerateResponse(
|
||||||
|
title=title or "生成的笑话",
|
||||||
|
content=content.strip(),
|
||||||
|
created_at=datetime.now(),
|
||||||
|
)
|
||||||
+16
-5
@@ -2,7 +2,14 @@ from fastapi import FastAPI
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
from app.config import API_TITLE, API_VERSION
|
from app.config import API_TITLE, API_VERSION
|
||||||
from app.routers import jokes_router, categories_router, auth_router, admin_router
|
from app.routers import jokes_router, categories_router, auth_router, admin_router, settings_router, links_router, feedback_router, generate_router
|
||||||
|
from app.database import Base, engine
|
||||||
|
from app.models.setting import AiSetting
|
||||||
|
from app.models.link import Link
|
||||||
|
from app.models.feedback import Feedback # 注册模型,确保 create_all 能看到
|
||||||
|
|
||||||
|
# 创建新表(如果不存在)
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
# Create FastAPI application
|
# Create FastAPI application
|
||||||
app = FastAPI(title=API_TITLE, version=API_VERSION)
|
app = FastAPI(title=API_TITLE, version=API_VERSION)
|
||||||
@@ -17,10 +24,14 @@ app.add_middleware(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Mount routers
|
# Mount routers
|
||||||
app.include_router(jokes_router)
|
app.include_router(jokes_router, prefix="/api")
|
||||||
app.include_router(categories_router)
|
app.include_router(categories_router, prefix="/api")
|
||||||
app.include_router(auth_router)
|
app.include_router(auth_router, prefix="/api")
|
||||||
app.include_router(admin_router)
|
app.include_router(admin_router, prefix="/api")
|
||||||
|
app.include_router(settings_router, prefix="/api")
|
||||||
|
app.include_router(links_router, prefix="/api")
|
||||||
|
app.include_router(feedback_router, prefix="/api")
|
||||||
|
app.include_router(generate_router, prefix="/api")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|||||||
Reference in New Issue
Block a user