Mohammed Foud commited on
Commit
726a02a
·
1 Parent(s): 0e17555

Add application file

Browse files
Files changed (2) hide show
  1. app.py +122 -47
  2. templates/index.html +63 -74
app.py CHANGED
@@ -1,5 +1,4 @@
1
- # app.py
2
- from flask import Flask, render_template, request, jsonify, send_from_directory
3
  import g4f
4
  import os
5
  import subprocess
@@ -7,6 +6,8 @@ from datetime import datetime
7
  from typing import List, Tuple
8
  import uuid
9
  from werkzeug.utils import secure_filename
 
 
10
 
11
  app = Flask(__name__)
12
  app.config['UPLOAD_FOLDER'] = 'output'
@@ -143,54 +144,128 @@ def index():
143
  models = get_available_models()
144
  return render_template('index.html', models=models)
145
 
146
- @app.route('/generate', methods=['POST'])
147
- def generate():
148
- data = request.json
149
- research_subject = data.get('subject', '').strip()
150
- selected_model = data.get('model', 'gpt-4')
151
- structure_type = data.get('structure', 'automatic')
152
-
153
- if not research_subject:
154
- return jsonify({'error': 'Research subject is required'}), 400
155
-
156
- try:
157
- # Generate filenames
158
- md_filename, docx_filename = generate_filename()
159
 
160
- # Generate sections based on structure type
161
- if structure_type == 'automatic':
162
- try:
163
- sections = generate_automatic_sections(selected_model, research_subject)
164
- except Exception:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  sections = get_manual_sections(research_subject)
 
 
166
  index_content = generate_index_content(selected_model, research_subject, [s[0] for s in sections[1:]])
167
  sections[0] = ("Index", index_content)
168
- else:
169
- sections = get_manual_sections(research_subject)
170
- index_content = generate_index_content(selected_model, research_subject, [s[0] for s in sections[1:]])
171
- sections[0] = ("Index", index_content)
172
-
173
- # Generate the paper
174
- write_research_paper(md_filename, research_subject, sections, selected_model)
175
-
176
- # Convert to Word
177
- try:
178
- convert_to_word(md_filename, docx_filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  except Exception as e:
180
- return jsonify({
181
- 'status': 'partial_success',
182
- 'message': f'Paper generated but Word conversion failed: {str(e)}',
183
- 'md_file': md_filename
184
- })
185
-
186
- return jsonify({
187
- 'status': 'success',
188
- 'docx_file': docx_filename,
189
- 'md_file': md_filename
190
- })
191
-
192
- except Exception as e:
193
- return jsonify({'error': f'Failed to generate paper: {str(e)}'}), 500
194
 
195
  @app.route('/download/<filename>')
196
  def download(filename):
@@ -201,5 +276,5 @@ def download(filename):
201
  as_attachment=True
202
  )
203
 
204
- if __name__ == '__main__':
205
- app.run(debug=True)
 
1
+ from flask import Flask, render_template, request, jsonify, send_from_directory, Response
 
2
  import g4f
3
  import os
4
  import subprocess
 
6
  from typing import List, Tuple
7
  import uuid
8
  from werkzeug.utils import secure_filename
9
+ import json
10
+ import time
11
 
12
  app = Flask(__name__)
13
  app.config['UPLOAD_FOLDER'] = 'output'
 
144
  models = get_available_models()
145
  return render_template('index.html', models=models)
146
 
147
+ @app.route('/stream')
148
+ def stream():
149
+ def generate():
150
+ data = request.args
151
+ research_subject = data.get('subject', '').strip()
152
+ selected_model = data.get('model', 'gpt-4')
153
+ structure_type = data.get('structure', 'automatic')
 
 
 
 
 
 
154
 
