EL GHAFRAOUI AYOUB commited on
Commit
3a5be9b
·
1 Parent(s): d4ce05f
app/app.rar ADDED
Binary file (36.7 kB). View file
 
app/controllers/__pycache__/f5_model.cpython-312.pyc CHANGED
Binary files a/app/controllers/__pycache__/f5_model.cpython-312.pyc and b/app/controllers/__pycache__/f5_model.cpython-312.pyc differ
 
app/controllers/f5_model.py CHANGED
@@ -1,92 +1,114 @@
1
- from typing import List, Optional
2
- from pydantic import BaseModel
3
- import torch
4
  import logging
5
  from transformers import pipeline
 
6
 
7
- class F5ModelHandler:
8
  def __init__(self):
9
- logging.info("Initializing F5ModelHandler...")
10
- try:
11
- # Using a larger T5 model for better generation
12
- logging.info("Loading model 'google/flan-t5-base'...")
13
- self.model_name = "google/flan-t5-large" # Upgraded from small to large
14
-
15
- # Use pipeline for simpler model loading with better parameters
16
- self.generator = pipeline(
17
- "text2text-generation",
18
- model=self.model_name,
19
- device="cuda" if torch.cuda.is_available() else "cpu",
20
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
21
- model_kwargs={"cache_dir": "./model_cache"} # Cache the model locally
22
- )
23
- logging.info(f"Model loaded successfully on {self.generator.device}")
24
- except Exception as e:
25
- logging.error(f"Error loading model: {str(e)}")
26
- raise
27
 
28
- async def generate_response(self, prompt: str, max_length: int = 3072) -> str:
29
- try:
30
- logging.info(f"Generating response for prompt: {prompt[:100]}...")
31
-
32
- # Enhanced generation parameters for better quality
33
- response = self.generator(
34
- prompt,
35
- max_length=max_length,
36
- min_length=1000, # Ensure minimum length
37
- num_beams=5,
38
- temperature=0.8,
39
- top_p=0.95,
40
- top_k=50,
41
- repetition_penalty=1.2,
42
- length_penalty=1.0,
43
- do_sample=True,
44
- num_return_sequences=1,
45
- early_stopping=True
46
- )[0]['generated_text']
47
 
48
- # Clean up the response
49
- response = response.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- # Ensure minimum content length
52
- if len(response) < 500: # Increased minimum length requirement
53
- logging.warning("Response too short, regenerating...")
54
- return await self.generate_response(prompt, max_length)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- logging.info(f"Generated response successfully: {response[:100]}...")
57
- return response
 
58
 
59
  except Exception as e:
60
- logging.error(f"Error generating response: {str(e)}")
61
  raise
62
 
63
- async def stream_response(self, prompt: str, max_length: int = 3072):
 
 
 
 
 
 
 
 
 
64
  try:
65
- response = self.generator(
66
- prompt,
67
- max_length=max_length,
68
- min_length=1000,
69
- num_beams=4,
70
- temperature=0.8,
71
- top_p=0.95,
72
- do_sample=True,
73
- return_full_text=False
74
- )[0]['generated_text']
75
 
76
- # Simulate streaming by yielding chunks of the response
77
- chunk_size = 20
78
- for i in range(0, len(response), chunk_size):
79
- chunk = response[i:i + chunk_size]
80
- yield chunk
81
-
 
 
 
 
 
 
 
 
82
  except Exception as e:
83
- logging.error(f"Error in stream_response: {str(e)}")
84
- raise
 
 
 
85
 
86
- # Initialize the model handler
87
  logging.basicConfig(
88
  level=logging.INFO,
89
- format='%(asctime)s - %(levelname)s - %(message)s'
 
 
 
 
90
  )
91
 
92
- f5_model = F5ModelHandler()
 
 
 
 
 
1
  import logging
2
  from transformers import pipeline
3
+ import asyncio
4
 
5
+ class TextGenerationHandler:
6
  def __init__(self):
