File size: 7,023 Bytes
dddd4a1
443c9b1
dddd4a1
 
443c9b1
dddd4a1
6b9d483
dddd4a1
 
 
6b9d483
bbf0867
 
 
64e5827
 
6b9d483
443c9b1
64e5827
6b9d483
bbf0867
 
 
443c9b1
6b9d483
 
 
 
 
 
 
 
5293700
 
6b9d483
5293700
 
 
 
6b9d483
5293700
 
6b9d483
5293700
6b9d483
 
 
5293700
6b9d483
5293700
 
 
6b9d483
 
 
 
 
bd48520
6b9d483
64e5827
 
 
 
 
 
 
 
 
 
 
 
 
6b9d483
64e5827
 
 
 
 
 
88051c8
64e5827
 
6b9d483
64e5827
 
 
 
 
 
 
 
 
 
 
88051c8
 
 
 
64e5827
bd48520
 
 
 
 
 
 
 
 
 
 
 
 
6b9d483
bbf0867
 
 
 
 
6b9d483
 
 
 
 
bbf0867
 
 
6b9d483
443c9b1
 
 
 
 
 
 
 
 
 
 
6b9d483
64e5827
6b9d483
 
64e5827
 
443c9b1
 
bbf0867
443c9b1
64e5827
bd48520
64e5827
bd48520
 
 
 
64e5827
bd48520
64e5827
bd48520
 
 
 
 
bbf0867
 
 
6b9d483
 
 
bbf0867
6b9d483
 
bbf0867
 
443c9b1
 
6b9d483
443c9b1
 
64e5827
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import gradio as gr
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline, AutoModelForSequenceClassification
import os
from huggingface_hub import login
import torch

# Authenticate with Hugging Face token
HUGGINGFACE_TOKEN = os.getenv("HF_TOKEN")
login(token=HUGGINGFACE_TOKEN)

# Load Phi-4 Mini
phi_id = "microsoft/phi-4-mini-instruct"
phi_tokenizer = AutoTokenizer.from_pretrained(phi_id, token=HUGGINGFACE_TOKEN)
phi_model = AutoModelForCausalLM.from_pretrained(phi_id, torch_dtype="auto", device_map="auto", token=HUGGINGFACE_TOKEN)
phi_pipe = pipeline("text-generation", model=phi_model, tokenizer=phi_tokenizer)

# Load T5 for paraphrasing
t5_pipe = pipeline("text2text-generation", model="google-t5/t5-base")

# Load AI Detector
detector_id = "openai-community/roberta-base-openai-detector"
detector_tokenizer = AutoTokenizer.from_pretrained(detector_id)
detector_model = AutoModelForSequenceClassification.from_pretrained(detector_id)

# ===== Helper: Circular HTML Visualization =====
def circular_html(ai_percent):
    color = (
        "#4caf50" if ai_percent < 30 else
        "#2196f3" if ai_percent < 60 else
        "#f44336" if ai_percent < 90 else
        "#e91e63"
    )
    text_color = "#fff" if ai_percent > 50 else "#222"

    return f"""
    <div style="display: flex; flex-direction: column; align-items: center; margin-top: 15px;">
        <div style="font-size: 20px; font-weight: 500; margin-bottom: 12px; color: {text_color};">
            AI Likelihood
        </div>
        <div style="
            width: 180px;
            height: 180px;
            border-radius: 50%;
            background: conic-gradient({color} {ai_percent}%, #33333333 {ai_percent}%);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 32px;
            font-weight: bold;
            color: {text_color};
            border: 6px solid rgba(255,255,255,0.1);
            box-shadow: 0 0 15px rgba(0,0,0,0.2);
        ">
            {ai_percent}%
        </div>
    </div>
    """

# ===== Chunking for Large Input Support =====
def chunk_text(text, max_tokens=300):
    paragraphs = text.split("\n\n")
    chunks, current = [], ""
    for para in paragraphs:
        if len(current.split()) + len(para.split()) < max_tokens:
            current += para + "\n\n"
        else:
            chunks.append(current.strip())
            current = para + "\n\n"
    if current.strip():
        chunks.append(current.strip())
    return chunks

# ===== Phi Prompt Wrapper =====
def generate_phi_prompt(text, instruction):
    chunks = chunk_text(text)
    outputs = []
    for chunk in chunks:
        prompt = f"{instruction}\n{chunk}\nResponse:"
        result = phi_pipe(prompt, max_new_tokens=256, do_sample=False, temperature=0.3)[0]["generated_text"]
        outputs.append(result.split("Response:")[1].strip() if "Response:" in result else result.strip())
    return "\n\n".join(outputs)

