Update app.py
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@ import os
|
|
| 2 |
import tempfile
|
| 3 |
from pathlib import Path
|
| 4 |
from typing import List
|
|
|
|
| 5 |
|
| 6 |
import gradio as gr
|
| 7 |
import pandas as pd
|
|
@@ -158,24 +159,53 @@ def document_summary(df: pd.DataFrame) -> pd.DataFrame:
|
|
| 158 |
|
| 159 |
|
| 160 |
# =========================
|
| 161 |
-
# VISUALIZATION
|
| 162 |
# =========================
|
| 163 |
-
def
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
-
|
| 167 |
-
ax.
|
| 168 |
-
ax.
|
| 169 |
-
|
| 170 |
-
ax.
|
| 171 |
-
ax.
|
| 172 |
|
| 173 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
|
| 174 |
-
fig.savefig(tmp.name, bbox_inches="tight")
|
| 175 |
-
|
| 176 |
|
| 177 |
plt.close(fig)
|
| 178 |
-
return
|
| 179 |
|
| 180 |
|
| 181 |
# =========================
|
|
@@ -203,9 +233,14 @@ def run_detector(text_input: str, uploaded_files, progress=gr.Progress()):
|
|
| 203 |
|
| 204 |
df = classify_chunks(chunks, progress)
|
| 205 |
final_df = document_summary(df)
|
| 206 |
-
plot_path = generate_confidence_plot(final_df)
|
| 207 |
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
|
| 210 |
|
| 211 |
# =========================
|
|
@@ -214,8 +249,8 @@ def run_detector(text_input: str, uploaded_files, progress=gr.Progress()):
|
|
| 214 |
with gr.Blocks(title="🧪 Offline AI Document Detector") as app:
|
| 215 |
gr.Markdown("## 🧪 Offline AI Document Detector")
|
| 216 |
gr.Markdown(
|
| 217 |
-
"Detect whether
|
| 218 |
-
"Supports **PDF, DOCX, TXT, and pasted text**. Optimized for CPU-only
|
| 219 |
)
|
| 220 |
|
| 221 |
text_input = gr.Textbox(
|
|
@@ -231,12 +266,12 @@ with gr.Blocks(title="🧪 Offline AI Document Detector") as app:
|
|
| 231 |
|
| 232 |
analyze_btn = gr.Button("🔍 Analyze")
|
| 233 |
output_table = gr.Dataframe(label="📊 Detection Results")
|
| 234 |
-
|
| 235 |
|
| 236 |
analyze_btn.click(
|
| 237 |
fn=run_detector,
|
| 238 |
inputs=[text_input, file_input],
|
| 239 |
-
outputs=[output_table,
|
| 240 |
)
|
| 241 |
|
| 242 |
if __name__ == "__main__":
|
|
|
|
| 2 |
import tempfile
|
| 3 |
from pathlib import Path
|
| 4 |
from typing import List
|
| 5 |
+
import numpy as np
|
| 6 |
|
| 7 |
import gradio as gr
|
| 8 |
import pandas as pd
|
|
|
|
| 159 |
|
| 160 |
|
| 161 |
# =========================
|
| 162 |
+
# GAUGE VISUALIZATION
|
| 163 |
# =========================
|
| 164 |
+
def generate_gauge(prob_percent: float, prediction: str) -> str:
|
| 165 |
+
fig, ax = plt.subplots(figsize=(6, 3))
|
| 166 |
+
|
| 167 |
+
angles = np.linspace(np.pi, 0, 100)
|
| 168 |
+
|
| 169 |
+
# Background arc
|
| 170 |
+
ax.plot(np.cos(angles), np.sin(angles), linewidth=20, alpha=0.15)
|
| 171 |
+
|
| 172 |
+
# Colored arc
|
| 173 |
+
for i, val in enumerate(np.linspace(0, 100, 99)):
|
| 174 |
+
if val < 40:
|
| 175 |
+
color = "green"
|
| 176 |
+
elif val < 70:
|
| 177 |
+
color = "orange"
|
| 178 |
+
else:
|
| 179 |
+
color = "red"
|
| 180 |
+
|
| 181 |
+
ax.plot(
|
| 182 |
+
np.cos(angles[i:i + 2]),
|
| 183 |
+
np.sin(angles[i:i + 2]),
|
| 184 |
+
linewidth=20,
|
| 185 |
+
color=color
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
# Needle
|
| 189 |
+
needle_angle = np.pi * (1 - prob_percent / 100)
|
| 190 |
+
ax.plot(
|
| 191 |
+
[0, 0.8 * np.cos(needle_angle)],
|
| 192 |
+
[0, 0.8 * np.sin(needle_angle)],
|
| 193 |
+
linewidth=4
|
| 194 |
+
)
|
| 195 |
|
| 196 |
+
# Text
|
| 197 |
+
ax.text(0, -0.1, f"{prob_percent:.0f}%", ha="center", va="center", fontsize=24, weight="bold")
|
| 198 |
+
ax.text(0, -0.32, prediction, ha="center", va="center", fontsize=12)
|
| 199 |
+
|
| 200 |
+
ax.set_aspect("equal")
|
| 201 |
+
ax.axis("off")
|
| 202 |
|
| 203 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
|
| 204 |
+
fig.savefig(tmp.name, bbox_inches="tight", dpi=150)
|
| 205 |
+
path = tmp.name
|
| 206 |
|
| 207 |
plt.close(fig)
|
| 208 |
+
return path
|
| 209 |
|
| 210 |
|
| 211 |
# =========================
|
|
|
|
| 233 |
|
| 234 |
df = classify_chunks(chunks, progress)
|
| 235 |
final_df = document_summary(df)
|
|
|
|
| 236 |
|
| 237 |
+
summary_row = final_df[final_df["Text Chunk"] == "📄 Document Summary"].iloc[0]
|
| 238 |
+
gauge_path = generate_gauge(
|
| 239 |
+
summary_row["AI Probability (%)"],
|
| 240 |
+
summary_row["Prediction"]
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
return final_df, gauge_path
|
| 244 |
|
| 245 |
|
| 246 |
# =========================
|
|
|
|
| 249 |
with gr.Blocks(title="🧪 Offline AI Document Detector") as app:
|
| 250 |
gr.Markdown("## 🧪 Offline AI Document Detector")
|
| 251 |
gr.Markdown(
|
| 252 |
+
"Detect whether content is AI-generated using an **offline, open-source model**. "
|
| 253 |
+
"Supports **PDF, DOCX, TXT, and pasted text**. Optimized for **CPU-only Hugging Face Spaces**."
|
| 254 |
)
|
| 255 |
|
| 256 |
text_input = gr.Textbox(
|
|
|
|
| 266 |
|
| 267 |
analyze_btn = gr.Button("🔍 Analyze")
|
| 268 |
output_table = gr.Dataframe(label="📊 Detection Results")
|
| 269 |
+
gauge_plot = gr.Image(label="🧠 AI Probability Gauge")
|
| 270 |
|
| 271 |
analyze_btn.click(
|
| 272 |
fn=run_detector,
|
| 273 |
inputs=[text_input, file_input],
|
| 274 |
+
outputs=[output_table, gauge_plot]
|
| 275 |
)
|
| 276 |
|
| 277 |
if __name__ == "__main__":
|