7
+ # Initialize the text generation pipeline
8
+ self.pipe = pipeline(
9
+ "text2text-generation",
10
+ model="google/flan-t5-small",
11
+ max_length=2048, # Increase max length for longer responses
12
+ num_return_sequences=1
13
+ )
14
+ self.logger = logging.getLogger(__name__)
15
+
16
+ async def generate_response(self, prompt: str) -> str:
17
+ """
18
+ Generate a complete response using the T5 model pipeline
 
 
 
 
 
 
19
 
20
+ Args:
21
+ prompt (str): Input text to generate from
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ Returns:
24
+ str: Generated text output
25
+ """
26
+ try:
27
+ # Break down the generation into sections for better coherence
28
+ sections = [
29
+ "1. Executive Summary",
30
+ "2. Project Scope and Objectives",
31
+ "3. Architecture Overview",
32
+ "4. Component Design",
33
+ "5. Security and Compliance",
34
+ "6. Deployment Strategy",
35
+ "7. Team Requirements",
36
+ "8. Cost Estimates",
37
+ "9. Project Timeline"
38
+ ]
39
 
40
+ complete_response = []
41
+ for section in sections:
42
+ section_prompt = f"{prompt}\nGenerate content for: {section}"
43
+ self.logger.info(f"Generating section: {section}")
44
+ self.logger.debug(f"Section prompt: {section_prompt}")
45
+
46
+ output = self.pipe(
47
+ section_prompt,
48
+ max_length=512,
49
+ do_sample=True,
50
+ temperature=0.7,
51
+ repetition_penalty=1.2,
52
+ no_repeat_ngram_size=3
53
+ )
54
+
55
+ section_text = output[0]['generated_text'].strip()
56
+ self.logger.info(f"Generated text for {section}:\n{section_text}\n")
57
+
58
+ complete_response.append(f"{section}\n{section_text}")
59
 
60
+ final_response = "\n\n".join(complete_response)
61
+ self.logger.info(f"Complete response:\n{final_response}")
62
+ return final_response
63
 
64
  except Exception as e:
65
+ self.logger.error(f"Error generating text: {str(e)}", exc_info=True)
66
  raise
67
 
68
+ async def stream_response(self, prompt: str):
69
+ """
70
+ Stream the generated response section by section
71
+
72
+ Args:
73
+ prompt (str): Input text to generate from
74
+
75
+ Yields:
76
+ dict: Response chunks with type and content
77
+ """
78
  try:
79
+ # Generate complete response first
80
+ response = await self.generate_response(prompt)
 
 
 
 
 
 
 
 
81
 
82
+ # Stream each section
83
+ accumulated_response = ""
84
+ sections = response.split('\n\n')
85
+
86
+ for section in sections:
87
+ accumulated_response += section + "\n\n"
88
+ self.logger.debug(f"Streaming section:\n{section}\n")
89
+
90
+ yield {
91
+ "type": "content",
92
+ "content": accumulated_response.strip()
93
+ }
94
+ await asyncio.sleep(0.1)
95
+
96
  except Exception as e:
97
+ self.logger.error(f"Error in stream_response: {str(e)}", exc_info=True)
98
+ yield {
99
+ "type": "error",
100
+ "content": str(e)
101
+ }
102
 
103
+ # Configure logging
104
  logging.basicConfig(
105
  level=logging.INFO,
106
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
107
+ handlers=[
108
+ logging.StreamHandler(),
109
+ logging.FileHandler('f5_model.log')
110
+ ]
111
  )
112
 
113
+ f5_model = TextGenerationHandler()
114
+
app/main.py CHANGED
@@ -3,10 +3,11 @@ from fastapi.responses import StreamingResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.templating import Jinja2Templates
5
  from pydantic import BaseModel
6
- from app.controllers.f5_model import F5ModelHandler
7
  from typing import List, Optional
8
  import logging
9
  from app.helpers.plan_parser import parse_plan_sections
 
10
 
11
  # Configure logging
