Rename core.py to utils.py
Browse files
    	
        core.py
    DELETED
    
    | 
         @@ -1,49 +0,0 @@ 
     | 
|
| 1 | 
         
            -
            # /core.py
         
     | 
| 2 | 
         
            -
            """ Core business logic for the code generation application. """
         
     | 
| 3 | 
         
            -
            from typing import Dict, List, Optional, Tuple, Generator, Any
         
     | 
| 4 | 
         
            -
            from config import (HTML_SYSTEM_PROMPT, GENERIC_SYSTEM_PROMPT, HTML_SYSTEM_PROMPT_WITH_SEARCH, 
         
     | 
| 5 | 
         
            -
                                GENERIC_SYSTEM_PROMPT_WITH_SEARCH, FollowUpSystemPrompt)
         
     | 
| 6 | 
         
            -
            from services import llm_service, search_service
         
     | 
| 7 | 
         
            -
            from extractor import extract_text_from_file, extract_website_content
         
     | 
| 8 | 
         
            -
            from utils import (history_to_messages, remove_code_block, process_image_for_model, 
         
     | 
| 9 | 
         
            -
                               apply_search_replace_changes)
         
     | 
| 10 | 
         
            -
             
     | 
| 11 | 
         
            -
            History = List[Tuple[Optional[str], Optional[str]]]
         
     | 
| 12 | 
         
            -
             
     | 
| 13 | 
         
            -
            def _determine_system_prompt(language: str, enable_search: bool, history: History) -> Tuple[str, bool]:
         
     | 
| 14 | 
         
            -
                is_follow_up = bool(history and history[-1][1] and ("<html" in history[-1][1]))
         
     | 
| 15 | 
         
            -
                if is_follow_up: return FollowUpSystemPrompt, True
         
     | 
| 16 | 
         
            -
             
     | 
| 17 | 
         
            -
                if language == "html":
         
     | 
| 18 | 
         
            -
                    return (HTML_SYSTEM_PROMPT_WITH_SEARCH if enable_search else HTML_SYSTEM_PROMPT), False
         
     | 
| 19 | 
         
            -
                else:
         
     | 
| 20 | 
         
            -
                    base = GENERIC_SYSTEM_PROMPT_WITH_SEARCH if enable_search else GENERIC_SYSTEM_PROMPT
         
     | 
| 21 | 
         
            -
                    return base.format(language=language), False
         
     | 
| 22 | 
         
            -
             
     | 
| 23 | 
         
            -
            def _prepare_user_content(query: str, image_data: Optional[Any], file_path: Optional[str], website_url: Optional[str], enable_search: bool) -> Any:
         
     | 
| 24 | 
         
            -
                context_parts = [query]
         
     | 
| 25 | 
         
            -
                if file_path: context_parts.append(f"\n\n--- Reference File ---\n{extract_text_from_file(file_path)[:8000]}")
         
     | 
| 26 | 
         
            -
                if website_url: context_parts.append(f"\n\n--- Website to Redesign ---\n{extract_website_content(website_url)}")
         
     | 
| 27 | 
         
            -
                full_query = "".join(context_parts)
         
     | 
| 28 | 
         
            -
                if enable_search and search_service.is_available(): full_query += f"\n\n--- Web Search Results ---\n{search_service.search(full_query)}"
         
     | 
| 29 | 
         
            -
                
         
     | 
| 30 | 
         
            -
                if image_data is not None:
         
     | 
| 31 | 
         
            -
                    return [{"type": "text", "text": full_query}, {"type": "image_url", "image_url": {"url": process_image_for_model(image_data)}}]
         
     | 
| 32 | 
         
            -
                return full_query
         
     | 
| 33 | 
         
            -
             
     | 
| 34 | 
         
            -
            def generate_code(query: str, image_data: Optional[Any], file_path: Optional[str], website_url: Optional[str], history: History, 
         
     | 
| 35 | 
         
            -
                              model_config: Dict[str, str], enable_search: bool, language: str) -> Generator[Dict[str, Any], None, None]:
         
     | 
| 36 | 
         
            -
                system_prompt, is_follow_up = _determine_system_prompt(language, enable_search, history)
         
     | 
