from typing import Dict import openai from contextlib import contextmanager import re import json @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 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 analyze_files(config_content: str, project_content: str, current_handlers: str) -> dict: """First analyze all files and extract relevant information""" analysis_prompt = f""" You are an expert software engineer performing a comprehensive analysis. You must understand these key relationships: 1. Config Output to UI Components Mapping: - A single output in config (e.g., 'generated_bd_SOW') can map to multiple UI components - UI components typically come in pairs: * _text: For textbox display * _markdown: For markdown display - As for mandays , it only maps to one UI components which is in *_dataframe Example: Config output 'generated_bd_SOW' maps to: - generated_bd_SOW_text - generated_bd_SOW_markdown 2. Config Input to UI Component Mapping : - The inputs should take the _text or _markdown of the component 3. Component Name and Config Key Validation: - Names in handlers must exactly match config - Both section names and component names must match - Pay special attention to component name changes - Pay attention to case sensitivity 4. Prompt Name Validation : - Prompt names in config are the source of truth - Any references to these names in Project.py and event_handlers.py must match EXACTLY - Check for: * Function names in event_handlers.py * Component references in all_components dictionary * Input/output mappings in step handlers * Method names in Project.py 4. Input/Output Name Validation: - Config keys must match exactly in handlers - Check all_components dictionary keys in handlers against config - Example: if config uses 'generate_development_mandays', handlers must use the same key - Flag any mismatches between config keys and handler references 4. Function Parameter Mapping: - Function parameters in Project.py define the actual data flow - UI components must map to these parameters correctly Context Files: 1. page_prompts_config.py (SOURCE OF TRUTH FOR NAMES & UI): {config_content} 2. Project.py (SOURCE OF TRUTH FOR FUNCTION PARAMETERS): {project_content} 3. event_handlers.py (NEEDS VALIDATION): {current_handlers} Analysis Requirements: 1. UI Component Validation: - Check each config output's UI components - Verify both _text and _markdown variants exist where specified - Ensure case matches exactly 2. Component Name Validation: - Compare against config definitions - Check both section names and component names - Flag any case mismatches 3. Function Parameter Validation: - Verify against Project.py signatures - Check parameter order and types - Ensure all required parameters are included Return your analysis in this EXACT JSON format: {{ "step_1": {{ "current_code": "exact current handler code", "needs_update": false, "updates": [] }}, "step_2": {{ "current_code": "exact current handler code", "needs_update": true, "updates": [ {{ "type": "component_name", "line": "all_components['generate_BD_SOW']['generated_BD_SOW_text']", "replacement": "all_components['generate_bd_SOW']['generated_bd_SOW_text']", "reason": "Case mismatch with config definition" }} ] }}, "step_3": {{ "current_code": "exact current handler code", "needs_update": true, "updates": [ {{ "type": "component_name", "line": "all_components['generate_BD_SOW']['generated_BD_SOW_text']", "replacement": "all_components['generate_bd_SOW']['generated_bd_SOW_text']", "reason": "Case mismatch with config definition" }} ] }} }} Requirements: 1. Include ALL steps (1-3) 2. For each step: - Include exact current code - Set needs_update to true if changes needed - List ALL required updates 3. For each update: - Provide exact current line - Provide exact replacement - Include clear reason 4. Check: - Case sensitivity - Component names - Function parameters - UI component mappings Every discrepancy must be reported as an issue with: - Clear description of the problem - Exact current implementation - Exact expected implementation from config - Specific location in the code If no issues are found for a step, include an empty issues array. Remember: - Component names must match config - Function parameters must match Project.py signatures - Some components may be correct in function but need case fixes from config - DO NOT suggest changes unless there is a 100% exact mismatch - If a component or parameter does not have any changes and it does exist and works, DO NOT suggest changes """ analysis = call_o1_mini(analysis_prompt) return analysis def generate_handler_code(step_number: int, analysis_result: dict) -> str: """Generate the handler code based on the analysis""" step_key = f"step_{step_number}" step_data = analysis_result.get(step_key, {}) if not step_data.get("needs_update", False): return "" current_code = step_data["current_code"] updates = step_data.get("updates", []) # Apply all updates updated_code = current_code for update in updates: updated_code = updated_code.replace(update["line"], update["replacement"]) return updated_code def analyze_prompts_and_generate_handlers() -> Dict[int, str]: """Generate step handlers separately""" try: # Read all files once config_content = extract_config_without_prompts() print("\nConfig content loaded") with open('Project.py', 'r') as f: project_content = f.read() print("Project.py content loaded") with open('event_handlers.py', 'r') as f: current_handlers = f.read() print("event_handlers.py content loaded") # Do one analysis for all files print("\nAnalyzing all files...") analysis_result = analyze_files(config_content, project_content, current_handlers) # Parse the analysis result from string to JSON try: if isinstance(analysis_result, str): # Remove ```json and ``` markers if present analysis_result = analysis_result.replace('```json\n', '').replace('\n```', '').strip() analysis_result = json.loads(analysis_result) print("\nRaw analysis result:") print(json.dumps(analysis_result, indent=2)) handlers = {} for step in range(1, 4): step_key = f"step_{step}" if step_key not in analysis_result: print(f"\nNo analysis results found for {step_key}") continue step_code = generate_handler_code(step, analysis_result) # Clean up and validate the response if step_code: step_code = step_code.strip() if not step_code.startswith(f"step_buttons['Step {step}"): print(f"\nInvalid handler code format for Step {step}") # Extract current handler as fallback pattern = rf"step_buttons\['Step {step} : [^']*'\]\.click\([^)]+\)" current_handler = re.search(pattern, current_handlers, re.DOTALL) handlers[step] = current_handler.group(0) if current_handler else None else: handlers[step] = step_code print(f"Generated handler for Step {step}") return handlers except json.JSONDecodeError as e: print(f"Error parsing analysis result: {str(e)}") print("Raw analysis result:", analysis_result) return {} except Exception as e: print(f"Error generating handlers: {str(e)}") import traceback print("Traceback:", traceback.format_exc()) return {} def main(): """Generate and save the step handlers""" handlers = analyze_prompts_and_generate_handlers() print("Generated handlers for steps:", list(handlers.keys())) # Let the user review the changes for step, code in handlers.items(): if not code: continue print(f"\nStep {step} Handler:") print(code) update = input(f"Do you want to update Step {step} handler? (y/n): ") if update.lower() == 'y': try: # Read current content with open('event_handlers.py', 'r') as f: content = f.read() # Find and replace the specific step handler pattern = rf"step_buttons\['Step {step} : [^']*'\]\.click\([^)]+\)" match = re.search(pattern, content, re.DOTALL) if not match: print(f"Warning: Couldn't find Step {step} handler in file") continue # Replace the matched content with the new code updated_content = content[:match.start()] + code.strip() + content[match.end():] # Write back to file with open('event_handlers.py', 'w') as f: f.write(updated_content) print(f"Successfully updated Step {step} handler in event_handlers.py") # Verify the write with open('event_handlers.py', 'r') as f: new_content = f.read() if code.strip() in new_content: print("Verified: Update successful") else: print("Warning: Update may not have been successful") except Exception as e: print(f"Error updating file: {str(e)}") print(f"Error details: {type(e).__name__}") if __name__ == "__main__": main()