Create main.py
Browse files- backend/app/main.py +63 -0
backend/app/main.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from .schemas import GenerateRequest, EmailResponse, ProposalResponse, LintRequest, LintResponse
|
| 4 |
+
from .prompt_templates import EMAIL_SYSTEM, EMAIL_USER, PROPOSAL_SYSTEM, PROPOSAL_USER
|
| 5 |
+
from .openai_utils import chat_complete, split_subject_body
|
| 6 |
+
from .hf_utils import toxicity_score, sentiment_polarity
|
| 7 |
+
|
| 8 |
+
app = FastAPI(title="Sales Writer API", version="0.1.0")
|
| 9 |
+
|
| 10 |
+
# 必要に応じてオリジン制限
|
| 11 |
+
app.add_middleware(
|
| 12 |
+
CORSMiddleware,
|
| 13 |
+
allow_origins=["http://localhost:5173"],
|
| 14 |
+
allow_credentials=True,
|
| 15 |
+
allow_methods=["*"],
|
| 16 |
+
allow_headers=["*"],
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
@app.get("/health")
|
| 20 |
+
def health():
|
| 21 |
+
return {"status": "ok"}
|
| 22 |
+
|
| 23 |
+
@app.post("/generate/email", response_model=EmailResponse)
|
| 24 |
+
async def generate_email(req: GenerateRequest):
|
| 25 |
+
user_prompt = EMAIL_USER.format(**req.model_dump())
|
| 26 |
+
raw = chat_complete(EMAIL_SYSTEM, user_prompt)
|
| 27 |
+
subject, body = split_subject_body(raw)
|
| 28 |
+
|
| 29 |
+
tox = await toxicity_score(body)
|
| 30 |
+
sent = await sentiment_polarity(body)
|
| 31 |
+
warnings = []
|
| 32 |
+
if tox > 0.2:
|
| 33 |
+
warnings.append("トーンが攻撃的/不適切の可能性があります。表現を柔らかくしてください。")
|
| 34 |
+
|
| 35 |
+
quality = {"toxicity": tox, "sentiment": sent}
|
| 36 |
+
return EmailResponse(subject=subject, body=body, quality=quality, warnings=warnings)
|
| 37 |
+
|
| 38 |
+
@app.post("/generate/proposal", response_model=ProposalResponse)
|
| 39 |
+
async def generate_proposal(req: GenerateRequest):
|
| 40 |
+
user_prompt = PROPOSAL_USER.format(**req.model_dump())
|
| 41 |
+
text = chat_complete(PROPOSAL_SYSTEM, user_prompt)
|
| 42 |
+
# 簡易パース:先頭の箇条書きを章立て、最後の段落をサマリーと仮定
|
| 43 |
+
lines = [ln.strip("-• ").strip() for ln in text.splitlines() if ln.strip()]
|
| 44 |
+
outline = [ln for ln in lines if not ln.startswith("エグゼクティブサマリー")]
|
| 45 |
+
# サマリー抽出(雑だがMVP)
|
| 46 |
+
summary = ""
|
| 47 |
+
for i, ln in enumerate(lines):
|
| 48 |
+
if "エグゼクティブサマリー" in ln:
|
| 49 |
+
summary = "\n".join(lines[i+1:])
|
| 50 |
+
break
|
| 51 |
+
return ProposalResponse(outline=outline[:8], executive_summary=summary or "(サマリー抽出に失敗しました)")
|
| 52 |
+
|
| 53 |
+
@app.post("/lint", response_model=LintResponse)
|
| 54 |
+
async def lint_text(req: LintRequest):
|
| 55 |
+
tox = await toxicity_score(req.text)
|
| 56 |
+
issues = []
|
| 57 |
+
if tox > 0.2:
|
| 58 |
+
issues.append("不適切・攻撃的な表現を含む可能性")
|
| 59 |
+
# 営業で避けたいフレーズ例(簡易ルール)
|
| 60 |
+
bad_patterns = ["絶対に", "必ず儲かる", "今だけ", "無料で全部"]
|
| 61 |
+
if any(p in req.text for p in bad_patterns):
|
| 62 |
+
issues.append("誇大広告と見なされる恐れのある表現")
|
| 63 |
+
return LintResponse(issues=issues, toxicity=tox)
|