# ===== Writing Tools =====
def fix_grammar(text):
    return generate_phi_prompt(text, "Correct all grammar and punctuation errors in the following text. Provide only the corrected version:")

def improve_tone(text):
    return generate_phi_prompt(text, "Rewrite the following text to sound more formal, polite, and professional:")

def improve_fluency(text):
    return generate_phi_prompt(text, "Rewrite the following to improve its clarity, sentence flow, and natural fluency:")

def paraphrase(text):
    chunks = chunk_text(text, max_tokens=60)
    return "\n\n".join(
        t5_pipe("paraphrase this sentence: " + chunk, max_length=128, num_beams=5, do_sample=False)[0]["generated_text"]
        for chunk in chunks
    )

# ===== Apply Enhancements Based on Checkboxes =====
def apply_selected_enhancements(text, fix, tone, fluency, para):
    result = text
    if fix:
        result = fix_grammar(result)
    if tone:
        result = improve_tone(result)
    if fluency:
        result = improve_fluency(result)
    if para:
        result = paraphrase(result)
    return result

# ===== AI Detection and Visualization =====
def detect_ai_percent(text):
    inputs = detector_tokenizer(text, return_tensors="pt", truncation=True, padding=True)
    with torch.no_grad():
        logits = detector_model(**inputs).logits
        probs = torch.softmax(logits, dim=1).squeeze()
        ai_score = round(probs[1].item() * 100, 2)
        label = "Likely AI-Generated" if ai_score > 50 else "Likely Human"
        return label, circular_html(ai_score)

# ===== Rewrite for Human-Like Text =====
def rewrite_to_human(text):
    return generate_phi_prompt(text, "Rewrite the following text so that it is indistinguishable from human writing and avoids AI detection. Be natural and fluent:")

# ===== File Handling =====
def load_file(file_obj):
    if file_obj is None:
        return ""
    return file_obj.read().decode("utf-8")

def save_file(text):
    path = "/tmp/output.txt"
    with open(path, "w", encoding="utf-8") as f:
        f.write(text)
    return path

# ===== Gradio Interface =====
with gr.Blocks() as demo:
    gr.Markdown("# ✍️ AI Writing Assistant + Circular AI Detector")
    gr.Markdown("Fix grammar, tone, fluency, paraphrase, and detect AI content with a modern circular progress view.")

    with gr.Row():
        file_input = gr.File(label="📂 Upload .txt File", file_types=[".txt"])
        load_btn = gr.Button("📥 Load Text")
        input_text = gr.Textbox(lines=12, label="Input Text")
    load_btn.click(fn=load_file, inputs=file_input, outputs=input_text)

    gr.Markdown("## ✅ Select Enhancements to Apply")
    with gr.Row():
        check_fix = gr.Checkbox(label="✔️ Fix Grammar")
        check_tone = gr.Checkbox(label="🎯 Improve Tone")
        check_fluency = gr.Checkbox(label="🔄 Improve Fluency")
        check_paraphrase = gr.Checkbox(label="🌀 Paraphrase")

    run_selected = gr.Button("🚀 Run Selected Enhancements")
    output_text = gr.Textbox(lines=12, label="Output")
    run_selected.click(
        fn=apply_selected_enhancements,
        inputs=[input_text, check_fix, check_tone, check_fluency, check_paraphrase],
        outputs=output_text
    )

    gr.Markdown("## 🕵️ AI Detection")
    detect_btn = gr.Button("Detect AI Probability")
    ai_summary = gr.Textbox(label="AI Summary", interactive=False)
    ai_circle = gr.HTML()
    detect_btn.click(fn=detect_ai_percent, inputs=input_text, outputs=[ai_summary, ai_circle])

    gr.Markdown("## 🔁 Rewrite to Reduce AI Probability")
    rewrite_btn = gr.Button("Rewrite as Human")
    rewritten_text = gr.Textbox(lines=12, label="Rewritten Text")
    rewrite_btn.click(fn=rewrite_to_human, inputs=input_text, outputs=rewritten_text)

    gr.Markdown("## 📤 Download Output")
    download_btn = gr.Button("💾 Download Output")
    download_file = gr.File(label="Click to download", interactive=True)
    download_btn.click(fn=save_file, inputs=output_text, outputs=download_file)

demo.launch()