@sogni-ai/sogni-client 5.72.1 → 5.74.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/CHANGELOG.md +20 -0
- package/README.md +59 -0
- package/dist/Account/index.d.ts +5 -0
- package/dist/Account/index.js +50 -23
- package/dist/Account/index.js.map +1 -1
- package/dist/Account/subscription.types.d.ts +2 -0
- package/dist/Account/types.d.ts +9 -0
- package/dist/ApiClient/WebSocketClient/events.d.ts +6 -0
- package/dist/Projects/Job.d.ts +24 -0
- package/dist/Projects/Job.js +38 -18
- package/dist/Projects/Job.js.map +1 -1
- package/dist/Projects/createJobRequestMessage.d.ts +12 -0
- package/dist/Projects/createJobRequestMessage.js +80 -0
- package/dist/Projects/createJobRequestMessage.js.map +1 -1
- package/dist/Projects/index.d.ts +4 -1
- package/dist/Projects/index.js +23 -7
- package/dist/Projects/index.js.map +1 -1
- package/dist/Projects/recovery.js +2 -0
- package/dist/Projects/recovery.js.map +1 -1
- package/dist/Projects/types/RawProject.d.ts +7 -0
- package/dist/Projects/types/events.d.ts +6 -0
- package/dist/Projects/types/index.d.ts +52 -0
- package/dist/Projects/types/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-esm/Account/index.js +50 -23
- package/dist-esm/Account/index.js.map +1 -1
- package/dist-esm/Projects/Job.js +37 -18
- package/dist-esm/Projects/Job.js.map +1 -1
- package/dist-esm/Projects/createJobRequestMessage.js +79 -0
- package/dist-esm/Projects/createJobRequestMessage.js.map +1 -1
- package/dist-esm/Projects/index.js +25 -9
- package/dist-esm/Projects/index.js.map +1 -1
- package/dist-esm/Projects/recovery.js +2 -0
- package/dist-esm/Projects/recovery.js.map +1 -1
- package/dist-esm/Projects/types/index.js.map +1 -1
- package/dist-esm/version.js +1 -1
- package/llms-full.txt +48 -0
- package/llms.txt +2 -0
- package/package.json +3 -1
- package/src/Account/index.ts +51 -4
- package/src/Account/subscription.types.ts +2 -0
- package/src/Account/types.ts +10 -0
- package/src/ApiClient/WebSocketClient/events.ts +6 -0
- package/src/Projects/Job.ts +62 -1
- package/src/Projects/createJobRequestMessage.ts +90 -0
- package/src/Projects/index.ts +24 -4
- package/src/Projects/recovery.ts +1 -0
- package/src/Projects/types/RawProject.ts +7 -0
- package/src/Projects/types/events.ts +6 -0
- package/src/Projects/types/index.ts +52 -0
- package/src/version.ts +1 -1
package/llms-full.txt
CHANGED
|
@@ -963,6 +963,49 @@ const project = await sogni.projects.create({
|
|
|
963
963
|
});
|
|
964
964
|
```
|
|
965
965
|
|
|
966
|
+
SEEDANCE 2.5 DRAFT -> 1080P FINAL:
|
|
967
|
+
|
|
968
|
+
Draft mode is chosen at submission; a normal job cannot be finalized. A draft
|
|
969
|
+
(`seedanceDraft: true`) takes every normal Seedance 2.5 input at a 480p size and
|
|
970
|
+
costs the same as that 480p job. The completed job reports `job.seedanceDraft`
|
|
971
|
+
and `job.seedanceDraftExpiresAt` (ISO 8601). Finalize it with
|
|
972
|
+
`seedanceDraftJobId: draftJob.id` on the same model and account before it
|
|
973
|
+
expires: the final reuses the draft's prompt, media, duration, aspect ratio,
|
|
974
|
+
seed and audio setting, so send none of them (no references, frames, audio,
|
|
975
|
+
`duration`, `frames`, `ratio`, `seed`, `generateAudio` or `seedanceTaskType`),
|
|
976
|
+
use `numberOfMedia: 1` and an empty `positivePrompt`. `outputFormat` and
|
|
977
|
+
`returnLastFrame` may be set. The output is the draft's aspect ratio at 1080p,
|
|
978
|
+
priced at 1080p for the draft's duration (video-input rate when the draft had
|
|
979
|
+
one); each finalize bills again. The final reports `job.seedanceDraftJobId`.
|
|
980
|
+
`estimateVideoCost` accepts both fields; for a finalize the server prices from
|
|
981
|
+
the draft, so width, height, duration and fps do not change the quote.
|
|
982
|
+
|
|
983
|
+
```javascript
|
|
984
|
+
const draft = await sogni.projects.create({
|
|
985
|
+
type: 'video',
|
|
986
|
+
network: 'fast',
|
|
987
|
+
modelId: 'seedance-2-5',
|
|
988
|
+
positivePrompt: 'A lighthouse keeper climbs a spiral stair as a storm rolls in',
|
|
989
|
+
numberOfMedia: 1,
|
|
990
|
+
duration: 8,
|
|
991
|
+
width: 864,
|
|
992
|
+
height: 496,
|
|
993
|
+
seedanceDraft: true
|
|
994
|
+
});
|
|
995
|
+
await draft.waitForCompletion();
|
|
996
|
+
const [draftJob] = draft.jobs; // draftJob.seedanceDraft === true
|
|
997
|
+
|
|
998
|
+
const final = await sogni.projects.create({
|
|
999
|
+
type: 'video',
|
|
1000
|
+
network: 'fast',
|
|
1001
|
+
modelId: 'seedance-2-5',
|
|
1002
|
+
positivePrompt: '',
|
|
1003
|
+
numberOfMedia: 1,
|
|
1004
|
+
seedanceDraftJobId: draftJob.id
|
|
1005
|
+
});
|
|
1006
|
+
await final.waitForCompletion(); // final.jobs[0].seedanceDraftJobId === draftJob.id
|
|
1007
|
+
```
|
|
1008
|
+
|
|
966
1009
|
SEEDANCE IMAGE-TO-VIDEO:
|
|
967
1010
|
|
|
968
1011
|
```javascript
|
|
@@ -2274,6 +2317,8 @@ VIDEO-SPECIFIC PARAMETERS:
|
|
|
2274
2317
|
referenceVideoUrls?: string[]; // Seedance only
|
|
2275
2318
|
referenceAudioUrls?: string[]; // Seedance only
|
|
2276
2319
|
seedanceTaskType?: 'reference' | 'edit' | 'extend'; // Seedance 2.5 loose-reference operation
|
|
2320
|
+
seedanceDraft?: boolean; // Seedance 2.5: 480p draft that can be finalized at 1080p
|
|
2321
|
+
seedanceDraftJobId?: string; // Seedance 2.5: finalize that draft (its job id) at 1080p; inherits its inputs
|
|
2277
2322
|
audioStart?: number; // Seconds into audio
|
|
2278
2323
|
audioDuration?: number; // Seconds of audio
|
|
2279
2324
|
videoStart?: number; // Seconds into video
|
|
@@ -2828,3 +2873,6 @@ For additional resources:
|
|
|
2828
2873
|
- Examples: https://github.com/Sogni-AI/sogni-client/tree/main/examples
|
|
2829
2874
|
- API Docs: https://sdk-docs.sogni.ai
|
|
2830
2875
|
- Support: https://www.sogni.ai/support
|
|
2876
|
+
|
|
2877
|
+
OPTIONAL PRIORITY MODE
|
|
2878
|
+
Paid Unlimited members may set priorityMode: true on projects.create() with network: fast, tokenType: spark and billingMode: subscription. Earlier Fast queue placement costs 1.5x daily and monthly usage credits, behind Premium Spark. Plan caps and concurrency are unchanged; rendering speed, quality and GPU assignment are not guaranteed to change. Omit the option for standard behavior. Trials, Relaxed, token-paid generations and third-party vendor models cannot use it. Image, video and audio estimate helpers also accept priorityMode and include the weighted daily share in dailyFairUsePct. This setting applies to media generations, not chat or prompt-writing calls.
|
package/llms.txt
CHANGED
|
@@ -482,6 +482,8 @@ Use `referenceImageUrls`, `referenceVideoUrls`, and `referenceAudioUrls` for See
|
|
|
482
482
|
|
|
483
483
|
For Seedance 2.5 loose-reference video operations, set `seedanceTaskType` to `reference`, `edit`, or `extend`. Edit/extend require a reference video. The server applies adaptive source aspect ratio to first/last-frame, edit, and extend requests and provider-selected duration to edit requests. Loose-reference requests require an explicit task type.
|
|
484
484
|
|
|
485
|
+
Seedance 2.5 draft mode: send `seedanceDraft: true` with a 480p size for a draft priced like the same 480p job (choose it at submission; a normal job cannot be finalized). The completed job reports `job.seedanceDraft` and `job.seedanceDraftExpiresAt` (ISO 8601). Finalize it at 1080p with `seedanceDraftJobId: draftJob.id` on the same model, `numberOfMedia: 1` and an empty `positivePrompt`: the final reuses the draft's prompt, media, duration, aspect ratio, seed and audio setting, so omit references, frames, audio, `duration`, `frames`, `ratio`, `seed`, `generateAudio` and `seedanceTaskType`. The final reports `job.seedanceDraftJobId`. `estimateVideoCost` accepts both fields; the server prices a finalize from the draft. The two fields are mutually exclusive.
|
|
486
|
+
|
|
485
487
|
Aliases used by chat tools and examples: `seedance2`, `seedance2-mini`, `seedance2-5`, `seedance2-5-uncensored`, and `seedance2-mini-uncensored`. V2V uses `seedance2` or `seedance2-mini` as the model selector and `seedance-v2v` as the control mode. Seedance Mini is the preferred lower-cost 720p tier. See `examples/workflow_partner_seedance_video.mjs` for focused endpoint coverage across Seedance T2V/I2V/IA2V/V2V and multimodal context prompt expansion.
|
|
486
488
|
|
|
487
489
|
### Happy Horse 1.1 (external API)
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "5.
|
|
6
|
+
"version": "5.74.0",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=22"
|
|
9
9
|
},
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"test:model-options": "npm run build && node scripts/check-video-model-options.cjs",
|
|
71
71
|
"test:video-estimates": "npm run build && node scripts/check-video-estimates.cjs",
|
|
72
72
|
"test:seedance-task-type": "npm run build && node scripts/check-seedance-task-type.cjs && node scripts/check-seedance-r2v-example.cjs",
|
|
73
|
+
"test:seedance-draft": "npm run build && node scripts/check-seedance-draft.cjs",
|
|
73
74
|
"test:model-consent-error": "npm run build && node scripts/check-model-consent-error.cjs",
|
|
74
75
|
"test:wan3-video": "npm run build && node scripts/check-wan3-video.cjs",
|
|
75
76
|
"test:sam3-image-segment": "npm run build && node scripts/check-sam3-image-segment.cjs",
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
"test:capability-exports": "npm run build && node scripts/check-capability-exports.cjs",
|
|
82
83
|
"test:generation-receipt-transport": "npm run build && node scripts/check-generation-receipt-transport.cjs",
|
|
83
84
|
"test:prompt-expansion": "npm run build && node scripts/check-prompt-expansion-transport.cjs",
|
|
85
|
+
"test:priority-mode": "npm run build && node scripts/check-priority-mode.cjs && node scripts/check-priority-capability.cjs",
|
|
84
86
|
"test:reference-content-types": "npm run build && node scripts/check-reference-content-types.cjs",
|
|
85
87
|
"test:minimax-h3-audio-policy": "node scripts/check-minimax-h3-source-audio-policy.cjs",
|
|
86
88
|
"test:minimax-h3-balanced": "npm run build && node scripts/check-minimax-h3-balanced.cjs",
|
package/src/Account/index.ts
CHANGED
|
@@ -102,6 +102,11 @@ class AccountApi extends ApiGroup {
|
|
|
102
102
|
*/
|
|
103
103
|
private appliedSubscriptionSocketWrites = 0;
|
|
104
104
|
private accountSessionVersion?: number;
|
|
105
|
+
/** Priority availability belongs to the current authenticated connection. */
|
|
106
|
+
private socketPriorityAvailability: {
|
|
107
|
+
sessionVersion: number;
|
|
108
|
+
available: boolean;
|
|
109
|
+
} | null = null;
|
|
105
110
|
|
|
106
111
|
constructor(config: ApiConfig) {
|
|
107
112
|
super(config);
|
|
@@ -133,6 +138,7 @@ class AccountApi extends ApiGroup {
|
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
private handleServerConnected({ network }: { network: SupernetType }) {
|
|
141
|
+
this.clearSocketPriorityAvailability();
|
|
136
142
|
this.currentAccount._update({
|
|
137
143
|
networkStatus: 'connected',
|
|
138
144
|
network
|
|
@@ -140,6 +146,7 @@ class AccountApi extends ApiGroup {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
private handleServerConnecting({ network }: { network: SupernetType }) {
|
|
149
|
+
this.clearSocketPriorityAvailability();
|
|
143
150
|
this.currentAccount._update({
|
|
144
151
|
networkStatus: 'connecting',
|
|
145
152
|
network
|
|
@@ -147,12 +154,41 @@ class AccountApi extends ApiGroup {
|
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
private handleServerDisconnected() {
|
|
157
|
+
this.clearSocketPriorityAvailability();
|
|
150
158
|
this.currentAccount._update({
|
|
151
159
|
networkStatus: 'disconnected',
|
|
152
160
|
network: null
|
|
153
161
|
});
|
|
154
162
|
}
|
|
155
163
|
|
|
164
|
+
private clearSocketPriorityAvailability() {
|
|
165
|
+
this.socketPriorityAvailability = null;
|
|
166
|
+
const subscription = this.currentAccount.subscription;
|
|
167
|
+
if (!subscription?.capabilities?.priorityMode) return;
|
|
168
|
+
const capabilities = { ...subscription.capabilities };
|
|
169
|
+
delete capabilities.priorityMode;
|
|
170
|
+
this.currentAccount._update({ subscription: { ...subscription, capabilities } });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Plan refreshes retain the current connection's coarse Priority availability. */
|
|
174
|
+
private withSocketPriorityAvailability(
|
|
175
|
+
subscription: SubscriptionEntitlementSnapshot
|
|
176
|
+
): SubscriptionEntitlementSnapshot {
|
|
177
|
+
const available = Boolean(
|
|
178
|
+
this.socketPriorityAvailability?.available &&
|
|
179
|
+
this.socketPriorityAvailability.sessionVersion === this.client.auth.sessionVersion &&
|
|
180
|
+
this.client.socket.isConnected &&
|
|
181
|
+
subscription.active &&
|
|
182
|
+
subscription.status !== 'trialing' &&
|
|
183
|
+
(subscription.tier === 'unlimited' || subscription.tier === 'unlimited_pro')
|
|
184
|
+
);
|
|
185
|
+
if (available === (subscription.capabilities?.priorityMode === true)) return subscription;
|
|
186
|
+
const capabilities = { ...subscription.capabilities };
|
|
187
|
+
if (available) capabilities.priorityMode = true;
|
|
188
|
+
else delete capabilities.priorityMode;
|
|
189
|
+
return { ...subscription, capabilities };
|
|
190
|
+
}
|
|
191
|
+
|
|
156
192
|
private mapSocketSubscriptionEntitlement(
|
|
157
193
|
data: SocketSubscriptionEntitlementData | undefined
|
|
158
194
|
): SubscriptionEntitlementSnapshot | undefined {
|
|
@@ -289,12 +325,17 @@ class AccountApi extends ApiGroup {
|
|
|
289
325
|
}
|
|
290
326
|
if (source === 'socket') {
|
|
291
327
|
this.appliedSubscriptionSocketWrites += 1;
|
|
328
|
+
this.socketPriorityAvailability = {
|
|
329
|
+
sessionVersion: this.client.auth.sessionVersion,
|
|
330
|
+
available: subscription.capabilities?.priorityMode === true
|
|
331
|
+
};
|
|
292
332
|
}
|
|
333
|
+
const normalizedSubscription = this.withSocketPriorityAvailability(subscription);
|
|
293
334
|
const current = this.currentAccount.subscription;
|
|
294
|
-
if (current && JSON.stringify(current) === JSON.stringify(
|
|
335
|
+
if (current && JSON.stringify(current) === JSON.stringify(normalizedSubscription)) {
|
|
295
336
|
return true;
|
|
296
337
|
}
|
|
297
|
-
this.currentAccount._update({ subscription });
|
|
338
|
+
this.currentAccount._update({ subscription: normalizedSubscription });
|
|
298
339
|
return true;
|
|
299
340
|
}
|
|
300
341
|
|
|
@@ -355,6 +396,7 @@ class AccountApi extends ApiGroup {
|
|
|
355
396
|
this.accountSessionVersion = this.client.auth.sessionVersion;
|
|
356
397
|
this.lastAppliedSubscriptionVersion = null;
|
|
357
398
|
this.appliedSubscriptionSocketWrites = 0;
|
|
399
|
+
this.socketPriorityAvailability = null;
|
|
358
400
|
this.currentAccount._clear();
|
|
359
401
|
}
|
|
360
402
|
|
|
@@ -364,6 +406,7 @@ class AccountApi extends ApiGroup {
|
|
|
364
406
|
// the next login starts from a clean slate.
|
|
365
407
|
this.lastAppliedSubscriptionVersion = null;
|
|
366
408
|
this.appliedSubscriptionSocketWrites = 0;
|
|
409
|
+
this.socketPriorityAvailability = null;
|
|
367
410
|
this.currentAccount._clear();
|
|
368
411
|
} else {
|
|
369
412
|
this.clearChangedAccountSession();
|
|
@@ -699,6 +742,7 @@ class AccountApi extends ApiGroup {
|
|
|
699
742
|
if (params.provider) {
|
|
700
743
|
query.provider = params.provider;
|
|
701
744
|
}
|
|
745
|
+
if (params.includePriorityAccounting === true) query.includePriorityAccounting = 'true';
|
|
702
746
|
const res = await this.client.rest.get<ApiResponse<TxHistoryData>>(
|
|
703
747
|
'/v1/transactions/list',
|
|
704
748
|
query
|
|
@@ -723,7 +767,10 @@ class AccountApi extends ApiGroup {
|
|
|
723
767
|
paymentModel: tx.paymentModel,
|
|
724
768
|
subscriptionTier: tx.subscriptionTier,
|
|
725
769
|
subscriptionTrialing: tx.subscriptionTrialing,
|
|
726
|
-
subscriptionThrottled: tx.subscriptionThrottled
|
|
770
|
+
subscriptionThrottled: tx.subscriptionThrottled,
|
|
771
|
+
...(params.includePriorityAccounting === true && tx.priorityUsage
|
|
772
|
+
? { priorityUsage: tx.priorityUsage }
|
|
773
|
+
: {})
|
|
727
774
|
})
|
|
728
775
|
),
|
|
729
776
|
next: {
|
|
@@ -965,7 +1012,7 @@ class AccountApi extends ApiGroup {
|
|
|
965
1012
|
// currentAccount.subscription.
|
|
966
1013
|
return this.currentAccount.subscription;
|
|
967
1014
|
}
|
|
968
|
-
return subscription;
|
|
1015
|
+
return this.currentAccount.subscription ?? subscription;
|
|
969
1016
|
}
|
|
970
1017
|
|
|
971
1018
|
/**
|
|
@@ -46,6 +46,8 @@ export type SubscriptionTerm = 'monthly' | 'annual';
|
|
|
46
46
|
/** Actionable monthly fair-use state returned only while Fast limits are active. */
|
|
47
47
|
export interface SubscriptionFairUseState {
|
|
48
48
|
limited: true;
|
|
49
|
+
/** Temporary accounting confirmation, rather than confirmed exhausted capacity. */
|
|
50
|
+
accountingStatus?: 'pending';
|
|
49
51
|
/** Subscription-funded Spark used in the current monthly fair-use window. */
|
|
50
52
|
usageSpark: number;
|
|
51
53
|
/** Retail-equivalent USD value of the subscription-funded usage. */
|
package/src/Account/types.ts
CHANGED
|
@@ -97,6 +97,14 @@ export interface TxHistoryParams {
|
|
|
97
97
|
limit: number;
|
|
98
98
|
provider?: string;
|
|
99
99
|
offset?: number;
|
|
100
|
+
/** Include saved Priority labels on plan usage entries. */
|
|
101
|
+
includePriorityAccounting?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface PriorityUsageSummary {
|
|
105
|
+
outputCount: number;
|
|
106
|
+
rate: 1.5;
|
|
107
|
+
coverage: 'complete' | 'partial';
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
export interface TxHistoryData {
|
|
@@ -105,6 +113,7 @@ export interface TxHistoryData {
|
|
|
105
113
|
}
|
|
106
114
|
|
|
107
115
|
export interface TxRaw {
|
|
116
|
+
priorityUsage?: PriorityUsageSummary;
|
|
108
117
|
_id: string;
|
|
109
118
|
id: string;
|
|
110
119
|
SID: number;
|
|
@@ -130,6 +139,7 @@ export interface TxRaw {
|
|
|
130
139
|
}
|
|
131
140
|
|
|
132
141
|
export interface TxHistoryEntry {
|
|
142
|
+
priorityUsage?: PriorityUsageSummary;
|
|
133
143
|
id: string;
|
|
134
144
|
address: string;
|
|
135
145
|
createTime: Date;
|
|
@@ -292,6 +292,12 @@ export type JobResultData = {
|
|
|
292
292
|
lastFrameUrl?: string;
|
|
293
293
|
lastFrameKey?: string;
|
|
294
294
|
outputFormat?: string;
|
|
295
|
+
/** Seedance 2.5: this job is a draft that can be finalized at 1080p. */
|
|
296
|
+
seedanceDraft?: boolean;
|
|
297
|
+
/** Seedance 2.5 draft: ISO 8601 time after which it can no longer be finalized. */
|
|
298
|
+
seedanceDraftExpiresAt?: string;
|
|
299
|
+
/** Seedance 2.5 final: the Sogni job id of the draft it finalized. */
|
|
300
|
+
seedanceDraftJobId?: string;
|
|
295
301
|
/**
|
|
296
302
|
* Files the worker uploaded for this result, as the worker reported them.
|
|
297
303
|
* The SDK reads `contentType` to pick the download endpoint for a result
|
package/src/Projects/Job.ts
CHANGED
|
@@ -117,6 +117,41 @@ function directResultUrlFromRawJob(rawJob: RawJob): string | null {
|
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
type SeedanceDraftResultFields = Pick<
|
|
121
|
+
JobData,
|
|
122
|
+
'seedanceDraft' | 'seedanceDraftExpiresAt' | 'seedanceDraftJobId'
|
|
123
|
+
>;
|
|
124
|
+
|
|
125
|
+
function pickSeedanceDraftFields(source: unknown): SeedanceDraftResultFields {
|
|
126
|
+
if (!source || typeof source !== 'object') return {};
|
|
127
|
+
const { seedanceDraft, seedanceDraftExpiresAt, seedanceDraftJobId } = source as Record<
|
|
128
|
+
string,
|
|
129
|
+
unknown
|
|
130
|
+
>;
|
|
131
|
+
return {
|
|
132
|
+
...(seedanceDraft === true ? { seedanceDraft } : {}),
|
|
133
|
+
...(typeof seedanceDraftExpiresAt === 'string' && seedanceDraftExpiresAt
|
|
134
|
+
? { seedanceDraftExpiresAt }
|
|
135
|
+
: {}),
|
|
136
|
+
...(typeof seedanceDraftJobId === 'string' && seedanceDraftJobId ? { seedanceDraftJobId } : {})
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Seedance 2.5 draft result fields from a socket result or a REST job record,
|
|
142
|
+
* read from the record itself or its allowlisted `result` receipt (the record
|
|
143
|
+
* wins). Only well-formed fields are returned, so a record without them never
|
|
144
|
+
* clears values a job already holds.
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
export function seedanceDraftResultFields(record: unknown): SeedanceDraftResultFields {
|
|
148
|
+
if (!record || typeof record !== 'object') return {};
|
|
149
|
+
return {
|
|
150
|
+
...pickSeedanceDraftFields((record as { result?: unknown }).result),
|
|
151
|
+
...pickSeedanceDraftFields(record)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
120
155
|
function etaProgressPercent(
|
|
121
156
|
startedAt: Date | undefined,
|
|
122
157
|
eta: Date | undefined
|
|
@@ -156,6 +191,12 @@ export interface JobData {
|
|
|
156
191
|
lastFrameUrl?: string;
|
|
157
192
|
lastFrameKey?: string;
|
|
158
193
|
outputFormat?: string;
|
|
194
|
+
/** Seedance 2.5: this job is a draft that can be finalized at 1080p. */
|
|
195
|
+
seedanceDraft?: boolean;
|
|
196
|
+
/** Seedance 2.5 draft: ISO 8601 time after which it can no longer be finalized. */
|
|
197
|
+
seedanceDraftExpiresAt?: string;
|
|
198
|
+
/** Seedance 2.5 final: the Sogni job id of the draft it finalized. */
|
|
199
|
+
seedanceDraftJobId?: string;
|
|
159
200
|
provenance?: JobProvenance;
|
|
160
201
|
error?: ErrorData;
|
|
161
202
|
positivePrompt?: string;
|
|
@@ -220,6 +261,7 @@ class Job extends DataEntity<JobData, JobEventMap> {
|
|
|
220
261
|
lastFrameUrl: rawJob.lastFrameUrl ?? (rawJob.result?.lastFrameUrl as string | undefined),
|
|
221
262
|
lastFrameKey: rawJob.lastFrameKey ?? (rawJob.result?.lastFrameKey as string | undefined),
|
|
222
263
|
outputFormat: rawJob.outputFormat ?? (rawJob.result?.outputFormat as string | undefined),
|
|
264
|
+
...seedanceDraftResultFields(rawJob),
|
|
223
265
|
provenance: rawJob.result
|
|
224
266
|
},
|
|
225
267
|
options
|
|
@@ -317,6 +359,24 @@ class Job extends DataEntity<JobData, JobEventMap> {
|
|
|
317
359
|
return this.data.lastFrameUrl;
|
|
318
360
|
}
|
|
319
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Seedance 2.5: `true` on a completed draft, which can be finalized at 1080p by
|
|
364
|
+
* passing this job's `id` as `seedanceDraftJobId`. Reported to the owner only.
|
|
365
|
+
*/
|
|
366
|
+
get seedanceDraft() {
|
|
367
|
+
return this.data.seedanceDraft;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Seedance 2.5 draft: ISO 8601 time after which it can no longer be finalized. */
|
|
371
|
+
get seedanceDraftExpiresAt() {
|
|
372
|
+
return this.data.seedanceDraftExpiresAt;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Seedance 2.5 final: the Sogni job id of the draft it finalized. */
|
|
376
|
+
get seedanceDraftJobId() {
|
|
377
|
+
return this.data.seedanceDraftJobId;
|
|
378
|
+
}
|
|
379
|
+
|
|
320
380
|
/** Refresh the signed URL for an exported final frame. */
|
|
321
381
|
async getLastFrameUrl(): Promise<string> {
|
|
322
382
|
const url = await this._api.mediaDownloadUrl({
|
|
@@ -552,7 +612,8 @@ class Job extends DataEntity<JobData, JobEventMap> {
|
|
|
552
612
|
...(data.result ? { provenance: data.result } : {}),
|
|
553
613
|
lastFrameUrl: data.lastFrameUrl ?? (data.result?.lastFrameUrl as string | undefined),
|
|
554
614
|
lastFrameKey: data.lastFrameKey ?? (data.result?.lastFrameKey as string | undefined),
|
|
555
|
-
outputFormat: data.outputFormat ?? (data.result?.outputFormat as string | undefined)
|
|
615
|
+
outputFormat: data.outputFormat ?? (data.result?.outputFormat as string | undefined),
|
|
616
|
+
...seedanceDraftResultFields(data)
|
|
556
617
|
};
|
|
557
618
|
if (JOB_STATUS_MAP[data.status]) {
|
|
558
619
|
delta.status = JOB_STATUS_MAP[data.status];
|
|
@@ -166,6 +166,8 @@ function validateVideoWorkflowAssets(params: VideoProjectParams): void {
|
|
|
166
166
|
if (exportError) {
|
|
167
167
|
throw new ApiError(400, { status: 'error', errorCode: 0, message: exportError });
|
|
168
168
|
}
|
|
169
|
+
validateSeedanceDraftFields(params);
|
|
170
|
+
validateSeedanceDraftFinalize(params);
|
|
169
171
|
validateVideoContextImages(params);
|
|
170
172
|
validateVideoKeyframes(params);
|
|
171
173
|
validateVideoReferenceArrays(params);
|
|
@@ -250,6 +252,80 @@ function validateVideoWorkflowAssets(params: VideoProjectParams): void {
|
|
|
250
252
|
}
|
|
251
253
|
}
|
|
252
254
|
|
|
255
|
+
/** Prompt a finalize request carries; the final always uses its draft's prompt. */
|
|
256
|
+
const SEEDANCE_DRAFT_FINALIZE_PROMPT = 'Finalize the Seedance 2.5 draft at 1080p.';
|
|
257
|
+
|
|
258
|
+
function seedanceDraftError(message: string): never {
|
|
259
|
+
throw new ApiError(400, { status: 'error', errorCode: 0, message });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Seedance 2.5 draft mode fields, shared by projects and video estimates:
|
|
264
|
+
* `seedanceDraft` creates a 480p draft and `seedanceDraftJobId` finalizes one
|
|
265
|
+
* at 1080p. The server checks ownership, expiry, model and size; this only
|
|
266
|
+
* catches requests no server would accept.
|
|
267
|
+
*/
|
|
268
|
+
export function validateSeedanceDraftFields(params: {
|
|
269
|
+
modelId?: string;
|
|
270
|
+
model?: string;
|
|
271
|
+
seedanceDraft?: unknown;
|
|
272
|
+
seedanceDraftJobId?: unknown;
|
|
273
|
+
}): void {
|
|
274
|
+
const { seedanceDraft, seedanceDraftJobId } = params;
|
|
275
|
+
if (seedanceDraft !== undefined && typeof seedanceDraft !== 'boolean') {
|
|
276
|
+
seedanceDraftError('seedanceDraft must be a boolean.');
|
|
277
|
+
}
|
|
278
|
+
if (
|
|
279
|
+
seedanceDraftJobId !== undefined &&
|
|
280
|
+
(typeof seedanceDraftJobId !== 'string' || !seedanceDraftJobId.trim())
|
|
281
|
+
) {
|
|
282
|
+
seedanceDraftError('seedanceDraftJobId must be the Sogni job id of a Seedance 2.5 draft.');
|
|
283
|
+
}
|
|
284
|
+
if (seedanceDraft !== true && seedanceDraftJobId === undefined) return;
|
|
285
|
+
if (!isSeedance25Model(String(params.modelId ?? params.model ?? ''))) {
|
|
286
|
+
seedanceDraftError('Seedance draft mode is supported only by Seedance 2.5.');
|
|
287
|
+
}
|
|
288
|
+
if (seedanceDraft === true && seedanceDraftJobId !== undefined) {
|
|
289
|
+
seedanceDraftError(
|
|
290
|
+
'seedanceDraft and seedanceDraftJobId are mutually exclusive: create a draft, or finalize one.'
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* A finalize renders an existing draft at 1080p and inherits every
|
|
297
|
+
* generation-defining input from it; the vendor refuses any of them sent again,
|
|
298
|
+
* even with identical values.
|
|
299
|
+
*/
|
|
300
|
+
function validateSeedanceDraftFinalize(params: VideoProjectParams): void {
|
|
301
|
+
if (params.seedanceDraftJobId === undefined) return;
|
|
302
|
+
const inherited: Array<[keyof VideoProjectParams, boolean]> = [
|
|
303
|
+
['referenceImage', Boolean(params.referenceImage)],
|
|
304
|
+
['referenceImageEnd', Boolean(params.referenceImageEnd)],
|
|
305
|
+
['referenceImageUrls', asReferenceUrlArray(params.referenceImageUrls).length > 0],
|
|
306
|
+
['referenceVideo', Boolean(params.referenceVideo)],
|
|
307
|
+
['referenceVideoUrls', asReferenceUrlArray(params.referenceVideoUrls).length > 0],
|
|
308
|
+
['referenceAudio', Boolean(params.referenceAudio)],
|
|
309
|
+
['referenceAudioUrls', asReferenceUrlArray(params.referenceAudioUrls).length > 0],
|
|
310
|
+
['referenceAudioIdentity', Boolean(params.referenceAudioIdentity)],
|
|
311
|
+
['duration', params.duration !== undefined],
|
|
312
|
+
['frames', params.frames !== undefined],
|
|
313
|
+
['ratio', params.ratio !== undefined],
|
|
314
|
+
['seed', params.seed !== undefined],
|
|
315
|
+
['generateAudio', params.generateAudio !== undefined],
|
|
316
|
+
['seedanceTaskType', params.seedanceTaskType !== undefined]
|
|
317
|
+
];
|
|
318
|
+
const sent = inherited.filter(([, present]) => present).map(([name]) => name);
|
|
319
|
+
if (sent.length) {
|
|
320
|
+
seedanceDraftError(
|
|
321
|
+
`A Seedance 2.5 finalize reuses its draft's inputs and settings; remove ${sent.join(', ')}.`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if ((params.numberOfMedia ?? 1) !== 1) {
|
|
325
|
+
seedanceDraftError('A Seedance 2.5 finalize renders its draft once; numberOfMedia must be 1.');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
253
329
|
function validateSeedanceTaskType(params: VideoProjectParams): void {
|
|
254
330
|
const taskType = params.seedanceTaskType;
|
|
255
331
|
const isSeedance25 = isSeedance25Model(params.modelId);
|
|
@@ -1754,6 +1830,15 @@ function applyVideoParams(
|
|
|
1754
1830
|
if (params.seedanceTaskType !== undefined) {
|
|
1755
1831
|
keyFrame.seedanceTaskType = params.seedanceTaskType;
|
|
1756
1832
|
}
|
|
1833
|
+
if (params.seedanceDraft === true) {
|
|
1834
|
+
keyFrame.seedanceDraft = true;
|
|
1835
|
+
}
|
|
1836
|
+
if (params.seedanceDraftJobId !== undefined) {
|
|
1837
|
+
keyFrame.seedanceDraftJobId = params.seedanceDraftJobId;
|
|
1838
|
+
// The final inherits the draft's prompt. The request still carries a
|
|
1839
|
+
// non-empty prompt, which the server ignores for a finalize.
|
|
1840
|
+
keyFrame.positivePrompt = SEEDANCE_DRAFT_FINALIZE_PROMPT;
|
|
1841
|
+
}
|
|
1757
1842
|
if (params.audioIdentityStrength !== undefined) {
|
|
1758
1843
|
keyFrame.identityGuidanceScale = params.audioIdentityStrength;
|
|
1759
1844
|
}
|
|
@@ -2059,6 +2144,11 @@ function createJobRequestMessage(id: string, params: ProjectParams, options: Mod
|
|
|
2059
2144
|
if (params.network) {
|
|
2060
2145
|
jobRequest.network = params.network;
|
|
2061
2146
|
}
|
|
2147
|
+
if (params.priorityMode !== undefined) {
|
|
2148
|
+
if (typeof params.priorityMode !== 'boolean')
|
|
2149
|
+
throw new Error('Priority mode must be true or false.');
|
|
2150
|
+
jobRequest.priorityMode = params.priorityMode;
|
|
2151
|
+
}
|
|
2062
2152
|
if (isImageParams(params) && params.embedPromptMetadata !== undefined) {
|
|
2063
2153
|
if (typeof params.embedPromptMetadata !== 'boolean') {
|
|
2064
2154
|
throw new Error('embedPromptMetadata must be a boolean');
|
package/src/Projects/index.ts
CHANGED
|
@@ -41,7 +41,8 @@ import {
|
|
|
41
41
|
} from './recovery.js';
|
|
42
42
|
import createJobRequestMessage, {
|
|
43
43
|
type JobRequestRaw,
|
|
44
|
-
rejectRetiredOutputScale
|
|
44
|
+
rejectRetiredOutputScale,
|
|
45
|
+
validateSeedanceDraftFields
|
|
45
46
|
} from './createJobRequestMessage.js';
|
|
46
47
|
import { ApiError, ApiResponse } from '../ApiClient/index.js';
|
|
47
48
|
import { EstimationResponse } from './types/EstimationResponse.js';
|
|
@@ -66,7 +67,7 @@ import type { WaitingReason } from './types/WaitingReason.js';
|
|
|
66
67
|
import ErrorData from '../types/ErrorData.js';
|
|
67
68
|
import { SupernetType } from '../ApiClient/WebSocketClient/types.js';
|
|
68
69
|
import Cache from '../lib/Cache.js';
|
|
69
|
-
import Job, { enhancementDefaults } from './Job.js';
|
|
70
|
+
import Job, { enhancementDefaults, seedanceDraftResultFields } from './Job.js';
|
|
70
71
|
import {
|
|
71
72
|
calculateVideoFrames,
|
|
72
73
|
getEnhacementStrength,
|
|
@@ -1035,6 +1036,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
1035
1036
|
let performedStepCount = data.performedStepCount;
|
|
1036
1037
|
let seed = data.lastSeed !== undefined ? Number(data.lastSeed) : undefined;
|
|
1037
1038
|
const provenance = jobProvenanceFromResult(data);
|
|
1039
|
+
const seedanceDraftFields = seedanceDraftResultFields(data);
|
|
1038
1040
|
if (project) {
|
|
1039
1041
|
const job = project.job(data.imgID);
|
|
1040
1042
|
if (job) {
|
|
@@ -1053,6 +1055,8 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
1053
1055
|
lastFrameUrl: data.lastFrameUrl,
|
|
1054
1056
|
lastFrameKey: data.lastFrameKey,
|
|
1055
1057
|
outputFormat: data.outputFormat,
|
|
1058
|
+
// Set with the status, so a `completed` listener can already read them.
|
|
1059
|
+
...seedanceDraftFields,
|
|
1056
1060
|
// Unchanged meaning: the server withheld the media. The label for
|
|
1057
1061
|
// media that WAS delivered is `nsfwDetected`, deliberately kept out of
|
|
1058
1062
|
// this flag so upgrading the SDK changes no existing app's behaviour.
|
|
@@ -1078,6 +1082,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
1078
1082
|
resultUrl: downloadUrl,
|
|
1079
1083
|
lastFrameUrl: data.lastFrameUrl,
|
|
1080
1084
|
outputFormat: data.outputFormat,
|
|
1085
|
+
...seedanceDraftFields,
|
|
1081
1086
|
isNSFW: Boolean(data.triggeredNSFWFilter),
|
|
1082
1087
|
nsfwDetected: data.nsfwDetected === true,
|
|
1083
1088
|
nsfwSources: Array.isArray(data.nsfwSources) ? [...data.nsfwSources] : [],
|
|
@@ -1467,7 +1472,9 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
1467
1472
|
delta.seed = event.seed;
|
|
1468
1473
|
}
|
|
1469
1474
|
job._update({
|
|
1470
|
-
...delta
|
|
1475
|
+
...delta,
|
|
1476
|
+
// A job first seen in its own completion still learns it is a draft.
|
|
1477
|
+
...seedanceDraftResultFields(event)
|
|
1471
1478
|
});
|
|
1472
1479
|
break;
|
|
1473
1480
|
}
|
|
@@ -2121,6 +2128,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
2121
2128
|
: (job.result as Record<string, unknown> | undefined)?.outputFormat) as
|
|
2122
2129
|
| string
|
|
2123
2130
|
| undefined,
|
|
2131
|
+
...seedanceDraftResultFields(job),
|
|
2124
2132
|
...(typeof job.resultUrl === 'string' && job.resultUrl
|
|
2125
2133
|
? { resultUrl: job.resultUrl }
|
|
2126
2134
|
: {}),
|
|
@@ -3336,7 +3344,8 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3336
3344
|
contextImages,
|
|
3337
3345
|
gptImageQuality,
|
|
3338
3346
|
outputFormat,
|
|
3339
|
-
billingMode
|
|
3347
|
+
billingMode,
|
|
3348
|
+
priorityMode
|
|
3340
3349
|
}: EstimateRequest): Promise<CostEstimation> {
|
|
3341
3350
|
let apiVersion = 2;
|
|
3342
3351
|
const modelOptions = await this.getModelOptions(model);
|
|
@@ -3372,6 +3381,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3372
3381
|
if (gptImageQuality) queryParams.set('gptImageQuality', gptImageQuality);
|
|
3373
3382
|
if (outputFormat) queryParams.set('outputFormat', outputFormat);
|
|
3374
3383
|
if (billingMode) queryParams.set('billingMode', billingMode);
|
|
3384
|
+
if (priorityMode !== undefined) queryParams.set('priorityMode', String(priorityMode));
|
|
3375
3385
|
const query = queryParams.toString();
|
|
3376
3386
|
const r = await this.client.socket.get<EstimationResponse>(
|
|
3377
3387
|
`/api/v${apiVersion}/job/estimate/${pathParams.join('/')}${query ? `?${query}` : ''}`
|
|
@@ -3429,6 +3439,9 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3429
3439
|
* - referenceVideoDurationSeconds: Combined duration of MiniMax H3 r2v video input.
|
|
3430
3440
|
* - keyframeCount / keyframes: MiniMax H3 intermediate keyframes (0-8). The first two are
|
|
3431
3441
|
* included; each extra keyframe adds 0.75 s of output time on FastH3 and 0.3 s on other tiers.
|
|
3442
|
+
* - seedanceDraft: Seedance 2.5 480p draft, priced like the same 480p job.
|
|
3443
|
+
* - seedanceDraftJobId: Seedance 2.5 finalize of that draft at 1080p, priced by the server from
|
|
3444
|
+
* the draft (duration, aspect ratio, input video); width, height, duration and fps do not change it.
|
|
3432
3445
|
* @return {Promise<Object>} Returns an object containing the estimated costs for the video in different units:
|
|
3433
3446
|
* - token: Cost in tokens.
|
|
3434
3447
|
* - usd: Cost in USD.
|
|
@@ -3437,6 +3450,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3437
3450
|
*/
|
|
3438
3451
|
async estimateVideoCost(params: VideoEstimateRequest) {
|
|
3439
3452
|
rejectRetiredOutputScale(params);
|
|
3453
|
+
validateSeedanceDraftFields(params);
|
|
3440
3454
|
const frames = params.frames
|
|
3441
3455
|
? params.frames
|
|
3442
3456
|
: calculateVideoFrames(params.model, params.duration, params.fps);
|
|
@@ -3500,6 +3514,11 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3500
3514
|
const network = params.network ?? this._currentNetworkType;
|
|
3501
3515
|
if (network) query.set('network', network);
|
|
3502
3516
|
if (params.billingMode) query.set('billingMode', params.billingMode);
|
|
3517
|
+
if (params.priorityMode !== undefined) query.set('priorityMode', String(params.priorityMode));
|
|
3518
|
+
if (params.seedanceDraft === true) query.set('seedanceDraft', '1');
|
|
3519
|
+
if (params.seedanceDraftJobId !== undefined) {
|
|
3520
|
+
query.set('seedanceDraftJobId', params.seedanceDraftJobId);
|
|
3521
|
+
}
|
|
3503
3522
|
const queryString = query.toString();
|
|
3504
3523
|
const r = await this.client.socket.get<EstimationResponse>(
|
|
3505
3524
|
`/api/v1/job-video/estimate/${path}${queryString ? `?${queryString}` : ''}`
|
|
@@ -3540,6 +3559,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
3540
3559
|
const network = params.network ?? this._currentNetworkType;
|
|
3541
3560
|
if (network) query.set('network', network);
|
|
3542
3561
|
if (params.billingMode) query.set('billingMode', params.billingMode);
|
|
3562
|
+
if (params.priorityMode !== undefined) query.set('priorityMode', String(params.priorityMode));
|
|
3543
3563
|
const queryString = query.toString();
|
|
3544
3564
|
const r = await this.client.socket.get<EstimationResponse>(
|
|
3545
3565
|
`/api/v1/job-audio/estimate/${path}${queryString ? `?${queryString}` : ''}`
|
package/src/Projects/recovery.ts
CHANGED
|
@@ -121,6 +121,7 @@ export function projectParamsFromRecoveredProject(project: RecoveredProject): Pr
|
|
|
121
121
|
if (tokenType) base.tokenType = tokenType;
|
|
122
122
|
const billingMode = project.billingMode || request.billingMode;
|
|
123
123
|
if (billingMode) base.billingMode = billingMode;
|
|
124
|
+
if (project.priorityMode !== undefined) base.priorityMode = project.priorityMode;
|
|
124
125
|
if (request.disableSafety === true) base.disableNSFWFilter = true;
|
|
125
126
|
if (typeof request.outputFormat === 'string' && request.outputFormat) {
|
|
126
127
|
base.outputFormat = request.outputFormat;
|
|
@@ -5,6 +5,7 @@ import type { WaitingReason, JobWaitingReason } from './WaitingReason.js';
|
|
|
5
5
|
export interface RawProject {
|
|
6
6
|
/** Missing on older projects or clients that do not report prompt preparation. */
|
|
7
7
|
promptExpanded?: boolean;
|
|
8
|
+
priorityMode?: boolean;
|
|
8
9
|
waitingReason?: WaitingReason | null;
|
|
9
10
|
jobWaitingReasons?: JobWaitingReason[];
|
|
10
11
|
id: string;
|
|
@@ -96,6 +97,12 @@ export interface RawJob {
|
|
|
96
97
|
lastFrameUrl?: string;
|
|
97
98
|
lastFrameKey?: string;
|
|
98
99
|
outputFormat?: string;
|
|
100
|
+
/** Seedance 2.5: this job is a draft that can be finalized at 1080p. */
|
|
101
|
+
seedanceDraft?: boolean;
|
|
102
|
+
/** Seedance 2.5 draft: ISO 8601 time after which it can no longer be finalized. */
|
|
103
|
+
seedanceDraftExpiresAt?: string;
|
|
104
|
+
/** Seedance 2.5 final: the Sogni job id of the draft it finalized. */
|
|
105
|
+
seedanceDraftJobId?: string;
|
|
99
106
|
result?: JobProvenance & Record<string, unknown>;
|
|
100
107
|
}
|
|
101
108
|
|
|
@@ -108,6 +108,12 @@ export interface JobCompleted extends JobEventBase {
|
|
|
108
108
|
resultUrl: string | null;
|
|
109
109
|
lastFrameUrl?: string;
|
|
110
110
|
outputFormat?: string;
|
|
111
|
+
/** Seedance 2.5: this job is a draft that can be finalized at 1080p. */
|
|
112
|
+
seedanceDraft?: boolean;
|
|
113
|
+
/** Seedance 2.5 draft: ISO 8601 time after which it can no longer be finalized. */
|
|
114
|
+
seedanceDraftExpiresAt?: string;
|
|
115
|
+
/** Seedance 2.5 final: the Sogni job id of the draft it finalized. */
|
|
116
|
+
seedanceDraftJobId?: string;
|
|
111
117
|
/**
|
|
112
118
|
* A safety signal fired. Either the media was withheld (filter on, no
|
|
113
119
|
* `resultUrl`) or it was delivered and merely labelled - see `nsfwDetected`.
|