File size: 31,723 Bytes
2127f72
 
 
 
 
 
025d333
 
2127f72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
025d333
2127f72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
025d333
2127f72
 
 
 
 
 
025d333
2127f72
 
025d333
2127f72
 
 
 
 
 
 
 
 
 
 
 
843279f
 
 
 
 
 
 
 
eb04412
 
843279f
 
2127f72
 
 
 
eb04412
2127f72
eb04412
 
 
 
 
 
 
2127f72
 
 
 
eb04412
2127f72
 
 
eb04412
2127f72
 
 
 
 
 
 
 
eb04412
 
2127f72
 
 
eb04412
2127f72
 
 
 
 
 
 
 
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
843279f
2127f72
 
 
 
eb04412
2127f72
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843279f
2127f72
 
 
025d333
2127f72
 
 
 
 
025d333
2127f72
 
 
 
 
 
 
 
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
eb04412
 
2127f72
eb04412
 
2127f72
 
 
 
 
 
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
 
 
 
 
 
 
 
 
 
eb04412
2127f72
eb04412
 
2127f72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb04412
2127f72
 
025d333
 
2127f72
 
 
 
 
 
 
025d333
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843279f
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
 
 
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
 
 
 
 
 
 
eb04412
 
 
 
2127f72
eb04412
 
 
 
 
 
843279f
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843279f
2127f72
 
 
 
 
 
 
eb04412
2127f72
025d333
eb04412
 
2127f72
eb04412
 
2127f72
eb04412
 
 
 
2127f72
eb04412
 
2127f72
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
eb04412
 
 
 
 
843279f
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
 
eb04412
2127f72
 
eb04412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127f72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb04412
2127f72
 
 
 
 
 
 
 
 
 
 
 
 
 
eb04412
2127f72
 
 
 
 
 
eb04412
 
 
 
2127f72
 
 
 
eb04412
 
 
 
2127f72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb04412
 
2127f72
eb04412
2127f72
eb04412
2127f72
 
 
 
 
 
 
 
 
 
eb04412
 
 
2127f72
 
 
 
 
 
 
 
 
 
025d333
 
eb04412
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
from typing import Dict, List, Set, Optional
from dataclasses import dataclass
from enum import Enum
import openai
from contextlib import contextmanager
import re
import json

@dataclass
class UIComponent:
    name: str
    type: str
    config: Dict

@dataclass
class PromptConfig:
    name: str
    inputs: List[str]
    outputs: List[str]
    ui_components: Dict[str, UIComponent]
    step: Optional[str] = None

class ChangeType(Enum):
    ADD_PROMPT = "add_prompt"
    REMOVE_PROMPT = "remove_prompt"
    RENAME_PROMPT = "rename_prompt"
    ADD_INPUT = "add_input"
    REMOVE_INPUT = "remove_input"
    RENAME_INPUT = "rename_input"
    ADD_OUTPUT = "add_output"
    REMOVE_OUTPUT = "remove_output"
    RENAME_OUTPUT = "rename_output"
    ADD_UI = "add_ui"
    REMOVE_UI = "remove_ui"
    RENAME_UI = "rename_ui"
    CHANGE_STEP = "change_step"

@dataclass
class ConfigChange:
    type: ChangeType
    prompt_name: str
    old_value: Optional[str] = None
    new_value: Optional[str] = None
    details: Optional[Dict] = None

@dataclass
class AnalysisResult:
    changes: List[ConfigChange]
    affected_files: Set[str]
    required_updates: Dict[str, List[Dict]]
    
@contextmanager
def openai_session():
    """Context manager to properly handle OpenAI API sessions"""
    try:
        client = openai.OpenAI()
        yield client
    finally:
        if hasattr(client, 'close'):
            client.close()

def call_o1_mini(prompt: str) -> str:
    """Call the o1-mini model with the given prompt"""
    with openai_session() as client:
        try:
            response = client.chat.completions.create(
                model="o1-mini",
                messages=[{"role": "user", "content": prompt}]
            )
            return response.choices[0].message.content
        except Exception as e:
            return f"Error generating output: {str(e)}"
        
def clean_ai_response(response: str) -> str:
    """Clean the AI response to ensure valid JSON"""
    try:
        # Remove any potential markdown code block markers
        response = re.sub(r'```json\s*', '', response)
        response = re.sub(r'```\s*$', '', response)
        
        # Remove any leading/trailing whitespace
        response = response.strip()
        
        # Attempt to parse JSON to validate
        json.loads(response)
        
        return response
    except json.JSONDecodeError as e:
        print(f"Error cleaning response: {str(e)}")
        print("Raw response:", response)
        return "{}"

