diff --git a/studio/frontend/.gitignore b/studio/frontend/.gitignore index 7358cc82ad..d87ba2c95d 100644 --- a/studio/frontend/.gitignore +++ b/studio/frontend/.gitignore @@ -26,4 +26,4 @@ test/ *.njsproj *.sln *.sw? -/src/features/canvas-lab/AGENTS.md +/src/features/recipe-studio/AGENTS.md diff --git a/studio/frontend/src/app/router.tsx b/studio/frontend/src/app/router.tsx index 2376127293..7aa19d38ba 100644 --- a/studio/frontend/src/app/router.tsx +++ b/studio/frontend/src/app/router.tsx @@ -1,6 +1,6 @@ import { createRouter } from "@tanstack/react-router"; import { Route as rootRoute } from "./routes/__root"; -import { Route as canvasLabRoute } from "./routes/canvas-lab"; +import { Route as dataRecipesNewRoute } from "./routes/data-recipes-new"; import { Route as chatRoute } from "./routes/chat"; import { Route as exportRoute } from "./routes/export"; import { Route as gridTestRoute } from "./routes/grid-test"; @@ -19,7 +19,7 @@ const routeTree = rootRoute.addChildren([ studioRoute, chatRoute, exportRoute, - canvasLabRoute, + dataRecipesNewRoute, ]); export const router = createRouter({ routeTree }); diff --git a/studio/frontend/src/app/routes/canvas-lab.tsx b/studio/frontend/src/app/routes/data-recipes-new.tsx similarity index 54% rename from studio/frontend/src/app/routes/canvas-lab.tsx rename to studio/frontend/src/app/routes/data-recipes-new.tsx index 29a7252b1d..b4b884b15e 100644 --- a/studio/frontend/src/app/routes/canvas-lab.tsx +++ b/studio/frontend/src/app/routes/data-recipes-new.tsx @@ -2,14 +2,14 @@ import { createRoute } from "@tanstack/react-router"; import { lazy } from "react"; import { Route as rootRoute } from "./__root"; -const CanvasLabPage = lazy(() => - import("@/features/canvas-lab").then((m) => ({ - default: m.CanvasLabPage, +const RecipeStudioPage = lazy(() => + import("@/features/recipe-studio").then((m) => ({ + default: m.RecipeStudioPage, })), ); export const Route = createRoute({ getParentRoute: () => rootRoute, - path: "/canvas-lab", - component: CanvasLabPage, + path: "/data-recipes/new", + component: RecipeStudioPage, }); diff --git a/studio/frontend/src/features/canvas-lab/index.ts b/studio/frontend/src/features/canvas-lab/index.ts deleted file mode 100644 index fa458f3d8b..0000000000 --- a/studio/frontend/src/features/canvas-lab/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { CanvasLabPage } from "./canvas-lab-page"; diff --git a/studio/frontend/src/features/canvas-lab/utils/graph.ts b/studio/frontend/src/features/canvas-lab/utils/graph.ts deleted file mode 100644 index 66b2454701..0000000000 --- a/studio/frontend/src/features/canvas-lab/utils/graph.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - applyCanvasConnection, - isValidCanvasConnection, -} from "./graph/canvas-connection"; diff --git a/studio/frontend/src/features/canvas-lab/utils/import/index.ts b/studio/frontend/src/features/canvas-lab/utils/import/index.ts deleted file mode 100644 index c1ca6bac0b..0000000000 --- a/studio/frontend/src/features/canvas-lab/utils/import/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { importCanvasPayload } from "./importer"; -export type { CanvasSnapshot, ImportResult } from "./types"; diff --git a/studio/frontend/src/features/canvas-lab/utils/payload/index.ts b/studio/frontend/src/features/canvas-lab/utils/payload/index.ts deleted file mode 100644 index c584f4dc10..0000000000 --- a/studio/frontend/src/features/canvas-lab/utils/payload/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { buildCanvasPayload } from "./build-payload"; -export type { CanvasPayload, CanvasPayloadResult } from "./types"; diff --git a/studio/frontend/src/features/canvas-lab/api/index.ts b/studio/frontend/src/features/recipe-studio/api/index.ts similarity index 73% rename from studio/frontend/src/features/canvas-lab/api/index.ts rename to studio/frontend/src/features/recipe-studio/api/index.ts index ea4890ba57..47bc496a1f 100644 --- a/studio/frontend/src/features/canvas-lab/api/index.ts +++ b/studio/frontend/src/features/recipe-studio/api/index.ts @@ -1,6 +1,6 @@ -const DEFAULT_BASE = "/preview-api"; +const DEFAULT_BASE = ""; -export const CANVAS_LAB_API_BASE = +export const DATA_DESIGNER_API_BASE = import.meta.env.VITE_DATA_DESIGNER_API ?? DEFAULT_BASE; type PreviewResponse = { @@ -8,10 +8,10 @@ type PreviewResponse = { processorArtifacts?: Record; }; -export async function previewCanvas( +export async function previewRecipe( payload: unknown, ): Promise { - const response = await fetch(`${CANVAS_LAB_API_BASE}/preview`, { + const response = await fetch(`${DATA_DESIGNER_API_BASE}/preview`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/studio/frontend/src/features/canvas-lab/blocks/registry.tsx b/studio/frontend/src/features/recipe-studio/blocks/registry.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/blocks/registry.tsx rename to studio/frontend/src/features/recipe-studio/blocks/registry.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/block-sheet.tsx b/studio/frontend/src/features/recipe-studio/components/block-sheet.tsx similarity index 86% rename from studio/frontend/src/features/canvas-lab/components/block-sheet.tsx rename to studio/frontend/src/features/recipe-studio/components/block-sheet.tsx index bb27ba4d5d..4c2e212531 100644 --- a/studio/frontend/src/features/canvas-lab/components/block-sheet.tsx +++ b/studio/frontend/src/features/recipe-studio/components/block-sheet.tsx @@ -18,7 +18,7 @@ import { } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import type { ReactElement } from "react"; -import { CANVAS_FLOATING_ICON_BUTTON_CLASS } from "./floating-icon-button-class"; +import { RECIPE_FLOATING_ICON_BUTTON_CLASS } from "./recipe-floating-icon-button-class"; import type { LlmType, SamplerType } from "../types"; import { BLOCK_GROUPS, getBlocksForKind } from "../blocks/registry"; @@ -34,8 +34,8 @@ type RootGroup = { type BlockSheetProps = { container: HTMLDivElement | null; - view: SheetView; - onViewChange: (view: SheetView) => void; + sheetView: SheetView; + onViewChange: (sheetView: SheetView) => void; onAddSampler: (type: SamplerType) => void; onAddLlm: (type: LlmType) => void; onAddModelProvider: () => void; @@ -47,17 +47,17 @@ type BlockSheetProps = { onImport: () => void; }; -function getSheetTitle(view: SheetView): string { - if (view === "root") { +function getSheetTitle(sheetView: SheetView): string { + if (sheetView === "root") { return "Add a block"; } - if (view === "sampler") { + if (sheetView === "sampler") { return "Sampler blocks"; } - if (view === "expression") { + if (sheetView === "expression") { return "Expression blocks"; } - if (view === "processor") { + if (sheetView === "processor") { return "Processor blocks"; } return "LLM blocks"; @@ -98,7 +98,7 @@ function BlockSheetButton({ )} - {title} + {sheetTitle}
- {view === "root" && + {sheetView === "root" && ROOT_GROUPS.map((item, index) => ( onViewChange(item.kind)} /> ))} - {view === "processor" && ( + {sheetView === "processor" && ( )} - {view !== "root" && - view !== "processor" && - getBlocksForKind(VIEW_KIND[view] ?? "sampler").map( + {sheetView !== "root" && + sheetView !== "processor" && + getBlocksForKind(VIEW_KIND[sheetView] ?? "sampler").map( (item, index) => ( state.configs); + const configs = useRecipeStudioStore((state) => state.configs); const vars = getAvailableVariables(configs, config.id); return ( diff --git a/studio/frontend/src/features/canvas-lab/components/inline/inline-field.tsx b/studio/frontend/src/features/recipe-studio/components/inline/inline-field.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/inline/inline-field.tsx rename to studio/frontend/src/features/recipe-studio/components/inline/inline-field.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/inline/inline-llm.tsx b/studio/frontend/src/features/recipe-studio/components/inline/inline-llm.tsx similarity index 96% rename from studio/frontend/src/features/canvas-lab/components/inline/inline-llm.tsx rename to studio/frontend/src/features/recipe-studio/components/inline/inline-llm.tsx index d9241dfb08..97644e561f 100644 --- a/studio/frontend/src/features/canvas-lab/components/inline/inline-llm.tsx +++ b/studio/frontend/src/features/recipe-studio/components/inline/inline-llm.tsx @@ -14,7 +14,7 @@ import { SelectValue, } from "@/components/ui/select"; import { type ReactElement, useEffect, useMemo, useRef, useState } from "react"; -import { useCanvasLabStore } from "../../stores/canvas-lab"; +import { useRecipeStudioStore } from "../../stores/recipe-studio"; import type { LlmConfig } from "../../types"; import { InlineField } from "./inline-field"; @@ -44,7 +44,7 @@ const CODE_LANG_OPTIONS = [ export function InlineLlm({ config, onUpdate }: InlineLlmProps): ReactElement { const isCode = config.llm_type === "code"; - const configs = useCanvasLabStore((state) => state.configs); + const configs = useRecipeStudioStore((state) => state.configs); const modelConfigAliases = useMemo( () => Object.values(configs) diff --git a/studio/frontend/src/features/canvas-lab/components/inline/inline-model.tsx b/studio/frontend/src/features/recipe-studio/components/inline/inline-model.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/inline/inline-model.tsx rename to studio/frontend/src/features/recipe-studio/components/inline/inline-model.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/inline/inline-policy.ts b/studio/frontend/src/features/recipe-studio/components/inline/inline-policy.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/inline/inline-policy.ts rename to studio/frontend/src/features/recipe-studio/components/inline/inline-policy.ts diff --git a/studio/frontend/src/features/canvas-lab/components/inline/inline-sampler.tsx b/studio/frontend/src/features/recipe-studio/components/inline/inline-sampler.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/inline/inline-sampler.tsx rename to studio/frontend/src/features/recipe-studio/components/inline/inline-sampler.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/floating-icon-button-class.ts b/studio/frontend/src/features/recipe-studio/components/recipe-floating-icon-button-class.ts similarity index 78% rename from studio/frontend/src/features/canvas-lab/components/floating-icon-button-class.ts rename to studio/frontend/src/features/recipe-studio/components/recipe-floating-icon-button-class.ts index 1172efd5e7..ed375eef7c 100644 --- a/studio/frontend/src/features/canvas-lab/components/floating-icon-button-class.ts +++ b/studio/frontend/src/features/recipe-studio/components/recipe-floating-icon-button-class.ts @@ -1,2 +1,2 @@ -export const CANVAS_FLOATING_ICON_BUTTON_CLASS = +export const RECIPE_FLOATING_ICON_BUTTON_CLASS = "corner-squircle group h-11 w-11 rounded-xl border border-border/60 bg-transparent p-0 text-muted-foreground hover:bg-transparent hover:text-primary hover:border-primary/60"; diff --git a/studio/frontend/src/features/canvas-lab/components/canvas-aux-node.tsx b/studio/frontend/src/features/recipe-studio/components/recipe-graph-aux-node.tsx similarity index 93% rename from studio/frontend/src/features/canvas-lab/components/canvas-aux-node.tsx rename to studio/frontend/src/features/recipe-studio/components/recipe-graph-aux-node.tsx index 1da19b939f..cccb535fae 100644 --- a/studio/frontend/src/features/canvas-lab/components/canvas-aux-node.tsx +++ b/studio/frontend/src/features/recipe-studio/components/recipe-graph-aux-node.tsx @@ -11,7 +11,7 @@ import { } from "@xyflow/react"; import { memo, type ReactElement } from "react"; import { MAX_NODE_WIDTH, MIN_NODE_WIDTH } from "../constants"; -import { useCanvasLabStore } from "../stores/canvas-lab"; +import { useRecipeStudioStore } from "../stores/recipe-studio"; import type { LayoutDirection, LlmConfig, Score, ScoreOption } from "../types"; import { HANDLE_IDS } from "../utils/handles"; import { getAvailableVariables } from "../utils/variables"; @@ -34,8 +34,8 @@ type JudgeScoreNodeData = { layoutDirection: LayoutDirection; }; -export type CanvasAuxNodeData = PromptInputNodeData | JudgeScoreNodeData; -export type CanvasAuxNodeType = Node; +export type RecipeGraphAuxNodeData = PromptInputNodeData | JudgeScoreNodeData; +export type RecipeGraphAuxNodeType = Node; function updateScoreAt( config: LlmConfig, @@ -59,7 +59,7 @@ function updateOptionAt( } function AuxVariableBadges({ llmId }: { llmId: string }): ReactElement | null { - const configs = useCanvasLabStore((state) => state.configs); + const configs = useRecipeStudioStore((state) => state.configs); const vars = getAvailableVariables(configs, llmId); if (vars.length === 0) return null; return ( @@ -82,9 +82,9 @@ function AuxVariableBadges({ llmId }: { llmId: string }): ReactElement | null { function AuxNodeBase({ data, -}: NodeProps): ReactElement | null { - const config = useCanvasLabStore((state) => state.configs[data.llmId]); - const updateConfig = useCanvasLabStore((state) => state.updateConfig); +}: NodeProps): ReactElement | null { + const config = useRecipeStudioStore((state) => state.configs[data.llmId]); + const updateConfig = useRecipeStudioStore((state) => state.updateConfig); if (!(config && config.kind === "llm")) { return null; @@ -255,4 +255,4 @@ function AuxNodeBase({ ); } -export const CanvasAuxNode = memo(AuxNodeBase); +export const RecipeGraphAuxNode = memo(AuxNodeBase); diff --git a/studio/frontend/src/features/canvas-lab/components/canvas-node.tsx b/studio/frontend/src/features/recipe-studio/components/recipe-graph-node.tsx similarity index 95% rename from studio/frontend/src/features/canvas-lab/components/canvas-node.tsx rename to studio/frontend/src/features/recipe-studio/components/recipe-graph-node.tsx index 37c329af40..4bc2f11a56 100644 --- a/studio/frontend/src/features/canvas-lab/components/canvas-node.tsx +++ b/studio/frontend/src/features/recipe-studio/components/recipe-graph-node.tsx @@ -27,9 +27,9 @@ import { } from "@xyflow/react"; import { memo, type ReactElement, useEffect } from "react"; import { MAX_NODE_WIDTH, MIN_NODE_WIDTH } from "../constants"; -import { useCanvasLabStore } from "../stores/canvas-lab"; +import { useRecipeStudioStore } from "../stores/recipe-studio"; import type { - CanvasNode as CanvasNodeType, + RecipeNode as RecipeGraphNodeType, LlmType, NodeConfig, SamplerType, @@ -90,8 +90,8 @@ const LLM_ICONS: Record = { }; function resolveNodeIcon( - kind: CanvasNodeType["data"]["kind"], - blockType: CanvasNodeType["data"]["blockType"], + kind: RecipeGraphNodeType["data"]["kind"], + blockType: RecipeGraphNodeType["data"]["blockType"], ): IconType { if (kind === "sampler" && blockType in SAMPLER_ICONS) { return SAMPLER_ICONS[blockType as SamplerType]; @@ -274,17 +274,17 @@ function LlmInputHandles({ items, isTopBottom }: LlmInputHandlesProps): ReactEle ); } -function CanvasNodeBase({ +function RecipeGraphNodeBase({ id, data, selected, -}: NodeProps): ReactElement { +}: NodeProps): ReactElement { const meta = NODE_META[data.kind]; const icon = resolveNodeIcon(data.kind, data.blockType); const layoutDirection = data.layoutDirection ?? "LR"; - const config = useCanvasLabStore((state) => state.configs[id]); - const openConfig = useCanvasLabStore((state) => state.openConfig); - const updateConfig = useCanvasLabStore((state) => state.updateConfig); + const config = useRecipeStudioStore((state) => state.configs[id]); + const openConfig = useRecipeStudioStore((state) => state.openConfig); + const updateConfig = useRecipeStudioStore((state) => state.updateConfig); const updateNodeInternals = useUpdateNodeInternals(); useEffect(() => { @@ -409,4 +409,4 @@ function CanvasNodeBase({ ); } -export const CanvasNode = memo(CanvasNodeBase); +export const RecipeNode = memo(RecipeGraphNodeBase); diff --git a/studio/frontend/src/features/canvas-lab/components/canvas-semantic-edge.tsx b/studio/frontend/src/features/recipe-studio/components/recipe-graph-semantic-edge.tsx similarity index 89% rename from studio/frontend/src/features/canvas-lab/components/canvas-semantic-edge.tsx rename to studio/frontend/src/features/recipe-studio/components/recipe-graph-semantic-edge.tsx index e7eb0a03fc..14a1f98dea 100644 --- a/studio/frontend/src/features/canvas-lab/components/canvas-semantic-edge.tsx +++ b/studio/frontend/src/features/recipe-studio/components/recipe-graph-semantic-edge.tsx @@ -1,7 +1,7 @@ import { BaseEdge, type EdgeProps, getSmoothStepPath } from "@xyflow/react"; import { memo, type ReactElement } from "react"; -export const CanvasSemanticEdge = memo(function CanvasSemanticEdge({ +export const RecipeGraphSemanticEdge = memo(function RecipeGraphSemanticEdge({ id, sourceX, sourceY, diff --git a/studio/frontend/src/features/canvas-lab/components/rf-ui/base-handle.tsx b/studio/frontend/src/features/recipe-studio/components/rf-ui/base-handle.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/rf-ui/base-handle.tsx rename to studio/frontend/src/features/recipe-studio/components/rf-ui/base-handle.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/rf-ui/base-node.tsx b/studio/frontend/src/features/recipe-studio/components/rf-ui/base-node.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/rf-ui/base-node.tsx rename to studio/frontend/src/features/recipe-studio/components/rf-ui/base-node.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/rf-ui/data-edge.tsx b/studio/frontend/src/features/recipe-studio/components/rf-ui/data-edge.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/rf-ui/data-edge.tsx rename to studio/frontend/src/features/recipe-studio/components/rf-ui/data-edge.tsx diff --git a/studio/frontend/src/features/canvas-lab/components/rf-ui/labeled-handle.tsx b/studio/frontend/src/features/recipe-studio/components/rf-ui/labeled-handle.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/components/rf-ui/labeled-handle.tsx rename to studio/frontend/src/features/recipe-studio/components/rf-ui/labeled-handle.tsx diff --git a/studio/frontend/src/features/canvas-lab/constants.ts b/studio/frontend/src/features/recipe-studio/constants.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/constants.ts rename to studio/frontend/src/features/recipe-studio/constants.ts diff --git a/studio/frontend/src/features/canvas-lab/dialogs/config-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/config-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/config-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/config-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/expression/expression-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/expression/expression-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/expression/expression-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/expression/expression-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/import-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/import-dialog.tsx similarity index 98% rename from studio/frontend/src/features/canvas-lab/dialogs/import-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/import-dialog.tsx index bf79f8adce..43d6e7038c 100644 --- a/studio/frontend/src/features/canvas-lab/dialogs/import-dialog.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/import-dialog.tsx @@ -24,7 +24,7 @@ export function ImportDialog({ }: ImportDialogProps): ReactElement { const [value, setValue] = useState(""); const [error, setError] = useState(null); - const payloadId = "canvas-import-payload"; + const payloadId = "recipe-import-payload"; const handleOpenChange = (nextOpen: boolean) => { if (!nextOpen) { setValue(""); diff --git a/studio/frontend/src/features/canvas-lab/dialogs/llm/llm-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/llm/llm-dialog.tsx similarity index 98% rename from studio/frontend/src/features/canvas-lab/dialogs/llm/llm-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/llm/llm-dialog.tsx index 76963d56ef..b13b85a491 100644 --- a/studio/frontend/src/features/canvas-lab/dialogs/llm/llm-dialog.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/llm/llm-dialog.tsx @@ -186,7 +186,7 @@ export function LlmDialog({
{scores.length === 0 && (

- Add scorer blocks. Each block spawns on canvas and connects to this judge node. + Add scorer blocks. Each block spawns on graph and connects to this judge node.

)} {scores.map((score, index) => ( diff --git a/studio/frontend/src/features/canvas-lab/dialogs/models/model-config-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/models/model-config-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/models/model-config-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/models/model-config-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/models/model-provider-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/models/model-provider-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/models/model-provider-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/models/model-provider-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/processors-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/processors-dialog.tsx similarity index 95% rename from studio/frontend/src/features/canvas-lab/dialogs/processors-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/processors-dialog.tsx index 539bdd46cb..293eba8d4e 100644 --- a/studio/frontend/src/features/canvas-lab/dialogs/processors-dialog.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/processors-dialog.tsx @@ -5,14 +5,14 @@ import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { VisuallyHidden } from "radix-ui"; import { type ReactElement, useMemo } from "react"; -import type { CanvasProcessorConfig } from "../types"; +import type { RecipeProcessorConfig } from "../types"; import { buildDefaultSchemaTransform } from "../utils/processors"; import { AvailableVariables } from "./shared/available-variables"; type ProcessorsDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; - processors: CanvasProcessorConfig[]; - onProcessorsChange: (processors: CanvasProcessorConfig[]) => void; + processors: RecipeProcessorConfig[]; + onProcessorsChange: (processors: RecipeProcessorConfig[]) => void; container?: HTMLDivElement | null; }; @@ -51,7 +51,7 @@ export function ProcessorsDialog({ ); }; - const updateSchema = (patch: Partial) => { + const updateSchema = (patch: Partial) => { if (!schemaProcessor) { return; } diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/bernoulli-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/bernoulli-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/bernoulli-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/bernoulli-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/category-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/category-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/category-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/category-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/datetime-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/datetime-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/datetime-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/datetime-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/gaussian-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/gaussian-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/gaussian-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/gaussian-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/person-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/person-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/person-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/person-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/subcategory-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/subcategory-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/subcategory-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/subcategory-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/timedelta-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/timedelta-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/timedelta-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/timedelta-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/uniform-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/uniform-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/uniform-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/uniform-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/samplers/uuid-dialog.tsx b/studio/frontend/src/features/recipe-studio/dialogs/samplers/uuid-dialog.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/samplers/uuid-dialog.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/samplers/uuid-dialog.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/shared/available-variables.tsx b/studio/frontend/src/features/recipe-studio/dialogs/shared/available-variables.tsx similarity index 87% rename from studio/frontend/src/features/canvas-lab/dialogs/shared/available-variables.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/shared/available-variables.tsx index a20bb76364..04d8ffe356 100644 --- a/studio/frontend/src/features/canvas-lab/dialogs/shared/available-variables.tsx +++ b/studio/frontend/src/features/recipe-studio/dialogs/shared/available-variables.tsx @@ -1,6 +1,6 @@ import { Badge } from "@/components/ui/badge"; import type { ReactElement } from "react"; -import { useCanvasLabStore } from "../../stores/canvas-lab"; +import { useRecipeStudioStore } from "../../stores/recipe-studio"; import { getAvailableVariables } from "../../utils/variables"; type AvailableVariablesProps = { @@ -10,7 +10,7 @@ type AvailableVariablesProps = { export function AvailableVariables({ configId, }: AvailableVariablesProps): ReactElement | null { - const configs = useCanvasLabStore((state) => state.configs); + const configs = useRecipeStudioStore((state) => state.configs); const vars = getAvailableVariables(configs, configId); if (vars.length === 0) return null; diff --git a/studio/frontend/src/features/canvas-lab/dialogs/shared/dialog-shell.tsx b/studio/frontend/src/features/recipe-studio/dialogs/shared/dialog-shell.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/shared/dialog-shell.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/shared/dialog-shell.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/shared/name-field.tsx b/studio/frontend/src/features/recipe-studio/dialogs/shared/name-field.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/shared/name-field.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/shared/name-field.tsx diff --git a/studio/frontend/src/features/canvas-lab/dialogs/shared/validation-banner.tsx b/studio/frontend/src/features/recipe-studio/dialogs/shared/validation-banner.tsx similarity index 100% rename from studio/frontend/src/features/canvas-lab/dialogs/shared/validation-banner.tsx rename to studio/frontend/src/features/recipe-studio/dialogs/shared/validation-banner.tsx diff --git a/studio/frontend/src/features/recipe-studio/index.ts b/studio/frontend/src/features/recipe-studio/index.ts new file mode 100644 index 0000000000..2060d86fa7 --- /dev/null +++ b/studio/frontend/src/features/recipe-studio/index.ts @@ -0,0 +1 @@ +export { RecipeStudioPage } from "./recipe-studio-page"; diff --git a/studio/frontend/src/features/canvas-lab/canvas-lab-page.tsx b/studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx similarity index 78% rename from studio/frontend/src/features/canvas-lab/canvas-lab-page.tsx rename to studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx index 3a4a74a6ce..ee50a16435 100644 --- a/studio/frontend/src/features/canvas-lab/canvas-lab-page.tsx +++ b/studio/frontend/src/features/recipe-studio/recipe-studio-page.tsx @@ -26,30 +26,30 @@ import { Spinner } from "@/components/ui/spinner"; import { EyeIcon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { Lock, LockOpen, Maximize2, Minus, Plus } from "lucide-react"; -import { previewCanvas } from "./api"; -import { CanvasAuxNode, type CanvasAuxNodeData } from "./components/canvas-aux-node"; +import { previewRecipe } from "./api"; +import { RecipeGraphAuxNode, type RecipeGraphAuxNodeData } from "./components/recipe-graph-aux-node"; import { BlockSheet } from "./components/block-sheet"; -import { CANVAS_FLOATING_ICON_BUTTON_CLASS } from "./components/floating-icon-button-class"; -import { CanvasNode } from "./components/canvas-node"; -import { CanvasSemanticEdge } from "./components/canvas-semantic-edge"; +import { RECIPE_FLOATING_ICON_BUTTON_CLASS } from "./components/recipe-floating-icon-button-class"; +import { RecipeNode } from "./components/recipe-graph-node"; +import { RecipeGraphSemanticEdge } from "./components/recipe-graph-semantic-edge"; import { DataEdge } from "./components/rf-ui/data-edge"; import { ConfigDialog } from "./dialogs/config-dialog"; import { ImportDialog } from "./dialogs/import-dialog"; import { ProcessorsDialog } from "./dialogs/processors-dialog"; -import { useCanvasLabStore } from "./stores/canvas-lab"; +import { useRecipeStudioStore } from "./stores/recipe-studio"; import type { - CanvasNode as CanvasBuilderNode, - CanvasNodeData, + RecipeNode as RecipeBuilderNode, + RecipeNodeData, SamplerConfig, } from "./types"; import { isCategoryConfig } from "./utils"; import { deriveDisplayGraph } from "./utils/graph/derive-display-graph"; -import { importCanvasPayload } from "./utils/import"; -import { buildCanvasPayload } from "./utils/payload"; +import { importRecipePayload } from "./utils/import"; +import { buildRecipePayload } from "./utils/payload"; import { buildDefaultSchemaTransform } from "./utils/processors"; -const NODE_TYPES: NodeTypes = { builder: CanvasNode, aux: CanvasAuxNode }; -const EDGE_TYPES: EdgeTypes = { canvas: DataEdge, semantic: CanvasSemanticEdge }; +const NODE_TYPES: NodeTypes = { builder: RecipeNode, aux: RecipeGraphAuxNode }; +const EDGE_TYPES: EdgeTypes = { canvas: DataEdge, semantic: RecipeGraphSemanticEdge }; type LayoutControlsProps = { direction: "LR" | "TB"; @@ -66,6 +66,17 @@ type InternalsSyncProps = { nodeIds: string[]; }; +type StatusTone = "success" | "error"; +type StatusMessage = { + tone: StatusTone; + text: string; +}; + +const STATUS_MESSAGE_CLASS: Record = { + success: "mt-2 text-xs text-emerald-600", + error: "mt-2 text-xs text-rose-600", +}; + function InternalsSync({ nodeIds }: InternalsSyncProps): null { const updateNodeInternals = useUpdateNodeInternals(); @@ -155,7 +166,7 @@ function ViewportControls({ type="button" variant="ghost" size="icon" - className={CANVAS_FLOATING_ICON_BUTTON_CLASS} + className={RECIPE_FLOATING_ICON_BUTTON_CLASS} onClick={handleZoomIn} aria-label="Zoom in" > @@ -165,7 +176,7 @@ function ViewportControls({ type="button" variant="ghost" size="icon" - className={CANVAS_FLOATING_ICON_BUTTON_CLASS} + className={RECIPE_FLOATING_ICON_BUTTON_CLASS} onClick={handleZoomOut} aria-label="Zoom out" > @@ -175,7 +186,7 @@ function ViewportControls({ type="button" variant="ghost" size="icon" - className={CANVAS_FLOATING_ICON_BUTTON_CLASS} + className={RECIPE_FLOATING_ICON_BUTTON_CLASS} onClick={handleFitView} aria-label="Fit view" > @@ -185,7 +196,7 @@ function ViewportControls({ type="button" variant="ghost" size="icon" - className={CANVAS_FLOATING_ICON_BUTTON_CLASS} + className={RECIPE_FLOATING_ICON_BUTTON_CLASS} onClick={onToggleInteractive} aria-label={interactive ? "Lock interaction" : "Unlock interaction"} > @@ -199,7 +210,7 @@ function ViewportControls({ ); } -export function CanvasLabPage(): ReactElement { +export function RecipeStudioPage(): ReactElement { const { nodes, edges, @@ -225,14 +236,14 @@ export function CanvasLabPage(): ReactElement { setSheetView, setProcessors, setDialogOpen, - loadCanvas, + loadRecipe, setLayoutDirection, applyLayout, setAuxNodePosition, setAuxNodeSize, syncAuxNodePositions, syncAuxNodeSizes, - } = useCanvasLabStore( + } = useRecipeStudioStore( useShallow((state) => ({ nodes: state.nodes, edges: state.edges, @@ -258,7 +269,7 @@ export function CanvasLabPage(): ReactElement { setSheetView: state.setSheetView, setProcessors: state.setProcessors, setDialogOpen: state.setDialogOpen, - loadCanvas: state.loadCanvas, + loadRecipe: state.loadRecipe, setLayoutDirection: state.setLayoutDirection, applyLayout: state.applyLayout, setAuxNodePosition: state.setAuxNodePosition, @@ -275,10 +286,7 @@ export function CanvasLabPage(): ReactElement { const [importOpen, setImportOpen] = useState(false); const [processorsOpen, setProcessorsOpen] = useState(false); const [interactive, setInteractive] = useState(true); - const [statusMessage, setStatusMessage] = useState<{ - tone: "success" | "error"; - text: string; - } | null>(null); + const [statusMessage, setStatusMessage] = useState(null); const baseNodeIds = useMemo( () => new Set(nodes.map((node) => node.id)), @@ -311,7 +319,7 @@ export function CanvasLabPage(): ReactElement { }, [displayGraph.auxNodeIds, syncAuxNodeSizes]); const handleNodeClick = useCallback( - (_: unknown, node: Node) => { + (_: unknown, node: Node) => { if (node.type !== "builder") { return; } @@ -321,12 +329,9 @@ export function CanvasLabPage(): ReactElement { ); const handleNodesChange = useCallback( - (changes: NodeChange>[]) => { + (changes: NodeChange>[]) => { for (const change of changes) { - if ( - !("id" in change) || - !change.id.startsWith("aux-") - ) { + if (!("id" in change) || !change.id.startsWith("aux-")) { continue; } if (change.type === "position" && change.position) { @@ -346,7 +351,7 @@ export function CanvasLabPage(): ReactElement { } } const next = changes.filter( - (change): change is NodeChange => + (change): change is NodeChange => "id" in change && baseNodeIds.has(change.id), ); if (next.length > 0) { @@ -369,67 +374,77 @@ export function CanvasLabPage(): ReactElement { [baseEdgeIds, onEdgesChange], ); + const configList = useMemo(() => Object.values(configs), [configs]); const config = activeConfigId ? configs[activeConfigId] : null; const categoryOptions = useMemo( - () => Object.values(configs).filter(isCategoryConfig), - [configs], + () => configList.filter(isCategoryConfig), + [configList], ); const modelConfigAliases = useMemo( - () => - Object.values(configs) - .filter((item) => item.kind === "model_config") - .map((item) => item.name), - [configs], + () => configList.filter((item) => item.kind === "model_config").map((item) => item.name), + [configList], ); const modelProviderOptions = useMemo( - () => - Object.values(configs) - .filter((item) => item.kind === "model_provider") - .map((item) => item.name), - [configs], + () => configList.filter((item) => item.kind === "model_provider").map((item) => item.name), + [configList], ); const datetimeOptions = useMemo( () => - Object.values(configs) + configList .filter( (item) => item.kind === "sampler" && item.sampler_type === "datetime", ) .map((item) => item.name), - [configs], + [configList], ); const handleToggleDirection = useCallback(() => { setLayoutDirection(layoutDirection === "LR" ? "TB" : "LR"); }, [layoutDirection, setLayoutDirection]); + const toggleInteractive = useCallback(() => { + setInteractive((value) => !value); + }, []); + + const setSuccessStatus = useCallback((text: string) => { + setStatusMessage({ tone: "success", text }); + }, []); + + const setErrorStatus = useCallback((text: string) => { + setStatusMessage({ tone: "error", text }); + }, []); + + const buildPayload = useCallback( + () => buildRecipePayload(configs, nodes, edges, processors), + [configs, nodes, edges, processors], + ); + + const readPayload = useCallback( + (fallbackError: string) => { + const { payload, errors } = buildPayload(); + if (errors.length === 0) { + return payload; + } + setErrorStatus(errors[0] ?? fallbackError); + return null; + }, + [buildPayload, setErrorStatus], + ); + const handlePreview = async (): Promise => { setPreviewLoading(true); setStatusMessage(null); + const payload = readPayload("Fix config errors before preview."); + if (!payload) { + setPreviewLoading(false); + return; + } try { - const { payload, errors } = buildCanvasPayload( - configs, - nodes, - edges, - processors, - ); - if (errors.length > 0) { - setStatusMessage({ - tone: "error", - text: errors[0] ?? "Fix config errors before preview.", - }); - return; - } - const result = await previewCanvas(payload); + const result = await previewRecipe(payload); const rows = Array.isArray(result.dataset) ? result.dataset.length : 0; - setStatusMessage({ - tone: "success", - text: `Preview ready (${rows} rows).`, - }); + setSuccessStatus(`Preview ready (${rows} rows).`); } catch (error) { - setStatusMessage({ - tone: "error", - text: error instanceof Error ? error.message : "Preview failed.", - }); + setErrorStatus(error instanceof Error ? error.message : "Preview failed."); } finally { setPreviewLoading(false); } @@ -438,53 +453,35 @@ export function CanvasLabPage(): ReactElement { const handleCopyRecipe = async (): Promise => { setCopied(false); setStatusMessage(null); - const { payload, errors } = buildCanvasPayload( - configs, - nodes, - edges, - processors, - ); - if (errors.length > 0) { - setStatusMessage({ - tone: "error", - text: errors[0] ?? "Fix config errors before copy.", - }); + const payload = readPayload("Fix config errors before copy."); + if (!payload) { return; } if (!navigator.clipboard) { - setStatusMessage({ - tone: "error", - text: "Clipboard not available.", - }); + setErrorStatus("Clipboard not available."); return; } try { await navigator.clipboard.writeText(JSON.stringify(payload, null, 2)); setCopied(true); window.setTimeout(() => setCopied(false), 1500); - setStatusMessage({ - tone: "success", - text: "Recipe copied to clipboard.", - }); + setSuccessStatus("Recipe copied to clipboard."); } catch (error) { - setStatusMessage({ - tone: "error", - text: error instanceof Error ? error.message : "Copy failed.", - }); + setErrorStatus(error instanceof Error ? error.message : "Copy failed."); } }; const handleImport = (value: string): string | null => { - const result = importCanvasPayload(value); + const result = importRecipePayload(value); if (result.errors.length > 0 || !result.snapshot) { return result.errors[0] ?? "Invalid payload."; } - loadCanvas(result.snapshot); - setStatusMessage({ tone: "success", text: "Recipe imported." }); + loadRecipe(result.snapshot); + setSuccessStatus("Recipe imported."); return null; }; - const handleOpenProcessorsFromSheet = useCallback(() => { + const openProcessorsFromSheet = useCallback(() => { if ( !processors.some( (processor) => processor.processor_type === "schema_transform", @@ -501,20 +498,12 @@ export function CanvasLabPage(): ReactElement {
-

- Canvas Lab -

+

Create Data Recipe

- Minimal React Flow canvas. + Design synthetic-data pipelines with Data Designer.

{statusMessage && ( -

+

{statusMessage.text}

)} @@ -577,14 +566,14 @@ export function CanvasLabPage(): ReactElement { setImportOpen(true)} @@ -592,7 +581,7 @@ export function CanvasLabPage(): ReactElement { setInteractive((value) => !value)} + onToggleInteractive={toggleInteractive} />
diff --git a/studio/frontend/src/features/canvas-lab/stores/canvas-lab-helpers.ts b/studio/frontend/src/features/recipe-studio/stores/recipe-studio-helpers.ts similarity index 79% rename from studio/frontend/src/features/canvas-lab/stores/canvas-lab-helpers.ts rename to studio/frontend/src/features/recipe-studio/stores/recipe-studio-helpers.ts index 55da7b7f28..9a14791156 100644 --- a/studio/frontend/src/features/canvas-lab/stores/canvas-lab-helpers.ts +++ b/studio/frontend/src/features/recipe-studio/stores/recipe-studio-helpers.ts @@ -1,7 +1,7 @@ import { type Edge, addEdge } from "@xyflow/react"; import { DEFAULT_NODE_WIDTH } from "../constants"; import type { - CanvasNode, + RecipeNode, LayoutDirection, LlmConfig, ModelConfig, @@ -15,14 +15,14 @@ import { getConfigUiMode } from "../components/inline/inline-policy"; type NodeUpdateState = { configs: Record; - nodes: CanvasNode[]; + nodes: RecipeNode[]; nextId: number; nextY: number; }; type NodeUpdateResult = { configs: Record; - nodes: CanvasNode[]; + nodes: RecipeNode[]; nextId: number; nextY: number; activeConfigId: string; @@ -30,11 +30,11 @@ type NodeUpdateResult = { }; export function updateNodeData( - nodes: CanvasNode[], + nodes: RecipeNode[], id: string, config: NodeConfig, layoutDirection: LayoutDirection, -): CanvasNode[] { +): RecipeNode[] { return nodes.map((node) => node.id === id ? { ...node, data: nodeDataFromConfig(config, layoutDirection) } @@ -52,12 +52,56 @@ function findNodeIdByName( return entry ? entry[0] : null; } +function addRecipeEdge(edges: Edge[], source: string, target: string): Edge[] { + return addEdge( + { + source, + target, + sourceHandle: HANDLE_IDS.dataOut, + targetHandle: HANDLE_IDS.dataIn, + type: "canvas", + }, + edges, + ); +} + +function addSemanticEdge(edges: Edge[], source: string, target: string): Edge[] { + return addEdge( + { + source, + target, + sourceHandle: HANDLE_IDS.semanticOut, + targetHandle: HANDLE_IDS.semanticIn, + type: "semantic", + }, + edges, + ); +} + +function removeTargetEdges(edges: Edge[], targetId: string): Edge[] { + return edges.filter((edge) => edge.target !== targetId); +} + +function removeTargetEdgesBySource( + edges: Edge[], + configs: Record, + targetId: string, + shouldRemove: (source: NodeConfig | undefined) => boolean, +): Edge[] { + return edges.filter((edge) => { + if (edge.target !== targetId) { + return true; + } + return !shouldRemove(configs[edge.source]); + }); +} + export function buildNodeUpdate( state: NodeUpdateState, config: NodeConfig, layoutDirection: LayoutDirection, ): NodeUpdateResult { - const node: CanvasNode = { + const node: RecipeNode = { id: config.id, type: "builder", position: { x: 0, y: state.nextY }, @@ -77,10 +121,10 @@ export function buildNodeUpdate( } export function applyLayoutDirectionToNodes( - nodes: CanvasNode[], + nodes: RecipeNode[], configs: Record, layoutDirection: LayoutDirection, -): CanvasNode[] { +): RecipeNode[] { return nodes.map((node) => { const config = configs[node.id]; if (config) { @@ -108,18 +152,9 @@ export function syncEdgesForConfigPatch( if (isSubcategoryConfig(current) && hasParentPatch) { const nextParent = (patch as Partial).subcategory_parent ?? ""; const parentId = nextParent ? findNodeIdByName(configs, nextParent) : null; - nextEdges = nextEdges.filter((edge) => edge.target !== current.id); + nextEdges = removeTargetEdges(nextEdges, current.id); if (parentId) { - nextEdges = addEdge( - { - source: parentId, - target: current.id, - sourceHandle: HANDLE_IDS.dataOut, - targetHandle: HANDLE_IDS.dataIn, - type: "canvas", - }, - nextEdges, - ); + nextEdges = addRecipeEdge(nextEdges, parentId, current.id); } } @@ -129,26 +164,16 @@ export function syncEdgesForConfigPatch( ); if (current.kind === "model_config" && hasProviderPatch) { const nextProvider = (patch as Partial).provider ?? ""; - nextEdges = nextEdges.filter((edge) => { - if (edge.target !== current.id) { - return true; - } - const source = configs[edge.source]; - return !(source && source.kind === "model_provider"); - }); + nextEdges = removeTargetEdgesBySource( + nextEdges, + configs, + current.id, + (source) => Boolean(source && source.kind === "model_provider"), + ); if (nextProvider) { const providerId = findNodeIdByName(configs, nextProvider); if (providerId) { - nextEdges = addEdge( - { - source: providerId, - target: current.id, - sourceHandle: HANDLE_IDS.semanticOut, - targetHandle: HANDLE_IDS.semanticIn, - type: "semantic", - }, - nextEdges, - ); + nextEdges = addSemanticEdge(nextEdges, providerId, current.id); } } } @@ -164,17 +189,17 @@ export function syncEdgesForConfigPatch( ) { const nextReference = (patch as Partial).reference_column_name ?? ""; - nextEdges = nextEdges.filter((edge) => { - if (edge.target !== current.id) { - return true; - } - const source = configs[edge.source]; - return !( - source && - source.kind === "sampler" && - source.sampler_type === "datetime" - ); - }); + nextEdges = removeTargetEdgesBySource( + nextEdges, + configs, + current.id, + (source) => + Boolean( + source && + source.kind === "sampler" && + source.sampler_type === "datetime", + ), + ); if (nextReference) { const referenceId = findNodeIdByName(configs, nextReference); const source = referenceId ? configs[referenceId] : null; @@ -184,16 +209,7 @@ export function syncEdgesForConfigPatch( source.kind === "sampler" && source.sampler_type === "datetime" ) { - nextEdges = addEdge( - { - source: referenceId, - target: current.id, - sourceHandle: HANDLE_IDS.dataOut, - targetHandle: HANDLE_IDS.dataIn, - type: "canvas", - }, - nextEdges, - ); + nextEdges = addRecipeEdge(nextEdges, referenceId, current.id); } } } @@ -205,26 +221,16 @@ export function syncEdgesForConfigPatch( if (current.kind === "llm" && hasModelAliasPatch) { const nextAlias = (patch as Partial & { model_alias?: string }).model_alias ?? ""; - nextEdges = nextEdges.filter((edge) => { - if (edge.target !== current.id) { - return true; - } - const source = configs[edge.source]; - return !(source && source.kind === "model_config"); - }); + nextEdges = removeTargetEdgesBySource( + nextEdges, + configs, + current.id, + (source) => Boolean(source && source.kind === "model_config"), + ); if (nextAlias) { const modelConfigId = findNodeIdByName(configs, nextAlias); if (modelConfigId) { - nextEdges = addEdge( - { - source: modelConfigId, - target: current.id, - sourceHandle: HANDLE_IDS.semanticOut, - targetHandle: HANDLE_IDS.semanticIn, - type: "semantic", - }, - nextEdges, - ); + nextEdges = addSemanticEdge(nextEdges, modelConfigId, current.id); } } } @@ -405,9 +411,18 @@ export function applyRenameToConfigs( if (!from || from === to) { return configs; } + return applyConfigTransform(configs, (config) => + applyRenameToConfig(config, from, to), + ); +} + +function applyConfigTransform( + configs: Record, + transform: (config: NodeConfig) => NodeConfig, +): Record { let next = configs; for (const [id, config] of Object.entries(configs)) { - const updated = applyRenameToConfig(config, from, to); + const updated = transform(config); if (updated !== config) { if (next === configs) { next = { ...configs }; @@ -425,15 +440,5 @@ export function applyRemovalToConfigs( if (!ref) { return configs; } - let next = configs; - for (const [id, config] of Object.entries(configs)) { - const updated = applyRemovalToConfig(config, ref); - if (updated !== config) { - if (next === configs) { - next = { ...configs }; - } - next[id] = updated; - } - } - return next; + return applyConfigTransform(configs, (config) => applyRemovalToConfig(config, ref)); } diff --git a/studio/frontend/src/features/canvas-lab/stores/canvas-lab.ts b/studio/frontend/src/features/recipe-studio/stores/recipe-studio.ts similarity index 78% rename from studio/frontend/src/features/canvas-lab/stores/canvas-lab.ts rename to studio/frontend/src/features/recipe-studio/stores/recipe-studio.ts index 9a50768d09..806b7b83cb 100644 --- a/studio/frontend/src/features/canvas-lab/stores/canvas-lab.ts +++ b/studio/frontend/src/features/recipe-studio/stores/recipe-studio.ts @@ -10,17 +10,21 @@ import { } from "@xyflow/react"; import { create } from "zustand"; import type { - CanvasNode, - CanvasProcessorConfig, + RecipeNode, + RecipeProcessorConfig, LayoutDirection, LlmType, NodeConfig, SamplerType, } from "../types"; -import { getBlockDefinition } from "../blocks/registry"; +import { + getBlockDefinition, + type BlockKind, + type BlockType, +} from "../blocks/registry"; import { isCategoryConfig, isSubcategoryConfig } from "../utils"; -import { applyCanvasConnection, isValidCanvasConnection } from "../utils/graph"; -import type { CanvasSnapshot } from "../utils/import"; +import { applyRecipeConnection, isValidRecipeConnection } from "../utils/graph"; +import type { RecipeSnapshot } from "../utils/import"; import { getLayoutedElements } from "../utils/layout"; import { applyRemovalToConfig, @@ -31,17 +35,17 @@ import { syncEdgesForConfigPatch, syncSubcategoryConfigsForCategoryUpdate, updateNodeData, -} from "./canvas-lab-helpers"; +} from "./recipe-studio-helpers"; type SheetView = "root" | "sampler" | "llm" | "expression" | "processor"; -type CanvasLabState = { - nodes: CanvasNode[]; +type RecipeStudioState = { + nodes: RecipeNode[]; edges: Edge[]; auxNodePositions: Record; auxNodeSizes: Record; configs: Record; - processors: CanvasProcessorConfig[]; + processors: RecipeProcessorConfig[]; sheetView: SheetView; activeConfigId: string | null; dialogOpen: boolean; @@ -49,7 +53,7 @@ type CanvasLabState = { nextId: number; nextY: number; setSheetView: (view: SheetView) => void; - setProcessors: (processors: CanvasProcessorConfig[]) => void; + setProcessors: (processors: RecipeProcessorConfig[]) => void; setDialogOpen: (open: boolean) => void; selectConfig: (id: string) => void; openConfig: (id: string) => void; @@ -61,7 +65,7 @@ type CanvasLabState = { addModelConfigNode: () => void; addExpressionNode: () => void; updateConfig: (id: string, patch: Partial) => void; - loadCanvas: (snapshot: CanvasSnapshot) => void; + loadRecipe: (snapshot: RecipeSnapshot) => void; setAuxNodePosition: (id: string, position: XYPosition) => void; setAuxNodeSize: ( id: string, @@ -72,13 +76,28 @@ type CanvasLabState = { defaults: Record, ) => void; syncAuxNodeSizes: (activeIds: string[]) => void; - onNodesChange: (changes: NodeChange[]) => void; + onNodesChange: (changes: NodeChange[]) => void; onEdgesChange: (changes: EdgeChange[]) => void; onConnect: (connection: Connection) => void; isValidConnection: IsValidConnection; }; -export const useCanvasLabStore = create((set, get) => ({ +function buildAddedNodeState( + state: RecipeStudioState, + kind: BlockKind, + type: BlockType, +): Partial | RecipeStudioState { + const id = `n${state.nextId}`; + const existing = Object.values(state.configs); + const definition = getBlockDefinition(kind, type); + if (!definition) { + return state; + } + const config = definition.createConfig(id, existing); + return buildNodeUpdate(state, config, state.layoutDirection); +} + +export const useRecipeStudioStore = create((set, get) => ({ nodes: [], edges: [], auxNodePositions: {}, @@ -123,67 +142,16 @@ export const useCanvasLabStore = create((set, get) => ({ ), }; }), - addSamplerNode: (type) => { - set((state) => { - const id = `n${state.nextId}`; - const existing = Object.values(state.configs); - const definition = getBlockDefinition("sampler", type); - if (!definition) { - return state; - } - const config = definition.createConfig(id, existing); - return buildNodeUpdate(state, config, state.layoutDirection); - }); - }, - addLlmNode: (type) => { - set((state) => { - const id = `n${state.nextId}`; - const existing = Object.values(state.configs); - const definition = getBlockDefinition("llm", type); - if (!definition) { - return state; - } - const config = definition.createConfig(id, existing); - return buildNodeUpdate(state, config, state.layoutDirection); - }); - }, - addModelProviderNode: () => { - set((state) => { - const id = `n${state.nextId}`; - const existing = Object.values(state.configs); - const definition = getBlockDefinition("llm", "model_provider"); - if (!definition) { - return state; - } - const config = definition.createConfig(id, existing); - return buildNodeUpdate(state, config, state.layoutDirection); - }); - }, - addModelConfigNode: () => { - set((state) => { - const id = `n${state.nextId}`; - const existing = Object.values(state.configs); - const definition = getBlockDefinition("llm", "model_config"); - if (!definition) { - return state; - } - const config = definition.createConfig(id, existing); - return buildNodeUpdate(state, config, state.layoutDirection); - }); - }, - addExpressionNode: () => { - set((state) => { - const id = `n${state.nextId}`; - const existing = Object.values(state.configs); - const definition = getBlockDefinition("expression", "expression"); - if (!definition) { - return state; - } - const config = definition.createConfig(id, existing); - return buildNodeUpdate(state, config, state.layoutDirection); - }); - }, - loadCanvas: (snapshot) => + addSamplerNode: (type) => + set((state) => buildAddedNodeState(state, "sampler", type)), + addLlmNode: (type) => set((state) => buildAddedNodeState(state, "llm", type)), + addModelProviderNode: () => + set((state) => buildAddedNodeState(state, "llm", "model_provider")), + addModelConfigNode: () => + set((state) => buildAddedNodeState(state, "llm", "model_config")), + addExpressionNode: () => + set((state) => buildAddedNodeState(state, "expression", "expression")), + loadRecipe: (snapshot) => set((state) => ({ configs: snapshot.configs, nodes: applyLayoutDirectionToNodes( @@ -281,7 +249,7 @@ export const useCanvasLabStore = create((set, get) => ({ return state; }), updateConfig: (id, patch) => { - const applyUpdate = (state: CanvasLabState) => { + const applyUpdate = (state: RecipeStudioState) => { const current = state.configs[id]; if (!current) { return state; @@ -320,7 +288,7 @@ export const useCanvasLabStore = create((set, get) => ({ }, onNodesChange: (changes) => { // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: store update - const applyNodesChange = (state: CanvasLabState) => { + const applyNodesChange = (state: RecipeStudioState) => { const removedIds = changes .filter((change) => change.type === "remove") .map((change) => change.id); @@ -367,7 +335,7 @@ export const useCanvasLabStore = create((set, get) => ({ } } - const nodes = applyNodeChanges(changes, state.nodes); + const nodes = applyNodeChanges(changes, state.nodes); return { nodes, edges, configs }; }; set(applyNodesChange); @@ -403,7 +371,7 @@ export const useCanvasLabStore = create((set, get) => ({ }, onConnect: (connection) => { set((state) => { - const result = applyCanvasConnection( + const result = applyRecipeConnection( connection, state.configs, state.edges, @@ -414,7 +382,7 @@ export const useCanvasLabStore = create((set, get) => ({ }); }, isValidConnection: (connection) => - isValidCanvasConnection( + isValidRecipeConnection( { source: connection.source ?? null, target: connection.target ?? null, diff --git a/studio/frontend/src/features/canvas-lab/types/index.ts b/studio/frontend/src/features/recipe-studio/types/index.ts similarity index 97% rename from studio/frontend/src/features/canvas-lab/types/index.ts rename to studio/frontend/src/features/recipe-studio/types/index.ts index 03057e0d99..0a4822b6b1 100644 --- a/studio/frontend/src/features/canvas-lab/types/index.ts +++ b/studio/frontend/src/features/recipe-studio/types/index.ts @@ -18,7 +18,7 @@ export type ExpressionDtype = "str" | "int" | "float" | "bool"; export type LayoutDirection = "LR" | "TB"; -export type CanvasNodeData = { +export type RecipeNodeData = { title: string; name: string; kind: "sampler" | "llm" | "expression" | "model_provider" | "model_config"; @@ -32,7 +32,7 @@ export type CanvasNodeData = { layoutDirection?: LayoutDirection; }; -export type CanvasNode = Node; +export type RecipeNode = Node; export type CategoryConditionalParams = { // biome-ignore lint/style/useNamingConvention: api schema @@ -171,7 +171,7 @@ export type SchemaTransformProcessorConfig = { template: string; }; -export type CanvasProcessorConfig = SchemaTransformProcessorConfig; +export type RecipeProcessorConfig = SchemaTransformProcessorConfig; export type NodeConfig = | SamplerConfig diff --git a/studio/frontend/src/features/recipe-studio/utils/graph.ts b/studio/frontend/src/features/recipe-studio/utils/graph.ts new file mode 100644 index 0000000000..d2fe8878d7 --- /dev/null +++ b/studio/frontend/src/features/recipe-studio/utils/graph.ts @@ -0,0 +1,4 @@ +export { + applyRecipeConnection, + isValidRecipeConnection, +} from "./graph/recipe-graph-connection"; diff --git a/studio/frontend/src/features/canvas-lab/utils/graph/derive-display-graph.ts b/studio/frontend/src/features/recipe-studio/utils/graph/derive-display-graph.ts similarity index 95% rename from studio/frontend/src/features/canvas-lab/utils/graph/derive-display-graph.ts rename to studio/frontend/src/features/recipe-studio/utils/graph/derive-display-graph.ts index eaad185115..7eeb90b8c4 100644 --- a/studio/frontend/src/features/canvas-lab/utils/graph/derive-display-graph.ts +++ b/studio/frontend/src/features/recipe-studio/utils/graph/derive-display-graph.ts @@ -1,11 +1,11 @@ import type { Edge, Node, XYPosition } from "@xyflow/react"; -import type { CanvasAuxNodeData } from "../../components/canvas-aux-node"; +import type { RecipeGraphAuxNodeData } from "../../components/recipe-graph-aux-node"; import { DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH } from "../../constants"; -import type { CanvasNode, LayoutDirection, NodeConfig } from "../../types"; +import type { RecipeNode, LayoutDirection, NodeConfig } from "../../types"; import { getLlmJudgeScoreHandleId, HANDLE_IDS } from "../handles"; type DisplayGraphInput = { - nodes: CanvasNode[]; + nodes: RecipeNode[]; edges: Edge[]; configs: Record; layoutDirection: LayoutDirection; @@ -14,7 +14,7 @@ type DisplayGraphInput = { }; export type DisplayGraph = { - nodes: Array>; + nodes: Array>; edges: Edge[]; auxNodeIds: string[]; auxDefaults: Record; @@ -23,7 +23,7 @@ export type DisplayGraph = { type AuxNodeItem = { key: string; targetHandle: string; - data: CanvasAuxNodeData; + data: RecipeGraphAuxNodeData; }; function getNodeWidth(node: Node): number { @@ -92,7 +92,7 @@ export function deriveDisplayGraph({ style: { ...node.style, width: DEFAULT_NODE_WIDTH }, }; }); - const auxNodes: Node[] = []; + const auxNodes: Node[] = []; const auxEdges: Edge[] = []; const auxDefaults: Record = {}; const auxNodeIds: string[] = []; diff --git a/studio/frontend/src/features/canvas-lab/utils/graph/canvas-connection.ts b/studio/frontend/src/features/recipe-studio/utils/graph/recipe-graph-connection.ts similarity index 97% rename from studio/frontend/src/features/canvas-lab/utils/graph/canvas-connection.ts rename to studio/frontend/src/features/recipe-studio/utils/graph/recipe-graph-connection.ts index eefe370e98..dee68af443 100644 --- a/studio/frontend/src/features/canvas-lab/utils/graph/canvas-connection.ts +++ b/studio/frontend/src/features/recipe-studio/utils/graph/recipe-graph-connection.ts @@ -126,7 +126,7 @@ function isCompetingIncomingEdge( return source.kind === "sampler" && source.sampler_type === "datetime"; } -export function isValidCanvasConnection( +export function isValidRecipeConnection( connection: Connection, configs: Record, ): boolean { @@ -151,12 +151,12 @@ export function isValidCanvasConnection( return isDataLane(connection); } -export function applyCanvasConnection( +export function applyRecipeConnection( connection: Connection, configs: Record, edges: Edge[], ): { edges: Edge[]; configs?: Record } { - if (!isValidCanvasConnection(connection, configs)) { + if (!isValidRecipeConnection(connection, configs)) { return { edges }; } const source = connection.source ? configs[connection.source] : null; diff --git a/studio/frontend/src/features/canvas-lab/utils/handles.ts b/studio/frontend/src/features/recipe-studio/utils/handles.ts similarity index 87% rename from studio/frontend/src/features/canvas-lab/utils/handles.ts rename to studio/frontend/src/features/recipe-studio/utils/handles.ts index 851ceb88ac..d42179a4ff 100644 --- a/studio/frontend/src/features/canvas-lab/utils/handles.ts +++ b/studio/frontend/src/features/recipe-studio/utils/handles.ts @@ -11,7 +11,7 @@ export const HANDLE_IDS = { llmInputOut: "llm-input-out", } as const; -export type CanvasHandleId = (typeof HANDLE_IDS)[keyof typeof HANDLE_IDS]; +export type RecipeHandleId = (typeof HANDLE_IDS)[keyof typeof HANDLE_IDS]; export function getLlmJudgeScoreHandleId(index: number): string { return `llm-judge-score-in-${index}`; diff --git a/studio/frontend/src/features/canvas-lab/utils/import/edges.ts b/studio/frontend/src/features/recipe-studio/utils/import/edges.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/import/edges.ts rename to studio/frontend/src/features/recipe-studio/utils/import/edges.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/import/helpers.ts b/studio/frontend/src/features/recipe-studio/utils/import/helpers.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/import/helpers.ts rename to studio/frontend/src/features/recipe-studio/utils/import/helpers.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/import/importer.ts b/studio/frontend/src/features/recipe-studio/utils/import/importer.ts similarity index 94% rename from studio/frontend/src/features/canvas-lab/utils/import/importer.ts rename to studio/frontend/src/features/recipe-studio/utils/import/importer.ts index e82c61f60e..38234e7d0a 100644 --- a/studio/frontend/src/features/canvas-lab/utils/import/importer.ts +++ b/studio/frontend/src/features/recipe-studio/utils/import/importer.ts @@ -1,4 +1,4 @@ -import type { CanvasProcessorConfig, NodeConfig } from "../../types"; +import type { RecipeProcessorConfig, NodeConfig } from "../../types"; import { buildEdges } from "./edges"; import { isRecord, parseJson, readString } from "./helpers"; import { @@ -21,11 +21,11 @@ type UiInput = { edges?: unknown; }; -function parseProcessors(input: unknown): CanvasProcessorConfig[] { +function parseProcessors(input: unknown): RecipeProcessorConfig[] { if (!Array.isArray(input)) { return []; } - const processors: CanvasProcessorConfig[] = []; + const processors: RecipeProcessorConfig[] = []; input.forEach((item, index) => { if (!isRecord(item)) { return; @@ -55,7 +55,7 @@ function parseProcessors(input: unknown): CanvasProcessorConfig[] { return processors; } -export function importCanvasPayload(input: string): ImportResult { +export function importRecipePayload(input: string): ImportResult { const parsed = parseJson(input); if (!parsed.data || !isRecord(parsed.data)) { return { diff --git a/studio/frontend/src/features/recipe-studio/utils/import/index.ts b/studio/frontend/src/features/recipe-studio/utils/import/index.ts new file mode 100644 index 0000000000..09e5f76bc5 --- /dev/null +++ b/studio/frontend/src/features/recipe-studio/utils/import/index.ts @@ -0,0 +1,2 @@ +export { importRecipePayload } from "./importer"; +export type { RecipeSnapshot, ImportResult } from "./types"; diff --git a/studio/frontend/src/features/canvas-lab/utils/import/parsers.ts b/studio/frontend/src/features/recipe-studio/utils/import/parsers.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/import/parsers.ts rename to studio/frontend/src/features/recipe-studio/utils/import/parsers.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/import/types.ts b/studio/frontend/src/features/recipe-studio/utils/import/types.ts similarity index 59% rename from studio/frontend/src/features/canvas-lab/utils/import/types.ts rename to studio/frontend/src/features/recipe-studio/utils/import/types.ts index a155a6f378..bc9d151f7f 100644 --- a/studio/frontend/src/features/canvas-lab/utils/import/types.ts +++ b/studio/frontend/src/features/recipe-studio/utils/import/types.ts @@ -1,20 +1,20 @@ import type { Edge } from "@xyflow/react"; import type { - CanvasNode, - CanvasProcessorConfig, + RecipeNode, + RecipeProcessorConfig, NodeConfig, } from "../../types"; -export type CanvasSnapshot = { +export type RecipeSnapshot = { configs: Record; - nodes: CanvasNode[]; + nodes: RecipeNode[]; edges: Edge[]; - processors: CanvasProcessorConfig[]; + processors: RecipeProcessorConfig[]; nextId: number; nextY: number; }; export type ImportResult = { errors: string[]; - snapshot: CanvasSnapshot | null; + snapshot: RecipeSnapshot | null; }; diff --git a/studio/frontend/src/features/canvas-lab/utils/import/ui.ts b/studio/frontend/src/features/recipe-studio/utils/import/ui.ts similarity index 96% rename from studio/frontend/src/features/canvas-lab/utils/import/ui.ts rename to studio/frontend/src/features/recipe-studio/utils/import/ui.ts index 9fbf532103..ece4c4a1b5 100644 --- a/studio/frontend/src/features/canvas-lab/utils/import/ui.ts +++ b/studio/frontend/src/features/recipe-studio/utils/import/ui.ts @@ -1,4 +1,4 @@ -import type { CanvasNode, NodeConfig } from "../../types"; +import type { RecipeNode, NodeConfig } from "../../types"; import { DEFAULT_NODE_WIDTH } from "../../constants"; import { nodeDataFromConfig } from "../index"; import { isRecord, readString } from "./helpers"; @@ -54,7 +54,7 @@ export function parseUi( export function buildNodes( configs: NodeConfig[], layouts: Map, -): CanvasNode[] { +): RecipeNode[] { return configs.map((config, index) => { const fallbackLayout: { x: number; y: number; width?: number } = { x: 0, diff --git a/studio/frontend/src/features/canvas-lab/utils/index.ts b/studio/frontend/src/features/recipe-studio/utils/index.ts similarity index 99% rename from studio/frontend/src/features/canvas-lab/utils/index.ts rename to studio/frontend/src/features/recipe-studio/utils/index.ts index 3a6a07414e..5a58103683 100644 --- a/studio/frontend/src/features/canvas-lab/utils/index.ts +++ b/studio/frontend/src/features/recipe-studio/utils/index.ts @@ -1,5 +1,5 @@ import type { - CanvasNodeData, + RecipeNodeData, ExpressionConfig, ExpressionDtype, LayoutDirection, @@ -334,7 +334,7 @@ export function labelForExpression(type: ExpressionDtype): string { export function nodeDataFromConfig( config: NodeConfig, layoutDirection: LayoutDirection = "LR", -): CanvasNodeData { +): RecipeNodeData { if (config.kind === "sampler") { return { title: "Sampler", diff --git a/studio/frontend/src/features/canvas-lab/utils/layout.ts b/studio/frontend/src/features/recipe-studio/utils/layout.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/layout.ts rename to studio/frontend/src/features/recipe-studio/utils/layout.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/payload/build-payload.ts b/studio/frontend/src/features/recipe-studio/utils/payload/build-payload.ts similarity index 94% rename from studio/frontend/src/features/canvas-lab/utils/payload/build-payload.ts rename to studio/frontend/src/features/recipe-studio/utils/payload/build-payload.ts index 3e1b1f5311..0bb36ce16c 100644 --- a/studio/frontend/src/features/canvas-lab/utils/payload/build-payload.ts +++ b/studio/frontend/src/features/recipe-studio/utils/payload/build-payload.ts @@ -1,7 +1,7 @@ import type { Edge } from "@xyflow/react"; import type { - CanvasProcessorConfig, - CanvasNode, + RecipeProcessorConfig, + RecipeNode, ModelConfig, ModelProviderConfig, NodeConfig, @@ -15,7 +15,7 @@ import { buildProcessors, buildSamplerColumn, } from "./builders"; -import type { CanvasPayloadResult } from "./types"; +import type { RecipePayloadResult } from "./types"; import { isSemanticRelation, validateModelAliasLinks, @@ -25,7 +25,7 @@ import { validateUsedProviders, } from "./validate"; -function getNodeWidth(node: CanvasNode): number | null { +function getNodeWidth(node: RecipeNode): number | null { if (typeof node.width === "number" && Number.isFinite(node.width)) { return node.width; } @@ -42,12 +42,12 @@ function getNodeWidth(node: CanvasNode): number | null { } // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: payload build -export function buildCanvasPayload( +export function buildRecipePayload( configs: Record, - nodes: CanvasNode[], + nodes: RecipeNode[], edges: Edge[], - processors: CanvasProcessorConfig[] = [], -): CanvasPayloadResult { + processors: RecipeProcessorConfig[] = [], +): RecipePayloadResult { const errors: string[] = []; const columns: Record[] = []; const modelAliases = new Set(); diff --git a/studio/frontend/src/features/canvas-lab/utils/payload/builders.ts b/studio/frontend/src/features/recipe-studio/utils/payload/builders.ts similarity index 99% rename from studio/frontend/src/features/canvas-lab/utils/payload/builders.ts rename to studio/frontend/src/features/recipe-studio/utils/payload/builders.ts index 98e3b3aec6..8aafa13327 100644 --- a/studio/frontend/src/features/canvas-lab/utils/payload/builders.ts +++ b/studio/frontend/src/features/recipe-studio/utils/payload/builders.ts @@ -1,5 +1,5 @@ import type { - CanvasProcessorConfig, + RecipeProcessorConfig, CategoryConditionalParams, ExpressionConfig, LlmConfig, @@ -349,7 +349,7 @@ export function buildExpressionColumn( } export function buildProcessors( - processors: CanvasProcessorConfig[], + processors: RecipeProcessorConfig[], errors: string[], ): Record[] { const output: Record[] = []; diff --git a/studio/frontend/src/features/recipe-studio/utils/payload/index.ts b/studio/frontend/src/features/recipe-studio/utils/payload/index.ts new file mode 100644 index 0000000000..4cfbb4aa20 --- /dev/null +++ b/studio/frontend/src/features/recipe-studio/utils/payload/index.ts @@ -0,0 +1,2 @@ +export { buildRecipePayload } from "./build-payload"; +export type { RecipePayload, RecipePayloadResult } from "./types"; diff --git a/studio/frontend/src/features/canvas-lab/utils/payload/parse.ts b/studio/frontend/src/features/recipe-studio/utils/payload/parse.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/payload/parse.ts rename to studio/frontend/src/features/recipe-studio/utils/payload/parse.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/payload/types.ts b/studio/frontend/src/features/recipe-studio/utils/payload/types.ts similarity index 87% rename from studio/frontend/src/features/canvas-lab/utils/payload/types.ts rename to studio/frontend/src/features/recipe-studio/utils/payload/types.ts index ab37d1c943..d2aa976c96 100644 --- a/studio/frontend/src/features/canvas-lab/utils/payload/types.ts +++ b/studio/frontend/src/features/recipe-studio/utils/payload/types.ts @@ -1,4 +1,4 @@ -export type CanvasPayload = { +export type RecipePayload = { recipe: { // biome-ignore lint/style/useNamingConvention: api schema model_providers: Record[]; @@ -19,7 +19,7 @@ export type CanvasPayload = { }; }; -export type CanvasPayloadResult = { +export type RecipePayloadResult = { errors: string[]; - payload: CanvasPayload; + payload: RecipePayload; }; diff --git a/studio/frontend/src/features/canvas-lab/utils/payload/validate.ts b/studio/frontend/src/features/recipe-studio/utils/payload/validate.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/payload/validate.ts rename to studio/frontend/src/features/recipe-studio/utils/payload/validate.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/processors.ts b/studio/frontend/src/features/recipe-studio/utils/processors.ts similarity index 67% rename from studio/frontend/src/features/canvas-lab/utils/processors.ts rename to studio/frontend/src/features/recipe-studio/utils/processors.ts index 97e2d9fb97..4622ee9334 100644 --- a/studio/frontend/src/features/canvas-lab/utils/processors.ts +++ b/studio/frontend/src/features/recipe-studio/utils/processors.ts @@ -1,6 +1,6 @@ -import type { CanvasProcessorConfig } from "../types"; +import type { RecipeProcessorConfig } from "../types"; -export function buildDefaultSchemaTransform(): CanvasProcessorConfig { +export function buildDefaultSchemaTransform(): RecipeProcessorConfig { return { id: "schema-transform-1", // biome-ignore lint/style/useNamingConvention: api schema diff --git a/studio/frontend/src/features/canvas-lab/utils/refs.ts b/studio/frontend/src/features/recipe-studio/utils/refs.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/refs.ts rename to studio/frontend/src/features/recipe-studio/utils/refs.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/validation.ts b/studio/frontend/src/features/recipe-studio/utils/validation.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/validation.ts rename to studio/frontend/src/features/recipe-studio/utils/validation.ts diff --git a/studio/frontend/src/features/canvas-lab/utils/variables.ts b/studio/frontend/src/features/recipe-studio/utils/variables.ts similarity index 100% rename from studio/frontend/src/features/canvas-lab/utils/variables.ts rename to studio/frontend/src/features/recipe-studio/utils/variables.ts diff --git a/studio/frontend/vite.config.ts b/studio/frontend/vite.config.ts index 6a3d48318d..41d81b7be7 100644 --- a/studio/frontend/vite.config.ts +++ b/studio/frontend/vite.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ target: "http://127.0.0.1:8000", changeOrigin: true, }, - "/preview-api": { + "/preview": { target: "http://127.0.0.1:8004", changeOrigin: true, },