import gradio as gr import boto3 import json import os from typing import Optional, Tuple, List import nbformat from io import StringIO import sys import tempfile import zipfile from datetime import datetime import re class CodingCopilot: def __init__(self): """Initialize the coding copilot with AWS Bedrock client.""" self.setup_aws_client() def setup_aws_client(self): """Setup AWS Bedrock client with credentials from environment variables.""" try: self.bedrock_client = boto3.client( 'bedrock-runtime', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'), region_name='us-east-1' ) except Exception as e: print(f"Error setting up AWS client: {e}") self.bedrock_client = None def call_claude(self, prompt: str, max_tokens: int = 8000) -> str: """Call Claude 3 Haiku via AWS Bedrock with increased token limit.""" if not self.bedrock_client: return "โ **Error**: AWS Bedrock client not initialized. Please check your credentials." try: body = { "anthropic_version": "bedrock-2023-05-31", "max_tokens": max_tokens, "messages": [{"role": "user", "content": prompt}], "temperature": 0.1, "top_p": 0.9, } response = self.bedrock_client.invoke_model( modelId="anthropic.claude-3-haiku-20240307-v1:0", contentType='application/json', accept='application/json', body=json.dumps(body) ) response_body = json.loads(response['body'].read()) return response_body['content'][0]['text'] except Exception as e: return f"โ **Error calling Claude**: {str(e)}" def read_python_file(self, file_path: str) -> str: """Read content from a Python file.""" try: with open(file_path, 'r', encoding='utf-8') as f: return f.read() except Exception as e: return f"Error reading file: {str(e)}" def read_notebook_file(self, file_path: str) -> str: """Read and extract code from a Jupyter notebook file.""" try: with open(file_path, 'r', encoding='utf-8') as f: nb = nbformat.read(f, as_version=4) code_cells = [] for cell in nb.cells: if cell.cell_type == 'code': code_cells.append(f"# Cell {len(code_cells) + 1}") code_cells.append(cell.source) code_cells.append("") return '\n'.join(code_cells) except Exception as e: return f"Error reading notebook: {str(e)}" def process_files(self, files: List) -> Tuple[str, str]: """Process uploaded files and return combined content.""" if not files: return "", "No files uploaded" all_content = [] file_info = [] for file in files: file_path = file.name file_name = os.path.basename(file_path) file_extension = os.path.splitext(file_path)[1].lower() if file_extension == '.py': content = self.read_python_file(file_path) elif file_extension == '.ipynb': content = self.read_notebook_file(file_path) else: continue if content and not content.startswith("Error"): all_content.append(f"# ๐ File: {file_name}") all_content.append(f"# ๐ Type: {file_extension}") all_content.append("# " + "="*50) all_content.append(content) all_content.append("\n" + "="*60 + "\n") file_info.append(f"{file_name}") combined_content = '\n'.join(all_content) status = f"โ Successfully loaded {len(file_info)} files: {', '.join(file_info)}" return combined_content, status def extract_code_blocks(self, text: str) -> List[str]: """Extract code blocks from response text.""" code_blocks = re.findall(r'```(?:\w+)?\n(.*?)\n```', text, re.DOTALL) return code_blocks def create_download_file(self, content: str, filename: str = "generated_code.py") -> str: """Create a downloadable file with the given content.""" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") base_name = os.path.splitext(filename)[0] extension = os.path.splitext(filename)[1] or '.py' filename = f"{base_name}_{timestamp}{extension}" temp_file = tempfile.NamedTemporaryFile(mode='w', suffix=extension, delete=False) temp_file.write(content) temp_file.close() return temp_file.name def process_message(self, message: str, files: List = None, history: List = None) -> Tuple[str, Optional[str]]: """Process user message and return response with optional download file.""" if history is None: history = [] # Process files if provided file_content = "" file_status = "" if files: file_content, file_status = self.process_files(files) # Detect continuation requests continue_keywords = ['continue', 'continue the code', 'continue from where', 'keep going', 'add more', 'extend', 'complete the'] is_continuation = any(keyword in message.lower() for keyword in continue_keywords) # Build comprehensive context from history context = "" if history: if is_continuation: context = "\n\nFull conversation context for continuation:\n" for i, (user_msg, assistant_msg) in enumerate(history[-5:]): context += f"Exchange {i+1}:\nUser: {user_msg}\nAssistant: {assistant_msg}\n\n" else: context = "\n\nRecent conversation context:\n" for i, (user_msg, assistant_msg) in enumerate(history[-3:]): context += f"User: {user_msg}\nAssistant: {assistant_msg}\n\n" # Build specialized prompts based on request type if is_continuation: continuation_instructions = """CRITICAL INSTRUCTIONS FOR CONTINUATION: 1. Look at the previous conversation to understand what code was generated 2. Continue from where the previous code left off - DO NOT start from the beginning 3. Maintain the same coding style, structure, and patterns 4. Add new functionality or complete incomplete sections 5. Generate substantial code (aim for 500-2000+ lines if needed) 6. Ensure the continuation integrates seamlessly with previous code""" file_content_section = f"\nCode files content for reference:\n```\n{file_content}\n```" if file_content else "" final_instruction = "Based on the previous conversation, continue the code generation. Do NOT restart - pick up exactly where the previous response left off and continue building upon it. Provide substantial, complete code sections." prompt = f"""You are an advanced coding assistant. The user is asking you to CONTINUE or EXTEND previous code. {continuation_instructions} User's continuation request: {message} {file_status} {file_content_section} {context} {final_instruction}""" else: generation_keywords = ['create', 'generate', 'build', 'make', 'develop', 'write code', 'implement', 'design'] is_generation = any(keyword in message.lower() for keyword in generation_keywords) if is_generation: generation_instructions = """CRITICAL INSTRUCTIONS FOR CODE GENERATION: 1. Generate COMPLETE, COMPREHENSIVE code - aim for 500-3000+ lines when appropriate 2. Include ALL necessary components: classes, functions, error handling, documentation 3. Create fully functional, production-ready applications 4. Add comprehensive comments and docstrings 5. Include proper imports, dependencies, and structure 6. Don't truncate or abbreviate - provide the FULL implementation 7. If the project is large, focus on core functionality but make it complete""" file_content_section = f"\nReference code files:\n```\n{file_content}\n```" if file_content else "" final_instruction = "Generate comprehensive, complete code that fully implements the requested functionality. Provide extensive code with proper structure, documentation, and all necessary components." prompt = f"""You are an advanced coding assistant specialized in generating comprehensive, production-ready code. {generation_instructions} User's request: {message} {file_status} {file_content_section} {context} {final_instruction}""" else: file_content_section = f"\nCode files content:\n```\n{file_content}\n```" if file_content else "" prompt = f"""You are an advanced coding assistant. Provide detailed, comprehensive responses. User's request: {message} {file_status} {file_content_section} {context} Provide a thorough and helpful response. If suggesting code changes, provide complete implementations.""" # Get response from Claude response = self.call_claude(prompt, max_tokens=8000) # Extract code blocks for download code_blocks = self.extract_code_blocks(response) download_file = None if code_blocks: combined_code = "\n\n".join(code_blocks) if len(combined_code.strip()) > 50: # Only create download if substantial code download_file = self.create_download_file(combined_code) return response, download_file # Initialize the copilot copilot = CodingCopilot() # FIXED CSS - Proper layout and visible input area fixed_css = """ /* Reset and base styles */ * { box-sizing: border-box !important; } .gradio-container { max-width: none !important; width: 100% !important; background: #0a0a0a !important; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif !important; color: #ffffff !important; padding: 1rem !important; min-height: 100vh !important; } /* Header */ .header-container { background: linear-gradient(135deg, #1a1a1a 0%, #2d1b1b 50%, #1a1a1a 100%) !important; border: 1px solid #dc2626 !important; border-radius: 16px !important; padding: 1.5rem !important; margin-bottom: 1rem !important; box-shadow: 0 8px 32px rgba(220, 38, 38, 0.15) !important; text-align: center !important; } .header-title { color: #ffffff !important; font-size: 2.2rem !important; font-weight: 700 !important; margin: 0 !important; background: linear-gradient(135deg, #ffffff 0%, #dc2626 100%) !important; -webkit-background-clip: text !important; -webkit-text-fill-color: transparent !important; background-clip: text !important; } .header-subtitle { color: #a1a1aa !important; font-size: 1rem !important; margin: 0.5rem 0 0 0 !important; font-weight: 400 !important; } /* Main layout */ .main-layout { display: flex !important; flex-direction: row !important; gap: 1.5rem !important; width: 100% !important; min-height: calc(100vh - 200px) !important; } /* Sidebar */ .sidebar { background: linear-gradient(180deg, #1a1a1a 0%, #0f0f0f 100%) !important; border: 1px solid #dc2626 !important; border-radius: 16px !important; padding: 1.5rem !important; width: 320px !important; min-width: 320px !important; max-width: 320px !important; box-shadow: 0 8px 32px rgba(220, 38, 38, 0.1) !important; overflow-y: auto !important; height: fit-content !important; max-height: calc(100vh - 200px) !important; } /* Chat area */ .chat-area { background: linear-gradient(180deg, #111111 0%, #0a0a0a 100%) !important; border: 1px solid #262626 !important; border-radius: 16px !important; flex: 1 !important; display: flex !important; flex-direction: column !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important; min-height: 600px !important; } /* Chat content area */ .chat-content { flex: 1 !important; padding: 1.5rem !important; overflow-y: auto !important; min-height: 400px !important; max-height: 500px !important; } /* Input section - FIXED to be visible */ .input-section { background: #1a1a1a !important; border-top: 1px solid #262626 !important; padding: 1.5rem !important; border-radius: 0 0 16px 16px !important; min-height: 300px !important; } /* Quick Actions */ .quick-actions { margin-bottom: 2rem !important; padding-bottom: 1.5rem !important; border-bottom: 1px solid #dc2626 !important; } .sidebar-title { color: #dc2626 !important; font-weight: 700 !important; font-size: 1.1rem !important; margin-bottom: 1rem !important; text-transform: uppercase !important; letter-spacing: 0.5px !important; display: flex !important; align-items: center !important; gap: 0.5rem !important; } .quick-btn { background: #1a1a1a !important; color: #dc2626 !important; border: 1px solid #dc2626 !important; border-radius: 8px !important; padding: 0.8rem 1rem !important; font-weight: 500 !important; cursor: pointer !important; transition: all 0.3s ease !important; margin-bottom: 0.6rem !important; width: 100% !important; text-align: left !important; font-size: 0.85rem !important; display: flex !important; align-items: center !important; gap: 0.6rem !important; } .quick-btn:hover { background: #dc2626 !important; color: #ffffff !important; transform: translateX(3px) !important; box-shadow: 0 4px 12px rgba(220, 38, 38, 0.2) !important; } /* Capabilities */ .capabilities { color: #d1d5db !important; font-size: 0.85rem !important; line-height: 1.5 !important; } .capabilities strong { color: #ffffff !important; display: block !important; margin-bottom: 0.3rem !important; } .capability-item { margin-bottom: 1rem !important; padding: 0.5rem 0 !important; border-bottom: 1px solid #262626 !important; } .capability-item:last-child { border-bottom: none !important; } /* Download section */ .download-section { background: #1a1a1a !important; border: 1px solid #dc2626 !important; border-radius: 8px !important; padding: 1rem !important; margin-bottom: 1rem !important; } /* File upload */ .file-upload { background: #1a1a1a !important; border: 1px dashed #404040 !important; border-radius: 8px !important; padding: 1rem !important; margin-bottom: 1rem !important; transition: all 0.3s ease !important; } .file-upload:hover { border-color: #dc2626 !important; background: #262626 !important; } /* Message input */ .msg-input { background: #262626 !important; border: 1px solid #404040 !important; border-radius: 8px !important; color: #ffffff !important; padding: 1rem !important; font-size: 0.95rem !important; line-height: 1.4 !important; resize: vertical !important; transition: all 0.3s ease !important; width: 100% !important; margin-bottom: 1rem !important; min-height: 100px !important; } .msg-input:focus { outline: none !important; border-color: #dc2626 !important; box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.1) !important; } .msg-input::placeholder { color: #6b7280 !important; } /* Button row */ .btn-row { display: flex !important; gap: 1rem !important; align-items: center !important; justify-content: flex-end !important; } .btn-primary { background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%) !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 0.8rem 1.5rem !important; font-weight: 600 !important; font-size: 0.9rem !important; cursor: pointer !important; transition: all 0.3s ease !important; box-shadow: 0 4px 16px rgba(220, 38, 38, 0.2) !important; text-transform: uppercase !important; letter-spacing: 0.5px !important; min-width: 80px !important; } .btn-primary:hover { background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%) !important; transform: translateY(-1px) !important; box-shadow: 0 6px 20px rgba(220, 38, 38, 0.3) !important; } .btn-secondary { background: #262626 !important; color: #ffffff !important; border: 1px solid #404040 !important; border-radius: 8px !important; padding: 0.8rem 1.5rem !important; font-weight: 500 !important; cursor: pointer !important; transition: all 0.3s ease !important; text-transform: uppercase !important; letter-spacing: 0.5px !important; font-size: 0.9rem !important; min-width: 80px !important; } .btn-secondary:hover { background: #404040 !important; border-color: #dc2626 !important; } /* Chatbot container */ .chatbot-container { border: none !important; background: transparent !important; overflow-y: auto !important; scroll-behavior: smooth !important; } /* Footer */ .footer { background: #0a0a0a !important; border-top: 1px solid #262626 !important; padding: 1rem !important; text-align: center !important; color: #6b7280 !important; border-radius: 8px !important; font-size: 0.8rem !important; margin-top: 2rem !important; } .footer strong { color: #dc2626 !important; } /* Scrollbar */ ::-webkit-scrollbar { width: 6px !important; height: 6px !important; } ::-webkit-scrollbar-track { background: #1a1a1a !important; border-radius: 3px !important; } ::-webkit-scrollbar-thumb { background: #404040 !important; border-radius: 3px !important; } ::-webkit-scrollbar-thumb:hover { background: #dc2626 !important; } /* Code blocks */ pre { background: #1a1a1a !important; border: 1px solid #404040 !important; border-radius: 6px !important; padding: 1rem !important; overflow-x: auto !important; font-size: 0.85rem !important; } code { background: #262626 !important; padding: 2px 4px !important; border-radius: 3px !important; font-size: 0.85rem !important; } /* Responsive design */ @media (max-width: 768px) { .main-layout { flex-direction: column !important; } .sidebar { width: 100% !important; max-width: none !important; min-width: auto !important; height: auto !important; max-height: 400px !important; } .header-title { font-size: 1.8rem !important; } } """ def process_chat_message(message, files, history): """Process a new chat message with download support.""" if not message.strip() and not files: return history, history, "", None, "" if history is None: history = [] user_msg = message if files: file_names = [os.path.basename(f.name) for f in files] user_msg += f" ๐ *[Files: {', '.join(file_names)}]*" # Get AI response with potential download file ai_response, download_file = copilot.process_message(message, files, history) # Add to history history.append([user_msg, ai_response]) # Return download file info download_info = "" if download_file: download_info = "๐ **Code generated successfully!** Click the download button below to save your code." return history, history, "", download_file, download_info def clear_chat(): """Clear the chat history.""" return [], [], None, "" def update_download_file(response, history): """Update download file when new response is generated.""" if not response: return None, "" # Extract code blocks from the latest response code_blocks = copilot.extract_code_blocks(response) if code_blocks: combined_code = "\n\n".join(code_blocks) if len(combined_code.strip()) > 50: download_file = copilot.create_download_file(combined_code) return download_file, "๐ **Code generated!** Download available below." return None, "" # Create the main interface with gr.Blocks(css=fixed_css, title="Coding Copilot Pro", theme=gr.themes.Base()) as app: # Header gr.HTML("""
Advanced AI-Powered Development Assistant โข Powered by Claude 3 Haiku