viskav commited on
Commit
77513d0
·
verified ·
1 Parent(s): a095a2a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -169
app.py CHANGED
@@ -1,201 +1,141 @@
1
- import os
2
- import re
3
- import time
4
- from typing import Literal
5
  from fastapi import FastAPI, HTTPException
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from pydantic import BaseModel, Field
 
8
  from llama_cpp import Llama
9
- from contextlib import asynccontextmanager
10
 
11
- # ==================== OPTIMIZED CONFIGURATION ====================
12
  MODEL_REPO = "bartowski/Phi-3.1-mini-4k-instruct-GGUF"
13
- MODEL_FILE = "Phi-3.1-mini-4k-instruct-Q4_K_M.gguf" # Faster than IQ3_M, better quality
14
- N_THREADS = int(os.environ.get("N_THREADS", "8")) # Increased from 4
15
- N_CTX = int(os.environ.get("N_CTX", "512")) # Reduced from 1024 - MAJOR SPEEDUP
16
- N_BATCH = int(os.environ.get("N_BATCH", "256")) # Optimized for CPU
17
- N_GPU_LAYERS = int(os.environ.get("N_GPU_LAYERS", "0")) # CPU only for HF Spaces
18
- MAX_INPUT_LENGTH = 500 # Reduced from 1000
19
-
20
- # ==================== GLOBAL MODEL ====================
21
- llm = None
22
- model_loading_error = None
23
-
24
- @asynccontextmanager
25
- async def lifespan(app: FastAPI):
26
- global llm, model_loading_error
27
- print("Starting Humanizer Pro 2025...")
28
- try:
29
- print(f"Loading {MODEL_FILE}...")
30
- llm = Llama.from_pretrained(
31
- repo_id=MODEL_REPO,
32
- filename=MODEL_FILE,
33
- n_ctx=N_CTX,
34
- n_batch=N_BATCH,
35
- n_threads=N_THREADS,
36
- n_gpu_layers=N_GPU_LAYERS,
37
- use_mmap=True,
38
- use_mlock=False,
39
- verbose=False,
40
- # CRITICAL OPTIMIZATIONS
41
- rope_freq_base=0.0, # Faster attention
42
- rope_freq_scale=0.0,
43
- )
44
- # Warmup with exact expected format
45
- llm("Test", max_tokens=10, temperature=0.7)
46
- print("✅ Model loaded & warmed up!")
47
- model_loading_error = None
48
- except Exception as e:
49
- print(f"❌ Model failed: {e}")
50
- model_loading_error = str(e)
51
- llm = None
52
- yield
53
- print("Shutting down...")
54
-
55
- app = FastAPI(
56
- title="Humanizer Pro 2025",
57
- description="Undetectable AI Humanizer (Turnitin-Proof)",
58
- version="3.1-OPTIMIZED",
59
- lifespan=lifespan
60
  )
 
 
 
 
 
61
  app.add_middleware(
62
  CORSMiddleware,
63
  allow_origins=["*"],
64
- allow_credentials=True,
65
  allow_methods=["*"],
66
  allow_headers=["*"],
67
  )
68
 
69
- # ==================== REQUEST MODELS ====================
70
- class TransformRequest(BaseModel):
71
- text: str = Field(..., min_length=1, max_length=MAX_INPUT_LENGTH)
72
- style: Literal["professional", "casual", "academic", "marketing", "humanizer"] = "humanizer"
73
-
74
  class HumanizeRequest(BaseModel):
75
- text: str = Field(..., min_length=1, max_length=MAX_INPUT_LENGTH)
 
76
 
77
- # ==================== ULTRA-SHORT PROMPTS (FASTER) ====================
78
  STYLE_PROMPTS = {
79
- "professional": """Rewrite professionally: {text}
80
- Output:""",
 
81
 
82
- "casual": """Rewrite casually: {text}
83
- Output:""",
 
84
 
85
- "academic": """Rewrite academically: {text}
86
- Output:""",
 
87
 
88
- "marketing": """Rewrite as marketing copy: {text}
89
- Output:""",
90
-
91
- "humanizer": """Humanize this text naturally: {text}
92
- Rewrite:""",
93
  }
94
 
95
- # ==================== CLEAN OUTPUT ====================
96
  def clean_output(text: str) -> str:
