Skip to main content
import { Aspects } from "smithers-orchestrator";

type TokenBudgetConfig = {
  max: number;
  perTask?: number;
  onExceeded?: "fail" | "warn" | "skip-remaining"; // default "fail"
};

type LatencySloConfig = {
  maxMs: number;
  perTask?: number;
  onExceeded?: "fail" | "warn"; // default "fail"
};

type CostBudgetConfig = {
  maxUsd: number;
  onExceeded?: "fail" | "warn" | "skip-remaining"; // default "fail"
};

type TrackingConfig = { tokens?: boolean; latency?: boolean; cost?: boolean };

type AspectsProps = {
  tokenBudget?: TokenBudgetConfig;
  latencySlo?: LatencySloConfig;
  costBudget?: CostBudgetConfig;
  tracking?: TrackingConfig; // default all true
  children?: ReactNode;
};
<Workflow name="budgeted-workflow">
  <Aspects
    tokenBudget={{ max: 100_000, perTask: 25_000, onExceeded: "warn" }}
    latencySlo={{ maxMs: 30_000, onExceeded: "fail" }}
    costBudget={{ maxUsd: 5.0, onExceeded: "skip-remaining" }}
  >
    <Task id="analyze" output={outputs.analysis} agent={codeAgent}>
      Analyze the repository.
    </Task>
    <Task id="review" output={outputs.review} agent={reviewAgent}>
      Review the analysis.
    </Task>
  </Aspects>
</Workflow>

Notes

  • Nested <Aspects> inherit outer budget configs wholesale — if an inner <Aspects> sets tokenBudget, latencySlo, or costBudget, that field’s entire parent config object is replaced (no per-field merge). The tracking config is the exception: tokens, latency, and cost each fall back to the parent value independently.
  • Budgets enforce regardless of tracking; tracking controls metric emission only.
  • Accumulator is per-run and resets on resume.