@civitai/app-sdk 0.76.0 → 0.78.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 CHANGED
@@ -151,7 +151,7 @@ interface BlockInitPayload {
151
151
  | `isModelSlotContext(ctx)` / `isPageSlotContext(ctx)` | Runtime narrowing for the `slotId`-discriminated `BlockContext` union. Real checks on a value that crossed a `postMessage` boundary — they verify every field they assert, not just `slotId`. |
152
152
  | `fetchNestedDocument(opts)` | Embed one of your OWN bundled documents as an iframe `srcdoc`, because a nested `<iframe src>` of your own content **cannot load** — see [Nested documents](#nested-documents) below. `NestedDocumentError` carries a `.code` of `'invalid-url' \| 'http-error' \| 'network-error'`. |
153
153
  | `isSignedIn(viewer)` | **The sign-in gate.** `isSignedIn(useBlockContext().viewer)` — do not open-code it as `viewer !== null` or `viewer?.signedIn === true`. Which of those is correct has already changed once with the host contract, and this is the one place it is decided. It reads neither `viewer.id` nor `viewer.username` (both `@deprecated`), so nothing written through it changes when those are removed. Need the identity rather than the presence? `useViewer()` — scope-gated and audited per call. |
154
- | types | `BlockManifestV1`, `ManifestSettings` (+ field types), `BlockContext`, `ModelSlotContext`, `BlockCheckpointInfo`, `ShowcaseImage`, `BlockToken`, `WrappedToken`, `BlockSettings`, `ViewerInfo`, `Theme`, `WorkflowBody`, `BlockTextToImageParams`, `WorkflowBodyCustomComfy` (+ its two arms `WorkflowBodyCustomComfyRecipe` / `WorkflowBodyCustomComfyInline`, and `InlineComfyNode`), `WorkflowBodyStep` / `WorkflowBodyPassThroughStep` (the two arms of `kind: 'step'`), `WorkflowBodyTraining` (+ `AiToolkitTrainingParams`, `BlockTrainingQuote`, `BlockTrainingDatasetItem`, `BlockTrainingDatasetResult` and the `BLOCK_TRAINING_*` bounds), `WorkflowBodyWorkflow` (+ `WorkflowBodyWorkflowStep`, `WorkflowStepRef`, `WithWorkflowStepRefs`, `BlockWorkflowStepSnapshot` and `BLOCK_WORKFLOW_MAX_STEPS`), `BlockStepQuote` / `BlockRunConfirmedStepHostError` (+ `BLOCK_CONFIRMED_STEP_MAX_BUZZ_PER_JOB`, `BLOCK_STEP_QUOTE_ID_REGEX`), `BlockModel3dOutput`, `BlockWorkflowSnapshot`, `WorkflowStatus`, `BlockInitPayload`, `ParentToBlockMessage`, `BlockToParentMessage`. |
154
+ | types | `BlockManifestV1`, `ManifestSettings` (+ field types), `BlockContext`, `ModelSlotContext`, `BlockCheckpointInfo`, `ShowcaseImage`, `BlockToken`, `WrappedToken`, `BlockSettings`, `ViewerInfo`, `Theme`, `WorkflowBody`, `BlockTextToImageParams`, `WorkflowBodyCustomComfy` (+ its two arms `WorkflowBodyCustomComfyRecipe` / `WorkflowBodyCustomComfyInline`, and `InlineComfyNode`), `WorkflowBodyStep` / `WorkflowBodyPassThroughStep` (the two arms of `kind: 'step'`), `WorkflowBodyTraining` (+ `AiToolkitTrainingParams`, `BlockTrainingQuote`, `BlockTrainingDatasetItem`, `BlockTrainingDataImage`, `BlockTrainingDataUploadResult`, `BlockRunTrainingHostError` and the `BLOCK_TRAINING_*` bounds), `WorkflowBodyWorkflow` (+ `WorkflowBodyWorkflowStep`, `WorkflowStepRef`, `WithWorkflowStepRefs`, `BlockWorkflowStepSnapshot` and `BLOCK_WORKFLOW_MAX_STEPS`), `BlockStepQuote` / `BlockRunConfirmedStepHostError` (+ `BLOCK_CONFIRMED_STEP_MAX_BUZZ_PER_JOB`, `BLOCK_STEP_QUOTE_ID_REGEX`), `BlockModel3dOutput`, `BlockWorkflowSnapshot`, `WorkflowStatus`, `BlockInitPayload`, `ParentToBlockMessage`, `BlockToParentMessage`. |
155
155
 
156
156
  `WorkflowBody`'s `customComfy` member is a discriminated union on `mode`, mirroring
157
157
  the host's `blockCustomComfyMemberSchema`. Narrow on the VALUE (`body.mode === 'inline'`),
@@ -267,15 +267,21 @@ const rigged = pickModel(snapshot, 'fbx', 'riggedFbxModel');
267
267
  ```
268
268
 
269
269
  `WorkflowBodyTraining` (`kind: 'training'`) is the **quoted** way to train: an ai-toolkit LoRA
270
- run on a dataset Civitai prepared from the viewer's own images, mirroring the host's
271
- `blockTrainingBodySchema` (civitai/civitai#5434). There is no `maxBuzz` and no timeout — the
270
+ run on images the viewer uploaded through the host, mirroring the host's
271
+ `blockTrainingBodySchema` (civitai/civitai#5434, #6461). Its dataset is `items: [{ air, caption }]`
272
+ (`BlockTrainingDatasetItem`; at most `BLOCK_TRAINING_DATASET_MAX_ITEMS` items and
273
+ `BLOCK_TRAINING_CAPTION_MAX_CHARS` caption characters, one blob at most once). There is no `maxBuzz` and no timeout — the
272
274
  `estimate` reply carries `snapshot.trainingQuote` (`quoteId`, `total`, `imageCount`,
273
275
  `expiresAt`), and the run starts only through the `RUN_TRAINING` bridge, after the viewer
274
276
  confirms that price in Civitai's own dialog. A confirmed run may exceed the token's per-call
275
277
  budget, up to `BLOCK_TRAINING_MAX_BUZZ_PER_RUN` (5,000). Page apps only, `ai:write:budgeted`,
276
278
  behind the host flag `app-blocks-training-kind` (off by default), and refused from development
277
- and review sessions. The dataset is prepared over `PREPARE_TRAINING_DATASET`
278
- (civitai/civitai#5438). Use it through `@civitai/blocks-react`'s `usePrepareTrainingDataset()`
279
+ and review sessions. The images are uploaded over `OPEN_IMAGE_UPLOAD { purpose: 'trainingData' }`,
280
+ which replies `IMAGE_UPLOAD_RESULT { selected: { images: [{ url, air }] } }`
281
+ (`BlockTrainingDataUploadResult`); the host refuses a run whose quote names a blob it did not
282
+ upload for the block, with the `BlockRunTrainingHostError` `training data not uploaded here`.
283
+ `PREPARE_TRAINING_DATASET` (the server-prepared dataset of civitai/civitai#5438) is removed from
284
+ the protocol. Use it through `@civitai/blocks-react`'s `useImageUpload({ purpose: 'trainingData' })`
279
285
  and `useRunTraining()`.
280
286
 
281
287
  `WorkflowBodyWorkflow` (`kind: 'workflow'`) submits **several orchestrator steps as one
@@ -660,7 +666,7 @@ The starters in `civitai/civitai-app-starters` wire this into framework-specific
660
666
 
661
667
  ## Choosing a workflow step type
662
668
 
663
- The orchestrator is a workflow API: each request submits a list of typed steps. `WORKFLOW_STEP_TYPES` is the in-code catalog of every step `$type` it accepts, with a one-line description for each — `textToImage`, `imageGen`, `videoGen`, `comfy`, `customComfy`, `textToSpeech`, `aceStepAudio`, `transcription`, `imageUpscaler` among them (55 in total).
669
+ The orchestrator is a workflow API: each request submits a list of typed steps. `WORKFLOW_STEP_TYPES` is the in-code catalog of every step `$type` it accepts, with a one-line description for each — `textToImage`, `imageGen`, `videoGen`, `comfy`, `customComfy`, `textToSpeech`, `aceStepAudio`, `transcription`, `imageUpscaler` among them (58 in total).
664
670
 
665
671
  The catalog is pinned to the orchestrator's published OpenAPI spec two ways — an offline unit test against a transcribed copy of the spec's `WorkflowStepTemplate` discriminator mapping, and a CI job (`pnpm check:catalogs`) that re-fetches the live spec and diffs it. If a `$type` is listed here, the orchestrator accepts it.
666
672
 
@@ -735,15 +741,15 @@ What it exports:
735
741
 
736
742
  | Export | What |
737
743
  |---|---|
738
- | `WorkflowStepTemplates` | `$type` → template type, for 47 of the catalog's 55 step types. Keyed by the WIRE name, which the generated type names don't always match (`model3DPreview` → `Model3dPreviewStepTemplate`). |
744
+ | `WorkflowStepTemplates` | `$type` → template type, for 47 of the catalog's 58 step types. Keyed by the WIRE name, which the generated type names don't always match (`model3DPreview` → `Model3dPreviewStepTemplate`). |
739
745
  | `WorkflowStepTemplateFor<'videoGen'>` | One step's template. |
740
746
  | `WorkflowStepInputFor<'videoGen'>` | One step's `input` shape, without needing the generated `*Input` name. |
741
747
  | `AnyWorkflowStepTemplate` | Discriminated union of all 47 mapped templates — `Extract<…, { $type: 'comfy' }>` and exhaustive `switch` work. `@civitai/client`'s base `WorkflowStepTemplate` has `$type` as a bare `string`, so it narrows nothing. |
742
748
  | `TypedWorkflowTemplate` | The submit envelope with `steps` narrowed to that union. Pass it straight to `submitWorkflow` / `estimateWorkflow`. |
743
749
 
744
- > 🔴 **The map is not total over the catalog, and that is the expected state.** `WORKFLOW_STEP_TYPES` documents 55 `$type`s; this map covers 47. The 8 with no generated template in the pinned `@civitai/client` are `imageScanning`, `liveTextToSpeech`, `liveTranscription`, `merge`, `preprocessVideo`, `promptModeration`, `soniloAudioGen`, `yuE2`, and `WorkflowStepTemplateFor<…>` is a compile error for each of them.
750
+ > 🔴 **The map is not total over the catalog, and that is the expected state.** `WORKFLOW_STEP_TYPES` documents 58 `$type`s; this map covers 47. The 11 with no generated template in the pinned `@civitai/client` are `archiveExtraction`, `audioModeration`, `imageScanning`, `liveTextToSpeech`, `liveTranscription`, `merge`, `multimodalEmbedding`, `preprocessVideo`, `promptModeration`, `soniloAudioGen`, `yuE2`, and `WorkflowStepTemplateFor<…>` is a compile error for each of them.
745
751
  >
746
- > The two surfaces move independently on purpose: the catalog tracks the **live** orchestrator spec (a daily job syncs it), while these types track whatever `@civitai/client` was last published from. So the catalog runs ahead and the client catches up. The gap is never silent — `test/orchestrator/step-templates.test-d.ts` carries it as a `never` ledger plus one `@ts-expect-error` per gap `$type`, and `test/orchestrator/step-count-prose.test.ts` derives all four numbers (55, 47, 8, and the names) from `WORKFLOW_STEP_TYPES` and the map's own AST, then fails if this paragraph or its twin in `src/orchestrator/steps.ts` disagrees by one character.
752
+ > The two surfaces move independently on purpose: the catalog tracks the **live** orchestrator spec (a daily job syncs it), while these types track whatever `@civitai/client` was last published from. So the catalog runs ahead and the client catches up. The gap is never silent — `test/orchestrator/step-templates.test-d.ts` carries it as a `never` ledger plus one `@ts-expect-error` per gap `$type`, and `test/orchestrator/step-count-prose.test.ts` derives all four numbers (58, 47, 11, and the names) from `WORKFLOW_STEP_TYPES` and the map's own AST, then fails if this paragraph or its twin in `src/orchestrator/steps.ts` disagrees by one character.
747
753
 
748
754
  The generated `*StepTemplate` and `*Input` types are **not** re-exported individually. Using this subpath already requires `@civitai/client` installed, so if you want one by name, import it straight from there — `import type { TextToImageStepTemplate } from '@civitai/client'`.
749
755
 
@@ -782,7 +788,7 @@ Several of the 47 exist to serve Civitai's own pipelines rather than third-party
782
788
 
783
789
  `training` and `imageResourceTraining` are the exception: an App Block's host explicitly **allows** them on the pass-through arm (see `WorkflowBodyPassThroughStep` under Primitives above for the `maxBuzz` bound, and how a trained epoch is published). For a standalone app, the orchestrator's own authorization still decides.
784
790
 
785
- Note that `WORKFLOW_STEP_TYPES` does **not** mark most of them: of its 55 entries exactly three — `comfyNodepackSnapshot`, `qwenImageBench` and `promptModeration` — sit under its "Platform internals" heading, and the rest are ordinary documented entries (`webScrape` even carries usage notes). The reason the platform steps are typed anyway is not that the catalog flags them as internal; it's that the catalog *documents* them, so skipping them would make `WorkflowStepTemplateFor<'training'>` a compile error for a step type the SDK documents — which is exactly what is live today for each `$type` the pinned client cannot type, and is why that gap is spelled out above rather than left to be discovered.
791
+ Note that `WORKFLOW_STEP_TYPES` does **not** mark most of them: of its 58 entries exactly three — `comfyNodepackSnapshot`, `qwenImageBench` and `promptModeration` — sit under its "Platform internals" heading, and the rest are ordinary documented entries (`webScrape` even carries usage notes). The reason the platform steps are typed anyway is not that the catalog flags them as internal; it's that the catalog *documents* them, so skipping them would make `WorkflowStepTemplateFor<'training'>` a compile error for a step type the SDK documents — which is exactly what is live today for each `$type` the pinned client cannot type, and is why that gap is spelled out above rather than left to be discovered.
786
792
 
787
793
  ## Public vs. confidential clients
788
794
 
@@ -116,7 +116,7 @@ export { isModelSlotContext, isPageSlotContext } from './types.js';
116
116
  * in `types.ts` for which of the two it uses and why.
117
117
  */
118
118
  export { isSignedIn } from './types.js';
119
- export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockAnalyticsDeclaration, BlockAnalyticsEventDeclaration, BlockAnalyticsPropertyDeclaration, BlockManifest, BlockManifestGood, BlockManifestV1, BlockSettings, BlockStorageCollectionDeclaration, BlockStorageDeclaration, BlockStorageIndexes, BlockStorageIndexType, BlockStoragePrivateCollectionDeclaration, BlockStorageSharedCollectionDeclaration, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockMediaKind, BlockMediaUploadInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockTextToImageParams, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockModelSubstitution, ModelSubstitutionReason, KnownBlockWorkflowErrorCode, BlockStepToolCall, BlockTrainedEpoch, BlockPublishedModel, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowBodyWorkflow, WorkflowBodyWorkflowStep, WorkflowStepRef, WithWorkflowStepRefs, BlockWorkflowStepSnapshot, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, WorkflowBodyTraining, AiToolkitTrainingParams, AiToolkitTrainingParamsBase, AiToolkitPlainEcosystem, AiToolkitLrScheduler, AiToolkitOptimizerType, BlockTrainingQuote, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, BlockPrepareTrainingDatasetHostError, BlockRunTrainingHostError, BlockStepQuote, BlockModel3dOutput, BlockRunConfirmedStepHostError, } from './types.js';
119
+ export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockAnalyticsDeclaration, BlockAnalyticsEventDeclaration, BlockAnalyticsPropertyDeclaration, BlockManifest, BlockManifestGood, BlockManifestV1, BlockSettings, BlockStorageCollectionDeclaration, BlockStorageDeclaration, BlockStorageIndexes, BlockStorageIndexType, BlockStoragePrivateCollectionDeclaration, BlockStorageSharedCollectionDeclaration, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockMediaKind, BlockMediaUploadInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockTextToImageParams, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockModelSubstitution, ModelSubstitutionReason, KnownBlockWorkflowErrorCode, BlockStepToolCall, BlockTrainedEpoch, BlockPublishedModel, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowBodyWorkflow, WorkflowBodyWorkflowStep, WorkflowStepRef, WithWorkflowStepRefs, BlockWorkflowStepSnapshot, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, WorkflowBodyTraining, AiToolkitTrainingParams, AiToolkitTrainingParamsBase, AiToolkitPlainEcosystem, AiToolkitLrScheduler, AiToolkitOptimizerType, BlockTrainingQuote, BlockTrainingDatasetItem, BlockTrainingDataImage, BlockTrainingDataUploadResult, BlockRunTrainingHostError, BlockStepQuote, BlockModel3dOutput, BlockRunConfirmedStepHostError, } from './types.js';
120
120
  /** Host bound for the App Blocks `kind: 'workflow'` body — see `WorkflowBodyWorkflow`. */
121
121
  export { BLOCK_WORKFLOW_MAX_STEPS, BLOCK_WORKFLOW_REPEAT_MAX_ITERATIONS } from './types.js';
122
122
  /**
@@ -8,7 +8,7 @@
8
8
  * Wire format: `window.postMessage({ type, payload }, targetOrigin)`.
9
9
  */
10
10
  import type { ColorDomain } from './browsingLevel.js';
11
- import type { BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockMediaKind, BlockMediaUploadInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockContext, BlockSettings, Theme, ViewerInfo, WorkflowBody, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostResult, BlockTrainingDatasetItem, BlockTrainingDatasetResult, WorkflowBodyTraining, WorkflowBodyPassThroughStep } from './types.js';
11
+ import type { BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockMediaKind, BlockMediaUploadInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockContext, BlockSettings, Theme, ViewerInfo, WorkflowBody, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostResult, BlockTrainingDataUploadResult, WorkflowBodyTraining, WorkflowBodyPassThroughStep } from './types.js';
12
12
  import type { BlockAppReaction, BlockEntityReactionErrorCode, BlockEntityReactionResult, BlockEntityReactionsResult, BlockReactionEntityType } from './reactions.js';
13
13
  /**
14
14
  * Filter params for `GET_BUZZ_TRANSACTIONS`. All optional; the host validates
@@ -510,13 +510,6 @@ export type ParentToBlockMessage = {
510
510
  result?: BlockCreatePostResult;
511
511
  error?: string;
512
512
  };
513
- } | {
514
- type: 'TRAINING_DATASET_RESULT';
515
- payload: {
516
- requestId: string;
517
- result?: BlockTrainingDatasetResult;
518
- error?: string;
519
- };
520
513
  } | {
521
514
  type: 'TRAINING_RESULT';
522
515
  payload: {
@@ -588,7 +581,7 @@ export type ParentToBlockMessage = {
588
581
  type: 'IMAGE_UPLOAD_RESULT';
589
582
  payload: {
590
583
  requestId: string;
591
- selected?: BlockUploadedImageInfo | BlockGenerationSourceImageInfo | BlockPendingImageInfo;
584
+ selected?: BlockUploadedImageInfo | BlockGenerationSourceImageInfo | BlockPendingImageInfo | BlockTrainingDataUploadResult;
592
585
  /** A `bytes` upload's refusal or failure: a host string or a forwarded server message. */
593
586
  error?: string;
594
587
  };
@@ -888,12 +881,6 @@ export type BlockToParentMessage = {
888
881
  /** Optional model-version gallery attach. Gated hard server-side. */
889
882
  modelVersionId?: number;
890
883
  };
891
- } | {
892
- type: 'PREPARE_TRAINING_DATASET';
893
- payload: {
894
- requestId: string;
895
- items: BlockTrainingDatasetItem[];
896
- };
897
884
  } | {
898
885
  type: 'RUN_TRAINING';
899
886
  payload: {
@@ -1229,7 +1216,7 @@ export type BlockToParentMessageType = BlockToParentMessage['type'];
1229
1216
  * it is a measurement, not an invariant, and civitai's own compile-time gate
1230
1217
  * (published SDK union ⊆ `keyof INVENTORY`) is what holds one direction of it.
1231
1218
  */
1232
- export declare const BLOCK_TO_PARENT_MESSAGE_TYPES: readonly ["BLOCK_HELLO", "BLOCK_READY", "BLOCK_ERROR", "BLOCK_MESSAGE_REJECTED", "REQUEST_TOKEN", "RESIZE_IFRAME", "SUBMIT_WORKFLOW", "ESTIMATE_WORKFLOW", "ESTIMATE_WORKFLOW_BATCH", "POLL_WORKFLOW", "CANCEL_WORKFLOW", "OPEN_BUZZ_PURCHASE", "GET_BUZZ_BALANCE", "GET_VIEWER", "GET_BUZZ_TRANSACTIONS", "GET_BUZZ_ACCOUNTS", "GET_DAILY_COMPENSATION", "GET_WILDCARD_PACK", "QUERY_APP_WORKFLOWS", "PUBLISH_GENERATION_OUTPUTS", "CREATE_POST_FROM_APP", "PREPARE_TRAINING_DATASET", "RUN_TRAINING", "RUN_CONFIRMED_STEP", "GET_IMAGES_BY_IDS", "CANCEL_APP_WORKFLOW", "OPEN_CHECKPOINT_PICKER", "OPEN_RESOURCE_PICKER", "OPEN_IMAGE_UPLOAD", "OPEN_IMAGE_PICKER", "OPEN_MEDIA_UPLOAD", "SET_USER_CHECKPOINT", "NAVIGATE", "REQUEST_SIGN_IN", "REQUEST_CONSENT", "TRACK_EVENT", "APP_STORAGE_GET", "APP_STORAGE_SET", "APP_STORAGE_DELETE", "APP_STORAGE_LIST", "APP_STORAGE_QUOTA", "SHARED_LIST", "SHARED_GET_COUNT", "SHARED_GET_COUNTS", "SHARED_APPEND", "SHARED_VOTE", "SHARED_UNVOTE", "SHARED_WITHDRAW", "SHARED_UPDATE", "SHARED_GET", "SHARED_REPORT", "SAVE_IMAGE", "SET_COLLECTION_FOLLOW", "SET_ENTITY_REACTION", "GET_ENTITY_REACTIONS"];
1219
+ export declare const BLOCK_TO_PARENT_MESSAGE_TYPES: readonly ["BLOCK_HELLO", "BLOCK_READY", "BLOCK_ERROR", "BLOCK_MESSAGE_REJECTED", "REQUEST_TOKEN", "RESIZE_IFRAME", "SUBMIT_WORKFLOW", "ESTIMATE_WORKFLOW", "ESTIMATE_WORKFLOW_BATCH", "POLL_WORKFLOW", "CANCEL_WORKFLOW", "OPEN_BUZZ_PURCHASE", "GET_BUZZ_BALANCE", "GET_VIEWER", "GET_BUZZ_TRANSACTIONS", "GET_BUZZ_ACCOUNTS", "GET_DAILY_COMPENSATION", "GET_WILDCARD_PACK", "QUERY_APP_WORKFLOWS", "PUBLISH_GENERATION_OUTPUTS", "CREATE_POST_FROM_APP", "RUN_TRAINING", "RUN_CONFIRMED_STEP", "GET_IMAGES_BY_IDS", "CANCEL_APP_WORKFLOW", "OPEN_CHECKPOINT_PICKER", "OPEN_RESOURCE_PICKER", "OPEN_IMAGE_UPLOAD", "OPEN_IMAGE_PICKER", "OPEN_MEDIA_UPLOAD", "SET_USER_CHECKPOINT", "NAVIGATE", "REQUEST_SIGN_IN", "REQUEST_CONSENT", "TRACK_EVENT", "APP_STORAGE_GET", "APP_STORAGE_SET", "APP_STORAGE_DELETE", "APP_STORAGE_LIST", "APP_STORAGE_QUOTA", "SHARED_LIST", "SHARED_GET_COUNT", "SHARED_GET_COUNTS", "SHARED_APPEND", "SHARED_VOTE", "SHARED_UNVOTE", "SHARED_WITHDRAW", "SHARED_UPDATE", "SHARED_GET", "SHARED_REPORT", "SAVE_IMAGE", "SET_COLLECTION_FOLLOW", "SET_ENTITY_REACTION", "GET_ENTITY_REACTIONS"];
1233
1220
  /**
1234
1221
  * The bucket a message type falls into when the protocol does not declare it.
1235
1222
  *
@@ -70,7 +70,6 @@ export const BLOCK_TO_PARENT_MESSAGE_TYPES = [
70
70
  'QUERY_APP_WORKFLOWS',
71
71
  'PUBLISH_GENERATION_OUTPUTS',
72
72
  'CREATE_POST_FROM_APP',
73
- 'PREPARE_TRAINING_DATASET',
74
73
  'RUN_TRAINING',
75
74
  'RUN_CONFIRMED_STEP',
76
75
  'GET_IMAGES_BY_IDS',
@@ -256,8 +256,17 @@ export type BlockImageScanResult = {
256
256
  * UNSCANNED — the orchestrator scans it at generation time — so the host
257
257
  * returns ONLY the source shape ({@link BlockGenerationSourceImageInfo}); no
258
258
  * `imageId`/`nsfwLevel`/`contentRating` crosses back into the block.
259
- */
260
- export type BlockUploadPurpose = 'display' | 'generationSource';
259
+ * - `'trainingData'`: PRIVATE training images for a `kind: 'training'` run
260
+ * (civitai/civitai#6461). The viewer picks up to
261
+ * {@link BLOCK_TRAINING_DATASET_MAX_ITEMS} PNG, JPEG or WebP files in a host
262
+ * modal; each is uploaded as a consumer blob, as Civitai's own training form
263
+ * does. ONE reply carries every image that uploaded
264
+ * ({@link BlockTrainingDataUploadResult}), and the host remembers each key it
265
+ * uploaded for this block — `RUN_TRAINING` refuses a quote naming any other.
266
+ * Page apps only. A host that predates the purpose normalises it to
267
+ * `'display'` and answers with ONE moderated image instead.
268
+ */
269
+ export type BlockUploadPurpose = 'display' | 'generationSource' | 'trainingData';
261
270
  /**
262
271
  * Which SPACE a `NAVIGATE` path is resolved in. Mirrors the host's
263
272
  * `NavigateScope` in civitai/civitai's `pageBlockHostLogic.ts`. Keep in lockstep.
@@ -296,6 +305,32 @@ export type BlockNavigateScope = 'app' | 'site';
296
305
  * lockstep.
297
306
  */
298
307
  export type BlockGenerationSourceImageInfo = BlockSourceImage;
308
+ /**
309
+ * One image an `OPEN_IMAGE_UPLOAD { purpose: 'trainingData' }` uploaded — an
310
+ * element of {@link BlockTrainingDataUploadResult.images}. Mirrors the host's
311
+ * `TrainingDataUpload` in civitai/civitai's
312
+ * `src/components/AppBlocks/trainingDataUpload.ts` (civitai/civitai#6461).
313
+ *
314
+ * The image is PRIVATE training data: it is not published, scanned or given an
315
+ * `Image` row, so there is no `imageId`.
316
+ */
317
+ export interface BlockTrainingDataImage {
318
+ /** The uploaded blob's url. Show it as the thumbnail; it is also accepted as an item's `air`. */
319
+ url: string;
320
+ /**
321
+ * The consumer-blob key — pass it as {@link BlockTrainingDatasetItem.air}.
322
+ * Opaque. The host compares keys case-insensitively.
323
+ */
324
+ air: string;
325
+ }
326
+ /**
327
+ * `IMAGE_UPLOAD_RESULT.selected` for `purpose: 'trainingData'`: every image that
328
+ * uploaded, in one reply. The viewer may finish with fewer than they picked when
329
+ * some failed to upload; a cancel is a reply with NO `selected`.
330
+ */
331
+ export interface BlockTrainingDataUploadResult {
332
+ images: BlockTrainingDataImage[];
333
+ }
299
334
  /**
300
335
  * Which of the viewer's own image libraries `OPEN_IMAGE_PICKER` (via
301
336
  * `useImagePicker`) offers:
@@ -1618,10 +1653,13 @@ export type WorkflowBodyWorkflow = {
1618
1653
  };
1619
1654
  /** Ceiling on what ONE confirmed `kind: 'training'` run may cost, in Buzz. Host constant `BLOCK_TRAINING_MAX_BUZZ_PER_RUN`. */
1620
1655
  export declare const BLOCK_TRAINING_MAX_BUZZ_PER_RUN = 5000;
1621
- /** Max images in one prepared training dataset. Host constant `BLOCK_TRAINING_DATASET_MAX_ITEMS`. */
1656
+ /**
1657
+ * Max images in one training dataset ({@link WorkflowBodyTraining.items}), and in
1658
+ * one `trainingData` upload. Host constant `BLOCK_TRAINING_DATASET_MAX_ITEMS`.
1659
+ */
1622
1660
  export declare const BLOCK_TRAINING_DATASET_MAX_ITEMS = 50;
1623
1661
  /** Max characters of one dataset caption. Host constant `BLOCK_TRAINING_CAPTION_MAX_CHARS`. */
1624
- export declare const BLOCK_TRAINING_CAPTION_MAX_CHARS = 1000;
1662
+ export declare const BLOCK_TRAINING_CAPTION_MAX_CHARS = 8192;
1625
1663
  /** Max sample prompts on a training body. Host constant `BLOCK_TRAINING_SAMPLE_PROMPTS_MAX`. */
1626
1664
  export declare const BLOCK_TRAINING_SAMPLE_PROMPTS_MAX = 6;
1627
1665
  /** Max characters of one sample prompt. Host constant `BLOCK_TRAINING_SAMPLE_PROMPT_MAX_CHARS`. */
@@ -1706,16 +1744,16 @@ export type AiToolkitTrainingParams = AiToolkitTrainingParamsBase & ({
1706
1744
  modelVariant: 'base' | 'sft';
1707
1745
  });
1708
1746
  /**
1709
- * Train a LoRA with the ai-toolkit engine on a dataset Civitai prepared from the
1710
- * VIEWER'S OWN images — the App Blocks `kind: 'training'` workflow body.
1747
+ * Train a LoRA with the ai-toolkit engine on images the VIEWER uploaded through
1748
+ * the host — the App Blocks `kind: 'training'` workflow body.
1711
1749
  *
1712
1750
  * ## Availability — read before building on it
1713
1751
  *
1714
1752
  * - 🔴 Behind the host flag **`app-blocks-training-kind`**, which ships OFF and is
1715
1753
  * evaluated per viewer. Where it is off, every training call is refused
1716
1754
  * (`training from apps is not enabled`).
1717
- * - **Page apps only** (`/apps/run/<slug>`). The model slot answers both training
1718
- * messages with an error, and the server refuses a non-page token.
1755
+ * - **Page apps only** (`/apps/run/<slug>`). The model slot answers
1756
+ * `RUN_TRAINING` with an error, and the server refuses a non-page token.
1719
1757
  * - The manifest must declare **`ai:write:budgeted`** and the viewer must have
1720
1758
  * granted it; the viewer must be signed in.
1721
1759
  * - **Not from a development or review session**: dev tokens (`dev:live`, dev
@@ -1724,17 +1762,22 @@ export type AiToolkitTrainingParams = AiToolkitTrainingParamsBase & ({
1724
1762
  *
1725
1763
  * ## The flow
1726
1764
  *
1727
- * 1. **Dataset** — `usePrepareTrainingDataset()` (`PREPARE_TRAINING_DATASET`)
1728
- * with `[{ imageId, caption }]`. Only the viewer's OWN scanned, unflagged
1729
- * images within the page's maturity ceiling are admitted; the rest come back
1730
- * in `rejected`. You get an opaque {@link WorkflowBodyTraining.datasetId}.
1765
+ * 1. **Upload** — `useImageUpload({ purpose: 'trainingData' }).open()`
1766
+ * (`OPEN_IMAGE_UPLOAD { purpose: 'trainingData' }`). The viewer picks up to
1767
+ * {@link BLOCK_TRAINING_DATASET_MAX_ITEMS} images in a host modal; you get
1768
+ * `[{ url, air }]` ({@link BlockTrainingDataImage}), or `null` on cancel.
1769
+ * Pair each `air` with your caption to build {@link WorkflowBodyTraining.items}.
1731
1770
  * 2. **Quote** — `useBuzzWorkflow().estimate(body)` with this body, `quoteId`
1732
1771
  * ABSENT. The reply's {@link BlockWorkflowSnapshot.trainingQuote} is the
1733
1772
  * orchestrator's own price for exactly this run, stored server-side.
1734
1773
  * 3. **Run** — `useRunTraining().runTraining({ ...body, quoteId })`
1735
1774
  * (`RUN_TRAINING`). Civitai shows the viewer a consent dialog in ITS chrome —
1736
1775
  * price, base model, length, dataset size, all read back from the server,
1737
- * never from your body — and submits only on their click.
1776
+ * never from your body, with the host's own copies of the uploads as
1777
+ * thumbnails — and submits only on their click. A quote naming an image this
1778
+ * host did not upload for the block is refused with no dialog
1779
+ * (`training data not uploaded here`) — upload in the same page session as the
1780
+ * run; a reload forgets the uploads.
1738
1781
  * 4. **Follow** — `useBuzzWorkflow().watch(snapshot.workflowId)`.
1739
1782
  *
1740
1783
  * 🔴 `useBuzzWorkflow().submit()` REFUSES THIS BODY before sending it. The server
@@ -1750,8 +1793,12 @@ export type AiToolkitTrainingParams = AiToolkitTrainingParamsBase & ({
1750
1793
  */
1751
1794
  export interface WorkflowBodyTraining {
1752
1795
  kind: 'training';
1753
- /** The handle `usePrepareTrainingDataset()` returned — `tds_` + 32 hex characters. */
1754
- datasetId: string;
1796
+ /**
1797
+ * The dataset: 1…{@link BLOCK_TRAINING_DATASET_MAX_ITEMS} images this host
1798
+ * uploaded for the viewer, each with its caption — see
1799
+ * {@link BlockTrainingDatasetItem}. One blob may appear once.
1800
+ */
1801
+ items: BlockTrainingDatasetItem[];
1755
1802
  /** The only engine accepted. */
1756
1803
  engine: 'ai-toolkit';
1757
1804
  /**
@@ -1760,12 +1807,17 @@ export interface WorkflowBodyTraining {
1760
1807
  */
1761
1808
  model: string;
1762
1809
  params: AiToolkitTrainingParams;
1763
- /** At most {@link BLOCK_TRAINING_TRIGGER_WORD_MAX_CHARS} characters. Moderated like a prompt. */
1810
+ /**
1811
+ * At most {@link BLOCK_TRAINING_TRIGGER_WORD_MAX_CHARS} characters. The host runs
1812
+ * it through the same trigger-word check Civitai's trainer and Training Studio
1813
+ * use, at the estimate and again at the submit, before anything is reserved; a
1814
+ * refused trigger word fails the estimate.
1815
+ */
1764
1816
  triggerWord: string;
1765
1817
  /**
1766
1818
  * At most {@link BLOCK_TRAINING_SAMPLE_PROMPTS_MAX} prompts of at most
1767
- * {@link BLOCK_TRAINING_SAMPLE_PROMPT_MAX_CHARS} characters each. Moderated like
1768
- * prompts.
1819
+ * {@link BLOCK_TRAINING_SAMPLE_PROMPT_MAX_CHARS} characters each. Checked for
1820
+ * those bounds only, as in the Training Studio.
1769
1821
  */
1770
1822
  samplePrompts: string[];
1771
1823
  /**
@@ -1795,7 +1847,7 @@ export interface WorkflowBodyTraining {
1795
1847
  * unmodified. Bounded by a platform-internal denylist and by `maxBuzz`, not
1796
1848
  * by a registry.
1797
1849
  * - {@link WorkflowBodyTraining} (`kind: 'training'`) — an ai-toolkit LoRA run
1798
- * on a server-prepared dataset of the viewer's own images. ESTIMATE ONLY
1850
+ * on images the viewer uploaded through the host. ESTIMATE ONLY
1799
1851
  * through `useBuzzWorkflow`; it runs through `useRunTraining()`, whose
1800
1852
  * host-chrome consent dialog is the only way to confirm its quote.
1801
1853
  * - {@link WorkflowBodyWorkflow} (`kind: 'workflow'`) — up to
@@ -2163,8 +2215,8 @@ export interface BlockWorkflowSnapshot {
2163
2215
  *
2164
2216
  * `cost.total` on the same snapshot equals `trainingQuote.total`.
2165
2217
  * `expiresAt` is an ISO-8601 timestamp: after it the run is refused and you
2166
- * estimate again. `imageCount` is the server-derived size of the prepared
2167
- * dataset, never a number the block sent.
2218
+ * estimate again. `imageCount` is the size of the dataset the server quoted
2219
+ * (`items.length`).
2168
2220
  *
2169
2221
  * Present ONLY on the reply to a `kind: 'training'` estimate. Absent on hosts
2170
2222
  * that predate civitai/civitai#5434.
@@ -2318,7 +2370,7 @@ export interface BlockTrainingQuote {
2318
2370
  quoteId: string;
2319
2371
  /** The quoted price, in Buzz. */
2320
2372
  total: number;
2321
- /** How many images the prepared dataset holds. */
2373
+ /** How many images the quoted dataset holds. */
2322
2374
  imageCount: number;
2323
2375
  /** ISO-8601. The quote cannot be run after this. */
2324
2376
  expiresAt: string;
@@ -3451,85 +3503,30 @@ export interface BlockCreatePostResult {
3451
3503
  */
3452
3504
  export type BlockCreatePostHostError = 'review-mode' | 'block is not ready' | 'sign in to post' | 'no images to post' | 'no block token' | 'declined';
3453
3505
  /**
3454
- * One image of a training dataset request — an `Image` id the VIEWER owns, and
3455
- * the caption to train it with.
3506
+ * One image of a training dataset ({@link WorkflowBodyTraining.items}) — a blob
3507
+ * this host uploaded for the viewer, and the caption to train it with. The
3508
+ * Training Studio's own `{ air, caption }` shape.
3456
3509
  *
3457
- * Host bounds (`blockTrainingDatasetItemsSchema`): `imageId` a positive integer,
3458
- * `caption` at most {@link BLOCK_TRAINING_CAPTION_MAX_CHARS} characters, no other
3459
- * keys; 1…{@link BLOCK_TRAINING_DATASET_MAX_ITEMS} items per request. A payload
3460
- * outside them is refused by the host with `invalid training dataset` before any
3461
- * server call. Captions are moderated like prompts.
3510
+ * Host bounds (`blockTrainingDatasetItemsSchema`, civitai/civitai#6461): `air`
3511
+ * is the blob's key ({@link BlockTrainingDataImage.air}) or its Civitai-hosted
3512
+ * blob url — the server keeps only the key; `caption` at most
3513
+ * {@link BLOCK_TRAINING_CAPTION_MAX_CHARS} characters; no other keys;
3514
+ * 1…{@link BLOCK_TRAINING_DATASET_MAX_ITEMS} items per body, and one blob at
3515
+ * most once (keys compare case-insensitively). A body outside them is refused at
3516
+ * the estimate.
3462
3517
  */
3463
3518
  export interface BlockTrainingDatasetItem {
3464
- imageId: number;
3519
+ air: string;
3465
3520
  caption: string;
3466
3521
  }
3467
- /**
3468
- * Why the server left an image out of a prepared dataset. Mirrors
3469
- * civitai/civitai's `BlockTrainingRejectionReason`.
3470
- *
3471
- * - `unavailable` — not found, OR not the viewer's own image. One reason for
3472
- * both, on purpose: the server will not tell an app whether someone else's
3473
- * image id exists.
3474
- * - `unsupported-media` — a video or audio row.
3475
- * - `not-eligible` — refused by moderation state or the page's maturity ceiling.
3476
- * - `pending-scan` — not scanned yet; retry once the scan finishes.
3477
- * - `import-failed` — the orchestrator did not accept the image.
3478
- * - `import-unavailable` — the import timed out or the orchestrator was
3479
- * unavailable; retryable.
3480
- *
3481
- * The SDK's inbound validator checks `reason` only as a STRING, so a reason the
3482
- * host adds later still arrives — give an exhaustive `switch` a default branch.
3483
- */
3484
- export type BlockTrainingRejectionReason = 'unavailable' | 'unsupported-media' | 'not-eligible' | 'pending-scan' | 'import-failed' | 'import-unavailable';
3485
- /**
3486
- * The success payload of `TRAINING_DATASET_RESULT` — what
3487
- * `usePrepareTrainingDataset().prepareDataset()` resolves with.
3488
- */
3489
- export interface BlockTrainingDatasetResult {
3490
- /**
3491
- * Opaque handle for {@link WorkflowBodyTraining.datasetId} (`tds_` + 32 hex
3492
- * characters), bound to this viewer, app and install.
3493
- */
3494
- datasetId: string;
3495
- /**
3496
- * How many images were ADMITTED — always at least 1: when nothing is admitted
3497
- * the server refuses the request instead of returning an empty dataset. The
3498
- * server derives it; a run trains on exactly these.
3499
- */
3500
- count: number;
3501
- /** The images left out, each with its reason. Empty when every image was admitted. */
3502
- rejected: Array<{
3503
- imageId: number;
3504
- reason: BlockTrainingRejectionReason;
3505
- }>;
3506
- }
3507
- /**
3508
- * The HOST'S OWN refusal codes on a `TRAINING_DATASET_RESULT`'s `error`. Mirrors
3509
- * civitai/civitai's `PREPARE_TRAINING_DATASET_HOST_ERRORS`
3510
- * (`src/components/AppBlocks/prepareTrainingDatasetGate.ts`, civitai/civitai#5438).
3511
- *
3512
- * 🔴 THE SET A BLOCK MAY RELY ON, NOT THE SET IT MAY RECEIVE: `error` also carries
3513
- * any server message verbatim (`training from apps is not enabled`, a rate limit,
3514
- * a moderation refusal of a caption). Compare by EQUALITY against these members
3515
- * and treat anything else as an opaque server message.
3516
- *
3517
- * - `review-mode` — a mod-review sandbox with "run for real" off.
3518
- * - `block is not ready` — the host handshake has not completed.
3519
- * - `sign in to train` — anonymous viewer; there are no images of theirs to read.
3520
- * - `invalid training dataset` — `items` failed the host's item schema (shape,
3521
- * count or caption length). Nothing was sent to the server.
3522
- * - `no block token` — the host holds no block token yet.
3523
- */
3524
- export type BlockPrepareTrainingDatasetHostError = 'review-mode' | 'block is not ready' | 'sign in to train' | 'invalid training dataset' | 'no block token';
3525
3522
  /**
3526
3523
  * The HOST'S OWN refusal codes on a `TRAINING_RESULT`'s `error`. Mirrors
3527
3524
  * civitai/civitai's `RUN_TRAINING_HOST_ERRORS`
3528
3525
  * (`src/components/AppBlocks/runTrainingGate.ts`).
3529
3526
  *
3530
3527
  * 🔴 THE SET A BLOCK MAY RELY ON, NOT THE SET IT MAY RECEIVE: server messages
3531
- * (an expired or already-used quote, a body that differs from the quoted one, an
3532
- * ineligible dataset image) arrive verbatim in the same field.
3528
+ * (an expired or already-used quote, a body that differs from the quoted one)
3529
+ * arrive verbatim in the same field.
3533
3530
  *
3534
3531
  * - `review-mode` — a mod-review sandbox with "run for real" off.
3535
3532
  * - `block is not ready` — the host handshake has not completed.
@@ -3537,6 +3534,11 @@ export type BlockPrepareTrainingDatasetHostError = 'review-mode' | 'block is not
3537
3534
  * - `invalid training request` — the body is not a `kind: 'training'` body
3538
3535
  * naming a `quoteId`.
3539
3536
  * - `no block token` — the host holds no block token yet.
3537
+ * - `training data not uploaded here` — the quote names an image this host did
3538
+ * not upload for this block (civitai/civitai#6461): the host remembers only the
3539
+ * blobs its own `OPEN_IMAGE_UPLOAD { purpose: 'trainingData' }` uploaded, and
3540
+ * forgets them on a reload. Refused BEFORE any dialog: NO RUN, nothing
3541
+ * charged. Upload the images again, estimate again, run that quote.
3540
3542
  * - `declined` — the viewer dismissed the consent dialog. 🔴 GUARANTEED to mean
3541
3543
  * NO RUN WAS SUBMITTED.
3542
3544
  * - `submission-unconfirmed` — the submit was SENT but its outcome is unknown
@@ -3546,7 +3548,7 @@ export type BlockPrepareTrainingDatasetHostError = 'review-mode' | 'block is not
3546
3548
  * offering a retry — a retry of the same body after the server did start the
3547
3549
  * run is a SECOND, separately charged run.
3548
3550
  */
3549
- export type BlockRunTrainingHostError = 'review-mode' | 'block is not ready' | 'sign in to train' | 'invalid training request' | 'no block token' | 'declined' | 'submission-unconfirmed';
3551
+ export type BlockRunTrainingHostError = 'review-mode' | 'block is not ready' | 'sign in to train' | 'invalid training request' | 'no block token' | 'training data not uploaded here' | 'declined' | 'submission-unconfirmed';
3550
3552
  /**
3551
3553
  * The HOST'S OWN refusal codes on a `CONFIRMED_STEP_RESULT`'s `error`. Mirrors
3552
3554
  * civitai/civitai's `RUN_CONFIRMED_STEP_HOST_ERRORS`
@@ -151,8 +151,10 @@ export const BLOCK_WORKFLOW_REPEAT_MAX_ITERATIONS = 16;
151
151
  // ============================================================
152
152
  //
153
153
  // 🔴 MIRRORED FROM THE HOST, NOT DESIGNED HERE. Every type in this section copies
154
- // civitai/civitai's zod at `a32f506516` (main, after civitai/civitai#5434):
155
- // `blockTrainingBodySchema` in `src/server/schema/blocks/workflow.schema.ts`, and
154
+ // civitai/civitai's zod as of civitai/civitai#6461 (head `05f1d1fa8f`):
155
+ // `blockTrainingBodySchema` in `src/server/schema/blocks/workflow.schema.ts`,
156
+ // `blockTrainingDatasetItemsSchema` in
157
+ // `src/server/schema/blocks/training-dataset.schema.ts` for `items`, and
156
158
  // `aiToolkitTrainingParamsSchema` in `src/server/schema/orchestrator/training.schema.ts`
157
159
  // for `params`. The host's zod is the definition; this is the copy. A field the
158
160
  // host adds is invisible here until this file is updated, and the host rejects a
@@ -160,10 +162,13 @@ export const BLOCK_WORKFLOW_REPEAT_MAX_ITERATIONS = 16;
160
162
  // as a silently different run.
161
163
  /** Ceiling on what ONE confirmed `kind: 'training'` run may cost, in Buzz. Host constant `BLOCK_TRAINING_MAX_BUZZ_PER_RUN`. */
162
164
  export const BLOCK_TRAINING_MAX_BUZZ_PER_RUN = 5000;
163
- /** Max images in one prepared training dataset. Host constant `BLOCK_TRAINING_DATASET_MAX_ITEMS`. */
165
+ /**
166
+ * Max images in one training dataset ({@link WorkflowBodyTraining.items}), and in
167
+ * one `trainingData` upload. Host constant `BLOCK_TRAINING_DATASET_MAX_ITEMS`.
168
+ */
164
169
  export const BLOCK_TRAINING_DATASET_MAX_ITEMS = 50;
165
170
  /** Max characters of one dataset caption. Host constant `BLOCK_TRAINING_CAPTION_MAX_CHARS`. */
166
- export const BLOCK_TRAINING_CAPTION_MAX_CHARS = 1000;
171
+ export const BLOCK_TRAINING_CAPTION_MAX_CHARS = 8192;
167
172
  /** Max sample prompts on a training body. Host constant `BLOCK_TRAINING_SAMPLE_PROMPTS_MAX`. */
168
173
  export const BLOCK_TRAINING_SAMPLE_PROMPTS_MAX = 6;
169
174
  /** Max characters of one sample prompt. Host constant `BLOCK_TRAINING_SAMPLE_PROMPT_MAX_CHARS`. */
package/dist/index.d.ts CHANGED
@@ -25,11 +25,11 @@
25
25
  *
26
26
  * - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
27
27
  * (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
28
- * 198 symbols and that side effect into the root would make every OAuth app
28
+ * 197 symbols and that side effect into the root would make every OAuth app
29
29
  * that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
30
30
  * Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
31
31
  * audiences; see this package's README.
32
- * 🔴 That 198 is ASSERTED, not merely quoted — by
32
+ * 🔴 That 197 is ASSERTED, not merely quoted — by
33
33
  * `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
34
34
  * `surface.get('./blocks').size`. It said 119 here against a real 125 until
35
35
  * 2026-10-04, because the existing checks pin the root as an exact union and
package/dist/index.js CHANGED
@@ -25,11 +25,11 @@
25
25
  *
26
26
  * - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
27
27
  * (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
28
- * 198 symbols and that side effect into the root would make every OAuth app
28
+ * 197 symbols and that side effect into the root would make every OAuth app
29
29
  * that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
30
30
  * Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
31
31
  * audiences; see this package's README.
32
- * 🔴 That 198 is ASSERTED, not merely quoted — by
32
+ * 🔴 That 197 is ASSERTED, not merely quoted — by
33
33
  * `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
34
34
  * `surface.get('./blocks').size`. It said 119 here against a real 125 until
35
35
  * 2026-10-04, because the existing checks pin the root as an exact union and
@@ -196,6 +196,14 @@ export declare const WORKFLOW_STEP_TYPES: {
196
196
  readonly imageScanning: "Unified image moderation scan (NSFW level, AI/anime recognition, tagging, age detection)";
197
197
  /** Shieldstral text/prompt safety moderation (`mode: 'prompt' | 'text'`). */
198
198
  readonly shieldstralModeration: "Text / prompt safety moderation (Shieldstral)";
199
+ /**
200
+ * Transcribe the audio at `mediaUrl` and classify the lyrics for sexualized
201
+ * minors. `transcriber` is `auto`, `aceStep` or `asr`; `auto` sends speech in
202
+ * a supported language to ASR and everything else to ACE-Step. The output
203
+ * carries the `lyrics`, a `score`, `sexualizedMinors` and `blocked`, plus
204
+ * `hasAudio` and a `skippedReason`.
205
+ */
206
+ readonly audioModeration: "Audio moderation — transcribe lyrics and flag sexualized minors";
199
207
  /** ClamAV scan a model file for malware. */
200
208
  readonly modelClamScan: "Antivirus scan a model file";
201
209
  /** Pickle-scan a model file for unsafe pickles. */
@@ -206,6 +214,13 @@ export declare const WORKFLOW_STEP_TYPES: {
206
214
  readonly chatCompletion: "LLM chat completion";
207
215
  /** Generate a richer prompt from a short seed prompt. */
208
216
  readonly promptEnhancement: "Prompt expansion via LLM";
217
+ /**
218
+ * Embed 1–128 `inputs` (each a unique `id` carrying `text`, an `imageUrl` or
219
+ * a `videoUrl` of at most 60 s / 100 MiB, audio ignored) into normalized
220
+ * vectors with `wemm-2b`. `dimensions` is 64–2048 in powers of two, default
221
+ * 256.
222
+ */
223
+ readonly multimodalEmbedding: "Text / image / video embeddings (wemm-2b)";
209
224
  /**
210
225
  * Fetch one page and return its content. `url` and `formats` are both
211
226
  * REQUIRED; `formats` selects which representations come back — the spec
@@ -226,6 +241,13 @@ export declare const WORKFLOW_STEP_TYPES: {
226
241
  readonly echo: "Echo step — round-trip the input for testing";
227
242
  /** Package multiple blobs into a zip archive. */
228
243
  readonly blobArchive: "Zip multiple blobs into an archive";
244
+ /**
245
+ * Unpack a `.zip`, `.tar` or `.tar.gz` at `sourceUrl`. `mediaTypes` limits
246
+ * which kinds of entry are extracted (default all) and `maxEntries` caps the
247
+ * count (1–2000, default 2000); anything filtered or over the cap comes back
248
+ * in `rejected` rather than failing the step. `entries` are in archive order.
249
+ */
250
+ readonly archiveExtraction: "Extract the entries of a zip / tar / tar.gz archive";
229
251
  /** Combine fields from earlier steps' outputs into a single result. */
230
252
  readonly merge: "Merge — combine fields from earlier steps' outputs into one result";
231
253
  /** Snapshot the installed ComfyUI custom-node packs on a worker. */
@@ -272,6 +294,14 @@ export declare const IMAGE_GEN_ENGINES: {
272
294
  * workers) — picking this engine pins the first-party vendor path.
273
295
  */
274
296
  readonly krea: "Krea (Krea 2, first-party API)";
297
+ /**
298
+ * Krea 3: text-to-image, or an edit of up to six `images`. `resolution` is
299
+ * `1k` or `2k` (default `2k`), `thinkingMode` is `instant` / `medium` /
300
+ * `ultra`, and `quantity` is capped at 4. Omit `aspectRatio` to let Krea
301
+ * choose. Note the spec marks `images` required (no minimum length) while
302
+ * its own description says to omit it for text-to-image.
303
+ */
304
+ readonly krea3: "Krea 3 (generation and editing, 1K / 2K)";
275
305
  /** Self-hosted SDCpp (stable-diffusion.cpp) gen. */
276
306
  readonly sdcpp: "SDCpp (self-hosted diffusion)";
277
307
  /** fal.ai routed gen. */
@@ -204,6 +204,14 @@ export const WORKFLOW_STEP_TYPES = {
204
204
  imageScanning: 'Unified image moderation scan (NSFW level, AI/anime recognition, tagging, age detection)',
205
205
  /** Shieldstral text/prompt safety moderation (`mode: 'prompt' | 'text'`). */
206
206
  shieldstralModeration: 'Text / prompt safety moderation (Shieldstral)',
207
+ /**
208
+ * Transcribe the audio at `mediaUrl` and classify the lyrics for sexualized
209
+ * minors. `transcriber` is `auto`, `aceStep` or `asr`; `auto` sends speech in
210
+ * a supported language to ASR and everything else to ACE-Step. The output
211
+ * carries the `lyrics`, a `score`, `sexualizedMinors` and `blocked`, plus
212
+ * `hasAudio` and a `skippedReason`.
213
+ */
214
+ audioModeration: 'Audio moderation — transcribe lyrics and flag sexualized minors',
207
215
  /** ClamAV scan a model file for malware. */
208
216
  modelClamScan: 'Antivirus scan a model file',
209
217
  /** Pickle-scan a model file for unsafe pickles. */
@@ -215,6 +223,13 @@ export const WORKFLOW_STEP_TYPES = {
215
223
  chatCompletion: 'LLM chat completion',
216
224
  /** Generate a richer prompt from a short seed prompt. */
217
225
  promptEnhancement: 'Prompt expansion via LLM',
226
+ /**
227
+ * Embed 1–128 `inputs` (each a unique `id` carrying `text`, an `imageUrl` or
228
+ * a `videoUrl` of at most 60 s / 100 MiB, audio ignored) into normalized
229
+ * vectors with `wemm-2b`. `dimensions` is 64–2048 in powers of two, default
230
+ * 256.
231
+ */
232
+ multimodalEmbedding: 'Text / image / video embeddings (wemm-2b)',
218
233
  // ----- Web ---------------------------------------------------------------
219
234
  /**
220
235
  * Fetch one page and return its content. `url` and `formats` are both
@@ -237,6 +252,13 @@ export const WORKFLOW_STEP_TYPES = {
237
252
  echo: 'Echo step — round-trip the input for testing',
238
253
  /** Package multiple blobs into a zip archive. */
239
254
  blobArchive: 'Zip multiple blobs into an archive',
255
+ /**
256
+ * Unpack a `.zip`, `.tar` or `.tar.gz` at `sourceUrl`. `mediaTypes` limits
257
+ * which kinds of entry are extracted (default all) and `maxEntries` caps the
258
+ * count (1–2000, default 2000); anything filtered or over the cap comes back
259
+ * in `rejected` rather than failing the step. `entries` are in archive order.
260
+ */
261
+ archiveExtraction: 'Extract the entries of a zip / tar / tar.gz archive',
240
262
  /** Combine fields from earlier steps' outputs into a single result. */
241
263
  merge: "Merge — combine fields from earlier steps' outputs into one result",
242
264
  // ----- Platform internals ------------------------------------------------
@@ -286,6 +308,14 @@ export const IMAGE_GEN_ENGINES = {
286
308
  * workers) — picking this engine pins the first-party vendor path.
287
309
  */
288
310
  krea: 'Krea (Krea 2, first-party API)',
311
+ /**
312
+ * Krea 3: text-to-image, or an edit of up to six `images`. `resolution` is
313
+ * `1k` or `2k` (default `2k`), `thinkingMode` is `instant` / `medium` /
314
+ * `ultra`, and `quantity` is capped at 4. Omit `aspectRatio` to let Krea
315
+ * choose. Note the spec marks `images` required (no minimum length) while
316
+ * its own description says to omit it for text-to-image.
317
+ */
318
+ krea3: 'Krea 3 (generation and editing, 1K / 2K)',
289
319
  /** Self-hosted SDCpp (stable-diffusion.cpp) gen. */
290
320
  sdcpp: 'SDCpp (self-hosted diffusion)',
291
321
  /** fal.ai routed gen. */
@@ -3,7 +3,7 @@
3
3
  * own generated client (`@civitai/client`).
4
4
  *
5
5
  * The sibling `@civitai/app-sdk/orchestrator` module gives you the *catalog*
6
- * (`WORKFLOW_STEP_TYPES` — 55 `$type` names and what each one does) and the
6
+ * (`WORKFLOW_STEP_TYPES` — 58 `$type` names and what each one does) and the
7
7
  * fetch helpers (`submitWorkflow`, `estimateWorkflow`, …), but its body
8
8
  * builders take `input: unknown`. This module is the missing half: the actual
9
9
  * per-step input shapes, tracked against the orchestrator's OpenAPI spec by
@@ -78,7 +78,7 @@
78
78
  * orchestrator's own authorization still decides.
79
79
  *
80
80
  * ⚠️ `WORKFLOW_STEP_TYPES` does NOT mark most of them. Counted at this commit:
81
- * of its 55 entries, exactly THREE sit under its "Platform internals" heading —
81
+ * of its 58 entries, exactly THREE sit under its "Platform internals" heading —
82
82
  * `comfyNodepackSnapshot`, `qwenImageBench` and `promptModeration`. `training`,
83
83
  * `webScrape`, `xGuardModeration`, `modelPickleScan` and the `model*` / `media*` steps are
84
84
  * ordinary documented entries under ordinary headings, and `webScrape` carries
@@ -97,10 +97,11 @@
97
97
  * (#315) without anything going red. The measured state, derived rather than
98
98
  * typed:
99
99
  *
100
- * `WORKFLOW_STEP_TYPES` documents 55 `$type`s; this map covers 47. The 8 with
101
- * no generated template in the pinned `@civitai/client` are `imageScanning`,
102
- * `liveTextToSpeech`, `liveTranscription`, `merge`, `preprocessVideo`,
103
- * `promptModeration`, `soniloAudioGen`, `yuE2`, and
100
+ * `WORKFLOW_STEP_TYPES` documents 58 `$type`s; this map covers 47. The 11 with
101
+ * no generated template in the pinned `@civitai/client` are `archiveExtraction`,
102
+ * `audioModeration`, `imageScanning`, `liveTextToSpeech`, `liveTranscription`,
103
+ * `merge`, `multimodalEmbedding`, `preprocessVideo`, `promptModeration`,
104
+ * `soniloAudioGen`, `yuE2`, and
104
105
  * `WorkflowStepTemplateFor<…>` is a compile error for each of them.
105
106
  *
106
107
  * That gap is EXPECTED and is not a defect in either surface. The catalog
@@ -309,7 +310,7 @@ interface StepTemplateMap {
309
310
  xGuardModeration: XGuardModerationStepTemplate;
310
311
  }
311
312
  /**
312
- * `$type` → its step-template type, for 47 of the catalog's 55 step types.
313
+ * `$type` → its step-template type, for 47 of the catalog's 58 step types.
313
314
  *
314
315
  * Keyed by the WIRE name rather than the generated type name, because the wire
315
316
  * name is what you actually have in hand and the generator does not always
@@ -3,7 +3,7 @@
3
3
  * own generated client (`@civitai/client`).
4
4
  *
5
5
  * The sibling `@civitai/app-sdk/orchestrator` module gives you the *catalog*
6
- * (`WORKFLOW_STEP_TYPES` — 55 `$type` names and what each one does) and the
6
+ * (`WORKFLOW_STEP_TYPES` — 58 `$type` names and what each one does) and the
7
7
  * fetch helpers (`submitWorkflow`, `estimateWorkflow`, …), but its body
8
8
  * builders take `input: unknown`. This module is the missing half: the actual
9
9
  * per-step input shapes, tracked against the orchestrator's OpenAPI spec by
@@ -78,7 +78,7 @@
78
78
  * orchestrator's own authorization still decides.
79
79
  *
80
80
  * ⚠️ `WORKFLOW_STEP_TYPES` does NOT mark most of them. Counted at this commit:
81
- * of its 55 entries, exactly THREE sit under its "Platform internals" heading —
81
+ * of its 58 entries, exactly THREE sit under its "Platform internals" heading —
82
82
  * `comfyNodepackSnapshot`, `qwenImageBench` and `promptModeration`. `training`,
83
83
  * `webScrape`, `xGuardModeration`, `modelPickleScan` and the `model*` / `media*` steps are
84
84
  * ordinary documented entries under ordinary headings, and `webScrape` carries
@@ -97,10 +97,11 @@
97
97
  * (#315) without anything going red. The measured state, derived rather than
98
98
  * typed:
99
99
  *
100
- * `WORKFLOW_STEP_TYPES` documents 55 `$type`s; this map covers 47. The 8 with
101
- * no generated template in the pinned `@civitai/client` are `imageScanning`,
102
- * `liveTextToSpeech`, `liveTranscription`, `merge`, `preprocessVideo`,
103
- * `promptModeration`, `soniloAudioGen`, `yuE2`, and
100
+ * `WORKFLOW_STEP_TYPES` documents 58 `$type`s; this map covers 47. The 11 with
101
+ * no generated template in the pinned `@civitai/client` are `archiveExtraction`,
102
+ * `audioModeration`, `imageScanning`, `liveTextToSpeech`, `liveTranscription`,
103
+ * `merge`, `multimodalEmbedding`, `preprocessVideo`, `promptModeration`,
104
+ * `soniloAudioGen`, `yuE2`, and
104
105
  * `WorkflowStepTemplateFor<…>` is a compile error for each of them.
105
106
  *
106
107
  * That gap is EXPECTED and is not a defect in either surface. The catalog
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@civitai/app-sdk",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "description": "OAuth + PKCE, encrypted-cookie sessions, scopes, and orchestrator helpers for building third-party Civitai apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",