97
- if not text:
98
- return ""
99
-
100
- # Remove common prefixes
101
- text = re.sub(r'^(output:|rewrite:|humanized:|here is|here\'s)\s*:?\s*', '', text, flags=re.IGNORECASE)
102
- text = re.sub(r'^["\'\-\*\>\#]+\s*', '', text)
103
-
104
- # Split by newlines and take first substantial line
105
- lines = [l.strip() for l in text.split('\n') if l.strip()]
106
- if not lines:
107
- return text.strip()
108
-
109
- # Filter out meta-text
110
- for line in lines:
111
- lower = line.lower()
112
- if any(bad in lower for bad in ['here is', 'rewritten', 'output:', 'version', 'assistant']):
113
- continue
114
- if len(line) > 10: # Must be substantial
115
- return line.strip(' "\'')
116
-
117
- return lines[0].strip(' "\'') if lines else text.strip()
118
-
119
- # ==================== OPTIMIZED INFERENCE ====================
120
- async def transform_with_model(text: str, style: str) -> str:
121
- global llm
122
- if not llm:
123
- raise HTTPException(status_code=503, detail="Model not ready")
124
-
125
- # Truncate long inputs
126
- if len(text) > MAX_INPUT_LENGTH:
127
- text = text[:MAX_INPUT_LENGTH]
128
-
129
- prompt = STYLE_PROMPTS[style].format(text=text)
130
-
 
 
 
 
 
 
 
 
 
 
 
131
  try:
132
- start = time.time()
133
  output = llm(
134
  prompt,
135
- max_tokens=150, # Reduced from 300 - BIG SPEEDUP
136
- temperature=0.75, # Slightly lower for faster sampling
137
- top_p=0.92, # Reduced from 0.96
138
- top_k=40, # Reduced from 70 - FASTER
139
  repeat_penalty=1.1,
140
- frequency_penalty=0.0, # Disabled for speed
141
- presence_penalty=0.0, # Disabled for speed
142
- stop=["<|end|>", "<|user|>", "\n\n"], # Keep simple
143
  echo=False,
144
  )
145
-
146
- raw = output["choices"][0]["text"] if output["choices"] else ""
147
- result = clean_output(raw)
148
- elapsed = time.time() - start
149
-
150
- print(f"⚡ Processed in {elapsed:.2f}s → {result[:50]}...")
151
-
152
- return result if result and len(result) > 5 else text
153
-
 
 
 
 
 
154
  except Exception as e:
155
- print(f"❌ Inference error: {e}")
156
- raise HTTPException(status_code=500, detail=f"Inference failed: {str(e)}")
 
 
 
 
 
157
 
158
- # ==================== ENDPOINTS ====================
159
  @app.get("/")
160
- async def root():
161
- return {
162
- "status": "ready" if llm else "loading",
163
- "model": MODEL_FILE,
164
- "message": "Humanizer Pro 2025 — Optimized for Speed ⚡",
165
- "error": model_loading_error
166
- }
167
-
168
- @app.get("/health")
169
- async def health():
170
- return {"status": "ok" if llm else "loading"}
171
-
172
- @app.post("/api/transform")
173
- async def transform(request: TransformRequest):
174
- if not request.text.strip():
175
- raise HTTPException(status_code=400, detail="Empty text")
176
-
177
- result = await transform_with_model(request.text, request.style)
178
-
179
- return {
180
- "original": request.text,
181
- "transformed": result,
182
- "style": request.style,
183
- "success": True
184
- }
185
-
186
- @app.post("/api/humanize")
187
- async def humanize(request: HumanizeRequest):
188
- if not request.text.strip():
189
- raise HTTPException(status_code=400, detail="Empty text")
190
-
191
- result = await transform_with_model(request.text, "humanizer")
192
-
193
- return {
194
- "original": request.text,
195
- "humanized": result,
196
- "score": "~99% Human (Turnitin-Proof)"
197
- }
198
-
199
- if __name__ == "__main__":
200
- import uvicorn
201
- uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=False)
 
 
 
 
 
1
  from fastapi import FastAPI, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from pydantic import BaseModel, Field
4
+ from typing import Literal
5
  from llama_cpp import Llama
6
+ import re
7
 
8
+ # ==================== MODEL CONFIG ====================
9
  MODEL_REPO = "bartowski/Phi-3.1-mini-4k-instruct-GGUF"
10
+ MODEL_FILE = "Phi-3.1-mini-4k-instruct-IQ2_M.gguf"
11
+
12
+ print("🚀 Loading Phi-3.1 Mini (Fast Humanizer)...")
13
+ llm = Llama.from_pretrained(
14
+ repo_id=MODEL_REPO,
15
+ filename=MODEL_FILE,
16
+ n_threads=4,
17
+ n_ctx=2048, # Smaller context = faster
18
+ n_batch=256,
19
+ n_gpu_layers=0,
20
+ verbose=False,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  )
22
+ print("✅ Model loaded")
23
+
24
+ # ==================== FASTAPI ====================
25
+ app = FastAPI(title="Fast Humanizer")
26
+
27
  app.add_middleware(
28
  CORSMiddleware,
29
  allow_origins=["*"],
 
30
  allow_methods=["*"],
31
  allow_headers=["*"],
32
  )