def analyze_config_structure(config_content: str, project_content: str, current_handlers: str) -> Dict:
    """First AI: Analyzes the current config structure and determines required changes"""
    analysis_prompt = f"""
    You are an expert software engineer performing a comprehensive analysis.You must analyze the current state of the code WITHOUT relying on any previous context or memory.
    
    IMPORTANT RULES:
    1. ONLY analyze the code provided in this prompt
    2. DO NOT reference or recall any previous analyses
    3. DO NOT suggest changes based on previous versions
    4. ONLY compare against the current files provided
    5. IGNORE any remembered patterns or previous suggestions
    6. The config file (page_prompts_config.py) is the source of truth for naming conventions
    7. Component names MUST match their corresponding config names EXACTLY
    
    Your task is to analyze and focus on the configuration structure and validate the relationships between components.

    1. OUTPUT AND INPUT VALIDATION RULES:
       - Not all outputs/inputs require UI component mappings
       - Special outputs like 'quotation_cost' can be internal values used as inputs and outputs in other steps
       - Internal values can be passed between steps without UI components , so just DO NOT suggest any changes for them.
       - Examples of internal values:
         * quotation_cost
         * project_detail
         * gathered_project_input
         * reviewed_project_input
         * client_follow_up_questions 
         * follow_up_questions
         * client_followup_question_sample_answers

    2. OUTPUT TO UI COMPONENT MAPPING RULES:
       - Most outputs in config map to specific UI components
       - Standard Output Pattern:
         * Base output name MUST map to TWO components:
           - base_name_text: For textbox display
           - base_name_markdown: For markdown display
       - Special Case - Mandays Pattern:
         * Mandays outputs MUST map to ONE component:
           - base_name_dataframe: For dataframe display
           
    3. INPUT TO UI COMPONENT MAPPING RULES:
       - Only UI-interactive inputs need component mappings
       - When needed, inputs MUST reference either the _text or _markdown variant

    4. NAMING CONVENTION (Source of Truth: Config File)
       MUST CHECK:
       - Prompt names 
       - Component names 
       - Input/Output references
       
       WHERE TO CHECK:
       - event_handlers.py: Function names, component references
       - Project.py: Method names, mappings
       - all_components dictionary: Component keys
       
    5. HANDLER VALIDATION RULES:
       - Each step handler must correctly map:
         * Inputs to component _text or _markdown
         * Outputs to both _text and _markdown (or _dataframe for mandays)
       - all_components dictionary keys must exactly match config naming
       - Function parameters should align with Project.py signatures
    
    6. PROMPTCONFIG CHANGES:
       A. REMOVED PROMPTCONFIGS:
          - When a PromptConfig is removed from config:
            * ALL references to that prompt must be removed from Project.py
            * ALL UI components associated with that prompt must be removed from event_handlers.py
            * ALL method signatures and return values must be updated accordingly
            * ALL dependent code that referenced the removed components must be updated
          - Check for:
            * Orphaned UI components
            * Unused function parameters
            * Outdated return values
            * Broken event handler mappings

       B. NEW PROMPTCONFIGS:
          - When a new PromptConfig is added to config:
            * Add corresponding execute_prompt() call in Project.py
            * Add UI component references in event_handlers.py
            * Add new step button handler if step is specified
            * Add necessary input/output mappings
          - Check for:
            * Required method signatures
            * Input/output component creation
            * Event handler connections
            * Step button integration
            * State management updates


    Context Files:
    1. Current Config:
    {config_content}

    2. Project.py:
    {project_content}

    3. event_handlers.py:
    {current_handlers}
    
    CRITICAL ANALYSIS RULES:
    1. RENAMED PROMPTS:
       - First check if a component missing from config exists with a similar name
       - Compare component names using similarity matching (e.g., generate_Tech_SOW vs generate_Technical_SOW)
       - If a similar name exists in config, treat it as a rename operation
       - Add it to the 'renamed_prompts' section with old and new names
       - Do NOT mark it as a removed prompt
       
    2. REMOVED PROMPTS:
       - Only mark a prompt as removed if NO similar name exists in config
       - If unsure, prefer rename over removal
       - For confirmed removals:
         * List ALL code locations that need cleanup
         * Include exact file paths and line numbers if possible
         * Specify what code needs to be removed

    3. NAMING UPDATES:
       - When a component name changes in config:
         * Update ALL references to match exactly
         * Maintain consistent casing and formatting
         * Update both component names and method references
    
    Return a JSON analysis with this EXACT structure as shown as an example below:
    {{
        "component_mappings": {{
            "outputs": [
                {{
                    "config_name": "base_output_name",
                    "required_components": ["component1", "component2"],
                    "current_components": ["existing1", "existing2"],
                    "is_valid": false,
                    "issues": ["detailed_issue_description"]
                }}
            ],
            "inputs": [
                {{
                    "config_name": "input_name",
                    "required_component": "required_component_name",
                    "current_component": "current_component_name",
                    "is_valid": false,
                    "issues": ["detailed_issue_description"]
                }}
            ],
            "buttons": [
                {{
                    "button_id": "button_name",
                    "type": "step|action|recalculate|upload",
                    "handler": "handler_function_name",
                    "inputs": ["input1", "input2"],
                    "outputs": ["output1", "output2"],
                    "state_updates": ["state1", "state2"],
                    "is_valid": false,
                    "issues": ["detailed_issue_description"]
                }}
            ]
        }},
       "event_handlers": {{
            "handlers": [
                {{
                    "button_id": "button_name",
                    "type": "step|recalculate|upload",
                    "input_mappings": {{
                        "is_valid": false,
                        "issues": ["detailed_issue_description"]
                    }},
                    "output_mappings": {{
                        "is_valid": false,
                        "issues": ["detailed_issue_description"]
                    }},
                    "state_mappings": {{
                        "is_valid": false,
                        "issues": ["detailed_issue_description"]
                    }}
                }}
            ]
        }},
        "prompt_changes": {{
            "removed_prompts": [
                {{
                    "prompt_name": "name_of_removed_prompt",
                    "affected_components": ["component1", "component2"],
                    "affected_handlers": ["handler1", "handler2"],
                    "cleanup_required": {{
                        "Project.py": [
                            {{
                                "type": "method_removal|signature_update",
                                "location": "exact_location",
                                "code_to_remove": "code_snippet",
                                "reason": "detailed_explanation"
                            }}
                        ],
                        "event_handlers.py": [
                            {{
                                "type": "component_removal|handler_update",
                                "location": "exact_location",
                                "code_to_remove": "code_snippet",
                                "reason": "detailed_explanation"
                            }}
                        ]
                    }}
                }}
            ],
              "renamed_prompts": [
                {{
                    "old_name": "old_prompt_name",
                    "new_name": "new_prompt_name",
                    "affected_files": {{
                        "Project.py": [
                            {{
                                "type": "method_rename",
                                "location": "method references",
                                "old_code": "old_code_snippet",
                                "new_code": "new_code_snippet",
                                "reason": "Prompt name updated in config"
                            }}
                        ],
                        "event_handlers.py": [
                            {{
                                "type": "component_rename",
                                "location": "component references",
                                "old_code": "old_code_snippet",
                                "new_code": "new_code_snippet",
                                "reason": "Prompt name updated in config"
                            }}
                        ]
                    }}
                }}
            ]
              "new_prompts": [
                {{
                    "prompt_name": "name_of_new_prompt",
                    "required_components": ["component1", "component2"],
                    "required_handlers": ["handler1", "handler2"],
                    "additions_required": {{
                        "Project.py": [
                            {{
                                "type": "method_addition|signature_update",
                                "location": "exact_location",
                                "code_to_add": "code_snippet",
                                "reason": "detailed_explanation"
                            }}
                        ],
                        "event_handlers.py": [
                            {{
                                "type": "component_addition|handler_creation",
                                "location": "exact_location",
                                "code_to_add": "code_snippet",
                                "reason": "detailed_explanation"
                            }}
                        ]
                    }}
                }}
            ]
        }},
        "required_updates": {{
            "Project.py": [
                {{
                    "type": "method|mapping",
                    "location": "exact_location",
                    "reason": "detailed_explanation"
                }}
            ],
            "event_handlers.py": [
                {{
                    "button_id": "button_name",
                    "type": "step|recalculate|upload",
                    "current": "current_code",
                    "required": "required_code",
                    "reason": "detailed_explanation"
                }}
            ]
        }}
    }}

    IMPORTANT:
    1. Validate EVERY output has correct component mappings
    2. Check EVERY input references correct component variant
    3. Verify ALL component names match exactly
    4. Flag ANY case mismatches or naming inconsistencies
    5. Return ONLY the JSON object, no additional text
    """

    try:
        result = call_o1_mini(analysis_prompt)
        cleaned_result = clean_ai_response(result)
        return json.loads(cleaned_result)
    except Exception as e:
        print(f"Error in analysis: {str(e)}")
        print("Raw response:", result)
        return {
            "component_mappings": {"outputs": [], "inputs": []},
            "event_handlers": {"steps": []},
            "required_updates": {}
        }


