npm package: @quotadev/sdk
Description: Quota SDK for usage-based billing of AI features
Latest version: 0.1.1

# `@quotadev/sdk`

Quota SDK for usage tracking, billing helpers, and AI SDK telemetry correlation.

## AI SDK Telemetry Middleware

`@quotadev/sdk` now exports `createQuotaAISDKTelemetryMiddleware()` for the Vercel AI SDK.

It:

- enables `experimental_telemetry` by default
- injects a correlation request ID (`x-request-id`)
- optionally injects Quota proxy headers (`x-quota-customer`, `x-quota-tag`)
- attaches Quota metadata to AI SDK telemetry spans

### Install

```bash
pnpm add @quotadev/sdk ai
```

## Quickstart (AI SDK + Quota Proxy)

```ts
import { wrapLanguageModel, generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { createQuotaAISDKTelemetryMiddleware } from "@quotadev/sdk";

const model = wrapLanguageModel({
  model: openai("gpt-4o", {
    // Point provider calls at your Quota proxy if desired
    baseURL: "https://your-proxy.example.com/v1",
    apiKey: process.env.QUOTA_KEY!,
  }),
  middleware: createQuotaAISDKTelemetryMiddleware({
    functionId: "chat.reply",
    customerId: "cus_123",
    tags: {
      feature: "chat",
      route: "support",
      env: process.env.NODE_ENV ?? "development",
    },
    metadata: {
      app: "dashboard-web",
      team: "growth",
    },
  }),
});

const result = await generateText({
  model,
  prompt: "Summarize the last 5 support tickets",
});
```

## Streaming Example

```ts
import { wrapLanguageModel, streamText } from "ai";
import { openrouter } from "@openrouter/ai-sdk-provider";
import { createQuotaAISDKTelemetryMiddleware } from "@quotadev/sdk";

const model = wrapLanguageModel({
  model: openrouter.chat("anthropic/claude-3.5-sonnet"),
  middleware: createQuotaAISDKTelemetryMiddleware({
    functionId: "assist.stream",
    customerId: "cus_123",
    tags: { feature: "assistant", mode: "stream" },
    onStart(event) {
      console.log("[ai:start]", event.requestId, event.operation);
    },
    onFinish(event) {
      console.log("[ai:finish]", event.requestId, event.latencyMs);
    },
    onError(event) {
      console.error("[ai:error]", event.requestId, event.error);
    },
  }),
});

const result = await streamText({
  model,
  prompt: "Draft a release note for the latest observability update",
});
```

## OpenTelemetry Exporter Setup (Node / OTLP)

AI SDK telemetry emits OpenTelemetry spans. Configure an exporter in your app runtime.

```ts
// instrumentation.ts (or your server bootstrap file)
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { Resource } from "@opentelemetry/resources";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    // e.g. Honeycomb/Tempo/Datadog OTLP endpoint
    url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
    headers: process.env.OTEL_EXPORTER_OTLP_HEADERS
      ? Object.fromEntries(
          process.env.OTEL_EXPORTER_OTLP_HEADERS.split(",").map((pair) => {
            const [k, v] = pair.split("=");
            return [k.trim(), v.trim()];
          }),
        )
      : undefined,
  }),
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: "my-ai-app",
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
      process.env.NODE_ENV ?? "development",
  }),
});

sdk.start();
```

## What You Get (Proxy + AI SDK Together)

Using both the Quota proxy and AI SDK telemetry gives you:

- proxy-level transport metrics (status, latency, stream timing, bytes, error taxonomy)
- SDK-level semantic spans (function IDs, metadata, tool activity, model call traces)
- end-to-end correlation via `x-request-id` / `quotaRequestId`

## Notes

- The middleware is additive; it does not replace provider SDK telemetry.
- `injectHeaders` is enabled by default so Quota proxy correlation works out of the box.
- For non-proxy direct provider calls, telemetry still works; only Quota-specific headers may be unused.