vidspotai-shared 1.0.71 → 1.0.73

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.
@@ -1 +1 @@
1
- {"version":3,"file":"llmCallerGateway.d.ts","sourceRoot":"","sources":["../../../src/services/agent/llmCallerGateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2CD,qBAAa,gBAAiB,YAAW,SAAS;IAMpC,OAAO,CAAC,QAAQ,CAAC,GAAG;IALhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAER,GAAG,EAAE,aAAa;IAOzC,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAkC7C,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACrC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAgEjC,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACrC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,GACxB;QACD,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;KACxC;YAqBc,uBAAuB;IAoItC,OAAO,CAAC,OAAO;YAID,aAAa;YAqBb,SAAS;IAcvB,OAAO,CAAC,KAAK;IAQb;;;;;OAKG;YACW,aAAa;YAIb,SAAS;YA+BT,QAAQ;CA6BvB"}
1
+ {"version":3,"file":"llmCallerGateway.d.ts","sourceRoot":"","sources":["../../../src/services/agent/llmCallerGateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2CD,qBAAa,gBAAiB,YAAW,SAAS;IAMpC,OAAO,CAAC,QAAQ,CAAC,GAAG;IALhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAER,GAAG,EAAE,aAAa;IAOzC,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAkC7C,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACrC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAgEjC,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACrC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,GACxB;QACD,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;KACxC;YAqBc,uBAAuB;IA4ItC,OAAO,CAAC,OAAO;YAID,aAAa;YAqBb,SAAS;IAcvB,OAAO,CAAC,KAAK;IAQb;;;;;OAKG;YACW,aAAa;YAIb,SAAS;YA+BT,QAAQ;CA6BvB"}
@@ -228,6 +228,14 @@ class GatewayLlmCaller {
228
228
  }
229
229
  }
230
230
  }
231
+ // Post-stream failures (bad JSON / schema mismatch) reject the RESULT
232
+ // promise but must NOT throw out of this generator. The tokens already
233
+ // streamed fine; throwing here would propagate into the caller's
234
+ // `for await (...stream.tokens)` loop and abort it before the caller can
235
+ // reach `await stream.result` — which is exactly where the planner's
236
+ // non-streaming retry fallback lives (Planner.planStream). Returning
237
+ // cleanly lets the token loop complete, then the rejected result drives
238
+ // the retry. See plan.controller.ts:planProjectStream.
231
239
  let parsed;
