@sandboxaq/flintai-sdk-ts
v0.3.1
Published
Lightweight SDK for routing AI agent LLM traffic through a guardrails proxy
Keywords
Readme
FlintAI SDK (TypeScript)
Lightweight SDK for routing LLM traffic through a guardrails proxy. Wraps OpenAI, Anthropic, Google GenAI, and LangChain LLM clients, with agent framework plugins for Google ADK.
Features
- LLM SDK wrapping — route LLM traffic through the FlintAI guardrails proxy with a single
flintai.wrap()call. Supports OpenAI, Anthropic, Google GenAI, and LangChain chat models. - Agent framework plugins — deeper integration with agent lifecycle hooks for metadata extraction and guardrails routing. Currently supports Google ADK.
- Plugin system for extensibility
Supported Integrations
Every integration package is an optional peer dependency — none is bundled in
the published artifact (which ships only dist/), and each is loaded dynamically at
runtime. Install only the ones you use.
| Integration | Category | Peer package | Supported version | Routing |
|---|---|---|---|---|
| OpenAI SDK | LLM SDK | openai | ^6.41.0 (≥6.41.0, <8) | wrap() |
| Anthropic SDK | LLM SDK | @anthropic-ai/sdk | ^0.100.1 (0.100.1–1.x) | wrap() |
| Google GenAI SDK | LLM SDK | @google/genai | ^2.8.0 (≥2.8.0, <4) | wrap() |
| LangChain (ChatOpenAI) | LLM SDK | @langchain/openai | ^1.4.7 | wrap() |
| LangChain (ChatAnthropic) | LLM SDK | @langchain/anthropic | ^1.4.0 | wrap() |
| LangChain (ChatGoogleGenerativeAI) | LLM SDK | @langchain/google-genai | ^2.1.31 | wrap() |
| Google ADK | Agent Framework | @google/adk | ^1.2.0 | ADKGuardrailsPlugin |
| .env loading (optional) | Utility | dotenv | ^17.4.2 | loadDotenv |
For the three LLM SDKs, wrap() also enforces the version at runtime — it rejects a
provider SDK below the minimum or above the maximum supported major and throws with
an actionable message. The ^ ranges above are the contract expressed in
peerDependencies; consumers pick the exact version.
Install
npm install @sandboxaq/flintai-sdk-ts
# With optional peer dependencies
npm install @sandboxaq/flintai-sdk-ts openai # OpenAI SDK
npm install @sandboxaq/flintai-sdk-ts @anthropic-ai/sdk # Anthropic SDK
npm install @sandboxaq/flintai-sdk-ts @google/genai # Google GenAI SDK
npm install @sandboxaq/flintai-sdk-ts @google/adk # Google ADK (includes GenAI)The base package has no runtime dependencies — installing @sandboxaq/flintai-sdk-ts
alone pulls nothing else. Add only the peer(s) for the providers you use, at a
version in the supported range. Because peers are loaded
dynamically, a missing peer surfaces only when you actually route through that
provider (with a clear "install X" error), not at install time.
Peer auto-installation is intentionally disabled for this repo (
autoInstallPeers: falseinpnpm-workspace.yaml). The SDK never statically imports a peer, so the dev build, typecheck, and tests do not need them — keeping the optional providers (and their transitive advisories) out of the development lockfile. To exercise the ADK or LangChain code paths locally, install those packages explicitly.
Guardrails
Route all LLM traffic through the FlintAI guardrails proxy. Create your client as usual, then call wrap() — it auto-detects the provider and applies guardrails routing.
OpenAI
import OpenAI from "openai";
import { wrap } from "@sandboxaq/flintai-sdk-ts";
const client = new OpenAI({ apiKey: "your-openai-api-key" });
wrap(client, {
gatewayUrl: "https://app.flintai.dev",
apiKey: "your-guardrails-api-key",
policyId: "your-policy-id", // optional
});
const response = await client.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: "Hello" }],
});Anthropic
import Anthropic from "@anthropic-ai/sdk";
import { wrap } from "@sandboxaq/flintai-sdk-ts";
const client = new Anthropic({ apiKey: "your-anthropic-api-key" });
wrap(client, {
gatewayUrl: "https://app.flintai.dev",
apiKey: "your-guardrails-api-key",
});
const message = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});Google GenAI
import { GoogleGenAI } from "@google/genai";
import { wrap } from "@sandboxaq/flintai-sdk-ts";
const client = new GoogleGenAI({ apiKey: "your-gemini-api-key" });
wrap(client, {
gatewayUrl: "https://app.flintai.dev",
apiKey: "your-guardrails-api-key",
});
const response = await client.models.generateContent({
model: "gemini-2.5-flash",
contents: "Hello",
});LangChain
Create your LangChain chat model as usual, then call wrap() — it detects the LangChain model, finds the underlying SDK client, and applies guardrails routing:
import { ChatOpenAI } from "@langchain/openai";
import { wrap } from "@sandboxaq/flintai-sdk-ts";
const llm = new ChatOpenAI({ model: "gpt-4", apiKey: "your-openai-api-key" });
wrap(llm, {
gatewayUrl: "https://app.flintai.dev",
apiKey: "your-guardrails-api-key",
});
const response = await llm.invoke("Hello");Works with ChatOpenAI, ChatAnthropic, and ChatGoogleGenerativeAI.
Google ADK
ADK agents lazily create their GenAI client at runtime and use generateContentConfig for per-request routing, so wrap() cannot be used. Use the ADK plugin instead:
import { ADKGuardrailsPlugin } from "@sandboxaq/flintai-sdk-ts/plugins/adk";
import { Agent } from "@google/adk";
const plugin = new ADKGuardrailsPlugin({
gatewayUrl: "https://app.flintai.dev",
apiKey: "your-guardrails-api-key",
});
const agent = new Agent({
model: "gemini-2.5-flash",
generateContentConfig: plugin.contentConfig,
beforeModelCallback: plugin.beforeModelCallback,
onModelErrorCallback: ADKGuardrailsPlugin.onModelError,
});Passing your own
contentConfig: if you supplycontentConfig: ...to the plugin, wireplugin.contentConfig(not your original object) into theAgent. The plugin returns a clone with the guardrailshttpOptionsattached and leaves your object untouched, so passing the original sends traffic straight to the model — and withrequireGuardrailsenabled by default,beforeModelCallbackraises.
beforeModelCallback extracts the ADK session ID and attaches it as an X-Agent-Session-Id header on each guardrails request. onModelErrorCallback converts FlintAI guardrails blocks (403 / keyword match) into an LlmResponse so the agent can handle them gracefully.
Configuration Reference
wrap() and init() accept these guardrails parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| gatewayUrl | string | Guardrails proxy URL |
| apiKey | string | Your guardrails API key |
| policyId | string \| undefined | Guardrails policy ID to apply (optional) |
| agentName | string \| undefined | Agent name used as agentId when no explicit agentId is provided |
| agentId | string \| undefined | Explicit agent ID (overrides agentName) |
| requireGuardrails | boolean \| undefined | When true (default), throws FlintAIGuardrailsError if guardrails config cannot be resolved. Set to false to allow operation without guardrails. |
| dangerouslyDisableGuardrails | boolean \| undefined | Explicit, self-documenting opt-out equivalent to requireGuardrails: false. Prefer this so unguarded deployments are greppable. Cannot be combined with requireGuardrails: true. Either opt-out emits a once-per-process SECURITY CONTROL DISABLED console warning so it stays auditable. |
Environment Variables
Instead of passing credentials in code, you can set them as environment variables:
| Environment Variable | Parameter | Description |
|---------------------------------|--------------|---------------------------------------------------------------------------------------------------------------|
| FLINTAI_GATEWAY_URL | gatewayUrl | Guardrails proxy URL |
| FLINTAI_API_KEY | apiKey | Your guardrails API key |
| FLINTAI_POLICY_ID | policyId | Guardrails policy ID (optional) |
| AGENT_ID | agentId | Agent identifier attached to guardrails requests |
| AGENT_NAME | agentName | Agent name attached to guardrails requests |
| FLINTAI_ALLOWED_GATEWAY_HOSTS | — | Comma-separated list of allowed gateway hostnames. Default: app.flintai.dev. The * wildcard requires allowInsecureGateway: true (see Security). |
Environment variables are read automatically. A .env file is not read by
default — pass loadDotenv: true to load <cwd>/.env, or loadDotenv: "/path/to/.env"
to load a specific trusted file (requires dotenv as a peer dependency):
# .env
FLINTAI_GATEWAY_URL=https://app.flintai.dev
FLINTAI_API_KEY=your-guardrails-api-keyThen use FlintAI SDK without passing credentials:
import OpenAI from "openai";
import { wrap } from "@sandboxaq/flintai-sdk-ts";
const client = new OpenAI();
wrap(client, { loadDotenv: true }); // reads env vars + <cwd>/.envPrecedence: Explicit parameters > environment variables.
Multiple provider keys: If multiple provider API keys are set in the environment (OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY), auto-detection will fail. Pass provider explicitly to init() to disambiguate.
How It Works
wrap(client, { gatewayUrl, ... })auto-detects the provider from the client type, computes the provider-specific path prefix (/openai,/anthropic,/gemini), rewrites the client's base URL, and injects custom headers (X-FlintAI-API-Key,X-Guardrails-Policy-Id)- The FlintAI guardrails proxy intercepts the request, applies policy checks (detectors), and strips the custom headers
- The proxy uses its own upstream credentials configured via environment variables to forward requests to the LLM provider
Global state: Each wrap() call updates the global FlintAI SDK client's guardrails config. If you wrap multiple clients with different parameters, each client retains its own headers and base URL, but only the last wrap() call's config is stored globally. For most applications (single provider, shared credentials), this is transparent.
Known Limitations
Private attribute mutation: wrap() redirects traffic by rewriting each client's base URL and injecting the guardrails headers (X-FlintAI-API-Key, X-Guardrails-Policy-Id). For OpenAI and Anthropic this uses the public baseURL property plus the internal _options.defaultHeaders, and for Google the internal apiClient.clientOptions.httpOptions. The internal attributes are not part of the respective SDKs' public API and may change without notice, so pin your SDK versions to the tested ranges:
openai >=6.41.0,<7@anthropic-ai/sdk >=0.100.1,<1@google/genai >=2.8.0,<3
wrap() resolves the installed version from each SDK's package.json and emits a console warning if it falls outside the tested range.
Google GenAI URL normalization: The Google GenAI SDK requires a trailing slash on the base URL. FlintAI SDK normalizes this automatically — do not add a trailing slash to gatewayUrl.
Security
Fail-closed by default: init() and wrap() throw FlintAIGuardrailsError when guardrails configuration cannot be resolved. To opt out, pass dangerouslyDisableGuardrails: true (preferred, self-documenting) or requireGuardrails: false. The opt-out is not silent: init(), wrap(), and the ADK plugin emit a SECURITY CONTROL DISABLED console warning (including the resolved agent identifier) — once per process — whenever guardrails are disabled, so unguarded deployments are detectable in logs/telemetry.
Inspect the effective posture: call flintai.status() (or client.guardrailsStatus()) at runtime to get { active, requireGuardrails, provider, agentId, agentName }, where active is true only when traffic is actually routed through the gateway. Returns null before init(). Use this to assert protection rather than trusting the opt-out flag alone.
HTTPS enforcement: Gateway URLs must use https:// for non-loopback hosts. http:// is allowed only for loopback (localhost, 127.0.0.1, ::1) and only when allowInsecureGateway: true is set (with a console warning).
Gateway host allowlist: By default, only app.flintai.dev is allowed as a gateway host. Override with the FLINTAI_ALLOWED_GATEWAY_HOSTS environment variable (comma-separated hostnames).
Loopback and wildcard require an explicit code opt-in: A loopback gatewayUrl and the FLINTAI_ALLOWED_GATEWAY_HOSTS='*' wildcard both let a caller redirect the FlintAI API key, provider credentials, and all prompt/response traffic to an unintended endpoint. Neither can be enabled through the environment or a .env file — you must pass allowInsecureGateway: true to init()/wrap() (intended for local development only). Requesting loopback or * without it throws at config time; using * with it also emits a console warning.
Plugins
Plugins handle events from the FlintAI SDK lifecycle. Extend FlintAIPlugin and override the methods you care about:
import { init, registerPlugin, FlintAIPlugin } from "@sandboxaq/flintai-sdk-ts";
class MyPlugin extends FlintAIPlugin {
name = "my-plugin";
onInit(client) {
console.log(`Plugin initialized with ${client}`);
}
onShutdown() {
console.log("Shutting down");
}
}
init();
registerPlugin(new MyPlugin());Plugin Methods
| Method | Called when |
|--------|-----------|
| onInit(client) | Plugin is registered |
| onShutdown() | shutdown() is called |
License
Apache-2.0
