@ai-sdk/provider-utils 4.0.5 → 4.0.7

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.
Files changed (167) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.d.mts +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +1 -1
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +4 -2
  9. package/src/__snapshots__/schema.test.ts.snap +346 -0
  10. package/src/add-additional-properties-to-json-schema.test.ts +289 -0
  11. package/src/add-additional-properties-to-json-schema.ts +53 -0
  12. package/src/combine-headers.ts +11 -0
  13. package/src/convert-async-iterator-to-readable-stream.test.ts +78 -0
  14. package/src/convert-async-iterator-to-readable-stream.ts +47 -0
  15. package/src/convert-image-model-file-to-data-uri.test.ts +85 -0
  16. package/src/convert-image-model-file-to-data-uri.ts +19 -0
  17. package/src/convert-to-form-data.test.ts +167 -0
  18. package/src/convert-to-form-data.ts +61 -0
  19. package/src/create-tool-name-mapping.test.ts +163 -0
  20. package/src/create-tool-name-mapping.ts +66 -0
  21. package/src/delay.test.ts +212 -0
  22. package/src/delay.ts +47 -0
  23. package/src/delayed-promise.test.ts +132 -0
  24. package/src/delayed-promise.ts +61 -0
  25. package/src/download-blob.test.ts +145 -0
  26. package/src/download-blob.ts +31 -0
  27. package/src/download-error.ts +39 -0
  28. package/src/extract-response-headers.ts +9 -0
  29. package/src/fetch-function.ts +4 -0
  30. package/src/generate-id.test.ts +31 -0
  31. package/src/generate-id.ts +57 -0
  32. package/src/get-error-message.ts +15 -0
  33. package/src/get-from-api.test.ts +199 -0
  34. package/src/get-from-api.ts +97 -0
  35. package/src/get-runtime-environment-user-agent.test.ts +47 -0
  36. package/src/get-runtime-environment-user-agent.ts +24 -0
  37. package/src/handle-fetch-error.ts +39 -0
  38. package/src/index.ts +67 -0
  39. package/src/inject-json-instruction.test.ts +404 -0
  40. package/src/inject-json-instruction.ts +63 -0
  41. package/src/is-abort-error.ts +8 -0
  42. package/src/is-async-iterable.ts +3 -0
  43. package/src/is-non-nullable.ts +12 -0
  44. package/src/is-url-supported.test.ts +282 -0
  45. package/src/is-url-supported.ts +40 -0
  46. package/src/load-api-key.ts +45 -0
  47. package/src/load-optional-setting.ts +30 -0
  48. package/src/load-setting.ts +62 -0
  49. package/src/maybe-promise-like.ts +3 -0
  50. package/src/media-type-to-extension.test.ts +26 -0
  51. package/src/media-type-to-extension.ts +22 -0
  52. package/src/normalize-headers.test.ts +64 -0
  53. package/src/normalize-headers.ts +38 -0
  54. package/src/parse-json-event-stream.ts +33 -0
  55. package/src/parse-json.test.ts +191 -0
  56. package/src/parse-json.ts +122 -0
  57. package/src/parse-provider-options.ts +32 -0
  58. package/src/post-to-api.ts +166 -0
  59. package/src/provider-tool-factory.ts +125 -0
  60. package/src/remove-undefined-entries.test.ts +57 -0
  61. package/src/remove-undefined-entries.ts +12 -0
  62. package/src/resolve.test.ts +125 -0
  63. package/src/resolve.ts +17 -0
  64. package/src/response-handler.test.ts +89 -0
  65. package/src/response-handler.ts +187 -0
  66. package/src/schema.test-d.ts +11 -0
  67. package/src/schema.test.ts +502 -0
  68. package/src/schema.ts +267 -0
  69. package/src/secure-json-parse.test.ts +59 -0
  70. package/src/secure-json-parse.ts +92 -0
  71. package/src/test/convert-array-to-async-iterable.ts +9 -0
  72. package/src/test/convert-array-to-readable-stream.ts +15 -0
  73. package/src/test/convert-async-iterable-to-array.ts +9 -0
  74. package/src/test/convert-readable-stream-to-array.ts +14 -0
  75. package/src/test/convert-response-stream-to-array.ts +9 -0
  76. package/src/test/index.ts +7 -0
  77. package/src/test/is-node-version.ts +4 -0
  78. package/src/test/mock-id.ts +8 -0
  79. package/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
  80. package/src/to-json-schema/zod3-to-json-schema/README.md +24 -0
  81. package/src/to-json-schema/zod3-to-json-schema/get-relative-path.ts +7 -0
  82. package/src/to-json-schema/zod3-to-json-schema/index.ts +1 -0
  83. package/src/to-json-schema/zod3-to-json-schema/options.ts +98 -0
  84. package/src/to-json-schema/zod3-to-json-schema/parse-def.test.ts +224 -0
  85. package/src/to-json-schema/zod3-to-json-schema/parse-def.ts +109 -0
  86. package/src/to-json-schema/zod3-to-json-schema/parse-types.ts +57 -0
  87. package/src/to-json-schema/zod3-to-json-schema/parsers/any.ts +5 -0
  88. package/src/to-json-schema/zod3-to-json-schema/parsers/array.test.ts +98 -0
  89. package/src/to-json-schema/zod3-to-json-schema/parsers/array.ts +38 -0
  90. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.test.ts +51 -0
  91. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts +44 -0
  92. package/src/to-json-schema/zod3-to-json-schema/parsers/boolean.ts +7 -0
  93. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.test.ts +16 -0
  94. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts +7 -0
  95. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.test.ts +15 -0
  96. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts +7 -0
  97. package/src/to-json-schema/zod3-to-json-schema/parsers/date.test.ts +97 -0
  98. package/src/to-json-schema/zod3-to-json-schema/parsers/date.ts +64 -0
  99. package/src/to-json-schema/zod3-to-json-schema/parsers/default.test.ts +54 -0
  100. package/src/to-json-schema/zod3-to-json-schema/parsers/default.ts +14 -0
  101. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.test.ts +41 -0
  102. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts +14 -0
  103. package/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts +13 -0
  104. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.test.ts +92 -0
  105. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts +52 -0
  106. package/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts +29 -0
  107. package/src/to-json-schema/zod3-to-json-schema/parsers/map.test.ts +48 -0
  108. package/src/to-json-schema/zod3-to-json-schema/parsers/map.ts +47 -0
  109. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.test.ts +102 -0
  110. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts +31 -0
  111. package/src/to-json-schema/zod3-to-json-schema/parsers/never.ts +9 -0
  112. package/src/to-json-schema/zod3-to-json-schema/parsers/null.ts +9 -0
  113. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.test.ts +67 -0
  114. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts +42 -0
  115. package/src/to-json-schema/zod3-to-json-schema/parsers/number.test.ts +65 -0
  116. package/src/to-json-schema/zod3-to-json-schema/parsers/number.ts +44 -0
  117. package/src/to-json-schema/zod3-to-json-schema/parsers/object.test.ts +149 -0
  118. package/src/to-json-schema/zod3-to-json-schema/parsers/object.ts +88 -0
  119. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.test.ts +147 -0
  120. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts +23 -0
  121. package/src/to-json-schema/zod3-to-json-schema/parsers/pipe.test.ts +35 -0
  122. package/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts +29 -0
  123. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.test.ts +15 -0
  124. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts +11 -0
  125. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.test.ts +20 -0
  126. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts +7 -0
  127. package/src/to-json-schema/zod3-to-json-schema/parsers/record.test.ts +108 -0
  128. package/src/to-json-schema/zod3-to-json-schema/parsers/record.ts +71 -0
  129. package/src/to-json-schema/zod3-to-json-schema/parsers/set.test.ts +20 -0
  130. package/src/to-json-schema/zod3-to-json-schema/parsers/set.ts +35 -0
  131. package/src/to-json-schema/zod3-to-json-schema/parsers/string.test.ts +438 -0
  132. package/src/to-json-schema/zod3-to-json-schema/parsers/string.ts +426 -0
  133. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.test.ts +33 -0
  134. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts +61 -0
  135. package/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts +11 -0
  136. package/src/to-json-schema/zod3-to-json-schema/parsers/union.test.ts +226 -0
  137. package/src/to-json-schema/zod3-to-json-schema/parsers/union.ts +144 -0
  138. package/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts +7 -0
  139. package/src/to-json-schema/zod3-to-json-schema/refs.test.ts +919 -0
  140. package/src/to-json-schema/zod3-to-json-schema/refs.ts +39 -0
  141. package/src/to-json-schema/zod3-to-json-schema/select-parser.ts +115 -0
  142. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.test.ts +862 -0
  143. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts +93 -0
  144. package/src/types/assistant-model-message.ts +39 -0
  145. package/src/types/content-part.ts +379 -0
  146. package/src/types/data-content.ts +4 -0
  147. package/src/types/execute-tool.ts +27 -0
  148. package/src/types/index.ts +40 -0
  149. package/src/types/model-message.ts +14 -0
  150. package/src/types/provider-options.ts +9 -0
  151. package/src/types/system-model-message.ts +20 -0
  152. package/src/types/tool-approval-request.ts +16 -0
  153. package/src/types/tool-approval-response.ts +27 -0
  154. package/src/types/tool-call.ts +31 -0
  155. package/src/types/tool-model-message.ts +23 -0
  156. package/src/types/tool-result.ts +35 -0
  157. package/src/types/tool.test-d.ts +228 -0
  158. package/src/types/tool.ts +324 -0
  159. package/src/types/user-model-message.ts +22 -0
  160. package/src/uint8-utils.ts +26 -0
  161. package/src/validate-types.test.ts +105 -0
  162. package/src/validate-types.ts +81 -0
  163. package/src/version.ts +6 -0
  164. package/src/with-user-agent-suffix.test.ts +84 -0
  165. package/src/with-user-agent-suffix.ts +27 -0
  166. package/src/without-trailing-slash.ts +3 -0
  167. package/LICENSE +0 -13
