Control Flow

branch

Conditional routing -- pick one step to execute or skip entirely.

Quick Example

import { branch, step } from '@noetic/core';

const triageAgent = branch({
  id: 'triage',
  route: (input: string, ctx) => {
    if (input.includes('urgent')) {
      return step.llm({
        id: 'urgent-handler',
        model: 'gpt-4o',
        system: 'Handle this urgent request immediately.',
      });
    }
    if (input.includes('ignore')) {
      return null; // skip -- no step executed
    }
    return step.llm({
      id: 'default-handler',
      model: 'gpt-4o-mini',
      system: 'Handle this routine request.',
    });
  },
});

What It Does

branch is a runtime router. Its route function receives the step input and context, and returns either:

  • A Step to execute, or
  • null to skip (the input passes through as-is).

This is the idiomatic way to add conditional logic to an Noetic pipeline. Instead of if/else blocks around step calls, you express the decision as data.

API Reference

PropertyTypeRequiredDescription
idstringYesUnique step identifier
route(input: I, ctx: Context) => Step<I, O> | null | Promise<Step<I, O> | null>YesFunction that picks the step to run (sync or async)

How Null Routes Work

When route returns null, the branch is skipped entirely. The input value is passed through as the output. This is useful for optional processing steps.

import { branch, step } from '@noetic/core';

const maybeTranslate = branch({
  id: 'maybe-translate',
  route: (input: { text: string; lang: string }) => {
    if (input.lang === 'en') {
      return null; // already English, skip
    }
    return step.llm({
      id: 'translate',
      model: 'gpt-4o',
      system: `Translate the following text to English.`,
    });
  },
});

Dynamic Step Creation

The route function creates steps at runtime, so you can parameterize them based on input.

import { branch, step } from '@noetic/core';

const modelPicker = branch({
  id: 'model-picker',
  route: (input: { task: string; budget: 'low' | 'high' }) => {
    const model = input.budget === 'high' ? 'gpt-4o' : 'gpt-4o-mini';
    return step.llm({
      id: `${input.task}-llm`,
      model,
      system: `Perform the following task: ${input.task}`,
    });
  },
});

Nesting Branches

Branches can return other branches, forks, or any other step type.

import { branch, fork, step } from '@noetic/core';

const nested = branch({
  id: 'outer',
  route: (input: string) => {
    if (input === 'compare') {
      return fork({
        id: 'compare-models',
        mode: 'all',
        paths: () => [
          step.llm({
            id: 'model-a',
            model: 'gpt-4o',
          }),
          step.llm({
            id: 'model-b',
            model: 'gpt-4o-mini',
          }),
        ],
        merge: (results) => results.join('\n---\n'),
      });
    }
    return step.llm({
      id: 'single',
      model: 'gpt-4o',
    });
  },
});

Semantic Routing

The route function can be async, enabling AI-powered and embedding-based routing. Noetic provides condition helpers like semanticSwitch, semanticRoute, embeddingMatch, and aiCondition that return async route functions:

import { branch, semanticSwitch, step } from '@noetic/core';

const router = branch({
  id: 'semantic-router',
  route: semanticSwitch({
    embed,
    cases: {
      'greeting or salutation': step.llm({ id: 'greet', model: 'gpt-4o-mini' }),
      'technical question': step.llm({ id: 'tech', model: 'gpt-4o' }),
    },
    threshold: 0.7,
  }),
});

See Semantic Conditions for the full API, including condition combinators, caching, and custom conditions.

  • fork -- parallel execution for when you need multiple paths at once.
  • Steps -- the step types you can return from a route function.
  • Loop & Until -- combine branching with iteration.

On this page