@@ -4,6 +4,7 @@ import { convertArrayToReadableStream } from '../test/convert-array-to-readable-
4
4
import { convertAsyncIterableToArray } from '../test/convert-async-iterable-to-array' ;
5
5
import { MockLanguageModelV1 } from '../test/mock-language-model-v1' ;
6
6
import { streamObject } from './stream-object' ;
7
+ import { TypeValidationError } from '@ai-sdk/provider' ;
7
8
8
9
describe ( 'result.objectStream' , ( ) => {
9
10
it ( 'should send object deltas with json mode' , async ( ) => {
@@ -255,3 +256,103 @@ describe('result.usage', () => {
255
256
} ) ;
256
257
} ) ;
257
258
} ) ;
259
+
260
+ describe ( 'result.object' , ( ) => {
261
+ it ( 'should resolve with typed object' , async ( ) => {
262
+ const result = await streamObject ( {
263
+ model : new MockLanguageModelV1 ( {
264
+ doStream : async ( { prompt, mode } ) => {
265
+ assert . deepStrictEqual ( mode , { type : 'object-json' } ) ;
266
+ assert . deepStrictEqual ( prompt , [
267
+ {
268
+ role : 'system' ,
269
+ content :
270
+ 'JSON schema:\n' +
271
+ '{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}\n' +
272
+ 'You MUST answer with a JSON object that matches the JSON schema above.' ,
273
+ } ,
274
+ { role : 'user' , content : [ { type : 'text' , text : 'prompt' } ] } ,
275
+ ] ) ;
276
+
277
+ return {
278
+ stream : convertArrayToReadableStream ( [
279
+ { type : 'text-delta' , textDelta : '{ ' } ,
280
+ { type : 'text-delta' , textDelta : '"content": ' } ,
281
+ { type : 'text-delta' , textDelta : `"Hello, ` } ,
282
+ { type : 'text-delta' , textDelta : `world` } ,
283
+ { type : 'text-delta' , textDelta : `!"` } ,
284
+ { type : 'text-delta' , textDelta : ' }' } ,
285
+ {
286
+ type : 'finish' ,
287
+ finishReason : 'stop' ,
288
+ usage : { completionTokens : 10 , promptTokens : 3 } ,
289
+ } ,
290
+ ] ) ,
291
+ rawCall : { rawPrompt : 'prompt' , rawSettings : { } } ,
292
+ } ;
293
+ } ,
294
+ } ) ,
295
+ schema : z . object ( { content : z . string ( ) } ) ,
296
+ mode : 'json' ,
297
+ prompt : 'prompt' ,
298
+ } ) ;
299
+
300
+ // consume stream (runs in parallel)
301
+ convertAsyncIterableToArray ( result . partialObjectStream ) ;
302
+
303
+ assert . deepStrictEqual ( await result . object , {
304
+ content : 'Hello, world!' ,
305
+ } ) ;
306
+ } ) ;
307
+
308
+ it ( 'should reject object promise when the streamed object does not match the schema' , async ( ) => {
309
+ const result = await streamObject ( {
310
+ model : new MockLanguageModelV1 ( {
311
+ doStream : async ( { prompt, mode } ) => {
312
+ assert . deepStrictEqual ( mode , { type : 'object-json' } ) ;
313
+ assert . deepStrictEqual ( prompt , [
314
+ {
315
+ role : 'system' ,
316
+ content :
317
+ 'JSON schema:\n' +
318
+ '{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}\n' +
319
+ 'You MUST answer with a JSON object that matches the JSON schema above.' ,
320
+ } ,
321
+ { role : 'user' , content : [ { type : 'text' , text : 'prompt' } ] } ,
322
+ ] ) ;
323
+
324
+ return {
325
+ stream : convertArrayToReadableStream ( [
326
+ { type : 'text-delta' , textDelta : '{ ' } ,
327
+ { type : 'text-delta' , textDelta : '"invalid": ' } ,
328
+ { type : 'text-delta' , textDelta : `"Hello, ` } ,
329
+ { type : 'text-delta' , textDelta : `world` } ,
330
+ { type : 'text-delta' , textDelta : `!"` } ,
331
+ { type : 'text-delta' , textDelta : ' }' } ,
332
+ {
333
+ type : 'finish' ,
334
+ finishReason : 'stop' ,
335
+ usage : { completionTokens : 10 , promptTokens : 3 } ,
336
+ } ,
337
+ ] ) ,
338
+ rawCall : { rawPrompt : 'prompt' , rawSettings : { } } ,
339
+ } ;
340
+ } ,
341
+ } ) ,
342
+ schema : z . object ( { content : z . string ( ) } ) ,
343
+ mode : 'json' ,
344
+ prompt : 'prompt' ,
345
+ } ) ;
346
+
347
+ // consume stream (runs in parallel)
348
+ convertAsyncIterableToArray ( result . partialObjectStream ) ;
349
+
350
+ await result . object
351
+ . then ( ( ) => {
352
+ assert . fail ( 'Expected object promise to be rejected' ) ;
353
+ } )
354
+ . catch ( error => {
355
+ expect ( TypeValidationError . isTypeValidationError ( error ) ) . toBeTruthy ( ) ;
356
+ } ) ;
357
+ } ) ;
358
+ } ) ;
0 commit comments