Steps

step.run

Execute arbitrary async code as a typed step.

Quick Example

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

const transform = step.run({
  id: 'normalize-input',
  execute: async (input: string, ctx) => {
    return input.trim().toLowerCase();
  },
});

What It Does

step.run wraps any async function as a step. It is the escape hatch for custom logic -- API calls, database operations, file I/O, data transformations, or anything that is not a model call.

The execute function receives two arguments:

  1. input -- the typed input from the previous step (or the initial pipeline input).
  2. ctx -- the Noetic Context, giving access to memory, tokens, abort signals, and more.

The return value becomes the output of the step, passed to whatever comes next.

Type Signature

type RunStepOptions<TMemory, I, O> = {
  id: string;
  execute: (input: I, ctx: Context<TMemory>) => Promise<O>;
  retry?: RetryPolicy;
};

declare const stepRun: <TMemory, I, O>(
  opts: RunStepOptions<TMemory, I, O>,
) => StepRun<TMemory, I, O>;

TMemory is the typed memory shape (from InferMemory), I is the input type, and O is the output type.

API Reference

Options

PropertyTypeRequiredDescription
idstringYesUnique step identifier
execute(input: I, ctx: Context<TMemory>) => Promise<O>YesThe async function to run
retryRetryPolicyNoRetry configuration on failure

RetryPolicy

PropertyTypeRequiredDefaultDescription
maxAttemptsnumberYes--Maximum number of attempts
backoff'fixed' | 'linear' | 'exponential'Yes--Backoff strategy between retries
initialDelaynumberYes--Delay in ms before first retry
maxDelaynumberNo--Cap on delay between retries

Retry Example

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

const fetchData = step.run({
  id: 'fetch-api',
  execute: async (url: string) => {
    const res = await fetch(url);
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}`);
    }
    return res.json();
  },
  retry: {
    maxAttempts: 3,
    backoff: 'exponential',
    initialDelay: 1e3,
    maxDelay: 1e4,
  },
});

Using Context

The Context object provides access to runtime state inside your execute function.

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

const contextAware = step.run({
  id: 'context-example',
  execute: async (input: string, ctx) => {
    // Check if the run has been cancelled
    if (ctx.aborted) {
      return 'Cancelled';
    }

    // Access token usage
    console.log('Tokens used so far:', ctx.tokens.total);

    // Access memory layer data
    const snapshot = ctx.memory['working-memory'].snapshot;

    return `Processed: ${input}`;
  },
});

The ctx.memory property provides typed access to data and functions exposed by memory layers via their provides field. See Context for the full property reference.

  • step.llm -- for language model calls.
  • step.tool -- for direct tool invocation.
  • Loop & Until -- use step.run as a loop body for custom iteration logic.

On this page