@prismnetwork/agent-sdk 0.1.0 → 0.2.0

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 (3) hide show
  1. package/README.md +3 -3
  2. package/package.json +29 -8
  3. package/prism.mjs +41 -17
package/README.md CHANGED
@@ -4,12 +4,12 @@ Headless GPU leasing on [Prism Network](https://prismnetwork.tech) for autonomou
4
4
 
5
5
  ## Install
6
6
 
7
- Not yet published to npm. Until it is, install it from the repo alongside its `viem` peer dependency:
8
-
9
7
  ```
10
- npm install /path/to/prism-public/sdk viem
8
+ npm install @prismnetwork/agent-sdk viem
11
9
  ```
12
10
 
11
+ `viem` is a peer dependency.
12
+
13
13
  ## Use
14
14
 
15
15
  ```js
package/package.json CHANGED
@@ -1,21 +1,42 @@
1
1
  {
2
2
  "name": "@prismnetwork/agent-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless GPU leasing on Prism Network for wallet-holding agents.",
5
5
  "type": "module",
6
6
  "main": "prism.mjs",
7
- "exports": { ".": "./prism.mjs" },
8
- "files": ["prism.mjs", "README.md"],
9
- "engines": { "node": ">=20" },
10
- "peerDependencies": { "viem": "^2" },
11
- "keywords": ["prism", "gpu", "agent", "web3", "usdg", "compute", "llm"],
7
+ "exports": {
8
+ ".": "./prism.mjs"
9
+ },
10
+ "files": [
11
+ "prism.mjs",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "peerDependencies": {
18
+ "viem": "^2"
19
+ },
20
+ "keywords": [
21
+ "prism",
22
+ "gpu",
23
+ "agent",
24
+ "web3",
25
+ "usdg",
26
+ "compute",
27
+ "llm"
28
+ ],
12
29
  "homepage": "https://prismnetwork.tech",
13
30
  "repository": {
14
31
  "type": "git",
15
32
  "url": "git+https://github.com/prismnetwork-tech/prism.git",
16
33
  "directory": "sdk"
17
34
  },
18
- "bugs": { "url": "https://github.com/prismnetwork-tech/prism/issues" },
35
+ "bugs": {
36
+ "url": "https://github.com/prismnetwork-tech/prism/issues"
37
+ },
19
38
  "license": "Apache-2.0",
20
- "publishConfig": { "access": "public" }
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
21
42
  }
package/prism.mjs CHANGED
@@ -29,6 +29,10 @@ export const USDG = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";
29
29
  export const DEFAULT_IMAGE =
30
30
  "docker.io/ollama/ollama@sha256:a61a8fd395dbb931cc8cb1b5da7a2510746575c87113fdc45b647ee59ef7f808";
31
31
 
32
+ // Weakest to strongest. "open" means the supplier can read everything the
33
+ // workload touches, so an agent handling anything sensitive should raise this.
34
+ export const TRUST_CLASSES = ["open", "isolated", "attested", "confidential"];
35
+
32
36
  const CONFIRMATIONS = 12;
33
37
  const FETCH_TIMEOUT_MS = 30_000;
34
38
 
@@ -42,6 +46,13 @@ const escrowAbi = parseAbi([
42
46
  "function createLease(bytes32 nodeId, uint32 duration, bytes32 clientReference) returns (uint256)",
43
47
  ]);
44
48
 
49
+ function assertTrustClass(value) {
50
+ if (!TRUST_CLASSES.includes(value)) {
51
+ throw new PrismError(400, "invalid_trust_class", { expected: TRUST_CLASSES });
52
+ }
53
+ return value;
54
+ }
55
+
45
56
  function parseBaseUnits(value, field) {
46
57
  if (typeof value === "number" && Number.isInteger(value) && value >= 0) return BigInt(value);
47
58
  if (typeof value === "string" && /^[0-9]+$/.test(value)) return BigInt(value);
@@ -96,8 +107,8 @@ export class PrismAgent {
96
107
  return session;
97
108
  }
98
109
 
99
- async offers() {
100
- return this.#proxy("GET", ["offers"]);
110
+ async offers({ minTrust = "open" } = {}) {
111
+ return this.#proxy("GET", ["offers"], { query: { min_trust: assertTrustClass(minTrust) } });
101
112
  }
102
113
 
103
114
  async balances() {
@@ -125,18 +136,21 @@ export class PrismAgent {
125
136
  }
126
137
  }
127
138
 
128
- async quote({ image, durationSeconds, minVramMib = 16000, preferredNodeId = null } = {}) {
139
+ async quote({ image, durationSeconds, minVramMib = 16000, preferredNodeId = null, minTrustClass = "open" } = {}) {
129
140
  if (typeof image !== "string" || !/@sha256:[0-9a-f]{64}$/.test(image)) {
130
141
  throw new PrismError(400, "image_must_be_digest_pinned", { hint: "use ollama@sha256:... or DEFAULT_IMAGE" });
131
142
  }
132
143
  if (!Number.isInteger(durationSeconds) || durationSeconds <= 0) throw new PrismError(400, "invalid_duration");
133
144
  if (!Number.isInteger(minVramMib) || minVramMib <= 0) throw new PrismError(400, "invalid_min_vram_mib");
134
145
  return this.#proxy("POST", ["leases", "match"], {
135
- request: {
136
- image,
137
- duration_seconds: durationSeconds,
138
- min_vram_mib: minVramMib,
139
- preferred_node_id: preferredNodeId,
146
+ body: {
147
+ request: {
148
+ image,
149
+ duration_seconds: durationSeconds,
150
+ min_vram_mib: minVramMib,
151
+ preferred_node_id: preferredNodeId,
152
+ min_trust_class: assertTrustClass(minTrustClass),
153
+ },
140
154
  },
141
155
  });
142
156
  }
@@ -185,9 +199,11 @@ export class PrismAgent {
185
199
 
186
200
  async confirm({ quoteId, transactionHash, sshAuthorizedKey }) {
187
201
  return this.#proxy("POST", ["leases", "confirm"], {
188
- quote_id: quoteId,
189
- transaction_hash: transactionHash,
190
- ssh_authorized_key: sshAuthorizedKey,
202
+ body: {
203
+ quote_id: quoteId,
204
+ transaction_hash: transactionHash,
205
+ ssh_authorized_key: sshAuthorizedKey,
206
+ },
191
207
  });
192
208
  }
193
209
 
@@ -202,7 +218,7 @@ export class PrismAgent {
202
218
  async waitForAccess(leaseId, { timeoutMs = 600_000, intervalMs = 10_000 } = {}) {
203
219
  const deadline = Date.now() + timeoutMs;
204
220
  while (Date.now() < deadline) {
205
- const res = await this.#proxy("GET", ["leases", String(leaseId), "access"], null, true);
221
+ const res = await this.#proxy("GET", ["leases", String(leaseId), "access"], { raw: true });
206
222
  if (res.status === 200) {
207
223
  if (!res.body?.ssh_host && res.body?.mode !== "gateway") throw new PrismError(502, "malformed_access");
208
224
  return res.body;
@@ -214,9 +230,16 @@ export class PrismAgent {
214
230
  }
215
231
 
216
232
  // quote -> ssh keygen -> fund on-chain -> confirm -> wait for access.
217
- async lease({ image, durationSeconds, minVramMib, preferredNodeId = null, maxDeposit = null } = {}) {
233
+ async lease({
234
+ image,
235
+ durationSeconds,
236
+ minVramMib,
237
+ preferredNodeId = null,
238
+ maxDeposit = null,
239
+ minTrustClass = "open",
240
+ } = {}) {
218
241
  if (!this.session) await this.authenticate();
219
- const quote = await this.quote({ image, durationSeconds, minVramMib, preferredNodeId });
242
+ const quote = await this.quote({ image, durationSeconds, minVramMib, preferredNodeId, minTrustClass });
220
243
  if (maxDeposit != null && parseBaseUnits(quote.maximum_escrow, "maximum_escrow") > BigInt(maxDeposit)) {
221
244
  throw new PrismError(402, "cost_exceeds_max", { required: quote.maximum_escrow, max: String(maxDeposit) });
222
245
  }
@@ -325,9 +348,10 @@ export class PrismAgent {
325
348
  });
326
349
  }
327
350
 
328
- async #proxy(method, segments, body = null, raw = false, reauthed = false) {
351
+ async #proxy(method, segments, { body = null, raw = false, query = null, reauthed = false } = {}) {
329
352
  if (!this.session) await this.authenticate();
330
- const res = await this.#fetch(`/api/agent/proxy/${segments.join("/")}`, {
353
+ const search = query ? `?${new URLSearchParams(query)}` : "";
354
+ const res = await this.#fetch(`/api/agent/proxy/${segments.join("/")}${search}`, {
331
355
  method,
332
356
  body,
333
357
  headers: { authorization: `Bearer ${this.session}` },
@@ -336,7 +360,7 @@ export class PrismAgent {
336
360
  if (res.status === 401 && !reauthed) {
337
361
  this.session = null;
338
362
  await this.authenticate();
339
- return this.#proxy(method, segments, body, raw, true);
363
+ return this.#proxy(method, segments, { body, raw, query, reauthed: true });
340
364
  }
341
365
  if (raw) return { status: res.status, body: await res.json().catch(() => null) };
342
366
  return this.#unwrap(res);