generate and stream calls.
Installation
Quick Start
Configuration
| Option | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | Vendor API key |
agentId | string | Yes | Agent UUID |
consumerId | string | Yes | Consumer UUID |
signingKey | string | Yes | PEM Ed25519 private key |
apiUrl | string | No | API base URL |
deriveJobId | function | No | Custom job ID logic |
deriveMetadata | function | No | Custom receipt metadata |
deriveResolution | function | No | Custom resolution logic |
fetchImpl | function | No | Custom fetch implementation |
onError | function | No | Error callback |
How It Works
The middleware wraps bothgenerate and stream model calls:
- Successful
generatecalls — submits a receipt with"success"resolution after generation returns - Failed calls — submits a receipt with
"failed"resolution, then re-throws the error streamcalls — currently submit once the stream is created, not after the stream has been fully consumed
- A job ID for correlation across systems
- Default metadata including framework source, run type, and execution hints
- An Ed25519 signature
ai-. If
you want to align receipts with your own internal runs, provide deriveJobId.
If you want to attach structured context like workflow names, trace IDs, or
tenant-specific references, provide deriveMetadata.
Derived metadata is merged on top of the default integration metadata. By
default, AI SDK receipts include:
source: "ai-sdk"runType: "generate" | "stream"submissionPhaseto show whether the receipt was emitted on response return or stream starthasError, pluserrorNamewhen a model call throws
Works with Streaming
The middleware works with bothgenerateText and streamText:
wrapStream records stream start rather than confirmed stream completion.
That means a streaming receipt tells you the call entered streaming mode, not
that every chunk was consumed by the caller. If you want to reflect that nuance
in your scoring, set deriveResolution for stream calls to something like
"partial".
Custom Resolution Logic
deriveJobId, deriveMetadata, and deriveResolution all receive the same
context object. Use type to distinguish generate from stream, and error
to branch explicitly on failed model calls.
Idempotency and Duplicate Suppression
The autogeneratedai-* job IDs are fine for demos, but they are not
idempotent across retries. In production, create the middleware inside your
request or job boundary and pass a stable vendor-controlled ID into
deriveJobId.
deriveMetadata, not in job_id.
Because the current middleware records stream when the stream is created,
you should decide whether generate and stream for the same top-level task
should share a logical job ID or intentionally use separate suffixes.
Error Handling
The middleware never interferes with your model calls. Submission errors are caught silently. UseonError to monitor: