@flutchai/flutch-sdk 0.1.24 → 0.1.25
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 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +133 -89
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -635,7 +635,6 @@ declare class CallbackStore {
|
|
|
635
635
|
private readonly redis;
|
|
636
636
|
private readonly isProduction;
|
|
637
637
|
constructor(redis: Redis);
|
|
638
|
-
private generateToken;
|
|
639
638
|
issue(entry: CallbackEntry): Promise<string>;
|
|
640
639
|
getAndLock(token: string): Promise<CallbackRecord | null>;
|
|
641
640
|
private getAndLockAtomic;
|
package/dist/index.d.ts
CHANGED
|
@@ -635,7 +635,6 @@ declare class CallbackStore {
|
|
|
635
635
|
private readonly redis;
|
|
636
636
|
private readonly isProduction;
|
|
637
637
|
constructor(redis: Redis);
|
|
638
|
-
private generateToken;
|
|
639
638
|
issue(entry: CallbackEntry): Promise<string>;
|
|
640
639
|
getAndLock(token: string): Promise<CallbackRecord | null>;
|
|
641
640
|
private getAndLockAtomic;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ApiOperation, ApiResponse, ApiTags, ApiParam, DocumentBuilder, SwaggerM
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path2 from 'path';
|
|
6
6
|
import * as os from 'os';
|
|
7
|
-
import {
|
|
7
|
+
import { createHash, randomUUID, randomBytes } from 'crypto';
|
|
8
8
|
import { Registry, collectDefaultMetrics, Counter, Histogram, Gauge } from 'prom-client';
|
|
9
9
|
import { NestFactory, MetadataScanner, ModuleRef, DiscoveryModule } from '@nestjs/core';
|
|
10
10
|
import * as net from 'net';
|
|
@@ -1216,28 +1216,54 @@ var init_versioned_graph_service = __esm({
|
|
|
1216
1216
|
], VersionedGraphService);
|
|
1217
1217
|
}
|
|
1218
1218
|
});
|
|
1219
|
+
function generateCallbackToken(graphType) {
|
|
1220
|
+
return `cb::${graphType}::${randomBytes(8).toString("base64url")}`;
|
|
1221
|
+
}
|
|
1222
|
+
function createCallbackRecord(entry, token, now) {
|
|
1223
|
+
return {
|
|
1224
|
+
...entry,
|
|
1225
|
+
token,
|
|
1226
|
+
status: "pending",
|
|
1227
|
+
createdAt: now,
|
|
1228
|
+
retries: 0
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
function resolveCallbackTTL(entry) {
|
|
1232
|
+
return entry.metadata?.ttlSec ?? 600;
|
|
1233
|
+
}
|
|
1234
|
+
function parseCallbackRecord(data) {
|
|
1235
|
+
try {
|
|
1236
|
+
return JSON.parse(data);
|
|
1237
|
+
} catch {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
function markAsFailed(record, error) {
|
|
1242
|
+
return {
|
|
1243
|
+
...record,
|
|
1244
|
+
status: "failed",
|
|
1245
|
+
retries: (record.retries || 0) + 1,
|
|
1246
|
+
lastError: error
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
function markAsPending(record) {
|
|
1250
|
+
return { ...record, status: "pending" };
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/callbacks/callback-store.ts
|
|
1219
1254
|
var CallbackStore = class {
|
|
1220
1255
|
constructor(redis) {
|
|
1221
1256
|
this.redis = redis;
|
|
1222
1257
|
this.isProduction = process.env.NODE_ENV === "production";
|
|
1223
1258
|
}
|
|
1224
1259
|
isProduction;
|
|
1225
|
-
generateToken(graphType) {
|
|
1226
|
-
return `cb::${graphType}::${randomBytes(8).toString("base64url")}`;
|
|
1227
|
-
}
|
|
1228
1260
|
/**
|
|
1229
1261
|
* Issues a new callback token and persists its payload.
|
|
1230
1262
|
*/
|
|
1231
1263
|
async issue(entry) {
|
|
1232
|
-
const token =
|
|
1233
|
-
const record =
|
|
1234
|
-
|
|
1235
|
-
token,
|
|
1236
|
-
status: "pending",
|
|
1237
|
-
createdAt: Date.now(),
|
|
1238
|
-
retries: 0
|
|
1239
|
-
};
|
|
1240
|
-
const ttl = entry.metadata?.ttlSec ?? 600;
|
|
1264
|
+
const token = generateCallbackToken(entry.graphType);
|
|
1265
|
+
const record = createCallbackRecord(entry, token, Date.now());
|
|
1266
|
+
const ttl = resolveCallbackTTL(entry);
|
|
1241
1267
|
await this.redis.setex(`callback:${token}`, ttl, JSON.stringify(record));
|
|
1242
1268
|
return token;
|
|
1243
1269
|
}
|
|
@@ -1252,7 +1278,9 @@ var CallbackStore = class {
|
|
|
1252
1278
|
}
|
|
1253
1279
|
}
|
|
1254
1280
|
/**
|
|
1255
|
-
* Production version
|
|
1281
|
+
* Production version: uses Redis Lua scripting for atomic get-and-lock.
|
|
1282
|
+
* NOTE: redis.eval() here executes a Lua script on the Redis server,
|
|
1283
|
+
* NOT JavaScript eval(). This is the standard ioredis API for Lua scripting.
|
|
1256
1284
|
*/
|
|
1257
1285
|
async getAndLockAtomic(token) {
|
|
1258
1286
|
const script = `
|
|
@@ -1276,18 +1304,17 @@ var CallbackStore = class {
|
|
|
1276
1304
|
if (!data) {
|
|
1277
1305
|
return null;
|
|
1278
1306
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
return null;
|
|
1283
|
-
}
|
|
1284
|
-
record.status = "processing";
|
|
1285
|
-
await this.redis.set(key, JSON.stringify(record));
|
|
1286
|
-
return record;
|
|
1287
|
-
} catch (error) {
|
|
1288
|
-
console.error("Failed to parse callback record:", error);
|
|
1307
|
+
const record = parseCallbackRecord(data);
|
|
1308
|
+
if (!record) {
|
|
1309
|
+
console.error("Failed to parse callback record");
|
|
1289
1310
|
return null;
|
|
1290
1311
|
}
|
|
1312
|
+
if (record.status !== "pending") {
|
|
1313
|
+
return null;
|
|
1314
|
+
}
|
|
1315
|
+
record.status = "processing";
|
|
1316
|
+
await this.redis.set(key, JSON.stringify(record));
|
|
1317
|
+
return record;
|
|
1291
1318
|
}
|
|
1292
1319
|
/**
|
|
1293
1320
|
* Finalizes callback processing by removing token.
|
|
@@ -1306,7 +1333,8 @@ var CallbackStore = class {
|
|
|
1306
1333
|
}
|
|
1307
1334
|
}
|
|
1308
1335
|
/**
|
|
1309
|
-
* Production version
|
|
1336
|
+
* Production version: uses Redis Lua scripting for atomic fail.
|
|
1337
|
+
* NOTE: redis.eval() here executes a Lua script on the Redis server.
|
|
1310
1338
|
*/
|
|
1311
1339
|
async failAtomic(token, error) {
|
|
1312
1340
|
const script = `
|
|
@@ -1331,17 +1359,14 @@ var CallbackStore = class {
|
|
|
1331
1359
|
if (!data) {
|
|
1332
1360
|
return null;
|
|
1333
1361
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
record.retries = (record.retries || 0) + 1;
|
|
1338
|
-
record.lastError = error;
|
|
1339
|
-
await this.redis.set(key, JSON.stringify(record));
|
|
1340
|
-
return record;
|
|
1341
|
-
} catch (parseError) {
|
|
1342
|
-
console.error("Failed to parse callback record:", parseError);
|
|
1362
|
+
const record = parseCallbackRecord(data);
|
|
1363
|
+
if (!record) {
|
|
1364
|
+
console.error("Failed to parse callback record");
|
|
1343
1365
|
return null;
|
|
1344
1366
|
}
|
|
1367
|
+
const updated = markAsFailed(record, error);
|
|
1368
|
+
await this.redis.set(key, JSON.stringify(updated));
|
|
1369
|
+
return updated;
|
|
1345
1370
|
}
|
|
1346
1371
|
/**
|
|
1347
1372
|
* Reset callback status to pending for retry.
|
|
@@ -1354,7 +1379,8 @@ var CallbackStore = class {
|
|
|
1354
1379
|
}
|
|
1355
1380
|
}
|
|
1356
1381
|
/**
|
|
1357
|
-
* Production version
|
|
1382
|
+
* Production version: uses Redis Lua scripting for atomic retry.
|
|
1383
|
+
* NOTE: redis.eval() here executes a Lua script on the Redis server.
|
|
1358
1384
|
*/
|
|
1359
1385
|
async retryAtomic(token) {
|
|
1360
1386
|
const script = `
|
|
@@ -1377,15 +1403,14 @@ var CallbackStore = class {
|
|
|
1377
1403
|
if (!data) {
|
|
1378
1404
|
return null;
|
|
1379
1405
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
await this.redis.set(key, JSON.stringify(record));
|
|
1384
|
-
return record;
|
|
1385
|
-
} catch (parseError) {
|
|
1386
|
-
console.error("Failed to parse callback record:", parseError);
|
|
1406
|
+
const record = parseCallbackRecord(data);
|
|
1407
|
+
if (!record) {
|
|
1408
|
+
console.error("Failed to parse callback record");
|
|
1387
1409
|
return null;
|
|
1388
1410
|
}
|
|
1411
|
+
const updated = markAsPending(record);
|
|
1412
|
+
await this.redis.set(key, JSON.stringify(updated));
|
|
1413
|
+
return updated;
|
|
1389
1414
|
}
|
|
1390
1415
|
};
|
|
1391
1416
|
|
|
@@ -3037,6 +3062,38 @@ CallbackController = __decorateClass([
|
|
|
3037
3062
|
|
|
3038
3063
|
// src/graph/abstract-graph.builder.ts
|
|
3039
3064
|
init_agent_ui();
|
|
3065
|
+
|
|
3066
|
+
// src/graph/graph.logic.ts
|
|
3067
|
+
function isValidSemver(version) {
|
|
3068
|
+
return /^\d+\.\d+\.\d+$/.test(version);
|
|
3069
|
+
}
|
|
3070
|
+
function parseCallbackToken(token) {
|
|
3071
|
+
const parts = token.split("_");
|
|
3072
|
+
if (parts.length < 4 || parts[0] !== "cb") {
|
|
3073
|
+
return null;
|
|
3074
|
+
}
|
|
3075
|
+
const graphName = parts[1];
|
|
3076
|
+
const handler = parts[2];
|
|
3077
|
+
const graphType = `${graphName}::1.0.0`;
|
|
3078
|
+
return { graphType, handler };
|
|
3079
|
+
}
|
|
3080
|
+
function decodeCallbackParams(token) {
|
|
3081
|
+
const parts = token.split("_");
|
|
3082
|
+
if (parts.length < 4) {
|
|
3083
|
+
return {};
|
|
3084
|
+
}
|
|
3085
|
+
try {
|
|
3086
|
+
const encodedParams = parts.slice(3).join("_");
|
|
3087
|
+
const decodedParams = Buffer.from(encodedParams, "base64url").toString(
|
|
3088
|
+
"utf8"
|
|
3089
|
+
);
|
|
3090
|
+
return JSON.parse(decodedParams);
|
|
3091
|
+
} catch {
|
|
3092
|
+
return {};
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
// src/graph/abstract-graph.builder.ts
|
|
3040
3097
|
var _AbstractGraphBuilder = class _AbstractGraphBuilder {
|
|
3041
3098
|
logger = new Logger(_AbstractGraphBuilder.name);
|
|
3042
3099
|
callbackRegistry;
|
|
@@ -3245,8 +3302,7 @@ var _AbstractGraphBuilder = class _AbstractGraphBuilder {
|
|
|
3245
3302
|
* Version validation
|
|
3246
3303
|
*/
|
|
3247
3304
|
validateVersion() {
|
|
3248
|
-
|
|
3249
|
-
if (!versionRegex.test(this.version)) {
|
|
3305
|
+
if (!isValidSemver(this.version)) {
|
|
3250
3306
|
throw new Error(
|
|
3251
3307
|
`Invalid version format: ${this.version}. Expected format: X.Y.Z`
|
|
3252
3308
|
);
|
|
@@ -3596,36 +3652,21 @@ var UniversalGraphService = class {
|
|
|
3596
3652
|
* Expected format: cb_{graphName}_{handler}_{encodedParams}
|
|
3597
3653
|
*/
|
|
3598
3654
|
parseCallbackToken(token) {
|
|
3599
|
-
const
|
|
3600
|
-
if (
|
|
3655
|
+
const result = parseCallbackToken(token);
|
|
3656
|
+
if (!result) {
|
|
3601
3657
|
throw new Error(`Invalid callback token format: ${token}`);
|
|
3602
3658
|
}
|
|
3603
|
-
|
|
3604
|
-
const handler = parts[2];
|
|
3605
|
-
const graphType = `${graphName}::1.0.0`;
|
|
3606
|
-
return { graphType, handler };
|
|
3659
|
+
return result;
|
|
3607
3660
|
}
|
|
3608
3661
|
/**
|
|
3609
3662
|
* Extract parameters from callback token
|
|
3610
3663
|
*/
|
|
3611
3664
|
parseCallbackParams(token) {
|
|
3612
|
-
const
|
|
3613
|
-
if (
|
|
3614
|
-
|
|
3615
|
-
}
|
|
3616
|
-
try {
|
|
3617
|
-
const encodedParams = parts.slice(3).join("_");
|
|
3618
|
-
const decodedParams = Buffer.from(encodedParams, "base64url").toString(
|
|
3619
|
-
"utf8"
|
|
3620
|
-
);
|
|
3621
|
-
return JSON.parse(decodedParams);
|
|
3622
|
-
} catch (error) {
|
|
3623
|
-
this.logger.warn(
|
|
3624
|
-
`Failed to parse callback params from token: ${token}`,
|
|
3625
|
-
error
|
|
3626
|
-
);
|
|
3627
|
-
return {};
|
|
3665
|
+
const result = decodeCallbackParams(token);
|
|
3666
|
+
if (Object.keys(result).length === 0 && token.split("_").length >= 4) {
|
|
3667
|
+
this.logger.warn(`Failed to parse callback params from token: ${token}`);
|
|
3628
3668
|
}
|
|
3669
|
+
return result;
|
|
3629
3670
|
}
|
|
3630
3671
|
/**
|
|
3631
3672
|
* Call a graph endpoint
|
|
@@ -5955,6 +5996,24 @@ var ChatFeature = /* @__PURE__ */ ((ChatFeature2) => {
|
|
|
5955
5996
|
ChatFeature2["JSON_MODE"] = "json_mode";
|
|
5956
5997
|
return ChatFeature2;
|
|
5957
5998
|
})(ChatFeature || {});
|
|
5999
|
+
function isReasoningModel(modelName) {
|
|
6000
|
+
return modelName.includes("gpt-5") || modelName.includes("gpt-o1") || modelName.includes("gpt-o2") || modelName.includes("gpt-o3") || modelName.includes("gpt-o4") || /^gpt-(5|6|7|8|9)/.test(modelName) || /^gpt-o[1-4]/.test(modelName);
|
|
6001
|
+
}
|
|
6002
|
+
function hashToolsConfig(toolsConfig) {
|
|
6003
|
+
const sorted = toolsConfig.map((t) => `${t.toolName}:${t.enabled}:${JSON.stringify(t.config || {})}`).sort().join("|");
|
|
6004
|
+
return createHash("md5").update(sorted).digest("hex").slice(0, 16);
|
|
6005
|
+
}
|
|
6006
|
+
function generateModelCacheKey(modelId, temperature, maxTokens, toolsConfig) {
|
|
6007
|
+
const parts = [
|
|
6008
|
+
modelId,
|
|
6009
|
+
temperature ?? "default",
|
|
6010
|
+
maxTokens ?? "default"
|
|
6011
|
+
];
|
|
6012
|
+
if (toolsConfig && toolsConfig.length > 0) {
|
|
6013
|
+
parts.push(hashToolsConfig(toolsConfig));
|
|
6014
|
+
}
|
|
6015
|
+
return parts.join(":");
|
|
6016
|
+
}
|
|
5958
6017
|
var VoyageAIRerank = class extends BaseDocumentCompressor {
|
|
5959
6018
|
apiKey;
|
|
5960
6019
|
model;
|
|
@@ -6250,8 +6309,7 @@ var ModelInitializer = class _ModelInitializer {
|
|
|
6250
6309
|
* Uses MD5 hash to create short, unique identifier
|
|
6251
6310
|
*/
|
|
6252
6311
|
hashToolsConfig(toolsConfig) {
|
|
6253
|
-
|
|
6254
|
-
return createHash("md5").update(sorted).digest("hex").slice(0, 16);
|
|
6312
|
+
return hashToolsConfig(toolsConfig);
|
|
6255
6313
|
}
|
|
6256
6314
|
/**
|
|
6257
6315
|
* Generate cache key from ModelByIdConfig
|
|
@@ -6259,16 +6317,12 @@ var ModelInitializer = class _ModelInitializer {
|
|
|
6259
6317
|
* Example: "model123:0.7:4096" or "model123:0.7:4096:a1b2c3d4e5f6g7h8"
|
|
6260
6318
|
*/
|
|
6261
6319
|
generateModelCacheKey(config) {
|
|
6262
|
-
|
|
6320
|
+
return generateModelCacheKey(
|
|
6263
6321
|
config.modelId,
|
|
6264
|
-
config.temperature
|
|
6265
|
-
config.maxTokens
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
const toolsHash = this.hashToolsConfig(config.toolsConfig);
|
|
6269
|
-
parts.push(toolsHash);
|
|
6270
|
-
}
|
|
6271
|
-
return parts.join(":");
|
|
6322
|
+
config.temperature,
|
|
6323
|
+
config.maxTokens,
|
|
6324
|
+
config.toolsConfig
|
|
6325
|
+
);
|
|
6272
6326
|
}
|
|
6273
6327
|
/**
|
|
6274
6328
|
* TEMPORARY SOLUTION for compatibility with new OpenAI models
|
|
@@ -6285,20 +6339,10 @@ var ModelInitializer = class _ModelInitializer {
|
|
|
6285
6339
|
* @returns true if model requires maxCompletionTokens and temperature = 1
|
|
6286
6340
|
*/
|
|
6287
6341
|
requiresMaxCompletionTokens(modelName) {
|
|
6288
|
-
const requiresNew =
|
|
6289
|
-
/^gpt-(5|6|7|8|9)/.test(modelName) || /^gpt-o[1-4]/.test(modelName);
|
|
6342
|
+
const requiresNew = isReasoningModel(modelName);
|
|
6290
6343
|
this.logger.debug(`Checking token parameter for model "${modelName}"`, {
|
|
6291
6344
|
modelName,
|
|
6292
|
-
requiresMaxCompletionTokens: requiresNew
|
|
6293
|
-
checks: {
|
|
6294
|
-
includesGpt5: modelName.includes("gpt-5"),
|
|
6295
|
-
includesO1: modelName.includes("gpt-o1"),
|
|
6296
|
-
includesO2: modelName.includes("gpt-o2"),
|
|
6297
|
-
includesO3: modelName.includes("gpt-o3"),
|
|
6298
|
-
includesO4: modelName.includes("gpt-o4"),
|
|
6299
|
-
regexGpt5Plus: /^gpt-(5|6|7|8|9)/.test(modelName),
|
|
6300
|
-
regexO1to4: /^gpt-o[1-4]/.test(modelName)
|
|
6301
|
-
}
|
|
6345
|
+
requiresMaxCompletionTokens: requiresNew
|
|
6302
6346
|
});
|
|
6303
6347
|
return requiresNew;
|
|
6304
6348
|
}
|