Skip to content

fix: Resolve asyncio event loop error and improve translate UI performance#222

Merged
NotYuSheng merged 3 commits into
devfrom
fix/translate-ui-asyncio-and-performance
Oct 3, 2025
Merged

fix: Resolve asyncio event loop error and improve translate UI performance#222
NotYuSheng merged 3 commits into
devfrom
fix/translate-ui-asyncio-and-performance

Conversation

@NotYuSheng

@NotYuSheng NotYuSheng commented Oct 3, 2025

Copy link
Copy Markdown
Owner

User description

Summary

Fixed asyncio event loop error that caused the second file to fail on the translate page, and improved performance by removing the PDF preview feature.


Changes Made

  • Fixed asyncio event loop binding issue where httpx.AsyncClient was shared across multiple asyncio.Runner instances
  • Each document now creates its own client within the runner's event loop context
  • Removed PDF preview iframe to eliminate lag caused by base64 encoding and rendering
  • Kept download button functionality with proper authentication

Context / Rationale

Bug Fix: When processing multiple files on the translate page, the second file would fail with the error: "Event object is bound to a different event loop". This occurred because a single httpx.AsyncClient created at module level was being used across multiple asyncio.Runner() instances, each with their own event loop.

Performance Improvement: The translate page was experiencing significant lag due to automatically fetching and base64-encoding PDFs for preview in iframes. By removing the preview feature and keeping only the download button, the page now loads much faster while maintaining full functionality.


Related Docs or References

  • asyncio event loop documentation
  • Streamlit performance best practices

General Checklist

  • I have tested these changes locally
  • I have updated relevant documentation or added comments where needed
  • I have linked relevant issues and tagged reviewers
  • I have followed coding conventions and naming standards

PR Type

Bug fix, Enhancement


Description

  • Fix asyncio event loop error for multi-file processing

  • Remove PDF preview feature to improve performance

  • Create separate HTTP client per document processing

  • Keep download functionality with proper authentication


Diagram Walkthrough

flowchart LR
  A["Multiple Documents"] --> B["Individual AsyncIO Runner"]
  B --> C["Dedicated HTTP Client"]
  C --> D["Translation Processing"]
  D --> E["Download Button Only"]
  F["PDF Preview Removed"] --> G["Improved Performance"]
Loading

File Walkthrough

Relevant files
Bug fix
6_translate_UI.py
Fix asyncio client and remove PDF preview                               

frontend/my_pages/6_translate_UI.py

  • Remove global httpx.AsyncClient and create per-document clients
  • Add client parameter to async functions for proper event loop binding
  • Remove PDF preview iframe and base64 encoding functionality
  • Keep download button with server-side PDF fetching
+14/-28 

NotYuSheng and others added 2 commits October 4, 2025 01:13
Fixed event loop binding issue where httpx.AsyncClient was shared
across multiple asyncio.Runner instances. Each document now creates
its own client within the runner's event loop context.

This resolves the error: "Event object is bound to a different event
loop" that occurred when processing the second file.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Removed the PDF preview (iframe) to improve page performance, while
keeping the download button functionality. The PDF is still fetched
server-side with session cookies to avoid authorization issues, but
the base64 encoding and iframe rendering are removed.

Changes:
- Removed base64 import
- Removed PDF preview expander with iframe
- Kept download button with server-side fetch for authentication

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @NotYuSheng, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug affecting multi-file processing on the translation page and delivers a notable performance boost to the user interface. The changes ensure robust handling of asynchronous operations and streamline the user experience by removing a resource-intensive feature that was causing slowdowns.

Highlights

  • Bug Fix: Asyncio Event Loop Error: Resolved an issue where processing multiple files on the translate page would fail with an 'Event object is bound to a different event loop' error. This was fixed by ensuring each document's processing creates its own httpx.AsyncClient within the asyncio.Runner's event loop context, preventing shared client conflicts.
  • Performance Improvement: Translate UI: Significantly improved the performance of the translate UI by removing the PDF preview feature. The previous implementation, which involved base64 encoding and rendering PDFs in iframes, caused substantial lag. The download functionality for translated PDFs remains intact.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codiumai-pr-agent-free

codiumai-pr-agent-free Bot commented Oct 3, 2025

Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively resolves the asyncio event loop error by instantiating a new httpx.AsyncClient for each document, which is a good fix. The removal of the PDF preview for performance is also a sensible improvement. My review includes a critical suggestion to prevent resource leaks by properly closing the httpx.AsyncClient and a minor improvement for user-facing error messages.

Comment thread frontend/my_pages/6_translate_UI.py Outdated
@codiumai-pr-agent-free

codiumai-pr-agent-free Bot commented Oct 3, 2025

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix resource leaks and race conditions

To prevent resource leaks and race conditions, refactor the main loop to use a
single asyncio.Runner and manage the httpx.AsyncClient lifecycle with an async
with block inside a dedicated async function.

