@ai-sdk/openai-compatible 2.0.15 → 2.0.17
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 +12 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +23 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +23 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/chat/convert-openai-compatible-chat-usage.ts +55 -0
- package/src/chat/convert-to-openai-compatible-chat-messages.test.ts +1238 -0
- package/src/chat/convert-to-openai-compatible-chat-messages.ts +246 -0
- package/src/chat/get-response-metadata.ts +15 -0
- package/src/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/src/chat/openai-compatible-api-types.ts +86 -0
- package/src/chat/openai-compatible-chat-language-model.test.ts +3292 -0
- package/src/chat/openai-compatible-chat-language-model.ts +830 -0
- package/src/chat/openai-compatible-chat-options.ts +34 -0
- package/src/chat/openai-compatible-metadata-extractor.ts +48 -0
- package/src/chat/openai-compatible-prepare-tools.test.ts +336 -0
- package/src/chat/openai-compatible-prepare-tools.ts +98 -0
- package/src/completion/convert-openai-compatible-completion-usage.ts +46 -0
- package/src/completion/convert-to-openai-compatible-completion-prompt.ts +93 -0
- package/src/completion/get-response-metadata.ts +15 -0
- package/src/completion/map-openai-compatible-finish-reason.ts +19 -0
- package/src/completion/openai-compatible-completion-language-model.test.ts +773 -0
- package/src/completion/openai-compatible-completion-language-model.ts +390 -0
- package/src/completion/openai-compatible-completion-options.ts +33 -0
- package/src/embedding/openai-compatible-embedding-model.test.ts +171 -0
- package/src/embedding/openai-compatible-embedding-model.ts +166 -0
- package/src/embedding/openai-compatible-embedding-options.ts +21 -0
- package/src/image/openai-compatible-image-model.test.ts +494 -0
- package/src/image/openai-compatible-image-model.ts +205 -0
- package/src/image/openai-compatible-image-settings.ts +1 -0
- package/src/index.ts +27 -0
- package/src/internal/index.ts +4 -0
- package/src/openai-compatible-error.ts +30 -0
- package/src/openai-compatible-provider.test.ts +329 -0
- package/src/openai-compatible-provider.ts +189 -0
- package/src/version.ts +5 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
|
|
3
|
+
export type OpenAICompatibleChatModelId = string;
|
|
4
|
+
|
|
5
|
+
export const openaiCompatibleProviderOptions = z.object({
|
|
6
|
+
/**
|
|
7
|
+
* A unique identifier representing your end-user, which can help the provider to
|
|
8
|
+
* monitor and detect abuse.
|
|
9
|
+
*/
|
|
10
|
+
user: z.string().optional(),
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reasoning effort for reasoning models. Defaults to `medium`.
|
|
14
|
+
*/
|
|
15
|
+
reasoningEffort: z.string().optional(),
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Controls the verbosity of the generated text. Defaults to `medium`.
|
|
19
|
+
*/
|
|
20
|
+
textVerbosity: z.string().optional(),
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether to use strict JSON schema validation.
|
|
24
|
+
* When true, the model uses constrained decoding to guarantee schema compliance.
|
|
25
|
+
* Only used when the provider supports structured outputs and a schema is provided.
|
|
26
|
+
*
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
strictJsonSchema: z.boolean().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type OpenAICompatibleProviderOptions = z.infer<
|
|
33
|
+
typeof openaiCompatibleProviderOptions
|
|
34
|
+
>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SharedV3ProviderMetadata } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Extracts provider-specific metadata from API responses.
|
|
5
|
+
Used to standardize metadata handling across different LLM providers while allowing
|
|
6
|
+
provider-specific metadata to be captured.
|
|
7
|
+
*/
|
|
8
|
+
export type MetadataExtractor = {
|
|
9
|
+
/**
|
|
10
|
+
* Extracts provider metadata from a complete, non-streaming response.
|
|
11
|
+
*
|
|
12
|
+
* @param parsedBody - The parsed response JSON body from the provider's API.
|
|
13
|
+
*
|
|
14
|
+
* @returns Provider-specific metadata or undefined if no metadata is available.
|
|
15
|
+
* The metadata should be under a key indicating the provider id.
|
|
16
|
+
*/
|
|
17
|
+
extractMetadata: ({
|
|
18
|
+
parsedBody,
|
|
19
|
+
}: {
|
|
20
|
+
parsedBody: unknown;
|
|
21
|
+
}) => Promise<SharedV3ProviderMetadata | undefined>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates an extractor for handling streaming responses. The returned object provides
|
|
25
|
+
* methods to process individual chunks and build the final metadata from the accumulated
|
|
26
|
+
* stream data.
|
|
27
|
+
*
|
|
28
|
+
* @returns An object with methods to process chunks and build metadata from a stream
|
|
29
|
+
*/
|
|
30
|
+
createStreamExtractor: () => {
|
|
31
|
+
/**
|
|
32
|
+
* Process an individual chunk from the stream. Called for each chunk in the response stream
|
|
33
|
+
* to accumulate metadata throughout the streaming process.
|
|
34
|
+
*
|
|
35
|
+
* @param parsedChunk - The parsed JSON response chunk from the provider's API
|
|
36
|
+
*/
|
|
37
|
+
processChunk(parsedChunk: unknown): void;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Builds the metadata object after all chunks have been processed.
|
|
41
|
+
* Called at the end of the stream to generate the complete provider metadata.
|
|
42
|
+
*
|
|
43
|
+
* @returns Provider-specific metadata or undefined if no metadata is available.
|
|
44
|
+
* The metadata should be under a key indicating the provider id.
|
|
45
|
+
*/
|
|
46
|
+
buildMetadata(): SharedV3ProviderMetadata | undefined;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { prepareTools } from './openai-compatible-prepare-tools';
|
|
3
|
+
|
|
4
|
+
describe('prepareTools', () => {
|
|
5
|
+
it('should return undefined tools and toolChoice when tools are null', () => {
|
|
6
|
+
const result = prepareTools({
|
|
7
|
+
tools: undefined,
|
|
8
|
+
toolChoice: undefined,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
expect(result).toEqual({
|
|
12
|
+
tools: undefined,
|
|
13
|
+
toolChoice: undefined,
|
|
14
|
+
toolWarnings: [],
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return undefined tools and toolChoice when tools are empty', () => {
|
|
19
|
+
const result = prepareTools({
|
|
20
|
+
tools: [],
|
|
21
|
+
toolChoice: undefined,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(result).toEqual({
|
|
25
|
+
tools: undefined,
|
|
26
|
+
toolChoice: undefined,
|
|
27
|
+
toolWarnings: [],
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should correctly prepare function tools', () => {
|
|
32
|
+
const result = prepareTools({
|
|
33
|
+
tools: [
|
|
34
|
+
{
|
|
35
|
+
type: 'function',
|
|
36
|
+
name: 'testFunction',
|
|
37
|
+
description: 'A test function',
|
|
38
|
+
inputSchema: { type: 'object', properties: {} },
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
toolChoice: undefined,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(result).toMatchInlineSnapshot(`
|
|
45
|
+
{
|
|
46
|
+
"toolChoice": undefined,
|
|
47
|
+
"toolWarnings": [],
|
|
48
|
+
"tools": [
|
|
49
|
+
{
|
|
50
|
+
"function": {
|
|
51
|
+
"description": "A test function",
|
|
52
|
+
"name": "testFunction",
|
|
53
|
+
"parameters": {
|
|
54
|
+
"properties": {},
|
|
55
|
+
"type": "object",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
"type": "function",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
}
|
|
62
|
+
`);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should add warnings for unsupported provider-defined tools', () => {
|
|
66
|
+
const result = prepareTools({
|
|
67
|
+
tools: [
|
|
68
|
+
{
|
|
69
|
+
type: 'provider',
|
|
70
|
+
id: 'some.unsupported_tool',
|
|
71
|
+
name: 'unsupported_tool',
|
|
72
|
+
args: {},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
toolChoice: undefined,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(result.tools).toEqual([]);
|
|
79
|
+
expect(result.toolChoice).toBeUndefined();
|
|
80
|
+
expect(result.toolWarnings).toMatchInlineSnapshot(`
|
|
81
|
+
[
|
|
82
|
+
{
|
|
83
|
+
"feature": "provider-defined tool some.unsupported_tool",
|
|
84
|
+
"type": "unsupported",
|
|
85
|
+
},
|
|
86
|
+
]
|
|
87
|
+
`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle tool choice "auto"', () => {
|
|
91
|
+
const result = prepareTools({
|
|
92
|
+
tools: [
|
|
93
|
+
{
|
|
94
|
+
type: 'function',
|
|
95
|
+
name: 'testFunction',
|
|
96
|
+
description: 'Test',
|
|
97
|
+
inputSchema: {},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
toolChoice: { type: 'auto' },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(result.toolChoice).toEqual('auto');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle tool choice "required"', () => {
|
|
107
|
+
const result = prepareTools({
|
|
108
|
+
tools: [
|
|
109
|
+
{
|
|
110
|
+
type: 'function',
|
|
111
|
+
name: 'testFunction',
|
|
112
|
+
description: 'Test',
|
|
113
|
+
inputSchema: {},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
toolChoice: { type: 'required' },
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(result.toolChoice).toEqual('required');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should handle tool choice "none"', () => {
|
|
123
|
+
const result = prepareTools({
|
|
124
|
+
tools: [
|
|
125
|
+
{
|
|
126
|
+
type: 'function',
|
|
127
|
+
name: 'testFunction',
|
|
128
|
+
description: 'Test',
|
|
129
|
+
inputSchema: {},
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
toolChoice: { type: 'none' },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(result.toolChoice).toEqual('none');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should handle tool choice "tool"', () => {
|
|
139
|
+
const result = prepareTools({
|
|
140
|
+
tools: [
|
|
141
|
+
{
|
|
142
|
+
type: 'function',
|
|
143
|
+
name: 'testFunction',
|
|
144
|
+
description: 'Test',
|
|
145
|
+
inputSchema: {},
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
toolChoice: { type: 'tool', toolName: 'testFunction' },
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(result.toolChoice).toEqual({
|
|
152
|
+
type: 'function',
|
|
153
|
+
function: { name: 'testFunction' },
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('strict mode', () => {
|
|
158
|
+
it('should pass through strict mode when strict is true', () => {
|
|
159
|
+
const result = prepareTools({
|
|
160
|
+
tools: [
|
|
161
|
+
{
|
|
162
|
+
type: 'function',
|
|
163
|
+
name: 'testFunction',
|
|
164
|
+
description: 'A test function',
|
|
165
|
+
inputSchema: { type: 'object', properties: {} },
|
|
166
|
+
strict: true,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
toolChoice: undefined,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(result).toMatchInlineSnapshot(`
|
|
173
|
+
{
|
|
174
|
+
"toolChoice": undefined,
|
|
175
|
+
"toolWarnings": [],
|
|
176
|
+
"tools": [
|
|
177
|
+
{
|
|
178
|
+
"function": {
|
|
179
|
+
"description": "A test function",
|
|
180
|
+
"name": "testFunction",
|
|
181
|
+
"parameters": {
|
|
182
|
+
"properties": {},
|
|
183
|
+
"type": "object",
|
|
184
|
+
},
|
|
185
|
+
"strict": true,
|
|
186
|
+
},
|
|
187
|
+
"type": "function",
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
}
|
|
191
|
+
`);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should pass through strict mode when strict is false', () => {
|
|
195
|
+
const result = prepareTools({
|
|
196
|
+
tools: [
|
|
197
|
+
{
|
|
198
|
+
type: 'function',
|
|
199
|
+
name: 'testFunction',
|
|
200
|
+
description: 'A test function',
|
|
201
|
+
inputSchema: { type: 'object', properties: {} },
|
|
202
|
+
strict: false,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
toolChoice: undefined,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(result).toMatchInlineSnapshot(`
|
|
209
|
+
{
|
|
210
|
+
"toolChoice": undefined,
|
|
211
|
+
"toolWarnings": [],
|
|
212
|
+
"tools": [
|
|
213
|
+
{
|
|
214
|
+
"function": {
|
|
215
|
+
"description": "A test function",
|
|
216
|
+
"name": "testFunction",
|
|
217
|
+
"parameters": {
|
|
218
|
+
"properties": {},
|
|
219
|
+
"type": "object",
|
|
220
|
+
},
|
|
221
|
+
"strict": false,
|
|
222
|
+
},
|
|
223
|
+
"type": "function",
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
}
|
|
227
|
+
`);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should not include strict mode when strict is undefined', () => {
|
|
231
|
+
const result = prepareTools({
|
|
232
|
+
tools: [
|
|
233
|
+
{
|
|
234
|
+
type: 'function',
|
|
235
|
+
name: 'testFunction',
|
|
236
|
+
description: 'A test function',
|
|
237
|
+
inputSchema: { type: 'object', properties: {} },
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
toolChoice: undefined,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
expect(result).toMatchInlineSnapshot(`
|
|
244
|
+
{
|
|
245
|
+
"toolChoice": undefined,
|
|
246
|
+
"toolWarnings": [],
|
|
247
|
+
"tools": [
|
|
248
|
+
{
|
|
249
|
+
"function": {
|
|
250
|
+
"description": "A test function",
|
|
251
|
+
"name": "testFunction",
|
|
252
|
+
"parameters": {
|
|
253
|
+
"properties": {},
|
|
254
|
+
"type": "object",
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
"type": "function",
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
}
|
|
261
|
+
`);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should pass through strict mode for multiple tools with different strict settings', () => {
|
|
265
|
+
const result = prepareTools({
|
|
266
|
+
tools: [
|
|
267
|
+
{
|
|
268
|
+
type: 'function',
|
|
269
|
+
name: 'strictTool',
|
|
270
|
+
description: 'A strict tool',
|
|
271
|
+
inputSchema: { type: 'object', properties: {} },
|
|
272
|
+
strict: true,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
type: 'function',
|
|
276
|
+
name: 'nonStrictTool',
|
|
277
|
+
description: 'A non-strict tool',
|
|
278
|
+
inputSchema: { type: 'object', properties: {} },
|
|
279
|
+
strict: false,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
type: 'function',
|
|
283
|
+
name: 'defaultTool',
|
|
284
|
+
description: 'A tool without strict setting',
|
|
285
|
+
inputSchema: { type: 'object', properties: {} },
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
toolChoice: undefined,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
expect(result).toMatchInlineSnapshot(`
|
|
292
|
+
{
|
|
293
|
+
"toolChoice": undefined,
|
|
294
|
+
"toolWarnings": [],
|
|
295
|
+
"tools": [
|
|
296
|
+
{
|
|
297
|
+
"function": {
|
|
298
|
+
"description": "A strict tool",
|
|
299
|
+
"name": "strictTool",
|
|
300
|
+
"parameters": {
|
|
301
|
+
"properties": {},
|
|
302
|
+
"type": "object",
|
|
303
|
+
},
|
|
304
|
+
"strict": true,
|
|
305
|
+
},
|
|
306
|
+
"type": "function",
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"function": {
|
|
310
|
+
"description": "A non-strict tool",
|
|
311
|
+
"name": "nonStrictTool",
|
|
312
|
+
"parameters": {
|
|
313
|
+
"properties": {},
|
|
314
|
+
"type": "object",
|
|
315
|
+
},
|
|
316
|
+
"strict": false,
|
|
317
|
+
},
|
|
318
|
+
"type": "function",
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"function": {
|
|
322
|
+
"description": "A tool without strict setting",
|
|
323
|
+
"name": "defaultTool",
|
|
324
|
+
"parameters": {
|
|
325
|
+
"properties": {},
|
|
326
|
+
"type": "object",
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
"type": "function",
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
}
|
|
333
|
+
`);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LanguageModelV3CallOptions,
|
|
3
|
+
SharedV3Warning,
|
|
4
|
+
UnsupportedFunctionalityError,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
|
|
7
|
+
export function prepareTools({
|
|
8
|
+
tools,
|
|
9
|
+
toolChoice,
|
|
10
|
+
}: {
|
|
11
|
+
tools: LanguageModelV3CallOptions['tools'];
|
|
12
|
+
toolChoice?: LanguageModelV3CallOptions['toolChoice'];
|
|
13
|
+
}): {
|
|
14
|
+
tools:
|
|
15
|
+
| undefined
|
|
16
|
+
| Array<{
|
|
17
|
+
type: 'function';
|
|
18
|
+
function: {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string | undefined;
|
|
21
|
+
parameters: unknown;
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
};
|
|
24
|
+
}>;
|
|
25
|
+
toolChoice:
|
|
26
|
+
| { type: 'function'; function: { name: string } }
|
|
27
|
+
| 'auto'
|
|
28
|
+
| 'none'
|
|
29
|
+
| 'required'
|
|
30
|
+
| undefined;
|
|
31
|
+
toolWarnings: SharedV3Warning[];
|
|
32
|
+
} {
|
|
33
|
+
// when the tools array is empty, change it to undefined to prevent errors:
|
|
34
|
+
tools = tools?.length ? tools : undefined;
|
|
35
|
+
|
|
36
|
+
const toolWarnings: SharedV3Warning[] = [];
|
|
37
|
+
|
|
38
|
+
if (tools == null) {
|
|
39
|
+
return { tools: undefined, toolChoice: undefined, toolWarnings };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const openaiCompatTools: Array<{
|
|
43
|
+
type: 'function';
|
|
44
|
+
function: {
|
|
45
|
+
name: string;
|
|
46
|
+
description: string | undefined;
|
|
47
|
+
parameters: unknown;
|
|
48
|
+
strict?: boolean;
|
|
49
|
+
};
|
|
50
|
+
}> = [];
|
|
51
|
+
|
|
52
|
+
for (const tool of tools) {
|
|
53
|
+
if (tool.type === 'provider') {
|
|
54
|
+
toolWarnings.push({
|
|
55
|
+
type: 'unsupported',
|
|
56
|
+
feature: `provider-defined tool ${tool.id}`,
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
openaiCompatTools.push({
|
|
60
|
+
type: 'function',
|
|
61
|
+
function: {
|
|
62
|
+
name: tool.name,
|
|
63
|
+
description: tool.description,
|
|
64
|
+
parameters: tool.inputSchema,
|
|
65
|
+
...(tool.strict != null ? { strict: tool.strict } : {}),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (toolChoice == null) {
|
|
72
|
+
return { tools: openaiCompatTools, toolChoice: undefined, toolWarnings };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const type = toolChoice.type;
|
|
76
|
+
|
|
77
|
+
switch (type) {
|
|
78
|
+
case 'auto':
|
|
79
|
+
case 'none':
|
|
80
|
+
case 'required':
|
|
81
|
+
return { tools: openaiCompatTools, toolChoice: type, toolWarnings };
|
|
82
|
+
case 'tool':
|
|
83
|
+
return {
|
|
84
|
+
tools: openaiCompatTools,
|
|
85
|
+
toolChoice: {
|
|
86
|
+
type: 'function',
|
|
87
|
+
function: { name: toolChoice.toolName },
|
|
88
|
+
},
|
|
89
|
+
toolWarnings,
|
|
90
|
+
};
|
|
91
|
+
default: {
|
|
92
|
+
const _exhaustiveCheck: never = type;
|
|
93
|
+
throw new UnsupportedFunctionalityError({
|
|
94
|
+
functionality: `tool choice type: ${_exhaustiveCheck}`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { LanguageModelV3Usage } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
export function convertOpenAICompatibleCompletionUsage(
|
|
4
|
+
usage:
|
|
5
|
+
| {
|
|
6
|
+
prompt_tokens?: number | null;
|
|
7
|
+
completion_tokens?: number | null;
|
|
8
|
+
}
|
|
9
|
+
| undefined
|
|
10
|
+
| null,
|
|
11
|
+
): LanguageModelV3Usage {
|
|
12
|
+
if (usage == null) {
|
|
13
|
+
return {
|
|
14
|
+
inputTokens: {
|
|
15
|
+
total: undefined,
|
|
16
|
+
noCache: undefined,
|
|
17
|
+
cacheRead: undefined,
|
|
18
|
+
cacheWrite: undefined,
|
|
19
|
+
},
|
|
20
|
+
outputTokens: {
|
|
21
|
+
total: undefined,
|
|
22
|
+
text: undefined,
|
|
23
|
+
reasoning: undefined,
|
|
24
|
+
},
|
|
25
|
+
raw: undefined,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const promptTokens = usage.prompt_tokens ?? 0;
|
|
30
|
+
const completionTokens = usage.completion_tokens ?? 0;
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
inputTokens: {
|
|
34
|
+
total: promptTokens,
|
|
35
|
+
noCache: promptTokens,
|
|
36
|
+
cacheRead: undefined,
|
|
37
|
+
cacheWrite: undefined,
|
|
38
|
+
},
|
|
39
|
+
outputTokens: {
|
|
40
|
+
total: completionTokens,
|
|
41
|
+
text: completionTokens,
|
|
42
|
+
reasoning: undefined,
|
|
43
|
+
},
|
|
44
|
+
raw: usage,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InvalidPromptError,
|
|
3
|
+
LanguageModelV3Prompt,
|
|
4
|
+
UnsupportedFunctionalityError,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
|
|
7
|
+
export function convertToOpenAICompatibleCompletionPrompt({
|
|
8
|
+
prompt,
|
|
9
|
+
user = 'user',
|
|
10
|
+
assistant = 'assistant',
|
|
11
|
+
}: {
|
|
12
|
+
prompt: LanguageModelV3Prompt;
|
|
13
|
+
user?: string;
|
|
14
|
+
assistant?: string;
|
|
15
|
+
}): {
|
|
16
|
+
prompt: string;
|
|
17
|
+
stopSequences?: string[];
|
|
18
|
+
} {
|
|
19
|
+
// transform to a chat message format:
|
|
20
|
+
let text = '';
|
|
21
|
+
|
|
22
|
+
// if first message is a system message, add it to the text:
|
|
23
|
+
if (prompt[0].role === 'system') {
|
|
24
|
+
text += `${prompt[0].content}\n\n`;
|
|
25
|
+
prompt = prompt.slice(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const { role, content } of prompt) {
|
|
29
|
+
switch (role) {
|
|
30
|
+
case 'system': {
|
|
31
|
+
throw new InvalidPromptError({
|
|
32
|
+
message: 'Unexpected system message in prompt: ${content}',
|
|
33
|
+
prompt,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
case 'user': {
|
|
38
|
+
const userMessage = content
|
|
39
|
+
.map(part => {
|
|
40
|
+
switch (part.type) {
|
|
41
|
+
case 'text': {
|
|
42
|
+
return part.text;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.join('');
|
|
48
|
+
|
|
49
|
+
text += `${user}:\n${userMessage}\n\n`;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
case 'assistant': {
|
|
54
|
+
const assistantMessage = content
|
|
55
|
+
.map(part => {
|
|
56
|
+
switch (part.type) {
|
|
57
|
+
case 'text': {
|
|
58
|
+
return part.text;
|
|
59
|
+
}
|
|
60
|
+
case 'tool-call': {
|
|
61
|
+
throw new UnsupportedFunctionalityError({
|
|
62
|
+
functionality: 'tool-call messages',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.join('');
|
|
68
|
+
|
|
69
|
+
text += `${assistant}:\n${assistantMessage}\n\n`;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'tool': {
|
|
74
|
+
throw new UnsupportedFunctionalityError({
|
|
75
|
+
functionality: 'tool messages',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
default: {
|
|
80
|
+
const _exhaustiveCheck: never = role;
|
|
81
|
+
throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Assistant message prefix:
|
|
87
|
+
text += `${assistant}:\n`;
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
prompt: text,
|
|
91
|
+
stopSequences: [`\n${user}:`],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function getResponseMetadata({
|
|
2
|
+
id,
|
|
3
|
+
model,
|
|
4
|
+
created,
|
|
5
|
+
}: {
|
|
6
|
+
id?: string | undefined | null;
|
|
7
|
+
created?: number | undefined | null;
|
|
8
|
+
model?: string | undefined | null;
|
|
9
|
+
}) {
|
|
10
|
+
return {
|
|
11
|
+
id: id ?? undefined,
|
|
12
|
+
modelId: model ?? undefined,
|
|
13
|
+
timestamp: created != null ? new Date(created * 1000) : undefined,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
export function mapOpenAICompatibleFinishReason(
|
|
4
|
+
finishReason: string | null | undefined,
|
|
5
|
+
): LanguageModelV3FinishReason['unified'] {
|
|
6
|
+
switch (finishReason) {
|
|
7
|
+
case 'stop':
|
|
8
|
+
return 'stop';
|
|
9
|
+
case 'length':
|
|
10
|
+
return 'length';
|
|
11
|
+
case 'content_filter':
|
|
12
|
+
return 'content-filter';
|
|
13
|
+
case 'function_call':
|
|
14
|
+
case 'tool_calls':
|
|
15
|
+
return 'tool-calls';
|
|
16
|
+
default:
|
|
17
|
+
return 'other';
|
|
18
|
+
}
|
|
19
|
+
}
|