155
+ if not research_subject:
156
+ yield "data: " + json.dumps({"error": "Research subject is required"}) + "\n\n"
157
+ return
158
+
159
+ try:
160
+ # Generate filenames
161
+ md_filename, docx_filename = generate_filename()
162
+
163
+ # Define progress steps
164
+ steps = [
165
+ {"id": 0, "text": "Preparing document structure...", "status": "pending"},
166
+ {"id": 1, "text": "Generating index/table of contents...", "status": "pending"},
167
+ {"id": 2, "text": "Writing introduction...", "status": "pending"},
168
+ {"id": 3, "text": "Generating chapters...", "status": "pending"},
169
+ {"id": 4, "text": "Creating conclusion...", "status": "pending"},
170
+ {"id": 5, "text": "Finalizing document...", "status": "pending"},
171
+ {"id": 6, "text": "Converting to Word format...", "status": "pending"}
172
+ ]
173
+
174
+ # Update progress
175
+ def update_step(step_id, status, message=None):
176
+ steps[step_id]["status"] = status
177
+ if message:
178
+ steps[step_id]["message"] = message
179
+ progress = int((step_id + 1) / len(steps) * 100)
180
+ yield "data: " + json.dumps({
181
+ "steps": steps,
182
+ "progress": progress,
183
+ "current_step": step_id
184
+ }) + "\n\n"
185
+
186
+ # Step 0: Prepare
187
+ for update in update_step(0, "in-progress"):
188
+ yield update
189
+
190
+ # Generate sections based on structure type
191
+ if structure_type == 'automatic':
192
+ try:
193
+ for update in update_step(1, "in-progress"):
194
+ yield update
195
+ sections = generate_automatic_sections(selected_model, research_subject)
196
+ for update in update_step(1, "complete"):
197
+ yield update
198
+ except Exception as e:
199
+ for update in update_step(1, "error", str(e)):
200
+ yield update
201
+ sections = get_manual_sections(research_subject)
202
+ for update in update_step(1, "in-progress", "Falling back to manual structure"):
203
+ yield update
204
+ index_content = generate_index_content(selected_model, research_subject, [s[0] for s in sections[1:]])
205
+ sections[0] = ("Index", index_content)
206
+ for update in update_step(1, "complete"):
207
+ yield update
208
+ else:
209
  sections = get_manual_sections(research_subject)
210
+ for update in update_step(1, "in-progress"):
211
+ yield update
212
  index_content = generate_index_content(selected_model, research_subject, [s[0] for s in sections[1:]])
213
  sections[0] = ("Index", index_content)
214
+ for update in update_step(1, "complete"):
215
+ yield update
216
+
217
+ # Generate the paper
218
+ for update in update_step(2, "in-progress"):
219
+ yield update
220
+ write_research_paper(md_filename, research_subject, sections, selected_model)
221
+ for update in update_step(2, "complete"):
222
+ yield update
223
+
224
+ # Generate chapters
225
+ chapter_sections = [s for s in sections if s[0].startswith("Chapter")]
226
+ for i, (section_title, prompt) in enumerate(chapter_sections, 3):
227
+ for update in update_step(i, "in-progress"):
228
+ yield update
229
+ try:
230
+ if not (isinstance(prompt, str) and (prompt.startswith("##") or prompt.startswith("#"))):
231
+ response = generate_section_content(selected_model, prompt)
232
+ for update in update_step(i, "complete"):
233
+ yield update
234
+ except Exception as e:
235
+ for update in update_step(i, "error", str(e)):
236
+ yield update
237
+
238
+ # Conclusion
239
+ for update in update_step(5, "in-progress"):
240
+ yield update
241
+ for update in update_step(5, "complete"):
242
+ yield update
243
+
244
+ # Convert to Word
245
+ for update in update_step(6, "in-progress"):
246
+ yield update
247
+ try:
248
+ convert_to_word(md_filename, docx_filename)
249
+ for update in update_step(6, "complete"):
250
+ yield update
251
+ yield "data: " + json.dumps({
252
+ "status": "complete",
253
+ "docx_file": docx_filename,
254
+ "md_file": md_filename
255
+ }) + "\n\n"
256
+ except Exception as e:
257
+ for update in update_step(6, "error", str(e)):
258
+ yield update
259
+ yield "data: " + json.dumps({
260
+ "status": "partial_success",
261
+ "message": f'Paper generated but Word conversion failed: {str(e)}',
262
+ "md_file": md_filename
263
+ }) + "\n\n"
264
+
265
  except Exception as e:
