@sogni-ai/sogni-client 5.65.0 → 5.66.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 +14 -0
- package/README.md +4 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.d.ts +9 -0
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.js +45 -3
- package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.js.map +1 -1
- package/dist/Chat/index.js +6 -3
- package/dist/Chat/index.js.map +1 -1
- package/dist/Projects/createJobRequestMessage.js +13 -0
- package/dist/Projects/createJobRequestMessage.js.map +1 -1
- package/dist/Projects/types/index.d.ts +4 -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/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.js +45 -3
- package/dist-esm/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.js.map +1 -1
- package/dist-esm/Chat/index.js +6 -3
- package/dist-esm/Chat/index.js.map +1 -1
- package/dist-esm/Projects/createJobRequestMessage.js +13 -0
- package/dist-esm/Projects/createJobRequestMessage.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 +1 -1
- package/package.json +1 -1
- package/src/ApiClient/WebSocketClient/BrowserWebSocketClient/ChannelCoordinator.ts +44 -3
- package/src/Chat/index.ts +6 -3
- package/src/Projects/createJobRequestMessage.ts +20 -3
- package/src/Projects/types/index.ts +4 -0
- package/src/version.ts +1 -1
|
@@ -4,6 +4,12 @@ import { MessageDeliveryUncertainError, SEND_READY_TIMEOUT_MS } from '../request
|
|
|
4
4
|
|
|
5
5
|
const PRIMARY_HEARTBEAT_INTERVAL = 2000;
|
|
6
6
|
const PRIMARY_TIMEOUT = 4000;
|
|
7
|
+
// How long a visible tab waits without a heartbeat from the primary before it
|
|
8
|
+
// takes the connection over itself. A primary in a background tab can keep
|
|
9
|
+
// answering elections (messages are not throttled) while its timers, and so its
|
|
10
|
+
// heartbeats, have all but stopped; requests forwarded to it can then wait on a
|
|
11
|
+
// connection that no longer works.
|
|
12
|
+
const PRIMARY_TAKEOVER_TIMEOUT = 12000;
|
|
7
13
|
const ELECTION_SETTLE_TIMEOUT = 600;
|
|
8
14
|
const ACK_TIMEOUT = 5000;
|
|
9
15
|
|
|
@@ -126,6 +132,11 @@ class ChannelCoordinator<M, N> {
|
|
|
126
132
|
|
|
127
133
|
// Heartbeat to detect primary tab death
|
|
128
134
|
private lastPrimaryHeartbeat: number = Date.now();
|
|
135
|
+
// The primary this tab follows, and when it last sent a heartbeat of its own.
|
|
136
|
+
// Unlike lastPrimaryHeartbeat, the primary re-announcing itself in answer to
|
|
137
|
+
// an election does not count.
|
|
138
|
+
private primaryId: string | null = null;
|
|
139
|
+
private lastHeartbeatFromPrimary: number = Date.now();
|
|
129
140
|
private heartbeatTimer: NodeJS.Timeout | null = null;
|
|
130
141
|
private primaryCheckTimer: NodeJS.Timeout | null = null;
|
|
131
142
|
private readyCallback: () => void | null = () => {};
|
|
@@ -160,6 +171,10 @@ class ChannelCoordinator<M, N> {
|
|
|
160
171
|
return this._isPrimary;
|
|
161
172
|
}
|
|
162
173
|
|
|
174
|
+
private tabIsVisible() {
|
|
175
|
+
return isActiveTab;
|
|
176
|
+
}
|
|
177
|
+
|
|
163
178
|
private get currentPriority() {
|
|
164
179
|
return isActiveTab ? this.priority * 10 : this.priority;
|
|
165
180
|
}
|
|
@@ -209,8 +224,24 @@ class ChannelCoordinator<M, N> {
|
|
|
209
224
|
return new Promise((resolve) => setTimeout(resolve, ELECTION_SETTLE_TIMEOUT));
|
|
210
225
|
}
|
|
211
226
|
|
|
227
|
+
/**
|
|
228
|
+
* A visible secondary whose primary has sent no heartbeat for
|
|
229
|
+
* PRIMARY_TAKEOVER_TIMEOUT becomes primary itself. Announcing demotes the
|
|
230
|
+
* silent primary, which closes its connection; this tab opens its own.
|
|
231
|
+
*/
|
|
232
|
+
private takeOverFromSilentPrimary(): boolean {
|
|
233
|
+
if (this._isPrimary || !this.tabIsVisible()) return false;
|
|
234
|
+
const silentFor = Date.now() - this.lastHeartbeatFromPrimary;
|
|
235
|
+
if (silentFor <= PRIMARY_TAKEOVER_TIMEOUT) return false;
|
|
236
|
+
this.logger.debug(`Primary sent no heartbeat for ${silentFor}ms, taking over as primary`);
|
|
237
|
+
this.electionInProgress = false;
|
|
238
|
+
this.electionResponses.clear();
|
|
239
|
+
this.becomePrimary();
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
212
243
|
private async ensureFreshPrimaryBeforeRequest(): Promise<void> {
|
|
213
|
-
if (this._isPrimary) {
|
|
244
|
+
if (this._isPrimary || this.takeOverFromSilentPrimary()) {
|
|
214
245
|
return;
|
|
215
246
|
}
|
|
216
247
|
|
|
@@ -260,6 +291,7 @@ class ChannelCoordinator<M, N> {
|
|
|
260
291
|
|
|
261
292
|
private becomePrimary() {
|
|
262
293
|
this._isPrimary = true;
|
|
294
|
+
this.primaryId = this.id;
|
|
263
295
|
this.lastPrimaryHeartbeat = Date.now();
|
|
264
296
|
this.broadcast({
|
|
265
297
|
type: MessageType.PRIMARY_ANNOUNCE,
|
|
@@ -300,6 +332,7 @@ class ChannelCoordinator<M, N> {
|
|
|
300
332
|
}
|
|
301
333
|
this.primaryCheckTimer = setInterval(() => {
|
|
302
334
|
if (!this._isPrimary) {
|
|
335
|
+
if (this.takeOverFromSilentPrimary()) return;
|
|
303
336
|
const timeSinceLastHeartbeat = Date.now() - this.lastPrimaryHeartbeat;
|
|
304
337
|
if (timeSinceLastHeartbeat > PRIMARY_TIMEOUT) {
|
|
305
338
|
this.startElections();
|
|
@@ -325,7 +358,7 @@ class ChannelCoordinator<M, N> {
|
|
|
325
358
|
case MessageType.PRIMARY_ANNOUNCE:
|
|
326
359
|
return this.handlePrimaryAnnounce(message);
|
|
327
360
|
case MessageType.PRIMARY_HEARTBEAT:
|
|
328
|
-
return this.handlePrimaryHeartbeat();
|
|
361
|
+
return this.handlePrimaryHeartbeat(message);
|
|
329
362
|
case MessageType.BROADCAST:
|
|
330
363
|
return this.handleBroadcast(message);
|
|
331
364
|
case MessageType.REQUEST:
|
|
@@ -373,6 +406,12 @@ class ChannelCoordinator<M, N> {
|
|
|
373
406
|
const wasPrimary = this._isPrimary;
|
|
374
407
|
this._isPrimary = false;
|
|
375
408
|
this.lastPrimaryHeartbeat = Date.now();
|
|
409
|
+
// A new primary starts with a clean record; the same primary answering an
|
|
410
|
+
// election again proves only that its tab still handles messages.
|
|
411
|
+
if (message.payload.id !== this.primaryId) {
|
|
412
|
+
this.primaryId = message.payload.id;
|
|
413
|
+
this.lastHeartbeatFromPrimary = Date.now();
|
|
414
|
+
}
|
|
376
415
|
this.electionInProgress = false;
|
|
377
416
|
this.electionResponses.clear();
|
|
378
417
|
|
|
@@ -386,8 +425,10 @@ class ChannelCoordinator<M, N> {
|
|
|
386
425
|
this.readyCallback();
|
|
387
426
|
}
|
|
388
427
|
|
|
389
|
-
private handlePrimaryHeartbeat() {
|
|
428
|
+
private handlePrimaryHeartbeat(message: PrimaryHeartbeat) {
|
|
390
429
|
this.lastPrimaryHeartbeat = Date.now();
|
|
430
|
+
this.primaryId = message.payload.id;
|
|
431
|
+
this.lastHeartbeatFromPrimary = Date.now();
|
|
391
432
|
}
|
|
392
433
|
|
|
393
434
|
private handleBroadcast(message: BroadcastMessage) {
|
package/src/Chat/index.ts
CHANGED
|
@@ -1220,11 +1220,14 @@ class ChatApi extends ApiGroup<ChatApiEvents> {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
|
|
1222
1222
|
private handleSocketAuthenticated(data: AuthenticatedData): void {
|
|
1223
|
-
if (!
|
|
1223
|
+
if (!Array.isArray(data?.activeLLMJobIDs)) return;
|
|
1224
1224
|
const live = new Set(data.activeLLMJobIDs.map((id) => String(id).toUpperCase()));
|
|
1225
|
+
// Every job already sent, not only those a 'connecting' put on watch: a tab
|
|
1226
|
+
// that takes the connection over from another tab gets no 'connecting', and
|
|
1227
|
+
// a job queued on the old connection was cancelled when it closed.
|
|
1225
1228
|
const gone: string[] = [];
|
|
1226
|
-
for (const jobID of this.
|
|
1227
|
-
if (!live.has(jobID.toUpperCase())) gone.push(jobID);
|
|
1229
|
+
for (const jobID of this.activeStreams.keys()) {
|
|
1230
|
+
if (!this.unsentJobs.has(jobID) && !live.has(jobID.toUpperCase())) gone.push(jobID);
|
|
1228
1231
|
}
|
|
1229
1232
|
this.jobsAwaitingReconnect.clear();
|
|
1230
1233
|
this.clearTransportGraceTimer();
|
|
@@ -1372,15 +1372,32 @@ function applyImageParams(
|
|
|
1372
1372
|
params: ImageProjectParams,
|
|
1373
1373
|
options: ImageModelOptions
|
|
1374
1374
|
) {
|
|
1375
|
-
if (
|
|
1376
|
-
|
|
1377
|
-
|
|
1375
|
+
if (
|
|
1376
|
+
['moss_image_sft_int8_convrot', 'moss_image_sft_dmd8_int8_convrot'].includes(params.modelId) &&
|
|
1377
|
+
(params.startingImage || params.contextImages?.length)
|
|
1378
|
+
) {
|
|
1379
|
+
throw new Error(
|
|
1380
|
+
'MOSS-Image SFT generates from text and does not accept guide or reference images.'
|
|
1381
|
+
);
|
|
1378
1382
|
}
|
|
1379
1383
|
const keyFrame: Record<string, any> = {
|
|
1380
1384
|
...inputKeyframe,
|
|
1381
1385
|
sizePreset: params.sizePreset
|
|
1382
1386
|
};
|
|
1383
1387
|
const contextImages = params.contextImages || [];
|
|
1388
|
+
for (const name of ['mossRopeScaleX', 'mossRopeScaleY'] as const) {
|
|
1389
|
+
const value = params[name];
|
|
1390
|
+
if (value === undefined) continue;
|
|
1391
|
+
if (
|
|
1392
|
+
!['moss_image_sft_int8_convrot', 'moss_image_sft_dmd8_int8_convrot'].includes(params.modelId)
|
|
1393
|
+
) {
|
|
1394
|
+
throw new Error(`${name} is only supported by MOSS-Image`);
|
|
1395
|
+
}
|
|
1396
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 100) {
|
|
1397
|
+
throw new Error(`${name} must be a finite number between 0 and 100`);
|
|
1398
|
+
}
|
|
1399
|
+
if (value !== 1) keyFrame[name] = value;
|
|
1400
|
+
}
|
|
1384
1401
|
for (let index = 1; index <= 16; index += 1) {
|
|
1385
1402
|
keyFrame[`hasContextImage${index}`] = !!contextImages[index - 1];
|
|
1386
1403
|
}
|
|
@@ -931,6 +931,10 @@ export interface Sam3Selection {
|
|
|
931
931
|
export interface ImageProjectParams
|
|
932
932
|
extends BaseProjectParams, Pixal3dGenerationOptions, Pixal3dMultiViewImages {
|
|
933
933
|
type: 'image';
|
|
934
|
+
/** MOSS only: horizontal spatial scaling, 0–100; 1 is neutral. Changes composition, not output size. */
|
|
935
|
+
mossRopeScaleX?: number;
|
|
936
|
+
/** MOSS only: vertical spatial scaling, 0–100; 1 is neutral. */
|
|
937
|
+
mossRopeScaleY?: number;
|
|
934
938
|
/**
|
|
935
939
|
* Number of previews to generate. Note that previews affect project cost
|
|
936
940
|
*/
|
package/src/version.ts
CHANGED