format / app.py
viskav's picture
Update app.py
77438f0 verified
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"
}