@sogni-ai/sogni-client 5.56.3 → 5.58.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/AGENTS.md +2 -0
- package/CHANGELOG.md +22 -0
- package/README.md +27 -1
- package/dist/Projects/createJobRequestMessage.js +121 -2
- package/dist/Projects/createJobRequestMessage.js.map +1 -1
- package/dist/Projects/index.d.ts +150 -1
- package/dist/Projects/index.js +230 -6
- package/dist/Projects/index.js.map +1 -1
- package/dist/Projects/types/index.d.ts +108 -5
- package/dist/Projects/types/index.js.map +1 -1
- package/dist/Projects/utils/index.d.ts +53 -3
- package/dist/Projects/utils/index.js +61 -4
- package/dist/Projects/utils/index.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-esm/Projects/createJobRequestMessage.js +122 -3
- package/dist-esm/Projects/createJobRequestMessage.js.map +1 -1
- package/dist-esm/Projects/index.js +231 -7
- package/dist-esm/Projects/index.js.map +1 -1
- package/dist-esm/Projects/types/index.js.map +1 -1
- package/dist-esm/Projects/utils/index.js +58 -3
- package/dist-esm/Projects/utils/index.js.map +1 -1
- package/dist-esm/index.js +5 -2
- package/dist-esm/index.js.map +1 -1
- package/dist-esm/version.js +1 -1
- package/llms-full.txt +44 -4
- package/llms.txt +36 -2
- package/package.json +3 -1
- package/src/Projects/createJobRequestMessage.ts +136 -2
- package/src/Projects/index.ts +364 -1
- package/src/Projects/types/index.ts +116 -4
- package/src/Projects/utils/index.ts +78 -3
- package/src/index.ts +24 -1
- package/src/version.ts +1 -1
package/dist/Projects/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { AvailableLorasParams, LoraCatalog, LoraCatalogEntry, LoraConstraints }
|
|
|
7
7
|
import PersonalLoras from './PersonalLoras.js';
|
|
8
8
|
import { ProjectApiEvents, type ProjectSyncReason, type ProjectSyncResult } from './types/events.js';
|
|
9
9
|
import { RawProject } from './types/RawProject.js';
|
|
10
|
+
import type { WaitingReason } from './types/WaitingReason.js';
|
|
10
11
|
import { SupernetType } from '../ApiClient/WebSocketClient/types.js';
|
|
11
12
|
import { type ResultMediaEvidence, type ResultMediaKind } from './utils/index.js';
|
|
12
13
|
import { TokenType } from '../types/token.js';
|
|
@@ -62,6 +63,86 @@ export type ProjectStatusSnapshot = Partial<Omit<RawProject, 'status' | 'workerJ
|
|
|
62
63
|
workerJobs: RawProject['workerJobs'];
|
|
63
64
|
completedWorkerJobs: RawProject['completedWorkerJobs'];
|
|
64
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* One render of a project returned by {@link ProjectsApi.getResult}.
|
|
68
|
+
*/
|
|
69
|
+
export interface ProjectResultJob {
|
|
70
|
+
/** The render's result id (the `imgID` of its events). */
|
|
71
|
+
id: string;
|
|
72
|
+
/**
|
|
73
|
+
* `completed`, `failed` or `canceled` once it has finished; otherwise the
|
|
74
|
+
* worker job's in-flight status (`queued`, `assigned`, `jobStarted`, ...).
|
|
75
|
+
*/
|
|
76
|
+
status: string;
|
|
77
|
+
/** Why a failed or cancelled render ended, e.g. `genfailure` or `artistCanceled`. */
|
|
78
|
+
reason?: string;
|
|
79
|
+
/** What the result is, when it is known. */
|
|
80
|
+
kind?: ResultMediaKind;
|
|
81
|
+
/** Signed download URL for a completed render's media. It expires; fetch a new one when it does. */
|
|
82
|
+
url?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Why a completed render has no `url`: `sensitiveContent` when the Sensitive
|
|
85
|
+
* Content Filter withheld it, `unknownMediaKind` when neither the model nor
|
|
86
|
+
* the result says what media it is, `downloadUrlFailed` when the API refused
|
|
87
|
+
* or failed to sign one (retry later).
|
|
88
|
+
*/
|
|
89
|
+
urlUnavailable?: 'sensitiveContent' | 'unknownMediaKind' | 'downloadUrlFailed';
|
|
90
|
+
/** Seed the render used, when recorded. */
|
|
91
|
+
seed?: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* One of this account's projects as {@link ProjectsApi.getResult} sees it:
|
|
95
|
+
* its current state, why it is still waiting if it is, and every render with
|
|
96
|
+
* a download URL for the ones that completed.
|
|
97
|
+
*/
|
|
98
|
+
export interface ProjectResult {
|
|
99
|
+
id: string;
|
|
100
|
+
status: ProjectLookupStatus;
|
|
101
|
+
/** `true` for `completed`, `failed` and `canceled`. */
|
|
102
|
+
finished: boolean;
|
|
103
|
+
modelId?: string;
|
|
104
|
+
/**
|
|
105
|
+
* While it is still queued: the server's explanation, e.g. the account's own
|
|
106
|
+
* plan concurrency limit (`concurrency_limit`, `model_concurrency_limit`)
|
|
107
|
+
* rather than a shortage of workers (`no_workers`). Absent when the server
|
|
108
|
+
* did not report one.
|
|
109
|
+
*/
|
|
110
|
+
waitingReason?: WaitingReason | null;
|
|
111
|
+
jobs: ProjectResultJob[];
|
|
112
|
+
}
|
|
113
|
+
/** A render of a project returned by {@link ProjectsApi.listRecent}. */
|
|
114
|
+
export interface RecentProjectJob {
|
|
115
|
+
/** The render's result id; pass the project id to {@link ProjectsApi.getResult} for its URL. */
|
|
116
|
+
id: string;
|
|
117
|
+
status: string;
|
|
118
|
+
/** The Sensitive Content Filter withheld this render's media. */
|
|
119
|
+
sensitiveContentWithheld: boolean;
|
|
120
|
+
/** Milliseconds since the epoch. */
|
|
121
|
+
finishedAt?: number;
|
|
122
|
+
}
|
|
123
|
+
/** A project returned by {@link ProjectsApi.listRecent}. */
|
|
124
|
+
export interface RecentProject {
|
|
125
|
+
id: string;
|
|
126
|
+
modelId?: string;
|
|
127
|
+
modelName?: string;
|
|
128
|
+
/** The app that submitted it, when recorded (e.g. `sogni-creative-agent-skill`). */
|
|
129
|
+
appSource?: string;
|
|
130
|
+
/** When its latest render finished, in milliseconds since the epoch. */
|
|
131
|
+
finishedAt?: number;
|
|
132
|
+
jobs: RecentProjectJob[];
|
|
133
|
+
}
|
|
134
|
+
export interface ListRecentProjectsOptions {
|
|
135
|
+
/**
|
|
136
|
+
* Oldest finish time to include, as a `Date` or milliseconds since the epoch.
|
|
137
|
+
* Defaults to 24 hours ago. The history keeps 7 days, so anything older is
|
|
138
|
+
* read as 7 days ago.
|
|
139
|
+
*/
|
|
140
|
+
since?: Date | number;
|
|
141
|
+
/** Renders to read, 1-100 (default 50). Projects are grouped from these. */
|
|
142
|
+
limit?: number;
|
|
143
|
+
/** Only projects submitted by this app source. */
|
|
144
|
+
appSource?: string;
|
|
145
|
+
}
|
|
65
146
|
declare class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
66
147
|
private _personalLoras?;
|
|
67
148
|
/** Import, inspect, discover, and remove the signed-in account's personal LoRAs. */
|
|
@@ -418,6 +499,62 @@ declare class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
418
499
|
* @param projectId
|
|
419
500
|
*/
|
|
420
501
|
getStatus(projectId: string): Promise<ProjectStatusSnapshot>;
|
|
502
|
+
/**
|
|
503
|
+
* State and results of one of this account's projects by id, whenever it is
|
|
504
|
+
* asked: while it is queued or rendering, and after it finished, including
|
|
505
|
+
* one that finished while this client was offline, or after it stopped
|
|
506
|
+
* waiting, or long after the socket stopped holding it for a reconnect.
|
|
507
|
+
*
|
|
508
|
+
* Completed renders come back with signed download URLs, minted the same way
|
|
509
|
+
* a live result's are. A queued project carries the server's `waitingReason`,
|
|
510
|
+
* which says whether it is held by the account's own plan concurrency or is
|
|
511
|
+
* waiting for a worker. Needs an authenticated client; another account's or
|
|
512
|
+
* an unknown project rejects with a 404 `ApiError`.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
* ```ts
|
|
516
|
+
* const result = await sogni.projects.getResult(projectId);
|
|
517
|
+
* if (result.finished) {
|
|
518
|
+
* for (const job of result.jobs) if (job.url) console.log(job.url);
|
|
519
|
+
* } else {
|
|
520
|
+
* console.log(result.status, result.waitingReason?.message);
|
|
521
|
+
* }
|
|
522
|
+
* ```
|
|
523
|
+
* @param projectId
|
|
524
|
+
* @param options.kind - What the project produces, when the caller knows it
|
|
525
|
+
* (`image`, `video`, `audio`, `model`). Used only when neither the model
|
|
526
|
+
* catalog nor the stored result says, so a URL is never minted from the
|
|
527
|
+
* wrong endpoint.
|
|
528
|
+
*/
|
|
529
|
+
getResult(projectId: string, options?: {
|
|
530
|
+
kind?: ResultMediaKind;
|
|
531
|
+
}): Promise<ProjectResult>;
|
|
532
|
+
/**
|
|
533
|
+
* This account's recently completed projects that produced media, newest
|
|
534
|
+
* first, read from the durable history rather than the socket. It includes
|
|
535
|
+
* projects that finished while no client was connected, and ones the socket
|
|
536
|
+
* has stopped holding for a reconnect (it keeps a finished project for one
|
|
537
|
+
* hour). Pass a project's id to {@link getResult} for its download URLs.
|
|
538
|
+
*
|
|
539
|
+
* Reaches back at most 7 days. Needs a signed-in account.
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* const recent = await sogni.projects.listRecent({ since: Date.now() - 6 * 3600_000 });
|
|
544
|
+
* for (const project of recent) {
|
|
545
|
+
* const result = await sogni.projects.getResult(project.id);
|
|
546
|
+
* }
|
|
547
|
+
* ```
|
|
548
|
+
*/
|
|
549
|
+
listRecent(options?: ListRecentProjectsOptions): Promise<RecentProject[]>;
|
|
550
|
+
private _accountAddress?;
|
|
551
|
+
/**
|
|
552
|
+
* How {@link listRecent} learns the signed-in account's address. Set by
|
|
553
|
+
* `SogniClient`, which owns the account.
|
|
554
|
+
* @internal
|
|
555
|
+
*/
|
|
556
|
+
_setAccountAddressResolver(resolve: () => Promise<string | undefined>): void;
|
|
557
|
+
private _resolveAccountAddress;
|
|
421
558
|
/**
|
|
422
559
|
* Ids of this account's projects that are currently live on the socket,
|
|
423
560
|
* including ones still queued with no worker assigned.
|
|
@@ -452,6 +589,12 @@ declare class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
452
589
|
private uploadGuideImage;
|
|
453
590
|
private uploadCNImage;
|
|
454
591
|
private uploadContextImage;
|
|
592
|
+
/**
|
|
593
|
+
* Upload one MiniMax H3 intermediate keyframe image to its `keyframeImage<slot>`
|
|
594
|
+
* slot.
|
|
595
|
+
* @internal
|
|
596
|
+
*/
|
|
597
|
+
private uploadKeyframeImage;
|
|
455
598
|
/**
|
|
456
599
|
* Upload reference image for WAN video workflows
|
|
457
600
|
* @internal
|
|
@@ -509,6 +652,8 @@ declare class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
509
652
|
* - referenceImageCount: Number of image references submitted by the estimated job.
|
|
510
653
|
* - referenceVideoCount: Number of video references submitted by a MiniMax H3 r2v job.
|
|
511
654
|
* - referenceVideoDurationSeconds: Combined duration of MiniMax H3 r2v video input.
|
|
655
|
+
* - keyframeCount / keyframes: MiniMax H3 intermediate keyframes (0-8). The first two are
|
|
656
|
+
* included; each extra keyframe adds 0.75 s of output time on FastH3 and 0.3 s on other tiers.
|
|
512
657
|
* @return {Promise<Object>} Returns an object containing the estimated costs for the video in different units:
|
|
513
658
|
* - token: Cost in tokens.
|
|
514
659
|
* - usd: Cost in USD.
|
|
@@ -551,7 +696,8 @@ declare class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
551
696
|
* specific request.
|
|
552
697
|
* - type: Asset role. Supported values include `'referenceImage'`,
|
|
553
698
|
* `'referenceImageEnd'`, `'startingImage'`, `'cnImage'`,
|
|
554
|
-
* `'contextImage1'`..`'contextImage16'`, `'
|
|
699
|
+
* `'contextImage1'`..`'contextImage16'`, `'keyframeImage1'`..`'keyframeImage8'`
|
|
700
|
+
* (MiniMax H3 intermediate keyframes), `'preview'`, `'complete'`.
|
|
555
701
|
* - contentType: Optional MIME type the caller will `PUT` (e.g.
|
|
556
702
|
* `"image/png"`). Forwarded so the storage layer can pin the
|
|
557
703
|
* Content-Type on the presigned URL.
|
|
@@ -652,6 +798,9 @@ declare class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
652
798
|
* This table describes the first upload slot only. MiniMax H3 r2v also uses
|
|
653
799
|
* `contextImages`, `referenceVideos`, and `referenceAudios`; callers should
|
|
654
800
|
* read those fields on `VideoProjectParams` for the multi-reference limits.
|
|
801
|
+
* Every MiniMax H3 id except text-to-video also accepts optional intermediate
|
|
802
|
+
* `keyframes`, which the table does not list: `isMinimaxH3KeyframeModel()`
|
|
803
|
+
* tells which ids take them, and `VideoProjectParams.keyframes` gives the rules.
|
|
655
804
|
*
|
|
656
805
|
* @param {string} modelId - The identifier of the video model to retrieve the configuration for.
|
|
657
806
|
* @return {Object} The video asset configuration object where key is asset field and value is
|
package/dist/Projects/index.js
CHANGED
|
@@ -313,6 +313,18 @@ const RESUBMIT_RECONNECT_TIMEOUT_MS = 60000;
|
|
|
313
313
|
const MISSING_PROJECT_ATTEMPTS = 4;
|
|
314
314
|
const MISSING_PROJECT_RETRY_MS = 2500;
|
|
315
315
|
const IN_FLIGHT_LOOKUP_STATUSES = new Set(['pending', 'queued', 'processing']);
|
|
316
|
+
const RECENT_PROJECTS_DEFAULT_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
317
|
+
// The history API refuses a media-only lookup reaching back more than 7 days;
|
|
318
|
+
// stay a minute inside it so a slow clock cannot turn a 7-day request into a 400.
|
|
319
|
+
const RECENT_PROJECTS_MAX_WINDOW_MS = 7 * 24 * 60 * 60 * 1000 - 60 * 1000;
|
|
320
|
+
/** A worker job's status in the names {@link ProjectResultJob} uses. */
|
|
321
|
+
function resultJobStatus(job) {
|
|
322
|
+
if (job.status === 'jobCompleted')
|
|
323
|
+
return 'completed';
|
|
324
|
+
if (job.status === 'jobError')
|
|
325
|
+
return job.reason === 'artistCanceled' ? 'canceled' : 'failed';
|
|
326
|
+
return job.status || 'unknown';
|
|
327
|
+
}
|
|
316
328
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
317
329
|
class ProjectsApi extends ApiGroup_js_1.default {
|
|
318
330
|
/** Import, inspect, discover, and remove the signed-in account's personal LoRAs. */
|
|
@@ -2034,6 +2046,13 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2034
2046
|
yield Promise.all((0, index_js_2.getVideoContextImageSlots)(data).map(({ slot, media }) => typeof media === 'boolean'
|
|
2035
2047
|
? undefined
|
|
2036
2048
|
: this.uploadContextImage(project.id, (slot - 1), media)));
|
|
2049
|
+
// MiniMax H3 intermediate keyframes, uploaded to their own keyframeImage1..N
|
|
2050
|
+
// slots in caller order, apart from the contextImage slots r2v references
|
|
2051
|
+
// use. createJobRequestMessage has already checked the model, the entries
|
|
2052
|
+
// and the frame indices.
|
|
2053
|
+
yield Promise.all((0, index_js_2.getMinimaxH3KeyframeSlots)(data).map(({ slot, media }) => typeof media === 'boolean'
|
|
2054
|
+
? undefined
|
|
2055
|
+
: this.uploadKeyframeImage(project.id, slot, media)));
|
|
2037
2056
|
if ((data === null || data === void 0 ? void 0 : data.referenceImageEnd) && data.referenceImageEnd !== true) {
|
|
2038
2057
|
yield this.uploadReferenceImageEnd(project.id, data.referenceImageEnd);
|
|
2039
2058
|
}
|
|
@@ -2132,6 +2151,164 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2132
2151
|
return data.project;
|
|
2133
2152
|
});
|
|
2134
2153
|
}
|
|
2154
|
+
/**
|
|
2155
|
+
* State and results of one of this account's projects by id, whenever it is
|
|
2156
|
+
* asked: while it is queued or rendering, and after it finished, including
|
|
2157
|
+
* one that finished while this client was offline, or after it stopped
|
|
2158
|
+
* waiting, or long after the socket stopped holding it for a reconnect.
|
|
2159
|
+
*
|
|
2160
|
+
* Completed renders come back with signed download URLs, minted the same way
|
|
2161
|
+
* a live result's are. A queued project carries the server's `waitingReason`,
|
|
2162
|
+
* which says whether it is held by the account's own plan concurrency or is
|
|
2163
|
+
* waiting for a worker. Needs an authenticated client; another account's or
|
|
2164
|
+
* an unknown project rejects with a 404 `ApiError`.
|
|
2165
|
+
*
|
|
2166
|
+
* @example
|
|
2167
|
+
* ```ts
|
|
2168
|
+
* const result = await sogni.projects.getResult(projectId);
|
|
2169
|
+
* if (result.finished) {
|
|
2170
|
+
* for (const job of result.jobs) if (job.url) console.log(job.url);
|
|
2171
|
+
* } else {
|
|
2172
|
+
* console.log(result.status, result.waitingReason?.message);
|
|
2173
|
+
* }
|
|
2174
|
+
* ```
|
|
2175
|
+
* @param projectId
|
|
2176
|
+
* @param options.kind - What the project produces, when the caller knows it
|
|
2177
|
+
* (`image`, `video`, `audio`, `model`). Used only when neither the model
|
|
2178
|
+
* catalog nor the stored result says, so a URL is never minted from the
|
|
2179
|
+
* wrong endpoint.
|
|
2180
|
+
*/
|
|
2181
|
+
getResult(projectId_1) {
|
|
2182
|
+
return __awaiter(this, arguments, void 0, function* (projectId, options = {}) {
|
|
2183
|
+
var _a, _b, _c, _d, _e;
|
|
2184
|
+
const assertSession = (0, requestSession_js_1.captureRequestSession)(this.client.auth);
|
|
2185
|
+
const snapshot = yield this.getStatus(projectId);
|
|
2186
|
+
assertSession();
|
|
2187
|
+
const modelId = (_a = snapshot.model) === null || _a === void 0 ? void 0 : _a.id;
|
|
2188
|
+
// The stored record's model carries its media type even though ProjectModel does not declare it.
|
|
2189
|
+
const recordType = (_b = snapshot.model) === null || _b === void 0 ? void 0 : _b.type;
|
|
2190
|
+
const projectType = typeof recordType === 'string' ? recordType : undefined;
|
|
2191
|
+
const jobs = [];
|
|
2192
|
+
const seen = new Set();
|
|
2193
|
+
for (const job of [...(snapshot.completedWorkerJobs || []), ...(snapshot.workerJobs || [])]) {
|
|
2194
|
+
const id = job.imgID || job.id;
|
|
2195
|
+
if (!id || seen.has(id))
|
|
2196
|
+
continue;
|
|
2197
|
+
seen.add(id);
|
|
2198
|
+
const status = resultJobStatus(job);
|
|
2199
|
+
const entry = { id, status };
|
|
2200
|
+
if (status !== 'completed' && job.reason)
|
|
2201
|
+
entry.reason = job.reason;
|
|
2202
|
+
if (typeof job.seedUsed === 'number' && job.seedUsed >= 0)
|
|
2203
|
+
entry.seed = job.seedUsed;
|
|
2204
|
+
if (status === 'completed') {
|
|
2205
|
+
// Same rule as a live result: withheld only when the filter fired and
|
|
2206
|
+
// no advisory label says the media was delivered anyway.
|
|
2207
|
+
if (job.triggeredNSFWFilter === true && job.nsfwDetected !== true) {
|
|
2208
|
+
entry.urlUnavailable = 'sensitiveContent';
|
|
2209
|
+
}
|
|
2210
|
+
else if (typeof job.resultUrl === 'string' && job.resultUrl) {
|
|
2211
|
+
entry.url = job.resultUrl;
|
|
2212
|
+
}
|
|
2213
|
+
else {
|
|
2214
|
+
const result = (0, index_js_2.resultMediaEvidence)(Object.assign(Object.assign({}, (job.result || {})), { outputFormat: (_c = job.outputFormat) !== null && _c !== void 0 ? _c : (_d = job.result) === null || _d === void 0 ? void 0 : _d.outputFormat }));
|
|
2215
|
+
const kind = (_e = this._resultMediaKind({ modelId, projectType, result })) !== null && _e !== void 0 ? _e : options.kind;
|
|
2216
|
+
if (!kind) {
|
|
2217
|
+
entry.urlUnavailable = 'unknownMediaKind';
|
|
2218
|
+
}
|
|
2219
|
+
else {
|
|
2220
|
+
entry.kind = kind;
|
|
2221
|
+
try {
|
|
2222
|
+
entry.url = yield this._mintResultUrl({
|
|
2223
|
+
projectId: snapshot.id,
|
|
2224
|
+
jobId: id,
|
|
2225
|
+
kind,
|
|
2226
|
+
audioContentType: result === null || result === void 0 ? void 0 : result.contentType
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
catch (error) {
|
|
2230
|
+
this.client.logger.error(`Failed to sign a download URL for ${snapshot.id}/${id}`);
|
|
2231
|
+
this.client.logger.error(error);
|
|
2232
|
+
entry.urlUnavailable = 'downloadUrlFailed';
|
|
2233
|
+
}
|
|
2234
|
+
assertSession();
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
jobs.push(entry);
|
|
2239
|
+
}
|
|
2240
|
+
return Object.assign(Object.assign(Object.assign({ id: snapshot.id, status: snapshot.status, finished: snapshot.finished }, (modelId ? { modelId } : {})), (snapshot.waitingReason !== undefined ? { waitingReason: snapshot.waitingReason } : {})), { jobs });
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* This account's recently completed projects that produced media, newest
|
|
2245
|
+
* first, read from the durable history rather than the socket. It includes
|
|
2246
|
+
* projects that finished while no client was connected, and ones the socket
|
|
2247
|
+
* has stopped holding for a reconnect (it keeps a finished project for one
|
|
2248
|
+
* hour). Pass a project's id to {@link getResult} for its download URLs.
|
|
2249
|
+
*
|
|
2250
|
+
* Reaches back at most 7 days. Needs a signed-in account.
|
|
2251
|
+
*
|
|
2252
|
+
* @example
|
|
2253
|
+
* ```ts
|
|
2254
|
+
* const recent = await sogni.projects.listRecent({ since: Date.now() - 6 * 3600_000 });
|
|
2255
|
+
* for (const project of recent) {
|
|
2256
|
+
* const result = await sogni.projects.getResult(project.id);
|
|
2257
|
+
* }
|
|
2258
|
+
* ```
|
|
2259
|
+
*/
|
|
2260
|
+
listRecent() {
|
|
2261
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
2262
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2263
|
+
const assertSession = (0, requestSession_js_1.captureRequestSession)(this.client.auth);
|
|
2264
|
+
const address = yield this._resolveAccountAddress();
|
|
2265
|
+
assertSession();
|
|
2266
|
+
if (!address) {
|
|
2267
|
+
throw new Error('listRecent needs a signed-in account');
|
|
2268
|
+
}
|
|
2269
|
+
const now = Date.now();
|
|
2270
|
+
const requestedSince = options.since instanceof Date
|
|
2271
|
+
? options.since.getTime()
|
|
2272
|
+
: typeof options.since === 'number'
|
|
2273
|
+
? options.since
|
|
2274
|
+
: now - RECENT_PROJECTS_DEFAULT_WINDOW_MS;
|
|
2275
|
+
const since = Math.max(requestedSince, now - RECENT_PROJECTS_MAX_WINDOW_MS);
|
|
2276
|
+
const limit = Math.min(100, Math.max(1, Math.floor((_a = options.limit) !== null && _a !== void 0 ? _a : 50)));
|
|
2277
|
+
const { data } = yield this.client.rest.get('/v1/jobs/list', Object.assign({ role: 'artist', address, state: 'completed', mediaOnly: true, since,
|
|
2278
|
+
limit }, (options.appSource ? { appSource: options.appSource } : {})));
|
|
2279
|
+
assertSession();
|
|
2280
|
+
const projects = new Map();
|
|
2281
|
+
for (const job of (data === null || data === void 0 ? void 0 : data.jobs) || []) {
|
|
2282
|
+
const projectId = (_b = job.parentRequest) === null || _b === void 0 ? void 0 : _b.id;
|
|
2283
|
+
if (!projectId)
|
|
2284
|
+
continue;
|
|
2285
|
+
let project = projects.get(projectId);
|
|
2286
|
+
if (!project) {
|
|
2287
|
+
project = Object.assign(Object.assign(Object.assign(Object.assign({ id: projectId }, (((_d = (_c = job.parentRequest) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.id) ? { modelId: job.parentRequest.model.id } : {})), (((_f = (_e = job.parentRequest) === null || _e === void 0 ? void 0 : _e.model) === null || _f === void 0 ? void 0 : _f.name) ? { modelName: job.parentRequest.model.name } : {})), (((_g = job.parentRequest) === null || _g === void 0 ? void 0 : _g.appSource) ? { appSource: job.parentRequest.appSource } : {})), { jobs: [] });
|
|
2288
|
+
projects.set(projectId, project);
|
|
2289
|
+
}
|
|
2290
|
+
const finishedAt = typeof job.endTime === 'number' && job.endTime > 0 ? job.endTime : undefined;
|
|
2291
|
+
project.jobs.push(Object.assign({ id: job.imgID || job.id, status: resultJobStatus(job), sensitiveContentWithheld: job.triggeredNSFWFilter === true && job.nsfwDetected !== true }, (finishedAt ? { finishedAt } : {})));
|
|
2292
|
+
if (finishedAt && (!project.finishedAt || finishedAt > project.finishedAt)) {
|
|
2293
|
+
project.finishedAt = finishedAt;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
return [...projects.values()].sort((a, b) => { var _a, _b; return ((_a = b.finishedAt) !== null && _a !== void 0 ? _a : 0) - ((_b = a.finishedAt) !== null && _b !== void 0 ? _b : 0); });
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* How {@link listRecent} learns the signed-in account's address. Set by
|
|
2301
|
+
* `SogniClient`, which owns the account.
|
|
2302
|
+
* @internal
|
|
2303
|
+
*/
|
|
2304
|
+
_setAccountAddressResolver(resolve) {
|
|
2305
|
+
this._accountAddress = resolve;
|
|
2306
|
+
}
|
|
2307
|
+
_resolveAccountAddress() {
|
|
2308
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2309
|
+
return this._accountAddress ? this._accountAddress() : undefined;
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2135
2312
|
/**
|
|
2136
2313
|
* Ids of this account's projects that are currently live on the socket,
|
|
2137
2314
|
* including ones still queued with no worker assigned.
|
|
@@ -2372,6 +2549,43 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2372
2549
|
return imageId;
|
|
2373
2550
|
});
|
|
2374
2551
|
}
|
|
2552
|
+
/**
|
|
2553
|
+
* Upload one MiniMax H3 intermediate keyframe image to its `keyframeImage<slot>`
|
|
2554
|
+
* slot.
|
|
2555
|
+
* @internal
|
|
2556
|
+
*/
|
|
2557
|
+
uploadKeyframeImage(projectId, slot, file) {
|
|
2558
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2559
|
+
var _a;
|
|
2560
|
+
const imageId = (0, getUUID_js_1.default)();
|
|
2561
|
+
const type = `keyframeImage${slot}`;
|
|
2562
|
+
const assertSession = (_a = this._preparingSessions.get(projectId)) !== null && _a !== void 0 ? _a : (0, requestSession_js_1.captureRequestSession)(this.client.auth);
|
|
2563
|
+
assertSession();
|
|
2564
|
+
const contentType = getFileContentType(file);
|
|
2565
|
+
if (yield this.assets.tryBindFile(file, contentType, { projectId, type }))
|
|
2566
|
+
return imageId;
|
|
2567
|
+
assertSession();
|
|
2568
|
+
const presignedUrl = yield this.uploadUrl({ imageId, jobId: projectId, type, contentType });
|
|
2569
|
+
const headers = {};
|
|
2570
|
+
if (contentType)
|
|
2571
|
+
headers['Content-Type'] = contentType;
|
|
2572
|
+
assertSession();
|
|
2573
|
+
const res = yield fetch(presignedUrl, {
|
|
2574
|
+
method: 'PUT',
|
|
2575
|
+
body: toFetchBody(file),
|
|
2576
|
+
headers
|
|
2577
|
+
});
|
|
2578
|
+
assertSession();
|
|
2579
|
+
if (!res.ok) {
|
|
2580
|
+
throw new index_js_1.ApiError(res.status, {
|
|
2581
|
+
status: 'error',
|
|
2582
|
+
errorCode: 0,
|
|
2583
|
+
message: `Failed to upload keyframe image ${slot}`
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
return imageId;
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2375
2589
|
// ============================================
|
|
2376
2590
|
// VIDEO WORKFLOW UPLOADS (WAN 2.2)
|
|
2377
2591
|
// ============================================
|
|
@@ -2665,6 +2879,8 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2665
2879
|
* - referenceImageCount: Number of image references submitted by the estimated job.
|
|
2666
2880
|
* - referenceVideoCount: Number of video references submitted by a MiniMax H3 r2v job.
|
|
2667
2881
|
* - referenceVideoDurationSeconds: Combined duration of MiniMax H3 r2v video input.
|
|
2882
|
+
* - keyframeCount / keyframes: MiniMax H3 intermediate keyframes (0-8). The first two are
|
|
2883
|
+
* included; each extra keyframe adds 0.75 s of output time on FastH3 and 0.3 s on other tiers.
|
|
2668
2884
|
* @return {Promise<Object>} Returns an object containing the estimated costs for the video in different units:
|
|
2669
2885
|
* - token: Cost in tokens.
|
|
2670
2886
|
* - usd: Cost in USD.
|
|
@@ -2673,7 +2889,7 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2673
2889
|
*/
|
|
2674
2890
|
estimateVideoCost(params) {
|
|
2675
2891
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2676
|
-
var _a, _b, _c, _d, _e;
|
|
2892
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2677
2893
|
(0, createJobRequestMessage_js_1.rejectRetiredOutputScale)(params);
|
|
2678
2894
|
const frames = params.frames
|
|
2679
2895
|
? params.frames
|
|
@@ -2719,8 +2935,12 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2719
2935
|
params.referenceVideoDurationSeconds >= 0) {
|
|
2720
2936
|
query.set('referenceVideoDurationSeconds', String(params.referenceVideoDurationSeconds));
|
|
2721
2937
|
}
|
|
2938
|
+
const keyframeCount = (_b = params.keyframeCount) !== null && _b !== void 0 ? _b : (Array.isArray(params.keyframes) ? params.keyframes.length : undefined);
|
|
2939
|
+
if (Number.isFinite(keyframeCount) && keyframeCount > 0) {
|
|
2940
|
+
query.set('keyframeCount', String(Math.floor(keyframeCount)));
|
|
2941
|
+
}
|
|
2722
2942
|
// Unpinned, the job renders on the connection's network, so quote that one.
|
|
2723
|
-
const network = (
|
|
2943
|
+
const network = (_c = params.network) !== null && _c !== void 0 ? _c : this._currentNetworkType;
|
|
2724
2944
|
if (network)
|
|
2725
2945
|
query.set('network', network);
|
|
2726
2946
|
if (params.billingMode)
|
|
@@ -2732,9 +2952,9 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2732
2952
|
usd: r.quote.project.costInUSD,
|
|
2733
2953
|
spark: r.quote.project.costInSpark,
|
|
2734
2954
|
sogni: r.quote.project.costInSogni,
|
|
2735
|
-
estimatedRenderSeconds: (
|
|
2736
|
-
estimatedTotalSeconds: (
|
|
2737
|
-
dailyFairUsePct: (
|
|
2955
|
+
estimatedRenderSeconds: (_d = r.benchmark) === null || _d === void 0 ? void 0 : _d.estimatedRenderTimeSec,
|
|
2956
|
+
estimatedTotalSeconds: (_e = r.benchmark) === null || _e === void 0 ? void 0 : _e.estimatedTotalTimeSec,
|
|
2957
|
+
dailyFairUsePct: (_f = r.dailyFairUse) === null || _f === void 0 ? void 0 : _f.pct
|
|
2738
2958
|
};
|
|
2739
2959
|
});
|
|
2740
2960
|
}
|
|
@@ -2798,7 +3018,8 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2798
3018
|
* specific request.
|
|
2799
3019
|
* - type: Asset role. Supported values include `'referenceImage'`,
|
|
2800
3020
|
* `'referenceImageEnd'`, `'startingImage'`, `'cnImage'`,
|
|
2801
|
-
* `'contextImage1'`..`'contextImage16'`, `'
|
|
3021
|
+
* `'contextImage1'`..`'contextImage16'`, `'keyframeImage1'`..`'keyframeImage8'`
|
|
3022
|
+
* (MiniMax H3 intermediate keyframes), `'preview'`, `'complete'`.
|
|
2802
3023
|
* - contentType: Optional MIME type the caller will `PUT` (e.g.
|
|
2803
3024
|
* `"image/png"`). Forwarded so the storage layer can pin the
|
|
2804
3025
|
* Content-Type on the presigned URL.
|
|
@@ -2965,6 +3186,9 @@ class ProjectsApi extends ApiGroup_js_1.default {
|
|
|
2965
3186
|
* This table describes the first upload slot only. MiniMax H3 r2v also uses
|
|
2966
3187
|
* `contextImages`, `referenceVideos`, and `referenceAudios`; callers should
|
|
2967
3188
|
* read those fields on `VideoProjectParams` for the multi-reference limits.
|
|
3189
|
+
* Every MiniMax H3 id except text-to-video also accepts optional intermediate
|
|
3190
|
+
* `keyframes`, which the table does not list: `isMinimaxH3KeyframeModel()`
|
|
3191
|
+
* tells which ids take them, and `VideoProjectParams.keyframes` gives the rules.
|
|
2968
3192
|
*
|
|
2969
3193
|
* @param {string} modelId - The identifier of the video model to retrieve the configuration for.
|
|
2970
3194
|
* @return {Object} The video asset configuration object where key is asset field and value is
|