Skip to content

UI Cleanup: Visible Keys and Objective Removal - #25

Open
Marcus-Mok-GH wants to merge 2 commits into
mainfrom
remove-debug-logs-and-restructure-ui-5080661316084715570
Open

UI Cleanup: Visible Keys and Objective Removal#25
Marcus-Mok-GH wants to merge 2 commits into
mainfrom
remove-debug-logs-and-restructure-ui-5080661316084715570

Conversation

@Marcus-Mok-GH

@Marcus-Mok-GH Marcus-Mok-GH commented May 12, 2026

Copy link
Copy Markdown
Owner

This update completes the UI cleanup by making stored API keys visible in the configuration panel, removing the redundant 'Objective' header, and ensuring all debug logs and intermediate step messages are gone. It also includes the previously implemented sidebar restructuring into a tabbed interface.

Summary by CodeRabbit

  • New Features

    • Reorganized UI into a sidebar "Control Center" with separate tabs for chat management and configuration.
  • Bug Fixes

    • Improved error handling by silently managing failures in screenshot deletion, data persistence, and chat operations for a cleaner user experience.
  • Refactor

    • Removed debug output from automation workflow and streamlined messaging throughout the application.
  • Tests

    • Added comprehensive test suites for core functionality validation.

Review Change Stack

…moval

- Removed `type="password"` from API key inputs to show stored values.
- Removed the 'Objective' header from the main chat interface.
- Verified all debug logs and redundant UI messages are removed.
- Finalized sidebar restructuring with tabbed Chats/Config.
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR systematically suppresses error output across the application while reorganizing the Streamlit UI into a sidebar with container-based chat and configuration panels. Session state no longer tracks minimization, intermediate automation messages are removed, and two unused utility functions are deleted. Four new comprehensive test suites validate all implementation changes.

Changes

Error Suppression & Sidebar UI Refactor

Layer / File(s) Summary
Sidebar layout & session state cleanup
app.py
Session state no longer initializes config_minimized; main app layout reorganized to render chat and configuration in a sidebar with two tabs instead of main columns.
Chat menu container refactor & operations
app.py
setup_chat_menu() refactored to accept a container parameter, uses container-based rendering and dividers, triggers st.rerun() after chat creation and deletion.
Configuration panel UI updates
app.py
Configuration panel moved to sidebar container; main-column configuration UI removed; Mistral and Firecrawl API key inputs lose explicit type="password" specifications.
Chat display and automation messages cleanup
app.py
"Objective:" and "Step N" status lines removed from chat UI; raw screenshot logging removed; error messages simplified without traceback.format_exc().
Error suppression in app.py
app.py
Screenshot deletion, local storage persistence, and chat loading now silently catch exceptions instead of printing diagnostic output.
Error suppression in browser automation
browser_automation.py
Session startup, screenshot format detection, element enumeration, and session closure no longer print errors or status messages on failure.
Error suppression in element detection
element_detector.py
Element detection, annotation, positioning, and screenshot creation methods silently fall back on exceptions without capturing or printing error details.
Error suppression and function removal in utilities
utils.py
JSON persistence and screenshot cleanup silently suppress exceptions; get_system_info() and log_automation_step() functions removed from module.
Test coverage for app.py changes
test_app_changes.py
Validates session state, screenshot deletion, storage operations, chat loading, and refactored setup_chat_menu container API with immediate refresh behavior.
Test coverage for browser automation
test_browser_automation.py
Validates constructor, session startup, screenshot format detection with silent fallback, element retrieval with exception silencing, and session closure.
Test coverage for element detector
test_element_detector.py
Validates element detection, annotation, positioning, and screenshot creation return fallback values without printing on exceptions.
Test coverage for utilities module
test_utils.py
Validates JSON persistence failure handling, screenshot cleanup error suppression, absence of removed functions, and error message formatting edge cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Marcus-Mok-GH/Mistral-Browser-Use#19: Both PRs modify app.py chat menu functionality, sidebar UI structure, and chat persistence operations; PR #19 may provide context for the setup_chat_menu refactor and chat state management patterns.

