|
1 |
| -import OpenAI from 'openai'; |
2 |
| -import { TranscriptionCreateParams } from 'openai/resources/audio'; |
| 1 | +import OpenAI, { toFile } from 'openai'; |
| 2 | +import { distance } from 'fastest-levenshtein'; |
3 | 3 |
|
4 |
| -// smoke test that importing and constructing the client works |
5 |
| -const openai = new OpenAI({ apiKey: '<invalid API key>' }); |
6 |
| -console.log(openai); |
| 4 | +type TestCase = { |
| 5 | + path: string[]; |
| 6 | + run: () => any; |
| 7 | + timeout?: number; |
| 8 | +}; |
| 9 | + |
| 10 | +const tests: TestCase[] = []; |
| 11 | + |
| 12 | +type TestResult = { path: string[]; passed: boolean; error?: string }; |
| 13 | + |
| 14 | +async function runTests() { |
| 15 | + const results: TestResult[] = []; |
| 16 | + function displayResults() { |
| 17 | + let pre = document.getElementById('results'); |
| 18 | + if (!pre) { |
| 19 | + pre = document.createElement('pre'); |
| 20 | + pre.id = 'results'; |
| 21 | + document.body.appendChild(pre); |
| 22 | + } |
| 23 | + pre.innerText = JSON.stringify(results, null, 2); |
| 24 | + } |
| 25 | + for (const { path, run, timeout } of tests) { |
| 26 | + console.log('running', ...path); |
| 27 | + try { |
| 28 | + await Promise.race([ |
| 29 | + run(), |
| 30 | + new Promise((_, reject) => |
| 31 | + setTimeout(() => reject(new Error(`Test timed out after ${timeout} ms`)), timeout), |
| 32 | + ), |
| 33 | + ]); |
| 34 | + console.log('passed ', ...path); |
| 35 | + results.push({ path, passed: true }); |
| 36 | + } catch (error) { |
| 37 | + console.log('error ', ...path); |
| 38 | + console.error(error); |
| 39 | + results.push({ path, passed: false, error: error instanceof Error ? error.stack : String(error) }); |
| 40 | + } |
| 41 | + displayResults(); |
| 42 | + } |
| 43 | + const runningEl = document.getElementById('running'); |
| 44 | + if (runningEl) runningEl.remove(); |
| 45 | +} |
| 46 | + |
| 47 | +const testPath: string[] = []; |
| 48 | + |
| 49 | +function describe(description: string, handler: () => void) { |
| 50 | + testPath.push(description); |
| 51 | + try { |
| 52 | + handler(); |
| 53 | + } finally { |
| 54 | + testPath.pop(); |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +function it(description: string, run: () => any, timeout = 15000) { |
| 59 | + tests.push({ path: [...testPath, description], run, timeout }); |
| 60 | +} |
| 61 | + |
| 62 | +function expect(received: any) { |
| 63 | + return { |
| 64 | + toEqual(expected: any): void { |
| 65 | + if (!Object.is(received, expected)) { |
| 66 | + throw new Error( |
| 67 | + [`Received: ${JSON.stringify(received)}`, `Expected: ${JSON.stringify(expected)}`].join('\n'), |
| 68 | + ); |
| 69 | + } |
| 70 | + }, |
| 71 | + toBeSimilarTo(comparedTo: string, expectedDistance: number) { |
| 72 | + const actualDistance = distance(received, comparedTo); |
| 73 | + if (actualDistance < expectedDistance) return; |
7 | 74 |
|
8 |
| -// smoke test that making an API request works, even if it errors |
9 |
| -openai.completions |
10 |
| - .create({ |
11 |
| - prompt: 'Say this is a test', |
12 |
| - model: 'text-davinci-003', |
13 |
| - }) |
14 |
| - .catch((err) => console.error(err)); |
| 75 | + throw new Error( |
| 76 | + [ |
| 77 | + `Received: ${JSON.stringify(received)}`, |
| 78 | + `Expected: ${JSON.stringify(comparedTo)}`, |
| 79 | + `Expected distance: ${expectedDistance}`, |
| 80 | + `Received distance: ${actualDistance}`, |
| 81 | + ].join('\n'), |
| 82 | + ); |
| 83 | + }, |
| 84 | + }; |
| 85 | +} |
| 86 | + |
| 87 | +const url = 'https://audio-samples.github.io/samples/mp3/blizzard_biased/sample-1.mp3'; |
| 88 | +const filename = 'sample-1.mp3'; |
| 89 | + |
| 90 | +const correctAnswer = |
| 91 | + 'It was anxious to find him no one that expectation of a man who were giving his father enjoyment. But he was avoided in sight in the minister to which indeed,'; |
| 92 | +const model = 'whisper-1'; |
| 93 | + |
| 94 | +const params = new URLSearchParams(location.search); |
| 95 | + |
| 96 | +const client = new OpenAI({ apiKey: params.get('apiKey') ?? undefined }); |
15 | 97 |
|
16 | 98 | async function typeTests() {
|
17 | 99 | // @ts-expect-error this should error if the `Uploadable` type was resolved correctly
|
18 |
| - await openai.audio.transcriptions.create({ file: { foo: true }, model: 'whisper-1' }); |
| 100 | + await client.audio.transcriptions.create({ file: { foo: true }, model: 'whisper-1' }); |
19 | 101 | // @ts-expect-error this should error if the `Uploadable` type was resolved correctly
|
20 |
| - await openai.audio.transcriptions.create({ file: null, model: 'whisper-1' }); |
| 102 | + await client.audio.transcriptions.create({ file: null, model: 'whisper-1' }); |
21 | 103 | // @ts-expect-error this should error if the `Uploadable` type was resolved correctly
|
22 |
| - await openai.audio.transcriptions.create({ file: 'test', model: 'whisper-1' }); |
| 104 | + await client.audio.transcriptions.create({ file: 'test', model: 'whisper-1' }); |
| 105 | +} |
| 106 | + |
| 107 | +if (typeof File !== 'undefined') { |
| 108 | + it('handles builtinFile', async function () { |
| 109 | + const file = await fetch(url) |
| 110 | + .then((x) => x.arrayBuffer()) |
| 111 | + .then((x) => new File([x], filename)); |
23 | 112 |
|
24 |
| - const url = 'https://audio-samples.github.io/samples/mp3/blizzard_biased/sample-1.mp3'; |
25 |
| - const model = 'whisper-1'; |
| 113 | + const result = await client.audio.transcriptions.create({ file, model }); |
| 114 | + expect(result.text).toBeSimilarTo(correctAnswer, 12); |
| 115 | + }); |
| 116 | +} |
26 | 117 |
|
| 118 | +it('handles Response', async function () { |
27 | 119 | const file = await fetch(url);
|
28 |
| - const params: TranscriptionCreateParams = { file, model }; |
29 | 120 |
|
30 |
| - await openai.audio.transcriptions.create(params); |
31 |
| -} |
| 121 | + const result = await client.audio.transcriptions.create({ file, model }); |
| 122 | + expect(result.text).toBeSimilarTo(correctAnswer, 12); |
| 123 | +}); |
32 | 124 |
|
33 |
| -// -------- |
34 |
| -class Greeter { |
35 |
| - greeting: string; |
36 |
| - constructor(message: string) { |
37 |
| - this.greeting = message; |
38 |
| - } |
39 |
| - greet(): string { |
40 |
| - return `Hello, ${this.greeting}`; |
41 |
| - } |
42 |
| -} |
| 125 | +const fineTune = `{"prompt": "<prompt text>", "completion": "<ideal generated text>"}`; |
43 | 126 |
|
44 |
| -const greeter = new Greeter('world'); |
| 127 | +describe('toFile', () => { |
| 128 | + if (typeof Blob !== 'undefined') { |
| 129 | + it('handles builtin Blob', async function () { |
| 130 | + const result = await client.files.create({ |
| 131 | + file: await toFile( |
| 132 | + // @ts-ignore avoid DOM lib for testing purposes |
| 133 | + new Blob([new TextEncoder().encode(fineTune)]), |
| 134 | + 'finetune.jsonl', |
| 135 | + ), |
| 136 | + purpose: 'fine-tune', |
| 137 | + }); |
| 138 | + expect(result.status).toEqual('uploaded'); |
| 139 | + }); |
| 140 | + } |
| 141 | + it('handles Uint8Array', async function () { |
| 142 | + const result = await client.files.create({ |
| 143 | + file: await toFile(new TextEncoder().encode(fineTune), 'finetune.jsonl'), |
| 144 | + purpose: 'fine-tune', |
| 145 | + }); |
| 146 | + expect(result.status).toEqual('uploaded'); |
| 147 | + }); |
| 148 | + it('handles ArrayBuffer', async function () { |
| 149 | + const result = await client.files.create({ |
| 150 | + file: await toFile(new TextEncoder().encode(fineTune).buffer, 'finetune.jsonl'), |
| 151 | + purpose: 'fine-tune', |
| 152 | + }); |
| 153 | + expect(result.status).toEqual('uploaded'); |
| 154 | + }); |
| 155 | + it('handles DataView', async function () { |
| 156 | + const result = await client.files.create({ |
| 157 | + file: await toFile(new DataView(new TextEncoder().encode(fineTune).buffer), 'finetune.jsonl'), |
| 158 | + purpose: 'fine-tune', |
| 159 | + }); |
| 160 | + expect(result.status).toEqual('uploaded'); |
| 161 | + }); |
| 162 | +}); |
45 | 163 |
|
46 |
| -const button = document.getElementById('myButton')!; |
47 |
| -button.onclick = () => { |
48 |
| - alert(greeter.greet()); |
49 |
| -}; |
| 164 | +runTests(); |
0 commit comments