Spaces:
Sleeping
Sleeping
| """ | |
| AI Humanizer β Gemma 3 27B via Inference API + AI Detector | |
| """ | |
| import os, time, gc | |
| import gradio as gr | |
| from fastapi import FastAPI, Request | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from huggingface_hub import InferenceClient | |
| from transformers import pipeline | |
| # ββ Config ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| HF_TOKEN = os.environ.get("HF_TOKEN", "") | |
| MODEL_ID = "google/gemma-3-27b-it" | |
| DETECTOR_MODEL = "Hello-SimpleAI/chatgpt-detector-roberta" | |
| TEMPERATURE = 0.8 | |
| INSTRUCTIONS = """Take this text and rewrite it as if you are a second-year university student who understands the material well but writes imperfectly. You are writing this for a class paper that will be checked by Turnitin. | |
| Your writing style: | |
| You understand the concepts but occasionally phrase things in a slightly roundabout way. You sometimes start sentences with "And" or "But." You use semicolons sometimes but not always correctly. Your paragraphs are uneven in length. You do not write with perfect parallel structure. Some of your sentences are too long and could be split, but you left them. You occasionally repeat a word when a synonym would be better. You write in a formal register but it is clearly a student's formal, not a professor's. | |
| Constraints: | |
| Same word count as the original, give or take ten percent. Every argument, citation, and claim preserved exactly. No dashes of any kind. No bullet points or lists. No symbols or decorative characters. Only periods, commas, semicolons, colons, apostrophes, question marks. | |
| Banned vocabulary. Do not use any of these anywhere: Furthermore, Moreover, Additionally, Nevertheless, Consequently, In conclusion, In summary, It is important to note, It should be noted, It is worth mentioning, This suggests, What stands out, The evidence points to, A closer look reveals, Ultimately, Significantly, Notably, Indeed, Thus, Hence, Thereby, Wherein, Whereby, Basically, Kind of, Sort of, It's like, The thing is, Really important, Plays a crucial role. | |
| Output the rewritten text only. No explanations.""" | |
| # ββ Load Inference Client βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("[INIT] Connecting to HF Inference API...") | |
| client = InferenceClient(token=HF_TOKEN) | |
| print(f"[INIT] Model: {MODEL_ID}") | |
| # ββ Load Detector βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("[INIT] Loading detector...") | |
| detector = pipeline("text-classification", model=DETECTOR_MODEL, device=-1, top_k=None) | |
| print("[INIT] Ready.") | |
| gc.collect() | |
| # ββ Functions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def detect_text(text): | |
| if not text or not text.strip(): | |
| return 0.0, 0.0 | |
| results = detector(text.strip(), truncation=True, max_length=512) | |
| scores = {r["label"]: r["score"] for r in results[0]} | |
| return scores.get("ChatGPT", 0.0), scores.get("Human", 0.0) | |
| def humanize_stream(text): | |
| if not text or not text.strip(): | |
| yield "", "", "" | |
| return | |
| t0 = time.perf_counter() | |
| input_words = len(text.split()) | |
| messages = [ | |
| {"role": "user", "content": f"{INSTRUCTIONS}\n\nText ({input_words} words):\n\n{text.strip()}"}, | |
| ] | |
| output = "" | |
| n_tokens = 0 | |
| ttft = 0 | |
| try: | |
| stream = client.chat.completions.create( | |
| model=MODEL_ID, | |
| messages=messages, | |
| max_tokens=int(input_words * 2), | |
| temperature=TEMPERATURE, | |
| top_p=0.92, | |
| stream=True, | |
| ) | |
| for chunk in stream: | |
| if chunk.choices and chunk.choices[0].delta.content: | |
| token = chunk.choices[0].delta.content | |
| output += token | |
| n_tokens += 1 | |
| if n_tokens == 1: | |
| ttft = time.perf_counter() - t0 | |
| elapsed = time.perf_counter() - t0 | |
| tps = round(n_tokens / elapsed, 1) if elapsed > 0 else 0 | |
| yield output, f"β± {elapsed:.1f}s Β· {n_tokens} tok Β· {tps} tok/s", "" | |
| except Exception as e: | |
| yield f"Error: {str(e)}", "", "" | |
| return | |
| elapsed = time.perf_counter() - t0 | |
| tps = round(n_tokens / elapsed, 1) if elapsed > 0 else 0 | |
| final = output.strip() | |
| ai_score, human_score = detect_text(final) | |
| if ai_score > 0.7: | |
| det = f"π€ AI: {ai_score:.0%} | Human: {human_score:.0%} β still detectable" | |
| elif ai_score > 0.4: | |
| det = f"β οΈ AI: {ai_score:.0%} | Human: {human_score:.0%} β borderline" | |
| else: | |
| det = f"β AI: {ai_score:.0%} | Human: {human_score:.0%} β passes" | |
| out_w = len(final.split()) | |
| yield final, f"β {elapsed:.1f}s Β· {n_tokens} tok Β· {tps} tok/s Β· TTFT: {ttft:.1f}s Β· {input_words}β{out_w} words", det | |
| def detect_only(text): | |
| if not text or not text.strip(): | |
| return "" | |
| ai, human = detect_text(text.strip()) | |
| if ai > 0.7: | |
| return f"π€ **AI-Generated** β AI: {ai:.0%} | Human: {human:.0%}" | |
| elif ai > 0.4: | |
| return f"β οΈ **Borderline** β AI: {ai:.0%} | Human: {human:.0%}" | |
| else: | |
| return f"β **Human-Written** β AI: {ai:.0%} | Human: {human:.0%}" | |
| # ββ FastAPI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app = FastAPI() | |
| app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, | |
| allow_methods=["*"], allow_headers=["*"]) | |
| async def health(): | |
| return {"status": "ok", "model": MODEL_ID} | |
| async def api_humanize(request: Request): | |
| body = await request.json() | |
| text = body.get("text", "").strip() | |
| if not text: | |
| return JSONResponse({"error": "text required"}, 400) | |
| input_words = len(text.split()) | |
| t0 = time.perf_counter() | |
| messages = [ | |
| {"role": "user", "content": f"{INSTRUCTIONS}\n\nText ({input_words} words):\n\n{text}"}, | |
| ] | |
| try: | |
| resp = client.chat.completions.create( | |
| model=MODEL_ID, messages=messages, | |
| max_tokens=int(input_words * 2), | |
| temperature=TEMPERATURE, top_p=0.92, | |
| ) | |
| result = resp.choices[0].message.content.strip() | |
| except Exception as e: | |
| return JSONResponse({"error": str(e)}, 500) | |
| elapsed = time.perf_counter() - t0 | |
| ai, human = detect_text(result) | |
| return JSONResponse({ | |
| "result": result, | |
| "input_words": input_words, | |
| "output_words": len(result.split()), | |
| "time_seconds": round(elapsed, 2), | |
| "detection": {"human": round(human, 3), "ai": round(ai, 3)} | |
| }) | |
| async def api_detect(request: Request): | |
| body = await request.json() | |
| text = body.get("text", "").strip() | |
| if not text: | |
| return JSONResponse({"error": "text required"}, 400) | |
| ai, human = detect_text(text) | |
| return JSONResponse({"human": round(human, 3), "ai": round(ai, 3), | |
| "prediction": "human" if human > 0.5 else "ai"}) | |
| # ββ Gradio ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with gr.Blocks(title="AI Humanizer") as demo: | |
| gr.Markdown("# AI Text Humanizer\nPaste AI text. Same length back, undetectable.") | |
| with gr.Tabs(): | |
| with gr.Tab("Humanize"): | |
| with gr.Row(): | |
| h_in = gr.Textbox(lines=12, label="Paste AI text", placeholder="Paste ChatGPT/Claude output...") | |
| h_out = gr.Textbox(lines=12, label="Humanized output", interactive=False) | |
| h_stats = gr.Markdown("") | |
| h_det = gr.Markdown("") | |
| with gr.Row(): | |
| gr.Button("Clear").click(fn=lambda: ("", "", "", ""), outputs=[h_in, h_out, h_stats, h_det]) | |
| gr.Button("Humanize", variant="primary").click( | |
| fn=humanize_stream, inputs=[h_in], outputs=[h_out, h_stats, h_det]) | |
| with gr.Tab("Check Detection"): | |
| d_in = gr.Textbox(lines=12, label="Text to check", placeholder="Paste text...") | |
| d_out = gr.Markdown("") | |
| with gr.Row(): | |
| gr.Button("Clear").click(fn=lambda: ("", ""), outputs=[d_in, d_out]) | |
| gr.Button("Check", variant="primary").click(fn=detect_only, inputs=[d_in], outputs=[d_out]) | |
| with gr.Accordion("API for React", open=False): | |
| gr.Markdown(""" | |
| ```javascript | |
| const API = "https://rofati-ai-humanizer.hf.space"; | |
| // Humanize | |
| const res = await fetch(`${API}/api/humanize`, { | |
| method: "POST", | |
| headers: {"Content-Type": "application/json"}, | |
| body: JSON.stringify({text: "your AI text"}) | |
| }); | |
| // Detect | |
| const res = await fetch(`${API}/api/detect`, { | |
| method: "POST", | |
| headers: {"Content-Type": "application/json"}, | |
| body: JSON.stringify({text: "text to check"}) | |
| }); | |
| ``` | |
| """) | |
| app = gr.mount_gradio_app(app, demo, path="/") | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("GRADIO_SERVER_PORT", 7860))) | |