@veryfront/ext-llm-openai 0.1.1185 → 0.1.1189
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 +49 -2
- package/esm/_dnt.polyfills.d.ts +12 -0
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +15 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +7 -1
- package/esm/openai-chat-request-builder.d.ts +5 -43
- package/esm/openai-chat-request-builder.d.ts.map +1 -1
- package/esm/openai-chat-request-builder.js +18 -4
- package/esm/openai-chat-stream.d.ts +8 -1
- package/esm/openai-chat-stream.d.ts.map +1 -1
- package/esm/openai-chat-stream.js +286 -104
- package/esm/openai-provider-options.d.ts +6 -0
- package/esm/openai-provider-options.d.ts.map +1 -0
- package/esm/openai-provider-options.js +14 -0
- package/esm/openai-provider.d.ts +6 -5
- package/esm/openai-provider.d.ts.map +1 -1
- package/esm/openai-provider.js +579 -129
- package/esm/openai-reasoning-models.d.ts +3 -6
- package/esm/openai-reasoning-models.d.ts.map +1 -1
- package/esm/openai-responses-request-builder.d.ts.map +1 -1
- package/esm/openai-responses-request-builder.js +240 -19
- package/esm/openai-responses-stream.d.ts +12 -1
- package/esm/openai-responses-stream.d.ts.map +1 -1
- package/esm/openai-responses-stream.js +1053 -113
- package/esm/openai-sse-buffer.d.ts +8 -0
- package/esm/openai-sse-buffer.d.ts.map +1 -0
- package/esm/openai-sse-buffer.js +47 -0
- package/esm/openai-stream-metadata.d.ts +6 -0
- package/esm/openai-stream-metadata.d.ts.map +1 -0
- package/esm/openai-stream-metadata.js +10 -0
- package/esm/openai-tool-input.d.ts +10 -0
- package/esm/openai-tool-input.d.ts.map +1 -0
- package/esm/openai-tool-input.js +30 -0
- package/esm/openai-web-search.d.ts +35 -0
- package/esm/openai-web-search.d.ts.map +1 -0
- package/esm/openai-web-search.js +369 -0
- package/package.json +3 -3
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mergeUsage, ProviderRequestError, readGatewayBillingMode, readRecord, } from "veryfront/provider/shared";
|
|
2
|
+
import { appendOpenAIStreamToolArgument, isJsonObjectText, joinOpenAIStreamToolArguments, MAX_OPENAI_STREAM_TOOL_ARGUMENT_BYTES, MAX_OPENAI_STREAM_TOOL_ARGUMENT_FRAGMENTS, } from "./openai-tool-input.js";
|
|
3
|
+
import { isBoundedOpenAIStreamString, MAX_OPENAI_STREAM_CONTENT_PARTS, MAX_OPENAI_STREAM_IDENTIFIER_BYTES, MAX_OPENAI_STREAM_TOOL_NAME_BYTES, } from "./openai-stream-metadata.js";
|
|
4
|
+
import { appendOpenAISseChunk, finishOpenAISseDecoding, parseOpenAISseBuffer, } from "./openai-sse-buffer.js";
|
|
5
|
+
export const MAX_OPENAI_STREAM_TOOL_CALLS = 1_024;
|
|
6
|
+
function invalidOpenAIStream(context, issue) {
|
|
7
|
+
const providerKind = context.providerKind ?? "openai";
|
|
8
|
+
return new ProviderRequestError({
|
|
9
|
+
provider: providerKind,
|
|
10
|
+
status: 200,
|
|
11
|
+
message: `${context.providerLabel ?? providerKind} request failed: invalid successful stream (${issue})`,
|
|
12
|
+
retryable: false,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
2
15
|
function normalizeOpenAIFinishReason(raw) {
|
|
3
16
|
if (typeof raw !== "string") {
|
|
4
17
|
return null;
|
|
@@ -74,108 +87,249 @@ function extractOpenAIUsage(payload) {
|
|
|
74
87
|
: {}),
|
|
75
88
|
};
|
|
76
89
|
}
|
|
77
|
-
function
|
|
90
|
+
function mergeOpenAIUsage(current, payload) {
|
|
91
|
+
const merged = mergeUsage(current, extractOpenAIUsage(payload));
|
|
92
|
+
return merged && Object.keys(merged).length > 0 ? merged : undefined;
|
|
93
|
+
}
|
|
94
|
+
function extractOpenAIContentText(content, context) {
|
|
95
|
+
if (content === undefined || content === null) {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
78
98
|
if (typeof content === "string") {
|
|
79
99
|
return content;
|
|
80
100
|
}
|
|
81
101
|
if (!Array.isArray(content)) {
|
|
82
|
-
|
|
102
|
+
throw invalidOpenAIStream(context, "choice delta content had an invalid type");
|
|
83
103
|
}
|
|
84
|
-
|
|
104
|
+
if (content.length > MAX_OPENAI_STREAM_CONTENT_PARTS) {
|
|
105
|
+
throw invalidOpenAIStream(context, `choice delta content exceeded ${MAX_OPENAI_STREAM_CONTENT_PARTS} parts`);
|
|
106
|
+
}
|
|
107
|
+
const textParts = [];
|
|
85
108
|
for (const part of content) {
|
|
86
109
|
const record = readRecord(part);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
110
|
+
if (!record) {
|
|
111
|
+
throw invalidOpenAIStream(context, "choice delta content part was not an object");
|
|
112
|
+
}
|
|
113
|
+
if (record.type === "text") {
|
|
114
|
+
if (typeof record.text !== "string") {
|
|
115
|
+
throw invalidOpenAIStream(context, "choice delta text part was malformed");
|
|
116
|
+
}
|
|
117
|
+
textParts.push(record.text);
|
|
118
|
+
}
|
|
119
|
+
else if (record.type === "refusal") {
|
|
120
|
+
if (typeof record.refusal !== "string") {
|
|
121
|
+
throw invalidOpenAIStream(context, "choice delta refusal part was malformed");
|
|
122
|
+
}
|
|
123
|
+
textParts.push(record.refusal);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw invalidOpenAIStream(context, "choice delta content part type was unsupported");
|
|
90
127
|
}
|
|
91
128
|
}
|
|
92
|
-
return
|
|
129
|
+
return textParts.join("");
|
|
93
130
|
}
|
|
94
|
-
function
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return undefined;
|
|
131
|
+
function assertCompleteToolInput(toolCall, context) {
|
|
132
|
+
const argumentsText = joinOpenAIStreamToolArguments(toolCall.argumentChunks);
|
|
133
|
+
if (!toolCall.started || !toolCall.id || !toolCall.name || !argumentsText) {
|
|
134
|
+
throw invalidOpenAIStream(context, "tool call was incomplete");
|
|
99
135
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return undefined;
|
|
136
|
+
if (!isJsonObjectText(argumentsText)) {
|
|
137
|
+
throw invalidOpenAIStream(context, "tool call arguments were not valid JSON object text");
|
|
103
138
|
}
|
|
104
|
-
return
|
|
139
|
+
return argumentsText;
|
|
140
|
+
}
|
|
141
|
+
function isToolCallsFinishReason(value) {
|
|
142
|
+
return typeof value === "object" && value?.unified === "tool-calls";
|
|
105
143
|
}
|
|
106
|
-
export async function* streamOpenAICompatibleParts(stream) {
|
|
107
|
-
const decoder = new TextDecoder();
|
|
144
|
+
export async function* streamOpenAICompatibleParts(stream, context = {}) {
|
|
145
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
108
146
|
let buffer = "";
|
|
109
147
|
const toolCalls = new Map();
|
|
148
|
+
const toolCallIndicesById = new Map();
|
|
110
149
|
let reasoningId = null;
|
|
111
150
|
let reasoningIndex = 0;
|
|
112
151
|
let finishReason = null;
|
|
113
152
|
let usage;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
let sawChoiceEnvelope = false;
|
|
154
|
+
let sawFinishReason = false;
|
|
155
|
+
let sawDone = false;
|
|
156
|
+
let sawPostFinishUsage = false;
|
|
157
|
+
const toolArgumentBudget = {
|
|
158
|
+
bytes: 0,
|
|
159
|
+
fragments: 0,
|
|
160
|
+
};
|
|
161
|
+
function* processEvent(event) {
|
|
162
|
+
if (event === "[DONE]") {
|
|
163
|
+
if (sawDone) {
|
|
164
|
+
throw invalidOpenAIStream(context, "stream contained multiple done markers");
|
|
165
|
+
}
|
|
166
|
+
if (!sawFinishReason) {
|
|
167
|
+
throw invalidOpenAIStream(context, "done marker arrived before a finish reason");
|
|
168
|
+
}
|
|
169
|
+
sawDone = true;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (sawDone) {
|
|
173
|
+
throw invalidOpenAIStream(context, "stream contained data after its done marker");
|
|
174
|
+
}
|
|
175
|
+
const record = readRecord(event);
|
|
176
|
+
if (!record) {
|
|
177
|
+
throw invalidOpenAIStream(context, "event was not an object");
|
|
178
|
+
}
|
|
179
|
+
const usageRecord = readRecord(record.usage);
|
|
180
|
+
usage = mergeOpenAIUsage(usage, record);
|
|
181
|
+
if (record.choices === undefined) {
|
|
182
|
+
if (!usageRecord) {
|
|
183
|
+
throw invalidOpenAIStream(context, "event had neither choices nor usage");
|
|
184
|
+
}
|
|
185
|
+
if (sawFinishReason) {
|
|
186
|
+
if (sawPostFinishUsage) {
|
|
187
|
+
throw invalidOpenAIStream(context, "stream contained multiple post-finish usage events");
|
|
136
188
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
189
|
+
sawPostFinishUsage = true;
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (!Array.isArray(record.choices)) {
|
|
194
|
+
throw invalidOpenAIStream(context, "choices was not an array");
|
|
195
|
+
}
|
|
196
|
+
if (record.choices.length === 0) {
|
|
197
|
+
if (!usageRecord) {
|
|
198
|
+
throw invalidOpenAIStream(context, "empty choices event had no usage");
|
|
199
|
+
}
|
|
200
|
+
if (sawFinishReason) {
|
|
201
|
+
if (sawPostFinishUsage) {
|
|
202
|
+
throw invalidOpenAIStream(context, "stream contained multiple post-finish usage events");
|
|
151
203
|
}
|
|
152
|
-
|
|
204
|
+
sawPostFinishUsage = true;
|
|
153
205
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (sawFinishReason) {
|
|
209
|
+
throw invalidOpenAIStream(context, "stream contained choice data after its finish reason");
|
|
210
|
+
}
|
|
211
|
+
const choice = readRecord(record.choices[0]);
|
|
212
|
+
if (!choice) {
|
|
213
|
+
throw invalidOpenAIStream(context, "first choice was not an object");
|
|
214
|
+
}
|
|
215
|
+
sawChoiceEnvelope = true;
|
|
216
|
+
if (choice.finish_reason !== undefined &&
|
|
217
|
+
choice.finish_reason !== null &&
|
|
218
|
+
(typeof choice.finish_reason !== "string" || choice.finish_reason.length === 0)) {
|
|
219
|
+
throw invalidOpenAIStream(context, "choice finish reason was malformed");
|
|
220
|
+
}
|
|
221
|
+
let delta;
|
|
222
|
+
if (choice.delta === undefined || choice.delta === null) {
|
|
223
|
+
if (typeof choice.finish_reason !== "string") {
|
|
224
|
+
throw invalidOpenAIStream(context, "choice had neither a delta nor a finish reason");
|
|
225
|
+
}
|
|
226
|
+
delta = {};
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const deltaRecord = readRecord(choice.delta);
|
|
230
|
+
if (!deltaRecord) {
|
|
231
|
+
throw invalidOpenAIStream(context, "choice delta was not an object");
|
|
232
|
+
}
|
|
233
|
+
delta = deltaRecord;
|
|
234
|
+
}
|
|
235
|
+
if (delta.role !== undefined &&
|
|
236
|
+
delta.role !== "assistant") {
|
|
237
|
+
throw invalidOpenAIStream(context, "choice delta role was not assistant");
|
|
238
|
+
}
|
|
239
|
+
if (delta.reasoning_content !== undefined &&
|
|
240
|
+
typeof delta.reasoning_content !== "string") {
|
|
241
|
+
throw invalidOpenAIStream(context, "reasoning delta was malformed");
|
|
242
|
+
}
|
|
243
|
+
if (typeof delta.reasoning_content === "string" &&
|
|
244
|
+
delta.reasoning_content.length > 0) {
|
|
245
|
+
if (!reasoningId) {
|
|
246
|
+
reasoningId = `reasoning-${reasoningIndex++}`;
|
|
247
|
+
yield { type: "reasoning-start", id: reasoningId };
|
|
248
|
+
}
|
|
249
|
+
yield {
|
|
250
|
+
type: "reasoning-delta",
|
|
251
|
+
id: reasoningId,
|
|
252
|
+
delta: delta.reasoning_content,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const refusal = delta.refusal;
|
|
256
|
+
if (refusal !== undefined && refusal !== null && typeof refusal !== "string") {
|
|
257
|
+
throw invalidOpenAIStream(context, "refusal delta was malformed");
|
|
258
|
+
}
|
|
259
|
+
const textDelta = extractOpenAIContentText(delta.content, context) +
|
|
260
|
+
(typeof refusal === "string" ? refusal : "");
|
|
261
|
+
if (textDelta.length > 0) {
|
|
262
|
+
if (reasoningId) {
|
|
263
|
+
yield { type: "reasoning-end", id: reasoningId };
|
|
264
|
+
reasoningId = null;
|
|
265
|
+
}
|
|
266
|
+
yield { type: "text-delta", delta: textDelta };
|
|
267
|
+
}
|
|
268
|
+
if (delta.tool_calls !== undefined &&
|
|
269
|
+
delta.tool_calls !== null &&
|
|
270
|
+
!Array.isArray(delta.tool_calls)) {
|
|
271
|
+
throw invalidOpenAIStream(context, "tool_calls delta was not an array");
|
|
272
|
+
}
|
|
273
|
+
const rawToolCalls = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
274
|
+
for (const rawToolCall of rawToolCalls) {
|
|
275
|
+
if (reasoningId) {
|
|
276
|
+
yield { type: "reasoning-end", id: reasoningId };
|
|
277
|
+
reasoningId = null;
|
|
278
|
+
}
|
|
279
|
+
const toolCallRecord = readRecord(rawToolCall);
|
|
280
|
+
if (!toolCallRecord) {
|
|
281
|
+
throw invalidOpenAIStream(context, "tool call delta was not an object");
|
|
282
|
+
}
|
|
283
|
+
const index = toolCallRecord.index;
|
|
284
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
285
|
+
throw invalidOpenAIStream(context, "tool call index was malformed");
|
|
286
|
+
}
|
|
287
|
+
const toolCallIndex = index;
|
|
288
|
+
if (toolCallRecord.id !== undefined &&
|
|
289
|
+
!isBoundedOpenAIStreamString(toolCallRecord.id, MAX_OPENAI_STREAM_IDENTIFIER_BYTES)) {
|
|
290
|
+
throw invalidOpenAIStream(context, "tool call id was malformed");
|
|
291
|
+
}
|
|
292
|
+
if (toolCallRecord.type !== undefined &&
|
|
293
|
+
toolCallRecord.type !== "function") {
|
|
294
|
+
throw invalidOpenAIStream(context, "tool call type was not function");
|
|
295
|
+
}
|
|
296
|
+
const existingToolCall = toolCalls.get(toolCallIndex);
|
|
297
|
+
if (!existingToolCall && toolCalls.size >= MAX_OPENAI_STREAM_TOOL_CALLS) {
|
|
298
|
+
throw invalidOpenAIStream(context, `stream exceeded ${MAX_OPENAI_STREAM_TOOL_CALLS} tool calls`);
|
|
299
|
+
}
|
|
300
|
+
const current = existingToolCall ?? {
|
|
301
|
+
id: "",
|
|
302
|
+
name: "",
|
|
303
|
+
argumentChunks: [],
|
|
304
|
+
started: false,
|
|
305
|
+
};
|
|
306
|
+
if (typeof toolCallRecord.id === "string") {
|
|
307
|
+
const existingIndex = toolCallIndicesById.get(toolCallRecord.id);
|
|
308
|
+
if (existingIndex !== undefined && existingIndex !== toolCallIndex) {
|
|
309
|
+
throw invalidOpenAIStream(context, "tool call id was reused at another index");
|
|
162
310
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const current = toolCalls.get(index) ?? {
|
|
166
|
-
id: typeof toolCallRecord?.id === "string" ? toolCallRecord.id : `tool-${index}`,
|
|
167
|
-
name: "",
|
|
168
|
-
arguments: "",
|
|
169
|
-
started: false,
|
|
170
|
-
};
|
|
171
|
-
if (typeof toolCallRecord?.id === "string") {
|
|
172
|
-
current.id = toolCallRecord.id;
|
|
311
|
+
if (current.id && current.id !== toolCallRecord.id) {
|
|
312
|
+
throw invalidOpenAIStream(context, "tool call id changed while streaming");
|
|
173
313
|
}
|
|
174
|
-
|
|
175
|
-
|
|
314
|
+
current.id = toolCallRecord.id;
|
|
315
|
+
toolCallIndicesById.set(toolCallRecord.id, toolCallIndex);
|
|
316
|
+
}
|
|
317
|
+
if (toolCallRecord.function !== undefined && toolCallRecord.function !== null) {
|
|
318
|
+
const fn = readRecord(toolCallRecord.function);
|
|
319
|
+
if (!fn) {
|
|
320
|
+
throw invalidOpenAIStream(context, "tool call function was not an object");
|
|
321
|
+
}
|
|
322
|
+
if (fn.name !== undefined &&
|
|
323
|
+
!isBoundedOpenAIStreamString(fn.name, MAX_OPENAI_STREAM_TOOL_NAME_BYTES)) {
|
|
324
|
+
throw invalidOpenAIStream(context, "tool call function name was malformed");
|
|
325
|
+
}
|
|
326
|
+
if (typeof fn.name === "string") {
|
|
327
|
+
if (current.name && current.name !== fn.name) {
|
|
328
|
+
throw invalidOpenAIStream(context, "tool call function name changed while streaming");
|
|
329
|
+
}
|
|
176
330
|
current.name = fn.name;
|
|
177
331
|
}
|
|
178
|
-
if (!current.started && current.
|
|
332
|
+
if (!current.started && current.id && current.name) {
|
|
179
333
|
current.started = true;
|
|
180
334
|
yield {
|
|
181
335
|
type: "tool-input-start",
|
|
@@ -183,50 +337,78 @@ export async function* streamOpenAICompatibleParts(stream) {
|
|
|
183
337
|
toolName: current.name,
|
|
184
338
|
};
|
|
185
339
|
}
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
340
|
+
if (fn.arguments !== undefined && typeof fn.arguments !== "string") {
|
|
341
|
+
throw invalidOpenAIStream(context, "tool call arguments delta was malformed");
|
|
342
|
+
}
|
|
343
|
+
if (typeof fn.arguments === "string") {
|
|
344
|
+
if (!current.started) {
|
|
345
|
+
throw invalidOpenAIStream(context, "tool call arguments arrived before its id and name");
|
|
346
|
+
}
|
|
347
|
+
const limit = appendOpenAIStreamToolArgument(toolArgumentBudget, current.argumentChunks, fn.arguments);
|
|
348
|
+
if (limit === "bytes") {
|
|
349
|
+
throw invalidOpenAIStream(context, `tool call arguments exceeded ${MAX_OPENAI_STREAM_TOOL_ARGUMENT_BYTES} UTF-8 bytes`);
|
|
350
|
+
}
|
|
351
|
+
if (limit === "fragments") {
|
|
352
|
+
throw invalidOpenAIStream(context, `tool call arguments exceeded ${MAX_OPENAI_STREAM_TOOL_ARGUMENT_FRAGMENTS} fragments`);
|
|
353
|
+
}
|
|
354
|
+
if (fn.arguments.length > 0) {
|
|
355
|
+
yield {
|
|
356
|
+
type: "tool-input-delta",
|
|
357
|
+
id: current.id,
|
|
358
|
+
delta: fn.arguments,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
193
361
|
}
|
|
194
|
-
toolCalls.set(index, current);
|
|
195
|
-
}
|
|
196
|
-
const normalizedFinishReason = normalizeOpenAIFinishReason(choice.finish_reason);
|
|
197
|
-
if (normalizedFinishReason) {
|
|
198
|
-
finishReason = normalizedFinishReason;
|
|
199
362
|
}
|
|
363
|
+
toolCalls.set(toolCallIndex, current);
|
|
364
|
+
}
|
|
365
|
+
if (typeof choice.finish_reason === "string") {
|
|
366
|
+
finishReason = normalizeOpenAIFinishReason(choice.finish_reason);
|
|
367
|
+
sawFinishReason = true;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
for await (const chunk of stream) {
|
|
371
|
+
buffer = appendOpenAISseChunk(decoder, buffer, chunk, (issue) => invalidOpenAIStream(context, issue));
|
|
372
|
+
const parsed = parseOpenAISseBuffer(buffer, (issue) => invalidOpenAIStream(context, issue));
|
|
373
|
+
buffer = parsed.remainder;
|
|
374
|
+
for (const event of parsed.events) {
|
|
375
|
+
yield* processEvent(event);
|
|
200
376
|
}
|
|
201
377
|
}
|
|
378
|
+
buffer = finishOpenAISseDecoding(decoder, buffer, (issue) => invalidOpenAIStream(context, issue));
|
|
202
379
|
if (buffer.trim().length > 0) {
|
|
203
|
-
const parsed =
|
|
380
|
+
const parsed = parseOpenAISseBuffer(buffer, (issue) => invalidOpenAIStream(context, issue), true);
|
|
204
381
|
for (const event of parsed.events) {
|
|
205
|
-
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
const record = readRecord(event);
|
|
209
|
-
usage = extractOpenAIUsage(record) ?? usage;
|
|
382
|
+
yield* processEvent(event);
|
|
210
383
|
}
|
|
211
384
|
}
|
|
385
|
+
if (!sawChoiceEnvelope) {
|
|
386
|
+
throw invalidOpenAIStream(context, "stream contained no choice envelope");
|
|
387
|
+
}
|
|
388
|
+
if (!sawFinishReason) {
|
|
389
|
+
throw invalidOpenAIStream(context, "stream ended before a finish reason");
|
|
390
|
+
}
|
|
212
391
|
if (reasoningId) {
|
|
213
|
-
yield {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
finishReason.unified === "tool-calls") {
|
|
392
|
+
yield { type: "reasoning-end", id: reasoningId };
|
|
393
|
+
}
|
|
394
|
+
const finishedWithToolCalls = isToolCallsFinishReason(finishReason);
|
|
395
|
+
if (finishedWithToolCalls) {
|
|
396
|
+
if (toolCalls.size === 0) {
|
|
397
|
+
throw invalidOpenAIStream(context, "tool-call finish contained no tool calls");
|
|
398
|
+
}
|
|
221
399
|
for (const toolCall of toolCalls.values()) {
|
|
400
|
+
const input = assertCompleteToolInput(toolCall, context);
|
|
222
401
|
yield {
|
|
223
402
|
type: "tool-call",
|
|
224
403
|
toolCallId: toolCall.id,
|
|
225
404
|
toolName: toolCall.name,
|
|
226
|
-
input
|
|
405
|
+
input,
|
|
227
406
|
};
|
|
228
407
|
}
|
|
229
408
|
}
|
|
409
|
+
else if (toolCalls.size > 0) {
|
|
410
|
+
throw invalidOpenAIStream(context, "stream ended with unfinished tool calls");
|
|
411
|
+
}
|
|
230
412
|
yield {
|
|
231
413
|
type: "finish",
|
|
232
414
|
finishReason,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge provider-native options without invoking legacy prototype setters such
|
|
3
|
+
* as `Object.prototype.__proto__`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function defineOpenAIProviderOptions(target: Record<string, unknown>, source: Record<string, unknown>): void;
|
|
6
|
+
//# sourceMappingURL=openai-provider-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-provider-options.d.ts","sourceRoot":"","sources":["../src/openai-provider-options.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI,CASN"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge provider-native options without invoking legacy prototype setters such
|
|
3
|
+
* as `Object.prototype.__proto__`.
|
|
4
|
+
*/
|
|
5
|
+
export function defineOpenAIProviderOptions(target, source) {
|
|
6
|
+
for (const key of Object.keys(source)) {
|
|
7
|
+
Object.defineProperty(target, key, {
|
|
8
|
+
value: source[key],
|
|
9
|
+
writable: true,
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
package/esm/openai-provider.d.ts
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
* @module extensions/ext-llm-openai/openai-provider
|
|
9
9
|
*/
|
|
10
10
|
import type { LLMProvider, LLMProviderConfig } from "veryfront/extensions/llm";
|
|
11
|
-
import type { EmbeddingRuntime, ModelRuntime } from "veryfront/provider/types";
|
|
11
|
+
import type { EmbeddingRuntime, ModelRuntime, RuntimeAssistantContentPart } from "veryfront/provider/types";
|
|
12
12
|
import { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, TOOL_INPUT_PENDING_THRESHOLD_MS } from "veryfront/provider/shared";
|
|
13
|
+
import { type OpenAICompatibleLanguageOptions } from "./openai-chat-request-builder.js";
|
|
13
14
|
export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, TOOL_INPUT_PENDING_THRESHOLD_MS, };
|
|
14
15
|
export interface OpenAIRuntimeConfig {
|
|
15
16
|
apiKey: string;
|
|
@@ -20,13 +21,13 @@ export interface OpenAIRuntimeConfig {
|
|
|
20
21
|
providerName?: string;
|
|
21
22
|
fetch?: typeof globalThis.fetch;
|
|
22
23
|
}
|
|
23
|
-
export declare function createOpenAIModelRuntime(config: OpenAIRuntimeConfig, modelId: string): ModelRuntime
|
|
24
|
-
export declare function createOpenAIResponsesRuntime(config: OpenAIRuntimeConfig, modelId: string): ModelRuntime
|
|
24
|
+
export declare function createOpenAIModelRuntime(config: OpenAIRuntimeConfig, modelId: string): ModelRuntime<OpenAICompatibleLanguageOptions, RuntimeAssistantContentPart>;
|
|
25
|
+
export declare function createOpenAIResponsesRuntime(config: OpenAIRuntimeConfig, modelId: string): ModelRuntime<OpenAICompatibleLanguageOptions, RuntimeAssistantContentPart>;
|
|
25
26
|
export declare function createOpenAIEmbeddingRuntime(config: OpenAIRuntimeConfig, modelId: string): EmbeddingRuntime;
|
|
26
27
|
export declare class OpenAIProvider implements LLMProvider {
|
|
27
28
|
readonly id = "openai";
|
|
28
|
-
createModel(modelId: string, config: LLMProviderConfig): ModelRuntime
|
|
29
|
+
createModel(modelId: string, config: LLMProviderConfig): ModelRuntime<OpenAICompatibleLanguageOptions, RuntimeAssistantContentPart>;
|
|
29
30
|
createEmbedding(modelId: string, config: LLMProviderConfig): EmbeddingRuntime;
|
|
30
|
-
createResponses(modelId: string, config: LLMProviderConfig): ModelRuntime
|
|
31
|
+
createResponses(modelId: string, config: LLMProviderConfig): ModelRuntime<OpenAICompatibleLanguageOptions, RuntimeAssistantContentPart>;
|
|
31
32
|
}
|
|
32
33
|
//# sourceMappingURL=openai-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../src/openai-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../src/openai-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAMlB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EAOpB,+BAA+B,EAChC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,kCAAkC,CAAC;AA2B1C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,+BAA+B,GAChC,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAo6BD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAAC,+BAA+B,EAAE,2BAA2B,CAAC,CA+E5E;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAAC,+BAA+B,EAAE,2BAA2B,CAAC,CAyF5E;AAgCD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,gBAAgB,CA2ClB;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,EAAE,YAAY;IAEvB,WAAW,CACT,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iBAAiB,GACxB,YAAY,CAAC,+BAA+B,EAAE,2BAA2B,CAAC;IAwB7E,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,gBAAgB;IAa7E,eAAe,CACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iBAAiB,GACxB,YAAY,CAAC,+BAA+B,EAAE,2BAA2B,CAAC;CAY9E"}
|