@outputai/llm 0.11.1-next.da26845.0 → 0.12.0

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 (47) hide show
  1. package/package.json +7 -3
  2. package/src/agent.js +7 -2
  3. package/src/generate.js +1 -0
  4. package/src/index.d.ts +14 -6
  5. package/src/utils/wrap.js +35 -2
  6. package/src/agent.spec.js +0 -595
  7. package/src/ai_provider.spec.js +0 -217
  8. package/src/ai_sdk_options.spec.js +0 -390
  9. package/src/fixtures/image_response_v7_google_vertex.js +0 -72
  10. package/src/fixtures/image_response_v7_openai.js +0 -77
  11. package/src/fixtures/models_api_light.json +0 -675
  12. package/src/fixtures/models_api_v7_subset.json +0 -259
  13. package/src/fixtures/stream_response_v7_anthropic.js +0 -389
  14. package/src/fixtures/stream_response_v7_google_vertex.js +0 -462
  15. package/src/fixtures/stream_response_v7_openai.js +0 -353
  16. package/src/fixtures/stream_response_v7_perplexity.js +0 -671
  17. package/src/fixtures/text_response_v7_anthropic.js +0 -359
  18. package/src/fixtures/text_response_v7_anthropic_cache.js +0 -719
  19. package/src/fixtures/text_response_v7_google_vertex.js +0 -435
  20. package/src/fixtures/text_response_v7_openai.js +0 -347
  21. package/src/fixtures/text_response_v7_perplexity.js +0 -764
  22. package/src/generate.spec.js +0 -525
  23. package/src/prompt/content.spec.js +0 -208
  24. package/src/prompt/interpolations.spec.js +0 -109
  25. package/src/prompt/loader.full.spec.js +0 -309
  26. package/src/prompt/loader.spec.js +0 -258
  27. package/src/prompt/markup/attributes.spec.js +0 -132
  28. package/src/prompt/markup/nodes.spec.js +0 -333
  29. package/src/prompt/markup/tokenizer.spec.js +0 -149
  30. package/src/prompt/markup/tokens.spec.js +0 -250
  31. package/src/prompt/validations.spec.js +0 -812
  32. package/src/utils/cost.full.spec.js +0 -272
  33. package/src/utils/cost.spec.js +0 -290
  34. package/src/utils/error_handler.spec.js +0 -230
  35. package/src/utils/file.spec.js +0 -89
  36. package/src/utils/image.spec.js +0 -20
  37. package/src/utils/legacy_cost_attribute.spec.js +0 -278
  38. package/src/utils/models.spec.js +0 -119
  39. package/src/utils/models_pricing.spec.js +0 -162
  40. package/src/utils/skills.spec.js +0 -168
  41. package/src/utils/sources.spec.js +0 -122
  42. package/src/utils/stream.spec.js +0 -55
  43. package/src/utils/tools.spec.js +0 -167
  44. package/src/utils/usage.full.spec.js +0 -215
  45. package/src/utils/usage.spec.js +0 -246
  46. package/src/utils/wrap.spec.js +0 -467
  47. package/src/validations.spec.js +0 -631