12
  logging.basicConfig(
@@ -23,7 +24,7 @@ app = FastAPI(title="F5 Model Test Application")
23
  templates = Jinja2Templates(directory="app/templates")
24
 
25
  # Initialize the F5 model
26
- model_handler = F5ModelHandler()
27
 
28
  class ChatMessage(BaseModel):
29
  role: str
@@ -190,57 +191,94 @@ async def generate_plan(request: ProjectPlanRequest):
190
  try:
191
  logger.info(f"Plan generation request received for project: {request.project_title}")
192
 
193
- # Simplified prompt for better generation
194
- prompt = f"""Create a detailed technical project plan for a {request.platform} project named '{request.project_title}'.
195
-
196
- Requirements: {request.requirements}
197
- Features: {', '.join(request.features)}
198
- Additional Requirements: {request.additional_requirements}
199
 
200
- Generate a complete plan with the following sections:
 
201
 
202
  1. Executive Summary
203
- Brief overview of the project, its goals, and business impact.
 
 
 
204
 
205
  2. Project Scope and Objectives
206
- Core functionalities and specific objectives.
 
 
 
 
 
207
 
208
  3. Architecture Overview
209
- {request.platform} architecture components and their interactions.
 
 
 
 
 
210
 
211
  4. Component Design
212
- Core components and their technical specifications.
 
 
 
213
 
214
  5. Security and Compliance
215
- Security measures and compliance requirements.
 
 
 
216
 
217
- 6. Deployment, Testing, and Monitoring
218
- Implementation approach and quality assurance.
 
 
 
219
 
220
- 7. Team Roles and Skills
221
- Required team members and their expertise.
 
 
222
 
223
- 8. Cost Estimates and Optimization
224
- Resource planning and cost optimization.
 
 
225
 
226
- 9. Project Tasks and Milestones
227
- Project phases and timeline.
 
 
 
228
 
229
- Provide detailed content for each section."""
230
-
231
- logger.info("Generating plan with model...")
232
- response = await model_handler.generate_response(prompt)
233
- logger.info(f"Response generated successfully: {response[:100]}...")
234
-
235
- # Parse the response into structured sections
236
- sections = parse_plan_sections(response)
237
-
238
- logger.info("Plan generated successfully")
239
- return {
240
- "project_title": request.project_title,
241
- "sections": sections,
242
- "raw_content": response
243
- }
 
 
 
 
 
 
 
244
 
245
  except Exception as e:
246
  logger.error(f"Error generating plan: {str(e)}", exc_info=True)
 
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.templating import Jinja2Templates
5
  from pydantic import BaseModel
6
+ from app.controllers.f5_model import TextGenerationHandler
7
  from typing import List, Optional
8
  import logging
9
  from app.helpers.plan_parser import parse_plan_sections
10
+ import json
11
 
12
  # Configure logging
13
  logging.basicConfig(
 
24
  templates = Jinja2Templates(directory="app/templates")
25
 
26
  # Initialize the F5 model
27
+ model_handler = TextGenerationHandler()
28
 
29
  class ChatMessage(BaseModel):
30
  role: str
 
191
  try:
192
  logger.info(f"Plan generation request received for project: {request.project_title}")
193
 
194
+ async def generate():
195
+ features_list = request.features
196
+ if features_list and isinstance(features_list[0], dict):
197
+ features_str = ', '.join([f.get('feature', '') for f in features_list])
198
+ else:
199
+ features_str = ', '.join(features_list)
200
 
201
+ prompt = f"""As a technical project planner, create a detailed project plan for a {request.platform} SaaS application named '{request.project_title}'.
202
+ Follow this structure exactly, replacing the placeholders with detailed content:
203
 
204
  1. Executive Summary
205
+ Brief overview of the {request.project_title} SaaS application
206
+ Key business objectives
207
+ Technical approach using {request.platform}
208
+ Expected outcomes
209
 
210
  2. Project Scope and Objectives
211
+ Core Features:
212
+ - {features_str}
213
+ Technical Goals:
214
+ - Scalable {request.platform} architecture
215
+ - Secure data handling
216
+ - High availability
217
 
218
  3. Architecture Overview
219
+ {request.platform} Components:
220
+ - Frontend: [Specify technology]
221
+ - Backend: [Specify {request.platform} services]
222
+ - Database: [Specify database solution]
223
+ - Storage: [Specify storage solution]
224
+ - Authentication: [Specify auth service]
225
 
226
  4. Component Design
227
+ [For each major component, specify:
228
+ - Technical specifications
229
+ - Integration points
230
+ - Performance requirements]
231
 
232
  5. Security and Compliance
233
+ {request.platform}-specific security measures:
234
+ - Data encryption
235
+ - Access controls
236
+ - Compliance requirements
237
 
238
+ 6. Deployment Strategy
239
+ - CI/CD pipeline
240
+ - Testing approach
241
+ - Monitoring setup
242
+ - {request.platform} specific considerations
243
 
244
+ 7. Team Requirements
245
+ - Required roles
246
+ - Technical skills
247
+ - Team structure
248
 
249
+ 8. Cost Estimation
250
+ - {request.platform} service costs
251
+ - Development costs
252
+ - Operational costs
253
 
254
+ 9. Project Timeline
255
+ Phase 1: Setup and Infrastructure
256
+ Phase 2: Core Development
257
+ Phase 3: Testing and Deployment
258
+ Phase 4: Launch and Monitoring
259
 
260
+ Additional Context:
261
+ Requirements: {request.requirements}
262
+ Additional Requirements: {request.additional_requirements}"""
263
+
264
+ response_content = ""
265
+ async for chunk in model_handler.stream_response(prompt):
266
+ if chunk["type"] == "content":
267
+ response_content = chunk["content"]
268
+ sections = parse_plan_sections(response_content)
269
+ yield f"data: {json.dumps({
270
+ 'type': 'complete',
271
+ 'project_title': request.project_title,
272
+ 'sections': sections,
273
+ 'raw_content': response_content
274
+ })}\n\n"
275
+ elif chunk["type"] == "error":
276
+ yield f"data: {json.dumps({
277
+ 'type': 'error',
278
+ 'content': chunk['content']
279
+ })}\n\n"
280
+
281
+ return StreamingResponse(generate(), media_type="text/event-stream")
282
 