| 37 | 
         
            -
                messages = history_to_messages(history, system_prompt)
         
     | 
| 38 | 
         
            -
                user_content = _prepare_user_content(query, image_data, file_path, website_url, enable_search)
         
     | 
| 39 | 
         
            -
                messages.append({'role': 'user', 'content': user_content})
         
     | 
| 40 | 
         
            -
             
     | 
| 41 | 
         
            -
                content_stream = ""
         
     | 
| 42 | 
         
            -
                for chunk in llm_service.generate_code_stream(model_config['id'], messages):
         
     | 
| 43 | 
         
            -
                    content_stream += chunk
         
     | 
| 44 | 
         
            -
                    processed_code = apply_search_replace_changes(history[-1][1], content_stream) if is_follow_up else remove_code_block(content_stream)
         
     | 
| 45 | 
         
            -
                    yield {"code_output": processed_code}
         
     | 
| 46 | 
         
            -
             
     | 
| 47 | 
         
            -
                final_code = apply_search_replace_changes(history[-1][1], content_stream) if is_follow_up else remove_code_block(content_stream)
         
     | 
| 48 | 
         
            -
                history.append((query, final_code))
         
     | 
| 49 | 
         
            -
                yield {"code_output": final_code, "history": history}
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
    	
        utils.py
    ADDED
    
    | 
         @@ -0,0 +1,123 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            from typing import List, Tuple, Dict
         
     | 
| 2 | 
         
            +
            import re
         
     | 
| 3 | 
         
            +
             
     | 
| 4 | 
         
            +
            # History/message helpers
         
     | 
| 5 | 
         
            +
            History = List[Tuple[str, str]]
         
     | 
| 6 | 
         
            +
            Messages = List[Dict[str, str]]
         
     | 
| 7 | 
         
            +
             
     | 
| 8 | 
         
            +
            def history_to_messages(history: History, system: str) -> Messages:
         
     | 
| 9 | 
         
            +
                messages = [{'role': 'system', 'content': system}]
         
     | 
| 10 | 
         
            +
                for h in history:
         
     | 
| 11 | 
         
            +
                    user_content = h[0]
         
     | 
| 12 | 
         
            +
                    if isinstance(user_content, list):
         
     | 
| 13 | 
         
            +
                        text_content = ""
         
     | 
| 14 | 
         
            +
                        for item in user_content:
         
     | 
| 15 | 
         
            +
                            if isinstance(item, dict) and item.get("type") == "text":
         
     | 
| 16 | 
         
            +
                                text_content += item.get("text", "")
         
     | 
| 17 | 
         
            +
                        user_content = text_content if text_content else str(user_content)
         
     | 
| 18 | 
         
            +
                    
         
     | 
| 19 | 
         
            +
                    messages.append({'role': 'user', 'content': user_content})
         
     | 
| 20 | 
         
            +
                    messages.append({'role': 'assistant', 'content': h[1]})
         
     | 
| 21 | 
         
            +
                return messages
         
     | 
| 22 | 
         
            +
             
     | 
| 23 | 
         
            +
            def messages_to_history(messages: Messages) -> Tuple[str, History]:
         
     | 
| 24 | 
         
            +
                assert messages[0]['role'] == 'system'
         
     | 
| 25 | 
         
            +
                history = []
         
     | 
| 26 | 
         
            +
                for q, r in zip(messages[1::2], messages[2::2]):
         
     | 
| 27 | 
         
            +
                    user_content = q['content']
         
     | 
| 28 | 
         
            +
                    if isinstance(user_content, list):
         
     | 
| 29 | 
         
            +
                        text_content = ""
         
     | 
| 30 | 
         
            +
                        for item in user_content:
         
     | 
| 31 | 
         
            +
                            if isinstance(item, dict) and item.get("type") == "text":
         
     | 
| 32 | 
         
            +
                                text_content += item.get("text", "")
         
     | 
| 33 | 
         
            +
                        user_content = text_content if text_content else str(user_content)
         
     | 
| 34 | 
         
            +
                    
         
     | 
| 35 | 
         
            +
                    history.append([user_content, r['content']])
         
     | 
| 36 | 
         
            +
                return history
         
     | 
