lightnode-sdk 0.4.3 → 0.4.4

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.
Files changed (2) hide show
  1. package/dist/inference.js +21 -10
  2. package/package.json +1 -1
package/dist/inference.js CHANGED
@@ -326,16 +326,31 @@ async function runOneAttempt(args, attempt) {
326
326
  throw new Error("JobSubmitted log missing in submitJob receipt");
327
327
  const jobId = topicAsUint(jobLog.topics[1]);
328
328
  // 6. wait for JobCompleted
329
- // KEY INVARIANT: the real result is the WS-delivered, session-key-decrypted
330
- // ciphertext. JobCompleted is an explorer pointer (the worker's commit-result
331
- // tx). If the WS already produced an answer (chunks.length > 0) we MUST
332
- // accept it even if the on-chain event hasn't landed - throwing stalled here
333
- // wipes a delivered answer on retry, which reads as "the call returned nothing".
329
+ // The actual *result* is the WS-delivered, session-key-decrypted ciphertext.
330
+ // JobCompleted is an explorer pointer (the worker's commit-result tx).
331
+ // Polling rules:
332
+ // - No chunks yet: poll for the full deadline (default 120s). Still nothing
333
+ // -> throw stalled so the outer loop can retry with a different worker.
334
+ // - Chunks arrived: keep polling for a 45s grace window after the FIRST
335
+ // chunk. Workers usually commit JobCompleted within ~10s of broadcasting
336
+ // the answer, so 45s is generous. If it still doesn't land, surface the
337
+ // answer with txs.jobCompleted=null (the answer is still session-key
338
+ // authentic; the on-chain proof can be polled for separately by callers).
334
339
  const deadline = Date.now() + jobCompletedTimeoutMs;
340
+ const POST_CHUNKS_GRACE_MS = 45000;
341
+ const waitStart = Date.now();
342
+ let firstChunkAt = chunks.length > 0 ? waitStart : null;
335
343
  const jobIdTopic = (`0x${jobId.toString(16).padStart(64, "0")}`);
336
344
  let completed = null;
337
- while (!completed && Date.now() < deadline) {
345
+ while (!completed) {
346
+ const now = Date.now();
347
+ if (now >= deadline)
348
+ break;
349
+ if (firstChunkAt != null && now - firstChunkAt >= POST_CHUNKS_GRACE_MS)
350
+ break;
338
351
  await new Promise((res) => setTimeout(res, 3000));
352
+ if (firstChunkAt == null && chunks.length > 0)
353
+ firstChunkAt = Date.now();
339
354
  const logs = await publicClient.getLogs({
340
355
  address: network.jobRegistry,
341
356
  fromBlock: submitReceipt.blockNumber,
@@ -345,10 +360,6 @@ async function runOneAttempt(args, attempt) {
345
360
  logs.find((l) => l.topics[0] === JOB_COMPLETED_TOPIC && l.topics[1] === jobIdTopic) ?? null;
346
361
  if (completed)
347
362
  break;
348
- // If the WS already delivered the answer, stop polling - no point burning
349
- // more time on a confirmation that only serves as an explorer link.
350
- if (chunks.length > 0)
351
- break;
352
363
  }
353
364
  if (!completed && chunks.length === 0) {
354
365
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightnode-sdk",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Read-only TypeScript client for LightChain AI: workers, jobs, models, on-chain registration, and per-model network analytics. Independent, community-built (not an official LightChain package).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",