Poem

🐰 Whiskers twitch with glee,
Silent errors disappear with ease,
Sidebar glows, tests aligned—
No more "Step N" or verbose prints to find!
Four test suites bloom, so thorough and bright, 🧪

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly reflects the main changes: removing password masking from API key inputs and eliminating the 'Objective' header from the chat interface, which are clearly visible in the code modifications.
Docstring Coverage ✅ Passed Docstring coverage is 87.42% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch remove-debug-logs-and-restructure-ui-5080661316084715570

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Clean up UI and remove debug logging across application

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Remove debug logs and print statements throughout codebase
• Make API keys visible by removing password input masking
• Remove redundant 'Objective' header from chat interface
• Restructure sidebar into tabbed interface for Chats and Config
• Remove minimize/expand configuration panel functionality
• Clean up exception handling by removing verbose error logging
Diagram
flowchart LR
  A["Debug Logs & Print Statements"] -->|Remove| B["Clean Codebase"]
  C["Password-Masked API Keys"] -->|Make Visible| D["Visible Configuration"]
  E["Objective Header"] -->|Remove| F["Simplified Chat UI"]
  G["Sidebar Layout"] -->|Restructure to Tabs| H["Tabbed Control Center"]
  B --> I["Final UI Cleanup"]
  D --> I
  F --> I
  H --> I
Loading

Grey Divider

File Changes

1. app.py ✨ Enhancement +29/-47

Restructure UI to tabs and remove debug output

• Removed config_minimized session state variable and related minimize/expand button logic
• Removed type="password" from Mistral and Firecrawl API key inputs to display stored values
• Removed 'Objective' header display from main chat interface
• Restructured sidebar to use tabbed interface with "Chats" and "Config" tabs
• Removed intermediate step messages and debug logging from automation loop
• Replaced verbose exception handling with silent pass statements
• Added st.rerun() calls after chat selection and creation for proper state updates
• Removed screenshot annotation intermediate messages

app.py


2. browser_automation.py 🐞 Bug fix +2/-13

Remove debug print statements from browser automation

• Removed print statements for session initialization and browser startup
• Removed debug logging for image format detection and header inspection
• Removed traceback printing in exception handlers
• Replaced verbose exception handling with silent error suppression
• Removed print statement on browser session closure

browser_automation.py


3. element_detector.py 🐞 Bug fix +4/-9

Remove debug logging from element detection

• Removed print statements from exception handlers in element detection methods
• Removed debug logging for element annotation and position extraction
• Replaced verbose exception handling with silent pass statements
• Cleaned up error messages in detect_and_annotate_elements, annotate_elements_with_positions,
 and create_annotated_screenshot methods

element_detector.py


View more (1)
4. utils.py 🐞 Bug fix +4/-45

Remove debug utilities and logging functions

• Removed print statements from JSON save/load error handlers
• Removed debug logging from screenshot cleanup function
• Removed get_system_info() function used only for debugging
• Removed log_automation_step() function for step-by-step automation logging
• Replaced verbose exception handling with silent error suppression

utils.py


Grey Divider

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0)

Grey Divider


Action required

1. API keys unmasked 🐞 Bug ⛨ Security
Description
The configuration panel now renders stored Mistral/Firecrawl API keys as plain text by removing
password masking, making accidental disclosure likely (screen share, screenshots, shoulder-surfing).
Because the values are also loaded from and persisted to cookies, the raw keys will be displayed
whenever present.
Code

app.py[R208-213]

            mistral_api_key = st.text_input(
                "Mistral API Key",
                value=st.session_state.mistral_api_key,
-                type="password",
                help="Enter your Mistral AI API key",
                key="mistral_input"
            )
