import subprocess import sys import traceback from typing import TypedDict import pandas as pd from h2o_wave import Q, expando_to_dict, ui from h2o_wave.types import Component from llm_studio.app_utils.sections.common import clean_dashboard from .config import default_cfg class ThemeColors(TypedDict): light: dict dark: dict class WaveTheme: _theme_colors: ThemeColors = { "light": { "primary": "#000000", "background_color": "#ffffff", }, "dark": { "primary": "#FEC925", "background_color": "#121212", }, } states = { "zombie": "#E0E0E0", "queued": "#B8B8B8", "running": "#FFE52B", "finished": "#92E95A", "failed": "#DA0000", "stopped": "#DA0000", } color = "#2196F3" color_range = "#2196F3 #CC7722 #2CA02C #D62728 #9467BD #17BECF #E377C2 #DDAA22" def __repr__(self) -> str: return "WaveTheme" def get_value_by_key(self, q: Q, key: str): value = ( self._theme_colors["dark"][key] if q.client.theme_dark else self._theme_colors["light"][key] ) return value def get_primary_color(self, q: Q): primary_color = self.get_value_by_key(q, "primary") return primary_color def get_background_color(self, q: Q): background_color = self.get_value_by_key(q, "background_color") return background_color wave_theme = WaveTheme() def ui_table_from_df( q: Q, df: pd.DataFrame, name: str, sortables: list = None, filterables: list = None, searchables: list = None, markdown_cells=None, numerics: list = None, times: list = None, tags: list = None, progresses: list = None, min_widths: dict = None, max_widths: dict = None, link_col: str = None, multiple: bool = False, groupable: bool = False, downloadable: bool = False, resettable: bool = False, height: str = None, checkbox_visibility: str = None, actions: dict = None, max_char_length: int = 500, cell_overflow="tooltip", ) -> Component: """ Convert a Pandas dataframe into Wave ui.table format. """ df = df.reset_index(drop=True) sortables = sortables or [] filterables = filterables or [] searchables = searchables or [] numerics = numerics or [] times = times or [] tags = tags or [] progresses = progresses or [] markdown_cells = markdown_cells or [] min_widths = min_widths or {} max_widths = max_widths or {} if numerics == []: numerics = df.select_dtypes(include=["float64", "float32"]).columns.tolist() cell_types = {} for col in tags: cell_types[col] = ui.tag_table_cell_type( name="tags", tags=[ ui.tag(label=state, color=wave_theme.states[state]) for state in wave_theme.states ], ) for col in progresses: cell_types[col] = ui.progress_table_cell_type( wave_theme.get_primary_color(q), ) for col in markdown_cells: # enables rendering of code in wave table cell_types[col] = ui.markdown_table_cell_type() columns = [ ui.table_column( name=str(col), label=str(col), sortable=True if col in sortables else False, filterable=True if col in filterables else False, searchable=True if col in searchables else False, data_type=( "number" if col in numerics else ("time" if col in times else "string") ), cell_type=cell_types[col] if col in cell_types else None, min_width=min_widths[col] if col in min_widths else None, max_width=max_widths[col] if col in max_widths else None, link=True if col == link_col else False, cell_overflow=cell_overflow, ) for col in df.columns.values ] if actions: commands = [ui.command(name=key, label=val) for key, val in actions.items()] action_column = ui.table_column( name="actions", label="action" if int(min_widths["actions"]) > 30 else "", cell_type=ui.menu_table_cell_type(name="commands", commands=commands), min_width=min_widths["actions"], ) columns.append(action_column) rows = [] for i, row in df.iterrows(): cells = [] for cell in row: str_repr = str(cell) if len(str_repr) >= max_char_length: str_repr = str_repr[:max_char_length] + "..." cells.append(str_repr) rows.append(ui.table_row(name=str(i), cells=cells)) table = ui.table( name=name, columns=columns, rows=rows, multiple=multiple, groupable=groupable, downloadable=downloadable, resettable=resettable, height=height, checkbox_visibility=checkbox_visibility, ) return table def wave_utils_error_card( q: Q, box: str, app_name: str, github: str, q_app: dict, error: Exception, q_user: dict, q_client: dict, q_events: dict, q_args: dict, ) -> ui.FormCard: """ Card for handling crash. """ q_app_str = ( "### q.app\n```" + "\n".join( [ f"{k}: {v}" for k, v in q_app.items() if "_key" not in k and "_token not in k" ] ) + "\n```" ) q_user_str = ( "### q.user\n```" + "\n".join( [ f"{k}: {v}" for k, v in q_user.items() if "_key" not in k and "_token" not in k ] ) + "\n```" ) q_client_str = ( "### q.client\n```" + "\n".join( [ f"{k}: {v}" for k, v in q_client.items() if "_key" not in k and "_token" not in k ] ) + "\n```" ) q_events_str = ( "### q.events\n```" + "\n".join( [ f"{k}: {v}" for k, v in q_events.items() if "_key" not in k and "_token" not in k ] ) + "\n```" ) q_args_str = ( "### q.args\n```" + "\n".join( [ f"{k}: {v}" for k, v in q_args.items() if "_key" not in k and "_token" not in k ] ) + "\n```" ) type_, value_, traceback_ = sys.exc_info() stack_trace = traceback.format_exception(type_, value_, traceback_) git_version = subprocess.getoutput("git rev-parse HEAD") if not q.app.wave_utils_stack_trace_str: q.app.wave_utils_stack_trace_str = "### stacktrace\n" + "\n".join(stack_trace) card = ui.form_card( box=box, items=[ ui.stats( items=[ ui.stat( label="", value="Oops!", caption="Something went wrong", icon="Error", icon_color="#CDDD38", ) ], justify="center", ), ui.separator(), ui.text_l(content="