|
1 | | -import OpenAI, { toFile } from 'openai'; |
| 1 | +import OpenAI, { APIUserAbortError, toFile } from 'openai'; |
2 | 2 | import { TranscriptionCreateParams } from 'openai/resources/audio/transcriptions'; |
3 | 3 | import fetch from 'node-fetch'; |
4 | 4 | import { File as FormDataFile, Blob as FormDataBlob } from 'formdata-node'; |
@@ -68,6 +68,92 @@ it(`streaming works`, async function () { |
68 | 68 | expect(chunks.map((c) => c.choices[0]?.delta.content || '').join('')).toBeSimilarTo('This is a test', 10); |
69 | 69 | }); |
70 | 70 |
|
| 71 | +it(`ChatCompletionStream works`, async function () { |
| 72 | + const chunks: OpenAI.Chat.ChatCompletionChunk[] = []; |
| 73 | + const contents: [string, string][] = []; |
| 74 | + const messages: OpenAI.Chat.ChatCompletionMessage[] = []; |
| 75 | + const chatCompletions: OpenAI.Chat.ChatCompletion[] = []; |
| 76 | + let finalContent: string | undefined; |
| 77 | + let finalMessage: OpenAI.Chat.ChatCompletionMessage | undefined; |
| 78 | + let finalChatCompletion: OpenAI.Chat.ChatCompletion | undefined; |
| 79 | + |
| 80 | + const stream = client.beta.chat.completions |
| 81 | + .stream({ |
| 82 | + model: 'gpt-4', |
| 83 | + messages: [{ role: 'user', content: 'Say this is a test' }], |
| 84 | + }) |
| 85 | + .on('chunk', (chunk) => chunks.push(chunk)) |
| 86 | + .on('content', (delta, snapshot) => contents.push([delta, snapshot])) |
| 87 | + .on('message', (message) => messages.push(message)) |
| 88 | + .on('chatCompletion', (completion) => chatCompletions.push(completion)) |
| 89 | + .on('finalContent', (content) => (finalContent = content)) |
| 90 | + .on('finalMessage', (message) => (finalMessage = message)) |
| 91 | + .on('finalChatCompletion', (completion) => (finalChatCompletion = completion)); |
| 92 | + const content = await stream.finalContent(); |
| 93 | + |
| 94 | + expect(content).toBeSimilarTo('This is a test', 10); |
| 95 | + expect(chunks.length).toBeGreaterThan(0); |
| 96 | + expect(contents.length).toBeGreaterThan(0); |
| 97 | + for (const chunk of chunks) { |
| 98 | + expect(chunk.id).toEqual(finalChatCompletion?.id); |
| 99 | + expect(chunk.created).toEqual(finalChatCompletion?.created); |
| 100 | + expect(chunk.model).toEqual(finalChatCompletion?.model); |
| 101 | + } |
| 102 | + expect(finalContent).toEqual(content); |
| 103 | + expect(contents.at(-1)?.[1]).toEqual(content); |
| 104 | + expect(finalMessage?.content).toEqual(content); |
| 105 | + expect(finalChatCompletion?.choices?.[0]?.message.content).toEqual(content); |
| 106 | + expect(messages).toEqual([finalMessage]); |
| 107 | + expect(chatCompletions).toEqual([finalChatCompletion]); |
| 108 | + expect(await stream.finalContent()).toEqual(content); |
| 109 | + expect(await stream.finalMessage()).toEqual(finalMessage); |
| 110 | + expect(await stream.finalChatCompletion()).toEqual(finalChatCompletion); |
| 111 | +}); |
| 112 | + |
| 113 | +it(`aborting ChatCompletionStream works`, async function () { |
| 114 | + const chunks: OpenAI.Chat.ChatCompletionChunk[] = []; |
| 115 | + const contents: [string, string][] = []; |
| 116 | + const messages: OpenAI.Chat.ChatCompletionMessage[] = []; |
| 117 | + const chatCompletions: OpenAI.Chat.ChatCompletion[] = []; |
| 118 | + let finalContent: string | undefined; |
| 119 | + let finalMessage: OpenAI.Chat.ChatCompletionMessage | undefined; |
| 120 | + let finalChatCompletion: OpenAI.Chat.ChatCompletion | undefined; |
| 121 | + let emittedError: any; |
| 122 | + let caughtError: any; |
| 123 | + const controller = new AbortController(); |
| 124 | + const stream = client.beta.chat.completions |
| 125 | + .stream( |
| 126 | + { |
| 127 | + model: 'gpt-4', |
| 128 | + messages: [{ role: 'user', content: 'Say this is a test' }], |
| 129 | + }, |
| 130 | + { signal: controller.signal }, |
| 131 | + ) |
| 132 | + .on('error', (e) => (emittedError = e)) |
| 133 | + .on('chunk', (chunk) => chunks.push(chunk)) |
| 134 | + .on('content', (delta, snapshot) => { |
| 135 | + contents.push([delta, snapshot]); |
| 136 | + controller.abort(); |
| 137 | + }) |
| 138 | + .on('message', (message) => messages.push(message)) |
| 139 | + .on('chatCompletion', (completion) => chatCompletions.push(completion)) |
| 140 | + .on('finalContent', (content) => (finalContent = content)) |
| 141 | + .on('finalMessage', (message) => (finalMessage = message)) |
| 142 | + .on('finalChatCompletion', (completion) => (finalChatCompletion = completion)); |
| 143 | + try { |
| 144 | + await stream.finalContent(); |
| 145 | + } catch (error) { |
| 146 | + caughtError = error; |
| 147 | + } |
| 148 | + expect(caughtError).toBeInstanceOf(APIUserAbortError); |
| 149 | + expect(finalContent).toBeUndefined(); |
| 150 | + expect(finalMessage).toBeUndefined(); |
| 151 | + expect(finalChatCompletion).toBeUndefined(); |
| 152 | + expect(chatCompletions).toEqual([]); |
| 153 | + expect(chunks.length).toBeGreaterThan(0); |
| 154 | + expect(contents.length).toBeGreaterThan(0); |
| 155 | +}); |
| 156 | + |
71 | 157 | it('handles formdata-node File', async function () { |
72 | 158 | const file = await fetch(url) |
73 | 159 | .then((x) => x.arrayBuffer()) |
|
0 commit comments