@tradejs/infra 1.0.4 → 1.0.6
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/README.md +1 -1
- package/dist/ai.d.mts +36 -0
- package/dist/ai.d.ts +36 -0
- package/dist/ai.js +456 -0
- package/dist/ai.mjs +386 -0
- package/dist/chunk-EFARW5QE.mjs +297 -0
- package/dist/chunk-KVEZORMS.mjs +142 -0
- package/dist/ml.d.mts +2 -20
- package/dist/ml.d.ts +2 -20
- package/dist/ml.js +17 -0
- package/dist/ml.mjs +20 -124
- package/dist/mlDatasetFile-sRGWgR_o.d.mts +24 -0
- package/dist/mlDatasetFile-sRGWgR_o.d.ts +24 -0
- package/dist/redis.mjs +9 -287
- package/dist/userSettings.d.mts +29 -0
- package/dist/userSettings.d.ts +29 -0
- package/dist/userSettings.js +304 -0
- package/dist/userSettings.mjs +48 -0
- package/package.json +21 -3
package/README.md
CHANGED
package/dist/ai.d.mts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AiDatasetRow } from '@tradejs/types';
|
|
2
|
+
export { m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-sRGWgR_o.mjs';
|
|
3
|
+
|
|
4
|
+
declare const getAiChunkFilePath: (strategyName: string, chunkId: string, outDir?: string) => string;
|
|
5
|
+
declare const appendAiDatasetRow: (params: {
|
|
6
|
+
strategyName: string;
|
|
7
|
+
chunkId: string;
|
|
8
|
+
row: AiDatasetRow;
|
|
9
|
+
outDir?: string;
|
|
10
|
+
}) => Promise<string>;
|
|
11
|
+
declare const flushAiDatasetWriter: (filePath: string) => Promise<void>;
|
|
12
|
+
declare const closeAiDatasetWriter: (filePath: string) => Promise<void>;
|
|
13
|
+
declare const closeAllAiDatasetWriters: () => Promise<void>;
|
|
14
|
+
declare const listAiChunkFiles: (params: {
|
|
15
|
+
strategyName: string;
|
|
16
|
+
outDir?: string;
|
|
17
|
+
}) => Promise<string[]>;
|
|
18
|
+
declare const listAiChunkStrategies: (params?: {
|
|
19
|
+
outDir?: string;
|
|
20
|
+
}) => Promise<string[]>;
|
|
21
|
+
declare const mergeAiJsonlFiles: (params: {
|
|
22
|
+
filePaths: string[];
|
|
23
|
+
outPath: string;
|
|
24
|
+
maxRowsInMemory?: number;
|
|
25
|
+
maxBytesInMemory?: number;
|
|
26
|
+
}) => Promise<void>;
|
|
27
|
+
declare const readAiDatasetRows: (params: {
|
|
28
|
+
filePath: string;
|
|
29
|
+
limitFromEnd?: number;
|
|
30
|
+
skipFromEnd?: number;
|
|
31
|
+
}) => Promise<{
|
|
32
|
+
rows: AiDatasetRow[];
|
|
33
|
+
totalRows: number;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export { appendAiDatasetRow, closeAiDatasetWriter, closeAllAiDatasetWriters, flushAiDatasetWriter, getAiChunkFilePath, listAiChunkFiles, listAiChunkStrategies, mergeAiJsonlFiles, readAiDatasetRows };
|
package/dist/ai.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AiDatasetRow } from '@tradejs/types';
|
|
2
|
+
export { m as mergeJsonlFiles, t as toFileToken } from './mlDatasetFile-sRGWgR_o.js';
|
|
3
|
+
|
|
4
|
+
declare const getAiChunkFilePath: (strategyName: string, chunkId: string, outDir?: string) => string;
|
|
5
|
+
declare const appendAiDatasetRow: (params: {
|
|
6
|
+
strategyName: string;
|
|
7
|
+
chunkId: string;
|
|
8
|
+
row: AiDatasetRow;
|
|
9
|
+
outDir?: string;
|
|
10
|
+
}) => Promise<string>;
|
|
11
|
+
declare const flushAiDatasetWriter: (filePath: string) => Promise<void>;
|
|
12
|
+
declare const closeAiDatasetWriter: (filePath: string) => Promise<void>;
|
|
13
|
+
declare const closeAllAiDatasetWriters: () => Promise<void>;
|
|
14
|
+
declare const listAiChunkFiles: (params: {
|
|
15
|
+
strategyName: string;
|
|
16
|
+
outDir?: string;
|
|
17
|
+
}) => Promise<string[]>;
|
|
18
|
+
declare const listAiChunkStrategies: (params?: {
|
|
19
|
+
outDir?: string;
|
|
20
|
+
}) => Promise<string[]>;
|
|
21
|
+
declare const mergeAiJsonlFiles: (params: {
|
|
22
|
+
filePaths: string[];
|
|
23
|
+
outPath: string;
|
|
24
|
+
maxRowsInMemory?: number;
|
|
25
|
+
maxBytesInMemory?: number;
|
|
26
|
+
}) => Promise<void>;
|
|
27
|
+
declare const readAiDatasetRows: (params: {
|
|
28
|
+
filePath: string;
|
|
29
|
+
limitFromEnd?: number;
|
|
30
|
+
skipFromEnd?: number;
|
|
31
|
+
}) => Promise<{
|
|
32
|
+
rows: AiDatasetRow[];
|
|
33
|
+
totalRows: number;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export { appendAiDatasetRow, closeAiDatasetWriter, closeAllAiDatasetWriters, flushAiDatasetWriter, getAiChunkFilePath, listAiChunkFiles, listAiChunkStrategies, mergeAiJsonlFiles, readAiDatasetRows };
|
package/dist/ai.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/ai.ts
|
|
31
|
+
var ai_exports = {};
|
|
32
|
+
__export(ai_exports, {
|
|
33
|
+
appendAiDatasetRow: () => appendAiDatasetRow,
|
|
34
|
+
closeAiDatasetWriter: () => closeAiDatasetWriter,
|
|
35
|
+
closeAllAiDatasetWriters: () => closeAllAiDatasetWriters,
|
|
36
|
+
flushAiDatasetWriter: () => flushAiDatasetWriter,
|
|
37
|
+
getAiChunkFilePath: () => getAiChunkFilePath,
|
|
38
|
+
listAiChunkFiles: () => listAiChunkFiles,
|
|
39
|
+
listAiChunkStrategies: () => listAiChunkStrategies,
|
|
40
|
+
mergeAiJsonlFiles: () => mergeAiJsonlFiles,
|
|
41
|
+
mergeJsonlFiles: () => mergeJsonlFiles,
|
|
42
|
+
readAiDatasetRows: () => readAiDatasetRows,
|
|
43
|
+
toFileToken: () => toFileToken
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(ai_exports);
|
|
46
|
+
|
|
47
|
+
// src/aiDatasetFile.ts
|
|
48
|
+
var import_events2 = require("events");
|
|
49
|
+
var import_fs2 = require("fs");
|
|
50
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
51
|
+
var import_path2 = __toESM(require("path"));
|
|
52
|
+
var import_node_fs = require("fs");
|
|
53
|
+
var import_node_readline = __toESM(require("readline"));
|
|
54
|
+
|
|
55
|
+
// src/mlDatasetFile.ts
|
|
56
|
+
var import_events = require("events");
|
|
57
|
+
var import_fs = require("fs");
|
|
58
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
59
|
+
var import_path = __toESM(require("path"));
|
|
60
|
+
var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
|
|
61
|
+
var mergeJsonlFiles = async (params) => {
|
|
62
|
+
const { filePaths, outPath } = params;
|
|
63
|
+
await import_promises.default.mkdir(import_path.default.dirname(outPath), { recursive: true });
|
|
64
|
+
const stream = (0, import_fs.createWriteStream)(outPath, { encoding: "utf8" });
|
|
65
|
+
const done = Promise.all([(0, import_events.once)(stream, "finish"), (0, import_events.once)(stream, "close")]);
|
|
66
|
+
try {
|
|
67
|
+
for (const filePath of filePaths) {
|
|
68
|
+
const reader = (0, import_fs.createReadStream)(filePath, { encoding: "utf8" });
|
|
69
|
+
for await (const chunk of reader) {
|
|
70
|
+
if (!stream.write(chunk)) {
|
|
71
|
+
await (0, import_events.once)(stream, "drain");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
stream.end();
|
|
77
|
+
await done;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/aiDatasetFile.ts
|
|
82
|
+
var DEFAULT_DIR = "data/ai/export";
|
|
83
|
+
var AI_DATASET_WRITE_BATCH_SIZE = 100;
|
|
84
|
+
var AI_MERGE_SORT_RUN_MAX_ROWS = 2e3;
|
|
85
|
+
var AI_MERGE_SORT_RUN_MAX_BYTES = 16 * 1024 * 1024;
|
|
86
|
+
var AI_CHUNK_FILE_RE = /^ai-dataset-(.+)-chunk-[^.]+\.jsonl$/;
|
|
87
|
+
var writerByPath = /* @__PURE__ */ new Map();
|
|
88
|
+
var getAiChunkFilePath = (strategyName, chunkId, outDir = DEFAULT_DIR) => import_path2.default.join(
|
|
89
|
+
outDir,
|
|
90
|
+
`ai-dataset-${toFileToken(strategyName)}-chunk-${toFileToken(chunkId)}.jsonl`
|
|
91
|
+
);
|
|
92
|
+
var appendAiDatasetRow = async (params) => {
|
|
93
|
+
const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
|
|
94
|
+
const filePath = getAiChunkFilePath(strategyName, chunkId, outDir);
|
|
95
|
+
let state = writerByPath.get(filePath);
|
|
96
|
+
if (!state) {
|
|
97
|
+
await import_promises2.default.mkdir(outDir, { recursive: true });
|
|
98
|
+
const stream = (0, import_fs2.createWriteStream)(filePath, {
|
|
99
|
+
encoding: "utf8",
|
|
100
|
+
flags: "a"
|
|
101
|
+
});
|
|
102
|
+
state = {
|
|
103
|
+
filePath,
|
|
104
|
+
stream,
|
|
105
|
+
buffer: [],
|
|
106
|
+
writeQueue: Promise.resolve(),
|
|
107
|
+
closed: false
|
|
108
|
+
};
|
|
109
|
+
writerByPath.set(filePath, state);
|
|
110
|
+
}
|
|
111
|
+
if (state.closed) {
|
|
112
|
+
throw new Error(`AI dataset writer is closed: ${filePath}`);
|
|
113
|
+
}
|
|
114
|
+
state.buffer.push(`${JSON.stringify(row)}
|
|
115
|
+
`);
|
|
116
|
+
if (state.buffer.length >= AI_DATASET_WRITE_BATCH_SIZE) {
|
|
117
|
+
await flushAiDatasetWriter(filePath);
|
|
118
|
+
}
|
|
119
|
+
return filePath;
|
|
120
|
+
};
|
|
121
|
+
var flushState = async (state) => {
|
|
122
|
+
if (state.closed || state.buffer.length === 0) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const chunk = state.buffer.join("");
|
|
126
|
+
state.buffer = [];
|
|
127
|
+
if (!state.stream.write(chunk)) {
|
|
128
|
+
await (0, import_events2.once)(state.stream, "drain");
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var flushAiDatasetWriter = async (filePath) => {
|
|
132
|
+
const state = writerByPath.get(filePath);
|
|
133
|
+
if (!state || state.closed) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
state.writeQueue = state.writeQueue.then(() => flushState(state));
|
|
137
|
+
await state.writeQueue;
|
|
138
|
+
};
|
|
139
|
+
var closeState = async (state) => {
|
|
140
|
+
if (state.closed) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
await flushState(state);
|
|
144
|
+
state.closed = true;
|
|
145
|
+
state.stream.end();
|
|
146
|
+
await Promise.all([
|
|
147
|
+
(0, import_events2.once)(state.stream, "finish"),
|
|
148
|
+
(0, import_events2.once)(state.stream, "close")
|
|
149
|
+
]);
|
|
150
|
+
};
|
|
151
|
+
var closeAiDatasetWriter = async (filePath) => {
|
|
152
|
+
const state = writerByPath.get(filePath);
|
|
153
|
+
if (!state) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
state.writeQueue = state.writeQueue.then(() => closeState(state));
|
|
157
|
+
await state.writeQueue;
|
|
158
|
+
writerByPath.delete(filePath);
|
|
159
|
+
};
|
|
160
|
+
var closeAllAiDatasetWriters = async () => {
|
|
161
|
+
const filePaths = [...writerByPath.keys()];
|
|
162
|
+
for (const filePath of filePaths) {
|
|
163
|
+
await closeAiDatasetWriter(filePath);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var listAiChunkFiles = async (params) => {
|
|
167
|
+
const { strategyName, outDir = DEFAULT_DIR } = params;
|
|
168
|
+
const prefix = `ai-dataset-${toFileToken(strategyName)}-chunk-`;
|
|
169
|
+
let entries = [];
|
|
170
|
+
try {
|
|
171
|
+
entries = await import_promises2.default.readdir(outDir);
|
|
172
|
+
} catch {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => import_path2.default.join(outDir, name)).sort();
|
|
176
|
+
};
|
|
177
|
+
var listAiChunkStrategies = async (params) => {
|
|
178
|
+
const outDir = params?.outDir ?? DEFAULT_DIR;
|
|
179
|
+
let entries = [];
|
|
180
|
+
try {
|
|
181
|
+
entries = await import_promises2.default.readdir(outDir);
|
|
182
|
+
} catch {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
return [
|
|
186
|
+
...new Set(
|
|
187
|
+
entries.map((name) => name.match(AI_CHUNK_FILE_RE)?.[1] || "").filter(Boolean)
|
|
188
|
+
)
|
|
189
|
+
].sort();
|
|
190
|
+
};
|
|
191
|
+
var parseAiDatasetLine = (line, filePath) => {
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(line);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const message = error?.message || String(error);
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Failed to parse AI dataset row from ${filePath}: ${message}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var getAiDatasetSortKey = (row) => {
|
|
202
|
+
const timestamp = Number(row.timestamp);
|
|
203
|
+
return {
|
|
204
|
+
timestamp: Number.isFinite(timestamp) ? timestamp : Number.MAX_SAFE_INTEGER,
|
|
205
|
+
symbol: String(row.symbol || ""),
|
|
206
|
+
signalId: String(row.signalId || "")
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
var compareAiDatasetSortKeys = (left, right) => {
|
|
210
|
+
if (left.timestamp !== right.timestamp) {
|
|
211
|
+
return left.timestamp - right.timestamp;
|
|
212
|
+
}
|
|
213
|
+
const symbolCompare = left.symbol.localeCompare(right.symbol);
|
|
214
|
+
if (symbolCompare !== 0) {
|
|
215
|
+
return symbolCompare;
|
|
216
|
+
}
|
|
217
|
+
return left.signalId.localeCompare(right.signalId);
|
|
218
|
+
};
|
|
219
|
+
var compareSortableAiDatasetLines = (left, right) => {
|
|
220
|
+
const keyCompare = compareAiDatasetSortKeys(left.sortKey, right.sortKey);
|
|
221
|
+
if (keyCompare !== 0) {
|
|
222
|
+
return keyCompare;
|
|
223
|
+
}
|
|
224
|
+
return left.sourceIndex - right.sourceIndex;
|
|
225
|
+
};
|
|
226
|
+
var compareSortedRunHeads = (left, right) => {
|
|
227
|
+
const keyCompare = compareAiDatasetSortKeys(left.sortKey, right.sortKey);
|
|
228
|
+
if (keyCompare !== 0) {
|
|
229
|
+
return keyCompare;
|
|
230
|
+
}
|
|
231
|
+
return left.runIndex - right.runIndex;
|
|
232
|
+
};
|
|
233
|
+
var writeJsonlLines = async (params) => {
|
|
234
|
+
const { filePath, lines } = params;
|
|
235
|
+
await import_promises2.default.mkdir(import_path2.default.dirname(filePath), { recursive: true });
|
|
236
|
+
const stream = (0, import_fs2.createWriteStream)(filePath, { encoding: "utf8" });
|
|
237
|
+
const done = Promise.all([(0, import_events2.once)(stream, "finish"), (0, import_events2.once)(stream, "close")]);
|
|
238
|
+
try {
|
|
239
|
+
for (const line of lines) {
|
|
240
|
+
if (!stream.write(`${line}
|
|
241
|
+
`)) {
|
|
242
|
+
await (0, import_events2.once)(stream, "drain");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} finally {
|
|
246
|
+
stream.end();
|
|
247
|
+
await done;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
var flushSortedRun = async (params) => {
|
|
251
|
+
const { tempDir, runIndex, entries } = params;
|
|
252
|
+
entries.sort(compareSortableAiDatasetLines);
|
|
253
|
+
const filePath = import_path2.default.join(
|
|
254
|
+
tempDir,
|
|
255
|
+
`run-${String(runIndex).padStart(6, "0")}.jsonl`
|
|
256
|
+
);
|
|
257
|
+
await writeJsonlLines({
|
|
258
|
+
filePath,
|
|
259
|
+
lines: entries.map(({ line }) => line)
|
|
260
|
+
});
|
|
261
|
+
return filePath;
|
|
262
|
+
};
|
|
263
|
+
var readNextNonEmptyLine = async (iterator) => {
|
|
264
|
+
while (true) {
|
|
265
|
+
const next = await iterator.next();
|
|
266
|
+
if (next.done) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const trimmed = String(next.value || "").trim();
|
|
270
|
+
if (trimmed) {
|
|
271
|
+
return trimmed;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
var mergeSortedRuns = async (params) => {
|
|
276
|
+
const { runPaths, outPath } = params;
|
|
277
|
+
await import_promises2.default.mkdir(import_path2.default.dirname(outPath), { recursive: true });
|
|
278
|
+
const output = (0, import_fs2.createWriteStream)(outPath, { encoding: "utf8" });
|
|
279
|
+
const outputDone = Promise.all([
|
|
280
|
+
(0, import_events2.once)(output, "finish"),
|
|
281
|
+
(0, import_events2.once)(output, "close")
|
|
282
|
+
]);
|
|
283
|
+
const heads = [];
|
|
284
|
+
try {
|
|
285
|
+
for (let runIndex = 0; runIndex < runPaths.length; runIndex += 1) {
|
|
286
|
+
const runPath = runPaths[runIndex];
|
|
287
|
+
const reader = import_node_readline.default.createInterface({
|
|
288
|
+
input: (0, import_node_fs.createReadStream)(runPath, { encoding: "utf8" }),
|
|
289
|
+
crlfDelay: Infinity
|
|
290
|
+
});
|
|
291
|
+
const iterator = reader[Symbol.asyncIterator]();
|
|
292
|
+
const line = await readNextNonEmptyLine(iterator);
|
|
293
|
+
if (!line) {
|
|
294
|
+
reader.close();
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
heads.push({
|
|
298
|
+
runIndex,
|
|
299
|
+
line,
|
|
300
|
+
sortKey: getAiDatasetSortKey(parseAiDatasetLine(line, runPath)),
|
|
301
|
+
iterator,
|
|
302
|
+
reader
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
while (heads.length > 0) {
|
|
306
|
+
let bestIndex = 0;
|
|
307
|
+
for (let index = 1; index < heads.length; index += 1) {
|
|
308
|
+
if (compareSortedRunHeads(heads[index], heads[bestIndex]) < 0) {
|
|
309
|
+
bestIndex = index;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const best = heads[bestIndex];
|
|
313
|
+
if (!output.write(`${best.line}
|
|
314
|
+
`)) {
|
|
315
|
+
await (0, import_events2.once)(output, "drain");
|
|
316
|
+
}
|
|
317
|
+
const nextLine = await readNextNonEmptyLine(best.iterator);
|
|
318
|
+
if (!nextLine) {
|
|
319
|
+
best.reader.close();
|
|
320
|
+
heads.splice(bestIndex, 1);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
best.line = nextLine;
|
|
324
|
+
best.sortKey = getAiDatasetSortKey(
|
|
325
|
+
parseAiDatasetLine(nextLine, runPaths[best.runIndex])
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
} finally {
|
|
329
|
+
for (const head of heads) {
|
|
330
|
+
head.reader.close();
|
|
331
|
+
}
|
|
332
|
+
output.end();
|
|
333
|
+
await outputDone;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
var mergeAiJsonlFiles = async (params) => {
|
|
337
|
+
const {
|
|
338
|
+
filePaths,
|
|
339
|
+
outPath,
|
|
340
|
+
maxRowsInMemory = AI_MERGE_SORT_RUN_MAX_ROWS,
|
|
341
|
+
maxBytesInMemory = AI_MERGE_SORT_RUN_MAX_BYTES
|
|
342
|
+
} = params;
|
|
343
|
+
const tempDir = import_path2.default.join(
|
|
344
|
+
import_path2.default.dirname(outPath),
|
|
345
|
+
`.ai-merge-${import_path2.default.basename(outPath)}-${Date.now()}-${process.pid}`
|
|
346
|
+
);
|
|
347
|
+
let batch = [];
|
|
348
|
+
let batchBytes = 0;
|
|
349
|
+
let sourceIndex = 0;
|
|
350
|
+
let runIndex = 0;
|
|
351
|
+
const runPaths = [];
|
|
352
|
+
const flushBatch = async () => {
|
|
353
|
+
if (!batch.length) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
runPaths.push(
|
|
357
|
+
await flushSortedRun({
|
|
358
|
+
tempDir,
|
|
359
|
+
runIndex,
|
|
360
|
+
entries: batch
|
|
361
|
+
})
|
|
362
|
+
);
|
|
363
|
+
runIndex += 1;
|
|
364
|
+
batch = [];
|
|
365
|
+
batchBytes = 0;
|
|
366
|
+
};
|
|
367
|
+
try {
|
|
368
|
+
await import_promises2.default.mkdir(tempDir, { recursive: true });
|
|
369
|
+
for (const filePath of filePaths) {
|
|
370
|
+
const reader = import_node_readline.default.createInterface({
|
|
371
|
+
input: (0, import_node_fs.createReadStream)(filePath, { encoding: "utf8" }),
|
|
372
|
+
crlfDelay: Infinity
|
|
373
|
+
});
|
|
374
|
+
try {
|
|
375
|
+
for await (const line of reader) {
|
|
376
|
+
const trimmed = line.trim();
|
|
377
|
+
if (!trimmed) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
batch.push({
|
|
381
|
+
line: trimmed,
|
|
382
|
+
sortKey: getAiDatasetSortKey(parseAiDatasetLine(trimmed, filePath)),
|
|
383
|
+
sourceIndex
|
|
384
|
+
});
|
|
385
|
+
sourceIndex += 1;
|
|
386
|
+
batchBytes += Buffer.byteLength(trimmed, "utf8") + 1;
|
|
387
|
+
if (batch.length >= maxRowsInMemory || batchBytes >= maxBytesInMemory) {
|
|
388
|
+
await flushBatch();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} finally {
|
|
392
|
+
reader.close();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
await flushBatch();
|
|
396
|
+
if (!runPaths.length) {
|
|
397
|
+
await import_promises2.default.mkdir(import_path2.default.dirname(outPath), { recursive: true });
|
|
398
|
+
await import_promises2.default.writeFile(outPath, "", "utf8");
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
await mergeSortedRuns({
|
|
402
|
+
runPaths,
|
|
403
|
+
outPath
|
|
404
|
+
});
|
|
405
|
+
} finally {
|
|
406
|
+
await import_promises2.default.rm(tempDir, { recursive: true, force: true });
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var readAiDatasetRows = async (params) => {
|
|
410
|
+
const { filePath, limitFromEnd = 0, skipFromEnd = 0 } = params;
|
|
411
|
+
const rows = [];
|
|
412
|
+
const recentLines = [];
|
|
413
|
+
let totalRows = 0;
|
|
414
|
+
const recentWindowLimit = limitFromEnd > 0 ? limitFromEnd + Math.max(0, skipFromEnd) : 0;
|
|
415
|
+
const reader = import_node_readline.default.createInterface({
|
|
416
|
+
input: (0, import_node_fs.createReadStream)(filePath, { encoding: "utf8" }),
|
|
417
|
+
crlfDelay: Infinity
|
|
418
|
+
});
|
|
419
|
+
for await (const line of reader) {
|
|
420
|
+
const trimmed = line.trim();
|
|
421
|
+
if (!trimmed) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
totalRows += 1;
|
|
425
|
+
if (limitFromEnd > 0) {
|
|
426
|
+
if (recentLines.length === recentWindowLimit) {
|
|
427
|
+
recentLines.shift();
|
|
428
|
+
}
|
|
429
|
+
recentLines.push(trimmed);
|
|
430
|
+
} else {
|
|
431
|
+
const row = JSON.parse(trimmed);
|
|
432
|
+
rows.push(row);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const effectiveSkip = Math.max(0, skipFromEnd);
|
|
436
|
+
const selectedRecentLines = effectiveSkip > 0 ? recentLines.slice(0, Math.max(0, recentLines.length - effectiveSkip)) : recentLines;
|
|
437
|
+
const selectedRows = limitFromEnd > 0 ? selectedRecentLines.map((line) => JSON.parse(line)) : effectiveSkip > 0 ? rows.slice(0, Math.max(0, rows.length - effectiveSkip)) : rows;
|
|
438
|
+
return {
|
|
439
|
+
rows: selectedRows,
|
|
440
|
+
totalRows
|
|
441
|
+
};
|
|
442
|
+
};
|
|
443
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
444
|
+
0 && (module.exports = {
|
|
445
|
+
appendAiDatasetRow,
|
|
446
|
+
closeAiDatasetWriter,
|
|
447
|
+
closeAllAiDatasetWriters,
|
|
448
|
+
flushAiDatasetWriter,
|
|
449
|
+
getAiChunkFilePath,
|
|
450
|
+
listAiChunkFiles,
|
|
451
|
+
listAiChunkStrategies,
|
|
452
|
+
mergeAiJsonlFiles,
|
|
453
|
+
mergeJsonlFiles,
|
|
454
|
+
readAiDatasetRows,
|
|
455
|
+
toFileToken
|
|
456
|
+
});
|