def generate_code_updates(analysis_result: Dict) -> Dict:
    """Second AI: Generates specific code updates based on the analysis"""
    code_generation_prompt = f"""
    You are an expert code generator. Based on this analysis, generate EXACT code updates.
    
    Analysis Result:
    {json.dumps(analysis_result, indent=2)}

    CRITICAL RULES:
    1. REMOVED PROMPTS:
       When a prompt is removed from config:
       - REMOVE all related component references
       - REMOVE all related handler code
       - DO NOT suggest adding new components
       - DO NOT suggest new mappings
       
    2. NEW PROMPTS:
       When a new prompt is added to config:
       - ADD corresponding execute_prompt() method in Project.py
       - ADD UI component references in event_handlers.py
       - ADD new step button handler if step is specified
       - ADD necessary input/output mappings
       - FOLLOW exact naming from config
       - ENSURE all required components are created
       
    3. Processing Order:
       a) Process removals FIRST (prompt_changes.removed_prompts)
       b) Then process additions (prompt_changes.new_prompts)
       c) Finally process other updates

    Generate a JSON response with this EXACT structure:
    {{
        "Project.py": {{
            "updates": [
                {{
                    "type": "removal|addition|update",  # Must specify type
                    "location": "exact_location",
                    "explanation": "Removing/Adding/Updating for prompt 'prompt_name'",
                    "old_code": "existing code to remove or update",
                    "new_code": "new code to add or update with",
                    "template": {{  # Only for additions
                        "method_signature": "def method_name(self, ...)",
                        "docstring": "\"\"\"Method documentation\"\"\"",
                        "implementation": "method implementation",
                        "return_statement": "return statement"
                    }}
                }}
            ]
        }},
        "event_handlers.py": {{
            "updates": [
                {{
                    "type": "removal|addition|update",
                    "location": "event_handlers.py - Component References",
                    "explanation": "Adding/Removing components for prompt 'prompt_name'",
                    "old_code": "code to remove or update",
                    "new_code": "new code or empty string for removals",
                    "components": [  # Only for additions
                        {{
                            "name": "component_name",
                            "type": "text|markdown|dataframe",
                            "initial_value": "initial value"
                        }}
                    ],
                    "handler": {{  # Only for additions requiring handlers
                        "name": "handler_name",
                        "inputs": ["input1", "input2"],
                        "outputs": ["output1", "output2"],
                        "implementation": "handler implementation"
                    }}
                }}
            ]
        }}
    }}

    Requirements:
    1. Process changes in correct order (removals -> additions -> updates)
    2. Each update MUST specify correct type (removal|addition|update)
    3. For removals, new_code should be empty string
    4. For additions:
       - Include complete method templates
       - Include all required components
       - Include handler implementations if needed
       - Follow exact naming from config
    5. Maintain exact function signatures
    6. Preserve existing code structure
    7. Include clear comments explaining changes
    8. Each file must have an 'updates' array, even if empty

    IMPORTANT: Return ONLY the JSON object, without any markdown formatting or explanation.
    """
    
    try:
        result = call_o1_mini(code_generation_prompt)
        cleaned_result = clean_ai_response(result)
        parsed_result = json.loads(cleaned_result)
        
        # Ensure correct structure and validate updates
        for file_name in ["Project.py", "event_handlers.py"]:
            if file_name not in parsed_result:
                parsed_result[file_name] = {"updates": []}
            
            # Ensure updates array exists
            if "updates" not in parsed_result[file_name]:
                parsed_result[file_name]["updates"] = []
                
            # Validate each update
            for update in parsed_result[file_name]["updates"]:
                if "type" not in update:
                    update["type"] = "update"  # Default type if missing
                if update["type"] == "removal" and update.get("new_code"):
                    update["new_code"] = ""  # Ensure removals have empty new_code
                if update["type"] == "addition":
                    # Ensure template exists for Project.py additions
                    if file_name == "Project.py" and "template" not in update:
                        update["template"] = {
                            "method_signature": "",
                            "docstring": "",
                            "implementation": "",
                            "return_statement": ""
                        }
                    # Ensure components and handler exist for event_handlers.py additions
                    if file_name == "event_handlers.py":
                        if "components" not in update:
                            update["components"] = []
                        if "handler" not in update:
                            update["handler"] = {
                                "name": "",
                                "inputs": [],
                                "outputs": [],
                                "implementation": ""
                            }
                    
        return parsed_result
        
    except Exception as e:
        print(f"Error generating code updates: {str(e)}")
        print("Raw response:", result)
        return {
            "Project.py": {"updates": []},
            "event_handlers.py": {"updates": []}
        }