frontend/my_pages/6_translate_UI.py [143-180]

 # Main content
 if "processed_data" in st.session_state and st.session_state.processed_data:
     response_lst = list(st.session_state.processed_data.items())
 
     if not response_lst:
         st.info("No documents have been processed yet. Upload a PDF to get started!")
     else:
+        runner = asyncio.Runner()
         for doc_id, data in response_lst:
             with st.expander(f"📄 {data['uploaded_filename']}", expanded=True):
-                runner = asyncio.Runner()
-                client = httpx.AsyncClient(cookies=st.session_state.httpx_cookies, timeout=300.0)
-                translation_data = runner.run(get_translation_status(doc_id, client))
+
+                async def process_document():
+                    async with httpx.AsyncClient(cookies=st.session_state.httpx_cookies, timeout=300.0) as client:
+                        translation_data = await get_translation_status(doc_id, client)
+                        renderer_data = None
+                        if translation_data.get("status") == "completed":
+                            renderer_data = await get_renderer_status(doc_id, client)
+                        return translation_data, renderer_data
+
+                translation_data, renderer_data = runner.run(process_document())
 
                 status = translation_data.get("status")
 
                 if status == "not_found":
                     st.warning("⚠️ No translation available for this document")
                     st.info("Translation was not enabled when this document was uploaded, or it's still processing.")
 
                 elif status == "error":
                     st.error(f"❌ Error: {translation_data.get('error', 'Unknown error')}")
 
                 elif status == "processing":
                     st.info("⏳ Translation is currently processing...")
 
                 elif status == "completed":
                     display_translation_info(translation_data)
 
                     # Check and display renderer section
-                    renderer_data = runner.run(get_renderer_status(doc_id, client))
-                    display_renderer_section(doc_id, data['uploaded_filename'], renderer_data, runner, client)
+                    # Create a new client for synchronous calls inside display_renderer_section
+                    with httpx.Client(cookies=st.session_state.httpx_cookies, timeout=300.0) as sync_client:
+                        display_renderer_section(doc_id, data['uploaded_filename'], renderer_data, runner, sync_client)
 
                 elif status == "failed":
                     st.error("❌ Translation failed")
                     st.info("Please try uploading the document again.")
 
                 else:
                     st.warning(f"Unknown status: {status}")

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical resource leak and unsafe asyncio usage where an httpx.AsyncClient is not closed and is used across multiple event loops, which could lead to instability.

High
General
Use synchronous client for synchronous operations

Refactor display_rendered_pdf to be fully synchronous by using a synchronous
httpx.Client instead of an httpx.AsyncClient with asyncio.Runner, simplifying
the function and improving efficiency.

frontend/my_pages/6_translate_UI.py [82-102]

-def display_rendered_pdf(doc_id: str, filename: str, runner: asyncio.Runner, client: httpx.AsyncClient):
+def display_rendered_pdf(doc_id: str, filename: str, client: httpx.Client):
     """Display download button for rendered PDF."""
     # Server-side URL for fetching PDF (internal service communication)
     server_pdf_url = f"{PDF_PROCESSOR_URL}/renderer/{doc_id}/rendered.pdf"
 
     try:
-        pdf_response = runner.run(client.get(server_pdf_url))
-        if pdf_response.status_code == 200:
-            st.download_button(
-                label="📥 Download Translated PDF",
-                data=pdf_response.content,
-                file_name=f"{filename.rsplit('.', 1)[0]}_translated.pdf",
-                mime="application/pdf",
-                key=f"download_{doc_id}",
-                use_container_width=True
-            )
-        else:
-            st.error(f"Failed to load PDF (HTTP {pdf_response.status_code})")
+        pdf_response = client.get(server_pdf_url)
+        pdf_response.raise_for_status()  # Raise an exception for bad status codes
+
+        st.download_button(
+            label="📥 Download Translated PDF",
+            data=pdf_response.content,
+            file_name=f"{filename.rsplit('.', 1)[0]}_translated.pdf",
+            mime="application/pdf",
+            key=f"download_{doc_id}",
+            use_container_width=True
+        )
+    except httpx.HTTPStatusError as e:
+        st.error(f"Failed to load PDF (HTTP {e.response.status_code})")
+        logger.error(f"Error fetching rendered PDF: {e}")
     except Exception as e:
         logger.error(f"Error fetching rendered PDF: {e}")
         st.error(f"Error loading PDF: {e}")
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that using runner.run for a single async call is inefficient and proposes a cleaner, fully synchronous implementation which improves code clarity and maintainability.

Medium
  • Update

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@NotYuSheng NotYuSheng merged commit 999a381 into dev Oct 3, 2025
3 checks passed
@NotYuSheng NotYuSheng deleted the fix/translate-ui-asyncio-and-performance branch October 3, 2025 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant