@ai-sdk/anthropic 3.0.39 → 3.0.41

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.
@@ -290,6 +290,98 @@ const result = await generateText({
290
290
  });
291
291
  ```
292
292
 
293
+ #### Compaction
294
+
295
+ The `compact_20260112` edit type automatically summarizes earlier conversation context when token limits are reached. This is useful for long-running conversations where you want to preserve the essence of earlier exchanges while staying within token limits.
296
+
297
+ ```ts highlight="7-19"
298
+ import { anthropic, AnthropicProviderOptions } from '@ai-sdk/anthropic';
299
+ import { streamText } from 'ai';
300
+
301
+ const result = streamText({
302
+ model: anthropic('claude-opus-4-6'),
303
+ messages: conversationHistory,
304
+ providerOptions: {
305
+ anthropic: {
306
+ contextManagement: {
307
+ edits: [
308
+ {
309
+ type: 'compact_20260112',
310
+ trigger: {
311
+ type: 'input_tokens',
312
+ value: 50000, // trigger compaction when input exceeds 50k tokens
313
+ },
314
+ instructions:
315
+ 'Summarize the conversation concisely, preserving key decisions and context.',
316
+ pauseAfterCompaction: false,
317
+ },
318
+ ],
319
+ },
320
+ } satisfies AnthropicProviderOptions,
321
+ },
322
+ });
323
+ ```
324
+
325
+ **Configuration:**
326
+
327
+ - **trigger** - Condition that triggers compaction (e.g., `{ type: 'input_tokens', value: 50000 }`)
328
+ - **instructions** - Custom instructions for how the model should summarize the conversation. Use this to guide the compaction summary towards specific aspects of the conversation you want to preserve.
329
+ - **pauseAfterCompaction** - When `true`, the model will pause after generating the compaction summary, allowing you to inspect or process it before continuing. Defaults to `false`.
330
+
331
+ When compaction occurs, the model generates a summary of the earlier context. This summary appears as a text block with special provider metadata.
332
+
333
+ ##### Detecting Compaction in Streams
334
+
335
+ When using `streamText`, you can detect compaction summaries by checking the `providerMetadata` on `text-start` events:
336
+
337
+ ```ts
338
+ for await (const part of result.fullStream) {
339
+ switch (part.type) {
340
+ case 'text-start': {
341
+ const isCompaction =
342
+ part.providerMetadata?.anthropic?.type === 'compaction';
343
+ if (isCompaction) {
344
+ console.log('[COMPACTION SUMMARY START]');
345
+ }
346
+ break;
347
+ }
348
+ case 'text-delta': {
349
+ process.stdout.write(part.text);
350
+ break;
351
+ }
352
+ }
353
+ }
354
+ ```
355
+
356
+ ##### Compaction in UI Applications
357
+
358
+ When using `useChat` or other UI hooks, compaction summaries appear as regular text parts with `providerMetadata`. You can style them differently in your UI:
359
+
360
+ ```tsx
361
+ {
362
+ message.parts.map((part, index) => {
363
+ if (part.type === 'text') {
364
+ const isCompaction =
365
+ (part.providerMetadata?.anthropic as { type?: string } | undefined)
366
+ ?.type === 'compaction';
367
+
368
+ if (isCompaction) {
369
+ return (
370
+ <div
371
+ key={index}
372
+ className="bg-yellow-100 border-l-4 border-yellow-500 p-2"
373
+ >
374
+ <span className="font-bold">[Compaction Summary]</span>
375
+ <div>{part.text}</div>
376
+ </div>
377
+ );
378
+ }
379
+ return <div key={index}>{part.text}</div>;
380
+ }
381
+ });
382
+ }
383
+ ```
384
+
293
385
  #### Applied Edits Metadata
294
386
 
295
387
  After generation, you can check which edits were applied in the provider metadata:
@@ -305,6 +397,9 @@ if (metadata?.appliedEdits) {
305
397
  } else if (edit.type === 'clear_thinking_20251015') {
306
398
  console.log(`Cleared ${edit.clearedThinkingTurns} thinking turns`);
307
399
  console.log(`Freed ${edit.clearedInputTokens} tokens`);
400
+ } else if (edit.type === 'compact_20260112') {
401
+ console.log('Compaction was applied');
402
+ console.log(`Freed ${edit.clearedInputTokens} tokens`);
308
403
  }
309
404
  });
310
405
  }
@@ -1174,17 +1269,17 @@ and the `mediaType` should be set to `'application/pdf'`.
1174
1269
 
1175
1270
  ### Model Capabilities
1176
1271
 
1177
- | Model | Image Input | Object Generation | Tool Usage | Computer Use | Web Search | Tool Search |
1178
- | -------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
1179
- | `claude-opus-4-6` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1180
- | `claude-opus-4-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1181
- | `claude-haiku-4-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1182
- | `claude-sonnet-4-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1183
- | `claude-opus-4-1` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1184
- | `claude-opus-4-0` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1185
- | `claude-sonnet-4-0` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1186
- | `claude-3-7-sonnet-latest` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1187
- | `claude-3-5-haiku-latest` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1272
+ | Model | Image Input | Object Generation | Tool Usage | Computer Use | Web Search | Tool Search | Compaction |
1273
+ | -------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
1274
+ | `claude-opus-4-6` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
1275
+ | `claude-opus-4-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1276
+ | `claude-haiku-4-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | | |
1277
+ | `claude-sonnet-4-5` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | |
1278
+ | `claude-opus-4-1` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | | |
1279
+ | `claude-opus-4-0` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | | |
1280
+ | `claude-sonnet-4-0` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | | |
1281
+ | `claude-3-7-sonnet-latest` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | | |
1282
+ | `claude-3-5-haiku-latest` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | | |
1188
1283
 
