feat: evals open as a dialog over the agent editor page

The editor page opens an agent's evals over itself, as the agent page does, with the unsaved
edits offered to a run; the editor dialog keeps evals as a level of its own. The two pages share
the dialog.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-09-30 12:42:22 +02:00
co-authored by Claude Opus 5.5
parent 954d8b0212
commit 0fd5973b12
3 changed files with 73 additions and 34 deletions
@@ -37,6 +37,7 @@
import { linkedModulesForAgent, linkedToolsScope } from '../linkedAgentToolsStore.svelte'
import AgentEditorHost from './AgentEditorHost.svelte'
import AgentSettings from './AgentSettings.svelte'
import AgentEvalsModal from './AgentEvalsModal.svelte'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
const operatingWorkspace = useOperatingWorkspace()
@@ -92,6 +93,7 @@
let host = $state<ReturnType<typeof AgentEditorHost> | undefined>(undefined)
let versionDrawer: Drawer | undefined = $state(undefined)
let settingsDrawer: Drawer | undefined = $state(undefined)
let evalsModal: AgentEvalsModal | undefined = $state(undefined)
let saving = $state(false)
// Counted per agent, so a deploy that leaves the editor on the same agent still refetches the
@@ -163,6 +165,12 @@
// belongs to that level rather than to the dialog's own name.
let description = $derived(inEvals || inSettings || refused ? undefined : AGENT_DESCRIPTION)
// The dialog's levels are its pages; a page has none, so evals open over it as a dialog.
function openEvals() {
if (layout === 'modal') showAgentEditorView('evals')
else evalsModal?.openModal()
}
function openSettings() {
if (layout === 'modal') showAgentEditorView('settings')
else settingsDrawer?.openDrawer()
@@ -410,7 +418,7 @@
variant="default"
startIcon={{ icon: FlaskConical }}
title="Run this agent against a dataset of cases"
on:click={() => showAgentEditorView('evals')}
on:click={openEvals}
>
Evals
</Button>
@@ -507,6 +515,15 @@
/>
{/snippet}
{#if layout === 'page' && ws}
<AgentEvalsModal
bind:this={evalsModal}
agentPath={target?.path ?? ''}
workspace={ws}
editedConfig={draft?.sync.hasDraft ? editedConfig : undefined}
/>
{/if}
<Drawer bind:this={settingsDrawer} size="600px">
<DrawerContent title="Settings" on:close={() => settingsDrawer?.closeDrawer()}>
{@render settingsFields()}
@@ -0,0 +1,49 @@
<script lang="ts">
import { Badge } from '$lib/components/common'
import Modal from '$lib/components/common/modal/Modal.svelte'
import EvalsPane from '$lib/components/aiEvals/EvalsPane.svelte'
import type { EvalsLocation } from '$lib/components/aiEvals/evalUtils'
import type { AgentDraft } from '$lib/gen'
/** An agent's evals in a dialog of their own, over the page that opened them. */
interface Props {
agentPath: string
workspace: string
/** The unsaved edits, offered to a run as an alternative to the deployed version. */
editedConfig?: () => AgentDraft
}
let { agentPath, workspace, editedConfig = undefined }: Props = $props()
let open = $state(false)
// Where the evals pane is within itself, so its levels extend the dialog's trail. Cleared on the
// way in: the pane reports a level once it is on one, and never that it is back at its root.
let location = $state<EvalsLocation | undefined>(undefined)
export function openModal() {
location = undefined
open = true
}
</script>
{#if open}
<Modal
bind:open
kind="X"
fillHeight
enterConfirms={false}
title="Evals"
trail={[
{ label: 'Evals', onclick: location ? location.back : undefined },
...(location ? [{ label: location.label }] : [])
]}
class="w-[92vw] sm:w-[92vw] max-w-[1500px] sm:max-w-[1500px] h-[88vh]"
>
{#snippet titleBadge()}
{#if !location}
<Badge color="blue" small class="shrink-0 !py-0 leading-4">Beta</Badge>
{/if}
{/snippet}
<EvalsPane {agentPath} opWorkspace={workspace} {editedConfig} bind:location active={open} />
</Modal>
{/if}
@@ -5,10 +5,8 @@
import { base } from '$lib/base'
import { goto } from '$lib/navigation'
import { copilotInfo } from '$lib/aiStore'
import { Badge, Button } from '$lib/components/common'
import Modal from '$lib/components/common/modal/Modal.svelte'
import EvalsPane from '$lib/components/aiEvals/EvalsPane.svelte'
import type { EvalsLocation } from '$lib/components/aiEvals/evalUtils'
import { Button } from '$lib/components/common'
import AgentEvalsModal from '$lib/components/flows/content/AgentEvalsModal.svelte'
import DropdownV2 from '$lib/components/DropdownV2.svelte'
import SummaryPathDisplay from '$lib/components/SummaryPathDisplay.svelte'
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
@@ -44,14 +42,7 @@
let shareModal: ShareModal | undefined = $state(undefined)
let deploymentDrawer: DeployWorkspaceDrawer | undefined = $state(undefined)
let deleteOpen = $state(false)
let evalsOpen = $state(false)
// Where the evals pane is within itself, so its levels extend the dialog's trail. Cleared on the
// way in: the pane reports a level once it is on one, and never that it is back at its root.
let evalsLocation = $state<EvalsLocation | undefined>(undefined)
function openEvals() {
evalsLocation = undefined
evalsOpen = true
}
let evalsModal: AgentEvalsModal | undefined = $state(undefined)
async function deleteAgent() {
if (!ws) return
@@ -131,7 +122,7 @@
unifiedSize="md"
startIcon={{ icon: FlaskConical }}
title="Run this agent against a dataset of cases"
onClick={openEvals}
onClick={() => evalsModal?.openModal()}
>
Evals
</Button>
@@ -204,26 +195,8 @@
</div>
</main>
{#if evalsOpen && ws}
<Modal
bind:open={evalsOpen}
kind="X"
fillHeight
enterConfirms={false}
title="Evals"
trail={[
{ label: 'Evals', onclick: evalsLocation ? evalsLocation.back : undefined },
...(evalsLocation ? [{ label: evalsLocation.label }] : [])
]}
class="w-[92vw] sm:w-[92vw] max-w-[1500px] sm:max-w-[1500px] h-[88vh]"
>
{#snippet titleBadge()}
{#if !evalsLocation}
<Badge color="blue" small class="shrink-0 !py-0 leading-4">Beta</Badge>
{/if}
{/snippet}
<EvalsPane agentPath={path} opWorkspace={ws} bind:location={evalsLocation} active={evalsOpen} />
</Modal>
{#if ws}
<AgentEvalsModal bind:this={evalsModal} agentPath={path} workspace={ws} />
{/if}
{#if config}