283
  except Exception as e:
284
  logger.error(f"Error generating plan: {str(e)}", exc_info=True)
app/templates/index.html CHANGED
@@ -218,36 +218,7 @@
218
  </div>
219
  </div>
220
 
221
- <!-- Add this right after the form -->
222
- <div id="rawOutput" class="mt-8 bg-white rounded-lg shadow-lg p-6">
223
- <h2 class="text-xl font-bold mb-4 text-gray-800">Raw Response Data</h2>
224
-
225
- <div class="space-y-6">
226
- <!-- Request Data -->
227
- <div class="bg-gray-50 p-4 rounded-lg">
228
- <h3 class="text-lg font-semibold mb-2 text-blue-600">Request Data</h3>
229
- <pre id="rawRequest" class="bg-gray-900 text-green-400 p-4 rounded overflow-auto max-h-60 font-mono text-sm whitespace-pre-wrap"></pre>
230
- </div>
231
-
232
- <!-- API Response -->
233
- <div class="bg-gray-50 p-4 rounded-lg">
234
- <h3 class="text-lg font-semibold mb-2 text-blue-600">API Response</h3>
235
- <pre id="rawApiResponse" class="bg-gray-900 text-green-400 p-4 rounded overflow-auto max-h-96 font-mono text-sm whitespace-pre-wrap"></pre>
236
- </div>
237
-
238
- <!-- Raw Content -->
239
- <div class="bg-gray-50 p-4 rounded-lg">
240
- <h3 class="text-lg font-semibold mb-2 text-blue-600">Raw Content</h3>
241
- <pre id="rawContent" class="bg-gray-900 text-green-400 p-4 rounded overflow-auto max-h-96 font-mono text-sm whitespace-pre-wrap"></pre>
242
- </div>
243
-
244
- <!-- Parsed Sections -->
245
- <div class="bg-gray-50 p-4 rounded-lg">
246
- <h3 class="text-lg font-semibold mb-2 text-blue-600">Parsed Sections</h3>
247
- <pre id="rawSections" class="bg-gray-900 text-green-400 p-4 rounded overflow-auto max-h-96 font-mono text-sm whitespace-pre-wrap"></pre>
248
- </div>
249
- </div>
250
- </div>
251
  </div>
252
 
253
  <!-- Chat Widget -->
@@ -297,6 +268,7 @@
297
  <script>
298
  let chatHistory = [];
299
  let chatWindowOpen = false;
 
300
 
301
  function toggleChat() {
302
  const chatWindow = document.getElementById('chatWindow');
@@ -458,8 +430,7 @@
458
  additional_requirements: additionalRequirements
459
  };
460
 
461
- // Display request data
462
- document.getElementById('rawRequest').textContent = JSON.stringify(requestData, null, 2);
463
 
464
  const loading = document.getElementById('planLoading');
465
  const output = document.getElementById('planOutput');
@@ -467,31 +438,38 @@
467
  output.classList.add('hidden');
468
 