1189
1284
  <Note>
1190
1285
  The table above lists popular models. Please see the [Anthropic
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/anthropic",
3
- "version": "3.0.39",
3
+ "version": "3.0.41",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -1,5 +1,24 @@
1
1
  import { JSONObject } from '@ai-sdk/provider';
2
2
 
3
+ /**
4
+ * Represents a single iteration in the usage breakdown.
5
+ * When compaction occurs, the API returns an iterations array showing
6
+ * usage for each sampling iteration (compaction + message).
7
+ */
8
+ export interface AnthropicUsageIteration {
9
+ type: 'compaction' | 'message';
10
+
11
+ /**
12
+ * Number of input tokens consumed in this iteration.
13
+ */
14
+ inputTokens: number;
15
+
16
+ /**
17
+ * Number of output tokens generated in this iteration.
18
+ */
19
+ outputTokens: number;
20
+ }
21
+
3
22
  export interface AnthropicMessageMetadata {
4
23
  usage: JSONObject;
5
24
  // TODO remove cacheCreationInputTokens in AI SDK 6
@@ -7,6 +26,15 @@ export interface AnthropicMessageMetadata {
7
26
  cacheCreationInputTokens: number | null;
8
27
  stopSequence: string | null;
9
28
 
29
+ /**
30
+ * Usage breakdown by iteration when compaction is triggered.
31
+ *
32
+ * When compaction occurs, this array contains usage for each sampling iteration.
33
+ * The first iteration is typically the compaction step, followed by the main
34
+ * message iteration.
35
+ */
36
+ iterations: AnthropicUsageIteration[] | null;
37
+
10
38
  /**
11
39
  * Information about the container used in this request.
12
40
  *
@@ -100,6 +128,16 @@ export interface AnthropicMessageMetadata {
100
128
  */
101
129
  clearedInputTokens: number;
102
130
  }
131
+ /**
132
+ * Represents a compaction edit where the conversation context was summarized.
133
+ */
134
+ | {
135
+ /**
136
+ * The type of context management edit applied.
137
+ * Possible value: 'compact_20260112'
138
+ */
139
+ type: 'compact_20260112';
140
+ }
103
141
  >;
104
142
  } | null;
105
143
  }
@@ -40,9 +40,16 @@ export interface AnthropicAssistantMessage {
40
40
  | AnthropicTextEditorCodeExecutionToolResultContent
41
41
  | AnthropicMcpToolUseContent
42
42
  | AnthropicMcpToolResultContent
43
+ | AnthropicCompactionContent
43
44
  >;
44
45
  }
45
46
 