@@ -0,0 +1,404 @@
1
+ import { JSONSchema7, LanguageModelV3Prompt } from '@ai-sdk/provider';
2
+ import { expect, it, describe } from 'vitest';
3
+ import {
4
+ injectJsonInstruction,
5
+ injectJsonInstructionIntoMessages,
6
+ } from './inject-json-instruction';
7
+
8
+ const basicSchema: JSONSchema7 = {
9
+ type: 'object',
10
+ properties: {
11
+ name: { type: 'string' },
12
+ age: { type: 'number' },
13
+ },
14
+ required: ['name', 'age'],
15
+ };
16
+
17
+ describe('injectJsonInstruction', () => {
18
+ it('should handle basic case with prompt and schema', () => {
19
+ const result = injectJsonInstruction({
20
+ prompt: 'Generate a person',
21
+ schema: basicSchema,
22
+ });
23
+ expect(result).toBe(
24
+ 'Generate a person\n\n' +
25
+ 'JSON schema:\n' +
26
+ '{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
27
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
28
+ );
29
+ });
30
+
31
+ it('should handle only prompt, no schema', () => {
32
+ const result = injectJsonInstruction({
33
+ prompt: 'Generate a person',
34
+ });
35
+ expect(result).toBe('Generate a person\n\nYou MUST answer with JSON.');
36
+ });
37
+
38
+ it('should handle only schema, no prompt', () => {
39
+ const result = injectJsonInstruction({
40
+ schema: basicSchema,
41
+ });
42
+ expect(result).toBe(
43
+ 'JSON schema:\n' +
44
+ '{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
45
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
46
+ );
47
+ });
48
+
49
+ it('should handle no prompt, no schema', () => {
50
+ const result = injectJsonInstruction({});
51
+ expect(result).toBe('You MUST answer with JSON.');
52
+ });
53
+
54
+ it('should handle custom schemaPrefix and schemaSuffix', () => {
55
+ const result = injectJsonInstruction({
56
+ prompt: 'Generate a person',
57
+ schema: basicSchema,
58
+ schemaPrefix: 'Custom prefix:',
59
+ schemaSuffix: 'Custom suffix',
60
+ });
61
+ expect(result).toBe(
62
+ 'Generate a person\n\n' +
63
+ 'Custom prefix:\n' +
64
+ '{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
65
+ 'Custom suffix',
66
+ );
67
+ });
68
+
69
+ it('should handle empty string prompt', () => {
70
+ const result = injectJsonInstruction({
71
+ prompt: '',
72
+ schema: basicSchema,
73
+ });
74
+ expect(result).toBe(
75
+ 'JSON schema:\n' +
76
+ '{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}\n' +
77
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
78
+ );
79
+ });
80
+
81
+ it('should handle empty object schema', () => {
82
+ const result = injectJsonInstruction({
83
+ prompt: 'Generate something',
84
+ schema: {},
85
+ });
86
+ expect(result).toBe(
87
+ 'Generate something\n\n' +
88
+ 'JSON schema:\n' +
89
+ '{}\n' +
90
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
91
+ );
92
+ });
93
+
94
+ it('should handle complex nested schema', () => {
95
+ const complexSchema: JSONSchema7 = {
96
+ type: 'object',
97
+ properties: {
98
+ person: {
99
+ type: 'object',
100
+ properties: {
101
+ name: { type: 'string' },
102
+ age: { type: 'number' },
103
+ address: {
104
+ type: 'object',
105
+ properties: {
106
+ street: { type: 'string' },
107
+ city: { type: 'string' },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ };
114
+ const result = injectJsonInstruction({
115
+ prompt: 'Generate a complex person',
116
+ schema: complexSchema,
117
+ });
118
+ expect(result).toBe(
119
+ 'Generate a complex person\n\n' +
120
+ 'JSON schema:\n' +
121
+ '{"type":"object","properties":{"person":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"},"address":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"}}}}}}}\n' +
122
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
123
+ );
124
+ });
125
+
126
+ it('should handle schema with special characters', () => {
127
+ const specialSchema: JSONSchema7 = {
128
+ type: 'object',
129
+ properties: {
130
+ 'special@property': { type: 'string' },
131
+ 'emoji😊': { type: 'string' },
132
+ },
133
+ };
134
+ const result = injectJsonInstruction({
135
+ schema: specialSchema,
136
+ });
137
+ expect(result).toBe(
138
+ 'JSON schema:\n' +
139
+ '{"type":"object","properties":{"special@property":{"type":"string"},"emoji😊":{"type":"string"}}}\n' +
140
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
141
+ );
142
+ });
143
+
144
+ it('should handle very long prompt and schema', () => {
145
+ const longPrompt = 'A'.repeat(1000);
146
+ const longSchema: JSONSchema7 = {
147
+ type: 'object',
148
+ properties: {},
149
+ };
150
+ for (let i = 0; i < 100; i++) {
151
+ longSchema.properties![`prop${i}`] = { type: 'string' };
152
+ }
153
+ const result = injectJsonInstruction({
154
+ prompt: longPrompt,
155
+ schema: longSchema,
156
+ });
157
+ expect(result).toBe(
158
+ longPrompt +
159
+ '\n\n' +
160
+ 'JSON schema:\n' +
161
+ JSON.stringify(longSchema) +
162
+ '\n' +
163
+ 'You MUST answer with a JSON object that matches the JSON schema above.',
164
+ );
165
+ });
166
+
167
+ it('should handle null values for optional parameters', () => {
168
+ const result = injectJsonInstruction({
169
+ prompt: null as any,
170
+ schema: null as any,
171
+ schemaPrefix: null as any,
172
+ schemaSuffix: null as any,
173
+ });
174
+ expect(result).toBe('');
175
+ });
176
+
177
+ it('should handle undefined values for optional parameters', () => {
178
+ const result = injectJsonInstruction({
179
+ prompt: undefined,
180
+ schema: undefined,
181
+ schemaPrefix: undefined,
182
+ schemaSuffix: undefined,
183
+ });
184
+ expect(result).toBe('You MUST answer with JSON.');
185
+ });
186
+ });
187
+
188
+ describe('injectJsonInstructionIntoMessages', () => {
189
+ it('should handle basic case with prompt and schema', () => {
190
+ const result = injectJsonInstructionIntoMessages({
191
+ messages: [{ role: 'system', content: 'Generate a person' }],
192
+ schema: basicSchema,
193
+ });
194
+
195
+ expect(result).toMatchInlineSnapshot(`
196
+ [
197
+ {
198
+ "content": "Generate a person
199
+
200
+ JSON schema:
201
+ {"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}
202
+ You MUST answer with a JSON object that matches the JSON schema above.",
203
+ "role": "system",
204
+ },
205
+ ]
206
+ `);
207
+ });
208
+
209
+ it('should not mutate the input messages array', () => {
210
+ const originalMessages: LanguageModelV3Prompt = [
211
+ { role: 'system', content: 'Generate a person' },
212
+ ];
213
+
214
+ injectJsonInstructionIntoMessages({
215
+ messages: originalMessages,
216
+ schema: basicSchema,
217
+ });
218
+
219
+ expect(originalMessages).toEqual([
220
+ { role: 'system', content: 'Generate a person' },
221
+ ]);
222
+ });
223
+
224
+ it('should handle empty messages array', () => {
225
+ const result = injectJsonInstructionIntoMessages({
226
+ messages: [],
227
+ schema: basicSchema,
228
+ });
229
+
230
+ expect(result).toMatchInlineSnapshot(`
231
+ [
232
+ {
233
+ "content": "JSON schema:
234
+ {"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}
235
+ You MUST answer with a JSON object that matches the JSON schema above.",
236
+ "role": "system",
237
+ },
238
+ ]
239
+ `);
240
+ });
241
+
242
+ it('should handle messages without initial system message', () => {
243
+ const result = injectJsonInstructionIntoMessages({
244
+ messages: [
245
+ { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
246
+ {
247
+ role: 'assistant',
248
+ content: [{ type: 'text', text: 'Hi there' }],
249
+ },
250
+ ],
251
+ schema: basicSchema,
252
+ });
253
+
254
+ expect(result).toMatchInlineSnapshot(`
255
+ [
256
+ {
257
+ "content": "JSON schema:
258
+ {"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}
259
+ You MUST answer with a JSON object that matches the JSON schema above.",
260
+ "role": "system",
261
+ },
262
+ {
263
+ "content": [
264
+ {
265
+ "text": "Hello",
266
+ "type": "text",
267
+ },
268
+ ],
269
+ "role": "user",
270
+ },
271
+ {
272
+ "content": [
273
+ {
274
+ "text": "Hi there",
275
+ "type": "text",
276
+ },
277
+ ],
278
+ "role": "assistant",
279
+ },
280
+ ]
281
+ `);
282
+ });
283
+
284
+ it('should handle system message with empty content', () => {
285
+ const result = injectJsonInstructionIntoMessages({
286
+ messages: [
287
+ { role: 'system', content: '' },
288
+ { role: 'user', content: [{ type: 'text', text: 'Generate data' }] },
289
+ ],
290
+ schema: basicSchema,
291
+ });
292
+
293
+ expect(result).toMatchInlineSnapshot(`
294
+ [
295
+ {
296
+ "content": "JSON schema:
297
+ {"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}
298
+ You MUST answer with a JSON object that matches the JSON schema above.",
299
+ "role": "system",
300
+ },
301
+ {
302
+ "content": [
303
+ {
304
+ "text": "Generate data",
305
+ "type": "text",
306
+ },
307
+ ],
308
+ "role": "user",
309
+ },
310
+ ]
311
+ `);
312
+ });
313
+
314
+ it('should preserve all non-system messages', () => {
315
+ const result = injectJsonInstructionIntoMessages({
316
+ messages: [
317
+ { role: 'system', content: 'You are helpful' },
318
+ { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
319
+ { role: 'assistant', content: [{ type: 'text', text: 'Hi' }] },
320
+ { role: 'user', content: [{ type: 'text', text: 'Generate person' }] },
321
+ ],
322
+ schema: basicSchema,
323
+ });
324
+
325
+ expect(result).toMatchInlineSnapshot(`
326
+ [
327
+ {
328
+ "content": "You are helpful
329
+
330
+ JSON schema:
331
+ {"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}
332
+ You MUST answer with a JSON object that matches the JSON schema above.",
333
+ "role": "system",
334
+ },
335
+ {
336
+ "content": [
337
+ {
338
+ "text": "Hello",
339
+ "type": "text",
340
+ },
341
+ ],
342
+ "role": "user",
343
+ },
344
+ {
345
+ "content": [
346
+ {
347
+ "text": "Hi",
348
+ "type": "text",
349
+ },
350
+ ],
351
+ "role": "assistant",
352
+ },
353
+ {
354
+ "content": [
355
+ {
356
+ "text": "Generate person",
357
+ "type": "text",
358
+ },
359
+ ],
360
+ "role": "user",
361
+ },
362
+ ]
363
+ `);
364
+ });
365
+
366
+ it('should handle case with no schema', () => {
367
+ const result = injectJsonInstructionIntoMessages({
368
+ messages: [{ role: 'system', content: 'Generate data' }],
369
+ });
370
+
371
+ expect(result).toMatchInlineSnapshot(`
372
+ [
373
+ {
374
+ "content": "Generate data
375
+
376
+ You MUST answer with JSON.",
377
+ "role": "system",
378
+ },
379
+ ]
380
+ `);
381
+ });
382
+
383
+ it('should handle custom schema prefix and suffix', () => {
384
+ const result = injectJsonInstructionIntoMessages({
385
+ messages: [{ role: 'system', content: 'Generate data' }],
386
+ schema: basicSchema,
387
+ schemaPrefix: 'Custom schema:',
388
+ schemaSuffix: 'Follow this format exactly.',
389
+ });
390
+
391
+ expect(result).toMatchInlineSnapshot(`
392
+ [
393
+ {
394
+ "content": "Generate data
395
+
396
+ Custom schema:
397
+ {"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}},"required":["name","age"]}
398
+ Follow this format exactly.",
399
+ "role": "system",
400
+ },
401
+ ]
402
+ `);
403
+ });
404
+ });
@@ -0,0 +1,63 @@
1
+ import {
2
+ JSONSchema7,
3
+ LanguageModelV3Message,
4
+ LanguageModelV3Prompt,
5
+ } from '@ai-sdk/provider';
6
+
7
+ const DEFAULT_SCHEMA_PREFIX = 'JSON schema:';
8
+ const DEFAULT_SCHEMA_SUFFIX =
9
+ 'You MUST answer with a JSON object that matches the JSON schema above.';
10
+ const DEFAULT_GENERIC_SUFFIX = 'You MUST answer with JSON.';
11
+
12
+ export function injectJsonInstruction({
13
+ prompt,
14
+ schema,
15
+ schemaPrefix = schema != null ? DEFAULT_SCHEMA_PREFIX : undefined,
16
+ schemaSuffix = schema != null
17
+ ? DEFAULT_SCHEMA_SUFFIX
18
+ : DEFAULT_GENERIC_SUFFIX,
19
+ }: {
20
+ prompt?: string;
21
+ schema?: JSONSchema7;
22
+ schemaPrefix?: string;
23
+ schemaSuffix?: string;
24
+ }): string {
25
+ return [
26
+ prompt != null && prompt.length > 0 ? prompt : undefined,
27
+ prompt != null && prompt.length > 0 ? '' : undefined, // add a newline if prompt is not null
28
+ schemaPrefix,
29
+ schema != null ? JSON.stringify(schema) : undefined,
30
+ schemaSuffix,
31
+ ]
32
+ .filter(line => line != null)
33
+ .join('\n');
34
+ }
35
+
36
+ export function injectJsonInstructionIntoMessages({
37
+ messages,
38
+ schema,
39
+ schemaPrefix,
40
+ schemaSuffix,
41
+ }: {
42
+ messages: LanguageModelV3Prompt;
43
+ schema?: JSONSchema7;
44
+ schemaPrefix?: string;
45
+ schemaSuffix?: string;
46
+ }): LanguageModelV3Prompt {
47
+ const systemMessage: LanguageModelV3Message =
48
+ messages[0]?.role === 'system'
49
+ ? { ...messages[0] }
50
+ : { role: 'system', content: '' };
51
+
52
+ systemMessage.content = injectJsonInstruction({
53
+ prompt: systemMessage.content,
54
+ schema,
55
+ schemaPrefix,
56
+ schemaSuffix,
57
+ });
58
+
59
+ return [
60
+ systemMessage,
61
+ ...(messages[0]?.role === 'system' ? messages.slice(1) : messages),
62
+ ];
63
+ }
@@ -0,0 +1,8 @@
1
+ export function isAbortError(error: unknown): error is Error {
2
+ return (
3
+ (error instanceof Error || error instanceof DOMException) &&
4
+ (error.name === 'AbortError' ||
5
+ error.name === 'ResponseAborted' || // Next.js
6
+ error.name === 'TimeoutError')
7
+ );
8
+ }
@@ -0,0 +1,3 @@
1
+ export function isAsyncIterable<T = any>(obj: any): obj is AsyncIterable<T> {
2
+ return obj != null && typeof obj[Symbol.asyncIterator] === 'function';
3
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Type guard that checks whether a value is not `null` or `undefined`.
3
+ *
4
+ * @template T - The type of the value to check.
5
+ * @param value - The value to check.
6
+ * @returns `true` if the value is neither `null` nor `undefined`, otherwise `false`.
7
+ */
8
+ export function isNonNullable<T>(
9
+ value: T | undefined | null,
10
+ ): value is NonNullable<T> {
11
+ return value != null;
12
+ }