@sogni-ai/sogni-client 3.0.0-alpha.9 → 3.0.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +328 -0
  2. package/README.md +4 -11
  3. package/dist/Account/CurrentAccount.d.ts +3 -3
  4. package/dist/Account/CurrentAccount.js +12 -4
  5. package/dist/Account/CurrentAccount.js.map +1 -1
  6. package/dist/Account/index.d.ts +42 -13
  7. package/dist/Account/index.js +132 -29
  8. package/dist/Account/index.js.map +1 -1
  9. package/dist/Account/types.d.ts +21 -0
  10. package/dist/ApiClient/WebSocketClient/events.d.ts +55 -7
  11. package/dist/ApiClient/WebSocketClient/index.js +1 -1
  12. package/dist/ApiClient/index.d.ts +4 -2
  13. package/dist/ApiClient/index.js +12 -3
  14. package/dist/ApiClient/index.js.map +1 -1
  15. package/dist/ApiGroup.d.ts +0 -3
  16. package/dist/ApiGroup.js +0 -1
  17. package/dist/ApiGroup.js.map +1 -1
  18. package/dist/Projects/Job.d.ts +43 -0
  19. package/dist/Projects/Job.js +76 -0
  20. package/dist/Projects/Job.js.map +1 -1
  21. package/dist/Projects/Project.d.ts +1 -1
  22. package/dist/Projects/Project.js +9 -18
  23. package/dist/Projects/Project.js.map +1 -1
  24. package/dist/Projects/createJobRequestMessage.js +1 -1
  25. package/dist/Projects/createJobRequestMessage.js.map +1 -1
  26. package/dist/Projects/index.d.ts +7 -2
  27. package/dist/Projects/index.js +48 -10
  28. package/dist/Projects/index.js.map +1 -1
  29. package/dist/Projects/types/ControlNetParams.d.ts +7 -2
  30. package/dist/Projects/types/events.d.ts +6 -0
  31. package/dist/Projects/types/index.d.ts +16 -3
  32. package/dist/Projects/utils.d.ts +2 -0
  33. package/dist/Projects/utils.js +14 -0
  34. package/dist/Projects/utils.js.map +1 -0
  35. package/dist/index.d.ts +10 -6
  36. package/dist/index.js +6 -15
  37. package/dist/index.js.map +1 -1
  38. package/dist/lib/utils.d.ts +1 -0
  39. package/dist/lib/utils.js +15 -0
  40. package/dist/lib/utils.js.map +1 -1
  41. package/dist/types/token.d.ts +1 -0
  42. package/dist/types/token.js +3 -0
  43. package/dist/types/token.js.map +1 -0
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +2 -1
  46. package/dist/version.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/Account/CurrentAccount.ts +14 -6
  49. package/src/Account/index.ts +163 -59
  50. package/src/Account/types.ts +26 -0
  51. package/src/ApiClient/WebSocketClient/events.ts +59 -7
  52. package/src/ApiClient/WebSocketClient/index.ts +1 -1
  53. package/src/ApiClient/index.ts +15 -4
  54. package/src/ApiGroup.ts +0 -4
  55. package/src/Projects/Job.ts +98 -0
  56. package/src/Projects/Project.ts +11 -19
  57. package/src/Projects/createJobRequestMessage.ts +2 -1
  58. package/src/Projects/index.ts +53 -13
  59. package/src/Projects/types/ControlNetParams.ts +8 -2
  60. package/src/Projects/types/events.ts +6 -0
  61. package/src/Projects/types/index.ts +17 -3
  62. package/src/Projects/utils.ts +12 -0
  63. package/src/Stats/index.ts +2 -2
  64. package/src/index.ts +23 -19
  65. package/src/lib/utils.ts +4 -0
  66. package/src/types/token.ts +1 -0
  67. package/src/version.ts +2 -1
@@ -1,3 +1,5 @@
1
+ import { TokenType } from '../types/token';
2
+
1
3
  export interface Nonce {
2
4
  nonce: string;
3
5
  }