47
+ export interface AnthropicCompactionContent {
48
+ type: 'compaction';
49
+ content: string;
50
+ cache_control?: AnthropicCacheControl;
51
+ }
52
+
46
53
  export interface AnthropicTextContent {
47
54
  type: 'text';
48
55
  text: string;
@@ -479,9 +486,17 @@ export type AnthropicClearThinkingBlockEdit = {
479
486
  keep?: 'all' | { type: 'thinking_turns'; value: number };
480
487
  };
481
488
 
489
+ export type AnthropicCompactEdit = {
490
+ type: 'compact_20260112';
491
+ trigger?: AnthropicInputTokensTrigger;
492
+ pause_after_compaction?: boolean;
493
+ instructions?: string;
494
+ };
495
+
482
496
  export type AnthropicContextManagementEdit =
483
497
  | AnthropicClearToolUsesEdit
484
- | AnthropicClearThinkingBlockEdit;
498
+ | AnthropicClearThinkingBlockEdit
499
+ | AnthropicCompactEdit;
485
500
 
486
501
  export type AnthropicContextManagementConfig = {
487
502
  edits: AnthropicContextManagementEdit[];
@@ -499,9 +514,14 @@ export type AnthropicResponseClearThinkingBlockEdit = {
499
514
  cleared_input_tokens: number;
500
515
  };
501
516
 
517
+ export type AnthropicResponseCompactEdit = {
518
+ type: 'compact_20260112';
519
+ };
520
+
502
521
  export type AnthropicResponseContextManagementEdit =
503
522
  | AnthropicResponseClearToolUsesEdit
504
- | AnthropicResponseClearThinkingBlockEdit;
523
+ | AnthropicResponseClearThinkingBlockEdit
524
+ | AnthropicResponseCompactEdit;
505
525
 
506
526
  export type AnthropicResponseContextManagement = {
507
527
  applied_edits: AnthropicResponseContextManagementEdit[];
@@ -559,6 +579,10 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
559
579
  type: z.literal('redacted_thinking'),
560
580
  data: z.string(),
561
581
  }),
582
+ z.object({
583
+ type: z.literal('compaction'),
584
+ content: z.string(),
585
+ }),
562
586
  z.object({
563
587
  type: z.literal('tool_use'),
564
588
  id: z.string(),
@@ -763,6 +787,15 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
763
787
  output_tokens: z.number(),
764
788
  cache_creation_input_tokens: z.number().nullish(),
765
789
  cache_read_input_tokens: z.number().nullish(),
790
+ iterations: z
791
+ .array(
792
+ z.object({
793
+ type: z.union([z.literal('compaction'), z.literal('message')]),
794
+ input_tokens: z.number(),
795
+ output_tokens: z.number(),
796
+ }),
797
+ )
798
+ .nullish(),
766
799
  }),
767
800
  container: z
768
801
  .object({
@@ -793,6 +826,9 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
793
826
  cleared_thinking_turns: z.number(),
794
827
  cleared_input_tokens: z.number(),
795
828
  }),
829
+ z.object({
830
+ type: z.literal('compact_20260112'),
831
+ }),
796
832
  ]),
797
833
  ),
798
834
  })
@@ -885,6 +921,10 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
885
921
  type: z.literal('redacted_thinking'),
886
922
  data: z.string(),
887
923
  }),
924
+ z.object({
925
+ type: z.literal('compaction'),
926
+ content: z.string().nullish(),
927
+ }),
888
928
  z.object({
889
929
  type: z.literal('server_tool_use'),
890
930
  id: z.string(),
@@ -1084,6 +1124,10 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
1084
1124
  type: z.literal('signature_delta'),
1085
1125
  signature: z.string(),
1086
1126
  }),
1127
+ z.object({
1128
+ type: z.literal('compaction_delta'),
1129
+ content: z.string(),
1130
+ }),
1087
1131
  z.object({
1088
1132
  type: z.literal('citations_delta'),
1089
1133
  citation: z.discriminatedUnion('type', [
@@ -1154,6 +1198,15 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
1154
1198
  output_tokens: z.number(),
1155
1199
  cache_creation_input_tokens: z.number().nullish(),
1156
1200
  cache_read_input_tokens: z.number().nullish(),
1201
+ iterations: z
1202
+ .array(
1203
+ z.object({
1204
+ type: z.union([z.literal('compaction'), z.literal('message')]),
1205
+ input_tokens: z.number(),
1206
+ output_tokens: z.number(),
1207
+ }),
1208
+ )
1209
+ .nullish(),
1157
1210
  }),
1158
1211
  context_management: z
1159
1212
  .object({
@@ -1169,6 +1222,9 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
1169
1222
  cleared_thinking_turns: z.number(),
1170
1223
  cleared_input_tokens: z.number(),
1171
1224
  }),
1225
+ z.object({
1226
+ type: z.literal('compact_20260112'),
1227
+ }),
1172
1228
  ]),
