@workglow/openai 0.2.34 → 0.2.36
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/dist/ai/OpenAiProvider.d.ts +15 -21
- package/dist/ai/OpenAiProvider.d.ts.map +1 -1
- package/dist/ai/OpenAiQueuedProvider.d.ts +16 -22
- package/dist/ai/OpenAiQueuedProvider.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_Capabilities.d.ts +38 -0
- package/dist/ai/common/OpenAI_Capabilities.d.ts.map +1 -0
- package/dist/ai/common/OpenAI_CapabilitySets.d.ts +30 -0
- package/dist/ai/common/OpenAI_CapabilitySets.d.ts.map +1 -0
- package/dist/ai/common/OpenAI_CountTokens.browser.d.ts +6 -1
- package/dist/ai/common/OpenAI_CountTokens.browser.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_CountTokens.d.ts +6 -1
- package/dist/ai/common/OpenAI_CountTokens.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ImageEdit.d.ts +5 -5
- package/dist/ai/common/OpenAI_ImageEdit.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ImageGenerate.d.ts +7 -8
- package/dist/ai/common/OpenAI_ImageGenerate.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_JobRunFns.browser.d.ts +7 -3
- package/dist/ai/common/OpenAI_JobRunFns.browser.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_JobRunFns.d.ts +10 -3
- package/dist/ai/common/OpenAI_JobRunFns.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ModelInfo.d.ts +6 -1
- package/dist/ai/common/OpenAI_ModelInfo.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ModelSchema.d.ts +3 -3
- package/dist/ai/common/OpenAI_ModelSearch.d.ts +6 -1
- package/dist/ai/common/OpenAI_ModelSearch.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_StructuredGeneration.d.ts +8 -3
- package/dist/ai/common/OpenAI_StructuredGeneration.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextEmbedding.d.ts +6 -1
- package/dist/ai/common/OpenAI_TextEmbedding.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextGeneration.d.ts +9 -3
- package/dist/ai/common/OpenAI_TextGeneration.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextRewriter.d.ts +6 -3
- package/dist/ai/common/OpenAI_TextRewriter.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_TextSummary.d.ts +6 -3
- package/dist/ai/common/OpenAI_TextSummary.d.ts.map +1 -1
- package/dist/ai/common/OpenAI_ToolCalling.d.ts +11 -3
- package/dist/ai/common/OpenAI_ToolCalling.d.ts.map +1 -1
- package/dist/ai/index.browser.d.ts.map +1 -1
- package/dist/ai/index.d.ts +26 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/registerOpenAiInline.browser.d.ts.map +1 -1
- package/dist/ai/registerOpenAiWorker.browser.d.ts.map +1 -1
- package/dist/ai/registerOpenAiWorker.d.ts.map +1 -1
- package/dist/ai/runtime.browser.d.ts.map +1 -1
- package/dist/ai/runtime.d.ts.map +1 -1
- package/dist/ai-runtime.browser.d.ts.map +1 -1
- package/dist/ai-runtime.browser.js +434 -509
- package/dist/ai-runtime.browser.js.map +21 -19
- package/dist/ai-runtime.d.ts.map +1 -1
- package/dist/ai-runtime.js +432 -549
- package/dist/ai-runtime.js.map +21 -20
- package/dist/ai.browser.d.ts.map +1 -1
- package/dist/ai.browser.js +73 -15
- package/dist/ai.browser.js.map +6 -4
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +548 -25
- package/dist/ai.js.map +19 -5
- package/package.json +12 -13
- package/dist/ai/common/OpenAI_Chat.d.ts +0 -10
- package/dist/ai/common/OpenAI_Chat.d.ts.map +0 -1
package/dist/ai-runtime.js
CHANGED
|
@@ -46,6 +46,290 @@ function getModelName(model) {
|
|
|
46
46
|
// src/ai/registerOpenAiInline.ts
|
|
47
47
|
import { registerProviderInline } from "@workglow/ai/provider-utils";
|
|
48
48
|
|
|
49
|
+
// src/ai/common/OpenAI_CapabilitySets.ts
|
|
50
|
+
var OPENAI_TEXT_GENERATION = ["text.generation"];
|
|
51
|
+
var OPENAI_TOOL_USE = ["text.generation", "tool-use"];
|
|
52
|
+
var OPENAI_JSON_MODE = ["text.generation", "json-mode"];
|
|
53
|
+
var OPENAI_TEXT_REWRITER = ["text.rewriter"];
|
|
54
|
+
var OPENAI_TEXT_SUMMARY = ["text.summary"];
|
|
55
|
+
var OPENAI_TEXT_EMBEDDING = ["text.embedding"];
|
|
56
|
+
var OPENAI_IMAGE_GENERATION = ["image.generation"];
|
|
57
|
+
var OPENAI_IMAGE_EDITING = ["image.editing"];
|
|
58
|
+
var OPENAI_COUNT_TOKENS = ["model.count-tokens"];
|
|
59
|
+
var OPENAI_MODEL_SEARCH = ["model.search"];
|
|
60
|
+
var OPENAI_MODEL_INFO = ["model.info"];
|
|
61
|
+
var OPENAI_CAPABILITY_SETS = [
|
|
62
|
+
OPENAI_TEXT_GENERATION,
|
|
63
|
+
OPENAI_TOOL_USE,
|
|
64
|
+
OPENAI_JSON_MODE,
|
|
65
|
+
OPENAI_TEXT_REWRITER,
|
|
66
|
+
OPENAI_TEXT_SUMMARY,
|
|
67
|
+
OPENAI_TEXT_EMBEDDING,
|
|
68
|
+
OPENAI_IMAGE_GENERATION,
|
|
69
|
+
OPENAI_IMAGE_EDITING,
|
|
70
|
+
OPENAI_COUNT_TOKENS,
|
|
71
|
+
OPENAI_MODEL_SEARCH,
|
|
72
|
+
OPENAI_MODEL_INFO
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
// src/ai/common/OpenAI_CountTokens.ts
|
|
76
|
+
var _tiktoken;
|
|
77
|
+
async function loadTiktoken() {
|
|
78
|
+
if (!_tiktoken) {
|
|
79
|
+
try {
|
|
80
|
+
_tiktoken = await import("tiktoken");
|
|
81
|
+
} catch {
|
|
82
|
+
throw new Error("tiktoken is required for OpenAI token counting. Install it with: bun add tiktoken");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return _tiktoken;
|
|
86
|
+
}
|
|
87
|
+
var _encoderCache = new Map;
|
|
88
|
+
async function getEncoder(modelName) {
|
|
89
|
+
const tiktoken = await loadTiktoken();
|
|
90
|
+
if (!_encoderCache.has(modelName)) {
|
|
91
|
+
try {
|
|
92
|
+
_encoderCache.set(modelName, tiktoken.encoding_for_model(modelName));
|
|
93
|
+
} catch {
|
|
94
|
+
const fallback = "cl100k_base";
|
|
95
|
+
if (!_encoderCache.has(fallback)) {
|
|
96
|
+
_encoderCache.set(fallback, tiktoken.get_encoding(fallback));
|
|
97
|
+
}
|
|
98
|
+
_encoderCache.set(modelName, _encoderCache.get(fallback));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return _encoderCache.get(modelName);
|
|
102
|
+
}
|
|
103
|
+
async function countTokens(input, model) {
|
|
104
|
+
const enc = await getEncoder(getModelName(model));
|
|
105
|
+
const tokens = enc.encode(input.text);
|
|
106
|
+
return { count: tokens.length };
|
|
107
|
+
}
|
|
108
|
+
var OpenAI_CountTokens_Stream = async (input, model, _signal, emit) => {
|
|
109
|
+
const result = await countTokens(input, model);
|
|
110
|
+
emit({ type: "finish", data: result });
|
|
111
|
+
};
|
|
112
|
+
var OpenAI_CountTokens_Preview = async (input, model) => {
|
|
113
|
+
return countTokens(input, model);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// src/ai/common/OpenAI_ImageEdit.ts
|
|
117
|
+
import { ImageGenerationContentPolicyError, ImageGenerationProviderError } from "@workglow/ai";
|
|
118
|
+
import { dataUriToImageValue, imageValueToPngBytes } from "@workglow/ai/provider-utils";
|
|
119
|
+
function aspectRatioToSize(aspectRatio) {
|
|
120
|
+
switch (aspectRatio) {
|
|
121
|
+
case "16:9":
|
|
122
|
+
case "4:3":
|
|
123
|
+
return "1536x1024";
|
|
124
|
+
case "9:16":
|
|
125
|
+
case "3:4":
|
|
126
|
+
return "1024x1536";
|
|
127
|
+
case "1:1":
|
|
128
|
+
default:
|
|
129
|
+
return "1024x1024";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function modelIdOf(model) {
|
|
133
|
+
return model?.model_id ?? model?.provider_config?.model_name ?? "openai";
|
|
134
|
+
}
|
|
135
|
+
async function gpuImageToOpenAiFile(image, name) {
|
|
136
|
+
const bytes = await imageValueToPngBytes(image);
|
|
137
|
+
const buffer = bytes.buffer instanceof ArrayBuffer ? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) : new Uint8Array(bytes).buffer;
|
|
138
|
+
const sdk = await import("openai");
|
|
139
|
+
if (typeof sdk.toFile === "function") {
|
|
140
|
+
return sdk.toFile(new Blob([buffer], { type: "image/png" }), name, { type: "image/png" });
|
|
141
|
+
}
|
|
142
|
+
return new File([buffer], name, { type: "image/png" });
|
|
143
|
+
}
|
|
144
|
+
async function decodeB64Png(b64) {
|
|
145
|
+
return dataUriToImageValue(`data:image/png;base64,${b64}`);
|
|
146
|
+
}
|
|
147
|
+
async function buildEditPayload(input, model) {
|
|
148
|
+
const modelName = getModelName(model);
|
|
149
|
+
const primary = await gpuImageToOpenAiFile(input.image, "image.png");
|
|
150
|
+
const additionalFiles = input.additionalImages && input.additionalImages.length > 0 ? await Promise.all(input.additionalImages.map((g, i) => gpuImageToOpenAiFile(g, `image-${i + 1}.png`))) : [];
|
|
151
|
+
const imageField = additionalFiles.length === 0 ? primary : [primary, ...additionalFiles];
|
|
152
|
+
const payload = {
|
|
153
|
+
model: modelName,
|
|
154
|
+
prompt: input.prompt,
|
|
155
|
+
image: imageField,
|
|
156
|
+
size: aspectRatioToSize(input.aspectRatio),
|
|
157
|
+
quality: input.quality,
|
|
158
|
+
n: 1,
|
|
159
|
+
...input.providerOptions ?? {}
|
|
160
|
+
};
|
|
161
|
+
if (input.mask) {
|
|
162
|
+
payload.mask = await gpuImageToOpenAiFile(input.mask, "mask.png");
|
|
163
|
+
}
|
|
164
|
+
return payload;
|
|
165
|
+
}
|
|
166
|
+
var OpenAI_ImageEdit_Stream = async (input, model, signal, emit) => {
|
|
167
|
+
const client = await getClient(model);
|
|
168
|
+
try {
|
|
169
|
+
const payload = await buildEditPayload(input, model);
|
|
170
|
+
const stream = await client.images.edit({ ...payload, stream: true, partial_images: 3 }, { signal });
|
|
171
|
+
for await (const event of stream) {
|
|
172
|
+
if (signal.aborted)
|
|
173
|
+
return;
|
|
174
|
+
const b64 = event.b64_json;
|
|
175
|
+
if (!b64)
|
|
176
|
+
continue;
|
|
177
|
+
const image = await decodeB64Png(b64);
|
|
178
|
+
emit({ type: "snapshot", data: { image } });
|
|
179
|
+
}
|
|
180
|
+
emit({ type: "finish", data: {} });
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (err instanceof ImageGenerationProviderError || err instanceof ImageGenerationContentPolicyError) {
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
const msg = err instanceof Error ? err.message : "unknown error";
|
|
186
|
+
if (/safety|policy|moderation/i.test(msg)) {
|
|
187
|
+
throw new ImageGenerationContentPolicyError(modelIdOf(model), msg);
|
|
188
|
+
}
|
|
189
|
+
throw new ImageGenerationProviderError(modelIdOf(model), msg, { cause: err });
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/ai/common/OpenAI_ImageGenerate.ts
|
|
194
|
+
import { ImageGenerationContentPolicyError as ImageGenerationContentPolicyError2, ImageGenerationProviderError as ImageGenerationProviderError2 } from "@workglow/ai";
|
|
195
|
+
import { dataUriToImageValue as dataUriToImageValue2 } from "@workglow/ai/provider-utils";
|
|
196
|
+
function aspectRatioToSize2(aspectRatio) {
|
|
197
|
+
switch (aspectRatio) {
|
|
198
|
+
case "16:9":
|
|
199
|
+
case "4:3":
|
|
200
|
+
return "1536x1024";
|
|
201
|
+
case "9:16":
|
|
202
|
+
case "3:4":
|
|
203
|
+
return "1024x1536";
|
|
204
|
+
case "1:1":
|
|
205
|
+
default:
|
|
206
|
+
return "1024x1024";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function decodeB64Png2(b64) {
|
|
210
|
+
return dataUriToImageValue2(`data:image/png;base64,${b64}`);
|
|
211
|
+
}
|
|
212
|
+
function modelIdOf2(model) {
|
|
213
|
+
return model?.model_id ?? model?.provider_config?.model_name ?? "openai";
|
|
214
|
+
}
|
|
215
|
+
var OpenAI_ImageGenerate_Stream = async (input, model, signal, emit) => {
|
|
216
|
+
const client = await getClient(model);
|
|
217
|
+
const modelName = getModelName(model);
|
|
218
|
+
const size = aspectRatioToSize2(input.aspectRatio);
|
|
219
|
+
if (modelName.startsWith("dall-e")) {
|
|
220
|
+
try {
|
|
221
|
+
const resp = await client.images.generate({
|
|
222
|
+
model: modelName,
|
|
223
|
+
prompt: input.prompt,
|
|
224
|
+
size,
|
|
225
|
+
quality: input.quality,
|
|
226
|
+
n: 1,
|
|
227
|
+
response_format: "b64_json",
|
|
228
|
+
...input.providerOptions ?? {}
|
|
229
|
+
}, { signal });
|
|
230
|
+
const b64 = resp.data?.[0]?.b64_json;
|
|
231
|
+
if (!b64) {
|
|
232
|
+
throw new ImageGenerationProviderError2(modelIdOf2(model), "Empty response (no b64_json)");
|
|
233
|
+
}
|
|
234
|
+
const image = await decodeB64Png2(b64);
|
|
235
|
+
emit({ type: "snapshot", data: { image } });
|
|
236
|
+
emit({ type: "finish", data: {} });
|
|
237
|
+
return;
|
|
238
|
+
} catch (err) {
|
|
239
|
+
if (err instanceof ImageGenerationProviderError2 || err instanceof ImageGenerationContentPolicyError2) {
|
|
240
|
+
throw err;
|
|
241
|
+
}
|
|
242
|
+
const msg = err instanceof Error ? err.message : "unknown error";
|
|
243
|
+
if (/safety|policy|moderation/i.test(msg)) {
|
|
244
|
+
throw new ImageGenerationContentPolicyError2(modelIdOf2(model), msg);
|
|
245
|
+
}
|
|
246
|
+
throw new ImageGenerationProviderError2(modelIdOf2(model), msg, { cause: err });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const stream = await client.images.generate({
|
|
251
|
+
model: modelName,
|
|
252
|
+
prompt: input.prompt,
|
|
253
|
+
size,
|
|
254
|
+
quality: input.quality,
|
|
255
|
+
n: 1,
|
|
256
|
+
stream: true,
|
|
257
|
+
partial_images: 3,
|
|
258
|
+
...input.providerOptions ?? {}
|
|
259
|
+
}, { signal });
|
|
260
|
+
for await (const event of stream) {
|
|
261
|
+
if (signal.aborted)
|
|
262
|
+
return;
|
|
263
|
+
const b64 = event.b64_json;
|
|
264
|
+
if (!b64)
|
|
265
|
+
continue;
|
|
266
|
+
const image = await decodeB64Png2(b64);
|
|
267
|
+
emit({ type: "snapshot", data: { image } });
|
|
268
|
+
}
|
|
269
|
+
emit({ type: "finish", data: {} });
|
|
270
|
+
} catch (err) {
|
|
271
|
+
if (err instanceof ImageGenerationProviderError2 || err instanceof ImageGenerationContentPolicyError2) {
|
|
272
|
+
throw err;
|
|
273
|
+
}
|
|
274
|
+
const msg = err instanceof Error ? err.message : "unknown error";
|
|
275
|
+
if (/safety|policy|moderation/i.test(msg)) {
|
|
276
|
+
throw new ImageGenerationContentPolicyError2(modelIdOf2(model), msg);
|
|
277
|
+
}
|
|
278
|
+
throw new ImageGenerationProviderError2(modelIdOf2(model), msg, { cause: err });
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// src/ai/common/OpenAI_ModelInfo.ts
|
|
283
|
+
var OPENAI_EMBEDDING_DIMENSIONS = {
|
|
284
|
+
"text-embedding-3-small": { native_dimensions: 1536, mrl: true },
|
|
285
|
+
"text-embedding-3-large": { native_dimensions: 3072, mrl: true },
|
|
286
|
+
"text-embedding-ada-002": { native_dimensions: 1536, mrl: false }
|
|
287
|
+
};
|
|
288
|
+
var OpenAI_ModelInfo_Stream = async (input, model, _signal, emit) => {
|
|
289
|
+
if (input.detail === "dimensions") {
|
|
290
|
+
const pc = model?.provider_config;
|
|
291
|
+
let native_dimensions = typeof pc?.native_dimensions === "number" ? pc.native_dimensions : undefined;
|
|
292
|
+
let mrl = typeof pc?.mrl === "boolean" ? pc.mrl : undefined;
|
|
293
|
+
if (native_dimensions === undefined) {
|
|
294
|
+
const modelName = pc?.model_name ?? "";
|
|
295
|
+
const known = OPENAI_EMBEDDING_DIMENSIONS[modelName];
|
|
296
|
+
if (known) {
|
|
297
|
+
native_dimensions = known.native_dimensions;
|
|
298
|
+
mrl = mrl ?? known.mrl;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
emit({
|
|
302
|
+
type: "finish",
|
|
303
|
+
data: {
|
|
304
|
+
model: input.model,
|
|
305
|
+
is_local: false,
|
|
306
|
+
is_remote: true,
|
|
307
|
+
supports_browser: true,
|
|
308
|
+
supports_node: true,
|
|
309
|
+
is_cached: false,
|
|
310
|
+
is_loaded: false,
|
|
311
|
+
file_sizes: null,
|
|
312
|
+
...native_dimensions !== undefined ? { native_dimensions } : {},
|
|
313
|
+
...mrl !== undefined ? { mrl } : {}
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
emit({
|
|
319
|
+
type: "finish",
|
|
320
|
+
data: {
|
|
321
|
+
model: input.model,
|
|
322
|
+
is_local: false,
|
|
323
|
+
is_remote: true,
|
|
324
|
+
supports_browser: true,
|
|
325
|
+
supports_node: true,
|
|
326
|
+
is_cached: false,
|
|
327
|
+
is_loaded: false,
|
|
328
|
+
file_sizes: null
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
};
|
|
332
|
+
|
|
49
333
|
// src/ai/common/OpenAI_ModelSearch.ts
|
|
50
334
|
import { filterLabeledModelsByQuery } from "@workglow/ai/provider-utils";
|
|
51
335
|
|
|
@@ -69,8 +353,8 @@ var OPENAI_FALLBACK = [
|
|
|
69
353
|
{ label: "o1-mini", value: "o1-mini" }
|
|
70
354
|
];
|
|
71
355
|
var OPENAI_IMAGE_MODELS = [
|
|
72
|
-
{ value: "gpt-image-2",
|
|
73
|
-
{ value: "dall-e-3",
|
|
356
|
+
{ value: "gpt-image-2", capabilities: ["image.generation", "image.editing"] },
|
|
357
|
+
{ value: "dall-e-3", capabilities: ["image.generation"] }
|
|
74
358
|
];
|
|
75
359
|
async function listOpenAiModels(credentialKey) {
|
|
76
360
|
const client = await getClient({
|
|
@@ -102,7 +386,7 @@ function mapModelList(models) {
|
|
|
102
386
|
provider: OPENAI,
|
|
103
387
|
title: m.value,
|
|
104
388
|
description: "",
|
|
105
|
-
|
|
389
|
+
capabilities: imageEntry?.capabilities ?? [],
|
|
106
390
|
provider_config: { model_name: m.value },
|
|
107
391
|
metadata: {}
|
|
108
392
|
},
|
|
@@ -110,7 +394,7 @@ function mapModelList(models) {
|
|
|
110
394
|
};
|
|
111
395
|
});
|
|
112
396
|
}
|
|
113
|
-
var
|
|
397
|
+
var OpenAI_ModelSearch_Stream = async (input, _model, _signal, emit) => {
|
|
114
398
|
let models;
|
|
115
399
|
if (!input.credential_key) {
|
|
116
400
|
models = OPENAI_FALLBACK;
|
|
@@ -118,156 +402,12 @@ var OpenAI_ModelSearch = async (input) => {
|
|
|
118
402
|
models = await listOpenAiModels(input.credential_key);
|
|
119
403
|
}
|
|
120
404
|
models = filterLabeledModelsByQuery(models, input.query);
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// src/ai/common/OpenAI_Chat.ts
|
|
125
|
-
import { toOpenAIMessages } from "@workglow/ai/worker";
|
|
126
|
-
function buildParams(input, model) {
|
|
127
|
-
const messages = toOpenAIMessages({
|
|
128
|
-
messages: input.messages,
|
|
129
|
-
systemPrompt: input.systemPrompt,
|
|
130
|
-
prompt: "",
|
|
131
|
-
tools: []
|
|
132
|
-
});
|
|
133
|
-
const params = {
|
|
134
|
-
model: getModelName(model),
|
|
135
|
-
messages
|
|
136
|
-
};
|
|
137
|
-
if (input.temperature !== undefined)
|
|
138
|
-
params.temperature = input.temperature;
|
|
139
|
-
if (input.maxTokens !== undefined)
|
|
140
|
-
params.max_completion_tokens = input.maxTokens;
|
|
141
|
-
return params;
|
|
142
|
-
}
|
|
143
|
-
var OpenAI_Chat = async (input, model, update_progress, signal) => {
|
|
144
|
-
update_progress(0, "OpenAI chat turn");
|
|
145
|
-
const client = await getClient(model);
|
|
146
|
-
const response = await client.chat.completions.create(buildParams(input, model), {
|
|
147
|
-
signal
|
|
148
|
-
});
|
|
149
|
-
const text = response.choices?.[0]?.message?.content ?? "";
|
|
150
|
-
update_progress(100, "Turn complete");
|
|
151
|
-
return { text };
|
|
152
|
-
};
|
|
153
|
-
var OpenAI_Chat_Stream = async function* (input, model, signal) {
|
|
154
|
-
const client = await getClient(model);
|
|
155
|
-
const stream = await client.chat.completions.create({ ...buildParams(input, model), stream: true }, { signal });
|
|
156
|
-
for await (const chunk of stream) {
|
|
157
|
-
const delta = chunk.choices?.[0]?.delta?.content;
|
|
158
|
-
if (delta)
|
|
159
|
-
yield { type: "text-delta", port: "text", textDelta: delta };
|
|
160
|
-
}
|
|
161
|
-
yield { type: "finish", data: {} };
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// src/ai/common/OpenAI_CountTokens.ts
|
|
165
|
-
var _tiktoken;
|
|
166
|
-
async function loadTiktoken() {
|
|
167
|
-
if (!_tiktoken) {
|
|
168
|
-
try {
|
|
169
|
-
_tiktoken = await import("tiktoken");
|
|
170
|
-
} catch {
|
|
171
|
-
throw new Error("tiktoken is required for OpenAI token counting. Install it with: bun add tiktoken");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return _tiktoken;
|
|
175
|
-
}
|
|
176
|
-
var _encoderCache = new Map;
|
|
177
|
-
async function getEncoder(modelName) {
|
|
178
|
-
const tiktoken = await loadTiktoken();
|
|
179
|
-
if (!_encoderCache.has(modelName)) {
|
|
180
|
-
try {
|
|
181
|
-
_encoderCache.set(modelName, tiktoken.encoding_for_model(modelName));
|
|
182
|
-
} catch {
|
|
183
|
-
const fallback = "cl100k_base";
|
|
184
|
-
if (!_encoderCache.has(fallback)) {
|
|
185
|
-
_encoderCache.set(fallback, tiktoken.get_encoding(fallback));
|
|
186
|
-
}
|
|
187
|
-
_encoderCache.set(modelName, _encoderCache.get(fallback));
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return _encoderCache.get(modelName);
|
|
191
|
-
}
|
|
192
|
-
var OpenAI_CountTokens = async (input, model) => {
|
|
193
|
-
const enc = await getEncoder(getModelName(model));
|
|
194
|
-
const tokens = enc.encode(input.text);
|
|
195
|
-
return { count: tokens.length };
|
|
196
|
-
};
|
|
197
|
-
var OpenAI_CountTokens_Preview = async (input, model) => {
|
|
198
|
-
return OpenAI_CountTokens(input, model, () => {}, new AbortController().signal);
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// src/ai/common/OpenAI_ModelInfo.ts
|
|
202
|
-
var OPENAI_EMBEDDING_DIMENSIONS = {
|
|
203
|
-
"text-embedding-3-small": { native_dimensions: 1536, mrl: true },
|
|
204
|
-
"text-embedding-3-large": { native_dimensions: 3072, mrl: true },
|
|
205
|
-
"text-embedding-ada-002": { native_dimensions: 1536, mrl: false }
|
|
206
|
-
};
|
|
207
|
-
var OpenAI_ModelInfo = async (input, model) => {
|
|
208
|
-
if (input.detail === "dimensions") {
|
|
209
|
-
const pc = model?.provider_config;
|
|
210
|
-
let native_dimensions = typeof pc?.native_dimensions === "number" ? pc.native_dimensions : undefined;
|
|
211
|
-
let mrl = typeof pc?.mrl === "boolean" ? pc.mrl : undefined;
|
|
212
|
-
if (native_dimensions === undefined) {
|
|
213
|
-
const modelName = pc?.model_name ?? "";
|
|
214
|
-
const known = OPENAI_EMBEDDING_DIMENSIONS[modelName];
|
|
215
|
-
if (known) {
|
|
216
|
-
native_dimensions = known.native_dimensions;
|
|
217
|
-
mrl = mrl ?? known.mrl;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
model: input.model,
|
|
222
|
-
is_local: false,
|
|
223
|
-
is_remote: true,
|
|
224
|
-
supports_browser: true,
|
|
225
|
-
supports_node: true,
|
|
226
|
-
is_cached: false,
|
|
227
|
-
is_loaded: false,
|
|
228
|
-
file_sizes: null,
|
|
229
|
-
...native_dimensions !== undefined ? { native_dimensions } : {},
|
|
230
|
-
...mrl !== undefined ? { mrl } : {}
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
return {
|
|
234
|
-
model: input.model,
|
|
235
|
-
is_local: false,
|
|
236
|
-
is_remote: true,
|
|
237
|
-
supports_browser: true,
|
|
238
|
-
supports_node: true,
|
|
239
|
-
is_cached: false,
|
|
240
|
-
is_loaded: false,
|
|
241
|
-
file_sizes: null
|
|
242
|
-
};
|
|
405
|
+
emit({ type: "finish", data: { results: mapModelList(models) } });
|
|
243
406
|
};
|
|
244
407
|
|
|
245
408
|
// src/ai/common/OpenAI_StructuredGeneration.ts
|
|
246
409
|
import { parsePartialJson } from "@workglow/util/worker";
|
|
247
|
-
var
|
|
248
|
-
update_progress(0, "Starting OpenAI structured generation");
|
|
249
|
-
const client = await getClient(model);
|
|
250
|
-
const modelName = getModelName(model);
|
|
251
|
-
const schema = input.outputSchema ?? outputSchema;
|
|
252
|
-
const response = await client.chat.completions.create({
|
|
253
|
-
model: modelName,
|
|
254
|
-
messages: [{ role: "user", content: input.prompt }],
|
|
255
|
-
response_format: {
|
|
256
|
-
type: "json_schema",
|
|
257
|
-
json_schema: {
|
|
258
|
-
name: "structured_output",
|
|
259
|
-
schema,
|
|
260
|
-
strict: true
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
max_completion_tokens: input.maxTokens,
|
|
264
|
-
temperature: input.temperature
|
|
265
|
-
}, { signal });
|
|
266
|
-
const content = response.choices[0]?.message?.content ?? "{}";
|
|
267
|
-
update_progress(100, "Completed OpenAI structured generation");
|
|
268
|
-
return { object: JSON.parse(content) };
|
|
269
|
-
};
|
|
270
|
-
var OpenAI_StructuredGeneration_Stream = async function* (input, model, signal, outputSchema) {
|
|
410
|
+
var OpenAI_StructuredGeneration_Stream = async (input, model, signal, emit, outputSchema) => {
|
|
271
411
|
const client = await getClient(model);
|
|
272
412
|
const modelName = getModelName(model);
|
|
273
413
|
const schema = input.outputSchema ?? outputSchema;
|
|
@@ -293,7 +433,7 @@ var OpenAI_StructuredGeneration_Stream = async function* (input, model, signal,
|
|
|
293
433
|
accumulatedJson += delta;
|
|
294
434
|
const partial = parsePartialJson(accumulatedJson);
|
|
295
435
|
if (partial !== undefined) {
|
|
296
|
-
|
|
436
|
+
emit({ type: "object-delta", port: "object", objectDelta: partial });
|
|
297
437
|
}
|
|
298
438
|
}
|
|
299
439
|
}
|
|
@@ -303,92 +443,80 @@ var OpenAI_StructuredGeneration_Stream = async function* (input, model, signal,
|
|
|
303
443
|
} catch {
|
|
304
444
|
finalObject = parsePartialJson(accumulatedJson) ?? {};
|
|
305
445
|
}
|
|
306
|
-
|
|
446
|
+
emit({ type: "finish", data: { object: finalObject } });
|
|
307
447
|
};
|
|
308
448
|
|
|
309
449
|
// src/ai/common/OpenAI_TextEmbedding.ts
|
|
310
450
|
import { getLogger } from "@workglow/util/worker";
|
|
311
|
-
var
|
|
451
|
+
var OpenAI_TextEmbedding_Stream = async (input, model, signal, emit) => {
|
|
312
452
|
const logger = getLogger();
|
|
313
|
-
const timerLabel = `openai:TextEmbedding:${model
|
|
314
|
-
logger.time(timerLabel, { model: model
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
logger.timeEnd(timerLabel, { model: model?.provider_config?.model_name });
|
|
324
|
-
if (Array.isArray(input.text)) {
|
|
325
|
-
return {
|
|
453
|
+
const timerLabel = `openai:TextEmbedding:${getModelName(model)}`;
|
|
454
|
+
logger.time(timerLabel, { model: getModelName(model) });
|
|
455
|
+
try {
|
|
456
|
+
const client = await getClient(model);
|
|
457
|
+
const modelName = getModelName(model);
|
|
458
|
+
const response = await client.embeddings.create({
|
|
459
|
+
model: modelName,
|
|
460
|
+
input: input.text
|
|
461
|
+
}, { signal });
|
|
462
|
+
const result = Array.isArray(input.text) ? {
|
|
326
463
|
vector: response.data.map((item) => new Float32Array(item.embedding))
|
|
327
|
-
};
|
|
464
|
+
} : { vector: new Float32Array(response.data[0].embedding) };
|
|
465
|
+
emit({ type: "finish", data: result });
|
|
466
|
+
} finally {
|
|
467
|
+
logger.timeEnd(timerLabel, { model: getModelName(model) });
|
|
328
468
|
}
|
|
329
|
-
return { vector: new Float32Array(response.data[0].embedding) };
|
|
330
469
|
};
|
|
331
470
|
|
|
332
471
|
// src/ai/common/OpenAI_TextGeneration.ts
|
|
472
|
+
import { toOpenAIMessages } from "@workglow/ai/worker";
|
|
333
473
|
import { getLogger as getLogger2 } from "@workglow/util/worker";
|
|
334
|
-
|
|
474
|
+
function buildChatParams(input, model) {
|
|
475
|
+
const hasMessages = Array.isArray(input.messages) && input.messages.length > 0;
|
|
476
|
+
const messages = hasMessages ? toOpenAIMessages({
|
|
477
|
+
messages: input.messages,
|
|
478
|
+
systemPrompt: input.systemPrompt,
|
|
479
|
+
prompt: "",
|
|
480
|
+
tools: []
|
|
481
|
+
}) : [{ role: "user", content: input.prompt }];
|
|
482
|
+
const params = {
|
|
483
|
+
model: getModelName(model),
|
|
484
|
+
messages
|
|
485
|
+
};
|
|
486
|
+
if (input.maxTokens !== undefined)
|
|
487
|
+
params.max_completion_tokens = input.maxTokens;
|
|
488
|
+
if (input.temperature !== undefined)
|
|
489
|
+
params.temperature = input.temperature;
|
|
490
|
+
if (input.topP !== undefined)
|
|
491
|
+
params.top_p = input.topP;
|
|
492
|
+
if (input.frequencyPenalty !== undefined)
|
|
493
|
+
params.frequency_penalty = input.frequencyPenalty;
|
|
494
|
+
if (input.presencePenalty !== undefined)
|
|
495
|
+
params.presence_penalty = input.presencePenalty;
|
|
496
|
+
return params;
|
|
497
|
+
}
|
|
498
|
+
var OpenAI_TextGeneration_Stream = async (input, model, signal, emit) => {
|
|
335
499
|
const logger = getLogger2();
|
|
336
|
-
const timerLabel = `openai:TextGeneration:${model
|
|
337
|
-
logger.time(timerLabel, { model: model
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
frequency_penalty: input.frequencyPenalty,
|
|
348
|
-
presence_penalty: input.presencePenalty
|
|
349
|
-
}, { signal });
|
|
350
|
-
update_progress(100, "Completed OpenAI text generation");
|
|
351
|
-
logger.timeEnd(timerLabel, { model: model?.provider_config?.model_name });
|
|
352
|
-
return { text: response.choices[0]?.message?.content ?? "" };
|
|
353
|
-
};
|
|
354
|
-
var OpenAI_TextGeneration_Stream = async function* (input, model, signal) {
|
|
355
|
-
const client = await getClient(model);
|
|
356
|
-
const modelName = getModelName(model);
|
|
357
|
-
const stream = await client.chat.completions.create({
|
|
358
|
-
model: modelName,
|
|
359
|
-
messages: [{ role: "user", content: input.prompt }],
|
|
360
|
-
max_completion_tokens: input.maxTokens,
|
|
361
|
-
temperature: input.temperature,
|
|
362
|
-
top_p: input.topP,
|
|
363
|
-
frequency_penalty: input.frequencyPenalty,
|
|
364
|
-
presence_penalty: input.presencePenalty,
|
|
365
|
-
stream: true
|
|
366
|
-
}, { signal });
|
|
367
|
-
for await (const chunk of stream) {
|
|
368
|
-
const delta = chunk.choices[0]?.delta?.content ?? "";
|
|
369
|
-
if (delta) {
|
|
370
|
-
yield { type: "text-delta", port: "text", textDelta: delta };
|
|
500
|
+
const timerLabel = `openai:TextGeneration:${getModelName(model)}`;
|
|
501
|
+
logger.time(timerLabel, { model: getModelName(model) });
|
|
502
|
+
try {
|
|
503
|
+
const client = await getClient(model);
|
|
504
|
+
const params = buildChatParams(input, model);
|
|
505
|
+
const stream = await client.chat.completions.create({ ...params, stream: true }, { signal });
|
|
506
|
+
for await (const chunk of stream) {
|
|
507
|
+
const delta = chunk.choices?.[0]?.delta?.content ?? "";
|
|
508
|
+
if (delta) {
|
|
509
|
+
emit({ type: "text-delta", port: "text", textDelta: delta });
|
|
510
|
+
}
|
|
371
511
|
}
|
|
512
|
+
emit({ type: "finish", data: {} });
|
|
513
|
+
} finally {
|
|
514
|
+
logger.timeEnd(timerLabel, { model: getModelName(model) });
|
|
372
515
|
}
|
|
373
|
-
yield { type: "finish", data: {} };
|
|
374
516
|
};
|
|
375
517
|
|
|
376
518
|
// src/ai/common/OpenAI_TextRewriter.ts
|
|
377
|
-
var
|
|
378
|
-
update_progress(0, "Starting OpenAI text rewriting");
|
|
379
|
-
const client = await getClient(model);
|
|
380
|
-
const modelName = getModelName(model);
|
|
381
|
-
const response = await client.chat.completions.create({
|
|
382
|
-
model: modelName,
|
|
383
|
-
messages: [
|
|
384
|
-
{ role: "system", content: input.prompt },
|
|
385
|
-
{ role: "user", content: input.text }
|
|
386
|
-
]
|
|
387
|
-
}, { signal });
|
|
388
|
-
update_progress(100, "Completed OpenAI text rewriting");
|
|
389
|
-
return { text: response.choices[0]?.message?.content ?? "" };
|
|
390
|
-
};
|
|
391
|
-
var OpenAI_TextRewriter_Stream = async function* (input, model, signal) {
|
|
519
|
+
var OpenAI_TextRewriter_Stream = async (input, model, signal, emit) => {
|
|
392
520
|
const client = await getClient(model);
|
|
393
521
|
const modelName = getModelName(model);
|
|
394
522
|
const stream = await client.chat.completions.create({
|
|
@@ -402,28 +530,14 @@ var OpenAI_TextRewriter_Stream = async function* (input, model, signal) {
|
|
|
402
530
|
for await (const chunk of stream) {
|
|
403
531
|
const delta = chunk.choices[0]?.delta?.content ?? "";
|
|
404
532
|
if (delta) {
|
|
405
|
-
|
|
533
|
+
emit({ type: "text-delta", port: "text", textDelta: delta });
|
|
406
534
|
}
|
|
407
535
|
}
|
|
408
|
-
|
|
536
|
+
emit({ type: "finish", data: {} });
|
|
409
537
|
};
|
|
410
538
|
|
|
411
539
|
// src/ai/common/OpenAI_TextSummary.ts
|
|
412
|
-
var
|
|
413
|
-
update_progress(0, "Starting OpenAI text summarization");
|
|
414
|
-
const client = await getClient(model);
|
|
415
|
-
const modelName = getModelName(model);
|
|
416
|
-
const response = await client.chat.completions.create({
|
|
417
|
-
model: modelName,
|
|
418
|
-
messages: [
|
|
419
|
-
{ role: "system", content: "Summarize the following text concisely." },
|
|
420
|
-
{ role: "user", content: input.text }
|
|
421
|
-
]
|
|
422
|
-
}, { signal });
|
|
423
|
-
update_progress(100, "Completed OpenAI text summarization");
|
|
424
|
-
return { text: response.choices[0]?.message?.content ?? "" };
|
|
425
|
-
};
|
|
426
|
-
var OpenAI_TextSummary_Stream = async function* (input, model, signal) {
|
|
540
|
+
var OpenAI_TextSummary_Stream = async (input, model, signal, emit) => {
|
|
427
541
|
const client = await getClient(model);
|
|
428
542
|
const modelName = getModelName(model);
|
|
429
543
|
const stream = await client.chat.completions.create({
|
|
@@ -437,10 +551,10 @@ var OpenAI_TextSummary_Stream = async function* (input, model, signal) {
|
|
|
437
551
|
for await (const chunk of stream) {
|
|
438
552
|
const delta = chunk.choices[0]?.delta?.content ?? "";
|
|
439
553
|
if (delta) {
|
|
440
|
-
|
|
554
|
+
emit({ type: "text-delta", port: "text", textDelta: delta });
|
|
441
555
|
}
|
|
442
556
|
}
|
|
443
|
-
|
|
557
|
+
emit({ type: "finish", data: {} });
|
|
444
558
|
};
|
|
445
559
|
|
|
446
560
|
// src/ai/common/OpenAI_ToolCalling.ts
|
|
@@ -448,30 +562,9 @@ import { filterValidToolCalls, toOpenAIMessages as toOpenAIMessages2 } from "@wo
|
|
|
448
562
|
import {
|
|
449
563
|
accumulateOpenAIStream,
|
|
450
564
|
buildOpenAITools,
|
|
451
|
-
mapOpenAIToolChoice
|
|
452
|
-
parseOpenAIToolCallMessage
|
|
565
|
+
mapOpenAIToolChoice
|
|
453
566
|
} from "@workglow/ai/provider-utils";
|
|
454
|
-
var
|
|
455
|
-
update_progress(0, "Starting OpenAI tool calling");
|
|
456
|
-
const client = await getClient(model);
|
|
457
|
-
const modelName = getModelName(model);
|
|
458
|
-
const tools = buildOpenAITools(input.tools);
|
|
459
|
-
const messages = toOpenAIMessages2(input);
|
|
460
|
-
const toolChoice = mapOpenAIToolChoice(input.toolChoice, true);
|
|
461
|
-
const response = await client.chat.completions.create({
|
|
462
|
-
model: modelName,
|
|
463
|
-
messages,
|
|
464
|
-
max_completion_tokens: input.maxTokens,
|
|
465
|
-
temperature: input.temperature,
|
|
466
|
-
tools,
|
|
467
|
-
tool_choice: toolChoice
|
|
468
|
-
}, { signal });
|
|
469
|
-
const text = response.choices[0]?.message?.content ?? "";
|
|
470
|
-
const toolCalls = parseOpenAIToolCallMessage(response.choices[0]?.message?.tool_calls);
|
|
471
|
-
update_progress(100, "Completed OpenAI tool calling");
|
|
472
|
-
return { text, toolCalls: filterValidToolCalls(toolCalls, input.tools) };
|
|
473
|
-
};
|
|
474
|
-
var OpenAI_ToolCalling_Stream = async function* (input, model, signal) {
|
|
567
|
+
var OpenAI_ToolCalling_Stream = async (input, model, signal, emit) => {
|
|
475
568
|
const client = await getClient(model);
|
|
476
569
|
const modelName = getModelName(model);
|
|
477
570
|
const tools = buildOpenAITools(input.tools);
|
|
@@ -486,267 +579,32 @@ var OpenAI_ToolCalling_Stream = async function* (input, model, signal) {
|
|
|
486
579
|
tools,
|
|
487
580
|
tool_choice: toolChoice
|
|
488
581
|
}, { signal });
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
import { getLogger as getLogger3 } from "@workglow/util/worker";
|
|
495
|
-
import { dataUriToImageValue } from "@workglow/ai/provider-utils";
|
|
496
|
-
function aspectRatioToSize(aspectRatio) {
|
|
497
|
-
switch (aspectRatio) {
|
|
498
|
-
case "16:9":
|
|
499
|
-
case "4:3":
|
|
500
|
-
return "1536x1024";
|
|
501
|
-
case "9:16":
|
|
502
|
-
case "3:4":
|
|
503
|
-
return "1024x1536";
|
|
504
|
-
case "1:1":
|
|
505
|
-
default:
|
|
506
|
-
return "1024x1024";
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
async function decodeB64Png(b64) {
|
|
510
|
-
return dataUriToImageValue(`data:image/png;base64,${b64}`);
|
|
511
|
-
}
|
|
512
|
-
function modelIdOf(model) {
|
|
513
|
-
return model?.model_id ?? model?.provider_config?.model_name ?? "openai";
|
|
514
|
-
}
|
|
515
|
-
var OpenAI_ImageGenerate = async (input, model, update_progress, signal) => {
|
|
516
|
-
const logger = getLogger3();
|
|
517
|
-
const timer = `openai:ImageGenerate:${getModelName(model)}`;
|
|
518
|
-
logger.time(timer);
|
|
519
|
-
update_progress(0, "Starting OpenAI image generation");
|
|
520
|
-
const client = await getClient(model);
|
|
521
|
-
const modelName = getModelName(model);
|
|
522
|
-
const size = aspectRatioToSize(input.aspectRatio);
|
|
523
|
-
try {
|
|
524
|
-
const resp = await client.images.generate({
|
|
525
|
-
model: modelName,
|
|
526
|
-
prompt: input.prompt,
|
|
527
|
-
size,
|
|
528
|
-
quality: input.quality,
|
|
529
|
-
n: 1,
|
|
530
|
-
response_format: "b64_json",
|
|
531
|
-
...input.providerOptions ?? {}
|
|
532
|
-
}, { signal });
|
|
533
|
-
const b64 = resp.data?.[0]?.b64_json;
|
|
534
|
-
if (!b64) {
|
|
535
|
-
throw new ImageGenerationProviderError(modelIdOf(model), "Empty response (no b64_json)");
|
|
536
|
-
}
|
|
537
|
-
const image = await decodeB64Png(b64);
|
|
538
|
-
update_progress(100, "Completed OpenAI image generation");
|
|
539
|
-
logger.timeEnd(timer);
|
|
540
|
-
return { image };
|
|
541
|
-
} catch (err) {
|
|
542
|
-
if (err instanceof ImageGenerationProviderError || err instanceof ImageGenerationContentPolicyError) {
|
|
543
|
-
throw err;
|
|
544
|
-
}
|
|
545
|
-
const msg = err instanceof Error ? err.message : "unknown error";
|
|
546
|
-
if (/safety|policy|moderation/i.test(msg)) {
|
|
547
|
-
throw new ImageGenerationContentPolicyError(modelIdOf(model), msg);
|
|
548
|
-
}
|
|
549
|
-
throw new ImageGenerationProviderError(modelIdOf(model), msg, { cause: err });
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
var OpenAI_ImageGenerate_Stream = async function* (input, model, signal) {
|
|
553
|
-
const client = await getClient(model);
|
|
554
|
-
const modelName = getModelName(model);
|
|
555
|
-
const size = aspectRatioToSize(input.aspectRatio);
|
|
556
|
-
if (modelName.startsWith("dall-e")) {
|
|
557
|
-
try {
|
|
558
|
-
const resp = await client.images.generate({
|
|
559
|
-
model: modelName,
|
|
560
|
-
prompt: input.prompt,
|
|
561
|
-
size,
|
|
562
|
-
quality: input.quality,
|
|
563
|
-
n: 1,
|
|
564
|
-
response_format: "b64_json",
|
|
565
|
-
...input.providerOptions ?? {}
|
|
566
|
-
}, { signal });
|
|
567
|
-
const b64 = resp.data?.[0]?.b64_json;
|
|
568
|
-
if (!b64) {
|
|
569
|
-
throw new ImageGenerationProviderError(modelIdOf(model), "Empty response (no b64_json)");
|
|
570
|
-
}
|
|
571
|
-
const image = await decodeB64Png(b64);
|
|
572
|
-
yield { type: "snapshot", data: { image } };
|
|
573
|
-
yield { type: "finish", data: {} };
|
|
574
|
-
return;
|
|
575
|
-
} catch (err) {
|
|
576
|
-
if (err instanceof ImageGenerationProviderError || err instanceof ImageGenerationContentPolicyError) {
|
|
577
|
-
throw err;
|
|
578
|
-
}
|
|
579
|
-
const msg = err instanceof Error ? err.message : "unknown error";
|
|
580
|
-
if (/safety|policy|moderation/i.test(msg)) {
|
|
581
|
-
throw new ImageGenerationContentPolicyError(modelIdOf(model), msg);
|
|
582
|
+
for await (const event of accumulateOpenAIStream(stream)) {
|
|
583
|
+
if (event.type === "object-delta" && event.port === "toolCalls") {
|
|
584
|
+
const validated = filterValidToolCalls(event.objectDelta, input.tools);
|
|
585
|
+
if (validated.length > 0) {
|
|
586
|
+
emit({ type: "object-delta", port: "toolCalls", objectDelta: validated });
|
|
582
587
|
}
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
try {
|
|
587
|
-
const stream = await client.images.generate({
|
|
588
|
-
model: modelName,
|
|
589
|
-
prompt: input.prompt,
|
|
590
|
-
size,
|
|
591
|
-
quality: input.quality,
|
|
592
|
-
n: 1,
|
|
593
|
-
stream: true,
|
|
594
|
-
partial_images: 3,
|
|
595
|
-
...input.providerOptions ?? {}
|
|
596
|
-
}, { signal });
|
|
597
|
-
for await (const event of stream) {
|
|
598
|
-
if (signal.aborted)
|
|
599
|
-
return;
|
|
600
|
-
const b64 = event.b64_json;
|
|
601
|
-
if (!b64)
|
|
602
|
-
continue;
|
|
603
|
-
const image = await decodeB64Png(b64);
|
|
604
|
-
yield { type: "snapshot", data: { image } };
|
|
605
|
-
}
|
|
606
|
-
yield { type: "finish", data: {} };
|
|
607
|
-
} catch (err) {
|
|
608
|
-
if (err instanceof ImageGenerationProviderError || err instanceof ImageGenerationContentPolicyError) {
|
|
609
|
-
throw err;
|
|
610
|
-
}
|
|
611
|
-
const msg = err instanceof Error ? err.message : "unknown error";
|
|
612
|
-
if (/safety|policy|moderation/i.test(msg)) {
|
|
613
|
-
throw new ImageGenerationContentPolicyError(modelIdOf(model), msg);
|
|
614
|
-
}
|
|
615
|
-
throw new ImageGenerationProviderError(modelIdOf(model), msg, { cause: err });
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
// src/ai/common/OpenAI_ImageEdit.ts
|
|
620
|
-
import { ImageGenerationContentPolicyError as ImageGenerationContentPolicyError2, ImageGenerationProviderError as ImageGenerationProviderError2 } from "@workglow/ai";
|
|
621
|
-
import { getLogger as getLogger4 } from "@workglow/util/worker";
|
|
622
|
-
import { dataUriToImageValue as dataUriToImageValue2, imageValueToPngBytes } from "@workglow/ai/provider-utils";
|
|
623
|
-
function aspectRatioToSize2(aspectRatio) {
|
|
624
|
-
switch (aspectRatio) {
|
|
625
|
-
case "16:9":
|
|
626
|
-
case "4:3":
|
|
627
|
-
return "1536x1024";
|
|
628
|
-
case "9:16":
|
|
629
|
-
case "3:4":
|
|
630
|
-
return "1024x1536";
|
|
631
|
-
case "1:1":
|
|
632
|
-
default:
|
|
633
|
-
return "1024x1024";
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
function modelIdOf2(model) {
|
|
637
|
-
return model?.model_id ?? model?.provider_config?.model_name ?? "openai";
|
|
638
|
-
}
|
|
639
|
-
async function gpuImageToOpenAiFile(image, name) {
|
|
640
|
-
const bytes = await imageValueToPngBytes(image);
|
|
641
|
-
const buffer = bytes.buffer instanceof ArrayBuffer ? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) : new Uint8Array(bytes).buffer;
|
|
642
|
-
const sdk = await import("openai");
|
|
643
|
-
if (typeof sdk.toFile === "function") {
|
|
644
|
-
return sdk.toFile(new Blob([buffer], { type: "image/png" }), name, { type: "image/png" });
|
|
645
|
-
}
|
|
646
|
-
return new File([buffer], name, { type: "image/png" });
|
|
647
|
-
}
|
|
648
|
-
async function decodeB64Png2(b64) {
|
|
649
|
-
return dataUriToImageValue2(`data:image/png;base64,${b64}`);
|
|
650
|
-
}
|
|
651
|
-
async function buildEditPayload(input, model) {
|
|
652
|
-
const modelName = getModelName(model);
|
|
653
|
-
const primary = await gpuImageToOpenAiFile(input.image, "image.png");
|
|
654
|
-
const additionalFiles = input.additionalImages && input.additionalImages.length > 0 ? await Promise.all(input.additionalImages.map((g, i) => gpuImageToOpenAiFile(g, `image-${i + 1}.png`))) : [];
|
|
655
|
-
const imageField = additionalFiles.length === 0 ? primary : [primary, ...additionalFiles];
|
|
656
|
-
const payload = {
|
|
657
|
-
model: modelName,
|
|
658
|
-
prompt: input.prompt,
|
|
659
|
-
image: imageField,
|
|
660
|
-
size: aspectRatioToSize2(input.aspectRatio),
|
|
661
|
-
quality: input.quality,
|
|
662
|
-
n: 1,
|
|
663
|
-
...input.providerOptions ?? {}
|
|
664
|
-
};
|
|
665
|
-
if (input.mask) {
|
|
666
|
-
payload.mask = await gpuImageToOpenAiFile(input.mask, "mask.png");
|
|
667
|
-
}
|
|
668
|
-
return payload;
|
|
669
|
-
}
|
|
670
|
-
var OpenAI_ImageEdit = async (input, model, update_progress, signal) => {
|
|
671
|
-
const logger = getLogger4();
|
|
672
|
-
const timer = `openai:ImageEdit:${getModelName(model)}`;
|
|
673
|
-
logger.time(timer);
|
|
674
|
-
update_progress(0, "Starting OpenAI image edit");
|
|
675
|
-
const client = await getClient(model);
|
|
676
|
-
try {
|
|
677
|
-
const payload = await buildEditPayload(input, model);
|
|
678
|
-
const resp = await client.images.edit(payload, { signal });
|
|
679
|
-
const b64 = resp.data?.[0]?.b64_json;
|
|
680
|
-
if (!b64) {
|
|
681
|
-
throw new ImageGenerationProviderError2(modelIdOf2(model), "Empty response (no b64_json)");
|
|
682
|
-
}
|
|
683
|
-
const image = await decodeB64Png2(b64);
|
|
684
|
-
update_progress(100, "Completed OpenAI image edit");
|
|
685
|
-
logger.timeEnd(timer);
|
|
686
|
-
return { image };
|
|
687
|
-
} catch (err) {
|
|
688
|
-
if (err instanceof ImageGenerationProviderError2 || err instanceof ImageGenerationContentPolicyError2) {
|
|
689
|
-
throw err;
|
|
690
|
-
}
|
|
691
|
-
const msg = err instanceof Error ? err.message : "unknown error";
|
|
692
|
-
if (/safety|policy|moderation/i.test(msg)) {
|
|
693
|
-
throw new ImageGenerationContentPolicyError2(modelIdOf2(model), msg);
|
|
694
|
-
}
|
|
695
|
-
throw new ImageGenerationProviderError2(modelIdOf2(model), msg, { cause: err });
|
|
696
|
-
}
|
|
697
|
-
};
|
|
698
|
-
var OpenAI_ImageEdit_Stream = async function* (input, model, signal) {
|
|
699
|
-
const client = await getClient(model);
|
|
700
|
-
try {
|
|
701
|
-
const payload = await buildEditPayload(input, model);
|
|
702
|
-
const stream = await client.images.edit({ ...payload, stream: true, partial_images: 3 }, { signal });
|
|
703
|
-
for await (const event of stream) {
|
|
704
|
-
if (signal.aborted)
|
|
705
|
-
return;
|
|
706
|
-
const b64 = event.b64_json;
|
|
707
|
-
if (!b64)
|
|
708
|
-
continue;
|
|
709
|
-
const image = await decodeB64Png2(b64);
|
|
710
|
-
yield { type: "snapshot", data: { image } };
|
|
711
|
-
}
|
|
712
|
-
yield { type: "finish", data: {} };
|
|
713
|
-
} catch (err) {
|
|
714
|
-
if (err instanceof ImageGenerationProviderError2 || err instanceof ImageGenerationContentPolicyError2) {
|
|
715
|
-
throw err;
|
|
588
|
+
continue;
|
|
716
589
|
}
|
|
717
|
-
|
|
718
|
-
if (/safety|policy|moderation/i.test(msg)) {
|
|
719
|
-
throw new ImageGenerationContentPolicyError2(modelIdOf2(model), msg);
|
|
720
|
-
}
|
|
721
|
-
throw new ImageGenerationProviderError2(modelIdOf2(model), msg, { cause: err });
|
|
590
|
+
emit(event);
|
|
722
591
|
}
|
|
723
592
|
};
|
|
724
593
|
|
|
725
594
|
// src/ai/common/OpenAI_JobRunFns.ts
|
|
726
|
-
var
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
};
|
|
740
|
-
var OPENAI_STREAM_TASKS = {
|
|
741
|
-
AiChatTask: OpenAI_Chat_Stream,
|
|
742
|
-
TextGenerationTask: OpenAI_TextGeneration_Stream,
|
|
743
|
-
TextRewriterTask: OpenAI_TextRewriter_Stream,
|
|
744
|
-
TextSummaryTask: OpenAI_TextSummary_Stream,
|
|
745
|
-
StructuredGenerationTask: OpenAI_StructuredGeneration_Stream,
|
|
746
|
-
ToolCallingTask: OpenAI_ToolCalling_Stream,
|
|
747
|
-
ImageGenerateTask: OpenAI_ImageGenerate_Stream,
|
|
748
|
-
ImageEditTask: OpenAI_ImageEdit_Stream
|
|
749
|
-
};
|
|
595
|
+
var OPENAI_RUN_FNS = [
|
|
596
|
+
{ serves: OPENAI_TEXT_GENERATION, runFn: OpenAI_TextGeneration_Stream },
|
|
597
|
+
{ serves: OPENAI_TOOL_USE, runFn: OpenAI_ToolCalling_Stream },
|
|
598
|
+
{ serves: OPENAI_JSON_MODE, runFn: OpenAI_StructuredGeneration_Stream },
|
|
599
|
+
{ serves: OPENAI_TEXT_REWRITER, runFn: OpenAI_TextRewriter_Stream },
|
|
600
|
+
{ serves: OPENAI_TEXT_SUMMARY, runFn: OpenAI_TextSummary_Stream },
|
|
601
|
+
{ serves: OPENAI_TEXT_EMBEDDING, runFn: OpenAI_TextEmbedding_Stream },
|
|
602
|
+
{ serves: OPENAI_IMAGE_GENERATION, runFn: OpenAI_ImageGenerate_Stream },
|
|
603
|
+
{ serves: OPENAI_IMAGE_EDITING, runFn: OpenAI_ImageEdit_Stream },
|
|
604
|
+
{ serves: OPENAI_COUNT_TOKENS, runFn: OpenAI_CountTokens_Stream },
|
|
605
|
+
{ serves: OPENAI_MODEL_SEARCH, runFn: OpenAI_ModelSearch_Stream },
|
|
606
|
+
{ serves: OPENAI_MODEL_INFO, runFn: OpenAI_ModelInfo_Stream }
|
|
607
|
+
];
|
|
750
608
|
var OPENAI_PREVIEW_TASKS = {
|
|
751
609
|
CountTokensTask: OpenAI_CountTokens_Preview
|
|
752
610
|
};
|
|
@@ -754,25 +612,57 @@ var OPENAI_PREVIEW_TASKS = {
|
|
|
754
612
|
// src/ai/OpenAiQueuedProvider.ts
|
|
755
613
|
import { AiProvider } from "@workglow/ai";
|
|
756
614
|
import { createCloudProviderClass } from "@workglow/ai/provider-utils";
|
|
757
|
-
var OPENAI_QUEUED_TASK_TYPES = [
|
|
758
|
-
"TextGenerationTask",
|
|
759
|
-
"TextEmbeddingTask",
|
|
760
|
-
"TextRewriterTask",
|
|
761
|
-
"TextSummaryTask",
|
|
762
|
-
"CountTokensTask",
|
|
763
|
-
"ModelInfoTask",
|
|
764
|
-
"StructuredGenerationTask",
|
|
765
|
-
"ToolCallingTask",
|
|
766
|
-
"ModelSearchTask",
|
|
767
|
-
"ImageGenerateTask",
|
|
768
|
-
"ImageEditTask"
|
|
769
|
-
];
|
|
770
615
|
|
|
616
|
+
// src/ai/common/OpenAI_Capabilities.ts
|
|
617
|
+
var OPENAI_RUN_FN_SPECS = OPENAI_CAPABILITY_SETS.map((serves) => ({ serves }));
|
|
618
|
+
function openAiWorkerRunFnSpecs() {
|
|
619
|
+
return OPENAI_RUN_FN_SPECS;
|
|
620
|
+
}
|
|
621
|
+
function inferOpenAiCapabilities(model) {
|
|
622
|
+
const id = String(model.model_id ?? model.provider_config?.model_name ?? "");
|
|
623
|
+
if (/^text-embedding/i.test(id)) {
|
|
624
|
+
return ["text.embedding", "model.count-tokens", "model.info", "model.search"];
|
|
625
|
+
}
|
|
626
|
+
if (/^dall-e/i.test(id)) {
|
|
627
|
+
return ["image.generation", "model.info", "model.search"];
|
|
628
|
+
}
|
|
629
|
+
if (/^gpt-image/i.test(id)) {
|
|
630
|
+
return ["image.generation", "image.editing", "model.info", "model.search"];
|
|
631
|
+
}
|
|
632
|
+
if (/^gpt-/i.test(id) || /^o\d/i.test(id)) {
|
|
633
|
+
const caps = [
|
|
634
|
+
"text.generation",
|
|
635
|
+
"text.rewriter",
|
|
636
|
+
"text.summary",
|
|
637
|
+
"tool-use",
|
|
638
|
+
"json-mode",
|
|
639
|
+
"model.count-tokens",
|
|
640
|
+
"model.info",
|
|
641
|
+
"model.search"
|
|
642
|
+
];
|
|
643
|
+
const supportsVision = /gpt-4o|gpt-4\.1|gpt-5|gpt-4-vision|gpt-4-turbo/i.test(id) || /^o\d/i.test(id);
|
|
644
|
+
if (supportsVision) {
|
|
645
|
+
caps.push("vision-input");
|
|
646
|
+
}
|
|
647
|
+
return caps;
|
|
648
|
+
}
|
|
649
|
+
const declared = model.capabilities ?? [];
|
|
650
|
+
if (declared.length > 0)
|
|
651
|
+
return declared;
|
|
652
|
+
return ["model.search", "model.info"];
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/ai/OpenAiQueuedProvider.ts
|
|
771
656
|
class OpenAiQueuedProvider extends createCloudProviderClass(AiProvider, {
|
|
772
657
|
name: OPENAI,
|
|
773
|
-
displayName: "OpenAI"
|
|
774
|
-
taskTypes: OPENAI_QUEUED_TASK_TYPES
|
|
658
|
+
displayName: "OpenAI"
|
|
775
659
|
}) {
|
|
660
|
+
inferCapabilities(model) {
|
|
661
|
+
return inferOpenAiCapabilities(model);
|
|
662
|
+
}
|
|
663
|
+
workerRunFnSpecs() {
|
|
664
|
+
return openAiWorkerRunFnSpecs();
|
|
665
|
+
}
|
|
776
666
|
}
|
|
777
667
|
|
|
778
668
|
// src/ai/common/OpenAI_ImageValidation.ts
|
|
@@ -792,7 +682,7 @@ function registerOpenAiImageValidator() {
|
|
|
792
682
|
// src/ai/registerOpenAiInline.ts
|
|
793
683
|
async function registerOpenAiInline(options) {
|
|
794
684
|
registerOpenAiImageValidator();
|
|
795
|
-
await registerProviderInline(new OpenAiQueuedProvider(
|
|
685
|
+
await registerProviderInline(new OpenAiQueuedProvider(OPENAI_RUN_FNS, OPENAI_PREVIEW_TASKS), "OpenAI", options);
|
|
796
686
|
}
|
|
797
687
|
|
|
798
688
|
// src/ai/registerOpenAiWorker.ts
|
|
@@ -801,28 +691,21 @@ import { registerProviderWorker } from "@workglow/ai/provider-utils";
|
|
|
801
691
|
// src/ai/OpenAiProvider.ts
|
|
802
692
|
import { AiProvider as AiProvider2 } from "@workglow/ai/worker";
|
|
803
693
|
import { createCloudProviderClass as createCloudProviderClass2 } from "@workglow/ai/provider-utils";
|
|
804
|
-
var OPENAI_WORKER_TASK_TYPES = [
|
|
805
|
-
"TextGenerationTask",
|
|
806
|
-
"TextEmbeddingTask",
|
|
807
|
-
"TextRewriterTask",
|
|
808
|
-
"TextSummaryTask",
|
|
809
|
-
"CountTokensTask",
|
|
810
|
-
"ModelInfoTask",
|
|
811
|
-
"StructuredGenerationTask",
|
|
812
|
-
"ToolCallingTask",
|
|
813
|
-
"ModelSearchTask"
|
|
814
|
-
];
|
|
815
|
-
|
|
816
694
|
class OpenAiProvider extends createCloudProviderClass2(AiProvider2, {
|
|
817
695
|
name: OPENAI,
|
|
818
|
-
displayName: "OpenAI"
|
|
819
|
-
taskTypes: OPENAI_WORKER_TASK_TYPES
|
|
696
|
+
displayName: "OpenAI"
|
|
820
697
|
}) {
|
|
698
|
+
inferCapabilities(model) {
|
|
699
|
+
return inferOpenAiCapabilities(model);
|
|
700
|
+
}
|
|
701
|
+
workerRunFnSpecs() {
|
|
702
|
+
return openAiWorkerRunFnSpecs();
|
|
703
|
+
}
|
|
821
704
|
}
|
|
822
705
|
|
|
823
706
|
// src/ai/registerOpenAiWorker.ts
|
|
824
707
|
async function registerOpenAiWorker() {
|
|
825
|
-
await registerProviderWorker((ws) => new OpenAiProvider(
|
|
708
|
+
await registerProviderWorker((ws) => new OpenAiProvider(OPENAI_RUN_FNS, OPENAI_PREVIEW_TASKS).registerOnWorkerServer(ws), "OpenAI");
|
|
826
709
|
}
|
|
827
710
|
export {
|
|
828
711
|
registerOpenAiWorker,
|
|
@@ -832,4 +715,4 @@ export {
|
|
|
832
715
|
getClient
|
|
833
716
|
};
|
|
834
717
|
|
|
835
|
-
//# debugId=
|
|
718
|
+
//# debugId=5287D55DD373DFF164756E2164756E21
|