Library

Async Streams & Scheduling

Production-grade scheduling for GenAI applications. Capacity management, priority queues, and backpressure mechanisms—out of the box.

The Challenge: Scaling GenAI Applications

LLM-based applications face unique scaling challenges. Every request can spawn multiple model calls, each consuming expensive GPU capacity. Without proper scheduling, you'll hit rate limits, exhaust resources, and deliver inconsistent experiences to your users.

Building this yourself means solving hard problems: How do you ensure paying customers get priority over free users? How do you prevent one runaway workflow from starving others? How do you gracefully degrade when you're at capacity instead of failing catastrophically?

These aren't trivial problems. We provide battle-tested solutions out of the box.

What You Get

Capacity Management

Define capacity limits at multiple levels. Tasks only start when capacity is available, preventing resource exhaustion and rate limit errors.

Priority Queues

Implement QoS tiers for your users. Enterprise customers get priority over free users. Critical workflows jump ahead of batch jobs. You define the rules.

Backpressure

When you're at capacity, slow down gracefully instead of failing. Producers wait for consumers. Queues stay bounded. Memory stays predictable.

High Concurrency

Handle thousands of concurrent workflows efficiently. Lazy evaluation means work only happens when resources are ready. Eager execution maximizes throughput.

Example: QoS Tiers for Your Users

Your paying customers expect better service. With our scheduling primitives, implementing differentiated QoS is straightforward:

Priority-based task scheduling:
import { HierarchicalTaskPoolRunner, PriorityCapacitySource } from '@firefoundry/agent-sdk';

// Define capacity with priority tiers
const capacitySource = new PriorityCapacitySource({
  totalCapacity: 100,  // Total concurrent LLM calls
  tiers: {
    enterprise: { reserved: 60, priority: 1 },  // 60 slots guaranteed
    pro: { reserved: 30, priority: 2 },         // 30 slots guaranteed
    free: { reserved: 10, priority: 3 }         // 10 slots, lowest priority
  }
});

// Create the task runner
const runner = new HierarchicalTaskPoolRunner(taskSource, capacitySource);

// Tasks automatically scheduled by tier
// Enterprise tasks start immediately if capacity exists
// Free tier tasks wait if higher-priority work is queued

Example: Backpressure That Actually Works

When your system is overwhelmed, you need controlled degradation—not crashes. Our buffering primitives implement backpressure automatically:

Bounded queue with backpressure:
import { PushPullBufferObj } from '@firefoundry/agent-sdk';

// Bounded buffer - producers block when full
const taskQueue = new PushPullBufferObj<Task>({
  maxSize: 1000,  // Queue won't grow unbounded
  onFull: 'block' // Producers wait instead of failing
});

// Ingestion (fast) - automatically slows when queue fills
async function ingestRequests(requests: Request[]) {
  for (const req of requests) {
    await taskQueue.next(createTask(req));  // Blocks if queue full
  }
}

// Processing (slow) - works at its own pace
async function processWithLLM() {
  for await (const task of taskQueue) {
    await llm.complete(task);  // Takes time, that's ok
  }
}

The Architecture

FireFoundry's async streaming is built on two complementary models that can be composed together:

Pull Model

Lazy, convergent. Multiple sources combine into one output. Work only happens when requested. Perfect for capacity-limited execution.

PullMap PullZip PullRace

Push Model

Eager, divergent. Single inputs fan out to multiple destinations. Data flows immediately. Perfect for event distribution and routing.

PushFork PushDistribute

PushPullBuffer: The Bridge

Connect eager producers to lazy consumers. Implement backpressure. Decouple ingestion speed from processing speed. The key to building resilient pipelines.

HierarchicalTaskPoolRunner

The complete orchestration solution

This is the highest-level abstraction that brings everything together. It manages capacity across hierarchical resource pools, ensures fair allocation, and maximizes throughput while respecting limits.

Hierarchical capacity allocation
Lazy task invocation (no wasted work)
Eager execution (maximum throughput)
Dynamic task injection at runtime

Why This Matters for GenAI

$$$
LLM calls are expensive. Efficient scheduling reduces wasted compute.
SLA
Priority queues let you guarantee response times for premium users.
Scale
Backpressure prevents cascading failures under load.

Learn More