| 37 | 
         
            +
             
     | 
| 38 | 
         
            +
            def history_render(history: History):
         
     | 
| 39 | 
         
            +
                return gr.update(visible=True), history
         
     | 
| 40 | 
         
            +
             
     | 
| 41 | 
         
            +
            def clear_history():
         
     | 
| 42 | 
         
            +
                return [], [], None, ""
         
     | 
| 43 | 
         
            +
             
     | 
| 44 | 
         
            +
            def update_image_input_visibility(model):
         
     | 
| 45 | 
         
            +
                is_ernie_vl = model.get("id") == "baidu/ERNIE-4.5-VL-424B-A47B-Base-PT"
         
     | 
| 46 | 
         
            +
                is_glm_vl = model.get("id") == "THUDM/GLM-4.1V-9B-Thinking"
         
     | 
| 47 | 
         
            +
                return gr.update(visible=is_ernie_vl or is_glm_vl)
         
     | 
| 48 | 
         
            +
             
     | 
| 49 | 
         
            +
            def process_image_for_model(image):
         
     | 
| 50 | 
         
            +
                import io, base64
         
     | 
| 51 | 
         
            +
                import numpy as np
         
     | 
| 52 | 
         
            +
                from PIL import Image
         
     | 
| 53 | 
         
            +
                
         
     | 
| 54 | 
         
            +
                if image is None:
         
     | 
| 55 | 
         
            +
                    return None
         
     | 
| 56 | 
         
            +
                
         
     | 
| 57 | 
         
            +
                if isinstance(image, np.ndarray):
         
     | 
| 58 | 
         
            +
                    image = Image.fromarray(image)
         
     | 
| 59 | 
         
            +
                
         
     | 
| 60 | 
         
            +
                buffer = io.BytesIO()
         
     | 
| 61 | 
         
            +
                image.save(buffer, format='PNG')
         
     | 
| 62 | 
         
            +
                img_str = base64.b64encode(buffer.getvalue()).decode()
         
     | 
| 63 | 
         
            +
                return f"data:image/png;base64,{img_str}"
         
     | 
| 64 | 
         
            +
             
     | 
| 65 | 
         
            +
            def create_multimodal_message(text, image=None):
         
     | 
| 66 | 
         
            +
                if image is None:
         
     | 
| 67 | 
         
            +
                    return {"role": "user", "content": text}
         
     | 
| 68 | 
         
            +
                
         
     | 
| 69 | 
         
            +
                content = [
         
     | 
| 70 | 
         
            +
                    {"type": "text", "text": text},
         
     | 
| 71 | 
         
            +
                    {"type": "image_url", "image_url": {"url": process_image_for_model(image)}}
         
     | 
| 72 | 
         
            +
                ]
         
     | 
| 73 | 
         
            +
                return {"role": "user", "content": content}
         
     | 
| 74 | 
         
            +
             
     | 
| 75 | 
         
            +
            # Code–block parsing
         
     | 
| 76 | 
         
            +
            def remove_code_block(text):
         
     | 
| 77 | 
         
            +
                patterns = [
         
     | 
| 78 | 
         
            +
                    r'```(?:html|HTML)\n([\s\S]+?)\n```',
         
     | 
| 79 | 
         
            +
                    r'```\n([\s\S]+?)\n```',
         
     | 
| 80 | 
         
            +
                    r'```([\s\S]+?)```'
         
     | 
| 81 | 
         
            +
                ]
         
     | 
| 82 | 
         
            +
                for pattern in patterns:
         
     | 
| 83 | 
         
            +
                    match = re.search(pattern, text, re.DOTALL)
         
     | 
| 84 | 
         
            +
                    if match:
         
     | 
| 85 | 
         
            +
                        extracted = match.group(1).strip()
         
     | 
| 86 | 
         
            +
                        if extracted.split('\n', 1)[0].strip().lower() in ['python','html','css','javascript','json','c','cpp','markdown','latex','jinja2','typescript','yaml','dockerfile','shell','r','sql','sql-mssql','sql-mysql','sql-mariadb','sql-sqlite','sql-cassandra','sql-plsql','sql-hive','sql-pgsql','sql-gql','sql-gpsql','sql-sparksql','sql-esper']:
         
     | 
