@discomedia/utils 1.0.71 → 1.0.72
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/index-frontend.cjs +147 -30
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +147 -31
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +147 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +147 -31
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +1 -1
- package/dist/test.js +4564 -5188
- package/dist/test.js.map +1 -1
- package/dist/types/index-frontend.d.ts +2 -1
- package/dist/types/index-frontend.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/llm-config.d.ts +2 -2
- package/dist/types/llm-config.d.ts.map +1 -1
- package/dist/types/llm-openai.d.ts +9 -2
- package/dist/types/llm-openai.d.ts.map +1 -1
- package/dist/types/types/llm-types.d.ts +48 -1
- package/dist/types/types/llm-types.d.ts.map +1 -1
- package/dist/types-frontend/index-frontend.d.ts +2 -1
- package/dist/types-frontend/index-frontend.d.ts.map +1 -1
- package/dist/types-frontend/index.d.ts +2 -1
- package/dist/types-frontend/index.d.ts.map +1 -1
- package/dist/types-frontend/llm-config.d.ts +2 -2
- package/dist/types-frontend/llm-config.d.ts.map +1 -1
- package/dist/types-frontend/llm-openai.d.ts +9 -2
- package/dist/types-frontend/llm-openai.d.ts.map +1 -1
- package/dist/types-frontend/types/llm-types.d.ts +48 -1
- package/dist/types-frontend/types/llm-types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index-frontend.cjs
CHANGED
|
@@ -12254,7 +12254,7 @@ function zodTextFormat(zodObject, name, props) {
|
|
|
12254
12254
|
}, (content) => zodObject.parse(JSON.parse(content)));
|
|
12255
12255
|
}
|
|
12256
12256
|
|
|
12257
|
-
const DEFAULT_MODEL = 'gpt-5-mini';
|
|
12257
|
+
const DEFAULT_MODEL = 'gpt-5.4-mini';
|
|
12258
12258
|
const GPT_5_HIGH_CONTEXT_THRESHOLD_TOKENS = 272_000;
|
|
12259
12259
|
const GPT_5_HIGH_CONTEXT_INPUT_MULTIPLIER = 2;
|
|
12260
12260
|
const GPT_5_HIGH_CONTEXT_OUTPUT_MULTIPLIER = 1.5;
|
|
@@ -12892,6 +12892,7 @@ async function parseResponse(content, responseFormat) {
|
|
|
12892
12892
|
}
|
|
12893
12893
|
|
|
12894
12894
|
// llm-openai.ts
|
|
12895
|
+
const DEFAULT_IMAGE_MIME_TYPE = 'image/webp';
|
|
12895
12896
|
/**
|
|
12896
12897
|
* Checks if the given direct OpenAI model supports the temperature parameter.
|
|
12897
12898
|
* @param model The model to check.
|
|
@@ -12926,6 +12927,135 @@ function buildJsonSchemaFormat(schema, schemaName, schemaDescription, schemaStri
|
|
|
12926
12927
|
...(schemaStrict !== undefined ? { strict: schemaStrict } : {}),
|
|
12927
12928
|
};
|
|
12928
12929
|
}
|
|
12930
|
+
function resolveOpenAIApiKey(apiKey) {
|
|
12931
|
+
const resolvedApiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
12932
|
+
if (!resolvedApiKey) {
|
|
12933
|
+
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
12934
|
+
}
|
|
12935
|
+
return resolvedApiKey;
|
|
12936
|
+
}
|
|
12937
|
+
function createOpenAIClient(apiKey) {
|
|
12938
|
+
return new OpenAI({
|
|
12939
|
+
apiKey,
|
|
12940
|
+
});
|
|
12941
|
+
}
|
|
12942
|
+
function normalizeImageDataUrl(imageData, mimeType = DEFAULT_IMAGE_MIME_TYPE) {
|
|
12943
|
+
return imageData.startsWith('data:') ? imageData : `data:${mimeType};base64,${imageData}`;
|
|
12944
|
+
}
|
|
12945
|
+
function getFilenameFromPath(filePath) {
|
|
12946
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
12947
|
+
const pathSegments = normalizedPath.split('/');
|
|
12948
|
+
const filename = pathSegments[pathSegments.length - 1];
|
|
12949
|
+
if (!filename) {
|
|
12950
|
+
throw new Error(`Could not determine a filename from file path: ${filePath}`);
|
|
12951
|
+
}
|
|
12952
|
+
return filename;
|
|
12953
|
+
}
|
|
12954
|
+
function inferImageMimeType(filePath) {
|
|
12955
|
+
const normalizedPath = filePath.toLowerCase();
|
|
12956
|
+
if (normalizedPath.endsWith('.png')) {
|
|
12957
|
+
return 'image/png';
|
|
12958
|
+
}
|
|
12959
|
+
if (normalizedPath.endsWith('.jpg') || normalizedPath.endsWith('.jpeg')) {
|
|
12960
|
+
return 'image/jpeg';
|
|
12961
|
+
}
|
|
12962
|
+
if (normalizedPath.endsWith('.webp')) {
|
|
12963
|
+
return 'image/webp';
|
|
12964
|
+
}
|
|
12965
|
+
if (normalizedPath.endsWith('.gif')) {
|
|
12966
|
+
return 'image/gif';
|
|
12967
|
+
}
|
|
12968
|
+
return undefined;
|
|
12969
|
+
}
|
|
12970
|
+
async function readLocalFileBytes(filePath) {
|
|
12971
|
+
try {
|
|
12972
|
+
const dynamicImport = new Function('specifier', 'return import(specifier);');
|
|
12973
|
+
const { readFile } = await dynamicImport('node:fs/promises');
|
|
12974
|
+
return await readFile(filePath);
|
|
12975
|
+
}
|
|
12976
|
+
catch {
|
|
12977
|
+
throw new Error('Local image file uploads require a Node.js runtime with access to node:fs/promises');
|
|
12978
|
+
}
|
|
12979
|
+
}
|
|
12980
|
+
async function uploadVisionFileFromPath(openai, filePath, filename, mimeType) {
|
|
12981
|
+
const fileBytes = await readLocalFileBytes(filePath);
|
|
12982
|
+
const resolvedFilename = filename || getFilenameFromPath(filePath);
|
|
12983
|
+
const resolvedMimeType = mimeType || inferImageMimeType(filePath);
|
|
12984
|
+
const uploadableFile = await OpenAI.toFile(fileBytes, resolvedFilename, resolvedMimeType ? { type: resolvedMimeType } : undefined);
|
|
12985
|
+
const uploadedFile = await openai.files.create({
|
|
12986
|
+
file: uploadableFile,
|
|
12987
|
+
purpose: 'vision',
|
|
12988
|
+
});
|
|
12989
|
+
return uploadedFile.id;
|
|
12990
|
+
}
|
|
12991
|
+
function collectImageInputs(image, images, imageBase64) {
|
|
12992
|
+
const collectedImages = [];
|
|
12993
|
+
if (image) {
|
|
12994
|
+
collectedImages.push(image);
|
|
12995
|
+
}
|
|
12996
|
+
if (images && images.length > 0) {
|
|
12997
|
+
collectedImages.push(...images);
|
|
12998
|
+
}
|
|
12999
|
+
if (imageBase64) {
|
|
13000
|
+
collectedImages.push({ base64: imageBase64 });
|
|
13001
|
+
}
|
|
13002
|
+
return collectedImages;
|
|
13003
|
+
}
|
|
13004
|
+
function hasLocalFileImage(images) {
|
|
13005
|
+
return images.some((image) => 'filePath' in image);
|
|
13006
|
+
}
|
|
13007
|
+
async function buildImageContentPart(image, defaultDetail, openai) {
|
|
13008
|
+
const detail = image.detail ?? defaultDetail;
|
|
13009
|
+
if ('url' in image) {
|
|
13010
|
+
return {
|
|
13011
|
+
type: 'input_image',
|
|
13012
|
+
detail,
|
|
13013
|
+
image_url: image.url,
|
|
13014
|
+
};
|
|
13015
|
+
}
|
|
13016
|
+
if ('dataUrl' in image) {
|
|
13017
|
+
return {
|
|
13018
|
+
type: 'input_image',
|
|
13019
|
+
detail,
|
|
13020
|
+
image_url: image.dataUrl,
|
|
13021
|
+
};
|
|
13022
|
+
}
|
|
13023
|
+
if ('base64' in image) {
|
|
13024
|
+
return {
|
|
13025
|
+
type: 'input_image',
|
|
13026
|
+
detail,
|
|
13027
|
+
image_url: normalizeImageDataUrl(image.base64, image.mimeType),
|
|
13028
|
+
};
|
|
13029
|
+
}
|
|
13030
|
+
if ('fileId' in image) {
|
|
13031
|
+
return {
|
|
13032
|
+
type: 'input_image',
|
|
13033
|
+
detail,
|
|
13034
|
+
file_id: image.fileId,
|
|
13035
|
+
};
|
|
13036
|
+
}
|
|
13037
|
+
if (!openai) {
|
|
13038
|
+
throw new Error('A local image file path was provided, but no OpenAI client is available to upload it');
|
|
13039
|
+
}
|
|
13040
|
+
const fileId = await uploadVisionFileFromPath(openai, image.filePath, image.filename, image.mimeType);
|
|
13041
|
+
return {
|
|
13042
|
+
type: 'input_image',
|
|
13043
|
+
detail,
|
|
13044
|
+
file_id: fileId,
|
|
13045
|
+
};
|
|
13046
|
+
}
|
|
13047
|
+
async function buildPromptContent(input, imageInputs, imageDetail, openai) {
|
|
13048
|
+
if (imageInputs.length === 0) {
|
|
13049
|
+
return input;
|
|
13050
|
+
}
|
|
13051
|
+
const imageContent = await Promise.all(imageInputs.map((image) => buildImageContentPart(image, imageDetail, openai)));
|
|
13052
|
+
return [{ type: 'input_text', text: input }, ...imageContent];
|
|
13053
|
+
}
|
|
13054
|
+
async function uploadVisionFile(filePath, options = {}) {
|
|
13055
|
+
const resolvedApiKey = resolveOpenAIApiKey(options.apiKey);
|
|
13056
|
+
const openai = createOpenAIClient(resolvedApiKey);
|
|
13057
|
+
return await uploadVisionFileFromPath(openai, filePath, options.filename, options.mimeType);
|
|
13058
|
+
}
|
|
12929
13059
|
/**
|
|
12930
13060
|
* Makes a call to OpenAI's Responses API for more advanced use cases with built-in tools.
|
|
12931
13061
|
*
|
|
@@ -12957,13 +13087,8 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
12957
13087
|
if (!isOpenAIModel(normalizedModel)) {
|
|
12958
13088
|
throw new Error(`Unsupported OpenAI model: ${normalizedModel}. Please use a supported GPT-5 model.`);
|
|
12959
13089
|
}
|
|
12960
|
-
const apiKey = options.apiKey
|
|
12961
|
-
|
|
12962
|
-
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
12963
|
-
}
|
|
12964
|
-
const openai = new OpenAI({
|
|
12965
|
-
apiKey: apiKey,
|
|
12966
|
-
});
|
|
13090
|
+
const apiKey = resolveOpenAIApiKey(options.apiKey);
|
|
13091
|
+
const openai = createOpenAIClient(apiKey);
|
|
12967
13092
|
// Remove apiKey from options before creating request body
|
|
12968
13093
|
const { apiKey: _, model: __, ...cleanOptions } = options;
|
|
12969
13094
|
const requestBody = {
|
|
@@ -13074,12 +13199,16 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
13074
13199
|
* });
|
|
13075
13200
|
*/
|
|
13076
13201
|
async function makeLLMCall(input, options = {}) {
|
|
13077
|
-
const { apiKey, model = DEFAULT_MODEL, responseFormat = 'text', schema, schemaName = 'response', schemaDescription, schemaStrict, tools, useCodeInterpreter = false, useWebSearch = false, imageBase64, imageDetail = 'high', context, } = options;
|
|
13202
|
+
const { apiKey, model = DEFAULT_MODEL, responseFormat = 'text', schema, schemaName = 'response', schemaDescription, schemaStrict, tools, useCodeInterpreter = false, useWebSearch = false, image, images, imageBase64, imageDetail = 'high', context, } = options;
|
|
13078
13203
|
// Validate model
|
|
13079
13204
|
const normalizedModel = normalizeModelName(model);
|
|
13080
13205
|
if (!isOpenAIModel(normalizedModel)) {
|
|
13081
13206
|
throw new Error(`Unsupported OpenAI model: ${normalizedModel}. Please use a supported GPT-5 model.`);
|
|
13082
13207
|
}
|
|
13208
|
+
const resolvedApiKey = resolveOpenAIApiKey(apiKey);
|
|
13209
|
+
const imageInputs = collectImageInputs(image, images, imageBase64);
|
|
13210
|
+
const openai = hasLocalFileImage(imageInputs) ? createOpenAIClient(resolvedApiKey) : undefined;
|
|
13211
|
+
const promptContent = await buildPromptContent(input, imageInputs, imageDetail, openai);
|
|
13083
13212
|
// Process input for conversation context and image analysis
|
|
13084
13213
|
let processedInput;
|
|
13085
13214
|
if (context && context.length > 0) {
|
|
@@ -13094,18 +13223,11 @@ async function makeLLMCall(input, options = {}) {
|
|
|
13094
13223
|
});
|
|
13095
13224
|
}
|
|
13096
13225
|
// Add current input message
|
|
13097
|
-
if (
|
|
13098
|
-
// Current message includes
|
|
13226
|
+
if (imageInputs.length > 0) {
|
|
13227
|
+
// Current message includes text and one or more images
|
|
13099
13228
|
conversationMessages.push({
|
|
13100
13229
|
role: 'user',
|
|
13101
|
-
content:
|
|
13102
|
-
{ type: 'input_text', text: input },
|
|
13103
|
-
{
|
|
13104
|
-
type: 'input_image',
|
|
13105
|
-
detail: imageDetail,
|
|
13106
|
-
image_url: imageBase64.startsWith('data:') ? imageBase64 : `data:image/webp;base64,${imageBase64}`,
|
|
13107
|
-
},
|
|
13108
|
-
],
|
|
13230
|
+
content: promptContent,
|
|
13109
13231
|
type: 'message',
|
|
13110
13232
|
});
|
|
13111
13233
|
}
|
|
@@ -13119,19 +13241,12 @@ async function makeLLMCall(input, options = {}) {
|
|
|
13119
13241
|
}
|
|
13120
13242
|
processedInput = conversationMessages;
|
|
13121
13243
|
}
|
|
13122
|
-
else if (
|
|
13123
|
-
// No context, but has image
|
|
13244
|
+
else if (imageInputs.length > 0) {
|
|
13245
|
+
// No context, but has image(s)
|
|
13124
13246
|
processedInput = [
|
|
13125
13247
|
{
|
|
13126
13248
|
role: 'user',
|
|
13127
|
-
content:
|
|
13128
|
-
{ type: 'input_text', text: input },
|
|
13129
|
-
{
|
|
13130
|
-
type: 'input_image',
|
|
13131
|
-
detail: imageDetail,
|
|
13132
|
-
image_url: imageBase64.startsWith('data:') ? imageBase64 : `data:image/webp;base64,${imageBase64}`,
|
|
13133
|
-
},
|
|
13134
|
-
],
|
|
13249
|
+
content: promptContent,
|
|
13135
13250
|
type: 'message',
|
|
13136
13251
|
},
|
|
13137
13252
|
];
|
|
@@ -13142,7 +13257,7 @@ async function makeLLMCall(input, options = {}) {
|
|
|
13142
13257
|
}
|
|
13143
13258
|
// Build the options object for makeResponsesAPICall
|
|
13144
13259
|
let responsesOptions = {
|
|
13145
|
-
apiKey,
|
|
13260
|
+
apiKey: resolvedApiKey,
|
|
13146
13261
|
model: normalizedModel,
|
|
13147
13262
|
parallel_tool_calls: false,
|
|
13148
13263
|
tools,
|
|
@@ -14628,6 +14743,7 @@ const disco = {
|
|
|
14628
14743
|
types: Types,
|
|
14629
14744
|
llm: {
|
|
14630
14745
|
call: makeLLMCall,
|
|
14746
|
+
uploadVisionFile: uploadVisionFile,
|
|
14631
14747
|
seek: makeDeepseekCall,
|
|
14632
14748
|
images: makeImagesCall,
|
|
14633
14749
|
},
|
|
@@ -14642,4 +14758,5 @@ exports.isOpenAIImageModel = isOpenAIImageModel;
|
|
|
14642
14758
|
exports.isOpenAIModel = isOpenAIModel;
|
|
14643
14759
|
exports.isOpenRouterModel = isOpenRouterModel;
|
|
14644
14760
|
exports.supportsTemperature = supportsTemperature;
|
|
14761
|
+
exports.uploadVisionFile = uploadVisionFile;
|
|
14645
14762
|
//# sourceMappingURL=index-frontend.cjs.map
|