@ai-sdk/openai 0.0.0-2f1ae29d-20260122140908 → 0.0.0-4115c213-20260122152721

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 (63) hide show
  1. package/CHANGELOG.md +17 -2
  2. package/dist/index.js +15 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +15 -1
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/internal/index.js +14 -0
  7. package/dist/internal/index.js.map +1 -1
  8. package/dist/internal/index.mjs +14 -0
  9. package/dist/internal/index.mjs.map +1 -1
  10. package/package.json +9 -5
  11. package/src/responses/convert-to-openai-responses-input.ts +20 -1
  12. package/src/chat/__fixtures__/azure-model-router.1.chunks.txt +0 -8
  13. package/src/chat/__snapshots__/openai-chat-language-model.test.ts.snap +0 -88
  14. package/src/chat/convert-to-openai-chat-messages.test.ts +0 -516
  15. package/src/chat/openai-chat-language-model.test.ts +0 -3496
  16. package/src/chat/openai-chat-prepare-tools.test.ts +0 -322
  17. package/src/completion/openai-completion-language-model.test.ts +0 -752
  18. package/src/embedding/__snapshots__/openai-embedding-model.test.ts.snap +0 -43
  19. package/src/embedding/openai-embedding-model.test.ts +0 -146
  20. package/src/image/openai-image-model.test.ts +0 -722
  21. package/src/openai-error.test.ts +0 -34
  22. package/src/openai-language-model-capabilities.test.ts +0 -93
  23. package/src/openai-provider.test.ts +0 -98
  24. package/src/responses/__fixtures__/openai-apply-patch-tool-delete.1.chunks.txt +0 -5
  25. package/src/responses/__fixtures__/openai-apply-patch-tool.1.chunks.txt +0 -38
  26. package/src/responses/__fixtures__/openai-apply-patch-tool.1.json +0 -69
  27. package/src/responses/__fixtures__/openai-code-interpreter-tool.1.chunks.txt +0 -393
  28. package/src/responses/__fixtures__/openai-code-interpreter-tool.1.json +0 -137
  29. package/src/responses/__fixtures__/openai-error.1.chunks.txt +0 -4
  30. package/src/responses/__fixtures__/openai-error.1.json +0 -8
  31. package/src/responses/__fixtures__/openai-file-search-tool.1.chunks.txt +0 -94
  32. package/src/responses/__fixtures__/openai-file-search-tool.1.json +0 -89
  33. package/src/responses/__fixtures__/openai-file-search-tool.2.chunks.txt +0 -93
  34. package/src/responses/__fixtures__/openai-file-search-tool.2.json +0 -112
  35. package/src/responses/__fixtures__/openai-image-generation-tool.1.chunks.txt +0 -16
  36. package/src/responses/__fixtures__/openai-image-generation-tool.1.json +0 -96
  37. package/src/responses/__fixtures__/openai-local-shell-tool.1.chunks.txt +0 -7
  38. package/src/responses/__fixtures__/openai-local-shell-tool.1.json +0 -70
  39. package/src/responses/__fixtures__/openai-mcp-tool-approval.1.chunks.txt +0 -11
  40. package/src/responses/__fixtures__/openai-mcp-tool-approval.1.json +0 -169
  41. package/src/responses/__fixtures__/openai-mcp-tool-approval.2.chunks.txt +0 -123
  42. package/src/responses/__fixtures__/openai-mcp-tool-approval.2.json +0 -176
  43. package/src/responses/__fixtures__/openai-mcp-tool-approval.3.chunks.txt +0 -11
  44. package/src/responses/__fixtures__/openai-mcp-tool-approval.3.json +0 -169
  45. package/src/responses/__fixtures__/openai-mcp-tool-approval.4.chunks.txt +0 -84
  46. package/src/responses/__fixtures__/openai-mcp-tool-approval.4.json +0 -182
  47. package/src/responses/__fixtures__/openai-mcp-tool.1.chunks.txt +0 -373
  48. package/src/responses/__fixtures__/openai-mcp-tool.1.json +0 -159
  49. package/src/responses/__fixtures__/openai-reasoning-encrypted-content.1.chunks.txt +0 -110
  50. package/src/responses/__fixtures__/openai-reasoning-encrypted-content.1.json +0 -117
  51. package/src/responses/__fixtures__/openai-shell-tool.1.chunks.txt +0 -182
  52. package/src/responses/__fixtures__/openai-shell-tool.1.json +0 -73
  53. package/src/responses/__fixtures__/openai-web-search-tool.1.chunks.txt +0 -185
  54. package/src/responses/__fixtures__/openai-web-search-tool.1.json +0 -266
  55. package/src/responses/__snapshots__/openai-responses-language-model.test.ts.snap +0 -10955
  56. package/src/responses/convert-to-openai-responses-input.test.ts +0 -2976
  57. package/src/responses/openai-responses-api.test.ts +0 -89
  58. package/src/responses/openai-responses-language-model.test.ts +0 -6927
  59. package/src/responses/openai-responses-prepare-tools.test.ts +0 -924
  60. package/src/speech/openai-speech-model.test.ts +0 -202
  61. package/src/tool/local-shell.test-d.ts +0 -20
  62. package/src/tool/web-search.test-d.ts +0 -13
  63. package/src/transcription/openai-transcription-model.test.ts +0 -507