469
  try {
 
470
  const response = await fetch('/generate-plan', {
471
  method: 'POST',
472
  headers: { 'Content-Type': 'application/json' },
473
  body: JSON.stringify(requestData)
474
  });
475
 
 
476
  const reader = response.body.getReader();
477
  const decoder = new TextDecoder();
478
 
479
- // Show progress bar
480
  document.getElementById('progressBar').classList.remove('hidden');
 
481
 
482
  while (true) {
483
  const { value, done } = await reader.read();
484
- if (done) break;
 
 
 
485
 
486
  const chunk = decoder.decode(value);
 
 
487
  const lines = chunk.split('\n');
488
-
489
  for (const line of lines) {
490
  if (line.startsWith('data: ')) {
491
  const data = JSON.parse(line.slice(5));
 
492
 
493
  if (data.type === 'progress') {
494
- // Update progress bar
495
  const progressFill = document.getElementById('progressFill');
496
  const progressText = document.getElementById('progressText');
497
  const progressStep = document.getElementById('progressStep');
@@ -500,13 +478,12 @@
500
  progressText.textContent = `${Math.round(data.progress)}%`;
501
  progressStep.textContent = `Step ${data.step}/${data.total}`;
502
  } else if (data.type === 'complete') {
503
- // Display final results
 
 
504
  displayPlan(data);
505
- document.getElementById('rawApiResponse').textContent = JSON.stringify(data, null, 2);
506
- document.getElementById('rawContent').textContent = data.raw_content;
507
- document.getElementById('rawSections').textContent = JSON.stringify(data.sections, null, 2);
508
  } else if (data.type === 'error') {
509
- console.error('Error:', data.error);
510
  alert(`Error: ${data.error}`);
511
  }
512
  }
@@ -514,11 +491,11 @@
514
  }
515
 
516
  } catch (error) {
517
- console.error('Error:', error);
518
  alert('Error generating plan');
519
  } finally {
 
520
  loading.style.display = 'none';
521
- // Hide progress bar when done
522
  document.getElementById('progressBar').classList.add('hidden');
523
  }
524
  }
@@ -540,38 +517,86 @@
540
  const title = document.getElementById('planTitle');
541
  const sections = document.getElementById('planSections');
542
 
543
- // Update debug panel with raw data
544
- document.getElementById('requestData').textContent = JSON.stringify({
545
- project_title: data.project_title,
546
- sections: data.sections
547
- }, null, 2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
- // Display raw content with line breaks preserved
550
- const rawContent = document.getElementById('rawContent');
551
- rawContent.textContent = data.raw_content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
 
553
- // Show content length
554
- const contentLength = data.raw_content.length;
555
- document.getElementById('rawContentLength').textContent =
556
- `Length: ${contentLength} characters`;
557
-
558
- // Display parsed sections
559
- document.getElementById('parsedSections').textContent =
560
- JSON.stringify(data.sections, null, 2);
561
-
562
- // Original plan display code
563
- title.textContent = `Technical Project Plan: ${data.project_title}`;
564
- sections.innerHTML = '';
565
-
566
- // Keep only debug panel toggle functionality
567
- document.addEventListener('keydown', function(e) {
568
- if (e.ctrlKey && e.key === 'd') {
569
- e.preventDefault();
570
- toggleDebugPanel();
 
 
 
 
 
 
 
 
 
 
 
571
  }
572
  });
573
 
574
- output.classList.add('hidden'); // Hide the output section
575
  }
576
  </script>
577
  </body>
 
218
  </div>
219
  </div>
220
 
221
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  </div>
223
 
224
  <!-- Chat Widget -->
 
268
  <script>
269
  let chatHistory = [];
270
  let chatWindowOpen = false;
271
+ let accumulatedContent = '';
272
 
273
  function toggleChat() {
274
  const chatWindow = document.getElementById('chatWindow');
 
430
  additional_requirements: additionalRequirements
431
  };
432
 
433
+ console.log('Request Data:', requestData);
 
434
 
435
  const loading = document.getElementById('planLoading');
436
  const output = document.getElementById('planOutput');
 
438
  output.classList.add('hidden');
439
 
