@ai-sdk/openai 4.0.0-beta.10 → 4.0.0-beta.12
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/CHANGELOG.md +47 -33
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +85 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -6
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.d.mts +17 -1
- package/dist/internal/index.d.ts +17 -1
- package/dist/internal/index.js +84 -5
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +84 -5
- package/dist/internal/index.mjs.map +1 -1
- package/docs/03-openai.mdx +124 -0
- package/package.json +3 -5
- package/src/index.ts +1 -0
- package/src/responses/convert-to-openai-responses-input.ts +35 -4
- package/src/responses/openai-responses-api.ts +23 -1
- package/src/responses/openai-responses-language-model.ts +36 -0
- package/src/responses/openai-responses-options.ts +12 -0
- package/src/responses/openai-responses-provider-metadata.ts +10 -0
package/docs/03-openai.mdx
CHANGED
|
@@ -257,6 +257,11 @@ The following provider options are available:
|
|
|
257
257
|
- **forceReasoning** _boolean_
|
|
258
258
|
Force treating this model as a reasoning model. This is useful for "stealth" reasoning models (e.g. via a custom baseURL) where the model ID is not recognized by the SDK's allowlist. When enabled, the SDK applies reasoning-model parameter compatibility rules and defaults `systemMessageMode` to `developer` unless overridden.
|
|
259
259
|
|
|
260
|
+
- **contextManagement** _Array<object>_
|
|
261
|
+
Enable server-side context management (compaction). When configured, the server automatically compresses conversation context when token usage crosses a specified threshold. Each object in the array should have:
|
|
262
|
+
- `type`: `'compaction'`
|
|
263
|
+
- `compactThreshold`: _number_ — the token count at which compaction is triggered
|
|
264
|
+
|
|
260
265
|
The OpenAI responses provider also returns provider-specific metadata:
|
|
261
266
|
|
|
262
267
|
For Responses models, you can type this metadata using `OpenaiResponsesProviderMetadata`:
|
|
@@ -1511,6 +1516,125 @@ for (const part of result.content) {
|
|
|
1511
1516
|
are fields like `filename` that are directly available on the source object.
|
|
1512
1517
|
</Note>
|
|
1513
1518
|
|
|
1519
|
+
#### Compaction
|
|
1520
|
+
|
|
1521
|
+
The OpenAI Responses API supports server-side context compaction. When enabled, the server automatically compresses conversation context when token usage crosses a configured threshold. This is useful for long-running conversations or agent loops where you want to stay within token limits without manually managing context.
|
|
1522
|
+
|
|
1523
|
+
The compaction item returned by the server is opaque and encrypted — it carries forward key prior state and reasoning into the next turn using fewer tokens. The AI SDK handles this automatically: compaction items are returned as text parts with special `providerMetadata`, and when passed back in subsequent requests they are sent as compaction input items.
|
|
1524
|
+
|
|
1525
|
+
```ts highlight="7-11"
|
|
1526
|
+
import {
|
|
1527
|
+
openai,
|
|
1528
|
+
type OpenAILanguageModelResponsesOptions,
|
|
1529
|
+
} from '@ai-sdk/openai';
|
|
1530
|
+
import { generateText } from 'ai';
|
|
1531
|
+
|
|
1532
|
+
const result = await generateText({
|
|
1533
|
+
model: openai.responses('gpt-5.2'),
|
|
1534
|
+
messages: conversationHistory,
|
|
1535
|
+
providerOptions: {
|
|
1536
|
+
openai: {
|
|
1537
|
+
store: false,
|
|
1538
|
+
contextManagement: [{ type: 'compaction', compactThreshold: 50000 }],
|
|
1539
|
+
} satisfies OpenAILanguageModelResponsesOptions,
|
|
1540
|
+
},
|
|
1541
|
+
});
|
|
1542
|
+
```
|
|
1543
|
+
|
|
1544
|
+
**Configuration:**
|
|
1545
|
+
|
|
1546
|
+
- **type** — Must be `'compaction'`
|
|
1547
|
+
- **compactThreshold** — The token count at which compaction is triggered. When the rendered input token count crosses this threshold, the server runs a compaction pass before continuing inference.
|
|
1548
|
+
|
|
1549
|
+
<Note>
|
|
1550
|
+
Server-side compaction is ZDR-friendly when you set `store: false` on your
|
|
1551
|
+
requests.
|
|
1552
|
+
</Note>
|
|
1553
|
+
|
|
1554
|
+
##### Detecting Compaction in Streams
|
|
1555
|
+
|
|
1556
|
+
When using `streamText`, you can detect compaction by checking the `providerMetadata` on `text-start` and `text-end` events:
|
|
1557
|
+
|
|
1558
|
+
```ts
|
|
1559
|
+
import {
|
|
1560
|
+
openai,
|
|
1561
|
+
type OpenAILanguageModelResponsesOptions,
|
|
1562
|
+
} from '@ai-sdk/openai';
|
|
1563
|
+
import { streamText } from 'ai';
|
|
1564
|
+
|
|
1565
|
+
const result = streamText({
|
|
1566
|
+
model: openai.responses('gpt-5.2'),
|
|
1567
|
+
messages: conversationHistory,
|
|
1568
|
+
providerOptions: {
|
|
1569
|
+
openai: {
|
|
1570
|
+
store: false,
|
|
1571
|
+
contextManagement: [{ type: 'compaction', compactThreshold: 50000 }],
|
|
1572
|
+
} satisfies OpenAILanguageModelResponsesOptions,
|
|
1573
|
+
},
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
for await (const part of result.fullStream) {
|
|
1577
|
+
switch (part.type) {
|
|
1578
|
+
case 'text-start': {
|
|
1579
|
+
const isCompaction = part.providerMetadata?.openai?.type === 'compaction';
|
|
1580
|
+
if (isCompaction) {
|
|
1581
|
+
// ... your logic
|
|
1582
|
+
}
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
case 'text-end': {
|
|
1586
|
+
const isCompaction = part.providerMetadata?.openai?.type === 'compaction';
|
|
1587
|
+
if (isCompaction) {
|
|
1588
|
+
// ... your logic
|
|
1589
|
+
}
|
|
1590
|
+
break;
|
|
1591
|
+
}
|
|
1592
|
+
case 'text-delta': {
|
|
1593
|
+
process.stdout.write(part.text);
|
|
1594
|
+
break;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
```
|
|
1599
|
+
|
|
1600
|
+
##### Compaction in UI Applications
|
|
1601
|
+
|
|
1602
|
+
When using `useChat` or other UI hooks, compaction items appear as text parts with `providerMetadata`. You can detect and style them differently in your UI:
|
|
1603
|
+
|
|
1604
|
+
```tsx
|
|
1605
|
+
{
|
|
1606
|
+
message.parts.map((part, index) => {
|
|
1607
|
+
if (part.type === 'text') {
|
|
1608
|
+
const isCompaction =
|
|
1609
|
+
(part.providerMetadata?.openai as { type?: string } | undefined)
|
|
1610
|
+
?.type === 'compaction';
|
|
1611
|
+
|
|
1612
|
+
if (isCompaction) {
|
|
1613
|
+
return (
|
|
1614
|
+
<div
|
|
1615
|
+
key={index}
|
|
1616
|
+
className="bg-yellow-100 border-l-4 border-yellow-500 p-2"
|
|
1617
|
+
>
|
|
1618
|
+
<span className="font-bold">[Context Compacted]</span>
|
|
1619
|
+
<p className="text-sm text-yellow-700">
|
|
1620
|
+
The server compressed the conversation context to reduce token
|
|
1621
|
+
usage.
|
|
1622
|
+
</p>
|
|
1623
|
+
</div>
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
return <div key={index}>{part.text}</div>;
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
```
|
|
1631
|
+
|
|
1632
|
+
The metadata includes the following fields:
|
|
1633
|
+
|
|
1634
|
+
- **type** — Always `'compaction'`
|
|
1635
|
+
- **itemId** _string_ — The ID of the compaction item in the Responses API
|
|
1636
|
+
- **encryptedContent** _string_ (optional) — The encrypted compaction state. This is automatically sent back to the API when the message is included in subsequent requests.
|
|
1637
|
+
|
|
1514
1638
|
### Chat Models
|
|
1515
1639
|
|
|
1516
1640
|
You can create models that call the [OpenAI chat API](https://platform.openai.com/docs/api-reference/chat) using the `.chat()` factory method.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.12",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ai-sdk/provider": "4.0.0-beta.
|
|
40
|
-
"@ai-sdk/provider-utils": "5.0.0-beta.
|
|
39
|
+
"@ai-sdk/provider": "4.0.0-beta.3",
|
|
40
|
+
"@ai-sdk/provider-utils": "5.0.0-beta.5"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "20.17.24",
|
|
@@ -71,9 +71,7 @@
|
|
|
71
71
|
"build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
|
|
72
72
|
"build:watch": "pnpm clean && tsup --watch",
|
|
73
73
|
"clean": "del-cli dist docs *.tsbuildinfo",
|
|
74
|
-
"lint": "eslint \"./**/*.ts*\"",
|
|
75
74
|
"type-check": "tsc --build",
|
|
76
|
-
"prettier-check": "prettier --check \"./**/*.ts*\"",
|
|
77
75
|
"test": "pnpm test:node && pnpm test:edge",
|
|
78
76
|
"test:update": "pnpm test:node -u",
|
|
79
77
|
"test:watch": "vitest --config vitest.node.config.js",
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type { OpenAIEmbeddingModelOptions } from './embedding/openai-embedding-o
|
|
|
15
15
|
export type { OpenAISpeechModelOptions } from './speech/openai-speech-options';
|
|
16
16
|
export type { OpenAITranscriptionModelOptions } from './transcription/openai-transcription-options';
|
|
17
17
|
export type {
|
|
18
|
+
OpenaiResponsesCompactionProviderMetadata,
|
|
18
19
|
OpenaiResponsesProviderMetadata,
|
|
19
20
|
OpenaiResponsesReasoningProviderMetadata,
|
|
20
21
|
OpenaiResponsesTextProviderMetadata,
|
|
@@ -23,15 +23,16 @@ import {
|
|
|
23
23
|
} from '../tool/local-shell';
|
|
24
24
|
import { shellInputSchema, shellOutputSchema } from '../tool/shell';
|
|
25
25
|
import {
|
|
26
|
-
|
|
27
|
-
toolSearchOutputSchema,
|
|
28
|
-
} from '../tool/tool-search';
|
|
29
|
-
import {
|
|
26
|
+
OpenAIResponsesCompactionItem,
|
|
30
27
|
OpenAIResponsesCustomToolCallOutput,
|
|
31
28
|
OpenAIResponsesFunctionCallOutput,
|
|
32
29
|
OpenAIResponsesInput,
|
|
33
30
|
OpenAIResponsesReasoning,
|
|
34
31
|
} from './openai-responses-api';
|
|
32
|
+
import {
|
|
33
|
+
toolSearchInputSchema,
|
|
34
|
+
toolSearchOutputSchema,
|
|
35
|
+
} from '../tool/tool-search';
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Check if a string is a file ID based on the given prefixes
|
|
@@ -543,6 +544,36 @@ export async function convertToOpenAIResponsesInput({
|
|
|
543
544
|
}
|
|
544
545
|
break;
|
|
545
546
|
}
|
|
547
|
+
|
|
548
|
+
case 'custom': {
|
|
549
|
+
if (part.kind === 'openai-compaction') {
|
|
550
|
+
const providerOpts =
|
|
551
|
+
part.providerOptions?.[providerOptionsName];
|
|
552
|
+
const id = providerOpts?.itemId as string | undefined;
|
|
553
|
+
|
|
554
|
+
if (hasConversation && id != null) {
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (store && id != null) {
|
|
559
|
+
input.push({ type: 'item_reference', id });
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const encryptedContent = providerOpts?.encryptedContent as
|
|
564
|
+
| string
|
|
565
|
+
| undefined;
|
|
566
|
+
|
|
567
|
+
if (id != null) {
|
|
568
|
+
input.push({
|
|
569
|
+
type: 'compaction',
|
|
570
|
+
id,
|
|
571
|
+
encrypted_content: encryptedContent!,
|
|
572
|
+
} satisfies OpenAIResponsesCompactionItem);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
546
577
|
}
|
|
547
578
|
}
|
|
548
579
|
|
|
@@ -34,7 +34,8 @@ export type OpenAIResponsesInputItem =
|
|
|
34
34
|
| OpenAIResponsesToolSearchCall
|
|
35
35
|
| OpenAIResponsesToolSearchOutput
|
|
36
36
|
| OpenAIResponsesReasoning
|
|
37
|
-
| OpenAIResponsesItemReference
|
|
37
|
+
| OpenAIResponsesItemReference
|
|
38
|
+
| OpenAIResponsesCompactionItem;
|
|
38
39
|
|
|
39
40
|
export type OpenAIResponsesIncludeValue =
|
|
40
41
|
| 'web_search_call.action.sources'
|
|
@@ -235,6 +236,12 @@ export type OpenAIResponsesItemReference = {
|
|
|
235
236
|
id: string;
|
|
236
237
|
};
|
|
237
238
|
|
|
239
|
+
export type OpenAIResponsesCompactionItem = {
|
|
240
|
+
type: 'compaction';
|
|
241
|
+
id: string;
|
|
242
|
+
encrypted_content: string;
|
|
243
|
+
};
|
|
244
|
+
|
|
238
245
|
/**
|
|
239
246
|
* A filter used to compare a specified attribute key to a given value using a defined comparison operation.
|
|
240
247
|
*/
|
|
@@ -611,6 +618,11 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
611
618
|
commands: z.array(z.string()),
|
|
612
619
|
}),
|
|
613
620
|
}),
|
|
621
|
+
z.object({
|
|
622
|
+
type: z.literal('compaction'),
|
|
623
|
+
id: z.string(),
|
|
624
|
+
encrypted_content: z.string().nullish(),
|
|
625
|
+
}),
|
|
614
626
|
z.object({
|
|
615
627
|
type: z.literal('shell_call_output'),
|
|
616
628
|
id: z.string(),
|
|
@@ -850,6 +862,11 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
850
862
|
commands: z.array(z.string()),
|
|
851
863
|
}),
|
|
852
864
|
}),
|
|
865
|
+
z.object({
|
|
866
|
+
type: z.literal('compaction'),
|
|
867
|
+
id: z.string(),
|
|
868
|
+
encrypted_content: z.string(),
|
|
869
|
+
}),
|
|
853
870
|
z.object({
|
|
854
871
|
type: z.literal('shell_call_output'),
|
|
855
872
|
id: z.string(),
|
|
@@ -1289,6 +1306,11 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
|
|
|
1289
1306
|
commands: z.array(z.string()),
|
|
1290
1307
|
}),
|
|
1291
1308
|
}),
|
|
1309
|
+
z.object({
|
|
1310
|
+
type: z.literal('compaction'),
|
|
1311
|
+
id: z.string(),
|
|
1312
|
+
encrypted_content: z.string(),
|
|
1313
|
+
}),
|
|
1292
1314
|
z.object({
|
|
1293
1315
|
type: z.literal('shell_call_output'),
|
|
1294
1316
|
id: z.string(),
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
} from './openai-responses-options';
|
|
68
68
|
import { prepareResponsesTools } from './openai-responses-prepare-tools';
|
|
69
69
|
import {
|
|
70
|
+
ResponsesCompactionProviderMetadata,
|
|
70
71
|
ResponsesProviderMetadata,
|
|
71
72
|
ResponsesReasoningProviderMetadata,
|
|
72
73
|
ResponsesSourceDocumentProviderMetadata,
|
|
@@ -337,6 +338,14 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
337
338
|
top_logprobs: topLogprobs,
|
|
338
339
|
truncation: openaiOptions?.truncation,
|
|
339
340
|
|
|
341
|
+
// context management (server-side compaction):
|
|
342
|
+
...(openaiOptions?.contextManagement && {
|
|
343
|
+
context_management: openaiOptions.contextManagement.map(cm => ({
|
|
344
|
+
type: cm.type,
|
|
345
|
+
compact_threshold: cm.compactThreshold,
|
|
346
|
+
})),
|
|
347
|
+
}),
|
|
348
|
+
|
|
340
349
|
// model-specific settings:
|
|
341
350
|
...(isReasoningModel &&
|
|
342
351
|
(openaiOptions?.reasoningEffort != null ||
|
|
@@ -977,6 +986,21 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
977
986
|
|
|
978
987
|
break;
|
|
979
988
|
}
|
|
989
|
+
|
|
990
|
+
case 'compaction': {
|
|
991
|
+
content.push({
|
|
992
|
+
type: 'custom',
|
|
993
|
+
kind: 'openai-compaction',
|
|
994
|
+
providerMetadata: {
|
|
995
|
+
[providerOptionsName]: {
|
|
996
|
+
type: 'compaction',
|
|
997
|
+
itemId: part.id,
|
|
998
|
+
encryptedContent: part.encrypted_content,
|
|
999
|
+
} satisfies ResponsesCompactionProviderMetadata,
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
break;
|
|
1003
|
+
}
|
|
980
1004
|
}
|
|
981
1005
|
}
|
|
982
1006
|
|
|
@@ -1782,6 +1806,18 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1782
1806
|
}
|
|
1783
1807
|
|
|
1784
1808
|
delete activeReasoning[value.item.id];
|
|
1809
|
+
} else if (value.item.type === 'compaction') {
|
|
1810
|
+
controller.enqueue({
|
|
1811
|
+
type: 'custom',
|
|
1812
|
+
kind: 'openai-compaction',
|
|
1813
|
+
providerMetadata: {
|
|
1814
|
+
[providerOptionsName]: {
|
|
1815
|
+
type: 'compaction',
|
|
1816
|
+
itemId: value.item.id,
|
|
1817
|
+
encryptedContent: value.item.encrypted_content,
|
|
1818
|
+
} satisfies ResponsesCompactionProviderMetadata,
|
|
1819
|
+
},
|
|
1820
|
+
});
|
|
1785
1821
|
}
|
|
1786
1822
|
} else if (isResponseFunctionCallArgumentsDeltaChunk(value)) {
|
|
1787
1823
|
const toolCall = ongoingToolCalls[value.output_index];
|
|
@@ -300,6 +300,18 @@ export const openaiLanguageModelResponsesOptionsSchema = lazySchema(() =>
|
|
|
300
300
|
* and defaults `systemMessageMode` to `developer` unless overridden.
|
|
301
301
|
*/
|
|
302
302
|
forceReasoning: z.boolean().optional(),
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Enable server-side context management (compaction).
|
|
306
|
+
*/
|
|
307
|
+
contextManagement: z
|
|
308
|
+
.array(
|
|
309
|
+
z.object({
|
|
310
|
+
type: z.literal('compaction'),
|
|
311
|
+
compactThreshold: z.number(),
|
|
312
|
+
}),
|
|
313
|
+
)
|
|
314
|
+
.nullish(),
|
|
303
315
|
}),
|
|
304
316
|
),
|
|
305
317
|
);
|
|
@@ -30,6 +30,16 @@ export type OpenaiResponsesProviderMetadata = {
|
|
|
30
30
|
openai: ResponsesProviderMetadata;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
export type ResponsesCompactionProviderMetadata = {
|
|
34
|
+
type: 'compaction';
|
|
35
|
+
itemId: string;
|
|
36
|
+
encryptedContent?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type OpenaiResponsesCompactionProviderMetadata = {
|
|
40
|
+
openai: ResponsesCompactionProviderMetadata;
|
|
41
|
+
};
|
|
42
|
+
|
|
33
43
|
export type ResponsesTextProviderMetadata = {
|
|
34
44
|
itemId: string;
|
|
35
45
|
phase?: 'commentary' | 'final_answer' | null;
|