Evidence
setup_configuration_panel loads API keys from cookies into session state and passes them as the
value of st.text_input without password masking, and also persists changes back to cookies, so
secrets will be displayed whenever set.

app.py[175-251]
app.py[206-246]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
API keys are rendered in clear text because `st.text_input(..., type="password")` was removed.

## Issue Context
Keys are loaded from cookies into session state and then passed as `value=` to the inputs, so any existing key immediately appears on-screen.

## Fix Focus Areas
- app.py[175-259]

### Implementation sketch
- Re-introduce `type="password"` for both key inputs.
- If visibility is desired, add a `st.checkbox("Show API keys")` and conditionally set `type=None` only when explicitly enabled (default to masked).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Persistence errors hidden 🐞 Bug ☼ Reliability
Description
save_chats_to_local() and load_chats_from_local() now swallow all exceptions, so
localStorage/JSON failures can silently drop chat history and usage counters with no user-visible
error or diagnostic trail. This is amplified because add_message() calls save_chats_to_local()
for every message, turning transient storage issues into silent data loss.
Code

app.py[R98-99]

+    except Exception:
+        pass
Evidence
The persistence functions now suppress all exceptions, while add_message() invokes persistence on
each message; therefore any storage/serialization error will break persistence without any feedback
to the user or logs.

app.py[83-122]
app.py[318-334]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Persistence failures are fully suppressed via `except Exception: pass`, causing silent loss of chats/usage data.

## Issue Context
`add_message()` persists on each message when a chat is active; if persistence breaks, users won’t know their state isn’t being saved.

## Fix Focus Areas
- app.py[72-122]
- app.py[318-334]

### Implementation sketch
- Replace `except Exception: pass` with:
 - `logging.exception("...")` (server-side), and
 - a lightweight user signal once per session (e.g., `st.session_state.last_persist_error` + `st.warning`/`st.toast`).
- Keep the app resilient (don’t crash), but do not fail silently.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Debug signals removed 🐞 Bug ◔ Observability
Description
Automation/browser/annotation failures now discard diagnostic detail by removing traceback output
and converting multiple exception handlers to except Exception: pass/return {} paths. When
automation fails, the system retains only str(e) and frequently returns empty results, making it
difficult to diagnose why element detection/automation degraded.
Code

app.py[R471-472]

+        error_msg = f"Automation step failed: {str(e)}"
        add_message("assistant", error_msg, "error")
Evidence
execute_automation_step now emits only str(e) (no traceback), while browser element extraction
and element annotation paths catch Exception and return empty/fallback values without logging,
removing the main diagnostic signals when automation quality drops.

app.py[355-474]
browser_automation.py[184-210]
element_detector.py[12-80]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several previously-informative error paths now suppress debugging context (no traceback/logs), increasing MTTR when automation fails.

## Issue Context
The UI can still show user-friendly errors, but the system also needs a developer-facing trail (tracebacks) for failures in automation, element extraction, and annotation.

## Fix Focus Areas
- app.py[355-474]
- browser_automation.py[184-210]
- element_detector.py[12-80]

