NOUKAI

Flows & Blocks

The core data model — flows are directed trees of processing blocks.

Flows

A flow is a directed tree of processing blocks. When called via the slug API, Noukai executes the blocks in order, passing output from one to the next.

Flow: "Translate & Extract"
├── Block: "Translate" (llm)
│     Prompt: "Translate {message} to {target_language}"
└── Block: "Extract Vocabulary" (llm)
      Prompt: "Extract key vocabulary from the translation..."

Each flow has:

  • A name and slug (URL identifier)
  • A steps tree (the block topology)
  • A global input schema (what the API accepts)
  • Versions (immutable snapshots for deployment)

Blocks

A block is a single processing step. Every block has a processor type that determines what it does.

llm — LLM Processing

Runs a prompt through a language model and returns structured output.

  • Prompt: The template text sent to the LLM
  • Model: Which LLM to use (e.g., anthropic/claude-sonnet-4-6)
  • Input Schema: What data the block receives
  • Output Schema: The structure of the block's response
  • Config: Temperature, max tokens, etc.

Use when the step requires understanding, reasoning, generation, classification, or extraction — anything that depends on language meaning.

Don't use when the transformation is purely deterministic (string formatting, math, slicing, filtering by exact match). Reach for code instead — it's faster, cheaper, and never hallucinates.

code — Custom Logic

Executes custom JavaScript code for transformations, filtering, or business logic that doesn't require an LLM.

Use when you need deterministic logic: parsing JSON, slicing arrays, formatting strings, computing scores, normalizing data, or filtering by exact criteria.

Don't use when the task involves understanding language or producing prose. code blocks can't reason — give that work to an llm block.

Common pairing: an llm block extracts structured data, then a code block reshapes it for the next step or for the final response.

passthrough — Data Forwarding

Passes input directly to output without modification. Useful for routing data between blocks or splitting a flow.

Use when you need to fan data into a parallel container without re-prompting it, or when you need a structural anchor in the tree (e.g., a join point).

Don't use when you actually need to transform the data — passthrough is purely structural.

Tree Structure

Blocks are organized in a tree with two grouping mechanisms.

Containers

Group blocks for sequential or parallel execution.

Sequential (h)

Blocks run one after another. Each block sees the previous block's output via {previous_output}.

Use when later blocks depend on earlier blocks' outputs — extracting then summarizing, classifying then routing, translating then glossing.

Don't use when the blocks are independent. Sequential execution is slower and forces a chain that doesn't need to exist.

Parallel (v)

Blocks run simultaneously. Each block sees the same upstream input; their outputs are merged into a single object keyed by block name.

Use when multiple analyses are independent — running tone, readability, and fact-checks against the same text; generating tweet, LinkedIn, and email drafts from the same source.

Don't use when one block needs another's output. Parallel branches can't see each other.

Loops

Iterate a block (or group) over an array field:

Loop over "items":
  └── Block: "Process Item" (llm)

Use when you need to apply the same prompt to each element of an array — glossing each word, scoring each candidate answer, validating each item.

Don't use when you have a fixed, small number of distinct steps. A loop is for "the same logic × N items," not "N different things in a row."

Choosing a Block

Use this flowchart when designing a new step:

Need to transform data?
├── Requires understanding meaning? ─── llm
├── Pure deterministic logic? ───────── code
└── No transformation needed? ───────── passthrough

Need to combine multiple steps?
├── Steps depend on each other? ─────── sequential container
├── Steps are independent? ──────────── parallel container
└── Same step over a list? ──────────── loop

Input & Output Schemas

Each block defines what data it accepts and produces. Schemas enable:

  • Type safety: validate data between blocks
  • Documentation: self-describing pipelines
  • Reliability: downstream blocks can rely on the shape of upstream output
  • Object models: reusable schema types at the project level

Set an output schema on any llm block whose result is consumed by a later block — without one, you're trusting the model to produce the same shape every time.

Next Steps

On this page