diff --git a/apps/docs/.gitignore b/apps/docs/.gitignore index 2473d52587b..d4dabf71e70 100644 --- a/apps/docs/.gitignore +++ b/apps/docs/.gitignore @@ -38,3 +38,6 @@ next-env.d.ts # Fumadocs /.source/ .plans/ + +# fumadocs generates .source dirs anywhere a source.config sits +**/.source/ diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index eaae5fe1c57..86539c52302 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx @@ -75,16 +75,23 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l } const isOpenAPI = '_openapi' in data && data._openapi != null const isApiReference = slug?.some((s) => s === 'api-reference') ?? false + // Academy lessons are video-first: drop the "On this page" TOC and go full + // width so the lesson hero/video gets the room (chapters live in-page instead). + const isAcademy = slug?.[0] === 'academy' const pageTreeRecord = source.pageTree as Record const pageTree = pageTreeRecord[lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0] const rawNeighbours = pageTree ? findNeighbour(pageTree, page.url) : null - const neighbours = isApiReference + // Academy and API Reference are self-contained sections; keep prev/next inside + // the section instead of spilling into the main documentation tree. Match both + // the section's pages (`//...`) and its index (`/`). + const sectionSlug = isApiReference ? 'api-reference' : isAcademy ? 'academy' : null + const inSection = (url?: string) => + url != null && (url.includes(`/${sectionSlug}/`) || url.endsWith(`/${sectionSlug}`)) + const neighbours = sectionSlug ? { - previous: rawNeighbours?.previous?.url.includes('/api-reference/') - ? rawNeighbours.previous - : undefined, - next: rawNeighbours?.next?.url.includes('/api-reference/') ? rawNeighbours.next : undefined, + previous: inSection(rawNeighbours?.previous?.url) ? rawNeighbours?.previous : undefined, + next: inSection(rawNeighbours?.next?.url) ? rawNeighbours?.next : undefined, } : rawNeighbours @@ -197,18 +204,18 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l /> !p.includes('/api-reference'), + match: (p: string) => !p.includes('/api-reference') && !p.includes('/academy'), + external: false, + }, + { + label: 'Academy', + href: '/academy', + match: (p: string) => p.includes('/academy'), external: false, }, { diff --git a/apps/docs/components/ui/video-chapters.tsx b/apps/docs/components/ui/video-chapters.tsx new file mode 100644 index 00000000000..ffdbbcd5610 --- /dev/null +++ b/apps/docs/components/ui/video-chapters.tsx @@ -0,0 +1,76 @@ +'use client' + +import { useEffect, useState } from 'react' +import { CirclePlay } from 'lucide-react' +import { cn } from '@/lib/utils' + +/** Parse a chapter timestamp ("M:SS" or "H:MM:SS") into seconds. */ +function parseTime(time: string): number { + const parts = time.split(':').map(Number) + if (parts.some(Number.isNaN)) return 0 + return parts.reduce((acc, n) => acc * 60 + n, 0) +} + +interface Chapter { + /** Chapter label. */ + title: string + /** Timestamp, e.g. "0:45". */ + time?: string +} + +interface VideoChaptersProps { + /** Panel heading. Defaults to "Chapters". */ + title?: string + chapters: Chapter[] + className?: string +} + +/** + * Right-rail panel listing the current video's chapters, styled to match the + * Academy's course panels. Rows are skip-to controls; they activate once the + * lesson's video is recorded. + */ +export function VideoChapters({ title = 'Chapters', chapters, className }: VideoChaptersProps) { + // Chapters only seek when a VideoPlaceholder with a real video is on the page. + // Handshake so the rows stay inert (not falsely clickable) on video-less lessons. + const [hasVideo, setHasVideo] = useState(false) + useEffect(() => { + const onReady = () => setHasVideo(true) + window.addEventListener('academy:video-ready', onReady) + window.dispatchEvent(new Event('academy:video-query')) + return () => window.removeEventListener('academy:video-ready', onReady) + }, []) + + return ( + + ) +} diff --git a/apps/docs/components/ui/video-placeholder.tsx b/apps/docs/components/ui/video-placeholder.tsx new file mode 100644 index 00000000000..c631bf35e9a --- /dev/null +++ b/apps/docs/components/ui/video-placeholder.tsx @@ -0,0 +1,206 @@ +'use client' + +import { useEffect, useRef, useState } from 'react' +import { cn, getAssetUrl } from '@/lib/utils' + +interface VideoPlaceholderProps { + /** Large title shown on the hero. */ + title?: string + /** Small italic eyebrow above the title, e.g. a module name. */ + eyebrow?: string + /** Pill in the top-right corner. Defaults to "Coming soon" (shown only until a video is set). */ + label?: string + /** + * Self-hosted video source. Accepts an absolute URL, a root-relative path + * (`/static/...`), or a bare asset name resolved through the Blob CDN. When + * set, the play button loads the video; otherwise the card is "coming soon". + */ + src?: string + className?: string +} + +/** Resolve a video source: pass absolute/root-relative through, send bare names to the Blob CDN. */ +function resolveVideoSrc(src: string): string { + if (/^https?:\/\//.test(src) || src.startsWith('/')) return src + return getAssetUrl(src) +} + +/** The sim logotype, drawn with currentColor so the theme can tint it. */ +function SimWordmark({ className }: { className?: string }) { + return ( + + + + + + + ) +} + +/** + * A 16:9 lesson hero used across the Academy. Always shows the design-system + * video card (title, blueprint grid, theme-aware dark/light). When a `src` is + * provided the play button loads the self-hosted video inline; otherwise the + * card reads "Coming soon" and the play button is muted. + */ +export function VideoPlaceholder({ + title, + eyebrow, + label = 'Coming soon', + src, + className, +}: VideoPlaceholderProps) { + const hasVideo = Boolean(src) + const [playing, setPlaying] = useState(false) + const videoRef = useRef(null) + const pendingSeek = useRef(null) + + // Chapter rows (VideoChapters) dispatch `academy:seek` with a time in seconds. + // Start the video if it isn't playing yet, then jump there. We also announce + // that a video exists (and answer a chapters-side query) so the chapter rows + // only become interactive when there's actually something to seek. + useEffect(() => { + if (!src) return + const onSeek = (e: Event) => { + const time = (e as CustomEvent<{ time: number }>).detail?.time + if (typeof time !== 'number') return + const video = videoRef.current + if (video) { + video.currentTime = time + void video.play() + } else { + pendingSeek.current = time + setPlaying(true) + } + } + const announce = () => window.dispatchEvent(new Event('academy:video-ready')) + window.addEventListener('academy:seek', onSeek) + window.addEventListener('academy:video-query', announce) + announce() + return () => { + window.removeEventListener('academy:seek', onSeek) + window.removeEventListener('academy:video-query', announce) + } + }, [src]) + + if (playing && src) { + return ( +
+ {/* biome-ignore lint/a11y/useMediaCaption: lesson videos have no caption track yet */} +
+ ) + } + + return ( +
+ {/* Blueprint grid — faint, fading to atmosphere at the edges */} +
+ + {/* Corner plus-marks, 20px inset */} + {['top-5 left-5', 'top-5 right-5', 'bottom-5 left-5', 'right-5 bottom-5'].map((pos) => ( + + + + + ))} + + {/* Top-right status pill — only until a video is wired up */} + {!hasVideo && ( + + + {label} + + )} + + {/* Heading: eyebrow + title, bottom-left (design: left:40 bottom:40) */} +
+ {eyebrow && ( + + {eyebrow} + + )} + {title && ( + + {title} + + )} +
+ + {/* Wordmark, bottom-right (design: right:40 bottom:40, svg height 22) */} + + + + + {/* Centered play button — active when a video is wired, muted otherwise */} +
+ {hasVideo ? ( + + ) : ( + + + + + + )} +
+
+ ) +} diff --git a/apps/docs/components/ui/what-you-will-learn.tsx b/apps/docs/components/ui/what-you-will-learn.tsx new file mode 100644 index 00000000000..042bbe764af --- /dev/null +++ b/apps/docs/components/ui/what-you-will-learn.tsx @@ -0,0 +1,30 @@ +import { cn } from '@/lib/utils' + +interface LearnItem { + title: string + body: string +} + +interface WhatYouWillLearnProps { + items: LearnItem[] + className?: string +} + +/** A bordered "What you will learn" card listing lesson takeaways. */ +export function WhatYouWillLearn({ items, className }: WhatYouWillLearnProps) { + return ( +
+

What you will learn

+
+ {items.map((item) => ( +
+

{item.title}

+

{item.body}

+
+ ))} +
+
+ ) +} diff --git a/apps/docs/content/docs/en/academy/agents/intro.mdx b/apps/docs/content/docs/en/academy/agents/intro.mdx new file mode 100644 index 00000000000..a9fb034c3c2 --- /dev/null +++ b/apps/docs/content/docs/en/academy/agents/intro.mdx @@ -0,0 +1,88 @@ +--- +title: Agents +description: An AI agent is a Sim workflow that uses Agent blocks — the reasoning engine you shape, parameterize, and compose into intelligent processes. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, CLASSIFY_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +In Sim, you build AI agents as **workflows that use the Agent block**. There's no separate "agent" object to learn — an AI agent *is* a workflow, and the Agent block is where it thinks. + + + +
+ + + +
+ +## An agent is a workflow + +This is the one idea to hold onto: **an AI agent is a workflow** — one built around the Agent block. Everything you learned about [workflows](/academy/workflows/intro) applies. You don't leave the model; you add reasoning to it. + +Here's the shape of it — a workflow built around an Agent block where it thinks: + + + +## Inside the Agent block + +The Agent block is **a chat with a model, spawned programmatically**. Picture a conversation in ChatGPT or Claude: the block's **Messages** parameter is exactly those messages, except you set them — you decide what instructions, context, and earlier outputs go in. The model reasons over that and returns a result the rest of the workflow can read. + +The Agent block is where it *thinks* — the reasoning engine of your agent. You design the blocks *around* it to shape how the agent processes data: what it reads before it reasons, what it does with the result after. + +## Tools and skills + +Two parameters extend the Agent block, and both fit the chat-box picture: + +- **Tools** are the functions and blocks the agent can decide to call while answering — search the web, send an email, run another workflow. They're the tool calls the model makes mid-response. +- **Skills** are prompt modules the model loads on demand — like a manual it looks up only when a task calls for it, instead of stuffing everything into every prompt. + +## Make a workflow agentic + +The clearest way to see an agent isn't to build one from nothing — it's to take a **deterministic workflow and let an agent into it**. Replace a fixed rule with an agent that *decides* the path (routing), or *augment* a step with real reasoning — or both. Same workflow, now with judgment in it. + +## Composing intelligence + +Your ability to compose workflows that make **multiple agent calls** is central to designing intelligent processes. And every agentic workflow you build becomes a building block — your workspace's library of agents composes into larger AI systems. + +Start with one Agent block; the platform scales as your ambition does. The most sophisticated systems are just more of these, composed — more agent blocks swapping in, more reasoning, more reach, all from the same model you learned here. + +## Related documentation + +- [Agents overview](/agents) +- [Choosing a model](/agents/choosing) +- [Custom tools](/agents/custom-tools) +- [MCP](/agents/mcp) +- [Skills](/agents/skills) diff --git a/apps/docs/content/docs/en/academy/files/intro.mdx b/apps/docs/content/docs/en/academy/files/intro.mdx new file mode 100644 index 00000000000..ea9900e38ca --- /dev/null +++ b/apps/docs/content/docs/en/academy/files/intro.mdx @@ -0,0 +1,75 @@ +--- +title: Files +description: Sim natively supports files — store, reference, and pass around documents and media so workflows can read and produce real assets like PDFs, images, audio, and video. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, FILE_SUMMARY_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +Sim has native support for **files** — a way to store, reference, and pass around documents, media, and generated outputs, so your workflows can read and produce real assets: PDFs, images, audio, video. + + + +
+ + + +
+ +## Why you need files + +Most of the work you'd want to automate touches files somewhere. You receive a PDF and need to extract from it. An email needs an image attached. Audio comes in and has to be transcribed. A user uploads a document to process. These aren't edge cases — workflows reference file types as inputs and outputs all the time, so files are a first-class thing in Sim rather than something you work around. + +Here's the shape of it — a workflow that takes a file in and produces a result from it: + + + +## Files in and out + +Workflows **consume** files and **produce** them, and hand them between blocks like any other value. A file goes in, a block reads it; a block creates one, the next step uses it. + +## What you can build + +The point isn't a new concept to learn — it's recognizing that the file-based operations you already picture are supported here: + +- Attach a generated report to an email. +- Transcribe an audio clip that came in through a trigger. +- Read an uploaded image and describe it with a vision model. +- Extract structured fields from a batch of PDFs. +- Produce a rendered document, chart, or audio file as a workflow's output. + +If you're thinking "could I automate the thing that involves *that* document?" — the answer is almost always yes. Files are how Sim handles the real assets your processes already run on. + +## Related documentation + +- [Files overview](/files) +- [Using files in workflows](/files/using-in-workflows) +- [Generating files](/files/generating) diff --git a/apps/docs/content/docs/en/academy/index.mdx b/apps/docs/content/docs/en/academy/index.mdx new file mode 100644 index 00000000000..930824142f7 --- /dev/null +++ b/apps/docs/content/docs/en/academy/index.mdx @@ -0,0 +1,44 @@ +--- +title: What is Sim? +description: Sim is a unified workspace for building and operating AI systems — data, workflows, and the integrations that connect them to the outside world. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' + + + +Sim is a unified workspace for **building and operating AI systems**. Everything you make lives in one place, and everything connects. + + + +## The workspace + +A **workspace** is a collection of shared resources and the workflows that use them. + +- **Resources** are your data: [tables](/academy/tables/intro) (structured records), [knowledge bases](/academy/knowledge-bases/intro) (searchable memory), and [files](/academy/files/intro) (documents and media). +- **Workflows** are your processes — the agents and automations that read those resources, act, and write results back. + +Everything in a workspace can see everything else in it. A workflow reads a table, searches a knowledge base, produces a file — and the next workflow picks up where it left off. + +## Integrations connect you to the outside world + +**Integrations** are plugins. They let your resources and workflows reach services beyond Sim — send a Slack message, read a Gmail inbox, write a row to a CRM, call any API. You connect an account once, and any workflow can use it. + +So the whole picture is small: a workspace is **data + processes**, integrations plug it into **everything else**, and the rest of this course is just learning each piece and watching them compose. + +## Related documentation + +- [Introduction](/introduction) +- [Getting started](/getting-started) diff --git a/apps/docs/content/docs/en/academy/knowledge-bases/intro.mdx b/apps/docs/content/docs/en/academy/knowledge-bases/intro.mdx new file mode 100644 index 00000000000..850dc6d8c2a --- /dev/null +++ b/apps/docs/content/docs/en/academy/knowledge-bases/intro.mdx @@ -0,0 +1,85 @@ +--- +title: Knowledge Bases +description: Knowledge bases give your workflows searchable memory — large volumes of text an agent can search by meaning and use to ground its answers. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, SUPPORT_KB_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +A **knowledge base** gives your workflows searchable memory. You put in large volumes of text, and an agent retrieves just the parts that matter — by meaning, not keywords. + + + +
+ + + +
+ +## The context problem + +The naive way to give a model knowledge is to paste everything into its context. That breaks down fast — too much text, too much cost, too much noise. A knowledge base is the answer: store the corpus once, and at run time pull back only the passages that bear on the question. + +Here's the shape of it — a workflow that searches a knowledge base and grounds the answer in what comes back: + + + +## Inside a knowledge base + +A knowledge base is a resource that lives in your workspace — you can have several. Open one and it holds **documents**, plus **connectors** that bring in and keep documents in sync from outside sources. + +Sim automatically **indexes and embeds** every document into searchable **chunks**, so the contents become queryable by meaning — you don't manage any of that yourself. + +## Searching by meaning + +Use a Knowledge block with a query and it returns the most relevant chunks, each with a **similarity score** — how closely it matches the *meaning* of the query, not its words. So "refund timelines" can match a passage that says "we process returns within 14 days," even with no shared words. + +## Grounding an answer + +On its own, that's retrieval. The power comes one step later: feed those chunks into an [Agent](/academy/agents/intro) block's context, and the model answers from your actual documents — accurate, up to date, and able to **cite** where each fact came from. That's grounding plus retrieval. + +Knowledge bases power systems that need to give agents accurate, current context — and they're where you store what your systems learn over time. + +## Related documentation + +- [Knowledge Bases overview](/knowledgebase) +- [Using a knowledge base in workflows](/knowledgebase/using-in-workflows) +- [Connectors](/knowledgebase/connectors) diff --git a/apps/docs/content/docs/en/academy/meta.json b/apps/docs/content/docs/en/academy/meta.json new file mode 100644 index 00000000000..7cda92f55ff --- /dev/null +++ b/apps/docs/content/docs/en/academy/meta.json @@ -0,0 +1,28 @@ +{ + "title": "Academy", + "root": true, + "pages": [ + "---Get Started---", + "index", + "---Workflows---", + "workflows/intro", + "workflows/logs", + "workflows/branching", + "workflows/loops", + "workflows/subworkflows", + "workflows/deployment", + "---Agents---", + "agents/intro", + "---Tables---", + "tables/intro", + "---Files---", + "files/intro", + "---Knowledge Bases---", + "knowledge-bases/intro", + "---Use Cases---", + "use-cases/slack-it-triage", + "use-cases/document-extraction", + "use-cases/monitoring-research", + "use-cases/sales-data-enrichment" + ] +} diff --git a/apps/docs/content/docs/en/academy/tables/intro.mdx b/apps/docs/content/docs/en/academy/tables/intro.mdx new file mode 100644 index 00000000000..de736769903 --- /dev/null +++ b/apps/docs/content/docs/en/academy/tables/intro.mdx @@ -0,0 +1,80 @@ +--- +title: Tables +description: Tables store structured data — columns as types, rows as entries — a spreadsheet or lightweight database your workflows read, write, and operate on at scale. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, TABLE_ENRICH_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +A **table** stores structured data: columns are the types, rows are the entries. Think of it as giving your workflows a spreadsheet — or a lightweight database — for tracking and maintaining records. + + + +
+ + + +
+ +## A familiar shape + +If you've used a spreadsheet, you already understand a table: columns with types, rows of entries. Your workflows read rows to work on, write rows they produce, and update rows in place. + +Here's the shape of it — a workflow that reads a table, operates on each record, and writes the result back: + + + +## Operations over data + +A surprising amount of real business work decomposes into a few operations over structured records: + +- **A query** — "which leads are still unprocessed?" +- **An update** — "mark these rows handled." +- **A combination with logic** — "for each new signup, score it, then write the score back." + +Once you see a use case that way, building it in Sim is mostly wiring those operations together. + +## A surface for scale + +Tables aren't only storage — they're a working surface for your AI systems. **Workflow columns** let you run an operation across every row at once, in parallel, so a table becomes the place you launch and track work in bulk. + +That makes a table a powerful interface for automation — the place you manage and operate agentic processes at scale. + +## Related documentation + +- [Tables overview](/tables) +- [Using tables in workflows](/tables/using-in-workflows) +- [Workflow columns](/tables/workflow-columns) diff --git a/apps/docs/content/docs/en/academy/use-cases/document-extraction.mdx b/apps/docs/content/docs/en/academy/use-cases/document-extraction.mdx new file mode 100644 index 00000000000..445237a5b18 --- /dev/null +++ b/apps/docs/content/docs/en/academy/use-cases/document-extraction.mdx @@ -0,0 +1,43 @@ +--- +title: Document Intake & Extraction +description: A pipeline that takes incoming documents, extracts the fields you care about, and writes structured rows you can act on. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' + + + +
+
+ +Turn a pile of unstructured documents — invoices, contracts, forms — into structured data. The workflow reads each file, extracts the fields that matter, and writes them as rows you can query and process. + + + +
+ + + +
+ +## What you'll build + +A **file** comes in, an **Agent** block extracts the fields into a [structured output](/academy/agents/intro) so the shape is predictable, and a **Table** block writes the row. Run it across a batch and a folder of documents becomes a queryable table. + +This composes [files](/academy/files/intro), [agents](/academy/agents/intro), and [tables](/academy/tables/intro). diff --git a/apps/docs/content/docs/en/academy/use-cases/monitoring-research.mdx b/apps/docs/content/docs/en/academy/use-cases/monitoring-research.mdx new file mode 100644 index 00000000000..7f1c900b453 --- /dev/null +++ b/apps/docs/content/docs/en/academy/use-cases/monitoring-research.mdx @@ -0,0 +1,43 @@ +--- +title: Monitoring & Automated Research +description: A scheduled agent that watches sources you care about, researches what changed, and reports back — on its own. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' + + + +
+
+ +Build a system that runs on a schedule, checks the sources you care about, researches what's new, and delivers a synthesized report — competitor moves, market signals, anything worth watching — without you asking each time. + + + +
+ + + +
+ +## What you'll build + +A **Schedule trigger** kicks the workflow off. An **Agent** block with search tools gathers and reads sources, synthesizes what changed, and a final step delivers it — a Slack message, or a saved report [file](/academy/files/intro). + +This composes [workflows](/academy/workflows/intro), [agents](/academy/agents/intro), and [files](/academy/files/intro). diff --git a/apps/docs/content/docs/en/academy/use-cases/sales-data-enrichment.mdx b/apps/docs/content/docs/en/academy/use-cases/sales-data-enrichment.mdx new file mode 100644 index 00000000000..5921598be05 --- /dev/null +++ b/apps/docs/content/docs/en/academy/use-cases/sales-data-enrichment.mdx @@ -0,0 +1,43 @@ +--- +title: Sales Data Management & Enrichment +description: A table-backed system that takes thin lead records, enriches them in parallel, scores them, and writes the results back. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' + + + +
+
+ +Run your sales pipeline as data. Start with thin lead records in a table, enrich each one — company info, fit signals — score it, and write the results back, all in parallel across the whole table. + + + +
+ + + +
+ +## What you'll build + +Leads live in a **table**. A **workflow column** runs per row: it enriches the record, an **Agent** block scores the fit, and the results are written straight back into the table. The table becomes both the queue and the record. + +This is [tables](/academy/tables/intro) and [agents](/academy/agents/intro) composed into a pipeline that operates at scale. diff --git a/apps/docs/content/docs/en/academy/use-cases/slack-it-triage.mdx b/apps/docs/content/docs/en/academy/use-cases/slack-it-triage.mdx new file mode 100644 index 00000000000..bc7b4e4c41c --- /dev/null +++ b/apps/docs/content/docs/en/academy/use-cases/slack-it-triage.mdx @@ -0,0 +1,43 @@ +--- +title: Slack IT Triage Bot +description: An agent that watches a Slack channel, classifies incoming IT requests, answers what it can from your docs, and routes the rest. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' + + + +
+
+ +Build an agent that lives in a Slack channel: it reads each incoming IT request, decides what kind it is, answers the ones it can from your internal docs, and routes the rest to the right person. + + + +
+ + + +
+ +## What you'll build + +A **Slack trigger** starts the workflow on each new message. An **Agent** block classifies the request and decides the path: a **knowledge-base search** drafts an answer for common questions, while anything it can't resolve is routed to a human or filed as a ticket. Every run is recorded in the [logs](/logs-debugging). + +This pulls together [workflows](/academy/workflows/intro), [agents](/academy/agents/intro), and [knowledge bases](/academy/knowledge-bases/intro) into one system — the foundations, composed. diff --git a/apps/docs/content/docs/en/academy/workflows/branching.mdx b/apps/docs/content/docs/en/academy/workflows/branching.mdx new file mode 100644 index 00000000000..e721089334a --- /dev/null +++ b/apps/docs/content/docs/en/academy/workflows/branching.mdx @@ -0,0 +1,76 @@ +--- +title: Branching +description: Split a workflow into different paths based on the result of a step — the Condition block decides with a rule, the Router lets a model decide. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, CONDITION_ROUTE_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +Branching is how a workflow **splits into different paths based on the result of a step**. Sim gives you two blocks for it — the **Condition** block, which decides with an explicit rule, and the **Router**, which lets a model decide from intent. + + + +
+ + + +
+ +## A fork in the flow + +Most workflows you've seen run in a line. Branching adds a **fork**: at some point the run looks at the result of a step and takes one path or another. The paths can do completely different work. + +Here's the shape of a fork — one step decides, and the run takes one path or the other: + + + +## The Condition block — decide with a rule + +The **Condition** block branches on an **explicit rule** — a comparison over an earlier block's output, like priority equals "high". The matching branch runs; the others don't. It's deterministic and predictable. + +## The Router block — let a model decide + +Sometimes the rule is fuzzy: "send billing questions one way, everything else another." The **Router** block hands the decision to a **model** — it reads the input, picks the branch that matches the intent, and the run continues down it. Same fork, but the decider is reasoning instead of a fixed rule. + +## Still one workflow + +Whichever way it forks, it's **still one workflow**, and every run is recorded. Open the logs and you can see exactly which path a given run took, and why. + +## Related documentation + +- [How workflows run](/workflows/how-it-runs) +- [Condition block](/workflows/blocks/condition) +- [Router block](/workflows/blocks/router) diff --git a/apps/docs/content/docs/en/academy/workflows/deployment.mdx b/apps/docs/content/docs/en/academy/workflows/deployment.mdx new file mode 100644 index 00000000000..cac52a21ca6 --- /dev/null +++ b/apps/docs/content/docs/en/academy/workflows/deployment.mdx @@ -0,0 +1,85 @@ +--- +title: Deployment +description: Deploying gives a workflow an address so the outside world can run it — the same Start block answers calls as an API, a chat, or an MCP tool. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, RESPONSE_API_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +Deploying publishes a workflow and gives it an **address**, so something other than the editor can run it. Nothing inside the workflow changes — the same blocks, the same Start block — but now an external request can reach it and start a run. + + + +
+ + + +
+ +## A working workflow + +Before deploying, you have a workflow that runs when *you* run it, in the editor. Deploying is the step that lets anything else run it too. + +Here's the shape of the workflow you'll deploy — a Start block, a chain, and a response back to the caller: + + + +## Deploy gives it an address + +Deploying publishes the workflow and gives it an **address**. Nothing inside the chain changes — same blocks, same Start block, same logic. What's new is that the workflow is now **live** and reachable from outside, at a stable, versioned URL. A deployment is a snapshot, so you can promote new versions and roll back. + +## A call from outside + +An external request comes in, hits that address, and flows straight into the **Start block** — the same entry you tested with in the editor. From there the run is identical: the chain executes and the response goes back to whoever called it. An external call is just a run; the only difference is where it came from. + +## The same workflow, three surfaces + +The one deployed workflow can be reached three ways, and you choose which: + +- **API** — other systems POST to an endpoint and get the response back. +- **Chat** — a hosted chat page anyone can open; each message becomes the workflow's input. +- **MCP** — the workflow becomes a tool that other AI agents (like Cursor or Claude) can call. + +Same blocks underneath, same Start — just different doors in. + +## Runs when they run it + +Once deployed, the workflow runs on demand — whenever a caller hits it, with no one in the editor. The same process you built is now an operational system. + +## Related documentation + +- [Deployment overview](/workflows/deployment) +- [Deploy as an API](/workflows/deployment/api) +- [Deploy as a chat](/workflows/deployment/chat) +- [Deploy as an MCP server](/workflows/deployment/mcp) diff --git a/apps/docs/content/docs/en/academy/workflows/intro.mdx b/apps/docs/content/docs/en/academy/workflows/intro.mdx new file mode 100644 index 00000000000..633306c5d67 --- /dev/null +++ b/apps/docs/content/docs/en/academy/workflows/intro.mdx @@ -0,0 +1,84 @@ +--- +title: Workflows +description: A workflow is a visual program made of blocks — where you compose data, operations, integrations, and AI agents into a process. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, CLASSIFY_REPLY_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +A **workflow** is a visual program made of blocks. It's where you compose everything else in Sim — data, operations, integrations, and AI agents — into a process, and bind it to a trigger so it runs. + + + +
+ + + +
+ +## The pieces + +- A workflow is a **visual program** — you build it by placing and wiring blocks, not by writing a script. +- It's made of **blocks**, each one a step. Some blocks are **triggers** — they decide what starts the run and what it receives. +- **Connections** link blocks together. They set the order: a block runs after the blocks wired into it finish. +- Blocks have **parameters** that configure how they work. A Fetch Webpage block takes a URL; an Agent block takes a model and a prompt. +- When a workflow runs, Sim records what each block produced. Any later block can read an earlier block's output through a **connection tag**, like ``. +- Everything that happened is saved in the **logs** — the trigger, every block, and each block's input and output — so you can see exactly where a value came from. + +Here's a small workflow you can read top to bottom — blocks wired in order, each one a step. The highlighted block reads an earlier block's output through a connection tag: + + + +## How execution flows + +A block waits only for the blocks it depends on — nothing else. + +So if `A` feeds both `B` and `C`, then `B` and `C` start at the same moment `A` finishes; they don't wait for each other. And if `B` and `C` both feed `D`, then `D` waits for both before it runs. The shape of the connections *is* the execution plan. + +## Branches, loops, and parallel + +From those primitives you get expressive control flow: **branch** down one path or another with a Condition or Router, **loop** over a list, run work in **parallel** across many items. These are the expressive core of what you'll build — and they're all just blocks and connections. + +Everything complicated — an automated software factory, an autonomous triage system that takes action on its own — is built from these same simple primitives. You start with a few blocks; the platform scales to your ambition. As you get more comfortable, the workflows you compose get richer — more branches, more agents, more reach — without ever leaving the same model you learned here. We're glad you're on this journey: Sim is built of simple primitives that compose into anything you can imagine. + +## Related documentation + +- [Workflows overview](/workflows) +- [How workflows run](/workflows/how-it-runs) +- [Data flow](/workflows/data-flow) +- [Connections](/workflows/connections) diff --git a/apps/docs/content/docs/en/academy/workflows/logs.mdx b/apps/docs/content/docs/en/academy/workflows/logs.mdx new file mode 100644 index 00000000000..9e8f888a991 --- /dev/null +++ b/apps/docs/content/docs/en/academy/workflows/logs.mdx @@ -0,0 +1,76 @@ +--- +title: Logs +description: A log is the complete record of one workflow run — and debugging is reading that record backwards, from a wrong value to the block that produced it. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, ROUTER_TRIAGE_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +Every time a workflow runs, Sim writes down exactly what happened: the trigger, every block in the order it ran, and for each block its input, its output, its timing, and any error. That record is a **log** — the ground truth for understanding and debugging a run. + + + +
+ + + +
+ +## Every run leaves a record + +When a workflow runs, the run isn't a black box. Sim records the **trigger** that started it, every **block** in the order it executed, and for each block its **input**, its **output**, its **timing**, and an error message if it failed. Nothing about a run is a mystery — it all gets written down. + +Here's the kind of workflow whose runs you'll read — a trigger, an agent, and the branches it routes between: + + + +## The record + +Open a run and the log lists every block in the order it ran, each with its **duration**. So when a run is slow, you can see exactly where the time went — which block took the seconds — instead of guessing. + +## What each block did + +Selecting a block shows everything it actually did: the **input** it received, the **output** it returned, and — for an agent — the **tool calls** it made and the **tokens** it used. From the outside a step can look instant; the log keeps everything that happened inside it. + +## Read it backwards + +The log also shows where every value came from. A block's input names the block that produced it — a connection tag like ``. So to debug, you start from the **wrong value** and step **backwards**: which block produced it, what that block read, and on back through the chain until you reach the source. Debugging a workflow is reading its log backward from the symptom. + +## Every run writes one + +You don't enable any of this — every run produces a log automatically. When a workflow does something you didn't expect, the log is the first place to look, because it's the one place that records what actually happened. + +## Related documentation + +- [Logs & debugging overview](/logs-debugging) +- [The Logs page](/logs-debugging/logging) diff --git a/apps/docs/content/docs/en/academy/workflows/loops.mdx b/apps/docs/content/docs/en/academy/workflows/loops.mdx new file mode 100644 index 00000000000..c1ea512144c --- /dev/null +++ b/apps/docs/content/docs/en/academy/workflows/loops.mdx @@ -0,0 +1,79 @@ +--- +title: Loops & Parallel +description: Repeat the same steps over a collection — the Loop block runs them one item at a time, the Parallel block runs every item at once. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, LOOP_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +Repeating the same steps across a whole collection is one of the most fundamental ideas in programming. Sim gives you two blocks for it — the **Loop** block and the **Parallel** block. They do the same job and differ only in *how* they run. + + + +
+ + + +
+ +## A block that holds blocks + +A Loop (or Parallel) is a **container** — a block you drop other blocks inside. You hand it a collection, and it runs whatever's inside once for every item in that collection, turning a single step into many. + +Here's the shape of it — a container wrapping the blocks it repeats over the collection: + + + +## What each run knows + +Inside the container, every iteration gets the **current item** to work on (and its index). That's how one lane of blocks becomes a run per lead, per row, or per file — the steps stay the same, the data changes each pass. + +## One at a time, or all at once + +This is the whole difference between the two blocks: + +- **Loop** runs the items **sequentially** — one finishes before the next begins. Reach for it when each step builds on the last, or when you need to respect order or rate limits. +- **Parallel** runs them **all at once** — every item concurrently. Reach for it when the items are independent; it's dramatically faster across a large collection. + +## Swapping the container + +Because the two blocks share the same shape, you can **swap a Loop for a Parallel** (or back) without rewiring anything inside. Same logic, different execution — sequential or concurrent — chosen by which container you wrap it in. + +## Related documentation + +- [How workflows run](/workflows/how-it-runs) +- [Loop block](/workflows/blocks/loop) +- [Parallel block](/workflows/blocks/parallel) diff --git a/apps/docs/content/docs/en/academy/workflows/subworkflows.mdx b/apps/docs/content/docs/en/academy/workflows/subworkflows.mdx new file mode 100644 index 00000000000..39a8697e10b --- /dev/null +++ b/apps/docs/content/docs/en/academy/workflows/subworkflows.mdx @@ -0,0 +1,74 @@ +--- +title: Subworkflows +description: Call one workflow from another, the way you'd call a function in code — reuse logic and compose small workflows into larger systems. +--- + +import { VideoPlaceholder } from '@/components/ui/video-placeholder' +import { WhatYouWillLearn } from '@/components/ui/what-you-will-learn' +import { VideoChapters } from '@/components/ui/video-chapters' +import { WorkflowPreview, WORKFLOW_CALL_WORKFLOW } from '@/components/workflow-preview' + + + +
+
+ +Once you've built a workflow that does something well, you can **call it from another workflow** — the way you'd call a function in code. The Workflow block turns any workflow into a reusable step. + + + +
+ + + +
+ +## The workflow you already know + +Start with a workflow you've built and trust — say one that classifies a message. On its own it's a complete process you can run. + +Here's the shape of it — one workflow invoked as a single block inside another: + + + +## It becomes a block + +Any workflow can be used as a **block** inside another. You drop a **Workflow block**, point it at the workflow you want, and pass it an input — just like calling a function. You don't rebuild its logic; you reuse it. + +## Call, run, return + +When the parent run reaches the Workflow block, the **child workflow runs** start to finish — its own blocks, its own logic — and its **result comes back** into the parent, where the next block reads it. The parent doesn't need to know how the child works, only what it returns. + +## Workflows all the way up + +This is how big systems stay manageable: factor shared logic into focused subworkflows and **compose** them. Each workflow you build becomes a building block for the next — small, tested pieces assembling into larger processes, without turning into a mess. + +## Related documentation + +- [Workflow block](/workflows/blocks/workflow) +- [Workflows overview](/workflows) diff --git a/apps/docs/lib/utils.ts b/apps/docs/lib/utils.ts index 61be1d99b6e..2a18386f2dc 100644 --- a/apps/docs/lib/utils.ts +++ b/apps/docs/lib/utils.ts @@ -14,6 +14,10 @@ export function cn(...inputs: ClassValue[]) { * - Otherwise falls back to local static assets served from root path */ export function getAssetUrl(filename: string) { + // Absolute URLs (e.g. blob-hosted academy videos) are already complete. + if (/^https?:\/\//.test(filename)) { + return filename + } const cdnBaseUrl = process.env.NEXT_PUBLIC_BLOB_BASE_URL if (cdnBaseUrl) { return `${cdnBaseUrl}/${filename}`