1173
1229
  ),
1174
1230
  })
@@ -433,6 +433,20 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
433
433
  ...(edit.keep !== undefined && { keep: edit.keep }),
434
434
  };
435
435
 
436
+ case 'compact_20260112':
437
+ return {
438
+ type: edit.type,
439
+ ...(edit.trigger !== undefined && {
440
+ trigger: edit.trigger,
441
+ }),
442
+ ...(edit.pauseAfterCompaction !== undefined && {
443
+ pause_after_compaction: edit.pauseAfterCompaction,
444
+ }),
445
+ ...(edit.instructions !== undefined && {
446
+ instructions: edit.instructions,
447
+ }),
448
+ };
449
+
436
450
  default:
437
451
  warnings.push({
438
452
  type: 'other',
@@ -528,6 +542,11 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
528
542
 
529
543
  if (contextManagement) {
530
544
  betas.add('context-management-2025-06-27');
545
+
546
+ // Add compaction beta if compact edit is present
547
+ if (contextManagement.edits.some(e => e.type === 'compact_20260112')) {
548
+ betas.add('compact-2026-01-12');
549
+ }
531
550
  }
532
551
 
533
552
  if (
@@ -796,6 +815,18 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
796
815
  });
797
816
  break;
798
817
  }
818
+ case 'compaction': {
819
+ content.push({
820
+ type: 'text',
821
+ text: part.content,
822
+ providerMetadata: {
823
+ anthropic: {
824
+ type: 'compaction',
825
+ },
826
+ },
827
+ });
828
+ break;
829
+ }
799
830
  case 'tool_use': {
800
831
  const isJsonResponseTool =
801
832
  usesJsonResponseTool && part.name === 'json';
@@ -1098,7 +1129,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1098
1129
  }),
1099
1130
  raw: response.stop_reason ?? undefined,
1100
1131
  },
1101
- usage: convertAnthropicMessagesUsage(response.usage),
1132
+ usage: convertAnthropicMessagesUsage({ usage: response.usage }),
1102
1133
  request: { body: args },
1103
1134
  response: {
1104
1135
  id: response.id ?? undefined,
@@ -1113,6 +1144,14 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1113
1144
  cacheCreationInputTokens:
1114
1145
  response.usage.cache_creation_input_tokens ?? null,
1115
1146
  stopSequence: response.stop_sequence ?? null,
1147
+
1148
+ iterations: response.usage.iterations
1149
+ ? response.usage.iterations.map(iter => ({
1150
+ type: iter.type,
1151
+ inputTokens: iter.input_tokens,
1152
+ outputTokens: iter.output_tokens,
1153
+ }))
1154
+ : null,
1116
1155
  container: response.container
1117
1156
  ? {
1118
1157
  expiresAt: response.container.expires_at,
@@ -1188,6 +1227,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1188
1227
  output_tokens: 0,
1189
1228
  cache_creation_input_tokens: 0,
1190
1229
  cache_read_input_tokens: 0,
1230
+ iterations: null,
1191
1231
  };
1192
1232
 
1193
1233
  const contentBlocks: Record<
@@ -1233,6 +1273,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1233
1273
  | 'tool_search_tool_result'
1234
1274
  | 'mcp_tool_use'
1235
1275
  | 'mcp_tool_result'
1276
+ | 'compaction'
1236
1277
  | undefined = undefined;
1237
1278
 
1238
1279
  const generateId = this.generateId;
@@ -1307,6 +1348,20 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1307
1348
  return;
1308
1349
  }
1309
1350
 
1351
+ case 'compaction': {
1352
+ contentBlocks[value.index] = { type: 'text' };
1353
+ controller.enqueue({
1354
+ type: 'text-start',
1355
+ id: String(value.index),
1356
+ providerMetadata: {
1357
+ anthropic: {
1358
+ type: 'compaction',
1359
+ },
1360
+ },
1361
+ });
1362
+ return;
1363
+ }
1364
+
1310
1365
  case 'tool_use': {
1311
1366
  const isJsonResponseTool =
1312
1367
  usesJsonResponseTool && part.name === 'json';
@@ -1784,6 +1839,16 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1784
1839
  return;
1785
1840
  }
