@sogni-ai/sogni-client 4.0.0-alpha.2 → 4.0.0-alpha.21
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 +142 -0
- package/README.md +38 -2
- package/dist/Account/index.d.ts +5 -2
- package/dist/Account/index.js +20 -7
- package/dist/Account/index.js.map +1 -1
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.d.ts +66 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.js +332 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.js.map +1 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/index.d.ts +28 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/index.js +207 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/index.js.map +1 -0
- package/dist/ApiClient/WebSocketClient/events.d.ts +1 -0
- package/dist/ApiClient/WebSocketClient/index.d.ts +2 -2
- package/dist/ApiClient/WebSocketClient/index.js +2 -2
- package/dist/ApiClient/WebSocketClient/index.js.map +1 -1
- package/dist/ApiClient/WebSocketClient/types.d.ts +13 -0
- package/dist/ApiClient/index.d.ts +4 -4
- package/dist/ApiClient/index.js +22 -3
- package/dist/ApiClient/index.js.map +1 -1
- package/dist/Projects/Job.d.ts +1 -1
- package/dist/Projects/createJobRequestMessage.js +3 -3
- package/dist/Projects/createJobRequestMessage.js.map +1 -1
- package/dist/Projects/index.d.ts +3 -9
- package/dist/Projects/index.js +7 -4
- package/dist/Projects/index.js.map +1 -1
- package/dist/Projects/types/EstimationResponse.d.ts +2 -0
- package/dist/Projects/types/SamplerParams.d.ts +13 -0
- package/dist/Projects/types/SamplerParams.js +26 -0
- package/dist/Projects/types/SamplerParams.js.map +1 -0
- package/dist/Projects/types/SchedulerParams.d.ts +14 -0
- package/dist/Projects/types/SchedulerParams.js +24 -0
- package/dist/Projects/types/SchedulerParams.js.map +1 -0
- package/dist/Projects/types/index.d.ts +21 -6
- package/dist/Projects/types/index.js +5 -0
- package/dist/Projects/types/index.js.map +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/DataEntity.js +4 -2
- package/dist/lib/DataEntity.js.map +1 -1
- package/dist/lib/validation.d.ts +2 -0
- package/dist/lib/validation.js +28 -0
- package/dist/lib/validation.js.map +1 -1
- package/package.json +4 -4
- package/src/Account/index.ts +22 -13
- package/src/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.ts +426 -0
- package/src/ApiClient/WebSocketClient/BrowserWebSocketClient/index.ts +241 -0
- package/src/ApiClient/WebSocketClient/events.ts +2 -0
- package/src/ApiClient/WebSocketClient/index.ts +4 -4
- package/src/ApiClient/WebSocketClient/types.ts +16 -0
- package/src/ApiClient/index.ts +29 -7
- package/src/Projects/createJobRequestMessage.ts +11 -5
- package/src/Projects/index.ts +9 -5
- package/src/Projects/types/EstimationResponse.ts +2 -0
- package/src/Projects/types/SamplerParams.ts +24 -0
- package/src/Projects/types/SchedulerParams.ts +22 -0
- package/src/Projects/types/index.ts +22 -12
- package/src/index.ts +23 -8
- package/src/lib/DataEntity.ts +4 -2
- package/src/lib/validation.ts +33 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { IWebSocketClient, SupernetType } from '../types';
|
|
2
|
+
import { AuthManager, TokenAuthManager } from '../../../lib/AuthManager';
|
|
3
|
+
import { Logger } from '../../../lib/DefaultLogger';
|
|
4
|
+
import WebSocketClient from '../index';
|
|
5
|
+
import RestClient from '../../../lib/RestClient';
|
|
6
|
+
import { SocketEventMap } from '../events';
|
|
7
|
+
import { MessageType, SocketMessageMap } from '../messages';
|
|
8
|
+
import ChannelCoordinator from './ChannelCoordinator';
|
|
9
|
+
|
|
10
|
+
interface SocketSend<T extends MessageType = MessageType> {
|
|
11
|
+
type: 'socket-send';
|
|
12
|
+
payload: { type: T; data: SocketMessageMap[T] };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SocketConnect {
|
|
16
|
+
type: 'connect';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface SocketDisconnect {
|
|
20
|
+
type: 'disconnect';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SwitchNetwork {
|
|
24
|
+
type: 'switchNetwork';
|
|
25
|
+
payload: SupernetType;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Message = SocketConnect | SocketDisconnect | SocketSend | SwitchNetwork;
|
|
29
|
+
|
|
30
|
+
interface EventNotification<T extends keyof SocketEventMap = keyof SocketEventMap> {
|
|
31
|
+
type: 'socket-event';
|
|
32
|
+
payload: { type: T; data: SocketEventMap[T] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface AuthStateChanged {
|
|
36
|
+
type: 'auth-state-changed';
|
|
37
|
+
payload: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type Notification = EventNotification | AuthStateChanged;
|
|
41
|
+
|
|
42
|
+
type EventInterceptor<T extends keyof SocketEventMap = keyof SocketEventMap> = (
|
|
43
|
+
eventType: T,
|
|
44
|
+
payload: SocketEventMap[T]
|
|
45
|
+
) => void;
|
|
46
|
+
|
|
47
|
+
class WrappedClient extends WebSocketClient {
|
|
48
|
+
private interceptor: EventInterceptor | undefined = undefined;
|
|
49
|
+
intercept(interceptor: EventInterceptor) {
|
|
50
|
+
this.interceptor = interceptor;
|
|
51
|
+
}
|
|
52
|
+
protected emit<T extends keyof SocketEventMap>(event: T, data: SocketEventMap[T]) {
|
|
53
|
+
super.emit(event, data);
|
|
54
|
+
if (this.interceptor) {
|
|
55
|
+
this.interceptor(event, data);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class BrowserWebSocketClient extends RestClient<SocketEventMap> implements IWebSocketClient {
|
|
61
|
+
appId: string;
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
private socketClient: WrappedClient;
|
|
64
|
+
private coordinator: ChannelCoordinator<Message, Notification>;
|
|
65
|
+
private _isConnected = false;
|
|
66
|
+
private _supernetType: SupernetType;
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
baseUrl: string,
|
|
70
|
+
auth: AuthManager,
|
|
71
|
+
appId: string,
|
|
72
|
+
supernetType: SupernetType,
|
|
73
|
+
logger: Logger
|
|
74
|
+
) {
|
|
75
|
+
const socketClient = new WrappedClient(baseUrl, auth, appId, supernetType, logger);
|
|
76
|
+
super(socketClient.baseUrl, auth, logger);
|
|
77
|
+
this.socketClient = socketClient;
|
|
78
|
+
this.appId = appId;
|
|
79
|
+
this.baseUrl = socketClient.baseUrl;
|
|
80
|
+
this._supernetType = supernetType;
|
|
81
|
+
this.coordinator = new ChannelCoordinator({
|
|
82
|
+
callbacks: {
|
|
83
|
+
onRoleChange: this.handleRoleChange.bind(this),
|
|
84
|
+
onMessage: this.handleMessage.bind(this),
|
|
85
|
+
onNotification: this.handleNotification.bind(this)
|
|
86
|
+
},
|
|
87
|
+
logger
|
|
88
|
+
});
|
|
89
|
+
this.auth.on('updated', this.handleAuthUpdated.bind(this));
|
|
90
|
+
this.socketClient.intercept(this.handleSocketEvent.bind(this));
|
|
91
|
+
//@ts-expect-error window is defined in browser
|
|
92
|
+
window.DISCONNECT = () => {
|
|
93
|
+
this.disconnect();
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get isConnected() {
|
|
98
|
+
return this.coordinator.isPrimary ? this.socketClient.isConnected : this._isConnected;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get supernetType() {
|
|
102
|
+
return this.coordinator.isPrimary ? this.socketClient.supernetType : this._supernetType;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async connect(): Promise<void> {
|
|
106
|
+
await this.coordinator.isReady();
|
|
107
|
+
if (this.coordinator.isPrimary) {
|
|
108
|
+
await this.socketClient.connect();
|
|
109
|
+
} else {
|
|
110
|
+
return this.coordinator.sendMessage({
|
|
111
|
+
type: 'connect'
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async disconnect() {
|
|
117
|
+
await this.coordinator.isReady();
|
|
118
|
+
if (this.coordinator.isPrimary) {
|
|
119
|
+
this.socketClient.disconnect();
|
|
120
|
+
} else {
|
|
121
|
+
this.coordinator.sendMessage({
|
|
122
|
+
type: 'disconnect'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async switchNetwork(supernetType: SupernetType): Promise<SupernetType> {
|
|
128
|
+
await this.coordinator.isReady();
|
|
129
|
+
if (this.coordinator.isPrimary) {
|
|
130
|
+
return this.socketClient.switchNetwork(supernetType);
|
|
131
|
+
}
|
|
132
|
+
await this.coordinator.sendMessage({
|
|
133
|
+
type: 'switchNetwork',
|
|
134
|
+
payload: supernetType
|
|
135
|
+
});
|
|
136
|
+
this._supernetType = supernetType;
|
|
137
|
+
return supernetType;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async send<T extends MessageType>(messageType: T, data: SocketMessageMap[T]): Promise<void> {
|
|
141
|
+
await this.coordinator.isReady();
|
|
142
|
+
if (this.coordinator.isPrimary) {
|
|
143
|
+
if (!this.socketClient.isConnected) {
|
|
144
|
+
await this.socketClient.connect();
|
|
145
|
+
}
|
|
146
|
+
return this.socketClient.send(messageType, data);
|
|
147
|
+
}
|
|
148
|
+
return this.coordinator.sendMessage({
|
|
149
|
+
type: 'socket-send',
|
|
150
|
+
payload: { type: messageType, data }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async handleMessage(message: Message) {
|
|
155
|
+
this._logger.debug('Received control message', message);
|
|
156
|
+
switch (message.type) {
|
|
157
|
+
case 'socket-send': {
|
|
158
|
+
if (!this.socketClient.isConnected) {
|
|
159
|
+
await this.socketClient.connect();
|
|
160
|
+
}
|
|
161
|
+
return this.socketClient.send(message.payload.type, message.payload.data);
|
|
162
|
+
}
|
|
163
|
+
case 'connect': {
|
|
164
|
+
if (!this.socketClient.isConnected) {
|
|
165
|
+
await this.socketClient.connect();
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
case 'disconnect': {
|
|
170
|
+
if (this.socketClient.isConnected) {
|
|
171
|
+
this.socketClient.disconnect();
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
case 'switchNetwork': {
|
|
176
|
+
await this.switchNetwork(message.payload);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
default: {
|
|
180
|
+
this._logger.error('Received unknown message type:', message);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private async handleNotification(notification: Notification) {
|
|
186
|
+
this._logger.debug('Received notification', notification.type, notification.payload);
|
|
187
|
+
switch (notification.type) {
|
|
188
|
+
case 'socket-event': {
|
|
189
|
+
this.emit(notification.payload.type, notification.payload.data);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
case 'auth-state-changed': {
|
|
193
|
+
this.handleAuthChanged(notification.payload);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
default: {
|
|
197
|
+
this._logger.error('Received unknown notification type:', notification);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private handleAuthChanged(isAuthenticated: boolean) {
|
|
203
|
+
if (this.auth instanceof TokenAuthManager) {
|
|
204
|
+
throw new Error('TokenAuthManager is not supported in multi client mode');
|
|
205
|
+
}
|
|
206
|
+
if (this.auth.isAuthenticated !== isAuthenticated) {
|
|
207
|
+
if (isAuthenticated) {
|
|
208
|
+
this.auth.authenticate();
|
|
209
|
+
} else {
|
|
210
|
+
this.auth.clear();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private handleSocketEvent(eventType: keyof SocketEventMap, payload: any) {
|
|
216
|
+
if (this.coordinator.isPrimary) {
|
|
217
|
+
this.coordinator.notify({
|
|
218
|
+
type: 'socket-event',
|
|
219
|
+
payload: { type: eventType, data: payload }
|
|
220
|
+
});
|
|
221
|
+
this.emit(eventType, payload);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private handleAuthUpdated(isAuthenticated: boolean) {
|
|
226
|
+
this.coordinator.notify({
|
|
227
|
+
type: 'auth-state-changed',
|
|
228
|
+
payload: isAuthenticated
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private handleRoleChange(isPrimary: boolean) {
|
|
233
|
+
if (isPrimary && !this.socketClient.isConnected && this.isConnected) {
|
|
234
|
+
this.socketClient.connect();
|
|
235
|
+
} else if (!isPrimary && this.socketClient.isConnected) {
|
|
236
|
+
this.socketClient.disconnect();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default BrowserWebSocketClient;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MessageType, SocketMessageMap } from './messages';
|
|
2
2
|
import { SocketEventMap } from './events';
|
|
3
3
|
import RestClient from '../../lib/RestClient';
|
|
4
|
-
import { SupernetType } from './types';
|
|
4
|
+
import { IWebSocketClient, SupernetType } from './types';
|
|
5
5
|
import WebSocket, { CloseEvent, ErrorEvent, MessageEvent } from 'isomorphic-ws';
|
|
6
6
|
import { base64Decode, base64Encode } from '../../lib/base64';
|
|
7
7
|
import isNodejs from '../../lib/isNodejs';
|
|
@@ -13,7 +13,7 @@ const PROTOCOL_VERSION = '3.0.0';
|
|
|
13
13
|
|
|
14
14
|
const PING_INTERVAL = 15000;
|
|
15
15
|
|
|
16
|
-
class WebSocketClient extends RestClient<SocketEventMap> {
|
|
16
|
+
class WebSocketClient extends RestClient<SocketEventMap> implements IWebSocketClient {
|
|
17
17
|
appId: string;
|
|
18
18
|
baseUrl: string;
|
|
19
19
|
private socket: WebSocket | null = null;
|
|
@@ -86,7 +86,7 @@ class WebSocketClient extends RestClient<SocketEventMap> {
|
|
|
86
86
|
socket.onmessage = null;
|
|
87
87
|
socket.onopen = null;
|
|
88
88
|
this.stopPing();
|
|
89
|
-
socket.close();
|
|
89
|
+
socket.close(1000, 'Client disconnected');
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
private startPing(socket: WebSocket) {
|
|
@@ -148,7 +148,7 @@ class WebSocketClient extends RestClient<SocketEventMap> {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
private handleClose(e: CloseEvent) {
|
|
151
|
-
if (e.target === this.socket) {
|
|
151
|
+
if (e.target === this.socket || !this.socket) {
|
|
152
152
|
this._logger.info('WebSocket disconnected, cleanup', e);
|
|
153
153
|
this.disconnect();
|
|
154
154
|
this.emit('disconnected', {
|
|
@@ -1 +1,17 @@
|
|
|
1
|
+
import { MessageType, SocketMessageMap } from './messages';
|
|
2
|
+
import RestClient from '../../lib/RestClient';
|
|
3
|
+
import { SocketEventMap } from './events';
|
|
4
|
+
|
|
1
5
|
export type SupernetType = 'relaxed' | 'fast';
|
|
6
|
+
|
|
7
|
+
export interface IWebSocketClient extends RestClient<SocketEventMap> {
|
|
8
|
+
appId: string;
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
isConnected: boolean;
|
|
11
|
+
supernetType: SupernetType;
|
|
12
|
+
|
|
13
|
+
connect(): Promise<void>;
|
|
14
|
+
disconnect(): void;
|
|
15
|
+
send<T extends MessageType>(messageType: T, data: SocketMessageMap[T]): Promise<void>;
|
|
16
|
+
switchNetwork(supernetType: SupernetType): Promise<SupernetType>;
|
|
17
|
+
}
|
package/src/ApiClient/index.ts
CHANGED
|
@@ -3,12 +3,14 @@ import WebSocketClient from './WebSocketClient';
|
|
|
3
3
|
import TypedEventEmitter from '../lib/TypedEventEmitter';
|
|
4
4
|
import { ApiClientEvents } from './events';
|
|
5
5
|
import { ServerConnectData, ServerDisconnectData } from './WebSocketClient/events';
|
|
6
|
-
import { isNotRecoverable } from './WebSocketClient/ErrorCode';
|
|
6
|
+
import { ErrorCode, isNotRecoverable } from './WebSocketClient/ErrorCode';
|
|
7
7
|
import { JSONValue } from '../types/json';
|
|
8
|
-
import { SupernetType } from './WebSocketClient/types';
|
|
8
|
+
import { IWebSocketClient, SupernetType } from './WebSocketClient/types';
|
|
9
9
|
import { Logger } from '../lib/DefaultLogger';
|
|
10
10
|
import CookieAuthManager from '../lib/AuthManager/CookieAuthManager';
|
|
11
11
|
import { AuthManager, TokenAuthManager } from '../lib/AuthManager';
|
|
12
|
+
import isNodejs from '../lib/isNodejs';
|
|
13
|
+
import BrowserWebSocketClient from './WebSocketClient/BrowserWebSocketClient';
|
|
12
14
|
|
|
13
15
|
const WS_RECONNECT_ATTEMPTS = 5;
|
|
14
16
|
|
|
@@ -42,13 +44,14 @@ export interface ApiClientOptions {
|
|
|
42
44
|
logger: Logger;
|
|
43
45
|
authType: 'token' | 'cookies';
|
|
44
46
|
disableSocket?: boolean;
|
|
47
|
+
multiInstance?: boolean;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
class ApiClient extends TypedEventEmitter<ApiClientEvents> {
|
|
48
51
|
readonly appId: string;
|
|
49
52
|
readonly logger: Logger;
|
|
50
53
|
private _rest: RestClient;
|
|
51
|
-
private _socket:
|
|
54
|
+
private _socket: IWebSocketClient;
|
|
52
55
|
private _auth: AuthManager;
|
|
53
56
|
private _reconnectAttempts = WS_RECONNECT_ATTEMPTS;
|
|
54
57
|
private _disableSocket: boolean = false;
|
|
@@ -60,7 +63,8 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
|
|
|
60
63
|
networkType,
|
|
61
64
|
authType,
|
|
62
65
|
logger,
|
|
63
|
-
disableSocket = false
|
|
66
|
+
disableSocket = false,
|
|
67
|
+
multiInstance = false
|
|
64
68
|
}: ApiClientOptions) {
|
|
65
69
|
super();
|
|
66
70
|
this.appId = appId;
|
|
@@ -68,7 +72,13 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
|
|
|
68
72
|
this._auth =
|
|
69
73
|
authType === 'token' ? new TokenAuthManager(baseUrl, logger) : new CookieAuthManager(logger);
|
|
70
74
|
this._rest = new RestClient(baseUrl, this._auth, logger);
|
|
71
|
-
|
|
75
|
+
const supportMultiInstance = !isNodejs && this._auth instanceof CookieAuthManager;
|
|
76
|
+
if (supportMultiInstance && multiInstance) {
|
|
77
|
+
// Use coordinated WebSocket client to share single connection between tabs
|
|
78
|
+
this._socket = new BrowserWebSocketClient(socketUrl, this._auth, appId, networkType, logger);
|
|
79
|
+
} else {
|
|
80
|
+
this._socket = new WebSocketClient(socketUrl, this._auth, appId, networkType, logger);
|
|
81
|
+
}
|
|
72
82
|
this._disableSocket = disableSocket;
|
|
73
83
|
this._auth.on('updated', this.handleAuthUpdated.bind(this));
|
|
74
84
|
this._socket.on('connected', this.handleSocketConnect.bind(this));
|
|
@@ -83,7 +93,7 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
|
|
|
83
93
|
return this._auth;
|
|
84
94
|
}
|
|
85
95
|
|
|
86
|
-
get socket():
|
|
96
|
+
get socket(): IWebSocketClient {
|
|
87
97
|
return this._socket;
|
|
88
98
|
}
|
|
89
99
|
|
|
@@ -101,14 +111,26 @@ class ApiClient extends TypedEventEmitter<ApiClientEvents> {
|
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
handleSocketDisconnect(data: ServerDisconnectData) {
|
|
114
|
+
// If user is not authenticated, we don't need to reconnect
|
|
115
|
+
if (!this.auth.isAuthenticated || data.code === 1000) {
|
|
116
|
+
this.emit('disconnected', data);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
104
119
|
if (!data.code || isNotRecoverable(data.code)) {
|
|
120
|
+
// If this is browser, another tab is probably claiming the connection, so we don't need to reconnect
|
|
121
|
+
if (
|
|
122
|
+
this._socket instanceof BrowserWebSocketClient &&
|
|
123
|
+
data.code === ErrorCode.SWITCH_CONNECTION
|
|
124
|
+
) {
|
|
125
|
+
this.logger.debug('Switching network connection, not reconnecting');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
105
128
|
this.auth.clear();
|
|
106
129
|
this.emit('disconnected', data);
|
|
107
130
|
this.logger.error('Not recoverable socket error', data);
|
|
108
131
|
return;
|
|
109
132
|
}
|
|
110
133
|
if (this._reconnectAttempts <= 0) {
|
|
111
|
-
this.auth.clear();
|
|
112
134
|
this.emit('disconnected', data);
|
|
113
135
|
this._reconnectAttempts = WS_RECONNECT_ATTEMPTS;
|
|
114
136
|
return;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { ProjectParams } from './types';
|
|
2
2
|
import { ControlNetParams, ControlNetParamsRaw } from './types/ControlNetParams';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
validateNumber,
|
|
5
|
+
validateCustomImageSize,
|
|
6
|
+
validateSampler,
|
|
7
|
+
validateScheduler
|
|
8
|
+
} from '../lib/validation';
|
|
9
|
+
|
|
4
10
|
// Mac worker can't process the data if some of the fields are missing, so we need to provide a default template
|
|
5
11
|
function getTemplate() {
|
|
6
12
|
return {
|
|
@@ -25,8 +31,8 @@ function getTemplate() {
|
|
|
25
31
|
guidanceScaleIsEnabled: true,
|
|
26
32
|
siImageBackgroundColor: 'black',
|
|
27
33
|
cnDragOffset: [0, 0],
|
|
28
|
-
scheduler:
|
|
29
|
-
timeStepSpacing:
|
|
34
|
+
scheduler: null,
|
|
35
|
+
timeStepSpacing: null,
|
|
30
36
|
steps: 20,
|
|
31
37
|
cnRotation: 0,
|
|
32
38
|
guidanceScale: 7.5,
|
|
@@ -119,8 +125,8 @@ function createJobRequestMessage(id: string, params: ProjectParams) {
|
|
|
119
125
|
keyFrames: [
|
|
120
126
|
{
|
|
121
127
|
...template.keyFrames[0],
|
|
122
|
-
scheduler: params.
|
|
123
|
-
timeStepSpacing: params.
|
|
128
|
+
scheduler: validateSampler(params.sampler),
|
|
129
|
+
timeStepSpacing: validateScheduler(params.scheduler),
|
|
124
130
|
steps: params.steps,
|
|
125
131
|
guidanceScale: params.guidance,
|
|
126
132
|
modelID: params.modelId,
|
package/src/Projects/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
EnhancementStrength,
|
|
5
5
|
EstimateRequest,
|
|
6
6
|
ImageUrlParams,
|
|
7
|
+
CostEstimation,
|
|
7
8
|
ProjectParams,
|
|
8
9
|
SizePreset,
|
|
9
10
|
SupportedModel
|
|
@@ -28,6 +29,7 @@ import Cache from '../lib/Cache';
|
|
|
28
29
|
import { enhancementDefaults } from './Job';
|
|
29
30
|
import { getEnhacementStrength } from './utils';
|
|
30
31
|
import { TokenType } from '../types/token';
|
|
32
|
+
import { validateSampler } from '../lib/validation';
|
|
31
33
|
|
|
32
34
|
const sizePresetCache = new Cache<SizePreset[]>(10 * 60 * 1000);
|
|
33
35
|
const GARBAGE_COLLECT_TIMEOUT = 30000;
|
|
@@ -515,9 +517,9 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
515
517
|
height,
|
|
516
518
|
sizePreset,
|
|
517
519
|
guidance,
|
|
518
|
-
|
|
520
|
+
sampler,
|
|
519
521
|
contextImages
|
|
520
|
-
}: EstimateRequest) {
|
|
522
|
+
}: EstimateRequest): Promise<CostEstimation> {
|
|
521
523
|
let apiVersion = 2;
|
|
522
524
|
const pathParams = [
|
|
523
525
|
tokenType || 'sogni',
|
|
@@ -541,10 +543,10 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
541
543
|
} else {
|
|
542
544
|
pathParams.push(0, 0);
|
|
543
545
|
}
|
|
544
|
-
if (
|
|
546
|
+
if (sampler) {
|
|
545
547
|
apiVersion = 3;
|
|
546
548
|
pathParams.push(guidance || 0);
|
|
547
|
-
pathParams.push(
|
|
549
|
+
pathParams.push(validateSampler(sampler)!);
|
|
548
550
|
pathParams.push(contextImages || 0);
|
|
549
551
|
}
|
|
550
552
|
const r = await this.client.socket.get<EstimationResponse>(
|
|
@@ -552,7 +554,9 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
|
|
|
552
554
|
);
|
|
553
555
|
return {
|
|
554
556
|
token: r.quote.project.costInToken,
|
|
555
|
-
usd: r.quote.project.costInUSD
|
|
557
|
+
usd: r.quote.project.costInUSD,
|
|
558
|
+
spark: r.quote.project.costInSpark,
|
|
559
|
+
sogni: r.quote.project.costInSogni
|
|
556
560
|
};
|
|
557
561
|
}
|
|
558
562
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const SupportedSamplers = {
|
|
2
|
+
dfs_sd3: 'Discrete Flow Scheduler (SD3)',
|
|
3
|
+
dpm_pp: 'DPM Solver Multistep (DPM-Solver++)',
|
|
4
|
+
dpm_pp_sde: 'DPM++ SDE',
|
|
5
|
+
dpm_pp_2m: 'DPM++ 2M',
|
|
6
|
+
//dpm_pp_2m_sde: 'DPM++ 2M SDE',
|
|
7
|
+
euler: 'Euler',
|
|
8
|
+
euler_a: 'Euler a',
|
|
9
|
+
//heun: 'Heun',
|
|
10
|
+
lcm: 'LCM (Latent Consistency Model)',
|
|
11
|
+
pndm_plms: 'PNDM (Pseudo-linear multi-step)'
|
|
12
|
+
//uni_pc: 'UniPC'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function isSampler(sampler: string): sampler is Sampler {
|
|
16
|
+
return sampler in SupportedSamplers;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isRawSampler(sampler: string): boolean {
|
|
20
|
+
const samplers = Object.values(SupportedSamplers);
|
|
21
|
+
return samplers.includes(sampler);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type Sampler = keyof typeof SupportedSamplers;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const SupportedSchedulers = {
|
|
2
|
+
beta: 'Beta',
|
|
3
|
+
ddim: 'DDIM',
|
|
4
|
+
karras: 'Karras',
|
|
5
|
+
kl_optimal: 'KL Optimal',
|
|
6
|
+
leading: 'Leading',
|
|
7
|
+
linear: 'Linear',
|
|
8
|
+
normal: 'Normal',
|
|
9
|
+
sgm_uniform: 'SGM Uniform',
|
|
10
|
+
simple: 'Simple'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function isScheduler(scheduler: string): scheduler is Scheduler {
|
|
14
|
+
return scheduler in SupportedSchedulers;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isRawScheduler(scheduler: string): boolean {
|
|
18
|
+
const schedulers = Object.values(SupportedSchedulers);
|
|
19
|
+
return schedulers.includes(scheduler);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Scheduler = keyof typeof SupportedSchedulers;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { SupernetType } from '../../ApiClient/WebSocketClient/types';
|
|
2
2
|
import { ControlNetParams } from './ControlNetParams';
|
|
3
3
|
import { TokenType } from '../../types/token';
|
|
4
|
+
import { Sampler, SupportedSamplers } from './SamplerParams';
|
|
5
|
+
import { Scheduler, SupportedSchedulers } from './SchedulerParams';
|
|
4
6
|
|
|
5
7
|
export interface SupportedModel {
|
|
6
8
|
id: string;
|
|
@@ -23,15 +25,9 @@ export interface SizePreset {
|
|
|
23
25
|
aspect: string;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
export type Scheduler
|
|
27
|
-
| 'DPM Solver Multistep (DPM-Solver++)'
|
|
28
|
-
| 'PNDM (Pseudo-linear multi-step)'
|
|
29
|
-
| 'LCM (Latent Consistency Model)'
|
|
30
|
-
| 'Discrete Flow Scheduler (SD3)'
|
|
31
|
-
| 'Euler' // Used by Flux
|
|
32
|
-
| 'Euler a'; // Used by Flux
|
|
28
|
+
export type { Sampler, Scheduler };
|
|
33
29
|
|
|
34
|
-
export
|
|
30
|
+
export { SupportedSamplers, SupportedSchedulers };
|
|
35
31
|
|
|
36
32
|
export type OutputFormat = 'png' | 'jpg';
|
|
37
33
|
|
|
@@ -108,11 +104,11 @@ export interface ProjectParams {
|
|
|
108
104
|
/**
|
|
109
105
|
* Scheduler to use
|
|
110
106
|
*/
|
|
111
|
-
|
|
107
|
+
sampler?: Sampler;
|
|
112
108
|
/**
|
|
113
109
|
* Time step spacing method
|
|
114
110
|
*/
|
|
115
|
-
|
|
111
|
+
scheduler?: Scheduler;
|
|
116
112
|
/**
|
|
117
113
|
* Size preset ID to use. You can query available size presets
|
|
118
114
|
* from `client.projects.sizePresets(network, modelId)`
|
|
@@ -202,9 +198,9 @@ export interface EstimateRequest {
|
|
|
202
198
|
*/
|
|
203
199
|
guidance?: number;
|
|
204
200
|
/**
|
|
205
|
-
*
|
|
201
|
+
* Sampler
|
|
206
202
|
*/
|
|
207
|
-
|
|
203
|
+
sampler?: Sampler;
|
|
208
204
|
/**
|
|
209
205
|
* Number of context images to use (for Flux Kontext).
|
|
210
206
|
* Note that this parameter is ignored if `scheduler` is not provided
|
|
@@ -212,4 +208,18 @@ export interface EstimateRequest {
|
|
|
212
208
|
contextImages?: number;
|
|
213
209
|
}
|
|
214
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Represents estimation of project cost in different currency formats
|
|
213
|
+
*/
|
|
214
|
+
export interface CostEstimation {
|
|
215
|
+
/** Cost in selected token type */
|
|
216
|
+
token: string;
|
|
217
|
+
/** Cost in USD */
|
|
218
|
+
usd: string;
|
|
219
|
+
/** Cost in Spark Points */
|
|
220
|
+
spark: string;
|
|
221
|
+
/** Cost in Sogni tokens */
|
|
222
|
+
sogni: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
215
225
|
export type EnhancementStrength = 'light' | 'medium' | 'heavy';
|
package/src/index.ts
CHANGED
|
@@ -16,8 +16,10 @@ import {
|
|
|
16
16
|
AvailableModel,
|
|
17
17
|
OutputFormat,
|
|
18
18
|
ProjectParams,
|
|
19
|
+
Sampler,
|
|
19
20
|
Scheduler,
|
|
20
|
-
|
|
21
|
+
SupportedSamplers,
|
|
22
|
+
SupportedSchedulers
|
|
21
23
|
} from './Projects/types';
|
|
22
24
|
// Stats API
|
|
23
25
|
import StatsApi from './Stats';
|
|
@@ -36,13 +38,13 @@ export type {
|
|
|
36
38
|
OutputFormat,
|
|
37
39
|
ProjectParams,
|
|
38
40
|
ProjectStatus,
|
|
39
|
-
|
|
41
|
+
Sampler,
|
|
40
42
|
SupernetType,
|
|
41
|
-
|
|
43
|
+
Scheduler,
|
|
42
44
|
TokenType
|
|
43
45
|
};
|
|
44
46
|
|
|
45
|
-
export { ApiError, CurrentAccount, Job, Project };
|
|
47
|
+
export { ApiError, CurrentAccount, Job, Project, SupportedSamplers, SupportedSchedulers };
|
|
46
48
|
|
|
47
49
|
export interface SogniClientConfig {
|
|
48
50
|
/**
|
|
@@ -61,7 +63,7 @@ export interface SogniClientConfig {
|
|
|
61
63
|
socketEndpoint?: string;
|
|
62
64
|
/**
|
|
63
65
|
* Disable WebSocket connection. Useful for testing or when WebSocket is not needed.
|
|
64
|
-
* Note that many may not work without WebSocket connection.
|
|
66
|
+
* Note that many APIs may not work without WebSocket connection.
|
|
65
67
|
* @experimental
|
|
66
68
|
* @internal
|
|
67
69
|
*/
|
|
@@ -93,6 +95,14 @@ export interface SogniClientConfig {
|
|
|
93
95
|
* @experimental
|
|
94
96
|
*/
|
|
95
97
|
authType?: 'token' | 'cookies';
|
|
98
|
+
/**
|
|
99
|
+
* Browser only. If true, the client will use a single WebSocket connection shared across multiple
|
|
100
|
+
* tabs. This is useful for browser apps that need to process multiple projects at the same time.
|
|
101
|
+
* Only works in browser environment and with cookie authentication.
|
|
102
|
+
* @default false
|
|
103
|
+
* @experimental
|
|
104
|
+
*/
|
|
105
|
+
multiInstance?: boolean;
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
export class SogniClient {
|
|
@@ -139,9 +149,13 @@ export class SogniClient {
|
|
|
139
149
|
throw Error('This method should only be called when using cookie auth');
|
|
140
150
|
}
|
|
141
151
|
try {
|
|
142
|
-
await this.apiClient.rest.get<ApiResponse<MeData>>('/v1/account/me');
|
|
152
|
+
const res = await this.apiClient.rest.get<ApiResponse<MeData>>('/v1/account/me');
|
|
143
153
|
await auth.authenticate();
|
|
144
|
-
|
|
154
|
+
this.currentAccount._update({
|
|
155
|
+
username: res.data.username,
|
|
156
|
+
email: res.data.currentEmail,
|
|
157
|
+
walletAddress: res.data.walletAddress
|
|
158
|
+
});
|
|
145
159
|
return true;
|
|
146
160
|
} catch (e) {
|
|
147
161
|
this.apiClient.logger.info('Client is not authenticated');
|
|
@@ -167,7 +181,8 @@ export class SogniClient {
|
|
|
167
181
|
networkType: network,
|
|
168
182
|
logger,
|
|
169
183
|
authType: config.authType || 'token',
|
|
170
|
-
disableSocket: config.disableSocket
|
|
184
|
+
disableSocket: config.disableSocket,
|
|
185
|
+
multiInstance: config.multiInstance
|
|
171
186
|
});
|
|
172
187
|
const eip712 = new EIP712Helper({
|
|
173
188
|
name: isTestnet ? 'Sogni-testnet' : 'Sogni AI',
|