@@ -27,12 +29,22 @@ export interface BalanceData {
27
29
  credit: string;
28
30
  debit: string;
29
31
  net: string;
32
+ /**
33
+ * Unclaimed worker earnings amount
34
+ * @experimental Socket messages do not provide this field yet, so it may not be available in all cases.
35
+ */
36
+ unclaimed?: string;
30
37
  }
31
38
 
39
+ export type Balances = Record<TokenType, BalanceData>;
40
+
41
+ export type FullBalances = Record<TokenType, Required<BalanceData>>;
42
+
32
43
  export interface TxHistoryParams {
33
44
  status: 'completed';
34
45
  address: string;
35
46
  limit: number;
47
+ provider?: string;
36
48
  offset?: number;
37
49
  }
38
50
 
@@ -58,6 +70,7 @@ export interface TxRaw {
58
70
  sourceSID: string;
59
71
  endTime: number;
60
72
  type: 'debit' | string;
73
+ tokenType: TokenType;
61
74
  }
62
75
 
63
76
  export interface TxHistoryEntry {
@@ -68,6 +81,7 @@ export interface TxHistoryEntry {
68
81
  status: 'completed';
69
82
  role: 'artist' | 'worker';
70
83
  amount: number;
84
+ tokenType: TokenType;
71
85
  description: string;
72
86
  source: 'project' | string;
73
87
  endTime: Date;
@@ -82,20 +96,32 @@ export interface RewardRaw {
82
96
  title: string;
83
97
  description: string;
84
98
  amount: string;
99
+ tokenType: TokenType;
85
100
  claimed: number;
86
101
  canClaim: number;
87
102
  lastClaimTimestamp: number;
88
103
  claimResetFrequencySec: number;
89
104
  }
90
105
 
106
+ export interface RewardsQuery {
107
+ provider?: string;
108
+ }
109
+
91
110
  export interface Reward {
92
111
  id: string;
93
112
  type: RewardType;
94
113
  title: string;
95
114
  description: string;
96
115
  amount: string;
116
+ tokenType: TokenType;
97
117
  claimed: boolean;
98
118
  canClaim: boolean;
99
119
  lastClaim: Date;
100
120
  nextClaim: Date | null;
121
+ provider?: string;
122
+ }
123
+
124
+ export interface ClaimOptions {
125
+ turnstileToken?: string;
126
+ provider?: string;
101
127
  }
@@ -1,11 +1,36 @@
1
1
  import { SupernetType } from './types';
2
+ import { Balances } from '../../Account/types';
2
3
 
3
- export type BalanceData = {
4
- settled: string;
5
- credit: string;
6
- debit: string;
7
- net: string;
8
- };
4
+ export interface AuthenticatedData {
5
+ id: string;
6
+ clientType: 'artist' | 'worker';
7
+ username: string;
8
+ address: string;
9
+ SID: number;
10
+ clientSID: number;
11
+ addressSID: number;
12
+ balanceVersion: 2;
13
+ tokens: {
14
+ sogni: {
15
+ settled: string;
16
+ credit: string;
17
+ debit: string;
18
+ net: string;
19
+ };
20
+ spark: {
21
+ settled: string;
22
+ credit: string;
23
+ debit: string;
24
+ net: string;
25
+ };
26
+ };
27
+ activeProjects: [];
28
+ unclaimedCompletedProjects: [];
29
+ isMainnet: boolean;
30
+ accountWasMigrated: boolean;
31
+ hasUnclaimedAirdrop: boolean;
32
+ firstLoginAfterMigration: boolean;
33
+ }
9
34
 
10
35
  export type JobErrorData = {
11
36
  jobID: string;
@@ -38,6 +63,9 @@ export type JobStateData =
38
63
  jobID: string;
39
64
  imgID: string;
40
65
  workerName: string;
66
+ positivePrompt?: string;
67
+ negativePrompt?: string;
68
+ jobIndex?: number;
41
69
  }
42
70
  | {
43
71
  jobID: string;
@@ -58,11 +86,29 @@ export type ServerDisconnectData = {
58
86
  reason: string;
59
87
  };
60
88
 
89
+ export type ToastMessage = {
90
+ type: 'info' | 'success' | 'warning' | 'error';
91
+ message: string;
92
+ // Number of milliseconds to show the toast
93
+ autoClose: number;
94
+ stickyID: string;
95
+ };
96
+
97
+ export type ArtistCancelConfirmation = {
98
+ didCancel: boolean;
99
+ error_message: string;
100
+ jobID: string;
101
+ };
102
+
61
103
  export type SocketEventMap = {
104
+ /**
105
+ * @event WebSocketClient#authenticated - Received after successful connection to the WebSocket server
106
+ */
107
+ authenticated: AuthenticatedData;
62
108
  /**
63
109
  * @event WebSocketClient#balanceUpdate - Received balance update
64
110
  */
65
- balanceUpdate: BalanceData;
111
+ balanceUpdate: Balances;
66
112
  /**
67
113
  * @event WebSocketClient#changeNetwork - Default network changed
68
114
  */
@@ -95,4 +141,10 @@ export type SocketEventMap = {
95
141
  * @event WebSocketClient#disconnected - WebSocket connection was closed
96
142
  */
97
143
  disconnected: ServerDisconnectData;
144
+ /**
145
+ * @event WebSocketClient#toastMessage - Toast message received
146
+ */
147
+ toastMessage: ToastMessage;
148
+
149
+ artistCancelConfirmation: ArtistCancelConfirmation;
98
150
  };
@@ -9,7 +9,7 @@ import { LIB_VERSION } from '../../version';
9
9
  import { Logger } from '../../lib/DefaultLogger';
10
10
  import AuthManager from '../../lib/AuthManager';
11
11
 
12
- const PROTOCOL_VERSION = '0.4.3';
12
+ const PROTOCOL_VERSION = '3.0.0';
13
13
 
14
14
  const PING_INTERVAL = 15000;
15
15
 
@@ -11,7 +11,7 @@ import AuthManager, { Tokens } from '../lib/AuthManager';
11
11
 
12
12
  const WS_RECONNECT_ATTEMPTS = 5;
13
13
 
14
- export interface ApiReponse<D = JSONValue> {
14
+ export interface ApiResponse<D = JSONValue> {
15
15
  status: 'success';
16
16
  data: D;
17
17
  }
@@ -40,13 +40,15 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
40
40
  private _socket: WebSocketClient;
41
41
  private _auth: AuthManager;
42
42
  private _reconnectAttempts = WS_RECONNECT_ATTEMPTS;
43
+ private _disableSocket: boolean = false;
43
44
 
44
45
  constructor(
45
46
  baseUrl: string,
46
47
  socketUrl: string,
47
48
  appId: string,
48
49
  networkType: SupernetType,
49
- logger: Logger
50
+ logger: Logger,
51
+ disableSocket: boolean = false
50
52
  ) {
51
53
  super();
52
54
  this.appId = appId;
@@ -54,6 +56,7 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
54
56
  this._auth = new AuthManager(baseUrl, logger);
55
57
  this._rest = new RestClient(baseUrl, this._auth, logger);
56
58
  this._socket = new WebSocketClient(socketUrl, this._auth, appId, networkType, logger);
59
+ this._disableSocket = disableSocket;
57
60
 
58
61
  this._auth.on('refreshFailed', this.handleRefreshFailed.bind(this));
59
62
  this._socket.on('connected', this.handleSocketConnect.bind(this));
@@ -76,14 +79,22 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
76
79
  return this._rest;
77
80
  }
78
81
 
82
+ get socketEnabled(): boolean {
83
+ return !this._disableSocket;
84
+ }
85
+
79
86
  async authenticate(tokens: Tokens) {
80
87
  await this.auth.setTokens(tokens);
81
- await this.socket.connect();
88
+ if (!this._disableSocket) {
89
+ await this.socket.connect();
90
+ }
82
91
  }
83
92
 
84
93
  removeAuth() {
85
94
  this.auth.clear();
86
- this.socket.disconnect();
95
+ if (this.socket.isConnected) {
96
+ this.socket.disconnect();
97
+ }
87
98
  }
88
99
 
89
100
  handleSocketConnect({ network }: ServerConnectData) {
package/src/ApiGroup.ts CHANGED
@@ -1,23 +1,19 @@
1
- import { AbstractProvider } from 'ethers';
2
1
  import ApiClient from './ApiClient';
3
2
  import EIP712Helper from './lib/EIP712Helper';
4
3
  import TypedEventEmitter, { EventMap } from './lib/TypedEventEmitter';
5
4
 
6
5
  export interface ApiConfig {
7
6
  client: ApiClient;
8
- provider: AbstractProvider;
9
7
  eip712: EIP712Helper;
10
8
  }
11
9
 
12
10
  abstract class ApiGroup<E extends EventMap = {}> extends TypedEventEmitter<E> {
13
11
  protected client: ApiClient;
14
- protected provider: AbstractProvider;
15
12
  protected eip712: EIP712Helper;
16
13
 
17
14
  constructor(config: ApiConfig) {
18
15
  super();
19
16
  this.client = config.client;
20
- this.provider = config.provider;
21
17
  this.eip712 = config.eip712;
22
18
  }
23
19
  }
@@ -4,6 +4,24 @@ import { RawJob, RawProject } from './types/RawProject';
4
4
  import ProjectsApi from './index';
5
5
  import { Logger } from '../lib/DefaultLogger';
6
6
  import getUUID from '../lib/getUUID';
7
+ import { EnhancementStrength } from './types';
8
+ import Project from './Project';
9
+ import { SupernetType } from '../ApiClient/WebSocketClient/types';
10
+ import { getEnhacementStrength } from './utils';
11
+ import { TokenType } from '../types/token';
12
+
13
+ export const enhancementDefaults = {
14
+ network: 'fast' as SupernetType,
15
+ modelId: 'flux1-schnell-fp8',
16
+ positivePrompt: '',
17
+ negativePrompt: '',
18
+ stylePrompt: '',
19
+ startingImageStrength: 0.5,
20
+ steps: 5,
21
+ guidance: 1,
22
+ numberOfImages: 1,
23
+ numberOfPreviews: 0
24
+ };
7
25
 
8
26
  export type JobStatus =
9
27
  | 'pending'
@@ -40,6 +58,9 @@ export interface JobData {
40
58
  previewUrl?: string;
41
59
  resultUrl?: string | null;
42
60
  error?: ErrorData;
61
+ positivePrompt?: string;
62
+ negativePrompt?: string;
63
+ jobIndex?: number;
43
64
  }
44
65
 
45
66
  export interface JobEventMap extends EntityEvents {
@@ -51,6 +72,7 @@ export interface JobEventMap extends EntityEvents {
51
72
  export interface JobOptions {
52
73
  api: ProjectsApi;
53
74
  logger: Logger;
75
+ project: Project;
54
76
  }
55
77
 
56
78
  class Job extends DataEntity<JobData, JobEventMap> {
@@ -72,14 +94,18 @@ class Job extends DataEntity<JobData, JobEventMap> {
72
94
 
73
95
  private readonly _api: ProjectsApi;
74
96
  private readonly _logger: Logger;
97
+ private readonly _project: Project;
98
+ private _enhancementProject: Project | null = null;
75
99
 
76
100
  constructor(data: JobData, options: JobOptions) {
77
101
  super(data);
78
102
 
79
103
  this._api = options.api;
80
104
  this._logger = options.logger;
105
+ this._project = options.project;
81
106
 
82
107
  this.on('updated', this.handleUpdated.bind(this));
108
+ this.handleEnhancementUpdate = this.handleEnhancementUpdate.bind(this);
83
109
  }
84
110
 
85
111
  get id() {
@@ -152,6 +178,25 @@ class Job extends DataEntity<JobData, JobEventMap> {
152
178
  return this.data.error;
153
179
  }
154
180
 
181
+ get hasResultImage() {
182
+ return this.status === 'completed' && !this.isNSFW;
183
+ }
184
+
185
+ get enhancedImage() {
186
+ if (!this._enhancementProject) {
187
+ return null;
188
+ }
189
+ const project = this._enhancementProject;
190
+ const job = project.jobs[0];
191
+ return {
192
+ status: project.status,
193
+ progress: project.progress,
194
+ result: job?.resultUrl || null,
195
+ error: project.error,
196
+ getResultUrl: () => job?.getResultUrl()
197
+ };
198
+ }
199
+
155
200
  /**
156
201
  * Get the result URL of the job. This method will make a request to the API to get signed URL.
157
202
  * IMPORTANT: URL expires after 30 minutes, so make sure to download the image as soon as possible.
@@ -225,6 +270,59 @@ class Job extends DataEntity<JobData, JobEventMap> {
225
270
  this.emit('failed', this.data.error!);
226
271
  }
227
272
  }
273
+
274
+ private handleEnhancementUpdate() {
275
+ this.emit('updated', ['enhancedImage']);
276
+ }
277
+
278
+ async getResultData() {
279
+ if (!this.hasResultImage) {
280
+ throw new Error('No result image available');
281
+ }
282
+ const url = await this.getResultUrl();
283
+ const response = await fetch(url);
284
+ if (!response.ok) {
285
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
286
+ }
287
+ return response.blob();
288
+ }
289
+
290
+ /**
291
+ * Enhance the image using the Flux model. This method will create a new project with the
292
+ * enhancement parameters and use the result image of the current job as the starting image.
293
+ * @param strength - how much freedom the model has to change the image.
294
+ * @param overrides - optional parameters to override original prompt, style or token type.
295
+ */
296
+ async enhance(
297
+ strength: EnhancementStrength,
298
+ overrides: { positivePrompt?: string; stylePrompt?: string; tokenType?: TokenType } = {}
299
+ ) {
300
+ if (this.status !== 'completed') {
301
+ throw new Error('Job is not completed yet');
302
+ }
303
+ if (this.isNSFW) {
304
+ throw new Error('Job did not pass NSFW filter');
305
+ }
306
+ if (this._enhancementProject) {
307
+ this._enhancementProject.off('updated', this.handleEnhancementUpdate);
308
+ this._enhancementProject = null;
309
+ }
310
+ const imageData = await this.getResultData();
311
+ const project = await this._api.create({
312
+ ...enhancementDefaults,
313
+ positivePrompt: overrides.positivePrompt || this._project.params.positivePrompt,
314
+ stylePrompt: overrides.stylePrompt || this._project.params.stylePrompt,
315
+ tokenType: overrides.tokenType || this._project.params.tokenType,
316
+ seed: this.seed || this._project.params.seed,
317
+ startingImage: imageData,
318
+ startingImageStrength: 1 - getEnhacementStrength(strength),
319
+ sizePreset: this._project.params.sizePreset
320
+ });
321
+ this._enhancementProject = project;
322
+ this._enhancementProject.on('updated', this.handleEnhancementUpdate);
323
+ const images = await project.waitForCompletion();
324
+ return images[0];
325
+ }
228
326
  }
229
327
 
230
328
  export default Job;
@@ -1,4 +1,4 @@
1
- import Job, { JobData, JobStatus } from './Job';
1
+ import Job, { JobData } from './Job';
2
2
  import DataEntity, { EntityEvents } from '../lib/DataEntity';
3
3
  import { ProjectParams } from './types';
4
4
  import cloneDeep from 'lodash/cloneDeep';
@@ -50,6 +50,7 @@ export interface ProjectEventMap extends EntityEvents {
50
50
  progress: number;
51
51
  completed: string[];
52
52
  failed: ErrorData;
53
+ jobStarted: Job;
53
54
  jobCompleted: Job;
54
55
  jobFailed: Job;
55
56
  }
@@ -209,37 +210,24 @@ class Project extends DataEntity<ProjectData, ProjectEventMap> {
209
210
  */
210
211
  _addJob(data: JobData | Job) {
211
212
  const job =
212
- data instanceof Job ? data : new Job(data, { api: this._api, logger: this._logger });
213
+ data instanceof Job
214
+ ? data
215
+ : new Job(data, { api: this._api, logger: this._logger, project: this });
213
216
  this._jobs.push(job);
214
217
  job.on('updated', () => {
215
218
  this.lastUpdated = new Date();
216
219
  this.emit('updated', ['jobs']);
217
220
  });
221
+ this.emit('jobStarted', job);
218
222
  job.on('completed', () => {
219
223
  this.emit('jobCompleted', job);
220
- this._handleJobFinished(job);
221
224
  });
222
225
  job.on('failed', () => {
223
226
  this.emit('jobFailed', job);
224
- this._handleJobFinished(job);
225
227
  });
226
228
  return job;
227
229
  }
228
230
 
229
- private _handleJobFinished(job: Job) {
230
- const finalStatus: JobStatus[] = ['completed', 'failed', 'canceled'];
231
- const allJobsDone = this.jobs.every((job) => finalStatus.includes(job.status));
232
- // If all jobs are done and project is not already failed or completed, update the project status
233
- if (allJobsDone && this.status !== 'failed' && this.status !== 'completed') {
234
- const allJobsFailed = this.jobs.every((job) => job.status === 'failed');
235
- if (allJobsFailed) {
236
- this._update({ status: 'failed' });
237
- } else {
238
- this._update({ status: 'completed' });
239
- }
240
- }
241
- }
242
-
243
231
  private _checkForTimeout() {
244
232
  if (this.lastUpdated.getTime() + PROJECT_TIMEOUT < Date.now()) {
245
233
  this._syncToServer().catch((error) => {
@@ -298,7 +286,11 @@ class Project extends DataEntity<ProjectData, ProjectEventMap> {
298
286
  // If there are any jobs left in jobData, it means they are new jobs that are not in the project yet
299
287
  if (Object.keys(jobData).length) {
300
288
  for (const job of Object.values(jobData)) {
301
- const jobInstance = Job.fromRaw(data, job, { api: this._api, logger: this._logger });
289
+ const jobInstance = Job.fromRaw(data, job, {
290
+ api: this._api,
291
+ logger: this._logger,
292
+ project: this
293
+ });
302
294
  this._addJob(jobInstance);
303
295
  }
304
296
  }
@@ -139,7 +139,8 @@ function createJobRequestMessage(id: string, params: ProjectParams) {
139
139
  previews: params.numberOfPreviews || 0,
140
140
  numberOfImages: params.numberOfImages,
141
141
  jobID: id,
142
- disableSafety: !!params.disableNSFWFilter
142
+ disableSafety: !!params.disableNSFWFilter,
143
+ tokenType: params.tokenType
143
144
  };
144
145
  if (params.network) {
145
146
  jobRequest.network = params.network;
@@ -1,6 +1,7 @@
1
1
  import ApiGroup, { ApiConfig } from '../ApiGroup';
2
2
  import {
3
3
  AvailableModel,
4
+ EnhancementStrength,
4
5
  EstimateRequest,
5
6
  ImageUrlParams,
6
7
  ProjectParams,
@@ -16,7 +17,7 @@ import {
16
17
  } from '../ApiClient/WebSocketClient/events';
17
18
  import Project from './Project';
18
19
  import createJobRequestMessage from './createJobRequestMessage';
19
- import { ApiError, ApiReponse } from '../ApiClient';
20
+ import { ApiError, ApiResponse } from '../ApiClient';
20
21
  import { EstimationResponse } from './types/EstimationResponse';
21
22
  import { JobEvent, ProjectApiEvents, ProjectEvent } from './types/events';
22
23
  import getUUID from '../lib/getUUID';
@@ -24,6 +25,9 @@ import { RawProject } from './types/RawProject';
24
25
  import ErrorData from '../types/ErrorData';
25
26
  import { SupernetType } from '../ApiClient/WebSocketClient/types';
26
27
  import Cache from '../lib/Cache';
28
+ import { enhancementDefaults } from './Job';
29
+ import { getEnhacementStrength } from './utils';
30
+ import { TokenType } from '../types/token';
27
31
 
28
32
  const sizePresetCache = new Cache<SizePreset[]>(10 * 60 * 1000);
29
33
  const GARBAGE_COLLECT_TIMEOUT = 30000;
@@ -67,7 +71,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
67
71
  this.client.socket.on('jobProgress', this.handleJobProgress.bind(this));
68
72
  this.client.socket.on('jobError', this.handleJobError.bind(this));
69
73
  this.client.socket.on('jobResult', this.handleJobResult.bind(this));
70
- // Listen to server disconnect event
74
+ // Listen to the server disconnect event
71
75
  this.client.on('disconnected', this.handleServerDisconnected.bind(this));
72
76
  // Listen to project and job events and update project and job instances
73
77
  this.on('project', this.handleProjectEvent.bind(this));
@@ -115,7 +119,10 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
115
119
  type: 'initiating',
116
120
  projectId: data.jobID,
117
121
  jobId: data.imgID,
118
- workerName: data.workerName
122
+ workerName: data.workerName,
123
+ positivePrompt: data.positivePrompt,
124
+ negativePrompt: data.negativePrompt,
125
+ jobIndex: data.jobIndex
119
126
  });
120
127
  return;
121
128
  case 'jobStarted': {
@@ -123,7 +130,10 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
123
130
  type: 'started',
124
131
  projectId: data.jobID,
125
132
  jobId: data.imgID,
126
- workerName: data.workerName
133
+ workerName: data.workerName,
134
+ positivePrompt: data.positivePrompt,
135
+ negativePrompt: data.negativePrompt,
136
+ jobIndex: data.jobIndex
127
137
  });
128
138
  return;
129
139
  }
@@ -263,15 +273,30 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
263
273
  }
264
274
  switch (event.type) {
265
275
  case 'initiating':
266
- job._update({ status: 'initiating', workerName: event.workerName });
276
+ // positivePrompt and negativePrompt are only received if a Dynamic Prompt was used for the project creating a different prompt for each job
277
+ job._update({
278
+ status: 'initiating',
279
+ workerName: event.workerName,
280
+ positivePrompt: event.positivePrompt,
281
+ negativePrompt: event.negativePrompt,
282
+ jobIndex: event.jobIndex
283
+ });
267
284
  break;
268
285
  case 'started':
269
- job._update({ status: 'processing', workerName: event.workerName });
286
+ // positivePrompt and negativePrompt are only received if a Dynamic Prompt was used for the project creating a different prompt for each job
287
+ job._update({
288
+ status: 'processing',
289
+ workerName: event.workerName,
290
+ positivePrompt: event.positivePrompt,
291
+ negativePrompt: event.negativePrompt,
292
+ jobIndex: event.jobIndex
293
+ });
270
294
  break;
271
295
  case 'progress':
272
296
  job._update({
273
297
  status: 'processing',
274
- step: event.step,
298
+ // Jus in case event comes out of order
299
+ step: Math.max(event.step, job.step),
275
300
  stepCount: event.stepCount
276
301
  });
277
302
  if (project.status !== 'processing') {
@@ -337,10 +362,10 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
337
362
  */
338
363
  async create(data: ProjectParams): Promise<Project> {
339
364
  const project = new Project({ ...data }, { api: this, logger: this.client.logger });
340
- if (data.startingImage) {
365
+ if (data.startingImage && data.startingImage !== true) {
341
366
  await this.uploadGuideImage(project.id, data.startingImage);
342
367
  }
343
- if (data.controlNet?.image) {
368
+ if (data.controlNet?.image && data.controlNet.image !== true) {
344
369
  await this.uploadCNImage(project.id, data.controlNet.image);
345
370
  }
346
371
  const request = createJobRequestMessage(project.id, data);
@@ -356,7 +381,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
356
381
  * @param projectId
357
382
  */
358
383
  async get(projectId: string) {
359
- const { data } = await this.client.rest.get<ApiReponse<{ project: RawProject }>>(
384
+ const { data } = await this.client.rest.get<ApiResponse<{ project: RawProject }>>(
360
385
  `/v1/projects/${projectId}`
361
386
  );
362
387
  return data.project;
@@ -441,6 +466,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
441
466
  */
442
467
  async estimateCost({
443
468
  network,
469
+ tokenType,
444
470
  model,
445
471
  imageCount,
446
472
  stepCount,
@@ -452,6 +478,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
452
478
  sizePreset
453
479
  }: EstimateRequest) {
454
480
  const pathParams = [
481
+ tokenType || 'sogni',
455
482
  network,
456
483
  model,
457
484
  imageCount,
@@ -471,7 +498,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
471
498
  pathParams.push(width, height);
472
499
  }
473
500
  const r = await this.client.socket.get<EstimationResponse>(
474
- `/api/v1/job/estimate/${pathParams.join('/')}`
501
+ `/api/v2/job/estimate/${pathParams.join('/')}`
475
502
  );
476
503
  return {
477
504
  token: r.quote.project.costInToken,
@@ -479,13 +506,26 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
479
506
  };
480
507
  }
481
508
 
509
+ async estimateEnhancementCost(strength: EnhancementStrength, tokenType: TokenType = 'sogni') {
510
+ return this.estimateCost({
511
+ network: enhancementDefaults.network,
512
+ tokenType,
513
+ model: enhancementDefaults.modelId,
514
+ imageCount: 1,
515
+ stepCount: enhancementDefaults.steps,
516
+ previewCount: 0,
517
+ cnEnabled: false,
518
+ startingImageStrength: getEnhacementStrength(strength)
519
+ });
520
+ }
521
+
482
522
  /**
483
523
  * Get upload URL for image
484
524
  * @internal
485
525
  * @param params
486
526
  */
487
527
  async uploadUrl(params: ImageUrlParams) {
488
- const r = await this.client.rest.get<ApiReponse<{ uploadUrl: string }>>(
528
+ const r = await this.client.rest.get<ApiResponse<{ uploadUrl: string }>>(
489
529
  `/v1/image/uploadUrl`,
490
530
  params
491
531
  );
@@ -498,7 +538,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
498
538
  * @param params
499
539
  */
500
540
  async downloadUrl(params: ImageUrlParams) {
501
- const r = await this.client.rest.get<ApiReponse<{ downloadUrl: string }>>(
541
+ const r = await this.client.rest.get<ApiResponse<{ downloadUrl: string }>>(
502
542
  `/v1/image/downloadUrl`,
503
543
  params
504
544
  );
@@ -16,7 +16,8 @@ export type ControlNetName =
16
16
  | 'segmentation'
17
17
  | 'shuffle'
18
18
  | 'softedge'
19
- | 'tile';
19
+ | 'tile'
20
+ | 'instantid';
20
21
 
21
22
  /**
22
23
  * Raw ControlNet parameters passed to the API
@@ -53,8 +54,13 @@ export interface ControlNetParams {
53
54
  name: ControlNetName;
54
55
  /**
55
56
  * ControlNet input image
57
+ * Supported types:
58
+ * `File` - file object from input[type=file]
59
+ * `Buffer` - Node.js buffer object with image data
60
+ * `Blob` - blob object with image data
61
+ * `true` - indicates that the image is already uploaded to the server
56
62
  */
57
- image?: File | Buffer | Blob;
63
+ image?: File | Buffer | Blob | boolean;
58
64
  /**
59
65
  * ControlNet strength 0 to 1. 0 full control to prompt, 1 full control to ControlNet
60
66
  */