1786
1841
 
1842
+ case 'compaction_delta': {
1843
+ controller.enqueue({
1844
+ type: 'text-delta',
1845
+ id: String(value.index),
1846
+ delta: value.delta.content,
1847
+ });
1848
+
1849
+ return;
1850
+ }
1851
+
1787
1852
  case 'input_json_delta': {
1788
1853
  const contentBlock = contentBlocks[value.index];
1789
1854
  let delta = value.delta.partial_json;
@@ -1972,6 +2037,9 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1972
2037
  cacheCreationInputTokens =
1973
2038
  value.usage.cache_creation_input_tokens;
1974
2039
  }
2040
+ if (value.usage.iterations != null) {
2041
+ usage.iterations = value.usage.iterations;
2042
+ }
1975
2043
 
1976
2044
  finishReason = {
1977
2045
  unified: mapAnthropicStopReason({
@@ -2015,6 +2083,13 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
2015
2083
  usage: (rawUsage as JSONObject) ?? null,
2016
2084
  cacheCreationInputTokens,
2017
2085
  stopSequence,
2086
+ iterations: usage.iterations
2087
+ ? usage.iterations.map(iter => ({
2088
+ type: iter.type,
2089
+ inputTokens: iter.input_tokens,
2090
+ outputTokens: iter.output_tokens,
2091
+ }))
2092
+ : null,
2018
2093
  container,
2019
2094
  contextManagement,
2020
2095
  } satisfies AnthropicMessageMetadata;
@@ -2033,7 +2108,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
2033
2108
  controller.enqueue({
2034
2109
  type: 'finish',
2035
2110
  finishReason,
2036
- usage: convertAnthropicMessagesUsage(usage),
2111
+ usage: convertAnthropicMessagesUsage({ usage, rawUsage }),
2037
2112
  providerMetadata,
2038
2113
  });
2039
2114
  return;
@@ -2188,6 +2263,11 @@ function mapAnthropicResponseContextManagement(
2188
2263
  clearedThinkingTurns: edit.cleared_thinking_turns,
2189
2264
  clearedInputTokens: edit.cleared_input_tokens,
2190
2265
  };
2266
+
2267
+ case 'compact_20260112':
2268
+ return {
2269
+ type: edit.type,
2270
+ };
2191
2271
  }
2192
2272
  })
2193
2273
  .filter(edit => edit !== undefined),
@@ -221,6 +221,17 @@ export const anthropicProviderOptions = z.object({
221
221
  ])
222
222
  .optional(),
223
223
  }),
224
+ z.object({
225
+ type: z.literal('compact_20260112'),
226
+ trigger: z
227
+ .object({
228
+ type: z.literal('input_tokens'),
229
+ value: z.number(),
230
+ })
231
+ .optional(),
232
+ pauseAfterCompaction: z.boolean().optional(),
233
+ instructions: z.string().optional(),
234
+ }),
224
235
  ]),
225
236
  ),
226
237
  })