232
240
  try {
233
241
  parsed = JSON.parse(accumulated);
@@ -235,13 +243,13 @@ class GatewayLlmCaller {
235
243
  catch {
236
244
  const err = new Error(`GatewayLlmCaller.structuredStream: model returned non-JSON for ${req.schemaName}: ${accumulated.slice(0, 200)}`);
237
245
  rejectResult(err);
238
- throw err;
246
+ return;
239
247
  }
240
248
  const validated = req.schema.safeParse(parsed);
241
249
  if (!validated.success) {
242
250
  const err = new Error(`GatewayLlmCaller.structuredStream: schema validation failed for ${req.schemaName}: ${validated.error.message}`);
243
251
  rejectResult(err);
244
- throw err;
252
+ return;
245
253
  }
246
254
  resolveResult({
247
255
  data: validated.data,
@@ -10,11 +10,36 @@ declare class BullMQService {
10
10
  private queues;
11
11
  private workers;
12
12
  private redisOptions;
13
+ private connection?;
13
14
  private concurrency;
14
15
  private initialized;
15
16
  private shuttingDown;
16
17
  private constructor();
17
18
  static getInstance(options: BullMQServiceOptions): BullMQService;
19
+ /**
20
+ * Lazily create and return the single shared ioredis connection for this
21
+ * process. ALL Queues and Workers must use this instance rather than a plain
22
+ * options object.
23
+ *
24
+ * WHY THIS EXISTS — Redis client exhaustion:
25
+ * Passing a plain RedisOptions object to `new Queue`/`new Worker` makes
26
+ * BullMQ open a BRAND-NEW connection for every component, so total
27
+ * connections scale as (instances × queues). With 7 queues across N warm
28
+ * Cloud Run instances + the worker fleet, this blew past the Redis
29
+ * `maxclients` cap → "ERR max number of clients reached".
30
+ *
31
+ * Passing a shared IORedis instance instead:
32
+ * - Queues reuse this one connection (0 extra per queue).
33
+ * - Workers reuse it for non-blocking commands and BullMQ internally
34
+ * `.duplicate()`s it for each worker's MANDATORY blocking client
35
+ * (one blocking conn per worker is unavoidable by BullMQ design).
36
+ * Net: a producer (Functions) holds exactly 1 connection regardless of how
37
+ * many queues it enqueues to; a worker host holds 1 + (1 per worker).
38
+ *
39
+ * `maxRetriesPerRequest: null` (in sharedRedisOptions) is REQUIRED by BullMQ
40
+ * for the connection it blocks on.
41
+ */
42
+ private getConnection;
18
43
  /** Initialize BullMQ service (like your initRedis) */
19
44
  init(): Promise<void>;
20
45
  /** Add a job */
@@ -1 +1 @@
1
- {"version":3,"file":"bullmq.service.d.ts","sourceRoot":"","sources":["../../src/services/bullmq.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIvC,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE1C,UAAU,oBAAoB;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAgB;IACvC,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO;WAQO,WAAW,CAAC,OAAO,EAAE,oBAAoB;IAOvD,sDAAsD;IACzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBlC,gBAAgB;IACH,MAAM,CACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAUpC,YAAY,CACjB,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,eAAe,CAAC,EAAE,MAAM,CACtB,MAAM,EACN;QACE,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CACF;CA+FJ;AASD,eAAO,MAAM,MAAM,eAGjB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC"}
1
+ {"version":3,"file":"bullmq.service.d.ts","sourceRoot":"","sources":["../../src/services/bullmq.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAgB,EAAwB,YAAY,EAAE,MAAM,SAAS,CAAC;AAItE,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE1C,UAAU,oBAAoB;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAgB;IACvC,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAgB;IAIpC,OAAO,CAAC,UAAU,CAAC,CAAc;IACjC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO;WAQO,WAAW,CAAC,OAAO,EAAE,oBAAoB;IAOvD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,aAAa;IAOrB,sDAAsD;IACzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBlC,gBAAgB;IACH,MAAM,CACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAUpC,YAAY,CACjB,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,eAAe,CAAC,EAAE,MAAM,CACtB,MAAM,EACN;QACE,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CACF;CAwGJ;AASD,eAAO,MAAM,MAAM,eAGjB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC"}
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.bullmq = void 0;
4
7
  // libs/bullmqService.ts
5
8
  const bullmq_1 = require("bullmq");
9
+ const ioredis_1 = __importDefault(require("ioredis"));
6
10
  const logger_1 = require("../utils/logger");
7
11
  const redisOptions_1 = require("./redisOptions");
8
12
  class BullMQService {
@@ -20,19 +24,44 @@ class BullMQService {
20
24
  }
21
25
  return BullMQService.instance;
22
26
  }
27
+ /**
28
+ * Lazily create and return the single shared ioredis connection for this
29
+ * process. ALL Queues and Workers must use this instance rather than a plain
30
+ * options object.
31
+ *
32
+ * WHY THIS EXISTS — Redis client exhaustion:
33
+ * Passing a plain RedisOptions object to `new Queue`/`new Worker` makes
34
+ * BullMQ open a BRAND-NEW connection for every component, so total
35
+ * connections scale as (instances × queues). With 7 queues across N warm
36
+ * Cloud Run instances + the worker fleet, this blew past the Redis
37
+ * `maxclients` cap → "ERR max number of clients reached".
38
+ *
39
+ * Passing a shared IORedis instance instead:
40
+ * - Queues reuse this one connection (0 extra per queue).
41
+ * - Workers reuse it for non-blocking commands and BullMQ internally
42
+ * `.duplicate()`s it for each worker's MANDATORY blocking client
43
+ * (one blocking conn per worker is unavoidable by BullMQ design).
44
+ * Net: a producer (Functions) holds exactly 1 connection regardless of how
45
+ * many queues it enqueues to; a worker host holds 1 + (1 per worker).
46
+ *
47
+ * `maxRetriesPerRequest: null` (in sharedRedisOptions) is REQUIRED by BullMQ
48
+ * for the connection it blocks on.
49
+ */
50
+ getConnection() {
51
+ if (this.connection)
52
+ return this.connection;
53
+ this.connection = new ioredis_1.default(this.redisOptions);
54
+ (0, redisOptions_1.attachRedisListeners)(this.connection, "BullMQ");
55
+ return this.connection;
56
+ }
23
57
  /** Initialize BullMQ service (like your initRedis) */
24
58
  async init() {
25
59
  if (this.initialized)
26
60
  return;
27
61
  try {
28
- // Create a temporary test queue
29
- const testQueue = new bullmq_1.Queue("__init__", {
30
- connection: this.redisOptions,
31
- });
32
- // Ping Redis via ioredis client
33
- const testQueueClient = await testQueue.client;
34
- testQueueClient.ping();
35
- await testQueue.close();
62
+ // Ping the shared connection directly — no throwaway Queue (which would
63
+ // open, then close, an extra connection on every cold start).
64
+ await this.getConnection().ping();
36
65
  this.initialized = true;
37
66
  logger_1.logger.info("BullMQService initialized and connected to Redis");
38
67
  }
@@ -48,7 +77,7 @@ class BullMQService {
48
77
  if (!this.initialized)
49
78
  await this.init();
50
79
  const queue = this.queues.get(queueName) ??
51
- new bullmq_1.Queue(queueName, { connection: this.redisOptions });
80
+ new bullmq_1.Queue(queueName, { connection: this.getConnection() });
52
81
  this.queues.set(queueName, queue);
53
82
  await queue.add(opts?.jobId || "default", data, { delay: opts?.delay });
54
83
  }
@@ -58,11 +87,11 @@ class BullMQService {
58
87
  await this.init();
59
88
  for (const queueName of queueNames) {
60
89
  const queue = this.queues.get(queueName) ??
61
- new bullmq_1.Queue(queueName, { connection: this.redisOptions });
90
+ new bullmq_1.Queue(queueName, { connection: this.getConnection() });
62
91
  this.queues.set(queueName, queue);
63
92
  const overrides = perQueueOptions?.[queueName] ?? {};
64
93
  const worker = new bullmq_1.Worker(queueName, async (job) => processor(job), {
65
- connection: this.redisOptions,
94
+ connection: this.getConnection(),
66
95
  concurrency: overrides.concurrency ?? this.concurrency,
67
96
  // N5 (agent refactor Stage 2). Explicit lock semantics:
68
97
  // - lockDuration: 60s default. BullMQ auto-renews while the
@@ -122,6 +151,15 @@ class BullMQService {
122
151
  await worker.close();
123
152
  logger_1.logger.info(`Worker for queue closed`, { queueName: worker.name });
124
153
  }
154
+ // Close queues and the shared connection so blocking-client dups are
155
+ // released cleanly (prevents lingering clients on the Redis side after
156
+ // a rolling deploy / SIGTERM on Railway).
157
+ for (const queue of this.queues.values()) {
158
+ await queue.close();
159
+ }
160
+ if (this.connection) {
161
+ await this.connection.quit();
162
+ }
125
163
  logger_1.logger.info("All workers closed. Exiting process.");
126
164
  process.exit(0);
127
165
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidspotai-shared",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "exports": {