266
+ yield "data: " + json.dumps({"error": f"Failed to generate paper: {str(e)}"}) + "\n\n"
267
+
268
+ return Response(generate(), mimetype="text/event-stream")
 
 
 
 
 
 
 
 
 
 
 
269
 
270
  @app.route('/download/<filename>')
271
  def download(filename):
 
276
  as_attachment=True
277
  )
278
 
279
+ # if __name__ == '__main__':
280
+ # app.run(debug=True)
templates/index.html CHANGED
@@ -1,4 +1,3 @@
1
- <!-- templates/index.html -->
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
@@ -141,26 +140,30 @@
141
  progressBar.style.width = '0%';
142
  progressText.textContent = '0%';
143
 
144
- // Show initial progress steps
145
  const steps = [
146
  'Preparing document structure...',
147
  'Generating index/table of contents...',
148
  'Writing introduction...',
149
- 'Generating chapters...',
 
 
150
  'Creating conclusion...',
151
  'Finalizing document...',
152
  'Converting to Word format...'
153
  ];
154
 
 
155
  steps.forEach((step, index) => {
156
  const stepElement = document.createElement('div');
157
  stepElement.className = 'flex items-center';
158
  stepElement.innerHTML = `
159
  <div class="flex-shrink-0 h-5 w-5 text-gray-400">
160
- <i class="far fa-circle"></i>
161
  </div>
162
  <div class="ml-3">
163
- <p class="text-sm font-medium text-gray-700">${step}</p>
 
164
  </div>
165
  `;
166
  stepElement.id = `step-${index}`;
@@ -174,86 +177,72 @@
174
  structure: form.structure.value
175
  };
176
 
177
- // Simulate progress updates (in a real app, you'd use SSE or websockets)
178
- const totalSteps = steps.length;
179
- let currentStep = 0;
180
 
181
- const updateProgress = () => {
182
- const progress = Math.min(100, Math.round((currentStep / totalSteps) * 100));
183
- progressBar.style.width = `${progress}%`;
184
- progressText.textContent = `${progress}%`;
185
 
186
- // Update step icons
187
- if (currentStep > 0) {
188
- const prevStepElement = document.getElementById(`step-${currentStep-1}`);
189
- if (prevStepElement) {
190
- prevStepElement.querySelector('i').className = 'fas fa-check-circle text-green-500';
191
- }
192
  }
193
 
194
- if (currentStep < totalSteps) {
195
- const currentStepElement = document.getElementById(`step-${currentStep}`);
196
- if (currentStepElement) {
197
- currentStepElement.querySelector('i').className = 'fas fa-spinner fa-spin text-indigo-500';
198
  }
199
- }
200
- };
201
-
202
- const interval = setInterval(() => {
203
- currentStep++;
204
- updateProgress();
205
-
206
- if (currentStep >= totalSteps) {
207
- clearInterval(interval);
208
- }
209
- }, 1000);
210
-
211
- // Send request to server
212
- fetch('/generate', {
213
- method: 'POST',
214
- headers: {
215
- 'Content-Type': 'application/json',
216
- },
217
- body: JSON.stringify(formData)
218
- })
219
- .then(response => response.json())
220
- .then(data => {
221
- clearInterval(interval);
222
- updateProgress();
223
-
224
- if (data.error) {
225
- throw new Error(data.error);
226
  }
227
 
228
- // Show download links
229
- if (data.docx_file) {
230
- document.getElementById('downloadDocx').href = `/download/${data.docx_file}`;
231
- }
232
- if (data.md_file) {
233
- document.getElementById('downloadMd').href = `/download/${data.md_file}`;
234
  }
235
 
236
- // Mark all steps as complete
237
- for (let i = 0; i < steps.length; i++) {
238
- const stepElement = document.getElementById(`step-${i}`);
239
- if (stepElement) {
240
- stepElement.querySelector('i').className = 'fas fa-check-circle text-green-500';
241
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  }
243
-
244
- progressBar.style.width = '100%';
245
- progressText.textContent = '100%';
246
- resultContainer.classList.remove('hidden');
247
- })
248
- .catch(error => {
249
- clearInterval(interval);
250
- document.getElementById('errorMessage').textContent = error.message;
251
- errorContainer.classList.remove('hidden');
252
- })
253
- .finally(() => {
254
  generateBtn.disabled = false;
255
- });
256
  });
