diff --git a/studio/frontend/src/features/recipe-studio/components/block-sheet.tsx b/studio/frontend/src/features/recipe-studio/components/block-sheet.tsx index f80f309f90..adc6512aab 100644 --- a/studio/frontend/src/features/recipe-studio/components/block-sheet.tsx +++ b/studio/frontend/src/features/recipe-studio/components/block-sheet.tsx @@ -15,6 +15,7 @@ import { type Database02Icon, DragDropVerticalIcon, PlusSignIcon, + Search01Icon, Tick02Icon, Upload01Icon, } from "@hugeicons/core-free-icons"; @@ -373,12 +374,18 @@ export function BlockSheet({ )} {sheetTitle} - setSearch(event.target.value)} - placeholder="Search blocks..." - className="corner-squircle mt-3 h-9" - /> +
+ + setSearch(event.target.value)} + placeholder="Search blocks..." + className="corner-squircle h-9 pl-8" + /> +
diff --git a/studio/frontend/src/features/recipe-studio/components/executions/execution-sidebar.tsx b/studio/frontend/src/features/recipe-studio/components/executions/execution-sidebar.tsx index aed43d75b4..75441daf09 100644 --- a/studio/frontend/src/features/recipe-studio/components/executions/execution-sidebar.tsx +++ b/studio/frontend/src/features/recipe-studio/components/executions/execution-sidebar.tsx @@ -2,7 +2,11 @@ import type { ReactElement } from "react"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import type { RecipeExecutionRecord } from "../../execution-types"; -import { isExecutionInProgress } from "../../executions/execution-helpers"; +import { + executionLabel, + isExecutionInProgress, + normalizeRunName, +} from "../../executions/execution-helpers"; import { formatStatus, formatTimestamp, @@ -34,43 +38,50 @@ export function ExecutionSidebar({ No executions yet.
) : ( - executions.map((execution) => ( - - )) + > +
+

+ {title} +

+ + {formatStatus(execution.status)} + +
+

{execution.rows} rows

+ {isExecutionInProgress(execution.status) && + typeof execution.batch?.total === "number" && + execution.batch.total > 1 && ( +

+ Batch {execution.batch.idx ?? "--"}/{execution.batch.total} +

+ )} +

+ {formatTimestamp(execution.createdAt)} +

+ + ); + }) )}
diff --git a/studio/frontend/src/features/recipe-studio/dialogs/llm/scores-tab.tsx b/studio/frontend/src/features/recipe-studio/dialogs/llm/scores-tab.tsx index de1ff51dda..f83d40161f 100644 --- a/studio/frontend/src/features/recipe-studio/dialogs/llm/scores-tab.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/llm/scores-tab.tsx @@ -1,4 +1,11 @@ import { Button } from "@/components/ui/button"; +import { + Empty, + EmptyContent, + EmptyDescription, + EmptyHeader, + EmptyTitle, +} from "@/components/ui/empty"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { type ReactElement } from "react"; @@ -91,14 +98,26 @@ export function LlmScoresTab({ label="Scorers" hint="Rubrics used by LLM Judge to score each generated row." /> - + {scores.length > 0 && ( + + )} {scores.length === 0 && ( -

- Add at least one scorer. -

+ + + No scorers yet + + Add a scorer rubric before running judge generation. + + + + + + )} {scores.map((score, index) => (
onUpdate({ name: value })} /> diff --git a/studio/frontend/src/features/recipe-studio/dialogs/models/model-provider-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/models/model-provider-dialog.tsx index 589c33b6f1..e50beb28c3 100644 --- a/studio/frontend/src/features/recipe-studio/dialogs/models/model-provider-dialog.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/models/model-provider-dialog.tsx @@ -35,6 +35,7 @@ export function ModelProviderDialog({ return (
onUpdate({ name: value })} /> diff --git a/studio/frontend/src/features/recipe-studio/dialogs/preview-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/preview-dialog.tsx index 5ab40d80a5..cb646876f0 100644 --- a/studio/frontend/src/features/recipe-studio/dialogs/preview-dialog.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/preview-dialog.tsx @@ -26,6 +26,8 @@ type RunDialogProps = { kind: RecipeExecutionKind; onKindChange: (kind: RecipeExecutionKind) => void; rows: number; + fullRunName: string; + onFullRunNameChange: (name: string) => void; onRowsChange: (rows: number) => void; settings: RecipeRunSettings; onSettingsChange: (patch: Partial) => void; @@ -207,6 +209,8 @@ export function RunDialog({ kind, onKindChange, rows, + fullRunName, + onFullRunNameChange, onRowsChange, settings, onSettingsChange, @@ -304,6 +308,23 @@ export function RunDialog({ />
+ {kind === "full" && ( +
+ + onFullRunNameChange(event.target.value)} + placeholder="Sprint dataset v2" + /> +
+ )} +
diff --git a/studio/frontend/src/features/recipe-studio/dialogs/seed/seed-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/seed/seed-dialog.tsx index 5f73e3543e..94e292c479 100644 --- a/studio/frontend/src/features/recipe-studio/dialogs/seed/seed-dialog.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/seed/seed-dialog.tsx @@ -102,6 +102,29 @@ function truncatePreviewValue(value: string): string { return `${value.slice(0, PREVIEW_TRUNCATE_AT)}…`; } +function getPreviewEmptyStateCopy(mode: SeedConfig["seed_source_type"]): { + title: string; + description: string; +} { + if (mode === "local") { + return { + title: "No local preview yet", + description: "Choose a CSV/JSON/JSONL file, then click Load to fetch 10 rows.", + }; + } + if (mode === "unstructured") { + return { + title: "No chunk preview yet", + description: + "Choose a TXT/PDF/DOCX file, then click Load to extract + preview chunk_text rows.", + }; + } + return { + title: "No dataset preview yet", + description: "Pick a Hugging Face dataset and click Load to fetch 10 sample rows.", + }; +} + function parseChunkNumber( value: string | undefined, fallback: number, @@ -196,6 +219,7 @@ export function SeedDialog({ config, onUpdate, open }: SeedDialogProps): ReactEl const [unstructuredFile, setUnstructuredFile] = useState(null); const mode = config.seed_source_type ?? "hf"; + const previewEmpty = getPreviewEmptyStateCopy(mode); useEffect(() => { setInspectError(null); @@ -753,12 +777,14 @@ export function SeedDialog({ config, onUpdate, open }: SeedDialogProps): ReactEl
- Seed preview + {previewEmpty.title} - Use the load button next to the source input to fetch 10 rows. + {previewEmpty.description} - + + Preview appears here after loading source metadata. +
) : ( diff --git a/studio/frontend/src/features/recipe-studio/dialogs/shared/name-field.tsx b/studio/frontend/src/features/recipe-studio/dialogs/shared/name-field.tsx index 533499d541..bd2212f79f 100644 --- a/studio/frontend/src/features/recipe-studio/dialogs/shared/name-field.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/shared/name-field.tsx @@ -6,6 +6,7 @@ type NameFieldProps = { id?: string; value: string; onChange: (value: string) => void; + label?: string; hint?: string; }; @@ -13,6 +14,7 @@ export function NameField({ id, value, onChange, + label, hint, }: NameFieldProps): ReactElement { const fallbackId = useId(); @@ -20,7 +22,7 @@ export function NameField({ return (
0 ? trimmed : null; +} + function executionSortWeight(status: RecipeExecutionStatus): number { if (isExecutionInProgress(status)) { return 0; @@ -133,6 +141,7 @@ export function withExecutionDefaults( return { ...record, + run_name: normalizeRunName(record.run_name), dataset, log_lines: logLines, datasetTotal, diff --git a/studio/frontend/src/features/recipe-studio/executions/runtime.ts b/studio/frontend/src/features/recipe-studio/executions/runtime.ts index fe86499803..e9f6a22002 100644 --- a/studio/frontend/src/features/recipe-studio/executions/runtime.ts +++ b/studio/frontend/src/features/recipe-studio/executions/runtime.ts @@ -114,6 +114,7 @@ export function createBaseExecutionRecord(input: { kind: RecipeExecutionKind; rows: number; currentSignature: string; + runName?: string | null; }): RecipeExecutionRecord { const createdAt = Date.now(); return { @@ -121,6 +122,7 @@ export function createBaseExecutionRecord(input: { recipeId: input.recipeId, jobId: null, kind: input.kind, + run_name: input.runName ?? null, status: "pending", rows: input.rows, createdAt, diff --git a/studio/frontend/src/features/recipe-studio/hooks/use-recipe-executions.ts b/studio/frontend/src/features/recipe-studio/hooks/use-recipe-executions.ts index 25f6d1337a..f597177da0 100644 --- a/studio/frontend/src/features/recipe-studio/hooks/use-recipe-executions.ts +++ b/studio/frontend/src/features/recipe-studio/hooks/use-recipe-executions.ts @@ -15,6 +15,7 @@ import type { import { DATASET_PAGE_SIZE, executionLabel, + normalizeRunName, normalizeDatasetRows, toErrorMessage, withExecutionDefaults, @@ -50,8 +51,10 @@ type UseRecipeExecutionsResult = { setRunDialogOpen: (open: boolean) => void; previewRows: number; fullRows: number; + fullRunName: string; setPreviewRows: (rows: number) => void; setFullRows: (rows: number) => void; + setFullRunName: (name: string) => void; runErrors: string[]; runSettings: RecipeRunSettings; setRunSettings: (patch: Partial) => void; @@ -109,6 +112,7 @@ export function useRecipeExecutions({ runDialogKind, previewRows, fullRows, + fullRunName, runErrors, runSettings, previewLoading, @@ -119,6 +123,7 @@ export function useRecipeExecutions({ setRunDialogKind, setPreviewRows, setFullRows, + setFullRunName, setRunErrors, setRunSettings, setPreviewLoading, @@ -133,6 +138,7 @@ export function useRecipeExecutions({ runDialogKind: state.runDialogKind, previewRows: state.previewRows, fullRows: state.fullRows, + fullRunName: state.fullRunName, runErrors: state.runErrors, runSettings: state.runSettings, previewLoading: state.previewLoading, @@ -143,6 +149,7 @@ export function useRecipeExecutions({ setRunDialogKind: state.setRunDialogKind, setPreviewRows: state.setPreviewRows, setFullRows: state.setFullRows, + setFullRunName: state.setFullRunName, setRunErrors: state.setRunErrors, setRunSettings: state.setRunSettings, setPreviewLoading: state.setPreviewLoading, @@ -238,10 +245,12 @@ export function useRecipeExecutions({ payload: RecipePayload; rows: number; settings: RecipeRunSettings; + runName: string | null; }): Promise => { - const { kind, payload, rows, settings } = input; + const { kind, payload, rows, settings, runName } = input; const setLoading = kind === "preview" ? setPreviewLoading : setFullLoading; const label = executionLabel(kind); + const normalizedRunName = kind === "full" ? normalizeRunName(runName) : null; setLoading(true); const baseExecution = createBaseExecutionRecord({ @@ -249,6 +258,7 @@ export function useRecipeExecutions({ kind, rows, currentSignature, + runName: normalizedRunName, }); upsertAndPersist(baseExecution); @@ -309,7 +319,11 @@ export function useRecipeExecutions({ ); const runWithValidation = useCallback( - async (kind: RecipeExecutionKind, rows: number): Promise => { + async ( + kind: RecipeExecutionKind, + rows: number, + runName: string | null, + ): Promise => { const payload = readExecutablePayload(); if (!payload) { return false; @@ -345,18 +359,19 @@ export function useRecipeExecutions({ payload, rows: normalizedRows, settings: runSettings, + runName, }); }, [readExecutablePayload, runExecution, runSettings, setRunErrors], ); const runPreview = useCallback(async (): Promise => { - return runWithValidation("preview", previewRows); + return runWithValidation("preview", previewRows, null); }, [previewRows, runWithValidation]); const runFull = useCallback(async (): Promise => { - return runWithValidation("full", fullRows); - }, [fullRows, runWithValidation]); + return runWithValidation("full", fullRows, fullRunName); + }, [fullRows, fullRunName, runWithValidation]); const runFromDialog = useCallback(async (): Promise => { setValidateResult(null); @@ -509,8 +524,10 @@ export function useRecipeExecutions({ setRunDialogOpen, previewRows, fullRows, + fullRunName, setPreviewRows, setFullRows, + setFullRunName, runErrors, runSettings, setRunSettings, diff --git a/studio/frontend/src/features/recipe-studio/hooks/use-recipe-studio-actions.ts b/studio/frontend/src/features/recipe-studio/hooks/use-recipe-studio-actions.ts index bb7531233b..f2e8331c5c 100644 --- a/studio/frontend/src/features/recipe-studio/hooks/use-recipe-studio-actions.ts +++ b/studio/frontend/src/features/recipe-studio/hooks/use-recipe-studio-actions.ts @@ -48,8 +48,10 @@ type UseRecipeStudioActionsResult = { setRunDialogOpen: (open: boolean) => void; previewRows: number; fullRows: number; + fullRunName: string; setPreviewRows: (rows: number) => void; setFullRows: (rows: number) => void; + setFullRunName: (name: string) => void; runErrors: string[]; runSettings: RecipeRunSettings; setRunSettings: (patch: Partial) => void; @@ -125,8 +127,10 @@ export function useRecipeStudioActions({ setRunDialogOpen: executions.setRunDialogOpen, previewRows: executions.previewRows, fullRows: executions.fullRows, + fullRunName: executions.fullRunName, setPreviewRows: executions.setPreviewRows, setFullRows: executions.setFullRows, + setFullRunName: executions.setFullRunName, runErrors: executions.runErrors, runSettings: executions.runSettings, setRunSettings: executions.setRunSettings, diff --git a/studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx b/studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx index dc5582b656..7ea8f8c7c5 100644 --- a/studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx +++ b/studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx @@ -261,8 +261,10 @@ export function RecipeStudioPage({ setRunDialogOpen, previewRows, fullRows, + fullRunName, setPreviewRows, setFullRows, + setFullRunName, runErrors, runSettings, setRunSettings, @@ -652,6 +654,8 @@ export function RecipeStudioPage({ kind={runDialogKind} onKindChange={setRunDialogKind} rows={runDialogRows} + fullRunName={fullRunName} + onFullRunNameChange={setFullRunName} onRowsChange={(rows) => { if (runDialogKind === "preview") { setPreviewRows(rows); diff --git a/studio/frontend/src/features/recipe-studio/stores/recipe-executions.ts b/studio/frontend/src/features/recipe-studio/stores/recipe-executions.ts index 3d0d778485..9ed37e1415 100644 --- a/studio/frontend/src/features/recipe-studio/stores/recipe-executions.ts +++ b/studio/frontend/src/features/recipe-studio/stores/recipe-executions.ts @@ -34,6 +34,7 @@ type RecipeExecutionsState = { runDialogKind: RecipeExecutionKind; previewRows: number; fullRows: number; + fullRunName: string; runErrors: string[]; runSettings: RecipeRunSettings; previewLoading: boolean; @@ -44,6 +45,7 @@ type RecipeExecutionsState = { setRunDialogKind: (kind: RecipeExecutionKind) => void; setPreviewRows: (rows: number) => void; setFullRows: (rows: number) => void; + setFullRunName: (name: string) => void; setRunErrors: (errors: string[]) => void; setRunSettings: (patch: Partial) => void; setPreviewLoading: (loading: boolean) => void; @@ -59,6 +61,7 @@ const INITIAL_STATE = { runDialogKind: "preview", previewRows: 5, fullRows: 1000, + fullRunName: "", runErrors: [], runSettings: DEFAULT_RUN_SETTINGS, previewLoading: false, @@ -71,6 +74,7 @@ const INITIAL_STATE = { | "runDialogKind" | "previewRows" | "fullRows" + | "fullRunName" | "runErrors" | "runSettings" | "previewLoading" @@ -87,6 +91,7 @@ export const useRecipeExecutionsStore = create((set) => ( set({ previewRows: Number.isFinite(rows) && rows > 0 ? Math.floor(rows) : 1 }), setFullRows: (rows) => set({ fullRows: Number.isFinite(rows) && rows > 0 ? Math.floor(rows) : 1 }), + setFullRunName: (name) => set({ fullRunName: name }), setRunErrors: (errors) => set({ runErrors: errors }), setRunSettings: (patch) => set((state) => ({ diff --git a/studio/frontend/src/features/recipe-studio/utils/config-factories.ts b/studio/frontend/src/features/recipe-studio/utils/config-factories.ts index f22cde4264..67de6dc7e4 100644 --- a/studio/frontend/src/features/recipe-studio/utils/config-factories.ts +++ b/studio/frontend/src/features/recipe-studio/utils/config-factories.ts @@ -216,20 +216,7 @@ export function makeLlmConfig( with_trace: "none", // biome-ignore lint/style/useNamingConvention: api schema extract_reasoning_content: false, - scores: - llmType === "judge" - ? [ - { - name: "Quality", - description: "Overall quality based on the criteria.", - options: [ - { value: "1", description: "Poor" }, - { value: "3", description: "Acceptable" }, - { value: "5", description: "Excellent" }, - ], - }, - ] - : undefined, + scores: llmType === "judge" ? [] : undefined, }; } @@ -268,7 +255,7 @@ export function makeModelConfig( // biome-ignore lint/style/useNamingConvention: api schema inference_temperature: "0.7", // biome-ignore lint/style/useNamingConvention: api schema - inference_max_tokens: "256", + inference_max_tokens: "", // biome-ignore lint/style/useNamingConvention: api schema inference_top_p: "", // biome-ignore lint/style/useNamingConvention: api schema