format / app.py
viskav's picture
Update app.py
77438f0 verified
Raw History Blame Contribute Delete
4.91 kB
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from typing import Literal, Optional
from llama_cpp import Llama
import re
# ==================== MODEL CONFIG ====================
MODEL_REPO = "bartowski/Phi-3.1-mini-4k-instruct-GGUF"
MODEL_FILE = "Phi-3.1-mini-4k-instruct-IQ2_M.gguf"
print("🚀 Loading Phi-3.1 Mini (Human Authorship Restorer)...")
llm = Llama.from_pretrained(
repo_id=MODEL_REPO,
filename=MODEL_FILE,
n_threads=4,
n_ctx=1024, # safer for HF Spaces
n_batch=128,
n_gpu_layers=0,
verbose=False,
)
print("✅ Model loaded")
# ==================== FASTAPI ====================
app = FastAPI(title="AI Humanizer – Author Voice Restorer")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# ==================== REQUEST ====================
class HumanizeRequest(BaseModel):
text: str = Field(..., min_length=1, max_length=3000)
section: Literal[
"abstract",
"introduction",
"methodology",
"results",
"discussion"
]
author_notes: Optional[str] = None
# ==================== SECTION-AWARE STYLE ====================
SECTION_GUIDANCE = {
"abstract":
"Write concisely and densely. Maintain objective academic tone.",
"introduction":
"Provide context and motivation. Sound like a researcher framing a problem.",
"methodology":
"Be procedural, precise, and restrained. No persuasive language.",
"results":
"Be cautious, observational, and factual. Avoid strong claims.",
"discussion":
"Be interpretive and reflective. Explain implications carefully."
}
# ==================== OUTPUT CLEANER ====================
def clean_output(text: str) -> str:
text = re.sub(r'<\|.*?\|>', '', text)
text = re.sub(r'\s+', ' ', text)
return text.strip()
# ==================== FALLBACK (UNCHANGED, SAFE) ====================
def fallback_humanize(text: str) -> str:
replacements = [
("utilize", "use"),
("commence", "start"),
("approximately", "about"),
("therefore", "so"),
("however", "but"),
("in order to", "to"),
("due to the fact that", "because"),
("prior to", "before"),
("subsequent to", "after"),
]
result = text
for formal, simple in replacements:
result = re.sub(formal, simple, result, flags=re.IGNORECASE)
return result
# ==================== PROMPT BUILDER ====================
def build_prompt(text: str, section: str, author_notes: Optional[str]) -> str:
guidance = SECTION_GUIDANCE.get(section, "Use formal academic tone.")
notes_block = (
f"\nAuthor context (do NOT invent new reasoning):\n{author_notes}\n"
if author_notes else ""
)
return f"""
You are an academic writing assistant.
GOAL:
Restore natural human authorship signals while preserving formal academic language.
NON-NEGOTIABLE RULES:
- Preserve all technical meaning, claims, numbers, and citations
- Do NOT add new information
- Do NOT remove uncertainty
- Do NOT invent justifications
- Do NOT change terminology
- Do NOT introduce grammar or punctuation errors
SECTION GUIDANCE:
{guidance}
HUMANIZATION RULES:
- Vary sentence rhythm naturally (short / medium / long)
- Reorder clauses where appropriate
- Reduce overused academic fillers (e.g., "Moreover", "Furthermore")
- Prefer implicit transitions
- Preserve author intent and constraints
{notes_block}
TEXT:
{text}
OUTPUT:
Return ONLY the revised text.
""".strip()
# ==================== ENDPOINT ====================
@app.post("/api/humanize")
async def humanize(req: HumanizeRequest):
text = req.text.strip()
prompt = build_prompt(text, req.section, req.author_notes)
try:
output = llm(
prompt,
max_tokens=400,
temperature=0.4, # controlled, academic-safe
top_p=0.9,
top_k=40,
stop=["<|user|>", "<|end|>"],
echo=False,
)
raw = output["choices"][0]["text"]
cleaned = clean_output(raw)
if not cleaned or cleaned.lower() == text.lower():
cleaned = fallback_humanize(text)
return {
"original": text,
"section": req.section,
"humanized": cleaned,
"success": True
}
except Exception as e:
print("❌ Inference error:", e)
return {
"original": text,
"section": req.section,
"humanized": fallback_humanize(text),
"success": False
}
# ==================== HEALTH ====================
@app.get("/")
def health():
return {
"status": "ok",
"model": MODEL_FILE,
"endpoint": "/api/humanize"
}