257
  </script>
258
  </body>
259
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
 
140
  progressBar.style.width = '0%';
141
  progressText.textContent = '0%';
142
 
143
+ // Define steps
144
  const steps = [
145
  'Preparing document structure...',
146
  'Generating index/table of contents...',
147
  'Writing introduction...',
148
+ 'Generating Chapter 1...',
149
+ 'Generating Chapter 2...',
150
+ 'Generating Chapter 3...',
151
  'Creating conclusion...',
152
  'Finalizing document...',
153
  'Converting to Word format...'
154
  ];
155
 
156
+ // Create step elements
157
  steps.forEach((step, index) => {
158
  const stepElement = document.createElement('div');
159
  stepElement.className = 'flex items-center';
160
  stepElement.innerHTML = `
161
  <div class="flex-shrink-0 h-5 w-5 text-gray-400">
162
+ <i class="far fa-circle" id="step-icon-${index}"></i>
163
  </div>
164
  <div class="ml-3">
165
+ <p class="text-sm font-medium text-gray-700" id="step-text-${index}">${step}</p>
166
+ <p class="text-xs text-gray-500 hidden" id="step-message-${index}"></p>
167
  </div>
168
  `;
169
  stepElement.id = `step-${index}`;
 
177
  structure: form.structure.value
178
  };
179
 
180
+ // Create SSE connection
181
+ const eventSource = new EventSource(`/stream?subject=${encodeURIComponent(formData.subject)}&model=${encodeURIComponent(formData.model)}&structure=${encodeURIComponent(formData.structure)}`);
 
182
 
183
+ eventSource.onmessage = function(event) {
184
+ const data = JSON.parse(event.data);
 
 
185
 
186
+ if (data.error) {
187
+ document.getElementById('errorMessage').textContent = data.error;
188
+ errorContainer.classList.remove('hidden');
189
+ eventSource.close();
190
+ generateBtn.disabled = false;
191
+ return;
192
  }
193
 
194
+ if (data.status === 'complete' || data.status === 'partial_success') {
195
+ // Show download links
196
+ if (data.docx_file) {
197
+ document.getElementById('downloadDocx').href = `/download/${data.docx_file}`;
198
  }
199
+ if (data.md_file) {
200
+ document.getElementById('downloadMd').href = `/download/${data.md_file}`;
201
+ }
202
+
203
+ resultContainer.classList.remove('hidden');
204
+ eventSource.close();
205
+ generateBtn.disabled = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
 
208
+ // Update progress
209
+ if (data.progress) {
210
+ progressBar.style.width = `${data.progress}%`;
211
+ progressText.textContent = `${data.progress}%`;
 
 
212
  }
213
 
214
+ // Update steps
215
+ if (data.steps) {
216
+ data.steps.forEach(step => {
217
+ const icon = document.getElementById(`step-icon-${step.id}`);
218
+ const text = document.getElementById(`step-text-${step.id}`);
219
+ const message = document.getElementById(`step-message-${step.id}`);
220
+
221
+ if (icon) {
222
+ if (step.status === 'pending') {
223
+ icon.className = 'far fa-circle text-gray-400';
224
+ } else if (step.status === 'in-progress') {
225
+ icon.className = 'fas fa-spinner fa-spin text-indigo-500';
226
+ } else if (step.status === 'complete') {
227
+ icon.className = 'fas fa-check-circle text-green-500';
228
+ } else if (step.status === 'error') {
229
+ icon.className = 'fas fa-exclamation-circle text-red-500';
230
+ }
231
+ }
232
+
233
+ if (message && step.message) {
234
+ message.textContent = step.message;
235
+ message.classList.remove('hidden');
236
+ }
237
+ });
238
  }
239
+ };
240
+
241
+ eventSource.onerror = function() {
242
+ eventSource.close();
 
 
 
 
 
 
 
243
  generateBtn.disabled = false;
244
+ };
245
  });
246
  </script>
247
  </body>
248
+ </html>