SOLWYN

TypeScript SDK AI SDK Middleware

Add Solwyn budget checks and usage reporting to Vercel AI SDK language models with the TypeScript SDK middleware.

@solwyn/sdk/ai-sdk provides a Vercel AI SDK language-model middleware for generateText() and streamText(). It checks budget authority before the model call and settles usage afterward while leaving the model result and stream parts unchanged.

import { openai } from "@ai-sdk/openai";
import { generateText, wrapLanguageModel } from "ai";
import { createSolwynMiddleware } from "@solwyn/sdk/ai-sdk";

const solwyn = createSolwynMiddleware({
  apiKey: process.env.SOLWYN_API_KEY!,
});

const model = wrapLanguageModel({
  model: openai("gpt-4o"),
  middleware: solwyn.middleware,
});

try {
  const { text } = await generateText({
    model,
    prompt: "Explain vector search in two sentences.",
  });
  console.log(text);
} finally {
  await solwyn.close();
}

The ai package (>=7.0.14 <8) is an optional peer dependency of @solwyn/sdk; install the AI SDK and the model provider package your application uses. AI SDK 7 requires Node.js 22 or later.

What the middleware does

For each buffered or streaming language-model call, the middleware:

  1. Reads the wrapped model's provider and modelId for attribution.
  2. Measures the normalized prompt locally and converts its length into an input-token estimate.
  3. Performs a budget check before doGenerate or doStream runs.
  4. Returns a hard denial as the SDK's typed error without invoking the model.
  5. Uses provider-reported usage for settlement when it is available.
  6. Falls back to explicitly estimated token counts when usage is missing or incomplete.
  7. Queues the settlement and content-free metadata event for delivery.

For streams, parts pass through unchanged and backpressure is preserved. Terminal usage settles the successful call. Cancellation settles the usage observed so far, and a stream error is reported once without replacing the original error.

The handle owns its budget and reporting resources. Always call close() so queued settlements and events can flush. close() is idempotent, and the handle also implements Symbol.asyncDispose for await using. Once close() begins, new calls reject with SolwynError("client is closed"); buffered calls already in flight drain within reporterShutdownDeadline, and open streams settle once from the usage observed so far. Create a new middleware handle for later work. An abandoned stream is settled from its observed usage when it is garbage-collected, but finalization timing is runtime-dependent, so consume or cancel streams explicitly.

Provider attribution

Common AI SDK provider identifiers are normalized to Solwyn's provider names. This includes OpenAI, Azure OpenAI, Anthropic (including Vertex Anthropic), Amazon Bedrock, Google, xAI, DeepSeek, Mistral, Z.ai, Groq, Together, Fireworks, Perplexity, OpenRouter, Ollama, LM Studio, vLLM, generic OpenAI-compatible providers, and Alibaba (attributed as qwen). Unknown provider identifiers use openai_compatible attribution.

Vercel AI Gateway models need one extra step. The AI SDK identifies a string model as gateway, so Solwyn derives the vendor from the model ID's vendor/model prefix. Install @ai-sdk/gateway when using this form:

import { gateway } from "@ai-sdk/gateway";
import { generateText, wrapLanguageModel } from "ai";
import { createSolwynMiddleware } from "@solwyn/sdk/ai-sdk";

const solwyn = createSolwynMiddleware({
  apiKey: process.env.SOLWYN_API_KEY!,
});

const model = wrapLanguageModel({
  model: gateway("anthropic/claude-sonnet-4.5"),
  middleware: solwyn.middleware,
});

try {
  await generateText({ model, prompt: "Hello" });
} finally {
  await solwyn.close();
}

This call is attributed to anthropic, rather than grouping all gateway traffic under a generic provider.

Agent runs and leases

The middleware reads the active Node run context installed by @solwyn/sdk/node. Calls inside a run() callback receive its run identity and explicit run tags. Untagged, run-scoped text calls can use local token leases; tagged or unscoped calls use per-call checks.

import { openai } from "@ai-sdk/openai";
import { streamText, wrapLanguageModel } from "ai";
import { run } from "@solwyn/sdk/node";
import { createSolwynMiddleware } from "@solwyn/sdk/ai-sdk";

const solwyn = createSolwynMiddleware({
  apiKey: process.env.SOLWYN_API_KEY!,
  leaseEnabled: true,
  leaseOutputBoundDefault: 2048,
});

const model = wrapLanguageModel({
  model: openai("gpt-4o"),
  middleware: solwyn.middleware,
});

try {
  await run("support-agent", async () => {
    const result = streamText({ model, prompt: "Draft a reply." });
    for await (const chunk of result.textStream) {
      process.stdout.write(chunk);
    }
  });
} finally {
  await solwyn.close();
}

A server stop returned by the middleware's pre-flight check surfaces as RunStoppedError before the next AI SDK model dispatch. The middleware also refuses dispatch after a concurrent stop and aborts an in-flight stream at its next chunk boundary. A stopped stream keeps its stop after clearRunTermination(). Run stops and caller aborts settle the stream usage observed so far, including the part pulled at the stop boundary. The call's abortSignal also cancels budget preflight. Local velocity detection is not supported by the middleware. See TypeScript SDK Runs for exact-ID stop and clear semantics.

Options

createSolwynMiddleware() accepts the following camelCase options:

GroupOptions
ConnectionapiKey, apiUrl, fetch
Budget behaviorfailOpen, budgetMode, budgetCheckCacheTtl, budgetCheckTimeout
LeasesleaseEnabled, leaseOutputBoundDefault
Control-plane healthcontrolPlaneFailureThreshold, controlPlaneRecoveryTimeout, breakerReportingEnabled
ReportingreporterBatchSize, reporterFlushInterval, reporterMaxQueueSize, reporterMaxInFlight, reporterMaxSendAttempts, reporterRetryBackoffBase, reporterRetryBackoffCap, reporterShutdownDeadline, breakerReportHeartbeat
Diagnosticslogger, traceCorrelation

Unknown option names fail synchronously with ConfigurationError. The general meanings and defaults match the corresponding client options; see TypeScript SDK Configuration.

Scope and limitations

The middleware wraps one AI SDK language model. It does not wrap a provider client, select a fallback client, translate requests between provider dialects, or expose the drop-in wrapper's provider routing options. If you need Solwyn-managed provider-client failover, use the Solwyn wrapper described in TypeScript SDK Failover.

The middleware applies to the AI SDK's language-model generate and stream paths. Provider client media methods, provider surface coverage controls, and provider-specific helpers belong to the drop-in wrapper rather than this entry.

breakerReportingEnabled is accepted for configuration parity, but this middleware has no provider-client breaker snapshot to report.

The middleware receives normalized prompt and result content because that is how the AI SDK middleware contract works. It inspects content locally only to derive length estimates. Prompt and response content is never sent to Solwyn or logged by the SDK. See TypeScript SDK Privacy.

On this page