Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import gradio as gr
|
3 |
+
import speech_recognition as sr
|
4 |
+
import time
|
5 |
+
import difflib
|
6 |
+
import random
|
7 |
+
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
|
8 |
+
from happytransformer import HappyTextToText, TTSettings
|
9 |
+
|
10 |
+
# Constants
|
11 |
+
MODEL_NAME = "prithivida/grammar_error_correcter_v1"
|
12 |
+
CSS = """
|
13 |
+
.gradio-container { max-width: 1400px !important; }
|
14 |
+
.header { text-align: center; padding: 2rem; background: linear-gradient(135deg, #3b82f6, #6366f1); color: white; border-radius: 15px; }
|
15 |
+
#container { height: 500px; width: 100%; background: #1a1a1a; border-radius: 10px; }
|
16 |
+
.diff-ins { color: #10b981; background: #d1fae5; padding: 2px 4px; border-radius: 4px; }
|
17 |
+
.diff-del { color: #ef4444; background: #fee2e2; padding: 2px 4px; border-radius: 4px; }
|
18 |
+
"""
|
19 |
+
|
20 |
+
# Three.js Template
|
21 |
+
THREEJS_TEMPLATE = """
|
22 |
+
<div id="container"></div>
|
23 |
+
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
|
24 |
+
<script type="importmap">
|
25 |
+
{
|
26 |
+
"imports": {
|
27 |
+
"three": "https://unpkg.com/[email protected]/build/three.module.js",
|
28 |
+
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
|
29 |
+
}
|
30 |
+
}
|
31 |
+
</script>
|
32 |
+
|
33 |
+
<script type="module">
|
34 |
+
import * as THREE from 'three';
|
35 |
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
36 |
+
|
37 |
+
class GrammarVisualizer {
|
38 |
+
constructor() {
|
39 |
+
this.initScene();
|
40 |
+
this.addLights();
|
41 |
+
this.createGrammarSphere();
|
42 |
+
this.setupControls();
|
43 |
+
this.animate();
|
44 |
+
}
|
45 |
+
|
46 |
+
initScene() {
|
47 |
+
this.scene = new THREE.Scene();
|
48 |
+
this.camera = new THREE.PerspectiveCamera(75, 500/400, 0.1, 1000);
|
49 |
+
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
50 |
+
document.getElementById('container').appendChild(this.renderer.domElement);
|
51 |
+
this.renderer.setSize(500, 400);
|
52 |
+
this.camera.position.z = 5;
|
53 |
+
}
|
54 |
+
|
55 |
+
addLights() {
|
56 |
+
const ambient = new THREE.AmbientLight(0x404040);
|
57 |
+
const directional = new THREE.DirectionalLight(0xffffff, 1);
|
58 |
+
directional.position.set(5, 5, 5);
|
59 |
+
this.scene.add(ambient, directional);
|
60 |
+
}
|
61 |
+
|
62 |
+
createGrammarSphere() {
|
63 |
+
const geometry = new THREE.SphereGeometry(2, 32, 32);
|
64 |
+
this.material = new THREE.MeshPhongMaterial({
|
65 |
+
color: 0x3b82f6,
|
66 |
+
transparent: true,
|
67 |
+
opacity: 0.9
|
68 |
+
});
|
69 |
+
this.sphere = new THREE.Mesh(geometry, this.material);
|
70 |
+
this.scene.add(this.sphere);
|
71 |
+
}
|
72 |
+
|
73 |
+
setupControls() {
|
74 |
+
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
75 |
+
this.controls.enableDamping = true;
|
76 |
+
this.controls.dampingFactor = 0.05;
|
77 |
+
}
|
78 |
+
|
79 |
+
animate() {
|
80 |
+
requestAnimationFrame(() => this.animate());
|
81 |
+
this.controls.update();
|
82 |
+
this.renderer.render(this.scene, this.camera);
|
83 |
+
}
|
84 |
+
|
85 |
+
updateVisuals(score) {
|
86 |
+
const hue = score / 100;
|
87 |
+
this.material.color.setHSL(hue, 0.8, 0.5);
|
88 |
+
this.sphere.rotation.x = (score / 50) * Math.PI;
|
89 |
+
this.sphere.rotation.y = (score / 75) * Math.PI;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
let visualizer;
|
94 |
+
window.addEventListener('DOMContentLoaded', () => {
|
95 |
+
visualizer = new GrammarVisualizer();
|
96 |
+
});
|
97 |
+
|
98 |
+
window.updateGrammarVisuals = (score) => {
|
99 |
+
if(visualizer) visualizer.updateVisuals(score);
|
100 |
+
};
|
101 |
+
</script>
|
102 |
+
"""
|
103 |
+
|
104 |
+
# Load models
|
105 |
+
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
|
106 |
+
model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)
|
107 |
+
happy_tt = HappyTextToText("T5", MODEL_NAME)
|
108 |
+
|
109 |
+
def create_diff_html(original, corrected):
|
110 |
+
d = difflib.Differ()
|
111 |
+
diff = d.compare(original.split(), corrected.split())
|
112 |
+
return " ".join([
|
113 |
+
f'<span class="diff-ins">{p[2:]}</span> ' if p.startswith('+ ') else
|
114 |
+
f'<span class="diff-del">{p[2:]}</span> ' if p.startswith('- ') else
|
115 |
+
f'{p[2:]} ' for p in diff
|
116 |
+
])
|
117 |
+
|
118 |
+
def analyze_grammar(text):
|
119 |
+
inputs = tokenizer.encode("gec: " + text, return_tensors="pt", max_length=256, truncation=True)
|
120 |
+
with torch.no_grad():
|
121 |
+
outputs = model.generate(inputs, max_length=256, num_beams=5)
|
122 |
+
correction = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
123 |
+
|
124 |
+
args = TTSettings(num_beams=5, min_length=1)
|
125 |
+
happy_correction = happy_tt.generate_text("gec: " + text, args=args).text
|
126 |
+
|
127 |
+
final_correction = happy_correction if len(happy_correction) > len(correction) else correction
|
128 |
+
changes = sum(1 for a, b in zip(text.split(), final_correction.split()) if a != b)
|
129 |
+
score = max(0, 100 - (changes * 2))
|
130 |
+
|
131 |
+
return {
|
132 |
+
"original": text,
|
133 |
+
"corrected": final_correction,
|
134 |
+
"score": score,
|
135 |
+
"diff_html": create_diff_html(text, final_correction)
|
136 |
+
}
|
137 |
+
|
138 |
+
def process_input(audio_path, text):
|
139 |
+
if audio_path:
|
140 |
+
recognizer = sr.Recognizer()
|
141 |
+
with sr.AudioFile(audio_path) as source:
|
142 |
+
audio = recognizer.record(source)
|
143 |
+
try:
|
144 |
+
text = recognizer.recognize_google(audio)
|
145 |
+
except sr.UnknownValueError:
|
146 |
+
return "Could not understand audio", 0, "", ""
|
147 |
+
|
148 |
+
if not text.strip():
|
149 |
+
return "No input", 0, "", ""
|
150 |
+
|
151 |
+
results = analyze_grammar(text)
|
152 |
+
return [
|
153 |
+
results['original'],
|
154 |
+
results['score'],
|
155 |
+
results['diff_html'],
|
156 |
+
f"<script>window.updateGrammarVisuals({results['score']})</script>"
|
157 |
+
]
|
158 |
+
|
159 |
+
with gr.Blocks(css=CSS) as app:
|
160 |
+
gr.Markdown("""
|
161 |
+
<div class="header">
|
162 |
+
<h1>π 3D Grammar Analyzer Pro</h1>
|
163 |
+
<p>Interactive AI-Powered Writing Assistant with 3D Visualization</p>
|
164 |
+
</div>
|
165 |
+
""")
|
166 |
+
|
167 |
+
with gr.Row():
|
168 |
+
with gr.Column(scale=1):
|
169 |
+
gr.Markdown("### Input Section")
|
170 |
+
audio_input = gr.Audio(sources=["microphone"], type="filepath", label="π€ Voice Input")
|
171 |
+
text_input = gr.Textbox(lines=5, placeholder="π Type your text here...", label="Text Input")
|
172 |
+
submit_btn = gr.Button("π Analyze Text", variant="primary")
|
173 |
+
|
174 |
+
with gr.Column(scale=2):
|
175 |
+
gr.Markdown("### 3D Visualization")
|
176 |
+
threejs = gr.HTML(THREEJS_TEMPLATE)
|
177 |
+
|
178 |
+
with gr.Row():
|
179 |
+
grammar_score = gr.Number(label="π Grammar Score", precision=0)
|
180 |
+
score_gauge = gr.BarPlot(x=["Score"], y=[0], color="#3b82f6", height=150)
|
181 |
+
|
182 |
+
diff_output = gr.HTML(label="π Text Corrections")
|
183 |
+
hidden_trigger = gr.HTML(visible=False)
|
184 |
+
|
185 |
+
gr.Markdown("### Example Sentences")
|
186 |
+
gr.Examples(
|
187 |
+
examples=[
|
188 |
+
["I is going to the park yesterday."],
|
189 |
+
["Their happy about there test results."],
|
190 |
+
["She dont like apples, but she like bananas."]
|
191 |
+
],
|
192 |
+
inputs=[text_input],
|
193 |
+
outputs=[text_input, grammar_score, diff_output, hidden_trigger],
|
194 |
+
fn=process_input,
|
195 |
+
cache_examples=True
|
196 |
+
)
|
197 |
+
|
198 |
+
submit_btn.click(
|
199 |
+
fn=process_input,
|
200 |
+
inputs=[audio_input, text_input],
|
201 |
+
outputs=[text_input, grammar_score, diff_output, hidden_trigger]
|
202 |
+
)
|
203 |
+
|
204 |
+
text_input.change(
|
205 |
+
lambda x: analyze_grammar(x)["score"] if x else 0,
|
206 |
+
inputs=text_input,
|
207 |
+
outputs=grammar_score
|
208 |
+
)
|
209 |
+
|
210 |
+
if __name__ == "__main__":
|
211 |
+
app.launch(
|
212 |
+
server_name="0.0.0.0",
|
213 |
+
server_port=7860,
|
214 |
+
share=True,
|
215 |
+
favicon_path="https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/logo.png"
|
216 |
+
)
|