@xmodeai/sdk 0.1.0 → 0.1.1
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/llms.txt +193 -0
- package/package.json +3 -2
package/llms.txt
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# @xmodeai/sdk — LLM usage guide
|
|
2
|
+
|
|
3
|
+
Official SDK for the xMode API (AI image and video generation). This file is a
|
|
4
|
+
dense, authoritative reference for AI coding agents. For prose docs see
|
|
5
|
+
README.md; for exact types read the bundled `.d.ts`.
|
|
6
|
+
|
|
7
|
+
## Mental model
|
|
8
|
+
|
|
9
|
+
- Generation is **async**: you submit a job (HTTP 202, status `queued`), then
|
|
10
|
+
either poll for the result or receive a signed webhook. The SDK's `*AndWait`
|
|
11
|
+
helpers poll for you.
|
|
12
|
+
- The SDK is a **tolerant reader**: responses are NOT validated. Unknown fields,
|
|
13
|
+
unknown `status` values, and unknown error `code`s pass through without
|
|
14
|
+
throwing. Always handle the `default`/unknown case; never assume the union is
|
|
15
|
+
exhaustive at runtime.
|
|
16
|
+
- Result URLs (`images[].url`, `videos[].url`) are signed and expire in ~23h —
|
|
17
|
+
download or re-host; do not store them long-term.
|
|
18
|
+
- Currency is **xTokens**. `cost.xTokens` on a succeeded record is what was
|
|
19
|
+
debited.
|
|
20
|
+
|
|
21
|
+
## Install & construct
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import XMode from '@xmodeai/sdk';
|
|
25
|
+
|
|
26
|
+
const client = new XMode({
|
|
27
|
+
apiKey: process.env.XMODE_API_KEY, // default: env XMODE_API_KEY. Required.
|
|
28
|
+
baseUrl: 'https://api.xmode.ai', // default
|
|
29
|
+
timeout: 30_000, // per-attempt ms, default
|
|
30
|
+
maxRetries: 2, // default
|
|
31
|
+
fetch: globalThis.fetch, // override for tests / custom runtimes
|
|
32
|
+
defaultHeaders: {}, // merged into every request
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Runtimes: Node ≥18.17, Bun, Deno, Cloudflare Workers, browsers (key exposure!).
|
|
37
|
+
Zero runtime dependencies; uses the platform `fetch`.
|
|
38
|
+
|
|
39
|
+
## Method surface
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
client.generations.create(input: CreateGenerationInput, opts?) => Promise<QueuedGenerationRecord>
|
|
43
|
+
client.generations.get(requestId: string, opts?) => Promise<GenerationRecord>
|
|
44
|
+
client.generations.list(params?: GenerationListParams, opts?) => PagePromise<GenerationRecord>
|
|
45
|
+
client.generations.createAndWait(input, wait?: WaitOptions) => Promise<GenerationRecord> // poll default 10 min
|
|
46
|
+
client.generations.wait(requestId, wait?: WaitOptions) => Promise<GenerationRecord>
|
|
47
|
+
|
|
48
|
+
client.videos.create(input: CreateVideoInput, opts?) => Promise<QueuedVideoRecord>
|
|
49
|
+
client.videos.get(requestId, opts?) => Promise<VideoRecord>
|
|
50
|
+
client.videos.createAndWait(input, wait?: WaitOptions) => Promise<VideoRecord> // poll default 20 min
|
|
51
|
+
client.videos.wait(requestId, wait?: WaitOptions) => Promise<VideoRecord>
|
|
52
|
+
|
|
53
|
+
client.balance.get(opts?) => Promise<BalanceResponse>
|
|
54
|
+
client.balance.history(params?, opts?) => PagePromise<BalanceLedgerEntry>
|
|
55
|
+
|
|
56
|
+
client.endUsers.list(params?: EndUserListParams, opts?) => PagePromise<EndUserRecord>
|
|
57
|
+
client.endUsers.block(email: string, opts?) => Promise<BlockEndUserResponse>
|
|
58
|
+
client.endUsers.unblock(email: string, opts?) => Promise<UnblockEndUserResponse>
|
|
59
|
+
client.endUsers.delete(email: string, opts?) => Promise<DeleteEndUserResponse> // 409 if pending jobs
|
|
60
|
+
|
|
61
|
+
client.request<T = unknown>({ method, path, body?, query?, policy?, signal?, timeout?, maxRetries? }) => Promise<T>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`opts` (RequestOptions): `{ signal?, timeout?, maxRetries?, headers? }`.
|
|
65
|
+
`WaitOptions`: `{ pollInterval? = 5000, timeout?, signal? }`.
|
|
66
|
+
|
|
67
|
+
## Inputs
|
|
68
|
+
|
|
69
|
+
CreateGenerationInput:
|
|
70
|
+
- `endUserEmail` (required) — your end-user's email; groups jobs per user.
|
|
71
|
+
- `prompt` (required) — 1–8000 chars, English.
|
|
72
|
+
- `model` (required) — alias string, e.g. `'xSD4.5'`. Any string is accepted
|
|
73
|
+
(forward-compat); known aliases autocomplete.
|
|
74
|
+
- `references?` — single HTTPS URL or array of 1–10 URLs.
|
|
75
|
+
- `size?` — `'2K'` | `'4K'` | explicit `'WxH'` (e.g. `'2048x2048'`). Default `2K`.
|
|
76
|
+
- `aspectRatio?` — `'auto' | 'square' | 'landscape' | 'portrait'`.
|
|
77
|
+
- `n?` — 1–15 images (each billed). Constraint: `references.length + n ≤ 15`.
|
|
78
|
+
- `seed?`, `watermark?`, `guidanceScale?` (1–20), `responseFormat?` (`'url'|'b64_json'`).
|
|
79
|
+
- `sequential?` (`'auto'|'disabled'`) + `sequentialOptions?: { maxImages }` for connected series.
|
|
80
|
+
- `webhookUrl?` — HTTPS URL to receive the terminal record.
|
|
81
|
+
|
|
82
|
+
CreateVideoInput (model `'xW2.7S'`):
|
|
83
|
+
- `endUserEmail`, `model`, `prompt`, `image` (HTTPS URL) — required.
|
|
84
|
+
- `negative_prompt?`, `audio_url?` (HTTPS), `resolution?` (`'720p'|'1080p'`, default `'1080p'`),
|
|
85
|
+
`duration?` (2–15s, default 5), `prompt_extend?` (default true), `seed?` (number|null), `webhookUrl?`.
|
|
86
|
+
|
|
87
|
+
## Records (responses)
|
|
88
|
+
|
|
89
|
+
Discriminated by `status`: `'queued' | 'processing' | 'succeeded' | 'failed' | 'expired'`
|
|
90
|
+
(plus possible future values — handle the default case).
|
|
91
|
+
|
|
92
|
+
- queued: `{ requestId, status:'queued', model, prompt, referencesCount, endUserEmail, createdAt, pollUrl }`
|
|
93
|
+
- processing: like queued without `pollUrl`.
|
|
94
|
+
- succeeded (image): `+ images: { id, url?, error?, size?, expiresAt }[], cost: { xTokens }, finishedAt`
|
|
95
|
+
- succeeded (video): `+ videos: { id, url?, error?, duration?, resolution?, expiresAt }[], cost, finishedAt`
|
|
96
|
+
- failed: `+ error: { code, message }, finishedAt`
|
|
97
|
+
- expired: `+ finishedAt` (URLs already dead).
|
|
98
|
+
|
|
99
|
+
An image record always has `referencesCount`; a video record never does — use
|
|
100
|
+
that to discriminate (see `isVideoEvent`).
|
|
101
|
+
|
|
102
|
+
## Recipes
|
|
103
|
+
|
|
104
|
+
Submit and wait:
|
|
105
|
+
```ts
|
|
106
|
+
const r = await client.generations.createAndWait({
|
|
107
|
+
endUserEmail: 'alice@your-product.com', model: 'xSD4.5', prompt: 'a red panda astronaut',
|
|
108
|
+
});
|
|
109
|
+
if (r.status === 'succeeded') console.log(r.images[0]?.url);
|
|
110
|
+
else if (r.status === 'failed') console.error(r.error.code, r.error.message);
|
|
111
|
+
// createAndWait/wait RETURN failed/expired records — they only throw on poll timeout.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Manual polling:
|
|
115
|
+
```ts
|
|
116
|
+
const queued = await client.generations.create({ endUserEmail, model: 'xSD4.5', prompt });
|
|
117
|
+
let rec = await client.generations.get(queued.requestId);
|
|
118
|
+
// recommended poll interval: 5s. Terminal = succeeded|failed|expired.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Paginate (await one page, or for-await all items):
|
|
122
|
+
```ts
|
|
123
|
+
const page = await client.generations.list({ pageSize: 50, status: 'failed' });
|
|
124
|
+
for await (const g of client.generations.list({ endUserEmail: 'alice@your-product.com' })) {
|
|
125
|
+
console.log(g.requestId);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Verify a webhook (subpath `@xmodeai/sdk/webhooks`, pass the RAW body):
|
|
130
|
+
```ts
|
|
131
|
+
import { verifyWebhook, isVideoEvent } from '@xmodeai/sdk/webhooks';
|
|
132
|
+
const { event } = await verifyWebhook({ payload: rawBody, headers: req.headers, secret });
|
|
133
|
+
if (event.status === 'succeeded') {/* same shape as generations.get / videos.get */}
|
|
134
|
+
// throws WebhookVerificationError (reason: missing_header|invalid_signature|timestamp_out_of_tolerance|malformed)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Use a not-yet-typed API parameter/endpoint without waiting for an SDK release:
|
|
138
|
+
```ts
|
|
139
|
+
await client.request({
|
|
140
|
+
method: 'POST', path: '/v1/videos',
|
|
141
|
+
body: { endUserEmail, model: 'xSV2.0', prompt, image, cameraMotion: 'orbit' },
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Errors
|
|
146
|
+
|
|
147
|
+
All API errors extend `XModeAPIError` (`{ status, code, message, fields?, requestId? }`),
|
|
148
|
+
subclassed by `code`:
|
|
149
|
+
|
|
150
|
+
| class | code(s) | http |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| ValidationError | validation_error | 400 |
|
|
153
|
+
| AuthenticationError | unauthorized, account_blocked | 401 |
|
|
154
|
+
| PaymentRequiredError | payment_required | 402 |
|
|
155
|
+
| PermissionDeniedError | forbidden | 403 |
|
|
156
|
+
| NotFoundError | not_found | 404 |
|
|
157
|
+
| ConflictError | conflict | 409 |
|
|
158
|
+
| ContentPolicyError | content_policy | 422 |
|
|
159
|
+
| RateLimitError | rate_limited (+retryAfter) | 429 |
|
|
160
|
+
| ProviderError | provider_error, provider_timeout | 5xx |
|
|
161
|
+
| InternalServerError | internal_error | 5xx |
|
|
162
|
+
|
|
163
|
+
Unknown `code` → base `XModeAPIError` (never a mapping crash). Network failures →
|
|
164
|
+
`APIConnectionError` / `APIConnectionTimeoutError`. Poll budget exceeded →
|
|
165
|
+
`XModePollTimeoutError` (carries `lastRecord`).
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { ContentPolicyError, RateLimitError } from '@xmodeai/sdk';
|
|
169
|
+
try { await client.generations.create({ endUserEmail, model: 'xSD4.5', prompt }); }
|
|
170
|
+
catch (e) {
|
|
171
|
+
if (e instanceof ContentPolicyError) {/* prompt rejected */}
|
|
172
|
+
else if (e instanceof RateLimitError) {/* back off; e.retryAfter seconds */}
|
|
173
|
+
else throw e;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Retries (important)
|
|
178
|
+
|
|
179
|
+
- GET / `block` / `unblock` / `delete` → retried on network errors, 408, 429, 5xx.
|
|
180
|
+
- `generations.create` / `videos.create` → retried **only on 429**. They debit
|
|
181
|
+
xTokens and have no idempotency key, so a timed-out/5xx create is NOT retried
|
|
182
|
+
automatically (it may have succeeded). Re-submitting risks a double charge.
|
|
183
|
+
|
|
184
|
+
Backoff is exponential with jitter and honors `Retry-After`. Tune with
|
|
185
|
+
`maxRetries` (client or per-call).
|
|
186
|
+
|
|
187
|
+
## Don'ts
|
|
188
|
+
|
|
189
|
+
- Don't assume `status`/`error.code` is one of the known literals at runtime —
|
|
190
|
+
new values can appear; handle the default branch.
|
|
191
|
+
- Don't re-serialize the webhook body before `verifyWebhook` — pass the raw string.
|
|
192
|
+
- Don't retry a failed `create` blindly — check via `list`/`get` first.
|
|
193
|
+
- Don't store result URLs long-term — they expire (~23h).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmodeai/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Official TypeScript/JavaScript SDK for the xMode API — AI image and video generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"files": [
|
|
38
38
|
"dist",
|
|
39
39
|
"README.md",
|
|
40
|
-
"LICENSE"
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"llms.txt"
|
|
41
42
|
],
|
|
42
43
|
"publishConfig": {
|
|
43
44
|
"access": "public"
|