def apply_updates_with_ai(file_path: str, updates: List[Dict]) -> bool:
    """Third AI: Intelligently applies code updates while preserving structure"""
    try:
        with open(file_path, 'r') as f:
            current_code = f.read()
            
        # Store original empty lines and their positions
        original_lines = current_code.splitlines(keepends=True)
        empty_line_indices = [i for i, line in enumerate(original_lines) if not line.strip()]
        trailing_spaces = [len(line) - len(line.rstrip()) for line in original_lines]
            
        update_prompt = f"""
        You are an expert code updater. Your task is to apply updates to the code while perfectly preserving its structure and formatting.
        
        CRITICAL RULES:
        1. DO NOT modify any line breaks or indentation
        2. DO NOT add or remove any lines
        3. DO NOT change any code structure or formatting
        4. ONLY replace the exact old_code with new_code
        5. Return the COMPLETE file with ALL original formatting preserved
        6. Keep ALL whitespace, commas, and brackets exactly as they are
        7. Return ONLY the code, no explanations or markdown
        8. Preserve ALL empty lines in their exact positions
        9. Keep ALL trailing spaces at the end of each line
        
        Current Code:
        {current_code}
        
        Required Updates:
        {json.dumps(updates, indent=2)}
        
        IMPORTANT: 
        - Empty lines are at these positions: {empty_line_indices}
        - Each line must maintain its exact trailing spaces
        - Return the complete file with perfect structure preservation
        """
        
        # Get AI-generated update
        result = call_o1_mini(update_prompt)
        updated_code = clean_code_response(result)
        
        # Split into lines while preserving endings
        updated_lines = updated_code.splitlines(keepends=True)
        
        # Handle line structure preservation
        updated_code = preserve_line_structure(original_lines, updated_lines, empty_line_indices, trailing_spaces)
        
        # Verify updates were applied using flexible pattern matching
        for update in updates:
            old_code = update.get('old_code', '').strip()
            new_code = update.get('new_code', '').strip()
            
            # Skip empty updates
            if not old_code and not new_code:
                continue
                
            # For renames, extract the core identifiers
            if update.get('type') == 'rename':
                old_identifier = extract_identifier(old_code)
                new_identifier = extract_identifier(new_code)
                
                if old_identifier in updated_code:
                    print(f"Error: Old identifier '{old_identifier}' still present")
                    return False
                if new_identifier not in updated_code:
                    print(f"Error: New identifier '{new_identifier}' not found")
                    return False
            else:
                # For other updates, check if the change was applied
                if old_code and old_code in updated_code:
                    print(f"Error: Old code still present")
                    return False
                if new_code and new_code not in updated_code:
                    print(f"Error: New code not found")
                    return False
        
        # Write the updated code
        with open(file_path, 'w') as f:
            f.write(updated_code)
            
        return True
        
    except Exception as e:
        print(f"Error applying AI updates: {str(e)}")
        return False

