@ljoukov/llm 4.1.0 → 4.1.1
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.cjs +132 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2533,7 +2533,7 @@ async function runGeminiCall(fn, modelId, runOptions) {
|
|
|
2533
2533
|
|
|
2534
2534
|
// src/openai/client.ts
|
|
2535
2535
|
import OpenAI2 from "openai";
|
|
2536
|
-
import { Agent as Agent2
|
|
2536
|
+
import { Agent as Agent2 } from "undici";
|
|
2537
2537
|
var openAiClientState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.openAiClientState"), () => ({
|
|
2538
2538
|
cachedApiKey: null,
|
|
2539
2539
|
cachedClient: null,
|
|
@@ -2562,7 +2562,7 @@ function getOpenAiFetch() {
|
|
|
2562
2562
|
headersTimeout: timeoutMs
|
|
2563
2563
|
});
|
|
2564
2564
|
openAiClientState.cachedFetch = ((input, init) => {
|
|
2565
|
-
return
|
|
2565
|
+
return fetch(input, {
|
|
2566
2566
|
...init ?? {},
|
|
2567
2567
|
dispatcher
|
|
2568
2568
|
});
|
|
@@ -3259,7 +3259,7 @@ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
|
3259
3259
|
import { Buffer as Buffer4, File as NodeFile } from "buffer";
|
|
3260
3260
|
import { createHash } from "crypto";
|
|
3261
3261
|
import { createReadStream, createWriteStream, openAsBlob } from "fs";
|
|
3262
|
-
import { mkdir as mkdir2, mkdtemp, stat, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
3262
|
+
import { copyFile, mkdir as mkdir2, mkdtemp, readFile, stat, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
3263
3263
|
import os3 from "os";
|
|
3264
3264
|
import path4 from "path";
|
|
3265
3265
|
import { Readable } from "stream";
|
|
@@ -3272,6 +3272,9 @@ var OPENAI_UPLOAD_PART_MAX_BYTES = 64 * 1024 * 1024;
|
|
|
3272
3272
|
var GEMINI_FILE_POLL_INTERVAL_MS = 1e3;
|
|
3273
3273
|
var GEMINI_FILE_POLL_TIMEOUT_MS = 6e4;
|
|
3274
3274
|
var FILES_TEMP_ROOT = path4.join(os3.tmpdir(), "ljoukov-llm-files");
|
|
3275
|
+
var FILES_CACHE_ROOT = path4.join(FILES_TEMP_ROOT, "cache");
|
|
3276
|
+
var FILES_CACHE_CONTENT_ROOT = path4.join(FILES_CACHE_ROOT, "content");
|
|
3277
|
+
var FILES_CACHE_METADATA_ROOT = path4.join(FILES_CACHE_ROOT, "metadata");
|
|
3275
3278
|
var filesState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.filesState"), () => ({
|
|
3276
3279
|
metadataById: /* @__PURE__ */ new Map(),
|
|
3277
3280
|
openAiUploadCacheByKey: /* @__PURE__ */ new Map(),
|
|
@@ -3417,6 +3420,12 @@ function toStoredFile(file) {
|
|
|
3417
3420
|
function buildCacheKey(filename, mimeType, sha256Hex) {
|
|
3418
3421
|
return `${sha256Hex}\0${filename}\0${mimeType}`;
|
|
3419
3422
|
}
|
|
3423
|
+
function buildCachedContentPath(sha256Hex) {
|
|
3424
|
+
return path4.join(FILES_CACHE_CONTENT_ROOT, sha256Hex);
|
|
3425
|
+
}
|
|
3426
|
+
function buildCachedMetadataPath(fileId) {
|
|
3427
|
+
return path4.join(FILES_CACHE_METADATA_ROOT, `${fileId}.json`);
|
|
3428
|
+
}
|
|
3420
3429
|
function isFresh(file) {
|
|
3421
3430
|
if (!file.expires_at) {
|
|
3422
3431
|
return true;
|
|
@@ -3437,6 +3446,82 @@ function recordMetadata(metadata) {
|
|
|
3437
3446
|
}
|
|
3438
3447
|
return metadata;
|
|
3439
3448
|
}
|
|
3449
|
+
async function ensureFilesCacheReady() {
|
|
3450
|
+
await mkdir2(FILES_CACHE_CONTENT_ROOT, { recursive: true });
|
|
3451
|
+
await mkdir2(FILES_CACHE_METADATA_ROOT, { recursive: true });
|
|
3452
|
+
}
|
|
3453
|
+
async function cacheBufferLocally(bytes, sha256Hex) {
|
|
3454
|
+
await ensureFilesCacheReady();
|
|
3455
|
+
const localPath = buildCachedContentPath(sha256Hex);
|
|
3456
|
+
try {
|
|
3457
|
+
await writeFile2(localPath, bytes, { flag: "wx" });
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
const code = error.code;
|
|
3460
|
+
if (code !== "EEXIST") {
|
|
3461
|
+
throw error;
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
return localPath;
|
|
3465
|
+
}
|
|
3466
|
+
async function cacheFileLocally(filePath, sha256Hex) {
|
|
3467
|
+
await ensureFilesCacheReady();
|
|
3468
|
+
const localPath = buildCachedContentPath(sha256Hex);
|
|
3469
|
+
try {
|
|
3470
|
+
await copyFile(filePath, localPath);
|
|
3471
|
+
} catch (error) {
|
|
3472
|
+
const code = error.code;
|
|
3473
|
+
if (code !== "EEXIST") {
|
|
3474
|
+
throw error;
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
return localPath;
|
|
3478
|
+
}
|
|
3479
|
+
async function persistMetadataToDisk(metadata) {
|
|
3480
|
+
await ensureFilesCacheReady();
|
|
3481
|
+
const payload = {
|
|
3482
|
+
file: metadata.file,
|
|
3483
|
+
filename: metadata.filename,
|
|
3484
|
+
bytes: metadata.bytes,
|
|
3485
|
+
mimeType: metadata.mimeType,
|
|
3486
|
+
sha256Hex: metadata.sha256Hex,
|
|
3487
|
+
localPath: metadata.localPath
|
|
3488
|
+
};
|
|
3489
|
+
await writeFile2(
|
|
3490
|
+
buildCachedMetadataPath(metadata.file.id),
|
|
3491
|
+
`${JSON.stringify(payload, null, 2)}
|
|
3492
|
+
`
|
|
3493
|
+
);
|
|
3494
|
+
}
|
|
3495
|
+
async function loadPersistedMetadata(fileId) {
|
|
3496
|
+
try {
|
|
3497
|
+
const payload = JSON.parse(
|
|
3498
|
+
await readFile(buildCachedMetadataPath(fileId), "utf8")
|
|
3499
|
+
);
|
|
3500
|
+
if (!payload || typeof payload !== "object" || !payload.file) {
|
|
3501
|
+
return void 0;
|
|
3502
|
+
}
|
|
3503
|
+
if (payload.localPath) {
|
|
3504
|
+
try {
|
|
3505
|
+
const localStats = await stat(payload.localPath);
|
|
3506
|
+
if (!localStats.isFile()) {
|
|
3507
|
+
return void 0;
|
|
3508
|
+
}
|
|
3509
|
+
} catch {
|
|
3510
|
+
return void 0;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
return recordMetadata({
|
|
3514
|
+
file: payload.file,
|
|
3515
|
+
filename: payload.filename,
|
|
3516
|
+
bytes: payload.bytes,
|
|
3517
|
+
mimeType: payload.mimeType,
|
|
3518
|
+
sha256Hex: payload.sha256Hex,
|
|
3519
|
+
localPath: payload.localPath
|
|
3520
|
+
});
|
|
3521
|
+
} catch {
|
|
3522
|
+
return void 0;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3440
3525
|
async function uploadOpenAiFileFromBytes(params) {
|
|
3441
3526
|
const cacheKey = buildCacheKey(params.filename, params.mimeType, params.sha256Hex);
|
|
3442
3527
|
const cached = filesState.openAiUploadCacheByKey.get(cacheKey);
|
|
@@ -3585,17 +3670,23 @@ async function retrieveOpenAiFile(fileId) {
|
|
|
3585
3670
|
if (cached && isFresh(cached.file)) {
|
|
3586
3671
|
return cached;
|
|
3587
3672
|
}
|
|
3673
|
+
const persisted = await loadPersistedMetadata(fileId);
|
|
3674
|
+
if (persisted && isFresh(persisted.file)) {
|
|
3675
|
+
return persisted;
|
|
3676
|
+
}
|
|
3588
3677
|
const client = getOpenAiClient();
|
|
3589
3678
|
const retrieved = await client.files.retrieve(fileId);
|
|
3590
3679
|
const file = toStoredFile(retrieved);
|
|
3591
|
-
|
|
3680
|
+
const metadata = recordMetadata({
|
|
3592
3681
|
file,
|
|
3593
3682
|
filename: file.filename,
|
|
3594
3683
|
bytes: file.bytes,
|
|
3595
|
-
mimeType: cached?.mimeType ?? resolveMimeType(file.filename, void 0),
|
|
3596
|
-
sha256Hex: cached?.sha256Hex,
|
|
3597
|
-
localPath: cached?.localPath
|
|
3684
|
+
mimeType: cached?.mimeType ?? persisted?.mimeType ?? resolveMimeType(file.filename, void 0),
|
|
3685
|
+
sha256Hex: cached?.sha256Hex ?? persisted?.sha256Hex,
|
|
3686
|
+
localPath: cached?.localPath ?? persisted?.localPath
|
|
3598
3687
|
});
|
|
3688
|
+
await persistMetadataToDisk(metadata);
|
|
3689
|
+
return metadata;
|
|
3599
3690
|
}
|
|
3600
3691
|
function buildGeminiMirrorName(sha256Hex) {
|
|
3601
3692
|
return `files/${sha256Hex.slice(0, 40)}`;
|
|
@@ -3707,6 +3798,7 @@ async function materializeOpenAiFile(fileId) {
|
|
|
3707
3798
|
sha256Hex,
|
|
3708
3799
|
localPath
|
|
3709
3800
|
});
|
|
3801
|
+
await persistMetadataToDisk(updated);
|
|
3710
3802
|
return {
|
|
3711
3803
|
file: updated.file,
|
|
3712
3804
|
filename: updated.filename,
|
|
@@ -3870,7 +3962,13 @@ async function filesCreate(params) {
|
|
|
3870
3962
|
sha256Hex: sha256Hex2,
|
|
3871
3963
|
bytes: info.size
|
|
3872
3964
|
});
|
|
3873
|
-
|
|
3965
|
+
const localPath2 = await cacheFileLocally(filePath, sha256Hex2);
|
|
3966
|
+
const cached2 = recordMetadata({
|
|
3967
|
+
...uploaded2,
|
|
3968
|
+
localPath: localPath2
|
|
3969
|
+
});
|
|
3970
|
+
await persistMetadataToDisk(cached2);
|
|
3971
|
+
return cached2.file;
|
|
3874
3972
|
}
|
|
3875
3973
|
const filename = normaliseFilename(params.filename);
|
|
3876
3974
|
const bytes = toBuffer(params.data);
|
|
@@ -3884,7 +3982,13 @@ async function filesCreate(params) {
|
|
|
3884
3982
|
expiresAfterSeconds,
|
|
3885
3983
|
sha256Hex
|
|
3886
3984
|
});
|
|
3887
|
-
|
|
3985
|
+
const localPath = await cacheBufferLocally(bytes, sha256Hex);
|
|
3986
|
+
const cached = recordMetadata({
|
|
3987
|
+
...uploaded,
|
|
3988
|
+
localPath
|
|
3989
|
+
});
|
|
3990
|
+
await persistMetadataToDisk(cached);
|
|
3991
|
+
return cached.file;
|
|
3888
3992
|
}
|
|
3889
3993
|
async function filesRetrieve(fileId) {
|
|
3890
3994
|
return (await retrieveOpenAiFile(fileId)).file;
|
|
@@ -3917,6 +4021,10 @@ async function filesDelete(fileId) {
|
|
|
3917
4021
|
const response = await getOpenAiClient().files.delete(fileId);
|
|
3918
4022
|
filesState.metadataById.delete(fileId);
|
|
3919
4023
|
filesState.materializedById.delete(fileId);
|
|
4024
|
+
try {
|
|
4025
|
+
await unlink(buildCachedMetadataPath(fileId));
|
|
4026
|
+
} catch {
|
|
4027
|
+
}
|
|
3920
4028
|
return {
|
|
3921
4029
|
id: response.id,
|
|
3922
4030
|
deleted: response.deleted,
|
|
@@ -4396,8 +4504,7 @@ function toGeminiPart(part) {
|
|
|
4396
4504
|
return {
|
|
4397
4505
|
fileData: {
|
|
4398
4506
|
fileUri: buildCanonicalGeminiFileUri(part.file_id),
|
|
4399
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4400
|
-
displayName: part.filename ?? void 0
|
|
4507
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4401
4508
|
}
|
|
4402
4509
|
};
|
|
4403
4510
|
}
|
|
@@ -4415,8 +4522,7 @@ function toGeminiPart(part) {
|
|
|
4415
4522
|
return {
|
|
4416
4523
|
fileData: {
|
|
4417
4524
|
fileUri: part.image_url,
|
|
4418
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4419
|
-
displayName: part.filename ?? void 0
|
|
4525
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4420
4526
|
}
|
|
4421
4527
|
};
|
|
4422
4528
|
}
|
|
@@ -4425,8 +4531,7 @@ function toGeminiPart(part) {
|
|
|
4425
4531
|
return {
|
|
4426
4532
|
fileData: {
|
|
4427
4533
|
fileUri: buildCanonicalGeminiFileUri(part.file_id),
|
|
4428
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4429
|
-
displayName: part.filename ?? void 0
|
|
4534
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4430
4535
|
}
|
|
4431
4536
|
};
|
|
4432
4537
|
}
|
|
@@ -4452,8 +4557,7 @@ function toGeminiPart(part) {
|
|
|
4452
4557
|
return {
|
|
4453
4558
|
fileData: {
|
|
4454
4559
|
fileUri: part.file_url,
|
|
4455
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4456
|
-
displayName: part.filename ?? void 0
|
|
4560
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4457
4561
|
}
|
|
4458
4562
|
};
|
|
4459
4563
|
}
|
|
@@ -4685,7 +4789,7 @@ async function prepareOpenAiPromptContentItem(item) {
|
|
|
4685
4789
|
mimeType,
|
|
4686
4790
|
filename
|
|
4687
4791
|
});
|
|
4688
|
-
return { type: "input_file", file_id: uploaded.fileId
|
|
4792
|
+
return { type: "input_file", file_id: uploaded.fileId };
|
|
4689
4793
|
}
|
|
4690
4794
|
if (typeof item.file_url === "string" && item.file_url.trim().toLowerCase().startsWith("data:")) {
|
|
4691
4795
|
const parsed = parseDataUrlPayload(item.file_url);
|
|
@@ -4700,7 +4804,7 @@ async function prepareOpenAiPromptContentItem(item) {
|
|
|
4700
4804
|
guessInlineDataFilename(parsed.mimeType)
|
|
4701
4805
|
)
|
|
4702
4806
|
});
|
|
4703
|
-
return { type: "input_file", file_id: uploaded.fileId
|
|
4807
|
+
return { type: "input_file", file_id: uploaded.fileId };
|
|
4704
4808
|
}
|
|
4705
4809
|
return item;
|
|
4706
4810
|
}
|
|
@@ -4765,21 +4869,16 @@ async function prepareGeminiPromptContents(contents) {
|
|
|
4765
4869
|
for (const part of content.parts ?? []) {
|
|
4766
4870
|
const canonicalFileId = parseCanonicalGeminiFileId(part.fileData?.fileUri);
|
|
4767
4871
|
if (canonicalFileId) {
|
|
4768
|
-
|
|
4872
|
+
await getCanonicalFileMetadata(canonicalFileId);
|
|
4769
4873
|
if (backend === "api") {
|
|
4770
4874
|
const mirrored = await ensureGeminiFileMirror(canonicalFileId);
|
|
4771
|
-
|
|
4772
|
-
if (metadata.filename && mirroredPart.fileData) {
|
|
4773
|
-
mirroredPart.fileData.displayName = metadata.filename;
|
|
4774
|
-
}
|
|
4775
|
-
parts.push(mirroredPart);
|
|
4875
|
+
parts.push(createPartFromUri(mirrored.uri, mirrored.mimeType));
|
|
4776
4876
|
} else {
|
|
4777
4877
|
const mirrored = await ensureVertexFileMirror(canonicalFileId);
|
|
4778
4878
|
parts.push({
|
|
4779
4879
|
fileData: {
|
|
4780
4880
|
fileUri: mirrored.fileUri,
|
|
4781
|
-
mimeType: mirrored.mimeType
|
|
4782
|
-
displayName: metadata.filename
|
|
4881
|
+
mimeType: mirrored.mimeType
|
|
4783
4882
|
}
|
|
4784
4883
|
});
|
|
4785
4884
|
}
|
|
@@ -4798,18 +4897,13 @@ async function prepareGeminiPromptContents(contents) {
|
|
|
4798
4897
|
});
|
|
4799
4898
|
if (backend === "api") {
|
|
4800
4899
|
const mirrored = await ensureGeminiFileMirror(stored.fileId);
|
|
4801
|
-
|
|
4802
|
-
if (filename && mirroredPart.fileData) {
|
|
4803
|
-
mirroredPart.fileData.displayName = filename;
|
|
4804
|
-
}
|
|
4805
|
-
parts.push(mirroredPart);
|
|
4900
|
+
parts.push(createPartFromUri(mirrored.uri, mirrored.mimeType));
|
|
4806
4901
|
} else {
|
|
4807
4902
|
const mirrored = await ensureVertexFileMirror(stored.fileId);
|
|
4808
4903
|
parts.push({
|
|
4809
4904
|
fileData: {
|
|
4810
4905
|
fileUri: mirrored.fileUri,
|
|
4811
|
-
mimeType: mirrored.mimeType
|
|
4812
|
-
displayName: filename
|
|
4906
|
+
mimeType: mirrored.mimeType
|
|
4813
4907
|
}
|
|
4814
4908
|
});
|
|
4815
4909
|
}
|
|
@@ -5330,7 +5424,7 @@ function toOpenAiInput(contents) {
|
|
|
5330
5424
|
...part.file_id ? { file_id: part.file_id } : {},
|
|
5331
5425
|
...part.file_data ? { file_data: part.file_data } : {},
|
|
5332
5426
|
...part.file_url ? { file_url: part.file_url } : {},
|
|
5333
|
-
|
|
5427
|
+
...!part.file_id && part.filename ? { filename: part.filename } : {}
|
|
5334
5428
|
});
|
|
5335
5429
|
break;
|
|
5336
5430
|
default:
|
|
@@ -5415,7 +5509,7 @@ function toChatGptInput(contents) {
|
|
|
5415
5509
|
...part.file_id ? { file_id: part.file_id } : {},
|
|
5416
5510
|
...part.file_data ? { file_data: part.file_data } : {},
|
|
5417
5511
|
...part.file_url ? { file_url: part.file_url } : {},
|
|
5418
|
-
|
|
5512
|
+
...!part.file_id && part.filename ? { filename: part.filename } : {}
|
|
5419
5513
|
});
|
|
5420
5514
|
break;
|
|
5421
5515
|
default:
|
|
@@ -6022,8 +6116,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6022
6116
|
return {
|
|
6023
6117
|
fileData: {
|
|
6024
6118
|
fileUri: buildCanonicalGeminiFileUri(item.file_id),
|
|
6025
|
-
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6026
|
-
displayName: item.filename ?? void 0
|
|
6119
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6027
6120
|
}
|
|
6028
6121
|
};
|
|
6029
6122
|
}
|
|
@@ -6042,8 +6135,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6042
6135
|
return {
|
|
6043
6136
|
fileData: {
|
|
6044
6137
|
fileUri: item.image_url,
|
|
6045
|
-
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6046
|
-
displayName: item.filename ?? void 0
|
|
6138
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6047
6139
|
}
|
|
6048
6140
|
};
|
|
6049
6141
|
}
|
|
@@ -6052,8 +6144,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6052
6144
|
return {
|
|
6053
6145
|
fileData: {
|
|
6054
6146
|
fileUri: buildCanonicalGeminiFileUri(item.file_id),
|
|
6055
|
-
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6056
|
-
displayName: item.filename ?? void 0
|
|
6147
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6057
6148
|
}
|
|
6058
6149
|
};
|
|
6059
6150
|
}
|
|
@@ -6076,12 +6167,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6076
6167
|
return part;
|
|
6077
6168
|
}
|
|
6078
6169
|
if (typeof item.file_url === "string" && item.file_url.trim().length > 0 && inferredMimeType) {
|
|
6079
|
-
|
|
6080
|
-
const displayName = item.filename?.trim();
|
|
6081
|
-
if (displayName && part.fileData) {
|
|
6082
|
-
part.fileData.displayName = displayName;
|
|
6083
|
-
}
|
|
6084
|
-
return part;
|
|
6170
|
+
return createPartFromUri(item.file_url, inferredMimeType);
|
|
6085
6171
|
}
|
|
6086
6172
|
}
|
|
6087
6173
|
return null;
|