TypeScript SDK
Installation, configuration, and API reference for the Sluice TypeScript SDK.
Sluice SDK -- TypeScript
Installation
pnpm add @ontopix/sluice
The package is published to the Ontopix CodeArtifact registry.
Configuration
Constructor
import { SluiceConfig } from "@ontopix/sluice";
const config = new SluiceConfig({
tableName: "ontopix-vendor-buckets-prod", // DynamoDB table
region: "eu-central-1", // AWS region
endpointUrl: undefined, // Custom DynamoDB endpoint (local dev)
leaseTtl: 60, // Lease TTL in seconds
maxRetries: 3, // Retries on contention
defaultSlotTimeout: 30.0, // Default timeout for slot() in seconds
logLevel: "INFO", // Logging level
});
All options are optional and fall back to the defaults shown above.
From environment variables
const config = SluiceConfig.fromEnv();
fromEnv() reads these environment variables, falling back to the same defaults:
| Variable | Type | Default |
|---|---|---|
SLUICE_TABLE_NAME | string | "ontopix-vendor-buckets-prod" |
SLUICE_AWS_REGION | string | "eu-central-1" |
SLUICE_ENDPOINT_URL | string | undefined |
SLUICE_LEASE_TTL | number | 60 |
SLUICE_MAX_RETRIES | number | 3 |
SLUICE_DEFAULT_SLOT_TIMEOUT | number | 30.0 |
SLUICE_LOG_LEVEL | string | "INFO" |
You can also pass overrides to fromEnv():
const config = SluiceConfig.fromEnv({ maxRetries: 5 });
When no config argument is passed to any function, SluiceConfig.fromEnv() is called automatically.
slot -- handle-based API (preferred)
slot retries acquire internally until a slot is granted or the timeout expires. Returns a SlotHandle that must be released when done.
import { slot } from "@ontopix/sluice";
const handle = await slot("openai#gpt-4o#requests", { timeout: 10 });
try {
const response = await callOpenAI(prompt);
} finally {
await handle.release();
}
Using Symbol.asyncDispose
SlotHandle implements Symbol.asyncDispose, so you can use await using (TypeScript 5.2+ with a compatible runtime):
import { slot } from "@ontopix/sluice";
{
await using handle = await slot("openai#gpt-4o#requests", { timeout: 10 });
const response = await callOpenAI(prompt);
}
// handle.release() called automatically
Signature
async function slot(
dimension: string,
options?: {
timeout?: number; // seconds; defaults to config.defaultSlotTimeout
config?: SluiceConfig;
},
): Promise<SlotHandle>
SlotHandle
interface SlotHandle {
result: AcquireResult;
release: () => Promise<void>;
[Symbol.asyncDispose]: () => Promise<void>;
}
Multiple dimensions
Use slotMany to acquire slots across multiple dimensions atomically:
import { slotMany } from "@ontopix/sluice";
const handle = await slotMany(
["openai#gpt-4o#requests", "openai#gpt-4o#tokens"],
{ timeout: 15 },
);
try {
const response = await callOpenAI(prompt);
} finally {
await handle.release();
}
acquire / release -- low-level API
acquire performs a single attempt to obtain a slot. It returns immediately with either Granted or RetryIn. The caller is responsible for retry logic and calling release().
import { acquire, AcquireOutcome } from "@ontopix/sluice";
const result = await acquire("openai#gpt-4o#requests");
if (result.outcome === AcquireOutcome.Granted) {
try {
const response = await callOpenAI(prompt);
} finally {
await result.release();
}
} else {
console.log(`Retry after ${result.waitSeconds.toFixed(1)}s`);
}
Signatures
async function acquire(
dimension: string,
config?: SluiceConfig,
): Promise<AcquireResult>
async function acquireMany(
dimensions: string[],
config?: SluiceConfig,
): Promise<AcquireResult>
acquireMany is all-or-nothing: if any dimension has insufficient tokens, none are acquired and a single RetryIn result is returned with the maximum wait time.
AcquireResult
interface AcquireResult {
outcome: AcquireOutcome;
waitSeconds: number;
leaseKey?: string;
dimension?: string;
release: () => Promise<void>;
}
AcquireOutcome
enum AcquireOutcome {
Granted = "granted",
RetryIn = "retry_in",
}
penalize
Reduces the token count for a dimension by a factor. Use this when a vendor returns 429 despite the slot being granted.
Best-effort: not transactional, no version check. Errors are silently swallowed.
import { penalize } from "@ontopix/sluice";
// Reduce tokens to 80% of current value (default factor)
await penalize("openai#gpt-4o#requests");
// Custom factor
await penalize("openai#gpt-4o#requests", 0.5);
Signature
async function penalize(
dimension: string,
factor?: number, // 0.0 to 1.0; defaults to 0.8
config?: SluiceConfig,
): Promise<void>
Error handling
SlotTimeout
Thrown by slot() and slotMany() when the timeout expires without acquiring a slot.
import { slot, SlotTimeout } from "@ontopix/sluice";
try {
const handle = await slot("openai#gpt-4o#requests", { timeout: 5 });
try {
await callOpenAI(prompt);
} finally {
await handle.release();
}
} catch (err) {
if (err instanceof SlotTimeout) {
// Handle timeout
}
throw err;
}
Missing dimensions
If a dimension does not exist in the DynamoDB table, acquire throws a standard Error. Ensure dimensions are provisioned before calling the SDK.
SluiceClient -- connection reuse
Module-level functions (slot, acquire, etc.) create a new DynamoDB connection on each call. For multiple calls within the same process, use SluiceClient to hold the connection:
import { SluiceClient } from "@ontopix/sluice";
const client = new SluiceClient();
try {
const handle = await client.slot("openai#gpt-4o#requests", { timeout: 10 });
try {
await callOpenAI(prompt);
} finally {
await handle.release();
}
} finally {
client.destroy();
}
SluiceClient accepts an optional SluiceConfig:
const client = new SluiceClient(new SluiceConfig({ maxRetries: 5 }));
The client provides the same methods as the module-level functions: acquire, acquireMany, slot, slotMany, penalize. Call destroy() when done to clean up the connection.
Full example
import { slot, penalize, SlotTimeout } from "@ontopix/sluice";
async function callWithRateLimit(prompt: string): Promise<string> {
const handle = await slot("openai#gpt-4o#requests", { timeout: 10 });
try {
return await callOpenAI(prompt);
} finally {
await handle.release();
}
}
async function callWithBackpressure(prompt: string): Promise<string> {
const handle = await slot("openai#gpt-4o#requests", { timeout: 10 });
try {
try {
return await callOpenAI(prompt);
} catch (err) {
if (isRateLimitError(err)) {
await penalize("openai#gpt-4o#requests", 0.5);
}
throw err;
}
} finally {
await handle.release();
}
}