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:
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:
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.
Push Model
Eager, divergent. Single inputs fan out to multiple destinations. Data flows immediately. Perfect for event distribution and routing.
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.