|
import hashlib |
|
import logging |
|
from typing import List |
|
|
|
from h2o_wave import Q, ui |
|
|
|
from llm_studio.app_utils.cards import card_zones |
|
from llm_studio.app_utils.config import default_cfg |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
async def meta(q: Q) -> None: |
|
if q.client["keep_meta"]: |
|
q.client["keep_meta"] = False |
|
return |
|
|
|
zones = card_zones(mode=q.client["mode_curr"]) |
|
|
|
if q.client["notification_bar"]: |
|
notification_bar = ui.notification_bar( |
|
type="warning", |
|
timeout=20, |
|
text=q.client["notification_bar"], |
|
position="top-right", |
|
) |
|
else: |
|
notification_bar = None |
|
|
|
|
|
q.page["meta"] = ui.meta_card( |
|
box="", |
|
title="H2O LLM Studio", |
|
layouts=[ |
|
ui.layout(breakpoint="0px", width="1430px", zones=zones), |
|
], |
|
scripts=[ |
|
ui.script(source, asynchronous=True) for source in q.app["script_sources"] |
|
], |
|
stylesheet=ui.inline_stylesheet( |
|
""" |
|
.ms-MessageBar { |
|
padding-top: 3px; |
|
padding-bottom: 3px; |
|
min-height: 18px; |
|
} |
|
div[data-test="nav_bar"] .ms-Nav-groupContent { |
|
margin-bottom: 0; |
|
} |
|
|
|
div[data-test="experiment/display/deployment/top_right"], |
|
div[data-test="experiment/display/deployment/top_right"] |
|
div[data-visible="true"]:last-child > div > div { |
|
display: flex; |
|
} |
|
|
|
div[data-test="experiment/display/deployment/top_right"] |
|
div[data-visible="true"]:last-child, |
|
div[data-test="experiment/display/deployment/top_right"] |
|
div[data-visible="true"]:last-child > div { |
|
display: flex; |
|
flex-grow: 1; |
|
} |
|
|
|
div[data-test="experiment/display/deployment/top_right"] |
|
div[data-visible="true"]:last-child > div > div > div { |
|
display: flex; |
|
flex-grow: 1; |
|
flex-direction: column; |
|
} |
|
|
|
div[data-test="experiment/display/deployment/top_right"] |
|
div[data-visible="true"]:last-child > div > div > div > div { |
|
flex-grow: 1; |
|
} |
|
""" |
|
), |
|
script=None, |
|
notification_bar=notification_bar, |
|
) |
|
|
|
if q.client.theme_dark: |
|
q.page["meta"].theme = "h2o-dark" |
|
else: |
|
q.page["meta"].theme = "light" |
|
|
|
|
|
def heap_analytics( |
|
userid, user_properties=None, event_properties=None |
|
) -> ui.InlineScript: |
|
script = ( |
|
"window.heap=window.heap||[],heap.load=function(e,t)" |
|
"{window.heap.appid=e,window.heap." |
|
'config=t=t||{};var r=document.createElement("script");' |
|
'r.type="text/javascript",' |
|
'r.async=!0,r.src="https://cdn.heapanalytics.com/js/heap-"+e+".js";' |
|
'var a=document.getElementsByTagName("script")[0];' |
|
"a.parentNode.insertBefore(r,a);" |
|
"for(var n=function(e){return function(){heap.push([e]." |
|
"concat(Array.prototype.slice.call(arguments,0)))}}," |
|
'p=["addEventProperties","addUserProperties","clearEventProperties","identify",' |
|
'"resetIdentity","removeEventProperty","setEventProperties","track",' |
|
'"unsetEventProperty"],o=0;o<p.length;o++)heap[p[o]]=n(p[o])};' |
|
'heap.load("1090178399");' |
|
) |
|
|
|
identity = hashlib.sha256(userid.encode()).hexdigest() |
|
script += f"heap.identify('{identity}');" |
|
|
|
if user_properties is not None: |
|
script += f"heap.addUserProperties({user_properties})" |
|
|
|
if event_properties is not None: |
|
script += f"heap.addEventProperties({event_properties})" |
|
|
|
return ui.inline_script(content=script) |
|
|
|
|
|
async def interface(q: Q) -> None: |
|
"""Display interface cards.""" |
|
|
|
await meta(q) |
|
|
|
navigation_pages = ["Home", "Settings"] |
|
|
|
if q.client["init_interface"] is None: |
|
|
|
q.page["header"] = ui.header_card( |
|
box="header", |
|
title=default_cfg.name, |
|
image=q.app["icon_path"], |
|
subtitle=f"v{default_cfg.version}", |
|
) |
|
|
|
if q.app.heap_mode: |
|
logger.info("Heap on") |
|
q.page["meta"].script = heap_analytics( |
|
userid=q.auth.subject, |
|
event_properties=( |
|
f"{{version: '{q.app.version}'" + f", product: '{q.app.name}'}}" |
|
), |
|
) |
|
|
|
await q.page.save() |
|
else: |
|
logger.info("Heap off") |
|
|
|
q.page["nav_bar"] = ui.nav_card( |
|
box="nav", |
|
items=[ |
|
ui.nav_group( |
|
"Navigation", |
|
items=[ |
|
ui.nav_item(page.lower(), page) for page in navigation_pages |
|
], |
|
), |
|
ui.nav_group( |
|
"Datasets", |
|
items=[ |
|
ui.nav_item(name="dataset/import", label="Import dataset"), |
|
ui.nav_item(name="dataset/list", label="View datasets"), |
|
], |
|
), |
|
ui.nav_group( |
|
"Experiments", |
|
items=[ |
|
ui.nav_item(name="experiment/start", label="Create experiment"), |
|
ui.nav_item(name="experiment/list", label="View experiments"), |
|
], |
|
), |
|
], |
|
value=( |
|
default_cfg.start_page |
|
if q.client["nav/active"] is None |
|
else q.client["nav/active"] |
|
), |
|
) |
|
else: |
|
|
|
q.page["nav_bar"].value = ( |
|
default_cfg.start_page |
|
if q.client["nav/active"] is None |
|
else q.client["nav/active"] |
|
) |
|
|
|
q.client["init_interface"] = True |
|
|
|
|
|
async def clean_dashboard(q: Q, mode: str = "full", exclude: List[str] = []): |
|
"""Drop cards from Q page.""" |
|
|
|
logger.info(q.client.delete_cards) |
|
for card_name in q.client.delete_cards: |
|
if card_name not in exclude: |
|
del q.page[card_name] |
|
|
|
q.page["meta"].layouts[0].zones = card_zones(mode=mode) |
|
q.client["mode_curr"] = mode |
|
q.client["notification_bar"] = None |
|
|
|
|
|
async def delete_dialog(q: Q, names: List[str], action, entity): |
|
title = "Do you really want to delete " |
|
n_datasets = len(names) |
|
|
|
if n_datasets == 1: |
|
title = f"{title} {entity} {names[0]}?" |
|
else: |
|
title = f"{title} {n_datasets} {entity}s?" |
|
|
|
q.page["meta"].dialog = ui.dialog( |
|
f"Delete {entity}", |
|
items=[ |
|
ui.text(title), |
|
ui.markup("<br>"), |
|
ui.buttons( |
|
[ |
|
ui.button(name=action, label="Delete", primary=True), |
|
ui.button(name="abort", label="Abort", primary=False), |
|
], |
|
justify="end", |
|
), |
|
], |
|
) |
|
q.client["keep_meta"] = True |
|
|
|
|
|
async def info_dialog(q: Q, title: str, message: str): |
|
q.page["meta"].dialog = ui.dialog( |
|
title, |
|
items=[ |
|
ui.text(message), |
|
ui.markup("<br>"), |
|
ui.buttons( |
|
[ |
|
ui.button(name="abort", label="Continue", primary=False), |
|
], |
|
justify="end", |
|
), |
|
], |
|
blocking=True, |
|
) |
|
q.client["keep_meta"] = True |
|
|
|
|
|
async def heap_redact(q: Q) -> None: |
|
if q.app.heap_mode: |
|
|
|
await q.page.save() |
|
|
|
|
|
q.page["meta"].script = ui.inline_script( |
|
""" |
|
document.querySelectorAll('div[data-automation-key="name"]').forEach(a => { |
|
a.setAttribute('data-heap-redact-text', '') |
|
}) |
|
|
|
document.querySelector('div[data-test="datasets_table"] \ |
|
.ms-ScrollablePane--contentContainer').addEventListener('scroll', () => { |
|
window.setTimeout(() => {{ |
|
document.querySelectorAll('div[data-automation-key="name"]').forEach(a => { |
|
a.setAttribute('data-heap-redact-text', '') |
|
}) |
|
}}, 100) |
|
}) |
|
""" |
|
) |
|
|