@hoodcompute/sdk 0.1.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.
- package/LICENSE +21 -0
- package/README.md +332 -0
- package/dist/chunk-72VFNNOI.js +575 -0
- package/dist/chunk-72VFNNOI.js.map +1 -0
- package/dist/client-CIPi_mUM.d.cts +418 -0
- package/dist/client-CIPi_mUM.d.ts +418 -0
- package/dist/index.cjs +625 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +181 -0
- package/dist/index.d.ts +181 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +642 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +64 -0
- package/dist/react.d.ts +64 -0
- package/dist/react.js +74 -0
- package/dist/react.js.map +1 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HoodCompute
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# @hoodcompute/sdk
|
|
2
|
+
|
|
3
|
+
The official TypeScript SDK for [HoodCompute](https://hoodcompute.com), the decentralized AI inference network on Robinhood Chain.
|
|
4
|
+
|
|
5
|
+
Private, censorship-free inference from a distributed pool of GPU providers. Every job is settled on-chain, so every completion comes with a receipt you can verify yourself on Blockscout.
|
|
6
|
+
|
|
7
|
+
- **OpenAI-compatible** chat completions. If you know the OpenAI SDK, you know this one.
|
|
8
|
+
- **Streaming** token delivery over Server-Sent Events, as an async iterable.
|
|
9
|
+
- **On-chain receipts** attached to every completion. Job ID and settlement transaction, no extra call.
|
|
10
|
+
- **Typed end to end.** Full TypeScript types for requests, responses, jobs, models, and webhooks.
|
|
11
|
+
- **React hooks** in a separate entry point, so the core stays framework-agnostic.
|
|
12
|
+
- **Zero runtime dependencies.** Built on the platform `fetch`.
|
|
13
|
+
|
|
14
|
+
> **Beta.** HoodCompute is an early, real, working network. The SDK follows semantic versioning. Expect additive changes as the network grows.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @hoodcompute/sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Node 18 or newer (for the built-in `fetch`), or any modern browser or edge runtime.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
Get an API key from the Settings tab at [hoodcompute.com/app/settings](https://hoodcompute.com/app/settings). Keys are formatted `hoodc_live_...` and are linked to your wallet's credit balance.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { HoodComputeClient } from "@hoodcompute/sdk"
|
|
34
|
+
|
|
35
|
+
const client = new HoodComputeClient({
|
|
36
|
+
apiKey: process.env.HOODCOMPUTE_API_KEY,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const completion = await client.chat.completions.create({
|
|
40
|
+
model: "qwen3-8b",
|
|
41
|
+
messages: [{ role: "user", content: "What is an ERC-4337 smart account?" }],
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
console.log(completion.choices[0].message.content)
|
|
45
|
+
console.log("Job:", completion.jobId)
|
|
46
|
+
console.log("Settled:", completion.settlementTx)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If you omit `apiKey`, the client reads `HOODCOMPUTE_API_KEY` from the environment.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Streaming
|
|
54
|
+
|
|
55
|
+
Pass `stream: true` to receive an async iterable of chunks. Once the stream closes, the settlement receipt is available on the stream object.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { HoodComputeClient, explorerTxUrl } from "@hoodcompute/sdk"
|
|
59
|
+
|
|
60
|
+
const client = new HoodComputeClient()
|
|
61
|
+
|
|
62
|
+
const stream = await client.chat.completions.create({
|
|
63
|
+
model: "llama-3.3-70b",
|
|
64
|
+
messages: [{ role: "user", content: "Explain how optimistic rollups work." }],
|
|
65
|
+
stream: true,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
for await (const chunk of stream) {
|
|
69
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? "")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log("\nSettled:", explorerTxUrl(stream.receipt.settlementTx!))
|
|
73
|
+
console.log("Credits remaining:", stream.receipt.creditsRemaining)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Need the whole response as a string? Use `await stream.text()`.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## On-chain receipts
|
|
81
|
+
|
|
82
|
+
Every completed job has a receipt whose transaction hashes resolve on the Robinhood Chain [Blockscout explorer](https://robinhoodchain.blockscout.com). The receipt never contains prompt or completion content, only settlement metadata.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Fetch the full receipt for any settled job
|
|
86
|
+
const receipt = await client.jobs.getReceipt("job_8fx2kp3m9qrstvwxyz")
|
|
87
|
+
|
|
88
|
+
console.log(receipt.workerAddress) // 0x9d24ab7e...
|
|
89
|
+
console.log(receipt.creditsCharged) // 8
|
|
90
|
+
console.log(receipt.settlementTx) // 0x3a91d5c0...
|
|
91
|
+
|
|
92
|
+
import { explorerTxUrl } from "@hoodcompute/sdk"
|
|
93
|
+
console.log(explorerTxUrl(receipt.settlementTx))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
You do not need to trust the API's report of what happened. The Robinhood Chain transaction is the authoritative record.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Models
|
|
101
|
+
|
|
102
|
+
The model list reflects the live worker pool. A model appears only while at least one worker is hosting it.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const { data: models } = await client.models.list()
|
|
106
|
+
|
|
107
|
+
for (const model of models) {
|
|
108
|
+
console.log(
|
|
109
|
+
`${model.id} ${model.hoodcompute.tier} ` +
|
|
110
|
+
`${model.hoodcompute.activeWorkers} workers ` +
|
|
111
|
+
`~${model.hoodcompute.medianLatencyMs}ms`,
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Inspect one model before submitting a job
|
|
116
|
+
const qwen = await client.models.retrieve("qwen3-8b")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Model ID | Parameters | Context | Tier | Credits/request |
|
|
120
|
+
|---|---|---|---|---|
|
|
121
|
+
| `llama-3.3-70b` | 70B | 128K | Max | 40 |
|
|
122
|
+
| `deepseek-r1` | 70B | 128K | Max | 40 |
|
|
123
|
+
| `qwen3-14b` | 14B | 32K | Pro | 18 |
|
|
124
|
+
| `qwen3-8b` | 8B | 32K | Standard | 8 |
|
|
125
|
+
| `mistral-7b` | 7B | 32K | Standard | 8 |
|
|
126
|
+
| `llama-3.2-3b` | 3B | 128K | Lite | 2 |
|
|
127
|
+
|
|
128
|
+
Call `client.models.list()` for the current catalog. One credit is $0.01.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Jobs
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Retrieve a job, including its on-chain settlement detail
|
|
136
|
+
const job = await client.jobs.get("job_8fx2kp3m9qrstvwxyz")
|
|
137
|
+
console.log(job.status) // "settled"
|
|
138
|
+
console.log(job.onChain?.settlementTx)
|
|
139
|
+
|
|
140
|
+
// List recent jobs
|
|
141
|
+
const jobs = await client.jobs.list({ limit: 20, status: "settled" })
|
|
142
|
+
for (const j of jobs.data) {
|
|
143
|
+
console.log(`${j.id} ${j.model} ${j.creditsCharged} credits`)
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Disputes
|
|
148
|
+
|
|
149
|
+
If a worker's on-chain proof does not match the output you received, open a dispute within 60 seconds of the final token. Hash the full response text you received (SHA-256, UTF-8, no trailing newline) and submit it.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const result = await client.jobs.dispute(
|
|
153
|
+
"job_8fx2kp3m9qrstvwxyz",
|
|
154
|
+
"sha256:a1b2c3d4...",
|
|
155
|
+
)
|
|
156
|
+
console.log(result.disputeOpened, result.disputeTx)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Account and credits
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const account = await client.account.get()
|
|
165
|
+
console.log(account.wallet) // 0x7f3ce8b1...
|
|
166
|
+
console.log(account.creditsRemaining) // 1420
|
|
167
|
+
console.log(account.usdgValue) // 14.20
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## React hooks
|
|
173
|
+
|
|
174
|
+
Import from the `@hoodcompute/sdk/react` entry point. `react` is an optional peer dependency, so it is pulled in only when you use this subpath.
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { useHoodComputeChat } from "@hoodcompute/sdk/react"
|
|
178
|
+
|
|
179
|
+
function Chat() {
|
|
180
|
+
const { send, messages, status, balance, lastReceipt } = useHoodComputeChat({
|
|
181
|
+
model: "qwen3-8b",
|
|
182
|
+
apiKey: process.env.NEXT_PUBLIC_HOODCOMPUTE_API_KEY,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<div>
|
|
187
|
+
<p>Credits: {balance?.creditsRemaining ?? "..."}</p>
|
|
188
|
+
{messages.map((m, i) => (
|
|
189
|
+
<p key={i}>
|
|
190
|
+
<strong>{m.role}:</strong> {m.content}
|
|
191
|
+
</p>
|
|
192
|
+
))}
|
|
193
|
+
<button disabled={status === "streaming"} onClick={() => send("Hello")}>
|
|
194
|
+
Send
|
|
195
|
+
</button>
|
|
196
|
+
{lastReceipt?.settlementTx && (
|
|
197
|
+
<a href={`https://robinhoodchain.blockscout.com/tx/${lastReceipt.settlementTx}`}>
|
|
198
|
+
View settlement
|
|
199
|
+
</a>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Calling the API directly from the browser exposes your key. In production, proxy requests through your own server. During beta, if you must call from the browser, use a `NEXT_PUBLIC_`-scoped key you can revoke at any time.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Webhooks
|
|
211
|
+
|
|
212
|
+
Verify the `HoodCompute-Signature` header against the raw request body before parsing it.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { constructWebhookEvent } from "@hoodcompute/sdk"
|
|
216
|
+
|
|
217
|
+
app.post("/hoodcompute-events", express.raw({ type: "application/json" }), (req, res) => {
|
|
218
|
+
try {
|
|
219
|
+
const event = constructWebhookEvent(
|
|
220
|
+
req.body.toString(),
|
|
221
|
+
req.header("HoodCompute-Signature"),
|
|
222
|
+
process.env.HOODCOMPUTE_WEBHOOK_SECRET!,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
if (event.event === "job.completed") {
|
|
226
|
+
console.log("Settled:", event.data.settlement_tx)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
res.json({ received: true })
|
|
230
|
+
} catch {
|
|
231
|
+
res.status(400).json({ error: "Invalid signature" })
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`verifyWebhookSignature(rawBody, signature, secret)` is also exported if you want to verify without parsing.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Configuration
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
const client = new HoodComputeClient({
|
|
244
|
+
apiKey: "hoodc_live_...", // or HOODCOMPUTE_API_KEY
|
|
245
|
+
baseURL: "https://api.hoodcompute.com/v1", // default
|
|
246
|
+
timeout: 120_000, // per-request timeout, ms
|
|
247
|
+
maxRetries: 2, // retries on 5xx, 429, network errors
|
|
248
|
+
defaultHeaders: { "x-app-version": "1.0.0" },
|
|
249
|
+
})
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Every request method accepts an `AbortSignal` for cancellation:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const controller = new AbortController()
|
|
256
|
+
const promise = client.chat.completions.create(
|
|
257
|
+
{ model: "qwen3-8b", messages },
|
|
258
|
+
{ signal: controller.signal },
|
|
259
|
+
)
|
|
260
|
+
controller.abort()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Error handling
|
|
266
|
+
|
|
267
|
+
Failed requests throw a typed `HoodComputeError` subclass. Retryable conditions (5xx, 429, network failures, and `no_workers_available`) are retried automatically up to `maxRetries`.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import {
|
|
271
|
+
HoodComputeError,
|
|
272
|
+
InsufficientCreditsError,
|
|
273
|
+
NoWorkersAvailableError,
|
|
274
|
+
} from "@hoodcompute/sdk"
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
await client.chat.completions.create({ model, messages })
|
|
278
|
+
} catch (err) {
|
|
279
|
+
if (err instanceof InsufficientCreditsError) {
|
|
280
|
+
console.error("Add credits to continue.")
|
|
281
|
+
} else if (err instanceof NoWorkersAvailableError) {
|
|
282
|
+
console.error(`No workers online. Retry in ${err.retryAfter}s.`)
|
|
283
|
+
} else if (err instanceof HoodComputeError) {
|
|
284
|
+
console.error(err.code, err.message)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
| Class | Status | When |
|
|
290
|
+
|---|---|---|
|
|
291
|
+
| `AuthenticationError` | 401, 403 | Missing, invalid, revoked, or suspended key |
|
|
292
|
+
| `InsufficientCreditsError` | 402 | Credit balance too low for the tier |
|
|
293
|
+
| `InvalidRequestError` | 400, 422 | Malformed request or invalid field |
|
|
294
|
+
| `NotFoundError` | 404 | Job, model, or resource does not exist |
|
|
295
|
+
| `RateLimitError` | 429 | Request rate to the API too high |
|
|
296
|
+
| `NoWorkersAvailableError` | 503 | No worker hosting the model right now |
|
|
297
|
+
| `JobTimeoutError` | 504 | No worker completed within the window, credits refunded |
|
|
298
|
+
| `ServerError` | 5xx | Internal error, usually safe to retry |
|
|
299
|
+
| `ConnectionError` | none | Network failure, timeout, or aborted request |
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Migrating from the OpenAI SDK
|
|
304
|
+
|
|
305
|
+
Requests and responses share the OpenAI shape, so most code changes only the client and the model ID.
|
|
306
|
+
|
|
307
|
+
```diff
|
|
308
|
+
- import OpenAI from "openai"
|
|
309
|
+
- const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
|
|
310
|
+
+ import { HoodComputeClient } from "@hoodcompute/sdk"
|
|
311
|
+
+ const client = new HoodComputeClient({ apiKey: process.env.HOODCOMPUTE_API_KEY })
|
|
312
|
+
|
|
313
|
+
const completion = await client.chat.completions.create({
|
|
314
|
+
- model: "gpt-4o",
|
|
315
|
+
+ model: "llama-3.3-70b",
|
|
316
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
The HoodCompute responses add `jobId`, `settlementTx`, and `creditsRemaining` on top of the standard fields.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Documentation
|
|
325
|
+
|
|
326
|
+
- API reference: [docs.hoodcompute.com](https://docs.hoodcompute.com)
|
|
327
|
+
- Concept and architecture: [hoodcompute.com](https://hoodcompute.com)
|
|
328
|
+
- Explorer: [robinhoodchain.blockscout.com](https://robinhoodchain.blockscout.com)
|
|
329
|
+
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
MIT
|