440
  try {
441
+ console.log('Starting plan generation request...');
442
  const response = await fetch('/generate-plan', {
443
  method: 'POST',
444
  headers: { 'Content-Type': 'application/json' },
445
  body: JSON.stringify(requestData)
446
  });
447
 
448
+ console.log('Got response, starting reader...');
449
  const reader = response.body.getReader();
450
  const decoder = new TextDecoder();
451
 
 
452
  document.getElementById('progressBar').classList.remove('hidden');
453
+ console.log('Progress bar shown');
454
 
455
  while (true) {
456
  const { value, done } = await reader.read();
457
+ if (done) {
458
+ console.log('Stream complete');
459
+ break;
460
+ }
461
 
462
  const chunk = decoder.decode(value);
463
+ console.log('Received chunk:', chunk);
464
+
465
  const lines = chunk.split('\n');
 
466
  for (const line of lines) {
467
  if (line.startsWith('data: ')) {
468
  const data = JSON.parse(line.slice(5));
469
+ console.log('Parsed data:', data);
470
 
471
  if (data.type === 'progress') {
472
+ console.log('Progress update:', data.progress + '%');
473
  const progressFill = document.getElementById('progressFill');
474
  const progressText = document.getElementById('progressText');
475
  const progressStep = document.getElementById('progressStep');
 
478
  progressText.textContent = `${Math.round(data.progress)}%`;
479
  progressStep.textContent = `Step ${data.step}/${data.total}`;
480
  } else if (data.type === 'complete') {
481
+ accumulatedContent += data.raw_content;
482
+ data.sections = parseSections(accumulatedContent);
483
+ console.log('Generation complete, displaying plan with content:', accumulatedContent);
484
  displayPlan(data);
 
 
 
485
  } else if (data.type === 'error') {
486
+ console.error('Error in generation:', data.error);
487
  alert(`Error: ${data.error}`);
488
  }
489
  }
 
491
  }
492
 
493
  } catch (error) {
494
+ console.error('Error in generatePlan:', error);
495
  alert('Error generating plan');
496
  } finally {
497
+ console.log('Cleaning up...');
498
  loading.style.display = 'none';
 
499
  document.getElementById('progressBar').classList.add('hidden');
500
  }
501
  }
 
517
  const title = document.getElementById('planTitle');
518
  const sections = document.getElementById('planSections');
519
 
520
+ try {
521
+ // Update debug panel with raw data
522
+ document.getElementById('requestData').textContent = JSON.stringify({
523
+ project_title: data.project_title,
524
+ sections: data.sections
525
+ }, null, 2);
526
+
527
+ // Display raw content with line breaks preserved
528
+ const rawContent = document.getElementById('rawContent');
529
+ rawContent.textContent = data.raw_content;
530
+
531
+ // Show content length
532
+ const contentLength = data.raw_content.length;
533
+ document.getElementById('rawContentLength').textContent =
534
+ `Length: ${contentLength} characters`;
535
+
536
+ // Display parsed sections
537
+ document.getElementById('parsedSections').textContent =
538
+ JSON.stringify(data.sections, null, 2);
539
+
540
+ // Original plan display code
541
+ title.textContent = `Technical Project Plan: ${data.project_title}`;
542
+ sections.innerHTML = '';
543
 
544
+ output.classList.remove('hidden'); // Show the output section instead of hiding it
545
+ } catch (error) {
546
+ console.error('Error in displayPlan:', error);
547
+ alert('Error displaying plan. Check console for details.');
548
+ }
549
+ }
550
+
551
+ function parseSections(content) {
552
+ const sections = {
553
+ "executive_summary": "",
554
+ "scope_objectives": "",
555
+ "architecture_overview": "",
556
+ "component_design": "",
557
+ "security_compliance": "",
558
+ "deployment_testing": "",
559
+ "team_roles": "",
560
+ "cost_estimates": "",
561
+ "project_phases": ""
562
+ };
563
+
564
+ let currentSection = null;
565
+ const lines = content.split('\n');
566
 
567
+ for (const line of lines) {
568
+ if (line.startsWith('1. Executive Summary')) {
569
+ currentSection = "executive_summary";
570
+ } else if (line.startsWith('2. Project Scope and Objectives')) {
571
+ currentSection = "scope_objectives";
572
+ } else if (line.startsWith('3. Architecture Overview')) {
573
+ currentSection = "architecture_overview";
574
+ } else if (line.startsWith('4. Component Design')) {
575
+ currentSection = "component_design";
576
+ } else if (line.startsWith('5. Security and Compliance')) {
577
+ currentSection = "security_compliance";
578
+ } else if (line.startsWith('6. Deployment and Testing')) {
579
+ currentSection = "deployment_testing";
580
+ } else if (line.startsWith('7. Team Roles')) {
581
+ currentSection = "team_roles";
582
+ } else if (line.startsWith('8. Cost Estimates')) {
583
+ currentSection = "cost_estimates";
584
+ } else if (line.startsWith('9. Project Phases')) {
585
+ currentSection = "project_phases";
586
+ } else if (currentSection && line.trim()) {
587
+ sections[currentSection] += line + '\n';
588
+ }
589
+ }
590
+
591
+ // Trim any trailing whitespace from sections
592
+ Object.keys(sections).forEach(key => {
593
+ sections[key] = sections[key].trim();
594
+ if (!sections[key]) {
595
+ sections[key] = "No content generated";
596
  }
597
  });
598
 
599
+ return sections;
600
  }
601
  </script>
602
  </body>