Mayank14 commited on
Commit
0a148ee
·
1 Parent(s): 71fe3d8

Setup for Hugging Face Spaces deployment

Browse files
Files changed (5) hide show
  1. Dockerfile +31 -0
  2. README.md +19 -2
  3. app.py +43 -51
  4. requirements.txt +4 -3
  5. static/js/script.js +45 -40
Dockerfile ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ build-essential \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Install Python dependencies
11
+ COPY requirements.txt .
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Copy only necessary files
15
+ COPY app.py .
16
+ COPY templates/ templates/
17
+ COPY static/ static/
18
+
19
+ # Create non-root user for security
20
+ RUN useradd -m appuser && chown -R appuser:appuser /app
21
+ USER appuser
22
+
23
+ # Environment variables
24
+ ENV PORT=7860
25
+ ENV PYTHONUNBUFFERED=1
26
+
27
+ # Expose the port the app runs on
28
+ EXPOSE 7860
29
+
30
+ # Command to run the application
31
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,3 +1,12 @@
 
 
 
 
 
 
 
 
 
1
  # Dyslexia AI Assistant
2
 
3
  A web application that simplifies complex text using AI to help people with dyslexia and reading difficulties.
@@ -41,8 +50,7 @@ A web application that simplifies complex text using AI to help people with dysl
41
  4. **Use TTS buttons** to hear the text read aloud
42
  5. **Toggle "Dyslexia-Friendly"** for enhanced readability
43
 
44
-
45
- ## API
46
 
47
  ### POST /simplify
48
  Simplifies text using the T5 model.
@@ -77,6 +85,15 @@ Dyslexia AI Assistant/
77
  └── t5_simplifier_model/ # AI model files
78
  ```
79
 
 
 
 
 
 
 
 
 
 
80
  ## Troubleshooting
81
 
82
  **Model not loading?**
 
1
+ ---
2
+ title: Dyslexia AI Assistant
3
+ emoji: 🧠
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ app_port: 7860
8
+ ---
9
+
10
  # Dyslexia AI Assistant
11
 
12
  A web application that simplifies complex text using AI to help people with dyslexia and reading difficulties.
 
50
  4. **Use TTS buttons** to hear the text read aloud
51
  5. **Toggle "Dyslexia-Friendly"** for enhanced readability
52
 
53
+ ## API Endpoints
 
54
 
55
  ### POST /simplify
56
  Simplifies text using the T5 model.
 
85
  └── t5_simplifier_model/ # AI model files
86
  ```
87
 
