@volter/twin-openai 0.1.2 → 2.0.1
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.
- package/README.md +33 -30
- package/defaults/handlers.json +10 -0
- package/dist/defaults/handlers.json +10 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +29 -0
- package/dist/src/generated/surface.gen.json +1 -0
- package/dist/src/generated/ui.gen.json +1 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.js +72 -0
- package/dist/src/manifest.d.ts +6 -0
- package/dist/src/manifest.js +323 -0
- package/dist/src/openai-budget.d.ts +53 -0
- package/dist/src/openai-budget.js +147 -0
- package/dist/src/openai-capabilities.d.ts +4 -0
- package/dist/src/openai-capabilities.js +1569 -0
- package/dist/src/openai-conformance.d.ts +13 -0
- package/dist/src/openai-conformance.js +116 -0
- package/dist/src/openai-connector.d.ts +86 -0
- package/dist/src/openai-connector.js +291 -0
- package/dist/src/openai-media.d.ts +43 -0
- package/dist/src/openai-media.js +257 -0
- package/dist/src/openai-models.d.ts +74 -0
- package/dist/src/openai-models.js +148 -0
- package/dist/src/openai-scenario.d.ts +51 -0
- package/dist/src/openai-scenario.js +166 -0
- package/dist/src/openai-server.d.ts +40 -0
- package/dist/src/openai-server.js +126 -0
- package/dist/src/openai-stub.d.ts +82 -0
- package/dist/src/openai-stub.js +256 -0
- package/dist/src/openai-twin.d.ts +182 -0
- package/dist/src/openai-twin.js +1117 -0
- package/dist/src/openai-types.d.ts +194 -0
- package/dist/src/openai-types.js +4 -0
- package/dist/src/openai-webhooks.d.ts +47 -0
- package/dist/src/openai-webhooks.js +99 -0
- package/dist/src/screens/api-keys.d.ts +16 -0
- package/dist/src/screens/api-keys.js +131 -0
- package/dist/src/screens/session.d.ts +22 -0
- package/dist/src/screens/session.js +115 -0
- package/dist/src/semantics/assistants.d.ts +2 -0
- package/dist/src/semantics/assistants.js +331 -0
- package/dist/src/semantics/audio.d.ts +2 -0
- package/dist/src/semantics/audio.js +27 -0
- package/dist/src/semantics/batches.d.ts +4 -0
- package/dist/src/semantics/batches.js +86 -0
- package/dist/src/semantics/chat-completions.d.ts +3 -0
- package/dist/src/semantics/chat-completions.js +58 -0
- package/dist/src/semantics/containers.d.ts +2 -0
- package/dist/src/semantics/containers.js +147 -0
- package/dist/src/semantics/embeddings.d.ts +2 -0
- package/dist/src/semantics/embeddings.js +13 -0
- package/dist/src/semantics/evals.d.ts +2 -0
- package/dist/src/semantics/evals.js +173 -0
- package/dist/src/semantics/files.d.ts +13 -0
- package/dist/src/semantics/files.js +59 -0
- package/dist/src/semantics/fine-tuning.d.ts +4 -0
- package/dist/src/semantics/fine-tuning.js +178 -0
- package/dist/src/semantics/images.d.ts +2 -0
- package/dist/src/semantics/images.js +18 -0
- package/dist/src/semantics/index.d.ts +8 -0
- package/dist/src/semantics/index.js +46 -0
- package/dist/src/semantics/models.d.ts +2 -0
- package/dist/src/semantics/models.js +34 -0
- package/dist/src/semantics/moderations.d.ts +2 -0
- package/dist/src/semantics/moderations.js +12 -0
- package/dist/src/semantics/organization.d.ts +2 -0
- package/dist/src/semantics/organization.js +67 -0
- package/dist/src/semantics/progress.d.ts +22 -0
- package/dist/src/semantics/progress.js +63 -0
- package/dist/src/semantics/responses.d.ts +3 -0
- package/dist/src/semantics/responses.js +153 -0
- package/dist/src/semantics/shared.d.ts +32 -0
- package/dist/src/semantics/shared.js +69 -0
- package/dist/src/semantics/uploads.d.ts +2 -0
- package/dist/src/semantics/uploads.js +84 -0
- package/dist/src/semantics/vector-stores.d.ts +2 -0
- package/dist/src/semantics/vector-stores.js +281 -0
- package/dist/test-fixtures/openai-openapi-operations.SOURCE.md +18 -0
- package/dist/test-fixtures/openai-openapi-operations.json +1849 -0
- package/package.json +21 -10
- package/src/cli.ts +9 -7
- package/src/generated/surface.gen.json +1 -0
- package/src/generated/ui.gen.json +1 -0
- package/src/index.ts +20 -10
- package/src/manifest.ts +343 -0
- package/src/openai-budget.ts +4 -4
- package/src/openai-capabilities.ts +177 -195
- package/src/openai-conformance.ts +1 -1
- package/src/openai-connector.ts +40 -43
- package/src/openai-media.ts +225 -0
- package/src/openai-models.ts +145 -15
- package/src/openai-scenario.ts +46 -10
- package/src/openai-server.ts +65 -108
- package/src/openai-stub.ts +54 -30
- package/src/openai-twin.ts +760 -1665
- package/src/openai-types.ts +24 -6
- package/src/openai-webhooks.ts +3 -2
- package/src/screens/api-keys.tsx +138 -0
- package/src/screens/session.tsx +131 -0
- package/src/semantics/assistants.ts +336 -0
- package/src/semantics/audio.ts +31 -0
- package/src/semantics/batches.ts +88 -0
- package/src/semantics/chat-completions.ts +66 -0
- package/src/semantics/containers.ts +151 -0
- package/src/semantics/embeddings.ts +19 -0
- package/src/semantics/evals.ts +182 -0
- package/src/semantics/files.ts +67 -0
- package/src/semantics/fine-tuning.ts +185 -0
- package/src/semantics/images.ts +23 -0
- package/src/semantics/index.ts +52 -0
- package/src/semantics/models.ts +41 -0
- package/src/semantics/moderations.ts +14 -0
- package/src/semantics/organization.ts +76 -0
- package/src/semantics/progress.ts +72 -0
- package/src/semantics/responses.ts +151 -0
- package/src/semantics/shared.ts +82 -0
- package/src/semantics/uploads.ts +92 -0
- package/src/semantics/vector-stores.ts +279 -0
- package/test-fixtures/openai-openapi-operations.SOURCE.md +4 -5
- package/test-fixtures/openai-openapi-operations.json +224 -1334
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// The media an image or audio endpoint takes and gives. OpenAI takes an uploaded image or audio file only in the
|
|
2
|
+
// formats its reference names, told by the file's bytes, and a GPT image model answers the image itself as base64:
|
|
3
|
+
// • an image to edit is "a `png`, `webp`, or `jpg` file" for the GPT image models, and for `dall-e-2` "a square
|
|
4
|
+
// `png`" (developers.openai.com/api/reference/resources/images/methods/edit, `image`); a variation's is "a valid
|
|
5
|
+
// PNG file, less than 4MB, and square" (…/images/methods/create_variation, `image`);
|
|
6
|
+
// • a transcription or translation takes "the audio file object (not file name)" in "one of these formats: flac,
|
|
7
|
+
// mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm" (…/audio/subresources/transcriptions/methods/create, `file`);
|
|
8
|
+
// • the GPT image models "always return base64-encoded images" (…/images/methods/generate, `response_format`).
|
|
9
|
+
// Where the documentation stops and the twin decides: a format is told by its signature bytes; the image the twin
|
|
10
|
+
// answers is a real PNG of the requested size in one flat colour drawn from the prompt, labeled in its own text
|
|
11
|
+
// chunk, since the twin renders no pixels.
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { deflateSync } from 'node:zlib';
|
|
14
|
+
/** The raw bytes of an uploaded file part, as the request's multipart parse keeps them; undefined for a string. */
|
|
15
|
+
export function partBytes(v) {
|
|
16
|
+
const b = v && typeof v === 'object' ? v.bytes : undefined;
|
|
17
|
+
return b instanceof Uint8Array ? b : undefined;
|
|
18
|
+
}
|
|
19
|
+
const has = (b, sig, at = 0) => (typeof sig === 'string' ? [...sig].map((c) => c.charCodeAt(0)) : sig).every((x, i) => b[at + i] === x);
|
|
20
|
+
const PNG = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
21
|
+
/** Each image format a signature tells, checked in this order. */
|
|
22
|
+
const IMAGE = [
|
|
23
|
+
['jpg', (b) => has(b, [0xff, 0xd8, 0xff])],
|
|
24
|
+
['webp', (b) => has(b, 'RIFF') && has(b, 'WEBP', 8)],
|
|
25
|
+
['png', (b) => has(b, PNG) && has(b, 'IHDR', 12)],
|
|
26
|
+
];
|
|
27
|
+
/** An image's format by its signature: png, jpg or webp, else undefined. */
|
|
28
|
+
export const imageFormat = (b) => IMAGE.find(([, is]) => is(b))?.[0];
|
|
29
|
+
/** A PNG's width and height from its IHDR chunk. */
|
|
30
|
+
export function pngSize(b) {
|
|
31
|
+
const v = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
32
|
+
return { width: v.getUint32(16), height: v.getUint32(20) };
|
|
33
|
+
}
|
|
34
|
+
/** Each audio format OpenAI transcribes that a signature tells, checked in this order. */
|
|
35
|
+
const AUDIO = [
|
|
36
|
+
['flac', (b) => has(b, 'fLaC')],
|
|
37
|
+
['ogg', (b) => has(b, 'OggS')],
|
|
38
|
+
['webm', (b) => has(b, [0x1a, 0x45, 0xdf, 0xa3])],
|
|
39
|
+
['mp4', (b) => has(b, 'ftyp', 4)],
|
|
40
|
+
['mp3', (b) => has(b, 'ID3') || (b[0] === 0xff && ((b[1] ?? 0) & 0xe0) === 0xe0)],
|
|
41
|
+
['wav', (b) => has(b, 'RIFF') && has(b, 'WAVE', 8)],
|
|
42
|
+
];
|
|
43
|
+
/** An audio file's format by its signature, among those OpenAI transcribes, else undefined. */
|
|
44
|
+
export const audioFormat = (b) => AUDIO.find(([, is]) => is(b))?.[0];
|
|
45
|
+
/** A WAV file's length in seconds, from its `fmt ` byte rate and its `data` size (RIFF chunks in any order); undefined
|
|
46
|
+
* for any other format, whose length the twin does not read. */
|
|
47
|
+
export function wavSeconds(b) {
|
|
48
|
+
if (!has(b, 'RIFF') || !has(b, 'WAVE', 8))
|
|
49
|
+
return undefined;
|
|
50
|
+
const v = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
51
|
+
let rate = 0;
|
|
52
|
+
let data = 0;
|
|
53
|
+
for (let at = 12; at + 8 <= b.length; at += 8 + v.getUint32(at + 4, true) + (v.getUint32(at + 4, true) & 1)) {
|
|
54
|
+
if (has(b, 'fmt ', at))
|
|
55
|
+
rate = v.getUint32(at + 16, true);
|
|
56
|
+
if (has(b, 'data', at))
|
|
57
|
+
data = v.getUint32(at + 4, true);
|
|
58
|
+
}
|
|
59
|
+
return rate > 0 ? Math.round((data / rate) * 1000) / 1000 : undefined;
|
|
60
|
+
}
|
|
61
|
+
const CRC = Array.from({ length: 256 }, (_, n) => {
|
|
62
|
+
let c = n;
|
|
63
|
+
for (let k = 0; k < 8; k++)
|
|
64
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
65
|
+
return c >>> 0;
|
|
66
|
+
});
|
|
67
|
+
function crc32(b) {
|
|
68
|
+
let c = 0xffffffff;
|
|
69
|
+
for (const x of b)
|
|
70
|
+
c = CRC[(c ^ x) & 0xff] ^ (c >>> 8);
|
|
71
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
72
|
+
}
|
|
73
|
+
function chunk(type, data) {
|
|
74
|
+
const out = new Uint8Array(12 + data.length);
|
|
75
|
+
const v = new DataView(out.buffer);
|
|
76
|
+
v.setUint32(0, data.length);
|
|
77
|
+
out.set([...type].map((c) => c.charCodeAt(0)), 4);
|
|
78
|
+
out.set(data, 8);
|
|
79
|
+
v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** The size an image request asks for (`1536x1024`), or the fallback for `auto` or none. */
|
|
83
|
+
export function requestedSize(size, fallback = { width: 1024, height: 1024 }) {
|
|
84
|
+
const m = typeof size === 'string' ? /^(\d+)x(\d+)$/.exec(size) : null;
|
|
85
|
+
return m ? { width: Number(m[1]), height: Number(m[2]) } : fallback;
|
|
86
|
+
}
|
|
87
|
+
/** A real PNG of the size asked, one flat colour drawn from `seed`, labeled as the twin's placeholder. */
|
|
88
|
+
export function placeholderPng(size, seed) {
|
|
89
|
+
const [r, g, bl] = createHash('sha256').update(seed).digest();
|
|
90
|
+
const { width, height } = size;
|
|
91
|
+
const ihdr = new Uint8Array(13);
|
|
92
|
+
const v = new DataView(ihdr.buffer);
|
|
93
|
+
v.setUint32(0, width);
|
|
94
|
+
v.setUint32(4, height);
|
|
95
|
+
ihdr.set([8, 2, 0, 0, 0], 8); // 8-bit RGB, no interlace
|
|
96
|
+
const row = new Uint8Array(1 + width * 3);
|
|
97
|
+
for (let x = 0; x < width; x++)
|
|
98
|
+
row.set([r, g, bl], 1 + x * 3);
|
|
99
|
+
const raw = new Uint8Array(row.length * height);
|
|
100
|
+
for (let y = 0; y < height; y++)
|
|
101
|
+
raw.set(row, y * row.length);
|
|
102
|
+
const text = new TextEncoder().encode(`Comment\0[twin-stub] placeholder image, no model is run: ${seed}`);
|
|
103
|
+
const parts = [Uint8Array.from(PNG), chunk('IHDR', ihdr), chunk('tEXt', text), chunk('IDAT', new Uint8Array(deflateSync(raw))), chunk('IEND', new Uint8Array(0))];
|
|
104
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
105
|
+
let at = 0;
|
|
106
|
+
for (const p of parts) {
|
|
107
|
+
out.set(p, at);
|
|
108
|
+
at += p.length;
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/** Bytes as base64. */
|
|
113
|
+
export function base64(b) {
|
|
114
|
+
let bin = '';
|
|
115
|
+
for (let i = 0; i < b.length; i += 0x8000)
|
|
116
|
+
bin += String.fromCharCode(...b.subarray(i, i + 0x8000));
|
|
117
|
+
return btoa(bin);
|
|
118
|
+
}
|
|
119
|
+
// ── Speech: the audio file itself ────────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
// Text to speech answers "the audio file content" in the requested `response_format`, `mp3` by default, of mp3, opus,
|
|
121
|
+
// aac, flac, wav and pcm (developers.openai.com/api/reference/resources/audio/subresources/speech/methods/create); its
|
|
122
|
+
// audio is 24 kHz mono. The twin speaks nothing: each is a real file of that format, a second of silence.
|
|
123
|
+
const RATE = 24000;
|
|
124
|
+
/** A second of 24 kHz 16-bit mono silence as samples' bytes: `pcm`, raw little-endian with no header. */
|
|
125
|
+
export const silentPcm = () => new Uint8Array(RATE * 2);
|
|
126
|
+
/** The same second as a WAV file. */
|
|
127
|
+
export function silentWav() {
|
|
128
|
+
const data = RATE * 2;
|
|
129
|
+
const b = new Uint8Array(44 + data);
|
|
130
|
+
const v = new DataView(b.buffer);
|
|
131
|
+
b.set(ascii('RIFF'), 0);
|
|
132
|
+
v.setUint32(4, 36 + data, true);
|
|
133
|
+
b.set(ascii('WAVEfmt '), 8);
|
|
134
|
+
v.setUint32(16, 16, true);
|
|
135
|
+
v.setUint16(20, 1, true);
|
|
136
|
+
v.setUint16(22, 1, true);
|
|
137
|
+
v.setUint32(24, RATE, true);
|
|
138
|
+
v.setUint32(28, RATE * 2, true);
|
|
139
|
+
v.setUint16(32, 2, true);
|
|
140
|
+
v.setUint16(34, 16, true);
|
|
141
|
+
b.set(ascii('data'), 36);
|
|
142
|
+
v.setUint32(40, data, true);
|
|
143
|
+
return b;
|
|
144
|
+
}
|
|
145
|
+
/** The same second as MP3: MPEG-2 Layer III frames at 24 kHz, 32 kbit/s, mono, each 576 samples in 96 bytes whose
|
|
146
|
+
* side information codes no spectral data, which a decoder plays as silence. */
|
|
147
|
+
export function silentMp3() {
|
|
148
|
+
const frames = Math.ceil(RATE / 576);
|
|
149
|
+
const b = new Uint8Array(frames * 96);
|
|
150
|
+
for (let i = 0; i < frames; i++)
|
|
151
|
+
b.set([0xff, 0xf3, 0x44, 0xc4], i * 96);
|
|
152
|
+
return b;
|
|
153
|
+
}
|
|
154
|
+
/** The same second as FLAC: a STREAMINFO block and frames of 4096 samples, each one CONSTANT subframe of zero. */
|
|
155
|
+
export function silentFlac() {
|
|
156
|
+
const block = 4096;
|
|
157
|
+
const info = new Bits();
|
|
158
|
+
info.put(block, 16);
|
|
159
|
+
info.put(block, 16);
|
|
160
|
+
info.put(0, 24);
|
|
161
|
+
info.put(0, 24);
|
|
162
|
+
info.put(RATE, 20);
|
|
163
|
+
info.put(0, 3);
|
|
164
|
+
info.put(15, 5);
|
|
165
|
+
info.put(0, 4);
|
|
166
|
+
info.put(RATE, 32);
|
|
167
|
+
for (let i = 0; i < 16; i++)
|
|
168
|
+
info.put(0, 8); // MD5 unknown
|
|
169
|
+
const out = [...ascii('fLaC'), 0x80, 0, 0, 34, ...info.bytes()];
|
|
170
|
+
for (let n = 0, left = RATE; left > 0; n++, left -= block) {
|
|
171
|
+
const size = Math.min(block, left);
|
|
172
|
+
// blocksize 4096 (code 12) or, for the last, 16 bits of size-1 at the header's end (code 7); 24 kHz is code 7
|
|
173
|
+
const head = [0xff, 0xf8, ((size === block ? 12 : 7) << 4) | 7, 0x08, n, ...(size === block ? [] : [(size - 1) >> 8, (size - 1) & 0xff])];
|
|
174
|
+
const frame = [...head, crc8(head), 0x00, 0x00, 0x00];
|
|
175
|
+
const c = crc16(frame);
|
|
176
|
+
out.push(...frame, c >> 8, c & 0xff);
|
|
177
|
+
}
|
|
178
|
+
return Uint8Array.from(out);
|
|
179
|
+
}
|
|
180
|
+
/** The same second as AAC in ADTS: AAC-LC frames of 1024 samples at 24 kHz, mono, each one channel element with
|
|
181
|
+
* no scale-factor bands, which a decoder plays as silence. */
|
|
182
|
+
export function silentAac() {
|
|
183
|
+
const payload = [0x00, 0x00, 0x00, 0x07]; // SCE, global gain 0, max_sfb 0, no tools; END
|
|
184
|
+
const length = 7 + payload.length;
|
|
185
|
+
const frame = new Bits();
|
|
186
|
+
frame.put(0xfff, 12);
|
|
187
|
+
frame.put(0, 1);
|
|
188
|
+
frame.put(0, 2);
|
|
189
|
+
frame.put(1, 1); // MPEG-4, no CRC
|
|
190
|
+
frame.put(1, 2);
|
|
191
|
+
frame.put(6, 4);
|
|
192
|
+
frame.put(0, 1);
|
|
193
|
+
frame.put(1, 3); // AAC-LC, 24 kHz, mono
|
|
194
|
+
frame.put(0, 4);
|
|
195
|
+
frame.put(length, 13);
|
|
196
|
+
frame.put(0x7ff, 11);
|
|
197
|
+
frame.put(0, 2);
|
|
198
|
+
const one = [...frame.bytes(), ...payload];
|
|
199
|
+
return Uint8Array.from(Array.from({ length: Math.ceil(RATE / 1024) }, () => one).flat());
|
|
200
|
+
}
|
|
201
|
+
/** The same second as Opus in Ogg: its identification and comment headers, then 20 ms packets of silence. */
|
|
202
|
+
export function silentOpus() {
|
|
203
|
+
const head = [...ascii('OpusHead'), 1, 1, 0x38, 0x01, ...le32(RATE), 0, 0, 0];
|
|
204
|
+
const tags = [...ascii('OpusTags'), ...le32(4), ...ascii('twin'), ...le32(0)];
|
|
205
|
+
const packets = Array.from({ length: 50 }, () => [0xf8, 0xff, 0xfe]);
|
|
206
|
+
return Uint8Array.from([...oggPage(0x02, 0, 0, [head]), ...oggPage(0, 0, 1, [tags]), ...oggPage(0x04, 312 + 48000, 2, packets)]);
|
|
207
|
+
}
|
|
208
|
+
const ascii = (s) => [...s].map((c) => c.charCodeAt(0));
|
|
209
|
+
const le32 = (n) => [n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >>> 24) & 0xff];
|
|
210
|
+
/** A big-endian bit writer. */
|
|
211
|
+
class Bits {
|
|
212
|
+
out = [];
|
|
213
|
+
acc = 0;
|
|
214
|
+
n = 0;
|
|
215
|
+
put(value, bits) {
|
|
216
|
+
for (let i = bits - 1; i >= 0; i--) {
|
|
217
|
+
this.acc = (this.acc << 1) | (Math.floor(value / 2 ** i) & 1);
|
|
218
|
+
if (++this.n === 8) {
|
|
219
|
+
this.out.push(this.acc);
|
|
220
|
+
this.acc = 0;
|
|
221
|
+
this.n = 0;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
bytes() { return this.out; }
|
|
226
|
+
}
|
|
227
|
+
function crc8(b) {
|
|
228
|
+
let c = 0;
|
|
229
|
+
for (const x of b) {
|
|
230
|
+
c ^= x;
|
|
231
|
+
for (let k = 0; k < 8; k++)
|
|
232
|
+
c = c & 0x80 ? ((c << 1) ^ 0x07) & 0xff : (c << 1) & 0xff;
|
|
233
|
+
}
|
|
234
|
+
return c;
|
|
235
|
+
}
|
|
236
|
+
function crc16(b) {
|
|
237
|
+
let c = 0;
|
|
238
|
+
for (const x of b) {
|
|
239
|
+
c ^= x << 8;
|
|
240
|
+
for (let k = 0; k < 8; k++)
|
|
241
|
+
c = c & 0x8000 ? ((c << 1) ^ 0x8005) & 0xffff : (c << 1) & 0xffff;
|
|
242
|
+
}
|
|
243
|
+
return c;
|
|
244
|
+
}
|
|
245
|
+
/** One Ogg page of stream 1 holding whole packets, its CRC over the page with the field zeroed. */
|
|
246
|
+
function oggPage(flags, granule, seq, packets) {
|
|
247
|
+
const lacing = packets.flatMap((p) => [...Array(Math.floor(p.length / 255)).fill(255), p.length % 255]);
|
|
248
|
+
const page = [...ascii('OggS'), 0, flags, ...le32(granule), 0, 0, 0, 0, ...le32(1), ...le32(seq), 0, 0, 0, 0, lacing.length, ...lacing, ...packets.flat()];
|
|
249
|
+
let c = 0;
|
|
250
|
+
for (const x of page) {
|
|
251
|
+
c ^= x << 24;
|
|
252
|
+
for (let k = 0; k < 8; k++)
|
|
253
|
+
c = c & 0x80000000 ? ((c << 1) ^ 0x04c11db7) >>> 0 : (c << 1) >>> 0;
|
|
254
|
+
}
|
|
255
|
+
page.splice(22, 4, ...le32(c));
|
|
256
|
+
return page;
|
|
257
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type OpenAIModel = {
|
|
2
|
+
id: string;
|
|
3
|
+
object: 'model';
|
|
4
|
+
created: number;
|
|
5
|
+
owned_by: string;
|
|
6
|
+
/** When the model shuts down, or null when no date is announced (https://platform.openai.com/docs/api-reference/models/object) */
|
|
7
|
+
shutdown_date: string | null;
|
|
8
|
+
};
|
|
9
|
+
export declare const OPENAI_MODELS: OpenAIModel[];
|
|
10
|
+
/** The instant a shutdown date takes effect: "At the time of the shut down, the model or endpoint will no longer be
|
|
11
|
+
* accessible" (the deprecations page); the twin takes the date's first instant, UTC. */
|
|
12
|
+
export declare const shutdownAt: (date: string) => number;
|
|
13
|
+
/** Whether a catalogued model is live at the World instant `now` (unix seconds): released, and not yet shut down. A model
|
|
14
|
+
* the catalog does not hold is not live: it does not exist. */
|
|
15
|
+
export declare function live(id: string, now: number): boolean;
|
|
16
|
+
/** The API families OpenAI removed, by the operations they serve, and the instant each is gone: the Assistants API on
|
|
17
|
+
* 2026-08-26 ("removal from the API one year later, on August 26, 2026"), the Evals API on 2026-11-30 ("The Evals
|
|
18
|
+
* dashboard and API are scheduled to shut down"), its evals read-only from 2026-10-31 ("Existing evals become
|
|
19
|
+
* read-only") (https://developers.openai.com/api/docs/deprecations). */
|
|
20
|
+
export declare const REMOVED_FAMILIES: Array<{
|
|
21
|
+
family: string;
|
|
22
|
+
removed: string;
|
|
23
|
+
operations: RegExp;
|
|
24
|
+
readOnlyFrom?: string;
|
|
25
|
+
writes?: RegExp;
|
|
26
|
+
}>;
|
|
27
|
+
/** A removed family's answer to an operation at `now`, or none while it is served. The deprecations page says only that
|
|
28
|
+
* a removed endpoint "will no longer be accessible": the status and wording of the answer are the twin's own. */
|
|
29
|
+
export declare function removedAt(operation: string, now: number): {
|
|
30
|
+
status: number;
|
|
31
|
+
message: string;
|
|
32
|
+
} | undefined;
|
|
33
|
+
/** Fine-tuning's dated availability (the deprecations page, "Self-serve fine-tuning availability"): from 2026-05-07
|
|
34
|
+
* "Creating fine-tuning jobs or training is not available to organizations that have not previously run fine-tuning";
|
|
35
|
+
* from 2026-07-02 "Creating fine-tuning jobs is no longer available to organizations that have not run inference on a
|
|
36
|
+
* fine-tuned model in the past 60 days"; from 2027-01-06 "Active existing customers will no longer be able to create
|
|
37
|
+
* new fine-tuning jobs". The page documents no error: the refusal (403, its wording) is the twin's own. */
|
|
38
|
+
export declare function fineTuningClosed(now: number, org: {
|
|
39
|
+
everFineTuned: boolean;
|
|
40
|
+
lastFineTunedInference?: number;
|
|
41
|
+
}): string | undefined;
|
|
42
|
+
/** Resolve a model object by id, or undefined if the twin doesn't model it. */
|
|
43
|
+
export declare function findModel(id: string): OpenAIModel | undefined;
|
|
44
|
+
/** Whether a model reasons, so that its responses list the reasoning item that describes its chain of thought and count
|
|
45
|
+
* its reasoning tokens (the spec's ReasoningItem: "A description of the chain of thought used by a reasoning model while
|
|
46
|
+
* generating a response"). The o-series and the GPT models from GPT-5 on reason: the spec speaks of "all reasoning models
|
|
47
|
+
* after `gpt-5`", and the reasoning guide (https://developers.openai.com/api/docs/guides/reasoning) lists gpt-6-astra,
|
|
48
|
+
* gpt-5.6, gpt-5.5 and gpt-5.4 among them; gpt-6-astra's page names "Reasoning token support"
|
|
49
|
+
* (https://developers.openai.com/api/docs/models/gpt-6-astra). A fine-tuned model reasons as its base does. */
|
|
50
|
+
export declare function reasons(model: string): boolean;
|
|
51
|
+
/** The reasoning efforts the spec's ReasoningEffort names; a model may document fewer. */
|
|
52
|
+
export declare const REASONING_EFFORTS: string[];
|
|
53
|
+
/** The effort a reasoning model spends when the request names none: the spec's ReasoningEffort `default: medium`. */
|
|
54
|
+
export declare const DEFAULT_EFFORT = "medium";
|
|
55
|
+
/** OpenAI's refusal of a request a model does not take (an effort, custom sampling, log probabilities), or none. */
|
|
56
|
+
export declare function unsupported(model: string, req: {
|
|
57
|
+
effort?: unknown;
|
|
58
|
+
effortParam: string;
|
|
59
|
+
temperature?: unknown;
|
|
60
|
+
top_p?: unknown;
|
|
61
|
+
logprobs?: unknown;
|
|
62
|
+
chatTools?: unknown;
|
|
63
|
+
}): {
|
|
64
|
+
message: string;
|
|
65
|
+
param: string;
|
|
66
|
+
} | undefined;
|
|
67
|
+
/** The model an operation runs on when the request names none, from the spec's request schemas: a generation's
|
|
68
|
+
* `dall-e-2` (CreateImageRequest `model` default), an edit's `gpt-image-1.5` (CreateImageEditRequest), a variation's
|
|
69
|
+
* `dall-e-2` (it takes no other), a moderation's `omni-moderation-latest` (CreateModerationRequest). */
|
|
70
|
+
export declare const DEFAULT_MODEL: Record<string, string>;
|
|
71
|
+
/** The model a request runs on: the one it names, else its operation's default (a variation runs on dall-e-2 whatever it names). */
|
|
72
|
+
export declare function modelOf(operation: string, params: {
|
|
73
|
+
model?: unknown;
|
|
74
|
+
}): string | undefined;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// OpenAI's timeline, as data (the catalog, each API family's removal, and the dated policy changes).
|
|
2
|
+
//
|
|
3
|
+
// Every model the pack's walks name, with its release (`created`) and its shutdown. Sources: the changelog
|
|
4
|
+
// (https://developers.openai.com/api/docs/changelog) dates a release: "Released GPT-6 Astra" 2026-09-03, "Released GPT
|
|
5
|
+
// Image 2.5 Sunburst and GPT Image 2.5 Flare" 2026-09-08, "Released GPT-5.2" 2025-12-11, "Released gpt-image-1.5"
|
|
6
|
+
// 2025-12-16, "Added a new image generation model, `gpt-image-1`" 2025-04-23, "Released GPT-5 family of models"
|
|
7
|
+
// 2025-08-07, "Added two new o-series reasoning models, `o3` and `o4-mini`" 2025-04-16, "Launched o3-mini" 2025-01-31,
|
|
8
|
+
// "Added `gpt-4o-mini-tts`, `gpt-4o-transcribe`..." 2025-03-20, "Released GPT-4o mini" 2024-07-18, "Released new
|
|
9
|
+
// `omni-moderation-latest`" 2024-09-26. The deprecations page (https://developers.openai.com/api/docs/deprecations) dates
|
|
10
|
+
// a shutdown: gpt-3.5-turbo, gpt-4-turbo, o4-mini, o3-mini and gpt-image-1 on 2026-10-23; dall-e-2 and dall-e-3 on
|
|
11
|
+
// 2026-05-12; gpt-image-1.5 on 2026-12-01; whisper-1 and the gpt-4o transcribe models on 2027-02-26.
|
|
12
|
+
// Where neither page dates a release, `created` is the value OpenAI's own model object answers (the earlier catalog's
|
|
13
|
+
// entries, and dall-e-2's and tts-1's). Extrapolation: gpt-4o-transcribe-diarize's release is documented nowhere the twin could
|
|
14
|
+
// read; it is dated with the transcribe family it belongs to (2025-03-20).
|
|
15
|
+
const model = (id, created, shutdown_date = null, owned_by = 'system') => ({ id, object: 'model', created, owned_by, shutdown_date });
|
|
16
|
+
export const OPENAI_MODELS = [
|
|
17
|
+
model('gpt-6-astra', 1788393600, null, 'openai'),
|
|
18
|
+
model('gpt-5.2', 1765411200),
|
|
19
|
+
model('gpt-5', 1754524800),
|
|
20
|
+
model('gpt-4o', 1715367049),
|
|
21
|
+
model('gpt-4o-mini', 1721172741),
|
|
22
|
+
model('gpt-4o-mini-2024-07-18', 1721260800),
|
|
23
|
+
model('gpt-4.1', 1744316542),
|
|
24
|
+
model('gpt-4.1-mini', 1744317547),
|
|
25
|
+
model('gpt-4-turbo', 1712361441, '2026-10-23'),
|
|
26
|
+
model('o3', 1744761600),
|
|
27
|
+
model('o4-mini', 1744761600, '2026-10-23'),
|
|
28
|
+
model('o3-mini', 1738281600, '2026-10-23'),
|
|
29
|
+
model('gpt-3.5-turbo', 1677610602, '2026-10-23', 'openai'),
|
|
30
|
+
model('text-embedding-3-small', 1705948997),
|
|
31
|
+
model('text-embedding-3-large', 1705953180),
|
|
32
|
+
model('text-embedding-ada-002', 1671217299, null, 'openai-internal'),
|
|
33
|
+
model('dall-e-2', 1698798177, '2026-05-12'),
|
|
34
|
+
model('dall-e-3', 1698785189, '2026-05-12'),
|
|
35
|
+
model('gpt-image-1', 1745366400, '2026-10-23'),
|
|
36
|
+
model('gpt-image-1.5', 1765843200, '2026-12-01'),
|
|
37
|
+
model('gpt-image-2.5-flare', 1788825600),
|
|
38
|
+
model('whisper-1', 1677532384, '2027-02-26', 'openai-internal'),
|
|
39
|
+
model('gpt-4o-transcribe', 1742428800, '2027-02-26'),
|
|
40
|
+
model('gpt-4o-mini-transcribe', 1742428800, '2027-02-26'),
|
|
41
|
+
model('gpt-4o-transcribe-diarize', 1742428800, '2027-02-26'),
|
|
42
|
+
model('tts-1', 1681940951, null, 'openai-internal'),
|
|
43
|
+
model('gpt-4o-mini-tts', 1742428800),
|
|
44
|
+
model('omni-moderation-latest', 1727308800),
|
|
45
|
+
];
|
|
46
|
+
/** The instant a shutdown date takes effect: "At the time of the shut down, the model or endpoint will no longer be
|
|
47
|
+
* accessible" (the deprecations page); the twin takes the date's first instant, UTC. */
|
|
48
|
+
export const shutdownAt = (date) => Date.parse(`${date}T00:00:00Z`) / 1000;
|
|
49
|
+
/** Whether a catalogued model is live at the World instant `now` (unix seconds): released, and not yet shut down. A model
|
|
50
|
+
* the catalog does not hold is not live: it does not exist. */
|
|
51
|
+
export function live(id, now) {
|
|
52
|
+
const m = OPENAI_MODELS.find((x) => x.id === id);
|
|
53
|
+
return !!m && m.created <= now && (m.shutdown_date === null || now < shutdownAt(m.shutdown_date));
|
|
54
|
+
}
|
|
55
|
+
/** The API families OpenAI removed, by the operations they serve, and the instant each is gone: the Assistants API on
|
|
56
|
+
* 2026-08-26 ("removal from the API one year later, on August 26, 2026"), the Evals API on 2026-11-30 ("The Evals
|
|
57
|
+
* dashboard and API are scheduled to shut down"), its evals read-only from 2026-10-31 ("Existing evals become
|
|
58
|
+
* read-only") (https://developers.openai.com/api/docs/deprecations). */
|
|
59
|
+
export const REMOVED_FAMILIES = [
|
|
60
|
+
{ family: 'Assistants API', removed: '2026-08-26', operations: /^(createAssistant|listAssistants|getAssistant|modifyAssistant|deleteAssistant|createThread|createThreadAndRun|getThread|modifyThread|deleteThread|createMessage|listMessages|getMessage|modifyMessage|deleteMessage|createRun|listRuns|getRun|modifyRun|cancelRun|submitToolOuputsToRun|listRunSteps|getRunStep)$/ },
|
|
61
|
+
{ family: 'Evals API', removed: '2026-11-30', operations: /^(createEval|listEvals|getEval|updateEval|deleteEval|createEvalRun|getEvalRuns|getEvalRun|cancelEvalRun|deleteEvalRun|getEvalRunOutputItems|getEvalRunOutputItem)$/, readOnlyFrom: '2026-10-31', writes: /^(createEval|updateEval|deleteEval|createEvalRun|cancelEvalRun|deleteEvalRun)$/ },
|
|
62
|
+
];
|
|
63
|
+
/** A removed family's answer to an operation at `now`, or none while it is served. The deprecations page says only that
|
|
64
|
+
* a removed endpoint "will no longer be accessible": the status and wording of the answer are the twin's own. */
|
|
65
|
+
export function removedAt(operation, now) {
|
|
66
|
+
for (const f of REMOVED_FAMILIES) {
|
|
67
|
+
if (!f.operations.test(operation))
|
|
68
|
+
continue;
|
|
69
|
+
if (now >= shutdownAt(f.removed))
|
|
70
|
+
return { status: 404, message: `The ${f.family} was removed on ${f.removed}.` };
|
|
71
|
+
if (f.readOnlyFrom && f.writes?.test(operation) && now >= shutdownAt(f.readOnlyFrom))
|
|
72
|
+
return { status: 400, message: `Evals are read-only since ${f.readOnlyFrom}.` };
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
/** Fine-tuning's dated availability (the deprecations page, "Self-serve fine-tuning availability"): from 2026-05-07
|
|
77
|
+
* "Creating fine-tuning jobs or training is not available to organizations that have not previously run fine-tuning";
|
|
78
|
+
* from 2026-07-02 "Creating fine-tuning jobs is no longer available to organizations that have not run inference on a
|
|
79
|
+
* fine-tuned model in the past 60 days"; from 2027-01-06 "Active existing customers will no longer be able to create
|
|
80
|
+
* new fine-tuning jobs". The page documents no error: the refusal (403, its wording) is the twin's own. */
|
|
81
|
+
export function fineTuningClosed(now, org) {
|
|
82
|
+
if (now >= shutdownAt('2027-01-06'))
|
|
83
|
+
return 'Creating fine-tuning jobs is no longer available (since 2027-01-06).';
|
|
84
|
+
if (now >= shutdownAt('2026-07-02') && (org.lastFineTunedInference === undefined || now - org.lastFineTunedInference > 60 * 86400))
|
|
85
|
+
return 'Creating fine-tuning jobs is not available to organizations that have not run inference on a fine-tuned model in the past 60 days (since 2026-07-02).';
|
|
86
|
+
if (now >= shutdownAt('2026-05-07') && !org.everFineTuned)
|
|
87
|
+
return 'Creating fine-tuning jobs is not available to organizations that have not previously run fine-tuning (since 2026-05-07).';
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
/** Resolve a model object by id, or undefined if the twin doesn't model it. */
|
|
91
|
+
export function findModel(id) {
|
|
92
|
+
return OPENAI_MODELS.find((m) => m.id === id);
|
|
93
|
+
}
|
|
94
|
+
/** Whether a model reasons, so that its responses list the reasoning item that describes its chain of thought and count
|
|
95
|
+
* its reasoning tokens (the spec's ReasoningItem: "A description of the chain of thought used by a reasoning model while
|
|
96
|
+
* generating a response"). The o-series and the GPT models from GPT-5 on reason: the spec speaks of "all reasoning models
|
|
97
|
+
* after `gpt-5`", and the reasoning guide (https://developers.openai.com/api/docs/guides/reasoning) lists gpt-6-astra,
|
|
98
|
+
* gpt-5.6, gpt-5.5 and gpt-5.4 among them; gpt-6-astra's page names "Reasoning token support"
|
|
99
|
+
* (https://developers.openai.com/api/docs/models/gpt-6-astra). A fine-tuned model reasons as its base does. */
|
|
100
|
+
export function reasons(model) {
|
|
101
|
+
const base = model.startsWith('ft:') ? model.split(':')[1] ?? '' : model;
|
|
102
|
+
return /^o\d/.test(base) || /^gpt-([5-9]|\d{2,})(\b|[.-])/.test(base);
|
|
103
|
+
}
|
|
104
|
+
/** The reasoning efforts the spec's ReasoningEffort names; a model may document fewer. */
|
|
105
|
+
export const REASONING_EFFORTS = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
106
|
+
/** The effort a reasoning model spends when the request names none: the spec's ReasoningEffort `default: medium`. */
|
|
107
|
+
export const DEFAULT_EFFORT = 'medium';
|
|
108
|
+
/** What a model's page says it takes and refuses. gpt-6-astra: "`reasoning.effort` supports `low`, `medium`, `high`,
|
|
109
|
+
* `xhigh`, and `max`" (its model page); "GPT-6 Astra does not support `none` reasoning effort. Setting
|
|
110
|
+
* `reasoning.effort` (Responses) or `reasoning_effort` (Chat Completions) to `none` returns HTTP 400" (the reasoning
|
|
111
|
+
* guide); "does not support custom `temperature` or `top_p` values or log probabilities (`logprobs`)" (the changelog,
|
|
112
|
+
* 2026-09-03). */
|
|
113
|
+
const LIMITS = {
|
|
114
|
+
'gpt-6-astra': { efforts: ['low', 'medium', 'high', 'xhigh', 'max'], sampling: false, logprobs: false, chatTools: false },
|
|
115
|
+
};
|
|
116
|
+
/** OpenAI's refusal of a request a model does not take (an effort, custom sampling, log probabilities), or none. */
|
|
117
|
+
export function unsupported(model, req) {
|
|
118
|
+
const base = model.startsWith('ft:') ? model.split(':')[1] ?? '' : model;
|
|
119
|
+
const limits = LIMITS[base];
|
|
120
|
+
const efforts = limits?.efforts ?? REASONING_EFFORTS;
|
|
121
|
+
if (req.effort !== undefined && (typeof req.effort !== 'string' || !efforts.includes(req.effort))) {
|
|
122
|
+
return { message: `Unsupported value: '${req.effortParam}' does not support ${JSON.stringify(req.effort)} with this model. Supported values are: ${efforts.map((e) => `'${e}'`).join(', ')}.`, param: req.effortParam };
|
|
123
|
+
}
|
|
124
|
+
if (!limits)
|
|
125
|
+
return undefined;
|
|
126
|
+
for (const k of ['temperature', 'top_p']) {
|
|
127
|
+
if (req[k] !== undefined && req[k] !== 1)
|
|
128
|
+
return { message: `Unsupported value: '${k}' does not support ${JSON.stringify(req[k])} with this model. Only the default (1) value is supported.`, param: k };
|
|
129
|
+
}
|
|
130
|
+
if (req.logprobs === true)
|
|
131
|
+
return { message: "Unsupported parameter: 'logprobs' is not supported with this model.", param: 'logprobs' };
|
|
132
|
+
// "Tool calling requires the Responses API. If you use tools with Chat Completions, follow the Responses migration
|
|
133
|
+
// guide" (https://developers.openai.com/api/docs/changelog, 2026-09-03). The changelog gives no wording for the
|
|
134
|
+
// refusal: this message is the twin's own.
|
|
135
|
+
if (Array.isArray(req.chatTools) && req.chatTools.length)
|
|
136
|
+
return { message: 'Tool calling with this model requires the Responses API: send the request to /v1/responses.', param: 'tools' };
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
/** The model an operation runs on when the request names none, from the spec's request schemas: a generation's
|
|
140
|
+
* `dall-e-2` (CreateImageRequest `model` default), an edit's `gpt-image-1.5` (CreateImageEditRequest), a variation's
|
|
141
|
+
* `dall-e-2` (it takes no other), a moderation's `omni-moderation-latest` (CreateModerationRequest). */
|
|
142
|
+
export const DEFAULT_MODEL = { createImage: 'dall-e-2', createImageEdit: 'gpt-image-1.5', createImageVariation: 'dall-e-2', createModeration: 'omni-moderation-latest' };
|
|
143
|
+
/** The model a request runs on: the one it names, else its operation's default (a variation runs on dall-e-2 whatever it names). */
|
|
144
|
+
export function modelOf(operation, params) {
|
|
145
|
+
if (operation === 'createImageVariation')
|
|
146
|
+
return DEFAULT_MODEL.createImageVariation;
|
|
147
|
+
return typeof params.model === 'string' && params.model ? params.model : DEFAULT_MODEL[operation];
|
|
148
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type PackScenarioAdapter, type ScenarioDecision, type ScenarioDocument, ScenarioEngine } from '@volter/world-core';
|
|
2
|
+
import type { ChatChoice, ChatMessageParam, ChatToolCall } from './openai-types.js';
|
|
3
|
+
export type OpenAIScenarioRequest = {
|
|
4
|
+
model: string;
|
|
5
|
+
messages: ChatMessageParam[];
|
|
6
|
+
tools?: unknown;
|
|
7
|
+
maxTokens?: number;
|
|
8
|
+
};
|
|
9
|
+
export type OpenAIScenarioEngine = ScenarioEngine<OpenAIScenarioRequest>;
|
|
10
|
+
export type ScenarioToolCall = {
|
|
11
|
+
name: string;
|
|
12
|
+
arguments: Record<string, unknown>;
|
|
13
|
+
id?: string;
|
|
14
|
+
};
|
|
15
|
+
export type OpenAIScenarioRespond = {
|
|
16
|
+
text?: string;
|
|
17
|
+
toolCalls?: ScenarioToolCall | ScenarioToolCall[];
|
|
18
|
+
finishReason?: 'stop' | 'length' | 'tool_calls' | 'content_filter';
|
|
19
|
+
};
|
|
20
|
+
export type ScriptedResult = {
|
|
21
|
+
text: string | null;
|
|
22
|
+
toolCalls: ChatToolCall[];
|
|
23
|
+
finishReason: 'stop' | 'length' | 'tool_calls' | 'content_filter';
|
|
24
|
+
};
|
|
25
|
+
export declare const openaiScenarioAdapter: PackScenarioAdapter<OpenAIScenarioRequest>;
|
|
26
|
+
export declare function loadOpenAIScenarioDocument(path: string): ScenarioDocument;
|
|
27
|
+
/** What the scenario makes of one request: the decision a builder realizes, or a fault answered at once
|
|
28
|
+
* (a status the script names; the script's own authoring failure is a 500 before any stream starts). */
|
|
29
|
+
export declare function serveScenario(engine: OpenAIScenarioEngine, req: OpenAIScenarioRequest, path: string): Promise<ScenarioDecision | {
|
|
30
|
+
kind: 'fault';
|
|
31
|
+
result: {
|
|
32
|
+
status: number;
|
|
33
|
+
body: unknown;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
};
|
|
36
|
+
}>;
|
|
37
|
+
/** A decision as a turn: the scripted result, or, on a miss, the note that teaches where to script it. */
|
|
38
|
+
export declare function scenarioTurn(decision: ScenarioDecision, callIdPrefix?: string): {
|
|
39
|
+
scripted: ScriptedResult;
|
|
40
|
+
missTeach?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
scripted?: undefined;
|
|
43
|
+
missTeach: string;
|
|
44
|
+
};
|
|
45
|
+
/** A scripted turn as a chat completion's choice. */
|
|
46
|
+
export declare function scriptedChoice(scripted: ScriptedResult, index: number): {
|
|
47
|
+
choice: ChatChoice;
|
|
48
|
+
completionTokens: number;
|
|
49
|
+
};
|
|
50
|
+
export declare function createOpenAIScenarioEngine(document?: ScenarioDocument): OpenAIScenarioEngine;
|
|
51
|
+
export declare function realizeOpenAIRespond(respond: OpenAIScenarioRespond, callIdPrefix?: string): ScriptedResult;
|