33
 
34
+ # ==================== REQUEST ====================
 
 
 
 
35
  class HumanizeRequest(BaseModel):
36
+ text: str = Field(..., min_length=1, max_length=500)
37
+ style: Literal["professional", "casual", "academic", "marketing"] = "professional"
38
 
39
+ # ==================== STYLE PROMPTS (SHORT & EFFECTIVE) ====================
40
  STYLE_PROMPTS = {
41
+ "professional":
42
+ "Rewrite this text in a clear, polished, professional tone. "
43
+ "Make it sound natural and confident. Output ONLY the rewritten text:\n",
44
 
45
+ "casual":
46
+ "Rewrite this text in a friendly, casual, human way. "
47
+ "Use natural phrasing and contractions. Output ONLY the rewritten text:\n",
48
 
49
+ "academic":
50
+ "Rewrite this text in a formal academic style. "
51
+ "Use clear structure and precise language. Output ONLY the rewritten text:\n",
52
 
53
+ "marketing":
54
+ "Rewrite this text as persuasive marketing copy. "
55
+ "Make it engaging and benefit-focused. Output ONLY the rewritten text:\n",
 
 
56
  }
57
 
58
+ # ==================== OUTPUT CLEANER ====================
59
  def clean_output(text: str) -> str:
60
+ text = re.sub(r'<\|.*?\|>', '', text)
61
+ text = re.sub(r'\s+', ' ', text)
62
+ text = text.strip()
63
+
64
+ lines = [l.strip() for l in text.split("\n") if len(l.strip()) > 10]
65
+ return lines[-1] if lines else text
66
+
67
+ # ==================== FALLBACK HUMANIZER ====================
68
+ def fallback_humanize(text: str) -> str:
69
+ replacements = [
70
+ ("utilize", "use"),
71
+ ("commence", "start"),
72
+ ("approximately", "about"),
73
+ ("therefore", "so"),
74
+ ("however", "but"),
75
+ ("in order to", "to"),
76
+ ("due to the fact that", "because"),
77
+ ("prior to", "before"),
78
+ ("subsequent to", "after"),
79
+ ]
80
+
81
+ result = text
82
+ for formal, casual in replacements:
83
+ result = re.sub(formal, casual, result, flags=re.IGNORECASE)
84
+
85
+ result = re.sub(r"\b(do not|does not|did not|cannot|will not|is not|are not)\b",
86
+ lambda m: m.group(1).replace(" ", "'").replace("cannot", "can't"),
87
+ result,
88
+ flags=re.IGNORECASE)
89
+ return result
90
+
91
+ # ==================== ENDPOINT ====================
92
+ @app.post("/api/humanize")
93
+ async def humanize(req: HumanizeRequest):
94
+ text = req.text.strip()
95
+ style = req.style
96
+
97
+ prompt = (
98
+ f"<|user|>\n"
99
+ f"{STYLE_PROMPTS[style]}"
100
+ f"{text}\n"
101
+ f"<|end|>\n"
102
+ f"<|assistant|>\n"
103
+ )
104
+
105
  try:
 
106
  output = llm(
107
  prompt,
108
+ max_tokens=180, # FAST
109
+ temperature=0.7,
110
+ top_p=0.9,
111
+ top_k=40,
112
  repeat_penalty=1.1,
113
+ stop=["<|end|>", "<|user|>"],
 
 
114
  echo=False,
115
  )
116
+
117
+ raw = output["choices"][0]["text"]
118
+ cleaned = clean_output(raw)
119
+
120
+ if not cleaned or cleaned.lower() == text.lower():
121
+ cleaned = fallback_humanize(text)
122
+
123
+ return {
124
+ "original": text,
125
+ "style": style,
126
+ "humanized": cleaned,
127
+ "success": True
128
+ }
129
+
130
  except Exception as e:
131
+ print("❌ Model error:", e)
132
+ return {
133
+ "original": text,
134
+ "style": style,
135
+ "humanized": fallback_humanize(text),
136
+ "success": False
137
+ }
138
 
 
139
  @app.get("/")
140
+ def health():
141
+ return {"status": "ok", "model": MODEL_FILE}