@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.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 { randomBytes, createHash, randomUUID } from 'crypto';
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 = this.generateToken(entry.graphType);
1233
- const record = {
1234
- ...entry,
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 with Lua script for atomicity
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
- try {
1280
- const record = JSON.parse(data);
1281
- if (record.status !== "pending") {
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 with Lua script for atomicity
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
- try {
1335
- const record = JSON.parse(data);
1336
- record.status = "failed";
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 with Lua script for atomicity
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
- try {
1381
- const record = JSON.parse(data);
1382
- record.status = "pending";
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
- const versionRegex = /^\d+\.\d+\.\d+$/;
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 parts = token.split("_");
3600
- if (parts.length < 4 || parts[0] !== "cb") {
3655
+ const result = parseCallbackToken(token);
3656
+ if (!result) {
3601
3657
  throw new Error(`Invalid callback token format: ${token}`);
3602
3658
  }
3603
- const graphName = parts[1];
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 parts = token.split("_");
3613
- if (parts.length < 4) {
3614
- return {};
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
- const sorted = toolsConfig.map((t) => `${t.toolName}:${t.enabled}:${JSON.stringify(t.config || {})}`).sort().join("|");
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
- const parts = [
6320
+ return generateModelCacheKey(
6263
6321
  config.modelId,
6264
- config.temperature ?? "default",
6265
- config.maxTokens ?? "default"
6266
- ];
6267
- if (config.toolsConfig && config.toolsConfig.length > 0) {
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 = modelName.includes("gpt-5") || modelName.includes("gpt-o1") || modelName.includes("gpt-o2") || modelName.includes("gpt-o3") || modelName.includes("gpt-o4") || // Add other patterns as new models are released
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
  }