Spaces:
Sleeping
Sleeping
| """ | |
| GenAI for Easy Read: Open-Source Multimodal Solution | |
| A Gradio-based prototype for converting documents into accessible Easy Read formats. | |
| """ | |
| import gradio as gr | |
| import os | |
| from typing import List, Tuple, Optional | |
| import re | |
| # ============================================================================ | |
| # DUMMY/MOCK FUNCTIONS FOR BACKEND PROCESSING | |
| # TODO: Replace these with actual API calls to OpenAI, GlobalSymbols, etc. | |
| # ============================================================================ | |
| def split_into_sentences(text: str) -> List[str]: | |
| """ | |
| Splits text into individual sentences. | |
| TODO: Enhance with better sentence boundary detection (e.g., using spaCy) | |
| """ | |
| if not text or not text.strip(): | |
| return [] | |
| # Simple sentence splitting (can be improved with NLP libraries like spaCy) | |
| sentences = re.split(r'(?<=[.!?])\s+', text.strip()) | |
| return [s.strip() for s in sentences if s.strip()] | |
| def convert_to_easy_read( | |
| pdf_file: Optional[str], | |
| text_input: Optional[str], | |
| context: str, | |
| unalterable_terms: str | |
| ) -> Tuple[str, List[str], List[str]]: | |
| """ | |
| Simulates the conversion of complex text to simplified Easy Read format. | |
| TODO: Replace with actual LLM API call (e.g., OpenAI GPT-4, Anthropic Claude) | |
| - Process PDF if provided, or use text_input | |
| - Apply simplification rules | |
| - Respect context/custom instructions | |
| - Preserve unalterable terms | |
| """ | |
| # Use text_input if provided, otherwise dummy text | |
| input_text = text_input if text_input else ( | |
| "This is sentence one. " | |
| "This is sentence two. " | |
| "This is sentence three. " | |
| "Each sentence can be edited separately." | |
| ) | |
| # Split into sentences | |
| sentences = split_into_sentences(input_text) | |
| # Dummy simplified text (sentence-by-sentence) | |
| dummy_simplified = "\n".join(sentences) | |
| # Dummy keyword list for symbol matching | |
| dummy_keywords = ["person", "help", "document", "read"] | |
| return dummy_simplified, dummy_keywords, sentences | |
| def update_sentence(sentence_index: int, new_text: str, all_sentences: List[str]) -> str: | |
| """ | |
| Updates a specific sentence and returns the combined text. | |
| """ | |
| if 0 <= sentence_index < len(all_sentences): | |
| all_sentences[sentence_index] = new_text | |
| return "\n".join(all_sentences) | |
| def fetch_symbols_from_libraries( | |
| keywords: List[str], selected_libraries: List[str] | |
| ) -> List[str]: | |
| """ | |
| Fetches symbols from selected symbol libraries based on keywords. | |
| TODO: Replace with actual API calls to: | |
| - GlobalSymbols API | |
| - ARASAAC API | |
| - AAC Image Library API | |
| - PiCom API | |
| """ | |
| # Dummy: Return placeholder image paths | |
| # In production, these would be URLs or file paths to actual symbols | |
| dummy_symbols = [ | |
| "https://via.placeholder.com/200x200/4A90E2/FFFFFF?text=Symbol1", | |
| "https://via.placeholder.com/200x200/50C878/FFFFFF?text=Symbol2", | |
| "https://via.placeholder.com/200x200/E94B3C/FFFFFF?text=Symbol3", | |
| "https://via.placeholder.com/200x200/F5A623/FFFFFF?text=Symbol4", | |
| ] | |
| return dummy_symbols | |
| def generate_ai_image(prompt: str) -> Optional[str]: | |
| """ | |
| Generates an image using AI based on the provided prompt. | |
| TODO: Replace with actual AI image generation API call: | |
| - OpenAI DALL-E | |
| - Stability AI Stable Diffusion | |
| - Midjourney API | |
| - Other image generation services | |
| """ | |
| # Dummy: Return placeholder image | |
| return "https://via.placeholder.com/400x400/9B59B6/FFFFFF?text=AI+Generated" | |
| def export_to_pdf(text: str, images: List[str]) -> str: | |
| """ | |
| Exports the Easy Read document to PDF format. | |
| TODO: Implement PDF generation using libraries like: | |
| - reportlab | |
| - fpdf | |
| - weasyprint | |
| """ | |
| return "PDF export functionality - TODO: Implement" | |
| def export_to_word(text: str, images: List[str]) -> str: | |
| """ | |
| Exports the Easy Read document to Word format. | |
| TODO: Implement Word document generation using: | |
| - python-docx | |
| """ | |
| return "Word export functionality - TODO: Implement" | |
| def generate_audio(text: str) -> str: | |
| """ | |
| Generates audio narration of the Easy Read text. | |
| TODO: Implement text-to-speech using: | |
| - OpenAI TTS API | |
| - Google Cloud Text-to-Speech | |
| - Amazon Polly | |
| - Azure Cognitive Services | |
| """ | |
| return "Audio generation functionality - TODO: Implement" | |
| # ============================================================================ | |
| # GRADIO INTERFACE COMPONENTS | |
| # ============================================================================ | |
| def create_convert_handler( | |
| pdf_file, text_input, context, unalterable_terms, selected_libraries | |
| ): | |
| """ | |
| Main handler for the "Convert to Easy Read" button. | |
| Processes input and returns simplified text and symbol suggestions. | |
| """ | |
| # Step 1: Convert text to Easy Read format | |
| simplified_text, keywords, sentences = convert_to_easy_read( | |
| pdf_file, text_input, context, unalterable_terms | |
| ) | |
| # Step 2: Fetch symbols from selected libraries | |
| symbols = fetch_symbols_from_libraries(keywords, selected_libraries) | |
| # Step 3: Create sentence components for display | |
| sentence_components = [] | |
| for i, sentence in enumerate(sentences): | |
| sentence_components.append((sentence, i)) | |
| return simplified_text, symbols, sentences | |
| def create_ai_image_handler(prompt: str): | |
| """Handler for AI image generation tab.""" | |
| if not prompt.strip(): | |
| return None | |
| return generate_ai_image(prompt) | |
| # ============================================================================ | |
| # MAIN GRADIO INTERFACE | |
| # ============================================================================ | |
| def create_interface(): | |
| """Creates and configures the main Gradio interface.""" | |
| with gr.Blocks(theme=gr.themes.Soft()) as app: | |
| # Store sentences in state | |
| sentences_state = gr.State([]) | |
| # ==================================================================== | |
| # HEADER SECTION | |
| # ==================================================================== | |
| gr.Markdown( | |
| """ | |
| # GenAI for Easy Read: Open-Source Multimodal Solution | |
| Convert documents into accessible Easy Read formats with simplified text and supported symbols. | |
| """, | |
| elem_classes=["header"] | |
| ) | |
| # ==================================================================== | |
| # STEP 1: INPUT SECTION | |
| # ==================================================================== | |
| gr.Markdown("## Step 1: Input Document or Text") | |
| with gr.Row(): | |
| # Left Column: File Upload | |
| with gr.Column(): | |
| pdf_upload = gr.File( | |
| label="Upload Document", | |
| file_types=[".pdf"], | |
| type="filepath" | |
| ) | |
| # Right Column: Text Input | |
| with gr.Column(): | |
| text_input = gr.Textbox( | |
| label="Or Paste Text Here", | |
| lines=10, | |
| placeholder="Enter your text here..." | |
| ) | |
| # Advanced Settings Accordion | |
| with gr.Accordion("Advanced Settings", open=False): | |
| context_input = gr.Textbox( | |
| label="Context/Custom Instructions", | |
| placeholder="e.g., 'never use term X', 'target audience: children'", | |
| lines=3 | |
| ) | |
| unalterable_terms_input = gr.Textbox( | |
| label="Unalterable Terms", | |
| placeholder="Enter terms that must not be changed (comma-separated)", | |
| lines=2 | |
| ) | |
| # ==================================================================== | |
| # STEP 2: SYMBOL CONFIGURATION | |
| # ==================================================================== | |
| gr.Markdown("## Step 2: Select Symbol Libraries") | |
| symbol_libraries = gr.CheckboxGroup( | |
| choices=[ | |
| "AAC Image Library", | |
| "GlobalSymbols", | |
| "ARASAAC", | |
| "PiCom", | |
| "AI Realistic Symbols" | |
| ], | |
| label="Available Symbol Libraries", | |
| value=["ARASAAC", "GlobalSymbols"] # Default selections | |
| ) | |
| # ==================================================================== | |
| # STEP 3: EASY READ EDITOR | |
| # ==================================================================== | |
| gr.Markdown("## Step 3: Easy Read Editor") | |
| # Convert Button | |
| convert_btn = gr.Button( | |
| "Convert to Easy Read", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| # Main Editor Layout: Text Editing + Image Selection | |
| with gr.Row(): | |
| # Left Side: Full Text Editor | |
| with gr.Column(scale=1): | |
| simplified_text = gr.Textbox( | |
| label="Full Simplified Text (Editable)", | |
| lines=15, | |
| placeholder="Simplified text will appear here after conversion..." | |
| ) | |
| # Right Side: Multi-Tab Image Interface | |
| with gr.Column(scale=1): | |
| with gr.Tabs() as image_tabs: | |
| # Tab 1: Symbol Library / Alternatives | |
| with gr.Tab("Symbol Library / Alternatives"): | |
| symbol_gallery = gr.Gallery( | |
| label="Available Symbols", | |
| show_label=True, | |
| elem_id="symbol_gallery", | |
| columns=3, | |
| rows=2, | |
| height="auto" | |
| ) | |
| # Tab 2: Generate with AI | |
| with gr.Tab("Generate with AI"): | |
| ai_prompt = gr.Textbox( | |
| label="Image Prompt", | |
| placeholder="Describe the image you want to generate...", | |
| lines=3 | |
| ) | |
| generate_ai_btn = gr.Button("Generate", variant="secondary") | |
| ai_generated_image = gr.Image( | |
| label="Generated Image", | |
| type="filepath" | |
| ) | |
| # Tab 3: Upload / Personal | |
| with gr.Tab("Upload / Personal"): | |
| personal_image_upload = gr.Image( | |
| label="Upload Your Image", | |
| sources=["upload"], | |
| type="filepath" | |
| ) | |
| # Tab 4: Favorites | |
| with gr.Tab("Favorites"): | |
| favorites_gallery = gr.Gallery( | |
| label="Saved Favorites", | |
| show_label=True, | |
| elem_id="favorites_gallery", | |
| columns=3, | |
| rows=2, | |
| height="auto" | |
| ) | |
| # ==================================================================== | |
| # INDIVIDUAL SENTENCE EDITOR | |
| # ==================================================================== | |
| gr.Markdown("## Individual Sentence Editor") | |
| gr.Markdown("*Edit each sentence separately below*") | |
| # Container for individual sentences | |
| sentence_editor_container = gr.Column() | |
| with sentence_editor_container: | |
| # Dynamic sentence textboxes will be created here | |
| sentence_textboxes = [] | |
| for i in range(10): # Pre-create 10 textboxes (will show/hide as needed) | |
| with gr.Row(visible=False) as sentence_row: | |
| sentence_num = gr.Markdown(f"**Sentence {i+1}:**") | |
| sentence_box = gr.Textbox( | |
| label="", | |
| lines=2, | |
| show_label=False, | |
| scale=4 | |
| ) | |
| sentence_textboxes.append((sentence_row, sentence_box, sentence_num)) | |
| # ==================================================================== | |
| # FOOTER / EXPORT SECTION | |
| # ==================================================================== | |
| gr.Markdown("## Export Options") | |
| with gr.Row(): | |
| export_pdf_btn = gr.Button("Export to PDF", variant="secondary") | |
| export_word_btn = gr.Button("Export to Word", variant="secondary") | |
| generate_audio_btn = gr.Button("Generate Audio", variant="secondary") | |
| # ==================================================================== | |
| # EVENT HANDLERS | |
| # ==================================================================== | |
| def update_sentence_display(sentences): | |
| """Updates the visibility and content of sentence textboxes""" | |
| updates = [] | |
| for i, (row, box, num) in enumerate(sentence_textboxes): | |
| if i < len(sentences): | |
| # Show this sentence | |
| updates.extend([ | |
| gr.update(visible=True), # row | |
| gr.update(value=sentences[i]), # textbox | |
| gr.update(value=f"**Sentence {i+1}:**") # label | |
| ]) | |
| else: | |
| # Hide this sentence | |
| updates.extend([ | |
| gr.update(visible=False), # row | |
| gr.update(value=""), # textbox | |
| gr.update(value="") # label | |
| ]) | |
| return updates | |
| def merge_sentences_to_full_text(*sentence_values): | |
| """Merges individual sentence values back into full text""" | |
| # Filter out empty sentences | |
| sentences = [s for s in sentence_values if s and s.strip()] | |
| return "\n".join(sentences) | |
| # Convert button handler | |
| def convert_handler(*args): | |
| simplified_text, symbols, sentences = create_convert_handler(*args) | |
| # Update sentence display | |
| sentence_updates = update_sentence_display(sentences) | |
| return [simplified_text, symbols, sentences] + sentence_updates | |
| # Collect all outputs for convert button | |
| convert_outputs = [simplified_text, symbol_gallery, sentences_state] | |
| for row, box, num in sentence_textboxes: | |
| convert_outputs.extend([row, box, num]) | |
| convert_btn.click( | |
| fn=convert_handler, | |
| inputs=[ | |
| pdf_upload, | |
| text_input, | |
| context_input, | |
| unalterable_terms_input, | |
| symbol_libraries | |
| ], | |
| outputs=convert_outputs | |
| ) | |
| # Update full text when any sentence is edited | |
| for row, box, num in sentence_textboxes: | |
| box.change( | |
| fn=merge_sentences_to_full_text, | |
| inputs=[b for r, b, n in sentence_textboxes], | |
| outputs=[simplified_text] | |
| ) | |
| # AI image generation handler | |
| generate_ai_btn.click( | |
| fn=create_ai_image_handler, | |
| inputs=[ai_prompt], | |
| outputs=[ai_generated_image] | |
| ) | |
| # Export handlers (placeholder) | |
| export_pdf_btn.click( | |
| fn=lambda t, imgs: gr.Info("PDF export - TODO: Implement backend"), | |
| inputs=[simplified_text, symbol_gallery], | |
| outputs=[] | |
| ) | |
| export_word_btn.click( | |
| fn=lambda t, imgs: gr.Info("Word export - TODO: Implement backend"), | |
| inputs=[simplified_text, symbol_gallery], | |
| outputs=[] | |
| ) | |
| generate_audio_btn.click( | |
| fn=lambda t: gr.Info("Audio generation - TODO: Implement backend"), | |
| inputs=[simplified_text], | |
| outputs=[] | |
| ) | |
| return app | |
| def main(): | |
| """Main entry point for the application.""" | |
| app = create_interface() | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False | |
| ) | |
| if __name__ == "__main__": | |
| main() |