88
+ ## Development
89
+
90
+ This application is built with:
91
+ - Flask (Backend)
92
+ - T5 Transformer Model
93
+ - HTML/CSS/JavaScript (Frontend)
94
+
95
+ The model is hosted on Hugging Face Hub at: [Mayank14/t5-simplifier-model](https://huggingface.co/Mayank14/t5-simplifier-model)
96
+
97
  ## Troubleshooting
98
 
99
  **Model not loading?**
app.py CHANGED
@@ -1,54 +1,52 @@
1
- from flask import Flask, jsonify, request, render_template
2
  from flask_cors import CORS
3
- from transformers import T5Tokenizer, T5ForConditionalGeneration
4
  import torch
5
  import logging
6
  import os
7
 
8
  app = Flask(__name__)
9
- CORS(app) # Enable CORS for frontend integration
10
 
11
  # Configure logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
- # Load model with error handling
16
- model_path = "t5_simplifier_model/t5_simplifier_model"
17
  try:
18
- logger.info("Loading T5 model and tokenizer...")
19
- tokenizer = T5Tokenizer.from_pretrained(model_path)
20
- model = T5ForConditionalGeneration.from_pretrained(model_path)
 
21
  logger.info("Model loaded successfully!")
22
  except Exception as e:
23
  logger.error(f"Failed to load model: {e}")
24
  raise
25
 
 
 
 
 
26
  @app.route('/simplify', methods=['POST'])
27
  def simplify():
28
  try:
29
- # Validate request
30
- if not request.is_json:
31
- return jsonify({"error": "Request must be JSON"}), 400
32
-
33
  data = request.get_json()
34
- if not data:
35
- return jsonify({"error": "No JSON data provided"}), 400
36
-
37
- text = data.get("text", "").strip()
38
  if not text:
39
- return jsonify({"error": "No text provided"}), 400
40
-
41
- if len(text) > 2000: # Reasonable limit
42
- return jsonify({"error": "Text too long. Please limit to 2000 characters."}), 400
43
-
44
- logger.info(f"Simplifying text of length: {len(text)}")
45
-
46
- # Prepare input for T5 model
47
  input_text = "simplify: " + text
48
  inputs = tokenizer.encode(
49
- input_text,
50
- return_tensors="pt",
51
- max_length=512,
52
  truncation=True,
53
  padding=True
54
  )
@@ -56,44 +54,38 @@ def simplify():
56
  # Generate simplified text
57
  with torch.no_grad():
58
  outputs = model.generate(
59
- inputs,
60
- max_length=512,
61
- num_beams=4,
62
  early_stopping=True,
63
  do_sample=False,
64
  temperature=0.7
65
  )
66
-
67
  simplified_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
68
 
69
- # Clean up the output (remove the "simplify:" prefix if present)
70
  if simplified_text.lower().startswith("simplify:"):
71
  simplified_text = simplified_text[9:].strip()
72
-
73
- logger.info("Text simplified successfully")
74
  return jsonify({
75
- "simplified_text": simplified_text,
76
- "original_length": len(text),
77
- "simplified_length": len(simplified_text)
78
  })
79
-
80
  except Exception as e:
81
  logger.error(f"Error in simplify endpoint: {e}")
82
- return jsonify({"error": "Internal server error"}), 500
83
 
84
- @app.route('/health', methods=['GET'])
85
- def health_check():
86
- """Health check endpoint"""
87
  return jsonify({
88
- "status": "healthy",
89
- "model_loaded": True,
90
- "service": "T5 Text Simplifier"
91
  })
92
 
93
- @app.route('/', methods=['GET'])
94
- def index():
95
- """Serve the main application page"""
96
- return render_template('index.html')
97
-
98
  if __name__ == '__main__':
99
- app.run(debug=True, host='0.0.0.0', port=5000)
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
  from flask_cors import CORS
3
+ from transformers import T5ForConditionalGeneration, T5Tokenizer
4
  import torch
5
  import logging
6
  import os
7
 
8
  app = Flask(__name__)
9
+ CORS(app)
10
 
11
  # Configure logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
+ # Load model from Hugging Face Hub
 
16
  try:
17
+ logger.info("Loading model from Hugging Face Hub...")
18
+ model_id = "Mayank14/t5-simplifier-model"
19
+ tokenizer = T5Tokenizer.from_pretrained(model_id)
20
+ model = T5ForConditionalGeneration.from_pretrained(model_id)
21
  logger.info("Model loaded successfully!")
22
  except Exception as e:
23
  logger.error(f"Failed to load model: {e}")
24
  raise
25
 
26
+ @app.route('/')
27
+ def index():
28
+ return render_template('index.html')
29
+
30
  @app.route('/simplify', methods=['POST'])
31
  def simplify():
32
  try:
 
 
 
 
33
  data = request.get_json()
34
+ if not data or 'text' not in data:
35
+ return jsonify({'error': 'No text provided'}), 400
36
+
37
+ text = data['text'].strip()
38
  if not text:
39
+ return jsonify({'error': 'Empty text provided'}), 400
40
+
41
+ if len(text) > 2000:
42
+ return jsonify({'error': 'Text too long. Please limit to 2000 characters.'}), 400
43
+
44
+ # Prepare input
 
 
45
  input_text = "simplify: " + text
46
  inputs = tokenizer.encode(
47
+ input_text,
48
+ return_tensors="pt",
49
+ max_length=512,
50
  truncation=True,
51
  padding=True
52
  )
 
54
  # Generate simplified text
55
  with torch.no_grad():
56
  outputs = model.generate(
57
+ inputs,
58
+ max_length=512,
59
+ num_beams=4,
60
  early_stopping=True,
61
  do_sample=False,
62
  temperature=0.7
63
  )
64
+
65
  simplified_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
66
 
67
+ # Clean up output
68
  if simplified_text.lower().startswith("simplify:"):
69
  simplified_text = simplified_text[9:].strip()
70
+
 
71
  return jsonify({
72
+ 'simplified_text': simplified_text,
73
+ 'original_length': len(text),
74
+ 'simplified_length': len(simplified_text)
75
  })
76
+
77
  except Exception as e:
78
  logger.error(f"Error in simplify endpoint: {e}")
79
+ return jsonify({'error': 'Internal server error'}), 500
80
 
81
+ @app.route('/health')
82
+ def health():
 
83
  return jsonify({
84
+ 'status': 'healthy',
85
+ 'model_loaded': True
 
86
  })
87
 
 
 
 
 
 
88
  if __name__ == '__main__':
89
+ # Get port from environment variable for Hugging Face Spaces
90
+ port = int(os.environ.get('PORT', 7860))
91
+ app.run(host='0.0.0.0', port=port)
requirements.txt CHANGED
@@ -1,8 +1,8 @@
1
  # T5 Text Simplifier - Python Dependencies
2
 
3
  # Web Framework
4
- Flask
5
- Flask-CORS
6
 
7
  # Machine Learning
8
  torch
@@ -11,4 +11,5 @@ tokenizers
11
 
12
  # Additional utilities
13
  numpy
14
- requests
 
 
1
  # T5 Text Simplifier - Python Dependencies
2
 
3
  # Web Framework
4
+ flask
5
+ flask-cors
6
 
7
  # Machine Learning
8
  torch
 
11
 
12
  # Additional utilities
13
  numpy
14
+ requests
15
+ huggingface-hub
static/js/script.js CHANGED
@@ -1,44 +1,48 @@
1
  // T5 Text Simplifier - JavaScript Functionality
2
  class TextSimplifier {
3
  constructor() {
 
 
4
  this.isDyslexiaMode = false;
5
  this.isProcessing = false;
6
  this.speechSynthesis = window.speechSynthesis;
7
  this.currentUtterance = null;
8
  this.isManuallyStopping = false;
9
 
10
- this.initializeElements();
11
- this.attachEventListeners();
12
  this.initializeTooltip();
13
  this.checkSpeechSupport();
14
  }
15
 
16
- initializeElements() {
17
- // Main elements
18
  this.inputText = document.getElementById('inputText');
 
 
 
 
19
  this.outputText = document.getElementById('outputText');
20
- this.simplifyBtn = document.getElementById('simplifyBtn');
21
- this.statusMessage = document.getElementById('statusMessage');
 
22
 
23
- // Control buttons
 
24
  this.dyslexiaToggle = document.getElementById('dyslexiaToggle');
25
- this.ttsInputBtn = document.getElementById('ttsInput');
26
- this.ttsOutputBtn = document.getElementById('ttsOutput');
27
-
28
- // Action buttons
29
- this.clearInputBtn = document.getElementById('clearInput');
30
- this.clearOutputBtn = document.getElementById('clearOutput');
31
- this.copyInputBtn = document.getElementById('copyInput');
32
- this.copyOutputBtn = document.getElementById('copyOutput');
33
- this.downloadOutputBtn = document.getElementById('downloadOutput');
34
 
35
  // Info elements
36
  this.infoBtn = document.querySelector('.info-btn');
37
  this.infoTooltip = document.getElementById('infoTooltip');
38
- this.closeTooltipBtn = document.querySelector('.close-tooltip');
 
 
 
39
  }
40
 
41
- attachEventListeners() {
42
  // Main functionality
43
  this.simplifyBtn.addEventListener('click', () => this.simplifyText());
44
 
@@ -60,19 +64,19 @@ class TextSimplifier {
60
  this.dyslexiaToggle.addEventListener('click', () => this.toggleDyslexiaMode());
61
 
62
  // Text-to-Speech
63
- this.ttsInputBtn.addEventListener('click', () => this.handleTTSButtonClick('input'));
64
- this.ttsOutputBtn.addEventListener('click', () => this.handleTTSButtonClick('output'));
65
 
66
  // Action buttons
67
- this.clearInputBtn.addEventListener('click', () => this.clearText(this.inputText));
68
- this.clearOutputBtn.addEventListener('click', () => this.clearText(this.outputText));
69
- this.copyInputBtn.addEventListener('click', () => this.copyToClipboard(this.inputText.value, 'Input text'));
70
- this.copyOutputBtn.addEventListener('click', () => this.copyToClipboard(this.outputText.value, 'Simplified text'));
71
- this.downloadOutputBtn.addEventListener('click', () => this.downloadText());
72
 
73
  // Info tooltip
74
  this.infoBtn.addEventListener('click', () => this.showTooltip());
75
- this.closeTooltipBtn.addEventListener('click', () => this.hideTooltip());
76
 
77
  // Close tooltip on outside click - temporarily disabled to test
78
  // document.addEventListener('click', (e) => {
@@ -114,10 +118,10 @@ class TextSimplifier {
114
 
115
  checkSpeechSupport() {
116
  if (!this.speechSynthesis) {
117
- this.ttsInputBtn.disabled = true;
118
- this.ttsOutputBtn.disabled = true;
119
- this.ttsInputBtn.title = 'Text-to-speech not supported in this browser';
120
- this.ttsOutputBtn.title = 'Text-to-speech not supported in this browser';
121
  } else {
122
  // Log available voices for debugging
123
  this.logAvailableVoices();
@@ -142,9 +146,9 @@ class TextSimplifier {
142
  }
143
 
144
  async simplifyText() {
145
- const inputText = this.inputText.value.trim();
146
 
147
- if (!inputText) {
148
  this.showStatus('Please enter some text to simplify.', 'warning');
149
  return;
150
  }
@@ -157,12 +161,12 @@ class TextSimplifier {
157
  this.showStatus('Simplifying your text...', 'info');
158
 
159
  try {
160
- const response = await fetch('/simplify', {
161
  method: 'POST',
162
  headers: {
163
  'Content-Type': 'application/json',
164
  },
165
- body: JSON.stringify({ text: inputText })
166
  });
167
 
168
  if (!response.ok) {
@@ -171,12 +175,13 @@ class TextSimplifier {
171
 
172
  const data = await response.json();
173
 
 
 
 
 
174
  if (data.simplified_text) {
175
  this.outputText.value = data.simplified_text;
176
  this.showStatus('Text simplified successfully!', 'success');
177
- // Don't automatically focus output - let user continue typing in input if needed
178
-
179
- // Auto-scroll to output
180
  this.outputText.scrollIntoView({ behavior: 'smooth', block: 'center' });
181
  } else {
182
  throw new Error('No simplified text received from server');
@@ -184,7 +189,7 @@ class TextSimplifier {
184
 
185
  } catch (error) {
186
  console.error('Error simplifying text:', error);
187
- this.showStatus('Failed to simplify text. Please try again.', 'error');
188
  } finally {
189
  this.setProcessingState(false);
190
  }
@@ -332,7 +337,7 @@ class TextSimplifier {
332
  }
333
 
334
  updateTTSButton(type, isSpeaking) {
335
- const button = type === 'input' ? this.ttsInputBtn : this.ttsOutputBtn;
336
  const icon = button.querySelector('i');
337
  const text = button.querySelector('span');
338
 
@@ -421,7 +426,7 @@ class TextSimplifier {
421
  this.infoTooltip.setAttribute('aria-hidden', 'false');
422
 
423
  // Focus management
424
- this.closeTooltipBtn.focus();
425
  }
426
 
427
  hideTooltip() {
 
1
  // T5 Text Simplifier - JavaScript Functionality
2
  class TextSimplifier {
3
  constructor() {
4
+ // Since we're running the Flask server locally, we'll use the local URL
5
+ this.apiUrl = 'http://localhost:5000';
6
  this.isDyslexiaMode = false;
7
  this.isProcessing = false;
8
  this.speechSynthesis = window.speechSynthesis;
9
  this.currentUtterance = null;
10
  this.isManuallyStopping = false;
11
 
12
+ this.setupElements();
13
+ this.setupEventListeners();
14
  this.initializeTooltip();
15
  this.checkSpeechSupport();
16
  }
17
 
18
+ setupElements() {
19
+ // Input elements
20
  this.inputText = document.getElementById('inputText');
21
+ this.clearInput = document.getElementById('clearInput');
22
+ this.copyInput = document.getElementById('copyInput');
23
+
24
+ // Output elements
25
  this.outputText = document.getElementById('outputText');
26
+ this.clearOutput = document.getElementById('clearOutput');
27
+ this.copyOutput = document.getElementById('copyOutput');
28
+ this.downloadOutput = document.getElementById('downloadOutput');
29
 
30
+ // Control elements
31
+ this.simplifyBtn = document.getElementById('simplifyBtn');
32
  this.dyslexiaToggle = document.getElementById('dyslexiaToggle');
33
+ this.ttsInput = document.getElementById('ttsInput');
34
+ this.ttsOutput = document.getElementById('ttsOutput');
 
 
 
 
 
 
 
35
 
36
  // Info elements
37
  this.infoBtn = document.querySelector('.info-btn');
38
  this.infoTooltip = document.getElementById('infoTooltip');
39
+ this.closeTooltip = document.querySelector('.close-tooltip');
40
+
41
+ // Status message
42
+ this.statusMessage = document.getElementById('statusMessage');
43
  }
44
 
45
+ setupEventListeners() {
46
  // Main functionality
47
  this.simplifyBtn.addEventListener('click', () => this.simplifyText());
48
 
 
64
  this.dyslexiaToggle.addEventListener('click', () => this.toggleDyslexiaMode());
65
 
66
  // Text-to-Speech
67
+ this.ttsInput.addEventListener('click', () => this.handleTTSButtonClick('input'));
68
+ this.ttsOutput.addEventListener('click', () => this.handleTTSButtonClick('output'));
69
 
70
  // Action buttons
71
+ this.clearInput.addEventListener('click', () => this.clearText(this.inputText));
72
+ this.clearOutput.addEventListener('click', () => this.clearText(this.outputText));
73
+ this.copyInput.addEventListener('click', () => this.copyToClipboard(this.inputText.value, 'Input text'));
74
+ this.copyOutput.addEventListener('click', () => this.copyToClipboard(this.outputText.value, 'Simplified text'));
75
+ this.downloadOutput.addEventListener('click', () => this.downloadText());
76
 
77
  // Info tooltip
78
  this.infoBtn.addEventListener('click', () => this.showTooltip());
79
+ this.closeTooltip.addEventListener('click', () => this.hideTooltip());
80
 
81
  // Close tooltip on outside click - temporarily disabled to test
82
  // document.addEventListener('click', (e) => {
 
118
 
119
  checkSpeechSupport() {
120
  if (!this.speechSynthesis) {
121
+ this.ttsInput.disabled = true;
122
+ this.ttsOutput.disabled = true;
123
+ this.ttsInput.title = 'Text-to-speech not supported in this browser';
124
+ this.ttsOutput.title = 'Text-to-speech not supported in this browser';
125
  } else {
126
  // Log available voices for debugging
127
  this.logAvailableVoices();
 
146
  }
147
 
148
  async simplifyText() {
149
+ const text = this.inputText.value.trim();
150
 
151
+ if (!text) {
152
  this.showStatus('Please enter some text to simplify.', 'warning');
153
  return;
154
  }
 
161
  this.showStatus('Simplifying your text...', 'info');
162
 
163
  try {
164
+ const response = await fetch(`${this.apiUrl}/simplify`, {
165
  method: 'POST',
166
  headers: {
167
  'Content-Type': 'application/json',
168
  },
169
+ body: JSON.stringify({ text })
170
  });
171
 
172
  if (!response.ok) {
 
175
 
176
  const data = await response.json();
177
 
178
+ if (data.error) {
179
+ throw new Error(data.error);
180
+ }
181
+
182
  if (data.simplified_text) {
183
  this.outputText.value = data.simplified_text;
184
  this.showStatus('Text simplified successfully!', 'success');
 
 
 
185
  this.outputText.scrollIntoView({ behavior: 'smooth', block: 'center' });
186
  } else {
187
  throw new Error('No simplified text received from server');
 
189
 
190
  } catch (error) {
191
  console.error('Error simplifying text:', error);
192
+ this.showStatus(`Failed to simplify text: ${error.message}`, 'error');
193
  } finally {
194
  this.setProcessingState(false);
195
  }
 
337
  }
338
 
339
  updateTTSButton(type, isSpeaking) {
340
+ const button = type === 'input' ? this.ttsInput : this.ttsOutput;
341
  const icon = button.querySelector('i');
342
  const text = button.querySelector('span');
343
 
 
426
  this.infoTooltip.setAttribute('aria-hidden', 'false');
427
 
428
  // Focus management
429
+ this.closeTooltip.focus();
430
  }
431
 
432
  hideTooltip() {