145 lines
4.7 KiB
Python
145 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from urllib.parse import urlparse
|
|
|
|
from fastapi import FastAPI, File, Request, UploadFile
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
from app.config import settings
|
|
from app.logging_setup import configure_logging
|
|
from app.middleware import RequestContextMiddleware
|
|
from app.schemas import IMPublishRequest, RewriteRequest, WechatPublishRequest
|
|
from app.services.ai_rewriter import AIRewriter
|
|
from app.services.im import IMPublisher
|
|
from app.services.wechat import WechatPublisher
|
|
|
|
configure_logging()
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = FastAPI(title=settings.app_name)
|
|
|
|
|
|
@app.on_event("startup")
|
|
async def _log_startup() -> None:
|
|
logger.info(
|
|
"app_start name=%s openai_configured=%s ai_soft_accept=%s",
|
|
settings.app_name,
|
|
bool(settings.openai_api_key),
|
|
settings.ai_soft_accept,
|
|
)
|
|
|
|
|
|
app.add_middleware(RequestContextMiddleware)
|
|
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
rewriter = AIRewriter()
|
|
wechat = WechatPublisher()
|
|
im = IMPublisher()
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def index(request: Request):
|
|
return templates.TemplateResponse("index.html", {"request": request, "app_name": settings.app_name})
|
|
|
|
|
|
@app.get("/api/config")
|
|
async def api_config():
|
|
"""供页面展示:当前是否接入模型、模型名、提供方(不含密钥)。"""
|
|
base = settings.openai_base_url or ""
|
|
provider = "dashscope" if "dashscope.aliyuncs.com" in base else "openai_compatible"
|
|
host = urlparse(base).netloc if base else ""
|
|
return {
|
|
"openai_configured": bool(settings.openai_api_key),
|
|
"openai_model": settings.openai_model,
|
|
"provider": provider,
|
|
"base_url_host": host or None,
|
|
"openai_timeout_sec": settings.openai_timeout,
|
|
"openai_max_output_tokens": settings.openai_max_output_tokens,
|
|
}
|
|
|
|
|
|
@app.post("/api/rewrite")
|
|
async def rewrite(req: RewriteRequest, request: Request):
|
|
rid = getattr(request.state, "request_id", "")
|
|
src = req.source_text or ""
|
|
logger.info(
|
|
"api_rewrite_in rid=%s source_chars=%d title_hint_chars=%d tone=%s audience=%s "
|
|
"keep_points_chars=%d avoid_words_chars=%d",
|
|
rid,
|
|
len(src),
|
|
len(req.title_hint or ""),
|
|
req.tone,
|
|
req.audience,
|
|
len(req.keep_points or ""),
|
|
len(req.avoid_words or ""),
|
|
)
|
|
result = rewriter.rewrite(req, request_id=rid)
|
|
tr = result.trace or {}
|
|
logger.info(
|
|
"api_rewrite_out rid=%s mode=%s duration_ms=%s quality_notes=%d trace_steps=%s soft_accept=%s",
|
|
rid,
|
|
result.mode,
|
|
tr.get("duration_ms"),
|
|
len(result.quality_notes or []),
|
|
len((tr.get("steps") or [])),
|
|
tr.get("quality_soft_accept"),
|
|
)
|
|
return result
|
|
|
|
|
|
@app.post("/api/publish/wechat")
|
|
async def publish_wechat(req: WechatPublishRequest, request: Request):
|
|
rid = getattr(request.state, "request_id", "")
|
|
logger.info(
|
|
"api_wechat_in rid=%s title_chars=%d summary_chars=%d body_md_chars=%d author_set=%s",
|
|
rid,
|
|
len(req.title or ""),
|
|
len(req.summary or ""),
|
|
len(req.body_markdown or ""),
|
|
bool((req.author or "").strip()),
|
|
)
|
|
out = await wechat.publish_draft(req, request_id=rid)
|
|
wcode = (out.data or {}).get("errcode") if isinstance(out.data, dict) else None
|
|
logger.info(
|
|
"api_wechat_out rid=%s ok=%s wechat_errcode=%s detail_preview=%s",
|
|
rid,
|
|
out.ok,
|
|
wcode,
|
|
(out.detail or "")[:240],
|
|
)
|
|
return out
|
|
|
|
|
|
@app.post("/api/wechat/cover/upload")
|
|
async def upload_wechat_cover(request: Request, file: UploadFile = File(...)):
|
|
rid = getattr(request.state, "request_id", "")
|
|
fn = file.filename or "cover.jpg"
|
|
content = await file.read()
|
|
logger.info("api_wechat_cover_upload_in rid=%s filename=%s bytes=%d", rid, fn, len(content))
|
|
out = await wechat.upload_cover(fn, content, request_id=rid)
|
|
logger.info(
|
|
"api_wechat_cover_upload_out rid=%s ok=%s detail=%s",
|
|
rid,
|
|
out.ok,
|
|
(out.detail or "")[:160],
|
|
)
|
|
return out
|
|
|
|
|
|
@app.post("/api/publish/im")
|
|
async def publish_im(req: IMPublishRequest, request: Request):
|
|
rid = getattr(request.state, "request_id", "")
|
|
logger.info(
|
|
"api_im_in rid=%s title_chars=%d body_md_chars=%d",
|
|
rid,
|
|
len(req.title or ""),
|
|
len(req.body_markdown or ""),
|
|
)
|
|
out = await im.publish(req, request_id=rid)
|
|
logger.info("api_im_out rid=%s ok=%s detail=%s", rid, out.ok, (out.detail or "")[:120])
|
|
return out
|