lightnode-sdk 0.8.7 → 0.8.9
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/add.js +81 -35
- package/package.json +1 -1
package/dist/add.js
CHANGED
|
@@ -978,7 +978,9 @@ type Turn = {
|
|
|
978
978
|
jobCompletedTx?: \`0x\${string}\` | null;
|
|
979
979
|
};
|
|
980
980
|
|
|
981
|
-
|
|
981
|
+
// Models live on LightChain mainnet. The visitor picks one per the dropdown.
|
|
982
|
+
const MODELS = ["llama3-8b", "llama3-70b"] as const;
|
|
983
|
+
type ModelId = (typeof MODELS)[number];
|
|
982
984
|
|
|
983
985
|
export default function ChatWeb3() {
|
|
984
986
|
const { address, chain } = useAccount();
|
|
@@ -987,6 +989,7 @@ export default function ChatWeb3() {
|
|
|
987
989
|
const { data: walletClient } = useWalletClient({ chainId: chain?.id });
|
|
988
990
|
const publicClient = usePublicClient({ chainId: chain?.id });
|
|
989
991
|
|
|
992
|
+
const [model, setModel] = useState<ModelId>("llama3-8b");
|
|
990
993
|
const [turns, setTurns] = useState<Turn[]>([]);
|
|
991
994
|
const [input, setInput] = useState("");
|
|
992
995
|
const [busy, setBusy] = useState(false);
|
|
@@ -1000,16 +1003,17 @@ export default function ChatWeb3() {
|
|
|
1000
1003
|
useEffect(() => {
|
|
1001
1004
|
if (!network) { setFeeLcai(null); return; }
|
|
1002
1005
|
let cancelled = false;
|
|
1003
|
-
estimateJobFee(NETWORKS[network],
|
|
1006
|
+
estimateJobFee(NETWORKS[network], model).then(
|
|
1004
1007
|
(fee) => { if (!cancelled) setFeeLcai(fee); },
|
|
1005
1008
|
() => { if (!cancelled) setFeeLcai(null); },
|
|
1006
1009
|
);
|
|
1007
1010
|
return () => { cancelled = true; };
|
|
1008
|
-
}, [network]);
|
|
1011
|
+
}, [network, model]);
|
|
1009
1012
|
|
|
1010
|
-
// Keep the latest turn
|
|
1013
|
+
// Keep the latest turn in view. Instant while streaming (smooth scrolling on
|
|
1014
|
+
// every chunk competes for the main thread); smooth once idle.
|
|
1011
1015
|
useEffect(() => {
|
|
1012
|
-
endRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1016
|
+
endRef.current?.scrollIntoView({ behavior: busy ? "auto" : "smooth", block: "nearest" });
|
|
1013
1017
|
}, [turns, busy]);
|
|
1014
1018
|
|
|
1015
1019
|
/** Build a single prompt from history + new user input. */
|
|
@@ -1061,7 +1065,7 @@ export default function ChatWeb3() {
|
|
|
1061
1065
|
wallet: walletClient as unknown as Parameters<typeof runInference>[0]["wallet"],
|
|
1062
1066
|
publicClient: publicClient as unknown as Parameters<typeof runInference>[0]["publicClient"],
|
|
1063
1067
|
network: NETWORKS[network],
|
|
1064
|
-
model
|
|
1068
|
+
model,
|
|
1065
1069
|
jobCompletedTimeoutMs: 120_000,
|
|
1066
1070
|
maxRetries: 1,
|
|
1067
1071
|
// Stream each decrypted chunk into the assistant bubble as it arrives.
|
|
@@ -1143,9 +1147,15 @@ export default function ChatWeb3() {
|
|
|
1143
1147
|
<LcaiMark className="mt-0.5 size-7 shrink-0" />
|
|
1144
1148
|
<div className="flex min-w-0 flex-1 flex-col gap-2">
|
|
1145
1149
|
{t.text ? (
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1150
|
+
t.streaming ? (
|
|
1151
|
+
// While streaming, render plain text (cheap) - markdown is
|
|
1152
|
+
// parsed once when the turn finalizes, below.
|
|
1153
|
+
<div className="whitespace-pre-wrap break-words text-sm leading-relaxed text-foreground">{t.text}</div>
|
|
1154
|
+
) : (
|
|
1155
|
+
<div className="max-w-none text-sm leading-relaxed text-foreground [&_*:first-child]:mt-0 [&_*:last-child]:mb-0">
|
|
1156
|
+
<Streamdown>{t.text}</Streamdown>
|
|
1157
|
+
</div>
|
|
1158
|
+
)
|
|
1149
1159
|
) : (
|
|
1150
1160
|
<div className="animate-pulse-dot pt-1 text-sm text-muted-foreground">
|
|
1151
1161
|
{busyStage || "Writing on chain..."}
|
|
@@ -1200,14 +1210,17 @@ export default function ChatWeb3() {
|
|
|
1200
1210
|
/>
|
|
1201
1211
|
</div>
|
|
1202
1212
|
<div className="mt-1 flex items-center justify-between gap-2">
|
|
1203
|
-
<
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1213
|
+
<select
|
|
1214
|
+
value={model}
|
|
1215
|
+
onChange={(e) => setModel(e.target.value as ModelId)}
|
|
1216
|
+
disabled={busy}
|
|
1217
|
+
title="Model (both live on LightChain mainnet)"
|
|
1218
|
+
className="rounded-lg border border-border bg-background px-2 py-1 text-xs font-medium text-muted-foreground outline-none focus:ring-2 focus:ring-primary disabled:opacity-50"
|
|
1219
|
+
>
|
|
1220
|
+
{MODELS.map((m) => (
|
|
1221
|
+
<option key={m} value={m}>{m}</option>
|
|
1222
|
+
))}
|
|
1223
|
+
</select>
|
|
1211
1224
|
<button
|
|
1212
1225
|
type="button"
|
|
1213
1226
|
onClick={() => send()}
|
|
@@ -1260,7 +1273,8 @@ type Result = {
|
|
|
1260
1273
|
elapsedMs: number;
|
|
1261
1274
|
};
|
|
1262
1275
|
|
|
1263
|
-
const
|
|
1276
|
+
const MODELS = ["llama3-8b", "llama3-70b"] as const;
|
|
1277
|
+
type ModelId = (typeof MODELS)[number];
|
|
1264
1278
|
|
|
1265
1279
|
export default function InferenceWeb3() {
|
|
1266
1280
|
const { address, chain } = useAccount();
|
|
@@ -1269,6 +1283,7 @@ export default function InferenceWeb3() {
|
|
|
1269
1283
|
const { data: walletClient } = useWalletClient({ chainId: chain?.id });
|
|
1270
1284
|
const publicClient = usePublicClient({ chainId: chain?.id });
|
|
1271
1285
|
|
|
1286
|
+
const [model, setModel] = useState<ModelId>("llama3-8b");
|
|
1272
1287
|
const [system, setSystem] = useState("You are a concise assistant. Reply in one or two short sentences.");
|
|
1273
1288
|
const [prompt, setPrompt] = useState("Reply with the single word OK.");
|
|
1274
1289
|
const [busy, setBusy] = useState(false);
|
|
@@ -1281,12 +1296,12 @@ export default function InferenceWeb3() {
|
|
|
1281
1296
|
useEffect(() => {
|
|
1282
1297
|
if (!network) { setFeeLcai(null); return; }
|
|
1283
1298
|
let cancelled = false;
|
|
1284
|
-
estimateJobFee(NETWORKS[network],
|
|
1299
|
+
estimateJobFee(NETWORKS[network], model).then(
|
|
1285
1300
|
(fee) => { if (!cancelled) setFeeLcai(fee); },
|
|
1286
1301
|
() => { if (!cancelled) setFeeLcai(null); },
|
|
1287
1302
|
);
|
|
1288
1303
|
return () => { cancelled = true; };
|
|
1289
|
-
}, [network]);
|
|
1304
|
+
}, [network, model]);
|
|
1290
1305
|
|
|
1291
1306
|
async function run() {
|
|
1292
1307
|
if (!walletClient || !publicClient || !address || !network) {
|
|
@@ -1312,7 +1327,7 @@ export default function InferenceWeb3() {
|
|
|
1312
1327
|
wallet: walletClient as unknown as Parameters<typeof runInference>[0]["wallet"],
|
|
1313
1328
|
publicClient: publicClient as unknown as Parameters<typeof runInference>[0]["publicClient"],
|
|
1314
1329
|
network: NETWORKS[network],
|
|
1315
|
-
model
|
|
1330
|
+
model,
|
|
1316
1331
|
jobCompletedTimeoutMs: 120_000,
|
|
1317
1332
|
maxRetries: 1,
|
|
1318
1333
|
// Stream the answer live as decrypted chunks arrive.
|
|
@@ -1375,10 +1390,23 @@ export default function InferenceWeb3() {
|
|
|
1375
1390
|
className="resize-none rounded-xl border border-border bg-card px-3 py-2 font-mono text-xs text-foreground outline-none focus:ring-2 focus:ring-primary" />
|
|
1376
1391
|
</label>
|
|
1377
1392
|
|
|
1378
|
-
<
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1393
|
+
<div className="flex items-center gap-3">
|
|
1394
|
+
<select
|
|
1395
|
+
value={model}
|
|
1396
|
+
onChange={(e) => setModel(e.target.value as ModelId)}
|
|
1397
|
+
disabled={busy}
|
|
1398
|
+
title="Model (both live on LightChain mainnet)"
|
|
1399
|
+
className="rounded-xl border border-border bg-card px-2 py-2 text-xs font-medium text-muted-foreground outline-none focus:ring-2 focus:ring-primary disabled:opacity-50"
|
|
1400
|
+
>
|
|
1401
|
+
{MODELS.map((m) => (
|
|
1402
|
+
<option key={m} value={m}>{m}</option>
|
|
1403
|
+
))}
|
|
1404
|
+
</select>
|
|
1405
|
+
<button type="button" onClick={() => run()} disabled={busy || !prompt.trim() || !address || !network}
|
|
1406
|
+
className="rounded-xl bg-gradient-primary px-4 py-2 text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40">
|
|
1407
|
+
{busy ? (busyStage || "Running...") : "Run inference"}
|
|
1408
|
+
</button>
|
|
1409
|
+
</div>
|
|
1382
1410
|
|
|
1383
1411
|
{err && (
|
|
1384
1412
|
<p className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
|
@@ -1391,10 +1419,13 @@ export default function InferenceWeb3() {
|
|
|
1391
1419
|
<div className="flex gap-3">
|
|
1392
1420
|
<LcaiMark className="mt-0.5 size-7 shrink-0" />
|
|
1393
1421
|
<div className="flex min-w-0 flex-1 flex-col gap-2">
|
|
1394
|
-
{result
|
|
1422
|
+
{result ? (
|
|
1395
1423
|
<div className="max-w-none text-sm leading-relaxed text-foreground [&_*:first-child]:mt-0 [&_*:last-child]:mb-0">
|
|
1396
|
-
<Streamdown>{result
|
|
1424
|
+
<Streamdown>{result.answer}</Streamdown>
|
|
1397
1425
|
</div>
|
|
1426
|
+
) : stream ? (
|
|
1427
|
+
// Plain text while streaming; markdown is parsed once on completion.
|
|
1428
|
+
<div className="whitespace-pre-wrap break-words text-sm leading-relaxed text-foreground">{stream}</div>
|
|
1398
1429
|
) : (
|
|
1399
1430
|
<div className="animate-pulse-dot pt-1 text-sm text-muted-foreground">
|
|
1400
1431
|
{busyStage || "Writing on chain..."}
|
|
@@ -1454,7 +1485,8 @@ type Result = {
|
|
|
1454
1485
|
jobCompleted: \`0x\${string}\` | null;
|
|
1455
1486
|
};
|
|
1456
1487
|
|
|
1457
|
-
const
|
|
1488
|
+
const MODELS = ["llama3-8b", "llama3-70b"] as const;
|
|
1489
|
+
type ModelId = (typeof MODELS)[number];
|
|
1458
1490
|
|
|
1459
1491
|
export default function JudgeWeb3() {
|
|
1460
1492
|
const { address, chain } = useAccount();
|
|
@@ -1463,6 +1495,7 @@ export default function JudgeWeb3() {
|
|
|
1463
1495
|
const { data: walletClient } = useWalletClient({ chainId: chain?.id });
|
|
1464
1496
|
const publicClient = usePublicClient({ chainId: chain?.id });
|
|
1465
1497
|
|
|
1498
|
+
const [model, setModel] = useState<ModelId>("llama3-8b");
|
|
1466
1499
|
const [criteria, setCriteria] = useState("Run a mile under 8 minutes");
|
|
1467
1500
|
const [evidence, setEvidence] = useState('{"distance_km": 1.61, "time_minutes": 7.4}');
|
|
1468
1501
|
const [busy, setBusy] = useState(false);
|
|
@@ -1475,12 +1508,12 @@ export default function JudgeWeb3() {
|
|
|
1475
1508
|
useEffect(() => {
|
|
1476
1509
|
if (!network) { setFeeLcai(null); return; }
|
|
1477
1510
|
let cancelled = false;
|
|
1478
|
-
estimateJobFee(NETWORKS[network],
|
|
1511
|
+
estimateJobFee(NETWORKS[network], model).then(
|
|
1479
1512
|
(fee) => { if (!cancelled) setFeeLcai(fee); },
|
|
1480
1513
|
() => { if (!cancelled) setFeeLcai(null); },
|
|
1481
1514
|
);
|
|
1482
1515
|
return () => { cancelled = true; };
|
|
1483
|
-
}, [network]);
|
|
1516
|
+
}, [network, model]);
|
|
1484
1517
|
|
|
1485
1518
|
/** Parse the verdict defensively; fall back to the first {...} block. */
|
|
1486
1519
|
function parseVerdict(answer: string): Verdict | null {
|
|
@@ -1525,7 +1558,7 @@ Reply with STRICT JSON only, matching: { "passed": boolean, "confidence": 0-1, "
|
|
|
1525
1558
|
wallet: walletClient as unknown as Parameters<typeof runInference>[0]["wallet"],
|
|
1526
1559
|
publicClient: publicClient as unknown as Parameters<typeof runInference>[0]["publicClient"],
|
|
1527
1560
|
network: NETWORKS[network],
|
|
1528
|
-
model
|
|
1561
|
+
model,
|
|
1529
1562
|
jobCompletedTimeoutMs: 120_000,
|
|
1530
1563
|
maxRetries: 1,
|
|
1531
1564
|
// Show the model's raw output streaming in while it generates the verdict.
|
|
@@ -1588,10 +1621,23 @@ Reply with STRICT JSON only, matching: { "passed": boolean, "confidence": 0-1, "
|
|
|
1588
1621
|
className="resize-none rounded-xl border border-border bg-card px-3 py-2 font-mono text-xs text-foreground outline-none focus:ring-2 focus:ring-primary" />
|
|
1589
1622
|
</label>
|
|
1590
1623
|
|
|
1591
|
-
<
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1624
|
+
<div className="flex items-center gap-3">
|
|
1625
|
+
<select
|
|
1626
|
+
value={model}
|
|
1627
|
+
onChange={(e) => setModel(e.target.value as ModelId)}
|
|
1628
|
+
disabled={busy}
|
|
1629
|
+
title="Model (both live on LightChain mainnet)"
|
|
1630
|
+
className="rounded-xl border border-border bg-card px-2 py-2 text-xs font-medium text-muted-foreground outline-none focus:ring-2 focus:ring-primary disabled:opacity-50"
|
|
1631
|
+
>
|
|
1632
|
+
{MODELS.map((m) => (
|
|
1633
|
+
<option key={m} value={m}>{m}</option>
|
|
1634
|
+
))}
|
|
1635
|
+
</select>
|
|
1636
|
+
<button type="button" onClick={() => run()} disabled={busy || !criteria.trim() || !evidence.trim() || !address || !network}
|
|
1637
|
+
className="rounded-xl bg-gradient-primary px-4 py-2 text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40">
|
|
1638
|
+
{busy ? (busyStage || "Judging...") : "Get AI verdict"}
|
|
1639
|
+
</button>
|
|
1640
|
+
</div>
|
|
1595
1641
|
|
|
1596
1642
|
{err && (
|
|
1597
1643
|
<p className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightnode-sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
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",
|