lightnode-sdk 0.7.4 → 0.7.5

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.ts CHANGED
@@ -13,7 +13,7 @@ import { OnchainModelRegistry, AIVM_MODEL_REGISTRY_ABI, BENCHMARK_REGISTRY_ABI,
13
13
  import { StalledWorkerError, OnChainRevertError, RelayTokenTimeoutError, GatewayAuthError, isStalledWorker } from "./errors.js";
14
14
  import { GatewayClient, GatewayHttpError } from "./gateway.js";
15
15
  import * as crypto from "./crypto.js";
16
- import type { NetworkId, NetworkConfig, Worker, Job, JobTransactions, ModelInfo, WorkerModel, NetworkStats, ModelStat, WorkerStat, NetworkAnalytics } from "./types.js";
16
+ import type { NetworkId, NetworkConfig, Worker, Job, JobTransactions, ModelInfo, WorkerModel, ServedModel, NetworkStats, ModelStat, WorkerStat, NetworkAnalytics } from "./types.js";
17
17
  /**
18
18
  * Read-only client for a LightChain AI network. Pure reads from the public indexer
19
19
  * and the chain; no keys, no writes. Independent, community-built.
@@ -42,6 +42,17 @@ export declare class LightNode {
42
42
  * whether the worker is currently serving them.
43
43
  */
44
44
  getWorkerModels(address: string): Promise<WorkerModel[]>;
45
+ /**
46
+ * The models a worker is serving, RECONCILED against the chain. getWorkerModels
47
+ * returns the raw subgraph rows, which go stale after a deregister/re-register
48
+ * (the indexer never clears removals). This joins those rows to model names AND
49
+ * to the authoritative on-chain WorkerRegistry.isEligible(worker, modelId), so
50
+ * each `ServedModel` carries both `indexedActive` (subgraph) and
51
+ * `onchainEligible` (chain truth). Use `onchainEligible === true` to decide what
52
+ * the worker ACTUALLY serves right now; it falls back to null (rely on
53
+ * `indexedActive`) if the chain read is unavailable.
54
+ */
55
+ getServedModels(address: string): Promise<ServedModel[]>;
45
56
  /** The network's registered models (name, fee, output limit, whitelist flags). */
46
57
  getModels(): Promise<ModelInfo[]>;
47
58
  /** Registered workers (default top 200). */
@@ -122,7 +133,7 @@ export declare class LightNode {
122
133
  * (especially in registry-proxy environments like StackBlitz where lockfiles
123
134
  * may pin an older minor than the local install command suggests).
124
135
  */
125
- export declare const SDK_VERSION = "0.7.4";
136
+ export declare const SDK_VERSION = "0.7.5";
126
137
  export { NETWORKS, WORKER_REGISTRY, REGISTRY_TOPICS, aggregateModelStats, aggregateWorkerStats, networkAnalytics, modelStatsCsv, workerStatsCsv, workerJobsCsv, fromWei, resolveJobTransactions, fetchWorkerModels, computeModelId as modelId, estimateJobFee, JOB_REGISTRY_CONSUMER_ABI, consumerGatewayUrl, consumerGatewayHost, GatewayClient, GatewayHttpError, prepareSession, submitPrompt, decryptResponse, generateEcdhKeyPair, crypto, runInference, runInferenceWithKey, runInferenceStream, Conversation, chat, runInferenceBatch, Agent, parseAgentOutput, workerPreflight, workerWatch, Bridge, BRIDGE_ROUTE, HYPERLANE_ROUTER_ABI, ERC20_ABI, addressToBytes32, quoteBridgeFee, bridgeableBalance, bridgeAllowance, approveBridge, bridgeTransfer, DAO, DAO_ADDRESSES, ProposalState, PROPOSAL_STATE_LABEL, VoteSupport, GOVERNOR_ABI, VOTES_ABI, OnchainModelRegistry, AIVM_MODEL_REGISTRY_ABI, BENCHMARK_REGISTRY_ABI, ModelStatus, MODEL_STATUS_LABEL, StalledWorkerError, OnChainRevertError, RelayTokenTimeoutError, GatewayAuthError, isStalledWorker, WorkerOperator, WORKER_REGISTRY_ABI, JOB_REGISTRY_OPERATOR_ABI, AI_CONFIG_ABI, JOB_STATE, decodeWorkerError, WorkerOpError, isWorkerOpError, };
127
138
  export type { BearerSource, GatewayClientOptions, SelectSessionResult, PrepareSessionResult, UploadBlobResult, SessionTokenResult } from "./gateway.js";
128
139
  export type { SessionPreparation, RunInferenceArgs, RunInferenceResult, RunInferenceWithKeyArgs, RunInferenceStreamResult } from "./inference.js";
@@ -134,4 +145,4 @@ export type { BridgeChain, BridgeEndpoints, BridgeTransferArgs } from "./bridge.
134
145
  export type { DaoChain, DaoAddresses, ProposalSummary, ProposalRow, DaoConfig } from "./dao.js";
135
146
  export type { BaseModel, ModelVariant, AccessTier, AccessPolicy, Benchmark, OnchainModelRegistryOptions } from "./onchain-models.js";
136
147
  export type { MinimalWalletClient, MinimalPublicClient, WorkerOperatorOpts, WorkerProtocolConfig, WorkerStatus, DeregisterReadiness, StuckJob, EarningsBreakdown, OnchainJob, JobState, DecodedWorkerError, } from "./worker-operator.js";
137
- export type { NetworkId, NetworkConfig, Worker, Job, JobTransactions, ModelInfo, WorkerModel, NetworkStats, ModelStat, WorkerStat, NetworkAnalytics };
148
+ export type { NetworkId, NetworkConfig, Worker, Job, JobTransactions, ModelInfo, WorkerModel, ServedModel, NetworkStats, ModelStat, WorkerStat, NetworkAnalytics };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NETWORKS, WORKER_REGISTRY, REGISTRY_TOPICS } from "./networks.js";
2
2
  import { fetchWorker, fetchWorkerJobs, fetchWorkerModels, fetchRecentJobs, fetchJob, fetchModels, fetchWorkers, summarize, fromWei, resolveJobTransactions, } from "./subgraph.js";
3
- import { isRegistered } from "./onchain.js";
3
+ import { isRegistered, fetchOnchainEligibleModels } from "./onchain.js";
4
4
  import { aggregateModelStats, aggregateWorkerStats, networkAnalytics, modelStatsCsv, workerStatsCsv, workerJobsCsv, } from "./analytics.js";
5
5
  import { modelId as computeModelId, estimateJobFee, JOB_REGISTRY_CONSUMER_ABI, consumerGatewayUrl, consumerGatewayHost, prepareSession, submitPrompt, decryptResponse, generateEcdhKeyPair, runInference, runInferenceWithKey, runInferenceStream, } from "./inference.js";
6
6
  import { Conversation, chat } from "./chat.js";
@@ -51,6 +51,37 @@ export class LightNode {
51
51
  getWorkerModels(address) {
52
52
  return fetchWorkerModels(this.network, address);
53
53
  }
54
+ /**
55
+ * The models a worker is serving, RECONCILED against the chain. getWorkerModels
56
+ * returns the raw subgraph rows, which go stale after a deregister/re-register
57
+ * (the indexer never clears removals). This joins those rows to model names AND
58
+ * to the authoritative on-chain WorkerRegistry.isEligible(worker, modelId), so
59
+ * each `ServedModel` carries both `indexedActive` (subgraph) and
60
+ * `onchainEligible` (chain truth). Use `onchainEligible === true` to decide what
61
+ * the worker ACTUALLY serves right now; it falls back to null (rely on
62
+ * `indexedActive`) if the chain read is unavailable.
63
+ */
64
+ async getServedModels(address) {
65
+ const [rows, registry] = await Promise.all([
66
+ fetchWorkerModels(this.network, address),
67
+ fetchModels(this.network),
68
+ ]);
69
+ if (rows.length === 0)
70
+ return [];
71
+ const byId = new Map(registry.map((m) => [m.id.toLowerCase(), m]));
72
+ const eligible = await fetchOnchainEligibleModels(this.network, address, rows.map((r) => r.model_id)).catch(() => null);
73
+ return rows.map((r) => {
74
+ const info = byId.get(r.model_id.toLowerCase());
75
+ return {
76
+ modelId: r.model_id,
77
+ name: info?.name ?? null,
78
+ feeWei: info?.fee,
79
+ maxOutputTokens: info?.max_output_tokens,
80
+ indexedActive: r.is_active,
81
+ onchainEligible: eligible ? (eligible.get(r.model_id.toLowerCase()) ?? null) : null,
82
+ };
83
+ });
84
+ }
54
85
  /** The network's registered models (name, fee, output limit, whitelist flags). */
55
86
  getModels() {
56
87
  return fetchModels(this.network);
@@ -181,7 +212,7 @@ export class LightNode {
181
212
  * (especially in registry-proxy environments like StackBlitz where lockfiles
182
213
  * may pin an older minor than the local install command suggests).
183
214
  */
184
- export const SDK_VERSION = "0.7.4";
215
+ export const SDK_VERSION = "0.7.5";
185
216
  export { NETWORKS, WORKER_REGISTRY, REGISTRY_TOPICS, aggregateModelStats, aggregateWorkerStats, networkAnalytics, modelStatsCsv, workerStatsCsv, workerJobsCsv, fromWei,
186
217
  // v0.7.3 per-job transaction-hash resolver (lifts the upstream
187
218
  // subgraph's "block-only" Job entity to a deep-linkable Job + tx pair).
package/dist/onchain.d.ts CHANGED
@@ -6,3 +6,13 @@ import type { NetworkConfig } from "./types.js";
6
6
  * event, or null when the chain can't answer (RPC error, or no events for it).
7
7
  */
8
8
  export declare function isRegistered(cfg: NetworkConfig, address: string): Promise<boolean | null>;
9
+ /**
10
+ * On-chain truth for which of `modelIds` a worker currently serves, via
11
+ * WorkerRegistry.isEligible(worker, modelId). Stronger than the subgraph's
12
+ * WorkerModel.is_active: the indexer lists a worker's models from its LAST
13
+ * registration and never indexes removals, so it goes stale after a
14
+ * deregister/re-register (it can show a model the worker no longer serves, or
15
+ * miss the current one). Returns a Map keyed by lowercased modelId; null on any
16
+ * RPC failure so callers can fall back to the index. One eth_call per model.
17
+ */
18
+ export declare function fetchOnchainEligibleModels(cfg: NetworkConfig, address: string, modelIds: string[]): Promise<Map<string, boolean> | null>;
package/dist/onchain.js CHANGED
@@ -55,3 +55,54 @@ export async function isRegistered(cfg, address) {
55
55
  clearTimeout(timer);
56
56
  }
57
57
  }
58
+ // WorkerRegistry.isEligible(address,bytes32) selector. This view does NOT revert
59
+ // on the deployed predeploy (unlike most of its getters), so it's a reliable
60
+ // on-chain read of whether a worker currently serves a given model.
61
+ const IS_ELIGIBLE_SELECTOR = "0xdb3ebef1";
62
+ /**
63
+ * On-chain truth for which of `modelIds` a worker currently serves, via
64
+ * WorkerRegistry.isEligible(worker, modelId). Stronger than the subgraph's
65
+ * WorkerModel.is_active: the indexer lists a worker's models from its LAST
66
+ * registration and never indexes removals, so it goes stale after a
67
+ * deregister/re-register (it can show a model the worker no longer serves, or
68
+ * miss the current one). Returns a Map keyed by lowercased modelId; null on any
69
+ * RPC failure so callers can fall back to the index. One eth_call per model.
70
+ */
71
+ export async function fetchOnchainEligibleModels(cfg, address, modelIds) {
72
+ if (!/^0x[a-fA-F0-9]{40}$/.test(address) || modelIds.length === 0)
73
+ return null;
74
+ const addrArg = address.toLowerCase().replace(/^0x/, "").padStart(64, "0");
75
+ const ctrl = new AbortController();
76
+ const timer = setTimeout(() => ctrl.abort(), 8000);
77
+ try {
78
+ const entries = await Promise.all(modelIds.map(async (id) => {
79
+ const idArg = id.toLowerCase().replace(/^0x/, "").padStart(64, "0");
80
+ const res = await fetch(cfg.rpc, {
81
+ method: "POST",
82
+ headers: { "content-type": "application/json" },
83
+ body: JSON.stringify({
84
+ jsonrpc: "2.0",
85
+ id: 1,
86
+ method: "eth_call",
87
+ params: [{ to: cfg.workerRegistry, data: `${IS_ELIGIBLE_SELECTOR}${addrArg}${idArg}` }, "latest"],
88
+ }),
89
+ signal: ctrl.signal,
90
+ });
91
+ if (!res.ok)
92
+ throw new Error(`rpc ${res.status}`);
93
+ const json = (await res.json());
94
+ if (json.error || typeof json.result !== "string")
95
+ throw new Error("eth_call failed");
96
+ // bool return: true iff the 32-byte word ends in 1.
97
+ const eligible = /0*1$/.test(json.result.replace(/^0x/, ""));
98
+ return [id.toLowerCase(), eligible];
99
+ }));
100
+ return new Map(entries);
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ finally {
106
+ clearTimeout(timer);
107
+ }
108
+ }
package/dist/types.d.ts CHANGED
@@ -78,6 +78,25 @@ export interface WorkerModel {
78
78
  created_at?: number;
79
79
  updated_at?: number;
80
80
  }
81
+ /**
82
+ * A worker's served model, reconciled against the chain. Combines the subgraph
83
+ * WorkerModel row (name, is_active) with the authoritative on-chain
84
+ * WorkerRegistry.isEligible read, so you can tell a model the worker ACTUALLY
85
+ * serves now from a stale index row left over after a deregister/re-register.
86
+ */
87
+ export interface ServedModel {
88
+ modelId: string;
89
+ name: string | null;
90
+ feeWei?: string;
91
+ maxOutputTokens?: number;
92
+ /** Subgraph WorkerModel.is_active (can be stale after deregister/re-register). */
93
+ indexedActive: boolean;
94
+ /**
95
+ * On-chain WorkerRegistry.isEligible(worker, modelId) - the authoritative
96
+ * "is it serving this right now". null when the chain read was unavailable.
97
+ */
98
+ onchainEligible: boolean | null;
99
+ }
81
100
  export interface NetworkStats {
82
101
  total: number;
83
102
  active: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightnode-sdk",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
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",