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