@@ -1,631 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { ValidationError } from '@outputai/core';
3
- import {
4
- parseGenerateTextArgs,
5
- parseGenerateTextWithStreamingArgs,
6
- parseStreamTextArgs,
7
- parseGenerateImageArgs,
8
- parseAgentArgs,
9
- parseAgentGenerateArgs,
10
- parseAgentGenerateWithStreamingArgs,
11
- parseAgentStreamArgs
12
- } from './validations.js';
13
-
14
- const makeOutput = () => ( { name: 'object' } );
15
-
16
- const searchTool = { description: 'Search' };
17
- const stopWhen = () => false;
18
- const promptObject = {
19
- name: 'custom',
20
- config: {
21
- provider: 'openai',
22
- model: 'gpt-4o',
23
- maxSteps: 10,
24
- skills: []
25
- },
26
- messages: [ { role: 'user', content: 'Hello' } ],
27
- instructions: null,
28
- fileDir: '/prompts',
29
- variables: {}
30
- };
31
-
32
- describe( 'prompt arguments', () => {
33
- it.each( [
34
- [ 'generateText', parseGenerateTextArgs ],
35
- [ 'generateTextWithStreaming', parseGenerateTextWithStreamingArgs ],
36
- [ 'streamText', parseStreamTextArgs ],
37
- [ 'generateImage', parseGenerateImageArgs ],
38
- [ 'Agent', parseAgentArgs ]
39
- ] )( 'normalizes a prompt object for %s', ( _, parse ) => {
40
- expect( parse( { prompt: promptObject } ) ).toEqual( {
41
- promptObject
42
- } );
43
- } );
44
-
45
- it.each( [ 'promptDir', 'variables' ] )( 'rejects %s with a prompt object', field => {
46
- expect( () => parseGenerateTextArgs( {
47
- prompt: promptObject,
48
- [field]: field === 'promptDir' ? '/other' : { topic: 'other' }
49
- } ) ).toThrow( `${field} cannot be used when prompt is an object` );
50
- } );
51
-
52
- it( 'rejects prompt file arguments with an Agent prompt object', () => {
53
- expect( () => parseAgentArgs( {
54
- prompt: promptObject,
55
- promptDir: '/other'
56
- } ) ).toThrow( 'promptDir cannot be used when prompt is an object' );
57
- } );
58
-
59
- it( 'reports specific validation issues for an invalid prompt object', () => {
60
- expect( () => parseGenerateTextArgs( {
61
- prompt: {
62
- ...promptObject,
63
- config: {
64
- provider: 'openai'
65
- }
66
- }
67
- } ) ).toThrow( /prompt\.config\.model/ );
68
- } );
69
-
70
- it.each( [
71
- [ 'missing', {} ],
72
- [ 'numeric', { prompt: 123, variables: { topic: 'testing' } } ]
73
- ] )( 'keeps filename-shaped errors for a %s prompt', ( _, args ) => {
74
- const error = { message: '' };
75
- try {
76
- parseGenerateTextArgs( args );
77
- } catch ( e ) {
78
- error.message = e.message;
79
- }
80
-
81
- expect( error.message ).toContain( 'expected string' );
82
- expect( error.message ).not.toContain( 'variables cannot be used when prompt is an object' );
83
- } );
84
- } );
85
-
86
- describe( 'parseGenerateTextArgs', () => {
87
- it( 'renames prompt to promptFile and keeps allowlisted leftovers', () => {
88
- const abortSignal = AbortSignal.abort();
89
- const tools = { search: searchTool };
90
- const output = makeOutput();
91
-
92
- expect( parseGenerateTextArgs( {
93
- prompt: 'summary@v1',
94
- variables: { topic: 'testing', count: 2, draft: false },
95
- promptDir: '/prompts',
96
- tools,
97
- output,
98
- toolChoice: { type: 'tool', toolName: 'search' },
99
- stopWhen,
100
- abortSignal
101
- } ) ).toEqual( {
102
- promptFile: 'summary@v1',
103
- variables: { topic: 'testing', count: 2, draft: false },
104
- promptDir: '/prompts',
105
- tools,
106
- output,
107
- toolChoice: { type: 'tool', toolName: 'search' },
108
- stopWhen,
109
- abortSignal
110
- } );
111
- } );
112
-
113
- it( 'accepts an array of stopWhen functions', () => {
114
- const parsed = parseGenerateTextArgs( {
115
- prompt: 'summary@v1',
116
- stopWhen: [ stopWhen ]
117
- } );
118
-
119
- expect( parsed.stopWhen ).toEqual( [ stopWhen ] );
120
- } );
121
-
122
- it( 'throws ValidationError with generateText prefix for invalid args', () => {
123
- expect( () => parseGenerateTextArgs( {
124
- variables: { topic: 'testing' }
125
- } ) ).toThrow( ValidationError );
126
-
127
- expect( () => parseGenerateTextArgs( {
128
- prompt: 123
129
- } ) ).toThrow( /Invalid generateText\(\) arguments/ );
130
-
131
- expect( () => parseGenerateTextArgs( {
132
- prompt: 'summary@v1',
133
- promptDir: ''
134
- } ) ).toThrow( ValidationError );
135
-
136
- expect( () => parseGenerateTextArgs( {
137
- prompt: 'summary@v1',
138
- abortSignal: {}
139
- } ) ).toThrow( ValidationError );
140
- } );
141
-
142
- it( 'does not keep prompt-owned call arguments on the parsed result', () => {
143
- expect( () => parseGenerateTextArgs( {
144
- prompt: 'summary@v1',
145
- skills: [ { name: 'style', description: 'Style', instructions: '# Style' } ]
146
- } ) ).toThrow( /skills must be set in the prompt file/ );
147
-
148
- expect( () => parseGenerateTextArgs( {
149
- prompt: 'summary@v1',
150
- maxSteps: 4,
151
- skills: [ './writer.md' ]
152
- } ) ).toThrow( /must be set in the prompt file/ );
153
- } );
154
-
155
- it( 'throws ValidationError for unrecognized call arguments', () => {
156
- expect( () => parseGenerateTextArgs( {
157
- prompt: 'summary@v1',
158
- temperature: 0.2
159
- } ) ).toThrow( /unrecognized key/i );
160
-
161
- expect( () => parseGenerateTextArgs( {
162
- prompt: 'summary@v1',
163
- maxRetries: 2
164
- } ) ).toThrow( /unrecognized key/i );
165
-
166
- expect( () => parseGenerateTextArgs( {
167
- prompt: 'summary@v1',
168
- onChunk: () => {}
169
- } ) ).toThrow( /unrecognized key/i );
170
- } );
171
-
172
- it( 'accepts nested objects and arrays as variables', () => {
173
- const variables = {
174
- company: { name: 'Acme', industries: [ 'SaaS', 'AI' ] },
175
- criteria: [ { name: 'Accuracy', required: true } ]
176
- };
177
-
178
- expect( parseGenerateTextArgs( {
179
- prompt: 'summary@v1',
180
- variables
181
- } ).variables ).toEqual( variables );
182
- } );
183
-
184
- it( 'throws ValidationError for invalid allowlisted field shapes', () => {
185
- expect( () => parseGenerateTextArgs( {
186
- prompt: 'summary@v1',
187
- variables: [ 'not a record' ]
188
- } ) ).toThrow( ValidationError );
189
-
190
- expect( () => parseGenerateTextArgs( {
191
- prompt: 'summary@v1',
192
- tools: { search: true }
193
- } ) ).toThrow( ValidationError );
194
-
195
- expect( () => parseGenerateTextArgs( {
196
- prompt: 'summary@v1',
197
- output: true
198
- } ) ).toThrow( ValidationError );
199
-
200
- expect( () => parseGenerateTextArgs( {
201
- prompt: 'summary@v1',
202
- stopWhen: { type: 'custom-stop' }
203
- } ) ).toThrow( ValidationError );
204
-
205
- expect( () => parseGenerateTextArgs( {
206
- prompt: 'summary@v1',
207
- toolChoice: 'maybe'
208
- } ) ).toThrow( ValidationError );
209
- } );
210
-
211
- it( 'accepts opaque tools and output objects', () => {
212
- expect( parseGenerateTextArgs( {
213
- prompt: 'summary@v1',
214
- tools: { search: { description: 'Search' } },
215
- output: { type: 'object' }
216
- } ) ).toMatchObject( {
217
- tools: { search: { description: 'Search' } },
218
- output: { type: 'object' }
219
- } );
220
- } );
221
-
222
- it( 'preserves class-based tool and output identities', () => {
223
- class TestTool {
224
- #result = 'searched';
225
-
226
- execute() {
227
- return this.#result;
228
- }
229
- }
230
-
231
- class TestOutput {
232
- #result = 'parsed';
233
-
234
- parseCompleteOutput() {
235
- return this.#result;
236
- }
237
- }
238
-
239
- const tool = new TestTool();
240
- const output = new TestOutput();
241
- const parsed = parseGenerateTextArgs( {
242
- prompt: 'summary@v1',
243
- tools: { search: tool },
244
- output
245
- } );
246
-
247
- expect( parsed.tools.search ).toBe( tool );
248
- expect( parsed.tools.search.execute() ).toBe( 'searched' );
249
- expect( parsed.output ).toBe( output );
250
- expect( parsed.output.parseCompleteOutput() ).toBe( 'parsed' );
251
- } );
252
- } );
253
-
254
- describe( 'parseStreamTextArgs', () => {
255
- it( 'keeps stream callbacks on the parsed result', () => {
256
- const parsed = parseStreamTextArgs( {
257
- prompt: 'summary@v1',
258
- variables: { topic: 'testing' },
259
- promptDir: '/prompts',
260
- onEnd: () => {},
261
- onError: () => {},
262
- onChunk: () => {}
263
- } );
264
-
265
- expect( parsed.promptFile ).toBe( 'summary@v1' );
266
- expect( parsed.variables ).toEqual( { topic: 'testing' } );
267
- expect( parsed.promptDir ).toBe( '/prompts' );
268
- expect( parsed.onEnd ).toBeTypeOf( 'function' );
269
- expect( parsed.onError ).toBeTypeOf( 'function' );
270
- expect( parsed.onChunk ).toBeTypeOf( 'function' );
271
- } );
272
-
273
- it( 'throws ValidationError with streamText prefix for invalid args', () => {
274
- expect( () => parseStreamTextArgs( {} ) ).toThrow( ValidationError );
275
- expect( () => parseStreamTextArgs( { prompt: null } ) ).toThrow( /Invalid streamText\(\) arguments/ );
276
-
277
- expect( () => parseStreamTextArgs( {
278
- prompt: 'summary@v1',
279
- promptDir: ''
280
- } ) ).toThrow( ValidationError );
281
- } );
282
-
283
- it( 'throws ValidationError for invalid callbacks', () => {
284
- expect( () => parseStreamTextArgs( {
285
- prompt: 'summary@v1',
286
- onEnd: 'not a function'
287
- } ) ).toThrow( ValidationError );
288
-
289
- expect( () => parseStreamTextArgs( {
290
- prompt: 'summary@v1',
291
- onError: {}
292
- } ) ).toThrow( ValidationError );
293
-
294
- expect( () => parseStreamTextArgs( {
295
- prompt: 'summary@v1',
296
- onChunk: 'not a function'
297
- } ) ).toThrow( ValidationError );
298
- } );
299
-
300
- it( 'throws ValidationError for unrecognized call arguments', () => {
301
- expect( () => parseStreamTextArgs( {
302
- prompt: 'summary@v1',
303
- temperature: 0.2
304
- } ) ).toThrow( /unrecognized key/i );
305
-
306
- expect( () => parseStreamTextArgs( {
307
- prompt: 'summary@v1',
308
- skills: [ { name: 'style', description: 'Style', instructions: '# Style' } ]
309
- } ) ).toThrow( /skills must be set in the prompt file/ );
310
-
311
- expect( () => parseStreamTextArgs( {
312
- prompt: 'summary@v1',
313
- maxSteps: 1.5
314
- } ) ).toThrow( /maxSteps must be set in the prompt file/ );
315
- } );
316
- } );
317
-
318
- describe( 'parseGenerateTextWithStreamingArgs', () => {
319
- it( 'keeps onChunk and omits streamText-only callbacks', () => {
320
- const parsed = parseGenerateTextWithStreamingArgs( {
321
- prompt: 'summary@v1',
322
- onChunk: () => {}
323
- } );
324
-
325
- expect( parsed.promptFile ).toBe( 'summary@v1' );
326
- expect( parsed.onChunk ).toBeTypeOf( 'function' );
327
-
328
- expect( () => parseGenerateTextWithStreamingArgs( {
329
- prompt: 'summary@v1',
330
- onEnd: () => {}
331
- } ) ).toThrow( /unrecognized key/i );
332
- } );
333
-
334
- it( 'uses the generateTextWithStreaming error prefix', () => {
335
- expect( () => parseGenerateTextWithStreamingArgs( {
336
- prompt: 'summary@v1',
337
- maxSteps: 0
338
- } ) ).toThrow( /Invalid generateTextWithStreaming\(\) arguments[\s\S]*maxSteps must be set in the prompt file/ );
339
- } );
340
- } );
341
-
342
- describe( 'parseGenerateImageArgs', () => {
343
- it( 'renames prompt to promptFile and keeps allowlisted leftovers', () => {
344
- const images = [ Buffer.from( 'image-bytes' ) ];
345
- const mask = Buffer.from( 'mask-bytes' );
346
- const abortSignal = AbortSignal.abort();
347
-
348
- expect( parseGenerateImageArgs( {
349
- prompt: 'image@v1',
350
- variables: { scene: 'race cars' },
351
- promptDir: '/prompts',
352
- images,
353
- mask,
354
- abortSignal
355
- } ) ).toEqual( {
356
- promptFile: 'image@v1',
357
- variables: { scene: 'race cars' },
358
- promptDir: '/prompts',
359
- images,
360
- mask,
361
- abortSignal
362
- } );
363
- } );
364
-
365
- it( 'accepts text-to-image args without images or mask', () => {
366
- expect( parseGenerateImageArgs( {
367
- prompt: 'image@v1',
368
- variables: { topic: 'race cars' },
369
- promptDir: '/prompts'
370
- } ) ).toEqual( {
371
- promptFile: 'image@v1',
372
- variables: { topic: 'race cars' },
373
- promptDir: '/prompts'
374
- } );
375
- } );
376
-
377
- it( 'accepts all supported image input shapes', () => {
378
- const buffer = Buffer.from( 'image-bytes' );
379
- const uint8Array = new Uint8Array( [ 1, 2, 3 ] );
380
- const arrayBuffer = new ArrayBuffer( 3 );
381
- const paddedBase64 = 'aW1hZ2UtYnl0ZXM=';
382
- const unpaddedBase64 = 'aW1hZ2U';
383
-
384
- expect( () => parseGenerateImageArgs( {
385
- prompt: 'image@v1',
386
- images: [
387
- buffer,
388
- uint8Array,
389
- arrayBuffer,
390
- paddedBase64,
391
- unpaddedBase64,
392
- { data: buffer, mediaType: 'image/png' },
393
- { data: uint8Array },
394
- { data: arrayBuffer, mediaType: 'image/jpeg' },
395
- { data: paddedBase64, mediaType: 'image/webp' }
396
- ],
397
- mask: { data: Buffer.from( 'mask-bytes' ), mediaType: 'image/png' }
398
- } ) ).not.toThrow();
399
- } );
400
-
401
- it( 'throws ValidationError for invalid image args', () => {
402
- expect( () => parseGenerateImageArgs( {
403
- prompt: ''
404
- } ) ).toThrow( /Invalid generateImage\(\) arguments/ );
405
-
406
- expect( () => parseGenerateImageArgs( {
407
- prompt: 'image@v1',
408
- images: []
409
- } ) ).toThrow( ValidationError );
410
-
411
- expect( () => parseGenerateImageArgs( {
412
- prompt: 'image@v1',
413
- images: [ { data: null } ]
414
- } ) ).toThrow( ValidationError );
415
-
416
- expect( () => parseGenerateImageArgs( {
417
- prompt: 'image@v1',
418
- images: [ { data: 'aW1hZ2U=', mediaType: '' } ]
419
- } ) ).toThrow( ValidationError );
420
- } );
421
-
422
- it( 'rejects image strings that are not raw base64 data', () => {
423
- for ( const image of [
424
- 'https://example.com/image.png',
425
- 'data:image/png;base64,aW1hZ2U=',
426
- 'not base64',
427
- 'abcde'
428
- ] ) {
429
- expect( () => parseGenerateImageArgs( {
430
- prompt: 'image@v1',
431
- images: [ image ]
432
- } ) ).toThrow( /Image strings must be raw base64 data/ );
433
- }
434
- } );
435
-
436
- it( 'requires images when mask is provided', () => {
437
- expect( () => parseGenerateImageArgs( {
438
- prompt: 'image@v1',
439
- mask: Buffer.from( 'mask-bytes' )
440
- } ) ).toThrow( /mask requires images/ );
441
- } );
442
-
443
- it( 'throws ValidationError for unrecognized or prompt-owned call arguments', () => {
444
- expect( () => parseGenerateImageArgs( {
445
- prompt: 'image@v1',
446
- size: '1024x1024',
447
- n: 2
448
- } ) ).toThrow( /unrecognized key/i );
449
-
450
- expect( () => parseGenerateImageArgs( {
451
- prompt: 'image@v1',
452
- aspectRatio: '1:1'
453
- } ) ).toThrow( /unrecognized key/i );
454
-
455
- expect( () => parseGenerateImageArgs( {
456
- prompt: 'image@v1',
457
- providerOptions: { openai: { quality: 'high' } }
458
- } ) ).toThrow( /unrecognized key/i );
459
-
460
- expect( () => parseGenerateImageArgs( {
461
- prompt: 'image@v1',
462
- maxSteps: 4
463
- } ) ).toThrow( /unrecognized key/i );
464
- } );
465
- } );
466
-
467
- describe( 'parseAgentArgs', () => {
468
- it( 'renames prompt to promptFile and keeps allowlisted leftovers', () => {
469
- const tools = { search: searchTool };
470
- const output = makeOutput();
471
- const messageStore = { getMessages() {}, addMessages() {} };
472
-
473
- const parsed = parseAgentArgs( {
474
- prompt: 'summary@v1',
475
- variables: { topic: 'testing' },
476
- promptDir: '/prompts',
477
- tools,
478
- output,
479
- stopWhen,
480
- messageStore
481
- } );
482
-
483
- expect( parsed ).toEqual( {
484
- promptFile: 'summary@v1',
485
- variables: { topic: 'testing' },
486
- promptDir: '/prompts',
487
- tools,
488
- output,
489
- stopWhen,
490
- messageStore
491
- } );
492
- expect( parsed.messageStore ).toBe( messageStore );
493
- } );
494
-
495
- it( 'preserves class-based message store identity', () => {
496
- class TestMessageStore {
497
- #messages = [];
498
-
499
- getMessages() {
500
- return this.#messages;
501
- }
502
-
503
- addMessages( messages ) {
504
- this.#messages.push( ...messages );
505
- }
506
- }
507
-
508
- const messageStore = new TestMessageStore();
509
- const parsed = parseAgentArgs( {
510
- prompt: 'summary@v1',
511
- messageStore
512
- } );
513
- const message = { role: 'user', content: 'Hello' };
514
-
515
- parsed.messageStore.addMessages( [ message ] );
516
-
517
- expect( parsed.messageStore ).toBe( messageStore );
518
- expect( parsed.messageStore.getMessages() ).toEqual( [ message ] );
519
- } );
520
-
521
- it( 'throws ValidationError with Agent prefix for invalid args', () => {
522
- expect( () => parseAgentArgs( {} ) ).toThrow( ValidationError );
523
- expect( () => parseAgentArgs( { prompt: '' } ) ).toThrow( /Invalid Agent\(\) arguments/ );
524
-
525
- expect( () => parseAgentArgs( {
526
- prompt: 'summary@v1',
527
- promptDir: ''
528
- } ) ).toThrow( ValidationError );
529
- } );
530
-
531
- it( 'does not keep prompt-owned call arguments on the parsed result', () => {
532
- expect( () => parseAgentArgs( {
533
- prompt: 'summary@v1',
534
- skills: [ { name: 'style', description: 'Style', instructions: '# Style' } ]
535
- } ) ).toThrow( /skills must be set in the prompt file/ );
536
-
537
- expect( () => parseAgentArgs( {
538
- prompt: 'summary@v1',
539
- maxSteps: 4,
540
- skills: [ './writer.md' ]
541
- } ) ).toThrow( /must be set in the prompt file/ );
542
- } );
543
-
544
- it( 'throws ValidationError for unrecognized constructor arguments', () => {
545
- expect( () => parseAgentArgs( {
546
- prompt: 'summary@v1',
547
- temperature: 0.2
548
- } ) ).toThrow( /unrecognized key/i );
549
- } );
550
-
551
- it( 'throws ValidationError for an invalid message store', () => {
552
- expect( () => parseAgentArgs( {
553
- prompt: 'summary@v1',
554
- messageStore: { getMessages() {} }
555
- } ) ).toThrow( ValidationError );
556
- } );
557
- } );
558
-
559
- describe( 'parseAgentGenerateArgs', () => {
560
- it( 'defaults omitted args and messages to an empty list', () => {
561
- expect( parseAgentGenerateArgs() ).toEqual( { messages: [] } );
562
- expect( parseAgentGenerateArgs( undefined ) ).toEqual( { messages: [] } );
563
- expect( parseAgentGenerateArgs( {} ) ).toEqual( { messages: [] } );
564
- } );
565
-
566
- it( 'keeps allowlisted generate options', () => {
567
- const messages = [ { role: 'user', content: 'Hello' } ];
568
- const abortSignal = AbortSignal.abort();
569
-
570
- expect( parseAgentGenerateArgs( {
571
- messages,
572
- abortSignal,
573
- toolChoice: 'required'
574
- } ) ).toEqual( {
575
- messages,
576
- abortSignal,
577
- toolChoice: 'required'
578
- } );
579
- } );
580
-
581
- it( 'throws ValidationError for unrecognized or prompt-owned call arguments', () => {
582
- expect( () => parseAgentGenerateArgs( { temperature: 0.2 } ) ).toThrow( /unrecognized key/i );
583
- expect( () => parseAgentGenerateArgs( { onChunk: () => {} } ) ).toThrow( /unrecognized key/i );
584
- expect( () => parseAgentGenerateArgs( { maxSteps: 4 } ) ).toThrow( /maxSteps must be set in the prompt file/ );
585
- } );
586
-
587
- it( 'throws ValidationError for invalid messages or toolChoice', () => {
588
- expect( () => parseAgentGenerateArgs( {
589
- messages: [ { content: 'Hello' } ]
590
- } ) ).toThrow( ValidationError );
591
-
592
- expect( () => parseAgentGenerateArgs( {
593
- messages: [ { role: 'moderator', content: 'Hello' } ]
594
- } ) ).toThrow( ValidationError );
595
-
596
- expect( () => parseAgentGenerateArgs( {
597
- toolChoice: { type: 'tool' }
598
- } ) ).toThrow( ValidationError );
599
- } );
600
- } );
601
-
602
- describe( 'parseAgentGenerateWithStreamingArgs', () => {
603
- it( 'defaults omitted args and accepts onChunk', () => {
604
- expect( parseAgentGenerateWithStreamingArgs() ).toEqual( { messages: [] } );
605
-
606
- const parsed = parseAgentGenerateWithStreamingArgs( { onChunk: () => {} } );
607
- expect( parsed.messages ).toEqual( [] );
608
- expect( parsed.onChunk ).toBeTypeOf( 'function' );
609
- } );
610
-
611
- it( 'rejects stream-only callbacks', () => {
612
- expect( () => parseAgentGenerateWithStreamingArgs( { onEnd: () => {} } ) ).toThrow( /unrecognized key/i );
613
- } );
614
- } );
615
-
616
- describe( 'parseAgentStreamArgs', () => {
617
- it( 'defaults omitted args and keeps stream callbacks', () => {
618
- expect( parseAgentStreamArgs() ).toEqual( { messages: [] } );
619
-
620
- const parsed = parseAgentStreamArgs( {
621
- onEnd: () => {},
622
- onError: () => {},
623
- onChunk: () => {}
624
- } );
625
-
626
- expect( parsed.messages ).toEqual( [] );
627
- expect( parsed.onEnd ).toBeTypeOf( 'function' );
628
- expect( parsed.onError ).toBeTypeOf( 'function' );
629
- expect( parsed.onChunk ).toBeTypeOf( 'function' );
630
- } );
631
- } );