@civitai/blocks-react 0.76.0 → 0.77.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/README.md +64 -53
- package/dist/hooks/useImageUpload.d.ts +40 -6
- package/dist/hooks/useImageUpload.js +19 -3
- package/dist/hooks/useRunTraining.d.ts +31 -10
- package/dist/hooks/useRunTraining.js +32 -7
- package/dist/hooks/useUploadImageBytes.d.ts +1 -1
- package/dist/hooks/useUploadImageBytes.js +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +0 -1
- package/dist/internal/liveHost.d.ts +5 -9
- package/dist/internal/liveHost.js +21 -29
- package/dist/internal/mockHost.d.ts +40 -46
- package/dist/internal/mockHost.js +160 -84
- package/dist/internal/uploadBytes.d.ts +2 -2
- package/dist/internal/uploadBytes.js +2 -2
- package/dist/transport/requestTimeouts.d.ts +19 -7
- package/dist/transport/requestTimeouts.js +19 -12
- package/dist/transport/validate.d.ts +7 -17
- package/dist/transport/validate.js +21 -47
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +2 -2
- package/dist/hooks/usePrepareTrainingDataset.d.ts +0 -99
- package/dist/hooks/usePrepareTrainingDataset.js +0 -159
package/README.md
CHANGED
|
@@ -1463,43 +1463,55 @@ options drive both arms (including `declined`). **`dev:live` refuses this bridge
|
|
|
1463
1463
|
it has no civitai chrome to render the server-resolved confirm in, and driving
|
|
1464
1464
|
the write without it would let dev prove out a flow production does not have.
|
|
1465
1465
|
|
|
1466
|
-
### `
|
|
1467
|
-
|
|
1468
|
-
Train a LoRA with the ai-toolkit engine on the
|
|
1469
|
-
inside a page app — the App Blocks `kind: 'training'` flow
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1466
|
+
### `useImageUpload({ purpose: 'trainingData' })` and `useRunTraining()` — LoRA training
|
|
1467
|
+
|
|
1468
|
+
Train a LoRA with the ai-toolkit engine on **images the viewer uploads**, from
|
|
1469
|
+
inside a page app — the App Blocks `kind: 'training'` flow, with the same upload
|
|
1470
|
+
path and `{ air, caption }` dataset shape as Civitai's own training form. Four
|
|
1471
|
+
steps, three hooks:
|
|
1472
|
+
|
|
1473
|
+
1. **Upload** — `useImageUpload({ purpose: 'trainingData' }).open()`
|
|
1474
|
+
(`OPEN_IMAGE_UPLOAD { purpose: 'trainingData' }`). Civitai opens an upload
|
|
1475
|
+
modal in **its own chrome**; the viewer picks up to 50 PNG, JPEG or WebP
|
|
1476
|
+
images, and each is uploaded as a private blob (not published). `open()`
|
|
1477
|
+
resolves with every image that uploaded, `[{ url, air }]`, or `null` when
|
|
1478
|
+
the viewer cancels. Pair each `air` with your caption — captions up to 8,192
|
|
1479
|
+
characters, one blob at most once. The host answers only when its modal
|
|
1480
|
+
closes, after every original has uploaded, so `open()` waits up to an hour,
|
|
1481
|
+
not the ten minutes the other pickers use.
|
|
1481
1482
|
2. **Quote** — `useBuzzWorkflow().estimate(body)` with a `WorkflowBodyTraining`
|
|
1482
|
-
(`kind: 'training'`, no `quoteId`). The reply
|
|
1483
|
-
{ quoteId, total, imageCount, expiresAt }` — the
|
|
1484
|
-
exactly this run, stored server-side for 15
|
|
1483
|
+
(`kind: 'training'`, `items: [{ air, caption }]`, no `quoteId`). The reply
|
|
1484
|
+
carries `trainingQuote: { quoteId, total, imageCount, expiresAt }` — the
|
|
1485
|
+
orchestrator's own price for exactly this run, stored server-side for 15
|
|
1486
|
+
minutes.
|
|
1485
1487
|
3. **Run** — `useRunTraining().runTraining({ ...body, quoteId })` (`RUN_TRAINING`).
|
|
1486
1488
|
**Civitai shows the viewer a consent dialog in its own chrome** — price, base
|
|
1487
1489
|
model, length and dataset size, all read back from the server, never from
|
|
1488
|
-
your body
|
|
1489
|
-
|
|
1490
|
+
your body, with the host's own copies of the uploads as thumbnails — and
|
|
1491
|
+
submits only on their click. Change nothing in the body between the estimate
|
|
1492
|
+
and the run; the server checks it against the quoted one.
|
|
1490
1493
|
4. **Follow** — `useBuzzWorkflow().watch(workflowId)`. Once the run's moderation
|
|
1491
1494
|
status is approved, `trainedEpochs` lists the epochs whose checkpoint is
|
|
1492
1495
|
ready; send the viewer to the publish wizard with one of them (see
|
|
1493
1496
|
`trainedEpochs` on `BlockWorkflowSnapshot`).
|
|
1494
1497
|
|
|
1498
|
+
🔴 **Upload and run in the same page session.** The host remembers the blobs
|
|
1499
|
+
its own upload modal uploaded for this block, and **refuses a quote naming any
|
|
1500
|
+
other** with `training data not uploaded here` — before any dialog, so no run
|
|
1501
|
+
and no charge. A page reload forgets the uploads: ask the viewer to upload
|
|
1502
|
+
again, then estimate again.
|
|
1503
|
+
|
|
1495
1504
|
🔴 **Availability.** Behind the host flag **`app-blocks-training-kind`**, which
|
|
1496
1505
|
ships **off** and is evaluated per viewer. **Page apps only** — the model slot
|
|
1497
|
-
answers
|
|
1506
|
+
answers `RUN_TRAINING` with an error. The manifest must declare
|
|
1498
1507
|
**`ai:write:budgeted`** and the viewer must be signed in and have granted it.
|
|
1499
1508
|
**Refused from `dev:live` and from review sessions** (the server refuses
|
|
1500
1509
|
development and review tokens); build the flow against the mock host. Requires a
|
|
1501
1510
|
civitai.com host carrying civitai/civitai#5434 (`kind: 'training'`,
|
|
1502
|
-
`RUN_TRAINING`) and civitai/civitai#
|
|
1511
|
+
`RUN_TRAINING`) and civitai/civitai#6461 (uploaded blobs as the dataset). The
|
|
1512
|
+
server-prepared dataset this flow used before #6461 (`usePrepareTrainingDataset`,
|
|
1513
|
+
`PREPARE_TRAINING_DATASET`) is **removed**; a host carrying #6461 answers that
|
|
1514
|
+
message with the error `removed`.
|
|
1503
1515
|
|
|
1504
1516
|
🔴 **Money.** There is no `maxBuzz` and no timeout knob — the price is the quote.
|
|
1505
1517
|
A confirmed run may cost more than the token's per-call `buzzBudget` (the viewer
|
|
@@ -1516,32 +1528,26 @@ saying anything about money:
|
|
|
1516
1528
|
| `err.declined` | the viewer dismissed the dialog — **no run was submitted** | revert, say nothing |
|
|
1517
1529
|
| `err.unconfirmed` | `submission-unconfirmed`, or no reply within the 10-min bound (`err.timedOut`) — the run **may be running and charged** | 🔴 **never retry automatically**: check the viewer's trainings first (`useAppWorkflows()` lists this app's runs). Re-running the same body after the server did start it is a **second, separately charged run** |
|
|
1518
1530
|
| `err.refused` (`code: 'refused'`) | the server refused the submit after the viewer confirmed — a **spend cap** (their daily or private-run Buzz cap, the per-app consent budget, the app's daily spend or rate limit, a dev-session cap) or a **temporary-availability** deny. Refunded: **no run, nothing charged**; the quote is used up | show `err.message` (the server's reason, e.g. `daily Buzz cap reached: …`); estimate again before any retry — buying Buzz does not lift these caps |
|
|
1531
|
+
| `err.trainingDataNotUploaded` (`code: 'training data not uploaded here'`) | the quote names an image this host did not upload for this block (typically after a reload) — refused before any dialog: **no run, nothing charged** | ask the viewer to upload the images again, then estimate and run the new quote |
|
|
1519
1532
|
| `err.signInRequired` | no session | route into `useRequestSignIn()` |
|
|
1520
1533
|
| `err.code` set otherwise | a host refusal (`review-mode` / `block is not ready` / `invalid training request` / `no block token`) | show or ignore per case |
|
|
1521
|
-
| `err.code === undefined`, no flag | a server refusal before any submit (expired or used quote, changed body
|
|
1534
|
+
| `err.code === undefined`, no flag | a server refusal before any submit (expired or used quote, changed body) | estimate again, then retry |
|
|
1522
1535
|
|
|
1523
|
-
`
|
|
1524
|
-
|
|
1525
|
-
`invalid training dataset`, `no block token` — plus `.signInRequired` and
|
|
1526
|
-
`.timedOut`). Preparing charges nothing, so none of them cost Buzz. It prompts
|
|
1527
|
-
for `ai:write:budgeted` and retries once on a grant (see the table below);
|
|
1528
|
-
`runTraining` deliberately does not — it carries no idempotency key and has an
|
|
1529
|
-
outcome where a run may exist, so it never re-sends.
|
|
1536
|
+
`runTraining` never prompts for `ai:write:budgeted` or re-sends — it carries no
|
|
1537
|
+
idempotency key and has an outcome where a run may exist.
|
|
1530
1538
|
|
|
1531
1539
|
```tsx
|
|
1532
|
-
const {
|
|
1540
|
+
const { open } = useImageUpload({ purpose: 'trainingData' });
|
|
1533
1541
|
const { estimate, watch } = useBuzzWorkflow();
|
|
1534
1542
|
const { runTraining } = useRunTraining();
|
|
1535
1543
|
|
|
1536
|
-
async function train(
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
const dataset = await prepareDataset(images.map((i) => ({ imageId: i.id, caption: i.caption })));
|
|
1540
|
-
if (dataset.rejected.length > 0) showNotice(`${dataset.rejected.length} image(s) left out`);
|
|
1544
|
+
async function train(captionFor: (image: BlockTrainingDataImage) => string) {
|
|
1545
|
+
const images = await open();
|
|
1546
|
+
if (!images) return; // the viewer cancelled
|
|
1541
1547
|
|
|
1542
1548
|
const body: WorkflowBodyTraining = {
|
|
1543
1549
|
kind: 'training',
|
|
1544
|
-
|
|
1550
|
+
items: images.map((image) => ({ air: image.air, caption: captionFor(image) })),
|
|
1545
1551
|
engine: 'ai-toolkit',
|
|
1546
1552
|
model: baseModelKey, // a key from Civitai's training catalog
|
|
1547
1553
|
params: aiToolkitParams, // AiToolkitTrainingParams
|
|
@@ -1561,6 +1567,7 @@ async function train(images: Array<{ id: number; caption: string }>) {
|
|
|
1561
1567
|
if (err.declined) return; // no run
|
|
1562
1568
|
if (err.unconfirmed) return showCheckYourTrainings(); // may be running — never auto-retry
|
|
1563
1569
|
if (err.refused) return showError(err.message); // a cap / availability refusal; no run, nothing charged
|
|
1570
|
+
if (err.trainingDataNotUploaded) return showError('Upload the images again.'); // no run
|
|
1564
1571
|
if (err.signInRequired) return requestSignIn();
|
|
1565
1572
|
showError('Could not start training. Get a new price and try again.');
|
|
1566
1573
|
}
|
|
@@ -1569,19 +1576,22 @@ async function train(images: Array<{ id: number; caption: string }>) {
|
|
|
1569
1576
|
|
|
1570
1577
|
With the mock host — `createMockHost` or `Harness` from
|
|
1571
1578
|
`@civitai/blocks-react/testing` — the training path is **kind-faithful**: it
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
`ai:write:budgeted` on the
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
+
remembers the blob keys its uploads handed out and refuses a run naming any
|
|
1580
|
+
other (`training data not uploaded here`, quote kept), refuses items outside the
|
|
1581
|
+
server's schema at the estimate, a quote above 5,000, a quote run twice and a
|
|
1582
|
+
body that differs from the quoted one, and needs `ai:write:budgeted` on the
|
|
1583
|
+
token and a signed-in viewer. Its knobs are `cannedTrainingDataUpload` (the
|
|
1584
|
+
images an upload returns; default three fresh ones per upload, `null` for a
|
|
1585
|
+
cancel), `trainingDataNotUploadedHere` (refuse every run as a host that forgot
|
|
1586
|
+
its uploads would), `trainingQuoteTotal`, `runTrainingError` (`'declined'`,
|
|
1587
|
+
`'submission-unconfirmed'`, or any server message), `runTrainingCapRefusal` (the
|
|
1588
|
+
server's resolved cap / availability refusal, which consumes the quote →
|
|
1579
1589
|
`err.refused`) and `generation.trainedEpochs` for the finished run. It has no
|
|
1580
|
-
consent dialog, so `runTraining`
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
**`dev:live` refuses
|
|
1584
|
-
|
|
1590
|
+
upload modal and no consent dialog, so `open()` and `runTraining` settle at once
|
|
1591
|
+
where the real host waits on the viewer; and it accepts any https blob url as an
|
|
1592
|
+
`air`, where the server accepts only Civitai-hosted ones — name items by `air`.
|
|
1593
|
+
**`dev:live` refuses the run** (the server refuses every training request from a
|
|
1594
|
+
dev token) and answers the upload as a cancel.
|
|
1585
1595
|
|
|
1586
1596
|
### `useRunConfirmedStep()` — one job above the per-call budget
|
|
1587
1597
|
|
|
@@ -2213,8 +2223,10 @@ answers a raw `multiple` + Checkpoint request with the host's error.
|
|
|
2213
2223
|
|
|
2214
2224
|
Host-mediated image upload — the host opens its native upload modal and the
|
|
2215
2225
|
iframe never handles the bytes. Resolves with a moderated image (or `null` on
|
|
2216
|
-
dismiss); pass `{ purpose: 'generationSource' }` for an unscanned img2img source
|
|
2217
|
-
|
|
2226
|
+
dismiss); pass `{ purpose: 'generationSource' }` for an unscanned img2img source,
|
|
2227
|
+
`{ purpose: 'trainingData' }` for several private training images in one call
|
|
2228
|
+
(`[{ url, air }] | null` — see the LoRA training section), or
|
|
2229
|
+
`{ asyncScan: true }` for the early-resolve + `scanStatus()` flow.
|
|
2218
2230
|
|
|
2219
2231
|
```tsx
|
|
2220
2232
|
const { open } = useImageUpload();
|
|
@@ -2253,7 +2265,7 @@ render), with no picker, so the app can then post it. Returns `{ upload }`.
|
|
|
2253
2265
|
sniffed type, whatever `filename` says.
|
|
2254
2266
|
- **The cap is 40 MiB.** The hook refuses a larger buffer before sending it,
|
|
2255
2267
|
with the host's error `file exceeds the maximum upload size`. The host also
|
|
2256
|
-
allows at most
|
|
2268
|
+
allows at most 8 uploads and 80 MiB per 60 seconds per page, and replies
|
|
2257
2269
|
`busy` past that.
|
|
2258
2270
|
- **The upload is blocking.** The host replies once the image is scanned, so
|
|
2259
2271
|
allow for a wait of up to a few minutes. An image above the SFW ceiling or
|
|
@@ -2863,7 +2875,6 @@ had just worked.
|
|
|
2863
2875
|
|---|---|---|
|
|
2864
2876
|
| `useBuzzWorkflow()` | `submit()` | `ai:write:budgeted` |
|
|
2865
2877
|
| `useCreatePostFromApp()` | `createPost()` | `posts:write:self` |
|
|
2866
|
-
| `usePrepareTrainingDataset()` | `prepareDataset()` | `ai:write:budgeted` |
|
|
2867
2878
|
| `useGoodPurchase()` | `purchase()` | `goods:purchase:self` |
|
|
2868
2879
|
| `useTip()` | `tip()` | `social:tip:self` |
|
|
2869
2880
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { BlockGenerationSourceImageInfo, BlockImageScanResult, BlockPendingImageInfo, BlockUploadedImageInfo, BlockUploadPurpose } from '@civitai/app-sdk/blocks';
|
|
1
|
+
import type { BlockGenerationSourceImageInfo, BlockImageScanResult, BlockPendingImageInfo, BlockTrainingDataImage, BlockUploadedImageInfo, BlockUploadPurpose } from '@civitai/app-sdk/blocks';
|
|
2
|
+
export type { BlockTrainingDataImage };
|
|
2
3
|
/**
|
|
3
4
|
* What {@link useImageUpload} returns for the DEFAULT (`purpose: 'display'`,
|
|
4
5
|
* blocking-scan) call — `useImageUpload()` with no arguments.
|
|
5
6
|
*
|
|
6
7
|
* 🔴 THIS HOOK IS OVERLOADED, SO ITS RETURN TYPE IS A FAMILY, NOT A SINGLETON
|
|
7
8
|
* (#380). Each overload names its own: {@link UseImageUploadGenerationSource},
|
|
8
|
-
* {@link UseImageUploadAsyncScan}, and this one. A single union would be worse
|
|
9
|
+
* {@link UseImageUploadTrainingData}, {@link UseImageUploadAsyncScan}, and this one. A single union would be worse
|
|
9
10
|
* than the anonymous literals it replaces — it would hand every caller a
|
|
10
11
|
* `scanStatus` that only one overload actually provides.
|
|
11
12
|
*/
|
|
@@ -16,6 +17,14 @@ export interface UseImageUpload {
|
|
|
16
17
|
export interface UseImageUploadGenerationSource {
|
|
17
18
|
open: () => Promise<BlockGenerationSourceImageInfo | null>;
|
|
18
19
|
}
|
|
20
|
+
/** What {@link useImageUpload} returns for `{ purpose: 'trainingData' }`. */
|
|
21
|
+
export interface UseImageUploadTrainingData {
|
|
22
|
+
/**
|
|
23
|
+
* Every image the viewer uploaded, in the host's order, or `null` when they
|
|
24
|
+
* cancelled. Each `air` is what a `BlockTrainingDatasetItem` names.
|
|
25
|
+
*/
|
|
26
|
+
open: () => Promise<BlockTrainingDataImage[] | null>;
|
|
27
|
+
}
|
|
19
28
|
/** What {@link useImageUpload} returns for `{ asyncScan: true }`. */
|
|
20
29
|
export interface UseImageUploadAsyncScan {
|
|
21
30
|
/** Early-resolve handle (image persisted, NOT yet scanned) or `null` (dismissed). */
|
|
@@ -37,7 +46,7 @@ export interface UseImageUploadAsyncScan {
|
|
|
37
46
|
* compatible; the async overload types it required.
|
|
38
47
|
*/
|
|
39
48
|
export interface UseImageUploadImplementation {
|
|
40
|
-
open: () => Promise<BlockUploadedImageInfo | BlockGenerationSourceImageInfo | BlockPendingImageInfo | null>;
|
|
49
|
+
open: () => Promise<BlockUploadedImageInfo | BlockGenerationSourceImageInfo | BlockPendingImageInfo | BlockTrainingDataImage[] | null>;
|
|
41
50
|
scanStatus?: (handle: BlockPendingImageInfo) => Promise<BlockImageScanResult>;
|
|
42
51
|
}
|
|
43
52
|
/** Options for {@link useImageUpload}. */
|
|
@@ -49,14 +58,18 @@ export interface UseImageUploadOptions {
|
|
|
49
58
|
* - `'generationSource'` — a PRIVATE img2img source; UNSCANNED, so the host
|
|
50
59
|
* returns ONLY the source shape {@link BlockGenerationSourceImageInfo}
|
|
51
60
|
* (`{ url, width, height }`).
|
|
61
|
+
* - `'trainingData'` — PRIVATE training images for a `kind: 'training'` run
|
|
62
|
+
* (page apps only): up to 50 picked in one host modal, returned together as
|
|
63
|
+
* {@link BlockTrainingDataImage}`[]`.
|
|
52
64
|
*/
|
|
53
65
|
purpose?: BlockUploadPurpose;
|
|
54
66
|
/**
|
|
55
67
|
* Opt in to the NON-BLOCKING `'display'` flow. When `true`, `open()` resolves
|
|
56
68
|
* EARLY (on persist) with a {@link BlockPendingImageInfo} and the scan verdict
|
|
57
69
|
* is delivered separately via `scanStatus()` (see the async overload). Only
|
|
58
|
-
* meaningful for `'display'`; ignored for `'generationSource'`
|
|
59
|
-
*
|
|
70
|
+
* meaningful for `'display'`; ignored for `'generationSource'` and
|
|
71
|
+
* `'trainingData'` (neither has a host-side scan). Absent/`false` keeps the
|
|
72
|
+
* legacy blocking behavior.
|
|
60
73
|
*/
|
|
61
74
|
asyncScan?: boolean;
|
|
62
75
|
}
|
|
@@ -82,9 +95,20 @@ export interface UseImageUploadOptions {
|
|
|
82
95
|
* {@link BlockGenerationSourceImageInfo} (`{ url, width, height }`, UNSCANNED —
|
|
83
96
|
* the orchestrator scans it at gen time), or `null` on dismiss. Feed it
|
|
84
97
|
* straight into a `WorkflowBody.sourceImage` for an img2img graph.
|
|
98
|
+
* - `'trainingData'` → resolves with EVERY image the viewer uploaded,
|
|
99
|
+
* {@link BlockTrainingDataImage}`[]` (`{ url, air }` each), or `null` on
|
|
100
|
+
* cancel. Step 1 of a `kind: 'training'` run: pair each `air` with a caption
|
|
101
|
+
* for `WorkflowBodyTraining.items`. The host remembers the keys it uploaded
|
|
102
|
+
* for this block and refuses a run naming any other
|
|
103
|
+
* (`training data not uploaded here`), so upload and run in the same page
|
|
104
|
+
* session. A host that predates the purpose answers with one display image,
|
|
105
|
+
* which this resolves as `null` — never as a list it did not upload.
|
|
85
106
|
*
|
|
86
107
|
* Human-interactive, so it uses the same generous {@link HUMAN_INTERACTION_TIMEOUT_MS}
|
|
87
|
-
* as the pickers (the host still resolves earlier on upload/dismiss/close)
|
|
108
|
+
* as the pickers (the host still resolves earlier on upload/dismiss/close) — except
|
|
109
|
+
* `'trainingData'`, which waits up to {@link TRAINING_DATA_UPLOAD_TIMEOUT_MS} (one
|
|
110
|
+
* hour): the host replies only after uploading up to 50 originals, which can take
|
|
111
|
+
* longer than ten minutes.
|
|
88
112
|
* Host-mediated, same trust model as `useResourcePicker` / `useBuzzWorkflow`.
|
|
89
113
|
*
|
|
90
114
|
* Backward-compatible: when `purpose` is omitted the hook sends NO `purpose`
|
|
@@ -117,10 +141,20 @@ export interface UseImageUploadOptions {
|
|
|
117
141
|
* if (!src) return;
|
|
118
142
|
* await submit({ kind: 'textToImage', modelId, modelVersionId,
|
|
119
143
|
* sourceImage: src, params: { prompt } });
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* // trainingData (private training images, several in one reply)
|
|
147
|
+
* const { open } = useImageUpload({ purpose: 'trainingData' });
|
|
148
|
+
* const images = await open(); // [{ url, air }, …] | null
|
|
149
|
+
* if (!images) return;
|
|
150
|
+
* const items = images.map((img) => ({ air: img.air, caption: captionFor(img) }));
|
|
120
151
|
*/
|
|
121
152
|
export declare function useImageUpload(options: {
|
|
122
153
|
purpose: 'generationSource';
|
|
123
154
|
}): UseImageUploadGenerationSource;
|
|
155
|
+
export declare function useImageUpload(options: {
|
|
156
|
+
purpose: 'trainingData';
|
|
157
|
+
}): UseImageUploadTrainingData;
|
|
124
158
|
export declare function useImageUpload(options: {
|
|
125
159
|
purpose?: 'display';
|
|
126
160
|
asyncScan: true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
2
|
+
import { HUMAN_INTERACTION_TIMEOUT_MS, TRAINING_DATA_UPLOAD_TIMEOUT_MS, } from '../transport/requestTimeouts.js';
|
|
3
3
|
import { getTransport } from '../transport/singleton.js';
|
|
4
4
|
import { sendTypedRequest, subscribeTyped } from '../transport/transport.js';
|
|
5
5
|
import { useTransportConnection } from './useTransportConnection.js';
|
|
@@ -31,7 +31,7 @@ function isTerminalVerdict(v) {
|
|
|
31
31
|
export function useImageUpload(options) {
|
|
32
32
|
useTransportConnection();
|
|
33
33
|
const purpose = options?.purpose;
|
|
34
|
-
const asyncScan = options?.asyncScan === true && purpose
|
|
34
|
+
const asyncScan = options?.asyncScan === true && (purpose === undefined || purpose === 'display');
|
|
35
35
|
// Per-instance scan tracking, keyed by imageId. Persists across renders; a
|
|
36
36
|
// verdict is correlated back to its upload by imageId + the generated requestId.
|
|
37
37
|
const scansRef = useRef(new Map());
|
|
@@ -129,6 +129,19 @@ export function useImageUpload(options) {
|
|
|
129
129
|
// payload) → treat as dismissed rather than fabricate a handle.
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
|
+
if (purpose === 'trainingData') {
|
|
133
|
+
const { selected } = await sendTypedRequest(getTransport(), { type: 'OPEN_IMAGE_UPLOAD', payload: { purpose } }, 'IMAGE_UPLOAD_RESULT',
|
|
134
|
+
// NOT the 10-minute human bound: the host answers only after uploading
|
|
135
|
+
// every original, which can outlast it — see TRAINING_DATA_UPLOAD_TIMEOUT_MS.
|
|
136
|
+
{ timeoutMs: TRAINING_DATA_UPLOAD_TIMEOUT_MS });
|
|
137
|
+
// Only the multi-image shape is an answer to THIS purpose. A host that
|
|
138
|
+
// predates `trainingData` normalises it to `display` and replies with one
|
|
139
|
+
// moderated image — nothing a training item can name — so that, an empty
|
|
140
|
+
// list and a cancel all resolve `null` rather than a fabricated list.
|
|
141
|
+
if (!selected || !('images' in selected) || selected.images.length === 0)
|
|
142
|
+
return null;
|
|
143
|
+
return selected.images.map(({ url, air }) => ({ url, air }));
|
|
144
|
+
}
|
|
132
145
|
// ---- Existing BLOCKING behavior (unchanged) ----
|
|
133
146
|
// Omit `purpose` for the default/display mode so the wire stays
|
|
134
147
|
// byte-compatible with an older host that predates the field.
|
|
@@ -136,7 +149,10 @@ export function useImageUpload(options) {
|
|
|
136
149
|
const { selected } = await sendTypedRequest(getTransport(), { type: 'OPEN_IMAGE_UPLOAD', payload }, 'IMAGE_UPLOAD_RESULT', { timeoutMs: HUMAN_INTERACTION_TIMEOUT_MS });
|
|
137
150
|
// Normalize the "dismissed" case to an explicit null so callers can
|
|
138
151
|
// `if (!img) return;` without an `undefined` ambiguity.
|
|
139
|
-
|
|
152
|
+
// A multi-image training reply is not an answer to a single-image purpose.
|
|
153
|
+
if (!selected || 'images' in selected)
|
|
154
|
+
return null;
|
|
155
|
+
return selected;
|
|
140
156
|
}, [asyncScan, purpose]);
|
|
141
157
|
const scanStatus = useCallback((handle) => {
|
|
142
158
|
const entry = scansRef.current.get(handle.imageId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { BlockRunTrainingHostError, BlockTrainingQuote, BlockWorkflowSnapshot, WorkflowBodyTraining } from '@civitai/app-sdk/blocks';
|
|
2
|
-
export type { BlockRunTrainingHostError, BlockTrainingQuote, WorkflowBodyTraining };
|
|
1
|
+
import type { BlockRunTrainingHostError, BlockTrainingDatasetItem, BlockTrainingQuote, BlockWorkflowSnapshot, WorkflowBodyTraining } from '@civitai/app-sdk/blocks';
|
|
2
|
+
export type { BlockRunTrainingHostError, BlockTrainingDatasetItem, BlockTrainingQuote, WorkflowBodyTraining, };
|
|
3
3
|
/**
|
|
4
4
|
* A training body ready to RUN: {@link WorkflowBodyTraining} with the `quoteId`
|
|
5
5
|
* its estimate returned. Required here so a body without one is a compile error
|
|
@@ -16,7 +16,7 @@ export type QuotedTrainingBody = WorkflowBodyTraining & {
|
|
|
16
16
|
* a body that differs from the quoted one) arrive in the same field — see
|
|
17
17
|
* {@link isRunTrainingErrorCode}.
|
|
18
18
|
*/
|
|
19
|
-
export declare const RUN_TRAINING_ERROR_CODES: readonly ["review-mode", "block is not ready", "sign in to train", "invalid training request", "no block token", "declined", "submission-unconfirmed"];
|
|
19
|
+
export declare const RUN_TRAINING_ERROR_CODES: readonly ["review-mode", "block is not ready", "sign in to train", "invalid training request", "no block token", "training data not uploaded here", "declined", "submission-unconfirmed"];
|
|
20
20
|
/**
|
|
21
21
|
* The `code` a {@link RunTrainingError} can carry: one of the host's closed
|
|
22
22
|
* refusal codes, or `'refused'` — assigned by THIS SDK, never sent by the host,
|
|
@@ -49,9 +49,15 @@ export declare function isRunTrainingErrorCode(error: string): error is BlockRun
|
|
|
49
49
|
* `.snapshot` the refusal it arrived in. Retrying needs a new estimate and
|
|
50
50
|
* usually a later time; buying Buzz does not lift these caps.
|
|
51
51
|
* - `.signInRequired` — no session. Route into `useRequestSignIn()`.
|
|
52
|
+
* - `.trainingDataNotUploaded` (`code: 'training data not uploaded here'`) —
|
|
53
|
+
* the quote names an image THIS host did not upload for this block. The host
|
|
54
|
+
* remembers only the blobs its own `useImageUpload({ purpose: 'trainingData' })`
|
|
55
|
+
* uploaded, and forgets them on a page reload. Refused before any dialog:
|
|
56
|
+
* NO RUN, nothing charged. Ask the viewer to upload the images again, then
|
|
57
|
+
* estimate and run the new quote.
|
|
52
58
|
* - anything else — a refusal before any submit (an expired or used quote, a
|
|
53
|
-
* body that no longer matches its quote
|
|
54
|
-
*
|
|
59
|
+
* body that no longer matches its quote). Estimate again before retrying;
|
|
60
|
+
* `.message` is the host's or server's text.
|
|
55
61
|
*/
|
|
56
62
|
export declare class RunTrainingError extends Error {
|
|
57
63
|
/**
|
|
@@ -78,6 +84,12 @@ export declare class RunTrainingError extends Error {
|
|
|
78
84
|
readonly timedOut: boolean;
|
|
79
85
|
/** There is no session (`sign in to train`, or a bare `UNAUTHORIZED`). */
|
|
80
86
|
readonly signInRequired: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* The quote names an image this host did not upload for this block
|
|
89
|
+
* (`training data not uploaded here`). No run, nothing charged: upload the
|
|
90
|
+
* images again, estimate again, and run that quote.
|
|
91
|
+
*/
|
|
92
|
+
readonly trainingDataNotUploaded: boolean;
|
|
81
93
|
constructor(error: string, opts?: {
|
|
82
94
|
timedOut?: boolean;
|
|
83
95
|
refusedSnapshot?: BlockWorkflowSnapshot;
|
|
@@ -112,15 +124,19 @@ export interface UseRunTraining {
|
|
|
112
124
|
* viewer. Refused from `dev:live` and from review sessions — build the flow
|
|
113
125
|
* against the mock host. See `WorkflowBodyTraining` in `@civitai/app-sdk`.
|
|
114
126
|
*
|
|
115
|
-
* The flow: `
|
|
116
|
-
*
|
|
117
|
-
* `useBuzzWorkflow().
|
|
118
|
-
*
|
|
127
|
+
* The flow: `useImageUpload({ purpose: 'trainingData' }).open()` (the viewer
|
|
128
|
+
* uploads the images; you caption them into `items: [{ air, caption }]`) →
|
|
129
|
+
* `useBuzzWorkflow().estimate(body)` (its `trainingQuote.quoteId`) →
|
|
130
|
+
* `runTraining({ ...body, quoteId })` → `useBuzzWorkflow().watch(workflowId)`.
|
|
131
|
+
* Change nothing in the body between the estimate and the run: the server checks
|
|
132
|
+
* it against the quoted one. Upload and run in the same page session: the host
|
|
133
|
+
* refuses a quote naming an image it did not upload for this block.
|
|
119
134
|
*
|
|
120
135
|
* 🔴 THE CONSENT DIALOG IS CIVITAI'S, AND IT IS THE CHARGE. Every number on it is
|
|
121
136
|
* read back from the server's stored quote — never from your body. A run may cost
|
|
122
137
|
* more than the token's per-call budget (the viewer confirms the exact price), up
|
|
123
|
-
* to `BLOCK_TRAINING_MAX_BUZZ_PER_RUN` (5,000 Buzz). Images are the
|
|
138
|
+
* to `BLOCK_TRAINING_MAX_BUZZ_PER_RUN` (5,000 Buzz). Images are the ones the
|
|
139
|
+
* viewer uploaded through the host.
|
|
124
140
|
*
|
|
125
141
|
* 🔴 NO AUTOMATIC CONSENT RETRY, unlike the other consent-gated hooks — on
|
|
126
142
|
* purpose. `RUN_TRAINING` carries no idempotency key and has an outcome
|
|
@@ -132,8 +148,12 @@ export interface UseRunTraining {
|
|
|
132
148
|
* the viewer clicks or dismisses its dialog.
|
|
133
149
|
*
|
|
134
150
|
* @example
|
|
151
|
+
* const { open } = useImageUpload({ purpose: 'trainingData' });
|
|
135
152
|
* const { estimate, watch } = useBuzzWorkflow();
|
|
136
153
|
* const { runTraining } = useRunTraining();
|
|
154
|
+
* const images = await open();
|
|
155
|
+
* if (!images) return; // cancelled
|
|
156
|
+
* const body = { ...settings, items: images.map((i) => ({ air: i.air, caption: captionFor(i) })) };
|
|
137
157
|
* const quote = (await estimate(body)).trainingQuote!;
|
|
138
158
|
* try {
|
|
139
159
|
* const started = await runTraining({ ...body, quoteId: quote.quoteId });
|
|
@@ -142,6 +162,7 @@ export interface UseRunTraining {
|
|
|
142
162
|
* if (!(e instanceof RunTrainingError)) throw e;
|
|
143
163
|
* if (e.declined) return; // no run — say nothing
|
|
144
164
|
* if (e.signInRequired) return requestSignIn();
|
|
165
|
+
* if (e.trainingDataNotUploaded) return askToUploadAgain(); // no run — re-upload, re-estimate
|
|
145
166
|
* if (e.unconfirmed) return showCheckYourTrainings(); // may be running — never auto-retry
|
|
146
167
|
* if (e.refused) return showError(e.message); // a cap / availability refusal; no run, nothing charged
|
|
147
168
|
* showError('Could not start training. Get a new price and try again.');
|
|
@@ -17,9 +17,12 @@ export const RUN_TRAINING_ERROR_CODES = [
|
|
|
17
17
|
'sign in to train',
|
|
18
18
|
'invalid training request',
|
|
19
19
|
'no block token',
|
|
20
|
+
'training data not uploaded here',
|
|
20
21
|
'declined',
|
|
21
22
|
'submission-unconfirmed',
|
|
22
23
|
];
|
|
24
|
+
/** The host code for a quote naming an image this host did not upload — see {@link RunTrainingError.trainingDataNotUploaded}. */
|
|
25
|
+
const NOT_UPLOADED_HERE = 'training data not uploaded here';
|
|
23
26
|
const CODE_SET = new Set(RUN_TRAINING_ERROR_CODES);
|
|
24
27
|
/**
|
|
25
28
|
* The `workflowId` the host's training submit stamps on a snapshot that is NOT a
|
|
@@ -55,9 +58,15 @@ export function isRunTrainingErrorCode(error) {
|
|
|
55
58
|
* `.snapshot` the refusal it arrived in. Retrying needs a new estimate and
|
|
56
59
|
* usually a later time; buying Buzz does not lift these caps.
|
|
57
60
|
* - `.signInRequired` — no session. Route into `useRequestSignIn()`.
|
|
61
|
+
* - `.trainingDataNotUploaded` (`code: 'training data not uploaded here'`) —
|
|
62
|
+
* the quote names an image THIS host did not upload for this block. The host
|
|
63
|
+
* remembers only the blobs its own `useImageUpload({ purpose: 'trainingData' })`
|
|
64
|
+
* uploaded, and forgets them on a page reload. Refused before any dialog:
|
|
65
|
+
* NO RUN, nothing charged. Ask the viewer to upload the images again, then
|
|
66
|
+
* estimate and run the new quote.
|
|
58
67
|
* - anything else — a refusal before any submit (an expired or used quote, a
|
|
59
|
-
* body that no longer matches its quote
|
|
60
|
-
*
|
|
68
|
+
* body that no longer matches its quote). Estimate again before retrying;
|
|
69
|
+
* `.message` is the host's or server's text.
|
|
61
70
|
*/
|
|
62
71
|
export class RunTrainingError extends Error {
|
|
63
72
|
/**
|
|
@@ -84,6 +93,12 @@ export class RunTrainingError extends Error {
|
|
|
84
93
|
timedOut;
|
|
85
94
|
/** There is no session (`sign in to train`, or a bare `UNAUTHORIZED`). */
|
|
86
95
|
signInRequired;
|
|
96
|
+
/**
|
|
97
|
+
* The quote names an image this host did not upload for this block
|
|
98
|
+
* (`training data not uploaded here`). No run, nothing charged: upload the
|
|
99
|
+
* images again, estimate again, and run that quote.
|
|
100
|
+
*/
|
|
101
|
+
trainingDataNotUploaded;
|
|
87
102
|
constructor(error, opts) {
|
|
88
103
|
super(error);
|
|
89
104
|
this.name = 'RunTrainingError';
|
|
@@ -99,6 +114,7 @@ export class RunTrainingError extends Error {
|
|
|
99
114
|
this.declined = error === 'declined';
|
|
100
115
|
this.unconfirmed = this.timedOut || error === 'submission-unconfirmed';
|
|
101
116
|
this.signInRequired = error === 'sign in to train' || error === 'UNAUTHORIZED';
|
|
117
|
+
this.trainingDataNotUploaded = error === NOT_UPLOADED_HERE;
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
/**
|
|
@@ -110,15 +126,19 @@ export class RunTrainingError extends Error {
|
|
|
110
126
|
* viewer. Refused from `dev:live` and from review sessions — build the flow
|
|
111
127
|
* against the mock host. See `WorkflowBodyTraining` in `@civitai/app-sdk`.
|
|
112
128
|
*
|
|
113
|
-
* The flow: `
|
|
114
|
-
*
|
|
115
|
-
* `useBuzzWorkflow().
|
|
116
|
-
*
|
|
129
|
+
* The flow: `useImageUpload({ purpose: 'trainingData' }).open()` (the viewer
|
|
130
|
+
* uploads the images; you caption them into `items: [{ air, caption }]`) →
|
|
131
|
+
* `useBuzzWorkflow().estimate(body)` (its `trainingQuote.quoteId`) →
|
|
132
|
+
* `runTraining({ ...body, quoteId })` → `useBuzzWorkflow().watch(workflowId)`.
|
|
133
|
+
* Change nothing in the body between the estimate and the run: the server checks
|
|
134
|
+
* it against the quoted one. Upload and run in the same page session: the host
|
|
135
|
+
* refuses a quote naming an image it did not upload for this block.
|
|
117
136
|
*
|
|
118
137
|
* 🔴 THE CONSENT DIALOG IS CIVITAI'S, AND IT IS THE CHARGE. Every number on it is
|
|
119
138
|
* read back from the server's stored quote — never from your body. A run may cost
|
|
120
139
|
* more than the token's per-call budget (the viewer confirms the exact price), up
|
|
121
|
-
* to `BLOCK_TRAINING_MAX_BUZZ_PER_RUN` (5,000 Buzz). Images are the
|
|
140
|
+
* to `BLOCK_TRAINING_MAX_BUZZ_PER_RUN` (5,000 Buzz). Images are the ones the
|
|
141
|
+
* viewer uploaded through the host.
|
|
122
142
|
*
|
|
123
143
|
* 🔴 NO AUTOMATIC CONSENT RETRY, unlike the other consent-gated hooks — on
|
|
124
144
|
* purpose. `RUN_TRAINING` carries no idempotency key and has an outcome
|
|
@@ -130,8 +150,12 @@ export class RunTrainingError extends Error {
|
|
|
130
150
|
* the viewer clicks or dismisses its dialog.
|
|
131
151
|
*
|
|
132
152
|
* @example
|
|
153
|
+
* const { open } = useImageUpload({ purpose: 'trainingData' });
|
|
133
154
|
* const { estimate, watch } = useBuzzWorkflow();
|
|
134
155
|
* const { runTraining } = useRunTraining();
|
|
156
|
+
* const images = await open();
|
|
157
|
+
* if (!images) return; // cancelled
|
|
158
|
+
* const body = { ...settings, items: images.map((i) => ({ air: i.air, caption: captionFor(i) })) };
|
|
135
159
|
* const quote = (await estimate(body)).trainingQuote!;
|
|
136
160
|
* try {
|
|
137
161
|
* const started = await runTraining({ ...body, quoteId: quote.quoteId });
|
|
@@ -140,6 +164,7 @@ export class RunTrainingError extends Error {
|
|
|
140
164
|
* if (!(e instanceof RunTrainingError)) throw e;
|
|
141
165
|
* if (e.declined) return; // no run — say nothing
|
|
142
166
|
* if (e.signInRequired) return requestSignIn();
|
|
167
|
+
* if (e.trainingDataNotUploaded) return askToUploadAgain(); // no run — re-upload, re-estimate
|
|
143
168
|
* if (e.unconfirmed) return showCheckYourTrainings(); // may be running — never auto-retry
|
|
144
169
|
* if (e.refused) return showError(e.message); // a cap / availability refusal; no run, nothing charged
|
|
145
170
|
* showError('Could not start training. Get a new price and try again.');
|
|
@@ -58,7 +58,7 @@ export interface UseUploadImageBytes {
|
|
|
58
58
|
* scope, so ask for it first with `useRequestConsent()`: the upload runs
|
|
59
59
|
* before any `useCreatePostFromApp()` call that would otherwise prompt.
|
|
60
60
|
* - **Images only:** PNG, WebP or JPEG by magic bytes. The cap is 40 MiB, and
|
|
61
|
-
* the host also allows at most
|
|
61
|
+
* the host also allows at most 8 uploads and 80 MiB per 60 s per page.
|
|
62
62
|
* - The buffer is COPIED across `postMessage`, never transferred, so the block
|
|
63
63
|
* can keep displaying it. Pass an `ArrayBuffer` (`await blob.arrayBuffer()`),
|
|
64
64
|
* not a `Blob` or a typed array, which the host refuses.
|
|
@@ -25,7 +25,7 @@ export const UPLOAD_IMAGE_BYTES_NO_IMAGE_ERROR = 'the host returned no uploaded
|
|
|
25
25
|
* scope, so ask for it first with `useRequestConsent()`: the upload runs
|
|
26
26
|
* before any `useCreatePostFromApp()` call that would otherwise prompt.
|
|
27
27
|
* - **Images only:** PNG, WebP or JPEG by magic bytes. The cap is 40 MiB, and
|
|
28
|
-
* the host also allows at most
|
|
28
|
+
* the host also allows at most 8 uploads and 80 MiB per 60 s per page.
|
|
29
29
|
* - The buffer is COPIED across `postMessage`, never transferred, so the block
|
|
30
30
|
* can keep displaying it. Pass an `ArrayBuffer` (`await blob.arrayBuffer()`),
|
|
31
31
|
* not a `Blob` or a typed array, which the host refuses.
|
package/dist/index.d.ts
CHANGED
|
@@ -73,10 +73,8 @@ export { useReactions, ReactionError, REACTIONS, REACTION_ERROR_CODES, isReactio
|
|
|
73
73
|
export type { UseReactions, UseReactionsOptions } from './hooks/useReactions.js';
|
|
74
74
|
export { useCreatePostFromApp, CreatePostError, CREATE_POST_ERROR_CODES, isCreatePostErrorCode, } from './hooks/useCreatePostFromApp.js';
|
|
75
75
|
export type { UseCreatePostFromApp, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, } from './hooks/useCreatePostFromApp.js';
|
|
76
|
-
export { usePrepareTrainingDataset, PrepareTrainingDatasetError, PREPARE_TRAINING_DATASET_ERROR_CODES, isPrepareTrainingDatasetErrorCode, } from './hooks/usePrepareTrainingDataset.js';
|
|
77
|
-
export type { UsePrepareTrainingDataset, BlockPrepareTrainingDatasetHostError, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, } from './hooks/usePrepareTrainingDataset.js';
|
|
78
76
|
export { useRunTraining, RunTrainingError, RUN_TRAINING_ERROR_CODES, isRunTrainingErrorCode, } from './hooks/useRunTraining.js';
|
|
79
|
-
export type { UseRunTraining, QuotedTrainingBody, RunTrainingErrorCode, BlockRunTrainingHostError, BlockTrainingQuote, WorkflowBodyTraining, } from './hooks/useRunTraining.js';
|
|
77
|
+
export type { UseRunTraining, QuotedTrainingBody, RunTrainingErrorCode, BlockRunTrainingHostError, BlockTrainingQuote, BlockTrainingDatasetItem, WorkflowBodyTraining, } from './hooks/useRunTraining.js';
|
|
80
78
|
export { useRunConfirmedStep, RunConfirmedStepError, RUN_CONFIRMED_STEP_ERROR_CODES, isRunConfirmedStepErrorCode, isStepQuoteId, } from './hooks/useRunConfirmedStep.js';
|
|
81
79
|
export type { UseRunConfirmedStep, QuotedStepBody, RunConfirmedStepErrorCode, BlockRunConfirmedStepHostError, BlockStepQuote, WorkflowBodyPassThroughStep, } from './hooks/useRunConfirmedStep.js';
|
|
82
80
|
export type { BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, AppWorkflowsParams, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockAppReaction, BlockStoredReaction, BlockReactionEntityType, BlockEntityReactionErrorCode, BlockReactionCounts, BlockEntityReactionState, BlockEntityReactionResult, BlockEntityReactionsResult, } from '@civitai/app-sdk/blocks';
|
|
@@ -85,7 +83,7 @@ export type { UseCheckpointPicker } from './hooks/useCheckpointPicker.js';
|
|
|
85
83
|
export { useResourcePicker, RESOURCE_PICKER_MULTIPLE_MAX } from './hooks/useResourcePicker.js';
|
|
86
84
|
export type { UseResourcePicker, ResourcePickerOpenOptions, ResourcePickerMultiple, } from './hooks/useResourcePicker.js';
|
|
87
85
|
export { useImageUpload } from './hooks/useImageUpload.js';
|
|
88
|
-
export type { UseImageUpload, UseImageUploadAsyncScan, UseImageUploadGenerationSource, UseImageUploadOptions, } from './hooks/useImageUpload.js';
|
|
86
|
+
export type { UseImageUpload, UseImageUploadAsyncScan, UseImageUploadGenerationSource, UseImageUploadTrainingData, UseImageUploadOptions, BlockTrainingDataImage, } from './hooks/useImageUpload.js';
|
|
89
87
|
export { useUploadImageBytes } from './hooks/useUploadImageBytes.js';
|
|
90
88
|
export type { UploadImageBytesOptions, UseUploadImageBytes, } from './hooks/useUploadImageBytes.js';
|
|
91
89
|
export { useMediaUpload } from './hooks/useMediaUpload.js';
|
package/dist/index.js
CHANGED
|
@@ -62,7 +62,6 @@ export { InvalidIdempotencyKeyError } from './transport/transport.js';
|
|
|
62
62
|
export { useCollectionFollow, CollectionFollowError, COLLECTION_FOLLOW_ERROR_CODES, isCollectionFollowErrorCode, } from './hooks/useCollectionFollow.js';
|
|
63
63
|
export { useReactions, ReactionError, REACTIONS, REACTION_ERROR_CODES, isReactionErrorCode, } from './hooks/useReactions.js';
|
|
64
64
|
export { useCreatePostFromApp, CreatePostError, CREATE_POST_ERROR_CODES, isCreatePostErrorCode, } from './hooks/useCreatePostFromApp.js';
|
|
65
|
-
export { usePrepareTrainingDataset, PrepareTrainingDatasetError, PREPARE_TRAINING_DATASET_ERROR_CODES, isPrepareTrainingDatasetErrorCode, } from './hooks/usePrepareTrainingDataset.js';
|
|
66
65
|
export { useRunTraining, RunTrainingError, RUN_TRAINING_ERROR_CODES, isRunTrainingErrorCode, } from './hooks/useRunTraining.js';
|
|
67
66
|
export { useRunConfirmedStep, RunConfirmedStepError, RUN_CONFIRMED_STEP_ERROR_CODES, isRunConfirmedStepErrorCode, isStepQuoteId, } from './hooks/useRunConfirmedStep.js';
|
|
68
67
|
export { useCheckpointPicker } from './hooks/useCheckpointPicker.js';
|