lightnode-sdk 0.10.2 → 0.10.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.
- package/dist/add.js +55 -32
- package/dist/cli.js +4 -6
- package/package.json +1 -1
package/dist/add.js
CHANGED
|
@@ -1281,7 +1281,7 @@ export default function ChatWeb3() {
|
|
|
1281
1281
|
onChange={(e) => setModel(e.target.value as ModelId)}
|
|
1282
1282
|
disabled={busy}
|
|
1283
1283
|
title="Model (both live on LightChain mainnet)"
|
|
1284
|
-
className="rounded-lg
|
|
1284
|
+
className="cursor-pointer rounded-lg bg-muted-foreground/10 px-2 py-1 text-xs font-medium text-muted-foreground outline-none transition-colors hover:bg-muted-foreground/15 focus:ring-1 focus:ring-primary disabled:opacity-50"
|
|
1285
1285
|
>
|
|
1286
1286
|
{MODELS.map((m) => (
|
|
1287
1287
|
<option key={m} value={m}>{m}</option>
|
|
@@ -1776,55 +1776,46 @@ Reply with STRICT JSON only, matching: { "passed": boolean, "confidence": 0-1, "
|
|
|
1776
1776
|
}
|
|
1777
1777
|
`;
|
|
1778
1778
|
const NODE_CHAT_REPL = `// chat-repl.ts
|
|
1779
|
-
// Generated by 'lightnode add chat'. Interactive chat
|
|
1780
|
-
//
|
|
1781
|
-
// tsx chat-repl.ts
|
|
1779
|
+
// Generated by 'lightnode add chat'. Interactive multi-turn chat in your terminal.
|
|
1780
|
+
// cp .env.example .env (put a funded PRIVATE_KEY in it)
|
|
1781
|
+
// npx tsx chat-repl.ts
|
|
1782
1782
|
import * as readline from "node:readline/promises";
|
|
1783
1783
|
import { stdin as input, stdout as output } from "node:process";
|
|
1784
|
-
import
|
|
1785
|
-
import { createPublicClient, createWalletClient, http } from "viem";
|
|
1786
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
1787
|
-
import { LightNode, runInference, GatewayClient, consumerGatewayUrl, type NetworkId } from "lightnode-sdk";
|
|
1784
|
+
import { runInferenceWithKey, type NetworkId } from "lightnode-sdk";
|
|
1788
1785
|
|
|
1789
|
-
const NETWORK = (process.env.NETWORK ?? "
|
|
1786
|
+
const NETWORK = (process.env.NETWORK ?? "mainnet") as NetworkId;
|
|
1790
1787
|
const MODEL = process.env.MODEL ?? "llama3-8b";
|
|
1788
|
+
const SYSTEM = "You are a concise assistant. Reply in one or two short sentences.";
|
|
1791
1789
|
const PRIVATE_KEY = process.env.PRIVATE_KEY as \`0x\${string}\` | undefined;
|
|
1792
|
-
if (!PRIVATE_KEY?.startsWith("0x") || PRIVATE_KEY.length !== 66) {
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
const chain = { id: ln.network.chainId, name: ln.network.label, nativeCurrency: { name: "LCAI", symbol: "LCAI", decimals: 18 }, rpcUrls: { default: { http: [ln.network.rpc] } } };
|
|
1797
|
-
const pub = createPublicClient({ transport: http(ln.network.rpc), chain });
|
|
1798
|
-
const wal = createWalletClient({ account: acct, transport: http(ln.network.rpc), chain });
|
|
1799
|
-
|
|
1800
|
-
// One SIWE handshake per process; the JWT is reused across all turns.
|
|
1801
|
-
const ch = await (await fetch(\`\${consumerGatewayUrl(NETWORK)}/api/auth/challenge?address=\${acct.address}\`)).json() as { message?: string };
|
|
1802
|
-
if (!ch.message) throw new Error("auth challenge failed");
|
|
1803
|
-
const sig = await wal.signMessage({ message: ch.message });
|
|
1804
|
-
const verify = await (await fetch(\`\${consumerGatewayUrl(NETWORK)}/api/auth/verify\`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: ch.message, signature: sig }) })).json() as { token?: string };
|
|
1805
|
-
if (!verify.token) throw new Error("auth verify failed");
|
|
1806
|
-
const gateway = new GatewayClient({ network: NETWORK, bearer: verify.token });
|
|
1790
|
+
if (!PRIVATE_KEY?.startsWith("0x") || PRIVATE_KEY.length !== 66) {
|
|
1791
|
+
console.error("Set a funded PRIVATE_KEY in .env (see .env.example).");
|
|
1792
|
+
process.exit(1);
|
|
1793
|
+
}
|
|
1807
1794
|
|
|
1808
1795
|
const rl = readline.createInterface({ input, output });
|
|
1809
1796
|
const turns: { role: "user" | "assistant"; text: string }[] = [];
|
|
1810
|
-
console.log(
|
|
1797
|
+
console.log(\`Chat on \${NETWORK} (\${MODEL}). Your funded key pays each turn. Ctrl+C to exit.\\n\`);
|
|
1811
1798
|
|
|
1812
1799
|
while (true) {
|
|
1813
|
-
const user = (await rl.question("> ")).trim();
|
|
1800
|
+
const user = (await rl.question("you > ")).trim();
|
|
1814
1801
|
if (!user) continue;
|
|
1815
1802
|
turns.push({ role: "user", text: user });
|
|
1816
|
-
const prompt = turns.map((t) => (t.role === "user" ?
|
|
1803
|
+
const prompt = SYSTEM + "\\n\\n" + turns.map((t) => (t.role === "user" ? "User: " : "Assistant: ") + t.text).join("\\n\\n") + "\\n\\nAssistant:";
|
|
1817
1804
|
try {
|
|
1818
|
-
process.stdout.write(" ");
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1805
|
+
process.stdout.write("ai > ");
|
|
1806
|
+
// runInferenceWithKey builds the viem clients, runs SIWE, and auto-loads
|
|
1807
|
+
// 'ws' in Node - no manual client wiring (and no type casts) needed.
|
|
1808
|
+
const { answer } = await runInferenceWithKey({
|
|
1809
|
+
network: NETWORK,
|
|
1810
|
+
privateKey: PRIVATE_KEY,
|
|
1811
|
+
model: MODEL,
|
|
1812
|
+
prompt,
|
|
1822
1813
|
onChunk: (chunk) => process.stdout.write(chunk),
|
|
1823
1814
|
});
|
|
1824
1815
|
process.stdout.write("\\n\\n");
|
|
1825
1816
|
turns.push({ role: "assistant", text: answer });
|
|
1826
1817
|
} catch (e) {
|
|
1827
|
-
console.log(
|
|
1818
|
+
console.log(" (error: " + (e as Error).message + ")\\n");
|
|
1828
1819
|
}
|
|
1829
1820
|
}
|
|
1830
1821
|
`;
|
|
@@ -2611,6 +2602,7 @@ body::before {
|
|
|
2611
2602
|
`;
|
|
2612
2603
|
/** Map an `add` target to the route folder its page lives in. */
|
|
2613
2604
|
const WEB3_ROUTE_DIR = {
|
|
2605
|
+
"chat": "chat",
|
|
2614
2606
|
"chat-web3": "chat-web3",
|
|
2615
2607
|
"inference-web3": "inference-web3",
|
|
2616
2608
|
"judge-web3": "judge-web3",
|
|
@@ -2677,6 +2669,37 @@ const SCAFFOLD_README_WHAT = {
|
|
|
2677
2669
|
/** A real README for a freshly scaffolded app, replacing the create-next-app
|
|
2678
2670
|
* default so the dev knows what they got and how to use it. */
|
|
2679
2671
|
function scaffoldReadme(target, dir) {
|
|
2672
|
+
if (target === "chat") {
|
|
2673
|
+
return `# LightNode chat (server-paid)
|
|
2674
|
+
|
|
2675
|
+
Generated by \`lightnode add chat\`. A streaming chatbot where YOUR funded wallet
|
|
2676
|
+
pays for every visitor's turn - users never touch a wallet (the classic SaaS
|
|
2677
|
+
chatbot pattern).
|
|
2678
|
+
|
|
2679
|
+
## Run it
|
|
2680
|
+
|
|
2681
|
+
cp .env.example .env # then put a funded mainnet PRIVATE_KEY in it
|
|
2682
|
+
npm run dev
|
|
2683
|
+
|
|
2684
|
+
Open http://localhost:3000 (the chat is the homepage; also served at /chat).
|
|
2685
|
+
Mainnet llama3-8b is ~0.02 LCAI per turn. Free testnet LCAI: https://lightfaucet.ai
|
|
2686
|
+
|
|
2687
|
+
## Where things live
|
|
2688
|
+
|
|
2689
|
+
- \`app/page.tsx\` - re-exports the chat as the homepage
|
|
2690
|
+
- \`app/chat/page.tsx\` - the streaming chat UI (also at /chat). Edit this.
|
|
2691
|
+
- \`app/api/inference/route.ts\` - the streaming server route (uses PRIVATE_KEY)
|
|
2692
|
+
- \`.env.example\` - PRIVATE_KEY (+ NETWORK, MODEL)
|
|
2693
|
+
- \`Dockerfile\` + \`docker-compose.yml\` - run the whole stack with no function timeout
|
|
2694
|
+
- \`LIGHTNODE-HOSTING.md\` - deploy notes
|
|
2695
|
+
|
|
2696
|
+
## Customize
|
|
2697
|
+
|
|
2698
|
+
Change the model or system prompt in \`app/api/inference/route.ts\`. The page is a
|
|
2699
|
+
normal React client component streaming from your route. Builder docs:
|
|
2700
|
+
https://lightnode.app/build
|
|
2701
|
+
`;
|
|
2702
|
+
}
|
|
2680
2703
|
const what = SCAFFOLD_README_WHAT[target] ?? "A self-contained, wallet-signed page.";
|
|
2681
2704
|
return `# LightNode ${dir}
|
|
2682
2705
|
|
package/dist/cli.js
CHANGED
|
@@ -583,7 +583,7 @@ async function main() {
|
|
|
583
583
|
// A Next.js client page needs a Next.js app to live in. In a bare folder,
|
|
584
584
|
// scaffold one first so the generated page renders instead of throwing
|
|
585
585
|
// "Cannot find module 'react'". Opt out with --no-scaffold.
|
|
586
|
-
const didScaffold = NEXT_PAGE_TARGETS.has(sub ?? "") && !existsSync(join(cwd, "package.json")) && !noScaffold
|
|
586
|
+
const didScaffold = (NEXT_PAGE_TARGETS.has(sub ?? "") || sub === "chat") && !existsSync(join(cwd, "package.json")) && !noScaffold
|
|
587
587
|
? scaffoldNextApp(cwd, sub ?? "")
|
|
588
588
|
: false;
|
|
589
589
|
// ---- write the requested files ----
|
|
@@ -617,7 +617,7 @@ async function main() {
|
|
|
617
617
|
// the LightChain theme so localhost:3000 lands on the chat, not the
|
|
618
618
|
// create-next-app starter. Skipped in an existing app (nothing to clobber).
|
|
619
619
|
let wiring = null;
|
|
620
|
-
if (didScaffold && isWeb3Page) {
|
|
620
|
+
if (didScaffold && (isWeb3Page || sub === "chat")) {
|
|
621
621
|
wiring = wireFreshScaffold(sub, { cwd });
|
|
622
622
|
printWritten(wiring.written);
|
|
623
623
|
if (wiring.homepageRoute)
|
|
@@ -669,10 +669,8 @@ async function main() {
|
|
|
669
669
|
console.log(` 3. AGENT_INTERVAL_MS=3600000 npx tsx agent.ts # or run under pm2/systemd`);
|
|
670
670
|
}
|
|
671
671
|
else if (sub === "chat" && result.template === "nextjs-api") {
|
|
672
|
-
console.log(` 3.
|
|
673
|
-
console.log(`
|
|
674
|
-
console.log(` b) npm run dev # local dev only`);
|
|
675
|
-
console.log(` Open http://localhost:3000/chat`);
|
|
672
|
+
console.log(` 3. npm run dev # then open http://localhost:3000${wiring ? "" : "/chat"}`);
|
|
673
|
+
console.log(` (or: docker compose up --build to run the whole stack with no function timeout)`);
|
|
676
674
|
}
|
|
677
675
|
else if (sub === "chat") {
|
|
678
676
|
console.log(` 3. npx tsx chat-repl.ts (interactive terminal chat)`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightnode-sdk",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.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",
|