feat(ai-studio): public AI workflow demo - Visualize node, agent web search, richer templates#48
Draft
librowski wants to merge 33 commits into
Draft
feat(ai-studio): public AI workflow demo - Visualize node, agent web search, richer templates#48librowski wants to merge 33 commits into
librowski wants to merge 33 commits into
Conversation
…laimer Replace the internal Synergy sales-inquiry template with a generic, relatable Customer Support Triage flagship (classify -> route by type -> specialist draft -> QA), built on the existing trigger/agent/decision nodes. Auto-load it on first visit via initialNodes/initialEdges on WorkflowBuilder.Root so visitors land on a runnable workflow instead of a blank canvas; a returning visitor's saved diagram still wins. Add a dismissible first-visit disclaimer modal stating the workflows run real OpenRouter calls, that the demo is not a model benchmark, and that its purpose is to showcase Workflow Builder.
Cheaper, fast default for the public demo (roughly an order of magnitude cheaper than claude-3.5-haiku). Quality-per-cost is what matters here - the model is the engine, not the product. Override with the AI_MODEL env var.
POST /api/workflows/:id/execute is the only endpoint that spends real LLM budget, so it gets a per-IP fixed-window rate limit plus an optional Cloudflare Turnstile check, applied right after authorization via guardExecution(). Both controls are no-ops when unconfigured (TURNSTILE_SECRET_KEY empty), so local dev runs unprotected; the rate limit is active out of the box as a budget backstop. Turnstile verification fails closed on a verifier error.
Before the execute request, fetch a Turnstile token from an invisible widget and send it as the cf-turnstile-token header. Degrades gracefully: with no VITE_TURNSTILE_SITE_KEY the token is undefined, no header is sent, and the backend skips verification - so local dev needs no keys.
…templates Grow the picker into a small gallery of relatable, runnable examples beyond the flagship: AI Debate (parallel personas that fan out then converge on a verdict), Content Repurposer (one post fanned out to three channel variants), and Meeting Notes to Action Items (a linear summarize -> extract -> format chain). All use the existing trigger/agent/decision nodes.
Generalize the on-canvas preview node from markdown-only to a future generic Visualizer: type ai-studio/markdown-preview -> ai-studio/visualize, palette label Visualize (Eye icon), worker domain VisualizeNode + executeVisualize + registry, flagship node preview-1 -> visualize-1. Rendering is still markdown for now; renderer set, format detection, charts, diagrams, export and expand land in the following commits. Includes the visualize plan and long-work backlog.
Heuristic detectFormat(text) -> {renderer, data?, chartable?} mapping an output
string to a renderer for the Visualize node's auto mode: mermaid->diagram,
JSON->{chart for {label,value}/{type,data}, table for object arrays, stat-cards
for flat scalar objects, json for nested}, CSV->table, else markdown. Conservative
(chart/diagram only on clear signals), prose falls back to markdown. 11 unit tests.
Adds a 'Render as' select to the Visualize node: VISUALIZE_MODES = auto + markdown/text/json/table/stat-cards/chart/diagram, default auto. Auto detects the format; the rest force a specific renderer.
Add a renderer registry (getRenderer) with markdown, text, JSON tree (collapsible, hand-rolled), table (JSON array / CSV rows), and stat-cards (flat object) renderers. The visualize card now runs detectFormat (or the node's mode), picks the renderer, shows an 'Auto > X' badge, and renders into a framed body. chart and diagram fall back to table/text for now (real renderers land next).
The visualize card now renders for the node at all times with a fixed minimum
size: an empty-state placeholder ('The visualization appears here after you run
the workflow') before a run, a generating indicator while running, and the
rendered output on completion. The reveal animation moved from the card frame to
the content so it plays when the result arrives, not on every render.
Add a lazy-loaded recharts renderer (bar/line/area/pie) for the chart mode and
chart-spec envelopes ({type, data}) or {label,value}/{x,y} arrays. recharts only
loads when a chart is shown (React.lazy + Suspense). The card shows a 'Try as
chart' chip when auto-detected data is chartable but rendered as a table.
Add a lazy-loaded mermaid renderer for the diagram mode: it renders a mermaid source string to SVG in the browser (securityLevel strict), and falls back to the raw text on a syntax error. mermaid (~150KB) only loads when a diagram is shown (React.lazy).
Add export-visualization util (html-to-image): download PNG, copy image to clipboard (with a download fallback on browsers like Firefox that cannot write image blobs), copy source text, and download SVG (native serialization fast-path for chart/diagram). The card header now offers Copy image / Download PNG / Copy source actions over the rendered content.
Add an Expand action that opens the visualization full-size in a modal (rendered through a portal to document.body so its fixed overlay escapes the React Flow viewport transform). The modal renders the same renderer larger and offers PNG / SVG / copy-image / copy-source export.
…omment All visualize work items complete and verified (test/typecheck/lint/build). chart/diagram visual smoke deferred (needs a structured upstream); covered by unit tests + build (lazy chunks confirmed).
Move the visualization from a detached floating panel into the node itself: the content renders in-flow inside the node body (via OptionalNodeContent), so the node grows vertically to contain it and reads as one cohesive card. Drops the redundant card title (the node header already shows it) in favor of a slim badge + export/expand toolbar; the body scrolls when the output is long. Empty state and the expand modal are unchanged.
Two causes of the giant 'Syntax error in text / mermaid version' graphics:
- The diagram renderer called mermaid.render on invalid input, and mermaid
injects its error graphic into the DOM on failure. Now it validates with
mermaid.parse({ suppressErrors: true }) first and falls back to raw text
(with a note) without ever calling render, so nothing is injected.
- Auto-detection matched a bare leading word (graph/pie/timeline/journey), so
prose starting with those was mis-detected as a diagram. Detection is now
strict: a fenced ```mermaid block, or flowchart/graph WITH a direction, or a
distinctive declaration keyword. Prose stays markdown. (+3 tests, 13 total)
Adapt mixed output to the right format without an LLM: the markdown renderer now renders a fenced ```mermaid block as a real diagram and a ```json block via the detected renderer (chart/table/...), so a markdown response with an embedded diagram renders it inline instead of as raw code. The diagram and chart renderers also extract a fenced block from a larger response when their mode is forced.
Adds an opt-in 'AI adapt' action on the Visualize node: it sends the upstream output to a new backend endpoint (POST /api/visualize/adapt) that uses an LLM to convert it into the active format (clean Mermaid / chart JSON / table / ...), then renders the result. The endpoint reuses the execution abuse gate (per-IP rate limit + optional Turnstile) and is disabled (501) when the backend has no OPENROUTER_API_KEY. Verified end-to-end: action-items prose -> clean mermaid.
Adds an 'AI: adapt output to this format' Switch to the Visualize node's properties panel. When on, the node automatically LLM-converts the upstream output into the active render format on each run (no need to click the on-card button, which is hidden while the toggle is on). Off keeps the manual on-card adapt button for structured formats.
The format-adapt prompts were terse and gave the model little guidance, so conversions were mediocre on a small model. Each format now has detailed, role-framed instructions (pick the fitting diagram type and quote labels safely; find a category + numeric measure and aggregate for charts; extract 2-8 KPIs for stat-cards; use only facts, no invention) plus temperature 0.2 and output trim. JSON renderers also strip a fenced block in case the model wraps the output.
LLM-produced field values often contain markdown (bold, lists, links, inline code). A new RichText helper renders such strings as markdown in table cells and stat-card values, while leaving plain strings (e.g. 'user_id') untouched so they are not mangled. Raw HTML stays escaped (no rehype-raw) to avoid XSS from untrusted model output.
…sualize label - Set explicit text color (theme token) on the Execution Log and node-detail panels so their text stays readable in dark mode instead of inheriting a dark color. - Align the welcome modal's icon and title on one horizontal row. - Shorten the Visualize node's palette description so its subtitle fits.
Hide the SDK app-bar logo (no prop to replace or link it yet) and render the Workflow Builder CDN logo over the slot, linked to workflowbuilder.io. Swap the transparent dark-text / white-text variants by the SDK theme so the logo reads on both light and dark app bars (the -solid asset bakes in a white background). Remove once the SDK exposes a logo/logoHref prop.
display:none on the SDK logo collapsed its flex box, sliding the Save button left under the fixed logo overlay. Hide the logo with visibility and reserve its width instead so the nav-segment keeps its place.
End the debate flow in a Visualize node and shape the verdict as markdown (verdict line + For/Against table + reasoning) so it renders richly.
End the flow in a Visualize node and shape the recap as markdown with an action-items table (owner/task/due) so it renders as a structured artifact.
Add a Content Pack agent that merges the three channel drafts into one sectioned markdown document, then a Visualize node to render it. Keeps the fan-out and gives the flow a single final artifact.
Remove the unused isTurnstileEnabled helper and de-export symbols that are only used within their own module (renderers, VISUALIZE_MODES/VisualizeMode, DetectResult, VisualizeNode). knip-clean across ai-studio, backend, worker.
Remove the long-work backlog and crystallize plan; agent-process scratch, not product docs for the public repo. Net diff vs main no longer includes them.
The ai-agent palette item had no outputSchema, so {{ nodes.<id>.response }}
references (e.g. in decision conditions) fell through to the SDK's unresolved
'missing mention' label. Declaring the response output renders a clean
'Classify Ticket · Response' pill instead.
Add an opt-in 'Web search' toggle to the AI Agent node. When enabled and TAVILY_API_KEY is set, the executor gives the agent a Tavily-backed web_search tool via the AI SDK tool-calling loop (capped at a few steps). The loop runs inside the activity, so it needs no graph cycles and stays DAG-compatible. Without the key the node runs unchanged (tool simply not exposed).
A Trigger -> Research Agent (web search enabled) -> Visualize flow that showcases the AI Agent web-search tool: the agent searches and writes a sourced markdown brief, rendered in the Visualize node. Needs TAVILY_API_KEY to actually search; runs without it, just answers from the model.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AI Studio public demo: Visualize node, agent web search, richer templates, demo hardening
What & why
Turns
apps/ai-studio(the SDK reference app) into a polished, runnable public demo of Workflow Builder. Visitors land on a real, executable workflow, can run it against live LLM calls, and see results rendered in-canvas.Scope:
apps/**only - zeropackages/sdkchanges, so no changeset needed. The published SDK is untouched; everything here is reference-app / backend / worker code.Highlights
Demo bootstrap
google/gemini-2.5-flash-lite).Visualize node (generic output renderer)
modeparam.POST /api/visualize/adapt(optional, 501 when no key).```mermaid/```jsonblocks render inline.Templates
Agent web search (tool calling)
TAVILY_API_KEY→ tool simply not exposed, node runs unchanged.Fixes & polish
outputSchemaso{{ nodes.<id>.response }}mentions resolve to a clean pill instead of an unresolved label.Hygiene
Configuration (all optional, features degrade gracefully)
OPENROUTER_API_KEY- worker (run workflows) + backend (Visualize AI adapt).TAVILY_API_KEY- worker (agent web search). Free tier ~1000/mo.Verification
Known issues / follow-ups (not in this PR)
apps/docshas 4 pre-existingastro checktype errors, unrelated to this branch (tracked: WB-326).arch.png/render-dark.pngat repo root are left untracked (not part of this PR).🤖 Generated with Claude Code