Skip to content

Commit

Permalink
create new trial on rollback and replay in studio
Browse files Browse the repository at this point in the history
- Add a new Modal component
  • Loading branch information
rasca committed Nov 14, 2024
1 parent 5f080d4 commit 488be84
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 69 deletions.
2 changes: 1 addition & 1 deletion flou/flou/engine/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ async def get_ltm(
data["errors"] = session.scalars(select(Error).where(Error.ltm_id == ltm_id)).all()

# Check if any trials reference this LTM and get experiment ID
current_trial = session.scalar(select(Trial).where(Trial.ltm_id == ltm_id).limit(1))
current_trial = session.scalar(select(Trial).where(Trial.ltm_id == ltm_id).order_by(Trial.created_at.desc()).limit(1))
if current_trial:
data["experiment_id"] = current_trial.experiment_id
data["current_trial"] = current_trial
Expand Down
2 changes: 2 additions & 0 deletions studio/src/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ form label:not(fieldset.radio > label) textarea {
border-radius: var(--8, 0.5rem);
border: none;
width: 100%;
background: var(--white-80);
color: var(--black-100);
}

form label:not(fieldset.radio > label) input:focus,
Expand Down
134 changes: 134 additions & 0 deletions studio/src/lib/Components/NewTrialFromRollback.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<script lang="ts">
import Modal from '$lib/UI/Modal.svelte';
import { Plus, TestTube } from 'phosphor-svelte';
import SnapshotItem from '$lib/Components/SnapshotItem.svelte';
import { superForm, defaults } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { z } from 'zod';
import { createEventDispatcher } from 'svelte';
let dispatch = createEventDispatcher();
export const _newTrialSchema = z.object({
name: z.string().optional(),
previous_trial_outputs: z.string().optional(),
outputs: z.string().optional()
});
export let showTrialModal = false;
export let selectedAction: 'rollback' | 'replay' = 'rollback';
export let selectedSnapshotIndex: number;
export let ltm: any = {};
const { form, errors, message, constraints, enhance } = superForm(
defaults(zod(_newTrialSchema)),
{
SPA: true,
dataType: 'json',
validators: zod(_newTrialSchema),
onUpdate({ form }) {
if (form.valid) {
showTrialModal = false;
dispatch('newTrial', {
snapshotIndex: selectedSnapshotIndex,
replay: selectedAction === 'replay',
newTrialData: form.data
});
}
}
}
);
</script>

<Modal
bind:show={showTrialModal}
title={`New Trial`}
icon={Plus}
on:close={() => (showTrialModal = false)}
>
<div class="modal-header">
<div class="icon-container">
<TestTube size="2.5rem" />
</div>
<div>
{#if selectedAction === 'rollback'}
Rollback
{:else}
Replay
{/if}
from snapshot #{selectedSnapshotIndex}
<div class="snapshot-item">
{#if ltm.snapshots[selectedSnapshotIndex]}
<SnapshotItem item={ltm.snapshots[selectedSnapshotIndex].item}></SnapshotItem>
{/if}
</div>
</div>
</div>
{#if $form}
<form use:enhance>
<label>
Name
<input
aria-invalid={$errors.name ? 'true' : undefined}
type="text"
bind:value={$form.name}
{...$constraints.name}
placeholder={`${ltm.current_trial?.name} #${ltm.current_trial?.index + 1}`}
/>
</label>
<label>
Previous trial results
<textarea
aria-invalid={$errors.previous_trial_outputs ? 'true' : undefined}
bind:value={$form.previous_trial_outputs}
{...$constraints.previous_trial_outputs}
/>
</label>
<label>
New trial description
<textarea
aria-invalid={$errors.outputs ? 'true' : undefined}
bind:value={$form.outputs}
{...$constraints.outputs}
/>
</label>
<div class="buttons full-width">
<button
type="button"
class="button large secondary"
on:click={() => (showTrialModal = false)}
>
Cancel
</button>
<button type="submit" class="button primary large"> Create Trial </button>
</div>
</form>
{/if}
</Modal>

<style lang="scss">
.icon-container {
padding: 1.25rem;
border-radius: 100%;
background-color: var(--black-5);
max-width: fit-content;
aspect-ratio: 1 / 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--black-100);
}
.modal-header {
display: flex;
align-items: center;
gap: 1rem;
color: var(--black-100);
}
.snapshot-item {
margin-top: 0.5rem;
font-size: 0.875rem;
overflow: auto;
text-overflow: ellipsis;
}
</style>
112 changes: 57 additions & 55 deletions studio/src/lib/Components/Rollbacks.svelte
Original file line number Diff line number Diff line change
@@ -1,72 +1,74 @@
<script lang="ts">
import { ClockCounterClockwise, Lifebuoy } from 'phosphor-svelte';
import { ClockCounterClockwise, Lifebuoy } from 'phosphor-svelte';
import { createEventDispatcher } from 'svelte';
import Paginator from '$lib/UI/Paginator.svelte';
import Paginator from '$lib/UI/Paginator.svelte';
import SnapshotItem from '$lib/Components/SnapshotItem.svelte';
import { formatDate } from '$lib/utils';
import { PUBLIC_API_BASE_URL } from '$env/static/public';
let dispatch = createEventDispatcher();
export let ltm: any;
export let ltm: any;
export let ltmId;
let index = 0;
const ltmUrl = `${PUBLIC_API_BASE_URL}ltm/${ltmId}`;
const ltmUrl = `${PUBLIC_API_BASE_URL}ltm/${ltmId}`;
let recoverRollback = async (snapshot_index: number) => {
let postData = {
index: snapshot_index,
};
await fetch(`${ltmUrl}/recover-rollback`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
})
.then((response) => {
let recoverRollback = async (snapshot_index: number) => {
let postData = {
index: snapshot_index,
};
await fetch(`${ltmUrl}/recover-rollback`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
})
.then((response) => {
dispatch('reloadLtm');
return response.json();
})
.then((data) => {})
.catch((error) => {
console.log(error);
});
};
return response.json();
})
.then((data) => {})
.catch((error) => {
console.log(error);
});
};
</script>

<h3><ClockCounterClockwise size="1.25rem" />Rollbacks</h3>
<table>
<tr>
<th>Reason</th>
<th>#Snapshots</th>
<th>Last Snapshot</th>
<th>Time</th>
<th></th>
</tr>
{#each ltm.rollbacks as rollback, i}
<table>
<tr>
<th>Reason</th>
<th>#Snapshots</th>
<th>Last Snapshot</th>
<th>Time</th>
<th></th>
</tr>
{#each ltm.rollbacks as rollback, i}
{@const lastSnapshot = rollback.snapshots.at(-1)}
<tr
class:current={i === index}
on:click={() => index = i}
on:keydown={() => index = i}
role="button"
>
<td title={rollback.reason}>{rollback.reason}</td>
<td title={rollback.snapshots.length}>{JSON.stringify(rollback.snapshots.length)}</td>
<td title={`${lastSnapshot.reason}: ${lastSnapshot.name}`}>{lastSnapshot.reason}: {lastSnapshot.name}</td>
<td title={rollback.time}>{rollback.time}</td>
<td>
<div class="snapshot-controls">
<button
on:click={() => {
recoverRollback(i);
}}
title="Recover rollback"
>
<Lifebuoy size="1.25rem" />
</button>
<tr
class:current={i === index}
on:click={() => index = i}
on:keydown={() => index = i}
role="button"
>
<td title={rollback.reason}>{rollback.reason}</td>
<td title={rollback.snapshots.length}>{rollback.snapshots.length}</td>
<td title={`${lastSnapshot.reason}`}>{lastSnapshot.reason}: <SnapshotItem item={lastSnapshot.item} />
<td title={formatDate(rollback.time)}>{formatDate(rollback.time)}</td>
<td>
<div class="snapshot-controls">
<button
on:click={() => {
recoverRollback(i);
}}
title="Recover rollback"
>
<Lifebuoy size="1.25rem" />
</button>
</td>
</tr>{/each}
</table>
<Paginator bind:index={index} collection={ltm.rollbacks} />
</tr>{/each}
</table>
<Paginator bind:index={index} collection={ltm.rollbacks} />
Loading

0 comments on commit 488be84

Please sign in to comment.