@tradejs/infra 1.0.5 → 1.0.8
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/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/aiEndpoints.d.mts +10 -0
- package/dist/aiEndpoints.d.ts +10 -0
- package/dist/aiEndpoints.js +159 -0
- package/dist/aiEndpoints.mjs +12 -0
- package/dist/aiLanguages.d.mts +11 -0
- package/dist/aiLanguages.d.ts +11 -0
- package/dist/aiLanguages.js +71 -0
- package/dist/aiLanguages.mjs +12 -0
- package/dist/aiModels.d.mts +12 -0
- package/dist/aiModels.d.ts +12 -0
- package/dist/aiModels.js +272 -0
- package/dist/aiModels.mjs +17 -0
- package/dist/chunk-CCC7DX2T.mjs +44 -0
- package/dist/chunk-DSTV67R5.mjs +345 -0
- package/dist/chunk-DTCLZIBM.mjs +163 -0
- package/dist/chunk-KVEZORMS.mjs +142 -0
- package/dist/chunk-XQ3YBULV.mjs +132 -0
- package/dist/ml.d.mts +2 -20
- package/dist/ml.d.ts +2 -20
- package/dist/ml.js +136 -0
- package/dist/ml.mjs +139 -124
- package/dist/mlDatasetFile-sRGWgR_o.d.mts +24 -0
- package/dist/mlDatasetFile-sRGWgR_o.d.ts +24 -0
- package/dist/redis.d.mts +17 -1
- package/dist/redis.d.ts +17 -1
- package/dist/redis.js +50 -0
- package/dist/redis.mjs +13 -287
- package/dist/timescale.d.mts +18 -7
- package/dist/timescale.d.ts +18 -7
- package/dist/timescale.js +142 -31
- package/dist/timescale.mjs +140 -31
- package/dist/userSettings.d.mts +31 -0
- package/dist/userSettings.d.ts +31 -0
- package/dist/userSettings.js +636 -0
- package/dist/userSettings.mjs +70 -0
- package/package.json +28 -3
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// src/redis.ts
|
|
2
|
+
import { randomBytes } from "crypto";
|
|
3
|
+
import Redis from "ioredis";
|
|
4
|
+
var TTL_1D = 86400;
|
|
5
|
+
var SCREENSHOT_TOKEN_TTL_SECONDS = 15 * 60;
|
|
6
|
+
var toJson = (value) => JSON.stringify(value);
|
|
7
|
+
var logger = {
|
|
8
|
+
log: (level, message, ...args) => {
|
|
9
|
+
const method = level === "error" ? "error" : level === "warn" ? "warn" : "log";
|
|
10
|
+
console[method](`[infra:redis] ${message}`, ...args);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var redisConnectionWarningShown = false;
|
|
14
|
+
var redisUnavailable = false;
|
|
15
|
+
var isRedisConnectivityError = (error) => /ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
|
|
16
|
+
error.message
|
|
17
|
+
);
|
|
18
|
+
var toNonNegativeInt = (value, fallback) => {
|
|
19
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
20
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
21
|
+
};
|
|
22
|
+
var toPositiveInt = (value, fallback) => {
|
|
23
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
24
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
25
|
+
};
|
|
26
|
+
var markRedisUnavailable = (error) => {
|
|
27
|
+
redisUnavailable = true;
|
|
28
|
+
if (redisConnectionWarningShown) return;
|
|
29
|
+
redisConnectionWarningShown = true;
|
|
30
|
+
logger.log(
|
|
31
|
+
"warn",
|
|
32
|
+
"Redis is unavailable: %s. Cache-dependent features are temporarily disabled.",
|
|
33
|
+
error.message
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
var getRedis = () => {
|
|
37
|
+
if (!global.__redis__) {
|
|
38
|
+
const host = process.env.REDIS_HOST || "127.0.0.1";
|
|
39
|
+
const port = toPositiveInt(process.env.REDIS_PORT, 6379);
|
|
40
|
+
const connectTimeout = toPositiveInt(
|
|
41
|
+
process.env.REDIS_CONNECT_TIMEOUT_MS,
|
|
42
|
+
3e3
|
|
43
|
+
);
|
|
44
|
+
const maxRetriesPerRequest = toNonNegativeInt(
|
|
45
|
+
process.env.REDIS_MAX_RETRIES_PER_REQUEST,
|
|
46
|
+
1
|
|
47
|
+
);
|
|
48
|
+
global.__redis__ = new Redis({
|
|
49
|
+
host,
|
|
50
|
+
port,
|
|
51
|
+
connectTimeout,
|
|
52
|
+
maxRetriesPerRequest,
|
|
53
|
+
enableOfflineQueue: false,
|
|
54
|
+
retryStrategy: (attempt) => Math.min(attempt * 200, 2e3)
|
|
55
|
+
});
|
|
56
|
+
global.__redis__.on("error", (error) => {
|
|
57
|
+
if (isRedisConnectivityError(error)) {
|
|
58
|
+
markRedisUnavailable(error);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
logger.log("error", "Redis client error: %s", String(error));
|
|
62
|
+
});
|
|
63
|
+
global.__redis__.on("ready", () => {
|
|
64
|
+
redisUnavailable = false;
|
|
65
|
+
if (redisConnectionWarningShown) {
|
|
66
|
+
redisConnectionWarningShown = false;
|
|
67
|
+
logger.log("info", "Redis connection restored");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return global.__redis__;
|
|
72
|
+
};
|
|
73
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
74
|
+
var getRedisStatus = (redis) => String(redis.status ?? "ready");
|
|
75
|
+
var waitForRedisReady = async (redis) => {
|
|
76
|
+
if (getRedisStatus(redis) === "ready") {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
const readyTimeoutMs = toPositiveInt(
|
|
80
|
+
process.env.REDIS_READY_TIMEOUT_MS,
|
|
81
|
+
toPositiveInt(process.env.REDIS_CONNECT_TIMEOUT_MS, 3e3)
|
|
82
|
+
);
|
|
83
|
+
const startedAt = Date.now();
|
|
84
|
+
while (Date.now() - startedAt < readyTimeoutMs) {
|
|
85
|
+
if (getRedisStatus(redis) === "ready") {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (getRedisStatus(redis) === "end") {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
await sleep(50);
|
|
92
|
+
}
|
|
93
|
+
return getRedisStatus(redis) === "ready";
|
|
94
|
+
};
|
|
95
|
+
var getReadyRedis = async () => {
|
|
96
|
+
if (redisUnavailable) return null;
|
|
97
|
+
const redis = getRedis();
|
|
98
|
+
const ready = await waitForRedisReady(redis);
|
|
99
|
+
if (ready) {
|
|
100
|
+
return redis;
|
|
101
|
+
}
|
|
102
|
+
markRedisUnavailable(
|
|
103
|
+
new Error(`Redis is not ready (status=${getRedisStatus(redis)})`)
|
|
104
|
+
);
|
|
105
|
+
return null;
|
|
106
|
+
};
|
|
107
|
+
var toResultString = (result) => {
|
|
108
|
+
if (result == null) return null;
|
|
109
|
+
if (typeof result === "string") return result;
|
|
110
|
+
if (Buffer.isBuffer(result)) return result.toString("utf8");
|
|
111
|
+
return String(result);
|
|
112
|
+
};
|
|
113
|
+
var DEFAULT_OPTIONS = {
|
|
114
|
+
expire: TTL_1D
|
|
115
|
+
};
|
|
116
|
+
var parseJsonOrDeleteKey = async (redis, key, raw, fallback) => {
|
|
117
|
+
try {
|
|
118
|
+
return JSON.parse(raw);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
logger.log("error", "failed JSON.parse(%s): %s", key, String(e));
|
|
121
|
+
await redis.del(key);
|
|
122
|
+
return fallback;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var getKeys = async (prefix) => {
|
|
126
|
+
if (redisUnavailable) return [];
|
|
127
|
+
const redis = await getReadyRedis();
|
|
128
|
+
if (!redis) return [];
|
|
129
|
+
const keys = [];
|
|
130
|
+
try {
|
|
131
|
+
let cursor = "0";
|
|
132
|
+
do {
|
|
133
|
+
const [nextCursor, batch] = await redis.scan(
|
|
134
|
+
cursor,
|
|
135
|
+
"MATCH",
|
|
136
|
+
`${prefix}*`,
|
|
137
|
+
"COUNT",
|
|
138
|
+
"200"
|
|
139
|
+
);
|
|
140
|
+
cursor = nextCursor;
|
|
141
|
+
for (const key of batch) {
|
|
142
|
+
if (key.startsWith(prefix)) {
|
|
143
|
+
keys.push(key);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} while (cursor !== "0");
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (e instanceof Error && isRedisConnectivityError(e)) {
|
|
149
|
+
markRedisUnavailable(e);
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
logger.log("warn", "failed SCAN for %s: %s", prefix, String(e));
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return keys;
|
|
156
|
+
};
|
|
157
|
+
var getData = async (key, fallback = []) => {
|
|
158
|
+
if (redisUnavailable) return fallback;
|
|
159
|
+
const redis = await getReadyRedis();
|
|
160
|
+
if (!redis) return fallback;
|
|
161
|
+
try {
|
|
162
|
+
const rawJson = await redis.call("JSON.GET", key);
|
|
163
|
+
const raw = toResultString(rawJson);
|
|
164
|
+
if (raw == null) return fallback;
|
|
165
|
+
return parseJsonOrDeleteKey(redis, key, raw, fallback);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
if (e instanceof Error && isRedisConnectivityError(e)) {
|
|
168
|
+
markRedisUnavailable(e);
|
|
169
|
+
return fallback;
|
|
170
|
+
}
|
|
171
|
+
logger.log(
|
|
172
|
+
"error",
|
|
173
|
+
"failed JSON.GET %s: %s (fallback to GET)",
|
|
174
|
+
key,
|
|
175
|
+
String(e)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const raw = await redis.get(key);
|
|
180
|
+
if (raw == null) return fallback;
|
|
181
|
+
return parseJsonOrDeleteKey(redis, key, raw, fallback);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
if (e instanceof Error && isRedisConnectivityError(e)) {
|
|
184
|
+
markRedisUnavailable(e);
|
|
185
|
+
return fallback;
|
|
186
|
+
}
|
|
187
|
+
logger.log("error", "failed GET %s: %s", key, String(e));
|
|
188
|
+
return fallback;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
var delKey = async (key) => {
|
|
192
|
+
return delKeyWithOptions(key);
|
|
193
|
+
};
|
|
194
|
+
var RedisWriteBlockedError = class extends Error {
|
|
195
|
+
constructor(message) {
|
|
196
|
+
super(message);
|
|
197
|
+
this.name = "RedisWriteBlockedError";
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
var delKeyWithOptions = async (key, options = {}) => {
|
|
201
|
+
if (redisUnavailable) return false;
|
|
202
|
+
const { raiseOnMisconf = false } = options;
|
|
203
|
+
const redis = await getReadyRedis();
|
|
204
|
+
if (!redis) return false;
|
|
205
|
+
try {
|
|
206
|
+
const result = await redis.del(key);
|
|
207
|
+
if (result === 1) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
} catch (e) {
|
|
212
|
+
if (e instanceof Error && isRedisConnectivityError(e)) {
|
|
213
|
+
markRedisUnavailable(e);
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const msg = String(e);
|
|
217
|
+
if (raiseOnMisconf && msg.includes("MISCONF")) {
|
|
218
|
+
throw new RedisWriteBlockedError(msg);
|
|
219
|
+
}
|
|
220
|
+
logger.log("error", "failed DEL %s: %s", key, String(e));
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
var setData = async (key, data, options = {}) => {
|
|
225
|
+
if (redisUnavailable) return;
|
|
226
|
+
const { expire } = { ...DEFAULT_OPTIONS, ...options };
|
|
227
|
+
const redis = await getReadyRedis();
|
|
228
|
+
if (!redis) return;
|
|
229
|
+
const value = toJson(data);
|
|
230
|
+
try {
|
|
231
|
+
await redis.call("JSON.SET", key, "$", value);
|
|
232
|
+
if (expire) {
|
|
233
|
+
await redis.expire(key, expire);
|
|
234
|
+
}
|
|
235
|
+
} catch (e) {
|
|
236
|
+
if (e instanceof Error && isRedisConnectivityError(e)) {
|
|
237
|
+
markRedisUnavailable(e);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
logger.log(
|
|
241
|
+
"error",
|
|
242
|
+
"failed JSON.SET %s: %s (fallback to SET)",
|
|
243
|
+
key,
|
|
244
|
+
String(e)
|
|
245
|
+
);
|
|
246
|
+
try {
|
|
247
|
+
if (expire) {
|
|
248
|
+
await redis.set(key, value, "EX", expire);
|
|
249
|
+
} else {
|
|
250
|
+
await redis.set(key, value);
|
|
251
|
+
}
|
|
252
|
+
} catch (e2) {
|
|
253
|
+
if (e2 instanceof Error && isRedisConnectivityError(e2)) {
|
|
254
|
+
markRedisUnavailable(e2);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
logger.log("error", "failed SET %s: %s", key, String(e2));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
var createScreenshotSessionToken = async (userName) => {
|
|
262
|
+
const token = randomBytes(24).toString("hex");
|
|
263
|
+
await setData(
|
|
264
|
+
redisKeys.screenshotSessionToken(token),
|
|
265
|
+
{
|
|
266
|
+
userName,
|
|
267
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
expire: SCREENSHOT_TOKEN_TTL_SECONDS
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
return token;
|
|
274
|
+
};
|
|
275
|
+
var consumeScreenshotSessionToken = async (token) => {
|
|
276
|
+
const normalizedToken = token.trim();
|
|
277
|
+
if (!normalizedToken) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const key = redisKeys.screenshotSessionToken(normalizedToken);
|
|
281
|
+
const payload = await getData(
|
|
282
|
+
key,
|
|
283
|
+
null
|
|
284
|
+
);
|
|
285
|
+
if (!payload?.userName) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
await delKey(key);
|
|
289
|
+
return payload.userName;
|
|
290
|
+
};
|
|
291
|
+
var redisKeys = {
|
|
292
|
+
users: () => "users:index:",
|
|
293
|
+
user: (userName) => `users:index:${userName}`,
|
|
294
|
+
bots: (userName) => `users:${userName}:bots`,
|
|
295
|
+
botsPrefix: () => "users:",
|
|
296
|
+
bot: (userName, botId) => `users:${userName}:bots:${botId}`,
|
|
297
|
+
backtestConfig: (userName, config) => `users:${userName}:backtests:configs:${config}`,
|
|
298
|
+
strategies: (userName) => `users:${userName}:strategies`,
|
|
299
|
+
strategyConfig: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:config`,
|
|
300
|
+
strategyResults: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:results`,
|
|
301
|
+
tests: (userName, strategyName) => strategyName ? `users:${userName}:tests:${strategyName}` : `users:${userName}:tests:`,
|
|
302
|
+
testOrders: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:orders`,
|
|
303
|
+
testConfig: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:config`,
|
|
304
|
+
testStat: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:stat`,
|
|
305
|
+
testSummaries: (userName) => `users:${userName}:tests:index:summary`,
|
|
306
|
+
cacheChunk: (userName, chunkId) => `users:${userName}:cache:tests:chunks:${chunkId}`,
|
|
307
|
+
cacheOrders: (userName, orderLogId) => `users:${userName}:cache:tests:orders:${orderLogId}`,
|
|
308
|
+
cachePositions: (userName, orderLogId) => `users:${userName}:cache:tests:positions:${orderLogId}`,
|
|
309
|
+
signal: (symbol, signalId) => `signals:${symbol}:${signalId}`,
|
|
310
|
+
signalsBySymbol: (symbol) => `signals:${symbol}:`,
|
|
311
|
+
storeSignal: (symbol, signalId) => `store:signals:${symbol}:${signalId}`,
|
|
312
|
+
runtimeSignals: (userName) => `users:${userName}:runtime:signals:`,
|
|
313
|
+
runtimeSignal: (userName, signalId) => `users:${userName}:runtime:signals:${signalId}`,
|
|
314
|
+
runtimeSignalEvaluations: (userName) => `users:${userName}:runtime:signal-evaluations:`,
|
|
315
|
+
runtimeSignalEvaluation: (userName, evaluationId) => `users:${userName}:runtime:signal-evaluations:${evaluationId}`,
|
|
316
|
+
runtimeTrades: (userName) => `users:${userName}:runtime:trade-records:`,
|
|
317
|
+
runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
|
|
318
|
+
runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
|
|
319
|
+
runtimeActiveTrade: (userName, symbol) => `users:${userName}:runtime:active-trades:${symbol}`,
|
|
320
|
+
aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
|
|
321
|
+
analysis: (symbol, signalId) => `analysis:${symbol}:${signalId}`,
|
|
322
|
+
screenshotSessionToken: (token) => `auth:screenshot:${token}`,
|
|
323
|
+
backtestResults: (userName, config, timestamp) => `users:${userName}:backtests:results:${config}:${timestamp}`,
|
|
324
|
+
researchRuns: (userName) => `users:${userName}:research:runs:`,
|
|
325
|
+
researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
|
|
326
|
+
researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
|
|
327
|
+
mlSignalsByStrategy: (strategyName) => `ml:${strategyName}:signals:`,
|
|
328
|
+
mlSignals: () => "ml:",
|
|
329
|
+
mlSignal: (strategyName, signalId) => `ml:${strategyName}:signals:${signalId}`,
|
|
330
|
+
mlResultsByStrategy: (strategyName) => `ml:${strategyName}:results:`,
|
|
331
|
+
mlResults: () => "ml:",
|
|
332
|
+
mlResult: (strategyName, signalId) => `ml:${strategyName}:results:${signalId}`
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export {
|
|
336
|
+
getKeys,
|
|
337
|
+
getData,
|
|
338
|
+
delKey,
|
|
339
|
+
RedisWriteBlockedError,
|
|
340
|
+
delKeyWithOptions,
|
|
341
|
+
setData,
|
|
342
|
+
createScreenshotSessionToken,
|
|
343
|
+
consumeScreenshotSessionToken,
|
|
344
|
+
redisKeys
|
|
345
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AI_CUSTOM_ENDPOINT_VALUE
|
|
3
|
+
} from "./chunk-XQ3YBULV.mjs";
|
|
4
|
+
|
|
5
|
+
// src/aiModels.ts
|
|
6
|
+
var AI_CUSTOM_MODEL_VALUE = "__custom_model__";
|
|
7
|
+
var OPENAI_ENDPOINT = "https://api.openai.com/v1";
|
|
8
|
+
var ANTHROPIC_ENDPOINT = "https://api.anthropic.com/v1";
|
|
9
|
+
var OPENROUTER_ENDPOINT = "https://openrouter.ai/api/v1";
|
|
10
|
+
var GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/openai";
|
|
11
|
+
var TOGETHER_ENDPOINT = "https://api.together.xyz/v1";
|
|
12
|
+
var GROQ_ENDPOINT = "https://api.groq.com/openai/v1";
|
|
13
|
+
var XAI_ENDPOINT = "https://api.x.ai/v1";
|
|
14
|
+
var QWEN_INTL_ENDPOINT = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
|
|
15
|
+
var QWEN_CN_ENDPOINT = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
16
|
+
var QWEN_US_ENDPOINT = "https://dashscope-us.aliyuncs.com/compatible-mode/v1";
|
|
17
|
+
var PERPLEXITY_ENDPOINT = "https://api.perplexity.ai";
|
|
18
|
+
var KIMI_ENDPOINT = "https://api.moonshot.ai/v1";
|
|
19
|
+
var PROXY_API_ENDPOINT = "https://openai.api.proxyapi.ru/v1";
|
|
20
|
+
var AI_MODEL_OPTIONS_BY_ENDPOINT = {
|
|
21
|
+
[OPENAI_ENDPOINT]: [
|
|
22
|
+
{ label: "GPT-5 mini", value: "gpt-5-mini" },
|
|
23
|
+
{ label: "GPT-5", value: "gpt-5" },
|
|
24
|
+
{ label: "GPT-5.2", value: "gpt-5.2" },
|
|
25
|
+
{ label: "GPT-4.1", value: "gpt-4.1" },
|
|
26
|
+
{ label: "GPT-4o", value: "gpt-4o" }
|
|
27
|
+
],
|
|
28
|
+
[ANTHROPIC_ENDPOINT]: [
|
|
29
|
+
{ label: "Claude Sonnet 4", value: "claude-sonnet-4-20250514" },
|
|
30
|
+
{ label: "Claude Opus 4.1", value: "claude-opus-4-1-20250805" },
|
|
31
|
+
{ label: "Claude Opus 4", value: "claude-opus-4-20250514" },
|
|
32
|
+
{ label: "Claude 3.7 Sonnet", value: "claude-3-7-sonnet-20250219" },
|
|
33
|
+
{ label: "Claude 3.5 Haiku", value: "claude-3-5-haiku-20241022" }
|
|
34
|
+
],
|
|
35
|
+
[OPENROUTER_ENDPOINT]: [
|
|
36
|
+
{ label: "OpenAI GPT-5", value: "openai/gpt-5" },
|
|
37
|
+
{ label: "Anthropic Claude Sonnet 4", value: "anthropic/claude-sonnet-4" },
|
|
38
|
+
{ label: "Google Gemini 2.5 Pro", value: "google/gemini-2.5-pro" },
|
|
39
|
+
{ label: "OpenAI GPT-5 mini", value: "openai/gpt-5-mini" },
|
|
40
|
+
{
|
|
41
|
+
label: "DeepSeek V3.1",
|
|
42
|
+
value: "deepseek/deepseek-chat-v3.1"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
[GEMINI_ENDPOINT]: [
|
|
46
|
+
{ label: "Gemini 2.5 Pro", value: "gemini-2.5-pro" },
|
|
47
|
+
{ label: "Gemini 2.5 Flash", value: "gemini-2.5-flash" },
|
|
48
|
+
{ label: "Gemini 2.5 Flash-Lite", value: "gemini-2.5-flash-lite" },
|
|
49
|
+
{ label: "Gemini 2.0 Flash", value: "gemini-2.0-flash" },
|
|
50
|
+
{ label: "Gemini 2.0 Flash-Lite", value: "gemini-2.0-flash-lite" }
|
|
51
|
+
],
|
|
52
|
+
[TOGETHER_ENDPOINT]: [
|
|
53
|
+
{ label: "DeepSeek V3.1", value: "deepseek-ai/DeepSeek-V3.1" },
|
|
54
|
+
{
|
|
55
|
+
label: "Qwen3 Coder 480B",
|
|
56
|
+
value: "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: "Llama 4 Maverick",
|
|
60
|
+
value: "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8"
|
|
61
|
+
},
|
|
62
|
+
{ label: "DeepSeek V3", value: "deepseek-ai/DeepSeek-V3" },
|
|
63
|
+
{
|
|
64
|
+
label: "Llama 3.3 70B Turbo",
|
|
65
|
+
value: "meta-llama/Llama-3.3-70B-Instruct-Turbo"
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
[GROQ_ENDPOINT]: [
|
|
69
|
+
{ label: "GPT-OSS 120B", value: "openai/gpt-oss-120b" },
|
|
70
|
+
{
|
|
71
|
+
label: "Llama 4 Scout",
|
|
72
|
+
value: "meta-llama/llama-4-scout-17b-16e-instruct"
|
|
73
|
+
},
|
|
74
|
+
{ label: "Qwen3 32B", value: "qwen/qwen3-32b" },
|
|
75
|
+
{ label: "Llama 3.3 70B", value: "llama-3.3-70b-versatile" },
|
|
76
|
+
{ label: "Llama 3.1 8B Instant", value: "llama-3.1-8b-instant" }
|
|
77
|
+
],
|
|
78
|
+
[XAI_ENDPOINT]: [
|
|
79
|
+
{ label: "Grok 4 Fast Reasoning", value: "grok-4-fast-reasoning" },
|
|
80
|
+
{ label: "Grok 4 Fast Non-Reasoning", value: "grok-4-fast-non-reasoning" },
|
|
81
|
+
{ label: "Grok Code Fast 1", value: "grok-code-fast-1" },
|
|
82
|
+
{ label: "Grok 4", value: "grok-4-0709" },
|
|
83
|
+
{ label: "Grok 3 Mini", value: "grok-3-mini" }
|
|
84
|
+
],
|
|
85
|
+
[QWEN_INTL_ENDPOINT]: [
|
|
86
|
+
{ label: "Qwen Plus Latest", value: "qwen-plus-latest" },
|
|
87
|
+
{ label: "Qwen Turbo Latest", value: "qwen-turbo-latest" },
|
|
88
|
+
{ label: "Qwen Max Latest", value: "qwen-max-latest" },
|
|
89
|
+
{ label: "Qwen3 Coder Plus", value: "qwen3-coder-plus" },
|
|
90
|
+
{ label: "Qwen3 Coder Next", value: "qwen3-coder-next" }
|
|
91
|
+
],
|
|
92
|
+
[QWEN_CN_ENDPOINT]: [
|
|
93
|
+
{ label: "Qwen Plus Latest", value: "qwen-plus-latest" },
|
|
94
|
+
{ label: "Qwen Turbo Latest", value: "qwen-turbo-latest" },
|
|
95
|
+
{ label: "Qwen Max Latest", value: "qwen-max-latest" },
|
|
96
|
+
{ label: "Qwen3 Coder Plus", value: "qwen3-coder-plus" },
|
|
97
|
+
{ label: "Qwen3 Coder Next", value: "qwen3-coder-next" }
|
|
98
|
+
],
|
|
99
|
+
[QWEN_US_ENDPOINT]: [
|
|
100
|
+
{ label: "Qwen Plus Latest", value: "qwen-plus-latest" },
|
|
101
|
+
{ label: "Qwen Turbo Latest", value: "qwen-turbo-latest" },
|
|
102
|
+
{ label: "Qwen Max Latest", value: "qwen-max-latest" },
|
|
103
|
+
{ label: "Qwen3 Coder Plus", value: "qwen3-coder-plus" },
|
|
104
|
+
{ label: "Qwen3 Coder Next", value: "qwen3-coder-next" }
|
|
105
|
+
],
|
|
106
|
+
[PERPLEXITY_ENDPOINT]: [
|
|
107
|
+
{ label: "Sonar Pro", value: "sonar-pro" },
|
|
108
|
+
{ label: "Sonar", value: "sonar" },
|
|
109
|
+
{ label: "Sonar Reasoning Pro", value: "sonar-reasoning-pro" },
|
|
110
|
+
{ label: "Sonar Deep Research", value: "sonar-deep-research" }
|
|
111
|
+
],
|
|
112
|
+
[KIMI_ENDPOINT]: [
|
|
113
|
+
{ label: "Kimi K2.5", value: "kimi-k2.5" },
|
|
114
|
+
{ label: "Kimi K2 Thinking", value: "kimi-k2-thinking" },
|
|
115
|
+
{ label: "Kimi K2 Turbo Preview", value: "kimi-k2-turbo-preview" },
|
|
116
|
+
{ label: "Kimi K2 0905 Preview", value: "kimi-k2-0905-preview" },
|
|
117
|
+
{ label: "Kimi K2", value: "kimi-k2" }
|
|
118
|
+
],
|
|
119
|
+
[PROXY_API_ENDPOINT]: [
|
|
120
|
+
{
|
|
121
|
+
label: "Anthropic Claude Sonnet 4",
|
|
122
|
+
value: "anthropic/claude-sonnet-4-20250514"
|
|
123
|
+
},
|
|
124
|
+
{ label: "Gemini 2.5 Flash", value: "gemini/gemini-2.5-flash" },
|
|
125
|
+
{ label: "OpenAI GPT-5 mini", value: "openai/gpt-5-mini" },
|
|
126
|
+
{ label: "OpenAI GPT-4o", value: "openai/gpt-4o" },
|
|
127
|
+
{
|
|
128
|
+
label: "OpenRouter DeepSeek Chat V3.1",
|
|
129
|
+
value: "openrouter/deepseek/deepseek-chat-v3.1"
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
};
|
|
133
|
+
var DEFAULT_AI_MODEL_BY_ENDPOINT = Object.fromEntries(
|
|
134
|
+
Object.entries(AI_MODEL_OPTIONS_BY_ENDPOINT).map(([endpoint, options]) => [
|
|
135
|
+
endpoint,
|
|
136
|
+
options[0]?.value ?? ""
|
|
137
|
+
])
|
|
138
|
+
);
|
|
139
|
+
var normalizeModel = (value) => value.trim();
|
|
140
|
+
var getAiModelOptionsForEndpoint = (endpoint) => AI_MODEL_OPTIONS_BY_ENDPOINT[endpoint] ?? [];
|
|
141
|
+
var getDefaultAiModelForEndpoint = (endpoint) => DEFAULT_AI_MODEL_BY_ENDPOINT[endpoint] ?? "";
|
|
142
|
+
var normalizeAiModel = (value, endpoint) => {
|
|
143
|
+
if (typeof value === "string") {
|
|
144
|
+
const trimmed = normalizeModel(value);
|
|
145
|
+
if (trimmed) {
|
|
146
|
+
return trimmed;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return getDefaultAiModelForEndpoint(endpoint);
|
|
150
|
+
};
|
|
151
|
+
var isKnownAiModelForEndpoint = (value, endpoint) => getAiModelOptionsForEndpoint(endpoint).some(
|
|
152
|
+
(option) => option.value === normalizeAiModel(value, endpoint)
|
|
153
|
+
);
|
|
154
|
+
var hasPresetAiModelsForEndpoint = (endpoint) => getAiModelOptionsForEndpoint(endpoint).length > 0 && endpoint !== AI_CUSTOM_ENDPOINT_VALUE;
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
AI_CUSTOM_MODEL_VALUE,
|
|
158
|
+
getAiModelOptionsForEndpoint,
|
|
159
|
+
getDefaultAiModelForEndpoint,
|
|
160
|
+
normalizeAiModel,
|
|
161
|
+
isKnownAiModelForEndpoint,
|
|
162
|
+
hasPresetAiModelsForEndpoint
|
|
163
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/mlDatasetFile.ts
|
|
2
|
+
import { once } from "events";
|
|
3
|
+
import { createReadStream, createWriteStream } from "fs";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
var DEFAULT_DIR = "data/ml/export";
|
|
7
|
+
var ML_DATASET_WRITE_BATCH_SIZE = 200;
|
|
8
|
+
var ML_CHUNK_FILE_RE = /^ml-dataset-(.+)-chunk-[^.]+\.jsonl$/;
|
|
9
|
+
var writerByPath = /* @__PURE__ */ new Map();
|
|
10
|
+
var toFileToken = (value) => value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "any";
|
|
11
|
+
var getMlChunkFilePath = (strategyName, chunkId, outDir = DEFAULT_DIR) => path.join(
|
|
12
|
+
outDir,
|
|
13
|
+
`ml-dataset-${toFileToken(strategyName)}-chunk-${toFileToken(chunkId)}.jsonl`
|
|
14
|
+
);
|
|
15
|
+
var appendMlDatasetRow = async (params) => {
|
|
16
|
+
const { strategyName, chunkId, row, outDir = DEFAULT_DIR } = params;
|
|
17
|
+
const filePath = getMlChunkFilePath(strategyName, chunkId, outDir);
|
|
18
|
+
let state = writerByPath.get(filePath);
|
|
19
|
+
if (!state) {
|
|
20
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
21
|
+
const stream = createWriteStream(filePath, {
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
flags: "a"
|
|
24
|
+
});
|
|
25
|
+
state = {
|
|
26
|
+
filePath,
|
|
27
|
+
stream,
|
|
28
|
+
buffer: [],
|
|
29
|
+
writeQueue: Promise.resolve(),
|
|
30
|
+
closed: false
|
|
31
|
+
};
|
|
32
|
+
writerByPath.set(filePath, state);
|
|
33
|
+
}
|
|
34
|
+
if (state.closed) {
|
|
35
|
+
throw new Error(`ML dataset writer is closed: ${filePath}`);
|
|
36
|
+
}
|
|
37
|
+
state.buffer.push(`${JSON.stringify(row)}
|
|
38
|
+
`);
|
|
39
|
+
if (state.buffer.length >= ML_DATASET_WRITE_BATCH_SIZE) {
|
|
40
|
+
await flushMlDatasetWriter(filePath);
|
|
41
|
+
}
|
|
42
|
+
return filePath;
|
|
43
|
+
};
|
|
44
|
+
var flushState = async (state) => {
|
|
45
|
+
if (state.closed || state.buffer.length === 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const chunk = state.buffer.join("");
|
|
49
|
+
state.buffer = [];
|
|
50
|
+
if (!state.stream.write(chunk)) {
|
|
51
|
+
await once(state.stream, "drain");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var flushMlDatasetWriter = async (filePath) => {
|
|
55
|
+
const state = writerByPath.get(filePath);
|
|
56
|
+
if (!state || state.closed) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
state.writeQueue = state.writeQueue.then(() => flushState(state));
|
|
60
|
+
await state.writeQueue;
|
|
61
|
+
};
|
|
62
|
+
var closeState = async (state) => {
|
|
63
|
+
if (state.closed) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
await flushState(state);
|
|
67
|
+
state.closed = true;
|
|
68
|
+
state.stream.end();
|
|
69
|
+
await Promise.all([
|
|
70
|
+
once(state.stream, "finish"),
|
|
71
|
+
once(state.stream, "close")
|
|
72
|
+
]);
|
|
73
|
+
};
|
|
74
|
+
var closeMlDatasetWriter = async (filePath) => {
|
|
75
|
+
const state = writerByPath.get(filePath);
|
|
76
|
+
if (!state) return;
|
|
77
|
+
state.writeQueue = state.writeQueue.then(() => closeState(state));
|
|
78
|
+
await state.writeQueue;
|
|
79
|
+
writerByPath.delete(filePath);
|
|
80
|
+
};
|
|
81
|
+
var closeAllMlDatasetWriters = async () => {
|
|
82
|
+
const filePaths = [...writerByPath.keys()];
|
|
83
|
+
for (const filePath of filePaths) {
|
|
84
|
+
await closeMlDatasetWriter(filePath);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var listMlChunkFiles = async (params) => {
|
|
88
|
+
const { strategyName, outDir = DEFAULT_DIR } = params;
|
|
89
|
+
const prefix = `ml-dataset-${toFileToken(strategyName)}-chunk-`;
|
|
90
|
+
let entries = [];
|
|
91
|
+
try {
|
|
92
|
+
entries = await fs.readdir(outDir);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).map((name) => path.join(outDir, name)).sort();
|
|
97
|
+
};
|
|
98
|
+
var listMlChunkStrategies = async (params) => {
|
|
99
|
+
const outDir = params?.outDir ?? DEFAULT_DIR;
|
|
100
|
+
let entries = [];
|
|
101
|
+
try {
|
|
102
|
+
entries = await fs.readdir(outDir);
|
|
103
|
+
} catch {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
return [
|
|
107
|
+
...new Set(
|
|
108
|
+
entries.map((name) => name.match(ML_CHUNK_FILE_RE)?.[1] || "").filter(Boolean)
|
|
109
|
+
)
|
|
110
|
+
].sort();
|
|
111
|
+
};
|
|
112
|
+
var mergeJsonlFiles = async (params) => {
|
|
113
|
+
const { filePaths, outPath } = params;
|
|
114
|
+
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
115
|
+
const stream = createWriteStream(outPath, { encoding: "utf8" });
|
|
116
|
+
const done = Promise.all([once(stream, "finish"), once(stream, "close")]);
|
|
117
|
+
try {
|
|
118
|
+
for (const filePath of filePaths) {
|
|
119
|
+
const reader = createReadStream(filePath, { encoding: "utf8" });
|
|
120
|
+
for await (const chunk of reader) {
|
|
121
|
+
if (!stream.write(chunk)) {
|
|
122
|
+
await once(stream, "drain");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} finally {
|
|
127
|
+
stream.end();
|
|
128
|
+
await done;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
toFileToken,
|
|
134
|
+
getMlChunkFilePath,
|
|
135
|
+
appendMlDatasetRow,
|
|
136
|
+
flushMlDatasetWriter,
|
|
137
|
+
closeMlDatasetWriter,
|
|
138
|
+
closeAllMlDatasetWriters,
|
|
139
|
+
listMlChunkFiles,
|
|
140
|
+
listMlChunkStrategies,
|
|
141
|
+
mergeJsonlFiles
|
|
142
|
+
};
|