omgy commited on
Commit
b059fff
Β·
verified Β·
1 Parent(s): e9cad79

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -81
app.py CHANGED
@@ -1484,9 +1484,25 @@ async def enhance_document(
1484
  prompt: str = Form(...),
1485
  output_format: str = Form("docx"),
1486
  ):
1487
- logger.info("Starting enhancement for file: %s", file.filename)
 
 
 
 
 
 
 
 
 
 
 
 
1488
  start_time = asyncio.get_event_loop().time()
1489
 
 
 
 
 
1490
  raw = _read_bytes(file)
1491
  if not raw:
1492
  raise HTTPException(400, detail="Empty file")
@@ -1494,113 +1510,131 @@ async def enhance_document(
1494
  file_kind = _detect_file_kind(file.filename, file.content_type)
1495
  suffix = os.path.splitext(file.filename or "upload.bin")[1] or ".bin"
1496
  temp_path = _save_to_temp(raw, suffix=suffix)
1497
- logger.info(f"File saved: {file_kind}, size: {len(raw)} bytes")
1498
 
1499
  try:
1500
- # Step 1: Extract text (fast)
1501
- plain_text, _ = extract_text_and_layout(temp_path, file_kind)
 
 
 
1502
  if not plain_text.strip():
1503
  raise HTTPException(422, detail="Could not extract text from the document")
1504
 
1505
- logger.info(f"Extracted text: {len(plain_text)} chars")
 
 
 
 
 
 
1506
 
1507
- # Remove headers/footers using fast pattern detection
1508
- logger.info("=" * 60)
1509
- logger.info("🧹 HEADER/FOOTER REMOVAL STARTING...")
1510
- logger.info("=" * 60)
1511
  original_len = len(plain_text)
1512
  plain_text = _remove_headers_footers_fast(plain_text)
1513
  removed_pct = (
1514
  100 * (1 - len(plain_text) / original_len) if original_len > 0 else 0
1515
  )
1516
- logger.info("=" * 60)
1517
  logger.info(
1518
- f"βœ“ CLEANING COMPLETE: {len(plain_text)} chars (removed {removed_pct:.1f}%)"
1519
  )
1520
- logger.info("=" * 60)
1521
 
1522
- # For very large documents, use fast mode (skip AI enhancement, use smart layout)
1523
- if len(plain_text) > 25000:
1524
- logger.info(
1525
- f"Very large document ({len(plain_text)} chars) - using FAST MODE"
1526
- )
 
 
 
 
 
 
 
 
 
 
1527
  logger.info(
1528
- "Skipping AI enhancement to avoid timeout, creating smart layout..."
1529
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1530
 
1531
- # Create professional layout without AI
1532
- design = _create_smart_layout(plain_text)
1533
- content_text = plain_text
1534
  else:
1535
- # Step 2: Edit text with timeout and chunking for medium docs
1536
- logger.info("Starting text enhancement...")
 
 
1537
 
1538
- # Validate and enhance the user prompt
1539
- if not prompt or prompt.strip().lower() in ("", "enhance", "improve"):
1540
- logger.info(
1541
- "No specific enhancement instruction provided, using default enhancement"
1542
- )
1543
- user_enhancement_prompt = "Improve this document by enhancing clarity, fixing grammar, improving flow, and using professional language while preserving all content."
1544
- else:
1545
- user_enhancement_prompt = prompt
1546
- logger.info(
1547
- f"Using user enhancement instruction: {user_enhancement_prompt[:100]}..."
1548
- )
1549
 
1550
- if len(plain_text) > 10000:
1551
- logger.info("Medium-large document detected, using chunking strategy")
1552
- chunks = _chunk_text(plain_text, max_chars=10000)
1553
- logger.info(f"Split into {len(chunks)} chunks")
1554
- enhanced_chunks = []
1555
- for i, chunk in enumerate(chunks):
1556
- logger.info(f"Processing chunk {i + 1}/{len(chunks)}")
1557
- try:
1558
- enhanced = await gemini_edit_text(
1559
- prompt=user_enhancement_prompt, text=chunk, timeout=120
1560
- )
1561
- enhanced_chunks.append(enhanced)
1562
- except Exception as e:
1563
- logger.warning(
1564
- f"Chunk {i + 1} failed: {str(e)}, using original"
1565
- )
1566
- enhanced_chunks.append(chunk)
1567
- content_text = "\n\n".join(enhanced_chunks)
1568
- else:
1569
- content_text = await gemini_edit_text(
1570
- prompt=user_enhancement_prompt, text=plain_text, timeout=120
1571
- )
1572
 
1573
- # Verify enhancement actually happened
1574
- if len(content_text.strip()) == 0:
1575
- logger.warning("Enhancement resulted in empty text, using original")
1576
- content_text = plain_text
1577
 
1578
- logger.info(
1579
- f"Enhanced text: {len(content_text)} chars (original: {len(plain_text)} chars)"
1580
- )
 
 
 
 
1581
 
1582
- # Step 3: Generate design with timeout
1583
- logger.info("Starting design generation...")
1584
- instruction = _house_style_instruction_v2()
1585
- # Use reasonable amount for design (up to 15K chars)
1586
- design_text = (
1587
- content_text[:15000] + "\n\n[Content continues...]"
1588
- if len(content_text) > 15000
1589
- else content_text
1590
- )
1591
- logger.info(f"Design generation using {len(design_text)} chars")
1592
  design = await gemini_generate_design(
1593
- user_instruction=instruction, source_text=design_text
 
 
 
 
 
 
 
 
 
1594
  )
1595
 
1596
- # Step 4: Build DOCX (fast)
1597
- logger.info("Building DOCX...")
 
 
1598
  out_bytes = build_docx_from_design(design)
 
1599
 
 
1600
  elapsed = asyncio.get_event_loop().time() - start_time
1601
- logger.info(
1602
- f"βœ“ SUCCESS - Total time: {elapsed:.2f}s, DOCX size: {len(out_bytes)} bytes"
1603
- )
 
 
1604
 
1605
  out_name_base = (
1606
  os.path.splitext(os.path.basename(file.filename or "document"))[0]
@@ -1613,18 +1647,22 @@ async def enhance_document(
1613
  media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1614
  headers={"Content-Disposition": f'attachment; filename="{out_name}"'},
1615
  )
 
1616
  except asyncio.TimeoutError:
1617
- logger.error("βœ— Request timed out after waiting too long")
1618
  raise HTTPException(
1619
  504,
1620
  detail="Request timed out. Document may be too large or complex. Try splitting it into smaller parts.",
1621
  )
 
 
1622
  except Exception as e:
1623
- logger.error(f"βœ— Enhancement failed: {str(e)}")
1624
  raise HTTPException(500, detail=f"Processing failed: {str(e)}")
1625
  finally:
1626
  try:
1627
  os.remove(temp_path)
 
1628
  except Exception:
1629
  pass
1630
 
 
1484
  prompt: str = Form(...),
1485
  output_format: str = Form("docx"),
1486
  ):
1487
+ """
1488
+ Enhanced document processing workflow:
1489
+ 1. OCR Extract - Extract text and layout from uploaded file
1490
+ 2. Cleanup - Remove headers/footers and artifacts
1491
+ 3. Gemini Enhancement - Enhance text based on user prompt
1492
+ 4. Gemini Layout - Generate best layout for enhanced text
1493
+ 5. DOCX Build - Create final formatted document
1494
+ """
1495
+ logger.info("=" * 70)
1496
+ logger.info("πŸ“„ STARTING DOCUMENT ENHANCEMENT WORKFLOW")
1497
+ logger.info("=" * 70)
1498
+ logger.info(f"File: {file.filename} | Prompt: {prompt[:50]}...")
1499
+
1500
  start_time = asyncio.get_event_loop().time()
1501
 
1502
+ # ============ STEP 1: FILE VALIDATION & EXTRACTION ============
1503
+ logger.info("\n[STEP 1/5] πŸ“‚ FILE EXTRACTION")
1504
+ logger.info("-" * 70)
1505
+
1506
  raw = _read_bytes(file)
1507
  if not raw:
1508
  raise HTTPException(400, detail="Empty file")
 
1510
  file_kind = _detect_file_kind(file.filename, file.content_type)
1511
  suffix = os.path.splitext(file.filename or "upload.bin")[1] or ".bin"
1512
  temp_path = _save_to_temp(raw, suffix=suffix)
1513
+ logger.info(f"βœ“ File saved: {file_kind} | Size: {len(raw):,} bytes")
1514
 
1515
  try:
1516
+ # ============ STEP 2: OCR & TEXT EXTRACTION ============
1517
+ logger.info("\n[STEP 2/5] πŸ” OCR TEXT EXTRACTION")
1518
+ logger.info("-" * 70)
1519
+
1520
+ plain_text, layout_info = extract_text_and_layout(temp_path, file_kind)
1521
  if not plain_text.strip():
1522
  raise HTTPException(422, detail="Could not extract text from the document")
1523
 
1524
+ logger.info(f"βœ“ Text extracted: {len(plain_text):,} characters")
1525
+ if layout_info:
1526
+ logger.info(f"βœ“ Layout info extracted: {len(layout_info):,} bytes")
1527
+
1528
+ # ============ STEP 3: CLEANUP & PREPROCESSING ============
1529
+ logger.info("\n[STEP 3/5] 🧹 CLEANUP & PREPROCESSING")
1530
+ logger.info("-" * 70)
1531
 
 
 
 
 
1532
  original_len = len(plain_text)
1533
  plain_text = _remove_headers_footers_fast(plain_text)
1534
  removed_pct = (
1535
  100 * (1 - len(plain_text) / original_len) if original_len > 0 else 0
1536
  )
 
1537
  logger.info(
1538
+ f"βœ“ Cleanup complete: {len(plain_text):,} characters (removed {removed_pct:.1f}%)"
1539
  )
 
1540
 
1541
+ # ============ STEP 4: GEMINI TEXT ENHANCEMENT ============
1542
+ logger.info("\n[STEP 4/5] ✨ GEMINI TEXT ENHANCEMENT")
1543
+ logger.info("-" * 70)
1544
+ logger.info(f"User prompt: {prompt}")
1545
+
1546
+ # Validate user prompt
1547
+ if not prompt or prompt.strip().lower() in ("", "enhance", "improve"):
1548
+ logger.info("⚠ No specific prompt provided, using default enhancement")
1549
+ enhancement_prompt = "Improve this document by enhancing clarity, fixing grammar, improving flow, and using professional language while preserving all content."
1550
+ else:
1551
+ enhancement_prompt = prompt
1552
+ logger.info("βœ“ Using custom enhancement prompt")
1553
+
1554
+ # Process text enhancement (with chunking for large docs)
1555
+ if len(plain_text) > 10000:
1556
  logger.info(
1557
+ f"⚠ Large document ({len(plain_text):,} chars) - using chunked enhancement"
1558
  )
1559
+ chunks = _chunk_text(plain_text, max_chars=10000)
1560
+ logger.info(f" Split into {len(chunks)} chunks")
1561
+
1562
+ enhanced_chunks = []
1563
+ for i, chunk in enumerate(chunks):
1564
+ logger.info(f" Processing chunk {i + 1}/{len(chunks)}...")
1565
+ try:
1566
+ enhanced = await gemini_edit_text(
1567
+ prompt=enhancement_prompt, text=chunk, timeout=120
1568
+ )
1569
+ enhanced_chunks.append(enhanced)
1570
+ logger.info(
1571
+ f" βœ“ Chunk {i + 1} enhanced: {len(enhanced):,} chars"
1572
+ )
1573
+ except Exception as e:
1574
+ logger.warning(
1575
+ f" βœ— Chunk {i + 1} failed: {str(e)[:100]} - using original"
1576
+ )
1577
+ enhanced_chunks.append(chunk)
1578
 
1579
+ enhanced_text = "\n\n".join(enhanced_chunks)
 
 
1580
  else:
1581
+ logger.info(f"Processing {len(plain_text):,} characters...")
1582
+ enhanced_text = await gemini_edit_text(
1583
+ prompt=enhancement_prompt, text=plain_text, timeout=120
1584
+ )
1585
 
1586
+ # Verify enhancement
1587
+ if len(enhanced_text.strip()) == 0:
1588
+ logger.warning("⚠ Enhancement resulted in empty text - using original")
1589
+ enhanced_text = plain_text
 
 
 
 
 
 
 
1590
 
1591
+ enhancement_ratio = (
1592
+ len(enhanced_text) / len(plain_text) if len(plain_text) > 0 else 1.0
1593
+ )
1594
+ logger.info(
1595
+ f"βœ“ Text enhanced: {len(enhanced_text):,} characters (ratio: {enhancement_ratio:.2f}x)"
1596
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1597
 
1598
+ # ============ STEP 5: GEMINI LAYOUT GENERATION ============
1599
+ logger.info("\n[STEP 5/5] 🎨 GEMINI LAYOUT GENERATION")
1600
+ logger.info("-" * 70)
 
1601
 
1602
+ # Use reasonable amount for design (up to 20K chars for better layouts)
1603
+ design_input_text = (
1604
+ enhanced_text[:20000] + "\n\n[Content continues...]"
1605
+ if len(enhanced_text) > 20000
1606
+ else enhanced_text
1607
+ )
1608
+ logger.info(f"Generating layout for {len(design_input_text):,} characters...")
1609
 
1610
+ try:
 
 
 
 
 
 
 
 
 
1611
  design = await gemini_generate_design(
1612
+ user_instruction="", source_text=design_input_text
1613
+ )
1614
+ logger.info(f"βœ“ Layout generated: {len(design.get('document', []))} blocks")
1615
+ except Exception as e:
1616
+ logger.warning(
1617
+ f"⚠ Layout generation failed: {str(e)[:100]} - using smart layout"
1618
+ )
1619
+ design = _create_smart_layout(enhanced_text)
1620
+ logger.info(
1621
+ f"βœ“ Smart layout applied: {len(design.get('document', []))} blocks"
1622
  )
1623
 
1624
+ # ============ STEP 6: DOCX BUILDING ============
1625
+ logger.info("\n[STEP 6/5] πŸ“ BUILDING DOCX OUTPUT")
1626
+ logger.info("-" * 70)
1627
+
1628
  out_bytes = build_docx_from_design(design)
1629
+ logger.info(f"βœ“ DOCX built: {len(out_bytes):,} bytes")
1630
 
1631
+ # ============ COMPLETION ============
1632
  elapsed = asyncio.get_event_loop().time() - start_time
1633
+ logger.info("\n" + "=" * 70)
1634
+ logger.info(f"βœ… SUCCESS - Total time: {elapsed:.2f}s")
1635
+ logger.info(f" Input: {len(raw):,} bytes β†’ Output: {len(out_bytes):,} bytes")
1636
+ logger.info(f" Text: {len(plain_text):,} β†’ {len(enhanced_text):,} chars")
1637
+ logger.info("=" * 70 + "\n")
1638
 
1639
  out_name_base = (
1640
  os.path.splitext(os.path.basename(file.filename or "document"))[0]
 
1647
  media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1648
  headers={"Content-Disposition": f'attachment; filename="{out_name}"'},
1649
  )
1650
+
1651
  except asyncio.TimeoutError:
1652
+ logger.error("❌ TIMEOUT - Request took too long")
1653
  raise HTTPException(
1654
  504,
1655
  detail="Request timed out. Document may be too large or complex. Try splitting it into smaller parts.",
1656
  )
1657
+ except HTTPException:
1658
+ raise
1659
  except Exception as e:
1660
+ logger.error(f"❌ FAILED - {str(e)}")
1661
  raise HTTPException(500, detail=f"Processing failed: {str(e)}")
1662
  finally:
1663
  try:
1664
  os.remove(temp_path)
1665
+ logger.info("βœ“ Temporary file cleaned up")
1666
  except Exception:
1667
  pass
1668