From 35b28d33dc67b764f14258927bbca7f1c886a04d Mon Sep 17 00:00:00 2001 From: Omkar Gaikwad Date: Sun, 9 Aug 2026 16:30:52 +0000 Subject: [PATCH 1/3] chore(viewer): capitalize the Product Status heading --- viewer/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/main.py b/viewer/main.py index f9c2c443..6ee48b6e 100644 --- a/viewer/main.py +++ b/viewer/main.py @@ -345,7 +345,7 @@ def status_component(): box_shadow="0 2px 4px rgba(0,0,0,0.05)", ) ): - me.text("Product status", type="headline-5") + me.text("Product Status", type="headline-5") me.box(style=me.Style(height="16px")) me.text(f"Total Evaluation Jobs: {len(directories)}") From 23188f3247a328e895f6477537a4a19b24c24b38 Mon Sep 17 00:00:00 2001 From: Omkar Gaikwad Date: Sun, 9 Aug 2026 16:34:22 +0000 Subject: [PATCH 2/3] chore(viewer): delete the unused state.py Nothing imports it. It is a stale copy of main.py's State: 16 fields against 30, still carrying eval_summaries, and selected_main_tab defaults to "List" rather than "Status". Leaving it is a trap. @me.stateclass registers by class identity, so two decorated State classes are two different state objects to Mesop. The first module to import this one instead of main would get its own state with its own defaults, silently, with no error to trace. --- viewer/state.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 viewer/state.py diff --git a/viewer/state.py b/viewer/state.py deleted file mode 100644 index 66f9229e..00000000 --- a/viewer/state.py +++ /dev/null @@ -1,16 +0,0 @@ -import mesop as me - -@me.stateclass -class State: - selected_directory: str = "" - selected_tab: str = "Dashboard" - conversation_index: int = 0 - eval_summaries: str = "" - eval_id_filter: str = "" - product_filter: str = "" - requester_filter: str = "" - sort_column: str = "date" - sort_descending: bool = True - open_dropdown: str = "" - selected_main_tab: str = "List" - trends_product_filter: str = "" From 654a2144f1212104d8170e15cd94b0debe0664f4 Mon Sep 17 00:00:00 2001 From: Omkar Gaikwad Date: Sun, 9 Aug 2026 17:04:42 +0000 Subject: [PATCH 3/3] chore(viewer): hoist function-local imports and drop the dead __main__ block The four local `import re` shadowed the module-level one added earlier; ast, threading and time each had a single use. The trends import stays local because trends imports from main. me.run does not exist in mesop 1.3.3, so the __main__ block only ever raised; gunicorn serves the module itself as `main:me`. --- viewer/main.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/viewer/main.py b/viewer/main.py index 6ee48b6e..45db7c1b 100644 --- a/viewer/main.py +++ b/viewer/main.py @@ -1,3 +1,4 @@ +import ast import os import re import hashlib @@ -7,6 +8,8 @@ import logging import json import subprocess +import threading +import time from functools import lru_cache import precompute_trends import dataset_quality @@ -85,8 +88,6 @@ class State: def df_to_config(df: pd.DataFrame) -> dict: - import ast - original_dict = {} for _, row in df.iterrows(): @@ -597,8 +598,6 @@ def _pct(value): def _build_summaries(cache_df): - import re - rows = [] for _, row in cache_df.iterrows(): score = row['ai_score'] if 'ai_score' in row else 0.0 @@ -2100,7 +2099,6 @@ def on_clear_cache_click(e: me.ClickEvent): logging.info("Cleared precomputed files. Triggering precompute...") - import threading threading.Thread(target=precompute_trends.precompute).start() state.cache_cleared_message = "Cache cleared. Precompute triggered in background." @@ -2135,7 +2133,6 @@ def on_clear_cache_click(e: me.ClickEvent): ), ) - import time cache_file = os.path.join(results_dir, "trends_cache.csv") cache_status = "Not Ready" cache_color = "#ef4444" # Red @@ -2223,7 +2220,6 @@ def on_generate_summary_click(e: me.ClickEvent): results_dir_full = os.path.join(results_dir, state.selected_directory) state.ai_summary = summarize_eval_scoring(results_dir_full) # Parse score - import re match = re.search(r"\*\*General Score:\s*(\d+(\.\d+)?)[^*]*\*\*", state.ai_summary) if match: state.ai_score = float(match.group(1)) @@ -2319,7 +2315,6 @@ def get_val(cfg_name): # Fallback if score was not parsed correctly in cache if state.ai_score == 0.0 and state.ai_summary: - import re match = re.search(r"General Score:.*?(\d+(\.\d+)?)", state.ai_summary) if match: state.ai_score = float(match.group(1)) @@ -2339,7 +2334,6 @@ def get_val(cfg_name): me.text("Formula: 0.4 * goal_completion + 0.2 * trajectory_matcher + 0.2 * behavioral_metrics + 0.2 * parameter_analysis", style=me.Style(font_size="14px", color="#6b7280", margin=me.Margin(bottom="16px"))) # Strip score from summary if present to avoid duplication - import re clean_summary = re.sub(r"^\s*\*\*General Score:\s*\d+(\.\d+)?[^*]*\*\*\s*", "", state.ai_summary) me.markdown(clean_summary) @@ -2410,6 +2404,7 @@ def on_next_conversation(e: me.ClickEvent): else: + # Local: trends imports from main, so hoisting this cycles. from trends import trends_component state = me.state(State) @@ -2476,5 +2471,4 @@ def on_main_tab_change(e: me.ButtonToggleChangeEvent): except Exception as e: logging.exception("render_app_content failed") me.text(f"Fatal Error: {e}") -if __name__ == "__main__": - me.run(app) +