def clean_code_response(result: str) -> str:
    """Clean up the AI response code"""
    if '```python' in result:
        result = result.split('```python', 1)[1]
    if '```' in result:
        result = result.split('```', 1)[0]
    return result.strip()

def preserve_line_structure(original_lines: List[str], updated_lines: List[str], 
                          empty_indices: List[int], trailing_spaces: List[int]) -> str:
    """Preserve the original code structure"""
    if len(original_lines) != len(updated_lines):
        fixed_lines = []
        for i in range(len(original_lines)):
            if i in empty_indices:
                fixed_lines.append('\n')
            else:
                line = updated_lines[i].rstrip() if i < len(updated_lines) else ''
                line = line + ' ' * trailing_spaces[i]
                fixed_lines.append(line + '\n')
        return ''.join(fixed_lines)
    return ''.join(updated_lines)

def extract_identifier(code: str) -> str:
    """Extract the core identifier from a code snippet"""
    # Remove common code patterns
    code = code.replace('self.', '')
    code = code.replace('execute_prompt(', '')
    code = code.replace('generated_', '')
    code = code.replace('all_components[', '')
    code = code.replace(']', '')
    code = code.replace('"', '')
    code = code.replace("'", '')
    
    # Return the cleaned identifier
    return code.strip()

def extract_config_without_prompts() -> str:
    """Extract config file content without prompts"""
    try:
        with open('page_prompts_config.py', 'r') as f:
            config_lines = []
            in_prompt_block = False
            in_multiline_string = False
            prompt_indent = 0
            
            for line in f:
                stripped_line = line.strip()
                
                # Skip empty lines
                if not stripped_line:
                    continue
                
                # Detect start of multi-line string
                if '"""' in line:
                    if not in_multiline_string:
                        in_multiline_string = True
                        in_prompt_block = True
                        continue
                    else:
                        in_multiline_string = False
                        in_prompt_block = False
                        continue
                
                # Skip lines while in a prompt block
                if in_prompt_block or in_multiline_string:
                    continue
                
                # Detect single-line prompt assignments
                if 'prompt=' in line and not in_multiline_string:
                    continue
                
                # Keep all other lines
                config_lines.append(line)
            
            return ''.join(config_lines)
    except Exception as e:
        return f"Error reading config file: {str(e)}"

def main():
    """Main function to handle config changes"""
    try:
        # Read current files
        config_content = extract_config_without_prompts()
        if config_content.startswith("Error"):
            print(config_content)
            return
            
        with open('Project.py', 'r') as f:
            project_content = f.read()
        with open('event_handlers.py', 'r') as f:
            current_handlers = f.read()

        # First AI: Analyze current structure
        print("\nAnalyzing configuration structure...")
        analysis_result = analyze_config_structure(config_content, project_content, current_handlers)

        if not analysis_result.get('component_mappings'):
            print("Error: Invalid analysis result format")
            return

        # Display analysis results
        print("\nConfiguration Analysis:")
        print("1. Output Mappings:", len(analysis_result['component_mappings']['outputs']))
        print("2. Input Mappings:", len(analysis_result['component_mappings']['inputs']))
        print("3. Event Handlers:", len(analysis_result['event_handlers']['handlers']))

        if analysis_result.get('required_updates'):
            print("\nRequired Updates:")
            for file, updates in analysis_result['required_updates'].items():
                print(f"\n{file}:")
                for update in updates:
                    # Safely access update information with fallbacks
                    update_type = update.get('type', 'Unknown')
                    explanation = update.get('reason', update.get('explanation', 'No explanation provided'))
                    print(f"- {update_type}: {explanation}")

        if analysis_result.get('validation_issues'):
            print("\nValidation Issues:")
            for issue in analysis_result['validation_issues']:
                severity = issue.get('severity', 'unknown')
                description = issue.get('description', 'No description provided')
                issue_type = issue.get('type', 'Unknown')
                print(f"- {issue_type} ({severity}): {description}")

        # Get user confirmation
        confirm = input("\nProceed with generating code updates? (y/n): ")
        if confirm.lower() != 'y':
            print("Analysis complete. Update cancelled.")
            return

        # Second AI: Generate code updates
        print("\nGenerating code updates...")
        code_updates = generate_code_updates(analysis_result)

        # Display proposed changes
        print("\nProposed Code Updates:")
        for file, updates in code_updates.items():
            print(f"\n{file}:")
            for update in updates['updates']:
                print(f"- Location: {update.get('location', 'Unknown location')}")
                print(f"  Explanation: {update.get('explanation', 'No explanation provided')}")
                print("  Old code:")
                print(f"```\n{update.get('old_code', 'No old code provided')}\n```")
                print("  New code:")
                print(f"```\n{update.get('new_code', 'No new code provided')}\n```")

        # Final confirmation
        confirm = input("\nApply these code updates? (y/n): ")
        if confirm.lower() != 'y':
            print("Updates cancelled.")
            return

        # Apply updates
        success = True
        if 'Project.py' in code_updates:
            success &= apply_updates_with_ai('Project.py' ,code_updates['Project.py']['updates'])
        if 'event_handlers.py' in code_updates:
            success &= apply_updates_with_ai('event_handlers.py' , code_updates['event_handlers.py']['updates'])

        if success:
            print("Successfully updated all files")
        else:
            print("Some updates failed - please check the files manually")

    except Exception as e:
        print(f"Error in update process: {str(e)}")
        import traceback
        print("Traceback:", traceback.format_exc())

if __name__ == "__main__":
    main()