### Implementation sketch
- In `execute_automation_step` exception handler: keep the short user message, but also `logging.exception(...)` to record the full traceback.
- In `BrowserAutomation.get_interactable_elements` and `ElementDetector.detect_and_annotate_elements`: log exceptions before returning `{}` / `screenshot_path`.
- Optionally gate verbose logs behind an env var (e.g., `DEBUG=1`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Qodo Logo

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

Comment thread app.py
Comment on lines 208 to 213
mistral_api_key = st.text_input(
"Mistral API Key",
value=st.session_state.mistral_api_key,
type="password",
help="Enter your Mistral AI API key",
key="mistral_input"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Api keys unmasked 🐞 Bug ⛨ Security

The configuration panel now renders stored Mistral/Firecrawl API keys as plain text by removing
password masking, making accidental disclosure likely (screen share, screenshots, shoulder-surfing).
Because the values are also loaded from and persisted to cookies, the raw keys will be displayed
whenever present.
Agent Prompt
## Issue description
API keys are rendered in clear text because `st.text_input(..., type="password")` was removed.

## Issue Context
Keys are loaded from cookies into session state and then passed as `value=` to the inputs, so any existing key immediately appears on-screen.

## Fix Focus Areas
- app.py[175-259]

### Implementation sketch
- Re-introduce `type="password"` for both key inputs.
- If visibility is desired, add a `st.checkbox("Show API keys")` and conditionally set `type=None` only when explicitly enabled (default to masked).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4da1527bd9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app.py
# Detect and highlight elements
annotated_image_path = st.session_state.element_detector.detect_and_annotate_elements(screenshot_path, st.session_state.browser)
add_message("assistant", annotated_image_path, "image", "Elements detected and indexed")
add_message("assistant", annotated_image_path, "image")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve raw screenshot path for later deletion

take_screenshot_and_analyze now stores only the annotated image message, but detect_and_annotate_elements writes that file as a copy and leaves the original screenshot on disk. Because delete_chat_screenshots deletes only image paths present in chat messages, every successful automation step leaves an untracked raw screenshot in screenshots/, causing disk growth and stale sensitive captures over time; either persist screenshot_path for cleanup or delete it immediately after annotation succeeds.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app.py (1)

208-213: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

API keys are now exposed in plain text without password masking.

While the PR explicitly states this is intentional, removing password masking from API key inputs creates significant security risks:

  1. Shoulder surfing: Keys are visible to anyone nearby
  2. Screen sharing: Keys exposed during support sessions or demos
  3. Screenshots/recordings: Accidental capture of credentials
  4. Live streaming: Easy to forget keys are visible

Industry best practice is to mask credentials by default. If users need to verify their input, add a reveal toggle (e.g., an eye icon) rather than showing keys in plain text by default.

🔒 Recommended fix: restore password masking
 mistral_api_key = st.text_input(
     "Mistral API Key",
     value=st.session_state.mistral_api_key,
+    type="password",
     help="Enter your Mistral AI API key",
     key="mistral_input"
 )

Apply the same fix to the Firecrawl API key input at line 241:

 firecrawl_api_key = st.text_input(
     "Firecrawl API Key",
     value=st.session_state.firecrawl_api_key,
+    type="password",
     help="Enter your Firecrawl API key",
     key="firecrawl_input"
 )

Also applies to: 241-246

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.py` around lines 208 - 213, Restore password masking for API key inputs
by changing the Streamlit text_input for the Mistral key (key="mistral_input" /
variable mistral_api_key) to use the password input type and add a reveal toggle
UI so users can optionally unmask; apply the same change to the Firecrawl API
key input (key likely "firecrawl_input" / variable firecrawl_api_key). Ensure
the inputs keep their session_state defaults (st.session_state.mistral_api_key
and st.session_state.firecrawl_api_key) and implement the reveal toggle (eye
icon or checkbox) to switch between masked and plain text modes without leaving
keys visible by default.
🧹 Nitpick comments (1)
utils.py (1)

38-39: ⚡ Quick win

Ambiguous return value: None conflates "file not found" with "parse error".

Returning None for all exceptions makes it impossible for callers to distinguish between expected conditions (file doesn't exist yet) and errors that need attention (corrupted JSON, permission denied). Consider returning a tuple (data, error) or raising exceptions and letting callers handle expected cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils.py` around lines 38 - 39, The catch-all "except Exception: return None"
in the function that reads/parses JSON (the except Exception block) hides
distinct outcomes; change the function to return a tuple (data, error) instead
of a bare None: on success return (data, None), catch FileNotFoundError and
return (None, None) to indicate "no file yet", and for
JSONDecodeError/PermissionError return (None, err) (or re-raise if you prefer
strict handling); update callers of this reader to handle the (data, error)
tuple appropriately.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app.py`:
- Around line 98-99: The current broad "except Exception: pass" in
save_chats_to_local and load_chats_from_local silently swallows storage errors
and can cause data loss; change these handlers to catch specific storage-related
exceptions (e.g., DOMException/StorageError or more specific exceptions raised
by your environment) instead of a bare Exception, log the error details to your
logger, and surface a UI-facing warning/state (e.g., set a persistenceError flag
or call showPersistenceWarning()) so the app can display a warning to the user;
ensure the functions return a success boolean or propagate the error state so
callers can react appropriately.

In `@element_detector.py`:
- Around line 78-79: The current bare except that returns screenshot_path
swallows errors; change the except block that catches Exception (the one
returning screenshot_path) to "except Exception as e:" and log the full
error/stack trace via the module logger (e.g., logger.exception or
logging.exception) so diagnostics are preserved, then return a tuple
(screenshot_path, False) instead of the raw path so callers can distinguish
failure vs success; update any callers of the function to handle the (path,
success_flag) return or alternatively keep a backward-compatible code path that
returns just the path while also emitting the logged exception.

---

Outside diff comments:
In `@app.py`:
- Around line 208-213: Restore password masking for API key inputs by changing
the Streamlit text_input for the Mistral key (key="mistral_input" / variable
mistral_api_key) to use the password input type and add a reveal toggle UI so
users can optionally unmask; apply the same change to the Firecrawl API key
input (key likely "firecrawl_input" / variable firecrawl_api_key). Ensure the
inputs keep their session_state defaults (st.session_state.mistral_api_key and
st.session_state.firecrawl_api_key) and implement the reveal toggle (eye icon or
checkbox) to switch between masked and plain text modes without leaving keys
visible by default.

---

Nitpick comments:
In `@utils.py`:
- Around line 38-39: The catch-all "except Exception: return None" in the
function that reads/parses JSON (the except Exception block) hides distinct
outcomes; change the function to return a tuple (data, error) instead of a bare
None: on success return (data, None), catch FileNotFoundError and return (None,
None) to indicate "no file yet", and for JSONDecodeError/PermissionError return
(None, err) (or re-raise if you prefer strict handling); update callers of this
reader to handle the (data, error) tuple appropriately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 122be4e7-7bef-4d80-bc71-4ec62dcbcece

📥 Commits

Reviewing files that changed from the base of the PR and between 8efb466 and 4da1527.

📒 Files selected for processing (4)
  • app.py
  • browser_automation.py
  • element_detector.py
  • utils.py

Comment thread app.py
Comment on lines +98 to +99
except Exception:
pass

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Silent localStorage failures could result in data loss.

Both save_chats_to_local() and load_chats_from_local() now suppress all exceptions. Users will not know if their chat history failed to persist due to quota limits, browser privacy settings, or other localStorage errors. Consider at minimum showing a warning indicator in the UI when persistence fails, even if detailed errors aren't logged.

Also applies to: 120-121

🧰 Tools
🪛 Ruff (0.15.12)

[error] 98-99: try-except-pass detected, consider logging the exception

(S110)


[warning] 98-98: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.py` around lines 98 - 99, The current broad "except Exception: pass" in
save_chats_to_local and load_chats_from_local silently swallows storage errors
and can cause data loss; change these handlers to catch specific storage-related
exceptions (e.g., DOMException/StorageError or more specific exceptions raised
by your environment) instead of a bare Exception, log the error details to your
logger, and surface a UI-facing warning/state (e.g., set a persistenceError flag
or call showPersistenceWarning()) so the app can display a warning to the user;
ensure the functions return a success boolean or propagate the error state so
callers can react appropriately.

Comment thread element_detector.py
Comment on lines +78 to 79
except Exception:
return screenshot_path # Return original if annotation fails

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.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Silent failure eliminates all diagnostic information.

The exception handler now suppresses all errors without providing any user feedback. Users cannot distinguish between successful annotation with no elements versus annotation failures due to file corruption, PIL errors, or font loading issues. In a browser automation context, this observability gap makes debugging significantly harder.

Consider at minimum returning a tuple (path, success_flag) or logging to a debug channel that developers can enable.

🧰 Tools
🪛 Ruff (0.15.12)

[warning] 78-78: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@element_detector.py` around lines 78 - 79, The current bare except that
returns screenshot_path swallows errors; change the except block that catches
Exception (the one returning screenshot_path) to "except Exception as e:" and
log the full error/stack trace via the module logger (e.g., logger.exception or
logging.exception) so diagnostics are preserved, then return a tuple
(screenshot_path, False) instead of the raw path so callers can distinguish
failure vs success; update any callers of the function to handle the (path,
success_flag) return or alternatively keep a backward-compatible code path that
returns just the path while also emitting the logged exception.

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

✅ Unit tests committed locally. Commit: ccb3aa46ac31d17251c17e99264b6b945f0cebd6

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_app_changes.py`:
- Around line 25-42: The test mutates global import state by inserting MagicMock
objects into sys.modules using _STUB_MODULES/_mod_name without restoring
originals; change this to save each original sys.modules.get(_mod_name) before
stubbing, replace or insert the MagicMock for the duration of the test (or wrap
in a fixture/context manager), and then restore the saved original (deleting the
key if it didn't exist before) after the test completes so sys.modules is
returned to its prior state; look for the _STUB_MODULES list, the loop over
_mod_name, sys.modules, and MagicMock to implement the save/restore logic or
move it into setup/teardown.

In `@test_browser_automation.py`:
- Around line 242-245: The test currently swallows all exceptions around the
call to _run_screenshot_with_bytes(self.ba, unknown_bytes) (and the similar
block later), which masks real failures; remove the broad try/except or replace
it with an explicit assertion of the expected error using pytest.raises (or
assertRaises) for the specific exception type you expect from
_run_screenshot_with_bytes, so unexpected exceptions will fail the test and the
intended error behavior is asserted instead.
- Around line 22-25: The top-level mutation of sys.modules via _STUBS and
MagicMock leaks import-state across tests; change this to a temporary,-restored
approach: replace the direct loop with a scoped mechanism (e.g., use
unittest.mock.patch.dict on sys.modules or a pytest fixture that records
original = {k: sys.modules.get(k) for k in _STUBS}, inserts MagicMock() for
missing keys, yields to run the test, then restores originals or deletes
inserted keys in a finally block). Target the symbols _STUBS, sys.modules, and
MagicMock when implementing the patch/dict or fixture so the mocked modules are
removed/restored after each test run.

In `@test_element_detector.py`:
- Around line 140-150: The test
test_returns_original_when_browser_raises_exception uses a corrupt temp image so
the method detect_and_annotate_elements may return the original path for
image-parsing reasons rather than because mock_browser.get_interactable_elements
raised; replace the invalid image with a small valid PNG (so image loading
succeeds), keep mock_browser.get_interactable_elements.side_effect =
RuntimeError("fail"), call detect_and_annotate_elements and assert the return
equals the original path, and additionally assert
mock_browser.get_interactable_elements was called (or session_id accessed) to
ensure the browser-exception branch was exercised.
- Around line 20-34: The test currently injects global stubs into sys.modules
via _STUBS and PIL mocks without restoring originals, which can leak into other
tests; update the setup to save original entries for each module in _STUBS and
for "PIL"/"PIL.Image"/"PIL.ImageDraw"/"PIL.ImageFont" (e.g., store a dict of
originals keyed by module name) before assigning MagicMock, and add teardown
logic (or use pytest's fixture/monkeypatch) to restore those originals (or
delete the injected mocks) and reset _PIL_AVAILABLE/_real_pil_image
appropriately after the test completes so module state is deterministic for
subsequent tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1f2b8b2-e80b-45f1-a926-b61f891fabfc

📥 Commits

Reviewing files that changed from the base of the PR and between 4da1527 and ccb3aa4.

📒 Files selected for processing (4)
  • test_app_changes.py
  • test_browser_automation.py
  • test_element_detector.py
  • test_utils.py

Comment thread test_app_changes.py
Comment on lines +25 to +42
_STUB_MODULES = [
"streamlit",
"extra_streamlit_components",
"streamlit_local_storage",
"browser_automation",
"mistral_client",
"fireworks_client",
"element_detector",
"cv2",
"PIL",
"PIL.Image",
"firecrawl",
"requests",
]
for _mod_name in _STUB_MODULES:
if _mod_name not in sys.modules:
sys.modules[_mod_name] = MagicMock()

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore sys.modules after stubbing to avoid cross-test contamination.

Line 25 mutates global import state and never restores it. This can make unrelated tests pass/fail depending on execution order.

Suggested fix
+_ORIGINAL_MODULES = {name: sys.modules.get(name) for name in _STUB_MODULES}
 for _mod_name in _STUB_MODULES:
     if _mod_name not in sys.modules:
         sys.modules[_mod_name] = MagicMock()
+
+def tearDownModule():
+    for name, original in _ORIGINAL_MODULES.items():
+        if original is None:
+            sys.modules.pop(name, None)
+        else:
+            sys.modules[name] = original
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_STUB_MODULES = [
"streamlit",
"extra_streamlit_components",
"streamlit_local_storage",
"browser_automation",
"mistral_client",
"fireworks_client",
"element_detector",
"cv2",
"PIL",
"PIL.Image",
"firecrawl",
"requests",
]
for _mod_name in _STUB_MODULES:
if _mod_name not in sys.modules:
sys.modules[_mod_name] = MagicMock()
_STUB_MODULES = [
"streamlit",
"extra_streamlit_components",
"streamlit_local_storage",
"browser_automation",
"mistral_client",
"fireworks_client",
"element_detector",
"cv2",
"PIL",
"PIL.Image",
"firecrawl",
"requests",
]
_ORIGINAL_MODULES = {name: sys.modules.get(name) for name in _STUB_MODULES}
for _mod_name in _STUB_MODULES:
if _mod_name not in sys.modules:
sys.modules[_mod_name] = MagicMock()
def tearDownModule():
for name, original in _ORIGINAL_MODULES.items():
if original is None:
sys.modules.pop(name, None)
else:
sys.modules[name] = original
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_app_changes.py` around lines 25 - 42, The test mutates global import
state by inserting MagicMock objects into sys.modules using
_STUB_MODULES/_mod_name without restoring originals; change this to save each
original sys.modules.get(_mod_name) before stubbing, replace or insert the
MagicMock for the duration of the test (or wrap in a fixture/context manager),
and then restore the saved original (deleting the key if it didn't exist before)
after the test completes so sys.modules is returned to its prior state; look for
the _STUB_MODULES list, the loop over _mod_name, sys.modules, and MagicMock to
implement the save/restore logic or move it into setup/teardown.

Comment on lines +22 to +25
_STUBS = ["firecrawl", "PIL", "PIL.Image", "cv2"]
for _mod in _STUBS:
if _mod not in sys.modules:
sys.modules[_mod] = MagicMock()

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent global import-state leakage from test stubs.

These top-level sys.modules mutations are not restored, which can make downstream tests order-dependent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_browser_automation.py` around lines 22 - 25, The top-level mutation of
sys.modules via _STUBS and MagicMock leaks import-state across tests; change
this to a temporary,-restored approach: replace the direct loop with a scoped
mechanism (e.g., use unittest.mock.patch.dict on sys.modules or a pytest fixture
that records original = {k: sys.modules.get(k) for k in _STUBS}, inserts
MagicMock() for missing keys, yields to run the test, then restores originals or
deletes inserted keys in a finally block). Target the symbols _STUBS,
sys.modules, and MagicMock when implementing the patch/dict or fixture so the
mocked modules are removed/restored after each test run.

Comment on lines +242 to +245
try:
_run_screenshot_with_bytes(self.ba, unknown_bytes)
except Exception:
pass

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid try/except Exception: pass in assertions.

These blocks can hide real failures and let the test pass vacuously. Let unexpected exceptions fail the test (or assert a specific expected exception).

Also applies to: 282-285

🧰 Tools
🪛 Ruff (0.15.12)

[error] 244-245: try-except-pass detected, consider logging the exception

(S110)


[warning] 244-244: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_browser_automation.py` around lines 242 - 245, The test currently
swallows all exceptions around the call to _run_screenshot_with_bytes(self.ba,
unknown_bytes) (and the similar block later), which masks real failures; remove
the broad try/except or replace it with an explicit assertion of the expected
error using pytest.raises (or assertRaises) for the specific exception type you
expect from _run_screenshot_with_bytes, so unexpected exceptions will fail the
test and the intended error behavior is asserted instead.

Comment thread test_element_detector.py
Comment on lines +20 to +34
_STUBS = ["numpy", "cv2"]
for _mod in _STUBS:
if _mod not in sys.modules:
sys.modules[_mod] = MagicMock()

# We keep PIL real (it's in requirements.txt) but fall back to a mock if absent.
try:
from PIL import Image as _real_pil_image
_PIL_AVAILABLE = True
except ImportError:
sys.modules["PIL"] = MagicMock()
sys.modules["PIL.Image"] = MagicMock()
sys.modules["PIL.ImageDraw"] = MagicMock()
sys.modules["PIL.ImageFont"] = MagicMock()
_PIL_AVAILABLE = False

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add module-stub cleanup to keep test isolation deterministic.

Line 20 writes global stubs into sys.modules but never restores originals. This can leak mocked dependencies into other test files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_element_detector.py` around lines 20 - 34, The test currently injects
global stubs into sys.modules via _STUBS and PIL mocks without restoring
originals, which can leak into other tests; update the setup to save original
entries for each module in _STUBS and for
"PIL"/"PIL.Image"/"PIL.ImageDraw"/"PIL.ImageFont" (e.g., store a dict of
originals keyed by module name) before assigning MagicMock, and add teardown
logic (or use pytest's fixture/monkeypatch) to restore those originals (or
delete the injected mocks) and reset _PIL_AVAILABLE/_real_pil_image
appropriately after the test completes so module state is deterministic for
subsequent tests.

Comment thread test_element_detector.py
Comment on lines +140 to +150
def test_returns_original_when_browser_raises_exception(self):
"""If get_element_positions_from_browser raises, original path is returned."""
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
f.write(b"not a real image")
path = f.name
try:
mock_browser = MagicMock()
mock_browser.session_id = "sess"
mock_browser.get_interactable_elements.side_effect = RuntimeError("fail")
result = self.detector.detect_and_annotate_elements(path, mock_browser)
self.assertEqual(result, path)

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

This test can pass without actually exercising the browser-error path.

Because the screenshot is intentionally invalid, returning the original path can happen even if the browser exception branch is never reached. Use a valid image and assert the browser call happened.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_element_detector.py` around lines 140 - 150, The test
test_returns_original_when_browser_raises_exception uses a corrupt temp image so
the method detect_and_annotate_elements may return the original path for
image-parsing reasons rather than because mock_browser.get_interactable_elements
raised; replace the invalid image with a small valid PNG (so image loading
succeeds), keep mock_browser.get_interactable_elements.side_effect =
RuntimeError("fail"), call detect_and_annotate_elements and assert the return
equals the original path, and additionally assert
mock_browser.get_interactable_elements was called (or session_id accessed) to
ensure the browser-exception branch was exercised.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant