Richard
Minor fixes + remove generate buttons for now
74dc293
raw
history blame
10.3 kB
import copy
import mesop as me
import mesop.labs as mel
import components as mex
import dialogs
import handlers
import llm
from eval_table import prompt_eval_table
from tool_sidebar import tool_sidebar
from helpers import find_prompt, parse_variables
from state import State, Prompt
from web_components import AsyncAction
from web_components import async_action_component
_INSTRUCTIONS = """
- Write your prompt.
- You can use variables using this syntax `{{VARIABLE_NAME}}`.
- If you used variables, populate them from the `Set variables` dialog.
- Adjust model settings if necessary from the `Model settings` dialog.
- When you're ready, press the run button.
- If you make adjustments to your prompt or model settings, pressing run will create a
new version of your prompt.
""".strip()
@me.page(
stylesheets=[
# Other themes here: https://www.jsdelivr.com/package/npm/highlight.js?tab=files&path=styles
"https://cdn.jsdelivr.net/npm/highlight.js@11.10.0/styles/github-dark.min.css",
"https://cdn.jsdelivr.net/npm/highlight.js@11.10.0/styles/github.min.css",
],
security_policy=me.SecurityPolicy(
allowed_script_srcs=[
"https://cdn.jsdelivr.net",
],
dangerously_disable_trusted_types=True,
allowed_iframe_parents=["https://huggingface.co"],
),
)
def app():
state = me.state(State)
action = (
AsyncAction(value=state.async_action_name, duration_seconds=state.async_action_duration)
if state.async_action_name
else None
)
async_action_component(action=action, on_finished=on_async_action_finished)
mex.snackbar(is_visible=state.show_snackbar, label=state.snackbar_message)
dialogs.update_title()
dialogs.model_settings()
dialogs.prompt_variables()
dialogs.prompt_version_history()
dialogs.add_comparisons()
dialogs.generate_prompt()
dialogs.load_prompt()
dialogs.add_row()
with me.box(
style=me.Style(
background=me.theme_var("surface-container-lowest"),
display="grid",
grid_template_columns="50fr 50fr 1fr",
grid_template_rows="1fr 50fr",
height="100vh",
)
):
with me.box(style=me.Style(grid_column="1 / -1")):
with mex.header(max_width=None):
with mex.header_section():
with me.box(on_click=on_click_title, style=me.Style(cursor="pointer")):
me.text(
state.title,
style=me.Style(font_size=16, font_weight="bold"),
)
if state.version:
me.text(f"v{state.version}")
with mex.header_section():
me.button_toggle(
value=state.mode,
buttons=[
me.ButtonToggleButton(label="Prompt", value="Prompt"),
me.ButtonToggleButton(label="Eval", value="Eval"),
],
on_change=on_mode_toggle,
)
if state.mode == "Prompt":
# Render prompt creation page
with me.box(
style=me.Style(padding=me.Padding(left=15, top=15, bottom=15, right=2), overflow_y="scroll")
):
with me.accordion():
with me.expansion_panel(
title="System Instructions",
style=me.Style(background=me.theme_var("surface-container-lowest")),
):
me.native_textarea(
autosize=True,
min_rows=2,
placeholder="Optional tone and style instructions for the model",
value=state.system_instructions,
on_blur=handlers.on_update_input,
style=_STYLE_INVISIBLE_TEXTAREA,
key="system_instructions",
)
with me.expansion_panel(
title="Prompt",
expanded=True,
style=me.Style(background=me.theme_var("surface-container-lowest")),
):
me.native_textarea(
autosize=True,
min_rows=2,
placeholder="Enter your prompt",
value=state.prompt,
on_blur=on_update_prompt,
style=_STYLE_INVISIBLE_TEXTAREA,
key="prompt",
)
with me.box(
style=me.Style(
align_items="center",
display="flex",
justify_content="space-between",
margin=me.Margin(top=15),
)
):
with me.content_button(
type="flat",
disabled=not state.prompt,
on_click=on_click_run,
style=me.Style(border_radius="10"),
):
with me.tooltip(message="Run prompt"):
me.icon("play_arrow")
with me.box(style=me.Style(padding=me.Padding.all(15), overflow_y="scroll")):
if state.response:
with me.card(
appearance="raised", style=me.Style(background=me.theme_var("surface-container-lowest"))
):
me.card_header(title="Response")
with me.card_content():
mex.markdown(state.response, has_copy_to_clipboard=True)
else:
with me.card(
appearance="raised", style=me.Style(background=me.theme_var("surface-container-lowest"))
):
me.card_header(title="Prompt Tuner Instructions")
with me.card_content():
mex.markdown(_INSTRUCTIONS, has_copy_to_clipboard=True)
else:
# Render eval page
with me.box(style=me.Style(grid_column="1 / -2", overflow_y="scroll")):
prompt = find_prompt(state.prompts, state.version)
if prompt:
with me.box(style=me.Style(margin=me.Margin.all(15))):
compare_prompts = [
prompt for prompt in state.prompts if prompt.version in state.comparisons
]
prompt_eval_table(
[prompt] + compare_prompts,
on_select_rating=on_select_rating,
on_click_run=on_click_eval_run,
)
mex.button(
label="Add row",
type="flat",
style=me.Style(
margin=me.Margin(top=10),
),
key="dialog_show_add_row",
on_click=handlers.on_open_dialog,
)
tool_sidebar()
# Event handlers
def on_click_system_instructions_header(e: me.ClickEvent):
"""Open/close system instructions card."""
state = me.state(State)
state.system_prompt_card_expanded = not state.system_prompt_card_expanded
def on_click_eval_run(e: me.ClickEvent):
state = me.state(State)
_, prompt_version, response_index, selected_prompt_response_index = e.key.split("_")
prompt = find_prompt(state.prompts, int(prompt_version))
selected_prompt = find_prompt(state.prompts, state.version)
selected_prompt
if response_index != "-1":
response = prompt.responses[int(response_index)]
else:
response = {
"variables": copy.copy(
selected_prompt.responses[int(selected_prompt_response_index)]["variables"]
),
"rating": 0,
}
prompt.responses.append(response)
prompt_text = prompt.prompt
for name, value in response["variables"].items():
prompt_text = prompt_text.replace("{{" + name + "}}", value)
response["output"] = llm.run_prompt(
prompt_text, prompt.system_instructions, prompt.model, prompt.model_temperature
)
def on_click_run(e: me.ClickEvent):
"""Runs the prompt with the given variables.
A new version of the prompt will be created if the prompt, system instructions, or
model settings have changed.
A new response will be added if the variables have been updated.
"""
state = me.state(State)
num_versions = len(state.prompts)
if state.version:
current_prompt_meta = state.prompts[state.version - 1]
else:
current_prompt_meta = Prompt()
variable_names = set(parse_variables(state.prompt))
prompt_variables = {
name: value for name, value in state.prompt_variables.items() if name in variable_names
}
if (
current_prompt_meta.prompt != state.prompt
or current_prompt_meta.system_instructions != state.system_instructions
or current_prompt_meta.model != state.model
or current_prompt_meta.model_temperature != state.model_temperature
):
new_version = num_versions + 1
state.prompts.append(
Prompt(
version=new_version,
prompt=state.prompt,
system_instructions=state.system_instructions,
model=state.model,
model_temperature=state.model_temperature,
variables=list(variable_names),
)
)
state.version = new_version
prompt = state.prompt
for name, value in prompt_variables.items():
prompt = prompt.replace("{{" + name + "}}", value)
state.response = llm.run_prompt(
prompt, state.system_instructions, state.model, state.model_temperature
)
state.prompts[-1].responses.append(dict(output=state.response, variables=prompt_variables))
def on_click_title(e: me.ClickEvent):
"""Show dialog for editing the title of the prompt."""
state = me.state(State)
state.temp_title = state.title
state.dialog_show_title = True
def on_update_prompt(e: me.InputBlurEvent):
"""Saves the prompt.
Any new variables will be extracted from the prompt and added to prompt variables in
the variables dialog.
"""
state = me.state(State)
state.prompt = e.value.strip()
variable_names = parse_variables(state.prompt)
for variable_name in variable_names:
if variable_name not in state.prompt_variables:
state.prompt_variables[variable_name] = ""
def on_mode_toggle(e: me.ButtonToggleChangeEvent):
"""Toggle between Prompt and Eval modes."""
state = me.state(State)
state.mode = e.value
def on_select_rating(e: me.SelectSelectionChangeEvent):
state = me.state(State)
_, prompt_version, response_index = e.key.split("_")
prompt = find_prompt(state.prompts, int(prompt_version))
prompt.responses[int(response_index)]["rating"] = e.value
def on_async_action_finished(e: mel.WebEvent):
state = me.state(State)
state.async_action_name = ""
state.snackbar_message = ""
state.show_snackbar = False
# Style helpers
_STYLE_INVISIBLE_TEXTAREA = me.Style(
background=me.theme_var("surface-container-lowest"),
color=me.theme_var("on-surface"),
overflow_y="hidden",
width="100%",
outline="none",
border=me.Border.all(me.BorderSide(style="none")),
)