@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.mjs
CHANGED
|
@@ -13606,7 +13606,7 @@ function zodTextFormat(zodObject, name, props) {
|
|
|
13606
13606
|
}, (content) => zodObject.parse(JSON.parse(content)));
|
|
13607
13607
|
}
|
|
13608
13608
|
|
|
13609
|
-
const DEFAULT_MODEL = 'gpt-5-mini';
|
|
13609
|
+
const DEFAULT_MODEL = 'gpt-5.4-mini';
|
|
13610
13610
|
const GPT_5_HIGH_CONTEXT_THRESHOLD_TOKENS = 272_000;
|
|
13611
13611
|
const GPT_5_HIGH_CONTEXT_INPUT_MULTIPLIER = 2;
|
|
13612
13612
|
const GPT_5_HIGH_CONTEXT_OUTPUT_MULTIPLIER = 1.5;
|
|
@@ -14244,6 +14244,7 @@ async function parseResponse(content, responseFormat) {
|
|
|
14244
14244
|
}
|
|
14245
14245
|
|
|
14246
14246
|
// llm-openai.ts
|
|
14247
|
+
const DEFAULT_IMAGE_MIME_TYPE = 'image/webp';
|
|
14247
14248
|
/**
|
|
14248
14249
|
* Checks if the given direct OpenAI model supports the temperature parameter.
|
|
14249
14250
|
* @param model The model to check.
|
|
@@ -14278,6 +14279,135 @@ function buildJsonSchemaFormat(schema, schemaName, schemaDescription, schemaStri
|
|
|
14278
14279
|
...(schemaStrict !== undefined ? { strict: schemaStrict } : {}),
|
|
14279
14280
|
};
|
|
14280
14281
|
}
|
|
14282
|
+
function resolveOpenAIApiKey(apiKey) {
|
|
14283
|
+
const resolvedApiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
14284
|
+
if (!resolvedApiKey) {
|
|
14285
|
+
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
14286
|
+
}
|
|
14287
|
+
return resolvedApiKey;
|
|
14288
|
+
}
|
|
14289
|
+
function createOpenAIClient(apiKey) {
|
|
14290
|
+
return new OpenAI({
|
|
14291
|
+
apiKey,
|
|
14292
|
+
});
|
|
14293
|
+
}
|
|
14294
|
+
function normalizeImageDataUrl(imageData, mimeType = DEFAULT_IMAGE_MIME_TYPE) {
|
|
14295
|
+
return imageData.startsWith('data:') ? imageData : `data:${mimeType};base64,${imageData}`;
|
|
14296
|
+
}
|
|
14297
|
+
function getFilenameFromPath(filePath) {
|
|
14298
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
14299
|
+
const pathSegments = normalizedPath.split('/');
|
|
14300
|
+
const filename = pathSegments[pathSegments.length - 1];
|
|
14301
|
+
if (!filename) {
|
|
14302
|
+
throw new Error(`Could not determine a filename from file path: ${filePath}`);
|
|
14303
|
+
}
|
|
14304
|
+
return filename;
|
|
14305
|
+
}
|
|
14306
|
+
function inferImageMimeType(filePath) {
|
|
14307
|
+
const normalizedPath = filePath.toLowerCase();
|
|
14308
|
+
if (normalizedPath.endsWith('.png')) {
|
|
14309
|
+
return 'image/png';
|
|
14310
|
+
}
|
|
14311
|
+
if (normalizedPath.endsWith('.jpg') || normalizedPath.endsWith('.jpeg')) {
|
|
14312
|
+
return 'image/jpeg';
|
|
14313
|
+
}
|
|
14314
|
+
if (normalizedPath.endsWith('.webp')) {
|
|
14315
|
+
return 'image/webp';
|
|
14316
|
+
}
|
|
14317
|
+
if (normalizedPath.endsWith('.gif')) {
|
|
14318
|
+
return 'image/gif';
|
|
14319
|
+
}
|
|
14320
|
+
return undefined;
|
|
14321
|
+
}
|
|
14322
|
+
async function readLocalFileBytes(filePath) {
|
|
14323
|
+
try {
|
|
14324
|
+
const dynamicImport = new Function('specifier', 'return import(specifier);');
|
|
14325
|
+
const { readFile } = await dynamicImport('node:fs/promises');
|
|
14326
|
+
return await readFile(filePath);
|
|
14327
|
+
}
|
|
14328
|
+
catch {
|
|
14329
|
+
throw new Error('Local image file uploads require a Node.js runtime with access to node:fs/promises');
|
|
14330
|
+
}
|
|
14331
|
+
}
|
|
14332
|
+
async function uploadVisionFileFromPath(openai, filePath, filename, mimeType) {
|
|
14333
|
+
const fileBytes = await readLocalFileBytes(filePath);
|
|
14334
|
+
const resolvedFilename = filename || getFilenameFromPath(filePath);
|
|
14335
|
+
const resolvedMimeType = mimeType || inferImageMimeType(filePath);
|
|
14336
|
+
const uploadableFile = await OpenAI.toFile(fileBytes, resolvedFilename, resolvedMimeType ? { type: resolvedMimeType } : undefined);
|
|
14337
|
+
const uploadedFile = await openai.files.create({
|
|
14338
|
+
file: uploadableFile,
|
|
14339
|
+
purpose: 'vision',
|
|
14340
|
+
});
|
|
14341
|
+
return uploadedFile.id;
|
|
14342
|
+
}
|
|
14343
|
+
function collectImageInputs(image, images, imageBase64) {
|
|
14344
|
+
const collectedImages = [];
|
|
14345
|
+
if (image) {
|
|
14346
|
+
collectedImages.push(image);
|
|
14347
|
+
}
|
|
14348
|
+
if (images && images.length > 0) {
|
|
14349
|
+
collectedImages.push(...images);
|
|
14350
|
+
}
|
|
14351
|
+
if (imageBase64) {
|
|
14352
|
+
collectedImages.push({ base64: imageBase64 });
|
|
14353
|
+
}
|
|
14354
|
+
return collectedImages;
|
|
14355
|
+
}
|
|
14356
|
+
function hasLocalFileImage(images) {
|
|
14357
|
+
return images.some((image) => 'filePath' in image);
|
|
14358
|
+
}
|
|
14359
|
+
async function buildImageContentPart(image, defaultDetail, openai) {
|
|
14360
|
+
const detail = image.detail ?? defaultDetail;
|
|
14361
|
+
if ('url' in image) {
|
|
14362
|
+
return {
|
|
14363
|
+
type: 'input_image',
|
|
14364
|
+
detail,
|
|
14365
|
+
image_url: image.url,
|
|
14366
|
+
};
|
|
14367
|
+
}
|
|
14368
|
+
if ('dataUrl' in image) {
|
|
14369
|
+
return {
|
|
14370
|
+
type: 'input_image',
|
|
14371
|
+
detail,
|
|
14372
|
+
image_url: image.dataUrl,
|
|
14373
|
+
};
|
|
14374
|
+
}
|
|
14375
|
+
if ('base64' in image) {
|
|
14376
|
+
return {
|
|
14377
|
+
type: 'input_image',
|
|
14378
|
+
detail,
|
|
14379
|
+
image_url: normalizeImageDataUrl(image.base64, image.mimeType),
|
|
14380
|
+
};
|
|
14381
|
+
}
|
|
14382
|
+
if ('fileId' in image) {
|
|
14383
|
+
return {
|
|
14384
|
+
type: 'input_image',
|
|
14385
|
+
detail,
|
|
14386
|
+
file_id: image.fileId,
|
|
14387
|
+
};
|
|
14388
|
+
}
|
|
14389
|
+
if (!openai) {
|
|
14390
|
+
throw new Error('A local image file path was provided, but no OpenAI client is available to upload it');
|
|
14391
|
+
}
|
|
14392
|
+
const fileId = await uploadVisionFileFromPath(openai, image.filePath, image.filename, image.mimeType);
|
|
14393
|
+
return {
|
|
14394
|
+
type: 'input_image',
|
|
14395
|
+
detail,
|
|
14396
|
+
file_id: fileId,
|
|
14397
|
+
};
|
|
14398
|
+
}
|
|
14399
|
+
async function buildPromptContent(input, imageInputs, imageDetail, openai) {
|
|
14400
|
+
if (imageInputs.length === 0) {
|
|
14401
|
+
return input;
|
|
14402
|
+
}
|
|
14403
|
+
const imageContent = await Promise.all(imageInputs.map((image) => buildImageContentPart(image, imageDetail, openai)));
|
|
14404
|
+
return [{ type: 'input_text', text: input }, ...imageContent];
|
|
14405
|
+
}
|
|
14406
|
+
async function uploadVisionFile(filePath, options = {}) {
|
|
14407
|
+
const resolvedApiKey = resolveOpenAIApiKey(options.apiKey);
|
|
14408
|
+
const openai = createOpenAIClient(resolvedApiKey);
|
|
14409
|
+
return await uploadVisionFileFromPath(openai, filePath, options.filename, options.mimeType);
|
|
14410
|
+
}
|
|
14281
14411
|
/**
|
|
14282
14412
|
* Makes a call to OpenAI's Responses API for more advanced use cases with built-in tools.
|
|
14283
14413
|
*
|
|
@@ -14309,13 +14439,8 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
14309
14439
|
if (!isOpenAIModel(normalizedModel)) {
|
|
14310
14440
|
throw new Error(`Unsupported OpenAI model: ${normalizedModel}. Please use a supported GPT-5 model.`);
|
|
14311
14441
|
}
|
|
14312
|
-
const apiKey = options.apiKey
|
|
14313
|
-
|
|
14314
|
-
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
14315
|
-
}
|
|
14316
|
-
const openai = new OpenAI({
|
|
14317
|
-
apiKey: apiKey,
|
|
14318
|
-
});
|
|
14442
|
+
const apiKey = resolveOpenAIApiKey(options.apiKey);
|
|
14443
|
+
const openai = createOpenAIClient(apiKey);
|
|
14319
14444
|
// Remove apiKey from options before creating request body
|
|
14320
14445
|
const { apiKey: _, model: __, ...cleanOptions } = options;
|
|
14321
14446
|
const requestBody = {
|
|
@@ -14426,12 +14551,16 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
14426
14551
|
* });
|
|
14427
14552
|
*/
|
|
14428
14553
|
async function makeLLMCall(input, options = {}) {
|
|
14429
|
-
const { apiKey, model = DEFAULT_MODEL, responseFormat = 'text', schema, schemaName = 'response', schemaDescription, schemaStrict, tools, useCodeInterpreter = false, useWebSearch = false, imageBase64, imageDetail = 'high', context, } = options;
|
|
14554
|
+
const { apiKey, model = DEFAULT_MODEL, responseFormat = 'text', schema, schemaName = 'response', schemaDescription, schemaStrict, tools, useCodeInterpreter = false, useWebSearch = false, image, images, imageBase64, imageDetail = 'high', context, } = options;
|
|
14430
14555
|
// Validate model
|
|
14431
14556
|
const normalizedModel = normalizeModelName(model);
|
|
14432
14557
|
if (!isOpenAIModel(normalizedModel)) {
|
|
14433
14558
|
throw new Error(`Unsupported OpenAI model: ${normalizedModel}. Please use a supported GPT-5 model.`);
|
|
14434
14559
|
}
|
|
14560
|
+
const resolvedApiKey = resolveOpenAIApiKey(apiKey);
|
|
14561
|
+
const imageInputs = collectImageInputs(image, images, imageBase64);
|
|
14562
|
+
const openai = hasLocalFileImage(imageInputs) ? createOpenAIClient(resolvedApiKey) : undefined;
|
|
14563
|
+
const promptContent = await buildPromptContent(input, imageInputs, imageDetail, openai);
|
|
14435
14564
|
// Process input for conversation context and image analysis
|
|
14436
14565
|
let processedInput;
|
|
14437
14566
|
if (context && context.length > 0) {
|
|
@@ -14446,18 +14575,11 @@ async function makeLLMCall(input, options = {}) {
|
|
|
14446
14575
|
});
|
|
14447
14576
|
}
|
|
14448
14577
|
// Add current input message
|
|
14449
|
-
if (
|
|
14450
|
-
// Current message includes
|
|
14578
|
+
if (imageInputs.length > 0) {
|
|
14579
|
+
// Current message includes text and one or more images
|
|
14451
14580
|
conversationMessages.push({
|
|
14452
14581
|
role: 'user',
|
|
14453
|
-
content:
|
|
14454
|
-
{ type: 'input_text', text: input },
|
|
14455
|
-
{
|
|
14456
|
-
type: 'input_image',
|
|
14457
|
-
detail: imageDetail,
|
|
14458
|
-
image_url: imageBase64.startsWith('data:') ? imageBase64 : `data:image/webp;base64,${imageBase64}`,
|
|
14459
|
-
},
|
|
14460
|
-
],
|
|
14582
|
+
content: promptContent,
|
|
14461
14583
|
type: 'message',
|
|
14462
14584
|
});
|
|
14463
14585
|
}
|
|
@@ -14471,19 +14593,12 @@ async function makeLLMCall(input, options = {}) {
|
|
|
14471
14593
|
}
|
|
14472
14594
|
processedInput = conversationMessages;
|
|
14473
14595
|
}
|
|
14474
|
-
else if (
|
|
14475
|
-
// No context, but has image
|
|
14596
|
+
else if (imageInputs.length > 0) {
|
|
14597
|
+
// No context, but has image(s)
|
|
14476
14598
|
processedInput = [
|
|
14477
14599
|
{
|
|
14478
14600
|
role: 'user',
|
|
14479
|
-
content:
|
|
14480
|
-
{ type: 'input_text', text: input },
|
|
14481
|
-
{
|
|
14482
|
-
type: 'input_image',
|
|
14483
|
-
detail: imageDetail,
|
|
14484
|
-
image_url: imageBase64.startsWith('data:') ? imageBase64 : `data:image/webp;base64,${imageBase64}`,
|
|
14485
|
-
},
|
|
14486
|
-
],
|
|
14601
|
+
content: promptContent,
|
|
14487
14602
|
type: 'message',
|
|
14488
14603
|
},
|
|
14489
14604
|
];
|
|
@@ -14494,7 +14609,7 @@ async function makeLLMCall(input, options = {}) {
|
|
|
14494
14609
|
}
|
|
14495
14610
|
// Build the options object for makeResponsesAPICall
|
|
14496
14611
|
let responsesOptions = {
|
|
14497
|
-
apiKey,
|
|
14612
|
+
apiKey: resolvedApiKey,
|
|
14498
14613
|
model: normalizedModel,
|
|
14499
14614
|
parallel_tool_calls: false,
|
|
14500
14615
|
tools,
|
|
@@ -23636,6 +23751,7 @@ const disco = {
|
|
|
23636
23751
|
},
|
|
23637
23752
|
llm: {
|
|
23638
23753
|
call: makeLLMCall,
|
|
23754
|
+
uploadVisionFile: uploadVisionFile,
|
|
23639
23755
|
seek: makeDeepseekCall,
|
|
23640
23756
|
images: makeImagesCall,
|
|
23641
23757
|
open: makeOpenRouterCall,
|
|
@@ -23666,5 +23782,5 @@ const disco = {
|
|
|
23666
23782
|
},
|
|
23667
23783
|
};
|
|
23668
23784
|
|
|
23669
|
-
export { AlpacaMarketDataAPI, AlpacaTradingAPI, OPENAI_IMAGE_MODELS, OPENAI_MODELS, OPENROUTER_MODELS, disco, isOpenAIImageModel, isOpenAIModel, isOpenRouterModel, supportsTemperature };
|
|
23785
|
+
export { AlpacaMarketDataAPI, AlpacaTradingAPI, OPENAI_IMAGE_MODELS, OPENAI_MODELS, OPENROUTER_MODELS, disco, isOpenAIImageModel, isOpenAIModel, isOpenRouterModel, supportsTemperature, uploadVisionFile };
|
|
23670
23786
|
//# sourceMappingURL=index.mjs.map
|