Skip to content

Node SDK (floopy-sdk)

floopy-sdk is the official Node/TypeScript client for the Floopy gateway. It wraps the upstream openai package by composition (no fork, no reimplementation) and adds first-class typed methods for every Floopy-only endpoint.

Terminal window
pnpm add floopy-sdk # or: npm i floopy-sdk / bun add floopy-sdk
import { Floopy } from "floopy-sdk";
const floopy = new Floopy({
apiKey: process.env.FLOOPY_API_KEY!,
});
const response = await floopy.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello from Floopy!" }],
});
console.log(response.choices[0]?.message?.content);

floopy.chat, floopy.embeddings, and floopy.models delegate to a lazy openai client preconfigured to talk to https://api.floopy.ai/v1. Types match the upstream openai package one-for-one.

Pass typed gateway behavior toggles through options. They map to Floopy-* headers and apply to every request the client makes:

const floopy = new Floopy({
apiKey: process.env.FLOOPY_API_KEY!,
options: {
cache: { enabled: true, bucketMaxSize: 3 },
promptId: "cd4249d5-44d5-46c8-8961-9eb3861e1f7e",
promptVersion: "1",
llmSecurityEnabled: true,
},
});
OptionHeaderPurpose
cache.enabledFloopy-Cache-EnabledToggle exact + semantic cache
cache.bucketMaxSizeFloopy-Cache-Bucket-Max-SizeMax entries per semantic bucket
promptIdFloopy-Prompt-IdStored prompt to resolve
promptVersionFloopy-Prompt-VersionPinned version for promptId
llmSecurityEnabledfloopy-llm-security-enabledLLM firewall pre-check

Per-call overrides go through the second argument of every Floopy resource method.

Each maps to a public /v1/* endpoint. Errors throw FloopyError subclasses (see below).

const r = await floopy.chat.completions.create({ /* ... */ });
await floopy.feedback.submit({ score: 9, useful: true, sessionId: r.id });
const decision = await floopy.decisions.get(requestId);
const page = await floopy.decisions.list({ from, to, limit: 50 });
for await (const d of floopy.decisions.iterate({ from })) { /* one decision */ }
for await (const p of floopy.decisions.pages({ from })) { /* one page */ }
const exp = await floopy.experiments.create({
name: "cost-vs-quality",
variantARoutingRuleId: ruleA,
variantBRoutingRuleId: ruleB,
});
const results = await floopy.experiments.results(exp.id);
await floopy.experiments.rollback(exp.id);

create and rollback automatically include the X-Floopy-Confirm: experiments header the gateway requires.

const current = await floopy.constraints.get();
await floopy.constraints.put({ costLimitMonthlyUsd: 100 });

put is full-replace: omitted fields are reset to null.

for await (const row of floopy.export.decisions({ from, to })) {
// streamed JSONL, parsed and typed
}
const { rows, trailer } = floopy.export.decisionsWithTrailer({ from, to });
for await (const r of rows) { /* ... */ }
console.log(trailer.value); // populated after iteration completes
const run = await floopy.evaluations.create({ datasetId, model: "gpt-4o" });
const status = await floopy.evaluations.get(run.id);
const page = await floopy.evaluations.results(run.id, { limit: 100 });
await floopy.evaluations.cancel(run.id);
const explain = await floopy.routing.explain({ model, messages });
console.log(explain.wouldSelect, explain.firewallDecision);

Pro plan only. wouldSelect is null when the firewall would block the request.

Chat streaming is delegated to openai-node and returns an AsyncIterable<ChatCompletionChunk>:

const stream = await floopy.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "stream a haiku" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

floopy.export.decisions is a true async iterator over the gateway’s JSONL stream and respects the trailer record.

import {
FloopyAuthError,
FloopyPlanError,
FloopyRateLimitError,
FloopyValidationError,
FloopyServerError,
} from "floopy-sdk";
try {
await floopy.export.decisions({ from, to });
} catch (err) {
if (err instanceof FloopyRateLimitError) {
await sleep((err.retryAfterSeconds ?? 1) * 1000);
} else if (err instanceof FloopyPlanError) {
console.error(`Upgrade plan: feature ${err.feature} not in current plan`);
} else throw err;
}

chat.completions and embeddings throw OpenAI.APIError subclasses from the upstream SDK.

const floopy = new Floopy({
apiKey: process.env.FLOOPY_API_KEY!,
baseURL: "https://gateway.internal.acme.com/v1",
});

The SDK is on 0.x while the surface stabilizes. Coming up:

  • React hooks helper (floopy-sdk/react).
  • Edge / Deno entry points.
  • Generated types from the gateway’s Rust models (today the types are hand-written and kept in sync via tests).