@@ -1,3496 +0,0 @@
1
- import fs from 'node:fs';
2
-
3
- import { LanguageModelV3Prompt } from '@ai-sdk/provider';
4
- import { createTestServer } from '@ai-sdk/test-server/with-vitest';
5
- import {
6
- convertReadableStreamToArray,
7
- isNodeVersion,
8
- } from '@ai-sdk/provider-utils/test';
9
- import { createOpenAI } from '../openai-provider';
10
- import { describe, it, expect, vi } from 'vitest';
11
-
12
- vi.mock('../version', () => ({
13
- VERSION: '0.0.0-test',
14
- }));
15
-
16
- const TEST_PROMPT: LanguageModelV3Prompt = [
17
- { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
18
- ];
19
-
20
- const TEST_LOGPROBS = {
21
- content: [
22
- {
23
- token: 'Hello',
24
- logprob: -0.0009994634,
25
- top_logprobs: [
26
- {
27
- token: 'Hello',
28
- logprob: -0.0009994634,
29
- },
30
- ],
31
- },
32
- {
33
- token: '!',
34
- logprob: -0.13410144,
35
- top_logprobs: [
36
- {
37
- token: '!',
38
- logprob: -0.13410144,
39
- },
40
- ],
41
- },
42
- {
43
- token: ' How',
44
- logprob: -0.0009250381,
45
- top_logprobs: [
46
- {
47
- token: ' How',
48
- logprob: -0.0009250381,
49
- },
50
- ],
51
- },
52
- {
53
- token: ' can',
54
- logprob: -0.047709424,
55
- top_logprobs: [
56
- {
57
- token: ' can',
58
- logprob: -0.047709424,
59
- },
60
- ],
61
- },
62
- {
63
- token: ' I',
64
- logprob: -0.000009014684,
65
- top_logprobs: [
66
- {
67
- token: ' I',
68
- logprob: -0.000009014684,
69
- },
70
- ],
71
- },
72
- {
73
- token: ' assist',
74
- logprob: -0.009125131,
75
- top_logprobs: [
76
- {
77
- token: ' assist',
78
- logprob: -0.009125131,
79
- },
80
- ],
81
- },
82
- {
83
- token: ' you',
84
- logprob: -0.0000066306106,
85
- top_logprobs: [
86
- {
87
- token: ' you',
88
- logprob: -0.0000066306106,
89
- },
90
- ],
91
- },
92
- {
93
- token: ' today',
94
- logprob: -0.00011093382,
95
- top_logprobs: [
96
- {
97
- token: ' today',
98
- logprob: -0.00011093382,
99
- },
100
- ],
101
- },
102
- {
103
- token: '?',
104
- logprob: -0.00004596782,
105
- top_logprobs: [
106
- {
107
- token: '?',
108
- logprob: -0.00004596782,
109
- },
110
- ],
111
- },
112
- ],
113
- };
114
-
115
- const provider = createOpenAI({
116
- apiKey: 'test-api-key',
117
- });
118
-
119
- const model = provider.chat('gpt-3.5-turbo');
120
-
121
- const server = createTestServer({
122
- 'https://api.openai.com/v1/chat/completions': {},
123
- });
124
-
125
- function prepareChunksFixtureResponse(filename: string) {
126
- const chunks = fs
127
- .readFileSync(`src/chat/__fixtures__/${filename}.chunks.txt`, 'utf8')
128
- .split('\n')
129
- .map(line => `data: ${line}\n\n`);
130
- chunks.push('data: [DONE]\n\n');
131
-
132
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
133
- type: 'stream-chunks',
134
- chunks,
135
- };
136
- }
137
-
138
- describe('doGenerate', () => {
139
- function prepareJsonResponse({
140
- content = '',
141
- tool_calls,
142
- function_call,
143
- annotations,
144
- usage = {
145
- prompt_tokens: 4,
146
- total_tokens: 34,
147
- completion_tokens: 30,
148
- },
149
- finish_reason = 'stop',
150
- id = 'chatcmpl-95ZTZkhr0mHNKqerQfiwkuox3PHAd',
151
- created = 1711115037,
152
- model = 'gpt-3.5-turbo-0125',
153
- logprobs = null,
154
- headers,
155
- }: {
156
- content?: string;
157
- tool_calls?: Array<{
158
- id: string;
159
- type: 'function';
160
- function: {
161
- name: string;
162
- arguments: string;
163
- };
164
- }>;
165
- function_call?: {
166
- name: string;
167
- arguments: string;
168
- };
169
- annotations?: Array<{
170
- type: 'url_citation';
171
- url_citation: {
172
- start_index: number;
173
- end_index: number;
174
- url: string;
175
- title: string;
176
- };
177
- }>;
178
- logprobs?: {
179
- content:
180
- | {
181
- token: string;
182
- logprob: number;
183
- top_logprobs: { token: string; logprob: number }[];
184
- }[]
185
- | null;
186
- } | null;
187
- usage?: {
188
- prompt_tokens?: number;
189
- total_tokens?: number;
190
- completion_tokens?: number;
191
- completion_tokens_details?: {
192
- reasoning_tokens?: number;
193
- accepted_prediction_tokens?: number;
194
- rejected_prediction_tokens?: number;
195
- };
196
- prompt_tokens_details?: {
197
- cached_tokens?: number;
198
- };
199
- };
200
- finish_reason?: string;
201
- created?: number;
202
- id?: string;
203
- model?: string;
204
- headers?: Record<string, string>;
205
- } = {}) {
206
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
207
- type: 'json-value',
208
- headers,
209
- body: {
210
- id,
211
- object: 'chat.completion',
212
- created,
213
- model,
214
- choices: [
215
- {
216
- index: 0,
217
- message: {
218
- role: 'assistant',
219
- content,
220
- tool_calls,
221
- function_call,
222
- annotations,
223
- },
224
- ...(logprobs ? { logprobs } : {}),
225
- finish_reason,
226
- },
227
- ],
228
- usage,
229
- system_fingerprint: 'fp_3bc1b5746c',
230
- },
231
- };
232
- }
233
-
234
- it('should extract text response', async () => {
235
- prepareJsonResponse({ content: 'Hello, World!' });
236
-
237
- const result = await model.doGenerate({
238
- prompt: TEST_PROMPT,
239
- });
240
-
241
- expect(result.content).toMatchInlineSnapshot(`
242
- [
243
- {
244
- "text": "Hello, World!",
245
- "type": "text",
246
- },
247
- ]
248
- `);
249
- });
250
-
251
- it('should extract usage', async () => {
252
- prepareJsonResponse({
253
- usage: { prompt_tokens: 20, total_tokens: 25, completion_tokens: 5 },
254
- });
255
-
256
- const { usage } = await model.doGenerate({
257
- prompt: TEST_PROMPT,
258
- });
259
-
260
- expect(usage).toMatchInlineSnapshot(`
261
- {
262
- "inputTokens": {
263
- "cacheRead": 0,
264
- "cacheWrite": undefined,
265
- "noCache": 20,
266
- "total": 20,
267
- },
268
- "outputTokens": {
269
- "reasoning": 0,
270
- "text": 5,
271
- "total": 5,
272
- },
273
- "raw": {
274
- "completion_tokens": 5,
275
- "prompt_tokens": 20,
276
- "total_tokens": 25,
277
- },
278
- }
279
- `);
280
- });
281
-
282
- it('should send request body', async () => {
283
- prepareJsonResponse({});
284
-
285
- const { request } = await model.doGenerate({
286
- prompt: TEST_PROMPT,
287
- });
288
-
289
- expect(request).toMatchInlineSnapshot(`
290
- {
291
- "body": {
292
- "frequency_penalty": undefined,
293
- "logit_bias": undefined,
294
- "logprobs": undefined,
295
- "max_completion_tokens": undefined,
296
- "max_tokens": undefined,
297
- "messages": [
298
- {
299
- "content": "Hello",
300
- "role": "user",
301
- },
302
- ],
303
- "metadata": undefined,
304
- "model": "gpt-3.5-turbo",
305
- "parallel_tool_calls": undefined,
306
- "prediction": undefined,
307
- "presence_penalty": undefined,
308
- "prompt_cache_key": undefined,
309
- "prompt_cache_retention": undefined,
310
- "reasoning_effort": undefined,
311
- "response_format": undefined,
312
- "safety_identifier": undefined,
313
- "seed": undefined,
314
- "service_tier": undefined,
315
- "stop": undefined,
316
- "store": undefined,
317
- "temperature": undefined,
318
- "tool_choice": undefined,
319
- "tools": undefined,
320
- "top_logprobs": undefined,
321
- "top_p": undefined,
322
- "user": undefined,
323
- "verbosity": undefined,
324
- },
325
- }
326
- `);
327
- });
328
-
329
- it('should send additional response information', async () => {
330
- prepareJsonResponse({
331
- id: 'test-id',
332
- created: 123,
333
- model: 'test-model',
334
- });
335
-
336
- const { response } = await model.doGenerate({
337
- prompt: TEST_PROMPT,
338
- });
339
-
340
- expect(response).toMatchInlineSnapshot(`
341
- {
342
- "body": {
343
- "choices": [
344
- {
345
- "finish_reason": "stop",
346
- "index": 0,
347
- "message": {
348
- "content": "",
349
- "role": "assistant",
350
- },
351
- },
352
- ],
353
- "created": 123,
354
- "id": "test-id",
355
- "model": "test-model",
356
- "object": "chat.completion",
357
- "system_fingerprint": "fp_3bc1b5746c",
358
- "usage": {
359
- "completion_tokens": 30,
360
- "prompt_tokens": 4,
361
- "total_tokens": 34,
362
- },
363
- },
364
- "headers": {
365
- "content-length": "275",
366
- "content-type": "application/json",
367
- },
368
- "id": "test-id",
369
- "modelId": "test-model",
370
- "timestamp": 1970-01-01T00:02:03.000Z,
371
- }
372
- `);
373
- });
374
-
375
- it('should support partial usage', async () => {
376
- prepareJsonResponse({
377
- usage: { prompt_tokens: 20, total_tokens: 20 },
378
- });
379
-
380
- const { usage } = await model.doGenerate({
381
- prompt: TEST_PROMPT,
382
- });
383
-
384
- expect(usage).toMatchInlineSnapshot(`
385
- {
386
- "inputTokens": {
387
- "cacheRead": 0,
388
- "cacheWrite": undefined,
389
- "noCache": 20,
390
- "total": 20,
391
- },
392
- "outputTokens": {
393
- "reasoning": 0,
394
- "text": 0,
395
- "total": 0,
396
- },
397
- "raw": {
398
- "prompt_tokens": 20,
399
- "total_tokens": 20,
400
- },
401
- }
402
- `);
403
- });
404
-
405
- it('should extract logprobs', async () => {
406
- prepareJsonResponse({
407
- logprobs: TEST_LOGPROBS,
408
- });
409
-
410
- const response = await provider.chat('gpt-3.5-turbo').doGenerate({
411
- prompt: TEST_PROMPT,
412
- providerOptions: {
413
- openai: {
414
- logprobs: 1,
415
- },
416
- },
417
- });
418
- expect(response.providerMetadata?.openai.logprobs).toStrictEqual(
419
- TEST_LOGPROBS.content,
420
- );
421
- });
422
-
423
- it('should extract finish reason', async () => {
424
- prepareJsonResponse({
425
- finish_reason: 'stop',
426
- });
427
-
428
- const response = await model.doGenerate({
429
- prompt: TEST_PROMPT,
430
- });
431
-
432
- expect(response.finishReason).toMatchInlineSnapshot(`
433
- {
434
- "raw": "stop",
435
- "unified": "stop",
436
- }
437
- `);
438
- });
439
-
440
- it('should support unknown finish reason', async () => {
441
- prepareJsonResponse({
442
- finish_reason: 'eos',
443
- });
444
-
445
- const response = await model.doGenerate({
446
- prompt: TEST_PROMPT,
447
- });
448
-
449
- expect(response.finishReason).toMatchInlineSnapshot(`
450
- {
451
- "raw": "eos",
452
- "unified": "other",
453
- }
454
- `);
455
- });
456
-
457
- it('should expose the raw response headers', async () => {
458
- prepareJsonResponse({
459
- headers: { 'test-header': 'test-value' },
460
- });
461
-
462
- const { response } = await model.doGenerate({
463
- prompt: TEST_PROMPT,
464
- });
465
-
466
- expect(response?.headers).toMatchInlineSnapshot(`
467
- {
468
- "content-length": "321",
469
- "content-type": "application/json",
470
- "test-header": "test-value",
471
- }
472
- `);
473
- });
474
-
475
- it('should pass the model and the messages', async () => {
476
- prepareJsonResponse({ content: '' });
477
-
478
- await model.doGenerate({
479
- prompt: TEST_PROMPT,
480
- });
481
-
482
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
483
- model: 'gpt-3.5-turbo',
484
- messages: [{ role: 'user', content: 'Hello' }],
485
- });
486
- });
487
-
488
- it('should pass settings', async () => {
489
- prepareJsonResponse();
490
-
491
- await provider.chat('gpt-3.5-turbo').doGenerate({
492
- prompt: TEST_PROMPT,
493
- providerOptions: {
494
- openai: {
495
- logitBias: { 50256: -100 },
496
- parallelToolCalls: false,
497
- user: 'test-user-id',
498
- },
499
- },
500
- });
501
-
502
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
503
- {
504
- "logit_bias": {
505
- "50256": -100,
506
- },
507
- "messages": [
508
- {
509
- "content": "Hello",
510
- "role": "user",
511
- },
512
- ],
513
- "model": "gpt-3.5-turbo",
514
- "parallel_tool_calls": false,
515
- "user": "test-user-id",
516
- }
517
- `);
518
- });
519
-
520
- it('should pass reasoningEffort setting from provider metadata', async () => {
521
- prepareJsonResponse({ content: '' });
522
-
523
- const model = provider.chat('o4-mini');
524
-
525
- await model.doGenerate({
526
- prompt: TEST_PROMPT,
527
- providerOptions: {
528
- openai: { reasoningEffort: 'low' },
529
- },
530
- });
531
-
532
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
533
- model: 'o4-mini',
534
- messages: [{ role: 'user', content: 'Hello' }],
535
- reasoning_effort: 'low',
536
- });
537
- });
538
-
539
- it('should pass reasoningEffort setting from settings', async () => {
540
- prepareJsonResponse({ content: '' });
541
-
542
- const model = provider.chat('o4-mini');
543
-
544
- await model.doGenerate({
545
- prompt: TEST_PROMPT,
546
- providerOptions: {
547
- openai: { reasoningEffort: 'high' },
548
- },
549
- });
550
-
551
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
552
- model: 'o4-mini',
553
- messages: [{ role: 'user', content: 'Hello' }],
554
- reasoning_effort: 'high',
555
- });
556
- });
557
-
558
- it('should pass reasoningEffort xhigh setting', async () => {
559
- prepareJsonResponse({ content: '' });
560
-
561
- const model = provider.chat('gpt-5.1-codex-max');
562
-
563
- await model.doGenerate({
564
- prompt: TEST_PROMPT,
565
- providerOptions: {
566
- openai: { reasoningEffort: 'xhigh' },
567
- },
568
- });
569
-
570
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
571
- model: 'gpt-5.1-codex-max',
572
- messages: [{ role: 'user', content: 'Hello' }],
573
- reasoning_effort: 'xhigh',
574
- });
575
- });
576
-
577
- it('should pass textVerbosity setting from provider options', async () => {
578
- prepareJsonResponse({ content: '' });
579
-
580
- const model = provider.chat('gpt-4o');
581
-
582
- await model.doGenerate({
583
- prompt: TEST_PROMPT,
584
- providerOptions: {
585
- openai: { textVerbosity: 'low' },
586
- },
587
- });
588
-
589
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
590
- model: 'gpt-4o',
591
- messages: [{ role: 'user', content: 'Hello' }],
592
- verbosity: 'low',
593
- });
594
- });
595
-
596
- it('should pass tools and toolChoice', async () => {
597
- prepareJsonResponse({ content: '' });
598
-
599
- await model.doGenerate({
600
- tools: [
601
- {
602
- type: 'function',
603
- name: 'test-tool',
604
- inputSchema: {
605
- type: 'object',
606
- properties: { value: { type: 'string' } },
607
- required: ['value'],
608
- additionalProperties: false,
609
- $schema: 'http://json-schema.org/draft-07/schema#',
610
- },
611
- },
612
- ],
613
- toolChoice: {
614
- type: 'tool',
615
- toolName: 'test-tool',
616
- },
617
- prompt: TEST_PROMPT,
618
- });
619
-
620
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
621
- {
622
- "messages": [
623
- {
624
- "content": "Hello",
625
- "role": "user",
626
- },
627
- ],
628
- "model": "gpt-3.5-turbo",
629
- "tool_choice": {
630
- "function": {
631
- "name": "test-tool",
632
- },
633
- "type": "function",
634
- },
635
- "tools": [
636
- {
637
- "function": {
638
- "name": "test-tool",
639
- "parameters": {
640
- "$schema": "http://json-schema.org/draft-07/schema#",
641
- "additionalProperties": false,
642
- "properties": {
643
- "value": {
644
- "type": "string",
645
- },
646
- },
647
- "required": [
648
- "value",
649
- ],
650
- "type": "object",
651
- },
652
- },
653
- "type": "function",
654
- },
655
- ],
656
- }
657
- `);
658
- });
659
-
660
- it('should pass headers', async () => {
661
- prepareJsonResponse({ content: '' });
662
-
663
- const provider = createOpenAI({
664
- apiKey: 'test-api-key',
665
- organization: 'test-organization',
666
- project: 'test-project',
667
- headers: {
668
- 'Custom-Provider-Header': 'provider-header-value',
669
- },
670
- });
671
-
672
- await provider.chat('gpt-3.5-turbo').doGenerate({
673
- prompt: TEST_PROMPT,
674
- headers: {
675
- 'Custom-Request-Header': 'request-header-value',
676
- },
677
- });
678
-
679
- expect(server.calls[0].requestHeaders).toStrictEqual({
680
- authorization: 'Bearer test-api-key',
681
- 'content-type': 'application/json',
682
- 'custom-provider-header': 'provider-header-value',
683
- 'custom-request-header': 'request-header-value',
684
- 'openai-organization': 'test-organization',
685
- 'openai-project': 'test-project',
686
- });
687
- expect(server.calls[0].requestUserAgent).toContain(
688
- `ai-sdk/openai/0.0.0-test`,
689
- );
690
- });
691
-
692
- it('should parse tool results', async () => {
693
- prepareJsonResponse({
694
- tool_calls: [
695
- {
696
- id: 'call_O17Uplv4lJvD6DVdIvFFeRMw',
697
- type: 'function',
698
- function: {
699
- name: 'test-tool',
700
- arguments: '{"value":"Spark"}',
701
- },
702
- },
703
- ],
704
- });
705
-
706
- const result = await model.doGenerate({
707
- tools: [
708
- {
709
- type: 'function',
710
- name: 'test-tool',
711
- inputSchema: {
712
- type: 'object',
713
- properties: { value: { type: 'string' } },
714
- required: ['value'],
715
- additionalProperties: false,
716
- $schema: 'http://json-schema.org/draft-07/schema#',
717
- },
718
- },
719
- ],
720
- toolChoice: {
721
- type: 'tool',
722
- toolName: 'test-tool',
723
- },
724
- prompt: TEST_PROMPT,
725
- });
726
-
727
- expect(result.content).toMatchInlineSnapshot(`
728
- [
729
- {
730
- "input": "{"value":"Spark"}",
731
- "toolCallId": "call_O17Uplv4lJvD6DVdIvFFeRMw",
732
- "toolName": "test-tool",
733
- "type": "tool-call",
734
- },
735
- ]
736
- `);
737
- });
738
-
739
- it('should parse annotations/citations', async () => {
740
- prepareJsonResponse({
741
- content: 'Based on the search results [doc1], I found information.',
742
- annotations: [
743
- {
744
- type: 'url_citation',
745
- url_citation: {
746
- start_index: 24,
747
- end_index: 29,
748
- url: 'https://example.com/doc1.pdf',
749
- title: 'Document 1',
750
- },
751
- },
752
- ],
753
- });
754
-
755
- const result = await model.doGenerate({
756
- prompt: TEST_PROMPT,
757
- });
758
-
759
- expect(result.content).toEqual([
760
- {
761
- text: 'Based on the search results [doc1], I found information.',
762
- type: 'text',
763
- },
764
- {
765
- id: expect.any(String),
766
- sourceType: 'url',
767
- title: 'Document 1',
768
- type: 'source',
769
- url: 'https://example.com/doc1.pdf',
770
- },
771
- ]);
772
- });
773
-
774
- describe('response format', () => {
775
- it('should not send a response_format when response format is text', async () => {
776
- prepareJsonResponse({ content: '{"value":"Spark"}' });
777
-
778
- const model = provider.chat('gpt-4o-2024-08-06');
779
-
780
- await model.doGenerate({
781
- prompt: TEST_PROMPT,
782
- responseFormat: { type: 'text' },
783
- });
784
-
785
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
786
- model: 'gpt-4o-2024-08-06',
787
- messages: [{ role: 'user', content: 'Hello' }],
788
- });
789
- });
790
-
791
- it('should forward json response format as "json_object" without schema', async () => {
792
- prepareJsonResponse({ content: '{"value":"Spark"}' });
793
-
794
- const model = provider.chat('gpt-4o-2024-08-06');
795
-
796
- await model.doGenerate({
797
- prompt: TEST_PROMPT,
798
- responseFormat: { type: 'json' },
799
- });
800
-
801
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
802
- model: 'gpt-4o-2024-08-06',
803
- messages: [{ role: 'user', content: 'Hello' }],
804
- response_format: { type: 'json_object' },
805
- });
806
- });
807
-
808
- it('should forward json response format as "json_object" and include schema', async () => {
809
- prepareJsonResponse({ content: '{"value":"Spark"}' });
810
-
811
- const model = provider.chat('gpt-4o-2024-08-06');
812
-
813
- const { warnings } = await model.doGenerate({
814
- prompt: TEST_PROMPT,
815
- responseFormat: {
816
- type: 'json',
817
- schema: {
818
- type: 'object',
819
- properties: { value: { type: 'string' } },
820
- required: ['value'],
821
- additionalProperties: false,
822
- $schema: 'http://json-schema.org/draft-07/schema#',
823
- },
824
- },
825
- });
826
-
827
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
828
- {
829
- "messages": [
830
- {
831
- "content": "Hello",
832
- "role": "user",
833
- },
834
- ],
835
- "model": "gpt-4o-2024-08-06",
836
- "response_format": {
837
- "json_schema": {
838
- "name": "response",
839
- "schema": {
840
- "$schema": "http://json-schema.org/draft-07/schema#",
841
- "additionalProperties": false,
842
- "properties": {
843
- "value": {
844
- "type": "string",
845
- },
846
- },
847
- "required": [
848
- "value",
849
- ],
850
- "type": "object",
851
- },
852
- "strict": true,
853
- },
854
- "type": "json_schema",
855
- },
856
- }
857
- `);
858
-
859
- expect(warnings).toEqual([]);
860
- });
861
-
862
- it('should use json_schema & strict with responseFormat json', async () => {
863
- prepareJsonResponse({ content: '{"value":"Spark"}' });
864
-
865
- const model = provider.chat('gpt-4o-2024-08-06');
866
-
867
- await model.doGenerate({
868
- responseFormat: {
869
- type: 'json',
870
- schema: {
871
- type: 'object',
872
- properties: { value: { type: 'string' } },
873
- required: ['value'],
874
- additionalProperties: false,
875
- $schema: 'http://json-schema.org/draft-07/schema#',
876
- },
877
- },
878
- prompt: TEST_PROMPT,
879
- });
880
-
881
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
882
- {
883
- "messages": [
884
- {
885
- "content": "Hello",
886
- "role": "user",
887
- },
888
- ],
889
- "model": "gpt-4o-2024-08-06",
890
- "response_format": {
891
- "json_schema": {
892
- "name": "response",
893
- "schema": {
894
- "$schema": "http://json-schema.org/draft-07/schema#",
895
- "additionalProperties": false,
896
- "properties": {
897
- "value": {
898
- "type": "string",
899
- },
900
- },
901
- "required": [
902
- "value",
903
- ],
904
- "type": "object",
905
- },
906
- "strict": true,
907
- },
908
- "type": "json_schema",
909
- },
910
- }
911
- `);
912
- });
913
-
914
- it('should set name & description with responseFormat json', async () => {
915
- prepareJsonResponse({ content: '{"value":"Spark"}' });
916
-
917
- const model = provider.chat('gpt-4o-2024-08-06');
918
-
919
- await model.doGenerate({
920
- responseFormat: {
921
- type: 'json',
922
- name: 'test-name',
923
- description: 'test description',
924
- schema: {
925
- type: 'object',
926
- properties: { value: { type: 'string' } },
927
- required: ['value'],
928
- additionalProperties: false,
929
- $schema: 'http://json-schema.org/draft-07/schema#',
930
- },
931
- },
932
- prompt: TEST_PROMPT,
933
- });
934
-
935
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
936
- {
937
- "messages": [
938
- {
939
- "content": "Hello",
940
- "role": "user",
941
- },
942
- ],
943
- "model": "gpt-4o-2024-08-06",
944
- "response_format": {
945
- "json_schema": {
946
- "description": "test description",
947
- "name": "test-name",
948
- "schema": {
949
- "$schema": "http://json-schema.org/draft-07/schema#",
950
- "additionalProperties": false,
951
- "properties": {
952
- "value": {
953
- "type": "string",
954
- },
955
- },
956
- "required": [
957
- "value",
958
- ],
959
- "type": "object",
960
- },
961
- "strict": true,
962
- },
963
- "type": "json_schema",
964
- },
965
- }
966
- `);
967
- });
968
-
969
- it('should allow for undefined schema with responseFormat json when structuredOutputs are enabled', async () => {
970
- prepareJsonResponse({ content: '{"value":"Spark"}' });
971
-
972
- const model = provider.chat('gpt-4o-2024-08-06');
973
-
974
- await model.doGenerate({
975
- responseFormat: {
976
- type: 'json',
977
- name: 'test-name',
978
- description: 'test description',
979
- },
980
- prompt: TEST_PROMPT,
981
- });
982
-
983
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
984
- model: 'gpt-4o-2024-08-06',
985
- messages: [{ role: 'user', content: 'Hello' }],
986
- response_format: {
987
- type: 'json_object',
988
- },
989
- });
990
- });
991
-
992
- it('should set strict with tool call', async () => {
993
- prepareJsonResponse({
994
- tool_calls: [
995
- {
996
- id: 'call_O17Uplv4lJvD6DVdIvFFeRMw',
997
- type: 'function',
998
- function: {
999
- name: 'test-tool',
1000
- arguments: '{"value":"Spark"}',
1001
- },
1002
- },
1003
- ],
1004
- });
1005
-
1006
- const model = provider.chat('gpt-4o-2024-08-06');
1007
-
1008
- const result = await model.doGenerate({
1009
- tools: [
1010
- {
1011
- type: 'function',
1012
- name: 'test-tool',
1013
- description: 'test description',
1014
- inputSchema: {
1015
- type: 'object',
1016
- properties: { value: { type: 'string' } },
1017
- required: ['value'],
1018
- additionalProperties: false,
1019
- $schema: 'http://json-schema.org/draft-07/schema#',
1020
- },
1021
- },
1022
- ],
1023
- toolChoice: { type: 'required' },
1024
- prompt: TEST_PROMPT,
1025
- });
1026
-
1027
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
1028
- {
1029
- "messages": [
1030
- {
1031
- "content": "Hello",
1032
- "role": "user",
1033
- },
1034
- ],
1035
- "model": "gpt-4o-2024-08-06",
1036
- "tool_choice": "required",
1037
- "tools": [
1038
- {
1039
- "function": {
1040
- "description": "test description",
1041
- "name": "test-tool",
1042
- "parameters": {
1043
- "$schema": "http://json-schema.org/draft-07/schema#",
1044
- "additionalProperties": false,
1045
- "properties": {
1046
- "value": {
1047
- "type": "string",
1048
- },
1049
- },
1050
- "required": [
1051
- "value",
1052
- ],
1053
- "type": "object",
1054
- },
1055
- },
1056
- "type": "function",
1057
- },
1058
- ],
1059
- }
1060
- `);
1061
-
1062
- expect(result.content).toMatchInlineSnapshot(`
1063
- [
1064
- {
1065
- "input": "{"value":"Spark"}",
1066
- "toolCallId": "call_O17Uplv4lJvD6DVdIvFFeRMw",
1067
- "toolName": "test-tool",
1068
- "type": "tool-call",
1069
- },
1070
- ]
1071
- `);
1072
- });
1073
- });
1074
-
1075
- it('should set strict for tool usage', async () => {
1076
- prepareJsonResponse({
1077
- tool_calls: [
1078
- {
1079
- id: 'call_O17Uplv4lJvD6DVdIvFFeRMw',
1080
- type: 'function',
1081
- function: {
1082
- name: 'test-tool',
1083
- arguments: '{"value":"Spark"}',
1084
- },
1085
- },
1086
- ],
1087
- });
1088
-
1089
- const model = provider.chat('gpt-4o-2024-08-06');
1090
-
1091
- const result = await model.doGenerate({
1092
- tools: [
1093
- {
1094
- type: 'function',
1095
- name: 'test-tool',
1096
- inputSchema: {
1097
- type: 'object',
1098
- properties: { value: { type: 'string' } },
1099
- required: ['value'],
1100
- additionalProperties: false,
1101
- $schema: 'http://json-schema.org/draft-07/schema#',
1102
- },
1103
- },
1104
- ],
1105
- toolChoice: {
1106
- type: 'tool',
1107
- toolName: 'test-tool',
1108
- },
1109
- prompt: TEST_PROMPT,
1110
- });
1111
-
1112
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
1113
- {
1114
- "messages": [
1115
- {
1116
- "content": "Hello",
1117
- "role": "user",
1118
- },
1119
- ],
1120
- "model": "gpt-4o-2024-08-06",
1121
- "tool_choice": {
1122
- "function": {
1123
- "name": "test-tool",
1124
- },
1125
- "type": "function",
1126
- },
1127
- "tools": [
1128
- {
1129
- "function": {
1130
- "name": "test-tool",
1131
- "parameters": {
1132
- "$schema": "http://json-schema.org/draft-07/schema#",
1133
- "additionalProperties": false,
1134
- "properties": {
1135
- "value": {
1136
- "type": "string",
1137
- },
1138
- },
1139
- "required": [
1140
- "value",
1141
- ],
1142
- "type": "object",
1143
- },
1144
- },
1145
- "type": "function",
1146
- },
1147
- ],
1148
- }
1149
- `);
1150
-
1151
- expect(result.content).toMatchInlineSnapshot(`
1152
- [
1153
- {
1154
- "input": "{"value":"Spark"}",
1155
- "toolCallId": "call_O17Uplv4lJvD6DVdIvFFeRMw",
1156
- "toolName": "test-tool",
1157
- "type": "tool-call",
1158
- },
1159
- ]
1160
- `);
1161
- });
1162
-
1163
- it('should return cached_tokens in prompt_details_tokens', async () => {
1164
- prepareJsonResponse({
1165
- usage: {
1166
- prompt_tokens: 15,
1167
- completion_tokens: 20,
1168
- total_tokens: 35,
1169
- prompt_tokens_details: {
1170
- cached_tokens: 1152,
1171
- },
1172
- },
1173
- });
1174
-
1175
- const model = provider.chat('gpt-4o-mini');
1176
-
1177
- const result = await model.doGenerate({
1178
- prompt: TEST_PROMPT,
1179
- });
1180
-
1181
- expect(result.usage).toMatchInlineSnapshot(`
1182
- {
1183
- "inputTokens": {
1184
- "cacheRead": 1152,
1185
- "cacheWrite": undefined,
1186
- "noCache": -1137,
1187
- "total": 15,
1188
- },
1189
- "outputTokens": {
1190
- "reasoning": 0,
1191
- "text": 20,
1192
- "total": 20,
1193
- },
1194
- "raw": {
1195
- "completion_tokens": 20,
1196
- "prompt_tokens": 15,
1197
- "prompt_tokens_details": {
1198
- "cached_tokens": 1152,
1199
- },
1200
- "total_tokens": 35,
1201
- },
1202
- }
1203
- `);
1204
- });
1205
-
1206
- it('should return accepted_prediction_tokens and rejected_prediction_tokens in completion_details_tokens', async () => {
1207
- prepareJsonResponse({
1208
- usage: {
1209
- prompt_tokens: 15,
1210
- completion_tokens: 20,
1211
- total_tokens: 35,
1212
- completion_tokens_details: {
1213
- accepted_prediction_tokens: 123,
1214
- rejected_prediction_tokens: 456,
1215
- },
1216
- },
1217
- });
1218
-
1219
- const model = provider.chat('gpt-4o-mini');
1220
-
1221
- const result = await model.doGenerate({
1222
- prompt: TEST_PROMPT,
1223
- });
1224
-
1225
- expect(result.providerMetadata).toStrictEqual({
1226
- openai: {
1227
- acceptedPredictionTokens: 123,
1228
- rejectedPredictionTokens: 456,
1229
- },
1230
- });
1231
- });
1232
-
1233
- describe('reasoning models', () => {
1234
- it('should clear out temperature, top_p, frequency_penalty, presence_penalty and return warnings', async () => {
1235
- prepareJsonResponse();
1236
-
1237
- const model = provider.chat('o4-mini');
1238
-
1239
- const result = await model.doGenerate({
1240
- prompt: TEST_PROMPT,
1241
- temperature: 0.5,
1242
- topP: 0.7,
1243
- frequencyPenalty: 0.2,
1244
- presencePenalty: 0.3,
1245
- });
1246
-
1247
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1248
- model: 'o4-mini',
1249
- messages: [{ role: 'user', content: 'Hello' }],
1250
- });
1251
-
1252
- expect(result.warnings).toMatchInlineSnapshot(`
1253
- [
1254
- {
1255
- "details": "temperature is not supported for reasoning models",
1256
- "feature": "temperature",
1257
- "type": "unsupported",
1258
- },
1259
- {
1260
- "details": "topP is not supported for reasoning models",
1261
- "feature": "topP",
1262
- "type": "unsupported",
1263
- },
1264
- {
1265
- "details": "frequencyPenalty is not supported for reasoning models",
1266
- "feature": "frequencyPenalty",
1267
- "type": "unsupported",
1268
- },
1269
- {
1270
- "details": "presencePenalty is not supported for reasoning models",
1271
- "feature": "presencePenalty",
1272
- "type": "unsupported",
1273
- },
1274
- ]
1275
- `);
1276
- });
1277
-
1278
- it('should convert maxOutputTokens to max_completion_tokens', async () => {
1279
- prepareJsonResponse();
1280
-
1281
- const model = provider.chat('o4-mini');
1282
-
1283
- await model.doGenerate({
1284
- prompt: TEST_PROMPT,
1285
- maxOutputTokens: 1000,
1286
- });
1287
-
1288
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1289
- model: 'o4-mini',
1290
- messages: [{ role: 'user', content: 'Hello' }],
1291
- max_completion_tokens: 1000,
1292
- });
1293
- });
1294
- });
1295
-
1296
- it('should allow forcing reasoning behavior for unrecognized model IDs via providerOptions', async () => {
1297
- prepareJsonResponse();
1298
-
1299
- const model = provider.chat('stealth-reasoning-model');
1300
-
1301
- const result = await model.doGenerate({
1302
- prompt: TEST_PROMPT,
1303
- temperature: 0.5,
1304
- topP: 0.7,
1305
- providerOptions: {
1306
- openai: {
1307
- forceReasoning: true,
1308
- },
1309
- },
1310
- });
1311
-
1312
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1313
- model: 'stealth-reasoning-model',
1314
- messages: [{ role: 'user', content: 'Hello' }],
1315
- });
1316
-
1317
- expect(result.warnings).toMatchInlineSnapshot(`
1318
- [
1319
- {
1320
- "details": "temperature is not supported for reasoning models",
1321
- "feature": "temperature",
1322
- "type": "unsupported",
1323
- },
1324
- {
1325
- "details": "topP is not supported for reasoning models",
1326
- "feature": "topP",
1327
- "type": "unsupported",
1328
- },
1329
- ]
1330
- `);
1331
- });
1332
-
1333
- it('should default systemMessageMode to developer when forcing reasoning', async () => {
1334
- prepareJsonResponse();
1335
-
1336
- const model = provider.chat('stealth-reasoning-model');
1337
-
1338
- const result = await model.doGenerate({
1339
- prompt: [
1340
- { role: 'system', content: 'You are a helpful assistant.' },
1341
- { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
1342
- ],
1343
- providerOptions: {
1344
- openai: {
1345
- forceReasoning: true,
1346
- },
1347
- },
1348
- });
1349
-
1350
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1351
- model: 'stealth-reasoning-model',
1352
- messages: [
1353
- { role: 'developer', content: 'You are a helpful assistant.' },
1354
- { role: 'user', content: 'Hello' },
1355
- ],
1356
- });
1357
-
1358
- expect(result.warnings).toStrictEqual([]);
1359
- });
1360
-
1361
- it('should use developer messages for o1', async () => {
1362
- prepareJsonResponse();
1363
-
1364
- const model = provider.chat('o1');
1365
-
1366
- const result = await model.doGenerate({
1367
- prompt: [
1368
- { role: 'system', content: 'You are a helpful assistant.' },
1369
- { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
1370
- ],
1371
- });
1372
-
1373
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1374
- model: 'o1',
1375
- messages: [
1376
- { role: 'developer', content: 'You are a helpful assistant.' },
1377
- { role: 'user', content: 'Hello' },
1378
- ],
1379
- });
1380
-
1381
- expect(result.warnings).toStrictEqual([]);
1382
- });
1383
-
1384
- it('should allow overriding systemMessageMode via providerOptions', async () => {
1385
- prepareJsonResponse();
1386
-
1387
- const model = provider.chat('gpt-4o');
1388
-
1389
- const result = await model.doGenerate({
1390
- prompt: [
1391
- { role: 'system', content: 'You are a helpful assistant.' },
1392
- { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
1393
- ],
1394
- providerOptions: {
1395
- openai: {
1396
- systemMessageMode: 'developer',
1397
- },
1398
- },
1399
- });
1400
-
1401
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1402
- model: 'gpt-4o',
1403
- messages: [
1404
- { role: 'developer', content: 'You are a helpful assistant.' },
1405
- { role: 'user', content: 'Hello' },
1406
- ],
1407
- });
1408
-
1409
- expect(result.warnings).toStrictEqual([]);
1410
- });
1411
-
1412
- it('should use default systemMessageMode when not overridden', async () => {
1413
- prepareJsonResponse();
1414
-
1415
- const model = provider.chat('gpt-4o');
1416
-
1417
- const result = await model.doGenerate({
1418
- prompt: [
1419
- { role: 'system', content: 'You are a helpful assistant.' },
1420
- { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
1421
- ],
1422
- });
1423
-
1424
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1425
- model: 'gpt-4o',
1426
- messages: [
1427
- { role: 'system', content: 'You are a helpful assistant.' },
1428
- { role: 'user', content: 'Hello' },
1429
- ],
1430
- });
1431
-
1432
- expect(result.warnings).toStrictEqual([]);
1433
- });
1434
-
1435
- it('should return the reasoning tokens in the provider metadata', async () => {
1436
- prepareJsonResponse({
1437
- usage: {
1438
- prompt_tokens: 15,
1439
- completion_tokens: 20,
1440
- total_tokens: 35,
1441
- completion_tokens_details: {
1442
- reasoning_tokens: 10,
1443
- },
1444
- },
1445
- });
1446
-
1447
- const model = provider.chat('o4-mini');
1448
-
1449
- const result = await model.doGenerate({
1450
- prompt: TEST_PROMPT,
1451
- });
1452
-
1453
- expect(result.usage).toMatchInlineSnapshot(`
1454
- {
1455
- "inputTokens": {
1456
- "cacheRead": 0,
1457
- "cacheWrite": undefined,
1458
- "noCache": 15,
1459
- "total": 15,
1460
- },
1461
- "outputTokens": {
1462
- "reasoning": 10,
1463
- "text": 10,
1464
- "total": 20,
1465
- },
1466
- "raw": {
1467
- "completion_tokens": 20,
1468
- "completion_tokens_details": {
1469
- "reasoning_tokens": 10,
1470
- },
1471
- "prompt_tokens": 15,
1472
- "total_tokens": 35,
1473
- },
1474
- }
1475
- `);
1476
- });
1477
-
1478
- it('should send max_completion_tokens extension setting', async () => {
1479
- prepareJsonResponse({ model: 'o4-mini' });
1480
-
1481
- const model = provider.chat('o4-mini');
1482
-
1483
- await model.doGenerate({
1484
- prompt: TEST_PROMPT,
1485
- providerOptions: {
1486
- openai: {
1487
- maxCompletionTokens: 255,
1488
- },
1489
- },
1490
- });
1491
-
1492
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1493
- model: 'o4-mini',
1494
- messages: [{ role: 'user', content: 'Hello' }],
1495
- max_completion_tokens: 255,
1496
- });
1497
- });
1498
-
1499
- it('should send prediction extension setting', async () => {
1500
- prepareJsonResponse({ content: '' });
1501
-
1502
- await model.doGenerate({
1503
- prompt: TEST_PROMPT,
1504
- providerOptions: {
1505
- openai: {
1506
- prediction: {
1507
- type: 'content',
1508
- content: 'Hello, World!',
1509
- },
1510
- },
1511
- },
1512
- });
1513
-
1514
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1515
- model: 'gpt-3.5-turbo',
1516
- messages: [{ role: 'user', content: 'Hello' }],
1517
- prediction: {
1518
- type: 'content',
1519
- content: 'Hello, World!',
1520
- },
1521
- });
1522
- });
1523
-
1524
- it('should send store extension setting', async () => {
1525
- prepareJsonResponse({ content: '' });
1526
-
1527
- await model.doGenerate({
1528
- prompt: TEST_PROMPT,
1529
- providerOptions: {
1530
- openai: {
1531
- store: true,
1532
- },
1533
- },
1534
- });
1535
-
1536
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1537
- model: 'gpt-3.5-turbo',
1538
- messages: [{ role: 'user', content: 'Hello' }],
1539
- store: true,
1540
- });
1541
- });
1542
-
1543
- it('should send metadata extension values', async () => {
1544
- prepareJsonResponse({ content: '' });
1545
-
1546
- await model.doGenerate({
1547
- prompt: TEST_PROMPT,
1548
- providerOptions: {
1549
- openai: {
1550
- metadata: {
1551
- custom: 'value',
1552
- },
1553
- },
1554
- },
1555
- });
1556
-
1557
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1558
- model: 'gpt-3.5-turbo',
1559
- messages: [{ role: 'user', content: 'Hello' }],
1560
- metadata: {
1561
- custom: 'value',
1562
- },
1563
- });
1564
- });
1565
-
1566
- it('should send promptCacheKey extension value', async () => {
1567
- prepareJsonResponse({ content: '' });
1568
-
1569
- await model.doGenerate({
1570
- prompt: TEST_PROMPT,
1571
- providerOptions: {
1572
- openai: {
1573
- promptCacheKey: 'test-cache-key-123',
1574
- },
1575
- },
1576
- });
1577
-
1578
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1579
- model: 'gpt-3.5-turbo',
1580
- messages: [{ role: 'user', content: 'Hello' }],
1581
- prompt_cache_key: 'test-cache-key-123',
1582
- });
1583
- });
1584
-
1585
- it('should send promptCacheRetention extension value', async () => {
1586
- prepareJsonResponse({ content: '' });
1587
-
1588
- await model.doGenerate({
1589
- prompt: TEST_PROMPT,
1590
- providerOptions: {
1591
- openai: {
1592
- promptCacheRetention: '24h',
1593
- },
1594
- },
1595
- });
1596
-
1597
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1598
- model: 'gpt-3.5-turbo',
1599
- messages: [{ role: 'user', content: 'Hello' }],
1600
- prompt_cache_retention: '24h',
1601
- });
1602
- });
1603
-
1604
- it('should send safetyIdentifier extension value', async () => {
1605
- prepareJsonResponse({ content: '' });
1606
-
1607
- await model.doGenerate({
1608
- prompt: TEST_PROMPT,
1609
- providerOptions: {
1610
- openai: {
1611
- safetyIdentifier: 'test-safety-identifier-123',
1612
- },
1613
- },
1614
- });
1615
-
1616
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
1617
- model: 'gpt-3.5-turbo',
1618
- messages: [{ role: 'user', content: 'Hello' }],
1619
- safety_identifier: 'test-safety-identifier-123',
1620
- });
1621
- });
1622
-
1623
- it('should remove temperature setting for gpt-4o-search-preview and add warning', async () => {
1624
- prepareJsonResponse();
1625
-
1626
- const model = provider.chat('gpt-4o-search-preview');
1627
-
1628
- const result = await model.doGenerate({
1629
- prompt: TEST_PROMPT,
1630
- temperature: 0.7,
1631
- });
1632
-
1633
- const requestBody = await server.calls[0].requestBodyJson;
1634
- expect(requestBody.model).toBe('gpt-4o-search-preview');
1635
- expect(requestBody.temperature).toBeUndefined();
1636
-
1637
- expect(result.warnings).toMatchInlineSnapshot(`
1638
- [
1639
- {
1640
- "details": "temperature is not supported for the search preview models and has been removed.",
1641
- "feature": "temperature",
1642
- "type": "unsupported",
1643
- },
1644
- ]
1645
- `);
1646
- });
1647
-
1648
- it('should remove temperature setting for gpt-4o-mini-search-preview and add warning', async () => {
1649
- prepareJsonResponse();
1650
-
1651
- const model = provider.chat('gpt-4o-mini-search-preview');
1652
-
1653
- const result = await model.doGenerate({
1654
- prompt: TEST_PROMPT,
1655
- temperature: 0.7,
1656
- });
1657
-
1658
- const requestBody = await server.calls[0].requestBodyJson;
1659
- expect(requestBody.model).toBe('gpt-4o-mini-search-preview');
1660
- expect(requestBody.temperature).toBeUndefined();
1661
-
1662
- expect(result.warnings).toMatchInlineSnapshot(`
1663
- [
1664
- {
1665
- "details": "temperature is not supported for the search preview models and has been removed.",
1666
- "feature": "temperature",
1667
- "type": "unsupported",
1668
- },
1669
- ]
1670
- `);
1671
- });
1672
-
1673
- it('should remove temperature setting for gpt-4o-mini-search-preview-2025-03-11 and add warning', async () => {
1674
- prepareJsonResponse();
1675
-
1676
- const model = provider.chat('gpt-4o-mini-search-preview-2025-03-11');
1677
-
1678
- const result = await model.doGenerate({
1679
- prompt: TEST_PROMPT,
1680
- temperature: 0.7,
1681
- });
1682
-
1683
- const requestBody = await server.calls[0].requestBodyJson;
1684
- expect(requestBody.model).toBe('gpt-4o-mini-search-preview-2025-03-11');
1685
- expect(requestBody.temperature).toBeUndefined();
1686
-
1687
- expect(result.warnings).toMatchInlineSnapshot(`
1688
- [
1689
- {
1690
- "details": "temperature is not supported for the search preview models and has been removed.",
1691
- "feature": "temperature",
1692
- "type": "unsupported",
1693
- },
1694
- ]
1695
- `);
1696
- });
1697
-
1698
- it('should send serviceTier flex processing setting', async () => {
1699
- prepareJsonResponse({ content: '' });
1700
-
1701
- const model = provider.chat('o4-mini');
1702
-
1703
- await model.doGenerate({
1704
- prompt: TEST_PROMPT,
1705
- providerOptions: {
1706
- openai: {
1707
- serviceTier: 'flex',
1708
- },
1709
- },
1710
- });
1711
-
1712
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
1713
- {
1714
- "messages": [
1715
- {
1716
- "content": "Hello",
1717
- "role": "user",
1718
- },
1719
- ],
1720
- "model": "o4-mini",
1721
- "service_tier": "flex",
1722
- }
1723
- `);
1724
- });
1725
-
1726
- it('should show warning when using flex processing with unsupported model', async () => {
1727
- prepareJsonResponse();
1728
-
1729
- const model = provider.chat('gpt-4o-mini');
1730
-
1731
- const result = await model.doGenerate({
1732
- prompt: TEST_PROMPT,
1733
- providerOptions: {
1734
- openai: {
1735
- serviceTier: 'flex',
1736
- },
1737
- },
1738
- });
1739
-
1740
- const requestBody = await server.calls[0].requestBodyJson;
1741
- expect(requestBody.service_tier).toBeUndefined();
1742
-
1743
- expect(result.warnings).toMatchInlineSnapshot(`
1744
- [
1745
- {
1746
- "details": "flex processing is only available for o3, o4-mini, and gpt-5 models",
1747
- "feature": "serviceTier",
1748
- "type": "unsupported",
1749
- },
1750
- ]
1751
- `);
1752
- });
1753
-
1754
- it('should allow flex processing with o4-mini model without warnings', async () => {
1755
- prepareJsonResponse();
1756
-
1757
- const model = provider.chat('o4-mini');
1758
-
1759
- const result = await model.doGenerate({
1760
- prompt: TEST_PROMPT,
1761
- providerOptions: {
1762
- openai: {
1763
- serviceTier: 'flex',
1764
- },
1765
- },
1766
- });
1767
-
1768
- const requestBody = await server.calls[0].requestBodyJson;
1769
- expect(requestBody.service_tier).toBe('flex');
1770
- expect(result.warnings).toEqual([]);
1771
- });
1772
-
1773
- it('should send serviceTier priority processing setting', async () => {
1774
- prepareJsonResponse();
1775
-
1776
- const model = provider.chat('gpt-4o-mini');
1777
-
1778
- await model.doGenerate({
1779
- prompt: TEST_PROMPT,
1780
- providerOptions: {
1781
- openai: {
1782
- serviceTier: 'priority',
1783
- },
1784
- },
1785
- });
1786
-
1787
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
1788
- {
1789
- "messages": [
1790
- {
1791
- "content": "Hello",
1792
- "role": "user",
1793
- },
1794
- ],
1795
- "model": "gpt-4o-mini",
1796
- "service_tier": "priority",
1797
- }
1798
- `);
1799
- });
1800
-
1801
- it('should show warning when using priority processing with unsupported model', async () => {
1802
- prepareJsonResponse();
1803
-
1804
- const model = provider.chat('gpt-3.5-turbo');
1805
-
1806
- const result = await model.doGenerate({
1807
- prompt: TEST_PROMPT,
1808
- providerOptions: {
1809
- openai: {
1810
- serviceTier: 'priority',
1811
- },
1812
- },
1813
- });
1814
-
1815
- const requestBody = await server.calls[0].requestBodyJson;
1816
- expect(requestBody.service_tier).toBeUndefined();
1817
-
1818
- expect(result.warnings).toMatchInlineSnapshot(`
1819
- [
1820
- {
1821
- "details": "priority processing is only available for supported models (gpt-4, gpt-5, gpt-5-mini, o3, o4-mini) and requires Enterprise access. gpt-5-nano is not supported",
1822
- "feature": "serviceTier",
1823
- "type": "unsupported",
1824
- },
1825
- ]
1826
- `);
1827
- });
1828
-
1829
- it('should allow priority processing with gpt-4o model without warnings', async () => {
1830
- prepareJsonResponse();
1831
-
1832
- const model = provider.chat('gpt-4o');
1833
-
1834
- const result = await model.doGenerate({
1835
- prompt: TEST_PROMPT,
1836
- providerOptions: {
1837
- openai: {
1838
- serviceTier: 'priority',
1839
- },
1840
- },
1841
- });
1842
-
1843
- const requestBody = await server.calls[0].requestBodyJson;
1844
- expect(requestBody.service_tier).toBe('priority');
1845
- expect(result.warnings).toEqual([]);
1846
- });
1847
-
1848
- it('should allow priority processing with o3 model without warnings', async () => {
1849
- prepareJsonResponse();
1850
-
1851
- const model = provider.chat('o4-mini');
1852
-
1853
- const result = await model.doGenerate({
1854
- prompt: TEST_PROMPT,
1855
- providerOptions: {
1856
- openai: {
1857
- serviceTier: 'priority',
1858
- },
1859
- },
1860
- });
1861
-
1862
- const requestBody = await server.calls[0].requestBodyJson;
1863
- expect(requestBody.service_tier).toBe('priority');
1864
- expect(result.warnings).toEqual([]);
1865
- });
1866
- });
1867
-
1868
- describe('doStream', () => {
1869
- function prepareStreamResponse({
1870
- content = [],
1871
- usage = {
1872
- prompt_tokens: 17,
1873
- total_tokens: 244,
1874
- completion_tokens: 227,
1875
- },
1876
- logprobs = null,
1877
- finish_reason = 'stop',
1878
- model = 'gpt-3.5-turbo-0613',
1879
- headers,
1880
- }: {
1881
- content?: string[];
1882
- usage?: {
1883
- prompt_tokens: number;
1884
- total_tokens: number;
1885
- completion_tokens: number;
1886
- prompt_tokens_details?: {
1887
- cached_tokens?: number;
1888
- };
1889
- completion_tokens_details?: {
1890
- reasoning_tokens?: number;
1891
- accepted_prediction_tokens?: number;
1892
- rejected_prediction_tokens?: number;
1893
- };
1894
- };
1895
- logprobs?: {
1896
- content:
1897
- | {
1898
- token: string;
1899
- logprob: number;
1900
- top_logprobs: { token: string; logprob: number }[];
1901
- }[]
1902
- | null;
1903
- } | null;
1904
- finish_reason?: string;
1905
- model?: string;
1906
- headers?: Record<string, string>;
1907
- }) {
1908
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
1909
- type: 'stream-chunks',
1910
- headers,
1911
- chunks: [
1912
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"${model}",` +
1913
- `"system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}\n\n`,
1914
- ...content.map(text => {
1915
- return (
1916
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"${model}",` +
1917
- `"system_fingerprint":null,"choices":[{"index":1,"delta":{"content":"${text}"},"finish_reason":null}]}\n\n`
1918
- );
1919
- }),
1920
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"${model}",` +
1921
- `"system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"${finish_reason}","logprobs":${JSON.stringify(
1922
- logprobs,
1923
- )}}]}\n\n`,
1924
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"${model}",` +
1925
- `"system_fingerprint":"fp_3bc1b5746c","choices":[],"usage":${JSON.stringify(
1926
- usage,
1927
- )}}\n\n`,
1928
- 'data: [DONE]\n\n',
1929
- ],
1930
- };
1931
- }
1932
-
1933
- it('should stream text deltas', async () => {
1934
- prepareStreamResponse({
1935
- content: ['Hello', ', ', 'World!'],
1936
- finish_reason: 'stop',
1937
- usage: {
1938
- prompt_tokens: 17,
1939
- total_tokens: 244,
1940
- completion_tokens: 227,
1941
- },
1942
- logprobs: TEST_LOGPROBS,
1943
- });
1944
-
1945
- const { stream } = await model.doStream({
1946
- prompt: TEST_PROMPT,
1947
- includeRawChunks: false,
1948
- });
1949
-
1950
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
1951
- [
1952
- {
1953
- "type": "stream-start",
1954
- "warnings": [],
1955
- },
1956
- {
1957
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
1958
- "modelId": "gpt-3.5-turbo-0613",
1959
- "timestamp": 2023-12-15T16:17:00.000Z,
1960
- "type": "response-metadata",
1961
- },
1962
- {
1963
- "id": "0",
1964
- "type": "text-start",
1965
- },
1966
- {
1967
- "delta": "",
1968
- "id": "0",
1969
- "type": "text-delta",
1970
- },
1971
- {
1972
- "delta": "Hello",
1973
- "id": "0",
1974
- "type": "text-delta",
1975
- },
1976
- {
1977
- "delta": ", ",
1978
- "id": "0",
1979
- "type": "text-delta",
1980
- },
1981
- {
1982
- "delta": "World!",
1983
- "id": "0",
1984
- "type": "text-delta",
1985
- },
1986
- {
1987
- "id": "0",
1988
- "type": "text-end",
1989
- },
1990
- {
1991
- "finishReason": {
1992
- "raw": "stop",
1993
- "unified": "stop",
1994
- },
1995
- "providerMetadata": {
1996
- "openai": {
1997
- "logprobs": [
1998
- {
1999
- "logprob": -0.0009994634,
2000
- "token": "Hello",
2001
- "top_logprobs": [
2002
- {
2003
- "logprob": -0.0009994634,
2004
- "token": "Hello",
2005
- },
2006
- ],
2007
- },
2008
- {
2009
- "logprob": -0.13410144,
2010
- "token": "!",
2011
- "top_logprobs": [
2012
- {
2013
- "logprob": -0.13410144,
2014
- "token": "!",
2015
- },
2016
- ],
2017
- },
2018
- {
2019
- "logprob": -0.0009250381,
2020
- "token": " How",
2021
- "top_logprobs": [
2022
- {
2023
- "logprob": -0.0009250381,
2024
- "token": " How",
2025
- },
2026
- ],
2027
- },
2028
- {
2029
- "logprob": -0.047709424,
2030
- "token": " can",
2031
- "top_logprobs": [
2032
- {
2033
- "logprob": -0.047709424,
2034
- "token": " can",
2035
- },
2036
- ],
2037
- },
2038
- {
2039
- "logprob": -0.000009014684,
2040
- "token": " I",
2041
- "top_logprobs": [
2042
- {
2043
- "logprob": -0.000009014684,
2044
- "token": " I",
2045
- },
2046
- ],
2047
- },
2048
- {
2049
- "logprob": -0.009125131,
2050
- "token": " assist",
2051
- "top_logprobs": [
2052
- {
2053
- "logprob": -0.009125131,
2054
- "token": " assist",
2055
- },
2056
- ],
2057
- },
2058
- {
2059
- "logprob": -0.0000066306106,
2060
- "token": " you",
2061
- "top_logprobs": [
2062
- {
2063
- "logprob": -0.0000066306106,
2064
- "token": " you",
2065
- },
2066
- ],
2067
- },
2068
- {
2069
- "logprob": -0.00011093382,
2070
- "token": " today",
2071
- "top_logprobs": [
2072
- {
2073
- "logprob": -0.00011093382,
2074
- "token": " today",
2075
- },
2076
- ],
2077
- },
2078
- {
2079
- "logprob": -0.00004596782,
2080
- "token": "?",
2081
- "top_logprobs": [
2082
- {
2083
- "logprob": -0.00004596782,
2084
- "token": "?",
2085
- },
2086
- ],
2087
- },
2088
- ],
2089
- },
2090
- },
2091
- "type": "finish",
2092
- "usage": {
2093
- "inputTokens": {
2094
- "cacheRead": 0,
2095
- "cacheWrite": undefined,
2096
- "noCache": 17,
2097
- "total": 17,
2098
- },
2099
- "outputTokens": {
2100
- "reasoning": 0,
2101
- "text": 227,
2102
- "total": 227,
2103
- },
2104
- "raw": {
2105
- "completion_tokens": 227,
2106
- "prompt_tokens": 17,
2107
- "total_tokens": 244,
2108
- },
2109
- },
2110
- },
2111
- ]
2112
- `);
2113
- });
2114
-
2115
- it('should stream annotations/citations', async () => {
2116
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2117
- type: 'stream-chunks',
2118
- chunks: [
2119
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"gpt-3.5-turbo-0125",` +
2120
- `"system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}\n\n`,
2121
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"gpt-3.5-turbo-0125",` +
2122
- `"system_fingerprint":null,"choices":[{"index":1,"delta":{"content":"Based on search results"},"finish_reason":null}]}\n\n`,
2123
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"gpt-3.5-turbo-0125",` +
2124
- `"system_fingerprint":null,"choices":[{"index":1,"delta":{"annotations":[{"type":"url_citation","url_citation":{"start_index":24,"end_index":29,"url":"https://example.com/doc1.pdf","title":"Document 1"}}]},"finish_reason":null}]}\n\n`,
2125
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"gpt-3.5-turbo-0125",` +
2126
- `"system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}\n\n`,
2127
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1702657020,"model":"gpt-3.5-turbo-0125",` +
2128
- `"system_fingerprint":"fp_3bc1b5746c","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":227,"total_tokens":244}}\n\n`,
2129
- 'data: [DONE]\n\n',
2130
- ],
2131
- };
2132
-
2133
- const { stream } = await model.doStream({
2134
- prompt: TEST_PROMPT,
2135
- includeRawChunks: false,
2136
- });
2137
-
2138
- const streamResult = await convertReadableStreamToArray(stream);
2139
-
2140
- expect(streamResult).toEqual(
2141
- expect.arrayContaining([
2142
- { type: 'stream-start', warnings: [] },
2143
- expect.objectContaining({
2144
- type: 'response-metadata',
2145
- id: 'chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP',
2146
- }),
2147
- { type: 'text-start', id: '0' },
2148
- { type: 'text-delta', id: '0', delta: '' },
2149
- { type: 'text-delta', id: '0', delta: 'Based on search results' },
2150
- {
2151
- type: 'source',
2152
- sourceType: 'url',
2153
- id: expect.any(String),
2154
- url: 'https://example.com/doc1.pdf',
2155
- title: 'Document 1',
2156
- },
2157
- { type: 'text-end', id: '0' },
2158
- expect.objectContaining({
2159
- type: 'finish',
2160
- finishReason: { unified: 'stop', raw: 'stop' },
2161
- }),
2162
- ]),
2163
- );
2164
- });
2165
-
2166
- it('should stream tool deltas', async () => {
2167
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2168
- type: 'stream-chunks',
2169
- chunks: [
2170
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2171
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"role":"assistant","content":null,` +
2172
- `"tool_calls":[{"index":0,"id":"call_O17Uplv4lJvD6DVdIvFFeRMw","type":"function","function":{"name":"test-tool","arguments":""}}]},` +
2173
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2174
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2175
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\\""}}]},` +
2176
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2177
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2178
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"value"}}]},` +
2179
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2180
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2181
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\":\\""}}]},` +
2182
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2183
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2184
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Spark"}}]},` +
2185
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2186
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2187
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le"}}]},` +
2188
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2189
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2190
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Day"}}]},` +
2191
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2192
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2193
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}"}}]},` +
2194
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2195
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2196
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]}\n\n`,
2197
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2198
- `"system_fingerprint":"fp_3bc1b5746c","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":17,"total_tokens":70}}\n\n`,
2199
- 'data: [DONE]\n\n',
2200
- ],
2201
- };
2202
-
2203
- const { stream } = await model.doStream({
2204
- tools: [
2205
- {
2206
- type: 'function',
2207
- name: 'test-tool',
2208
- inputSchema: {
2209
- type: 'object',
2210
- properties: { value: { type: 'string' } },
2211
- required: ['value'],
2212
- additionalProperties: false,
2213
- $schema: 'http://json-schema.org/draft-07/schema#',
2214
- },
2215
- },
2216
- ],
2217
- prompt: TEST_PROMPT,
2218
- includeRawChunks: false,
2219
- });
2220
-
2221
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
2222
- [
2223
- {
2224
- "type": "stream-start",
2225
- "warnings": [],
2226
- },
2227
- {
2228
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
2229
- "modelId": "gpt-3.5-turbo-0125",
2230
- "timestamp": 2024-03-25T09:06:38.000Z,
2231
- "type": "response-metadata",
2232
- },
2233
- {
2234
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2235
- "toolName": "test-tool",
2236
- "type": "tool-input-start",
2237
- },
2238
- {
2239
- "delta": "{"",
2240
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2241
- "type": "tool-input-delta",
2242
- },
2243
- {
2244
- "delta": "value",
2245
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2246
- "type": "tool-input-delta",
2247
- },
2248
- {
2249
- "delta": "":"",
2250
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2251
- "type": "tool-input-delta",
2252
- },
2253
- {
2254
- "delta": "Spark",
2255
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2256
- "type": "tool-input-delta",
2257
- },
2258
- {
2259
- "delta": "le",
2260
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2261
- "type": "tool-input-delta",
2262
- },
2263
- {
2264
- "delta": " Day",
2265
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2266
- "type": "tool-input-delta",
2267
- },
2268
- {
2269
- "delta": ""}",
2270
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2271
- "type": "tool-input-delta",
2272
- },
2273
- {
2274
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2275
- "type": "tool-input-end",
2276
- },
2277
- {
2278
- "input": "{"value":"Sparkle Day"}",
2279
- "toolCallId": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2280
- "toolName": "test-tool",
2281
- "type": "tool-call",
2282
- },
2283
- {
2284
- "finishReason": {
2285
- "raw": "tool_calls",
2286
- "unified": "tool-calls",
2287
- },
2288
- "providerMetadata": {
2289
- "openai": {},
2290
- },
2291
- "type": "finish",
2292
- "usage": {
2293
- "inputTokens": {
2294
- "cacheRead": 0,
2295
- "cacheWrite": undefined,
2296
- "noCache": 53,
2297
- "total": 53,
2298
- },
2299
- "outputTokens": {
2300
- "reasoning": 0,
2301
- "text": 17,
2302
- "total": 17,
2303
- },
2304
- "raw": {
2305
- "completion_tokens": 17,
2306
- "prompt_tokens": 53,
2307
- "total_tokens": 70,
2308
- },
2309
- },
2310
- },
2311
- ]
2312
- `);
2313
- });
2314
-
2315
- it('should stream tool call deltas when tool call arguments are passed in the first chunk', async () => {
2316
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2317
- type: 'stream-chunks',
2318
- chunks: [
2319
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2320
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"role":"assistant","content":null,` +
2321
- `"tool_calls":[{"index":0,"id":"call_O17Uplv4lJvD6DVdIvFFeRMw","type":"function","function":{"name":"test-tool","arguments":"{\\""}}]},` +
2322
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2323
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2324
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"va"}}]},` +
2325
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2326
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2327
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"lue"}}]},` +
2328
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2329
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2330
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\":\\""}}]},` +
2331
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2332
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2333
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Spark"}}]},` +
2334
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2335
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2336
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"le"}}]},` +
2337
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2338
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2339
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Day"}}]},` +
2340
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2341
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2342
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\"}"}}]},` +
2343
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2344
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2345
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]}\n\n`,
2346
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2347
- `"system_fingerprint":"fp_3bc1b5746c","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":17,"total_tokens":70}}\n\n`,
2348
- 'data: [DONE]\n\n',
2349
- ],
2350
- };
2351
-
2352
- const { stream } = await model.doStream({
2353
- tools: [
2354
- {
2355
- type: 'function',
2356
- name: 'test-tool',
2357
- inputSchema: {
2358
- type: 'object',
2359
- properties: { value: { type: 'string' } },
2360
- required: ['value'],
2361
- additionalProperties: false,
2362
- $schema: 'http://json-schema.org/draft-07/schema#',
2363
- },
2364
- },
2365
- ],
2366
- prompt: TEST_PROMPT,
2367
- includeRawChunks: false,
2368
- });
2369
-
2370
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
2371
- [
2372
- {
2373
- "type": "stream-start",
2374
- "warnings": [],
2375
- },
2376
- {
2377
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
2378
- "modelId": "gpt-3.5-turbo-0125",
2379
- "timestamp": 2024-03-25T09:06:38.000Z,
2380
- "type": "response-metadata",
2381
- },
2382
- {
2383
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2384
- "toolName": "test-tool",
2385
- "type": "tool-input-start",
2386
- },
2387
- {
2388
- "delta": "{"",
2389
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2390
- "type": "tool-input-delta",
2391
- },
2392
- {
2393
- "delta": "va",
2394
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2395
- "type": "tool-input-delta",
2396
- },
2397
- {
2398
- "delta": "lue",
2399
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2400
- "type": "tool-input-delta",
2401
- },
2402
- {
2403
- "delta": "":"",
2404
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2405
- "type": "tool-input-delta",
2406
- },
2407
- {
2408
- "delta": "Spark",
2409
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2410
- "type": "tool-input-delta",
2411
- },
2412
- {
2413
- "delta": "le",
2414
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2415
- "type": "tool-input-delta",
2416
- },
2417
- {
2418
- "delta": " Day",
2419
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2420
- "type": "tool-input-delta",
2421
- },
2422
- {
2423
- "delta": ""}",
2424
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2425
- "type": "tool-input-delta",
2426
- },
2427
- {
2428
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2429
- "type": "tool-input-end",
2430
- },
2431
- {
2432
- "input": "{"value":"Sparkle Day"}",
2433
- "toolCallId": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2434
- "toolName": "test-tool",
2435
- "type": "tool-call",
2436
- },
2437
- {
2438
- "finishReason": {
2439
- "raw": "tool_calls",
2440
- "unified": "tool-calls",
2441
- },
2442
- "providerMetadata": {
2443
- "openai": {},
2444
- },
2445
- "type": "finish",
2446
- "usage": {
2447
- "inputTokens": {
2448
- "cacheRead": 0,
2449
- "cacheWrite": undefined,
2450
- "noCache": 53,
2451
- "total": 53,
2452
- },
2453
- "outputTokens": {
2454
- "reasoning": 0,
2455
- "text": 17,
2456
- "total": 17,
2457
- },
2458
- "raw": {
2459
- "completion_tokens": 17,
2460
- "prompt_tokens": 53,
2461
- "total_tokens": 70,
2462
- },
2463
- },
2464
- },
2465
- ]
2466
- `);
2467
- });
2468
-
2469
- it('should not duplicate tool calls when there is an additional empty chunk after the tool call has been completed', async () => {
2470
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2471
- type: 'stream-chunks',
2472
- chunks: [
2473
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2474
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],` +
2475
- `"usage":{"prompt_tokens":226,"total_tokens":226,"completion_tokens":0}}\n\n`,
2476
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2477
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"id":"chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",` +
2478
- `"type":"function","index":0,"function":{"name":"searchGoogle"}}]},"logprobs":null,"finish_reason":null}],` +
2479
- `"usage":{"prompt_tokens":226,"total_tokens":233,"completion_tokens":7}}\n\n`,
2480
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2481
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,` +
2482
- `"function":{"arguments":"{\\"query\\": \\""}}]},"logprobs":null,"finish_reason":null}],` +
2483
- `"usage":{"prompt_tokens":226,"total_tokens":241,"completion_tokens":15}}\n\n`,
2484
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2485
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,` +
2486
- `"function":{"arguments":"latest"}}]},"logprobs":null,"finish_reason":null}],` +
2487
- `"usage":{"prompt_tokens":226,"total_tokens":242,"completion_tokens":16}}\n\n`,
2488
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2489
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,` +
2490
- `"function":{"arguments":" news"}}]},"logprobs":null,"finish_reason":null}],` +
2491
- `"usage":{"prompt_tokens":226,"total_tokens":243,"completion_tokens":17}}\n\n`,
2492
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2493
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,` +
2494
- `"function":{"arguments":" on"}}]},"logprobs":null,"finish_reason":null}],` +
2495
- `"usage":{"prompt_tokens":226,"total_tokens":244,"completion_tokens":18}}\n\n`,
2496
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2497
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,` +
2498
- `"function":{"arguments":" ai\\"}"}}]},"logprobs":null,"finish_reason":null}],` +
2499
- `"usage":{"prompt_tokens":226,"total_tokens":245,"completion_tokens":19}}\n\n`,
2500
- // empty arguments chunk after the tool call has already been finished:
2501
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2502
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,` +
2503
- `"function":{"arguments":""}}]},"logprobs":null,"finish_reason":"tool_calls","stop_reason":128008}],` +
2504
- `"usage":{"prompt_tokens":226,"total_tokens":246,"completion_tokens":20}}\n\n`,
2505
- `data: {"id":"chat-2267f7e2910a4254bac0650ba74cfc1c","object":"chat.completion.chunk","created":1733162241,` +
2506
- `"model":"meta/llama-3.1-8b-instruct:fp8","choices":[],` +
2507
- `"usage":{"prompt_tokens":226,"total_tokens":246,"completion_tokens":20}}\n\n`,
2508
- `data: [DONE]\n\n`,
2509
- ],
2510
- };
2511
-
2512
- const { stream } = await model.doStream({
2513
- tools: [
2514
- {
2515
- type: 'function',
2516
- name: 'searchGoogle',
2517
- inputSchema: {
2518
- type: 'object',
2519
- properties: { query: { type: 'string' } },
2520
- required: ['query'],
2521
- additionalProperties: false,
2522
- $schema: 'http://json-schema.org/draft-07/schema#',
2523
- },
2524
- },
2525
- ],
2526
- prompt: TEST_PROMPT,
2527
- includeRawChunks: false,
2528
- });
2529
-
2530
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
2531
- [
2532
- {
2533
- "type": "stream-start",
2534
- "warnings": [],
2535
- },
2536
- {
2537
- "id": "chat-2267f7e2910a4254bac0650ba74cfc1c",
2538
- "modelId": "meta/llama-3.1-8b-instruct:fp8",
2539
- "timestamp": 2024-12-02T17:57:21.000Z,
2540
- "type": "response-metadata",
2541
- },
2542
- {
2543
- "id": "0",
2544
- "type": "text-start",
2545
- },
2546
- {
2547
- "delta": "",
2548
- "id": "0",
2549
- "type": "text-delta",
2550
- },
2551
- {
2552
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2553
- "toolName": "searchGoogle",
2554
- "type": "tool-input-start",
2555
- },
2556
- {
2557
- "delta": "{"query": "",
2558
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2559
- "type": "tool-input-delta",
2560
- },
2561
- {
2562
- "delta": "latest",
2563
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2564
- "type": "tool-input-delta",
2565
- },
2566
- {
2567
- "delta": " news",
2568
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2569
- "type": "tool-input-delta",
2570
- },
2571
- {
2572
- "delta": " on",
2573
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2574
- "type": "tool-input-delta",
2575
- },
2576
- {
2577
- "delta": " ai"}",
2578
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2579
- "type": "tool-input-delta",
2580
- },
2581
- {
2582
- "id": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2583
- "type": "tool-input-end",
2584
- },
2585
- {
2586
- "input": "{"query": "latest news on ai"}",
2587
- "toolCallId": "chatcmpl-tool-b3b307239370432d9910d4b79b4dbbaa",
2588
- "toolName": "searchGoogle",
2589
- "type": "tool-call",
2590
- },
2591
- {
2592
- "id": "0",
2593
- "type": "text-end",
2594
- },
2595
- {
2596
- "finishReason": {
2597
- "raw": "tool_calls",
2598
- "unified": "tool-calls",
2599
- },
2600
- "providerMetadata": {
2601
- "openai": {},
2602
- },
2603
- "type": "finish",
2604
- "usage": {
2605
- "inputTokens": {
2606
- "cacheRead": 0,
2607
- "cacheWrite": undefined,
2608
- "noCache": 226,
2609
- "total": 226,
2610
- },
2611
- "outputTokens": {
2612
- "reasoning": 0,
2613
- "text": 20,
2614
- "total": 20,
2615
- },
2616
- "raw": {
2617
- "completion_tokens": 20,
2618
- "prompt_tokens": 226,
2619
- "total_tokens": 246,
2620
- },
2621
- },
2622
- },
2623
- ]
2624
- `);
2625
- });
2626
-
2627
- it('should stream tool call that is sent in one chunk', async () => {
2628
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2629
- type: 'stream-chunks',
2630
- chunks: [
2631
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2632
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{"role":"assistant","content":null,` +
2633
- `"tool_calls":[{"index":0,"id":"call_O17Uplv4lJvD6DVdIvFFeRMw","type":"function","function":{"name":"test-tool","arguments":"{\\"value\\":\\"Sparkle Day\\"}"}}]},` +
2634
- `"logprobs":null,"finish_reason":null}]}\n\n`,
2635
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2636
- `"system_fingerprint":"fp_3bc1b5746c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]}\n\n`,
2637
- `data: {"id":"chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP","object":"chat.completion.chunk","created":1711357598,"model":"gpt-3.5-turbo-0125",` +
2638
- `"system_fingerprint":"fp_3bc1b5746c","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":17,"total_tokens":70}}\n\n`,
2639
- 'data: [DONE]\n\n',
2640
- ],
2641
- };
2642
-
2643
- const { stream } = await model.doStream({
2644
- tools: [
2645
- {
2646
- type: 'function',
2647
- name: 'test-tool',
2648
- inputSchema: {
2649
- type: 'object',
2650
- properties: { value: { type: 'string' } },
2651
- required: ['value'],
2652
- additionalProperties: false,
2653
- $schema: 'http://json-schema.org/draft-07/schema#',
2654
- },
2655
- },
2656
- ],
2657
- prompt: TEST_PROMPT,
2658
- includeRawChunks: false,
2659
- });
2660
-
2661
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
2662
- [
2663
- {
2664
- "type": "stream-start",
2665
- "warnings": [],
2666
- },
2667
- {
2668
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
2669
- "modelId": "gpt-3.5-turbo-0125",
2670
- "timestamp": 2024-03-25T09:06:38.000Z,
2671
- "type": "response-metadata",
2672
- },
2673
- {
2674
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2675
- "toolName": "test-tool",
2676
- "type": "tool-input-start",
2677
- },
2678
- {
2679
- "delta": "{"value":"Sparkle Day"}",
2680
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2681
- "type": "tool-input-delta",
2682
- },
2683
- {
2684
- "id": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2685
- "type": "tool-input-end",
2686
- },
2687
- {
2688
- "input": "{"value":"Sparkle Day"}",
2689
- "toolCallId": "call_O17Uplv4lJvD6DVdIvFFeRMw",
2690
- "toolName": "test-tool",
2691
- "type": "tool-call",
2692
- },
2693
- {
2694
- "finishReason": {
2695
- "raw": "tool_calls",
2696
- "unified": "tool-calls",
2697
- },
2698
- "providerMetadata": {
2699
- "openai": {},
2700
- },
2701
- "type": "finish",
2702
- "usage": {
2703
- "inputTokens": {
2704
- "cacheRead": 0,
2705
- "cacheWrite": undefined,
2706
- "noCache": 53,
2707
- "total": 53,
2708
- },
2709
- "outputTokens": {
2710
- "reasoning": 0,
2711
- "text": 17,
2712
- "total": 17,
2713
- },
2714
- "raw": {
2715
- "completion_tokens": 17,
2716
- "prompt_tokens": 53,
2717
- "total_tokens": 70,
2718
- },
2719
- },
2720
- },
2721
- ]
2722
- `);
2723
- });
2724
-
2725
- it('should handle error stream parts', async () => {
2726
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2727
- type: 'stream-chunks',
2728
- chunks: [
2729
- `data: {"error":{"message": "The server had an error processing your request. Sorry about that! You can retry your request, or contact us through our ` +
2730
- `help center at help.openai.com if you keep seeing this error.","type":"server_error","param":null,"code":null}}\n\n`,
2731
- 'data: [DONE]\n\n',
2732
- ],
2733
- };
2734
-
2735
- const { stream } = await model.doStream({
2736
- prompt: TEST_PROMPT,
2737
- includeRawChunks: false,
2738
- });
2739
-
2740
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
2741
- [
2742
- {
2743
- "type": "stream-start",
2744
- "warnings": [],
2745
- },
2746
- {
2747
- "error": {
2748
- "code": null,
2749
- "message": "The server had an error processing your request. Sorry about that! You can retry your request, or contact us through our help center at help.openai.com if you keep seeing this error.",
2750
- "param": null,
2751
- "type": "server_error",
2752
- },
2753
- "type": "error",
2754
- },
2755
- {
2756
- "finishReason": {
2757
- "raw": undefined,
2758
- "unified": "error",
2759
- },
2760
- "providerMetadata": {
2761
- "openai": {},
2762
- },
2763
- "type": "finish",
2764
- "usage": {
2765
- "inputTokens": {
2766
- "cacheRead": undefined,
2767
- "cacheWrite": undefined,
2768
- "noCache": undefined,
2769
- "total": undefined,
2770
- },
2771
- "outputTokens": {
2772
- "reasoning": undefined,
2773
- "text": undefined,
2774
- "total": undefined,
2775
- },
2776
- "raw": undefined,
2777
- },
2778
- },
2779
- ]
2780
- `);
2781
- });
2782
-
2783
- it.skipIf(isNodeVersion(20))(
2784
- 'should handle unparsable stream parts',
2785
- async () => {
2786
- server.urls['https://api.openai.com/v1/chat/completions'].response = {
2787
- type: 'stream-chunks',
2788
- chunks: [`data: {unparsable}\n\n`, 'data: [DONE]\n\n'],
2789
- };
2790
-
2791
- const { stream } = await model.doStream({
2792
- prompt: TEST_PROMPT,
2793
- includeRawChunks: false,
2794
- });
2795
-
2796
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
2797
- [
2798
- {
2799
- "type": "stream-start",
2800
- "warnings": [],
2801
- },
2802
- {
2803
- "error": [AI_JSONParseError: JSON parsing failed: Text: {unparsable}.
2804
- Error message: Expected property name or '}' in JSON at position 1 (line 1 column 2)],
2805
- "type": "error",
2806
- },
2807
- {
2808
- "finishReason": {
2809
- "raw": undefined,
2810
- "unified": "error",
2811
- },
2812
- "providerMetadata": {
2813
- "openai": {},
2814
- },
2815
- "type": "finish",
2816
- "usage": {
2817
- "inputTokens": {
2818
- "cacheRead": undefined,
2819
- "cacheWrite": undefined,
2820
- "noCache": undefined,
2821
- "total": undefined,
2822
- },
2823
- "outputTokens": {
2824
- "reasoning": undefined,
2825
- "text": undefined,
2826
- "total": undefined,
2827
- },
2828
- "raw": undefined,
2829
- },
2830
- },
2831
- ]
2832
- `);
2833
- },
2834
- );
2835
-
2836
- it('should send request body', async () => {
2837
- prepareStreamResponse({ content: [] });
2838
-
2839
- const { request } = await model.doStream({
2840
- prompt: TEST_PROMPT,
2841
- includeRawChunks: false,
2842
- });
2843
-
2844
- expect(request).toMatchInlineSnapshot(`
2845
- {
2846
- "body": {
2847
- "frequency_penalty": undefined,
2848
- "logit_bias": undefined,
2849
- "logprobs": undefined,
2850
- "max_completion_tokens": undefined,
2851
- "max_tokens": undefined,
2852
- "messages": [
2853
- {
2854
- "content": "Hello",
2855
- "role": "user",
2856
- },
2857
- ],
2858
- "metadata": undefined,
2859
- "model": "gpt-3.5-turbo",
2860
- "parallel_tool_calls": undefined,
2861
- "prediction": undefined,
2862
- "presence_penalty": undefined,
2863
- "prompt_cache_key": undefined,
2864
- "prompt_cache_retention": undefined,
2865
- "reasoning_effort": undefined,
2866
- "response_format": undefined,
2867
- "safety_identifier": undefined,
2868
- "seed": undefined,
2869
- "service_tier": undefined,
2870
- "stop": undefined,
2871
- "store": undefined,
2872
- "stream": true,
2873
- "stream_options": {
2874
- "include_usage": true,
2875
- },
2876
- "temperature": undefined,
2877
- "tool_choice": undefined,
2878
- "tools": undefined,
2879
- "top_logprobs": undefined,
2880
- "top_p": undefined,
2881
- "user": undefined,
2882
- "verbosity": undefined,
2883
- },
2884
- }
2885
- `);
2886
- });
2887
-
2888
- it('should expose the raw response headers', async () => {
2889
- prepareStreamResponse({
2890
- headers: { 'test-header': 'test-value' },
2891
- });
2892
-
2893
- const { response } = await model.doStream({
2894
- prompt: TEST_PROMPT,
2895
- includeRawChunks: false,
2896
- });
2897
-
2898
- expect(response?.headers).toStrictEqual({
2899
- // default headers:
2900
- 'content-type': 'text/event-stream',
2901
- 'cache-control': 'no-cache',
2902
- connection: 'keep-alive',
2903
-
2904
- // custom header
2905
- 'test-header': 'test-value',
2906
- });
2907
- });
2908
-
2909
- it('should pass the messages and the model', async () => {
2910
- prepareStreamResponse({ content: [] });
2911
-
2912
- await model.doStream({
2913
- prompt: TEST_PROMPT,
2914
- includeRawChunks: false,
2915
- });
2916
-
2917
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
2918
- stream: true,
2919
- stream_options: { include_usage: true },
2920
- model: 'gpt-3.5-turbo',
2921
- messages: [{ role: 'user', content: 'Hello' }],
2922
- });
2923
- });
2924
-
2925
- it('should pass headers', async () => {
2926
- prepareStreamResponse({ content: [] });
2927
-
2928
- const provider = createOpenAI({
2929
- apiKey: 'test-api-key',
2930
- organization: 'test-organization',
2931
- project: 'test-project',
2932
- headers: {
2933
- 'Custom-Provider-Header': 'provider-header-value',
2934
- },
2935
- });
2936
-
2937
- await provider.chat('gpt-3.5-turbo').doStream({
2938
- prompt: TEST_PROMPT,
2939
- headers: {
2940
- 'Custom-Request-Header': 'request-header-value',
2941
- },
2942
- includeRawChunks: false,
2943
- });
2944
-
2945
- expect(server.calls[0].requestHeaders).toStrictEqual({
2946
- authorization: 'Bearer test-api-key',
2947
- 'content-type': 'application/json',
2948
- 'custom-provider-header': 'provider-header-value',
2949
- 'custom-request-header': 'request-header-value',
2950
- 'openai-organization': 'test-organization',
2951
- 'openai-project': 'test-project',
2952
- });
2953
- });
2954
-
2955
- it('should return cached tokens in providerMetadata', async () => {
2956
- prepareStreamResponse({
2957
- content: [],
2958
- usage: {
2959
- prompt_tokens: 15,
2960
- completion_tokens: 20,
2961
- total_tokens: 35,
2962
- prompt_tokens_details: {
2963
- cached_tokens: 1152,
2964
- },
2965
- },
2966
- });
2967
-
2968
- const { stream } = await model.doStream({
2969
- prompt: TEST_PROMPT,
2970
- includeRawChunks: false,
2971
- });
2972
-
2973
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
2974
- stream: true,
2975
- stream_options: { include_usage: true },
2976
- model: 'gpt-3.5-turbo',
2977
- messages: [{ role: 'user', content: 'Hello' }],
2978
- });
2979
-
2980
- expect((await convertReadableStreamToArray(stream)).at(-1))
2981
- .toMatchInlineSnapshot(`
2982
- {
2983
- "finishReason": {
2984
- "raw": "stop",
2985
- "unified": "stop",
2986
- },
2987
- "providerMetadata": {
2988
- "openai": {},
2989
- },
2990
- "type": "finish",
2991
- "usage": {
2992
- "inputTokens": {
2993
- "cacheRead": 1152,
2994
- "cacheWrite": undefined,
2995
- "noCache": -1137,
2996
- "total": 15,
2997
- },
2998
- "outputTokens": {
2999
- "reasoning": 0,
3000
- "text": 20,
3001
- "total": 20,
3002
- },
3003
- "raw": {
3004
- "completion_tokens": 20,
3005
- "prompt_tokens": 15,
3006
- "prompt_tokens_details": {
3007
- "cached_tokens": 1152,
3008
- },
3009
- "total_tokens": 35,
3010
- },
3011
- },
3012
- }
3013
- `);
3014
- });
3015
-
3016
- it('should return accepted_prediction_tokens and rejected_prediction_tokens in providerMetadata', async () => {
3017
- prepareStreamResponse({
3018
- content: [],
3019
- usage: {
3020
- prompt_tokens: 15,
3021
- completion_tokens: 20,
3022
- total_tokens: 35,
3023
- completion_tokens_details: {
3024
- accepted_prediction_tokens: 123,
3025
- rejected_prediction_tokens: 456,
3026
- },
3027
- },
3028
- });
3029
-
3030
- const { stream } = await model.doStream({
3031
- prompt: TEST_PROMPT,
3032
- includeRawChunks: false,
3033
- });
3034
-
3035
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
3036
- stream: true,
3037
- stream_options: { include_usage: true },
3038
- model: 'gpt-3.5-turbo',
3039
- messages: [{ role: 'user', content: 'Hello' }],
3040
- });
3041
-
3042
- expect((await convertReadableStreamToArray(stream)).at(-1))
3043
- .toMatchInlineSnapshot(`
3044
- {
3045
- "finishReason": {
3046
- "raw": "stop",
3047
- "unified": "stop",
3048
- },
3049
- "providerMetadata": {
3050
- "openai": {
3051
- "acceptedPredictionTokens": 123,
3052
- "rejectedPredictionTokens": 456,
3053
- },
3054
- },
3055
- "type": "finish",
3056
- "usage": {
3057
- "inputTokens": {
3058
- "cacheRead": 0,
3059
- "cacheWrite": undefined,
3060
- "noCache": 15,
3061
- "total": 15,
3062
- },
3063
- "outputTokens": {
3064
- "reasoning": 0,
3065
- "text": 20,
3066
- "total": 20,
3067
- },
3068
- "raw": {
3069
- "completion_tokens": 20,
3070
- "completion_tokens_details": {
3071
- "accepted_prediction_tokens": 123,
3072
- "rejected_prediction_tokens": 456,
3073
- },
3074
- "prompt_tokens": 15,
3075
- "total_tokens": 35,
3076
- },
3077
- },
3078
- }
3079
- `);
3080
- });
3081
-
3082
- it('should send store extension setting', async () => {
3083
- prepareStreamResponse({ content: [] });
3084
-
3085
- await model.doStream({
3086
- prompt: TEST_PROMPT,
3087
- providerOptions: {
3088
- openai: {
3089
- store: true,
3090
- },
3091
- },
3092
- includeRawChunks: false,
3093
- });
3094
-
3095
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
3096
- model: 'gpt-3.5-turbo',
3097
- stream: true,
3098
- stream_options: { include_usage: true },
3099
- messages: [{ role: 'user', content: 'Hello' }],
3100
- store: true,
3101
- });
3102
- });
3103
-
3104
- it('should send metadata extension values', async () => {
3105
- prepareStreamResponse({ content: [] });
3106
-
3107
- await model.doStream({
3108
- prompt: TEST_PROMPT,
3109
- providerOptions: {
3110
- openai: {
3111
- metadata: {
3112
- custom: 'value',
3113
- },
3114
- },
3115
- },
3116
- includeRawChunks: false,
3117
- });
3118
-
3119
- expect(await server.calls[0].requestBodyJson).toStrictEqual({
3120
- model: 'gpt-3.5-turbo',
3121
- stream: true,
3122
- stream_options: { include_usage: true },
3123
- messages: [{ role: 'user', content: 'Hello' }],
3124
- metadata: {
3125
- custom: 'value',
3126
- },
3127
- });
3128
- });
3129
-
3130
- it('should send serviceTier flex processing setting in streaming', async () => {
3131
- prepareStreamResponse({ content: [] });
3132
-
3133
- const model = provider.chat('o4-mini');
3134
-
3135
- await model.doStream({
3136
- prompt: TEST_PROMPT,
3137
- providerOptions: {
3138
- openai: {
3139
- serviceTier: 'flex',
3140
- },
3141
- },
3142
- includeRawChunks: false,
3143
- });
3144
-
3145
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
3146
- {
3147
- "messages": [
3148
- {
3149
- "content": "Hello",
3150
- "role": "user",
3151
- },
3152
- ],
3153
- "model": "o4-mini",
3154
- "service_tier": "flex",
3155
- "stream": true,
3156
- "stream_options": {
3157
- "include_usage": true,
3158
- },
3159
- }
3160
- `);
3161
- });
3162
-
3163
- it('should send serviceTier priority processing setting in streaming', async () => {
3164
- prepareStreamResponse({ content: [] });
3165
-
3166
- const model = provider.chat('gpt-4o-mini');
3167
-
3168
- await model.doStream({
3169
- prompt: TEST_PROMPT,
3170
- providerOptions: {
3171
- openai: {
3172
- serviceTier: 'priority',
3173
- },
3174
- },
3175
- includeRawChunks: false,
3176
- });
3177
-
3178
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
3179
- {
3180
- "messages": [
3181
- {
3182
- "content": "Hello",
3183
- "role": "user",
3184
- },
3185
- ],
3186
- "model": "gpt-4o-mini",
3187
- "service_tier": "priority",
3188
- "stream": true,
3189
- "stream_options": {
3190
- "include_usage": true,
3191
- },
3192
- }
3193
- `);
3194
- });
3195
-
3196
- it('should set .modelId for model-router request', async () => {
3197
- prepareChunksFixtureResponse('azure-model-router.1');
3198
-
3199
- const result = await provider.chat('test-azure-model-router').doStream({
3200
- prompt: TEST_PROMPT,
3201
- });
3202
-
3203
- expect(await convertReadableStreamToArray(result.stream)).toMatchSnapshot();
3204
- });
3205
-
3206
- describe('reasoning models', () => {
3207
- it('should stream text delta', async () => {
3208
- prepareStreamResponse({
3209
- content: ['Hello, World!'],
3210
- model: 'o4-mini',
3211
- });
3212
-
3213
- const model = provider.chat('o4-mini');
3214
-
3215
- const { stream } = await model.doStream({
3216
- prompt: TEST_PROMPT,
3217
- includeRawChunks: false,
3218
- });
3219
-
3220
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
3221
- [
3222
- {
3223
- "type": "stream-start",
3224
- "warnings": [],
3225
- },
3226
- {
3227
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3228
- "modelId": "o4-mini",
3229
- "timestamp": 2023-12-15T16:17:00.000Z,
3230
- "type": "response-metadata",
3231
- },
3232
- {
3233
- "id": "0",
3234
- "type": "text-start",
3235
- },
3236
- {
3237
- "delta": "",
3238
- "id": "0",
3239
- "type": "text-delta",
3240
- },
3241
- {
3242
- "delta": "Hello, World!",
3243
- "id": "0",
3244
- "type": "text-delta",
3245
- },
3246
- {
3247
- "id": "0",
3248
- "type": "text-end",
3249
- },
3250
- {
3251
- "finishReason": {
3252
- "raw": "stop",
3253
- "unified": "stop",
3254
- },
3255
- "providerMetadata": {
3256
- "openai": {},
3257
- },
3258
- "type": "finish",
3259
- "usage": {
3260
- "inputTokens": {
3261
- "cacheRead": 0,
3262
- "cacheWrite": undefined,
3263
- "noCache": 17,
3264
- "total": 17,
3265
- },
3266
- "outputTokens": {
3267
- "reasoning": 0,
3268
- "text": 227,
3269
- "total": 227,
3270
- },
3271
- "raw": {
3272
- "completion_tokens": 227,
3273
- "prompt_tokens": 17,
3274
- "total_tokens": 244,
3275
- },
3276
- },
3277
- },
3278
- ]
3279
- `);
3280
- });
3281
-
3282
- it('should send reasoning tokens', async () => {
3283
- prepareStreamResponse({
3284
- content: ['Hello, World!'],
3285
- model: 'o4-mini',
3286
- usage: {
3287
- prompt_tokens: 15,
3288
- completion_tokens: 20,
3289
- total_tokens: 35,
3290
- completion_tokens_details: {
3291
- reasoning_tokens: 10,
3292
- },
3293
- },
3294
- });
3295
-
3296
- const model = provider.chat('o4-mini');
3297
-
3298
- const { stream } = await model.doStream({
3299
- prompt: TEST_PROMPT,
3300
- includeRawChunks: false,
3301
- });
3302
-
3303
- expect(await convertReadableStreamToArray(stream)).toMatchInlineSnapshot(`
3304
- [
3305
- {
3306
- "type": "stream-start",
3307
- "warnings": [],
3308
- },
3309
- {
3310
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3311
- "modelId": "o4-mini",
3312
- "timestamp": 2023-12-15T16:17:00.000Z,
3313
- "type": "response-metadata",
3314
- },
3315
- {
3316
- "id": "0",
3317
- "type": "text-start",
3318
- },
3319
- {
3320
- "delta": "",
3321
- "id": "0",
3322
- "type": "text-delta",
3323
- },
3324
- {
3325
- "delta": "Hello, World!",
3326
- "id": "0",
3327
- "type": "text-delta",
3328
- },
3329
- {
3330
- "id": "0",
3331
- "type": "text-end",
3332
- },
3333
- {
3334
- "finishReason": {
3335
- "raw": "stop",
3336
- "unified": "stop",
3337
- },
3338
- "providerMetadata": {
3339
- "openai": {},
3340
- },
3341
- "type": "finish",
3342
- "usage": {
3343
- "inputTokens": {
3344
- "cacheRead": 0,
3345
- "cacheWrite": undefined,
3346
- "noCache": 15,
3347
- "total": 15,
3348
- },
3349
- "outputTokens": {
3350
- "reasoning": 10,
3351
- "text": 10,
3352
- "total": 20,
3353
- },
3354
- "raw": {
3355
- "completion_tokens": 20,
3356
- "completion_tokens_details": {
3357
- "reasoning_tokens": 10,
3358
- },
3359
- "prompt_tokens": 15,
3360
- "total_tokens": 35,
3361
- },
3362
- },
3363
- },
3364
- ]
3365
- `);
3366
- });
3367
- });
3368
-
3369
- describe('raw chunks', () => {
3370
- it('should include raw chunks when includeRawChunks is enabled', async () => {
3371
- prepareStreamResponse({
3372
- content: ['Hello', ' World!'],
3373
- });
3374
-
3375
- const { stream } = await model.doStream({
3376
- prompt: TEST_PROMPT,
3377
- includeRawChunks: true,
3378
- });
3379
-
3380
- const chunks = await convertReadableStreamToArray(stream);
3381
-
3382
- expect(chunks.filter(chunk => chunk.type === 'raw'))
3383
- .toMatchInlineSnapshot(`
3384
- [
3385
- {
3386
- "rawValue": {
3387
- "choices": [
3388
- {
3389
- "delta": {
3390
- "content": "",
3391
- "role": "assistant",
3392
- },
3393
- "finish_reason": null,
3394
- "index": 0,
3395
- },
3396
- ],
3397
- "created": 1702657020,
3398
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3399
- "model": "gpt-3.5-turbo-0613",
3400
- "object": "chat.completion.chunk",
3401
- "system_fingerprint": null,
3402
- },
3403
- "type": "raw",
3404
- },
3405
- {
3406
- "rawValue": {
3407
- "choices": [
3408
- {
3409
- "delta": {
3410
- "content": "Hello",
3411
- },
3412
- "finish_reason": null,
3413
- "index": 1,
3414
- },
3415
- ],
3416
- "created": 1702657020,
3417
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3418
- "model": "gpt-3.5-turbo-0613",
3419
- "object": "chat.completion.chunk",
3420
- "system_fingerprint": null,
3421
- },
3422
- "type": "raw",
3423
- },
3424
- {
3425
- "rawValue": {
3426
- "choices": [
3427
- {
3428
- "delta": {
3429
- "content": " World!",
3430
- },
3431
- "finish_reason": null,
3432
- "index": 1,
3433
- },
3434
- ],
3435
- "created": 1702657020,
3436
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3437
- "model": "gpt-3.5-turbo-0613",
3438
- "object": "chat.completion.chunk",
3439
- "system_fingerprint": null,
3440
- },
3441
- "type": "raw",
3442
- },
3443
- {
3444
- "rawValue": {
3445
- "choices": [
3446
- {
3447
- "delta": {},
3448
- "finish_reason": "stop",
3449
- "index": 0,
3450
- "logprobs": null,
3451
- },
3452
- ],
3453
- "created": 1702657020,
3454
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3455
- "model": "gpt-3.5-turbo-0613",
3456
- "object": "chat.completion.chunk",
3457
- "system_fingerprint": null,
3458
- },
3459
- "type": "raw",
3460
- },
3461
- {
3462
- "rawValue": {
3463
- "choices": [],
3464
- "created": 1702657020,
3465
- "id": "chatcmpl-96aZqmeDpA9IPD6tACY8djkMsJCMP",
3466
- "model": "gpt-3.5-turbo-0613",
3467
- "object": "chat.completion.chunk",
3468
- "system_fingerprint": "fp_3bc1b5746c",
3469
- "usage": {
3470
- "completion_tokens": 227,
3471
- "prompt_tokens": 17,
3472
- "total_tokens": 244,
3473
- },
3474
- },
3475
- "type": "raw",
3476
- },
3477
- ]
3478
- `);
3479
- });
3480
-
3481
- it('should not include raw chunks when includeRawChunks is false', async () => {
3482
- prepareStreamResponse({
3483
- content: ['Hello', ' World!'],
3484
- });
3485
-
3486
- const { stream } = await model.doStream({
3487
- prompt: TEST_PROMPT,
3488
- includeRawChunks: false,
3489
- });
3490
-
3491
- const chunks = await convertReadableStreamToArray(stream);
3492
-
3493
- expect(chunks.filter(chunk => chunk.type === 'raw')).toHaveLength(0);
3494
- });
3495
- });
3496
- });