ai-retry 1.7.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1092,6 +1092,85 @@ const retryableModel = createRetryable({
1092
1092
  });
1093
1093
  ```
1094
1094
 
1095
+ ### Telemetry
1096
+
1097
+ > [!NOTE]
1098
+ > Experimental: Span names and attributes may change in patch versions.
1099
+
1100
+ `ai-retry` can emit [OpenTelemetry](https://opentelemetry.io/) spans for each request and every retry attempt. The spans are created on the active OpenTelemetry context, so they nest automatically under the AI SDK's own spans (e.g. `ai.generateText.doGenerate`) when you also enable `experimental_telemetry` on `generateText`/`streamText`. A single trace then shows the individual attempts — which model each used, why it was retried, and the backoff between them — that the SDK's own span otherwise hides.
1101
+
1102
+ #### Setup
1103
+
1104
+ Telemetry uses the optional peer dependency `@opentelemetry/api` (already present if you use the AI SDK). Register an OpenTelemetry SDK once at startup, then opt in per model:
1105
+
1106
+ ```typescript
1107
+ import { createRetryable } from 'ai-retry';
1108
+
1109
+ const retryableModel = createRetryable({
1110
+ model: openai('gpt-4o'),
1111
+ retries: [anthropic('claude-sonnet-4-5')],
1112
+ experimental_telemetry: { isEnabled: true },
1113
+ });
1114
+ ```
1115
+
1116
+ The settings mirror the AI SDK's `experimental_telemetry` shape:
1117
+
1118
+ ```ts
1119
+ interface RetryTelemetrySettings {
1120
+ isEnabled?: boolean; // off by default while experimental
1121
+ tracer?: Tracer; // defaults to trace.getTracer('ai-retry')
1122
+ functionId?: string; // groups telemetry by function
1123
+ metadata?: Record<string, AttributeValue>;
1124
+ }
1125
+ ```
1126
+
1127
+ Spans are emitted only when `isEnabled` is `true`. By default the global tracer is used, which is a no-op until an OpenTelemetry SDK is registered — so enabling it in code that runs without an SDK has no effect and no cost.
1128
+
1129
+ > [!NOTE]
1130
+ > Prompts and generated content are **not** recorded — only metadata (models, outcomes, errors, timing). The AI SDK's own telemetry records the prompt/response on its spans when you enable `recordInputs`/`recordOutputs`.
1131
+
1132
+ #### Spans
1133
+
1134
+ Each request creates one operation span (`ai_retry.doGenerate`, `ai_retry.doStream`, or `ai_retry.doEmbed`) with one child `ai_retry.attempt` span per attempt:
1135
+
1136
+ ```
1137
+ ai_retry.doGenerate outcome=success, attempts=2
1138
+ ├─ ai_retry.attempt #1 outcome=retry, type=error (529 → fallback)
1139
+ └─ ai_retry.attempt #2 outcome=success, type=result
1140
+ ```
1141
+
1142
+ **Operation span** attributes:
1143
+
1144
+ | Attribute | Description |
1145
+ | --------------------------------------------------------------- | ---------------------------------------------------------------------------- |
1146
+ | `ai_retry.operation` | `doGenerate`, `doStream`, or `doEmbed` |
1147
+ | `ai_retry.outcome` | `success` or `failure` |
1148
+ | `ai_retry.attempts` | total number of attempts |
1149
+ | `ai_retry.model.start` | the model the request started with (`provider/modelId`) |
1150
+ | `ai_retry.model.final` | the model that produced the final outcome |
1151
+ | `ai_retry.error.{name,message,status,cause.name,cause.message,cause.status}` | the failing error (on failure); `status` when it carries an HTTP status code |
1152
+ | `ai_retry.function.id`, `ai_retry.metadata.*` | from the telemetry settings |
1153
+
1154
+ **Attempt span** (`ai_retry.attempt`) attributes:
1155
+
1156
+ | Attribute | Description |
1157
+ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------ |
1158
+ | `ai_retry.attempt.number` | 1-based attempt index |
1159
+ | `ai_retry.attempt.model` | model used (`provider/modelId`) |
1160
+ | `ai_retry.attempt.outcome` | `success`, `retry`, or `failure` |
1161
+ | `ai_retry.attempt.type` | `result` or `error` |
1162
+ | `ai_retry.attempt.finish_reason` | finish reason (result attempts) |
1163
+ | `ai_retry.attempt.delay_ms` | backoff scheduled before the next attempt |
1164
+ | `ai_retry.attempt.timeout_ms` | timeout budget, when the retry set one |
1165
+ | `ai_retry.attempt.error.{name,message,status,cause.name,cause.message,cause.status}` | the error (error attempts); `status` when it carries an HTTP status code |
1166
+
1167
+ Attempt spans also carry the standard `gen_ai.request.model` / `gen_ai.provider.name` attributes so observability tools (Langfuse, etc.) recognize and render them.
1168
+
1169
+ > [!NOTE]
1170
+ > **Streaming:** retries only happen before the first content chunk (see [Streaming](#streaming)), so a `ai_retry.doStream` attempt is marked `success` once content begins flowing; mid-stream retries appear as additional attempt spans.
1171
+
1172
+ See [`examples/telemetry`](./examples/telemetry) for a runnable example that exports to Langfuse.
1173
+
1095
1174
  ### Streaming
1096
1175
 
1097
1176
  Errors during streaming requests can occur in two ways:
@@ -1118,6 +1197,7 @@ interface RetryableModelOptions<
1118
1197
  retries: Array<Retryable<MODEL> | MODEL>;
1119
1198
  disabled?: boolean | (() => boolean);
1120
1199
  reset?: Reset;
1200
+ experimental_telemetry?: RetryTelemetrySettings;
1121
1201
  onError?: (context: RetryContext<MODEL>) => void;
1122
1202
  onRetry?: (
1123
1203
  context: RetryContext<MODEL>,
@@ -1132,6 +1212,7 @@ interface RetryableModelOptions<
1132
1212
  - `retries`: Array of retryables (functions, models, or retry objects) to attempt on failure.
1133
1213
  - `disabled`: Disable all retry logic. Can be a boolean or function returning boolean. Default: `false` (retries enabled).
1134
1214
  - `reset`: Controls when to reset back to the base model after a successful retry. Default: `after-request`.
1215
+ - `experimental_telemetry`: OpenTelemetry instrumentation for retries. Off by default. See [Telemetry](#telemetry).
1135
1216
  - `onError`: Callback invoked when an error occurs.
1136
1217
  - `onRetry`: Callback invoked before attempting a retry. May optionally return an `OnRetryOverrides` object (or a `Promise` of one) to override `options.*` for the upcoming attempt only. See [Dynamic Call Options via `onRetry`](#dynamic-call-options-via-onretry).
1137
1218
  - `onSuccess`: Callback invoked after a successful request. Receives the model that handled the request and all previous attempts.