| 87 | 
         
            +
                            return extracted.split('\n',1)[1] if '\n' in extracted else ''
         
     | 
| 88 | 
         
            +
                        return extracted
         
     | 
| 89 | 
         
            +
                if text.strip().startswith('<!DOCTYPE') or text.strip().startswith('<html'):
         
     | 
| 90 | 
         
            +
                    return text.strip()
         
     | 
| 91 | 
         
            +
                if text.strip().startswith('```python'):
         
     | 
| 92 | 
         
            +
                    return text.strip()[9:-3].strip()
         
     | 
| 93 | 
         
            +
                lines = text.strip().split('\n',1)
         
     | 
| 94 | 
         
            +
                if lines[0].strip().lower() in ['python','html','css','javascript','json','c','cpp','markdown','latex','jinja2','typescript','yaml','dockerfile','shell','r','sql','sql-mssql','sql-mysql','sql-mariadb','sql-sqlite','sql-cassandra','sql-plsql','sql-hive','sql-pgsql','sql-gql','sql-gpsql','sql-sparksql','sql-esper']:
         
     | 
| 95 | 
         
            +
                    return lines[1] if len(lines)>1 else ''
         
     | 
| 96 | 
         
            +
                return text.strip()
         
     | 
| 97 | 
         
            +
             
     | 
| 98 | 
         
            +
            def parse_transformers_js_output(text):
         
     | 
| 99 | 
         
            +
                files={'index.html':'','index.js':'','style.css':''}
         
     | 
| 100 | 
         
            +
                html_match=re.search(r'```html\s*\n([\s\S]+?)\n```',text,re.IGNORECASE)
         
     | 
| 101 | 
         
            +
                if html_match: files['index.html']=html_match.group(1).strip()
         
     | 
| 102 | 
         
            +
                js_match=re.search(r'```javascript\s*\n([\s\S]+?)\n```',text,re.IGNORECASE)
         
     | 
| 103 | 
         
            +
                if js_match: files['index.js']=js_match.group(1).strip()
         
     | 
| 104 | 
         
            +
                css_match=re.search(r'```css\s*\n([\s\S]+?)\n```',text,re.IGNORECASE)
         
     | 
| 105 | 
         
            +
                if css_match: files['style.css']=css_match.group(1).strip()
         
     | 
| 106 | 
         
            +
                if not(files['index.html'] and files['index.js'] and files['style.css']):
         
     | 
| 107 | 
         
            +
                    html_fallback=re.search(r'===\s*index\.html\s*===\n([\s\S]+?)(?=\n===|$)',text,re.IGNORECASE)
         
     | 
| 108 | 
         
            +
                    js_fallback=re.search(r'===\s*index\.js\s*===\n([\s\S]+?)(?=\n===|$)',text,re.IGNORECASE)
         
     | 
| 109 | 
         
            +
                    css_fallback=re.search(r'===\s*style\.css\s*===\n([\s\S]+?)(?=\n===|$)',text,re.IGNORECASE)
         
     | 
| 110 | 
         
            +
                    if html_fallback: files['index.html']=html_fallback.group(1).strip()
         
     | 
| 111 | 
         
            +
                    if js_fallback: files['index.js']=js_fallback.group(1).strip()
         
     | 
| 112 | 
         
            +
                    if css_fallback: files['style.css']=css_fallback.group(1).strip()
         
     | 
| 113 | 
         
            +
                return files
         
     | 
| 114 | 
         
            +
             
     | 
| 115 | 
         
            +
            def format_transformers_js_output(files):
         
     | 
| 116 | 
         
            +
                output=[]
         
     | 
| 117 | 
         
            +
                output.append("=== index.html ===")
         
     | 
| 118 | 
         
            +
                output.append(files['index.html'])
         
     | 
| 119 | 
         
            +
                output.append("\n=== index.js ===")
         
     | 
| 120 | 
         
            +
                output.append(files['index.js'])
         
     | 
| 121 | 
         
            +
                output.append("\n=== style.css ===")
         
     | 
| 122 | 
         
            +
                output.append(files['style.css'])
         
     | 
| 123 | 
         
            +
                return '\n'.join(output)
         
     |