@@ -1,20 +1,61 @@
1
- import { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import { JSONObject, LanguageModelV3Usage } from '@ai-sdk/provider';
2
+
3
+ /**
4
+ * Represents a single iteration in the usage breakdown.
5
+ * When compaction occurs, the API returns an iterations array showing
6
+ * usage for each sampling iteration (compaction + message).
7
+ */
8
+ export type AnthropicUsageIteration = {
9
+ type: 'compaction' | 'message';
10
+ input_tokens: number;
11
+ output_tokens: number;
12
+ };
2
13
 
3
14
  export type AnthropicMessagesUsage = {
4
15
  input_tokens: number;
5
16
  output_tokens: number;
6
17
  cache_creation_input_tokens?: number | null;
7
18
  cache_read_input_tokens?: number | null;
19
+ /**
20
+ * When compaction is triggered, this array contains usage for each
21
+ * sampling iteration. The top-level input_tokens and output_tokens
22
+ * do NOT include compaction iteration usage - to get total tokens
23
+ * consumed and billed, sum across all entries in this array.
24
+ */
25
+ iterations?: AnthropicUsageIteration[] | null;
8
26
  };
9
27
 
10
- export function convertAnthropicMessagesUsage(
11
- usage: AnthropicMessagesUsage,
12
- ): LanguageModelV3Usage {
13
- const inputTokens = usage.input_tokens;
14
- const outputTokens = usage.output_tokens;
28
+ export function convertAnthropicMessagesUsage({
29
+ usage,
30
+ rawUsage,
31
+ }: {
32
+ usage: AnthropicMessagesUsage;
33
+ rawUsage?: JSONObject;
34
+ }): LanguageModelV3Usage {
15
35
  const cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
16
36
  const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
17
37
 
38
+ // When iterations is present (compaction occurred), sum across all iterations
39
+ // to get the true total tokens consumed/billed. The top-level input_tokens
40
+ // and output_tokens exclude compaction iteration usage.
41
+ let inputTokens: number;
42
+ let outputTokens: number;
43
+
44
+ if (usage.iterations && usage.iterations.length > 0) {
45
+ const totals = usage.iterations.reduce(
46
+ (acc, iter) => ({
47
+ input: acc.input + iter.input_tokens,
48
+ output: acc.output + iter.output_tokens,
49
+ }),
50
+ { input: 0, output: 0 },
51
+ );
52
+ inputTokens = totals.input;
53
+ outputTokens = totals.output;
54
+ } else {
55
+ inputTokens = usage.input_tokens;
56
+ outputTokens = usage.output_tokens;
57
+ }
58
+
18
59
  return {
19
60
  inputTokens: {
20
61
  total: inputTokens + cacheCreationTokens + cacheReadTokens,
@@ -27,6 +68,6 @@ export function convertAnthropicMessagesUsage(
27
68
  text: undefined,
28
69
  reasoning: undefined,
29
70
  },
30
- raw: usage,
71
+ raw: rawUsage ?? usage,
31
72
  };
32
73
  }
@@ -456,18 +456,31 @@ export async function convertToAnthropicMessagesPrompt({
456
456
 
457
457
  switch (part.type) {
458
458
  case 'text': {
459
- anthropicContent.push({
460
- type: 'text',
461
- text:
462
- // trim the last text part if it's the last message in the block
463
- // because Anthropic does not allow trailing whitespace
464
- // in pre-filled assistant responses
465
- isLastBlock && isLastMessage && isLastContentPart
466
- ? part.text.trim()
467
- : part.text,
459
+ // Check if this is a compaction block (via providerMetadata)
460
+ const textMetadata = part.providerOptions?.anthropic as
461
+ | { type?: string }
462
+ | undefined;
468
463
 
469
- cache_control: cacheControl,
470
- });
464
+ if (textMetadata?.type === 'compaction') {
465
+ anthropicContent.push({
466
+ type: 'compaction',
467
+ content: part.text,
468
+ cache_control: cacheControl,
469
+ });
470
+ } else {
471
+ anthropicContent.push({
472
+ type: 'text',
473
+ text:
474
+ // trim the last text part if it's the last message in the block
475
+ // because Anthropic does not allow trailing whitespace
476
+ // in pre-filled assistant responses
477
+ isLastBlock && isLastMessage && isLastContentPart
478
+ ? part.text.trim()
479
+ : part.text,
480
+
481
+ cache_control: cacheControl,
482
+ });
483
+ }
471
484
  break;
472
485
  }
473
486
 
package/src/index.ts CHANGED
@@ -1,4 +1,7 @@
1
- export type { AnthropicMessageMetadata } from './anthropic-message-metadata';
1
+ export type {
2
+ AnthropicMessageMetadata,
3
+ AnthropicUsageIteration,
4
+ } from './anthropic-message-metadata';
2
5
  export type { AnthropicProviderOptions } from './anthropic-messages-options';
3
6
  export type { AnthropicToolOptions } from './anthropic-prepare-tools';
4
7
  export { anthropic, createAnthropic } from './anthropic-provider';
@@ -22,6 +22,8 @@ export function mapAnthropicStopReason({
22
22
  case 'max_tokens':
23
23
  case 'model_context_window_exceeded':
24
24
  return 'length';
25
+ case 'compaction':
26
+ return 'other';
25
27
  default:
26
28
  return 'other';
27
29
  }